Skip to content

Add initial support for ZZZ deobf'd DM - #3187

Open
nguyentvan7 wants to merge 3 commits into
masterfrom
van/zzz/deobf
Open

Add initial support for ZZZ deobf'd DM#3187
nguyentvan7 wants to merge 3 commits into
masterfrom
van/zzz/deobf

Conversation

@nguyentvan7

@nguyentvan7 nguyentvan7 commented Apr 7, 2026

Copy link
Copy Markdown
Collaborator

Describe your changes

Add 2 pipelines that help in mapping known DM properties to human-readable names

There is still additional work that needs to be done for proper support in the code, such as:

  • Generating typing
  • Ensuring all properties we want are available (we are probably missing a lot and need to reverse search to find them and how to connect them)
  • other stuff probably

Issue or discord link

Testing/validation

Checklist before requesting a review (leave this PR as draft if any part of this list is not done.)

  • I have commented my code in hard-to understand areas.
  • I have made corresponding changes to README or wiki.
  • For front-end changes, I have updated the corresponding English translations.
  • I have run yarn run mini-ci locally to validate format and lint.
  • If I have added a new library or app, I have updated the deployment scripts to ignore changes as needed

Summary by CodeRabbit

  • New Features

    • Added support for Zenless Zone Zero datamine updates alongside existing games
    • Introduced deobfuscation mapping generation for game data
  • Documentation

    • Updated datamine process to cover multi-game support
    • Marked deprecated data source as no longer operational; added new deobfuscation workflow guidance

@nguyentvan7
nguyentvan7 requested a review from priolette April 7, 2026 16:18
@coderabbitai

coderabbitai Bot commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This pull request introduces a comprehensive deobfuscation mapping generation system for Zenless Zone Zero datamining. It adds Git submodules, Nx generators and executors, utility functions, and deobfuscation template modules that transform obfuscated DM JSON files into readable mapped structures with type information.

Changes

Cohort / File(s) Summary
Git Submodules & Configuration
.gitmodules, libs/zzz/dm/ZenlessData, libs/zzz/dm/ZenlessDataDeobf
Added new ZenlessDataDeobf submodule with shallow cloning and updated existing ZenlessData submodule pointer.
Nx Project Configuration
libs/zzz/dm/project.json, libs/zzz/dm/executors.json, libs/zzz/dm/generators.json, libs/zzz/dm/package.json, libs/zzz/dm/.eslintrc.json
Updated Nx configuration to register deobf executor, add generators field, declare @nx/devkit dependency, add ESLint overrides for JSON manifests, and introduce new deobf build target with outputs configuration.
Type Definitions & Utilities
libs/zzz/dm/src/dm/deobf/types.ts, libs/zzz/dm/src/dm/deobf/util.ts, libs/zzz/dm/src/util.ts
Added GenericDmFile type, implemented generateMapping/generateTyping transformation functions, and extended readDMJSON with optional root parameter for flexible path resolution.
Constants & Exports
libs/zzz/dm/src/consts.ts, libs/zzz/dm/src/dm/deobf/index.ts
Defined file config identifier arrays (allCharFileCfgs, allDiscFileCfgs, allWengineFileCfgs, allFileCfgs) and created barrel export for deobfuscation modules.
Deobfuscation Template Modules
libs/zzz/dm/src/dm/deobf/FileCfg/*.ts
Added 11 template modules (Avatar variants, Equipment, Item, Weapon, Skill) that load DM JSON, define reverse mappings from obfuscated keys to readable field names, generate mapping/typing artifacts, and export transformation data.
Deobf Executor
libs/zzz/dm/src/executors/deobf/*.ts, libs/zzz/dm/src/executors/deobf/schema.*
Implemented Nx executor that iterates deobf mappings, transforms DM JSON objects via key remapping, formats output, and writes results to ZenlessDataDeobf directory. Includes executor schema and test spec.
Generators
libs/zzz/dm/src/generators/generate-*-deobf-template.ts, libs/zzz/dm/src/generators/schema.d.ts, libs/zzz/dm/src/generators/*-schema.json
Added two generators: generate-single-deobf-template (analyzes DM structure, computes reverse mappings with duplicate-key handling, generates template file) and generate-all-deobf-template (orchestrates generation for all configs plus index). Includes schemas and helper for index generation.
Tests & Documentation
libs/zzz/dm/src/**/*.spec.ts, libs/zzz/dm/README.md, libs/sr/dm-localization/src/executors/gen-locale/lib/util.ts
Added Jest specs for executor and both generators. Updated README to describe Zenless deobf mapping workflow and mark Hakush.in section as deprecated. Fixed hash comment in Star Rail utility.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Generator as Single Template Generator
    participant Reader as DM JSON Reader
    participant Analyzer as Reverse Mapping Analyzer
    participant Writer as File Writer
    
    User->>Generator: invoke generate-single-deobf-template({ file })
    Generator->>Reader: readDMJSON('FileCfg/${file}.json')
    Reader-->>Generator: parsed DM object
    Generator->>Analyzer: analyze object structure
    Analyzer->>Analyzer: compute reverseMapping<br/>(value → field name)
    Analyzer-->>Generator: reverseMapping + keyValueSet
    Generator->>Generator: build template file with<br/>embedded reverseMapping
    Generator->>Generator: generateMapping() +<br/>generateTyping()
    Generator->>Writer: formatText(dest, contents)
    Writer->>Writer: format TypeScript code
    Writer-->>Generator: formatted content
    Generator->>Writer: writeFileSync(path, content)
    Writer-->>User: Template file written
Loading
sequenceDiagram
    participant User
    participant AllGenerator as All Templates Generator
    participant SingleGen as Single Template Generator
    participant IndexGen as Index Generator
    participant Executor as Deobf Executor
    participant FileSystem as File System
    
    User->>AllGenerator: invoke generate-all-deobf-template()
    loop for each file in allFileCfgs
        AllGenerator->>SingleGen: generateSingleDeobfTemplate(file)
        SingleGen-->>AllGenerator: template generated
    end
    AllGenerator->>IndexGen: generateIndex(tree)
    IndexGen->>IndexGen: build index.ts with<br/>all imports + exports
    IndexGen->>FileSystem: writeFileSync(index.ts)
    FileSystem-->>IndexGen: written
    Note over Executor: Later, when deobf executor runs:
    User->>Executor: yarn run deobf
    Executor->>FileSystem: iterate deobfMappings
    loop for each DM object
        Executor->>Executor: remap keys using<br/>deobfObj.mapping
        Executor->>FileSystem: formatText + writeFileSync<br/>to ZenlessDataDeobf/
    end
    Executor-->>User: { success: true }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #2856: Introduces formatText utility from common/pipeline that is heavily imported and used throughout the new executor and generator implementations.
  • PR #2671: Adds shallow cloning configuration (shallow = true) for ZenlessData submodule; this PR extends that pattern to the new ZenlessDataDeobf submodule.

Suggested labels

ZO

Suggested reviewers

  • frzyc

Poem

🌙 Behold, the deobf machine awakes at 3 AM,
Transforming obfuscated keys to blessed clarity,
While gacha rolls blur and stamina counts fall—
We extract the Zenless truth, one mapping at a time, 💎
No sleep needed when datamining calls.


[Adjusts glasses while Red Bull slowly loses carbonation]

Okay, so... this is actually a pretty impressive change set, ngl. You've built out an entire deobfuscation pipeline here—generators that analyze DM JSON structure, figure out what those obfuscated field names actually mean, generate TypeScript files with reverse mappings, and then an executor that applies those mappings to transform the raw data into something readable.

The sheer number of template files (11+ FileCfg modules) all following a similar-but-distinct pattern is what's gonna make review time climb—each one needs verification that the reverse mappings make sense for that specific game entity. The generator logic that handles duplicate key suffixes and dynamic mapping computation is also dense enough to merit careful reading.

As someone who's wasted many a late night analyzing game data structures... chef's kiss. This is the good stuff. Just make sure those reverseMapping objects are actually correct, yeah?

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add initial support for ZZZ deobf'd DM' clearly and specifically describes the main change: introducing deobfuscation support for Zenless Zone Zero datamine properties.
Description check ✅ Passed The PR description covers the core changes (2 pipelines for mapping DM properties to human-readable names) and acknowledges remaining work needed, demonstrating understanding of the changeset scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch van/zzz/deobf

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

[zzz-frontend] [Tue Apr 7 16:21:46 UTC 2026] - Deployed 3713ca9 to https://genshin-optimizer-prs.github.io/pr/3187/zzz-frontend (Takes 3-5 minutes after this completes to be available)

[frontend] [Tue Apr 7 16:22:28 UTC 2026] - Deployed 3713ca9 to https://genshin-optimizer-prs.github.io/pr/3187/frontend (Takes 3-5 minutes after this completes to be available)

[sr-frontend] [Tue Apr 7 16:23:01 UTC 2026] - Deployed 3713ca9 to https://genshin-optimizer-prs.github.io/pr/3187/sr-frontend (Takes 3-5 minutes after this completes to be available)

[Mon Aug 17 00:42:15 UTC 2026] - Deleted deployment

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (11)
libs/zzz/dm/src/executors/deobf/schema.d.ts (1)

1-1: Acceptable scaffolding, but consider adding a brief doc comment for future maintainers.

yawns Empty interface with ESLint disable is fine for initial implementation... I've seen worse at 3 AM. Since the PR objectives mention this is preliminary work with more properties coming later (type definitions, additional reverse search, etc.), a quick JSDoc comment would help future-you (or future-me, if I ever sleep) understand why this is empty.

That said, this is totally optional for now - the inline disable is honest about the situation. Ship it and get back to pulling for that limited banner before pity resets.

📝 Optional: Add clarifying comment
+/**
+ * Schema for the deobf executor options.
+ * TODO: Add configuration properties as deobfuscation pipeline matures.
+ */
-export interface DeobfExecutorSchema {} // eslint-disable-line
+export interface DeobfExecutorSchema {} // eslint-disable-line `@typescript-eslint/no-empty-object-type`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/executors/deobf/schema.d.ts` at line 1, Add a short JSDoc
comment above the DeobfExecutorSchema interface explaining that it is
intentionally empty for now (placeholder for future executor configuration such
as type definitions and reverse-search options) and note why the
eslint-disable-line is present; reference the interface name DeobfExecutorSchema
so maintainers know this is scaffolding to be extended later.
libs/zzz/dm/src/generators/all-schema.json (1)

4-4: Somnia note: give this generator schema a real title.

Line 4 is empty, so generator docs/CLI help lose clarity. A concrete title helps future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/generators/all-schema.json` at line 4, The schema's "title"
property in all-schema.json is empty which causes unclear generator docs/CLI
help; update the "title" value to a concise, descriptive string (e.g., "All
Schemas Generator" or another project-appropriate name) by editing the "title"
field in all-schema.json so documentation and CLI help display a meaningful
title.
libs/zzz/dm/src/executors/deobf/schema.json (1)

5-5: Somnia note: fill in schema description before this ships.

Line 5 has an empty description, which makes Nx help output less useful. Give this a short actionable description.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/executors/deobf/schema.json` at line 5, The schema.json
currently has an empty top-level "description" which makes CLI/help output
unhelpful; update the "description" field in
libs/zzz/dm/src/executors/deobf/schema.json (the top-level "description"
property) to a short, actionable sentence describing what the executor does
(e.g., what inputs/actions it performs and when to use it) so Nx help displays
meaningful information.
libs/zzz/dm/src/dm/deobf/FileCfg/SkillPropertyTemplateTb.ts (1)

5-11: LGTM with minor typo.

squints at screen The comments explaining the constant values are helpful - good thinking ahead for when someone else (or sleep-deprived future-you) looks at this. Minor typo on line 6: "ALways" should be "Always". Not gonna lose sleep over it... actually I'm already losing sleep over everything else.

📝 Typo fix
 const reverseMapping = {
-  '3': 'todo-GJBIMEAALIF', // ALways 3
+  '3': 'todo-GJBIMEAALIF', // Always 3
   '1001': 'SkillPropertyId',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/dm/deobf/FileCfg/SkillPropertyTemplateTb.ts` around lines 5 -
11, Fix the typo in the comment inside the reverseMapping constant: change
"ALways 3" to "Always 3" (update the comment associated with the '3' key in
reverseMapping so it reads "Always 3"); the symbol to locate is reverseMapping
in SkillPropertyTemplateTb.ts.
libs/zzz/dm/src/generators/single-schema.json (1)

4-9: Consider adding meaningful title and description values.

rubs eyes Look, I get it... I've also left empty strings in schemas at 4 AM because "I'll fix it later." But empty title (line 4) and description (line 9) make the developer experience worse when someone runs nx generate and wonders what they're supposed to put. Future-you (or future-me, running on 2 hours of sleep after farming Shiyu Defense) will thank you.

📝 Suggested improvement
 {
   "$schema": "https://json-schema.org/schema",
   "$id": "GenerateSingleDeobfTemplate",
-  "title": "",
+  "title": "Generate Single Deobf Template",
   "type": "object",
   "properties": {
     "file": {
       "type": "string",
-      "description": "",
+      "description": "The DM file name (e.g., 'ItemTemplateTb') to generate a deobfuscation template for",
       "$default": {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/generators/single-schema.json` around lines 4 - 9, The schema
currently has empty metadata causing poor UX; update the top-level "title" and
add a clear top-level "description" for the schema, and replace the empty
"description" under "properties.file" with a concise explanation of what the
file value should be (e.g., expected file path or format). Locate and edit the
"title" key and the "description" fields in single-schema.json (specifically the
top-level "title" and the "properties.file.description") to include meaningful,
user-facing text that guides authors when running nx generate.
libs/zzz/dm/src/dm/deobf/FileCfg/ItemTemplateTb.ts (1)

36-36: Add a comment explaining why index [4] is used instead of [0].

brain.exe has stopped responding Okay wait, every other template uses Object.values(...)[0][0] but this one uses [0][4]. The context snippets confirm this is intentional, but future-me (or anyone else maintaining this at 2 AM while waiting for stamina to recharge) will have NO idea why without a comment. Is index 4 where the gold item template lives? Is it a specific item type? Please save us from this mystery!

📝 Add explanatory comment
-const dmObj = Object.values(itemTemplateTb)[0][4]
+// Index 4 used because [explain why - e.g., gold item template structure differs]
+const dmObj = Object.values(itemTemplateTb)[0][4]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/dm/deobf/FileCfg/ItemTemplateTb.ts` at line 36, The
expression const dmObj = Object.values(itemTemplateTb)[0][4] uses index [4]
intentionally (not [0]) to pick the specific item template stored at the 5th
position in the underlying data array; update ItemTemplateTb.ts by adding a
concise inline comment next to the dmObj assignment explaining why [4] is used
(e.g., "selects the gold/item-template entry per data layout/spec where slot 4
contains the gold item template") and reference the data structure reason (the
shape of itemTemplateTb values) so future maintainers know this is deliberate;
mention the symbol names itemTemplateTb and dmObj in the comment for clarity.
libs/zzz/dm/src/generators/generate-index-deobf.ts (2)

13-15: Unusual join separator produces unconventional formatting.

yawns mid-review The separator '\n, ' on line 14 puts the comma at the start of lines rather than at the end. While formatText might normalize this, it's an unconventional pattern. Most codebases use trailing commas. Not blocking since Prettier/formatter probably handles it, but it made my sleep-deprived brain do a double-take.

📝 Alternative with trailing commas
 export const deobfMappings = {
-  ${allFileCfgs.join('\n,  ')}
+  ${allFileCfgs.join(',\n  ')},
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/generators/generate-index-deobf.ts` around lines 13 - 15,
Change the join separator so commas trail items instead of leading the next
line: in the deobfMappings construction where allFileCfgs.join('\n,  ') is used,
replace the separator with a trailing-comma pattern (e.g. ',\n  ') so each entry
ends with a comma; update the code that builds deobfMappings (symbol:
deobfMappings, source: allFileCfgs.join(...)) to use the new separator.

17-18: Consider using tree.write() instead of writeFileSync for Nx consistency.

stares at screen with bloodshot eyes You're passing a Tree parameter but then using writeFileSync directly... That's like pulling on the standard banner when there's a limited character you want. The Nx Tree API exists so generators can work in dry-run mode and integrate properly with the Nx virtual file system. Using writeFileSync bypasses all that.

♻️ Proposed fix using Tree API
-import { writeFileSync } from 'fs'
-import * as path from 'path'
 import { formatText } from '@genshin-optimizer/common/pipeline'
 import type { Tree } from '@nx/devkit'
 import { allFileCfgs } from '../consts'
 
 export default async function generateIndex(tree: Tree) {
   const file_location = `libs/zzz/dm/src/dm/deobf/FileCfg`
-  const dest = path.join(tree.root, file_location, `index.ts`)
+  const dest = `${file_location}/index.ts`
   const contents = `// WARNING: Generated file, do not modify
 ${allFileCfgs.map((cfg) => `import ${cfg} from './${cfg}'`).join('\n')}
 
 export const deobfMappings = {
   ${allFileCfgs.join('\n,  ')}
 }
 `
   const formatted = await formatText(dest, contents)
-  writeFileSync(dest, formatted)
+  tree.write(dest, formatted)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/generators/generate-index-deobf.ts` around lines 17 - 18,
Replace the direct fs write with the Nx Tree API so dry runs and virtual FS
work: instead of calling writeFileSync(dest, formatted) after const formatted =
await formatText(dest, contents), call tree.write(dest, formatted) (use the Tree
parameter passed into this generator, e.g., the function handling
generate-index-deobf) and remove the writeFileSync import/usage; ensure the
variables dest, contents and formatted are used the same way and that formatText
remains unchanged.
libs/zzz/dm/src/dm/deobf/FileCfg/AvatarPassiveSkillTemplateTb.ts (1)

12-16: Patch-dependent obfuscation keys will require maintenance.

groans Yeah, I feel this in my soul. The TODOs about property obf names changing every patch are a real concern. For CoreStats and Cost, those JSON-stringified keys with internal identifiers like MPIANOLFJMB and BALDAPBGEPK will break when ZZZ updates.

Consider documenting a maintenance process or adding a CI check that validates these mappings against the latest datamine. Otherwise, someone's gonna be doing emergency hotfixes at 2am after every patch... not that I'd know anything about that.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/dm/deobf/FileCfg/AvatarPassiveSkillTemplateTb.ts` around
lines 12 - 16, The TODO notes flag that the JSON-stringified obfuscation keys
used for CoreStats and Cost in AvatarPassiveSkillTemplateTb.ts are
patch-dependent and will break; add a maintenance plan and an automated
validation step: create a small CI job (or unit test) that loads the latest
datamine schema and verifies that the mapped obf-key strings (the entries
currently mapping to 'CoreStats' and 'Cost') exist and warn/fail the build if
they change, and document the required update steps in the file header or
contributing docs so maintainers know to update the mappings when obf names
change.
libs/zzz/dm/src/executors/deobf/deobf.ts (1)

10-27: No error handling for file read/write operations.

The executor doesn't wrap file operations in try-catch. If readDMJSON throws (file not found) or writeFileSync fails (permissions, disk full), the executor crashes without useful context about which file failed.

For an executor that processes multiple files, this makes debugging painful when something breaks mid-run. Consider wrapping the loop body or at least logging which file is being processed before operations.

🛠️ Suggested improvement
   for (const [file, deobfObj] of Object.entries(deobfMappings)) {
+    console.log(`Processing ${file}...`)
     const outputFile = `${workspaceRoot}/libs/zzz/dm/ZenlessDataDeobf/FileCfg/${file}.json`
-    const dm = JSON.parse(readDMJSON(`FileCfg/${file}.json`)) as GenericDmFile
+    let dm: GenericDmFile
+    try {
+      dm = JSON.parse(readDMJSON(`FileCfg/${file}.json`)) as GenericDmFile
+    } catch (e) {
+      console.error(`Failed to read/parse ${file}.json:`, e)
+      throw e
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/executors/deobf/deobf.ts` around lines 10 - 27, The loop in
runExecutor iterating over deobfMappings performs file IO (readDMJSON,
formatText, writeFileSync) with no error handling; wrap the per-file processing
(the body using deobfObj, readDMJSON, objKeyValMap, formatText, writeFileSync)
in a try-catch that logs a clear error including the file variable and
deobfObj.mapping, and on error either continue to the next file or collect
failures; update the final return to reflect overall success (e.g., success:
false if any file failed) so callers know the executor had partial/total
failures.
libs/zzz/dm/src/generators/generate-single-deobf-template.ts (1)

85-88: Magic number for ItemTemplateTb could use a comment.

squinting at screen through coffee-stained glasses ...why index 4? Is there something special about the 5th item in ItemTemplateTb that makes it a better representative object? Future sleep-deprived me (or anyone else) would appreciate knowing why this specific index matters.

📝 Add explanatory comment
 function getObjToUse(file: string) {
-  if (file === 'ItemTemplateTb') return 4
-  else return 0
+  // ItemTemplateTb: Use index 4 as the representative object because
+  // earlier entries may have incomplete/atypical field coverage
+  if (file === 'ItemTemplateTb') return 4
+  return 0
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/generators/generate-single-deobf-template.ts` around lines 85
- 88, The special-case return value in getObjToUse (return 4 when file ===
'ItemTemplateTb') is a magic number; either add a clear explanatory comment
above that branch explaining why index 4 is chosen (e.g., representative object,
schema version, or stable sample at position 5), or replace the literal with a
named constant (e.g., DEFAULT_ITEM_TEMPLATE_INDEX = 4) and document the reason
in the constant's comment so readers of getObjToUse and anyone working with
ItemTemplateTb understand the rationale.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.gitmodules:
- Around line 24-28: The new submodule entry libs/zzz/dm/ZenlessDataDeobf
introduces a hard dependency for the deobf step; update the deobf CI/job (or the
deobf script) to first verify the submodule is present and initialized (check
for the path libs/zzz/dm/ZenlessDataDeobf or run git submodule status) and fail
fast with a clear error if missing, or gate the deobf job with an explicit step
that performs git submodule update --init --recursive (or your runner's
submodule checkout) before any writes to ZenlessDataDeobf so the pipeline never
attempts to write into a non-initialized submodule.

In `@libs/zzz/dm/.eslintrc.json`:
- Around line 21-25: The override's "files" glob patterns are using a leading
"./" so ESLint won't match them; update the "files" array in the overrides (the
key "files" that currently contains "./package.json", "./generators.json",
"./executors.json") to remove the "./" prefix so they read "package.json",
"generators.json", and "executors.json" so the "@nx/nx-plugin-checks" rule will
actually be applied.

In `@libs/zzz/dm/README.md`:
- Around line 9-14: The README's "[DEPRECATED] Getting data from Hakush.in"
section is contradictory: it says the service is taken down but still provides
download instructions; update the section to remove or clearly mark the download
steps as non-functional by deleting or commenting out the instructions
(`https://api.hakush.in/zzz/` and `nx get-hakushin zzz-dm`) and replace them
with a single clear sentence stating the endpoint is removed and no longer
available (or provide an alternative source if one exists) so contributors are
not misled.

In `@libs/zzz/dm/src/dm/deobf/FileCfg/AvatarBaseTemplateTb.ts`:
- Around line 18-24: The code assumes avatarBaseTemplateTb contains at least one
array element by doing Object.values(avatarBaseTemplateTb)[0][0], which can
throw if the JSON is an empty array; update the logic around dmObj (the value
derived from avatarBaseTemplateTb) to defensively handle empty data: check that
Object.values(avatarBaseTemplateTb)[0] exists and has length > 0 before
indexing, and if not either provide a safe default (e.g., an empty object) or
bail early with a clear error/log; ensure generateMapping and generateTyping
(which consume dmObj) either tolerate the default or are only called when dmObj
is valid. Use the unique symbols avatarBaseTemplateTb, dmObj, generateMapping,
generateTyping, GenericDmFile and readDMJSON to locate and change the code.

In `@libs/zzz/dm/src/dm/deobf/FileCfg/AvatarBattleTemplateTb.ts`:
- Around line 45-55: The code uses non-null assertions on the results of two
chained .find() calls when computing potentialsKey from avatarBattleTemplateTb,
which can throw if the target array disappears; update the logic in the block
that computes potentialsKey so it first validates that avatarBattleTemplateTb
has values, captures the intermediate find result into a variable (e.g.,
foundObject) and ensures it is not undefined before attempting
Object.entries/find again, and if either lookup fails, handle defensively by
either logging/throwing a clear error that includes the search target array
([119100,...,119105]) or skipping mapping assignment; leave the final assignment
mapping[potentialsKey] = 'Potentials' only when potentialsKey is successfully
resolved.

In `@libs/zzz/dm/src/dm/deobf/util.ts`:
- Around line 22-25: generateTyping is always JSON.stringify-ing values before
reverseMapping lookup while generateMapping only stringifies when the value is
an object, causing mismatched keys and dropped entries; update generateTyping
(the return that calls objKeyValMap over Object.values(dmObj)) to match
generateMapping's logic: use the raw primitive value for reverseMapping lookups
and only JSON.stringify(v) when typeof v === "object" (reference reverseMapping,
dmObj, objKeyValMap, generateMapping/generateTyping to locate the code), so keys
align and entries are not lost.

In `@libs/zzz/dm/src/executors/deobf/deobf.spec.ts`:
- Around line 23-26: The test currently calls the real executor pipeline
(executor(options, context)) causing non-deterministic IO; to harden it,
mock/stub the executor's external collaborators (filesystem/datamine functions
or modules it imports) before calling executor and assert those mocks were
invoked in addition to expect(output.success). Specifically, replace real IO
with jest mocks/spies for the modules or functions the executor uses, inject the
mocked context/options as needed, call executor(options, context), then add
assertions that the mocked collaborators were called (e.g.,
expect(mockFn).toHaveBeenCalled()) and keep expect(output.success).toBe(true).

In `@libs/zzz/dm/src/executors/deobf/deobf.ts`:
- Around line 15-20: The deobfuscation step (deobfedDm creation) can produce
properties named undefined when deobfObj.mapping[key] is missing for keys in
dmObj; update the mapping callback used by objKeyValMap (the ([key, value]) =>
... function) to either skip entries with no mapping or use a safe fallback
(e.g., deobfObj.mapping[key] ?? key) so unmapped keys don't become literal
undefined properties; locate the deobfedDm construction and change the mapping
expression to use the fallback or filter out unmapped entries before calling
objKeyValMap.

In `@libs/zzz/dm/src/generators/generate-all-deobf-template.spec.ts`:
- Around line 14-18: The test is asserting a project config ('test') unrelated
to generateAllDeobfTemplateGenerator; update the spec to assert generator output
instead: remove the readProjectConfiguration('test') assertion and instead call
generateAllDeobfTemplateGenerator(tree) then verify generated artifacts—use
tree.exists('<expected/path>') and readFile(tree, '<expected/path>') (or
appropriate devkit helpers) to assert the template/index (or other expected
files and contents) were created/updated; reference the
generateAllDeobfTemplateGenerator function and the spec file
generate-all-deobf-template.spec.ts when making the change.

In `@libs/zzz/dm/src/generators/generate-single-deobf-template.spec.ts`:
- Around line 10-19: The test passes an invalid contract to
generateSingleDeobfTemplateGenerator (using
GenerateSingleDeobfTemplateGeneratorSchema { file: 'test' }) but then asserts a
project was registered via readProjectConfiguration; instead, update the spec to
align with the generator's contract by either (A) supplying a valid DM file key
and expected DM JSON shape or (B) mocking the DM file IO and readDMJSON call so
the generator receives deterministic data, then assert the generator created the
expected template files/changes on the in-memory tree (e.g., check tree.read or
readTextFile results) rather than readProjectConfiguration; refer to
generateSingleDeobfTemplateGenerator,
GenerateSingleDeobfTemplateGeneratorSchema, and readDMJSON when adding the mock
and updating assertions.

---

Nitpick comments:
In `@libs/zzz/dm/src/dm/deobf/FileCfg/AvatarPassiveSkillTemplateTb.ts`:
- Around line 12-16: The TODO notes flag that the JSON-stringified obfuscation
keys used for CoreStats and Cost in AvatarPassiveSkillTemplateTb.ts are
patch-dependent and will break; add a maintenance plan and an automated
validation step: create a small CI job (or unit test) that loads the latest
datamine schema and verifies that the mapped obf-key strings (the entries
currently mapping to 'CoreStats' and 'Cost') exist and warn/fail the build if
they change, and document the required update steps in the file header or
contributing docs so maintainers know to update the mappings when obf names
change.

In `@libs/zzz/dm/src/dm/deobf/FileCfg/ItemTemplateTb.ts`:
- Line 36: The expression const dmObj = Object.values(itemTemplateTb)[0][4] uses
index [4] intentionally (not [0]) to pick the specific item template stored at
the 5th position in the underlying data array; update ItemTemplateTb.ts by
adding a concise inline comment next to the dmObj assignment explaining why [4]
is used (e.g., "selects the gold/item-template entry per data layout/spec where
slot 4 contains the gold item template") and reference the data structure reason
(the shape of itemTemplateTb values) so future maintainers know this is
deliberate; mention the symbol names itemTemplateTb and dmObj in the comment for
clarity.

In `@libs/zzz/dm/src/dm/deobf/FileCfg/SkillPropertyTemplateTb.ts`:
- Around line 5-11: Fix the typo in the comment inside the reverseMapping
constant: change "ALways 3" to "Always 3" (update the comment associated with
the '3' key in reverseMapping so it reads "Always 3"); the symbol to locate is
reverseMapping in SkillPropertyTemplateTb.ts.

In `@libs/zzz/dm/src/executors/deobf/deobf.ts`:
- Around line 10-27: The loop in runExecutor iterating over deobfMappings
performs file IO (readDMJSON, formatText, writeFileSync) with no error handling;
wrap the per-file processing (the body using deobfObj, readDMJSON, objKeyValMap,
formatText, writeFileSync) in a try-catch that logs a clear error including the
file variable and deobfObj.mapping, and on error either continue to the next
file or collect failures; update the final return to reflect overall success
(e.g., success: false if any file failed) so callers know the executor had
partial/total failures.

In `@libs/zzz/dm/src/executors/deobf/schema.d.ts`:
- Line 1: Add a short JSDoc comment above the DeobfExecutorSchema interface
explaining that it is intentionally empty for now (placeholder for future
executor configuration such as type definitions and reverse-search options) and
note why the eslint-disable-line is present; reference the interface name
DeobfExecutorSchema so maintainers know this is scaffolding to be extended
later.

In `@libs/zzz/dm/src/executors/deobf/schema.json`:
- Line 5: The schema.json currently has an empty top-level "description" which
makes CLI/help output unhelpful; update the "description" field in
libs/zzz/dm/src/executors/deobf/schema.json (the top-level "description"
property) to a short, actionable sentence describing what the executor does
(e.g., what inputs/actions it performs and when to use it) so Nx help displays
meaningful information.

In `@libs/zzz/dm/src/generators/all-schema.json`:
- Line 4: The schema's "title" property in all-schema.json is empty which causes
unclear generator docs/CLI help; update the "title" value to a concise,
descriptive string (e.g., "All Schemas Generator" or another project-appropriate
name) by editing the "title" field in all-schema.json so documentation and CLI
help display a meaningful title.

In `@libs/zzz/dm/src/generators/generate-index-deobf.ts`:
- Around line 13-15: Change the join separator so commas trail items instead of
leading the next line: in the deobfMappings construction where
allFileCfgs.join('\n,  ') is used, replace the separator with a trailing-comma
pattern (e.g. ',\n  ') so each entry ends with a comma; update the code that
builds deobfMappings (symbol: deobfMappings, source: allFileCfgs.join(...)) to
use the new separator.
- Around line 17-18: Replace the direct fs write with the Nx Tree API so dry
runs and virtual FS work: instead of calling writeFileSync(dest, formatted)
after const formatted = await formatText(dest, contents), call tree.write(dest,
formatted) (use the Tree parameter passed into this generator, e.g., the
function handling generate-index-deobf) and remove the writeFileSync
import/usage; ensure the variables dest, contents and formatted are used the
same way and that formatText remains unchanged.

In `@libs/zzz/dm/src/generators/generate-single-deobf-template.ts`:
- Around line 85-88: The special-case return value in getObjToUse (return 4 when
file === 'ItemTemplateTb') is a magic number; either add a clear explanatory
comment above that branch explaining why index 4 is chosen (e.g., representative
object, schema version, or stable sample at position 5), or replace the literal
with a named constant (e.g., DEFAULT_ITEM_TEMPLATE_INDEX = 4) and document the
reason in the constant's comment so readers of getObjToUse and anyone working
with ItemTemplateTb understand the rationale.

In `@libs/zzz/dm/src/generators/single-schema.json`:
- Around line 4-9: The schema currently has empty metadata causing poor UX;
update the top-level "title" and add a clear top-level "description" for the
schema, and replace the empty "description" under "properties.file" with a
concise explanation of what the file value should be (e.g., expected file path
or format). Locate and edit the "title" key and the "description" fields in
single-schema.json (specifically the top-level "title" and the
"properties.file.description") to include meaningful, user-facing text that
guides authors when running nx generate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 56012260-68f5-40aa-8665-fe05c02a69ff

📥 Commits

Reviewing files that changed from the base of the PR and between 783660f and 04c8b8a.

📒 Files selected for processing (40)
  • .gitmodules
  • libs/sr/dm-localization/src/executors/gen-locale/lib/util.ts
  • libs/zzz/dm/.eslintrc.json
  • libs/zzz/dm/README.md
  • libs/zzz/dm/ZenlessData
  • libs/zzz/dm/ZenlessDataDeobf
  • libs/zzz/dm/executors.json
  • libs/zzz/dm/generators.json
  • libs/zzz/dm/package.json
  • libs/zzz/dm/project.json
  • libs/zzz/dm/src/consts.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/AvatarBaseTemplateTb.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/AvatarBattleTemplateTb.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/AvatarPassiveSkillDesTemplateTb.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/AvatarPassiveSkillTemplateTb.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/AvatarSkillDesTemplateTb.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/AvatarSkillTemplateTb.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/EquipmentTemplateTb.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/ItemTemplateTb.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/SkillPropertyTemplateTb.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/WeaponTalentTemplateTb.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/WeaponTemplateTb.ts
  • libs/zzz/dm/src/dm/deobf/FileCfg/index.ts
  • libs/zzz/dm/src/dm/deobf/index.ts
  • libs/zzz/dm/src/dm/deobf/types.ts
  • libs/zzz/dm/src/dm/deobf/util.ts
  • libs/zzz/dm/src/executors/deobf/deobf.spec.ts
  • libs/zzz/dm/src/executors/deobf/deobf.ts
  • libs/zzz/dm/src/executors/deobf/index.ts
  • libs/zzz/dm/src/executors/deobf/schema.d.ts
  • libs/zzz/dm/src/executors/deobf/schema.json
  • libs/zzz/dm/src/generators/all-schema.json
  • libs/zzz/dm/src/generators/generate-all-deobf-template.spec.ts
  • libs/zzz/dm/src/generators/generate-all-deobf-template.ts
  • libs/zzz/dm/src/generators/generate-index-deobf.ts
  • libs/zzz/dm/src/generators/generate-single-deobf-template.spec.ts
  • libs/zzz/dm/src/generators/generate-single-deobf-template.ts
  • libs/zzz/dm/src/generators/schema.d.ts
  • libs/zzz/dm/src/generators/single-schema.json
  • libs/zzz/dm/src/util.ts

Comment thread .gitmodules
Comment on lines +24 to +28
[submodule "libs/zzz/dm/ZenlessDataDeobf"]
path = libs/zzz/dm/ZenlessDataDeobf
url = https://github.com/genshin-optimizer/ZenlessDataDeobf
shallow = true
branch = master

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

New submodule introduces a CI/runtime dependency that needs guarding.

At Line 24, this is valid, but it creates a hard dependency: deobf writes into libs/zzz/dm/ZenlessDataDeobf/.... If submodules aren’t initialized in a runner/job, the pipeline can fail with missing-path writes.

Please gate deobf jobs with submodule checkout (or add a preflight check with a clear error before writing).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitmodules around lines 24 - 28, The new submodule entry
libs/zzz/dm/ZenlessDataDeobf introduces a hard dependency for the deobf step;
update the deobf CI/job (or the deobf script) to first verify the submodule is
present and initialized (check for the path libs/zzz/dm/ZenlessDataDeobf or run
git submodule status) and fail fast with a clear error if missing, or gate the
deobf job with an explicit step that performs git submodule update --init
--recursive (or your runner's submodule checkout) before any writes to
ZenlessDataDeobf so the pipeline never attempts to write into a non-initialized
submodule.

Comment on lines +21 to +25
"files": ["./package.json", "./generators.json", "./executors.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/nx-plugin-checks": "error"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

In ESLint legacy .eslintrc overrides.files, does a pattern like "./package.json" match a same-directory file, or should it be "package.json"?

💡 Result:

In legacy .eslintrc overrides.files, ESLint matches your glob against the file path relative to the directory containing the config file (e.g. lib/util.js). Therefore, a same-directory file is matched as package.json, not ./package.json—so use "package.json". [1]

"./package.json" generally won’t match because ESLint doesn’t prefix the tested path with ./. [1][2]


Remove ./ prefix from glob patterns in overrides—ESLint won't match them that way.

The "files" patterns at line 21 use ./package.json, ./generators.json, and ./executors.json. ESLint's glob matching in overrides.files tests paths relative to the config directory without the ./ prefix, so these patterns silently fail to match. This means @nx/nx-plugin-checks never runs on these files.

Suggested fix
-      "files": ["./package.json", "./generators.json", "./executors.json"],
+      "files": ["package.json", "generators.json", "executors.json"],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"files": ["./package.json", "./generators.json", "./executors.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/nx-plugin-checks": "error"
}
"files": ["package.json", "generators.json", "executors.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/nx-plugin-checks": "error"
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/.eslintrc.json` around lines 21 - 25, The override's "files" glob
patterns are using a leading "./" so ESLint won't match them; update the "files"
array in the overrides (the key "files" that currently contains
"./package.json", "./generators.json", "./executors.json") to remove the "./"
prefix so they read "package.json", "generators.json", and "executors.json" so
the "@nx/nx-plugin-checks" rule will actually be applied.

Comment thread libs/zzz/dm/README.md
Comment on lines +9 to 14
## [DEPRECATED] Getting data from Hakush.in

NOTE: Hakush.in has been taken down, so this no longer works

Download all the English JSONs from `https://api.hakush.in/zzz/`
`nx get-hakushin zzz-dm`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Doc contradiction in deprecated Hakush section.

Line 11 says it no longer works, but Lines 13–14 still instruct users to download from that endpoint. That’s confusing for contributors.

Suggested doc cleanup
 ## [DEPRECATED] Getting data from Hakush.in
 
 NOTE: Hakush.in has been taken down, so this no longer works
-
-Download all the English JSONs from `https://api.hakush.in/zzz/`
-`nx get-hakushin zzz-dm`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/README.md` around lines 9 - 14, The README's "[DEPRECATED]
Getting data from Hakush.in" section is contradictory: it says the service is
taken down but still provides download instructions; update the section to
remove or clearly mark the download steps as non-functional by deleting or
commenting out the instructions (`https://api.hakush.in/zzz/` and `nx
get-hakushin zzz-dm`) and replace them with a single clear sentence stating the
endpoint is removed and no longer available (or provide an alternative source if
one exists) so contributors are not misled.

Comment on lines +18 to +24
const avatarBaseTemplateTb = JSON.parse(
readDMJSON('FileCfg/AvatarBaseTemplateTb.json')
) as GenericDmFile

const dmObj = Object.values(avatarBaseTemplateTb)[0][0]
const mapping = generateMapping(reverseMapping, dmObj)
const typing = generateTyping(reverseMapping, dmObj)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential runtime crash if DM JSON array is empty.

yawns Okay so... been staring at gacha rates all night and this pattern jumped out at me. The GenericDmFile type allows empty arrays, but Object.values(avatarBaseTemplateTb)[0][0] assumes there's at least one element. If ZZZ devs ever ship an empty template file (wouldn't put it past them honestly, seen weirder things in datamines), this blows up with undefined.

Since this pattern repeats across all template files, consider adding a guard or utility function.

🛡️ Suggested defensive check
 const avatarBaseTemplateTb = JSON.parse(
   readDMJSON('FileCfg/AvatarBaseTemplateTb.json')
 ) as GenericDmFile

-const dmObj = Object.values(avatarBaseTemplateTb)[0][0]
+const dmArray = Object.values(avatarBaseTemplateTb)[0]
+if (!dmArray || dmArray.length === 0) {
+  throw new Error('AvatarBaseTemplateTb.json has no entries')
+}
+const dmObj = dmArray[0]
 const mapping = generateMapping(reverseMapping, dmObj)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/dm/deobf/FileCfg/AvatarBaseTemplateTb.ts` around lines 18 -
24, The code assumes avatarBaseTemplateTb contains at least one array element by
doing Object.values(avatarBaseTemplateTb)[0][0], which can throw if the JSON is
an empty array; update the logic around dmObj (the value derived from
avatarBaseTemplateTb) to defensively handle empty data: check that
Object.values(avatarBaseTemplateTb)[0] exists and has length > 0 before
indexing, and if not either provide a safe default (e.g., an empty object) or
bail early with a clear error/log; ensure generateMapping and generateTyping
(which consume dmObj) either tolerate the default or are only called when dmObj
is valid. Use the unique symbols avatarBaseTemplateTb, dmObj, generateMapping,
generateTyping, GenericDmFile and readDMJSON to locate and change the code.

Comment on lines +45 to +55
const potentialsKey = Object.entries(
Object.values(avatarBattleTemplateTb)[0].find((o) =>
Object.values(o).some(
(v) => JSON.stringify(v) === '[119100,119101,119102,119103,119104,119105]'
)
)!
).find(
([_, v]) =>
JSON.stringify(v) === '[119100,119101,119102,119103,119104,119105]'
)![0]
mapping[potentialsKey] = 'Potentials'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Non-null assertions could cause runtime crashes if data structure changes.

rubs eyes ...at 3am, these ! assertions are giving me anxiety like waiting for a 5-star pity break. If the datamine structure changes and that magic array [119100,119101,119102,119103,119104,119105] disappears or moves, both .find() calls will return undefined, and those ! assertions will throw at runtime.

Given this is datamine processing where upstream data can change unpredictably (trust me, I've seen HoYo change things overnight), consider adding defensive checks or at least a meaningful error message.

🛡️ Proposed defensive handling
-const potentialsKey = Object.entries(
+const potentialsEntry = Object.entries(
   Object.values(avatarBattleTemplateTb)[0].find((o) =>
     Object.values(o).some(
       (v) => JSON.stringify(v) === '[119100,119101,119102,119103,119104,119105]'
     )
-  )!
-).find(
+  ) ?? {}
+).find(
   ([_, v]) =>
     JSON.stringify(v) === '[119100,119101,119102,119103,119104,119105]'
-)![0]
-mapping[potentialsKey] = 'Potentials'
+)
+if (potentialsEntry) {
+  mapping[potentialsEntry[0]] = 'Potentials'
+} else {
+  console.warn('Could not locate Potentials key in AvatarBattleTemplateTb')
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const potentialsKey = Object.entries(
Object.values(avatarBattleTemplateTb)[0].find((o) =>
Object.values(o).some(
(v) => JSON.stringify(v) === '[119100,119101,119102,119103,119104,119105]'
)
)!
).find(
([_, v]) =>
JSON.stringify(v) === '[119100,119101,119102,119103,119104,119105]'
)![0]
mapping[potentialsKey] = 'Potentials'
const potentialsEntry = Object.entries(
Object.values(avatarBattleTemplateTb)[0].find((o) =>
Object.values(o).some(
(v) => JSON.stringify(v) === '[119100,119101,119102,119103,119104,119105]'
)
) ?? {}
).find(
([_, v]) =>
JSON.stringify(v) === '[119100,119101,119102,119103,119104,119105]'
)
if (potentialsEntry) {
mapping[potentialsEntry[0]] = 'Potentials'
} else {
console.warn('Could not locate Potentials key in AvatarBattleTemplateTb')
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/dm/deobf/FileCfg/AvatarBattleTemplateTb.ts` around lines 45 -
55, The code uses non-null assertions on the results of two chained .find()
calls when computing potentialsKey from avatarBattleTemplateTb, which can throw
if the target array disappears; update the logic in the block that computes
potentialsKey so it first validates that avatarBattleTemplateTb has values,
captures the intermediate find result into a variable (e.g., foundObject) and
ensures it is not undefined before attempting Object.entries/find again, and if
either lookup fails, handle defensively by either logging/throwing a clear error
that includes the search target array ([119100,...,119105]) or skipping mapping
assignment; leave the final assignment mapping[potentialsKey] = 'Potentials'
only when potentialsKey is successfully resolved.

Comment on lines +22 to +25
return objKeyValMap(Object.values(dmObj), (v) => [
reverseMapping[JSON.stringify(v)],
typeof v,
])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Inconsistent JSON.stringify usage between generateMapping and generateTyping.

yawns ...okay so I've been staring at this code for way too long pulling an all-nighter (again), but hear me out... In generateMapping (line 12), you only JSON.stringify(v) when v is an object, using the raw value for primitives. But here in generateTyping, you're always calling JSON.stringify(v) on line 23.

For a primitive like 95, generateMapping looks up reverseMapping[95], but generateTyping looks up reverseMapping["95"] (stringified). If your reverseMapping keys are numeric like '95': 'Attack', this mismatch could cause lookup failures and silently drop entries thanks to objKeyValMap behavior.

Maybe sync these up? The gacha gods won't bless your pulls if you leave bugs in datamine code...

☕ Proposed fix to align stringify logic
 export function generateTyping(
   reverseMapping: Record<string | number, string>,
   dmObj: Record<string, string | number | object>
 ) {
   return objKeyValMap(Object.values(dmObj), (v) => [
-    reverseMapping[JSON.stringify(v)],
+    reverseMapping[typeof v === 'object' ? JSON.stringify(v) : v],
     typeof v,
   ])
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return objKeyValMap(Object.values(dmObj), (v) => [
reverseMapping[JSON.stringify(v)],
typeof v,
])
return objKeyValMap(Object.values(dmObj), (v) => [
reverseMapping[typeof v === 'object' ? JSON.stringify(v) : v],
typeof v,
])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/dm/deobf/util.ts` around lines 22 - 25, generateTyping is
always JSON.stringify-ing values before reverseMapping lookup while
generateMapping only stringifies when the value is an object, causing mismatched
keys and dropped entries; update generateTyping (the return that calls
objKeyValMap over Object.values(dmObj)) to match generateMapping's logic: use
the raw primitive value for reverseMapping lookups and only JSON.stringify(v)
when typeof v === "object" (reference reverseMapping, dmObj, objKeyValMap,
generateMapping/generateTyping to locate the code), so keys align and entries
are not lost.

Comment on lines +23 to +26
it("can run", async () => {
const output = await executor(options, context);
expect(output.success).toBe(true);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This “unit” test is not isolated and is likely flaky.

Lines 24–25 execute the real IO pipeline, so failures can come from filesystem/datamine state instead of executor logic.

Proposed test-hardening direction
+jest.mock('fs', () => ({ writeFileSync: jest.fn() }))
+jest.mock('../../util', () => ({
+  readDMJSON: jest.fn(() => JSON.stringify({ Test: [{ a: 1 }] })),
+}))
+jest.mock('../../dm/deobf', () => ({
+  deobfMappings: { Test: { mapping: { a: 'alpha' } } },
+}))
+jest.mock('@genshin-optimizer/common/pipeline', () => ({
+  formatText: jest.fn(async (_path: string, text: string) => text),
+}))

Then keep the success assertion and add assertions that mocked collaborators were called.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/executors/deobf/deobf.spec.ts` around lines 23 - 26, The test
currently calls the real executor pipeline (executor(options, context)) causing
non-deterministic IO; to harden it, mock/stub the executor's external
collaborators (filesystem/datamine functions or modules it imports) before
calling executor and assert those mocks were invoked in addition to
expect(output.success). Specifically, replace real IO with jest mocks/spies for
the modules or functions the executor uses, inject the mocked context/options as
needed, call executor(options, context), then add assertions that the mocked
collaborators were called (e.g., expect(mockFn).toHaveBeenCalled()) and keep
expect(output.success).toBe(true).

Comment on lines +15 to +20
const deobfedDm = Object.values(dm)[0].map((dmObj) =>
objKeyValMap(
Object.entries(dmObj),
([key, value]) => [deobfObj.mapping[key], value] as const
)
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Unmapped keys will produce undefined keys in output JSON.

Okay so... rubs eyes ...if dmObj contains keys that aren't in the reverseMapping, then deobfObj.mapping[key] returns undefined. This means your deobfuscated output will have literal undefined as property names, which JSON.stringify converts to... well, let's just say it gets weird.

Either filter out unmapped keys or preserve the original key as a fallback. Unless you want mystery undefined fields showing up in your datamine output at 4am when you're trying to figure out a new character's stats.

🐛 Proposed fix: fallback to original key
     const deobfedDm = Object.values(dm)[0].map((dmObj) =>
       objKeyValMap(
         Object.entries(dmObj),
-        ([key, value]) => [deobfObj.mapping[key], value] as const
+        ([key, value]) => [deobfObj.mapping[key] ?? key, value] as const
       )
     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const deobfedDm = Object.values(dm)[0].map((dmObj) =>
objKeyValMap(
Object.entries(dmObj),
([key, value]) => [deobfObj.mapping[key], value] as const
)
)
const deobfedDm = Object.values(dm)[0].map((dmObj) =>
objKeyValMap(
Object.entries(dmObj),
([key, value]) => [deobfObj.mapping[key] ?? key, value] as const
)
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/executors/deobf/deobf.ts` around lines 15 - 20, The
deobfuscation step (deobfedDm creation) can produce properties named undefined
when deobfObj.mapping[key] is missing for keys in dmObj; update the mapping
callback used by objKeyValMap (the ([key, value]) => ... function) to either
skip entries with no mapping or use a safe fallback (e.g., deobfObj.mapping[key]
?? key) so unmapped keys don't become literal undefined properties; locate the
deobfedDm construction and change the mapping expression to use the fallback or
filter out unmapped entries before calling objKeyValMap.

Comment on lines +14 to +18
it('should run successfully', async () => {
await generateAllDeobfTemplateGenerator(tree)
const config = readProjectConfiguration(tree, 'test')
expect(config).toBeDefined()
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The assertion is disconnected from generator behavior.

Line 16 checks for project config 'test', but this generator’s job is template/index generation, not project registration. This test can fail even when generator behavior is correct.

Suggested test focus (orchestration check)
-import { readProjectConfiguration } from '@nx/devkit'
+import { allFileCfgs } from '../consts'
+import generateIndex from './generate-index-deobf'
+import generateSingleDeobfTemplateGenerator from './generate-single-deobf-template'
+
+jest.mock('./generate-index-deobf', () => ({ __esModule: true, default: jest.fn() }))
+jest.mock('./generate-single-deobf-template', () => ({
+  __esModule: true,
+  default: jest.fn(async () => undefined),
+}))
@@
   it('should run successfully', async () => {
     await generateAllDeobfTemplateGenerator(tree)
-    const config = readProjectConfiguration(tree, 'test')
-    expect(config).toBeDefined()
+    expect(generateSingleDeobfTemplateGenerator).toHaveBeenCalledTimes(allFileCfgs.length)
+    expect(generateIndex).toHaveBeenCalledWith(tree)
   })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/generators/generate-all-deobf-template.spec.ts` around lines
14 - 18, The test is asserting a project config ('test') unrelated to
generateAllDeobfTemplateGenerator; update the spec to assert generator output
instead: remove the readProjectConfiguration('test') assertion and instead call
generateAllDeobfTemplateGenerator(tree) then verify generated artifacts—use
tree.exists('<expected/path>') and readFile(tree, '<expected/path>') (or
appropriate devkit helpers) to assert the template/index (or other expected
files and contents) were created/updated; reference the
generateAllDeobfTemplateGenerator function and the spec file
generate-all-deobf-template.spec.ts when making the change.

Comment on lines +10 to +19
const options: GenerateSingleDeobfTemplateGeneratorSchema = { file: 'test' }

beforeEach(() => {
tree = createTreeWithEmptyWorkspace()
})

it('should run successfully', async () => {
await generateSingleDeobfTemplateGenerator(tree, options)
const config = readProjectConfiguration(tree, 'test')
expect(config).toBeDefined()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Test input and expectation don’t match generator contract.

Line 10 uses file: 'test', and Lines 18–19 assert project registration. This generator is file-template generation and expects a real DM file key, so this test is unlikely to validate the intended behavior.

Suggested correction direction
-const options: GenerateSingleDeobfTemplateGeneratorSchema = { file: 'test' }
+const options: GenerateSingleDeobfTemplateGeneratorSchema = {
+  file: 'AvatarBaseTemplateTb',
+}
@@
-    const config = readProjectConfiguration(tree, 'test')
-    expect(config).toBeDefined()
+    // Assert generated file behavior (or mock readDMJSON/writeFileSync and assert calls)
+    // e.g. expect(writeFileSync).toHaveBeenCalled()

Prefer mocking readDMJSON + file IO in this unit test so it stays deterministic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/zzz/dm/src/generators/generate-single-deobf-template.spec.ts` around
lines 10 - 19, The test passes an invalid contract to
generateSingleDeobfTemplateGenerator (using
GenerateSingleDeobfTemplateGeneratorSchema { file: 'test' }) but then asserts a
project was registered via readProjectConfiguration; instead, update the spec to
align with the generator's contract by either (A) supplying a valid DM file key
and expected DM JSON shape or (B) mocking the DM file IO and readDMJSON call so
the generator receives deterministic data, then assert the generator created the
expected template files/changes on the in-memory tree (e.g., check tree.read or
readTextFile results) rather than readProjectConfiguration; refer to
generateSingleDeobfTemplateGenerator,
GenerateSingleDeobfTemplateGeneratorSchema, and readDMJSON when adding the mock
and updating assertions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant