@vibeorm/extensions 2.3.2 → 3.0.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/LICENSE +21 -0
- package/README.md +2 -0
- package/dist/index.js +36 -26
- package/dist/index.js.map +4 -4
- package/dist/search/index.js +32 -23
- package/dist/search/index.js.map +3 -3
- package/dist/vector/index.js +5 -4
- package/dist/vector/index.js.map +3 -3
- package/package.json +7 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/search/index.d.ts.map +0 -1
- package/dist/shared.d.ts.map +0 -1
- package/dist/trgm/index.d.ts.map +0 -1
- package/dist/vector/index.d.ts.map +0 -1
package/dist/search/index.js
CHANGED
|
@@ -84,7 +84,7 @@ async function pgExtensionCheck(params) {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// packages/extensions/src/search/index.ts
|
|
87
|
-
import { ParamCollector, getSqlDialect, renderExpr } from "@vibeorm/sql";
|
|
87
|
+
import { ParamCollector, getSqlDialect, renderExpr, renderSqliteFtsUpdateTrigger } from "@vibeorm/sql";
|
|
88
88
|
var VERSION = "2.0.0-alpha.2";
|
|
89
89
|
var SCORE_ALIAS = "_vibe_score";
|
|
90
90
|
var HIGHLIGHT_ALIAS = "_vibe_highlight";
|
|
@@ -327,6 +327,17 @@ function readDeclaration(params) {
|
|
|
327
327
|
filterFields: declStringArray(params.declaration, "filterFields")
|
|
328
328
|
};
|
|
329
329
|
}
|
|
330
|
+
function resolveLanguage(params) {
|
|
331
|
+
const language = params.decl.language ?? params.options["language"] ?? "english";
|
|
332
|
+
if (!isSafeSqlName({ name: language })) {
|
|
333
|
+
throw new VibeError2({
|
|
334
|
+
code: "VIBE_SCHEMA_INVALID",
|
|
335
|
+
message: `search language "${language}" is not a plain text-search config name`,
|
|
336
|
+
meta: { model: params.model.name, language }
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return language;
|
|
340
|
+
}
|
|
330
341
|
function weightLetters(params) {
|
|
331
342
|
const { decl } = params;
|
|
332
343
|
if (decl.weights === undefined || decl.weights.length !== decl.fields.length)
|
|
@@ -438,12 +449,12 @@ function searchArtifacts(ctx) {
|
|
|
438
449
|
const decl = readDeclaration({ model, declaration });
|
|
439
450
|
if (decl === null)
|
|
440
451
|
continue;
|
|
452
|
+
const language = resolveLanguage({ model, decl, options: ctx.options });
|
|
441
453
|
const table = tableNameOf({ model });
|
|
442
454
|
const columns = decl.fields.map((field) => columnNameOf({ model, field }));
|
|
443
455
|
resolveRank({ decl, options: ctx.options, engine, dialect: ctx.dialect });
|
|
444
456
|
assertBm25Declarations({ decl, engine, dialect: ctx.dialect });
|
|
445
457
|
if (engine === "tsvector") {
|
|
446
|
-
const language = decl.language ?? ctx.options["language"] ?? "english";
|
|
447
458
|
const { tsvColumn, indexName } = resolveArtifactNames({ decl, engine, dialect: ctx.dialect });
|
|
448
459
|
const expression = tsvectorExpression({ dialect, model, decl, language });
|
|
449
460
|
artifacts.push({
|
|
@@ -501,6 +512,7 @@ function searchArtifacts(ctx) {
|
|
|
501
512
|
const columnList = columns.map((column) => quote(column)).join(", ");
|
|
502
513
|
const newList = columns.map((column) => `new.${quote(column)}`).join(", ");
|
|
503
514
|
const oldList = columns.map((column) => `old.${quote(column)}`).join(", ");
|
|
515
|
+
const updateTrigger = renderSqliteFtsUpdateTrigger({ table, ftsTable: fts, columns });
|
|
504
516
|
artifacts.push({
|
|
505
517
|
id: `search:${modelName}:fts5-table`,
|
|
506
518
|
owns: {
|
|
@@ -522,10 +534,7 @@ END;`,
|
|
|
522
534
|
`CREATE TRIGGER IF NOT EXISTS ${quote(`${fts}_ad`)} AFTER DELETE ON ${quote(table)} BEGIN
|
|
523
535
|
INSERT INTO ${quote(fts)}(${quote(fts)}, rowid, ${columnList}) VALUES ('delete', old.rowid, ${oldList});
|
|
524
536
|
END;`,
|
|
525
|
-
|
|
526
|
-
INSERT INTO ${quote(fts)}(${quote(fts)}, rowid, ${columnList}) VALUES ('delete', old.rowid, ${oldList});
|
|
527
|
-
INSERT INTO ${quote(fts)}(rowid, ${columnList}) VALUES (new.rowid, ${newList});
|
|
528
|
-
END;`,
|
|
537
|
+
updateTrigger.create,
|
|
529
538
|
`INSERT INTO ${quote(fts)}(${quote(fts)}) VALUES ('rebuild');`
|
|
530
539
|
],
|
|
531
540
|
drop: [
|
|
@@ -533,6 +542,17 @@ END;`,
|
|
|
533
542
|
`DROP TRIGGER IF EXISTS ${quote(`${fts}_ad`)};`,
|
|
534
543
|
`DROP TRIGGER IF EXISTS ${quote(`${fts}_au`)};`
|
|
535
544
|
]
|
|
545
|
+
}, {
|
|
546
|
+
id: `search:${modelName}:fts5-update-trigger`,
|
|
547
|
+
owns: { kind: "trigger", table, name: `${fts}_au` },
|
|
548
|
+
expected: {
|
|
549
|
+
identity: { kind: "trigger", table: { namespace: "main", name: table }, name: `${fts}_au` },
|
|
550
|
+
facts: { definition: updateTrigger.definition, type: "trigger" },
|
|
551
|
+
dependencies: [],
|
|
552
|
+
format: "catalog-v2"
|
|
553
|
+
},
|
|
554
|
+
create: [],
|
|
555
|
+
drop: []
|
|
536
556
|
});
|
|
537
557
|
continue;
|
|
538
558
|
}
|
|
@@ -563,15 +583,13 @@ function highlightColumn(params) {
|
|
|
563
583
|
return field.column;
|
|
564
584
|
}
|
|
565
585
|
function shapeHits(params) {
|
|
566
|
-
const { rows,
|
|
586
|
+
const { rows, highlightsRequested, engineHasHighlights } = params;
|
|
567
587
|
return rows.map((raw) => {
|
|
568
588
|
const row = { ...raw };
|
|
569
589
|
const score = Number(row[SCORE_ALIAS]);
|
|
570
590
|
const highlight = row[HIGHLIGHT_ALIAS];
|
|
571
591
|
delete row[SCORE_ALIAS];
|
|
572
592
|
delete row[HIGHLIGHT_ALIAS];
|
|
573
|
-
for (const name of strip)
|
|
574
|
-
delete row[name];
|
|
575
593
|
const highlights = engineHasHighlights && highlightsRequested && highlight !== null && highlight !== undefined ? [String(highlight)] : null;
|
|
576
594
|
return { row, score, highlights };
|
|
577
595
|
});
|
|
@@ -632,7 +650,7 @@ function createSearchDelegate(config) {
|
|
|
632
650
|
meta: { table: config.table }
|
|
633
651
|
});
|
|
634
652
|
}
|
|
635
|
-
const projection = idOnly ? `${t}.${quote(config.pkColumn ?? "")}` :
|
|
653
|
+
const projection = idOnly ? `${t}.${quote(config.pkColumn ?? "")}` : ctx.projection({ qualify: "t" });
|
|
636
654
|
const collector = new ParamCollector(dialect);
|
|
637
655
|
const userWhere = (qualify) => {
|
|
638
656
|
if (args.where === undefined)
|
|
@@ -641,13 +659,13 @@ function createSearchDelegate(config) {
|
|
|
641
659
|
return expr === undefined ? null : renderExpr({ dialect, expr, collector });
|
|
642
660
|
};
|
|
643
661
|
let text;
|
|
644
|
-
let strip = [];
|
|
645
662
|
let engineHasHighlights = true;
|
|
646
663
|
if (config.engine === "tsvector") {
|
|
647
664
|
const language = config.language ?? "english";
|
|
648
665
|
const ranker = config.rank ?? "ts_rank_cd";
|
|
649
666
|
const tsv = `${t}.${quote(config.tsvColumn ?? "")}`;
|
|
650
|
-
const
|
|
667
|
+
const queryParameter = dialect.name === "postgres" ? collector.add(matchText) : undefined;
|
|
668
|
+
const tsquery = () => prefix ? `to_tsquery('${language}', ${queryParameter ?? collector.add(matchText)})` : `websearch_to_tsquery('${language}', ${queryParameter ?? collector.add(matchText)})`;
|
|
651
669
|
text = `SELECT ${projection}, ${ranker}(${tsv}, ${tsquery()}) AS ${quote(SCORE_ALIAS)}`;
|
|
652
670
|
if (hlColumn !== null) {
|
|
653
671
|
text += `, ts_headline('${language}', ${t}.${quote(hlColumn)}, ${tsquery()}) AS ${quote(HIGHLIGHT_ALIAS)}`;
|
|
@@ -660,7 +678,6 @@ function createSearchDelegate(config) {
|
|
|
660
678
|
text += ` AND ${ranker}(${tsv}, ${tsquery()}) >= ${collector.add(args.minScore)}`;
|
|
661
679
|
}
|
|
662
680
|
text += ` ORDER BY ${quote(SCORE_ALIAS)} DESC`;
|
|
663
|
-
strip = idOnly ? [] : [config.tsvColumn ?? ""];
|
|
664
681
|
} else if (config.engine === "fts5") {
|
|
665
682
|
const fts = quote(config.ftsTable ?? "");
|
|
666
683
|
text = `SELECT ${projection}, -bm25(${fts}) AS ${quote(SCORE_ALIAS)}`;
|
|
@@ -717,7 +734,6 @@ function createSearchDelegate(config) {
|
|
|
717
734
|
const rows = await ctx.executeRows({ text, values: collector.values, method: "search" });
|
|
718
735
|
return shapeHits({
|
|
719
736
|
rows,
|
|
720
|
-
strip,
|
|
721
737
|
highlightsRequested: args.highlight !== undefined,
|
|
722
738
|
engineHasHighlights
|
|
723
739
|
});
|
|
@@ -794,14 +810,7 @@ function search(options = {}) {
|
|
|
794
810
|
return null;
|
|
795
811
|
const requested = extOptions["engine"] ?? "auto";
|
|
796
812
|
const engine = resolveSearchEngine({ dialect, provider, requested });
|
|
797
|
-
const language = decl
|
|
798
|
-
if (!isSafeSqlName({ name: language })) {
|
|
799
|
-
throw new VibeError2({
|
|
800
|
-
code: "VIBE_SCHEMA_INVALID",
|
|
801
|
-
message: `search language "${language}" is not a plain text-search config name`,
|
|
802
|
-
meta: { model: model.name, language }
|
|
803
|
-
});
|
|
804
|
-
}
|
|
813
|
+
const language = resolveLanguage({ model, decl, options: extOptions });
|
|
805
814
|
const pkField = primaryKeyFieldOf({ model });
|
|
806
815
|
const rank = resolveRank({ decl, options: extOptions, engine, dialect });
|
|
807
816
|
assertBm25Declarations({ decl, engine, dialect });
|
|
@@ -898,4 +907,4 @@ export {
|
|
|
898
907
|
search
|
|
899
908
|
};
|
|
900
909
|
|
|
901
|
-
//# debugId=
|
|
910
|
+
//# debugId=5A96240B33D83FB764756E2164756E21
|
package/dist/search/index.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/search/index.ts", "../src/shared.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * `search` — unified full-text search (extensions.md §4).\n *\n * One API, four engines resolved per dialect at GENERATE time:\n * - `tsvector` (postgres/pglite): generated STORED tsvector column + GIN,\n * websearch_to_tsquery / ts_rank_cd / ts_headline.\n * - `bm25` (postgres + ParadeDB pg_search, opt-in via `engine: \"bm25\"`):\n * bm25 index + `@@@` + paradedb.score/snippet.\n * - `fts5` (sqlite): external-content virtual table + sync triggers,\n * MATCH / bm25() / snippet().\n * - `fulltext` (mysql): FULLTEXT index + MATCH … AGAINST; highlights are\n * typed `null` (mysql has none).\n *\n * `engine: \"auto\"` picks the dialect's native engine (never bm25 — ParadeDB\n * availability is unknowable at generate time; opt in explicitly).\n *\n * `prefix: true` (per query) turns the text into per-token prefix matching\n * for type-ahead boxes: unicode letter/number tokens, each matched as a\n * prefix (tsvector `tok:*` via to_tsquery, fts5 `\"tok\"*`, fulltext BOOLEAN\n * MODE `+tok*`). On bm25 the index tokenizer decides: an `edge_ngram`\n * declaration (`@@search(…, tokenizer: \"edge_ngram\", minGram: 1, maxGram: 12)`)\n * makes every token a prefix through `&&&`, and `fuzzy` prefix-matches within\n * an edit distance on any tokenizer (`pdb.fuzzy(n, t)`).\n *\n * bm25 index options (`tokenizer`, `minGram`/`maxGram`, `stemmer`,\n * `asciiFolding`, `lowercase`, `stopwordsLanguage`) are declared once per\n * model and applied to every searched field as `pdb.<tokenizer>(…)` casts in\n * the index column list; `weights` become per-field `pdb.boost(w)` factors.\n * Every SQL shape here was verified against ParadeDB 0.25.7.\n */\n\nimport { VibeError, assertIdentifierFits, defineExtension, fitDefaultName, unsupportedCapability } from \"@vibeorm/schema\";\nimport type {\n Dialect,\n ExtensionArtifact,\n ExtensionSchemaContext,\n FieldIR,\n ModelIR,\n Provider,\n VibeExtension,\n} from \"@vibeorm/schema\";\nimport { finiteNumber } from \"../shared.ts\";\nimport { ParamCollector, getSqlDialect, renderExpr } from \"@vibeorm/sql\";\nimport type { SqlDialect } from \"@vibeorm/sql\";\nimport type { ExtensionMountContext } from \"@vibeorm/runtime\";\nimport {\n columnNameOf,\n declNumberArray,\n declString,\n declStringArray,\n isSafeSqlName,\n pgExtensionCheck,\n primaryKeyFieldOf,\n tableNameOf,\n} from \"../shared.ts\";\n\n// ─── Options & types ──────────────────────────────────────────────\n\nexport type SearchEngine = \"bm25\" | \"tsvector\" | \"fts5\" | \"fulltext\";\n\nexport type SearchOptions = {\n /** Engine choice; `\"auto\"` (default) resolves per dialect. */\n readonly engine?: SearchEngine | \"auto\";\n /** Default text-search language for the tsvector engine (default \"english\"). */\n readonly language?: string;\n /**\n * Ranking function for the tsvector engine (default `\"ts_rank_cd\"`).\n * `ts_rank` ignores cover density, so the two order results differently —\n * consumers migrating hand-written `ts_rank` queries keep their ordering by\n * setting this. A per-model `rank:` declaration overrides the default.\n */\n readonly rank?: \"ts_rank\" | \"ts_rank_cd\";\n};\n\n/** One search hit. `highlights` is `null` on engines without highlighting. */\nexport type SearchHit<T> = {\n readonly row: T;\n readonly score: number;\n readonly highlights: readonly string[] | null;\n};\n\nconst VERSION = \"2.0.0-alpha.2\";\nconst SCORE_ALIAS = \"_vibe_score\";\nconst HIGHLIGHT_ALIAS = \"_vibe_highlight\";\nconst WEIGHT_LETTERS = [\"A\", \"B\", \"C\", \"D\"] as const;\n\n// ─── bm25 tokenizer declaration ───────────────────────────────────\n\n/** ParadeDB tokenizers this extension emits as `pdb.<name>` casts (each verified on 0.25.7). */\nconst BM25_TOKENIZERS: readonly string[] = Object.freeze([\n \"unicode_words\", \"simple\", \"whitespace\", \"ngram\", \"edge_ngram\", \"icu\",\n \"literal_normalized\", \"source_code\", \"chinese_compatible\", \"jieba\",\n]);\n\n/** Snowball languages ParadeDB's `stemmer=<language>` filter accepts (spelled lowercase in the index). */\nconst STEMMER_LANGUAGES: readonly string[] = Object.freeze([\n \"arabic\", \"czech\", \"danish\", \"dutch\", \"english\", \"finnish\", \"french\", \"german\", \"greek\", \"hungarian\",\n \"italian\", \"norwegian\", \"polish\", \"portuguese\", \"romanian\", \"russian\", \"spanish\", \"swedish\", \"tamil\", \"turkish\",\n]);\n\n/** Stopword lists ParadeDB ships (`stopwords_language=`); both lists are matched case-insensitively. */\nconst STOPWORD_LANGUAGES: readonly string[] = Object.freeze([\n \"czech\", \"danish\", \"dutch\", \"english\", \"finnish\", \"french\", \"german\", \"hungarian\", \"italian\", \"norwegian\",\n \"polish\", \"portuguese\", \"russian\", \"spanish\", \"swedish\",\n]);\n\nconst TOKENIZER_KEYS = [\"tokenizer\", \"minGram\", \"maxGram\", \"stemmer\", \"asciiFolding\", \"lowercase\", \"stopwordsLanguage\"] as const;\n\n/** The bm25 index tokenizer of one model's searched fields, normalized from `@@search`. */\ntype Bm25Tokenizer = {\n readonly name: string;\n /** Gram bounds — present exactly for `ngram` / `edge_ngram`. */\n readonly minGram?: number;\n readonly maxGram?: number;\n /** Token filters in emission order: `lowercase=false`, `ascii_folding=true`, `stemmer=…`, `stopwords_language=…`. */\n readonly filters: readonly string[];\n};\n\nfunction isPositiveInteger(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 1;\n}\n\n/**\n * Read the tokenizer keys of a `@@search` declaration. `undefined` when none\n * is declared (ParadeDB's default `unicode_words` applies); `{ error }` names\n * the first malformed key — the attribute parser and the artifact/client\n * readers share this so the schema plane and the generate plane agree.\n */\nfunction readBm25Tokenizer(\n declaration: Readonly<Record<string, unknown>>,\n): { readonly tokenizer: Bm25Tokenizer | undefined } | { readonly error: string } {\n if (TOKENIZER_KEYS.every((key) => declaration[key] === undefined)) return { tokenizer: undefined };\n const name = declaration[\"tokenizer\"] ?? \"unicode_words\";\n if (typeof name !== \"string\" || !BM25_TOKENIZERS.includes(name)) {\n return { error: `tokenizer must be one of ${BM25_TOKENIZERS.join(\", \")}` };\n }\n const grams = name === \"ngram\" || name === \"edge_ngram\";\n const minGram = declaration[\"minGram\"];\n const maxGram = declaration[\"maxGram\"];\n if (grams) {\n if (!isPositiveInteger(minGram) || !isPositiveInteger(maxGram) || minGram > maxGram) {\n return { error: `${name} needs integer minGram and maxGram with 1 <= minGram <= maxGram` };\n }\n } else if (minGram !== undefined || maxGram !== undefined) {\n return { error: \"minGram/maxGram apply only to the ngram and edge_ngram tokenizers\" };\n }\n const filters: string[] = [];\n const lowercase = declaration[\"lowercase\"];\n if (lowercase !== undefined && typeof lowercase !== \"boolean\") return { error: \"lowercase must be a boolean\" };\n if (lowercase === false) filters.push(\"lowercase=false\");\n const folding = declaration[\"asciiFolding\"];\n if (folding !== undefined && typeof folding !== \"boolean\") return { error: \"asciiFolding must be a boolean\" };\n if (folding === true) filters.push(\"ascii_folding=true\");\n const stemmer = declaration[\"stemmer\"];\n if (stemmer !== undefined) {\n if (typeof stemmer !== \"string\" || !STEMMER_LANGUAGES.includes(stemmer.toLowerCase())) {\n return { error: `stemmer must be one of ${STEMMER_LANGUAGES.join(\", \")}` };\n }\n filters.push(`stemmer=${stemmer.toLowerCase()}`);\n }\n const stopwords = declaration[\"stopwordsLanguage\"];\n if (stopwords !== undefined) {\n // One language: the edge_ngram search_tokenizer option is itself a\n // comma-separated argument list, so a comma inside a value is ambiguous.\n if (typeof stopwords !== \"string\" || !STOPWORD_LANGUAGES.includes(stopwords.toLowerCase())) {\n return { error: `stopwordsLanguage must be one of ${STOPWORD_LANGUAGES.join(\", \")}` };\n }\n filters.push(`stopwords_language=${stopwords.toLowerCase()}`);\n }\n return {\n tokenizer: {\n name,\n ...(grams ? { minGram: minGram as number, maxGram: maxGram as number } : {}),\n filters,\n },\n };\n}\n\n/** `pdb.<tokenizer>(min, max, 'filter=…')` — grams as bare integers, filters as string literals; no parens when empty. */\nfunction bm25TokenizerArguments(params: { dialect: SqlDialect; tokenizer: Bm25Tokenizer }): string {\n const { dialect, tokenizer } = params;\n const args = [\n ...(tokenizer.minGram === undefined ? [] : [String(tokenizer.minGram), String(tokenizer.maxGram)]),\n ...tokenizer.filters.map((filter) => dialect.quoteStringLiteral({ value: filter })),\n ];\n return args.length === 0 ? \"\" : `(${args.join(\", \")})`;\n}\n\n/** One searched column of the bm25 column list: bare, or wrapped in the declared tokenizer cast. */\nfunction bm25TokenizedColumn(params: { dialect: SqlDialect; column: string; tokenizer: Bm25Tokenizer | undefined }): string {\n const { dialect, column, tokenizer } = params;\n const quoted = dialect.quoteIdent({ name: column });\n if (tokenizer === undefined) return quoted;\n return `(${quoted}::pdb.${tokenizer.name}${bm25TokenizerArguments({ dialect, tokenizer })})`;\n}\n\n/**\n * Query-time tokenizer. An edge_ngram index must NOT tokenize the query into\n * edge ngrams as well (\"sho\" → s, sh, sho would match every s-word), so the\n * query goes through `unicode_words` with the same token filters. Every other\n * tokenizer keeps ParadeDB's default: the same tokenizer at both ends.\n */\nfunction bm25SearchTokenizer(params: { tokenizer: Bm25Tokenizer | undefined }): string | undefined {\n const { tokenizer } = params;\n if (tokenizer === undefined || tokenizer.name !== \"edge_ngram\") return undefined;\n return tokenizer.filters.length === 0 ? \"unicode_words\" : `unicode_words(${tokenizer.filters.join(\",\")})`;\n}\n\n/** Scalar columns ParadeDB pushes `WHERE` filters into the index for (docs: filtering/indexed). */\nconst BM25_FILTER_SCALARS: readonly string[] = Object.freeze([\"Boolean\", \"Int\", \"BigInt\", \"Float\", \"Decimal\", \"DateTime\"]);\n\n/** ParadeDB caps `pdb.boost` factors at ±2048. */\nconst BM25_BOOST_LIMIT = 2048;\n\n/**\n * The PostgreSQL storage of a `String` field that ParadeDB can put in a bm25\n * index: `text` / `varchar` as text, `uuid` as a literal key. `char(n)` is\n * refused by 0.25.7 at CREATE INDEX (\"Type 1042 is not yet supported\"), and\n * bit, xml, inet, citext and `Unsupported(…)` columns are not text either —\n * they are refused here, at generate time, instead of at the migration.\n */\nfunction bm25StringStorage(field: FieldIR): \"text\" | \"uuid\" | null {\n if (field.isUnsupported === true || field.isList || field.type !== \"String\") return null;\n const native = (field.nativeType ?? \"\").toLowerCase();\n if (native === \"\" || native === \"text\" || native.startsWith(\"varchar\")) return \"text\";\n if (native === \"uuid\") return \"uuid\";\n return null;\n}\n\nfunction describeField(field: FieldIR): string {\n const scalar = typeof field.type === \"string\" ? field.type : `enum ${field.type.enum}`;\n return `${scalar}${field.isList ? \"[]\" : \"\"}${field.nativeType === undefined ? \"\" : ` @db.${field.nativeType}`}`;\n}\n\n/**\n * The column list of one model's bm25 index, validated once for BOTH planes\n * (the migrate artifact and the generated client agree on what is refused):\n * a single-column primary key (ParadeDB's `key_field`), searched fields of\n * text storage, and `filterFields` — text columns get the `pdb.literal`\n * tokenizer for equality/IN pushdown, uuid, numeric, boolean and timestamp\n * columns are indexed as-is, anything else is refused rather than indexed\n * under semantics ParadeDB does not push down.\n */\nfunction bm25IndexColumns(params: { model: ModelIR; decl: SearchDecl }): {\n readonly pkColumn: string;\n readonly searched: readonly string[];\n readonly filters: readonly { readonly column: string; readonly literal: boolean }[];\n} {\n const { model, decl } = params;\n const pkField = primaryKeyFieldOf({ model });\n if (pkField === null) {\n throw new VibeError({\n code: \"VIBE_SCHEMA_INVALID\",\n message: `@@search with engine \"bm25\" needs a single-column primary key on \"${model.name}\" (key_field)`,\n meta: { model: model.name },\n });\n }\n const searched = decl.fields.map((name) => {\n const field = model.fields.find((candidate) => candidate.name === name);\n if (field === undefined) return columnNameOf({ model, field: name });\n if (bm25StringStorage(field) !== \"text\") {\n throw unsupportedCapability({\n dialect: \"postgres\",\n feature: `@@search field \"${name}\" (${describeField(field)}) on the bm25 index`,\n hint: \"ParadeDB tokenizes text and varchar columns; search another column or store this one as text\",\n });\n }\n return field.dbName ?? field.name;\n });\n const filters = (decl.filterFields ?? []).map((name) => {\n const field = model.fields.find((candidate) => candidate.name === name);\n if (field === undefined || name === pkField || decl.fields.includes(name)) {\n throw new VibeError({\n code: \"VIBE_SCHEMA_INVALID\",\n message: `@@search filterFields on \"${model.name}\": \"${name}\" must be a scalar field that is neither the primary key nor a searched field`,\n meta: { model: model.name, field: name },\n });\n }\n const column = field.dbName ?? field.name;\n const storage = bm25StringStorage(field);\n if (storage !== null) return { column, literal: storage === \"text\" };\n if (!field.isList && typeof field.type === \"string\" && BM25_FILTER_SCALARS.includes(field.type)) return { column, literal: false };\n throw unsupportedCapability({\n dialect: \"postgres\",\n feature: `@@search filterFields entry \"${name}\" (${describeField(field)}) on the bm25 index`,\n hint: \"ParadeDB pushes filters into the index for text/varchar (exact match / IN), uuid, Boolean, Int, BigInt, Float, Decimal and DateTime columns — filter other columns outside the index\",\n });\n });\n return { pkColumn: columnNameOf({ model, field: pkField }), searched, filters };\n}\n\n// ─── Engine resolution (generate/migrate time) ────────────────────\n\n/** Resolve the engine for a dialect; throws on impossible explicit choices. */\nexport function resolveSearchEngine(params: {\n dialect: Dialect;\n provider: Provider;\n requested: SearchEngine | \"auto\";\n}): SearchEngine {\n const { dialect, provider, requested } = params;\n const native: SearchEngine = dialect === \"postgres\" ? \"tsvector\" : dialect === \"sqlite\" ? \"fts5\" : \"fulltext\";\n if (requested === \"auto\") return native;\n if (requested === native) return native;\n if (requested === \"bm25\" && dialect === \"postgres\") {\n if (provider === \"pglite\") {\n throw unsupportedCapability({\n dialect,\n feature: 'the search engine \"bm25\" (ParadeDB pg_search)',\n hint: 'pglite does not bundle pg_search — use engine \"auto\"/\"tsvector\"',\n });\n }\n return \"bm25\";\n }\n throw unsupportedCapability({\n dialect,\n feature: `the search engine \"${requested}\"`,\n hint: `this dialect supports \"${native}\"${dialect === \"postgres\" ? ' and \"bm25\"' : \"\"}`,\n });\n}\n\n// ─── Declaration ──────────────────────────────────────────────────\n\ntype SearchDecl = {\n readonly fields: readonly string[];\n readonly weights: readonly number[] | undefined;\n readonly base: string;\n readonly language: string | undefined;\n /** Override for the tsvector column name (adopt-existing; tsvector engine only). */\n readonly columnName: string | undefined;\n /** Override for the index name (tsvector / bm25 / fulltext engines). */\n readonly indexName: string | undefined;\n /** Ranking function override (tsvector engine only). */\n readonly rank: \"ts_rank\" | \"ts_rank_cd\" | undefined;\n /** Index tokenizer applied to every searched field (bm25 engine only). */\n readonly tokenizer: Bm25Tokenizer | undefined;\n /** Non-searched columns added to the bm25 index so `where` filters on them run inside the index scan (bm25 engine only). */\n readonly filterFields: readonly string[] | undefined;\n};\n\nfunction declRank(\n declaration: Readonly<Record<string, unknown>>,\n): \"ts_rank\" | \"ts_rank_cd\" | undefined {\n const value = declaration[\"rank\"];\n return value === \"ts_rank\" || value === \"ts_rank_cd\" ? value : undefined;\n}\n\nfunction readDeclaration(params: {\n model: ModelIR;\n declaration: Readonly<Record<string, unknown>>;\n}): SearchDecl | null {\n const fields = declStringArray(params.declaration, \"fields\");\n if (fields === undefined || fields.length === 0) return null;\n if (params.declaration[\"weights\"] !== undefined && (\n !Array.isArray(params.declaration[\"weights\"]) ||\n params.declaration[\"weights\"].length !== fields.length ||\n params.declaration[\"weights\"].some((weight: unknown) => typeof weight !== \"number\" || !Number.isFinite(weight))\n )) {\n throw new VibeError({ code: \"VIBE_SCHEMA_INVALID\", message: \"weights must be a finite number list matching the field list\", meta: { model: params.model.name } });\n }\n const tokenizer = readBm25Tokenizer(params.declaration);\n if (\"error\" in tokenizer) {\n throw new VibeError({ code: \"VIBE_SCHEMA_INVALID\", message: `@@search on \"${params.model.name}\": ${tokenizer.error}`, meta: { model: params.model.name } });\n }\n return {\n fields,\n weights: declNumberArray(params.declaration, \"weights\"),\n base: declString(params.declaration, \"name\") ?? `${tableNameOf({ model: params.model })}_search`,\n language: declString(params.declaration, \"language\"),\n columnName: declString(params.declaration, \"columnName\"),\n indexName: declString(params.declaration, \"indexName\"),\n rank: declRank(params.declaration),\n tokenizer: tokenizer.tokenizer,\n filterFields: declStringArray(params.declaration, \"filterFields\"),\n };\n}\n\n/** Weight letter per field: highest weight → 'A', capped at 'D'. */\nfunction weightLetters(params: { decl: SearchDecl }): readonly string[] | null {\n const { decl } = params;\n if (decl.weights === undefined || decl.weights.length !== decl.fields.length) return null;\n const ranked = [...new Set(decl.weights)].sort((a, b) => b - a);\n return decl.weights.map(\n (weight) => WEIGHT_LETTERS[Math.min(ranked.indexOf(weight), WEIGHT_LETTERS.length - 1)] as string,\n );\n}\n\n/**\n * Artifact names with the adoption overrides applied (`columnName:` /\n * `indexName:` — declared independently so a consumer with LIVE objects can\n * adopt them without a rename migration). Engines without the named object\n * refuse the override instead of silently ignoring it.\n */\nfunction resolveArtifactNames(params: {\n decl: SearchDecl;\n engine: SearchEngine;\n dialect: Dialect;\n}): { tsvColumn: string; indexName: string } {\n const { decl, engine, dialect } = params;\n if (engine === \"fts5\" && (decl.columnName !== undefined || decl.indexName !== undefined)) {\n throw unsupportedCapability({\n dialect,\n feature: \"@@search columnName/indexName overrides on the fts5 engine\",\n hint: \"fts5 emits a shadow table + triggers — there is no column or index to rename; use name: to change the table base\",\n });\n }\n if (decl.columnName !== undefined && engine !== \"tsvector\") {\n throw unsupportedCapability({\n dialect,\n feature: `@@search columnName on the ${engine} engine`,\n hint: \"only the tsvector engine materializes a generated column — drop columnName or target tsvector\",\n });\n }\n // Minted names are shortened to the dialect's identifier limit (the same\n // rule migrate's default index names follow); an explicit override past the\n // limit is refused here — PostgreSQL would silently truncate it, the\n // ownership handshake with migrate would then miss the real object, and\n // every push would DROP and recreate the column and index.\n const remedy = \"Shorten it, or set a shorter `name:` on the @@search declaration.\";\n if (decl.columnName !== undefined) assertIdentifierFits({ name: decl.columnName, dialect, what: \"@@search columnName\", remedy });\n if (decl.indexName !== undefined) assertIdentifierFits({ name: decl.indexName, dialect, what: \"@@search indexName\", remedy });\n return {\n tsvColumn: decl.columnName ?? fitDefaultName({ prefix: decl.base, suffix: \"_tsv\", dialect }),\n indexName: decl.indexName ?? fitDefaultName({ prefix: decl.base, suffix: \"_idx\", dialect }),\n };\n}\n\n/**\n * Ranking function for the tsvector engine: the model's `rank:` declaration\n * wins over the factory-level default. A DECLARED rank on any other engine is\n * refused (it names postgres ranking functions); the factory default is just\n * a default and stays inert off tsvector.\n */\nfunction resolveRank(params: {\n decl: SearchDecl;\n options: Readonly<Record<string, unknown>>;\n engine: SearchEngine;\n dialect: Dialect;\n}): \"ts_rank\" | \"ts_rank_cd\" | undefined {\n const { decl, options, engine, dialect } = params;\n if (decl.weights !== undefined && engine !== \"tsvector\" && engine !== \"bm25\") {\n throw unsupportedCapability({\n dialect,\n feature: `@@search weights on the ${engine} engine`,\n hint: \"weights are supported by tsvector (setweight classes) and bm25 (pdb.boost factors); omit weights for this engine\",\n });\n }\n if (decl.weights !== undefined && engine === \"bm25\" && decl.weights.some((weight) => Math.abs(weight) > BM25_BOOST_LIMIT)) {\n throw new VibeError({\n code: \"VIBE_SCHEMA_INVALID\",\n message: `@@search weights on the bm25 engine are pdb.boost factors and must stay within ±${BM25_BOOST_LIMIT}`,\n meta: { weights: decl.weights },\n });\n }\n if (decl.rank !== undefined && engine !== \"tsvector\") {\n throw unsupportedCapability({\n dialect,\n feature: `@@search rank on the ${engine} engine`,\n hint: \"rank chooses between the postgres ranking functions ts_rank and ts_rank_cd — a tsvector-only concept\",\n });\n }\n if (engine !== \"tsvector\") return undefined;\n const fallback = options[\"rank\"];\n return decl.rank ?? (fallback === \"ts_rank\" || fallback === \"ts_rank_cd\" ? fallback : undefined);\n}\n\n/**\n * bm25 index declarations (`tokenizer`, `minGram`/`maxGram`, token filters,\n * `filterFields`) name ParadeDB concepts; every other engine refuses them at\n * the earliest knowable moment instead of silently building a different index.\n */\nfunction assertBm25Declarations(params: { decl: SearchDecl; engine: SearchEngine; dialect: Dialect }): void {\n const { decl, engine, dialect } = params;\n if (engine === \"bm25\") return;\n if (decl.tokenizer !== undefined) {\n throw unsupportedCapability({\n dialect,\n feature: `@@search tokenizer options on the ${engine} engine`,\n hint: 'tokenizer, minGram, maxGram, stemmer, asciiFolding, lowercase and stopwordsLanguage configure a ParadeDB bm25 index — drop them, or opt in with search({ engine: \"bm25\" })',\n });\n }\n if (decl.filterFields !== undefined) {\n throw unsupportedCapability({\n dialect,\n feature: `@@search filterFields on the ${engine} engine`,\n hint: 'filterFields add columns to a ParadeDB bm25 index for filter pushdown — drop them, or opt in with search({ engine: \"bm25\" })',\n });\n }\n}\n\nfunction tsvectorExpression(params: {\n dialect: SqlDialect;\n model: ModelIR;\n decl: SearchDecl;\n language: string;\n}): string {\n const { dialect, model, decl, language } = params;\n const quote = (name: string): string => dialect.quoteIdent({ name });\n const letters = weightLetters({ decl });\n const parts = decl.fields.map((field, index) => {\n const vector = `to_tsvector('${language}', coalesce(${quote(columnNameOf({ model, field }))}, ''))`;\n return letters === null ? vector : `setweight(${vector}, '${letters[index]}')`;\n });\n return letters === null && parts.length > 1\n ? `to_tsvector('${language}', ${decl.fields\n .map((field) => `coalesce(${quote(columnNameOf({ model, field }))}, '')`)\n .join(\" || ' ' || \")})`\n : parts.join(\" || \");\n}\n\n// ─── Artifacts ────────────────────────────────────────────────────\n\nfunction searchArtifacts(ctx: ExtensionSchemaContext): readonly ExtensionArtifact[] {\n const requested = (ctx.options[\"engine\"] as SearchEngine | \"auto\" | undefined) ?? \"auto\";\n const engine = resolveSearchEngine({ dialect: ctx.dialect, provider: ctx.provider, requested });\n const dialect = getSqlDialect({ dialect: ctx.dialect });\n const quote = (name: string): string => dialect.quoteIdent({ name });\n const artifacts: ExtensionArtifact[] = [];\n\n if (engine === \"bm25\") {\n // pg_search 0.25+ declares `requires = 'vector'`; CASCADE installs that\n // prerequisite where it is missing (a pristine template0 shadow, a server\n // without the ParadeDB image's template1 pre-installs) instead of failing.\n artifacts.push({\n id: \"search:extension:pg_search\",\n owns: { kind: \"postgresExtension\", name: \"pg_search\" },\n create: [\"CREATE EXTENSION IF NOT EXISTS pg_search CASCADE;\"],\n drop: [\"DROP EXTENSION IF EXISTS pg_search;\"],\n });\n }\n\n for (const [modelName, declaration] of Object.entries(ctx.declarations)) {\n const model = ctx.schema.models.find((candidate) => candidate.name === modelName);\n if (model === undefined) continue;\n const decl = readDeclaration({ model, declaration });\n if (decl === null) continue;\n const table = tableNameOf({ model });\n const columns = decl.fields.map((field) => columnNameOf({ model, field }));\n resolveRank({ decl, options: ctx.options, engine, dialect: ctx.dialect });\n assertBm25Declarations({ decl, engine, dialect: ctx.dialect });\n\n if (engine === \"tsvector\") {\n const language = decl.language ?? (ctx.options[\"language\"] as string | undefined) ?? \"english\";\n const { tsvColumn, indexName } = resolveArtifactNames({ decl, engine, dialect: ctx.dialect });\n const expression = tsvectorExpression({ dialect, model, decl, language });\n artifacts.push(\n {\n id: `search:${modelName}:column`,\n owns: { kind: \"column\", table, column: tsvColumn },\n create: [\n `ALTER TABLE ${quote(table)} ADD COLUMN IF NOT EXISTS ${quote(tsvColumn)} tsvector GENERATED ALWAYS AS (${expression}) STORED;`,\n ],\n drop: [`ALTER TABLE ${quote(table)} DROP COLUMN IF EXISTS ${quote(tsvColumn)};`],\n },\n {\n id: `search:${modelName}:index`,\n owns: { kind: \"index\", table, name: indexName },\n create: [\n `CREATE INDEX IF NOT EXISTS ${quote(indexName)} ON ${quote(table)} USING gin (${quote(tsvColumn)});`,\n ],\n drop: [`DROP INDEX IF EXISTS ${quote(indexName)};`],\n },\n );\n continue;\n }\n\n if (engine === \"bm25\") {\n const { pkColumn, searched, filters } = bm25IndexColumns({ model, decl });\n const bm25Names = resolveArtifactNames({ decl, engine, dialect: ctx.dialect });\n // Column list: key field first (ParadeDB requires it), searched columns\n // through the declared tokenizer cast, then filter columns. Every shape\n // here was verified against ParadeDB 0.25.7 (`pdb.<tokenizer>(…)` casts,\n // `search_tokenizer` WITH option).\n const indexed = [\n quote(pkColumn),\n ...searched.map((column) => bm25TokenizedColumn({ dialect, column, tokenizer: decl.tokenizer })),\n ...filters.map((filter) => (filter.literal ? `(${quote(filter.column)}::pdb.literal)` : quote(filter.column))),\n ];\n const withOptions = [`key_field=${dialect.quoteStringLiteral({ value: pkColumn })}`];\n const searchTokenizer = bm25SearchTokenizer({ tokenizer: decl.tokenizer });\n if (searchTokenizer !== undefined) withOptions.push(`search_tokenizer=${dialect.quoteStringLiteral({ value: searchTokenizer })}`);\n artifacts.push({\n id: `search:${modelName}:index`,\n owns: { kind: \"index\", table, name: bm25Names.indexName },\n create: [\n `CREATE INDEX IF NOT EXISTS ${quote(bm25Names.indexName)} ON ${quote(table)} USING bm25 (${indexed.join(\", \")}) WITH (${withOptions.join(\", \")});`,\n ],\n drop: [`DROP INDEX IF EXISTS ${quote(bm25Names.indexName)};`],\n });\n continue;\n }\n\n if (engine === \"fts5\") {\n resolveArtifactNames({ decl, engine, dialect: ctx.dialect });\n // N7 (SQL review): the external-content table and its sync triggers key\n // on the host's ROWID. Only a single Int/BigInt @id aliases (and pins)\n // the rowid; any other primary key leaves an IMPLICIT rowid that VACUUM\n // may renumber — a documented sqlite non-guarantee — after which the\n // shadow table silently points at the WRONG rows. Refuse at generate\n // time (earliest-moment rule); recovery for a database that already\n // drifted is `INSERT INTO <fts>(<fts>) VALUES('rebuild')`.\n const pkName = primaryKeyFieldOf({ model });\n const pkField = pkName === null ? undefined : model.fields.find((field) => field.name === pkName);\n const pkIsRowidAlias =\n pkField !== undefined && !pkField.isList && (pkField.type === \"Int\" || pkField.type === \"BigInt\");\n if (!pkIsRowidAlias) {\n throw unsupportedCapability({\n dialect: \"sqlite\",\n feature: `@@search (fts5) on \"${modelName}\", whose primary key is not a single INTEGER column`,\n hint: \"fts5 external-content tables key on the rowid; only an Int/BigInt @id aliases and PINS it — any other key's implicit rowid can be renumbered by VACUUM, silently mis-linking search hits. Give the searched model an Int/BigInt @id.\",\n });\n }\n const fts = `${decl.base}_fts`;\n const columnList = columns.map((column) => quote(column)).join(\", \");\n const newList = columns.map((column) => `new.${quote(column)}`).join(\", \");\n const oldList = columns.map((column) => `old.${quote(column)}`).join(\", \");\n artifacts.push(\n {\n id: `search:${modelName}:fts5-table`,\n owns: {\n kind: \"table\",\n name: fts,\n auxiliaryPrefixes: [`${fts}_`],\n },\n create: [\n // The host table name is a STRING literal here: a `@@map` containing a\n // quote must be escaped like any other literal, never spliced.\n `CREATE VIRTUAL TABLE IF NOT EXISTS ${quote(fts)} USING fts5(${columnList}, content=${dialect.quoteStringLiteral({ value: table })});`,\n ],\n drop: [`DROP TABLE IF EXISTS ${quote(fts)};`],\n },\n {\n id: `search:${modelName}:fts5-triggers`,\n owns: { kind: \"trigger\", table, name: `${fts}_ai` },\n create: [\n `CREATE TRIGGER IF NOT EXISTS ${quote(`${fts}_ai`)} AFTER INSERT ON ${quote(table)} BEGIN\\n INSERT INTO ${quote(fts)}(rowid, ${columnList}) VALUES (new.rowid, ${newList});\\nEND;`,\n `CREATE TRIGGER IF NOT EXISTS ${quote(`${fts}_ad`)} AFTER DELETE ON ${quote(table)} BEGIN\\n INSERT INTO ${quote(fts)}(${quote(fts)}, rowid, ${columnList}) VALUES ('delete', old.rowid, ${oldList});\\nEND;`,\n `CREATE TRIGGER IF NOT EXISTS ${quote(`${fts}_au`)} AFTER UPDATE ON ${quote(table)} BEGIN\\n INSERT INTO ${quote(fts)}(${quote(fts)}, rowid, ${columnList}) VALUES ('delete', old.rowid, ${oldList});\\n INSERT INTO ${quote(fts)}(rowid, ${columnList}) VALUES (new.rowid, ${newList});\\nEND;`,\n // Installation runs in the migration transaction after synchronization is installed.\n `INSERT INTO ${quote(fts)}(${quote(fts)}) VALUES ('rebuild');`,\n ],\n drop: [\n `DROP TRIGGER IF EXISTS ${quote(`${fts}_ai`)};`,\n `DROP TRIGGER IF EXISTS ${quote(`${fts}_ad`)};`,\n `DROP TRIGGER IF EXISTS ${quote(`${fts}_au`)};`,\n ],\n },\n );\n continue;\n }\n\n // fulltext (mysql): no IF NOT EXISTS form exists — migrate's presence\n // check (introspected index name) decides whether the create runs.\n const fulltextNames = resolveArtifactNames({ decl, engine, dialect: ctx.dialect });\n artifacts.push({\n id: `search:${modelName}:index`,\n owns: { kind: \"index\", table, name: fulltextNames.indexName },\n create: [\n `CREATE FULLTEXT INDEX ${quote(fulltextNames.indexName)} ON ${quote(table)} (${columns\n .map((column) => quote(column))\n .join(\", \")});`,\n ],\n drop: [`DROP INDEX ${quote(fulltextNames.indexName)} ON ${quote(table)};`],\n });\n }\n return artifacts;\n}\n\n// ─── Runtime delegate (generated-glue target) ─────────────────────\n\n/** Baked per-model config the generator writes into the client. */\nexport type SearchDelegateConfig = {\n readonly engine: SearchEngine;\n readonly table: string;\n /** Searched fields in declaration order → column names. */\n readonly fields: readonly { readonly name: string; readonly column: string }[];\n /** tsvector engine: the generated column. */\n readonly tsvColumn?: string;\n /** fts5 engine: the shadow table. */\n readonly ftsTable?: string;\n /** tsvector engine: regconfig name (validated identifier). */\n readonly language?: string;\n /** tsvector engine: ranking function (default `ts_rank_cd`). */\n readonly rank?: \"ts_rank\" | \"ts_rank_cd\";\n /** Single-column primary key: bm25's key_field and the `select: \"id\"` projection. */\n readonly pkColumn?: string;\n /** bm25 engine: per-field `pdb.boost` factors (the declared `weights`), parallel to `fields`. */\n readonly boosts?: readonly number[];\n /** bm25 engine: the index tokenizer is edge_ngram, so every query token already matches as a prefix. */\n readonly prefixIndex?: boolean;\n};\n\nexport type SearchArgs = {\n readonly query: string;\n readonly where?: Record<string, unknown>;\n readonly take?: number;\n readonly skip?: number;\n readonly minScore?: number;\n readonly highlight?: { readonly field: string };\n /**\n * Type-ahead prefix matching. The query is split into unicode\n * letter/number tokens (punctuation separates and never matches) and every\n * token matches as a prefix — \"piz\" finds \"Pizza\". Per engine: tsvector\n * builds `to_tsquery('lang', 'piz:*')` (lexemes are STEMMED, so a stemming\n * `language` can miss mid-stem prefixes — declare `language: \"simple\"` for\n * type-ahead columns), fts5 `\"piz\"*`, fulltext `+piz*` in BOOLEAN MODE\n * (InnoDB ignores tokens under `innodb_ft_min_token_size`, default 3). A\n * query with no tokens resolves to `[]` without touching the database.\n * On bm25 the index decides: an `edge_ngram` tokenizer declaration makes\n * every token a prefix (`&&&` match-all over the raw query); otherwise\n * combine with `fuzzy` (`pdb.fuzzy(n, t)`), or the call is refused with\n * `VIBE_UNSUPPORTED_CAPABILITY`.\n */\n readonly prefix?: boolean;\n /**\n * Typo tolerance (bm25 engine only): every query token matches within this\n * Levenshtein edit distance — `1` accepts one insertion, deletion or\n * substitution (\"storgate\" finds \"Storgata\"); ParadeDB caps it at `2`, and\n * a transposition costs 2. Switches the match to `&&&` (every token must\n * match) and combines with `prefix`. Other engines refuse it\n * (`VIBE_UNSUPPORTED_CAPABILITY`) — use the trgm extension's `similarTo`\n * there.\n */\n readonly fuzzy?: 1 | 2;\n /**\n * Projection: `\"row\"` (default) returns full rows; `\"id\"` returns only the\n * primary key — for repositories that re-read hits through their own typed\n * queries (full rows are wasted bandwidth there).\n */\n readonly select?: \"row\" | \"id\";\n};\n\nexport type SearchMethod = (args: SearchArgs) => Promise<SearchHit<Record<string, unknown>>[]>;\n\nfunction highlightColumn(params: { config: SearchDelegateConfig; args: SearchArgs }): string | null {\n const { config, args } = params;\n if (args.highlight === undefined) return null;\n const field = config.fields.find((candidate) => candidate.name === args.highlight?.field);\n if (field === undefined) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `highlight.field \"${args.highlight.field}\" is not searched — declared: ${config.fields\n .map((candidate) => candidate.name)\n .join(\", \")}`,\n meta: { field: args.highlight.field },\n });\n }\n return field.column;\n}\n\nfunction shapeHits(params: {\n rows: readonly Record<string, unknown>[];\n strip: readonly string[];\n highlightsRequested: boolean;\n engineHasHighlights: boolean;\n}): SearchHit<Record<string, unknown>>[] {\n const { rows, strip, highlightsRequested, engineHasHighlights } = params;\n return rows.map((raw) => {\n const row: Record<string, unknown> = { ...raw };\n const score = Number(row[SCORE_ALIAS]);\n const highlight = row[HIGHLIGHT_ALIAS];\n delete row[SCORE_ALIAS];\n delete row[HIGHLIGHT_ALIAS];\n for (const name of strip) delete row[name];\n const highlights =\n engineHasHighlights && highlightsRequested && highlight !== null && highlight !== undefined\n ? [String(highlight)]\n : null;\n return { row, score, highlights };\n });\n}\n\n/**\n * The generated client's glue target: bakes engine + names at generate time,\n * mounts as the `search` method at runtime (D9).\n */\nexport function createSearchDelegate(\n config: SearchDelegateConfig,\n): (ctx: ExtensionMountContext) => SearchMethod {\n return (ctx: ExtensionMountContext): SearchMethod => {\n const dialect = ctx.dialect;\n const quote = (name: string): string => dialect.quoteIdent({ name });\n const t = quote(\"t\");\n\n return async (args: SearchArgs): Promise<SearchHit<Record<string, unknown>>[]> => {\n if (typeof args.query !== \"string\" || args.query.trim().length === 0) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: \"search requires a non-empty query string\",\n meta: { table: config.table },\n });\n }\n const take = finiteNumber({ value: args.take === undefined ? 10 : args.take, name: \"take\", integer: true, min: 1 });\n const skip = args.skip === undefined ? null : finiteNumber({ value: args.skip, name: \"skip\", integer: true, min: 0 });\n if (args.minScore !== undefined) finiteNumber({ value: args.minScore, name: \"minScore\" });\n const prefix = args.prefix === true;\n const fuzzy = args.fuzzy;\n if (fuzzy !== undefined && fuzzy !== 1 && fuzzy !== 2) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: \"fuzzy must be 1 or 2 (ParadeDB caps the edit distance at 2)\",\n meta: { argument: \"fuzzy\" },\n });\n }\n if (fuzzy !== undefined && config.engine !== \"bm25\") {\n throw unsupportedCapability({\n dialect: dialect.name,\n feature: `search fuzzy matching on the \"${config.engine}\" engine`,\n hint: \"only the bm25 engine (ParadeDB pdb.fuzzy) matches within an edit distance — on postgres/pglite declare @@trgm and filter with similarTo\",\n });\n }\n if (prefix && config.engine === \"bm25\" && config.prefixIndex !== true && fuzzy === undefined) {\n throw unsupportedCapability({\n dialect: \"postgres\",\n feature: 'search prefix mode on the \"bm25\" engine without an edge_ngram index',\n hint: 'declare @@search(…, tokenizer: \"edge_ngram\", minGram: 1, maxGram: 12) so every token is indexed as a prefix, or pass fuzzy: 1 | 2 for fuzzy prefix matching (pdb.fuzzy(n, t))',\n });\n }\n let matchText = args.query;\n if (prefix) {\n const tokens = args.query.match(/[\\p{L}\\p{N}]+/gu) ?? [];\n if (tokens.length === 0) return [];\n // bm25 tokenizes the raw query itself (index tokenizer or\n // search_tokenizer); the other engines need per-token prefix syntax.\n matchText =\n config.engine === \"tsvector\"\n ? tokens.map((token) => `${token}:*`).join(\" & \")\n : config.engine === \"fts5\"\n ? tokens.map((token) => `\"${token}\"*`).join(\" \")\n : config.engine === \"bm25\"\n ? args.query\n : tokens.map((token) => `+${token}*`).join(\" \");\n }\n const hlColumn = highlightColumn({ config, args });\n const idOnly = args.select === \"id\";\n if (idOnly && config.pkColumn === undefined) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `select: \"id\" needs a single-column primary key baked into the client — ${config.table} has none, or the client predates the projection (run vibeorm generate)`,\n meta: { table: config.table },\n });\n }\n const projection = idOnly ? `${t}.${quote(config.pkColumn ?? \"\")}` : `${t}.*`;\n const collector = new ParamCollector(dialect);\n\n const userWhere = (qualify: string): string | null => {\n if (args.where === undefined) return null;\n const expr = ctx.compileWhere({ where: args.where, qualify });\n return expr === undefined ? null : renderExpr({ dialect, expr, collector });\n };\n\n let text: string;\n let strip: readonly string[] = [];\n let engineHasHighlights = true;\n\n if (config.engine === \"tsvector\") {\n const language = config.language ?? \"english\";\n const ranker: \"ts_rank\" | \"ts_rank_cd\" = config.rank ?? \"ts_rank_cd\";\n const tsv = `${t}.${quote(config.tsvColumn ?? \"\")}`;\n const tsquery = (): string =>\n prefix\n ? `to_tsquery('${language}', ${collector.add(matchText)})`\n : `websearch_to_tsquery('${language}', ${collector.add(matchText)})`;\n text = `SELECT ${projection}, ${ranker}(${tsv}, ${tsquery()}) AS ${quote(SCORE_ALIAS)}`;\n if (hlColumn !== null) {\n text += `, ts_headline('${language}', ${t}.${quote(hlColumn)}, ${tsquery()}) AS ${quote(HIGHLIGHT_ALIAS)}`;\n }\n text += ` FROM ${quote(config.table)} AS ${t} WHERE ${tsv} @@ ${tsquery()}`;\n const where = userWhere(\"t\");\n if (where !== null) text += ` AND (${where})`;\n if (args.minScore !== undefined) {\n text += ` AND ${ranker}(${tsv}, ${tsquery()}) >= ${collector.add(args.minScore)}`;\n }\n text += ` ORDER BY ${quote(SCORE_ALIAS)} DESC`;\n strip = idOnly ? [] : [config.tsvColumn ?? \"\"];\n } else if (config.engine === \"fts5\") {\n const fts = quote(config.ftsTable ?? \"\");\n text = `SELECT ${projection}, -bm25(${fts}) AS ${quote(SCORE_ALIAS)}`;\n if (hlColumn !== null) {\n const index = config.fields.findIndex((field) => field.column === hlColumn);\n text += `, snippet(${fts}, ${index}, '<b>', '</b>', '…', 16) AS ${quote(HIGHLIGHT_ALIAS)}`;\n }\n text += ` FROM ${fts} JOIN ${quote(config.table)} AS ${t} ON ${t}.rowid = ${fts}.rowid`;\n text += ` WHERE ${fts} MATCH ${collector.add(matchText)}`;\n const where = userWhere(\"t\");\n if (where !== null) text += ` AND (${where})`;\n if (args.minScore !== undefined) {\n text += ` AND -bm25(${fts}) >= ${collector.add(args.minScore)}`;\n }\n text += ` ORDER BY ${quote(SCORE_ALIAS)} DESC`;\n } else if (config.engine === \"fulltext\") {\n engineHasHighlights = false;\n const mode = prefix ? \"IN BOOLEAN MODE\" : \"IN NATURAL LANGUAGE MODE\";\n const match = (): string =>\n `MATCH (${config.fields.map((field) => `${t}.${quote(field.column)}`).join(\", \")}) AGAINST (${collector.add(matchText)} ${mode})`;\n text = `SELECT ${projection}, ${match()} AS ${quote(SCORE_ALIAS)} FROM ${quote(config.table)} AS ${t}`;\n text += ` WHERE ${match()}`;\n const where = userWhere(\"t\");\n if (where !== null) text += ` AND (${where})`;\n if (args.minScore !== undefined) {\n text += ` AND ${match()} >= ${collector.add(args.minScore)}`;\n }\n text += ` ORDER BY ${quote(SCORE_ALIAS)} DESC`;\n } else {\n // bm25 (ParadeDB). `@@@` is the query parser (OR between words);\n // prefix and fuzzy switch to `&&&` (every token must match) with the\n // bound query cast to pdb.fuzzy(n[, t]); declared weights ride as\n // pdb.boost(w) per field. minScore compiles to a score_filter inside\n // the index scan (EXPLAIN-verified on 0.25.7), so it narrows BEFORE\n // the LIMIT — a client-side filter after it would return fewer rows\n // than `take` while qualifying rows exist.\n const pk = `${t}.${quote(config.pkColumn ?? \"\")}`;\n const operator = prefix || fuzzy !== undefined ? \"&&&\" : \"@@@\";\n const fuzzyCast = fuzzy === undefined ? \"\" : `::pdb.fuzzy(${fuzzy}${prefix ? \", t\" : \"\"})`;\n text = `SELECT ${projection}, paradedb.score(${pk}) AS ${quote(SCORE_ALIAS)}`;\n if (hlColumn !== null) {\n text += `, paradedb.snippet(${t}.${quote(hlColumn)}) AS ${quote(HIGHLIGHT_ALIAS)}`;\n }\n text += ` FROM ${quote(config.table)} AS ${t} WHERE (${config.fields\n .map((field, index) => {\n const boost = config.boosts?.[index];\n const boostCast = boost === undefined || boost === 1 ? \"\" : `::pdb.boost(${boost})`;\n return `${t}.${quote(field.column)} ${operator} ${collector.add(matchText)}${fuzzyCast}${boostCast}`;\n })\n .join(\" OR \")})`;\n const where = userWhere(\"t\");\n if (where !== null) text += ` AND (${where})`;\n if (args.minScore !== undefined) {\n text += ` AND paradedb.score(${pk}) >= ${collector.add(args.minScore)}`;\n }\n text += ` ORDER BY ${quote(SCORE_ALIAS)} DESC`;\n }\n\n text += ` LIMIT ${take}`;\n if (skip !== null) text += ` OFFSET ${skip}`;\n\n const rows = await ctx.executeRows({ text, values: collector.values, method: \"search\" });\n return shapeHits({\n rows,\n strip,\n highlightsRequested: args.highlight !== undefined,\n engineHasHighlights,\n });\n };\n };\n}\n\n// ─── Factory ──────────────────────────────────────────────────────\n\n/**\n * Unified full-text search.\n *\n * @example schema\n * // @@search([title, body], name: \"post_search\", weights: [2, 1])\n * const hits = await db.post.search({ query: \"sqlite full text\", take: 20 });\n * // → [{ row, score, highlights }]\n */\nexport function search(options: SearchOptions = {}): VibeExtension {\n return defineExtension({\n name: \"search\",\n version: VERSION,\n options: { ...options },\n requires: { postgresql: null, pglite: null, sqlite: null, mysql: null },\n\n schema: {\n modelAttributes: [\n {\n name: \"search\",\n parse: ({ args, fieldNames }) => {\n const fields = args.fields ?? [];\n if (fields.length === 0) {\n return { error: \"@@search requires a field list, e.g. @@search([title, body])\" };\n }\n const missing = fields.find((name) => !fieldNames.includes(name));\n if (missing !== undefined) {\n return { error: `@@search references unknown field \"${missing}\"` };\n }\n const name = args.named[\"name\"];\n if (name !== undefined && (typeof name !== \"string\" || !isSafeSqlName({ name }))) {\n return { error: `name must be a plain identifier` };\n }\n const weights = args.named[\"weights\"];\n if (weights !== undefined) {\n if (\n !Array.isArray(weights) ||\n weights.length !== fields.length ||\n weights.some((weight) => typeof weight !== \"number\" || !Number.isFinite(weight))\n ) {\n return { error: `weights must be a number list matching the field list` };\n }\n }\n const language = args.named[\"language\"];\n if (language !== undefined && (typeof language !== \"string\" || !isSafeSqlName({ name: language }))) {\n return { error: `language must be a plain text-search config name` };\n }\n for (const key of [\"columnName\", \"indexName\"] as const) {\n const value = args.named[key];\n if (value !== undefined && (typeof value !== \"string\" || !isSafeSqlName({ name: value }))) {\n return { error: `${key} must be a plain identifier` };\n }\n }\n const rank = args.named[\"rank\"];\n if (rank !== undefined && rank !== \"ts_rank\" && rank !== \"ts_rank_cd\") {\n return { error: `rank must be \"ts_rank\" or \"ts_rank_cd\"` };\n }\n const tokenizer = readBm25Tokenizer(args.named);\n if (\"error\" in tokenizer) return { error: tokenizer.error };\n const filterFields = args.named[\"filterFields\"];\n if (filterFields !== undefined) {\n if (!Array.isArray(filterFields) || filterFields.length === 0 || filterFields.some((name) => typeof name !== \"string\")) {\n return { error: \"filterFields must be a non-empty field list, e.g. filterFields: [municipalityNumber, active]\" };\n }\n const invalid = filterFields.find((name) => !fieldNames.includes(String(name)) || fields.includes(String(name)));\n if (invalid !== undefined) {\n return { error: `filterFields entry \"${String(invalid)}\" must be a field on the model that is not also searched` };\n }\n }\n return { declaration: { fields, ...args.named } };\n },\n },\n ],\n indexKinds: [\"bm25\", \"fts5\", \"fulltext\", \"gin\"],\n },\n\n migrate: { artifacts: searchArtifacts },\n\n client: {\n model: ({ model, dialect, provider, options: extOptions, declaration, typeNames }) => {\n const decl = readDeclaration({ model, declaration });\n if (decl === null) return null;\n const requested = (extOptions[\"engine\"] as SearchEngine | \"auto\" | undefined) ?? \"auto\";\n const engine = resolveSearchEngine({ dialect, provider, requested });\n const language =\n decl.language ?? (extOptions[\"language\"] as string | undefined) ?? \"english\";\n if (!isSafeSqlName({ name: language })) {\n throw new VibeError({\n code: \"VIBE_SCHEMA_INVALID\",\n message: `search language \"${language}\" is not a plain text-search config name`,\n meta: { model: model.name, language },\n });\n }\n\n const pkField = primaryKeyFieldOf({ model });\n const rank = resolveRank({ decl, options: extOptions, engine, dialect });\n assertBm25Declarations({ decl, engine, dialect });\n // The client refuses exactly what the migrate artifact refuses (key\n // field, column storage, filter fields), so `vibeorm generate` is the\n // earliest moment, not `migrate generate`.\n if (engine === \"bm25\") bm25IndexColumns({ model, decl });\n const names = resolveArtifactNames({ decl, engine, dialect });\n const config: SearchDelegateConfig = {\n engine,\n table: tableNameOf({ model }),\n fields: decl.fields.map((field) => ({\n name: field,\n column: columnNameOf({ model, field }),\n })),\n ...(engine === \"tsvector\"\n ? { tsvColumn: names.tsvColumn, language, ...(rank === undefined ? {} : { rank }) }\n : {}),\n ...(engine === \"fts5\" ? { ftsTable: `${decl.base}_fts` } : {}),\n ...(pkField !== null ? { pkColumn: columnNameOf({ model, field: pkField }) } : {}),\n ...(engine === \"bm25\" && decl.weights !== undefined ? { boosts: decl.weights } : {}),\n ...(engine === \"bm25\" && decl.tokenizer?.name === \"edge_ngram\" ? { prefixIndex: true } : {}),\n };\n\n const fieldUnion = decl.fields.map((field) => JSON.stringify(field)).join(\" | \");\n const argLines: string[] = [\n ` readonly query: string;`,\n ` readonly where?: ${typeNames.whereInput};`,\n ` readonly take?: number;`,\n ` readonly skip?: number;`,\n ` readonly minScore?: number;`,\n ` readonly highlight?: { readonly field: ${fieldUnion} };`,\n ` readonly prefix?: boolean;`,\n // Typed honesty: fuzzy exists only where the engine can honor it.\n ...(engine === \"bm25\" ? [` readonly fuzzy?: 1 | 2;`] : []),\n ];\n // With a single-column pk the id projection is offered as an overload:\n // `select: \"id\"` narrows the hit row to the pk.\n const dts: string[] =\n pkField === null\n ? [\n `readonly search: (args: {`,\n ...argLines,\n `}) => Promise<SearchHit<${typeNames.payload}>[]>;`,\n ]\n : [\n `readonly search: {`,\n ` (args: {`,\n ...argLines.map((line) => ` ${line}`),\n ` readonly select: \"id\";`,\n ` }): Promise<SearchHit<Pick<${typeNames.payload}, ${JSON.stringify(pkField)}>>[]>;`,\n ` (args: {`,\n ...argLines.map((line) => ` ${line}`),\n ` readonly select?: \"row\";`,\n ` }): Promise<SearchHit<${typeNames.payload}>[]>;`,\n `};`,\n ];\n return {\n kind: \"method\",\n // Verified read: the mount issues exactly one SELECT per call (the\n // index-maintenance DDL lives in `migrate.artifacts`, not here), so it\n // is available on a $readOnly client.\n readOnly: true,\n runtimeGlue: `createSearchDelegate(${JSON.stringify(config)})`,\n imports: [{ from: \"@vibeorm/extensions/search\", names: [\"createSearchDelegate\"] }],\n dtsImports: [{ from: \"@vibeorm/extensions/search\", names: [\"SearchHit\"] }],\n dts,\n };\n },\n },\n\n doctor: async ({ execute, dialect, options: extOptions }) => {\n if (dialect === \"postgres\") {\n const requested = (extOptions[\"engine\"] as SearchEngine | \"auto\" | undefined) ?? \"auto\";\n if (requested === \"bm25\") {\n return { checks: [await pgExtensionCheck({ execute, name: \"pg_search\" })] };\n }\n return { checks: [{ name: \"tsvector\", ok: true, note: \"built in\" }] };\n }\n if (dialect === \"sqlite\") {\n try {\n const rows = await execute({\n text: \"SELECT 1 AS ok FROM pragma_compile_options WHERE compile_options = 'ENABLE_FTS5'\",\n });\n return {\n checks: [\n rows.length > 0\n ? { name: \"fts5\", ok: true, note: \"compiled in\" }\n : { name: \"fts5\", ok: false, note: \"sqlite built without FTS5\" },\n ],\n };\n } catch (error) {\n return {\n checks: [\n {\n name: \"fts5\",\n ok: false,\n note: `check failed: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n };\n }\n }\n return { checks: [{ name: \"fulltext\", ok: true, note: \"built in (InnoDB FULLTEXT)\" }] };\n },\n });\n}\n",
|
|
5
|
+
"/**\n * `search` — unified full-text search (extensions.md §4).\n *\n * One API, four engines resolved per dialect at GENERATE time:\n * - `tsvector` (postgres/pglite): generated STORED tsvector column + GIN,\n * websearch_to_tsquery / ts_rank_cd / ts_headline.\n * - `bm25` (postgres + ParadeDB pg_search, opt-in via `engine: \"bm25\"`):\n * bm25 index + `@@@` + paradedb.score/snippet.\n * - `fts5` (sqlite): external-content virtual table + sync triggers,\n * MATCH / bm25() / snippet().\n * - `fulltext` (mysql): FULLTEXT index + MATCH … AGAINST; highlights are\n * typed `null` (mysql has none).\n *\n * `engine: \"auto\"` picks the dialect's native engine (never bm25 — ParadeDB\n * availability is unknowable at generate time; opt in explicitly).\n *\n * `prefix: true` (per query) turns the text into per-token prefix matching\n * for type-ahead boxes: unicode letter/number tokens, each matched as a\n * prefix (tsvector `tok:*` via to_tsquery, fts5 `\"tok\"*`, fulltext BOOLEAN\n * MODE `+tok*`). On bm25 the index tokenizer decides: an `edge_ngram`\n * declaration (`@@search(…, tokenizer: \"edge_ngram\", minGram: 1, maxGram: 12)`)\n * makes every token a prefix through `&&&`, and `fuzzy` prefix-matches within\n * an edit distance on any tokenizer (`pdb.fuzzy(n, t)`).\n *\n * bm25 index options (`tokenizer`, `minGram`/`maxGram`, `stemmer`,\n * `asciiFolding`, `lowercase`, `stopwordsLanguage`) are declared once per\n * model and applied to every searched field as `pdb.<tokenizer>(…)` casts in\n * the index column list; `weights` become per-field `pdb.boost(w)` factors.\n * Every SQL shape here was verified against ParadeDB 0.25.7.\n */\n\nimport { VibeError, assertIdentifierFits, defineExtension, fitDefaultName, unsupportedCapability } from \"@vibeorm/schema\";\nimport type {\n Dialect,\n ExtensionArtifact,\n ExtensionSchemaContext,\n FieldIR,\n ModelIR,\n Provider,\n VibeExtension,\n} from \"@vibeorm/schema\";\nimport { finiteNumber } from \"../shared.ts\";\nimport { ParamCollector, getSqlDialect, renderExpr, renderSqliteFtsUpdateTrigger } from \"@vibeorm/sql\";\nimport type { SqlDialect } from \"@vibeorm/sql\";\nimport type { ExtensionMountContext } from \"@vibeorm/runtime\";\nimport {\n columnNameOf,\n declNumberArray,\n declString,\n declStringArray,\n isSafeSqlName,\n pgExtensionCheck,\n primaryKeyFieldOf,\n tableNameOf,\n} from \"../shared.ts\";\n\n// ─── Options & types ──────────────────────────────────────────────\n\nexport type SearchEngine = \"bm25\" | \"tsvector\" | \"fts5\" | \"fulltext\";\n\nexport type SearchOptions = {\n /** Engine choice; `\"auto\"` (default) resolves per dialect. */\n readonly engine?: SearchEngine | \"auto\";\n /** Default text-search language for the tsvector engine (default \"english\"). */\n readonly language?: string;\n /**\n * Ranking function for the tsvector engine (default `\"ts_rank_cd\"`).\n * `ts_rank` ignores cover density, so the two order results differently —\n * consumers migrating hand-written `ts_rank` queries keep their ordering by\n * setting this. A per-model `rank:` declaration overrides the default.\n */\n readonly rank?: \"ts_rank\" | \"ts_rank_cd\";\n};\n\n/** One search hit. `highlights` is `null` on engines without highlighting. */\nexport type SearchHit<T> = {\n readonly row: T;\n readonly score: number;\n readonly highlights: readonly string[] | null;\n};\n\nconst VERSION = \"2.0.0-alpha.2\";\nconst SCORE_ALIAS = \"_vibe_score\";\nconst HIGHLIGHT_ALIAS = \"_vibe_highlight\";\nconst WEIGHT_LETTERS = [\"A\", \"B\", \"C\", \"D\"] as const;\n\n// ─── bm25 tokenizer declaration ───────────────────────────────────\n\n/** ParadeDB tokenizers this extension emits as `pdb.<name>` casts (each verified on 0.25.7). */\nconst BM25_TOKENIZERS: readonly string[] = Object.freeze([\n \"unicode_words\", \"simple\", \"whitespace\", \"ngram\", \"edge_ngram\", \"icu\",\n \"literal_normalized\", \"source_code\", \"chinese_compatible\", \"jieba\",\n]);\n\n/** Snowball languages ParadeDB's `stemmer=<language>` filter accepts (spelled lowercase in the index). */\nconst STEMMER_LANGUAGES: readonly string[] = Object.freeze([\n \"arabic\", \"czech\", \"danish\", \"dutch\", \"english\", \"finnish\", \"french\", \"german\", \"greek\", \"hungarian\",\n \"italian\", \"norwegian\", \"polish\", \"portuguese\", \"romanian\", \"russian\", \"spanish\", \"swedish\", \"tamil\", \"turkish\",\n]);\n\n/** Stopword lists ParadeDB ships (`stopwords_language=`); both lists are matched case-insensitively. */\nconst STOPWORD_LANGUAGES: readonly string[] = Object.freeze([\n \"czech\", \"danish\", \"dutch\", \"english\", \"finnish\", \"french\", \"german\", \"hungarian\", \"italian\", \"norwegian\",\n \"polish\", \"portuguese\", \"russian\", \"spanish\", \"swedish\",\n]);\n\nconst TOKENIZER_KEYS = [\"tokenizer\", \"minGram\", \"maxGram\", \"stemmer\", \"asciiFolding\", \"lowercase\", \"stopwordsLanguage\"] as const;\n\n/** The bm25 index tokenizer of one model's searched fields, normalized from `@@search`. */\ntype Bm25Tokenizer = {\n readonly name: string;\n /** Gram bounds — present exactly for `ngram` / `edge_ngram`. */\n readonly minGram?: number;\n readonly maxGram?: number;\n /** Token filters in emission order: `lowercase=false`, `ascii_folding=true`, `stemmer=…`, `stopwords_language=…`. */\n readonly filters: readonly string[];\n};\n\nfunction isPositiveInteger(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 1;\n}\n\n/**\n * Read the tokenizer keys of a `@@search` declaration. `undefined` when none\n * is declared (ParadeDB's default `unicode_words` applies); `{ error }` names\n * the first malformed key — the attribute parser and the artifact/client\n * readers share this so the schema plane and the generate plane agree.\n */\nfunction readBm25Tokenizer(\n declaration: Readonly<Record<string, unknown>>,\n): { readonly tokenizer: Bm25Tokenizer | undefined } | { readonly error: string } {\n if (TOKENIZER_KEYS.every((key) => declaration[key] === undefined)) return { tokenizer: undefined };\n const name = declaration[\"tokenizer\"] ?? \"unicode_words\";\n if (typeof name !== \"string\" || !BM25_TOKENIZERS.includes(name)) {\n return { error: `tokenizer must be one of ${BM25_TOKENIZERS.join(\", \")}` };\n }\n const grams = name === \"ngram\" || name === \"edge_ngram\";\n const minGram = declaration[\"minGram\"];\n const maxGram = declaration[\"maxGram\"];\n if (grams) {\n if (!isPositiveInteger(minGram) || !isPositiveInteger(maxGram) || minGram > maxGram) {\n return { error: `${name} needs integer minGram and maxGram with 1 <= minGram <= maxGram` };\n }\n } else if (minGram !== undefined || maxGram !== undefined) {\n return { error: \"minGram/maxGram apply only to the ngram and edge_ngram tokenizers\" };\n }\n const filters: string[] = [];\n const lowercase = declaration[\"lowercase\"];\n if (lowercase !== undefined && typeof lowercase !== \"boolean\") return { error: \"lowercase must be a boolean\" };\n if (lowercase === false) filters.push(\"lowercase=false\");\n const folding = declaration[\"asciiFolding\"];\n if (folding !== undefined && typeof folding !== \"boolean\") return { error: \"asciiFolding must be a boolean\" };\n if (folding === true) filters.push(\"ascii_folding=true\");\n const stemmer = declaration[\"stemmer\"];\n if (stemmer !== undefined) {\n if (typeof stemmer !== \"string\" || !STEMMER_LANGUAGES.includes(stemmer.toLowerCase())) {\n return { error: `stemmer must be one of ${STEMMER_LANGUAGES.join(\", \")}` };\n }\n filters.push(`stemmer=${stemmer.toLowerCase()}`);\n }\n const stopwords = declaration[\"stopwordsLanguage\"];\n if (stopwords !== undefined) {\n // One language: the edge_ngram search_tokenizer option is itself a\n // comma-separated argument list, so a comma inside a value is ambiguous.\n if (typeof stopwords !== \"string\" || !STOPWORD_LANGUAGES.includes(stopwords.toLowerCase())) {\n return { error: `stopwordsLanguage must be one of ${STOPWORD_LANGUAGES.join(\", \")}` };\n }\n filters.push(`stopwords_language=${stopwords.toLowerCase()}`);\n }\n return {\n tokenizer: {\n name,\n ...(grams ? { minGram: minGram as number, maxGram: maxGram as number } : {}),\n filters,\n },\n };\n}\n\n/** `pdb.<tokenizer>(min, max, 'filter=…')` — grams as bare integers, filters as string literals; no parens when empty. */\nfunction bm25TokenizerArguments(params: { dialect: SqlDialect; tokenizer: Bm25Tokenizer }): string {\n const { dialect, tokenizer } = params;\n const args = [\n ...(tokenizer.minGram === undefined ? [] : [String(tokenizer.minGram), String(tokenizer.maxGram)]),\n ...tokenizer.filters.map((filter) => dialect.quoteStringLiteral({ value: filter })),\n ];\n return args.length === 0 ? \"\" : `(${args.join(\", \")})`;\n}\n\n/** One searched column of the bm25 column list: bare, or wrapped in the declared tokenizer cast. */\nfunction bm25TokenizedColumn(params: { dialect: SqlDialect; column: string; tokenizer: Bm25Tokenizer | undefined }): string {\n const { dialect, column, tokenizer } = params;\n const quoted = dialect.quoteIdent({ name: column });\n if (tokenizer === undefined) return quoted;\n return `(${quoted}::pdb.${tokenizer.name}${bm25TokenizerArguments({ dialect, tokenizer })})`;\n}\n\n/**\n * Query-time tokenizer. An edge_ngram index must NOT tokenize the query into\n * edge ngrams as well (\"sho\" → s, sh, sho would match every s-word), so the\n * query goes through `unicode_words` with the same token filters. Every other\n * tokenizer keeps ParadeDB's default: the same tokenizer at both ends.\n */\nfunction bm25SearchTokenizer(params: { tokenizer: Bm25Tokenizer | undefined }): string | undefined {\n const { tokenizer } = params;\n if (tokenizer === undefined || tokenizer.name !== \"edge_ngram\") return undefined;\n return tokenizer.filters.length === 0 ? \"unicode_words\" : `unicode_words(${tokenizer.filters.join(\",\")})`;\n}\n\n/** Scalar columns ParadeDB pushes `WHERE` filters into the index for (docs: filtering/indexed). */\nconst BM25_FILTER_SCALARS: readonly string[] = Object.freeze([\"Boolean\", \"Int\", \"BigInt\", \"Float\", \"Decimal\", \"DateTime\"]);\n\n/** ParadeDB caps `pdb.boost` factors at ±2048. */\nconst BM25_BOOST_LIMIT = 2048;\n\n/**\n * The PostgreSQL storage of a `String` field that ParadeDB can put in a bm25\n * index: `text` / `varchar` as text, `uuid` as a literal key. `char(n)` is\n * refused by 0.25.7 at CREATE INDEX (\"Type 1042 is not yet supported\"), and\n * bit, xml, inet, citext and `Unsupported(…)` columns are not text either —\n * they are refused here, at generate time, instead of at the migration.\n */\nfunction bm25StringStorage(field: FieldIR): \"text\" | \"uuid\" | null {\n if (field.isUnsupported === true || field.isList || field.type !== \"String\") return null;\n const native = (field.nativeType ?? \"\").toLowerCase();\n if (native === \"\" || native === \"text\" || native.startsWith(\"varchar\")) return \"text\";\n if (native === \"uuid\") return \"uuid\";\n return null;\n}\n\nfunction describeField(field: FieldIR): string {\n const scalar = typeof field.type === \"string\" ? field.type : `enum ${field.type.enum}`;\n return `${scalar}${field.isList ? \"[]\" : \"\"}${field.nativeType === undefined ? \"\" : ` @db.${field.nativeType}`}`;\n}\n\n/**\n * The column list of one model's bm25 index, validated once for BOTH planes\n * (the migrate artifact and the generated client agree on what is refused):\n * a single-column primary key (ParadeDB's `key_field`), searched fields of\n * text storage, and `filterFields` — text columns get the `pdb.literal`\n * tokenizer for equality/IN pushdown, uuid, numeric, boolean and timestamp\n * columns are indexed as-is, anything else is refused rather than indexed\n * under semantics ParadeDB does not push down.\n */\nfunction bm25IndexColumns(params: { model: ModelIR; decl: SearchDecl }): {\n readonly pkColumn: string;\n readonly searched: readonly string[];\n readonly filters: readonly { readonly column: string; readonly literal: boolean }[];\n} {\n const { model, decl } = params;\n const pkField = primaryKeyFieldOf({ model });\n if (pkField === null) {\n throw new VibeError({\n code: \"VIBE_SCHEMA_INVALID\",\n message: `@@search with engine \"bm25\" needs a single-column primary key on \"${model.name}\" (key_field)`,\n meta: { model: model.name },\n });\n }\n const searched = decl.fields.map((name) => {\n const field = model.fields.find((candidate) => candidate.name === name);\n if (field === undefined) return columnNameOf({ model, field: name });\n if (bm25StringStorage(field) !== \"text\") {\n throw unsupportedCapability({\n dialect: \"postgres\",\n feature: `@@search field \"${name}\" (${describeField(field)}) on the bm25 index`,\n hint: \"ParadeDB tokenizes text and varchar columns; search another column or store this one as text\",\n });\n }\n return field.dbName ?? field.name;\n });\n const filters = (decl.filterFields ?? []).map((name) => {\n const field = model.fields.find((candidate) => candidate.name === name);\n if (field === undefined || name === pkField || decl.fields.includes(name)) {\n throw new VibeError({\n code: \"VIBE_SCHEMA_INVALID\",\n message: `@@search filterFields on \"${model.name}\": \"${name}\" must be a scalar field that is neither the primary key nor a searched field`,\n meta: { model: model.name, field: name },\n });\n }\n const column = field.dbName ?? field.name;\n const storage = bm25StringStorage(field);\n if (storage !== null) return { column, literal: storage === \"text\" };\n if (!field.isList && typeof field.type === \"string\" && BM25_FILTER_SCALARS.includes(field.type)) return { column, literal: false };\n throw unsupportedCapability({\n dialect: \"postgres\",\n feature: `@@search filterFields entry \"${name}\" (${describeField(field)}) on the bm25 index`,\n hint: \"ParadeDB pushes filters into the index for text/varchar (exact match / IN), uuid, Boolean, Int, BigInt, Float, Decimal and DateTime columns — filter other columns outside the index\",\n });\n });\n return { pkColumn: columnNameOf({ model, field: pkField }), searched, filters };\n}\n\n// ─── Engine resolution (generate/migrate time) ────────────────────\n\n/** Resolve the engine for a dialect; throws on impossible explicit choices. */\nexport function resolveSearchEngine(params: {\n dialect: Dialect;\n provider: Provider;\n requested: SearchEngine | \"auto\";\n}): SearchEngine {\n const { dialect, provider, requested } = params;\n const native: SearchEngine = dialect === \"postgres\" ? \"tsvector\" : dialect === \"sqlite\" ? \"fts5\" : \"fulltext\";\n if (requested === \"auto\") return native;\n if (requested === native) return native;\n if (requested === \"bm25\" && dialect === \"postgres\") {\n if (provider === \"pglite\") {\n throw unsupportedCapability({\n dialect,\n feature: 'the search engine \"bm25\" (ParadeDB pg_search)',\n hint: 'pglite does not bundle pg_search — use engine \"auto\"/\"tsvector\"',\n });\n }\n return \"bm25\";\n }\n throw unsupportedCapability({\n dialect,\n feature: `the search engine \"${requested}\"`,\n hint: `this dialect supports \"${native}\"${dialect === \"postgres\" ? ' and \"bm25\"' : \"\"}`,\n });\n}\n\n// ─── Declaration ──────────────────────────────────────────────────\n\ntype SearchDecl = {\n readonly fields: readonly string[];\n readonly weights: readonly number[] | undefined;\n readonly base: string;\n readonly language: string | undefined;\n /** Override for the tsvector column name (adopt-existing; tsvector engine only). */\n readonly columnName: string | undefined;\n /** Override for the index name (tsvector / bm25 / fulltext engines). */\n readonly indexName: string | undefined;\n /** Ranking function override (tsvector engine only). */\n readonly rank: \"ts_rank\" | \"ts_rank_cd\" | undefined;\n /** Index tokenizer applied to every searched field (bm25 engine only). */\n readonly tokenizer: Bm25Tokenizer | undefined;\n /** Non-searched columns added to the bm25 index so `where` filters on them run inside the index scan (bm25 engine only). */\n readonly filterFields: readonly string[] | undefined;\n};\n\nfunction declRank(\n declaration: Readonly<Record<string, unknown>>,\n): \"ts_rank\" | \"ts_rank_cd\" | undefined {\n const value = declaration[\"rank\"];\n return value === \"ts_rank\" || value === \"ts_rank_cd\" ? value : undefined;\n}\n\nfunction readDeclaration(params: {\n model: ModelIR;\n declaration: Readonly<Record<string, unknown>>;\n}): SearchDecl | null {\n const fields = declStringArray(params.declaration, \"fields\");\n if (fields === undefined || fields.length === 0) return null;\n if (params.declaration[\"weights\"] !== undefined && (\n !Array.isArray(params.declaration[\"weights\"]) ||\n params.declaration[\"weights\"].length !== fields.length ||\n params.declaration[\"weights\"].some((weight: unknown) => typeof weight !== \"number\" || !Number.isFinite(weight))\n )) {\n throw new VibeError({ code: \"VIBE_SCHEMA_INVALID\", message: \"weights must be a finite number list matching the field list\", meta: { model: params.model.name } });\n }\n const tokenizer = readBm25Tokenizer(params.declaration);\n if (\"error\" in tokenizer) {\n throw new VibeError({ code: \"VIBE_SCHEMA_INVALID\", message: `@@search on \"${params.model.name}\": ${tokenizer.error}`, meta: { model: params.model.name } });\n }\n return {\n fields,\n weights: declNumberArray(params.declaration, \"weights\"),\n base: declString(params.declaration, \"name\") ?? `${tableNameOf({ model: params.model })}_search`,\n language: declString(params.declaration, \"language\"),\n columnName: declString(params.declaration, \"columnName\"),\n indexName: declString(params.declaration, \"indexName\"),\n rank: declRank(params.declaration),\n tokenizer: tokenizer.tokenizer,\n filterFields: declStringArray(params.declaration, \"filterFields\"),\n };\n}\n\nfunction resolveLanguage(params: {\n model: ModelIR;\n decl: SearchDecl;\n options: Readonly<Record<string, unknown>>;\n}): string {\n const language = params.decl.language ?? (params.options[\"language\"] as string | undefined) ?? \"english\";\n if (!isSafeSqlName({ name: language })) {\n throw new VibeError({\n code: \"VIBE_SCHEMA_INVALID\",\n message: `search language \"${language}\" is not a plain text-search config name`,\n meta: { model: params.model.name, language },\n });\n }\n return language;\n}\n\n/** Weight letter per field: highest weight → 'A', capped at 'D'. */\nfunction weightLetters(params: { decl: SearchDecl }): readonly string[] | null {\n const { decl } = params;\n if (decl.weights === undefined || decl.weights.length !== decl.fields.length) return null;\n const ranked = [...new Set(decl.weights)].sort((a, b) => b - a);\n return decl.weights.map(\n (weight) => WEIGHT_LETTERS[Math.min(ranked.indexOf(weight), WEIGHT_LETTERS.length - 1)] as string,\n );\n}\n\n/**\n * Artifact names with the adoption overrides applied (`columnName:` /\n * `indexName:` — declared independently so a consumer with LIVE objects can\n * adopt them without a rename migration). Engines without the named object\n * refuse the override instead of silently ignoring it.\n */\nfunction resolveArtifactNames(params: {\n decl: SearchDecl;\n engine: SearchEngine;\n dialect: Dialect;\n}): { tsvColumn: string; indexName: string } {\n const { decl, engine, dialect } = params;\n if (engine === \"fts5\" && (decl.columnName !== undefined || decl.indexName !== undefined)) {\n throw unsupportedCapability({\n dialect,\n feature: \"@@search columnName/indexName overrides on the fts5 engine\",\n hint: \"fts5 emits a shadow table + triggers — there is no column or index to rename; use name: to change the table base\",\n });\n }\n if (decl.columnName !== undefined && engine !== \"tsvector\") {\n throw unsupportedCapability({\n dialect,\n feature: `@@search columnName on the ${engine} engine`,\n hint: \"only the tsvector engine materializes a generated column — drop columnName or target tsvector\",\n });\n }\n // Minted names are shortened to the dialect's identifier limit (the same\n // rule migrate's default index names follow); an explicit override past the\n // limit is refused here — PostgreSQL would silently truncate it, the\n // ownership handshake with migrate would then miss the real object, and\n // every push would DROP and recreate the column and index.\n const remedy = \"Shorten it, or set a shorter `name:` on the @@search declaration.\";\n if (decl.columnName !== undefined) assertIdentifierFits({ name: decl.columnName, dialect, what: \"@@search columnName\", remedy });\n if (decl.indexName !== undefined) assertIdentifierFits({ name: decl.indexName, dialect, what: \"@@search indexName\", remedy });\n return {\n tsvColumn: decl.columnName ?? fitDefaultName({ prefix: decl.base, suffix: \"_tsv\", dialect }),\n indexName: decl.indexName ?? fitDefaultName({ prefix: decl.base, suffix: \"_idx\", dialect }),\n };\n}\n\n/**\n * Ranking function for the tsvector engine: the model's `rank:` declaration\n * wins over the factory-level default. A DECLARED rank on any other engine is\n * refused (it names postgres ranking functions); the factory default is just\n * a default and stays inert off tsvector.\n */\nfunction resolveRank(params: {\n decl: SearchDecl;\n options: Readonly<Record<string, unknown>>;\n engine: SearchEngine;\n dialect: Dialect;\n}): \"ts_rank\" | \"ts_rank_cd\" | undefined {\n const { decl, options, engine, dialect } = params;\n if (decl.weights !== undefined && engine !== \"tsvector\" && engine !== \"bm25\") {\n throw unsupportedCapability({\n dialect,\n feature: `@@search weights on the ${engine} engine`,\n hint: \"weights are supported by tsvector (setweight classes) and bm25 (pdb.boost factors); omit weights for this engine\",\n });\n }\n if (decl.weights !== undefined && engine === \"bm25\" && decl.weights.some((weight) => Math.abs(weight) > BM25_BOOST_LIMIT)) {\n throw new VibeError({\n code: \"VIBE_SCHEMA_INVALID\",\n message: `@@search weights on the bm25 engine are pdb.boost factors and must stay within ±${BM25_BOOST_LIMIT}`,\n meta: { weights: decl.weights },\n });\n }\n if (decl.rank !== undefined && engine !== \"tsvector\") {\n throw unsupportedCapability({\n dialect,\n feature: `@@search rank on the ${engine} engine`,\n hint: \"rank chooses between the postgres ranking functions ts_rank and ts_rank_cd — a tsvector-only concept\",\n });\n }\n if (engine !== \"tsvector\") return undefined;\n const fallback = options[\"rank\"];\n return decl.rank ?? (fallback === \"ts_rank\" || fallback === \"ts_rank_cd\" ? fallback : undefined);\n}\n\n/**\n * bm25 index declarations (`tokenizer`, `minGram`/`maxGram`, token filters,\n * `filterFields`) name ParadeDB concepts; every other engine refuses them at\n * the earliest knowable moment instead of silently building a different index.\n */\nfunction assertBm25Declarations(params: { decl: SearchDecl; engine: SearchEngine; dialect: Dialect }): void {\n const { decl, engine, dialect } = params;\n if (engine === \"bm25\") return;\n if (decl.tokenizer !== undefined) {\n throw unsupportedCapability({\n dialect,\n feature: `@@search tokenizer options on the ${engine} engine`,\n hint: 'tokenizer, minGram, maxGram, stemmer, asciiFolding, lowercase and stopwordsLanguage configure a ParadeDB bm25 index — drop them, or opt in with search({ engine: \"bm25\" })',\n });\n }\n if (decl.filterFields !== undefined) {\n throw unsupportedCapability({\n dialect,\n feature: `@@search filterFields on the ${engine} engine`,\n hint: 'filterFields add columns to a ParadeDB bm25 index for filter pushdown — drop them, or opt in with search({ engine: \"bm25\" })',\n });\n }\n}\n\nfunction tsvectorExpression(params: {\n dialect: SqlDialect;\n model: ModelIR;\n decl: SearchDecl;\n language: string;\n}): string {\n const { dialect, model, decl, language } = params;\n const quote = (name: string): string => dialect.quoteIdent({ name });\n const letters = weightLetters({ decl });\n const parts = decl.fields.map((field, index) => {\n const vector = `to_tsvector('${language}', coalesce(${quote(columnNameOf({ model, field }))}, ''))`;\n return letters === null ? vector : `setweight(${vector}, '${letters[index]}')`;\n });\n return letters === null && parts.length > 1\n ? `to_tsvector('${language}', ${decl.fields\n .map((field) => `coalesce(${quote(columnNameOf({ model, field }))}, '')`)\n .join(\" || ' ' || \")})`\n : parts.join(\" || \");\n}\n\n// ─── Artifacts ────────────────────────────────────────────────────\n\nfunction searchArtifacts(ctx: ExtensionSchemaContext): readonly ExtensionArtifact[] {\n const requested = (ctx.options[\"engine\"] as SearchEngine | \"auto\" | undefined) ?? \"auto\";\n const engine = resolveSearchEngine({ dialect: ctx.dialect, provider: ctx.provider, requested });\n const dialect = getSqlDialect({ dialect: ctx.dialect });\n const quote = (name: string): string => dialect.quoteIdent({ name });\n const artifacts: ExtensionArtifact[] = [];\n\n if (engine === \"bm25\") {\n // pg_search 0.25+ declares `requires = 'vector'`; CASCADE installs that\n // prerequisite where it is missing (a pristine template0 shadow, a server\n // without the ParadeDB image's template1 pre-installs) instead of failing.\n artifacts.push({\n id: \"search:extension:pg_search\",\n owns: { kind: \"postgresExtension\", name: \"pg_search\" },\n create: [\"CREATE EXTENSION IF NOT EXISTS pg_search CASCADE;\"],\n drop: [\"DROP EXTENSION IF EXISTS pg_search;\"],\n });\n }\n\n for (const [modelName, declaration] of Object.entries(ctx.declarations)) {\n const model = ctx.schema.models.find((candidate) => candidate.name === modelName);\n if (model === undefined) continue;\n const decl = readDeclaration({ model, declaration });\n if (decl === null) continue;\n const language = resolveLanguage({ model, decl, options: ctx.options });\n const table = tableNameOf({ model });\n const columns = decl.fields.map((field) => columnNameOf({ model, field }));\n resolveRank({ decl, options: ctx.options, engine, dialect: ctx.dialect });\n assertBm25Declarations({ decl, engine, dialect: ctx.dialect });\n\n if (engine === \"tsvector\") {\n const { tsvColumn, indexName } = resolveArtifactNames({ decl, engine, dialect: ctx.dialect });\n const expression = tsvectorExpression({ dialect, model, decl, language });\n artifacts.push(\n {\n id: `search:${modelName}:column`,\n owns: { kind: \"column\", table, column: tsvColumn },\n create: [\n `ALTER TABLE ${quote(table)} ADD COLUMN IF NOT EXISTS ${quote(tsvColumn)} tsvector GENERATED ALWAYS AS (${expression}) STORED;`,\n ],\n drop: [`ALTER TABLE ${quote(table)} DROP COLUMN IF EXISTS ${quote(tsvColumn)};`],\n },\n {\n id: `search:${modelName}:index`,\n owns: { kind: \"index\", table, name: indexName },\n create: [\n `CREATE INDEX IF NOT EXISTS ${quote(indexName)} ON ${quote(table)} USING gin (${quote(tsvColumn)});`,\n ],\n drop: [`DROP INDEX IF EXISTS ${quote(indexName)};`],\n },\n );\n continue;\n }\n\n if (engine === \"bm25\") {\n const { pkColumn, searched, filters } = bm25IndexColumns({ model, decl });\n const bm25Names = resolveArtifactNames({ decl, engine, dialect: ctx.dialect });\n // Column list: key field first (ParadeDB requires it), searched columns\n // through the declared tokenizer cast, then filter columns. Every shape\n // here was verified against ParadeDB 0.25.7 (`pdb.<tokenizer>(…)` casts,\n // `search_tokenizer` WITH option).\n const indexed = [\n quote(pkColumn),\n ...searched.map((column) => bm25TokenizedColumn({ dialect, column, tokenizer: decl.tokenizer })),\n ...filters.map((filter) => (filter.literal ? `(${quote(filter.column)}::pdb.literal)` : quote(filter.column))),\n ];\n const withOptions = [`key_field=${dialect.quoteStringLiteral({ value: pkColumn })}`];\n const searchTokenizer = bm25SearchTokenizer({ tokenizer: decl.tokenizer });\n if (searchTokenizer !== undefined) withOptions.push(`search_tokenizer=${dialect.quoteStringLiteral({ value: searchTokenizer })}`);\n artifacts.push({\n id: `search:${modelName}:index`,\n owns: { kind: \"index\", table, name: bm25Names.indexName },\n create: [\n `CREATE INDEX IF NOT EXISTS ${quote(bm25Names.indexName)} ON ${quote(table)} USING bm25 (${indexed.join(\", \")}) WITH (${withOptions.join(\", \")});`,\n ],\n drop: [`DROP INDEX IF EXISTS ${quote(bm25Names.indexName)};`],\n });\n continue;\n }\n\n if (engine === \"fts5\") {\n resolveArtifactNames({ decl, engine, dialect: ctx.dialect });\n // N7 (SQL review): the external-content table and its sync triggers key\n // on the host's ROWID. Only a single Int/BigInt @id aliases (and pins)\n // the rowid; any other primary key leaves an IMPLICIT rowid that VACUUM\n // may renumber — a documented sqlite non-guarantee — after which the\n // shadow table silently points at the WRONG rows. Refuse at generate\n // time (earliest-moment rule); recovery for a database that already\n // drifted is `INSERT INTO <fts>(<fts>) VALUES('rebuild')`.\n const pkName = primaryKeyFieldOf({ model });\n const pkField = pkName === null ? undefined : model.fields.find((field) => field.name === pkName);\n const pkIsRowidAlias =\n pkField !== undefined && !pkField.isList && (pkField.type === \"Int\" || pkField.type === \"BigInt\");\n if (!pkIsRowidAlias) {\n throw unsupportedCapability({\n dialect: \"sqlite\",\n feature: `@@search (fts5) on \"${modelName}\", whose primary key is not a single INTEGER column`,\n hint: \"fts5 external-content tables key on the rowid; only an Int/BigInt @id aliases and PINS it — any other key's implicit rowid can be renumbered by VACUUM, silently mis-linking search hits. Give the searched model an Int/BigInt @id.\",\n });\n }\n const fts = `${decl.base}_fts`;\n const columnList = columns.map((column) => quote(column)).join(\", \");\n const newList = columns.map((column) => `new.${quote(column)}`).join(\", \");\n const oldList = columns.map((column) => `old.${quote(column)}`).join(\", \");\n const updateTrigger = renderSqliteFtsUpdateTrigger({ table, ftsTable: fts, columns });\n artifacts.push(\n {\n id: `search:${modelName}:fts5-table`,\n owns: {\n kind: \"table\",\n name: fts,\n auxiliaryPrefixes: [`${fts}_`],\n },\n create: [\n // The host table name is a STRING literal here: a `@@map` containing a\n // quote must be escaped like any other literal, never spliced.\n `CREATE VIRTUAL TABLE IF NOT EXISTS ${quote(fts)} USING fts5(${columnList}, content=${dialect.quoteStringLiteral({ value: table })});`,\n ],\n drop: [`DROP TABLE IF EXISTS ${quote(fts)};`],\n },\n {\n id: `search:${modelName}:fts5-triggers`,\n owns: { kind: \"trigger\", table, name: `${fts}_ai` },\n create: [\n `CREATE TRIGGER IF NOT EXISTS ${quote(`${fts}_ai`)} AFTER INSERT ON ${quote(table)} BEGIN\\n INSERT INTO ${quote(fts)}(rowid, ${columnList}) VALUES (new.rowid, ${newList});\\nEND;`,\n `CREATE TRIGGER IF NOT EXISTS ${quote(`${fts}_ad`)} AFTER DELETE ON ${quote(table)} BEGIN\\n INSERT INTO ${quote(fts)}(${quote(fts)}, rowid, ${columnList}) VALUES ('delete', old.rowid, ${oldList});\\nEND;`,\n updateTrigger.create,\n // Installation runs in the migration transaction after synchronization is installed.\n `INSERT INTO ${quote(fts)}(${quote(fts)}) VALUES ('rebuild');`,\n ],\n drop: [\n `DROP TRIGGER IF EXISTS ${quote(`${fts}_ai`)};`,\n `DROP TRIGGER IF EXISTS ${quote(`${fts}_ad`)};`,\n `DROP TRIGGER IF EXISTS ${quote(`${fts}_au`)};`,\n ],\n },\n {\n id: `search:${modelName}:fts5-update-trigger`,\n owns: { kind: \"trigger\", table, name: `${fts}_au` },\n expected: {\n identity: { kind: \"trigger\", table: { namespace: \"main\", name: table }, name: `${fts}_au` },\n facts: { definition: updateTrigger.definition, type: \"trigger\" },\n dependencies: [],\n format: \"catalog-v2\",\n },\n // The legacy bundle still installs all three triggers before rebuilding.\n // This record supplies independent UPDATE ownership, not duplicate DDL.\n create: [],\n drop: [],\n },\n );\n continue;\n }\n\n // fulltext (mysql): no IF NOT EXISTS form exists — migrate's presence\n // check (introspected index name) decides whether the create runs.\n const fulltextNames = resolveArtifactNames({ decl, engine, dialect: ctx.dialect });\n artifacts.push({\n id: `search:${modelName}:index`,\n owns: { kind: \"index\", table, name: fulltextNames.indexName },\n create: [\n `CREATE FULLTEXT INDEX ${quote(fulltextNames.indexName)} ON ${quote(table)} (${columns\n .map((column) => quote(column))\n .join(\", \")});`,\n ],\n drop: [`DROP INDEX ${quote(fulltextNames.indexName)} ON ${quote(table)};`],\n });\n }\n return artifacts;\n}\n\n// ─── Runtime delegate (generated-glue target) ─────────────────────\n\n/** Baked per-model config the generator writes into the client. */\nexport type SearchDelegateConfig = {\n readonly engine: SearchEngine;\n readonly table: string;\n /** Searched fields in declaration order → column names. */\n readonly fields: readonly { readonly name: string; readonly column: string }[];\n /** tsvector engine: the generated column. */\n readonly tsvColumn?: string;\n /** fts5 engine: the shadow table. */\n readonly ftsTable?: string;\n /** tsvector engine: regconfig name (validated identifier). */\n readonly language?: string;\n /** tsvector engine: ranking function (default `ts_rank_cd`). */\n readonly rank?: \"ts_rank\" | \"ts_rank_cd\";\n /** Single-column primary key: bm25's key_field and the `select: \"id\"` projection. */\n readonly pkColumn?: string;\n /** bm25 engine: per-field `pdb.boost` factors (the declared `weights`), parallel to `fields`. */\n readonly boosts?: readonly number[];\n /** bm25 engine: the index tokenizer is edge_ngram, so every query token already matches as a prefix. */\n readonly prefixIndex?: boolean;\n};\n\nexport type SearchArgs = {\n readonly query: string;\n readonly where?: Record<string, unknown>;\n readonly take?: number;\n readonly skip?: number;\n readonly minScore?: number;\n readonly highlight?: { readonly field: string };\n /**\n * Type-ahead prefix matching. The query is split into unicode\n * letter/number tokens (punctuation separates and never matches) and every\n * token matches as a prefix — \"piz\" finds \"Pizza\". Per engine: tsvector\n * builds `to_tsquery('lang', 'piz:*')` (lexemes are STEMMED, so a stemming\n * `language` can miss mid-stem prefixes — declare `language: \"simple\"` for\n * type-ahead columns), fts5 `\"piz\"*`, fulltext `+piz*` in BOOLEAN MODE\n * (InnoDB ignores tokens under `innodb_ft_min_token_size`, default 3). A\n * query with no tokens resolves to `[]` without touching the database.\n * On bm25 the index decides: an `edge_ngram` tokenizer declaration makes\n * every token a prefix (`&&&` match-all over the raw query); otherwise\n * combine with `fuzzy` (`pdb.fuzzy(n, t)`), or the call is refused with\n * `VIBE_UNSUPPORTED_CAPABILITY`.\n */\n readonly prefix?: boolean;\n /**\n * Typo tolerance (bm25 engine only): every query token matches within this\n * Levenshtein edit distance — `1` accepts one insertion, deletion or\n * substitution (\"storgate\" finds \"Storgata\"); ParadeDB caps it at `2`, and\n * a transposition costs 2. Switches the match to `&&&` (every token must\n * match) and combines with `prefix`. Other engines refuse it\n * (`VIBE_UNSUPPORTED_CAPABILITY`) — use the trgm extension's `similarTo`\n * there.\n */\n readonly fuzzy?: 1 | 2;\n /**\n * Projection: `\"row\"` (default) returns full rows; `\"id\"` returns only the\n * primary key — for repositories that re-read hits through their own typed\n * queries (full rows are wasted bandwidth there).\n */\n readonly select?: \"row\" | \"id\";\n};\n\nexport type SearchMethod = (args: SearchArgs) => Promise<SearchHit<Record<string, unknown>>[]>;\n\nfunction highlightColumn(params: { config: SearchDelegateConfig; args: SearchArgs }): string | null {\n const { config, args } = params;\n if (args.highlight === undefined) return null;\n const field = config.fields.find((candidate) => candidate.name === args.highlight?.field);\n if (field === undefined) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `highlight.field \"${args.highlight.field}\" is not searched — declared: ${config.fields\n .map((candidate) => candidate.name)\n .join(\", \")}`,\n meta: { field: args.highlight.field },\n });\n }\n return field.column;\n}\n\nfunction shapeHits(params: {\n rows: readonly Record<string, unknown>[];\n highlightsRequested: boolean;\n engineHasHighlights: boolean;\n}): SearchHit<Record<string, unknown>>[] {\n const { rows, highlightsRequested, engineHasHighlights } = params;\n return rows.map((raw) => {\n const row: Record<string, unknown> = { ...raw };\n const score = Number(row[SCORE_ALIAS]);\n const highlight = row[HIGHLIGHT_ALIAS];\n delete row[SCORE_ALIAS];\n delete row[HIGHLIGHT_ALIAS];\n const highlights =\n engineHasHighlights && highlightsRequested && highlight !== null && highlight !== undefined\n ? [String(highlight)]\n : null;\n return { row, score, highlights };\n });\n}\n\n/**\n * The generated client's glue target: bakes engine + names at generate time,\n * mounts as the `search` method at runtime (D9).\n */\nexport function createSearchDelegate(\n config: SearchDelegateConfig,\n): (ctx: ExtensionMountContext) => SearchMethod {\n return (ctx: ExtensionMountContext): SearchMethod => {\n const dialect = ctx.dialect;\n const quote = (name: string): string => dialect.quoteIdent({ name });\n const t = quote(\"t\");\n\n return async (args: SearchArgs): Promise<SearchHit<Record<string, unknown>>[]> => {\n if (typeof args.query !== \"string\" || args.query.trim().length === 0) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: \"search requires a non-empty query string\",\n meta: { table: config.table },\n });\n }\n const take = finiteNumber({ value: args.take === undefined ? 10 : args.take, name: \"take\", integer: true, min: 1 });\n const skip = args.skip === undefined ? null : finiteNumber({ value: args.skip, name: \"skip\", integer: true, min: 0 });\n if (args.minScore !== undefined) finiteNumber({ value: args.minScore, name: \"minScore\" });\n const prefix = args.prefix === true;\n const fuzzy = args.fuzzy;\n if (fuzzy !== undefined && fuzzy !== 1 && fuzzy !== 2) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: \"fuzzy must be 1 or 2 (ParadeDB caps the edit distance at 2)\",\n meta: { argument: \"fuzzy\" },\n });\n }\n if (fuzzy !== undefined && config.engine !== \"bm25\") {\n throw unsupportedCapability({\n dialect: dialect.name,\n feature: `search fuzzy matching on the \"${config.engine}\" engine`,\n hint: \"only the bm25 engine (ParadeDB pdb.fuzzy) matches within an edit distance — on postgres/pglite declare @@trgm and filter with similarTo\",\n });\n }\n if (prefix && config.engine === \"bm25\" && config.prefixIndex !== true && fuzzy === undefined) {\n throw unsupportedCapability({\n dialect: \"postgres\",\n feature: 'search prefix mode on the \"bm25\" engine without an edge_ngram index',\n hint: 'declare @@search(…, tokenizer: \"edge_ngram\", minGram: 1, maxGram: 12) so every token is indexed as a prefix, or pass fuzzy: 1 | 2 for fuzzy prefix matching (pdb.fuzzy(n, t))',\n });\n }\n let matchText = args.query;\n if (prefix) {\n const tokens = args.query.match(/[\\p{L}\\p{N}]+/gu) ?? [];\n if (tokens.length === 0) return [];\n // bm25 tokenizes the raw query itself (index tokenizer or\n // search_tokenizer); the other engines need per-token prefix syntax.\n matchText =\n config.engine === \"tsvector\"\n ? tokens.map((token) => `${token}:*`).join(\" & \")\n : config.engine === \"fts5\"\n ? tokens.map((token) => `\"${token}\"*`).join(\" \")\n : config.engine === \"bm25\"\n ? args.query\n : tokens.map((token) => `+${token}*`).join(\" \");\n }\n const hlColumn = highlightColumn({ config, args });\n const idOnly = args.select === \"id\";\n if (idOnly && config.pkColumn === undefined) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `select: \"id\" needs a single-column primary key baked into the client — ${config.table} has none, or the client predates the projection (run vibeorm generate)`,\n meta: { table: config.table },\n });\n }\n const projection = idOnly ? `${t}.${quote(config.pkColumn ?? \"\")}` : ctx.projection({ qualify: \"t\" });\n const collector = new ParamCollector(dialect);\n\n const userWhere = (qualify: string): string | null => {\n if (args.where === undefined) return null;\n const expr = ctx.compileWhere({ where: args.where, qualify });\n return expr === undefined ? null : renderExpr({ dialect, expr, collector });\n };\n\n let text: string;\n let engineHasHighlights = true;\n\n if (config.engine === \"tsvector\") {\n const language = config.language ?? \"english\";\n const ranker: \"ts_rank\" | \"ts_rank_cd\" = config.rank ?? \"ts_rank_cd\";\n const tsv = `${t}.${quote(config.tsvColumn ?? \"\")}`;\n const queryParameter = dialect.name === \"postgres\" ? collector.add(matchText) : undefined;\n const tsquery = (): string => prefix\n ? `to_tsquery('${language}', ${queryParameter ?? collector.add(matchText)})`\n : `websearch_to_tsquery('${language}', ${queryParameter ?? collector.add(matchText)})`;\n text = `SELECT ${projection}, ${ranker}(${tsv}, ${tsquery()}) AS ${quote(SCORE_ALIAS)}`;\n if (hlColumn !== null) {\n text += `, ts_headline('${language}', ${t}.${quote(hlColumn)}, ${tsquery()}) AS ${quote(HIGHLIGHT_ALIAS)}`;\n }\n text += ` FROM ${quote(config.table)} AS ${t} WHERE ${tsv} @@ ${tsquery()}`;\n const where = userWhere(\"t\");\n if (where !== null) text += ` AND (${where})`;\n if (args.minScore !== undefined) {\n text += ` AND ${ranker}(${tsv}, ${tsquery()}) >= ${collector.add(args.minScore)}`;\n }\n text += ` ORDER BY ${quote(SCORE_ALIAS)} DESC`;\n } else if (config.engine === \"fts5\") {\n const fts = quote(config.ftsTable ?? \"\");\n text = `SELECT ${projection}, -bm25(${fts}) AS ${quote(SCORE_ALIAS)}`;\n if (hlColumn !== null) {\n const index = config.fields.findIndex((field) => field.column === hlColumn);\n text += `, snippet(${fts}, ${index}, '<b>', '</b>', '…', 16) AS ${quote(HIGHLIGHT_ALIAS)}`;\n }\n text += ` FROM ${fts} JOIN ${quote(config.table)} AS ${t} ON ${t}.rowid = ${fts}.rowid`;\n text += ` WHERE ${fts} MATCH ${collector.add(matchText)}`;\n const where = userWhere(\"t\");\n if (where !== null) text += ` AND (${where})`;\n if (args.minScore !== undefined) {\n text += ` AND -bm25(${fts}) >= ${collector.add(args.minScore)}`;\n }\n text += ` ORDER BY ${quote(SCORE_ALIAS)} DESC`;\n } else if (config.engine === \"fulltext\") {\n engineHasHighlights = false;\n const mode = prefix ? \"IN BOOLEAN MODE\" : \"IN NATURAL LANGUAGE MODE\";\n const match = (): string =>\n `MATCH (${config.fields.map((field) => `${t}.${quote(field.column)}`).join(\", \")}) AGAINST (${collector.add(matchText)} ${mode})`;\n text = `SELECT ${projection}, ${match()} AS ${quote(SCORE_ALIAS)} FROM ${quote(config.table)} AS ${t}`;\n text += ` WHERE ${match()}`;\n const where = userWhere(\"t\");\n if (where !== null) text += ` AND (${where})`;\n if (args.minScore !== undefined) {\n text += ` AND ${match()} >= ${collector.add(args.minScore)}`;\n }\n text += ` ORDER BY ${quote(SCORE_ALIAS)} DESC`;\n } else {\n // bm25 (ParadeDB). `@@@` is the query parser (OR between words);\n // prefix and fuzzy switch to `&&&` (every token must match) with the\n // bound query cast to pdb.fuzzy(n[, t]); declared weights ride as\n // pdb.boost(w) per field. minScore compiles to a score_filter inside\n // the index scan (EXPLAIN-verified on 0.25.7), so it narrows BEFORE\n // the LIMIT — a client-side filter after it would return fewer rows\n // than `take` while qualifying rows exist.\n const pk = `${t}.${quote(config.pkColumn ?? \"\")}`;\n const operator = prefix || fuzzy !== undefined ? \"&&&\" : \"@@@\";\n const fuzzyCast = fuzzy === undefined ? \"\" : `::pdb.fuzzy(${fuzzy}${prefix ? \", t\" : \"\"})`;\n text = `SELECT ${projection}, paradedb.score(${pk}) AS ${quote(SCORE_ALIAS)}`;\n if (hlColumn !== null) {\n text += `, paradedb.snippet(${t}.${quote(hlColumn)}) AS ${quote(HIGHLIGHT_ALIAS)}`;\n }\n text += ` FROM ${quote(config.table)} AS ${t} WHERE (${config.fields\n .map((field, index) => {\n const boost = config.boosts?.[index];\n const boostCast = boost === undefined || boost === 1 ? \"\" : `::pdb.boost(${boost})`;\n return `${t}.${quote(field.column)} ${operator} ${collector.add(matchText)}${fuzzyCast}${boostCast}`;\n })\n .join(\" OR \")})`;\n const where = userWhere(\"t\");\n if (where !== null) text += ` AND (${where})`;\n if (args.minScore !== undefined) {\n text += ` AND paradedb.score(${pk}) >= ${collector.add(args.minScore)}`;\n }\n text += ` ORDER BY ${quote(SCORE_ALIAS)} DESC`;\n }\n\n text += ` LIMIT ${take}`;\n if (skip !== null) text += ` OFFSET ${skip}`;\n\n const rows = await ctx.executeRows({ text, values: collector.values, method: \"search\" });\n return shapeHits({\n rows,\n highlightsRequested: args.highlight !== undefined,\n engineHasHighlights,\n });\n };\n };\n}\n\n// ─── Factory ──────────────────────────────────────────────────────\n\n/**\n * Unified full-text search.\n *\n * @example schema\n * // @@search([title, body], name: \"post_search\", weights: [2, 1])\n * const hits = await db.post.search({ query: \"sqlite full text\", take: 20 });\n * // → [{ row, score, highlights }]\n */\nexport function search(options: SearchOptions = {}): VibeExtension {\n return defineExtension({\n name: \"search\",\n version: VERSION,\n options: { ...options },\n requires: { postgresql: null, pglite: null, sqlite: null, mysql: null },\n\n schema: {\n modelAttributes: [\n {\n name: \"search\",\n parse: ({ args, fieldNames }) => {\n const fields = args.fields ?? [];\n if (fields.length === 0) {\n return { error: \"@@search requires a field list, e.g. @@search([title, body])\" };\n }\n const missing = fields.find((name) => !fieldNames.includes(name));\n if (missing !== undefined) {\n return { error: `@@search references unknown field \"${missing}\"` };\n }\n const name = args.named[\"name\"];\n if (name !== undefined && (typeof name !== \"string\" || !isSafeSqlName({ name }))) {\n return { error: `name must be a plain identifier` };\n }\n const weights = args.named[\"weights\"];\n if (weights !== undefined) {\n if (\n !Array.isArray(weights) ||\n weights.length !== fields.length ||\n weights.some((weight) => typeof weight !== \"number\" || !Number.isFinite(weight))\n ) {\n return { error: `weights must be a number list matching the field list` };\n }\n }\n const language = args.named[\"language\"];\n if (language !== undefined && (typeof language !== \"string\" || !isSafeSqlName({ name: language }))) {\n return { error: `language must be a plain text-search config name` };\n }\n for (const key of [\"columnName\", \"indexName\"] as const) {\n const value = args.named[key];\n if (value !== undefined && (typeof value !== \"string\" || !isSafeSqlName({ name: value }))) {\n return { error: `${key} must be a plain identifier` };\n }\n }\n const rank = args.named[\"rank\"];\n if (rank !== undefined && rank !== \"ts_rank\" && rank !== \"ts_rank_cd\") {\n return { error: `rank must be \"ts_rank\" or \"ts_rank_cd\"` };\n }\n const tokenizer = readBm25Tokenizer(args.named);\n if (\"error\" in tokenizer) return { error: tokenizer.error };\n const filterFields = args.named[\"filterFields\"];\n if (filterFields !== undefined) {\n if (!Array.isArray(filterFields) || filterFields.length === 0 || filterFields.some((name) => typeof name !== \"string\")) {\n return { error: \"filterFields must be a non-empty field list, e.g. filterFields: [municipalityNumber, active]\" };\n }\n const invalid = filterFields.find((name) => !fieldNames.includes(String(name)) || fields.includes(String(name)));\n if (invalid !== undefined) {\n return { error: `filterFields entry \"${String(invalid)}\" must be a field on the model that is not also searched` };\n }\n }\n return { declaration: { fields, ...args.named } };\n },\n },\n ],\n indexKinds: [\"bm25\", \"fts5\", \"fulltext\", \"gin\"],\n },\n\n migrate: { artifacts: searchArtifacts },\n\n client: {\n model: ({ model, dialect, provider, options: extOptions, declaration, typeNames }) => {\n const decl = readDeclaration({ model, declaration });\n if (decl === null) return null;\n const requested = (extOptions[\"engine\"] as SearchEngine | \"auto\" | undefined) ?? \"auto\";\n const engine = resolveSearchEngine({ dialect, provider, requested });\n const language = resolveLanguage({ model, decl, options: extOptions });\n\n const pkField = primaryKeyFieldOf({ model });\n const rank = resolveRank({ decl, options: extOptions, engine, dialect });\n assertBm25Declarations({ decl, engine, dialect });\n // The client refuses exactly what the migrate artifact refuses (key\n // field, column storage, filter fields), so `vibeorm generate` is the\n // earliest moment, not `migrate generate`.\n if (engine === \"bm25\") bm25IndexColumns({ model, decl });\n const names = resolveArtifactNames({ decl, engine, dialect });\n const config: SearchDelegateConfig = {\n engine,\n table: tableNameOf({ model }),\n fields: decl.fields.map((field) => ({\n name: field,\n column: columnNameOf({ model, field }),\n })),\n ...(engine === \"tsvector\"\n ? { tsvColumn: names.tsvColumn, language, ...(rank === undefined ? {} : { rank }) }\n : {}),\n ...(engine === \"fts5\" ? { ftsTable: `${decl.base}_fts` } : {}),\n ...(pkField !== null ? { pkColumn: columnNameOf({ model, field: pkField }) } : {}),\n ...(engine === \"bm25\" && decl.weights !== undefined ? { boosts: decl.weights } : {}),\n ...(engine === \"bm25\" && decl.tokenizer?.name === \"edge_ngram\" ? { prefixIndex: true } : {}),\n };\n\n const fieldUnion = decl.fields.map((field) => JSON.stringify(field)).join(\" | \");\n const argLines: string[] = [\n ` readonly query: string;`,\n ` readonly where?: ${typeNames.whereInput};`,\n ` readonly take?: number;`,\n ` readonly skip?: number;`,\n ` readonly minScore?: number;`,\n ` readonly highlight?: { readonly field: ${fieldUnion} };`,\n ` readonly prefix?: boolean;`,\n // Typed honesty: fuzzy exists only where the engine can honor it.\n ...(engine === \"bm25\" ? [` readonly fuzzy?: 1 | 2;`] : []),\n ];\n // With a single-column pk the id projection is offered as an overload:\n // `select: \"id\"` narrows the hit row to the pk.\n const dts: string[] =\n pkField === null\n ? [\n `readonly search: (args: {`,\n ...argLines,\n `}) => Promise<SearchHit<${typeNames.payload}>[]>;`,\n ]\n : [\n `readonly search: {`,\n ` (args: {`,\n ...argLines.map((line) => ` ${line}`),\n ` readonly select: \"id\";`,\n ` }): Promise<SearchHit<Pick<${typeNames.payload}, ${JSON.stringify(pkField)}>>[]>;`,\n ` (args: {`,\n ...argLines.map((line) => ` ${line}`),\n ` readonly select?: \"row\";`,\n ` }): Promise<SearchHit<${typeNames.payload}>[]>;`,\n `};`,\n ];\n return {\n kind: \"method\",\n // Verified read: the mount issues exactly one SELECT per call (the\n // index-maintenance DDL lives in `migrate.artifacts`, not here), so it\n // is available on a $readOnly client.\n readOnly: true,\n runtimeGlue: `createSearchDelegate(${JSON.stringify(config)})`,\n imports: [{ from: \"@vibeorm/extensions/search\", names: [\"createSearchDelegate\"] }],\n dtsImports: [{ from: \"@vibeorm/extensions/search\", names: [\"SearchHit\"] }],\n dts,\n };\n },\n },\n\n doctor: async ({ execute, dialect, options: extOptions }) => {\n if (dialect === \"postgres\") {\n const requested = (extOptions[\"engine\"] as SearchEngine | \"auto\" | undefined) ?? \"auto\";\n if (requested === \"bm25\") {\n return { checks: [await pgExtensionCheck({ execute, name: \"pg_search\" })] };\n }\n return { checks: [{ name: \"tsvector\", ok: true, note: \"built in\" }] };\n }\n if (dialect === \"sqlite\") {\n try {\n const rows = await execute({\n text: \"SELECT 1 AS ok FROM pragma_compile_options WHERE compile_options = 'ENABLE_FTS5'\",\n });\n return {\n checks: [\n rows.length > 0\n ? { name: \"fts5\", ok: true, note: \"compiled in\" }\n : { name: \"fts5\", ok: false, note: \"sqlite built without FTS5\" },\n ],\n };\n } catch (error) {\n return {\n checks: [\n {\n name: \"fts5\",\n ok: false,\n note: `check failed: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n };\n }\n }\n return { checks: [{ name: \"fulltext\", ok: true, note: \"built in (InnoDB FULLTEXT)\" }] };\n },\n });\n}\n",
|
|
6
6
|
"/**\n * Helpers shared by the tier-1 extensions. Nothing here is privileged —\n * userland extensions can do everything these do with the same public hooks.\n */\n\nimport { VibeError } from \"@vibeorm/schema\";\nimport type {\n ExtensionDoctorCheck,\n ExtensionSqlExecutor,\n ModelIR,\n} from \"@vibeorm/schema\";\n\n// ─── Numeric admission ────────────────────────────────────────────\n\n/** Refuse non-finite extension arguments before compilation or execution. */\nexport function finiteNumber(params: { value: unknown; name: string; integer?: boolean; min?: number }): number {\n const { value, name, integer, min } = params;\n if (typeof value !== \"number\" || !Number.isFinite(value) ||\n (integer === true && !Number.isSafeInteger(value)) || (min !== undefined && value < min)) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `${name} must be a finite ${integer === true ? \"safe integer\" : \"number\"}${min === undefined ? \"\" : ` >= ${min}`}`,\n meta: { argument: name },\n });\n }\n return value;\n}\n\n// ─── IR lookups ───────────────────────────────────────────────────\n\n/** Table name of a model (`dbName ?? name`). */\nexport function tableNameOf(params: { model: ModelIR }): string {\n return params.model.dbName ?? params.model.name;\n}\n\n/** Column name of a field (`dbName ?? name`); the field must exist. */\nexport function columnNameOf(params: { model: ModelIR; field: string }): string {\n const found = params.model.fields.find((candidate) => candidate.name === params.field);\n if (found === undefined) {\n throw new VibeError({\n code: \"VIBE_INTERNAL\",\n message: `extension referenced unknown field \"${params.field}\" on model \"${params.model.name}\"`,\n meta: { model: params.model.name, field: params.field },\n });\n }\n return found.dbName ?? found.name;\n}\n\n/** The model's single primary-key field name, for key_field-style options. */\nexport function primaryKeyFieldOf(params: { model: ModelIR }): string | null {\n const { model } = params;\n if (model.primaryKey !== undefined && model.primaryKey.fields.length === 1) {\n return model.primaryKey.fields[0] ?? null;\n }\n const idField = model.fields.find((field) => field.isId);\n return idField?.name ?? null;\n}\n\n// ─── Declaration readers (ExtensionDeclIR.options are untyped JSON) ─\n\nexport function declString(decl: Readonly<Record<string, unknown>>, key: string): string | undefined {\n const value = decl[key];\n return typeof value === \"string\" ? value : undefined;\n}\n\nexport function declStringArray(\n decl: Readonly<Record<string, unknown>>,\n key: string,\n): readonly string[] | undefined {\n const value = decl[key];\n if (!Array.isArray(value)) return undefined;\n return value.every((entry): entry is string => typeof entry === \"string\") ? value : undefined;\n}\n\nexport function declNumberArray(\n decl: Readonly<Record<string, unknown>>,\n key: string,\n): readonly number[] | undefined {\n const value = decl[key];\n if (!Array.isArray(value)) return undefined;\n return value.every((entry): entry is number => typeof entry === \"number\") ? value : undefined;\n}\n\n// ─── Identifier safety ────────────────────────────────────────────\n\nconst SQL_LITERAL_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Names an extension splices as SQL LITERALS (regconfig names, key_field\n * options) must be plain identifiers — everything else is rejected at parse\n * time so no schema-borne text ever reaches SQL unquoted.\n */\nexport function isSafeSqlName(params: { name: string }): boolean {\n return SQL_LITERAL_NAME.test(params.name);\n}\n\n// ─── Doctor helpers ───────────────────────────────────────────────\n\n/** Availability of a postgres extension, per pg_available_extensions. */\nexport async function pgExtensionCheck(params: {\n execute: ExtensionSqlExecutor;\n name: string;\n}): Promise<ExtensionDoctorCheck> {\n const { execute, name } = params;\n try {\n const rows = await execute({\n text: \"SELECT name, default_version, installed_version FROM pg_available_extensions WHERE name = $1\",\n values: [name],\n });\n const row = rows[0];\n if (row === undefined) {\n return { name: `extension ${name}`, ok: false, note: \"not available on this server\" };\n }\n const installed = row[\"installed_version\"];\n return {\n name: `extension ${name}`,\n ok: true,\n note: installed === null || installed === undefined ? \"available, not installed\" : `installed ${String(installed)}`,\n };\n } catch (error) {\n return {\n name: `extension ${name}`,\n ok: false,\n note: `check failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": "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",
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "5A96240B33D83FB764756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|