mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-07 02:58:02 +00:00
Merge branch 'main' into fix/2561-enum-constant-receiver-dispatch
This commit is contained in:
commit
c111dfd4ae
21 changed files with 139 additions and 119 deletions
2
.github/claude-canary-runtime/package-lock.json
generated
vendored
2
.github/claude-canary-runtime/package-lock.json
generated
vendored
|
|
@ -11,7 +11,7 @@
|
|||
"@anthropic-ai/claude-code": "2.1.214"
|
||||
},
|
||||
"engines": {
|
||||
"node": "22.16.0"
|
||||
"node": "22.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-code": {
|
||||
|
|
|
|||
2
.github/claude-canary-runtime/package.json
vendored
2
.github/claude-canary-runtime/package.json
vendored
|
|
@ -3,7 +3,7 @@
|
|||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "22.16.0"
|
||||
"node": "22.18.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-code": "2.1.214"
|
||||
|
|
|
|||
2
.github/gitnexus-review-runtime/package-lock.json
generated
vendored
2
.github/gitnexus-review-runtime/package-lock.json
generated
vendored
|
|
@ -11,7 +11,7 @@
|
|||
"gitnexus": "1.6.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": "22.16.0"
|
||||
"node": "22.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
|
|
|
|||
2
.github/gitnexus-review-runtime/package.json
vendored
2
.github/gitnexus-review-runtime/package.json
vendored
|
|
@ -3,7 +3,7 @@
|
|||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"node": "22.16.0"
|
||||
"node": "22.18.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"gitnexus": "1.6.9"
|
||||
|
|
|
|||
25
.github/workflows/ci-tests.yml
vendored
25
.github/workflows/ci-tests.yml
vendored
|
|
@ -378,15 +378,16 @@ jobs:
|
|||
"$PREFIX/bin/gitnexus" --version
|
||||
fi
|
||||
|
||||
# Node engines-floor gate (#2372). The embedding resolvers statically named
|
||||
# `module.registerHooks`, which only exists on Node >= 22.15 / >= 23.5, so on
|
||||
# the supported floor (engines: >=22.0.0) those ESM modules failed to LINK —
|
||||
# a class vitest/tsx transforms structurally mask, and the default
|
||||
# `node-version: 22` (resolves to latest) never hits. Build the dist on 22.x,
|
||||
# then import-link every module R1 names as a load surface on a pinned 22.14
|
||||
# so a regression fails here instead of shipping to users on that Node range.
|
||||
# Node engines-floor gate (#2372). A module that statically names an API
|
||||
# newer than the supported floor (e.g. `module.registerHooks`, added in
|
||||
# 22.15) fails to LINK on the floor — a class vitest/tsx transforms
|
||||
# structurally mask, and the default `node-version: 22` (resolves to latest)
|
||||
# never hits. Build the dist on 22.x, then import-link every module R1 names
|
||||
# as a load surface on the pinned engines floor (22.18.0, per package.json
|
||||
# `engines: ^22.18.0 || >=24.11.0`) so a regression fails here instead of
|
||||
# shipping to users on the minimum supported Node.
|
||||
node-floor-compat:
|
||||
name: node floor compat (22.14)
|
||||
name: node floor compat (22.18)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
|
|
@ -415,14 +416,14 @@ jobs:
|
|||
# (so no package-manager cache is needed).
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '22.14.0'
|
||||
node-version: '22.18.0'
|
||||
package-manager-cache: false
|
||||
- name: Import-link the built dist on Node 22.14
|
||||
- name: Import-link the built dist on Node 22.18
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node --version
|
||||
node --version | grep -q '^v22\.14\.' || { echo "expected Node 22.14.x" >&2; exit 1; }
|
||||
node --version | grep -q '^v22\.18\.' || { echo "expected Node 22.18.x" >&2; exit 1; }
|
||||
for m in \
|
||||
core/embeddings/runtime-install \
|
||||
core/embeddings/onnxruntime-node-resolver \
|
||||
|
|
@ -556,7 +557,7 @@ jobs:
|
|||
persist-credentials: false
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '22.16.0'
|
||||
node-version: '22.18.0'
|
||||
cache: npm
|
||||
cache-dependency-path: |
|
||||
gitnexus/package-lock.json
|
||||
|
|
|
|||
10
.github/workflows/gitnexus-review-agent.yml
vendored
10
.github/workflows/gitnexus-review-agent.yml
vendored
|
|
@ -325,7 +325,7 @@ jobs:
|
|||
if: steps.context.outputs.ready == 'true'
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '22.16.0'
|
||||
node-version: '22.18.0'
|
||||
|
||||
- name: Install and preflight Claude subprocess isolation
|
||||
id: isolation
|
||||
|
|
@ -377,7 +377,7 @@ jobs:
|
|||
.github/claude-canary-runtime/package-lock.json \
|
||||
"${runtime_dir}/package-lock.json"
|
||||
printf '%s\n' 'registry=https://registry.npmjs.org/' 'audit=false' 'fund=false' > "${npmrc}"
|
||||
test "$(node --version)" = 'v22.16.0'
|
||||
test "$(node --version)" = 'v22.18.0'
|
||||
test "$(uname -m)" = 'x86_64'
|
||||
|
||||
# The trusted lock and these independent receipts pin both the thin
|
||||
|
|
@ -398,7 +398,7 @@ jobs:
|
|||
if (
|
||||
lock.lockfileVersion !== 3 ||
|
||||
lock.packages?.['']?.dependencies?.['@anthropic-ai/claude-code'] !== '2.1.214' ||
|
||||
lock.packages?.['']?.engines?.node !== '22.16.0'
|
||||
lock.packages?.['']?.engines?.node !== '22.18.0'
|
||||
) {
|
||||
throw new Error('Claude runtime lock root is not exact');
|
||||
}
|
||||
|
|
@ -506,7 +506,7 @@ jobs:
|
|||
install -m 0600 .github/gitnexus-review-runtime/package.json "${runtime_dir}/package.json"
|
||||
install -m 0600 .github/gitnexus-review-runtime/package-lock.json "${runtime_dir}/package-lock.json"
|
||||
printf '%s\n' 'registry=https://registry.npmjs.org/' 'audit=false' 'fund=false' > "${npmrc}"
|
||||
test "$(node --version)" = 'v22.16.0'
|
||||
test "$(node --version)" = 'v22.18.0'
|
||||
npm ci \
|
||||
--prefix "${runtime_dir}" \
|
||||
--userconfig "${npmrc}" \
|
||||
|
|
@ -1241,7 +1241,7 @@ jobs:
|
|||
CLAUDE_CONFIG_DIR: ${{ runner.temp }}/gitnexus-review-claude-config
|
||||
CLAUDE_WORKING_DIR: ${{ runner.temp }}/gitnexus-review-control
|
||||
NPM_CONFIG_IGNORE_SCRIPTS: 'true'
|
||||
NODE_VERSION: '22.16.0'
|
||||
NODE_VERSION: '22.18.0'
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
path_to_claude_code_executable: ${{ runner.temp }}/gitnexus-review-claude-runtime/node_modules/@anthropic-ai/claude-code/bin/claude.exe
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ jobs:
|
|||
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '22.16.0'
|
||||
node-version: '22.18.0'
|
||||
cache: npm
|
||||
cache-dependency-path: |
|
||||
gitnexus/package-lock.json
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -68,9 +68,8 @@ gitnexus-web/test-results/
|
|||
eval/.coverage
|
||||
eval/.hypothesis/
|
||||
|
||||
# Local docs (docs/plans/ stays tracked — gitnexus-plan output travels with the work)
|
||||
# Local docs — planning output (gitnexus-plan / gitnexus-work) stays local, not tracked
|
||||
docs/*
|
||||
!docs/plans/
|
||||
|
||||
gitnexus/test/fixtures/mini-repo/*.md
|
||||
gitnexus/test/fixtures/mini-repo/.claude
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ This project uses the [PolyForm Noncommercial License 1.0.0](https://polyformpro
|
|||
|
||||
## Development setup
|
||||
|
||||
**Prerequisites:** Node.js — `gitnexus/` requires `>=22.0.0` and `gitnexus-web/` requires `^20.19.0 || >=22.12.0` (enforced via the `engines` field in each package). Use `nvm install` to match the local version.
|
||||
**Prerequisites:** Node.js — `gitnexus/` requires `^22.18.0 || >=24.11.0` and `gitnexus-web/` requires `^20.19.0 || >=22.12.0` (enforced via the `engines` field in each package). Use `nvm install` to match the local version.
|
||||
|
||||
1. Clone the repository.
|
||||
2. **Shared package:** `cd gitnexus-shared && npm install && npm run build`
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ def test_eval_ci_uses_locked_uv_and_blocking_native_containment_jobs():
|
|||
}
|
||||
assert containment["timeout-minutes"] == 20
|
||||
assert containment_node_setup["with"] == {
|
||||
"node-version": "22.16.0",
|
||||
"node-version": "22.18.0",
|
||||
"cache": "npm",
|
||||
"cache-dependency-path": "gitnexus/package-lock.json\ngitnexus-shared/package-lock.json\n",
|
||||
}
|
||||
|
|
|
|||
60
gitnexus/package-lock.json
generated
60
gitnexus/package-lock.json
generated
|
|
@ -60,7 +60,6 @@
|
|||
"@types/express": "^5.0.6",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^26.0.0",
|
||||
"@types/uuid": "^11.0.0",
|
||||
"@vitest/coverage-v8": "^4.0.18",
|
||||
"gitnexus-shared": "file:../gitnexus-shared",
|
||||
"tsx": "^4.0.0",
|
||||
|
|
@ -68,7 +67,7 @@
|
|||
"vitest": "^4.0.18"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0"
|
||||
"node": "^22.18.0 || >=24.11.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@huggingface/transformers": "^4.1.0",
|
||||
|
|
@ -1254,9 +1253,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@ladybugdb/core": {
|
||||
"version": "0.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core/-/core-0.18.1.tgz",
|
||||
"integrity": "sha512-0c1kXDpdv7z/GB0oyFYnLEjLsXFwPHz1YD4wxtrk9hav8zJX5T1PHQMr+XRfdDI1NQjx4iNdbPQGGT7Bx/X2aw==",
|
||||
"version": "0.18.2",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core/-/core-0.18.2.tgz",
|
||||
"integrity": "sha512-222FjGciEO5Z+/MRQGU+b4IaGAjOgSQzj7fMpOuhMQN4F8nf654kuKRk1iybSiNy6XSw69hIJ0mKwUeBQ8y6Fg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -1265,17 +1264,17 @@
|
|||
"node-addon-api": "^6.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@ladybugdb/core-darwin-arm64": "0.18.1",
|
||||
"@ladybugdb/core-darwin-x64": "0.18.1",
|
||||
"@ladybugdb/core-linux-arm64": "0.18.1",
|
||||
"@ladybugdb/core-linux-x64": "0.18.1",
|
||||
"@ladybugdb/core-win32-x64": "0.18.1"
|
||||
"@ladybugdb/core-darwin-arm64": "0.18.2",
|
||||
"@ladybugdb/core-darwin-x64": "0.18.2",
|
||||
"@ladybugdb/core-linux-arm64": "0.18.2",
|
||||
"@ladybugdb/core-linux-x64": "0.18.2",
|
||||
"@ladybugdb/core-win32-x64": "0.18.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@ladybugdb/core-darwin-arm64": {
|
||||
"version": "0.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-arm64/-/core-darwin-arm64-0.18.1.tgz",
|
||||
"integrity": "sha512-M5YZuAONRAv3awkr+cfaibn9Da+3pgDzRiek/JabWQuz48xgzW3Vh9yQH4s8Dq/bfQo6YTsaLIBRcUCCUzCtcg==",
|
||||
"version": "0.18.2",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-arm64/-/core-darwin-arm64-0.18.2.tgz",
|
||||
"integrity": "sha512-gAwxsdijBFTz4aZ9ITG6zdQw3lAki0eY33hNBLCfKXjKJLvW/8wCvgCVBglqfqBF5WyI7icFUt+wfy/Fbdfl5A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -1286,9 +1285,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@ladybugdb/core-darwin-x64": {
|
||||
"version": "0.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-x64/-/core-darwin-x64-0.18.1.tgz",
|
||||
"integrity": "sha512-kq+pyTskfCx++Mrbk7QssE/f/CpSuU50T8lhRtv4PaOKhC2Jf8/wAUOA17UxI594wAru3ERpqVBFUBWGcPk2ag==",
|
||||
"version": "0.18.2",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-x64/-/core-darwin-x64-0.18.2.tgz",
|
||||
"integrity": "sha512-oUjYLc1fW3ntCrO9te55PoPfvhFo8AKeNa/sU66fiQyEAZB7qZJxeHnnLgl/bLueTF2os3RSawq46ZftoD/9Eg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -1299,9 +1298,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@ladybugdb/core-linux-arm64": {
|
||||
"version": "0.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-arm64/-/core-linux-arm64-0.18.1.tgz",
|
||||
"integrity": "sha512-fu7ke1haa5rPINcQn0+kxQijZ0A8ZDWP9e+X8xcDH94RagDbPWwG8yFC890cGSdc/j7mTV+xkA/y/kVHpmVI6w==",
|
||||
"version": "0.18.2",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-arm64/-/core-linux-arm64-0.18.2.tgz",
|
||||
"integrity": "sha512-UppokeTaPl9pN0xOsdMa+hmM68zbN2eKReTZhZNYM16qX0d2OlgbS/NlXi09Wdot+w5qvlZ9Q0iCCPfr7qvPaw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -1312,9 +1311,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@ladybugdb/core-linux-x64": {
|
||||
"version": "0.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-x64/-/core-linux-x64-0.18.1.tgz",
|
||||
"integrity": "sha512-qp5HilHzDGuArfOyD+VyA7lVJ7IwQDKd81NZKKTmUwIAOJtdwqniYx6JZICPnlr36zFJBx/lGYoSsEzbC+TVdw==",
|
||||
"version": "0.18.2",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-x64/-/core-linux-x64-0.18.2.tgz",
|
||||
"integrity": "sha512-GypOxCnP2ix/FWM8YhQ41aQYlS+ruoNMJp7pmaF5laJHhL/a+P/apywNTE+9N41Walsl+Emgg9xwxwTC93slow==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -1325,9 +1324,9 @@
|
|||
]
|
||||
},
|
||||
"node_modules/@ladybugdb/core-win32-x64": {
|
||||
"version": "0.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core-win32-x64/-/core-win32-x64-0.18.1.tgz",
|
||||
"integrity": "sha512-vHcXr7Df2X1dbb5ORK+SBmNstd/3tApGFImbAnaWiTuLDFlAdfY8lbiSBSp3OgFjc0BB7F3GYUUdvgDRJjK3zA==",
|
||||
"version": "0.18.2",
|
||||
"resolved": "https://registry.npmjs.org/@ladybugdb/core-win32-x64/-/core-win32-x64-0.18.2.tgz",
|
||||
"integrity": "sha512-hvFwjhTYdwG2sijapx963a27jP9mlLW0ZFv5Yfj19e0B3T/FqD9CaKULPy23mU2XlkISN2LUkY5qdgvCFztl/g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -1990,17 +1989,6 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-11.0.0.tgz",
|
||||
"integrity": "sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==",
|
||||
"deprecated": "This is a stub types definition. uuid provides its own type definitions, so you do not need this installed.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"uuid": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "4.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz",
|
||||
|
|
|
|||
|
|
@ -107,7 +107,6 @@
|
|||
"@types/express": "^5.0.6",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^26.0.0",
|
||||
"@types/uuid": "^11.0.0",
|
||||
"@vitest/coverage-v8": "^4.0.18",
|
||||
"gitnexus-shared": "file:../gitnexus-shared",
|
||||
"tsx": "^4.0.0",
|
||||
|
|
@ -120,6 +119,6 @@
|
|||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0"
|
||||
"node": "^22.18.0 || >=24.11.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
*
|
||||
* `module.registerHooks` — the synchronous ESM/CJS resolution-hook API the
|
||||
* embedding-stack resolvers rely on — was added in Node 22.15.0 (and 23.5.0 on
|
||||
* the 23.x line). The gitnexus engines floor is `>=22.0.0`, which admits Node
|
||||
* 22.0–22.14 AND 23.0–23.4, where the export is absent.
|
||||
* the 23.x line). The gitnexus engines floor is `^22.18.0 || >=24.11.0`, so
|
||||
* every supported runtime exposes it — but `engines` is advisory (not
|
||||
* engine-strict), so a below-floor Node (22.0–22.14, or the unsupported
|
||||
* 23.0–23.4 line) can still run, where the export is absent.
|
||||
*
|
||||
* In this `"type": "module"` package, a *static named* import of a missing
|
||||
* builtin export (`import { registerHooks } from 'node:module'`) is a
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@
|
|||
* per-resolution cost is a single string comparison.
|
||||
*
|
||||
* `module.registerHooks` is marked `@experimental` and requires Node >= 22.15
|
||||
* (the gitnexus engines floor is >= 22.0.0). On older runtimes it is absent and
|
||||
* this is a graceful no-op: embeddings then resolve onnxruntime-common exactly
|
||||
* (below the gitnexus engines floor of `^22.18.0 || >=24.11.0`). On below-floor
|
||||
* runtimes it is absent and this is a graceful no-op: embeddings then resolve onnxruntime-common exactly
|
||||
* as before — fine on hoisted layouts. Any failure during installation is
|
||||
* swallowed.
|
||||
*/
|
||||
|
|
@ -100,9 +100,9 @@ export const ensureOnnxRuntimeCommonResolvable = (): void => {
|
|||
attempted = true;
|
||||
|
||||
try {
|
||||
// Node < 22.15 / < 23.5 (the gitnexus engines floor is >= 22.0.0): no
|
||||
// synchronous hooks API. Degrade gracefully — the import still works on
|
||||
// hoisted layouts.
|
||||
// Node < 22.15 / < 23.5 (below the gitnexus engines floor of
|
||||
// ^22.18.0 || >=24.11.0): no synchronous hooks API. Degrade gracefully —
|
||||
// the import still works on hoisted layouts.
|
||||
const registerHooks = getRegisterHooks();
|
||||
if (typeof registerHooks !== 'function') return;
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@
|
|||
* So CUDA-12 hosts, Windows (DirectML), macOS, and CPU-only hosts are
|
||||
* untouched. Idempotent; any failure is swallowed and leaves the default
|
||||
* resolution exactly as before. `module.registerHooks` requires Node >= 22.15
|
||||
* (the gitnexus engines floor is >= 22.0.0); on older runtimes the redirect is
|
||||
* a no-op, but the default copy's CUDA major is still probed so an
|
||||
* (below the gitnexus engines floor of `^22.18.0 || >=24.11.0`); on below-floor
|
||||
* runtimes the redirect is a no-op, but the default copy's CUDA major is still probed so an
|
||||
* already-matching host (e.g. CUDA 12 + transformers' CUDA-12 build) keeps
|
||||
* auto-selecting the GPU.
|
||||
* `npm link` / symlinked local-dev checkouts are a known caveat: `resolveOurOrtNodeDir`/
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ export const ensureEmbeddingStackResolvable = (): void => {
|
|||
hookAttempted = true;
|
||||
|
||||
try {
|
||||
// Node < 22.15 / < 23.5 (engines floor is >= 22.0.0): no synchronous hooks
|
||||
// Node < 22.15 / < 23.5 (below the engines floor of ^22.18.0 || >=24.11.0): no synchronous hooks
|
||||
// API. Degrade gracefully — normally-installed stacks still resolve; only
|
||||
// the runtime-prefix fallback is unavailable. Reachable now that the import
|
||||
// is a namespace access (see node-module-compat.ts) rather than a static
|
||||
|
|
|
|||
|
|
@ -1,3 +1,46 @@
|
|||
import { existsSync, readdirSync, statSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
||||
/** A valid `libfts.lbug_extension` is ~2.2MB; anything smaller is truncated/corrupt. */
|
||||
const MIN_VALID_FTS_EXTENSION_BYTES = 1024 * 1024;
|
||||
|
||||
/**
|
||||
* Find the installed FTS extension file under a `.lbdb/extension` root,
|
||||
* discovering the version directory instead of assuming it equals the npm
|
||||
* `@ladybugdb/core` package version. LadybugDB's native INSTALL/LOAD resolves
|
||||
* its own extension-ABI version directory, which does not always track the
|
||||
* npm package version — e.g. #2587: bumping the package from 0.18.1 to 0.18.2
|
||||
* still installs into a `0.18.1` directory, because the underlying
|
||||
* extension-ABI build did not change with that patch release.
|
||||
*
|
||||
* Scans every version subdirectory for a `<platform>/fts/libfts.lbug_extension`
|
||||
* file and returns the most recently modified one (the one an install/load
|
||||
* actually just resolved), or null when nothing is installed.
|
||||
*/
|
||||
export const findInstalledFtsExtension = (extensionRoot: string): string | null => {
|
||||
// Fail closed on any FS error (permission quirks, AV file locks on Windows,
|
||||
// a directory vanishing mid-scan) — same contract as the callers this
|
||||
// replaces: "not found" is a valid outcome, a thrown exception is not.
|
||||
try {
|
||||
if (!existsSync(extensionRoot)) return null;
|
||||
let best: { path: string; mtimeMs: number } | null = null;
|
||||
for (const versionEntry of readdirSync(extensionRoot)) {
|
||||
const versionDir = join(extensionRoot, versionEntry);
|
||||
if (!statSync(versionDir).isDirectory()) continue;
|
||||
for (const platformEntry of readdirSync(versionDir)) {
|
||||
const candidate = join(versionDir, platformEntry, 'fts', 'libfts.lbug_extension');
|
||||
if (!existsSync(candidate)) continue;
|
||||
const stat = statSync(candidate);
|
||||
if (stat.size < MIN_VALID_FTS_EXTENSION_BYTES) continue;
|
||||
if (!best || stat.mtimeMs > best.mtimeMs) best = { path: candidate, mtimeMs: stat.mtimeMs };
|
||||
}
|
||||
}
|
||||
return best?.path ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const FTS_UNAVAILABLE_NOTE =
|
||||
'FTS extension unavailable (load-only policy; LOAD failed on this machine)';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,21 +2,21 @@ import {
|
|||
copyFileSync,
|
||||
existsSync,
|
||||
mkdtempSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
statSync,
|
||||
writeFileSync,
|
||||
} from 'node:fs';
|
||||
import { homedir, tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { afterAll, describe, expect, it } from 'vitest';
|
||||
import lbug from '@ladybugdb/core';
|
||||
import {
|
||||
diagnoseExtensionLoad,
|
||||
inspectExtensionBinary,
|
||||
} from '../../src/core/lbug/extension-load-error.js';
|
||||
import { requireFtsResourceOrSkip } from '../helpers/fts-availability.js';
|
||||
import {
|
||||
findInstalledFtsExtension,
|
||||
requireFtsResourceOrSkip,
|
||||
} from '../helpers/fts-availability.js';
|
||||
|
||||
/**
|
||||
* #2374: exercise the language-independent structural classifier against REAL
|
||||
|
|
@ -46,20 +46,14 @@ function resolveLbugNative(): string | null {
|
|||
return null;
|
||||
}
|
||||
|
||||
/** The actual installed FTS extension binary for the running lbug version. */
|
||||
/**
|
||||
* The actual installed FTS extension binary for the running lbug version.
|
||||
* `os.homedir()` already honors `$HOME` (POSIX) / `%USERPROFILE%` (Windows) —
|
||||
* the same resolution LadybugDB's native layer uses — so it stays correct
|
||||
* under the hermetic-home overrides other tests in this suite set via env vars.
|
||||
*/
|
||||
function resolveInstalledFtsExtension(): string | null {
|
||||
const home = process.env.USERPROFILE ?? process.env.HOME ?? homedir();
|
||||
const base = join(home, '.lbdb', 'extension', lbug.VERSION);
|
||||
try {
|
||||
const platformDir = readdirSync(base).find((entry) =>
|
||||
statSync(join(base, entry)).isDirectory(),
|
||||
);
|
||||
if (!platformDir) return null;
|
||||
const ext = join(base, platformDir, 'fts', 'libfts.lbug_extension');
|
||||
return existsSync(ext) ? ext : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return findInstalledFtsExtension(join(homedir(), '.lbdb', 'extension'));
|
||||
}
|
||||
|
||||
const lbugNative = resolveLbugNative();
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ import path from 'path';
|
|||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
|
||||
import lbug from '@ladybugdb/core';
|
||||
import { getExtensionInstallChildProcessArgs } from '../../src/core/lbug/extension-loader.js';
|
||||
import { cleanupTempDirSync } from '../helpers/test-db.js';
|
||||
import { findInstalledFtsExtension } from '../helpers/fts-availability.js';
|
||||
|
||||
/** `.lbdb/extension/<version>/<platform>/fts/libfts.lbug_extension`, discovered not hardcoded. */
|
||||
let extensionRelPath: string;
|
||||
|
|
@ -52,16 +52,12 @@ const makeTmpDir = (label: string): string => {
|
|||
* home — the production installer script, not a reimplementation.
|
||||
*/
|
||||
const resolveSeedExtension = (): void => {
|
||||
const relBase = path.join('.lbdb', 'extension', lbug.VERSION);
|
||||
const realVersionDir = path.join(os.homedir(), relBase);
|
||||
const platformDirs = fs.existsSync(realVersionDir) ? fs.readdirSync(realVersionDir) : [];
|
||||
for (const platform of platformDirs) {
|
||||
const candidate = path.join(realVersionDir, platform, 'fts', 'libfts.lbug_extension');
|
||||
if (fs.existsSync(candidate) && fs.statSync(candidate).size > 1024 * 1024) {
|
||||
extensionRelPath = path.join(relBase, platform, 'fts', 'libfts.lbug_extension');
|
||||
seedExtensionFile = candidate;
|
||||
return;
|
||||
}
|
||||
const realExtensionRoot = path.join(os.homedir(), '.lbdb', 'extension');
|
||||
const installed = findInstalledFtsExtension(realExtensionRoot);
|
||||
if (installed) {
|
||||
extensionRelPath = path.relative(os.homedir(), installed);
|
||||
seedExtensionFile = installed;
|
||||
return;
|
||||
}
|
||||
// No local copy — run the real installer against a hermetic probe home.
|
||||
const probeHome = makeTmpDir('seed-home');
|
||||
|
|
@ -70,16 +66,13 @@ const resolveSeedExtension = (): void => {
|
|||
timeout: 120_000,
|
||||
env: { ...process.env, HOME: probeHome, USERPROFILE: probeHome },
|
||||
});
|
||||
const probeVersionDir = path.join(probeHome, relBase);
|
||||
const probePlatforms = fs.existsSync(probeVersionDir) ? fs.readdirSync(probeVersionDir) : [];
|
||||
for (const platform of probePlatforms) {
|
||||
const candidate = path.join(probeVersionDir, platform, 'fts', 'libfts.lbug_extension');
|
||||
if (install.status === 0 && fs.existsSync(candidate)) {
|
||||
extensionRelPath = path.join(relBase, platform, 'fts', 'libfts.lbug_extension');
|
||||
seedExtensionFile = candidate;
|
||||
networkAvailable = true;
|
||||
return;
|
||||
}
|
||||
const probeExtensionRoot = path.join(probeHome, '.lbdb', 'extension');
|
||||
const probeInstalled = findInstalledFtsExtension(probeExtensionRoot);
|
||||
if (install.status === 0 && probeInstalled) {
|
||||
extensionRelPath = path.relative(probeHome, probeInstalled);
|
||||
seedExtensionFile = probeInstalled;
|
||||
networkAvailable = true;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ import { describe, it, expect, vi, afterEach } from 'vitest';
|
|||
|
||||
/**
|
||||
* Tests for the #2372 `node:module` compat seam. `module.registerHooks` was
|
||||
* added in Node 22.15 / 23.5, but the engines floor is >=22.0.0, so on
|
||||
* 22.0–22.14 and 23.0–23.4 the export is absent. `getRegisterHooks()` must
|
||||
* added in Node 22.15 / 23.5. The engines floor is ^22.18.0 || >=24.11.0 (all
|
||||
* >=22.15), but engines is advisory, so a below-floor 22.0–22.14 / 23.0–23.4
|
||||
* runtime can still run, where the export is absent. `getRegisterHooks()` must
|
||||
* hand back the real function when present and `undefined` when not — the value
|
||||
* the resolver guards degrade on. `isPrefixRuntimeLoadable()` (exported from
|
||||
* runtime-install.ts so CLI code never imports the compat module) is the
|
||||
|
|
|
|||
|
|
@ -589,12 +589,12 @@ describe('gitnexus review-agent workflow security contract', () => {
|
|||
}
|
||||
|
||||
expect(runtimePackage.dependencies?.gitnexus).toBe('1.6.9');
|
||||
expect(runtimePackage.engines?.node).toBe('22.16.0');
|
||||
expect(runtimePackage.engines?.node).toBe('22.18.0');
|
||||
expect(runtimeLock.packages?.['node_modules/gitnexus']?.version).toBe('1.6.9');
|
||||
expect(runtimeLock.packages?.['node_modules/gitnexus']?.integrity).toMatch(/^sha512-/);
|
||||
expect(workflow).not.toMatch(/gitnexus@(latest|next|beta)/);
|
||||
expect(workflow).toContain("node-version: '22.16.0'");
|
||||
expect(workflow).toContain('test "$(node --version)" = \'v22.16.0\'');
|
||||
expect(workflow).toContain("node-version: '22.18.0'");
|
||||
expect(workflow).toContain('test "$(node --version)" = \'v22.18.0\'');
|
||||
expect(workflow).toContain('npm ci');
|
||||
expect(workflow).not.toContain('--package-lock=false');
|
||||
expect(workflow).toContain(
|
||||
|
|
@ -682,7 +682,7 @@ describe('gitnexus review-agent workflow security contract', () => {
|
|||
'${{ runner.temp }}/gitnexus-review-claude-runtime/node_modules/@anthropic-ai/claude-code/bin/claude.exe';
|
||||
|
||||
expect(claudeRuntimePackage.dependencies?.['@anthropic-ai/claude-code']).toBe('2.1.214');
|
||||
expect(claudeRuntimePackage.engines?.node).toBe('22.16.0');
|
||||
expect(claudeRuntimePackage.engines?.node).toBe('22.18.0');
|
||||
expect(claudeRuntimeLock.lockfileVersion).toBe(3);
|
||||
expect(claudeRuntimeLock.packages?.['node_modules/@anthropic-ai/claude-code']).toMatchObject({
|
||||
version: '2.1.214',
|
||||
|
|
@ -1119,7 +1119,7 @@ describe('gitnexus review-agent workflow security contract', () => {
|
|||
'CLAUDE_CONFIG_DIR: ${{ runner.temp }}/gitnexus-review-claude-config',
|
||||
);
|
||||
expect(analyze).toContain('CLAUDE_WORKING_DIR: ${{ runner.temp }}/gitnexus-review-control');
|
||||
expect(analyze).toContain("NODE_VERSION: '22.16.0'");
|
||||
expect(analyze).toContain("NODE_VERSION: '22.18.0'");
|
||||
expect(analyze).toContain('checkout-index --all --force');
|
||||
expect(analyze).toContain('find "${review_dir}" -type l -print0');
|
||||
expect(analyze).toContain('Escaping copied review symlink');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue