chore(deps): consolidate updates and vendor compatible VECTOR (#3709)

* chore(deps): consolidate open Dependabot updates

Combine the lockfile updates from #3698, #3701, and #3702:
ip-address 10.7.4, zod 4.6.7, and js-yaml 5.5.0 with argparse 3.0.3.
Preserve each upstream package record and all unrelated dependency pins.

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(lbug): vendor matching VECTOR extensions

---------

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Gergő Magyar 2026-10-24 14:42:43 +01:00 • committed by GitHub
parent 11a49a15c6
commit 5dd58a5eec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 285 additions and 31 deletions

View file

@ -1937,10 +1937,20 @@
"license": "MIT"
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"license": "Python-2.0"
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-3.0.3.tgz",
"integrity": "sha512-Sn98ftV3KfsO8xfLPHG6oT8y+b0yvbrLgZjgPDXsharNY15tTAjqIsIju4S32nl6/iEUNNE4v+yL2Zhm8ooP3w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "PSF-2.0"
},
"node_modules/array-back": {
"version": "6.2.3",
@ -3134,9 +3144,9 @@
"license": "ISC"
},
"node_modules/ip-address": {
"version": "10.7.2",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz",
"integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==",
"version": "10.7.4",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.4.tgz",
"integrity": "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg==",
"license": "MIT",
"engines": {
"node": ">= 12"
@ -3213,9 +3223,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.3.tgz",
"integrity": "sha512-yzzNtgczwgoVHNigCyKTXcQJU4Y8t/ikU4UxXW0BvrLfHTGWZhsNcOT2s/a7vn81/4KqVnNmHjaUEPlvWnNwVw==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.5.0.tgz",
"integrity": "sha512-paNr4LFIFPjeeSmwNYdrb8FafLLG/Cr2Af3GjbsHJ5CxIjH4r0c2emlmRdAtpPAieP1gRVzk6buBbVP5gBMGAg==",
"funding": [
{
"type": "github",
@ -3228,7 +3238,7 @@
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
"argparse": "^3.0.3"
},
"bin": {
"js-yaml": "bin/js-yaml.mjs"
@ -5426,9 +5436,9 @@
}
},
"node_modules/zod": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"version": "4.6.7",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz",
"integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"

View file

@ -45,7 +45,8 @@
"vendor/**/README.md",
"vendor/leiden/index.cjs",
"vendor/leiden/utils.cjs",
"vendor/lbug-fts/manifest.json"
"vendor/lbug-fts/manifest.json",
"vendor/lbug-vector/manifest.json"
],
"scripts": {
"build": "node scripts/build.js",

View file

@ -13,6 +13,7 @@ import {
nodePlatformTuple,
resolveFtsVersionPair,
resolveVendoredFtsPath,
resolveVendoredVectorPath,
} from './vendored-extension-path.js';
import { logger } from '../logger.js';
@ -219,8 +220,8 @@ export const installDuckDbExtensionOutOfProcess = async (
* Centralized lifecycle manager for optional LadybugDB extensions.
*
* Tries `LOAD` first — it is per-connection, idempotent, and never
* touches the network. For FTS, a packaged vendored path is path-LOADed
* before the named `LOAD EXTENSION fts`. If `LOAD` fails and the active
* touches the network. For FTS and VECTOR, a packaged vendored path is
* path-LOADed before the named `LOAD EXTENSION`. If `LOAD` fails and the active
* policy permits, the manager runs a single bounded out-of-process
* `INSTALL` attempt per process and retries `LOAD`. Capability outcomes
* are cached so unavailable extensions degrade search features without
@ -289,10 +290,13 @@ export class ExtensionManager {
return false;
}
if (name === 'fts') {
const isVector = name.toLowerCase() === 'vector';
if (name === 'fts' || isVector) {
const tuple = opts.platformTuple ?? nodePlatformTuple();
const vendorRoot = opts.vendorRoot ?? defaultVendorRoot();
const vendored = resolveVendoredFtsPath({ vendorRoot, tuple });
const vendored = isVector
? resolveVendoredVectorPath({ vendorRoot, tuple })
: resolveVendoredFtsPath({ vendorRoot, tuple });
if (vendored) {
attempts.push({ source: 'vendored', tuple });
const vendoredError = await this.tryLoadPath(query, vendored);
@ -301,7 +305,7 @@ export class ExtensionManager {
return true;
}
lastInspectPath = vendored;
} else if (isUnsupportedFtsTuple(tuple, vendorRoot)) {
} else if (name === 'fts' && isUnsupportedFtsTuple(tuple, vendorRoot)) {
attempts.push({ source: 'vendored', tuple });
this.markUnavailable(
name,

View file

@ -3,7 +3,7 @@ import path from 'path';
import { createRequire } from 'node:module';
import { spawnSync, type SpawnSyncReturns } from 'node:child_process';
import { escapeCypherString } from './cypher-escape.js';
import { resolveVendoredFtsPath } from './vendored-extension-path.js';
import { resolveVendoredFtsPath, resolveVendoredVectorPath } from './vendored-extension-path.js';
/** Cap the out-of-process native load probe so a hung filesystem cannot wedge a
* CLI startup gate (same bounding rationale as the extension probe below). */
@ -505,7 +505,7 @@ export async function probeFtsExtensionLoad(
export async function probeVectorExtensionLoad(
timeoutMs: number = DEFAULT_FTS_PROBE_TIMEOUT_MS,
): Promise<ExtensionProbeResult> {
return await probeExtensionLoad('vector', timeoutMs);
return await probeExtensionLoad('vector', timeoutMs, resolveVendoredVectorPath());
}
/**
@ -529,7 +529,7 @@ async function probeExtensionLoad(
});
const statements: string[] = [];
if (extension === 'fts' && vendoredPath) {
if (vendoredPath) {
statements.push(`LOAD EXTENSION '${escapeCypherString(path.resolve(vendoredPath))}'`);
}
statements.push(`LOAD EXTENSION ${extension}`);

View file

@ -130,3 +130,21 @@ export const resolveVendoredFtsPath = (opts?: {
if (!existsSync(candidate)) return null;
return validateVendoredExtensionPath(candidate, vendorRoot);
};
/** VECTOR builds must match the core pin; the CDN's compatibility path can drift. */
export const resolveVendoredVectorPath = (opts?: {
tuple?: string;
vendorRoot?: string;
}): string | null => {
const vendorRoot = opts?.vendorRoot ?? defaultVendorRoot();
const tuple = opts?.tuple ?? nodePlatformTuple();
const candidate = path.resolve(
vendorRoot,
'lbug-vector',
'prebuilds',
tuple,
'libvector.lbug_extension',
);
if (!existsSync(candidate)) return null;
return validateVendoredExtensionPath(candidate, vendorRoot);
};

View file

@ -135,9 +135,9 @@ describe('ExtensionManager — install policies', () => {
const manager = new ExtensionManager({ policy: 'auto', installExtension, warn });
const query = vi.fn().mockRejectedValue(new Error('Extension "vector" not found'));
await expect(manager.ensure(query, 'vector', 'VECTOR', { installTimeoutMs: 10 })).resolves.toBe(
false,
);
await expect(
manager.ensure(query, 'vector', 'VECTOR', { ...noVendored, installTimeoutMs: 10 }),
).resolves.toBe(false);
expect(query.mock.calls.map(([sql]) => sql)).toEqual(['LOAD EXTENSION vector']);
expect(warn).toHaveBeenCalledWith(
@ -690,7 +690,54 @@ describe('ExtensionManager — vendored-first FTS (U2)', () => {
expect(String(query.mock.calls[0][0])).not.toContain('vendor-evil');
});
it('does not try a vendored path for VECTOR and records no tuple', async () => {
it.each(['vector', 'VECTOR'])(
'loads packaged %s before a broken home-cache copy',
async (name) => {
const vendorRoot = makeRoot();
const dir = path.join(vendorRoot, 'lbug-vector', 'prebuilds', 'linux-x64');
mkdirSync(dir, { recursive: true });
const artifact = path.join(dir, 'libvector.lbug_extension');
writeFileSync(artifact, 'placeholder');
const loadPackaged = `LOAD EXTENSION '${escapeCypherString(realpathSync(artifact))}'`;
const query = vi.fn(async (sql: string) => {
if (sql !== loadPackaged) throw new Error('cached VECTOR has an undefined native symbol');
return {};
});
const installExtension = vi.fn();
const manager = new ExtensionManager({
policy: 'load-only',
installExtension,
warn: noopWarn,
});
await expect(
manager.ensure(query, name, 'VECTOR', { vendorRoot, platformTuple: 'linux-x64' }),
).resolves.toBe(true);
expect(query.mock.calls.map(([sql]) => sql)).toEqual([loadPackaged]);
expect(installExtension).not.toHaveBeenCalled();
expect(manager.getCapabilities()[0]?.attempts).toEqual([
{ source: 'vendored', tuple: 'linux-x64' },
]);
},
);
it('does not load packaged VECTOR under the never policy', async () => {
const vendorRoot = makeRoot();
const dir = path.join(vendorRoot, 'lbug-vector', 'prebuilds', 'linux-x64');
mkdirSync(dir, { recursive: true });
writeFileSync(path.join(dir, 'libvector.lbug_extension'), 'placeholder');
const query = vi.fn();
const installExtension = vi.fn();
const manager = new ExtensionManager({ policy: 'never', installExtension, warn: noopWarn });
await expect(
manager.ensure(query, 'VECTOR', 'VECTOR', { vendorRoot, platformTuple: 'linux-x64' }),
).resolves.toBe(false);
expect(query).not.toHaveBeenCalled();
expect(installExtension).not.toHaveBeenCalled();
});
it('falls back to named VECTOR when no packaged VECTOR artifact exists', async () => {
const vendorRoot = makeRoot();
writeVendorArtifact(vendorRoot, 'linux-x64');
const query = vi.fn().mockResolvedValue({});

View file

@ -1,5 +1,7 @@
import path from 'node:path';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { resolveVendoredVectorPath } from '../../src/core/lbug/vendored-extension-path.js';
import { escapeCypherString } from '../../src/core/lbug/cypher-escape.js';
/**
* Unit coverage for probeFtsExtensionLoad (#2374, PR #2375): the doctor FTS
@ -136,12 +138,24 @@ describe('probeFtsExtensionLoad (#2374)', () => {
});
describe('probeVectorExtensionLoad (#2623 follow-up)', () => {
it('issues LOAD EXTENSION vector and reports loaded on success — no platform short-circuit', async () => {
it('probes the same packaged VECTOR artifact as the runtime', async () => {
h.query.mockResolvedValue(closeable());
await expect(probeVectorExtensionLoad()).resolves.toEqual({ loaded: true });
// The probe must really attempt the LOAD (the old code refused Windows
// before ever touching the engine; the artifact ships for win_amd64 too).
expect(h.query).toHaveBeenCalledWith('LOAD EXTENSION vector');
const vendoredPath = resolveVendoredVectorPath();
expect(h.query).toHaveBeenCalledWith(
vendoredPath
? `LOAD EXTENSION '${escapeCypherString(path.resolve(vendoredPath))}'`
: 'LOAD EXTENSION vector',
);
});
it('falls back to named VECTOR when the packaged binary cannot load', async () => {
h.query.mockImplementation(async (sql) => {
if (sql !== 'LOAD EXTENSION vector') throw new Error('packaged binary unavailable');
return closeable();
});
await expect(probeVectorExtensionLoad()).resolves.toEqual({ loaded: true });
expect(h.query).toHaveBeenLastCalledWith('LOAD EXTENSION vector');
});
it('reports the collapsed reason when LOAD fails', async () => {

View file

@ -0,0 +1,37 @@
import { createHash } from 'node:crypto';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { resolveVendoredVectorPath } from '../../src/core/lbug/vendored-extension-path.js';
const vendorRoot = fileURLToPath(new URL('../../vendor/', import.meta.url));
const manifest = JSON.parse(
readFileSync(new URL('../../vendor/lbug-vector/manifest.json', import.meta.url), 'utf8'),
) as {
coreVersion: string;
artifacts: Array<{ tuple: string; sha256: string }>;
};
const pkg = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')) as {
dependencies: Record<string, string>;
files: string[];
};
describe('packaged VECTOR artifacts', () => {
it('pins the artifacts to the installed core version and includes their provenance in npm', () => {
expect(manifest.coreVersion).toBe(pkg.dependencies['@ladybugdb/core']);
expect(pkg.files).toContain('vendor/lbug-vector/manifest.json');
expect(manifest.artifacts.map(({ tuple }) => tuple).sort()).toEqual([
'darwin-arm64',
'darwin-x64',
'linux-arm64',
'linux-x64',
'win32-x64',
]);
});
it.each(manifest.artifacts)('ships the verified $tuple binary', ({ tuple, sha256 }) => {
const artifact = resolveVendoredVectorPath({ vendorRoot, tuple });
if (artifact === null) throw new Error(`Missing packaged VECTOR artifact for ${tuple}`);
expect(createHash('sha256').update(readFileSync(artifact)).digest('hex')).toBe(sha256);
});
});

View file

@ -9,6 +9,7 @@ import {
readFtsArtifactManifest,
resolveFtsVersionPair,
resolveVendoredFtsPath,
resolveVendoredVectorPath,
isUnsupportedFtsTuple,
validateVendoredExtensionPath,
} from '../../src/core/lbug/vendored-extension-path.js';
@ -77,6 +78,28 @@ describe('resolveVendoredFtsPath', () => {
});
});
describe('resolveVendoredVectorPath', () => {
it('resolves only the requested platform artifact', () => {
const vendorRoot = makeVendorRoot();
const dir = join(vendorRoot, 'lbug-vector', 'prebuilds', 'linux-x64');
mkdirSync(dir, { recursive: true });
const artifact = join(dir, 'libvector.lbug_extension');
writeFileSync(artifact, 'placeholder');
expect(resolveVendoredVectorPath({ vendorRoot, tuple: 'linux-x64' })).toBe(artifact);
expect(resolveVendoredVectorPath({ vendorRoot, tuple: 'win32-arm64' })).toBeNull();
});
it('rejects a tuple that escapes the vendor root', () => {
const parent = makeVendorRoot();
const vendorRoot = join(parent, 'vendor');
mkdirSync(vendorRoot);
writeFileSync(join(parent, 'libvector.lbug_extension'), 'outside');
expect(resolveVendoredVectorPath({ vendorRoot, tuple: '../../..' })).toBeNull();
});
});
describe('resolveFtsVersionPair', () => {
it('reads expected from the manifest and found from a Ladybug home path', () => {
const vendorRoot = makeVendorRoot();

22
gitnexus/vendor/lbug-vector/LICENSE vendored Normal file
View file

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2022-2025 Kùzu Inc.
Copyright (c) 2025-2026 Ladybug Memory Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

32
gitnexus/vendor/lbug-vector/README.md vendored Normal file
View file

@ -0,0 +1,32 @@
# LadybugDB VECTOR artifacts
These unmodified binaries come from the official LadybugDB
[v0.21.1 build](https://github.com/LadybugDB/ladybug/actions/runs/36647903773),
at core commit `17a70e51c31b50d30231da32154d9ff4bef0d95f` and extensions
commit `bf74ab654af6bd40a0e3208ac4e8bcf5cb45a7a0`.
`manifest.json` records each GitHub artifact ID, the archive digest supplied by
GitHub, and the extracted binary's SHA-256. Every archive was checked against
that digest before extracting `vector/libvector.lbug_extension`.
The core's `0.21.0` CDN compatibility path can serve a VECTOR binary built
against incompatible core symbols. Load the matching packaged artifact first
so vector indexes work without a network install or a mutable home-cache copy.
The existing `never` policy still disables the extension; missing artifacts
retain the normal named-load and install fallback.
To refresh, select an official build matching the exact `@ladybugdb/core` pin,
download its platform artifacts, verify their GitHub archive digests, and
update the binaries and manifest together. Validate Linux, macOS, and Windows
native tests before changing the pin. Do not substitute a nightly binary.
For example, the Linux x64 archive is available with:
```sh
gh api repos/LadybugDB/ladybug/actions/artifacts/11069747578/zip > vector-linux.zip
```
GitHub Actions artifacts expire; the committed binaries and checksums preserve
the verified build. `test/unit/vector-artifact-coverage.test.ts` checks the core
pin, platform coverage, and binary digests. The upstream MIT license is included
in `LICENSE`.

View file

@ -0,0 +1,46 @@
{
"coreVersion": "0.21.1",
"filename": "libvector.lbug_extension",
"upstreamRepository": "https://github.com/LadybugDB/ladybug",
"upstreamTag": "v0.21.1",
"upstreamCommit": "17a70e51c31b50d30231da32154d9ff4bef0d95f",
"extensionsCommit": "bf74ab654af6bd40a0e3208ac4e8bcf5cb45a7a0",
"workflowRun": "https://github.com/LadybugDB/ladybug/actions/runs/36647903773",
"artifacts": [
{
"tuple": "linux-x64",
"artifactId": 11069747578,
"artifactName": "lbug-extensions-linux-x86_64",
"archiveSha256": "377be87f6cf0e56a38230a536e768a0914f273163f112b8101f69894a088777b",
"sha256": "571726cc2ea0d202df1909ae5a3b0528c9dea3ea8d40be6c16b310ec14aa8821"
},
{
"tuple": "linux-arm64",
"artifactId": 11069462823,
"artifactName": "lbug-extensions-linux-aarch64",
"archiveSha256": "bec0b8c5eb7f75b1f448b1a10360dbbd82c4553e3514418e90a37065468cb4cd",
"sha256": "583101f5bf2d0e5d40de2ee8966018a3bdf0fb8ad8951ef51ef46cd59169445f"
},
{
"tuple": "darwin-x64",
"artifactId": 11069679640,
"artifactName": "lbug-extensions-osx-x86_64",
"archiveSha256": "2ddd47cd3afb5a2bcdae37e928125a81a1aa73775bb2e5e9448033f51eb02c18",
"sha256": "7c0cea65d68c341453ce567e0d12897d0691b5f9ff01cff0fdf0db5756ab7851"
},
{
"tuple": "darwin-arm64",
"artifactId": 11069049174,
"artifactName": "lbug-extensions-osx-arm64",
"archiveSha256": "5d004e4bb9e3dd6f6da7248135eba64998adca283390c88e95f17d46224bbf5c",
"sha256": "6f878f3c0a2320a34967ff568fcf6bbe9065666e95035f0eddfe28bbc73b53f4"
},
{
"tuple": "win32-x64",
"artifactId": 11070343650,
"artifactName": "lbug-extensions-win-x86_64",
"archiveSha256": "cef691c45f28be517b703bfdfafdd66a7d30b7da236f96c4e57297f5e67cf9f2",
"sha256": "6a8878eb2c93400222187460f8fc20ffc9cda4fc2c382fcbdf88f72a350d5746"
}
]
}