@aigne/afs-sqlite 1.1.0 → 1.11.0-beta.1

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 (163) hide show
  1. package/LICENSE.md +17 -84
  2. package/README.md +51 -39
  3. package/dist/actions/built-in.cjs +142 -0
  4. package/dist/actions/built-in.d.cts +10 -0
  5. package/dist/actions/built-in.d.cts.map +1 -0
  6. package/dist/actions/built-in.d.mts +10 -0
  7. package/dist/actions/built-in.d.mts.map +1 -0
  8. package/dist/actions/built-in.mjs +143 -0
  9. package/dist/actions/built-in.mjs.map +1 -0
  10. package/dist/actions/registry.cjs +91 -0
  11. package/dist/actions/registry.d.cts +54 -0
  12. package/dist/actions/registry.d.cts.map +1 -0
  13. package/dist/actions/registry.d.mts +54 -0
  14. package/dist/actions/registry.d.mts.map +1 -0
  15. package/dist/actions/registry.mjs +91 -0
  16. package/dist/actions/registry.mjs.map +1 -0
  17. package/dist/actions/types.d.cts +56 -0
  18. package/dist/actions/types.d.cts.map +1 -0
  19. package/dist/actions/types.d.mts +56 -0
  20. package/dist/actions/types.d.mts.map +1 -0
  21. package/dist/config.cjs +27 -0
  22. package/dist/config.d.cts +81 -0
  23. package/dist/config.d.cts.map +1 -0
  24. package/dist/config.d.mts +81 -0
  25. package/dist/config.d.mts.map +1 -0
  26. package/dist/config.mjs +28 -0
  27. package/dist/config.mjs.map +1 -0
  28. package/dist/index.cjs +38 -0
  29. package/dist/index.d.cts +14 -0
  30. package/dist/index.d.mts +14 -0
  31. package/dist/index.mjs +12 -0
  32. package/dist/node/builder.cjs +179 -0
  33. package/dist/node/builder.d.cts +48 -0
  34. package/dist/node/builder.d.cts.map +1 -0
  35. package/dist/node/builder.d.mts +48 -0
  36. package/dist/node/builder.d.mts.map +1 -0
  37. package/dist/node/builder.mjs +172 -0
  38. package/dist/node/builder.mjs.map +1 -0
  39. package/dist/operations/crud.cjs +176 -0
  40. package/dist/operations/crud.d.cts +69 -0
  41. package/dist/operations/crud.d.cts.map +1 -0
  42. package/dist/operations/crud.d.mts +69 -0
  43. package/dist/operations/crud.d.mts.map +1 -0
  44. package/dist/operations/crud.mjs +177 -0
  45. package/dist/operations/crud.mjs.map +1 -0
  46. package/dist/operations/query-builder.cjs +77 -0
  47. package/dist/operations/query-builder.d.cts +34 -0
  48. package/dist/operations/query-builder.d.cts.map +1 -0
  49. package/dist/operations/query-builder.d.mts +34 -0
  50. package/dist/operations/query-builder.d.mts.map +1 -0
  51. package/dist/operations/query-builder.mjs +72 -0
  52. package/dist/operations/query-builder.mjs.map +1 -0
  53. package/dist/operations/search.cjs +141 -0
  54. package/dist/operations/search.d.cts +79 -0
  55. package/dist/operations/search.d.cts.map +1 -0
  56. package/dist/operations/search.d.mts +79 -0
  57. package/dist/operations/search.d.mts.map +1 -0
  58. package/dist/operations/search.mjs +141 -0
  59. package/dist/operations/search.mjs.map +1 -0
  60. package/dist/router/path-router.cjs +79 -0
  61. package/{lib/dts/router/path-router.d.ts → dist/router/path-router.d.cts} +12 -8
  62. package/dist/router/path-router.d.cts.map +1 -0
  63. package/{lib/cjs/router/path-router.d.ts → dist/router/path-router.d.mts} +12 -8
  64. package/dist/router/path-router.d.mts.map +1 -0
  65. package/dist/router/path-router.mjs +76 -0
  66. package/dist/router/path-router.mjs.map +1 -0
  67. package/dist/router/types.d.cts +34 -0
  68. package/dist/router/types.d.cts.map +1 -0
  69. package/dist/router/types.d.mts +34 -0
  70. package/dist/router/types.d.mts.map +1 -0
  71. package/dist/schema/introspector.cjs +160 -0
  72. package/dist/schema/introspector.d.cts +49 -0
  73. package/dist/schema/introspector.d.cts.map +1 -0
  74. package/dist/schema/introspector.d.mts +49 -0
  75. package/dist/schema/introspector.d.mts.map +1 -0
  76. package/dist/schema/introspector.mjs +161 -0
  77. package/dist/schema/introspector.mjs.map +1 -0
  78. package/dist/schema/types.cjs +15 -0
  79. package/dist/schema/types.d.cts +104 -0
  80. package/dist/schema/types.d.cts.map +1 -0
  81. package/dist/schema/types.d.mts +104 -0
  82. package/dist/schema/types.d.mts.map +1 -0
  83. package/dist/schema/types.mjs +15 -0
  84. package/dist/schema/types.mjs.map +1 -0
  85. package/dist/sqlite-afs.cjs +264 -0
  86. package/dist/sqlite-afs.d.cts +152 -0
  87. package/dist/sqlite-afs.d.cts.map +1 -0
  88. package/dist/sqlite-afs.d.mts +152 -0
  89. package/dist/sqlite-afs.d.mts.map +1 -0
  90. package/dist/sqlite-afs.mjs +265 -0
  91. package/dist/sqlite-afs.mjs.map +1 -0
  92. package/package.json +31 -44
  93. package/CHANGELOG.md +0 -73
  94. package/lib/cjs/actions/built-in.d.ts +0 -5
  95. package/lib/cjs/actions/built-in.js +0 -165
  96. package/lib/cjs/actions/registry.d.ts +0 -49
  97. package/lib/cjs/actions/registry.js +0 -102
  98. package/lib/cjs/actions/types.d.ts +0 -51
  99. package/lib/cjs/actions/types.js +0 -2
  100. package/lib/cjs/config.d.ts +0 -89
  101. package/lib/cjs/config.js +0 -33
  102. package/lib/cjs/index.d.ts +0 -13
  103. package/lib/cjs/index.js +0 -47
  104. package/lib/cjs/node/builder.d.ts +0 -43
  105. package/lib/cjs/node/builder.js +0 -187
  106. package/lib/cjs/operations/crud.d.ts +0 -64
  107. package/lib/cjs/operations/crud.js +0 -225
  108. package/lib/cjs/operations/query-builder.d.ts +0 -37
  109. package/lib/cjs/operations/query-builder.js +0 -102
  110. package/lib/cjs/operations/search.d.ts +0 -75
  111. package/lib/cjs/operations/search.js +0 -172
  112. package/lib/cjs/package.json +0 -3
  113. package/lib/cjs/router/path-router.js +0 -90
  114. package/lib/cjs/router/types.d.ts +0 -30
  115. package/lib/cjs/router/types.js +0 -2
  116. package/lib/cjs/schema/introspector.d.ts +0 -48
  117. package/lib/cjs/schema/introspector.js +0 -186
  118. package/lib/cjs/schema/types.d.ts +0 -104
  119. package/lib/cjs/schema/types.js +0 -13
  120. package/lib/cjs/sqlite-afs.d.ts +0 -144
  121. package/lib/cjs/sqlite-afs.js +0 -337
  122. package/lib/dts/actions/built-in.d.ts +0 -5
  123. package/lib/dts/actions/registry.d.ts +0 -49
  124. package/lib/dts/actions/types.d.ts +0 -51
  125. package/lib/dts/config.d.ts +0 -89
  126. package/lib/dts/index.d.ts +0 -13
  127. package/lib/dts/node/builder.d.ts +0 -43
  128. package/lib/dts/operations/crud.d.ts +0 -64
  129. package/lib/dts/operations/query-builder.d.ts +0 -37
  130. package/lib/dts/operations/search.d.ts +0 -75
  131. package/lib/dts/router/types.d.ts +0 -30
  132. package/lib/dts/schema/introspector.d.ts +0 -48
  133. package/lib/dts/schema/types.d.ts +0 -104
  134. package/lib/dts/sqlite-afs.d.ts +0 -144
  135. package/lib/esm/actions/built-in.d.ts +0 -5
  136. package/lib/esm/actions/built-in.js +0 -162
  137. package/lib/esm/actions/registry.d.ts +0 -49
  138. package/lib/esm/actions/registry.js +0 -98
  139. package/lib/esm/actions/types.d.ts +0 -51
  140. package/lib/esm/actions/types.js +0 -1
  141. package/lib/esm/config.d.ts +0 -89
  142. package/lib/esm/config.js +0 -30
  143. package/lib/esm/index.d.ts +0 -13
  144. package/lib/esm/index.js +0 -17
  145. package/lib/esm/node/builder.d.ts +0 -43
  146. package/lib/esm/node/builder.js +0 -177
  147. package/lib/esm/operations/crud.d.ts +0 -64
  148. package/lib/esm/operations/crud.js +0 -221
  149. package/lib/esm/operations/query-builder.d.ts +0 -37
  150. package/lib/esm/operations/query-builder.js +0 -92
  151. package/lib/esm/operations/search.d.ts +0 -75
  152. package/lib/esm/operations/search.js +0 -167
  153. package/lib/esm/package.json +0 -3
  154. package/lib/esm/router/path-router.d.ts +0 -38
  155. package/lib/esm/router/path-router.js +0 -83
  156. package/lib/esm/router/types.d.ts +0 -30
  157. package/lib/esm/router/types.js +0 -1
  158. package/lib/esm/schema/introspector.d.ts +0 -48
  159. package/lib/esm/schema/introspector.js +0 -182
  160. package/lib/esm/schema/types.d.ts +0 -104
  161. package/lib/esm/schema/types.js +0 -10
  162. package/lib/esm/sqlite-afs.d.ts +0 -144
  163. package/lib/esm/sqlite-afs.js +0 -333
@@ -0,0 +1,34 @@
1
+ import { TableSchema } from "../schema/types.mjs";
2
+
3
+ //#region src/operations/query-builder.d.ts
4
+ /**
5
+ * Builds a SELECT query string for a single row by primary key
6
+ */
7
+ declare function buildSelectByPK(tableName: string, schema: TableSchema, pk: string): string;
8
+ /**
9
+ * Builds a SELECT query string for listing rows with optional limit and offset
10
+ */
11
+ declare function buildSelectAll(tableName: string, options?: {
12
+ limit?: number;
13
+ offset?: number;
14
+ orderBy?: [string, "asc" | "desc"][];
15
+ }): string;
16
+ /**
17
+ * Builds an INSERT query string from content object
18
+ */
19
+ declare function buildInsert(tableName: string, schema: TableSchema, content: Record<string, unknown>): string;
20
+ /**
21
+ * Builds an UPDATE query string from content object
22
+ */
23
+ declare function buildUpdate(tableName: string, schema: TableSchema, pk: string, content: Record<string, unknown>): string;
24
+ /**
25
+ * Builds a DELETE query string by primary key
26
+ */
27
+ declare function buildDelete(tableName: string, schema: TableSchema, pk: string): string;
28
+ /**
29
+ * Gets the last inserted rowid query string
30
+ */
31
+ declare function buildGetLastRowId(): string;
32
+ //#endregion
33
+ export { buildDelete, buildGetLastRowId, buildInsert, buildSelectAll, buildSelectByPK, buildUpdate };
34
+ //# sourceMappingURL=query-builder.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-builder.d.mts","names":[],"sources":["../../src/operations/query-builder.ts"],"mappings":";;;;AAKA;AAQA;iBARgB,eAAA,CAAA,SAAA,UAAA,MAAA,EAA2C,WAAA,EAAA,EAAA;AAAA;AAQ3D;AA6BA;AArC2D,iBAQ3C,cAAA,CAAA,SAAA,UAAA,OAAA;EAAA,KAAA;EAAA,MAAA;EAAA,OAAA;AAAA;AAAA;AA6BhB;AAsBA;AAnDgB,iBA6BA,WAAA,CAAA,SAAA,UAAA,MAAA,EAEN,WAAA,EAAA,OAAA,EACC,MAAA;AAAA;AAmBX;AA0BA;AA7CW,iBAmBK,WAAA,CAAA,SAAA,UAAA,MAAA,EAEN,WAAA,EAAA,EAAA,UAAA,OAAA,EAEC,MAAA;AAAA;AAsBX;AA0CA;AAhEW,iBAsBK,WAAA,CAAA,SAAA,UAAA,MAAA,EAAuC,WAAA,EAAA,EAAA;AAAA;AA0CvD;;AA1CuD,iBA0CvC,iBAAA,CAAA"}
@@ -0,0 +1,72 @@
1
+ //#region src/operations/query-builder.ts
2
+ /**
3
+ * Builds a SELECT query string for a single row by primary key
4
+ */
5
+ function buildSelectByPK(tableName, schema, pk) {
6
+ return `SELECT * FROM "${tableName}" WHERE "${schema.primaryKey[0] ?? "rowid"}" = '${escapeSQLString(pk)}'`;
7
+ }
8
+ /**
9
+ * Builds a SELECT query string for listing rows with optional limit and offset
10
+ */
11
+ function buildSelectAll(tableName, options) {
12
+ let query = `SELECT * FROM "${tableName}"`;
13
+ if (options?.orderBy?.length) {
14
+ const orderClauses = options.orderBy.map(([col, dir]) => `"${col}" ${dir.toUpperCase()}`);
15
+ query += ` ORDER BY ${orderClauses.join(", ")}`;
16
+ }
17
+ if (options?.limit !== void 0) query += ` LIMIT ${options.limit}`;
18
+ if (options?.offset !== void 0) query += ` OFFSET ${options.offset}`;
19
+ return query;
20
+ }
21
+ /**
22
+ * Builds an INSERT query string from content object
23
+ */
24
+ function buildInsert(tableName, schema, content) {
25
+ const validColumns = new Set(schema.columns.map((c) => c.name));
26
+ const entries = Object.entries(content).filter(([key]) => validColumns.has(key));
27
+ if (entries.length === 0) throw new Error(`No valid columns provided for INSERT into ${tableName}`);
28
+ return `INSERT INTO "${tableName}" (${entries.map(([key]) => `"${key}"`).join(", ")}) VALUES (${entries.map(([, value]) => formatValue(value)).join(", ")})`;
29
+ }
30
+ /**
31
+ * Builds an UPDATE query string from content object
32
+ */
33
+ function buildUpdate(tableName, schema, pk, content) {
34
+ const pkColumn = schema.primaryKey[0] ?? "rowid";
35
+ const validColumns = new Set(schema.columns.map((c) => c.name));
36
+ const entries = Object.entries(content).filter(([key]) => validColumns.has(key) && key !== pkColumn);
37
+ if (entries.length === 0) throw new Error(`No valid columns provided for UPDATE on ${tableName}`);
38
+ return `UPDATE "${tableName}" SET ${entries.map(([key, value]) => `"${key}" = ${formatValue(value)}`).join(", ")} WHERE "${pkColumn}" = '${escapeSQLString(pk)}'`;
39
+ }
40
+ /**
41
+ * Builds a DELETE query string by primary key
42
+ */
43
+ function buildDelete(tableName, schema, pk) {
44
+ return `DELETE FROM "${tableName}" WHERE "${schema.primaryKey[0] ?? "rowid"}" = '${escapeSQLString(pk)}'`;
45
+ }
46
+ /**
47
+ * Formats a value for SQL insertion
48
+ */
49
+ function formatValue(value) {
50
+ if (value === null || value === void 0) return "NULL";
51
+ if (typeof value === "number") return String(value);
52
+ if (typeof value === "boolean") return value ? "1" : "0";
53
+ if (value instanceof Date) return `'${value.toISOString()}'`;
54
+ if (typeof value === "object") return `'${escapeSQLString(JSON.stringify(value))}'`;
55
+ return `'${escapeSQLString(String(value))}'`;
56
+ }
57
+ /**
58
+ * Escapes a string for safe SQL insertion
59
+ */
60
+ function escapeSQLString(str) {
61
+ return str.replace(/'/g, "''");
62
+ }
63
+ /**
64
+ * Gets the last inserted rowid query string
65
+ */
66
+ function buildGetLastRowId() {
67
+ return "SELECT last_insert_rowid() as id";
68
+ }
69
+
70
+ //#endregion
71
+ export { buildDelete, buildGetLastRowId, buildInsert, buildSelectAll, buildSelectByPK, buildUpdate };
72
+ //# sourceMappingURL=query-builder.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-builder.mjs","names":[],"sources":["../../src/operations/query-builder.ts"],"sourcesContent":["import type { TableSchema } from \"../schema/types.js\";\n\n/**\n * Builds a SELECT query string for a single row by primary key\n */\nexport function buildSelectByPK(tableName: string, schema: TableSchema, pk: string): string {\n const pkColumn = schema.primaryKey[0] ?? \"rowid\";\n return `SELECT * FROM \"${tableName}\" WHERE \"${pkColumn}\" = '${escapeSQLString(pk)}'`;\n}\n\n/**\n * Builds a SELECT query string for listing rows with optional limit and offset\n */\nexport function buildSelectAll(\n tableName: string,\n options?: {\n limit?: number;\n offset?: number;\n orderBy?: [string, \"asc\" | \"desc\"][];\n },\n): string {\n let query = `SELECT * FROM \"${tableName}\"`;\n\n if (options?.orderBy?.length) {\n const orderClauses = options.orderBy.map(([col, dir]) => `\"${col}\" ${dir.toUpperCase()}`);\n query += ` ORDER BY ${orderClauses.join(\", \")}`;\n }\n\n if (options?.limit !== undefined) {\n query += ` LIMIT ${options.limit}`;\n }\n\n if (options?.offset !== undefined) {\n query += ` OFFSET ${options.offset}`;\n }\n\n return query;\n}\n\n/**\n * Builds an INSERT query string from content object\n */\nexport function buildInsert(\n tableName: string,\n schema: TableSchema,\n content: Record<string, unknown>,\n): string {\n // Filter to only valid columns\n const validColumns = new Set(schema.columns.map((c) => c.name));\n const entries = Object.entries(content).filter(([key]) => validColumns.has(key));\n\n if (entries.length === 0) {\n throw new Error(`No valid columns provided for INSERT into ${tableName}`);\n }\n\n const columns = entries.map(([key]) => `\"${key}\"`).join(\", \");\n const values = entries.map(([, value]) => formatValue(value)).join(\", \");\n\n return `INSERT INTO \"${tableName}\" (${columns}) VALUES (${values})`;\n}\n\n/**\n * Builds an UPDATE query string from content object\n */\nexport function buildUpdate(\n tableName: string,\n schema: TableSchema,\n pk: string,\n content: Record<string, unknown>,\n): string {\n const pkColumn = schema.primaryKey[0] ?? \"rowid\";\n\n // Filter to only valid columns, excluding PK\n const validColumns = new Set(schema.columns.map((c) => c.name));\n const entries = Object.entries(content).filter(\n ([key]) => validColumns.has(key) && key !== pkColumn,\n );\n\n if (entries.length === 0) {\n throw new Error(`No valid columns provided for UPDATE on ${tableName}`);\n }\n\n const setClauses = entries.map(([key, value]) => `\"${key}\" = ${formatValue(value)}`).join(\", \");\n\n return `UPDATE \"${tableName}\" SET ${setClauses} WHERE \"${pkColumn}\" = '${escapeSQLString(pk)}'`;\n}\n\n/**\n * Builds a DELETE query string by primary key\n */\nexport function buildDelete(tableName: string, schema: TableSchema, pk: string): string {\n const pkColumn = schema.primaryKey[0] ?? \"rowid\";\n return `DELETE FROM \"${tableName}\" WHERE \"${pkColumn}\" = '${escapeSQLString(pk)}'`;\n}\n\n/**\n * Formats a value for SQL insertion\n */\nexport function formatValue(value: unknown): string {\n if (value === null || value === undefined) {\n return \"NULL\";\n }\n\n if (typeof value === \"number\") {\n return String(value);\n }\n\n if (typeof value === \"boolean\") {\n return value ? \"1\" : \"0\";\n }\n\n if (value instanceof Date) {\n return `'${value.toISOString()}'`;\n }\n\n if (typeof value === \"object\") {\n return `'${escapeSQLString(JSON.stringify(value))}'`;\n }\n\n return `'${escapeSQLString(String(value))}'`;\n}\n\n/**\n * Escapes a string for safe SQL insertion\n */\nexport function escapeSQLString(str: string): string {\n return str.replace(/'/g, \"''\");\n}\n\n/**\n * Gets the last inserted rowid query string\n */\nexport function buildGetLastRowId(): string {\n return \"SELECT last_insert_rowid() as id\";\n}\n"],"mappings":";;;;AAKA,SAAgB,gBAAgB,WAAmB,QAAqB,IAAoB;AAE1F,QAAO,kBAAkB,UAAU,WADlB,OAAO,WAAW,MAAM,QACc,OAAO,gBAAgB,GAAG,CAAC;;;;;AAMpF,SAAgB,eACd,WACA,SAKQ;CACR,IAAI,QAAQ,kBAAkB,UAAU;AAExC,KAAI,SAAS,SAAS,QAAQ;EAC5B,MAAM,eAAe,QAAQ,QAAQ,KAAK,CAAC,KAAK,SAAS,IAAI,IAAI,IAAI,IAAI,aAAa,GAAG;AACzF,WAAS,aAAa,aAAa,KAAK,KAAK;;AAG/C,KAAI,SAAS,UAAU,OACrB,UAAS,UAAU,QAAQ;AAG7B,KAAI,SAAS,WAAW,OACtB,UAAS,WAAW,QAAQ;AAG9B,QAAO;;;;;AAMT,SAAgB,YACd,WACA,QACA,SACQ;CAER,MAAM,eAAe,IAAI,IAAI,OAAO,QAAQ,KAAK,MAAM,EAAE,KAAK,CAAC;CAC/D,MAAM,UAAU,OAAO,QAAQ,QAAQ,CAAC,QAAQ,CAAC,SAAS,aAAa,IAAI,IAAI,CAAC;AAEhF,KAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MAAM,6CAA6C,YAAY;AAM3E,QAAO,gBAAgB,UAAU,KAHjB,QAAQ,KAAK,CAAC,SAAS,IAAI,IAAI,GAAG,CAAC,KAAK,KAAK,CAGf,YAF/B,QAAQ,KAAK,GAAG,WAAW,YAAY,MAAM,CAAC,CAAC,KAAK,KAAK,CAEP;;;;;AAMnE,SAAgB,YACd,WACA,QACA,IACA,SACQ;CACR,MAAM,WAAW,OAAO,WAAW,MAAM;CAGzC,MAAM,eAAe,IAAI,IAAI,OAAO,QAAQ,KAAK,MAAM,EAAE,KAAK,CAAC;CAC/D,MAAM,UAAU,OAAO,QAAQ,QAAQ,CAAC,QACrC,CAAC,SAAS,aAAa,IAAI,IAAI,IAAI,QAAQ,SAC7C;AAED,KAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MAAM,2CAA2C,YAAY;AAKzE,QAAO,WAAW,UAAU,QAFT,QAAQ,KAAK,CAAC,KAAK,WAAW,IAAI,IAAI,MAAM,YAAY,MAAM,GAAG,CAAC,KAAK,KAAK,CAEhD,UAAU,SAAS,OAAO,gBAAgB,GAAG,CAAC;;;;;AAM/F,SAAgB,YAAY,WAAmB,QAAqB,IAAoB;AAEtF,QAAO,gBAAgB,UAAU,WADhB,OAAO,WAAW,MAAM,QACY,OAAO,gBAAgB,GAAG,CAAC;;;;;AAMlF,SAAgB,YAAY,OAAwB;AAClD,KAAI,UAAU,QAAQ,UAAU,OAC9B,QAAO;AAGT,KAAI,OAAO,UAAU,SACnB,QAAO,OAAO,MAAM;AAGtB,KAAI,OAAO,UAAU,UACnB,QAAO,QAAQ,MAAM;AAGvB,KAAI,iBAAiB,KACnB,QAAO,IAAI,MAAM,aAAa,CAAC;AAGjC,KAAI,OAAO,UAAU,SACnB,QAAO,IAAI,gBAAgB,KAAK,UAAU,MAAM,CAAC,CAAC;AAGpD,QAAO,IAAI,gBAAgB,OAAO,MAAM,CAAC,CAAC;;;;;AAM5C,SAAgB,gBAAgB,KAAqB;AACnD,QAAO,IAAI,QAAQ,MAAM,KAAK;;;;;AAMhC,SAAgB,oBAA4B;AAC1C,QAAO"}
@@ -0,0 +1,141 @@
1
+ const require_builder = require('../node/builder.cjs');
2
+ let _aigne_sqlite = require("@aigne/sqlite");
3
+
4
+ //#region src/operations/search.ts
5
+ /**
6
+ * Executes a raw SQL query and returns all rows
7
+ */
8
+ async function execAll(db, query) {
9
+ return db.all(_aigne_sqlite.sql.raw(query)).execute();
10
+ }
11
+ /**
12
+ * FTS5 Search operations for SQLite AFS
13
+ */
14
+ var FTSSearch = class {
15
+ constructor(db, schemas, config, basePath = "") {
16
+ this.db = db;
17
+ this.schemas = schemas;
18
+ this.config = config;
19
+ this.basePath = basePath;
20
+ }
21
+ /**
22
+ * Performs full-text search across configured tables
23
+ */
24
+ async search(query, options) {
25
+ if (!this.config.enabled) return {
26
+ data: [],
27
+ message: "Full-text search is not enabled"
28
+ };
29
+ const results = [];
30
+ const limit = options?.limit ?? 50;
31
+ const buildOptions = { basePath: this.basePath };
32
+ const tablesToSearch = options?.tables ? options.tables.filter((t) => this.config.tables.has(t)) : Array.from(this.config.tables.keys());
33
+ const ftsQuery = this.prepareFTSQuery(query, options?.caseSensitive);
34
+ for (const tableName of tablesToSearch) {
35
+ const tableConfig = this.config.tables.get(tableName);
36
+ const schema = this.schemas.get(tableName);
37
+ if (!tableConfig || !schema) continue;
38
+ const ftsTableName = `${tableName}_fts`;
39
+ try {
40
+ if (!await this.ftsTableExists(ftsTableName)) continue;
41
+ const highlightColumn = tableConfig.columns[0] ?? "";
42
+ const highlightIndex = highlightColumn ? tableConfig.columns.indexOf(highlightColumn) : 0;
43
+ const rows = await execAll(this.db, `
44
+ SELECT t.*, highlight("${ftsTableName}", ${highlightIndex}, '<mark>', '</mark>') as snippet
45
+ FROM "${ftsTableName}" fts
46
+ JOIN "${tableName}" t ON fts.rowid = t.rowid
47
+ WHERE "${ftsTableName}" MATCH '${ftsQuery}'
48
+ LIMIT ${Math.ceil(limit / tablesToSearch.length)}
49
+ `);
50
+ for (const row of rows) {
51
+ const { snippet, ...rowData } = row;
52
+ results.push(require_builder.buildSearchEntry(tableName, schema, rowData, snippet, buildOptions));
53
+ }
54
+ } catch (error) {
55
+ console.warn(`FTS search failed for table ${tableName}:`, error);
56
+ }
57
+ if (results.length >= limit) break;
58
+ }
59
+ return {
60
+ data: results.slice(0, limit),
61
+ message: results.length === 0 ? `No results found for "${query}"` : void 0
62
+ };
63
+ }
64
+ /**
65
+ * Searches within a specific table
66
+ */
67
+ async searchTable(tableName, query, options) {
68
+ return this.search(query, {
69
+ ...options,
70
+ tables: [tableName]
71
+ });
72
+ }
73
+ /**
74
+ * Checks if FTS is configured for a table
75
+ */
76
+ hasFTS(tableName) {
77
+ return this.config.enabled && this.config.tables.has(tableName);
78
+ }
79
+ /**
80
+ * Gets FTS configuration for a table
81
+ */
82
+ getFTSConfig(tableName) {
83
+ return this.config.tables.get(tableName);
84
+ }
85
+ /**
86
+ * Checks if an FTS table exists
87
+ */
88
+ async ftsTableExists(ftsTableName) {
89
+ return (await execAll(this.db, `SELECT name FROM sqlite_master WHERE type = 'table' AND name = '${ftsTableName}'`)).length > 0;
90
+ }
91
+ /**
92
+ * Prepares a query string for FTS5
93
+ * Handles special characters and case sensitivity
94
+ */
95
+ prepareFTSQuery(query, _caseSensitive) {
96
+ let prepared = query.replace(/"/g, "\"\"").replace(/'/g, "''");
97
+ if (prepared.includes(" ") && !prepared.startsWith("\"")) prepared = `"${prepared}"`;
98
+ return prepared;
99
+ }
100
+ /**
101
+ * Updates the schemas map (after refresh)
102
+ */
103
+ setSchemas(schemas) {
104
+ this.schemas = schemas;
105
+ }
106
+ /**
107
+ * Simple search fallback when FTS is not available
108
+ * Uses LIKE queries on specified columns
109
+ */
110
+ async simpleLikeSearch(tableName, query, columns, options) {
111
+ const schema = this.schemas.get(tableName);
112
+ if (!schema) return {
113
+ data: [],
114
+ message: `Table '${tableName}' not found`
115
+ };
116
+ const buildOptions = { basePath: this.basePath };
117
+ const limit = options?.limit ?? 50;
118
+ const escapedQuery = query.replace(/'/g, "''");
119
+ const conditions = columns.filter((col) => schema.columns.some((c) => c.name === col)).map((col) => `"${col}" LIKE '%${escapedQuery}%'`).join(" OR ");
120
+ if (!conditions) return {
121
+ data: [],
122
+ message: "No valid columns to search"
123
+ };
124
+ return { data: (await execAll(this.db, `SELECT * FROM "${tableName}" WHERE ${conditions} LIMIT ${limit}`)).map((row) => require_builder.buildSearchEntry(tableName, schema, row, void 0, buildOptions)) };
125
+ }
126
+ };
127
+ /**
128
+ * Creates FTS configuration from options
129
+ */
130
+ function createFTSConfig(options) {
131
+ const config = {
132
+ enabled: options?.enabled ?? false,
133
+ tables: /* @__PURE__ */ new Map()
134
+ };
135
+ if (options?.tables) for (const [table, columns] of Object.entries(options.tables)) config.tables.set(table, { columns });
136
+ return config;
137
+ }
138
+
139
+ //#endregion
140
+ exports.FTSSearch = FTSSearch;
141
+ exports.createFTSConfig = createFTSConfig;
@@ -0,0 +1,79 @@
1
+ import { TableSchema } from "../schema/types.cjs";
2
+ import { LibSQLDatabase } from "drizzle-orm/libsql";
3
+ import { AFSSearchOptions, AFSSearchResult } from "@aigne/afs";
4
+
5
+ //#region src/operations/search.d.ts
6
+ /**
7
+ * FTS5 search configuration for a table
8
+ */
9
+ interface FTSTableConfig {
10
+ /** Columns to include in FTS index */
11
+ columns: string[];
12
+ /** Whether FTS table has been created */
13
+ initialized?: boolean;
14
+ }
15
+ /**
16
+ * FTS5 search configuration
17
+ */
18
+ interface FTSConfig {
19
+ /** Whether FTS is enabled */
20
+ enabled: boolean;
21
+ /** Per-table FTS configuration */
22
+ tables: Map<string, FTSTableConfig>;
23
+ }
24
+ /**
25
+ * FTS5 Search operations for SQLite AFS
26
+ */
27
+ declare class FTSSearch {
28
+ private db;
29
+ private schemas;
30
+ private config;
31
+ private basePath;
32
+ constructor(db: LibSQLDatabase, schemas: Map<string, TableSchema>, config: FTSConfig, basePath?: string);
33
+ /**
34
+ * Performs full-text search across configured tables
35
+ */
36
+ search(query: string, options?: AFSSearchOptions & {
37
+ /** Specific tables to search (defaults to all FTS-enabled tables) */tables?: string[];
38
+ }): Promise<AFSSearchResult>;
39
+ /**
40
+ * Searches within a specific table
41
+ */
42
+ searchTable(tableName: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult>;
43
+ /**
44
+ * Checks if FTS is configured for a table
45
+ */
46
+ hasFTS(tableName: string): boolean;
47
+ /**
48
+ * Gets FTS configuration for a table
49
+ */
50
+ getFTSConfig(tableName: string): FTSTableConfig | undefined;
51
+ /**
52
+ * Checks if an FTS table exists
53
+ */
54
+ private ftsTableExists;
55
+ /**
56
+ * Prepares a query string for FTS5
57
+ * Handles special characters and case sensitivity
58
+ */
59
+ private prepareFTSQuery;
60
+ /**
61
+ * Updates the schemas map (after refresh)
62
+ */
63
+ setSchemas(schemas: Map<string, TableSchema>): void;
64
+ /**
65
+ * Simple search fallback when FTS is not available
66
+ * Uses LIKE queries on specified columns
67
+ */
68
+ simpleLikeSearch(tableName: string, query: string, columns: string[], options?: AFSSearchOptions): Promise<AFSSearchResult>;
69
+ }
70
+ /**
71
+ * Creates FTS configuration from options
72
+ */
73
+ declare function createFTSConfig(options?: {
74
+ enabled?: boolean;
75
+ tables?: Record<string, string[]>;
76
+ }): FTSConfig;
77
+ //#endregion
78
+ export { FTSConfig, FTSSearch, FTSTableConfig, createFTSConfig };
79
+ //# sourceMappingURL=search.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.cts","names":[],"sources":["../../src/operations/search.ts"],"mappings":";;;;;;AAgBA;AAUA;UAViB,cAAA;EAAA;EAAA,OAAA;EAAA;EAAA,WAAA;AAAA;AAAA;AAUjB;AAUA;AApBiB,UAUA,SAAA;EAAA;EAAA,OAAA;EAAA;EAAA,MAAA,EAIP,GAAA,SAAY,cAAA;AAAA;AAAA;AAMtB;;AANsB,cAMT,SAAA;EAAA,QAAA,EAAA;EAAA,QAAA,OAAA;EAAA,QAAA,MAAA;EAAA,QAAA,QAAA;EAAA,YAAA,EAAA,EAEG,cAAA,EAAA,OAAA,EACK,GAAA,SAAY,WAAA,GAAA,MAAA,EACb,SAAA,EAAA,QAAA;EAAA;;;EAAA,OAAA,KAAA,UAAA,OAAA,GASN,gBAAA;IAAA,qEAAA,MAAA;EAAA,IAIT,OAAA,CAAQ,eAAA;EAAA;;;EAAA,YAAA,SAAA,UAAA,KAAA,UAAA,OAAA,GA+EC,gBAAA,GACT,OAAA,CAAQ,eAAA;EAAA;;;EAAA,OAAA,SAAA;EAAA;;;EAAA,aAAA,SAAA,WAcsB,cAAA;EAAA;;;EAAA,QAAA,cAAA;EAAA;;;;EAAA,QAAA,eAAA;EAAA;;;EAAA,WAAA,OAAA,EAuCb,GAAA,SAAY,WAAA;EAAA;;;;EAAA,iBAAA,SAAA,UAAA,KAAA,UAAA,OAAA,YAAA,OAAA,GAYpB,gBAAA,GACT,OAAA,CAAQ,eAAA;AAAA;AAAA;;;AAAA,iBAkCG,eAAA,CAAA,OAAA;EAAA,OAAA;EAAA,MAAA,GAEL,MAAA;AAAA,IACP,SAAA"}
@@ -0,0 +1,79 @@
1
+ import { TableSchema } from "../schema/types.mjs";
2
+ import { AFSSearchOptions, AFSSearchResult } from "@aigne/afs";
3
+ import { LibSQLDatabase } from "drizzle-orm/libsql";
4
+
5
+ //#region src/operations/search.d.ts
6
+ /**
7
+ * FTS5 search configuration for a table
8
+ */
9
+ interface FTSTableConfig {
10
+ /** Columns to include in FTS index */
11
+ columns: string[];
12
+ /** Whether FTS table has been created */
13
+ initialized?: boolean;
14
+ }
15
+ /**
16
+ * FTS5 search configuration
17
+ */
18
+ interface FTSConfig {
19
+ /** Whether FTS is enabled */
20
+ enabled: boolean;
21
+ /** Per-table FTS configuration */
22
+ tables: Map<string, FTSTableConfig>;
23
+ }
24
+ /**
25
+ * FTS5 Search operations for SQLite AFS
26
+ */
27
+ declare class FTSSearch {
28
+ private db;
29
+ private schemas;
30
+ private config;
31
+ private basePath;
32
+ constructor(db: LibSQLDatabase, schemas: Map<string, TableSchema>, config: FTSConfig, basePath?: string);
33
+ /**
34
+ * Performs full-text search across configured tables
35
+ */
36
+ search(query: string, options?: AFSSearchOptions & {
37
+ /** Specific tables to search (defaults to all FTS-enabled tables) */tables?: string[];
38
+ }): Promise<AFSSearchResult>;
39
+ /**
40
+ * Searches within a specific table
41
+ */
42
+ searchTable(tableName: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult>;
43
+ /**
44
+ * Checks if FTS is configured for a table
45
+ */
46
+ hasFTS(tableName: string): boolean;
47
+ /**
48
+ * Gets FTS configuration for a table
49
+ */
50
+ getFTSConfig(tableName: string): FTSTableConfig | undefined;
51
+ /**
52
+ * Checks if an FTS table exists
53
+ */
54
+ private ftsTableExists;
55
+ /**
56
+ * Prepares a query string for FTS5
57
+ * Handles special characters and case sensitivity
58
+ */
59
+ private prepareFTSQuery;
60
+ /**
61
+ * Updates the schemas map (after refresh)
62
+ */
63
+ setSchemas(schemas: Map<string, TableSchema>): void;
64
+ /**
65
+ * Simple search fallback when FTS is not available
66
+ * Uses LIKE queries on specified columns
67
+ */
68
+ simpleLikeSearch(tableName: string, query: string, columns: string[], options?: AFSSearchOptions): Promise<AFSSearchResult>;
69
+ }
70
+ /**
71
+ * Creates FTS configuration from options
72
+ */
73
+ declare function createFTSConfig(options?: {
74
+ enabled?: boolean;
75
+ tables?: Record<string, string[]>;
76
+ }): FTSConfig;
77
+ //#endregion
78
+ export { FTSConfig, FTSSearch, FTSTableConfig, createFTSConfig };
79
+ //# sourceMappingURL=search.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.mts","names":[],"sources":["../../src/operations/search.ts"],"mappings":";;;;;;AAgBA;AAUA;UAViB,cAAA;EAAA;EAAA,OAAA;EAAA;EAAA,WAAA;AAAA;AAAA;AAUjB;AAUA;AApBiB,UAUA,SAAA;EAAA;EAAA,OAAA;EAAA;EAAA,MAAA,EAIP,GAAA,SAAY,cAAA;AAAA;AAAA;AAMtB;;AANsB,cAMT,SAAA;EAAA,QAAA,EAAA;EAAA,QAAA,OAAA;EAAA,QAAA,MAAA;EAAA,QAAA,QAAA;EAAA,YAAA,EAAA,EAEG,cAAA,EAAA,OAAA,EACK,GAAA,SAAY,WAAA,GAAA,MAAA,EACb,SAAA,EAAA,QAAA;EAAA;;;EAAA,OAAA,KAAA,UAAA,OAAA,GASN,gBAAA;IAAA,qEAAA,MAAA;EAAA,IAIT,OAAA,CAAQ,eAAA;EAAA;;;EAAA,YAAA,SAAA,UAAA,KAAA,UAAA,OAAA,GA+EC,gBAAA,GACT,OAAA,CAAQ,eAAA;EAAA;;;EAAA,OAAA,SAAA;EAAA;;;EAAA,aAAA,SAAA,WAcsB,cAAA;EAAA;;;EAAA,QAAA,cAAA;EAAA;;;;EAAA,QAAA,eAAA;EAAA;;;EAAA,WAAA,OAAA,EAuCb,GAAA,SAAY,WAAA;EAAA;;;;EAAA,iBAAA,SAAA,UAAA,KAAA,UAAA,OAAA,YAAA,OAAA,GAYpB,gBAAA,GACT,OAAA,CAAQ,eAAA;AAAA;AAAA;;;AAAA,iBAkCG,eAAA,CAAA,OAAA;EAAA,OAAA;EAAA,MAAA,GAEL,MAAA;AAAA,IACP,SAAA"}
@@ -0,0 +1,141 @@
1
+ import { buildSearchEntry } from "../node/builder.mjs";
2
+ import { sql } from "@aigne/sqlite";
3
+
4
+ //#region src/operations/search.ts
5
+ /**
6
+ * Executes a raw SQL query and returns all rows
7
+ */
8
+ async function execAll(db, query) {
9
+ return db.all(sql.raw(query)).execute();
10
+ }
11
+ /**
12
+ * FTS5 Search operations for SQLite AFS
13
+ */
14
+ var FTSSearch = class {
15
+ constructor(db, schemas, config, basePath = "") {
16
+ this.db = db;
17
+ this.schemas = schemas;
18
+ this.config = config;
19
+ this.basePath = basePath;
20
+ }
21
+ /**
22
+ * Performs full-text search across configured tables
23
+ */
24
+ async search(query, options) {
25
+ if (!this.config.enabled) return {
26
+ data: [],
27
+ message: "Full-text search is not enabled"
28
+ };
29
+ const results = [];
30
+ const limit = options?.limit ?? 50;
31
+ const buildOptions = { basePath: this.basePath };
32
+ const tablesToSearch = options?.tables ? options.tables.filter((t) => this.config.tables.has(t)) : Array.from(this.config.tables.keys());
33
+ const ftsQuery = this.prepareFTSQuery(query, options?.caseSensitive);
34
+ for (const tableName of tablesToSearch) {
35
+ const tableConfig = this.config.tables.get(tableName);
36
+ const schema = this.schemas.get(tableName);
37
+ if (!tableConfig || !schema) continue;
38
+ const ftsTableName = `${tableName}_fts`;
39
+ try {
40
+ if (!await this.ftsTableExists(ftsTableName)) continue;
41
+ const highlightColumn = tableConfig.columns[0] ?? "";
42
+ const highlightIndex = highlightColumn ? tableConfig.columns.indexOf(highlightColumn) : 0;
43
+ const rows = await execAll(this.db, `
44
+ SELECT t.*, highlight("${ftsTableName}", ${highlightIndex}, '<mark>', '</mark>') as snippet
45
+ FROM "${ftsTableName}" fts
46
+ JOIN "${tableName}" t ON fts.rowid = t.rowid
47
+ WHERE "${ftsTableName}" MATCH '${ftsQuery}'
48
+ LIMIT ${Math.ceil(limit / tablesToSearch.length)}
49
+ `);
50
+ for (const row of rows) {
51
+ const { snippet, ...rowData } = row;
52
+ results.push(buildSearchEntry(tableName, schema, rowData, snippet, buildOptions));
53
+ }
54
+ } catch (error) {
55
+ console.warn(`FTS search failed for table ${tableName}:`, error);
56
+ }
57
+ if (results.length >= limit) break;
58
+ }
59
+ return {
60
+ data: results.slice(0, limit),
61
+ message: results.length === 0 ? `No results found for "${query}"` : void 0
62
+ };
63
+ }
64
+ /**
65
+ * Searches within a specific table
66
+ */
67
+ async searchTable(tableName, query, options) {
68
+ return this.search(query, {
69
+ ...options,
70
+ tables: [tableName]
71
+ });
72
+ }
73
+ /**
74
+ * Checks if FTS is configured for a table
75
+ */
76
+ hasFTS(tableName) {
77
+ return this.config.enabled && this.config.tables.has(tableName);
78
+ }
79
+ /**
80
+ * Gets FTS configuration for a table
81
+ */
82
+ getFTSConfig(tableName) {
83
+ return this.config.tables.get(tableName);
84
+ }
85
+ /**
86
+ * Checks if an FTS table exists
87
+ */
88
+ async ftsTableExists(ftsTableName) {
89
+ return (await execAll(this.db, `SELECT name FROM sqlite_master WHERE type = 'table' AND name = '${ftsTableName}'`)).length > 0;
90
+ }
91
+ /**
92
+ * Prepares a query string for FTS5
93
+ * Handles special characters and case sensitivity
94
+ */
95
+ prepareFTSQuery(query, _caseSensitive) {
96
+ let prepared = query.replace(/"/g, "\"\"").replace(/'/g, "''");
97
+ if (prepared.includes(" ") && !prepared.startsWith("\"")) prepared = `"${prepared}"`;
98
+ return prepared;
99
+ }
100
+ /**
101
+ * Updates the schemas map (after refresh)
102
+ */
103
+ setSchemas(schemas) {
104
+ this.schemas = schemas;
105
+ }
106
+ /**
107
+ * Simple search fallback when FTS is not available
108
+ * Uses LIKE queries on specified columns
109
+ */
110
+ async simpleLikeSearch(tableName, query, columns, options) {
111
+ const schema = this.schemas.get(tableName);
112
+ if (!schema) return {
113
+ data: [],
114
+ message: `Table '${tableName}' not found`
115
+ };
116
+ const buildOptions = { basePath: this.basePath };
117
+ const limit = options?.limit ?? 50;
118
+ const escapedQuery = query.replace(/'/g, "''");
119
+ const conditions = columns.filter((col) => schema.columns.some((c) => c.name === col)).map((col) => `"${col}" LIKE '%${escapedQuery}%'`).join(" OR ");
120
+ if (!conditions) return {
121
+ data: [],
122
+ message: "No valid columns to search"
123
+ };
124
+ return { data: (await execAll(this.db, `SELECT * FROM "${tableName}" WHERE ${conditions} LIMIT ${limit}`)).map((row) => buildSearchEntry(tableName, schema, row, void 0, buildOptions)) };
125
+ }
126
+ };
127
+ /**
128
+ * Creates FTS configuration from options
129
+ */
130
+ function createFTSConfig(options) {
131
+ const config = {
132
+ enabled: options?.enabled ?? false,
133
+ tables: /* @__PURE__ */ new Map()
134
+ };
135
+ if (options?.tables) for (const [table, columns] of Object.entries(options.tables)) config.tables.set(table, { columns });
136
+ return config;
137
+ }
138
+
139
+ //#endregion
140
+ export { FTSSearch, createFTSConfig };
141
+ //# sourceMappingURL=search.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.mjs","names":[],"sources":["../../src/operations/search.ts"],"sourcesContent":["import type { AFSEntry, AFSSearchOptions, AFSSearchResult } from \"@aigne/afs\";\nimport { sql } from \"@aigne/sqlite\";\nimport type { LibSQLDatabase } from \"drizzle-orm/libsql\";\nimport { type BuildEntryOptions, buildSearchEntry } from \"../node/builder.js\";\nimport type { TableSchema } from \"../schema/types.js\";\n\n/**\n * Executes a raw SQL query and returns all rows\n */\nasync function execAll<T>(db: LibSQLDatabase, query: string): Promise<T[]> {\n return db.all<T>(sql.raw(query)).execute();\n}\n\n/**\n * FTS5 search configuration for a table\n */\nexport interface FTSTableConfig {\n /** Columns to include in FTS index */\n columns: string[];\n /** Whether FTS table has been created */\n initialized?: boolean;\n}\n\n/**\n * FTS5 search configuration\n */\nexport interface FTSConfig {\n /** Whether FTS is enabled */\n enabled: boolean;\n /** Per-table FTS configuration */\n tables: Map<string, FTSTableConfig>;\n}\n\n/**\n * FTS5 Search operations for SQLite AFS\n */\nexport class FTSSearch {\n constructor(\n private db: LibSQLDatabase,\n private schemas: Map<string, TableSchema>,\n private config: FTSConfig,\n private basePath: string = \"\",\n ) {}\n\n /**\n * Performs full-text search across configured tables\n */\n async search(\n query: string,\n options?: AFSSearchOptions & {\n /** Specific tables to search (defaults to all FTS-enabled tables) */\n tables?: string[];\n },\n ): Promise<AFSSearchResult> {\n if (!this.config.enabled) {\n return { data: [], message: \"Full-text search is not enabled\" };\n }\n\n const results: AFSEntry[] = [];\n const limit = options?.limit ?? 50;\n const buildOptions: BuildEntryOptions = { basePath: this.basePath };\n\n // Determine which tables to search\n const tablesToSearch = options?.tables\n ? options.tables.filter((t) => this.config.tables.has(t))\n : Array.from(this.config.tables.keys());\n\n // Escape and prepare the query for FTS5\n const ftsQuery = this.prepareFTSQuery(query, options?.caseSensitive);\n\n for (const tableName of tablesToSearch) {\n const tableConfig = this.config.tables.get(tableName);\n const schema = this.schemas.get(tableName);\n\n if (!tableConfig || !schema) continue;\n\n const ftsTableName = `${tableName}_fts`;\n\n try {\n // Check if FTS table exists\n const ftsExists = await this.ftsTableExists(ftsTableName);\n if (!ftsExists) continue;\n\n // Get the first column for highlighting\n const highlightColumn = tableConfig.columns[0] ?? \"\";\n const highlightIndex = highlightColumn ? tableConfig.columns.indexOf(highlightColumn) : 0;\n\n // Build FTS query with highlight\n const rows = await execAll<Record<string, unknown> & { snippet?: string }>(\n this.db,\n `\n SELECT t.*, highlight(\"${ftsTableName}\", ${highlightIndex}, '<mark>', '</mark>') as snippet\n FROM \"${ftsTableName}\" fts\n JOIN \"${tableName}\" t ON fts.rowid = t.rowid\n WHERE \"${ftsTableName}\" MATCH '${ftsQuery}'\n LIMIT ${Math.ceil(limit / tablesToSearch.length)}\n `,\n );\n\n for (const row of rows) {\n const { snippet, ...rowData } = row;\n results.push(\n buildSearchEntry(\n tableName,\n schema,\n rowData,\n snippet as string | undefined,\n buildOptions,\n ),\n );\n }\n } catch (error) {\n // Log but continue with other tables\n console.warn(`FTS search failed for table ${tableName}:`, error);\n }\n\n // Stop if we have enough results\n if (results.length >= limit) break;\n }\n\n return {\n data: results.slice(0, limit),\n message: results.length === 0 ? `No results found for \"${query}\"` : undefined,\n };\n }\n\n /**\n * Searches within a specific table\n */\n async searchTable(\n tableName: string,\n query: string,\n options?: AFSSearchOptions,\n ): Promise<AFSSearchResult> {\n return this.search(query, { ...options, tables: [tableName] });\n }\n\n /**\n * Checks if FTS is configured for a table\n */\n hasFTS(tableName: string): boolean {\n return this.config.enabled && this.config.tables.has(tableName);\n }\n\n /**\n * Gets FTS configuration for a table\n */\n getFTSConfig(tableName: string): FTSTableConfig | undefined {\n return this.config.tables.get(tableName);\n }\n\n /**\n * Checks if an FTS table exists\n */\n private async ftsTableExists(ftsTableName: string): Promise<boolean> {\n const result = await execAll<{ name: string }>(\n this.db,\n `SELECT name FROM sqlite_master WHERE type = 'table' AND name = '${ftsTableName}'`,\n );\n return result.length > 0;\n }\n\n /**\n * Prepares a query string for FTS5\n * Handles special characters and case sensitivity\n */\n private prepareFTSQuery(query: string, _caseSensitive?: boolean): string {\n // Escape special FTS5 characters\n let prepared = query\n .replace(/\"/g, '\"\"') // Escape double quotes\n .replace(/'/g, \"''\"); // Escape single quotes\n\n // For case-insensitive search (default), we don't need to modify\n // FTS5 is case-insensitive by default for ASCII\n\n // If the query contains multiple words, search for the phrase\n if (prepared.includes(\" \") && !prepared.startsWith('\"')) {\n prepared = `\"${prepared}\"`;\n }\n\n return prepared;\n }\n\n /**\n * Updates the schemas map (after refresh)\n */\n setSchemas(schemas: Map<string, TableSchema>): void {\n this.schemas = schemas;\n }\n\n /**\n * Simple search fallback when FTS is not available\n * Uses LIKE queries on specified columns\n */\n async simpleLikeSearch(\n tableName: string,\n query: string,\n columns: string[],\n options?: AFSSearchOptions,\n ): Promise<AFSSearchResult> {\n const schema = this.schemas.get(tableName);\n if (!schema) {\n return { data: [], message: `Table '${tableName}' not found` };\n }\n\n const buildOptions: BuildEntryOptions = { basePath: this.basePath };\n const limit = options?.limit ?? 50;\n const escapedQuery = query.replace(/'/g, \"''\");\n\n // Build LIKE conditions for each column\n const conditions = columns\n .filter((col) => schema.columns.some((c) => c.name === col))\n .map((col) => `\"${col}\" LIKE '%${escapedQuery}%'`)\n .join(\" OR \");\n\n if (!conditions) {\n return { data: [], message: \"No valid columns to search\" };\n }\n\n const rows = await execAll<Record<string, unknown>>(\n this.db,\n `SELECT * FROM \"${tableName}\" WHERE ${conditions} LIMIT ${limit}`,\n );\n\n return {\n data: rows.map((row) => buildSearchEntry(tableName, schema, row, undefined, buildOptions)),\n };\n }\n}\n\n/**\n * Creates FTS configuration from options\n */\nexport function createFTSConfig(options?: {\n enabled?: boolean;\n tables?: Record<string, string[]>;\n}): FTSConfig {\n const config: FTSConfig = {\n enabled: options?.enabled ?? false,\n tables: new Map(),\n };\n\n if (options?.tables) {\n for (const [table, columns] of Object.entries(options.tables)) {\n config.tables.set(table, { columns });\n }\n }\n\n return config;\n}\n"],"mappings":"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"}