From 88802154ff9b6d55252093e79f2cdfea752843b3 Mon Sep 17 00:00:00 2001 From: David Antoon Date: Mon, 5 Oct 2026 17:10:17 +0300 Subject: [PATCH] fix: load the worker and browser entry without Node built-ins The worker entry is documented as safe for browsers and V8 isolates, but vectoria-tfidf imported sanitizeObject from storage/serialization.utils, which imports Node's crypto at load for hash(). Every browser bundle of vectoriadb/worker (or of the root export under the browser condition) failed on "crypto", and an isolate without Node compatibility could not load it. sanitizeObject now lives in its own module with no imports; serialization.utils re-exports it, so the public API is unchanged. The package also declares tslib, which the build's importHelpers output requires but was never listed as a dependency. A new spec loads the worker entry with every Node built-in mocked to throw, and runs a TF-IDF search through it. --- libs/vectoriadb/package.json | 3 ++ .../src/__tests__/worker-entry.spec.ts | 32 +++++++++++++++++ libs/vectoriadb/src/sanitize.utils.ts | 30 ++++++++++++++++ .../src/storage/serialization.utils.ts | 34 ++----------------- libs/vectoriadb/src/vectoria-tfidf.ts | 2 +- yarn.lock | 1 + 6 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 libs/vectoriadb/src/__tests__/worker-entry.spec.ts create mode 100644 libs/vectoriadb/src/sanitize.utils.ts diff --git a/libs/vectoriadb/package.json b/libs/vectoriadb/package.json index 2bd27b6..fa06ee1 100644 --- a/libs/vectoriadb/package.json +++ b/libs/vectoriadb/package.json @@ -51,6 +51,9 @@ "default": "./dist/src/worker.js" } }, + "dependencies": { + "tslib": "^2.8.1" + }, "peerDependencies": { "@huggingface/transformers": "^3.2.2" }, diff --git a/libs/vectoriadb/src/__tests__/worker-entry.spec.ts b/libs/vectoriadb/src/__tests__/worker-entry.spec.ts new file mode 100644 index 0000000..bb5b7d0 --- /dev/null +++ b/libs/vectoriadb/src/__tests__/worker-entry.spec.ts @@ -0,0 +1,32 @@ +/** + * The worker/browser entry must load without Node built-ins: a browser bundle cannot resolve them, + * and a V8 isolate without Node compatibility has none. + */ + +const NODE_BUILT_INS = ['crypto', 'fs', 'fs/promises', 'path', 'os', 'child_process', 'net', 'stream']; + +describe('the worker entry', () => { + it('loads without requiring a Node built-in', () => { + jest.isolateModules(() => { + for (const builtIn of NODE_BUILT_INS) { + jest.doMock(builtIn, () => { + throw new Error(`the worker entry required the Node built-in "${builtIn}"`); + }); + } + + expect(() => require('../worker')).not.toThrow(); + }); + }); + + it('builds and searches a TF-IDF index', () => { + jest.isolateModules(() => { + const { TFIDFVectoria } = require('../worker'); + const db = new TFIDFVectoria({ defaultTopK: 2 }); + db.addDocument('deploy', 'deploy the server to cloudflare workers', { id: 'deploy' }); + db.addDocument('test', 'write unit tests with jest', { id: 'test' }); + db.reindex(); + + expect(db.search('cloudflare deploy')[0]?.id).toBe('deploy'); + }); + }); +}); diff --git a/libs/vectoriadb/src/sanitize.utils.ts b/libs/vectoriadb/src/sanitize.utils.ts new file mode 100644 index 0000000..b09f778 --- /dev/null +++ b/libs/vectoriadb/src/sanitize.utils.ts @@ -0,0 +1,30 @@ +/** + * Sanitize an object to prevent prototype pollution + * Creates a clean object without dangerous properties + */ +export function sanitizeObject(obj: any): any { + if (obj === null || typeof obj !== 'object') { + return obj; + } + + // Handle arrays + if (Array.isArray(obj)) { + return obj.map((item) => sanitizeObject(item)); + } + + // Create clean object without prototype chain + const clean: any = {}; + + // Copy only safe properties + for (const key of Object.keys(obj)) { + // Block dangerous keys + if (key === '__proto__' || key === 'constructor' || key === 'prototype') { + continue; + } + + // Recursively sanitize nested objects + clean[key] = sanitizeObject(obj[key]); + } + + return clean; +} diff --git a/libs/vectoriadb/src/storage/serialization.utils.ts b/libs/vectoriadb/src/storage/serialization.utils.ts index 949c812..cdd1936 100644 --- a/libs/vectoriadb/src/storage/serialization.utils.ts +++ b/libs/vectoriadb/src/storage/serialization.utils.ts @@ -1,6 +1,9 @@ import * as crypto from 'crypto'; import type { DocumentEmbedding, DocumentMetadata } from '../interfaces'; import type { SerializedEmbedding } from './adapter.interface'; +import { sanitizeObject } from '../sanitize.utils'; + +export { sanitizeObject }; /** * Serialize a DocumentEmbedding to a SerializedEmbedding @@ -55,34 +58,3 @@ export function createToolsHash(documents: Array<{ id: string; text: string }>): .join('|'); return hash(content); } - -/** - * Sanitize an object to prevent prototype pollution - * Creates a clean object without dangerous properties - */ -export function sanitizeObject(obj: any): any { - if (obj === null || typeof obj !== 'object') { - return obj; - } - - // Handle arrays - if (Array.isArray(obj)) { - return obj.map((item) => sanitizeObject(item)); - } - - // Create clean object without prototype chain - const clean: any = {}; - - // Copy only safe properties - for (const key of Object.keys(obj)) { - // Block dangerous keys - if (key === '__proto__' || key === 'constructor' || key === 'prototype') { - continue; - } - - // Recursively sanitize nested objects - clean[key] = sanitizeObject(obj[key]); - } - - return clean; -} diff --git a/libs/vectoriadb/src/vectoria-tfidf.ts b/libs/vectoriadb/src/vectoria-tfidf.ts index 65f11b5..6566186 100644 --- a/libs/vectoriadb/src/vectoria-tfidf.ts +++ b/libs/vectoriadb/src/vectoria-tfidf.ts @@ -1,6 +1,6 @@ import { TFIDFEmbeddingService } from './tfidf.embedding.service'; import type { DocumentMetadata, SearchOptions, SearchResult } from './interfaces'; -import { sanitizeObject } from './storage/serialization.utils'; +import { sanitizeObject } from './sanitize.utils'; /** * Document with TF-IDF sparse vector representation diff --git a/yarn.lock b/yarn.lock index ff36f42..a48c34e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6803,6 +6803,7 @@ __metadata: version: 0.0.0-use.local resolution: "vectoriadb@workspace:libs/vectoriadb" dependencies: + tslib: "npm:^2.8.1" typescript: "npm:^5.9.3" peerDependencies: "@huggingface/transformers": ^3.2.2