@framers/sql-storage-adapter 0.7.4 → 0.7.5

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.
Files changed (39) hide show
  1. package/README.md +24 -13
  2. package/dist/cjs/core/contracts/features.js +4 -2
  3. package/dist/cjs/core/contracts/fts.d.ts +20 -8
  4. package/dist/cjs/core/portable.d.ts +1 -0
  5. package/dist/cjs/core/portable.js +3 -0
  6. package/dist/cjs/fts/PostgresFts.d.ts +14 -13
  7. package/dist/cjs/fts/PostgresFts.js +61 -33
  8. package/dist/cjs/fts/SqliteFts4.d.ts +28 -0
  9. package/dist/cjs/fts/SqliteFts4.js +67 -0
  10. package/dist/cjs/fts/SqliteFts5.d.ts +1 -0
  11. package/dist/cjs/fts/SqliteFts5.js +3 -0
  12. package/dist/cjs/index.d.ts +1 -0
  13. package/dist/cjs/index.js +3 -0
  14. package/dist/core/contracts/features.d.ts.map +1 -1
  15. package/dist/core/contracts/features.js +6 -2
  16. package/dist/core/contracts/features.js.map +1 -1
  17. package/dist/core/contracts/fts.d.ts +20 -8
  18. package/dist/core/contracts/fts.d.ts.map +1 -1
  19. package/dist/core/portable.d.ts +1 -0
  20. package/dist/core/portable.d.ts.map +1 -1
  21. package/dist/core/portable.js +1 -0
  22. package/dist/core/portable.js.map +1 -1
  23. package/dist/fts/PostgresFts.d.ts +14 -13
  24. package/dist/fts/PostgresFts.d.ts.map +1 -1
  25. package/dist/fts/PostgresFts.js +83 -39
  26. package/dist/fts/PostgresFts.js.map +1 -1
  27. package/dist/fts/SqliteFts4.d.ts +28 -0
  28. package/dist/fts/SqliteFts4.d.ts.map +1 -0
  29. package/dist/fts/SqliteFts4.js +56 -0
  30. package/dist/fts/SqliteFts4.js.map +1 -0
  31. package/dist/fts/SqliteFts5.d.ts +1 -0
  32. package/dist/fts/SqliteFts5.d.ts.map +1 -1
  33. package/dist/fts/SqliteFts5.js +3 -0
  34. package/dist/fts/SqliteFts5.js.map +1 -1
  35. package/dist/index.d.ts +1 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +1 -0
  38. package/dist/index.js.map +1 -1
  39. package/package.json +1 -1
package/README.md CHANGED
@@ -730,30 +730,41 @@ const ddl = `CREATE TABLE notes (id ${d.columnType('uuid')} PRIMARY KEY DEFAULT
730
730
  - Column values follow each engine: a PostgreSQL `BOOLEAN` returns `true` and the SQLite `INTEGER` returns `1`, and a `TIMESTAMPTZ` returns a `Date` where SQLite returns text. `typeParsers` (see "Values") narrows the PostgreSQL side.
731
731
  - `pragma()` returns null on PostgreSQL and `advisoryTransactionLock()` returns null on SQLite: skip the statement when it is null.
732
732
 
733
- ### IFullTextSearch — FTS5 & tsvector
733
+ ### IFullTextSearch — FTS5, FTS4 & tsvector
734
+
735
+ `createStorageFeatures` gives each engine the full-text search it has:
736
+ - FTS5 on better-sqlite3;
737
+ - FTS4 on sql.js and IndexedDB, whose stock sql.js build compiles FTS3 and FTS4 and no FTS5;
738
+ - a tsvector column with a GIN index on PostgreSQL, the Supabase adapter included.
739
+
740
+ The same calls return the same rows on all three, and `tests/fts.parity.spec.ts` runs them on each.
734
741
 
735
742
  ```typescript
736
743
  const { fts } = features;
737
744
 
738
- // Create index: FTS5 virtual table (SQLite) or tsvector + GIN (Postgres)
739
- await adapter.exec(fts.createIndex({
745
+ // FTS5 or FTS4 virtual table (SQLite), or a _tsv column with a GIN index (PostgreSQL)
746
+ await db.exec(fts.createIndex({
740
747
  table: 'docs_fts',
741
748
  columns: ['title', 'body'],
742
749
  contentTable: 'documents',
743
750
  tokenizer: 'porter ascii',
751
+ weights: { title: 'A', body: 'C' }, // PostgreSQL ranks with them; SQLite ignores them
744
752
  }));
753
+ await db.exec(fts.rebuildCommand('docs_fts'));
754
+
755
+ // The best match first on every engine; a named placeholder binds once wherever it appears
756
+ const rows = await db.all(
757
+ `SELECT t.id FROM ${fts.joinClause('documents', 't', 'docs_fts', 'docs_fts')}
758
+ WHERE ${fts.matchClause('docs_fts', '@q')} ORDER BY ${fts.relevanceOrder('docs_fts', '@q')}`,
759
+ { q: fts.sanitizeQuery(userText) },
760
+ );
761
+ ```
745
762
 
746
- // Search query
747
- const sql = `
748
- SELECT t.*
749
- FROM ${fts.joinClause('documents', 't', 'fts', 'docs_fts')}
750
- WHERE ${fts.matchClause('docs_fts', '?')}
751
- ORDER BY ${fts.rankExpression('fts')}
752
- `;
763
+ - **Ordering.** `relevanceOrder` orders best first everywhere. `rankExpression` keeps each engine's own direction: FTS5's `rank` is lower-better, FTS4's `length(offsets())` and PostgreSQL's `ts_rank` are higher-better.
764
+ - **Queries.** `sanitizeQuery` turns text into its words, quoted and joined with OR, on every engine. PostgreSQL reads queries with `websearch_to_tsquery`: words are ANDed, and `or`, `-word` and quoted phrases work as in a search box.
765
+ - **One index, many features objects.** On PostgreSQL, `createIndex` records each index's table, columns, language and weights for the whole process, so a features object made later syncs, rebuilds and queries an index another one created, and each index keeps its own language. Run `createIndex` (it is idempotent) in each process before `syncInsert` or `rebuildCommand`.
766
+ - **Aliases.** On SQLite, alias the index as itself in `joinClause` (or pass the alias as `indexName` to `matchClause`), since a MATCH names the table as the query writes it.
753
767
 
754
- // Rebuild index
755
- await adapter.exec(fts.rebuildCommand('docs_fts'));
756
- ```
757
768
 
758
769
  ### IBlobCodec — Cross-Platform Binary Encoding
759
770
 
@@ -23,6 +23,7 @@ module.exports = __toCommonJS(features_exports);
23
23
  var import_SqliteDialect = require("../../dialects/SqliteDialect.js");
24
24
  var import_PostgresDialect = require("../../dialects/PostgresDialect.js");
25
25
  var import_SqliteFts5 = require("../../fts/SqliteFts5.js");
26
+ var import_SqliteFts4 = require("../../fts/SqliteFts4.js");
26
27
  var import_PostgresFts = require("../../fts/PostgresFts.js");
27
28
  var import_NodeBlobCodec = require("../../codecs/NodeBlobCodec.js");
28
29
  var import_BrowserBlobCodec = require("../../codecs/BrowserBlobCodec.js");
@@ -45,12 +46,13 @@ function extractPostgresConnectionString(adapter) {
45
46
  return `postgresql://${auth}${host}:${port}/${options.database}${sslQuery}`;
46
47
  }
47
48
  function createStorageFeatures(adapter) {
48
- const isPostgres = adapter.kind === "postgres";
49
+ const isPostgres = adapter.kind === "postgres" || adapter.kind === "supabase-postgres";
50
+ const hasFts5 = adapter.kind !== "sqljs" && adapter.kind !== "indexeddb";
49
51
  const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
50
52
  const postgresConnectionString = isPostgres ? extractPostgresConnectionString(adapter) : void 0;
51
53
  return {
52
54
  dialect: isPostgres ? new import_PostgresDialect.PostgresDialect() : new import_SqliteDialect.SqliteDialect(),
53
- fts: isPostgres ? new import_PostgresFts.PostgresFts() : new import_SqliteFts5.SqliteFts5(),
55
+ fts: isPostgres ? new import_PostgresFts.PostgresFts() : hasFts5 ? new import_SqliteFts5.SqliteFts5() : new import_SqliteFts4.SqliteFts4(),
54
56
  blobCodec: isBrowser ? new import_BrowserBlobCodec.BrowserBlobCodec() : new import_NodeBlobCodec.NodeBlobCodec(),
55
57
  exporter: isPostgres ? new import_PostgresExporter.PostgresExporter(postgresConnectionString) : new import_SqliteFileExporter.SqliteFileExporter(adapter)
56
58
  };
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Full-text search abstraction.
3
3
  *
4
- * SQLite implementations use FTS5 virtual tables.
5
- * Postgres implementations use tsvector columns with GIN indexes.
4
+ * better-sqlite3 uses FTS5 virtual tables; sql.js and IndexedDB use FTS4, which
5
+ * their stock sql.js build has (it has no FTS5). Postgres implementations use
6
+ * tsvector columns with GIN indexes.
6
7
  * Methods return SQL strings — no database calls.
7
8
  */
8
9
  export interface IFullTextSearch {
@@ -13,25 +14,36 @@ export interface IFullTextSearch {
13
14
  * @param config.columns - Columns to index.
14
15
  * @param config.contentTable - Source table (for external-content FTS5).
15
16
  * @param config.tokenizer - Tokenizer config (e.g. 'porter ascii').
17
+ * @param config.weights - PostgreSQL: a weight per column (A highest, D lowest) for ranking. The SQLite engines rank without it.
16
18
  */
17
19
  createIndex(config: {
18
20
  table: string;
19
21
  columns: string[];
20
22
  contentTable?: string;
21
23
  tokenizer?: string;
24
+ weights?: Record<string, 'A' | 'B' | 'C' | 'D'>;
22
25
  }): string;
23
26
  /**
24
27
  * Generate a WHERE clause fragment for full-text matching.
25
28
  * SQLite: `memory_traces_fts MATCH ?`
26
- * Postgres: `memory_traces._tsv @@ plainto_tsquery('english', $1)`
29
+ * Postgres: `_tsv @@ websearch_to_tsquery('english', $1)`, in the index's own language
27
30
  */
28
31
  matchClause(indexName: string, queryPlaceholder: string): string;
29
32
  /**
30
- * Generate an ORDER BY rank expression.
31
- * SQLite: `memory_traces_fts.rank`
32
- * Postgres: `ts_rank(memory_traces._tsv, plainto_tsquery('english', $1))`
33
+ * A rank value, in each engine's own direction.
34
+ * SQLite FTS5: `memory_traces_fts.rank` (lower is better)
35
+ * SQLite FTS4: `length(offsets(memory_traces_fts))` (higher is better)
36
+ * Postgres: `ts_rank(_tsv, websearch_to_tsquery('english', $1))` (higher is better)
37
+ * Use relevanceOrder to order the same way on every engine.
33
38
  */
34
39
  rankExpression(indexName: string, queryPlaceholder?: string): string;
40
+ /**
41
+ * An ORDER BY fragment that puts the best match first on every engine.
42
+ * SQLite FTS5: `memory_traces_fts.rank`
43
+ * SQLite FTS4: `length(offsets(memory_traces_fts)) DESC`
44
+ * Postgres: `ts_rank(_tsv, websearch_to_tsquery('english', $1)) DESC`
45
+ */
46
+ relevanceOrder(indexName: string, queryPlaceholder?: string): string;
35
47
  /**
36
48
  * Generate the rebuild/reindex command.
37
49
  * SQLite: `INSERT INTO fts_table(fts_table) VALUES('rebuild')`
@@ -46,8 +58,8 @@ export interface IFullTextSearch {
46
58
  syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
47
59
  /**
48
60
  * Sanitize natural-language input into a safe search query.
49
- * SQLite: wraps words in quotes, strips FTS5 operators.
50
- * Postgres: pass-through (plainto_tsquery handles it).
61
+ * Every engine: the words, quoted, joined with OR, with the operators of
62
+ * the query syntax stripped, so it matches the same rows everywhere.
51
63
  */
52
64
  sanitizeQuery(input: string): string;
53
65
  /**
@@ -10,6 +10,7 @@ export * from '../types/index.js';
10
10
  export { SqliteDialect } from '../dialects/SqliteDialect.js';
11
11
  export { PostgresDialect } from '../dialects/PostgresDialect.js';
12
12
  export { SqliteFts5 } from '../fts/SqliteFts5.js';
13
+ export { SqliteFts4 } from '../fts/SqliteFts4.js';
13
14
  export { PostgresFts } from '../fts/PostgresFts.js';
14
15
  export { NodeBlobCodec } from '../codecs/NodeBlobCodec.js';
15
16
  export { BrowserBlobCodec } from '../codecs/BrowserBlobCodec.js';
@@ -25,6 +25,7 @@ __export(portable_exports, {
25
25
  PostgresFts: () => import_PostgresFts.PostgresFts,
26
26
  SqliteDialect: () => import_SqliteDialect.SqliteDialect,
27
27
  SqliteFileExporter: () => import_SqliteFileExporter.SqliteFileExporter,
28
+ SqliteFts4: () => import_SqliteFts4.SqliteFts4,
28
29
  SqliteFts5: () => import_SqliteFts5.SqliteFts5,
29
30
  createStorageFeatures: () => import_features.createStorageFeatures,
30
31
  getStorageLogger: () => import_logger.getStorageLogger,
@@ -36,6 +37,7 @@ __reExport(portable_exports, require("../types/index.js"), module.exports);
36
37
  var import_SqliteDialect = require("../dialects/SqliteDialect.js");
37
38
  var import_PostgresDialect = require("../dialects/PostgresDialect.js");
38
39
  var import_SqliteFts5 = require("../fts/SqliteFts5.js");
40
+ var import_SqliteFts4 = require("../fts/SqliteFts4.js");
39
41
  var import_PostgresFts = require("../fts/PostgresFts.js");
40
42
  var import_NodeBlobCodec = require("../codecs/NodeBlobCodec.js");
41
43
  var import_BrowserBlobCodec = require("../codecs/BrowserBlobCodec.js");
@@ -56,6 +58,7 @@ __reExport(portable_exports, require("../shared/parameterUtils.js"), module.expo
56
58
  PostgresFts,
57
59
  SqliteDialect,
58
60
  SqliteFileExporter,
61
+ SqliteFts4,
59
62
  SqliteFts5,
60
63
  createStorageFeatures,
61
64
  getStorageLogger,
@@ -1,29 +1,30 @@
1
1
  import type { IFullTextSearch } from '../core/contracts/fts.js';
2
+ type Weight = 'A' | 'B' | 'C' | 'D';
2
3
  /**
3
4
  * Postgres full-text search using tsvector columns and GIN indexes.
4
5
  *
5
- * Unlike FTS5 (separate virtual table), Postgres stores the tsvector
6
- * as a column `_tsv` on the content table itself. The `createIndex()`
7
- * call adds the column, creates the GIN index, and backfills existing rows.
6
+ * Unlike FTS5 (separate virtual table), Postgres stores the tsvector as a
7
+ * column `_tsv` on the content table itself. The `createIndex()` call adds the
8
+ * column, creates the GIN index, and backfills existing rows. Queries go
9
+ * through `websearch_to_tsquery`, which reads raw user text without syntax
10
+ * errors: words are ANDed, and `or`, `-word` and quoted phrases work as in a
11
+ * web search box.
8
12
  */
9
13
  export declare class PostgresFts implements IFullTextSearch {
10
- /** The content table is stored during createIndex for use by other methods. */
11
- private _contentTable;
12
- private _columns;
13
- private _lang;
14
14
  createIndex(config: {
15
15
  table: string;
16
16
  columns: string[];
17
17
  contentTable?: string;
18
18
  tokenizer?: string;
19
+ weights?: Record<string, Weight>;
19
20
  }): string;
20
- matchClause(_indexName: string, queryPlaceholder: string): string;
21
- rankExpression(_indexName: string, queryPlaceholder?: string): string;
22
- rebuildCommand(_indexName: string): string;
23
- syncInsert(_indexName: string, _rowIdExpr: string, columns: string[]): string;
21
+ matchClause(indexName: string, queryPlaceholder: string): string;
22
+ rankExpression(indexName: string, queryPlaceholder?: string): string;
23
+ relevanceOrder(indexName: string, queryPlaceholder?: string): string;
24
+ rebuildCommand(indexName: string): string;
25
+ syncInsert(indexName: string, _rowIdExpr: string, columns: string[]): string;
24
26
  sanitizeQuery(input: string): string;
25
27
  joinClause(contentTable: string, contentAlias: string, _ftsAlias: string, _indexName: string): string;
26
- /** Map FTS5-style tokenizer strings to Postgres text search configurations. */
27
- private _tokenizerToLang;
28
28
  }
29
+ export {};
29
30
  //# sourceMappingURL=PostgresFts.d.ts.map
@@ -20,57 +20,85 @@ __export(PostgresFts_exports, {
20
20
  PostgresFts: () => PostgresFts
21
21
  });
22
22
  module.exports = __toCommonJS(PostgresFts_exports);
23
+ const INDEXES = /* @__PURE__ */ new Map();
24
+ const WEIGHTS = /* @__PURE__ */ new Set(["A", "B", "C", "D"]);
23
25
  class PostgresFts {
24
- constructor() {
25
- this._contentTable = "";
26
- this._columns = [];
27
- this._lang = "english";
28
- }
29
26
  createIndex(config) {
30
- const ct = config.contentTable ?? config.table;
31
- this._contentTable = ct;
32
- this._columns = config.columns;
33
- const colConcat = config.columns.map((c) => `COALESCE(${c}, '')`).join(" || ' ' || ");
34
- const lang = this._tokenizerToLang(config.tokenizer);
35
- this._lang = lang;
27
+ const index = {
28
+ contentTable: config.contentTable ?? config.table,
29
+ columns: config.columns,
30
+ lang: tokenizerToLang(config.tokenizer),
31
+ weights: checkWeights(config.columns, config.weights)
32
+ };
33
+ INDEXES.set(config.table, index);
34
+ const ct = index.contentTable;
36
35
  return [
37
36
  `ALTER TABLE ${ct} ADD COLUMN IF NOT EXISTS _tsv tsvector`,
38
37
  `CREATE INDEX IF NOT EXISTS idx_${config.table}_tsv ON ${ct} USING GIN(_tsv)`,
39
- `UPDATE ${ct} SET _tsv = to_tsvector('${lang}', ${colConcat}) WHERE _tsv IS NULL`
38
+ `UPDATE ${ct} SET _tsv = ${tsvector(index, index.columns.map((column) => `COALESCE(${column}, '')`))} WHERE _tsv IS NULL`
40
39
  ].join(";\n");
41
40
  }
42
- matchClause(_indexName, queryPlaceholder) {
43
- return `_tsv @@ plainto_tsquery('${this._lang}', ${queryPlaceholder})`;
41
+ matchClause(indexName, queryPlaceholder) {
42
+ return `_tsv @@ websearch_to_tsquery('${langOf(indexName)}', ${queryPlaceholder})`;
44
43
  }
45
- rankExpression(_indexName, queryPlaceholder) {
44
+ rankExpression(indexName, queryPlaceholder) {
46
45
  const qp = queryPlaceholder ?? "$1";
47
- return `ts_rank(_tsv, plainto_tsquery('${this._lang}', ${qp}))`;
46
+ return `ts_rank(_tsv, websearch_to_tsquery('${langOf(indexName)}', ${qp}))`;
47
+ }
48
+ relevanceOrder(indexName, queryPlaceholder) {
49
+ return `${this.rankExpression(indexName, queryPlaceholder)} DESC`;
48
50
  }
49
- rebuildCommand(_indexName) {
50
- const ct = this._contentTable;
51
- const colConcat = this._columns.map((c) => `COALESCE(${c}, '')`).join(" || ' ' || ");
52
- return `UPDATE ${ct} SET _tsv = to_tsvector('${this._lang}', ${colConcat})`;
51
+ rebuildCommand(indexName) {
52
+ const index = described(indexName);
53
+ return `UPDATE ${index.contentTable} SET _tsv = ${tsvector(index, index.columns.map((column) => `COALESCE(${column}, '')`))}`;
53
54
  }
54
- syncInsert(_indexName, _rowIdExpr, columns) {
55
- const ct = this._contentTable;
56
- const colConcat = columns.map((_, index) => `COALESCE($${index + 2}, '')`).join(" || ' ' || ");
57
- return `UPDATE ${ct} SET _tsv = to_tsvector('${this._lang}', ${colConcat}) WHERE id = $1`;
55
+ syncInsert(indexName, _rowIdExpr, columns) {
56
+ const index = described(indexName);
57
+ const values = columns.map((_, i) => `COALESCE($${i + 2}, '')`);
58
+ return `UPDATE ${index.contentTable} SET _tsv = ${tsvector({ ...index, columns }, values)} WHERE id = $1`;
58
59
  }
59
60
  sanitizeQuery(input) {
60
- return input;
61
+ return input.replace(/[*()":^~{}[\]\\-]/g, " ").split(/\s+/).filter((word) => word.length > 0 && word.toLowerCase() !== "or").map((word) => `"${word}"`).join(" or ");
61
62
  }
62
63
  joinClause(contentTable, contentAlias, _ftsAlias, _indexName) {
63
64
  return `${contentTable} ${contentAlias}`;
64
65
  }
65
- /** Map FTS5-style tokenizer strings to Postgres text search configurations. */
66
- _tokenizerToLang(tokenizer) {
67
- if (!tokenizer)
68
- return "english";
69
- if (tokenizer.includes("porter"))
70
- return "english";
71
- return "simple";
72
- }
73
66
  }
67
+ const tokenizerToLang = (tokenizer) => {
68
+ if (!tokenizer)
69
+ return "english";
70
+ if (tokenizer.includes("porter"))
71
+ return "english";
72
+ return "simple";
73
+ };
74
+ const checkWeights = (columns, weights) => {
75
+ if (!weights)
76
+ return void 0;
77
+ for (const [column, weight] of Object.entries(weights)) {
78
+ if (!columns.includes(column))
79
+ throw new Error(`weights names ${JSON.stringify(column)}, which is not one of the indexed columns.`);
80
+ if (!WEIGHTS.has(weight))
81
+ throw new Error(`The weight of ${column} must be 'A', 'B', 'C' or 'D'; got ${JSON.stringify(weight)}.`);
82
+ }
83
+ return weights;
84
+ };
85
+ const tsvector = (index, values) => {
86
+ if (!index.weights)
87
+ return `to_tsvector('${index.lang}', ${values.join(" || ' ' || ")})`;
88
+ return values.map((value, i) => {
89
+ const weight = index.weights[index.columns[i]];
90
+ const vector = `to_tsvector('${index.lang}', ${value})`;
91
+ return weight ? `setweight(${vector}, '${weight}')` : vector;
92
+ }).join(" || ");
93
+ };
94
+ const described = (indexName) => {
95
+ const index = INDEXES.get(indexName);
96
+ if (!index) {
97
+ throw new Error(`No PostgreSQL full-text index named ${JSON.stringify(indexName)} has been described in this process: run its createIndex() first (it is idempotent).`);
98
+ }
99
+ return index;
100
+ };
101
+ const langOf = (indexName) => INDEXES.get(indexName)?.lang ?? "english";
74
102
  // Annotate the CommonJS export names for ESM import in node:
75
103
  0 && (module.exports = {
76
104
  PostgresFts
@@ -0,0 +1,28 @@
1
+ import type { IFullTextSearch } from '../core/contracts/fts.js';
2
+ /**
3
+ * SQLite FTS4 full-text search, for sql.js and the IndexedDB adapter: the
4
+ * stock sql.js build compiles SQLite with FTS3 and FTS4 and without FTS5.
5
+ *
6
+ * Uses FTS4 virtual tables with external content, the porter, unicode61 or
7
+ * simple tokenizer, and rowid joins to the content table. FTS4 has no rank
8
+ * column; relevanceOrder orders by the length of `offsets()`, which grows with
9
+ * the number of matching terms, so the best match comes first.
10
+ */
11
+ export declare class SqliteFts4 implements IFullTextSearch {
12
+ createIndex(config: {
13
+ table: string;
14
+ columns: string[];
15
+ contentTable?: string;
16
+ tokenizer?: string;
17
+ }): string;
18
+ matchClause(indexName: string, queryPlaceholder: string): string;
19
+ rankExpression(indexName: string, _queryPlaceholder?: string): string;
20
+ relevanceOrder(indexName: string, _queryPlaceholder?: string): string;
21
+ rebuildCommand(indexName: string): string;
22
+ syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
23
+ sanitizeQuery(input: string): string;
24
+ joinClause(contentTable: string, contentAlias: string, ftsAlias: string, indexName: string): string;
25
+ /** An FTS5-style tokenizer string as an FTS4 tokenizer name. */
26
+ private tokenizer;
27
+ }
28
+ //# sourceMappingURL=SqliteFts4.d.ts.map
@@ -0,0 +1,67 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var SqliteFts4_exports = {};
19
+ __export(SqliteFts4_exports, {
20
+ SqliteFts4: () => SqliteFts4
21
+ });
22
+ module.exports = __toCommonJS(SqliteFts4_exports);
23
+ class SqliteFts4 {
24
+ createIndex(config) {
25
+ const parts = [...config.columns];
26
+ if (config.contentTable)
27
+ parts.push(`content="${config.contentTable}"`);
28
+ parts.push(`tokenize=${this.tokenizer(config.tokenizer)}`);
29
+ return `CREATE VIRTUAL TABLE IF NOT EXISTS ${config.table} USING fts4(${parts.join(", ")})`;
30
+ }
31
+ matchClause(indexName, queryPlaceholder) {
32
+ return `${indexName} MATCH ${queryPlaceholder}`;
33
+ }
34
+ rankExpression(indexName, _queryPlaceholder) {
35
+ return `length(offsets(${indexName}))`;
36
+ }
37
+ relevanceOrder(indexName, _queryPlaceholder) {
38
+ return `length(offsets(${indexName})) DESC`;
39
+ }
40
+ rebuildCommand(indexName) {
41
+ return `INSERT INTO ${indexName}(${indexName}) VALUES('rebuild')`;
42
+ }
43
+ syncInsert(indexName, rowIdExpr, columns) {
44
+ const placeholders = columns.map(() => "?").join(", ");
45
+ return `INSERT INTO ${indexName} (docid, ${columns.join(", ")}) VALUES (${rowIdExpr}, ${placeholders})`;
46
+ }
47
+ sanitizeQuery(input) {
48
+ return input.replace(/[*()":^~{}[\]\\]/g, "").split(/\s+/).filter((word) => word.length > 0).map((word) => `"${word}"`).join(" OR ");
49
+ }
50
+ joinClause(contentTable, contentAlias, ftsAlias, indexName) {
51
+ return `${indexName} ${ftsAlias} JOIN ${contentTable} ${contentAlias} ON ${contentAlias}.rowid = ${ftsAlias}.docid`;
52
+ }
53
+ /** An FTS5-style tokenizer string as an FTS4 tokenizer name. */
54
+ tokenizer(tokenizer) {
55
+ if (!tokenizer)
56
+ return "unicode61";
57
+ if (tokenizer.includes("porter"))
58
+ return "porter";
59
+ if (tokenizer.includes("unicode61"))
60
+ return "unicode61";
61
+ return "simple";
62
+ }
63
+ }
64
+ // Annotate the CommonJS export names for ESM import in node:
65
+ 0 && (module.exports = {
66
+ SqliteFts4
67
+ });
@@ -14,6 +14,7 @@ export declare class SqliteFts5 implements IFullTextSearch {
14
14
  }): string;
15
15
  matchClause(indexName: string, queryPlaceholder: string): string;
16
16
  rankExpression(indexName: string, _queryPlaceholder?: string): string;
17
+ relevanceOrder(indexName: string, _queryPlaceholder?: string): string;
17
18
  rebuildCommand(indexName: string): string;
18
19
  syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
19
20
  sanitizeQuery(input: string): string;
@@ -38,6 +38,9 @@ class SqliteFts5 {
38
38
  rankExpression(indexName, _queryPlaceholder) {
39
39
  return `${indexName}.rank`;
40
40
  }
41
+ relevanceOrder(indexName, _queryPlaceholder) {
42
+ return `${indexName}.rank`;
43
+ }
41
44
  rebuildCommand(indexName) {
42
45
  return `INSERT INTO ${indexName}(${indexName}) VALUES('rebuild')`;
43
46
  }
@@ -20,6 +20,7 @@ export { segmentFilePath, segmentName, validateSegment } from './shared/segment.
20
20
  export { SqliteDialect } from './dialects/SqliteDialect.js';
21
21
  export { PostgresDialect } from './dialects/PostgresDialect.js';
22
22
  export { SqliteFts5 } from './fts/SqliteFts5.js';
23
+ export { SqliteFts4 } from './fts/SqliteFts4.js';
23
24
  export { PostgresFts } from './fts/PostgresFts.js';
24
25
  export { NodeBlobCodec } from './codecs/NodeBlobCodec.js';
25
26
  export { BrowserBlobCodec } from './codecs/BrowserBlobCodec.js';
package/dist/cjs/index.js CHANGED
@@ -25,6 +25,7 @@ __export(dist_exports, {
25
25
  PostgresFts: () => import_PostgresFts.PostgresFts,
26
26
  SqliteDialect: () => import_SqliteDialect.SqliteDialect,
27
27
  SqliteFileExporter: () => import_SqliteFileExporter.SqliteFileExporter,
28
+ SqliteFts4: () => import_SqliteFts4.SqliteFts4,
28
29
  SqliteFts5: () => import_SqliteFts5.SqliteFts5,
29
30
  createStorageFeatures: () => import_features.createStorageFeatures,
30
31
  getStorageLogger: () => import_logger.getStorageLogger,
@@ -57,6 +58,7 @@ var import_segment = require("./shared/segment.js");
57
58
  var import_SqliteDialect = require("./dialects/SqliteDialect.js");
58
59
  var import_PostgresDialect = require("./dialects/PostgresDialect.js");
59
60
  var import_SqliteFts5 = require("./fts/SqliteFts5.js");
61
+ var import_SqliteFts4 = require("./fts/SqliteFts4.js");
60
62
  var import_PostgresFts = require("./fts/PostgresFts.js");
61
63
  var import_NodeBlobCodec = require("./codecs/NodeBlobCodec.js");
62
64
  var import_BrowserBlobCodec = require("./codecs/BrowserBlobCodec.js");
@@ -74,6 +76,7 @@ var import_logger = require("./shared/logger.js");
74
76
  PostgresFts,
75
77
  SqliteDialect,
76
78
  SqliteFileExporter,
79
+ SqliteFts4,
77
80
  SqliteFts5,
78
81
  createStorageFeatures,
79
82
  getStorageLogger,
@@ -1 +1 @@
1
- {"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../../src/core/contracts/features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAmCjD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,CAa9E"}
1
+ {"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../../src/core/contracts/features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAoCjD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,CAgB9E"}
@@ -1,6 +1,7 @@
1
1
  import { SqliteDialect } from '../../dialects/SqliteDialect.js';
2
2
  import { PostgresDialect } from '../../dialects/PostgresDialect.js';
3
3
  import { SqliteFts5 } from '../../fts/SqliteFts5.js';
4
+ import { SqliteFts4 } from '../../fts/SqliteFts4.js';
4
5
  import { PostgresFts } from '../../fts/PostgresFts.js';
5
6
  import { NodeBlobCodec } from '../../codecs/NodeBlobCodec.js';
6
7
  import { BrowserBlobCodec } from '../../codecs/BrowserBlobCodec.js';
@@ -29,12 +30,15 @@ function extractPostgresConnectionString(adapter) {
29
30
  * dialect, FTS, BLOB codec, and exporter implementations.
30
31
  */
31
32
  export function createStorageFeatures(adapter) {
32
- const isPostgres = adapter.kind === 'postgres';
33
+ // The Supabase adapter runs PostgreSQL too.
34
+ const isPostgres = adapter.kind === 'postgres' || adapter.kind === 'supabase-postgres';
35
+ // sql.js, and the IndexedDB adapter over it, have FTS3 and FTS4 and no FTS5.
36
+ const hasFts5 = adapter.kind !== 'sqljs' && adapter.kind !== 'indexeddb';
33
37
  const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
34
38
  const postgresConnectionString = isPostgres ? extractPostgresConnectionString(adapter) : undefined;
35
39
  return {
36
40
  dialect: isPostgres ? new PostgresDialect() : new SqliteDialect(),
37
- fts: isPostgres ? new PostgresFts() : new SqliteFts5(),
41
+ fts: isPostgres ? new PostgresFts() : hasFts5 ? new SqliteFts5() : new SqliteFts4(),
38
42
  blobCodec: isBrowser ? new BrowserBlobCodec() : new NodeBlobCodec(),
39
43
  exporter: isPostgres
40
44
  ? new PostgresExporter(postgresConnectionString)
@@ -1 +1 @@
1
- {"version":3,"file":"features.js","sourceRoot":"","sources":["../../../src/core/contracts/features.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAYvE,SAAS,+BAA+B,CAAC,OAAuB;IAC9D,MAAM,OAAO,GAAI,OAAqE,CAAC,OAAO,CAAC;IAC/F,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,IAAI,OAAO,CAAC,gBAAgB;QAAE,OAAO,OAAO,CAAC,gBAAgB,CAAC;IAC9D,IAAI,CAAC,OAAO,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAExC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IACzC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,OAAO,gBAAgB,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,OAAO,CAAC,QAAQ,GAAG,QAAQ,EAAE,CAAC;AAC9E,CAAC;AAeD;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAuB;IAC3D,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,KAAK,UAAU,CAAC;IAC/C,MAAM,SAAS,GAAG,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,WAAW,CAAC;IAC1F,MAAM,wBAAwB,GAAG,UAAU,CAAC,CAAC,CAAC,+BAA+B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnG,OAAO;QACL,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,eAAe,EAAE,CAAC,CAAC,CAAC,IAAI,aAAa,EAAE;QACjE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,UAAU,EAAE;QACtD,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,gBAAgB,EAAE,CAAC,CAAC,CAAC,IAAI,aAAa,EAAE;QACnE,QAAQ,EAAE,UAAU;YAClB,CAAC,CAAC,IAAI,gBAAgB,CAAC,wBAAwB,CAAC;YAChD,CAAC,CAAC,IAAI,kBAAkB,CAAC,OAAO,CAAC;KACpC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"features.js","sourceRoot":"","sources":["../../../src/core/contracts/features.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Full-text search abstraction.
3
3
  *
4
- * SQLite implementations use FTS5 virtual tables.
5
- * Postgres implementations use tsvector columns with GIN indexes.
4
+ * better-sqlite3 uses FTS5 virtual tables; sql.js and IndexedDB use FTS4, which
5
+ * their stock sql.js build has (it has no FTS5). Postgres implementations use
6
+ * tsvector columns with GIN indexes.
6
7
  * Methods return SQL strings — no database calls.
7
8
  */
8
9
  export interface IFullTextSearch {
@@ -13,25 +14,36 @@ export interface IFullTextSearch {
13
14
  * @param config.columns - Columns to index.
14
15
  * @param config.contentTable - Source table (for external-content FTS5).
15
16
  * @param config.tokenizer - Tokenizer config (e.g. 'porter ascii').
17
+ * @param config.weights - PostgreSQL: a weight per column (A highest, D lowest) for ranking. The SQLite engines rank without it.
16
18
  */
17
19
  createIndex(config: {
18
20
  table: string;
19
21
  columns: string[];
20
22
  contentTable?: string;
21
23
  tokenizer?: string;
24
+ weights?: Record<string, 'A' | 'B' | 'C' | 'D'>;
22
25
  }): string;
23
26
  /**
24
27
  * Generate a WHERE clause fragment for full-text matching.
25
28
  * SQLite: `memory_traces_fts MATCH ?`
26
- * Postgres: `memory_traces._tsv @@ plainto_tsquery('english', $1)`
29
+ * Postgres: `_tsv @@ websearch_to_tsquery('english', $1)`, in the index's own language
27
30
  */
28
31
  matchClause(indexName: string, queryPlaceholder: string): string;
29
32
  /**
30
- * Generate an ORDER BY rank expression.
31
- * SQLite: `memory_traces_fts.rank`
32
- * Postgres: `ts_rank(memory_traces._tsv, plainto_tsquery('english', $1))`
33
+ * A rank value, in each engine's own direction.
34
+ * SQLite FTS5: `memory_traces_fts.rank` (lower is better)
35
+ * SQLite FTS4: `length(offsets(memory_traces_fts))` (higher is better)
36
+ * Postgres: `ts_rank(_tsv, websearch_to_tsquery('english', $1))` (higher is better)
37
+ * Use relevanceOrder to order the same way on every engine.
33
38
  */
34
39
  rankExpression(indexName: string, queryPlaceholder?: string): string;
40
+ /**
41
+ * An ORDER BY fragment that puts the best match first on every engine.
42
+ * SQLite FTS5: `memory_traces_fts.rank`
43
+ * SQLite FTS4: `length(offsets(memory_traces_fts)) DESC`
44
+ * Postgres: `ts_rank(_tsv, websearch_to_tsquery('english', $1)) DESC`
45
+ */
46
+ relevanceOrder(indexName: string, queryPlaceholder?: string): string;
35
47
  /**
36
48
  * Generate the rebuild/reindex command.
37
49
  * SQLite: `INSERT INTO fts_table(fts_table) VALUES('rebuild')`
@@ -46,8 +58,8 @@ export interface IFullTextSearch {
46
58
  syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
47
59
  /**
48
60
  * Sanitize natural-language input into a safe search query.
49
- * SQLite: wraps words in quotes, strips FTS5 operators.
50
- * Postgres: pass-through (plainto_tsquery handles it).
61
+ * Every engine: the words, quoted, joined with OR, with the operators of
62
+ * the query syntax stripped, so it matches the same rows everywhere.
51
63
  */
52
64
  sanitizeQuery(input: string): string;
53
65
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"fts.d.ts","sourceRoot":"","sources":["../../../src/core/contracts/fts.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;OAOG;IACH,WAAW,CAAC,MAAM,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,MAAM,CAAC;IAEX;;;;OAIG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAAC;IAEjE;;;;OAIG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAErE;;;;OAIG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAE1C;;;;OAIG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAE5E;;;;OAIG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAErC;;;;;;;;;;OAUG;IACH,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CACrG"}
1
+ {"version":3,"file":"fts.d.ts","sourceRoot":"","sources":["../../../src/core/contracts/fts.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;;OAQG;IACH,WAAW,CAAC,MAAM,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;KACjD,GAAG,MAAM,CAAC;IAEX;;;;OAIG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAAC;IAEjE;;;;;;OAMG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAErE;;;;;OAKG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAErE;;;;OAIG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAE1C;;;;OAIG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAE5E;;;;OAIG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAErC;;;;;;;;;;OAUG;IACH,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CACrG"}
@@ -10,6 +10,7 @@ export * from '../types/index.js';
10
10
  export { SqliteDialect } from '../dialects/SqliteDialect.js';
11
11
  export { PostgresDialect } from '../dialects/PostgresDialect.js';
12
12
  export { SqliteFts5 } from '../fts/SqliteFts5.js';
13
+ export { SqliteFts4 } from '../fts/SqliteFts4.js';
13
14
  export { PostgresFts } from '../fts/PostgresFts.js';
14
15
  export { NodeBlobCodec } from '../codecs/NodeBlobCodec.js';
15
16
  export { BrowserBlobCodec } from '../codecs/BrowserBlobCodec.js';
@@ -1 +1 @@
1
- {"version":3,"file":"portable.d.ts","sourceRoot":"","sources":["../../src/core/portable.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC1F,cAAc,yCAAyC,CAAC;AACxD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"portable.d.ts","sourceRoot":"","sources":["../../src/core/portable.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC1F,cAAc,yCAAyC,CAAC;AACxD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC"}
@@ -10,6 +10,7 @@ export * from '../types/index.js';
10
10
  export { SqliteDialect } from '../dialects/SqliteDialect.js';
11
11
  export { PostgresDialect } from '../dialects/PostgresDialect.js';
12
12
  export { SqliteFts5 } from '../fts/SqliteFts5.js';
13
+ export { SqliteFts4 } from '../fts/SqliteFts4.js';
13
14
  export { PostgresFts } from '../fts/PostgresFts.js';
14
15
  export { NodeBlobCodec } from '../codecs/NodeBlobCodec.js';
15
16
  export { BrowserBlobCodec } from '../codecs/BrowserBlobCodec.js';
@@ -1 +1 @@
1
- {"version":3,"file":"portable.js","sourceRoot":"","sources":["../../src/core/portable.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAsB,MAAM,kBAAkB,CAAC;AAC1F,cAAc,yCAAyC,CAAC;AACxD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"portable.js","sourceRoot":"","sources":["../../src/core/portable.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAsB,MAAM,kBAAkB,CAAC;AAC1F,cAAc,yCAAyC,CAAC;AACxD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC"}
@@ -1,29 +1,30 @@
1
1
  import type { IFullTextSearch } from '../core/contracts/fts.js';
2
+ type Weight = 'A' | 'B' | 'C' | 'D';
2
3
  /**
3
4
  * Postgres full-text search using tsvector columns and GIN indexes.
4
5
  *
5
- * Unlike FTS5 (separate virtual table), Postgres stores the tsvector
6
- * as a column `_tsv` on the content table itself. The `createIndex()`
7
- * call adds the column, creates the GIN index, and backfills existing rows.
6
+ * Unlike FTS5 (separate virtual table), Postgres stores the tsvector as a
7
+ * column `_tsv` on the content table itself. The `createIndex()` call adds the
8
+ * column, creates the GIN index, and backfills existing rows. Queries go
9
+ * through `websearch_to_tsquery`, which reads raw user text without syntax
10
+ * errors: words are ANDed, and `or`, `-word` and quoted phrases work as in a
11
+ * web search box.
8
12
  */
9
13
  export declare class PostgresFts implements IFullTextSearch {
10
- /** The content table is stored during createIndex for use by other methods. */
11
- private _contentTable;
12
- private _columns;
13
- private _lang;
14
14
  createIndex(config: {
15
15
  table: string;
16
16
  columns: string[];
17
17
  contentTable?: string;
18
18
  tokenizer?: string;
19
+ weights?: Record<string, Weight>;
19
20
  }): string;
20
- matchClause(_indexName: string, queryPlaceholder: string): string;
21
- rankExpression(_indexName: string, queryPlaceholder?: string): string;
22
- rebuildCommand(_indexName: string): string;
23
- syncInsert(_indexName: string, _rowIdExpr: string, columns: string[]): string;
21
+ matchClause(indexName: string, queryPlaceholder: string): string;
22
+ rankExpression(indexName: string, queryPlaceholder?: string): string;
23
+ relevanceOrder(indexName: string, queryPlaceholder?: string): string;
24
+ rebuildCommand(indexName: string): string;
25
+ syncInsert(indexName: string, _rowIdExpr: string, columns: string[]): string;
24
26
  sanitizeQuery(input: string): string;
25
27
  joinClause(contentTable: string, contentAlias: string, _ftsAlias: string, _indexName: string): string;
26
- /** Map FTS5-style tokenizer strings to Postgres text search configurations. */
27
- private _tokenizerToLang;
28
28
  }
29
+ export {};
29
30
  //# sourceMappingURL=PostgresFts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PostgresFts.d.ts","sourceRoot":"","sources":["../../src/fts/PostgresFts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE;;;;;;GAMG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,+EAA+E;IAC/E,OAAO,CAAC,aAAa,CAAM;IAC3B,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,KAAK,CAAa;IAE1B,WAAW,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAe5G,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM;IAIjE,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM;IAKrE,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAM1C,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAQ7E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIpC,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM;IAIrG,+EAA+E;IAC/E,OAAO,CAAC,gBAAgB;CAKzB"}
1
+ {"version":3,"file":"PostgresFts.d.ts","sourceRoot":"","sources":["../../src/fts/PostgresFts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,KAAK,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAoBpC;;;;;;;;;GASG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,WAAW,CAAC,MAAM,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,GAAG,MAAM;IAgBV,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM;IAIhE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM;IAKpE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIpE,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAKzC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAM5E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IASpC,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM;CAGtG"}
@@ -1,62 +1,106 @@
1
+ /**
2
+ * The indexes createIndex has described in this process, by index name. It
3
+ * is the module's, not an instance's, so a features object made later (or a
4
+ * second one) syncs, rebuilds and queries an index another one created, and
5
+ * each index keeps its own language.
6
+ */
7
+ const INDEXES = new Map();
8
+ const WEIGHTS = new Set(['A', 'B', 'C', 'D']);
1
9
  /**
2
10
  * Postgres full-text search using tsvector columns and GIN indexes.
3
11
  *
4
- * Unlike FTS5 (separate virtual table), Postgres stores the tsvector
5
- * as a column `_tsv` on the content table itself. The `createIndex()`
6
- * call adds the column, creates the GIN index, and backfills existing rows.
12
+ * Unlike FTS5 (separate virtual table), Postgres stores the tsvector as a
13
+ * column `_tsv` on the content table itself. The `createIndex()` call adds the
14
+ * column, creates the GIN index, and backfills existing rows. Queries go
15
+ * through `websearch_to_tsquery`, which reads raw user text without syntax
16
+ * errors: words are ANDed, and `or`, `-word` and quoted phrases work as in a
17
+ * web search box.
7
18
  */
8
19
  export class PostgresFts {
9
- constructor() {
10
- /** The content table is stored during createIndex for use by other methods. */
11
- this._contentTable = '';
12
- this._columns = [];
13
- this._lang = 'english';
14
- }
15
20
  createIndex(config) {
16
- const ct = config.contentTable ?? config.table;
17
- this._contentTable = ct;
18
- this._columns = config.columns;
19
- const colConcat = config.columns.map((c) => `COALESCE(${c}, '')`).join(" || ' ' || ");
20
- const lang = this._tokenizerToLang(config.tokenizer);
21
- this._lang = lang;
21
+ const index = {
22
+ contentTable: config.contentTable ?? config.table,
23
+ columns: config.columns,
24
+ lang: tokenizerToLang(config.tokenizer),
25
+ weights: checkWeights(config.columns, config.weights),
26
+ };
27
+ INDEXES.set(config.table, index);
28
+ const ct = index.contentTable;
22
29
  return [
23
30
  `ALTER TABLE ${ct} ADD COLUMN IF NOT EXISTS _tsv tsvector`,
24
31
  `CREATE INDEX IF NOT EXISTS idx_${config.table}_tsv ON ${ct} USING GIN(_tsv)`,
25
- `UPDATE ${ct} SET _tsv = to_tsvector('${lang}', ${colConcat}) WHERE _tsv IS NULL`,
32
+ `UPDATE ${ct} SET _tsv = ${tsvector(index, index.columns.map((column) => `COALESCE(${column}, '')`))} WHERE _tsv IS NULL`,
26
33
  ].join(';\n');
27
34
  }
28
- matchClause(_indexName, queryPlaceholder) {
29
- return `_tsv @@ plainto_tsquery('${this._lang}', ${queryPlaceholder})`;
35
+ matchClause(indexName, queryPlaceholder) {
36
+ return `_tsv @@ websearch_to_tsquery('${langOf(indexName)}', ${queryPlaceholder})`;
30
37
  }
31
- rankExpression(_indexName, queryPlaceholder) {
38
+ rankExpression(indexName, queryPlaceholder) {
32
39
  const qp = queryPlaceholder ?? '$1';
33
- return `ts_rank(_tsv, plainto_tsquery('${this._lang}', ${qp}))`;
40
+ return `ts_rank(_tsv, websearch_to_tsquery('${langOf(indexName)}', ${qp}))`;
41
+ }
42
+ relevanceOrder(indexName, queryPlaceholder) {
43
+ return `${this.rankExpression(indexName, queryPlaceholder)} DESC`;
34
44
  }
35
- rebuildCommand(_indexName) {
36
- const ct = this._contentTable;
37
- const colConcat = this._columns.map((c) => `COALESCE(${c}, '')`).join(" || ' ' || ");
38
- return `UPDATE ${ct} SET _tsv = to_tsvector('${this._lang}', ${colConcat})`;
45
+ rebuildCommand(indexName) {
46
+ const index = described(indexName);
47
+ return `UPDATE ${index.contentTable} SET _tsv = ${tsvector(index, index.columns.map((column) => `COALESCE(${column}, '')`))}`;
39
48
  }
40
- syncInsert(_indexName, _rowIdExpr, columns) {
41
- const ct = this._contentTable;
42
- const colConcat = columns
43
- .map((_, index) => `COALESCE($${index + 2}, '')`)
44
- .join(" || ' ' || ");
45
- return `UPDATE ${ct} SET _tsv = to_tsvector('${this._lang}', ${colConcat}) WHERE id = $1`;
49
+ syncInsert(indexName, _rowIdExpr, columns) {
50
+ const index = described(indexName);
51
+ const values = columns.map((_, i) => `COALESCE($${i + 2}, '')`);
52
+ return `UPDATE ${index.contentTable} SET _tsv = ${tsvector({ ...index, columns }, values)} WHERE id = $1`;
46
53
  }
47
54
  sanitizeQuery(input) {
48
- return input;
55
+ return input
56
+ .replace(/[*()":^~{}[\]\\-]/g, ' ')
57
+ .split(/\s+/)
58
+ .filter((word) => word.length > 0 && word.toLowerCase() !== 'or')
59
+ .map((word) => `"${word}"`)
60
+ .join(' or ');
49
61
  }
50
62
  joinClause(contentTable, contentAlias, _ftsAlias, _indexName) {
51
63
  return `${contentTable} ${contentAlias}`;
52
64
  }
53
- /** Map FTS5-style tokenizer strings to Postgres text search configurations. */
54
- _tokenizerToLang(tokenizer) {
55
- if (!tokenizer)
56
- return 'english';
57
- if (tokenizer.includes('porter'))
58
- return 'english';
59
- return 'simple';
60
- }
61
65
  }
66
+ /** Map FTS5-style tokenizer strings to Postgres text search configurations. */
67
+ const tokenizerToLang = (tokenizer) => {
68
+ if (!tokenizer)
69
+ return 'english';
70
+ if (tokenizer.includes('porter'))
71
+ return 'english';
72
+ return 'simple';
73
+ };
74
+ const checkWeights = (columns, weights) => {
75
+ if (!weights)
76
+ return undefined;
77
+ for (const [column, weight] of Object.entries(weights)) {
78
+ if (!columns.includes(column))
79
+ throw new Error(`weights names ${JSON.stringify(column)}, which is not one of the indexed columns.`);
80
+ if (!WEIGHTS.has(weight))
81
+ throw new Error(`The weight of ${column} must be 'A', 'B', 'C' or 'D'; got ${JSON.stringify(weight)}.`);
82
+ }
83
+ return weights;
84
+ };
85
+ /** The tsvector expression over `values`, one per indexed column, weighted when the index has weights. */
86
+ const tsvector = (index, values) => {
87
+ if (!index.weights)
88
+ return `to_tsvector('${index.lang}', ${values.join(" || ' ' || ")})`;
89
+ return values
90
+ .map((value, i) => {
91
+ const weight = index.weights[index.columns[i]];
92
+ const vector = `to_tsvector('${index.lang}', ${value})`;
93
+ return weight ? `setweight(${vector}, '${weight}')` : vector;
94
+ })
95
+ .join(' || ');
96
+ };
97
+ const described = (indexName) => {
98
+ const index = INDEXES.get(indexName);
99
+ if (!index) {
100
+ throw new Error(`No PostgreSQL full-text index named ${JSON.stringify(indexName)} has been described in this process: run its createIndex() first (it is idempotent).`);
101
+ }
102
+ return index;
103
+ };
104
+ /** The language of an index, or 'english' for an index this process has not described. */
105
+ const langOf = (indexName) => INDEXES.get(indexName)?.lang ?? 'english';
62
106
  //# sourceMappingURL=PostgresFts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PostgresFts.js","sourceRoot":"","sources":["../../src/fts/PostgresFts.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,OAAO,WAAW;IAAxB;QACE,+EAA+E;QACvE,kBAAa,GAAG,EAAE,CAAC;QACnB,aAAQ,GAAa,EAAE,CAAC;QACxB,UAAK,GAAG,SAAS,CAAC;IAsD5B,CAAC;IApDC,WAAW,CAAC,MAAuF;QACjG,MAAM,EAAE,GAAG,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,KAAK,CAAC;QAC/C,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC;QAC/B,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACtF,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACrD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAElB,OAAO;YACL,eAAe,EAAE,yCAAyC;YAC1D,kCAAkC,MAAM,CAAC,KAAK,WAAW,EAAE,kBAAkB;YAC7E,UAAU,EAAE,4BAA4B,IAAI,MAAM,SAAS,sBAAsB;SAClF,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC;IAED,WAAW,CAAC,UAAkB,EAAE,gBAAwB;QACtD,OAAO,4BAA4B,IAAI,CAAC,KAAK,MAAM,gBAAgB,GAAG,CAAC;IACzE,CAAC;IAED,cAAc,CAAC,UAAkB,EAAE,gBAAyB;QAC1D,MAAM,EAAE,GAAG,gBAAgB,IAAI,IAAI,CAAC;QACpC,OAAO,kCAAkC,IAAI,CAAC,KAAK,MAAM,EAAE,IAAI,CAAC;IAClE,CAAC;IAED,cAAc,CAAC,UAAkB;QAC/B,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACrF,OAAO,UAAU,EAAE,4BAA4B,IAAI,CAAC,KAAK,MAAM,SAAS,GAAG,CAAC;IAC9E,CAAC;IAED,UAAU,CAAC,UAAkB,EAAE,UAAkB,EAAE,OAAiB;QAClE,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;QAC9B,MAAM,SAAS,GAAG,OAAO;aACtB,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,aAAa,KAAK,GAAG,CAAC,OAAO,CAAC;aAChD,IAAI,CAAC,aAAa,CAAC,CAAC;QACvB,OAAO,UAAU,EAAE,4BAA4B,IAAI,CAAC,KAAK,MAAM,SAAS,iBAAiB,CAAC;IAC5F,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,UAAU,CAAC,YAAoB,EAAE,YAAoB,EAAE,SAAiB,EAAE,UAAkB;QAC1F,OAAO,GAAG,YAAY,IAAI,YAAY,EAAE,CAAC;IAC3C,CAAC;IAED,+EAA+E;IACvE,gBAAgB,CAAC,SAAkB;QACzC,IAAI,CAAC,SAAS;YAAE,OAAO,SAAS,CAAC;QACjC,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAC;QACnD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
1
+ {"version":3,"file":"PostgresFts.js","sourceRoot":"","sources":["../../src/fts/PostgresFts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { IFullTextSearch } from '../core/contracts/fts.js';
2
+ /**
3
+ * SQLite FTS4 full-text search, for sql.js and the IndexedDB adapter: the
4
+ * stock sql.js build compiles SQLite with FTS3 and FTS4 and without FTS5.
5
+ *
6
+ * Uses FTS4 virtual tables with external content, the porter, unicode61 or
7
+ * simple tokenizer, and rowid joins to the content table. FTS4 has no rank
8
+ * column; relevanceOrder orders by the length of `offsets()`, which grows with
9
+ * the number of matching terms, so the best match comes first.
10
+ */
11
+ export declare class SqliteFts4 implements IFullTextSearch {
12
+ createIndex(config: {
13
+ table: string;
14
+ columns: string[];
15
+ contentTable?: string;
16
+ tokenizer?: string;
17
+ }): string;
18
+ matchClause(indexName: string, queryPlaceholder: string): string;
19
+ rankExpression(indexName: string, _queryPlaceholder?: string): string;
20
+ relevanceOrder(indexName: string, _queryPlaceholder?: string): string;
21
+ rebuildCommand(indexName: string): string;
22
+ syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
23
+ sanitizeQuery(input: string): string;
24
+ joinClause(contentTable: string, contentAlias: string, ftsAlias: string, indexName: string): string;
25
+ /** An FTS5-style tokenizer string as an FTS4 tokenizer name. */
26
+ private tokenizer;
27
+ }
28
+ //# sourceMappingURL=SqliteFts4.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqliteFts4.d.ts","sourceRoot":"","sources":["../../src/fts/SqliteFts4.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE;;;;;;;;GAQG;AACH,qBAAa,UAAW,YAAW,eAAe;IAChD,WAAW,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAO5G,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM;IAIhE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrE,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIzC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAK3E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IASpC,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAInG,gEAAgE;IAChE,OAAO,CAAC,SAAS;CAMlB"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * SQLite FTS4 full-text search, for sql.js and the IndexedDB adapter: the
3
+ * stock sql.js build compiles SQLite with FTS3 and FTS4 and without FTS5.
4
+ *
5
+ * Uses FTS4 virtual tables with external content, the porter, unicode61 or
6
+ * simple tokenizer, and rowid joins to the content table. FTS4 has no rank
7
+ * column; relevanceOrder orders by the length of `offsets()`, which grows with
8
+ * the number of matching terms, so the best match comes first.
9
+ */
10
+ export class SqliteFts4 {
11
+ createIndex(config) {
12
+ const parts = [...config.columns];
13
+ if (config.contentTable)
14
+ parts.push(`content="${config.contentTable}"`);
15
+ parts.push(`tokenize=${this.tokenizer(config.tokenizer)}`);
16
+ return `CREATE VIRTUAL TABLE IF NOT EXISTS ${config.table} USING fts4(${parts.join(', ')})`;
17
+ }
18
+ matchClause(indexName, queryPlaceholder) {
19
+ return `${indexName} MATCH ${queryPlaceholder}`;
20
+ }
21
+ rankExpression(indexName, _queryPlaceholder) {
22
+ return `length(offsets(${indexName}))`;
23
+ }
24
+ relevanceOrder(indexName, _queryPlaceholder) {
25
+ return `length(offsets(${indexName})) DESC`;
26
+ }
27
+ rebuildCommand(indexName) {
28
+ return `INSERT INTO ${indexName}(${indexName}) VALUES('rebuild')`;
29
+ }
30
+ syncInsert(indexName, rowIdExpr, columns) {
31
+ const placeholders = columns.map(() => '?').join(', ');
32
+ return `INSERT INTO ${indexName} (docid, ${columns.join(', ')}) VALUES (${rowIdExpr}, ${placeholders})`;
33
+ }
34
+ sanitizeQuery(input) {
35
+ return input
36
+ .replace(/[*()":^~{}[\]\\]/g, '')
37
+ .split(/\s+/)
38
+ .filter((word) => word.length > 0)
39
+ .map((word) => `"${word}"`)
40
+ .join(' OR ');
41
+ }
42
+ joinClause(contentTable, contentAlias, ftsAlias, indexName) {
43
+ return `${indexName} ${ftsAlias} JOIN ${contentTable} ${contentAlias} ON ${contentAlias}.rowid = ${ftsAlias}.docid`;
44
+ }
45
+ /** An FTS5-style tokenizer string as an FTS4 tokenizer name. */
46
+ tokenizer(tokenizer) {
47
+ if (!tokenizer)
48
+ return 'unicode61';
49
+ if (tokenizer.includes('porter'))
50
+ return 'porter';
51
+ if (tokenizer.includes('unicode61'))
52
+ return 'unicode61';
53
+ return 'simple';
54
+ }
55
+ }
56
+ //# sourceMappingURL=SqliteFts4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqliteFts4.js","sourceRoot":"","sources":["../../src/fts/SqliteFts4.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,OAAO,UAAU;IACrB,WAAW,CAAC,MAAuF;QACjG,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,YAAY;YAAE,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;QACxE,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC3D,OAAO,sCAAsC,MAAM,CAAC,KAAK,eAAe,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAC9F,CAAC;IAED,WAAW,CAAC,SAAiB,EAAE,gBAAwB;QACrD,OAAO,GAAG,SAAS,UAAU,gBAAgB,EAAE,CAAC;IAClD,CAAC;IAED,cAAc,CAAC,SAAiB,EAAE,iBAA0B;QAC1D,OAAO,kBAAkB,SAAS,IAAI,CAAC;IACzC,CAAC;IAED,cAAc,CAAC,SAAiB,EAAE,iBAA0B;QAC1D,OAAO,kBAAkB,SAAS,SAAS,CAAC;IAC9C,CAAC;IAED,cAAc,CAAC,SAAiB;QAC9B,OAAO,eAAe,SAAS,IAAI,SAAS,qBAAqB,CAAC;IACpE,CAAC;IAED,UAAU,CAAC,SAAiB,EAAE,SAAiB,EAAE,OAAiB;QAChE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,eAAe,SAAS,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,SAAS,KAAK,YAAY,GAAG,CAAC;IAC1G,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,OAAO,KAAK;aACT,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;aAChC,KAAK,CAAC,KAAK,CAAC;aACZ,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;aACjC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC;aAC1B,IAAI,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,UAAU,CAAC,YAAoB,EAAE,YAAoB,EAAE,QAAgB,EAAE,SAAiB;QACxF,OAAO,GAAG,SAAS,IAAI,QAAQ,SAAS,YAAY,IAAI,YAAY,OAAO,YAAY,YAAY,QAAQ,QAAQ,CAAC;IACtH,CAAC;IAED,gEAAgE;IACxD,SAAS,CAAC,SAAkB;QAClC,IAAI,CAAC,SAAS;YAAE,OAAO,WAAW,CAAC;QACnC,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,OAAO,QAAQ,CAAC;QAClD,IAAI,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,OAAO,WAAW,CAAC;QACxD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
@@ -14,6 +14,7 @@ export declare class SqliteFts5 implements IFullTextSearch {
14
14
  }): string;
15
15
  matchClause(indexName: string, queryPlaceholder: string): string;
16
16
  rankExpression(indexName: string, _queryPlaceholder?: string): string;
17
+ relevanceOrder(indexName: string, _queryPlaceholder?: string): string;
17
18
  rebuildCommand(indexName: string): string;
18
19
  syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
19
20
  sanitizeQuery(input: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"SqliteFts5.d.ts","sourceRoot":"","sources":["../../src/fts/SqliteFts5.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE;;;;;GAKG;AACH,qBAAa,UAAW,YAAW,eAAe;IAChD,WAAW,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAY5G,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM;IAIhE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrE,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIzC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAK3E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IASpC,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;CAGpG"}
1
+ {"version":3,"file":"SqliteFts5.d.ts","sourceRoot":"","sources":["../../src/fts/SqliteFts5.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE;;;;;GAKG;AACH,qBAAa,UAAW,YAAW,eAAe;IAChD,WAAW,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAY5G,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM;IAIhE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrE,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIzC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAK3E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IASpC,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;CAGpG"}
@@ -22,6 +22,9 @@ export class SqliteFts5 {
22
22
  rankExpression(indexName, _queryPlaceholder) {
23
23
  return `${indexName}.rank`;
24
24
  }
25
+ relevanceOrder(indexName, _queryPlaceholder) {
26
+ return `${indexName}.rank`;
27
+ }
25
28
  rebuildCommand(indexName) {
26
29
  return `INSERT INTO ${indexName}(${indexName}) VALUES('rebuild')`;
27
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SqliteFts5.js","sourceRoot":"","sources":["../../src/fts/SqliteFts5.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,OAAO,UAAU;IACrB,WAAW,CAAC,MAAuF;QACjG,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,sCAAsC,MAAM,CAAC,KAAK,eAAe,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAC9F,CAAC;IAED,WAAW,CAAC,SAAiB,EAAE,gBAAwB;QACrD,OAAO,GAAG,SAAS,UAAU,gBAAgB,EAAE,CAAC;IAClD,CAAC;IAED,cAAc,CAAC,SAAiB,EAAE,iBAA0B;QAC1D,OAAO,GAAG,SAAS,OAAO,CAAC;IAC7B,CAAC;IAED,cAAc,CAAC,SAAiB;QAC9B,OAAO,eAAe,SAAS,IAAI,SAAS,qBAAqB,CAAC;IACpE,CAAC;IAED,UAAU,CAAC,SAAiB,EAAE,SAAiB,EAAE,OAAiB;QAChE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,eAAe,SAAS,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,SAAS,KAAK,YAAY,GAAG,CAAC;IAC1G,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,OAAO,KAAK;aACT,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;aAChC,KAAK,CAAC,KAAK,CAAC;aACZ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;aAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;aACpB,IAAI,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,UAAU,CAAC,YAAoB,EAAE,YAAoB,EAAE,QAAgB,EAAE,SAAiB;QACxF,OAAO,GAAG,SAAS,IAAI,QAAQ,SAAS,YAAY,IAAI,YAAY,OAAO,YAAY,YAAY,QAAQ,QAAQ,CAAC;IACtH,CAAC;CACF"}
1
+ {"version":3,"file":"SqliteFts5.js","sourceRoot":"","sources":["../../src/fts/SqliteFts5.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,OAAO,UAAU;IACrB,WAAW,CAAC,MAAuF;QACjG,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,sCAAsC,MAAM,CAAC,KAAK,eAAe,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAC9F,CAAC;IAED,WAAW,CAAC,SAAiB,EAAE,gBAAwB;QACrD,OAAO,GAAG,SAAS,UAAU,gBAAgB,EAAE,CAAC;IAClD,CAAC;IAED,cAAc,CAAC,SAAiB,EAAE,iBAA0B;QAC1D,OAAO,GAAG,SAAS,OAAO,CAAC;IAC7B,CAAC;IAED,cAAc,CAAC,SAAiB,EAAE,iBAA0B;QAC1D,OAAO,GAAG,SAAS,OAAO,CAAC;IAC7B,CAAC;IAED,cAAc,CAAC,SAAiB;QAC9B,OAAO,eAAe,SAAS,IAAI,SAAS,qBAAqB,CAAC;IACpE,CAAC;IAED,UAAU,CAAC,SAAiB,EAAE,SAAiB,EAAE,OAAiB;QAChE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,eAAe,SAAS,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,SAAS,KAAK,YAAY,GAAG,CAAC;IAC1G,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,OAAO,KAAK;aACT,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;aAChC,KAAK,CAAC,KAAK,CAAC;aACZ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;aAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;aACpB,IAAI,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,UAAU,CAAC,YAAoB,EAAE,YAAoB,EAAE,QAAgB,EAAE,SAAiB;QACxF,OAAO,GAAG,SAAS,IAAI,QAAQ,SAAS,YAAY,IAAI,YAAY,OAAO,YAAY,YAAY,QAAQ,QAAQ,CAAC;IACtH,CAAC;CACF"}
package/dist/index.d.ts CHANGED
@@ -20,6 +20,7 @@ export { segmentFilePath, segmentName, validateSegment } from './shared/segment.
20
20
  export { SqliteDialect } from './dialects/SqliteDialect.js';
21
21
  export { PostgresDialect } from './dialects/PostgresDialect.js';
22
22
  export { SqliteFts5 } from './fts/SqliteFts5.js';
23
+ export { SqliteFts4 } from './fts/SqliteFts4.js';
23
24
  export { PostgresFts } from './fts/PostgresFts.js';
24
25
  export { NodeBlobCodec } from './codecs/NodeBlobCodec.js';
25
26
  export { BrowserBlobCodec } from './codecs/BrowserBlobCodec.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAQ9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAM5C,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAQ9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAM5C,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ export { segmentFilePath, segmentName, validateSegment } from './shared/segment.
34
34
  export { SqliteDialect } from './dialects/SqliteDialect.js';
35
35
  export { PostgresDialect } from './dialects/PostgresDialect.js';
36
36
  export { SqliteFts5 } from './fts/SqliteFts5.js';
37
+ export { SqliteFts4 } from './fts/SqliteFts4.js';
37
38
  export { PostgresFts } from './fts/PostgresFts.js';
38
39
  export { NodeBlobCodec } from './codecs/NodeBlobCodec.js';
39
40
  export { BrowserBlobCodec } from './codecs/BrowserBlobCodec.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,cAAc,SAAS,CAAC;AAExB,8EAA8E;AAC9E,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAEhC,+EAA+E;AAC/E,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAE9C,wEAAwE;AACxE,yDAAyD;AACzD,2HAA2H;AAC3H,mEAAmE;AAEnE,+EAA+E;AAC/E,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAE5C,uFAAuF;AACvF,sFAAsF;AAEtF,+EAA+E;AAC/E,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEjF,6FAA6F;AAC7F,uGAAuG;AAEvG,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAsB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,cAAc,SAAS,CAAC;AAExB,8EAA8E;AAC9E,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAEhC,+EAA+E;AAC/E,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAE9C,wEAAwE;AACxE,yDAAyD;AACzD,2HAA2H;AAC3H,mEAAmE;AAEnE,+EAA+E;AAC/E,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAE5C,uFAAuF;AACvF,sFAAsF;AAEtF,+EAA+E;AAC/E,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEjF,6FAA6F;AAC7F,uGAAuG;AAEvG,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAsB,MAAM,iBAAiB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@framers/sql-storage-adapter",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "Robust cross-platform SQL storage abstraction with automatic fallbacks and runtime detection",
5
5
  "type": "module",
6
6
  "main": "dist/cjs/index.js",