@ganju/utils 0.0.14 → 0.0.16

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 (111) hide show
  1. package/dist/channelCustomer.d.ts +99 -0
  2. package/dist/channelCustomer.d.ts.map +1 -0
  3. package/dist/channelCustomer.js +533 -0
  4. package/dist/channelDebounce.d.ts +7 -0
  5. package/dist/channelDebounce.d.ts.map +1 -1
  6. package/dist/chunkOverlap.d.ts +2 -0
  7. package/dist/chunkOverlap.d.ts.map +1 -0
  8. package/dist/chunkOverlap.js +9 -0
  9. package/dist/chunking.d.ts.map +1 -1
  10. package/dist/chunking.js +24 -4
  11. package/dist/citations.d.ts +28 -0
  12. package/dist/citations.d.ts.map +1 -0
  13. package/dist/citations.js +111 -0
  14. package/dist/constants.d.ts +49 -0
  15. package/dist/constants.d.ts.map +1 -1
  16. package/dist/constants.js +202 -17
  17. package/dist/data.d.ts +9 -0
  18. package/dist/data.d.ts.map +1 -0
  19. package/dist/data.js +78 -0
  20. package/dist/dataApi.d.ts +511 -0
  21. package/dist/dataApi.d.ts.map +1 -0
  22. package/dist/dataApi.js +355 -0
  23. package/dist/dataCompiler.d.ts +68 -0
  24. package/dist/dataCompiler.d.ts.map +1 -0
  25. package/dist/dataCompiler.js +726 -0
  26. package/dist/dataDdl.d.ts +29 -0
  27. package/dist/dataDdl.d.ts.map +1 -0
  28. package/dist/dataDdl.js +354 -0
  29. package/dist/dataEngine.d.ts +143 -0
  30. package/dist/dataEngine.d.ts.map +1 -0
  31. package/dist/dataEngine.js +2022 -0
  32. package/dist/dataIdentifiers.d.ts +8 -0
  33. package/dist/dataIdentifiers.d.ts.map +1 -0
  34. package/dist/dataIdentifiers.js +55 -0
  35. package/dist/dataImport.d.ts +77 -0
  36. package/dist/dataImport.d.ts.map +1 -0
  37. package/dist/dataImport.js +450 -0
  38. package/dist/dataRpc.d.ts +131 -0
  39. package/dist/dataRpc.d.ts.map +1 -0
  40. package/dist/dataRpc.js +2 -0
  41. package/dist/dataTypes.d.ts +157 -0
  42. package/dist/dataTypes.d.ts.map +1 -0
  43. package/dist/dataTypes.js +160 -0
  44. package/dist/index.d.ts +570 -36
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +72 -0
  47. package/dist/jsonSchemaToZodShape.d.ts +5 -1
  48. package/dist/jsonSchemaToZodShape.d.ts.map +1 -1
  49. package/dist/jsonSchemaToZodShape.js +21 -9
  50. package/dist/oauth.d.ts +14 -0
  51. package/dist/oauth.d.ts.map +1 -1
  52. package/dist/oauth.js +42 -2
  53. package/dist/pagination.d.ts +47 -0
  54. package/dist/pagination.d.ts.map +1 -0
  55. package/dist/pagination.js +160 -0
  56. package/dist/processQueueBatch.d.ts +6 -1
  57. package/dist/processQueueBatch.d.ts.map +1 -1
  58. package/dist/processQueueBatch.js +23 -10
  59. package/dist/queueFailure.d.ts +14 -0
  60. package/dist/queueFailure.d.ts.map +1 -0
  61. package/dist/queueFailure.js +43 -0
  62. package/dist/resourceRerank.d.ts +3 -2
  63. package/dist/resourceRerank.d.ts.map +1 -1
  64. package/dist/resourceRerank.js +8 -3
  65. package/dist/schema.d.ts +374 -29
  66. package/dist/schema.d.ts.map +1 -1
  67. package/dist/schema.js +290 -6
  68. package/dist/ssrf.d.ts +1 -0
  69. package/dist/ssrf.d.ts.map +1 -1
  70. package/dist/ssrf.js +48 -12
  71. package/dist/toolConfirmation.d.ts +48 -0
  72. package/dist/toolConfirmation.d.ts.map +1 -1
  73. package/dist/toolConfirmation.js +75 -1
  74. package/dist/toolCount.d.ts +13 -0
  75. package/dist/toolCount.d.ts.map +1 -0
  76. package/dist/toolCount.js +45 -0
  77. package/dist/toolLint.d.ts +1 -1
  78. package/dist/toolLint.d.ts.map +1 -1
  79. package/dist/toolLint.js +20 -0
  80. package/dist/untrustedContent.d.ts +135 -0
  81. package/dist/untrustedContent.d.ts.map +1 -0
  82. package/dist/untrustedContent.js +402 -0
  83. package/package.json +17 -1
  84. package/src/channelCustomer.ts +604 -0
  85. package/src/channelDebounce.ts +17 -0
  86. package/src/chunkOverlap.ts +6 -0
  87. package/src/chunking.ts +25 -4
  88. package/src/citations.ts +127 -0
  89. package/src/constants.ts +219 -17
  90. package/src/data.ts +74 -0
  91. package/src/dataApi.ts +428 -0
  92. package/src/dataCompiler.ts +948 -0
  93. package/src/dataDdl.ts +419 -0
  94. package/src/dataEngine.ts +2753 -0
  95. package/src/dataIdentifiers.ts +63 -0
  96. package/src/dataImport.ts +554 -0
  97. package/src/dataRpc.ts +121 -0
  98. package/src/dataTypes.ts +312 -0
  99. package/src/index.ts +156 -2
  100. package/src/jsonSchemaToZodShape.ts +31 -13
  101. package/src/oauth.ts +42 -1
  102. package/src/pagination.ts +193 -0
  103. package/src/processQueueBatch.ts +42 -12
  104. package/src/queueFailure.ts +63 -0
  105. package/src/resourceRerank.ts +10 -3
  106. package/src/schema.ts +346 -4
  107. package/src/ssrf.ts +40 -11
  108. package/src/toolConfirmation.ts +123 -0
  109. package/src/toolCount.ts +53 -0
  110. package/src/toolLint.ts +27 -0
  111. package/src/untrustedContent.ts +476 -0
@@ -0,0 +1,29 @@
1
+ import type { DataColumn, DataColumnType, DataDefaultValue, DataTable } from './dataTypes';
2
+ export declare const DATA_NOW_SQL = "strftime('%Y-%m-%dT%H:%M:%SZ', 'now')";
3
+ export declare const TEXT_LIKE_TYPES: ReadonlySet<DataColumnType>;
4
+ export declare const NUMERIC_TYPES: ReadonlySet<DataColumnType>;
5
+ export declare const SEARCHABLE_TYPES: ReadonlySet<DataColumnType>;
6
+ export declare const UNIQUE_TYPES: ReadonlySet<DataColumnType>;
7
+ export declare const sqlLiteral: (value: DataDefaultValue) => string;
8
+ export declare const indexName: (table: Pick<DataTable, "id">, column: DataColumn) => string;
9
+ export declare const uniqueIndexName: (table: Pick<DataTable, "id">, column: DataColumn) => string;
10
+ export declare const participantIndexName: (table: Pick<DataTable, "id">) => string;
11
+ export declare const ftsTableName: (table: Pick<DataTable, "name">) => string;
12
+ export declare const triggerPrefix: (table: Pick<DataTable, "id">) => string;
13
+ export declare const checkConstraintName: (column: Pick<DataColumn, "id">) => string;
14
+ export declare const minConstraintName: (column: Pick<DataColumn, "id">) => string;
15
+ export declare const storedDefault: (column: Pick<DataColumn, "type">, value: DataDefaultValue) => DataDefaultValue;
16
+ export declare const columnDefinitionSql: (column: DataColumn) => string;
17
+ export declare const createTableSql: (name: string, columns: readonly DataColumn[]) => string;
18
+ export interface NamedStatement {
19
+ name: string;
20
+ sql: string;
21
+ }
22
+ export declare const desiredIndexes: (table: DataTable) => NamedStatement[];
23
+ export declare const isIndexedColumn: (column: DataColumn) => boolean;
24
+ export declare const searchableColumns: (table: DataTable) => DataColumn[];
25
+ export declare const ftsCreateSql: (table: DataTable) => string | null;
26
+ export declare const desiredTriggers: (table: DataTable) => NamedStatement[];
27
+ export declare const conversionSql: (fromType: DataColumnType, to: DataColumn, src: string) => string;
28
+ export declare const invalidAfterConversionSql: (to: DataColumn, src: string, converted: string) => string;
29
+ //# sourceMappingURL=dataDdl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataDdl.d.ts","sourceRoot":"","sources":["../src/dataDdl.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,SAAS,EACV,MAAM,aAAa,CAAC;AAErB,eAAO,MAAM,YAAY,0CAA0C,CAAC;AAEpE,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,cAAc,CAOtD,CAAC;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,cAAc,CAGpD,CAAC;AAEH,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,cAAc,CAOvD,CAAC;AAEH,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,cAAc,CAWnD,CAAC;AAIH,eAAO,MAAM,UAAU,GAAI,OAAO,gBAAgB,KAAG,MAQpD,CAAC;AAIF,eAAO,MAAM,SAAS,GAAI,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,QAAQ,UAAU,WAC3C,CAAC;AAChC,eAAO,MAAM,eAAe,GAC1B,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAC5B,QAAQ,UAAU,WACc,CAAC;AACnC,eAAO,MAAM,oBAAoB,GAAI,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,WACnC,CAAC;AAC/B,eAAO,MAAM,YAAY,GAAI,OAAO,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,WACrC,CAAC;AACvB,eAAO,MAAM,aAAa,GAAI,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,WACvC,CAAC;AACpB,eAAO,MAAM,mBAAmB,GAAI,QAAQ,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,WAC9C,CAAC;AACrB,eAAO,MAAM,iBAAiB,GAAI,QAAQ,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,WAC5C,CAAC;AAGrB,eAAO,MAAM,aAAa,GACxB,QAAQ,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,EAChC,OAAO,gBAAgB,KACtB,gBAKF,CAAC;AA2CF,eAAO,MAAM,mBAAmB,GAAI,QAAQ,UAAU,KAAG,MAuBxD,CAAC;AAKF,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,SAAS,SAAS,UAAU,EAAE,KAC7B,MAcW,CAAC;AAEf,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAMD,eAAO,MAAM,cAAc,GAAI,OAAO,SAAS,KAAG,cAAc,EA6B/D,CAAC;AAGF,eAAO,MAAM,eAAe,GAAI,QAAQ,UAAU,KAAG,OAI7B,CAAC;AAEzB,eAAO,MAAM,iBAAiB,GAAI,OAAO,SAAS,KAAG,UAAU,EAG5D,CAAC;AAKJ,eAAO,MAAM,YAAY,GAAI,OAAO,SAAS,KAAG,MAAM,GAAG,IAQxD,CAAC;AAkGF,eAAO,MAAM,eAAe,GAAI,OAAO,SAAS,KAAG,cAAc,EAGhE,CAAC;AAOF,eAAO,MAAM,aAAa,GACxB,UAAU,cAAc,EACxB,IAAI,UAAU,EACd,KAAK,MAAM,KACV,MAgCF,CAAC;AAIF,eAAO,MAAM,yBAAyB,GACpC,IAAI,UAAU,EACd,KAAK,MAAM,EACX,WAAW,MAAM,KAChB,MAyBF,CAAC"}
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.invalidAfterConversionSql = exports.conversionSql = exports.desiredTriggers = exports.ftsCreateSql = exports.searchableColumns = exports.isIndexedColumn = exports.desiredIndexes = exports.createTableSql = exports.columnDefinitionSql = exports.storedDefault = exports.minConstraintName = exports.checkConstraintName = exports.triggerPrefix = exports.ftsTableName = exports.participantIndexName = exports.uniqueIndexName = exports.indexName = exports.sqlLiteral = exports.UNIQUE_TYPES = exports.SEARCHABLE_TYPES = exports.NUMERIC_TYPES = exports.TEXT_LIKE_TYPES = exports.DATA_NOW_SQL = void 0;
4
+ // The SQL a project's database runs to build and change its tables. Pure
5
+ // string building: every name in here passed identifierIssue or is one of
6
+ // ours, and every owner-supplied value is a literal escaped by sqlLiteral.
7
+ const dataIdentifiers_1 = require("./dataIdentifiers");
8
+ exports.DATA_NOW_SQL = "strftime('%Y-%m-%dT%H:%M:%SZ', 'now')";
9
+ exports.TEXT_LIKE_TYPES = new Set([
10
+ 'text',
11
+ 'long_text',
12
+ 'email',
13
+ 'url',
14
+ 'phone',
15
+ 'file'
16
+ ]);
17
+ exports.NUMERIC_TYPES = new Set([
18
+ 'integer',
19
+ 'decimal'
20
+ ]);
21
+ // Columns full-text search can cover.
22
+ exports.SEARCHABLE_TYPES = new Set([
23
+ 'text',
24
+ 'long_text',
25
+ 'email',
26
+ 'url',
27
+ 'phone',
28
+ 'enum'
29
+ ]);
30
+ // Columns a unique index makes sense on.
31
+ exports.UNIQUE_TYPES = new Set([
32
+ 'text',
33
+ 'email',
34
+ 'url',
35
+ 'phone',
36
+ 'integer',
37
+ 'decimal',
38
+ 'date',
39
+ 'datetime',
40
+ 'enum',
41
+ 'relation'
42
+ ]);
43
+ // Literal for a value the owner typed (a default, an enum value). Strings are
44
+ // single-quoted with quotes doubled; nothing else reaches SQL unescaped.
45
+ const sqlLiteral = (value) => {
46
+ if (value === null)
47
+ return 'NULL';
48
+ if (typeof value === 'boolean')
49
+ return value ? '1' : '0';
50
+ if (typeof value === 'number') {
51
+ if (!Number.isFinite(value))
52
+ throw new Error('not a finite number');
53
+ return String(value);
54
+ }
55
+ return `'${String(value).replace(/'/g, "''")}'`;
56
+ };
57
+ exports.sqlLiteral = sqlLiteral;
58
+ // Names derived from stable ids, so renaming a table or column never leaves
59
+ // an index or constraint behind under the old name.
60
+ const indexName = (table, column) => `ix_${table.id}_${column.id}`;
61
+ exports.indexName = indexName;
62
+ const uniqueIndexName = (table, column) => `ux_${table.id}_${column.id}`;
63
+ exports.uniqueIndexName = uniqueIndexName;
64
+ const participantIndexName = (table) => `ix_${table.id}_participant`;
65
+ exports.participantIndexName = participantIndexName;
66
+ const ftsTableName = (table) => `${table.name}__fts`;
67
+ exports.ftsTableName = ftsTableName;
68
+ const triggerPrefix = (table) => `tr_${table.id}_`;
69
+ exports.triggerPrefix = triggerPrefix;
70
+ const checkConstraintName = (column) => `chk_${column.id}`;
71
+ exports.checkConstraintName = checkConstraintName;
72
+ const minConstraintName = (column) => `min_${column.id}`;
73
+ exports.minConstraintName = minConstraintName;
74
+ // The default as SQLite stores it: booleans as 0/1, JSON as its text.
75
+ const storedDefault = (column, value) => {
76
+ if (value === null)
77
+ return null;
78
+ if (column.type === 'boolean')
79
+ return value ? 1 : 0;
80
+ if (column.type === 'json')
81
+ return JSON.stringify(value);
82
+ return value;
83
+ };
84
+ exports.storedDefault = storedDefault;
85
+ const typeCheck = (column) => {
86
+ const c = (0, dataIdentifiers_1.quoteIdentifier)(column.name);
87
+ switch (column.type) {
88
+ case 'integer':
89
+ case 'relation':
90
+ return { affinity: 'INTEGER', check: `typeof(${c}) = 'integer'` };
91
+ case 'decimal':
92
+ return {
93
+ affinity: 'REAL',
94
+ check: `typeof(${c}) IN ('real', 'integer')`
95
+ };
96
+ case 'boolean':
97
+ return { affinity: 'INTEGER', check: `${c} IN (0, 1)` };
98
+ case 'date':
99
+ return {
100
+ affinity: 'TEXT',
101
+ check: `typeof(${c}) = 'text' AND date(${c}) = ${c}`
102
+ };
103
+ case 'datetime':
104
+ return {
105
+ affinity: 'TEXT',
106
+ check: `typeof(${c}) = 'text' AND strftime('%Y-%m-%dT%H:%M:%SZ', ${c}) = ${c}`
107
+ };
108
+ case 'enum':
109
+ return {
110
+ affinity: 'TEXT',
111
+ check: `${c} IN (${(column.values ?? []).map(exports.sqlLiteral).join(', ')})`
112
+ };
113
+ case 'json':
114
+ return { affinity: 'TEXT', check: `json_valid(${c})` };
115
+ default:
116
+ return { affinity: 'TEXT', check: `typeof(${c}) = 'text'` };
117
+ }
118
+ };
119
+ const ON_DELETE_SQL = {
120
+ restrict: 'RESTRICT',
121
+ cascade: 'CASCADE',
122
+ set_null: 'SET NULL'
123
+ };
124
+ const columnDefinitionSql = (column) => {
125
+ const c = (0, dataIdentifiers_1.quoteIdentifier)(column.name);
126
+ const { affinity, check } = typeCheck(column);
127
+ const parts = [
128
+ c,
129
+ affinity,
130
+ `CONSTRAINT ${(0, dataIdentifiers_1.quoteIdentifier)((0, exports.checkConstraintName)(column))} CHECK (${c} IS NULL OR (${check}))`
131
+ ];
132
+ if (column.min !== null && exports.NUMERIC_TYPES.has(column.type)) {
133
+ parts.push(`CONSTRAINT ${(0, dataIdentifiers_1.quoteIdentifier)((0, exports.minConstraintName)(column))} CHECK (${c} IS NULL OR ${c} >= ${(0, exports.sqlLiteral)(column.min)})`);
134
+ }
135
+ if (column.type === 'relation' && column.table) {
136
+ parts.push(`REFERENCES ${(0, dataIdentifiers_1.quoteIdentifier)(column.table)}(id) ON DELETE ${ON_DELETE_SQL[column.onDelete ?? 'restrict']}`);
137
+ }
138
+ if (column.required)
139
+ parts.push('NOT NULL');
140
+ if (column.default !== undefined && column.default !== null) {
141
+ parts.push(`DEFAULT ${(0, exports.sqlLiteral)((0, exports.storedDefault)(column, column.default))}`);
142
+ }
143
+ return parts.join(' ');
144
+ };
145
+ exports.columnDefinitionSql = columnDefinitionSql;
146
+ // Every table has the same system columns around the owner's. The hidden
147
+ // participant_id is always there, so turning customer scoping on is an index,
148
+ // not a table change.
149
+ const createTableSql = (name, columns) => [
150
+ `CREATE TABLE ${(0, dataIdentifiers_1.quoteIdentifier)(name)} (`,
151
+ [
152
+ 'id INTEGER PRIMARY KEY',
153
+ ...columns.map(exports.columnDefinitionSql),
154
+ `created_at TEXT NOT NULL DEFAULT (${exports.DATA_NOW_SQL})`,
155
+ `updated_at TEXT NOT NULL DEFAULT (${exports.DATA_NOW_SQL})`,
156
+ 'version INTEGER NOT NULL DEFAULT 1',
157
+ 'created_by TEXT',
158
+ 'updated_by TEXT',
159
+ 'participant_id TEXT'
160
+ ].join(',\n '),
161
+ ')'
162
+ ].join('\n');
163
+ exports.createTableSql = createTableSql;
164
+ // The indexes a table should have: every relation and file column (SQLite
165
+ // doesn't index the child side of a foreign key), every unique column (as a
166
+ // unique index, so the column can still be dropped with ALTER TABLE) and
167
+ // every filterable one.
168
+ const desiredIndexes = (table) => {
169
+ const out = [];
170
+ for (const column of table.columns) {
171
+ if (column.unique) {
172
+ const name = (0, exports.uniqueIndexName)(table, column);
173
+ out.push({
174
+ name,
175
+ sql: `CREATE UNIQUE INDEX ${(0, dataIdentifiers_1.quoteIdentifier)(name)} ON ${(0, dataIdentifiers_1.quoteIdentifier)(table.name)}(${(0, dataIdentifiers_1.quoteIdentifier)(column.name)})`
176
+ });
177
+ }
178
+ else if (column.type === 'relation' ||
179
+ column.type === 'file' ||
180
+ column.filterable) {
181
+ const name = (0, exports.indexName)(table, column);
182
+ out.push({
183
+ name,
184
+ sql: `CREATE INDEX ${(0, dataIdentifiers_1.quoteIdentifier)(name)} ON ${(0, dataIdentifiers_1.quoteIdentifier)(table.name)}(${(0, dataIdentifiers_1.quoteIdentifier)(column.name)})`
185
+ });
186
+ }
187
+ }
188
+ if (table.customerScoped) {
189
+ const name = (0, exports.participantIndexName)(table);
190
+ out.push({
191
+ name,
192
+ sql: `CREATE INDEX ${(0, dataIdentifiers_1.quoteIdentifier)(name)} ON ${(0, dataIdentifiers_1.quoteIdentifier)(table.name)}(participant_id)`
193
+ });
194
+ }
195
+ return out;
196
+ };
197
+ exports.desiredIndexes = desiredIndexes;
198
+ // Whether a filter or sort on this column is served by an index.
199
+ const isIndexedColumn = (column) => column.unique ||
200
+ column.filterable ||
201
+ column.type === 'relation' ||
202
+ column.type === 'file';
203
+ exports.isIndexedColumn = isIndexedColumn;
204
+ const searchableColumns = (table) => table.columns.filter(column => column.searchable && exports.SEARCHABLE_TYPES.has(column.type));
205
+ exports.searchableColumns = searchableColumns;
206
+ // The search table: an external-content FTS5 table over the searchable
207
+ // columns, accent-insensitive ("jalapeno" finds "jalapeño"). Null when the
208
+ // table has nothing searchable.
209
+ const ftsCreateSql = (table) => {
210
+ const columns = (0, exports.searchableColumns)(table);
211
+ if (columns.length === 0)
212
+ return null;
213
+ return `CREATE VIRTUAL TABLE ${(0, dataIdentifiers_1.quoteIdentifier)((0, exports.ftsTableName)(table))} USING fts5(${columns
214
+ .map(c => (0, dataIdentifiers_1.quoteIdentifier)(c.name))
215
+ .join(', ')}, content=${(0, exports.sqlLiteral)(table.name)}, content_rowid='id', tokenize='unicode61 remove_diacritics 2')`;
216
+ };
217
+ exports.ftsCreateSql = ftsCreateSql;
218
+ const ftsTriggers = (table) => {
219
+ const columns = (0, exports.searchableColumns)(table);
220
+ if (columns.length === 0)
221
+ return [];
222
+ const fts = (0, dataIdentifiers_1.quoteIdentifier)((0, exports.ftsTableName)(table));
223
+ const t = (0, dataIdentifiers_1.quoteIdentifier)(table.name);
224
+ const list = columns.map(c => (0, dataIdentifiers_1.quoteIdentifier)(c.name)).join(', ');
225
+ const newValues = columns.map(c => `new.${(0, dataIdentifiers_1.quoteIdentifier)(c.name)}`).join(', ');
226
+ const oldValues = columns.map(c => `old.${(0, dataIdentifiers_1.quoteIdentifier)(c.name)}`).join(', ');
227
+ const prefix = (0, exports.triggerPrefix)(table);
228
+ return [
229
+ {
230
+ name: `${prefix}fts_ai`,
231
+ sql: `CREATE TRIGGER ${(0, dataIdentifiers_1.quoteIdentifier)(`${prefix}fts_ai`)} AFTER INSERT ON ${t} BEGIN INSERT INTO ${fts}(rowid, ${list}) VALUES (new.id, ${newValues}); END`
232
+ },
233
+ {
234
+ name: `${prefix}fts_ad`,
235
+ sql: `CREATE TRIGGER ${(0, dataIdentifiers_1.quoteIdentifier)(`${prefix}fts_ad`)} AFTER DELETE ON ${t} BEGIN INSERT INTO ${fts}(${fts}, rowid, ${list}) VALUES ('delete', old.id, ${oldValues}); END`
236
+ },
237
+ {
238
+ // Only when a searchable column changes, so a stock decrement doesn't
239
+ // rewrite the search index.
240
+ name: `${prefix}fts_au`,
241
+ sql: `CREATE TRIGGER ${(0, dataIdentifiers_1.quoteIdentifier)(`${prefix}fts_au`)} AFTER UPDATE OF ${list} ON ${t} BEGIN INSERT INTO ${fts}(${fts}, rowid, ${list}) VALUES ('delete', old.id, ${oldValues}); INSERT INTO ${fts}(rowid, ${list}) VALUES (new.id, ${newValues}); END`
242
+ }
243
+ ];
244
+ };
245
+ // json_object takes at most 127 arguments, so wide tables are built in
246
+ // pieces and merged. json_patch drops keys whose value is null, which is what
247
+ // leaves only the changed columns.
248
+ const changedColumnsJson = (columns, value) => {
249
+ let out = "'{}'";
250
+ for (let i = 0; i < columns.length; i += 50) {
251
+ const pairs = columns
252
+ .slice(i, i + 50)
253
+ .map(c => `${(0, exports.sqlLiteral)(c.name)}, ${value((0, dataIdentifiers_1.quoteIdentifier)(c.name))}`)
254
+ .join(', ');
255
+ out = `json_patch(${out}, json_object(${pairs}))`;
256
+ }
257
+ return out;
258
+ };
259
+ // Row history: who changed which columns, old → new. Triggers rather than
260
+ // code, so cascades and SET NULL are recorded too. The actor comes from
261
+ // _ganju_context, which every write sets first.
262
+ const historyTriggers = (table) => {
263
+ if (!table.history)
264
+ return [];
265
+ const t = (0, dataIdentifiers_1.quoteIdentifier)(table.name);
266
+ const prefix = (0, exports.triggerPrefix)(table);
267
+ const columns = table.columns;
268
+ const log = (op, rowId, changes) => `INSERT INTO _ganju_row_log (table_id, row_id, op, actor, at, changes) VALUES (${table.id}, ${rowId}, '${op}', (SELECT actor FROM _ganju_context WHERE id = 1), ${exports.DATA_NOW_SQL}, ${changes})`;
269
+ const changed = columns.length
270
+ ? columns.map(c => `old.${(0, dataIdentifiers_1.quoteIdentifier)(c.name)} IS NOT new.${(0, dataIdentifiers_1.quoteIdentifier)(c.name)}`).join(' OR ')
271
+ : '0';
272
+ return [
273
+ {
274
+ name: `${prefix}log_ai`,
275
+ sql: `CREATE TRIGGER ${(0, dataIdentifiers_1.quoteIdentifier)(`${prefix}log_ai`)} AFTER INSERT ON ${t} BEGIN ${log('insert', 'new.id', changedColumnsJson(columns, c => `CASE WHEN new.${c} IS NOT NULL THEN json_array(NULL, new.${c}) END`))}; END`
276
+ },
277
+ {
278
+ name: `${prefix}log_au`,
279
+ sql: `CREATE TRIGGER ${(0, dataIdentifiers_1.quoteIdentifier)(`${prefix}log_au`)} AFTER UPDATE ON ${t} WHEN ${changed} BEGIN ${log('update', 'new.id', changedColumnsJson(columns, c => `CASE WHEN old.${c} IS NOT new.${c} THEN json_array(old.${c}, new.${c}) END`))}; END`
280
+ },
281
+ {
282
+ name: `${prefix}log_ad`,
283
+ sql: `CREATE TRIGGER ${(0, dataIdentifiers_1.quoteIdentifier)(`${prefix}log_ad`)} AFTER DELETE ON ${t} BEGIN ${log('delete', 'old.id', changedColumnsJson(columns, c => `CASE WHEN old.${c} IS NOT NULL THEN json_array(old.${c}, NULL) END`))}; END`
284
+ }
285
+ ];
286
+ };
287
+ const desiredTriggers = (table) => [
288
+ ...ftsTriggers(table),
289
+ ...historyTriggers(table)
290
+ ];
291
+ exports.desiredTriggers = desiredTriggers;
292
+ // Type changes
293
+ // SQL that turns `src` (the old column's value) into what `to` stores, or
294
+ // NULL when it can't. The rows where it yields NULL from a non-null value
295
+ // are the ones a type change lists and refuses on.
296
+ const conversionSql = (fromType, to, src) => {
297
+ if (fromType === to.type)
298
+ return src;
299
+ switch (to.type) {
300
+ case 'text':
301
+ case 'long_text':
302
+ case 'email':
303
+ case 'url':
304
+ case 'phone':
305
+ case 'file':
306
+ return fromType === 'boolean'
307
+ ? `CASE ${src} WHEN 1 THEN 'true' WHEN 0 THEN 'false' END`
308
+ : `CAST(${src} AS TEXT)`;
309
+ case 'integer':
310
+ case 'relation':
311
+ return `CASE WHEN typeof(${src}) = 'integer' THEN ${src} WHEN typeof(${src}) = 'real' AND ${src} = CAST(${src} AS INTEGER) THEN CAST(${src} AS INTEGER) WHEN typeof(${src}) = 'text' AND trim(${src}) <> '' AND CAST(CAST(trim(${src}) AS INTEGER) AS TEXT) = ltrim(trim(${src}), '+') THEN CAST(trim(${src}) AS INTEGER) END`;
312
+ case 'decimal':
313
+ return `CASE WHEN typeof(${src}) IN ('integer', 'real') THEN ${src} WHEN typeof(${src}) = 'text' AND trim(${src}) <> '' AND trim(${src}) NOT GLOB '*[^0-9.eE+-]*' AND trim(${src}) GLOB '*[0-9]*' THEN CAST(trim(${src}) AS REAL) END`;
314
+ case 'boolean':
315
+ return `CASE WHEN typeof(${src}) IN ('integer', 'real') AND ${src} IN (0, 1) THEN CAST(${src} AS INTEGER) WHEN typeof(${src}) = 'text' AND lower(trim(${src})) IN ('true', '1', 'yes', 'si', 'sí') THEN 1 WHEN typeof(${src}) = 'text' AND lower(trim(${src})) IN ('false', '0', 'no') THEN 0 END`;
316
+ case 'date':
317
+ return `CASE WHEN typeof(${src}) = 'text' THEN date(${src}) END`;
318
+ case 'datetime':
319
+ return `CASE WHEN typeof(${src}) = 'text' THEN strftime('%Y-%m-%dT%H:%M:%SZ', ${src}) END`;
320
+ case 'enum':
321
+ return `CASE lower(trim(CAST(${src} AS TEXT))) ${(to.values ?? [])
322
+ .map(v => `WHEN ${(0, exports.sqlLiteral)(v.toLowerCase())} THEN ${(0, exports.sqlLiteral)(v)}`)
323
+ .join(' ')} END`;
324
+ case 'json':
325
+ return fromType === 'boolean'
326
+ ? `CASE ${src} WHEN 1 THEN 'true' WHEN 0 THEN 'false' END`
327
+ : `CASE WHEN typeof(${src}) = 'text' AND json_valid(${src}) THEN ${src} ELSE json_quote(${src}) END`;
328
+ }
329
+ };
330
+ exports.conversionSql = conversionSql;
331
+ // The condition that marks a row whose value can't be kept after the change:
332
+ // it doesn't convert, or breaks one of the new column's rules.
333
+ const invalidAfterConversionSql = (to, src, converted) => {
334
+ const parts = [`(${src} IS NOT NULL AND (${converted}) IS NULL)`];
335
+ if (to.required)
336
+ parts.push(`(${converted}) IS NULL`);
337
+ if (to.min !== null && exports.NUMERIC_TYPES.has(to.type)) {
338
+ parts.push(`(${converted}) < ${(0, exports.sqlLiteral)(to.min)}`);
339
+ }
340
+ if (to.type === 'enum') {
341
+ parts.push(`((${converted}) IS NOT NULL AND (${converted}) NOT IN (${(to.values ?? []).map(exports.sqlLiteral).join(', ')}))`);
342
+ }
343
+ if (to.type === 'email') {
344
+ parts.push(`(${converted}) NOT LIKE '%_@_%._%'`);
345
+ }
346
+ if (to.type === 'url') {
347
+ parts.push(`NOT ((${converted}) LIKE 'http://%' OR (${converted}) LIKE 'https://%')`);
348
+ }
349
+ if (to.type === 'relation' && to.table) {
350
+ parts.push(`((${converted}) IS NOT NULL AND NOT EXISTS (SELECT 1 FROM ${(0, dataIdentifiers_1.quoteIdentifier)(to.table)} WHERE id = (${converted})))`);
351
+ }
352
+ return parts.join(' OR ');
353
+ };
354
+ exports.invalidAfterConversionSql = invalidAfterConversionSql;
@@ -0,0 +1,143 @@
1
+ import type { SqlValue } from './dataCompiler';
2
+ import { toTableInput, DataError } from './dataTypes';
3
+ import type { DataCaller, DataColumn, DataDefaultValue, DataDeleteImpact, DataSchema, DataTable, DataTableCopy } from './dataTypes';
4
+ export type SqlRow = Record<string, SqlValue>;
5
+ export interface SqlRunner {
6
+ all(sql: string, params?: readonly SqlValue[]): SqlRow[];
7
+ iterate(sql: string, params?: readonly SqlValue[]): Iterable<SqlRow>;
8
+ transaction<T>(fn: () => T): T;
9
+ databaseSize(): number;
10
+ }
11
+ export declare const INTERNAL_MIGRATIONS: readonly (readonly string[])[];
12
+ export declare const INTERNAL_SCHEMA_VERSION: number;
13
+ export declare const hasInternalTables: (runner: SqlRunner) => boolean;
14
+ export declare const getMeta: (runner: SqlRunner, key: string) => string | null;
15
+ export declare const setMeta: (runner: SqlRunner, key: string, value: string | null) => void;
16
+ export declare const migrateInternal: (runner: SqlRunner) => number;
17
+ export declare const loadSchema: (runner: SqlRunner) => DataSchema;
18
+ export { toTableInput };
19
+ type ColumnDraft = Omit<DataColumn, 'id' | 'position'>;
20
+ export declare const normaliseColumn: (input: unknown, schema: DataSchema, selfTable: string) => ColumnDraft;
21
+ export declare const translateSqliteError: (schema: DataSchema, table: DataTable, error: unknown) => DataError;
22
+ type ShapedRow = Record<string, unknown> & {
23
+ id: number;
24
+ };
25
+ export declare const describe: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, rawArgs?: unknown) => {
26
+ tables: ({
27
+ displayColumn: string;
28
+ searchable: string[];
29
+ columns: ({
30
+ description?: string | undefined;
31
+ min?: number | undefined;
32
+ default?: DataDefaultValue | undefined;
33
+ references?: string | null | undefined;
34
+ values?: string[] | undefined;
35
+ unique?: boolean | undefined;
36
+ required?: boolean | undefined;
37
+ label?: string | undefined;
38
+ name: string;
39
+ type: "boolean" | "email" | "json" | "text" | "long_text" | "integer" | "decimal" | "date" | "datetime" | "enum" | "url" | "phone" | "relation" | "file";
40
+ } | {
41
+ name: string;
42
+ type: string;
43
+ })[];
44
+ referencedBy: string[];
45
+ rowsAbout: number;
46
+ onlyThisCustomersRows?: boolean | undefined;
47
+ description: string | null;
48
+ access: "none" | "read" | "add" | "full";
49
+ label?: string | undefined;
50
+ name: string;
51
+ } | {
52
+ displayColumn: string;
53
+ searchable: string[];
54
+ columns: ({
55
+ description?: string | undefined;
56
+ min?: number | undefined;
57
+ default?: DataDefaultValue | undefined;
58
+ references?: string | null | undefined;
59
+ values?: string[] | undefined;
60
+ unique?: boolean | undefined;
61
+ required?: boolean | undefined;
62
+ label?: string | undefined;
63
+ name: string;
64
+ type: "boolean" | "email" | "json" | "text" | "long_text" | "integer" | "decimal" | "date" | "datetime" | "enum" | "url" | "phone" | "relation" | "file";
65
+ } | {
66
+ name: string;
67
+ type: string;
68
+ })[];
69
+ referencedBy: string[];
70
+ rows: number;
71
+ onlyThisCustomersRows?: boolean | undefined;
72
+ description: string | null;
73
+ access: "none" | "read" | "add" | "full";
74
+ label?: string | undefined;
75
+ name: string;
76
+ })[];
77
+ };
78
+ export declare const query: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, rawArgs: unknown) => {
79
+ groupsTruncated?: string | undefined;
80
+ scope?: string | undefined;
81
+ table: string;
82
+ } | {
83
+ nextCursor?: string | undefined;
84
+ includeTruncated?: string | undefined;
85
+ rows: ShapedRow[];
86
+ searchMatched?: "all words" | "any word" | undefined;
87
+ total?: number | undefined;
88
+ scope?: string | undefined;
89
+ table: string;
90
+ };
91
+ export declare const assertActor: (caller: DataCaller) => void;
92
+ export declare const assertWritable: (runner: SqlRunner, caller: DataCaller, addsData: boolean) => void;
93
+ type WriteResult = {
94
+ op: 'insert';
95
+ table: string;
96
+ id: number;
97
+ } | {
98
+ op: 'insert';
99
+ table: string;
100
+ ids: number[];
101
+ } | {
102
+ op: 'update' | 'delete';
103
+ table: string;
104
+ changed: number;
105
+ };
106
+ export declare const write: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, rawArgs: unknown) => {
107
+ ok: true;
108
+ results: WriteResult[];
109
+ };
110
+ export declare const deleteImpact: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, rawArgs: unknown) => DataDeleteImpact;
111
+ export declare const rowHistory: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, args: {
112
+ table: string;
113
+ id: number;
114
+ limit?: number;
115
+ cursor?: number;
116
+ }) => {
117
+ nextCursor?: number | undefined;
118
+ table: string;
119
+ id: number;
120
+ entries: {
121
+ op: SqlValue;
122
+ actor: SqlValue;
123
+ at: string;
124
+ changes: Record<string, [unknown, unknown]>;
125
+ }[];
126
+ };
127
+ export declare const pruneHistory: (runner: SqlRunner, now?: Date, batch?: number) => number;
128
+ export declare const createTable: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, input: unknown) => DataTable;
129
+ export declare const updateTable: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, name: string, patch: unknown) => DataTable;
130
+ export declare const dropTable: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, name: string) => {
131
+ dropped: string;
132
+ };
133
+ export declare const addColumn: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, tableName: string, input: unknown) => DataTable;
134
+ export declare const columnChangeNeedsRebuild: (schema: DataSchema, tableName: string, columnName: string, patch: unknown) => boolean;
135
+ export declare const dropColumnNeedsRebuild: (schema: DataSchema, tableName: string, columnName: string) => boolean;
136
+ export declare const readTableCopy: (runner: SqlRunner) => DataTableCopy | null;
137
+ export declare const writeReachesCopy: (schema: DataSchema, copy: Pick<DataTableCopy, "table">, rawArgs: unknown) => boolean;
138
+ export declare const clearFailedTableCopy: (runner: SqlRunner) => void;
139
+ export declare const copyTableStep: (runner: SqlRunner, schema: DataSchema) => "more" | "done" | "failed" | "idle";
140
+ export declare const updateColumn: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, tableName: string, columnName: string, patch: unknown) => DataTable;
141
+ export declare const dropColumn: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, tableName: string, columnName: string) => DataTable;
142
+ export declare const exportRows: (runner: SqlRunner, table: DataTable, afterId: number) => Iterable<SqlRow>;
143
+ //# sourceMappingURL=dataEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataEngine.d.ts","sourceRoot":"","sources":["../src/dataEngine.ts"],"names":[],"mappings":"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"}