@peam-ai/search 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +21 -6
- package/dist/index.d.ts +21 -6
- package/dist/index.js +33 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +33 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -63,6 +63,13 @@ declare class SearchEngine {
|
|
|
63
63
|
import(handler: (key: string) => Promise<string>, keys: string[]): Promise<void>;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
type ExportOptions = {
|
|
67
|
+
/**
|
|
68
|
+
* Whether to override existing index data
|
|
69
|
+
* @default false
|
|
70
|
+
*/
|
|
71
|
+
override?: boolean;
|
|
72
|
+
};
|
|
66
73
|
/**
|
|
67
74
|
* Interface for exporting and importing search indexes
|
|
68
75
|
*/
|
|
@@ -75,19 +82,26 @@ interface SearchIndexExporter {
|
|
|
75
82
|
/**
|
|
76
83
|
* Export a search index to storage
|
|
77
84
|
* @param data The search index data to save
|
|
85
|
+
* @param options Exporter options
|
|
78
86
|
*/
|
|
79
|
-
export(data: SearchIndexData): Promise<void>;
|
|
87
|
+
export(data: SearchIndexData, options?: ExportOptions): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Synchronous export of search index to storage
|
|
90
|
+
* @param data
|
|
91
|
+
* @param options Exporter options
|
|
92
|
+
*/
|
|
93
|
+
exportSync?(data: SearchIndexData, options?: ExportOptions): void;
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
interface FileBasedSearchIndexExporterOptions {
|
|
83
|
-
/**
|
|
84
|
-
* The directory where the index file is located
|
|
85
|
-
*/
|
|
86
|
-
baseDir: string;
|
|
87
97
|
/**
|
|
88
98
|
* The path to the index file relative to baseDir
|
|
89
99
|
*/
|
|
90
100
|
indexPath: string;
|
|
101
|
+
/**
|
|
102
|
+
* The directory where the index file is located
|
|
103
|
+
*/
|
|
104
|
+
baseDir?: string;
|
|
91
105
|
}
|
|
92
106
|
/**
|
|
93
107
|
* File-based implementation of SearchIndexExporter
|
|
@@ -101,7 +115,8 @@ declare class FileBasedSearchIndexExporter implements SearchIndexExporter {
|
|
|
101
115
|
private getFullPath;
|
|
102
116
|
private loadData;
|
|
103
117
|
import(): Promise<SearchIndexData | null>;
|
|
104
|
-
export(data: SearchIndexData): Promise<void>;
|
|
118
|
+
export(data: SearchIndexData, options?: ExportOptions): Promise<void>;
|
|
119
|
+
exportSync(data: SearchIndexData, options?: ExportOptions): void;
|
|
105
120
|
}
|
|
106
121
|
|
|
107
122
|
type SearchExporterConfig = {
|
package/dist/index.d.ts
CHANGED
|
@@ -63,6 +63,13 @@ declare class SearchEngine {
|
|
|
63
63
|
import(handler: (key: string) => Promise<string>, keys: string[]): Promise<void>;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
type ExportOptions = {
|
|
67
|
+
/**
|
|
68
|
+
* Whether to override existing index data
|
|
69
|
+
* @default false
|
|
70
|
+
*/
|
|
71
|
+
override?: boolean;
|
|
72
|
+
};
|
|
66
73
|
/**
|
|
67
74
|
* Interface for exporting and importing search indexes
|
|
68
75
|
*/
|
|
@@ -75,19 +82,26 @@ interface SearchIndexExporter {
|
|
|
75
82
|
/**
|
|
76
83
|
* Export a search index to storage
|
|
77
84
|
* @param data The search index data to save
|
|
85
|
+
* @param options Exporter options
|
|
78
86
|
*/
|
|
79
|
-
export(data: SearchIndexData): Promise<void>;
|
|
87
|
+
export(data: SearchIndexData, options?: ExportOptions): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Synchronous export of search index to storage
|
|
90
|
+
* @param data
|
|
91
|
+
* @param options Exporter options
|
|
92
|
+
*/
|
|
93
|
+
exportSync?(data: SearchIndexData, options?: ExportOptions): void;
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
interface FileBasedSearchIndexExporterOptions {
|
|
83
|
-
/**
|
|
84
|
-
* The directory where the index file is located
|
|
85
|
-
*/
|
|
86
|
-
baseDir: string;
|
|
87
97
|
/**
|
|
88
98
|
* The path to the index file relative to baseDir
|
|
89
99
|
*/
|
|
90
100
|
indexPath: string;
|
|
101
|
+
/**
|
|
102
|
+
* The directory where the index file is located
|
|
103
|
+
*/
|
|
104
|
+
baseDir?: string;
|
|
91
105
|
}
|
|
92
106
|
/**
|
|
93
107
|
* File-based implementation of SearchIndexExporter
|
|
@@ -101,7 +115,8 @@ declare class FileBasedSearchIndexExporter implements SearchIndexExporter {
|
|
|
101
115
|
private getFullPath;
|
|
102
116
|
private loadData;
|
|
103
117
|
import(): Promise<SearchIndexData | null>;
|
|
104
|
-
export(data: SearchIndexData): Promise<void>;
|
|
118
|
+
export(data: SearchIndexData, options?: ExportOptions): Promise<void>;
|
|
119
|
+
exportSync(data: SearchIndexData, options?: ExportOptions): void;
|
|
105
120
|
}
|
|
106
121
|
|
|
107
122
|
type SearchExporterConfig = {
|
package/dist/index.js
CHANGED
|
@@ -311,13 +311,15 @@ function buildSearchIndex(pages) {
|
|
|
311
311
|
|
|
312
312
|
// src/exporters/FileBasedSearchIndexExporter.ts
|
|
313
313
|
var import_logger3 = require("@peam-ai/logger");
|
|
314
|
+
var fsSync = __toESM(require("fs"));
|
|
314
315
|
var fs = __toESM(require("fs/promises"));
|
|
315
316
|
var path = __toESM(require("path"));
|
|
316
317
|
var log3 = import_logger3.loggers.search;
|
|
317
318
|
var FileBasedSearchIndexExporter = class {
|
|
318
319
|
constructor(options) {
|
|
319
320
|
this.cachedData = null;
|
|
320
|
-
|
|
321
|
+
var _a;
|
|
322
|
+
this.baseDir = (_a = options.baseDir) != null ? _a : process.cwd();
|
|
321
323
|
this.indexPath = options.indexPath;
|
|
322
324
|
}
|
|
323
325
|
getFullPath() {
|
|
@@ -355,10 +357,18 @@ var FileBasedSearchIndexExporter = class {
|
|
|
355
357
|
return data;
|
|
356
358
|
});
|
|
357
359
|
}
|
|
358
|
-
export(
|
|
359
|
-
return __async(this,
|
|
360
|
+
export(_0) {
|
|
361
|
+
return __async(this, arguments, function* (data, options = { override: true }) {
|
|
360
362
|
const fullPath = this.getFullPath();
|
|
361
363
|
try {
|
|
364
|
+
if (!(options == null ? void 0 : options.override)) {
|
|
365
|
+
try {
|
|
366
|
+
yield fs.access(fullPath);
|
|
367
|
+
log3.debug("Search index file already exists and override is false, skipping export:", fullPath);
|
|
368
|
+
return;
|
|
369
|
+
} catch (e) {
|
|
370
|
+
}
|
|
371
|
+
}
|
|
362
372
|
const dir = path.dirname(fullPath);
|
|
363
373
|
yield fs.mkdir(dir, { recursive: true });
|
|
364
374
|
yield fs.writeFile(fullPath, JSON.stringify(data, null, 2), "utf-8");
|
|
@@ -369,6 +379,26 @@ var FileBasedSearchIndexExporter = class {
|
|
|
369
379
|
}
|
|
370
380
|
});
|
|
371
381
|
}
|
|
382
|
+
exportSync(data, options = { override: true }) {
|
|
383
|
+
const fullPath = this.getFullPath();
|
|
384
|
+
try {
|
|
385
|
+
if (!(options == null ? void 0 : options.override)) {
|
|
386
|
+
try {
|
|
387
|
+
fsSync.accessSync(fullPath);
|
|
388
|
+
log3.debug("Search index file already exists and override is false, skipping export:", fullPath);
|
|
389
|
+
return;
|
|
390
|
+
} catch (e) {
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
const dir = path.dirname(fullPath);
|
|
394
|
+
fsSync.mkdirSync(dir, { recursive: true });
|
|
395
|
+
fsSync.writeFileSync(fullPath, JSON.stringify(data, null, 2), "utf-8");
|
|
396
|
+
log3.debug("Search index saved to file:", fullPath, "with", data.keys.length, "keys");
|
|
397
|
+
} catch (error) {
|
|
398
|
+
log3.error("Failed to save search index to file:", fullPath, error);
|
|
399
|
+
throw error;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
372
402
|
};
|
|
373
403
|
|
|
374
404
|
// src/exporters/config.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/searchEngine.ts","../src/textSearch.ts","../src/indexBuilder.ts","../src/exporters/FileBasedSearchIndexExporter.ts","../src/exporters/config.ts"],"sourcesContent":["export { buildSearchIndex, type PageToIndex, type SearchIndexData } from './indexBuilder';\nexport { SearchEngine } from './searchEngine';\nexport { TextSearch } from './textSearch';\n\nexport type { StructuredPageDocumentData } from './types';\n\nexport type { SearchEngineConfig } from './searchEngine';\n\nexport {\n FileBasedSearchIndexExporter,\n createExporterFromConfig,\n type FileBasedSearchIndexExporterOptions,\n type SearchExporterConfig,\n type SearchIndexExporter,\n} from './exporters';\n","import { loggers } from '@peam-ai/logger';\nimport type { StructuredPage } from '@peam-ai/parser';\nimport { TextSearch, type TextSearchOptions } from './textSearch';\nimport type { StructuredPageDocumentData } from './types';\n\nconst log = loggers.search;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface SearchEngineConfig {\n // Reserved for future configuration options\n}\n\nexport class SearchEngine {\n private textSearch: TextSearch;\n private initialized: boolean;\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n constructor(_config?: SearchEngineConfig) {\n this.textSearch = new TextSearch();\n this.initialized = false;\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n log.debug('Initializing search engine');\n await this.textSearch.initialize();\n this.initialized = true;\n }\n\n async addPage(path: string, content: StructuredPage): Promise<void> {\n if (!this.initialized) {\n throw new Error('Search engine not initialized. Call initialize() first.');\n }\n\n const document: StructuredPageDocumentData = {\n id: path,\n path,\n content,\n };\n\n await this.textSearch.addDocument(document);\n log.debug('Page added to search engine:', path);\n }\n\n async search(query: string, options: TextSearchOptions = {}): Promise<StructuredPageDocumentData[]> {\n if (!this.initialized) {\n throw new Error('Search engine not initialized. Call initialize() first.');\n }\n\n log.debug('Performing text search:', query);\n return this.textSearch.search(query, options);\n }\n\n count(): number {\n return this.textSearch.count();\n }\n\n getDocument(path: string) {\n return this.textSearch.getDocument(path);\n }\n\n getAllDocuments(limit?: number): StructuredPageDocumentData[] {\n return this.textSearch.getAllDocuments(limit);\n }\n\n clear(): void {\n this.textSearch.clear();\n }\n\n async export(handler: (key: string, data: string) => Promise<void>): Promise<{ keys: string[] }> {\n return this.textSearch.export(handler);\n }\n\n async import(handler: (key: string) => Promise<string>, keys: string[]): Promise<void> {\n await this.textSearch.import(handler, keys);\n this.initialized = true;\n log.debug('Search engine initialized from imported data');\n }\n}\n","import { loggers } from '@peam-ai/logger';\nimport { Charset, Document } from 'flexsearch';\nimport type { StructuredPageDocumentData } from './types';\n\nexport interface TextSearchOptions {\n limit?: number;\n offset?: number;\n suggest?: boolean;\n}\n\nconst PEAM_DOCUMENT_IDS_KEY = 'peam.documentIds';\nconst MAX_DOCUMENTS_RETRIEVE = 25;\nconst log = loggers.search;\n\nexport class TextSearch {\n private index: Document<StructuredPageDocumentData>;\n private initialized: boolean;\n private documentIds: Set<string>;\n\n constructor() {\n this.initialized = false;\n this.index = this.getIndex();\n this.documentIds = new Set();\n }\n\n private getIndex() {\n return new Document<StructuredPageDocumentData>({\n worker: false,\n document: {\n id: 'path',\n index: ['content:title', 'content:description', 'content:textContent', 'content:author', 'content:keywords'],\n store: true,\n },\n tokenize: 'forward',\n resolution: 9,\n context: {\n resolution: 3,\n depth: 2,\n bidirectional: true,\n },\n cache: 100,\n encoder: Charset.LatinExtra,\n });\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n log.debug('Text search already initialized');\n return;\n }\n\n this.initialized = true;\n }\n\n async addDocument(document: StructuredPageDocumentData): Promise<void> {\n if (!this.initialized) {\n throw new Error('TextSearch not initialized. Call initialize() first.');\n }\n\n log.debug('Adding document to text search:', document.path);\n\n this.index.add(document);\n this.documentIds.add(document.path);\n }\n\n async search(query: string, options: TextSearchOptions = {}): Promise<StructuredPageDocumentData[]> {\n if (!this.initialized) {\n throw new Error('TextSearch not initialized. Call initialize() first.');\n }\n\n const limit = options.limit || MAX_DOCUMENTS_RETRIEVE;\n const offset = options.offset || 0;\n\n log.debug('Searching for:', query);\n\n const results = await this.index.search(query, {\n limit: limit + offset,\n suggest: options.suggest,\n enrich: true,\n });\n\n const pathSet = new Set<string>();\n const documents: StructuredPageDocumentData[] = [];\n\n for (const fieldResults of results) {\n if (Array.isArray(fieldResults.result)) {\n for (const result of fieldResults.result) {\n const id = typeof result === 'object' && 'id' in result ? result.id : result;\n const doc = typeof result === 'object' && 'doc' in result ? result.doc : null;\n\n if (!pathSet.has(id as string) && doc) {\n pathSet.add(id as string);\n documents.push(doc);\n }\n }\n }\n }\n\n const pagedResults = documents.slice(offset, offset + limit);\n\n return pagedResults;\n }\n\n count(): number {\n return this.documentIds.size;\n }\n\n getDocument(path: string) {\n return this.index.get(path);\n }\n\n getAllDocuments(limit?: number): StructuredPageDocumentData[] {\n const documents: StructuredPageDocumentData[] = [];\n let count = 0;\n limit = limit || MAX_DOCUMENTS_RETRIEVE;\n\n for (const id of this.documentIds) {\n if (count >= limit) {\n break;\n }\n\n const doc = this.index.get(id);\n if (doc) {\n documents.push(doc);\n count++;\n }\n }\n\n log.debug('Retrieved documents from store (limit):', documents.length, limit);\n return documents;\n }\n\n clear(): void {\n this.index.clear();\n this.index = this.getIndex();\n this.documentIds.clear();\n }\n\n async export(handler: (key: string, data: string) => Promise<void>): Promise<{ keys: string[] }> {\n const keys: string[] = [];\n\n await handler(PEAM_DOCUMENT_IDS_KEY, JSON.stringify(Array.from(this.documentIds)));\n keys.push(PEAM_DOCUMENT_IDS_KEY);\n\n await this.index.export(async (key: string, data: string) => {\n keys.push(key);\n await handler(key, data);\n });\n\n log.debug('Exported keys:', keys.length);\n\n return { keys };\n }\n\n async import(handler: (key: string) => Promise<string>, keys: string[]): Promise<void> {\n const documentIdsData = await handler(PEAM_DOCUMENT_IDS_KEY);\n if (documentIdsData) {\n const parsed = typeof documentIdsData === 'string' ? JSON.parse(documentIdsData) : documentIdsData;\n this.documentIds = new Set(parsed);\n }\n\n for (const key of keys) {\n if (key === PEAM_DOCUMENT_IDS_KEY) {\n continue;\n }\n\n try {\n const data = await handler(key);\n if (data) {\n this.index.import(key, data);\n }\n } catch (error) {\n log.error('Error importing key:', key, error);\n }\n }\n\n this.initialized = true;\n log.debug('Import completed with keys:', keys.length);\n }\n}\n","import type { StructuredPage } from '@peam-ai/parser';\nimport { SearchEngine } from './searchEngine';\n\nexport interface PageToIndex {\n path: string;\n structuredPage: StructuredPage;\n}\n\nexport interface SearchIndexData {\n keys: string[];\n data: Record<string, string>;\n}\n\n/**\n * Build a search index from structured pages\n */\nexport async function buildSearchIndex(pages: PageToIndex[]): Promise<SearchIndexData> {\n const searchEngine = new SearchEngine();\n await searchEngine.initialize();\n\n for (const page of pages) {\n await searchEngine.addPage(page.path, page.structuredPage);\n }\n\n const exportedData: Record<string, string> = {};\n const result = await searchEngine.export(async (key, data) => {\n exportedData[key] = data;\n });\n\n return {\n keys: result.keys,\n data: exportedData,\n };\n}\n","import { loggers } from '@peam-ai/logger';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport type { SearchIndexData } from '../indexBuilder';\nimport type { SearchIndexExporter } from './SearchIndexExporter';\n\nconst log = loggers.search;\n\nexport interface FileBasedSearchIndexExporterOptions {\n /**\n * The directory where the index file is located\n */\n baseDir: string;\n\n /**\n * The path to the index file relative to baseDir\n */\n indexPath: string;\n}\n\n/**\n * File-based implementation of SearchIndexExporter\n * Reads and writes search index data to/from a JSON file\n */\nexport class FileBasedSearchIndexExporter implements SearchIndexExporter {\n private baseDir: string;\n private indexPath: string;\n private cachedData: SearchIndexData | null = null;\n\n constructor(options: FileBasedSearchIndexExporterOptions) {\n this.baseDir = options.baseDir;\n this.indexPath = options.indexPath;\n }\n\n private getFullPath(): string {\n return path.join(this.baseDir, this.indexPath);\n }\n\n private async loadData(): Promise<SearchIndexData | null> {\n if (this.cachedData) {\n return this.cachedData;\n }\n\n const fullPath = this.getFullPath();\n\n try {\n const fileContent = await fs.readFile(fullPath, 'utf-8');\n const data = JSON.parse(fileContent) as SearchIndexData;\n\n if (!data || !data.keys || !Array.isArray(data.keys) || !data.data) {\n log.warn('Invalid search index structure in file:', fullPath);\n return null;\n }\n\n if (data.keys.length === 0) {\n log.debug('Search index is empty:', fullPath);\n return null;\n }\n\n this.cachedData = data;\n log.debug('Search index loaded from file:', fullPath, 'with', data.keys.length, 'keys');\n return data;\n } catch (error) {\n log.error('Failed to load search index from file:', fullPath, error);\n return null;\n }\n }\n\n async import(): Promise<SearchIndexData | null> {\n const data = await this.loadData();\n return data;\n }\n\n async export(data: SearchIndexData): Promise<void> {\n const fullPath = this.getFullPath();\n\n try {\n const dir = path.dirname(fullPath);\n await fs.mkdir(dir, { recursive: true });\n\n await fs.writeFile(fullPath, JSON.stringify(data, null, 2), 'utf-8');\n\n log.debug('Search index saved to file:', fullPath, 'with', data.keys.length, 'keys');\n } catch (error) {\n log.error('Failed to save search index to file:', fullPath, error);\n throw error;\n }\n }\n}\n","import { FileBasedSearchIndexExporter, FileBasedSearchIndexExporterOptions } from './FileBasedSearchIndexExporter';\nimport { SearchIndexExporter } from './SearchIndexExporter';\n\nexport type SearchExporterConfig = {\n type: 'fileBased';\n config: FileBasedSearchIndexExporterOptions;\n};\n\n/**\n * Creates a SearchIndexExporter instance from a SearchExporterConfig\n */\nexport function createExporterFromConfig(exporterConfig: SearchExporterConfig): SearchIndexExporter {\n if (exporterConfig.type === 'fileBased') {\n return new FileBasedSearchIndexExporter({\n ...exporterConfig.config,\n });\n }\n\n throw new Error(`Unknown exporter type: ${exporterConfig.type}`);\n}\n"],"mappings":"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","names":["import_logger","path","log","path","import_logger","log"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/searchEngine.ts","../src/textSearch.ts","../src/indexBuilder.ts","../src/exporters/FileBasedSearchIndexExporter.ts","../src/exporters/config.ts"],"sourcesContent":["export { buildSearchIndex, type PageToIndex, type SearchIndexData } from './indexBuilder';\nexport { SearchEngine } from './searchEngine';\nexport { TextSearch } from './textSearch';\n\nexport type { StructuredPageDocumentData } from './types';\n\nexport type { SearchEngineConfig } from './searchEngine';\n\nexport {\n FileBasedSearchIndexExporter,\n createExporterFromConfig,\n type FileBasedSearchIndexExporterOptions,\n type SearchExporterConfig,\n type SearchIndexExporter,\n} from './exporters';\n","import { loggers } from '@peam-ai/logger';\nimport type { StructuredPage } from '@peam-ai/parser';\nimport { TextSearch, type TextSearchOptions } from './textSearch';\nimport type { StructuredPageDocumentData } from './types';\n\nconst log = loggers.search;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface SearchEngineConfig {\n // Reserved for future configuration options\n}\n\nexport class SearchEngine {\n private textSearch: TextSearch;\n private initialized: boolean;\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n constructor(_config?: SearchEngineConfig) {\n this.textSearch = new TextSearch();\n this.initialized = false;\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n log.debug('Initializing search engine');\n await this.textSearch.initialize();\n this.initialized = true;\n }\n\n async addPage(path: string, content: StructuredPage): Promise<void> {\n if (!this.initialized) {\n throw new Error('Search engine not initialized. Call initialize() first.');\n }\n\n const document: StructuredPageDocumentData = {\n id: path,\n path,\n content,\n };\n\n await this.textSearch.addDocument(document);\n log.debug('Page added to search engine:', path);\n }\n\n async search(query: string, options: TextSearchOptions = {}): Promise<StructuredPageDocumentData[]> {\n if (!this.initialized) {\n throw new Error('Search engine not initialized. Call initialize() first.');\n }\n\n log.debug('Performing text search:', query);\n return this.textSearch.search(query, options);\n }\n\n count(): number {\n return this.textSearch.count();\n }\n\n getDocument(path: string) {\n return this.textSearch.getDocument(path);\n }\n\n getAllDocuments(limit?: number): StructuredPageDocumentData[] {\n return this.textSearch.getAllDocuments(limit);\n }\n\n clear(): void {\n this.textSearch.clear();\n }\n\n async export(handler: (key: string, data: string) => Promise<void>): Promise<{ keys: string[] }> {\n return this.textSearch.export(handler);\n }\n\n async import(handler: (key: string) => Promise<string>, keys: string[]): Promise<void> {\n await this.textSearch.import(handler, keys);\n this.initialized = true;\n log.debug('Search engine initialized from imported data');\n }\n}\n","import { loggers } from '@peam-ai/logger';\nimport { Charset, Document } from 'flexsearch';\nimport type { StructuredPageDocumentData } from './types';\n\nexport interface TextSearchOptions {\n limit?: number;\n offset?: number;\n suggest?: boolean;\n}\n\nconst PEAM_DOCUMENT_IDS_KEY = 'peam.documentIds';\nconst MAX_DOCUMENTS_RETRIEVE = 25;\nconst log = loggers.search;\n\nexport class TextSearch {\n private index: Document<StructuredPageDocumentData>;\n private initialized: boolean;\n private documentIds: Set<string>;\n\n constructor() {\n this.initialized = false;\n this.index = this.getIndex();\n this.documentIds = new Set();\n }\n\n private getIndex() {\n return new Document<StructuredPageDocumentData>({\n worker: false,\n document: {\n id: 'path',\n index: ['content:title', 'content:description', 'content:textContent', 'content:author', 'content:keywords'],\n store: true,\n },\n tokenize: 'forward',\n resolution: 9,\n context: {\n resolution: 3,\n depth: 2,\n bidirectional: true,\n },\n cache: 100,\n encoder: Charset.LatinExtra,\n });\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n log.debug('Text search already initialized');\n return;\n }\n\n this.initialized = true;\n }\n\n async addDocument(document: StructuredPageDocumentData): Promise<void> {\n if (!this.initialized) {\n throw new Error('TextSearch not initialized. Call initialize() first.');\n }\n\n log.debug('Adding document to text search:', document.path);\n\n this.index.add(document);\n this.documentIds.add(document.path);\n }\n\n async search(query: string, options: TextSearchOptions = {}): Promise<StructuredPageDocumentData[]> {\n if (!this.initialized) {\n throw new Error('TextSearch not initialized. Call initialize() first.');\n }\n\n const limit = options.limit || MAX_DOCUMENTS_RETRIEVE;\n const offset = options.offset || 0;\n\n log.debug('Searching for:', query);\n\n const results = await this.index.search(query, {\n limit: limit + offset,\n suggest: options.suggest,\n enrich: true,\n });\n\n const pathSet = new Set<string>();\n const documents: StructuredPageDocumentData[] = [];\n\n for (const fieldResults of results) {\n if (Array.isArray(fieldResults.result)) {\n for (const result of fieldResults.result) {\n const id = typeof result === 'object' && 'id' in result ? result.id : result;\n const doc = typeof result === 'object' && 'doc' in result ? result.doc : null;\n\n if (!pathSet.has(id as string) && doc) {\n pathSet.add(id as string);\n documents.push(doc);\n }\n }\n }\n }\n\n const pagedResults = documents.slice(offset, offset + limit);\n\n return pagedResults;\n }\n\n count(): number {\n return this.documentIds.size;\n }\n\n getDocument(path: string) {\n return this.index.get(path);\n }\n\n getAllDocuments(limit?: number): StructuredPageDocumentData[] {\n const documents: StructuredPageDocumentData[] = [];\n let count = 0;\n limit = limit || MAX_DOCUMENTS_RETRIEVE;\n\n for (const id of this.documentIds) {\n if (count >= limit) {\n break;\n }\n\n const doc = this.index.get(id);\n if (doc) {\n documents.push(doc);\n count++;\n }\n }\n\n log.debug('Retrieved documents from store (limit):', documents.length, limit);\n return documents;\n }\n\n clear(): void {\n this.index.clear();\n this.index = this.getIndex();\n this.documentIds.clear();\n }\n\n async export(handler: (key: string, data: string) => Promise<void>): Promise<{ keys: string[] }> {\n const keys: string[] = [];\n\n await handler(PEAM_DOCUMENT_IDS_KEY, JSON.stringify(Array.from(this.documentIds)));\n keys.push(PEAM_DOCUMENT_IDS_KEY);\n\n await this.index.export(async (key: string, data: string) => {\n keys.push(key);\n await handler(key, data);\n });\n\n log.debug('Exported keys:', keys.length);\n\n return { keys };\n }\n\n async import(handler: (key: string) => Promise<string>, keys: string[]): Promise<void> {\n const documentIdsData = await handler(PEAM_DOCUMENT_IDS_KEY);\n if (documentIdsData) {\n const parsed = typeof documentIdsData === 'string' ? JSON.parse(documentIdsData) : documentIdsData;\n this.documentIds = new Set(parsed);\n }\n\n for (const key of keys) {\n if (key === PEAM_DOCUMENT_IDS_KEY) {\n continue;\n }\n\n try {\n const data = await handler(key);\n if (data) {\n this.index.import(key, data);\n }\n } catch (error) {\n log.error('Error importing key:', key, error);\n }\n }\n\n this.initialized = true;\n log.debug('Import completed with keys:', keys.length);\n }\n}\n","import type { StructuredPage } from '@peam-ai/parser';\nimport { SearchEngine } from './searchEngine';\n\nexport interface PageToIndex {\n path: string;\n structuredPage: StructuredPage;\n}\n\nexport interface SearchIndexData {\n keys: string[];\n data: Record<string, string>;\n}\n\n/**\n * Build a search index from structured pages\n */\nexport async function buildSearchIndex(pages: PageToIndex[]): Promise<SearchIndexData> {\n const searchEngine = new SearchEngine();\n await searchEngine.initialize();\n\n for (const page of pages) {\n await searchEngine.addPage(page.path, page.structuredPage);\n }\n\n const exportedData: Record<string, string> = {};\n const result = await searchEngine.export(async (key, data) => {\n exportedData[key] = data;\n });\n\n return {\n keys: result.keys,\n data: exportedData,\n };\n}\n","import { loggers } from '@peam-ai/logger';\nimport * as fsSync from 'fs';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport type { SearchIndexData } from '../indexBuilder';\nimport type { ExportOptions, SearchIndexExporter } from './SearchIndexExporter';\n\nconst log = loggers.search;\n\nexport interface FileBasedSearchIndexExporterOptions {\n /**\n * The path to the index file relative to baseDir\n */\n indexPath: string;\n\n /**\n * The directory where the index file is located\n */\n baseDir?: string;\n}\n\n/**\n * File-based implementation of SearchIndexExporter\n * Reads and writes search index data to/from a JSON file\n */\nexport class FileBasedSearchIndexExporter implements SearchIndexExporter {\n private baseDir: string;\n private indexPath: string;\n private cachedData: SearchIndexData | null = null;\n\n constructor(options: FileBasedSearchIndexExporterOptions) {\n this.baseDir = options.baseDir ?? process.cwd();\n this.indexPath = options.indexPath;\n }\n\n private getFullPath(): string {\n return path.join(this.baseDir, this.indexPath);\n }\n\n private async loadData(): Promise<SearchIndexData | null> {\n if (this.cachedData) {\n return this.cachedData;\n }\n\n const fullPath = this.getFullPath();\n\n try {\n const fileContent = await fs.readFile(fullPath, 'utf-8');\n const data = JSON.parse(fileContent) as SearchIndexData;\n\n if (!data || !data.keys || !Array.isArray(data.keys) || !data.data) {\n log.warn('Invalid search index structure in file:', fullPath);\n return null;\n }\n\n if (data.keys.length === 0) {\n log.debug('Search index is empty:', fullPath);\n return null;\n }\n\n this.cachedData = data;\n log.debug('Search index loaded from file:', fullPath, 'with', data.keys.length, 'keys');\n return data;\n } catch (error) {\n log.error('Failed to load search index from file:', fullPath, error);\n return null;\n }\n }\n\n async import(): Promise<SearchIndexData | null> {\n const data = await this.loadData();\n return data;\n }\n\n async export(data: SearchIndexData, options: ExportOptions = { override: true }): Promise<void> {\n const fullPath = this.getFullPath();\n\n try {\n if (!options?.override) {\n try {\n await fs.access(fullPath);\n log.debug('Search index file already exists and override is false, skipping export:', fullPath);\n return;\n } catch {\n // noop\n }\n }\n\n const dir = path.dirname(fullPath);\n await fs.mkdir(dir, { recursive: true });\n\n await fs.writeFile(fullPath, JSON.stringify(data, null, 2), 'utf-8');\n\n log.debug('Search index saved to file:', fullPath, 'with', data.keys.length, 'keys');\n } catch (error) {\n log.error('Failed to save search index to file:', fullPath, error);\n throw error;\n }\n }\n\n exportSync(data: SearchIndexData, options: ExportOptions = { override: true }): void {\n const fullPath = this.getFullPath();\n\n try {\n if (!options?.override) {\n try {\n fsSync.accessSync(fullPath);\n log.debug('Search index file already exists and override is false, skipping export:', fullPath);\n return;\n } catch {\n // noop\n }\n }\n\n const dir = path.dirname(fullPath);\n fsSync.mkdirSync(dir, { recursive: true });\n\n fsSync.writeFileSync(fullPath, JSON.stringify(data, null, 2), 'utf-8');\n\n log.debug('Search index saved to file:', fullPath, 'with', data.keys.length, 'keys');\n } catch (error) {\n log.error('Failed to save search index to file:', fullPath, error);\n throw error;\n }\n }\n}\n","import { FileBasedSearchIndexExporter, FileBasedSearchIndexExporterOptions } from './FileBasedSearchIndexExporter';\nimport { SearchIndexExporter } from './SearchIndexExporter';\n\nexport type SearchExporterConfig = {\n type: 'fileBased';\n config: FileBasedSearchIndexExporterOptions;\n};\n\n/**\n * Creates a SearchIndexExporter instance from a SearchExporterConfig\n */\nexport function createExporterFromConfig(exporterConfig: SearchExporterConfig): SearchIndexExporter {\n if (exporterConfig.type === 'fileBased') {\n return new FileBasedSearchIndexExporter({\n ...exporterConfig.config,\n });\n }\n\n throw new Error(`Unknown exporter type: ${exporterConfig.type}`);\n}\n"],"mappings":"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","names":["import_logger","path","log","path","import_logger","log"]}
|
package/dist/index.mjs
CHANGED
|
@@ -274,13 +274,15 @@ function buildSearchIndex(pages) {
|
|
|
274
274
|
|
|
275
275
|
// src/exporters/FileBasedSearchIndexExporter.ts
|
|
276
276
|
import { loggers as loggers3 } from "@peam-ai/logger";
|
|
277
|
+
import * as fsSync from "fs";
|
|
277
278
|
import * as fs from "fs/promises";
|
|
278
279
|
import * as path from "path";
|
|
279
280
|
var log3 = loggers3.search;
|
|
280
281
|
var FileBasedSearchIndexExporter = class {
|
|
281
282
|
constructor(options) {
|
|
282
283
|
this.cachedData = null;
|
|
283
|
-
|
|
284
|
+
var _a;
|
|
285
|
+
this.baseDir = (_a = options.baseDir) != null ? _a : process.cwd();
|
|
284
286
|
this.indexPath = options.indexPath;
|
|
285
287
|
}
|
|
286
288
|
getFullPath() {
|
|
@@ -318,10 +320,18 @@ var FileBasedSearchIndexExporter = class {
|
|
|
318
320
|
return data;
|
|
319
321
|
});
|
|
320
322
|
}
|
|
321
|
-
export(
|
|
322
|
-
return __async(this,
|
|
323
|
+
export(_0) {
|
|
324
|
+
return __async(this, arguments, function* (data, options = { override: true }) {
|
|
323
325
|
const fullPath = this.getFullPath();
|
|
324
326
|
try {
|
|
327
|
+
if (!(options == null ? void 0 : options.override)) {
|
|
328
|
+
try {
|
|
329
|
+
yield fs.access(fullPath);
|
|
330
|
+
log3.debug("Search index file already exists and override is false, skipping export:", fullPath);
|
|
331
|
+
return;
|
|
332
|
+
} catch (e) {
|
|
333
|
+
}
|
|
334
|
+
}
|
|
325
335
|
const dir = path.dirname(fullPath);
|
|
326
336
|
yield fs.mkdir(dir, { recursive: true });
|
|
327
337
|
yield fs.writeFile(fullPath, JSON.stringify(data, null, 2), "utf-8");
|
|
@@ -332,6 +342,26 @@ var FileBasedSearchIndexExporter = class {
|
|
|
332
342
|
}
|
|
333
343
|
});
|
|
334
344
|
}
|
|
345
|
+
exportSync(data, options = { override: true }) {
|
|
346
|
+
const fullPath = this.getFullPath();
|
|
347
|
+
try {
|
|
348
|
+
if (!(options == null ? void 0 : options.override)) {
|
|
349
|
+
try {
|
|
350
|
+
fsSync.accessSync(fullPath);
|
|
351
|
+
log3.debug("Search index file already exists and override is false, skipping export:", fullPath);
|
|
352
|
+
return;
|
|
353
|
+
} catch (e) {
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const dir = path.dirname(fullPath);
|
|
357
|
+
fsSync.mkdirSync(dir, { recursive: true });
|
|
358
|
+
fsSync.writeFileSync(fullPath, JSON.stringify(data, null, 2), "utf-8");
|
|
359
|
+
log3.debug("Search index saved to file:", fullPath, "with", data.keys.length, "keys");
|
|
360
|
+
} catch (error) {
|
|
361
|
+
log3.error("Failed to save search index to file:", fullPath, error);
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
335
365
|
};
|
|
336
366
|
|
|
337
367
|
// src/exporters/config.ts
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/searchEngine.ts","../src/textSearch.ts","../src/indexBuilder.ts","../src/exporters/FileBasedSearchIndexExporter.ts","../src/exporters/config.ts"],"sourcesContent":["import { loggers } from '@peam-ai/logger';\nimport type { StructuredPage } from '@peam-ai/parser';\nimport { TextSearch, type TextSearchOptions } from './textSearch';\nimport type { StructuredPageDocumentData } from './types';\n\nconst log = loggers.search;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface SearchEngineConfig {\n // Reserved for future configuration options\n}\n\nexport class SearchEngine {\n private textSearch: TextSearch;\n private initialized: boolean;\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n constructor(_config?: SearchEngineConfig) {\n this.textSearch = new TextSearch();\n this.initialized = false;\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n log.debug('Initializing search engine');\n await this.textSearch.initialize();\n this.initialized = true;\n }\n\n async addPage(path: string, content: StructuredPage): Promise<void> {\n if (!this.initialized) {\n throw new Error('Search engine not initialized. Call initialize() first.');\n }\n\n const document: StructuredPageDocumentData = {\n id: path,\n path,\n content,\n };\n\n await this.textSearch.addDocument(document);\n log.debug('Page added to search engine:', path);\n }\n\n async search(query: string, options: TextSearchOptions = {}): Promise<StructuredPageDocumentData[]> {\n if (!this.initialized) {\n throw new Error('Search engine not initialized. Call initialize() first.');\n }\n\n log.debug('Performing text search:', query);\n return this.textSearch.search(query, options);\n }\n\n count(): number {\n return this.textSearch.count();\n }\n\n getDocument(path: string) {\n return this.textSearch.getDocument(path);\n }\n\n getAllDocuments(limit?: number): StructuredPageDocumentData[] {\n return this.textSearch.getAllDocuments(limit);\n }\n\n clear(): void {\n this.textSearch.clear();\n }\n\n async export(handler: (key: string, data: string) => Promise<void>): Promise<{ keys: string[] }> {\n return this.textSearch.export(handler);\n }\n\n async import(handler: (key: string) => Promise<string>, keys: string[]): Promise<void> {\n await this.textSearch.import(handler, keys);\n this.initialized = true;\n log.debug('Search engine initialized from imported data');\n }\n}\n","import { loggers } from '@peam-ai/logger';\nimport { Charset, Document } from 'flexsearch';\nimport type { StructuredPageDocumentData } from './types';\n\nexport interface TextSearchOptions {\n limit?: number;\n offset?: number;\n suggest?: boolean;\n}\n\nconst PEAM_DOCUMENT_IDS_KEY = 'peam.documentIds';\nconst MAX_DOCUMENTS_RETRIEVE = 25;\nconst log = loggers.search;\n\nexport class TextSearch {\n private index: Document<StructuredPageDocumentData>;\n private initialized: boolean;\n private documentIds: Set<string>;\n\n constructor() {\n this.initialized = false;\n this.index = this.getIndex();\n this.documentIds = new Set();\n }\n\n private getIndex() {\n return new Document<StructuredPageDocumentData>({\n worker: false,\n document: {\n id: 'path',\n index: ['content:title', 'content:description', 'content:textContent', 'content:author', 'content:keywords'],\n store: true,\n },\n tokenize: 'forward',\n resolution: 9,\n context: {\n resolution: 3,\n depth: 2,\n bidirectional: true,\n },\n cache: 100,\n encoder: Charset.LatinExtra,\n });\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n log.debug('Text search already initialized');\n return;\n }\n\n this.initialized = true;\n }\n\n async addDocument(document: StructuredPageDocumentData): Promise<void> {\n if (!this.initialized) {\n throw new Error('TextSearch not initialized. Call initialize() first.');\n }\n\n log.debug('Adding document to text search:', document.path);\n\n this.index.add(document);\n this.documentIds.add(document.path);\n }\n\n async search(query: string, options: TextSearchOptions = {}): Promise<StructuredPageDocumentData[]> {\n if (!this.initialized) {\n throw new Error('TextSearch not initialized. Call initialize() first.');\n }\n\n const limit = options.limit || MAX_DOCUMENTS_RETRIEVE;\n const offset = options.offset || 0;\n\n log.debug('Searching for:', query);\n\n const results = await this.index.search(query, {\n limit: limit + offset,\n suggest: options.suggest,\n enrich: true,\n });\n\n const pathSet = new Set<string>();\n const documents: StructuredPageDocumentData[] = [];\n\n for (const fieldResults of results) {\n if (Array.isArray(fieldResults.result)) {\n for (const result of fieldResults.result) {\n const id = typeof result === 'object' && 'id' in result ? result.id : result;\n const doc = typeof result === 'object' && 'doc' in result ? result.doc : null;\n\n if (!pathSet.has(id as string) && doc) {\n pathSet.add(id as string);\n documents.push(doc);\n }\n }\n }\n }\n\n const pagedResults = documents.slice(offset, offset + limit);\n\n return pagedResults;\n }\n\n count(): number {\n return this.documentIds.size;\n }\n\n getDocument(path: string) {\n return this.index.get(path);\n }\n\n getAllDocuments(limit?: number): StructuredPageDocumentData[] {\n const documents: StructuredPageDocumentData[] = [];\n let count = 0;\n limit = limit || MAX_DOCUMENTS_RETRIEVE;\n\n for (const id of this.documentIds) {\n if (count >= limit) {\n break;\n }\n\n const doc = this.index.get(id);\n if (doc) {\n documents.push(doc);\n count++;\n }\n }\n\n log.debug('Retrieved documents from store (limit):', documents.length, limit);\n return documents;\n }\n\n clear(): void {\n this.index.clear();\n this.index = this.getIndex();\n this.documentIds.clear();\n }\n\n async export(handler: (key: string, data: string) => Promise<void>): Promise<{ keys: string[] }> {\n const keys: string[] = [];\n\n await handler(PEAM_DOCUMENT_IDS_KEY, JSON.stringify(Array.from(this.documentIds)));\n keys.push(PEAM_DOCUMENT_IDS_KEY);\n\n await this.index.export(async (key: string, data: string) => {\n keys.push(key);\n await handler(key, data);\n });\n\n log.debug('Exported keys:', keys.length);\n\n return { keys };\n }\n\n async import(handler: (key: string) => Promise<string>, keys: string[]): Promise<void> {\n const documentIdsData = await handler(PEAM_DOCUMENT_IDS_KEY);\n if (documentIdsData) {\n const parsed = typeof documentIdsData === 'string' ? JSON.parse(documentIdsData) : documentIdsData;\n this.documentIds = new Set(parsed);\n }\n\n for (const key of keys) {\n if (key === PEAM_DOCUMENT_IDS_KEY) {\n continue;\n }\n\n try {\n const data = await handler(key);\n if (data) {\n this.index.import(key, data);\n }\n } catch (error) {\n log.error('Error importing key:', key, error);\n }\n }\n\n this.initialized = true;\n log.debug('Import completed with keys:', keys.length);\n }\n}\n","import type { StructuredPage } from '@peam-ai/parser';\nimport { SearchEngine } from './searchEngine';\n\nexport interface PageToIndex {\n path: string;\n structuredPage: StructuredPage;\n}\n\nexport interface SearchIndexData {\n keys: string[];\n data: Record<string, string>;\n}\n\n/**\n * Build a search index from structured pages\n */\nexport async function buildSearchIndex(pages: PageToIndex[]): Promise<SearchIndexData> {\n const searchEngine = new SearchEngine();\n await searchEngine.initialize();\n\n for (const page of pages) {\n await searchEngine.addPage(page.path, page.structuredPage);\n }\n\n const exportedData: Record<string, string> = {};\n const result = await searchEngine.export(async (key, data) => {\n exportedData[key] = data;\n });\n\n return {\n keys: result.keys,\n data: exportedData,\n };\n}\n","import { loggers } from '@peam-ai/logger';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport type { SearchIndexData } from '../indexBuilder';\nimport type { SearchIndexExporter } from './SearchIndexExporter';\n\nconst log = loggers.search;\n\nexport interface FileBasedSearchIndexExporterOptions {\n /**\n * The directory where the index file is located\n */\n baseDir: string;\n\n /**\n * The path to the index file relative to baseDir\n */\n indexPath: string;\n}\n\n/**\n * File-based implementation of SearchIndexExporter\n * Reads and writes search index data to/from a JSON file\n */\nexport class FileBasedSearchIndexExporter implements SearchIndexExporter {\n private baseDir: string;\n private indexPath: string;\n private cachedData: SearchIndexData | null = null;\n\n constructor(options: FileBasedSearchIndexExporterOptions) {\n this.baseDir = options.baseDir;\n this.indexPath = options.indexPath;\n }\n\n private getFullPath(): string {\n return path.join(this.baseDir, this.indexPath);\n }\n\n private async loadData(): Promise<SearchIndexData | null> {\n if (this.cachedData) {\n return this.cachedData;\n }\n\n const fullPath = this.getFullPath();\n\n try {\n const fileContent = await fs.readFile(fullPath, 'utf-8');\n const data = JSON.parse(fileContent) as SearchIndexData;\n\n if (!data || !data.keys || !Array.isArray(data.keys) || !data.data) {\n log.warn('Invalid search index structure in file:', fullPath);\n return null;\n }\n\n if (data.keys.length === 0) {\n log.debug('Search index is empty:', fullPath);\n return null;\n }\n\n this.cachedData = data;\n log.debug('Search index loaded from file:', fullPath, 'with', data.keys.length, 'keys');\n return data;\n } catch (error) {\n log.error('Failed to load search index from file:', fullPath, error);\n return null;\n }\n }\n\n async import(): Promise<SearchIndexData | null> {\n const data = await this.loadData();\n return data;\n }\n\n async export(data: SearchIndexData): Promise<void> {\n const fullPath = this.getFullPath();\n\n try {\n const dir = path.dirname(fullPath);\n await fs.mkdir(dir, { recursive: true });\n\n await fs.writeFile(fullPath, JSON.stringify(data, null, 2), 'utf-8');\n\n log.debug('Search index saved to file:', fullPath, 'with', data.keys.length, 'keys');\n } catch (error) {\n log.error('Failed to save search index to file:', fullPath, error);\n throw error;\n }\n }\n}\n","import { FileBasedSearchIndexExporter, FileBasedSearchIndexExporterOptions } from './FileBasedSearchIndexExporter';\nimport { SearchIndexExporter } from './SearchIndexExporter';\n\nexport type SearchExporterConfig = {\n type: 'fileBased';\n config: FileBasedSearchIndexExporterOptions;\n};\n\n/**\n * Creates a SearchIndexExporter instance from a SearchExporterConfig\n */\nexport function createExporterFromConfig(exporterConfig: SearchExporterConfig): SearchIndexExporter {\n if (exporterConfig.type === 'fileBased') {\n return new FileBasedSearchIndexExporter({\n ...exporterConfig.config,\n });\n }\n\n throw new Error(`Unknown exporter type: ${exporterConfig.type}`);\n}\n"],"mappings":"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","names":["loggers","path","log","loggers","path","loggers","log","loggers"]}
|
|
1
|
+
{"version":3,"sources":["../src/searchEngine.ts","../src/textSearch.ts","../src/indexBuilder.ts","../src/exporters/FileBasedSearchIndexExporter.ts","../src/exporters/config.ts"],"sourcesContent":["import { loggers } from '@peam-ai/logger';\nimport type { StructuredPage } from '@peam-ai/parser';\nimport { TextSearch, type TextSearchOptions } from './textSearch';\nimport type { StructuredPageDocumentData } from './types';\n\nconst log = loggers.search;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface SearchEngineConfig {\n // Reserved for future configuration options\n}\n\nexport class SearchEngine {\n private textSearch: TextSearch;\n private initialized: boolean;\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n constructor(_config?: SearchEngineConfig) {\n this.textSearch = new TextSearch();\n this.initialized = false;\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n log.debug('Initializing search engine');\n await this.textSearch.initialize();\n this.initialized = true;\n }\n\n async addPage(path: string, content: StructuredPage): Promise<void> {\n if (!this.initialized) {\n throw new Error('Search engine not initialized. Call initialize() first.');\n }\n\n const document: StructuredPageDocumentData = {\n id: path,\n path,\n content,\n };\n\n await this.textSearch.addDocument(document);\n log.debug('Page added to search engine:', path);\n }\n\n async search(query: string, options: TextSearchOptions = {}): Promise<StructuredPageDocumentData[]> {\n if (!this.initialized) {\n throw new Error('Search engine not initialized. Call initialize() first.');\n }\n\n log.debug('Performing text search:', query);\n return this.textSearch.search(query, options);\n }\n\n count(): number {\n return this.textSearch.count();\n }\n\n getDocument(path: string) {\n return this.textSearch.getDocument(path);\n }\n\n getAllDocuments(limit?: number): StructuredPageDocumentData[] {\n return this.textSearch.getAllDocuments(limit);\n }\n\n clear(): void {\n this.textSearch.clear();\n }\n\n async export(handler: (key: string, data: string) => Promise<void>): Promise<{ keys: string[] }> {\n return this.textSearch.export(handler);\n }\n\n async import(handler: (key: string) => Promise<string>, keys: string[]): Promise<void> {\n await this.textSearch.import(handler, keys);\n this.initialized = true;\n log.debug('Search engine initialized from imported data');\n }\n}\n","import { loggers } from '@peam-ai/logger';\nimport { Charset, Document } from 'flexsearch';\nimport type { StructuredPageDocumentData } from './types';\n\nexport interface TextSearchOptions {\n limit?: number;\n offset?: number;\n suggest?: boolean;\n}\n\nconst PEAM_DOCUMENT_IDS_KEY = 'peam.documentIds';\nconst MAX_DOCUMENTS_RETRIEVE = 25;\nconst log = loggers.search;\n\nexport class TextSearch {\n private index: Document<StructuredPageDocumentData>;\n private initialized: boolean;\n private documentIds: Set<string>;\n\n constructor() {\n this.initialized = false;\n this.index = this.getIndex();\n this.documentIds = new Set();\n }\n\n private getIndex() {\n return new Document<StructuredPageDocumentData>({\n worker: false,\n document: {\n id: 'path',\n index: ['content:title', 'content:description', 'content:textContent', 'content:author', 'content:keywords'],\n store: true,\n },\n tokenize: 'forward',\n resolution: 9,\n context: {\n resolution: 3,\n depth: 2,\n bidirectional: true,\n },\n cache: 100,\n encoder: Charset.LatinExtra,\n });\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n log.debug('Text search already initialized');\n return;\n }\n\n this.initialized = true;\n }\n\n async addDocument(document: StructuredPageDocumentData): Promise<void> {\n if (!this.initialized) {\n throw new Error('TextSearch not initialized. Call initialize() first.');\n }\n\n log.debug('Adding document to text search:', document.path);\n\n this.index.add(document);\n this.documentIds.add(document.path);\n }\n\n async search(query: string, options: TextSearchOptions = {}): Promise<StructuredPageDocumentData[]> {\n if (!this.initialized) {\n throw new Error('TextSearch not initialized. Call initialize() first.');\n }\n\n const limit = options.limit || MAX_DOCUMENTS_RETRIEVE;\n const offset = options.offset || 0;\n\n log.debug('Searching for:', query);\n\n const results = await this.index.search(query, {\n limit: limit + offset,\n suggest: options.suggest,\n enrich: true,\n });\n\n const pathSet = new Set<string>();\n const documents: StructuredPageDocumentData[] = [];\n\n for (const fieldResults of results) {\n if (Array.isArray(fieldResults.result)) {\n for (const result of fieldResults.result) {\n const id = typeof result === 'object' && 'id' in result ? result.id : result;\n const doc = typeof result === 'object' && 'doc' in result ? result.doc : null;\n\n if (!pathSet.has(id as string) && doc) {\n pathSet.add(id as string);\n documents.push(doc);\n }\n }\n }\n }\n\n const pagedResults = documents.slice(offset, offset + limit);\n\n return pagedResults;\n }\n\n count(): number {\n return this.documentIds.size;\n }\n\n getDocument(path: string) {\n return this.index.get(path);\n }\n\n getAllDocuments(limit?: number): StructuredPageDocumentData[] {\n const documents: StructuredPageDocumentData[] = [];\n let count = 0;\n limit = limit || MAX_DOCUMENTS_RETRIEVE;\n\n for (const id of this.documentIds) {\n if (count >= limit) {\n break;\n }\n\n const doc = this.index.get(id);\n if (doc) {\n documents.push(doc);\n count++;\n }\n }\n\n log.debug('Retrieved documents from store (limit):', documents.length, limit);\n return documents;\n }\n\n clear(): void {\n this.index.clear();\n this.index = this.getIndex();\n this.documentIds.clear();\n }\n\n async export(handler: (key: string, data: string) => Promise<void>): Promise<{ keys: string[] }> {\n const keys: string[] = [];\n\n await handler(PEAM_DOCUMENT_IDS_KEY, JSON.stringify(Array.from(this.documentIds)));\n keys.push(PEAM_DOCUMENT_IDS_KEY);\n\n await this.index.export(async (key: string, data: string) => {\n keys.push(key);\n await handler(key, data);\n });\n\n log.debug('Exported keys:', keys.length);\n\n return { keys };\n }\n\n async import(handler: (key: string) => Promise<string>, keys: string[]): Promise<void> {\n const documentIdsData = await handler(PEAM_DOCUMENT_IDS_KEY);\n if (documentIdsData) {\n const parsed = typeof documentIdsData === 'string' ? JSON.parse(documentIdsData) : documentIdsData;\n this.documentIds = new Set(parsed);\n }\n\n for (const key of keys) {\n if (key === PEAM_DOCUMENT_IDS_KEY) {\n continue;\n }\n\n try {\n const data = await handler(key);\n if (data) {\n this.index.import(key, data);\n }\n } catch (error) {\n log.error('Error importing key:', key, error);\n }\n }\n\n this.initialized = true;\n log.debug('Import completed with keys:', keys.length);\n }\n}\n","import type { StructuredPage } from '@peam-ai/parser';\nimport { SearchEngine } from './searchEngine';\n\nexport interface PageToIndex {\n path: string;\n structuredPage: StructuredPage;\n}\n\nexport interface SearchIndexData {\n keys: string[];\n data: Record<string, string>;\n}\n\n/**\n * Build a search index from structured pages\n */\nexport async function buildSearchIndex(pages: PageToIndex[]): Promise<SearchIndexData> {\n const searchEngine = new SearchEngine();\n await searchEngine.initialize();\n\n for (const page of pages) {\n await searchEngine.addPage(page.path, page.structuredPage);\n }\n\n const exportedData: Record<string, string> = {};\n const result = await searchEngine.export(async (key, data) => {\n exportedData[key] = data;\n });\n\n return {\n keys: result.keys,\n data: exportedData,\n };\n}\n","import { loggers } from '@peam-ai/logger';\nimport * as fsSync from 'fs';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport type { SearchIndexData } from '../indexBuilder';\nimport type { ExportOptions, SearchIndexExporter } from './SearchIndexExporter';\n\nconst log = loggers.search;\n\nexport interface FileBasedSearchIndexExporterOptions {\n /**\n * The path to the index file relative to baseDir\n */\n indexPath: string;\n\n /**\n * The directory where the index file is located\n */\n baseDir?: string;\n}\n\n/**\n * File-based implementation of SearchIndexExporter\n * Reads and writes search index data to/from a JSON file\n */\nexport class FileBasedSearchIndexExporter implements SearchIndexExporter {\n private baseDir: string;\n private indexPath: string;\n private cachedData: SearchIndexData | null = null;\n\n constructor(options: FileBasedSearchIndexExporterOptions) {\n this.baseDir = options.baseDir ?? process.cwd();\n this.indexPath = options.indexPath;\n }\n\n private getFullPath(): string {\n return path.join(this.baseDir, this.indexPath);\n }\n\n private async loadData(): Promise<SearchIndexData | null> {\n if (this.cachedData) {\n return this.cachedData;\n }\n\n const fullPath = this.getFullPath();\n\n try {\n const fileContent = await fs.readFile(fullPath, 'utf-8');\n const data = JSON.parse(fileContent) as SearchIndexData;\n\n if (!data || !data.keys || !Array.isArray(data.keys) || !data.data) {\n log.warn('Invalid search index structure in file:', fullPath);\n return null;\n }\n\n if (data.keys.length === 0) {\n log.debug('Search index is empty:', fullPath);\n return null;\n }\n\n this.cachedData = data;\n log.debug('Search index loaded from file:', fullPath, 'with', data.keys.length, 'keys');\n return data;\n } catch (error) {\n log.error('Failed to load search index from file:', fullPath, error);\n return null;\n }\n }\n\n async import(): Promise<SearchIndexData | null> {\n const data = await this.loadData();\n return data;\n }\n\n async export(data: SearchIndexData, options: ExportOptions = { override: true }): Promise<void> {\n const fullPath = this.getFullPath();\n\n try {\n if (!options?.override) {\n try {\n await fs.access(fullPath);\n log.debug('Search index file already exists and override is false, skipping export:', fullPath);\n return;\n } catch {\n // noop\n }\n }\n\n const dir = path.dirname(fullPath);\n await fs.mkdir(dir, { recursive: true });\n\n await fs.writeFile(fullPath, JSON.stringify(data, null, 2), 'utf-8');\n\n log.debug('Search index saved to file:', fullPath, 'with', data.keys.length, 'keys');\n } catch (error) {\n log.error('Failed to save search index to file:', fullPath, error);\n throw error;\n }\n }\n\n exportSync(data: SearchIndexData, options: ExportOptions = { override: true }): void {\n const fullPath = this.getFullPath();\n\n try {\n if (!options?.override) {\n try {\n fsSync.accessSync(fullPath);\n log.debug('Search index file already exists and override is false, skipping export:', fullPath);\n return;\n } catch {\n // noop\n }\n }\n\n const dir = path.dirname(fullPath);\n fsSync.mkdirSync(dir, { recursive: true });\n\n fsSync.writeFileSync(fullPath, JSON.stringify(data, null, 2), 'utf-8');\n\n log.debug('Search index saved to file:', fullPath, 'with', data.keys.length, 'keys');\n } catch (error) {\n log.error('Failed to save search index to file:', fullPath, error);\n throw error;\n }\n }\n}\n","import { FileBasedSearchIndexExporter, FileBasedSearchIndexExporterOptions } from './FileBasedSearchIndexExporter';\nimport { SearchIndexExporter } from './SearchIndexExporter';\n\nexport type SearchExporterConfig = {\n type: 'fileBased';\n config: FileBasedSearchIndexExporterOptions;\n};\n\n/**\n * Creates a SearchIndexExporter instance from a SearchExporterConfig\n */\nexport function createExporterFromConfig(exporterConfig: SearchExporterConfig): SearchIndexExporter {\n if (exporterConfig.type === 'fileBased') {\n return new FileBasedSearchIndexExporter({\n ...exporterConfig.config,\n });\n }\n\n throw new Error(`Unknown exporter type: ${exporterConfig.type}`);\n}\n"],"mappings":"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","names":["loggers","path","log","loggers","path","loggers","log","loggers"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peam-ai/search",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Vector database and search functionality for Peam",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"flexsearch": "^0.8.212",
|
|
32
|
-
"@peam-ai/logger": "0.1.
|
|
33
|
-
"@peam-ai/parser": "0.1.
|
|
32
|
+
"@peam-ai/logger": "0.1.3",
|
|
33
|
+
"@peam-ai/parser": "0.1.3"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^22.10.2",
|