From 251a8af783c8e9f49f32fe864a5b7b326d6fffb8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 5 Oct 2026 14:16:52 +0000 Subject: [PATCH] Cherry-pick: fix: load the worker and browser entry without Node built-ins Cherry-picked from #17 (merged to release/2.3.x) Original commit: 240ac09b91594a33030537659c23acead4fb94ff Co-Authored-By: frontegg-david <69419539+frontegg-david@users.noreply.github.com> --- 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 4503928..4e2333a 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