@silicajs/search 0.1.0
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/README.md +7 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +176 -0
- package/dist/index.js.map +1 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @silicajs/search
|
|
2
|
+
|
|
3
|
+
Server-side FlexSearch helpers for Silica.
|
|
4
|
+
|
|
5
|
+
The build step creates a serialized `Document` index and records bundle. The generated Next.js `/api/search` route lazy-loads that artifact into a process-level singleton and returns ranked results with excerpts.
|
|
6
|
+
|
|
7
|
+
Includes a benchmark helper for cold/warm search latency checks.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Document } from 'flexsearch';
|
|
2
|
+
|
|
3
|
+
type SearchRecord = {
|
|
4
|
+
id: string;
|
|
5
|
+
slug: string;
|
|
6
|
+
title: string;
|
|
7
|
+
content: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
tags: string[];
|
|
10
|
+
};
|
|
11
|
+
type StoredSearchRecord = Omit<SearchRecord, "content"> & {
|
|
12
|
+
excerpt: string;
|
|
13
|
+
};
|
|
14
|
+
type SerializedSearchIndex = {
|
|
15
|
+
version: 1;
|
|
16
|
+
config: SearchIndexConfig;
|
|
17
|
+
records: StoredSearchRecord[];
|
|
18
|
+
exported: Record<string, string>;
|
|
19
|
+
builtAt: string;
|
|
20
|
+
};
|
|
21
|
+
type SearchIndexConfig = {
|
|
22
|
+
tokenize: "forward" | "full" | "reverse" | "strict";
|
|
23
|
+
document: {
|
|
24
|
+
id: "id";
|
|
25
|
+
index: ["title", "content", "tags"];
|
|
26
|
+
store: ["slug", "title", "description", "tags"];
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
type SearchResult = {
|
|
30
|
+
slug: string;
|
|
31
|
+
title: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
tags: string[];
|
|
34
|
+
excerpt: string;
|
|
35
|
+
score: number;
|
|
36
|
+
};
|
|
37
|
+
type SearchQueryOptions = {
|
|
38
|
+
limit?: number;
|
|
39
|
+
tags?: string[];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
type SearchBenchmarkOptions = SearchQueryOptions & {
|
|
43
|
+
query: string;
|
|
44
|
+
warmRuns?: number;
|
|
45
|
+
};
|
|
46
|
+
type SearchBenchmarkResult = {
|
|
47
|
+
coldMs: number;
|
|
48
|
+
warmMs: number;
|
|
49
|
+
warmRuns: number;
|
|
50
|
+
resultCount: number;
|
|
51
|
+
};
|
|
52
|
+
declare function benchmarkSearchIndex(artifact: SerializedSearchIndex, { query, warmRuns, ...queryOptions }: SearchBenchmarkOptions): Promise<SearchBenchmarkResult>;
|
|
53
|
+
|
|
54
|
+
declare const DEFAULT_SEARCH_CONFIG: SearchIndexConfig;
|
|
55
|
+
declare function createSearchDocument(config?: SearchIndexConfig): Document;
|
|
56
|
+
declare function buildSearchIndex(records: SearchRecord[]): Promise<SerializedSearchIndex>;
|
|
57
|
+
|
|
58
|
+
declare function normalizeSearchText(value: string): string;
|
|
59
|
+
declare function makeExcerpt(content: string, query: string, maxLength?: number): string;
|
|
60
|
+
|
|
61
|
+
type LoadedSearchIndex = {
|
|
62
|
+
document: ReturnType<typeof createSearchDocument>;
|
|
63
|
+
artifact: SerializedSearchIndex;
|
|
64
|
+
recordsById: Map<string, StoredSearchRecord>;
|
|
65
|
+
};
|
|
66
|
+
declare function hydrateSearchIndex(artifact: SerializedSearchIndex): Promise<LoadedSearchIndex>;
|
|
67
|
+
declare function loadSearchIndex(artifactPath: string): Promise<LoadedSearchIndex>;
|
|
68
|
+
|
|
69
|
+
declare function querySearchIndex(loaded: LoadedSearchIndex, query: string, options?: SearchQueryOptions): SearchResult[];
|
|
70
|
+
|
|
71
|
+
export { DEFAULT_SEARCH_CONFIG, type LoadedSearchIndex, type SearchBenchmarkOptions, type SearchBenchmarkResult, type SearchIndexConfig, type SearchQueryOptions, type SearchRecord, type SearchResult, type SerializedSearchIndex, benchmarkSearchIndex, buildSearchIndex, createSearchDocument, hydrateSearchIndex, loadSearchIndex, makeExcerpt, normalizeSearchText, querySearchIndex };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// src/benchmark.ts
|
|
2
|
+
import { performance } from "perf_hooks";
|
|
3
|
+
|
|
4
|
+
// src/load.ts
|
|
5
|
+
import { readFile } from "fs/promises";
|
|
6
|
+
|
|
7
|
+
// src/build.ts
|
|
8
|
+
import { Document } from "flexsearch";
|
|
9
|
+
|
|
10
|
+
// src/excerpt.ts
|
|
11
|
+
var WORD_BOUNDARY = /\s+/g;
|
|
12
|
+
function normalizeSearchText(value) {
|
|
13
|
+
return value.replace(/\s+/g, " ").trim();
|
|
14
|
+
}
|
|
15
|
+
function makeExcerpt(content, query, maxLength = 180) {
|
|
16
|
+
const normalized = normalizeSearchText(content);
|
|
17
|
+
if (normalized.length <= maxLength) return normalized;
|
|
18
|
+
const firstTerm = query.toLowerCase().split(WORD_BOUNDARY).find((term) => term.length > 1);
|
|
19
|
+
const matchIndex = firstTerm ? normalized.toLowerCase().indexOf(firstTerm) : -1;
|
|
20
|
+
const center = matchIndex >= 0 ? matchIndex : 0;
|
|
21
|
+
const half = Math.floor(maxLength / 2);
|
|
22
|
+
const start = Math.max(0, center - half);
|
|
23
|
+
const end = Math.min(normalized.length, start + maxLength);
|
|
24
|
+
const excerpt = normalized.slice(start, end).trim();
|
|
25
|
+
return `${start > 0 ? "\u2026" : ""}${excerpt}${end < normalized.length ? "\u2026" : ""}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/build.ts
|
|
29
|
+
var DEFAULT_SEARCH_CONFIG = {
|
|
30
|
+
tokenize: "forward",
|
|
31
|
+
document: {
|
|
32
|
+
id: "id",
|
|
33
|
+
index: ["title", "content", "tags"],
|
|
34
|
+
store: ["slug", "title", "description", "tags"]
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
function createSearchDocument(config = DEFAULT_SEARCH_CONFIG) {
|
|
38
|
+
return new Document(
|
|
39
|
+
config
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
async function buildSearchIndex(records) {
|
|
43
|
+
const index = createSearchDocument();
|
|
44
|
+
for (const record of records) {
|
|
45
|
+
index.add({
|
|
46
|
+
...record,
|
|
47
|
+
tags: record.tags.join(" ")
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const exported = {};
|
|
51
|
+
await index.export((key, data) => {
|
|
52
|
+
if (typeof data === "string") exported[key] = data;
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
version: 1,
|
|
56
|
+
config: DEFAULT_SEARCH_CONFIG,
|
|
57
|
+
records: records.map(({ content, ...record }) => ({
|
|
58
|
+
...record,
|
|
59
|
+
excerpt: makeExcerpt(content, record.description ?? record.title)
|
|
60
|
+
})),
|
|
61
|
+
exported,
|
|
62
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/load.ts
|
|
67
|
+
var globalCache = globalThis;
|
|
68
|
+
async function hydrateSearchIndex(artifact) {
|
|
69
|
+
const document = createSearchDocument(artifact.config);
|
|
70
|
+
for (const [key, data] of Object.entries(artifact.exported)) {
|
|
71
|
+
document.import(key, data);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
document,
|
|
75
|
+
artifact,
|
|
76
|
+
recordsById: new Map(artifact.records.map((record) => [record.id, record]))
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function loadSearchIndex(artifactPath) {
|
|
80
|
+
const cache = globalCache.__silicaSearchIndexes ??= /* @__PURE__ */ new Map();
|
|
81
|
+
const cached = cache.get(artifactPath);
|
|
82
|
+
if (cached) return cached;
|
|
83
|
+
const raw = await readFile(artifactPath, "utf8");
|
|
84
|
+
const artifact = JSON.parse(raw);
|
|
85
|
+
const loaded = await hydrateSearchIndex(artifact);
|
|
86
|
+
cache.set(artifactPath, loaded);
|
|
87
|
+
return loaded;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/query.ts
|
|
91
|
+
function querySearchIndex(loaded, query, options = {}) {
|
|
92
|
+
const normalized = normalizeSearchText(query);
|
|
93
|
+
const limit = options.limit ?? 10;
|
|
94
|
+
const tagFilter = (options.tags ?? []).map(normalizeTag).filter(Boolean);
|
|
95
|
+
if (!normalized && tagFilter.length === 0) return [];
|
|
96
|
+
if (!normalized) {
|
|
97
|
+
return [...loaded.recordsById.values()].filter((record) => matchesTagFilter(record, tagFilter)).map((record) => toResult(record, 1)).sort((a, b) => a.title.localeCompare(b.title)).slice(0, limit);
|
|
98
|
+
}
|
|
99
|
+
const rawResults = loaded.document.search(normalized, {
|
|
100
|
+
limit: Math.max(limit * 4, 20),
|
|
101
|
+
enrich: false
|
|
102
|
+
});
|
|
103
|
+
const scoreById = /* @__PURE__ */ new Map();
|
|
104
|
+
for (const fieldResult of rawResults) {
|
|
105
|
+
const fieldWeight = fieldResult.field === "title" ? 5 : fieldResult.field === "tags" ? 3 : 1;
|
|
106
|
+
for (const id of fieldResult.result) {
|
|
107
|
+
const key = String(id);
|
|
108
|
+
scoreById.set(key, (scoreById.get(key) ?? 0) + fieldWeight);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return [...scoreById.entries()].map(([id, score]) => {
|
|
112
|
+
const record = loaded.recordsById.get(id);
|
|
113
|
+
if (!record) return void 0;
|
|
114
|
+
if (!matchesTagFilter(record, tagFilter)) return void 0;
|
|
115
|
+
return toResult(record, score);
|
|
116
|
+
}).filter((result) => Boolean(result)).sort((a, b) => b.score - a.score || a.title.localeCompare(b.title)).slice(0, limit);
|
|
117
|
+
}
|
|
118
|
+
function normalizeTag(tag) {
|
|
119
|
+
return tag.trim().replace(/^#/, "").toLowerCase();
|
|
120
|
+
}
|
|
121
|
+
function tagMatches(candidate, query) {
|
|
122
|
+
const tag = normalizeTag(candidate);
|
|
123
|
+
const normalizedQuery = normalizeTag(query);
|
|
124
|
+
if (!tag || !normalizedQuery) return false;
|
|
125
|
+
return tag === normalizedQuery || tag.startsWith(`${normalizedQuery}/`);
|
|
126
|
+
}
|
|
127
|
+
function matchesTagFilter(record, tagFilter) {
|
|
128
|
+
return tagFilter.length === 0 || record.tags.some(
|
|
129
|
+
(tag) => tagFilter.some((filterTag) => tagMatches(tag, filterTag))
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
function toResult(record, score) {
|
|
133
|
+
return {
|
|
134
|
+
slug: record.slug,
|
|
135
|
+
title: record.title,
|
|
136
|
+
description: record.description,
|
|
137
|
+
tags: record.tags,
|
|
138
|
+
excerpt: record.excerpt,
|
|
139
|
+
score
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/benchmark.ts
|
|
144
|
+
async function benchmarkSearchIndex(artifact, { query, warmRuns = 10, ...queryOptions }) {
|
|
145
|
+
const coldStart = performance.now();
|
|
146
|
+
const loaded = await hydrateSearchIndex(artifact);
|
|
147
|
+
const coldResults = querySearchIndex(loaded, query, queryOptions);
|
|
148
|
+
const coldMs = performance.now() - coldStart;
|
|
149
|
+
const runs = Math.max(1, warmRuns);
|
|
150
|
+
const warmStart = performance.now();
|
|
151
|
+
for (let index = 0; index < runs; index += 1) {
|
|
152
|
+
querySearchIndex(loaded, query, queryOptions);
|
|
153
|
+
}
|
|
154
|
+
const warmMs = (performance.now() - warmStart) / runs;
|
|
155
|
+
return {
|
|
156
|
+
coldMs: round(coldMs),
|
|
157
|
+
warmMs: round(warmMs),
|
|
158
|
+
warmRuns: runs,
|
|
159
|
+
resultCount: coldResults.length
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function round(value) {
|
|
163
|
+
return Math.round(value * 100) / 100;
|
|
164
|
+
}
|
|
165
|
+
export {
|
|
166
|
+
DEFAULT_SEARCH_CONFIG,
|
|
167
|
+
benchmarkSearchIndex,
|
|
168
|
+
buildSearchIndex,
|
|
169
|
+
createSearchDocument,
|
|
170
|
+
hydrateSearchIndex,
|
|
171
|
+
loadSearchIndex,
|
|
172
|
+
makeExcerpt,
|
|
173
|
+
normalizeSearchText,
|
|
174
|
+
querySearchIndex
|
|
175
|
+
};
|
|
176
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/benchmark.ts","../src/load.ts","../src/build.ts","../src/excerpt.ts","../src/query.ts"],"sourcesContent":["import { performance } from \"node:perf_hooks\";\nimport { hydrateSearchIndex } from \"./load.js\";\nimport { querySearchIndex } from \"./query.js\";\nimport type { SearchQueryOptions, SerializedSearchIndex } from \"./types.js\";\n\nexport type SearchBenchmarkOptions = SearchQueryOptions & {\n query: string;\n warmRuns?: number;\n};\n\nexport type SearchBenchmarkResult = {\n coldMs: number;\n warmMs: number;\n warmRuns: number;\n resultCount: number;\n};\n\nexport async function benchmarkSearchIndex(\n artifact: SerializedSearchIndex,\n { query, warmRuns = 10, ...queryOptions }: SearchBenchmarkOptions,\n): Promise<SearchBenchmarkResult> {\n const coldStart = performance.now();\n const loaded = await hydrateSearchIndex(artifact);\n const coldResults = querySearchIndex(loaded, query, queryOptions);\n const coldMs = performance.now() - coldStart;\n\n const runs = Math.max(1, warmRuns);\n const warmStart = performance.now();\n for (let index = 0; index < runs; index += 1) {\n querySearchIndex(loaded, query, queryOptions);\n }\n const warmMs = (performance.now() - warmStart) / runs;\n\n return {\n coldMs: round(coldMs),\n warmMs: round(warmMs),\n warmRuns: runs,\n resultCount: coldResults.length,\n };\n}\n\nfunction round(value: number): number {\n return Math.round(value * 100) / 100;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { createSearchDocument } from \"./build.js\";\nimport type { SerializedSearchIndex, StoredSearchRecord } from \"./types.js\";\n\nexport type LoadedSearchIndex = {\n document: ReturnType<typeof createSearchDocument>;\n artifact: SerializedSearchIndex;\n recordsById: Map<string, StoredSearchRecord>;\n};\n\nconst globalCache = globalThis as typeof globalThis & {\n __silicaSearchIndexes?: Map<string, LoadedSearchIndex>;\n};\n\nexport async function hydrateSearchIndex(\n artifact: SerializedSearchIndex,\n): Promise<LoadedSearchIndex> {\n const document = createSearchDocument(artifact.config);\n\n for (const [key, data] of Object.entries(artifact.exported)) {\n document.import(key, data);\n }\n\n return {\n document,\n artifact,\n recordsById: new Map(artifact.records.map((record) => [record.id, record])),\n };\n}\n\nexport async function loadSearchIndex(\n artifactPath: string,\n): Promise<LoadedSearchIndex> {\n const cache = (globalCache.__silicaSearchIndexes ??= new Map());\n const cached = cache.get(artifactPath);\n if (cached) return cached;\n\n const raw = await readFile(artifactPath, \"utf8\");\n const artifact = JSON.parse(raw) as SerializedSearchIndex;\n const loaded = await hydrateSearchIndex(artifact);\n cache.set(artifactPath, loaded);\n return loaded;\n}\n","import { Document } from \"flexsearch\";\nimport type {\n SearchIndexConfig,\n SearchRecord,\n SerializedSearchIndex,\n} from \"./types.js\";\nimport { makeExcerpt } from \"./excerpt.js\";\n\nexport const DEFAULT_SEARCH_CONFIG: SearchIndexConfig = {\n tokenize: \"forward\",\n document: {\n id: \"id\",\n index: [\"title\", \"content\", \"tags\"],\n store: [\"slug\", \"title\", \"description\", \"tags\"],\n },\n};\n\nexport function createSearchDocument(\n config: SearchIndexConfig = DEFAULT_SEARCH_CONFIG,\n): Document {\n return new Document(\n config as unknown as ConstructorParameters<typeof Document>[0],\n );\n}\n\nexport async function buildSearchIndex(\n records: SearchRecord[],\n): Promise<SerializedSearchIndex> {\n const index = createSearchDocument();\n\n for (const record of records) {\n index.add({\n ...record,\n tags: record.tags.join(\" \"),\n });\n }\n\n const exported: Record<string, string> = {};\n await index.export((key, data) => {\n if (typeof data === \"string\") exported[key] = data;\n });\n\n return {\n version: 1,\n config: DEFAULT_SEARCH_CONFIG,\n records: records.map(({ content, ...record }) => ({\n ...record,\n excerpt: makeExcerpt(content, record.description ?? record.title),\n })),\n exported,\n builtAt: new Date().toISOString(),\n };\n}\n","const WORD_BOUNDARY = /\\s+/g;\n\nexport function normalizeSearchText(value: string): string {\n return value.replace(/\\s+/g, \" \").trim();\n}\n\nexport function makeExcerpt(content: string, query: string, maxLength = 180): string {\n const normalized = normalizeSearchText(content);\n if (normalized.length <= maxLength) return normalized;\n\n const firstTerm = query\n .toLowerCase()\n .split(WORD_BOUNDARY)\n .find((term) => term.length > 1);\n\n const matchIndex = firstTerm ? normalized.toLowerCase().indexOf(firstTerm) : -1;\n const center = matchIndex >= 0 ? matchIndex : 0;\n const half = Math.floor(maxLength / 2);\n const start = Math.max(0, center - half);\n const end = Math.min(normalized.length, start + maxLength);\n const excerpt = normalized.slice(start, end).trim();\n\n return `${start > 0 ? \"…\" : \"\"}${excerpt}${end < normalized.length ? \"…\" : \"\"}`;\n}\n","import { normalizeSearchText } from \"./excerpt.js\";\nimport type { LoadedSearchIndex } from \"./load.js\";\nimport type {\n SearchQueryOptions,\n SearchResult,\n StoredSearchRecord,\n} from \"./types.js\";\n\ntype FlexDocumentResult = {\n field?: string;\n result: Array<string | number>;\n};\n\nexport function querySearchIndex(\n loaded: LoadedSearchIndex,\n query: string,\n options: SearchQueryOptions = {},\n): SearchResult[] {\n const normalized = normalizeSearchText(query);\n const limit = options.limit ?? 10;\n const tagFilter = (options.tags ?? []).map(normalizeTag).filter(Boolean);\n if (!normalized && tagFilter.length === 0) return [];\n\n if (!normalized) {\n return [...loaded.recordsById.values()]\n .filter((record) => matchesTagFilter(record, tagFilter))\n .map((record) => toResult(record, 1))\n .sort((a, b) => a.title.localeCompare(b.title))\n .slice(0, limit);\n }\n\n const rawResults = loaded.document.search(normalized, {\n limit: Math.max(limit * 4, 20),\n enrich: false,\n }) as FlexDocumentResult[];\n\n const scoreById = new Map<string, number>();\n for (const fieldResult of rawResults) {\n const fieldWeight =\n fieldResult.field === \"title\" ? 5 : fieldResult.field === \"tags\" ? 3 : 1;\n for (const id of fieldResult.result) {\n const key = String(id);\n scoreById.set(key, (scoreById.get(key) ?? 0) + fieldWeight);\n }\n }\n\n return [...scoreById.entries()]\n .map(([id, score]) => {\n const record = loaded.recordsById.get(id);\n if (!record) return undefined;\n if (!matchesTagFilter(record, tagFilter)) return undefined;\n return toResult(record, score);\n })\n .filter((result): result is SearchResult => Boolean(result))\n .sort((a, b) => b.score - a.score || a.title.localeCompare(b.title))\n .slice(0, limit);\n}\n\nfunction normalizeTag(tag: string): string {\n return tag.trim().replace(/^#/, \"\").toLowerCase();\n}\n\nfunction tagMatches(candidate: string, query: string): boolean {\n const tag = normalizeTag(candidate);\n const normalizedQuery = normalizeTag(query);\n if (!tag || !normalizedQuery) return false;\n return tag === normalizedQuery || tag.startsWith(`${normalizedQuery}/`);\n}\n\nfunction matchesTagFilter(\n record: StoredSearchRecord,\n tagFilter: string[],\n): boolean {\n return (\n tagFilter.length === 0 ||\n record.tags.some((tag) =>\n tagFilter.some((filterTag) => tagMatches(tag, filterTag)),\n )\n );\n}\n\nfunction toResult(record: StoredSearchRecord, score: number): SearchResult {\n return {\n slug: record.slug,\n title: record.title,\n description: record.description,\n tags: record.tags,\n excerpt: record.excerpt,\n score,\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@silicajs/search",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Server-side FlexSearch index builder and query helpers for Silica.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"lint": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"flexsearch": "^0.8.212"
|
|
27
|
+
}
|
|
28
|
+
}
|