@peam-ai/search 0.1.3 → 0.1.4

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 CHANGED
@@ -16,7 +16,7 @@ declare function buildSearchIndex(pages: PageToIndex[]): Promise<SearchIndexData
16
16
  type StructuredPageDocumentData = {
17
17
  id: string;
18
18
  path: string;
19
- content: StructuredPage;
19
+ content: Pick<StructuredPage, 'title' | 'description' | 'author' | 'keywords' | 'content' | 'language' | 'publishedTime'>;
20
20
  [key: string]: any;
21
21
  };
22
22
 
package/dist/index.d.ts CHANGED
@@ -16,7 +16,7 @@ declare function buildSearchIndex(pages: PageToIndex[]): Promise<SearchIndexData
16
16
  type StructuredPageDocumentData = {
17
17
  id: string;
18
18
  path: string;
19
- content: StructuredPage;
19
+ content: Pick<StructuredPage, 'title' | 'description' | 'author' | 'keywords' | 'content' | 'language' | 'publishedTime'>;
20
20
  [key: string]: any;
21
21
  };
22
22
 
package/dist/index.js CHANGED
@@ -92,8 +92,8 @@ var TextSearch = class {
92
92
  worker: false,
93
93
  document: {
94
94
  id: "path",
95
- index: ["content:title", "content:description", "content:textContent", "content:author", "content:keywords"],
96
- store: true
95
+ index: ["content:title", "content:description", "content:content", "content:keywords", "content:author"],
96
+ store: ["id", "path", "content:title", "content:content", "content:language"]
97
97
  },
98
98
  tokenize: "forward",
99
99
  resolution: 9,
@@ -126,16 +126,22 @@ var TextSearch = class {
126
126
  });
127
127
  }
128
128
  search(_0) {
129
- return __async(this, arguments, function* (query, options = {}) {
129
+ return __async(this, arguments, function* (query, options = {
130
+ limit: MAX_DOCUMENTS_RETRIEVE,
131
+ offset: 0,
132
+ suggest: true
133
+ }) {
130
134
  if (!this.initialized) {
131
135
  throw new Error("TextSearch not initialized. Call initialize() first.");
132
136
  }
133
137
  const limit = options.limit || MAX_DOCUMENTS_RETRIEVE;
134
138
  const offset = options.offset || 0;
139
+ const suggest = options.suggest || true;
135
140
  log.debug("Searching for:", query);
136
141
  const results = yield this.index.search(query, {
137
- limit: limit + offset,
138
- suggest: options.suggest,
142
+ offset,
143
+ limit,
144
+ suggest,
139
145
  enrich: true
140
146
  });
141
147
  const pathSet = /* @__PURE__ */ new Set();
@@ -152,8 +158,7 @@ var TextSearch = class {
152
158
  }
153
159
  }
154
160
  }
155
- const pagedResults = documents.slice(offset, offset + limit);
156
- return pagedResults;
161
+ return documents;
157
162
  });
158
163
  }
159
164
  count() {
@@ -249,7 +254,15 @@ var SearchEngine = class {
249
254
  const document = {
250
255
  id: path2,
251
256
  path: path2,
252
- content
257
+ content: {
258
+ title: content.title,
259
+ description: content.description,
260
+ content: content.markdownContent ? content.markdownContent : content.textContent,
261
+ author: content.author,
262
+ keywords: content.keywords,
263
+ language: content.language,
264
+ publishedTime: content.publishedTime
265
+ }
253
266
  };
254
267
  yield this.textSearch.addDocument(document);
255
268
  log2.debug("Page added to search engine:", path2);
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 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"]}
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 title: content.title,\n description: content.description,\n content: content.markdownContent ? content.markdownContent : content.textContent,\n author: content.author,\n keywords: content.keywords,\n language: content.language,\n publishedTime: content.publishedTime,\n },\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:content', 'content:keywords', 'content:author'],\n store: ['id', 'path', 'content:title', 'content:content', 'content:language'],\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(\n query: string,\n options: TextSearchOptions = {\n limit: MAX_DOCUMENTS_RETRIEVE,\n offset: 0,\n suggest: true,\n }\n ): 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 const suggest = options.suggest || true;\n\n log.debug('Searching for:', query);\n\n const results = await this.index.search(query, {\n offset,\n limit,\n 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 return documents;\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
@@ -55,8 +55,8 @@ var TextSearch = class {
55
55
  worker: false,
56
56
  document: {
57
57
  id: "path",
58
- index: ["content:title", "content:description", "content:textContent", "content:author", "content:keywords"],
59
- store: true
58
+ index: ["content:title", "content:description", "content:content", "content:keywords", "content:author"],
59
+ store: ["id", "path", "content:title", "content:content", "content:language"]
60
60
  },
61
61
  tokenize: "forward",
62
62
  resolution: 9,
@@ -89,16 +89,22 @@ var TextSearch = class {
89
89
  });
90
90
  }
91
91
  search(_0) {
92
- return __async(this, arguments, function* (query, options = {}) {
92
+ return __async(this, arguments, function* (query, options = {
93
+ limit: MAX_DOCUMENTS_RETRIEVE,
94
+ offset: 0,
95
+ suggest: true
96
+ }) {
93
97
  if (!this.initialized) {
94
98
  throw new Error("TextSearch not initialized. Call initialize() first.");
95
99
  }
96
100
  const limit = options.limit || MAX_DOCUMENTS_RETRIEVE;
97
101
  const offset = options.offset || 0;
102
+ const suggest = options.suggest || true;
98
103
  log.debug("Searching for:", query);
99
104
  const results = yield this.index.search(query, {
100
- limit: limit + offset,
101
- suggest: options.suggest,
105
+ offset,
106
+ limit,
107
+ suggest,
102
108
  enrich: true
103
109
  });
104
110
  const pathSet = /* @__PURE__ */ new Set();
@@ -115,8 +121,7 @@ var TextSearch = class {
115
121
  }
116
122
  }
117
123
  }
118
- const pagedResults = documents.slice(offset, offset + limit);
119
- return pagedResults;
124
+ return documents;
120
125
  });
121
126
  }
122
127
  count() {
@@ -212,7 +217,15 @@ var SearchEngine = class {
212
217
  const document = {
213
218
  id: path2,
214
219
  path: path2,
215
- content
220
+ content: {
221
+ title: content.title,
222
+ description: content.description,
223
+ content: content.markdownContent ? content.markdownContent : content.textContent,
224
+ author: content.author,
225
+ keywords: content.keywords,
226
+ language: content.language,
227
+ publishedTime: content.publishedTime
228
+ }
216
229
  };
217
230
  yield this.textSearch.addDocument(document);
218
231
  log2.debug("Page added to search engine:", path2);
@@ -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 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,WAAAA,gBAAe;;;ACAxB,SAAS,eAAe;AACxB,SAAS,SAAS,gBAAgB;AASlC,IAAM,wBAAwB;AAC9B,IAAM,yBAAyB;AAC/B,IAAM,MAAM,QAAQ;AAEb,IAAM,aAAN,MAAiB;AAAA,EAKtB,cAAc;AACZ,SAAK,cAAc;AACnB,SAAK,QAAQ,KAAK,SAAS;AAC3B,SAAK,cAAc,oBAAI,IAAI;AAAA,EAC7B;AAAA,EAEQ,WAAW;AACjB,WAAO,IAAI,SAAqC;AAAA,MAC9C,QAAQ;AAAA,MACR,UAAU;AAAA,QACR,IAAI;AAAA,QACJ,OAAO,CAAC,iBAAiB,uBAAuB,uBAAuB,kBAAkB,kBAAkB;AAAA,QAC3G,OAAO;AAAA,MACT;AAAA,MACA,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,SAAS;AAAA,QACP,YAAY;AAAA,QACZ,OAAO;AAAA,QACP,eAAe;AAAA,MACjB;AAAA,MACA,OAAO;AAAA,MACP,SAAS,QAAQ;AAAA,IACnB,CAAC;AAAA,EACH;AAAA,EAEM,aAA4B;AAAA;AAChC,UAAI,KAAK,aAAa;AACpB,YAAI,MAAM,iCAAiC;AAC3C;AAAA,MACF;AAEA,WAAK,cAAc;AAAA,IACrB;AAAA;AAAA,EAEM,YAAY,UAAqD;AAAA;AACrE,UAAI,CAAC,KAAK,aAAa;AACrB,cAAM,IAAI,MAAM,sDAAsD;AAAA,MACxE;AAEA,UAAI,MAAM,mCAAmC,SAAS,IAAI;AAE1D,WAAK,MAAM,IAAI,QAAQ;AACvB,WAAK,YAAY,IAAI,SAAS,IAAI;AAAA,IACpC;AAAA;AAAA,EAEM,OAAO,IAAuF;AAAA,+CAAvF,OAAe,UAA6B,CAAC,GAA0C;AAClG,UAAI,CAAC,KAAK,aAAa;AACrB,cAAM,IAAI,MAAM,sDAAsD;AAAA,MACxE;AAEA,YAAM,QAAQ,QAAQ,SAAS;AAC/B,YAAM,SAAS,QAAQ,UAAU;AAEjC,UAAI,MAAM,kBAAkB,KAAK;AAEjC,YAAM,UAAU,MAAM,KAAK,MAAM,OAAO,OAAO;AAAA,QAC7C,OAAO,QAAQ;AAAA,QACf,SAAS,QAAQ;AAAA,QACjB,QAAQ;AAAA,MACV,CAAC;AAED,YAAM,UAAU,oBAAI,IAAY;AAChC,YAAM,YAA0C,CAAC;AAEjD,iBAAW,gBAAgB,SAAS;AAClC,YAAI,MAAM,QAAQ,aAAa,MAAM,GAAG;AACtC,qBAAW,UAAU,aAAa,QAAQ;AACxC,kBAAM,KAAK,OAAO,WAAW,YAAY,QAAQ,SAAS,OAAO,KAAK;AACtE,kBAAM,MAAM,OAAO,WAAW,YAAY,SAAS,SAAS,OAAO,MAAM;AAEzE,gBAAI,CAAC,QAAQ,IAAI,EAAY,KAAK,KAAK;AACrC,sBAAQ,IAAI,EAAY;AACxB,wBAAU,KAAK,GAAG;AAAA,YACpB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,YAAM,eAAe,UAAU,MAAM,QAAQ,SAAS,KAAK;AAE3D,aAAO;AAAA,IACT;AAAA;AAAA,EAEA,QAAgB;AACd,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA,EAEA,YAAYC,OAAc;AACxB,WAAO,KAAK,MAAM,IAAIA,KAAI;AAAA,EAC5B;AAAA,EAEA,gBAAgB,OAA8C;AAC5D,UAAM,YAA0C,CAAC;AACjD,QAAI,QAAQ;AACZ,YAAQ,SAAS;AAEjB,eAAW,MAAM,KAAK,aAAa;AACjC,UAAI,SAAS,OAAO;AAClB;AAAA,MACF;AAEA,YAAM,MAAM,KAAK,MAAM,IAAI,EAAE;AAC7B,UAAI,KAAK;AACP,kBAAU,KAAK,GAAG;AAClB;AAAA,MACF;AAAA,IACF;AAEA,QAAI,MAAM,2CAA2C,UAAU,QAAQ,KAAK;AAC5E,WAAO;AAAA,EACT;AAAA,EAEA,QAAc;AACZ,SAAK,MAAM,MAAM;AACjB,SAAK,QAAQ,KAAK,SAAS;AAC3B,SAAK,YAAY,MAAM;AAAA,EACzB;AAAA,EAEM,OAAO,SAAoF;AAAA;AAC/F,YAAM,OAAiB,CAAC;AAExB,YAAM,QAAQ,uBAAuB,KAAK,UAAU,MAAM,KAAK,KAAK,WAAW,CAAC,CAAC;AACjF,WAAK,KAAK,qBAAqB;AAE/B,YAAM,KAAK,MAAM,OAAO,CAAO,KAAa,SAAiB;AAC3D,aAAK,KAAK,GAAG;AACb,cAAM,QAAQ,KAAK,IAAI;AAAA,MACzB,EAAC;AAED,UAAI,MAAM,kBAAkB,KAAK,MAAM;AAEvC,aAAO,EAAE,KAAK;AAAA,IAChB;AAAA;AAAA,EAEM,OAAO,SAA2C,MAA+B;AAAA;AACrF,YAAM,kBAAkB,MAAM,QAAQ,qBAAqB;AAC3D,UAAI,iBAAiB;AACnB,cAAM,SAAS,OAAO,oBAAoB,WAAW,KAAK,MAAM,eAAe,IAAI;AACnF,aAAK,cAAc,IAAI,IAAI,MAAM;AAAA,MACnC;AAEA,iBAAW,OAAO,MAAM;AACtB,YAAI,QAAQ,uBAAuB;AACjC;AAAA,QACF;AAEA,YAAI;AACF,gBAAM,OAAO,MAAM,QAAQ,GAAG;AAC9B,cAAI,MAAM;AACR,iBAAK,MAAM,OAAO,KAAK,IAAI;AAAA,UAC7B;AAAA,QACF,SAAS,OAAO;AACd,cAAI,MAAM,wBAAwB,KAAK,KAAK;AAAA,QAC9C;AAAA,MACF;AAEA,WAAK,cAAc;AACnB,UAAI,MAAM,+BAA+B,KAAK,MAAM;AAAA,IACtD;AAAA;AACF;;;AD9KA,IAAMC,OAAMC,SAAQ;AAOb,IAAM,eAAN,MAAmB;AAAA;AAAA,EAKxB,YAAY,SAA8B;AACxC,SAAK,aAAa,IAAI,WAAW;AACjC,SAAK,cAAc;AAAA,EACrB;AAAA,EAEM,aAA4B;AAAA;AAChC,UAAI,KAAK,aAAa;AACpB;AAAA,MACF;AAEA,MAAAD,KAAI,MAAM,4BAA4B;AACtC,YAAM,KAAK,WAAW,WAAW;AACjC,WAAK,cAAc;AAAA,IACrB;AAAA;AAAA,EAEM,QAAQE,OAAc,SAAwC;AAAA;AAClE,UAAI,CAAC,KAAK,aAAa;AACrB,cAAM,IAAI,MAAM,yDAAyD;AAAA,MAC3E;AAEA,YAAM,WAAuC;AAAA,QAC3C,IAAIA;AAAA,QACJ,MAAAA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,KAAK,WAAW,YAAY,QAAQ;AAC1C,MAAAF,KAAI,MAAM,gCAAgCE,KAAI;AAAA,IAChD;AAAA;AAAA,EAEM,OAAO,IAAuF;AAAA,+CAAvF,OAAe,UAA6B,CAAC,GAA0C;AAClG,UAAI,CAAC,KAAK,aAAa;AACrB,cAAM,IAAI,MAAM,yDAAyD;AAAA,MAC3E;AAEA,MAAAF,KAAI,MAAM,2BAA2B,KAAK;AAC1C,aAAO,KAAK,WAAW,OAAO,OAAO,OAAO;AAAA,IAC9C;AAAA;AAAA,EAEA,QAAgB;AACd,WAAO,KAAK,WAAW,MAAM;AAAA,EAC/B;AAAA,EAEA,YAAYE,OAAc;AACxB,WAAO,KAAK,WAAW,YAAYA,KAAI;AAAA,EACzC;AAAA,EAEA,gBAAgB,OAA8C;AAC5D,WAAO,KAAK,WAAW,gBAAgB,KAAK;AAAA,EAC9C;AAAA,EAEA,QAAc;AACZ,SAAK,WAAW,MAAM;AAAA,EACxB;AAAA,EAEM,OAAO,SAAoF;AAAA;AAC/F,aAAO,KAAK,WAAW,OAAO,OAAO;AAAA,IACvC;AAAA;AAAA,EAEM,OAAO,SAA2C,MAA+B;AAAA;AACrF,YAAM,KAAK,WAAW,OAAO,SAAS,IAAI;AAC1C,WAAK,cAAc;AACnB,MAAAF,KAAI,MAAM,8CAA8C;AAAA,IAC1D;AAAA;AACF;;;AEjEA,SAAsB,iBAAiB,OAAgD;AAAA;AACrF,UAAM,eAAe,IAAI,aAAa;AACtC,UAAM,aAAa,WAAW;AAE9B,eAAW,QAAQ,OAAO;AACxB,YAAM,aAAa,QAAQ,KAAK,MAAM,KAAK,cAAc;AAAA,IAC3D;AAEA,UAAM,eAAuC,CAAC;AAC9C,UAAM,SAAS,MAAM,aAAa,OAAO,CAAO,KAAK,SAAS;AAC5D,mBAAa,GAAG,IAAI;AAAA,IACtB,EAAC;AAED,WAAO;AAAA,MACL,MAAM,OAAO;AAAA,MACb,MAAM;AAAA,IACR;AAAA,EACF;AAAA;;;ACjCA,SAAS,WAAAG,gBAAe;AACxB,YAAY,YAAY;AACxB,YAAY,QAAQ;AACpB,YAAY,UAAU;AAItB,IAAMC,OAAMC,SAAQ;AAkBb,IAAM,+BAAN,MAAkE;AAAA,EAKvE,YAAY,SAA8C;AAF1D,SAAQ,aAAqC;AA5B/C;AA+BI,SAAK,WAAU,aAAQ,YAAR,YAAmB,QAAQ,IAAI;AAC9C,SAAK,YAAY,QAAQ;AAAA,EAC3B;AAAA,EAEQ,cAAsB;AAC5B,WAAY,UAAK,KAAK,SAAS,KAAK,SAAS;AAAA,EAC/C;AAAA,EAEc,WAA4C;AAAA;AACxD,UAAI,KAAK,YAAY;AACnB,eAAO,KAAK;AAAA,MACd;AAEA,YAAM,WAAW,KAAK,YAAY;AAElC,UAAI;AACF,cAAM,cAAc,MAAS,YAAS,UAAU,OAAO;AACvD,cAAM,OAAO,KAAK,MAAM,WAAW;AAEnC,YAAI,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAAI,KAAK,CAAC,KAAK,MAAM;AAClE,UAAAD,KAAI,KAAK,2CAA2C,QAAQ;AAC5D,iBAAO;AAAA,QACT;AAEA,YAAI,KAAK,KAAK,WAAW,GAAG;AAC1B,UAAAA,KAAI,MAAM,0BAA0B,QAAQ;AAC5C,iBAAO;AAAA,QACT;AAEA,aAAK,aAAa;AAClB,QAAAA,KAAI,MAAM,kCAAkC,UAAU,QAAQ,KAAK,KAAK,QAAQ,MAAM;AACtF,eAAO;AAAA,MACT,SAAS,OAAO;AACd,QAAAA,KAAI,MAAM,0CAA0C,UAAU,KAAK;AACnE,eAAO;AAAA,MACT;AAAA,IACF;AAAA;AAAA,EAEM,SAA0C;AAAA;AAC9C,YAAM,OAAO,MAAM,KAAK,SAAS;AACjC,aAAO;AAAA,IACT;AAAA;AAAA,EAEM,OAAO,IAAmF;AAAA,+CAAnF,MAAuB,UAAyB,EAAE,UAAU,KAAK,GAAkB;AAC9F,YAAM,WAAW,KAAK,YAAY;AAElC,UAAI;AACF,YAAI,EAAC,mCAAS,WAAU;AACtB,cAAI;AACF,kBAAS,UAAO,QAAQ;AACxB,YAAAA,KAAI,MAAM,4EAA4E,QAAQ;AAC9F;AAAA,UACF,SAAQ;AAAA,UAER;AAAA,QACF;AAEA,cAAM,MAAW,aAAQ,QAAQ;AACjC,cAAS,SAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAEvC,cAAS,aAAU,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,OAAO;AAEnE,QAAAA,KAAI,MAAM,+BAA+B,UAAU,QAAQ,KAAK,KAAK,QAAQ,MAAM;AAAA,MACrF,SAAS,OAAO;AACd,QAAAA,KAAI,MAAM,wCAAwC,UAAU,KAAK;AACjE,cAAM;AAAA,MACR;AAAA,IACF;AAAA;AAAA,EAEA,WAAW,MAAuB,UAAyB,EAAE,UAAU,KAAK,GAAS;AACnF,UAAM,WAAW,KAAK,YAAY;AAElC,QAAI;AACF,UAAI,EAAC,mCAAS,WAAU;AACtB,YAAI;AACF,UAAO,kBAAW,QAAQ;AAC1B,UAAAA,KAAI,MAAM,4EAA4E,QAAQ;AAC9F;AAAA,QACF,SAAQ;AAAA,QAER;AAAA,MACF;AAEA,YAAM,MAAW,aAAQ,QAAQ;AACjC,MAAO,iBAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAEzC,MAAO,qBAAc,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,OAAO;AAErE,MAAAA,KAAI,MAAM,+BAA+B,UAAU,QAAQ,KAAK,KAAK,QAAQ,MAAM;AAAA,IACrF,SAAS,OAAO;AACd,MAAAA,KAAI,MAAM,wCAAwC,UAAU,KAAK;AACjE,YAAM;AAAA,IACR;AAAA,EACF;AACF;;;AClHO,SAAS,yBAAyB,gBAA2D;AAClG,MAAI,eAAe,SAAS,aAAa;AACvC,WAAO,IAAI,6BAA6B,mBACnC,eAAe,OACnB;AAAA,EACH;AAEA,QAAM,IAAI,MAAM,0BAA0B,eAAe,IAAI,EAAE;AACjE;","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 title: content.title,\n description: content.description,\n content: content.markdownContent ? content.markdownContent : content.textContent,\n author: content.author,\n keywords: content.keywords,\n language: content.language,\n publishedTime: content.publishedTime,\n },\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:content', 'content:keywords', 'content:author'],\n store: ['id', 'path', 'content:title', 'content:content', 'content:language'],\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(\n query: string,\n options: TextSearchOptions = {\n limit: MAX_DOCUMENTS_RETRIEVE,\n offset: 0,\n suggest: true,\n }\n ): 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 const suggest = options.suggest || true;\n\n log.debug('Searching for:', query);\n\n const results = await this.index.search(query, {\n offset,\n limit,\n 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 return documents;\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",
3
+ "version": "0.1.4",
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.3",
33
- "@peam-ai/parser": "0.1.3"
32
+ "@peam-ai/logger": "0.1.4",
33
+ "@peam-ai/parser": "0.1.4"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^22.10.2",
@@ -41,9 +41,8 @@
41
41
  "build": "tsup",
42
42
  "dev": "tsup --watch",
43
43
  "clean": "rm -rf dist",
44
- "lint": "eslint .",
45
- "test:eslint": "eslint \"src/**/*.ts*\"",
44
+ "test:lint": "eslint \"src/**/*.ts*\"",
46
45
  "test:prettier": "prettier --check \"src/**/*.ts*\"",
47
- "test:jest": "jest"
46
+ "test:unit": "vitest run"
48
47
  }
49
48
  }