diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 000000000..9557df3ef --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,15 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "printWidth": 80, + "sortImports": false, + "sortPackageJson": false, + "ignorePatterns": [ + "dist/**", + "build/**", + "docs/api/**", + "node_modules/**", + "CHANGELOG.md", + "package-lock.json", + "_reference/**" + ] +} diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 9f9ec1f52..000000000 --- a/.prettierignore +++ /dev/null @@ -1,8 +0,0 @@ -dist -docs/api -node_modules -CHANGELOG.md -package-lock.json - -# Local reference sources and analysis artifacts -/_reference/ diff --git a/AGENTS.md b/AGENTS.md index 81c074371..5b532e788 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,7 +47,7 @@ Configuration is parsed and validated by `src/config.ts` and `src/config/`. Keep ## Coding Style & Naming Conventions -Use ESM TypeScript, two-space indentation, and Prettier defaults. Keep compiler strictness intact (`strict`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`, `verbatimModuleSyntax`). Use `camelCase` for values/functions, `PascalCase` for classes/types, and `UPPER_SNAKE_CASE` for constants. Parse unknown values at every MCP, environment, and subprocess boundary. Avoid `any`, unchecked casts, non-null assertions, import-time I/O, and floating promises. Exported APIs require concise JSDoc. Model expected failures with the tagged error algebra and `Result`, not broad exception wrappers. +Use ESM TypeScript, two-space indentation, and the committed Oxfmt configuration. Keep compiler strictness intact (`strict`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`, `verbatimModuleSyntax`). Use `camelCase` for values/functions, `PascalCase` for classes/types, and `UPPER_SNAKE_CASE` for constants. Parse unknown values at every MCP, environment, and subprocess boundary. Avoid `any`, unchecked casts, non-null assertions, import-time I/O, and floating promises. Exported APIs require concise JSDoc. Model expected failures with the tagged error algebra and `Result`, not broad exception wrappers. ## Boundary Contracts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d2767f0e2..20a063dd1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,7 +49,9 @@ require the matching real-provider `verify:*` lane. `check:fast` runs cached typecheck and lint, reporting diagnostics on failure. `check` adds formatting, dead-code, and package-metadata freshness checks. -Pre-commit formats and lints staged files; pre-push runs `check:fast`. +Formatting uses Oxfmt and the committed `.oxfmtrc.json`; generated sources use +the same configuration. Pre-commit formats and lints staged files; pre-push runs +`check:fast`. `docs:check` checks committed generated metadata without rendering API HTML. `docs:generate` regenerates those files; render API HTML separately with `npm run docs:api:cached`. PR CI renders and uploads the `api-docs` artifact. diff --git a/README_ar.md b/README_ar.md index ab2fffbcd..de4e46813 100644 --- a/README_ar.md +++ b/README_ar.md @@ -59,22 +59,22 @@ ## من الملف التنفيذي إلى السلوك -| فك الترجمة | الفهم | إعادة الإنشاء | -| ----------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------- | +| فك الترجمة | الفهم | إعادة الإنشاء | +| ---------------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ | | استخرج الشيفرة شبه المصدرية، والتعليمات، والسلاسل النصية، والرموز | تتبع تدفق التحكم والمراجع المتبادلة والاستدعاءات والأنواع | حوّل ما تعلمه الوكيل إلى ميزة تناسب تقنياتك وواجهتك ومتطلباتك | يوضح REA كيف وصل إلى نتائجه. وهو لا يدّعي استعادة الشيفرة المصدرية الأصلية أو استنساخ التطبيق تلقائيًا. ## لماذا REA؟ -| | | -| -------------------- | ----------------------------------------------------------------------------------------- | -| **مصمم للوكلاء** | اسأل عما يفعله تطبيق مترجم ودع الوكيل يجمع الأدلة بدلًا من التخمين. | -| **CLI وMCP** | استخدم قدرات الهندسة العكسية نفسها من الطرفية أو وكيل البرمجة. | -| **يتولى التعقيد** | يدير REA إعداد الأدوات وفتح التطبيق واستمرار الاستقصاء والتنظيف بعد الانتهاء. | +| | | +| -------------------- | ------------------------------------------------------------------------------------- | +| **مصمم للوكلاء** | اسأل عما يفعله تطبيق مترجم ودع الوكيل يجمع الأدلة بدلًا من التخمين. | +| **CLI وMCP** | استخدم قدرات الهندسة العكسية نفسها من الطرفية أو وكيل البرمجة. | +| **يتولى التعقيد** | يدير REA إعداد الأدوات وفتح التطبيق واستمرار الاستقصاء والتنظيف بعد الانتهاء. | | **سير عمل كامل** | انتقل من النظرة الأولى إلى الشيفرة شبه المصدرية وعلاقات الاستدعاء والأنواع وأدلة التنفيذ. | -| **محلي حسب التصميم** | يجري التحليل على جهازك المدعوم ولا يرفع REA الملف التنفيذي إلى خدمة تحليل مستضافة. | -| **يحافظ على السياق** | استقصِ عدة ملفات تنفيذية من دون بدء التحليل من جديد عند كل سؤال. | +| **محلي حسب التصميم** | يجري التحليل على جهازك المدعوم ولا يرفع REA الملف التنفيذي إلى خدمة تحليل مستضافة. | +| **يحافظ على السياق** | استقصِ عدة ملفات تنفيذية من دون بدء التحليل من جديد عند كل سؤال. | ## البدء السريع @@ -200,18 +200,18 @@ rea setup ## منصة أدوات التحقيق -| عائلة الأدوات | العدد | الاستخدام | -| --------------------- | ----: | ---------------------------------------------------------------------------------------- | -| فحص الملفات التنفيذية | 39 | الدوال والشيفرة شبه المصدرية والتعليمات والسلاسل والرموز والمراجع والتعليقات | -| التحليل المركب | 14 | النظرة العامة وتحليل الدوال وفك الترجمة الدفعي ومخطط الاستدعاء وفحص Swift وObjC | -| أدوات macOS الأصلية | 7 | بيانات Mach-O والتوقيعات وplist والمعماريات واستعادة أسماء Swift | +| عائلة الأدوات | العدد | الاستخدام | +| --------------------- | ----: | -------------------------------------------------------------------------------------- | +| فحص الملفات التنفيذية | 39 | الدوال والشيفرة شبه المصدرية والتعليمات والسلاسل والرموز والمراجع والتعليقات | +| التحليل المركب | 14 | النظرة العامة وتحليل الدوال وفك الترجمة الدفعي ومخطط الاستدعاء وفحص Swift وObjC | +| أدوات macOS الأصلية | 7 | بيانات Mach-O والتوقيعات وplist والمعماريات واستعادة أسماء Swift | | الملفات والحزم | 5 | فحص الدلائل والحزم وInterface Builder وموارد Apple والاستخراج | | .NET PE/CLI | 7 | هوية التجميع والبيانات الوصفية وCIL والاستدعاءات الأصلية والمقارنة واستيراد إعادة البناء | -| مراقبة المتصفح | 9 | الصفحات والبرامج النصية وخرائط المصدر وWebMCP والصور ومقارنة الالتقاط | -| تحليل Electron | 5 | مراقبة الصفحات وبنية التطبيق وربط النتائج الثابتة بنتائج التشغيل | -| وقت تشغيل JavaScript | 2 | الاتصال بهدف Node/Electron Inspector موجود لمراقبة البرامج وسياقات التنفيذ | -| سير عمل التطبيقات | 7 | تتبع الميزات ومقارنة الإصدارات وبنية قيم الإرجاع والتحقق من إعادة الإنشاء | -| جلسة الملف التنفيذي | 21 | تبديل الأهداف وحفظ الأدلة ومقارنة العمليات والدوال وتسجيل الأسئلة غير المحسومة | +| مراقبة المتصفح | 9 | الصفحات والبرامج النصية وخرائط المصدر وWebMCP والصور ومقارنة الالتقاط | +| تحليل Electron | 5 | مراقبة الصفحات وبنية التطبيق وربط النتائج الثابتة بنتائج التشغيل | +| وقت تشغيل JavaScript | 2 | الاتصال بهدف Node/Electron Inspector موجود لمراقبة البرامج وسياقات التنفيذ | +| سير عمل التطبيقات | 7 | تتبع الميزات ومقارنة الإصدارات وبنية قيم الإرجاع والتحقق من إعادة الإنشاء | +| جلسة الملف التنفيذي | 21 | تبديل الأهداف وحفظ الأدلة ومقارنة العمليات والدوال وتسجيل الأسئلة غير المحسومة | ## خطة العمل diff --git a/docs/adr/0003-managed-code-evidence-and-provider-boundary.md b/docs/adr/0003-managed-code-evidence-and-provider-boundary.md index 250459fe5..bd3895fb4 100644 --- a/docs/adr/0003-managed-code-evidence-and-provider-boundary.md +++ b/docs/adr/0003-managed-code-evidence-and-provider-boundary.md @@ -11,7 +11,7 @@ metadata, signatures, method bodies, raw CIL hashes, limited decoded-instruction-tuple v1 hashes, exception regions, call edges, and field-access anchors are shipped through `inspect_managed_members`; `rea -inspect-managed-members` provides CLI parity. Static member comparison and build-local + inspect-managed-members` provides CLI parity. Static member comparison and build-local token remapping are shipped through `compare_managed_members` / `rea compare-managed-members`. Declaration-only managed/native boundary inventory for ModuleRef, ImplMap/PInvoke, ReadyToRun indicators, and non-IL diff --git a/docs/native-investigation.md b/docs/native-investigation.md index dcd30da42..f584af1c4 100644 --- a/docs/native-investigation.md +++ b/docs/native-investigation.md @@ -25,7 +25,7 @@ coverage and unknowns. The CLI and MCP use the same application workflows. a source-owned AppKit XIB with `ibtool`. Storyboards require an installed iOS platform; unsupported archive forms remain explicit. - `inspect_native_dispatch_metadata` / `rea inspect-native-dispatch-metadata -` prefers a validated macOS Mach-O byte reader. It decodes + ` prefers a validated macOS Mach-O byte reader. It decodes 64-bit little-endian Objective-C class/metaclass records, superclass pointers, absolute/relative method entries, ivar offsets/sizes/alignment and protocol declarations. It also decodes simple Swift conformances, static synchronous diff --git a/package-lock.json b/package-lock.json index 57c7ab47f..7c8ca08ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,8 +55,8 @@ "jscpd": "5.0.12", "knip": "6.26.0", "lint-staged": "16.4.0", + "oxfmt": "0.72.0", "oxlint": "1.73.0", - "prettier": "3.6.2", "turbo": "2.10.5", "typedoc": "0.28.20", "typescript": "5.9.3", @@ -1104,6 +1104,329 @@ "win32" ] }, + "node_modules/@oxfmt/binding-android-arm-eabi": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.72.0.tgz", + "integrity": "sha512-u4U1uaDOVBuorGNC/07NLm48nECHOiJJka002YGML16Md2DRvf38RGBt01lH60EOGvNlFVmbLoksQedNBoZHKw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-android-arm64": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.72.0.tgz", + "integrity": "sha512-GCnM+Ae2mWOKC+92y3FOb6eZJeVnBAVz82e3lITUUg+kxukYKzBNAvJ/hhHIfa2A0md55SYYhdWPddSSy3KPVA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-darwin-arm64": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.72.0.tgz", + "integrity": "sha512-LjQ8tlevdtwraFlWC6AnQ1Forzde0clRmnsP3QmK+ydSElDt/J+5Jc+m+//Ly1G97aP7WCNUw110QMIUw2K7xA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-darwin-x64": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.72.0.tgz", + "integrity": "sha512-+ZbcxhB1C34RQR5IldSyY6Z/yalJIxHXhP36PGE4sEq0f/lgL5jCLmOKoDeVm8dqd9gFGDsd1bNWHSGCAwdSkw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-freebsd-x64": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.72.0.tgz", + "integrity": "sha512-yGPhA8sePEBFV9iGcprhyOk3Bz3fZshswMH3DpYIPZFLT1DG5LjiZ0j6AJQpSueaL6bUK0av1K+5+fe+ldy5EQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm-gnueabihf": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.72.0.tgz", + "integrity": "sha512-/g5axuqFMcAtoQrFLR5EJEMDjR5Ea4GuzpdDmMpPWAgrF1DmL4kD1NPNVlpXEOxpFJnD6lPvLKYj1lzdlrfifA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm-musleabihf": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.72.0.tgz", + "integrity": "sha512-InVitdHbS09PM+w+WinIiyX37rC9R3L7lUP9mKy9uKTEDSUFaLjWsLoreRIxKivnv7Ndqc6Trvi+K+b6ZzhiJg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm64-gnu": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.72.0.tgz", + "integrity": "sha512-WKjlyRAxpPjXoqeuwFBim5LscgvY+RNEiZ971C9JlRQZONhJTsXDki/oHFYQE+fzzRpzXh3owZvOdT3kNL2g+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm64-musl": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.72.0.tgz", + "integrity": "sha512-poafnbsPpJhR+5Mbwpf8CscugTyiRAzjTOxRVn8ra3X2jnURs2xL5Du2xl5g2iW85lICMglpmLf8jDjZEbNgzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-ppc64-gnu": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.72.0.tgz", + "integrity": "sha512-zNUi0gKRsO7S3IfPzsTbnm88OedPuX32hmZFWZRWHvWJlAoZRwVbeO6Nb3hng4vDgL7qUKp4TGqzPz1mhsycYg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-riscv64-gnu": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.72.0.tgz", + "integrity": "sha512-qTY7z/iyF/rWI2HWwWMevXqZW5PZUOofUy2afK7v6rseXnpld3yjgyIk6pCux3DqWQ0lB+f5atvLJNBwzLvFsw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-riscv64-musl": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.72.0.tgz", + "integrity": "sha512-w18qfo8vEMD6EKEHtzTvjc6jH8yCyXIT1Bn7PFWJd0PpZyzH/VQDx4xvjCh0LGz6GRzqiezbisTdG9AoXtpkZQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-s390x-gnu": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.72.0.tgz", + "integrity": "sha512-grW8HwEir+vK7fCcq1ZgL3NHQCfZ1PidiERE9Hbf+24xGys+DoDaoqTht4ccfn0PcBwbViLkwkda/KNpJu2KRQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-x64-gnu": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.72.0.tgz", + "integrity": "sha512-wQJRQfWBRIn88Pkr449EUViwRayrYUScg7ty+Ihiem21mQ9BUA6S9e/4RAgyNk9lMag0KBouyKskjq61rOC0WQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-x64-musl": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.72.0.tgz", + "integrity": "sha512-T1B0uQzafxuGPjjaPrZJ497nxoi1BiI65JfsYc56nNpmev3msfb/iaTmH2kQuNFqa8PKxpMlf3GhWrWBvBPORQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-openharmony-arm64": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.72.0.tgz", + "integrity": "sha512-OKxxgTurl7+hypVplO8QRAbbQlwEs0j1Y6FZv+ZN2qNbdxQk2muMlzuJNBGTCWwxQ0rkkOD1uK/DW1fvzoUSJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-arm64-msvc": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.72.0.tgz", + "integrity": "sha512-L78TKzURqvjuZOCo88ixMbPXf/RP7u31Y0cmjJ/KEmrHmfYhmhEvsDaTYPiyTRne64jUgCbADfY0YeoQk4/Xyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-ia32-msvc": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.72.0.tgz", + "integrity": "sha512-gQtFW+Ii8BWgtJTAD17TY/WsdNS5gUfAPov8iI1jeq4r9lRRYImmxPL08UU00/RLaCfBJwh2UgINnDY0iOiZ+A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-x64-msvc": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.72.0.tgz", + "integrity": "sha512-uhooIN+DCzWX5uH7mQkSVCsODqsGluGJ4n3ch4+mITtWKpESMDjUJUSDBAVFuYQDwqY2McPDKdKJbM0oZVW/Qw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@oxlint/binding-android-arm-eabi": { "version": "1.73.0", "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.73.0.tgz", @@ -4219,6 +4542,58 @@ "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" } }, + "node_modules/oxfmt": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.72.0.tgz", + "integrity": "sha512-16OQeL0uhPZbfmT9S9WWM0Nv9+BmVi8Ra1uTYaSKj/qSCs6FB30KOhz9JoNYw6thT+K5bVxsk/tSEOu497i34g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinypool": "2.2.0" + }, + "bin": { + "oxfmt": "bin/oxfmt" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + }, + "optionalDependencies": { + "@oxfmt/binding-android-arm-eabi": "0.72.0", + "@oxfmt/binding-android-arm64": "0.72.0", + "@oxfmt/binding-darwin-arm64": "0.72.0", + "@oxfmt/binding-darwin-x64": "0.72.0", + "@oxfmt/binding-freebsd-x64": "0.72.0", + "@oxfmt/binding-linux-arm-gnueabihf": "0.72.0", + "@oxfmt/binding-linux-arm-musleabihf": "0.72.0", + "@oxfmt/binding-linux-arm64-gnu": "0.72.0", + "@oxfmt/binding-linux-arm64-musl": "0.72.0", + "@oxfmt/binding-linux-ppc64-gnu": "0.72.0", + "@oxfmt/binding-linux-riscv64-gnu": "0.72.0", + "@oxfmt/binding-linux-riscv64-musl": "0.72.0", + "@oxfmt/binding-linux-s390x-gnu": "0.72.0", + "@oxfmt/binding-linux-x64-gnu": "0.72.0", + "@oxfmt/binding-linux-x64-musl": "0.72.0", + "@oxfmt/binding-openharmony-arm64": "0.72.0", + "@oxfmt/binding-win32-arm64-msvc": "0.72.0", + "@oxfmt/binding-win32-ia32-msvc": "0.72.0", + "@oxfmt/binding-win32-x64-msvc": "0.72.0" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, "node_modules/oxlint": { "version": "1.73.0", "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.73.0.tgz", @@ -4444,22 +4819,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -4982,6 +5341,16 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinypool": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-2.2.0.tgz", + "integrity": "sha512-jBrmx4lYmaC9k/mgPbylxs7kBUxHtD8256up+HjLaDFfXScKJQyil+SWXSvhAtT7XHo+yTVlpB81PGmHP8oLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + } + }, "node_modules/tinyrainbow": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", diff --git a/package.json b/package.json index 111a7285c..361aa5e82 100644 --- a/package.json +++ b/package.json @@ -77,8 +77,8 @@ "precheck:ci": "npm run deps:check", "check:ci": "turbo run typecheck lint format:check knip metadata:check jscpd scan:todos", "build:cached": "turbo run build", - "format": "prettier --write .", - "format:check": "prettier --check .", + "format": "oxfmt .", + "format:check": "oxfmt --check .", "lint": "oxlint", "lint:fix": "oxlint --fix", "knip": "knip", @@ -160,10 +160,10 @@ }, "lint-staged": { "*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}": [ - "prettier --write", + "oxfmt", "oxlint --fix" ], - "*.{json,md,yml,yaml}": "prettier --write" + "*.{json,md,yml,yaml}": "oxfmt" }, "dependencies": { "@babel/parser": "8.0.4", @@ -208,8 +208,8 @@ "jscpd": "5.0.12", "knip": "6.26.0", "lint-staged": "16.4.0", + "oxfmt": "0.72.0", "oxlint": "1.73.0", - "prettier": "3.6.2", "turbo": "2.10.5", "typedoc": "0.28.20", "typescript": "5.9.3", diff --git a/scripts/generate-error-schema.mjs b/scripts/generate-error-schema.mjs index 74aa3c02c..f9430f951 100644 --- a/scripts/generate-error-schema.mjs +++ b/scripts/generate-error-schema.mjs @@ -2,7 +2,7 @@ import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { analysisErrorJsonSchema } from "../dist/contracts/errorSchemas.js"; -import { format } from "prettier"; +import { formatGeneratedFile } from "./lib/format-generated-file.mjs"; import { ensureGeneratedFile } from "./lib/generated-file.mjs"; const arguments_ = new Set(process.argv.slice(2)); @@ -13,9 +13,10 @@ for (const argument of arguments_) const root = join(dirname(fileURLToPath(import.meta.url)), ".."); await ensureGeneratedFile({ path: join(root, "docs/error-contract.schema.json"), - source: await format(JSON.stringify(analysisErrorJsonSchema), { - parser: "json", - }), + source: await formatGeneratedFile( + "docs/error-contract.schema.json", + JSON.stringify(analysisErrorJsonSchema), + ), check: arguments_.has("--check"), generateCommand: "npm run docs:generate", }); diff --git a/scripts/generate-mcp-tool-catalog.mjs b/scripts/generate-mcp-tool-catalog.mjs index f9bdeaa34..d09e061c9 100644 --- a/scripts/generate-mcp-tool-catalog.mjs +++ b/scripts/generate-mcp-tool-catalog.mjs @@ -1,6 +1,6 @@ import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; -import { format } from "prettier"; +import { formatGeneratedFile } from "./lib/format-generated-file.mjs"; import { Client, InMemoryTransport } from "@modelcontextprotocol/client"; import { Ajv2020 } from "ajv/dist/2020.js"; import { McpServer } from "@modelcontextprotocol/server"; @@ -110,7 +110,8 @@ const canonicalizeJson = (value) => { const payloadJson = JSON.stringify( canonicalizeJson({ catalog, auxiliaryProviders }), ); -const source = await format( +const source = await formatGeneratedFile( + "src/generatedMcpToolCatalog.ts", `import type { ToolAnnotations } from "@modelcontextprotocol/server"; import type { CapabilityDescriptor, @@ -194,7 +195,6 @@ function isRecord( return typeof value === "object" && value !== null && !Array.isArray(value); } `, - { parser: "typescript" }, ); await ensureGeneratedFile({ diff --git a/scripts/lib/format-generated-file.mjs b/scripts/lib/format-generated-file.mjs new file mode 100644 index 000000000..c18af645c --- /dev/null +++ b/scripts/lib/format-generated-file.mjs @@ -0,0 +1,12 @@ +import { format } from "oxfmt"; +import config from "../../.oxfmtrc.json" with { type: "json" }; + +/** Format generated source with the same options as the repository formatter. */ +export const formatGeneratedFile = async (fileName, source) => { + const result = await format(fileName, source, config); + if (result.errors.length > 0) + throw new Error( + `Cannot format ${fileName}: ${result.errors.map(({ message }) => message).join("; ")}`, + ); + return result.code; +}; diff --git a/scripts/lib/product-catalog.mjs b/scripts/lib/product-catalog.mjs index 9ed2111e6..8c4aaae4f 100644 --- a/scripts/lib/product-catalog.mjs +++ b/scripts/lib/product-catalog.mjs @@ -1,4 +1,4 @@ -import { format } from "prettier"; +import { formatGeneratedFile } from "./format-generated-file.mjs"; import { assertSameNames, digest, loadSources } from "./catalog-core.mjs"; import { @@ -67,4 +67,7 @@ export const createProductCatalog = async (root) => { /** Stable checked-in representation of the product catalog. */ export const serializeProductCatalog = (catalog) => - format(JSON.stringify(catalog, null, 2), { parser: "json" }); + formatGeneratedFile( + "docs/product-catalog.json", + JSON.stringify(catalog, null, 2), + ); diff --git a/scripts/verify-real-ghidra-switch-controls.mjs b/scripts/verify-real-ghidra-switch-controls.mjs index fdff1df81..55837db26 100644 --- a/scripts/verify-real-ghidra-switch-controls.mjs +++ b/scripts/verify-real-ghidra-switch-controls.mjs @@ -47,12 +47,10 @@ export async function verifySwitchEvidenceControls({ workspace, runId, }) { - const { spawnOwnedProviderProcess, ProviderProcessSupervisor } = await import( - "../dist/process/ProviderProcess.js" - ); - const { cleanupOwnedProcessGroup } = await import( - "../dist/process/ProcessOwnership.js" - ); + const { spawnOwnedProviderProcess, ProviderProcessSupervisor } = + await import("../dist/process/ProviderProcess.js"); + const { cleanupOwnedProcessGroup } = + await import("../dist/process/ProcessOwnership.js"); const bridge = await realpath( join(dirname(entrypoint), "../bridge/ghidra/ReaGhidraBridge.java"), ); diff --git a/scripts/verify-real-ghidra-switch.mjs b/scripts/verify-real-ghidra-switch.mjs index de4852d46..1c9c03b19 100644 --- a/scripts/verify-real-ghidra-switch.mjs +++ b/scripts/verify-real-ghidra-switch.mjs @@ -67,9 +67,8 @@ for (const [role, command] of Object.entries(commands)) { } let installation; if (!compilerOnly) { - const { inspectGhidraInstallation } = await import( - "../dist/ghidra/GhidraInstallation.js" - ); + const { inspectGhidraInstallation } = + await import("../dist/ghidra/GhidraInstallation.js"); assert.ok( process.env.GHIDRA_INSTALL_DIR, "Ghidra switch lane prerequisite missing: GHIDRA_INSTALL_DIR (bring your own)", diff --git a/src/application/SetupPlan.ts b/src/application/SetupPlan.ts index 4a263c326..85b5cb97f 100644 --- a/src/application/SetupPlan.ts +++ b/src/application/SetupPlan.ts @@ -245,23 +245,21 @@ const setupPlan = (input: { : []), ...input.clients .filter(({ client }) => client.format !== "unsupported") - .map( - ({ client, operation, backupPath }): SetupAction => ({ - id: `configure_client:${client.name}`, - kind: "configure_client", - label: client.displayName ?? client.name, - target: client.configPath, - detail: clientConfigurationDetail( - client.displayName ?? client.name, - input.command, - input.providerEnvironment, - input.installHopper, - ), - external: false, - operation, - ...(backupPath === undefined ? {} : { backupPath }), - }), - ), + .map(({ client, operation, backupPath }): SetupAction => ({ + id: `configure_client:${client.name}`, + kind: "configure_client", + label: client.displayName ?? client.name, + target: client.configPath, + detail: clientConfigurationDetail( + client.displayName ?? client.name, + input.command, + input.providerEnvironment, + input.installHopper, + ), + external: false, + operation, + ...(backupPath === undefined ? {} : { backupPath }), + })), ...(input.installSkill ? [ { diff --git a/src/application/runtime.ts b/src/application/runtime.ts index dfe856133..51007976b 100644 --- a/src/application/runtime.ts +++ b/src/application/runtime.ts @@ -45,21 +45,18 @@ export const createBinarySession = ( new AnalysisProviderRegistry([hopper, ghidra], config.analysisProvider), [ lazyProvider("rea-artifact-graph", async () => { - const { ArtifactProvider } = await import( - "../artifacts/ArtifactProvider.js" - ); + const { ArtifactProvider } = + await import("../artifacts/ArtifactProvider.js"); return new ArtifactProvider(); }), lazyProvider("native-macos", async () => { - const { NativeMacOSProvider } = await import( - "../native/NativeMacOSProvider.js" - ); + const { NativeMacOSProvider } = + await import("../native/NativeMacOSProvider.js"); return new NativeMacOSProvider(); }), lazyProvider("rea-dotnet-static", async () => { - const { ManagedStaticProvider } = await import( - "../dotnet/ManagedStaticProvider.js" - ); + const { ManagedStaticProvider } = + await import("../dotnet/ManagedStaticProvider.js"); return new ManagedStaticProvider(); }), ], diff --git a/src/browser/PlaywrightBrowserScenarioProvider.ts b/src/browser/PlaywrightBrowserScenarioProvider.ts index 54142a9da..71b602698 100644 --- a/src/browser/PlaywrightBrowserScenarioProvider.ts +++ b/src/browser/PlaywrightBrowserScenarioProvider.ts @@ -311,9 +311,7 @@ const runScenario = async ( }; /** Controlled Playwright/CDP scenario driver with exact process ownership. */ -export class PlaywrightBrowserScenarioProvider - implements BrowserScenarioCapturePort -{ +export class PlaywrightBrowserScenarioProvider implements BrowserScenarioCapturePort { constructor( private readonly factory: BrowserScenarioSessionFactory = lazyPlaywrightFactory, ) {} diff --git a/src/browser/PlaywrightElectronActiveActions.ts b/src/browser/PlaywrightElectronActiveActions.ts index 2aa9ae0c0..86ab89dfc 100644 --- a/src/browser/PlaywrightElectronActiveActions.ts +++ b/src/browser/PlaywrightElectronActiveActions.ts @@ -305,13 +305,11 @@ export const readApplicationState = async ( }> => { const pages = application.windows(); const pageSnapshotsPromise: Promise = Promise.all( - pages.map( - async (page: Page, index): Promise => ({ - index, - url: page.url(), - title: await page.title(), - }), - ), + pages.map(async (page: Page, index): Promise => ({ + index, + url: page.url(), + title: await page.title(), + })), ); const windowMetadataPromise: Promise = application .evaluate(({ BrowserWindow }) => diff --git a/src/browser/PlaywrightElectronActiveProvider.ts b/src/browser/PlaywrightElectronActiveProvider.ts index fd112302b..75f436ced 100644 --- a/src/browser/PlaywrightElectronActiveProvider.ts +++ b/src/browser/PlaywrightElectronActiveProvider.ts @@ -146,9 +146,7 @@ const createCoverage = (hookSnapshot: ElectronHookSnapshot) => { }; /** Launch an owned Electron application through the official Playwright API. */ -export class PlaywrightElectronActiveProvider - implements ElectronActiveObservationPort -{ +export class PlaywrightElectronActiveProvider implements ElectronActiveObservationPort { identity(): ProviderIdentity { return PLAYWRIGHT_ELECTRON_ACTIVE_PROVIDER_IDENTITY; } diff --git a/src/browser/PlaywrightScenarioSession.ts b/src/browser/PlaywrightScenarioSession.ts index c71022f0c..a87c09a47 100644 --- a/src/browser/PlaywrightScenarioSession.ts +++ b/src/browser/PlaywrightScenarioSession.ts @@ -274,9 +274,7 @@ export class PlaywrightScenarioSession implements BrowserScenarioSessionPort { } /** Production Playwright/CDP session factory. */ -export class PlaywrightScenarioSessionFactory - implements BrowserScenarioSessionFactory -{ +export class PlaywrightScenarioSessionFactory implements BrowserScenarioSessionFactory { constructor( private readonly environment: Readonly< Record diff --git a/src/domain/appleAssetCatalog.ts b/src/domain/appleAssetCatalog.ts index 3e6d553d5..0312b90ad 100644 --- a/src/domain/appleAssetCatalog.ts +++ b/src/domain/appleAssetCatalog.ts @@ -102,24 +102,22 @@ export const projectAppleAssetCatalogPage = (input: { }[]; }) => { const all = input.catalogs.flatMap((catalog) => - catalog.records.map( - (metadata, index): AppleAssetCatalogRecord => ({ - catalog_path: catalog.path, - catalog_sha256: catalog.sha256, - index, - kind: - typeof metadata.RenditionName === "string" || - typeof metadata.Name === "string" - ? "rendition" - : "catalog", - asset_name: typeof metadata.Name === "string" ? metadata.Name : null, - rendition_name: - typeof metadata.RenditionName === "string" - ? metadata.RenditionName - : null, - metadata, - }), - ), + catalog.records.map((metadata, index): AppleAssetCatalogRecord => ({ + catalog_path: catalog.path, + catalog_sha256: catalog.sha256, + index, + kind: + typeof metadata.RenditionName === "string" || + typeof metadata.Name === "string" + ? "rendition" + : "catalog", + asset_name: typeof metadata.Name === "string" ? metadata.Name : null, + rendition_name: + typeof metadata.RenditionName === "string" + ? metadata.RenditionName + : null, + metadata, + })), ); const records = all.slice(input.offset, input.offset + input.limit); const next = input.offset + records.length; diff --git a/src/domain/errors.ts b/src/domain/errors.ts index df982a98b..561c77468 100644 --- a/src/domain/errors.ts +++ b/src/domain/errors.ts @@ -478,8 +478,9 @@ export class BinaryTargetError extends AnalysisError { } } -export interface AnalysisErrorProjection - extends Readonly> { +export interface AnalysisErrorProjection extends Readonly< + Record +> { readonly code: | "invalid_request" | "unreadable_output" diff --git a/src/domain/javascriptApplicationVersionItems.ts b/src/domain/javascriptApplicationVersionItems.ts index 078be08a3..c9692c174 100644 --- a/src/domain/javascriptApplicationVersionItems.ts +++ b/src/domain/javascriptApplicationVersionItems.ts @@ -47,8 +47,8 @@ type OneSidedNode = }; type ComparisonItemSemantic< - Item extends - ApplicationVersionComparisonItem = ApplicationVersionComparisonItem, + Item extends ApplicationVersionComparisonItem = + ApplicationVersionComparisonItem, > = Item extends ApplicationVersionComparisonItem ? Omit : never; diff --git a/src/ghidra/GhidraInstallation.ts b/src/ghidra/GhidraInstallation.ts index 697a632b6..23e447952 100644 --- a/src/ghidra/GhidraInstallation.ts +++ b/src/ghidra/GhidraInstallation.ts @@ -55,8 +55,7 @@ interface GhidraInstallationObservation { } /** Bounded observation of a supported BYO Ghidra installation and JDK. */ -export interface AvailableGhidraInstallation - extends GhidraInstallationObservation { +export interface AvailableGhidraInstallation extends GhidraInstallationObservation { readonly status: "available"; readonly installDir: string; readonly analyzeHeadlessPath: string; @@ -67,8 +66,7 @@ export interface AvailableGhidraInstallation } /** Bounded observation explaining why a BYO Ghidra installation is unusable. */ -export interface UnavailableGhidraInstallation - extends GhidraInstallationObservation { +export interface UnavailableGhidraInstallation extends GhidraInstallationObservation { readonly status: "unavailable"; readonly installDir: string | null; readonly analyzeHeadlessPath: string | null;