@lix-js/sdk 0.5.0 → 0.5.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.
- package/README.md +38 -289
- package/dist/environment/load-from-string.d.ts.map +1 -1
- package/dist/environment/load-from-string.js +9 -2
- package/dist/environment/load-from-string.js.map +1 -1
- package/dist/filesystem/file/schema.d.ts +7 -0
- package/dist/filesystem/file/schema.d.ts.map +1 -1
- package/dist/filesystem/file/schema.js +11 -1
- package/dist/filesystem/file/schema.js.map +1 -1
- package/dist/lix/open-lix.d.ts +18 -5
- package/dist/lix/open-lix.d.ts.map +1 -1
- package/dist/lix/open-lix.js.map +1 -1
- package/dist/observe/create-observe.d.ts.map +1 -1
- package/dist/observe/create-observe.js +19 -0
- package/dist/observe/create-observe.js.map +1 -1
- package/dist/observe/create-observe.test.js +73 -0
- package/dist/observe/create-observe.test.js.map +1 -1
- package/dist/observe/determine-schema-keys.d.ts +1 -0
- package/dist/observe/determine-schema-keys.d.ts.map +1 -1
- package/dist/observe/determine-schema-keys.js +110 -37
- package/dist/observe/determine-schema-keys.js.map +1 -1
- package/dist/observe/determine-schema-keys.test.js +23 -0
- package/dist/observe/determine-schema-keys.test.js.map +1 -1
- package/dist/services/env-variables/index.d.ts +1 -1
- package/dist/services/env-variables/index.js +2 -2
- package/dist/services/env-variables/index.js.map +1 -1
- package/dist/state-history/schema.test.js +1 -1
- package/dist/state-history/schema.test.js.map +1 -1
- package/package.json +3 -3
- package/src/environment/load-from-string.ts +11 -2
- package/src/filesystem/file/schema.ts +11 -1
- package/src/lix/open-lix.ts +18 -5
- package/src/observe/create-observe.test.ts +89 -0
- package/src/observe/create-observe.ts +25 -0
- package/src/observe/determine-schema-keys.test.ts +31 -0
- package/src/observe/determine-schema-keys.ts +136 -37
- package/src/state-history/schema.test.ts +1 -1
- package/src/database/sqlite/sqlite-wasm-binary.js +0 -42835
|
@@ -140,15 +140,37 @@ export function extractLiteralFilters(compiledQuery: any): {
|
|
|
140
140
|
schemaKeys: string[];
|
|
141
141
|
versionIds: string[];
|
|
142
142
|
entityIds: string[];
|
|
143
|
+
fileIds: string[];
|
|
143
144
|
} {
|
|
144
145
|
const schemaKeys = new Set<string>();
|
|
145
146
|
const versionIds = new Set<string>();
|
|
146
147
|
const entityIds = new Set<string>();
|
|
148
|
+
const fileIds = new Set<string>();
|
|
149
|
+
const tableNames = new Set<string>();
|
|
150
|
+
const tableAliases = new Map<string, string>();
|
|
147
151
|
|
|
148
152
|
const root = compiledQuery?.query ?? compiledQuery;
|
|
149
153
|
if (!root) {
|
|
150
|
-
return { schemaKeys: [], versionIds: [], entityIds: [] };
|
|
154
|
+
return { schemaKeys: [], versionIds: [], entityIds: [], fileIds: [] };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const rootQuery = root?.query ?? root;
|
|
158
|
+
if (rootQuery?.from)
|
|
159
|
+
extractTableNamesFromQueryNode(rootQuery.from, tableNames, tableAliases);
|
|
160
|
+
if (rootQuery?.joins) {
|
|
161
|
+
for (const join of rootQuery.joins) {
|
|
162
|
+
extractTableNamesFromQueryNode(join, tableNames, tableAliases);
|
|
163
|
+
}
|
|
151
164
|
}
|
|
165
|
+
if (rootQuery?.where)
|
|
166
|
+
extractTableNamesFromQueryNode(rootQuery.where, tableNames, tableAliases);
|
|
167
|
+
|
|
168
|
+
const fileTablesPresent =
|
|
169
|
+
tableNames.has("file") || tableNames.has("file_by_version");
|
|
170
|
+
const onlyFileTablesPresent =
|
|
171
|
+
fileTablesPresent &&
|
|
172
|
+
tableNames.size > 0 &&
|
|
173
|
+
Array.from(tableNames).every(isFileTableName);
|
|
152
174
|
|
|
153
175
|
const record = (column: string | undefined, values: string[] | undefined) => {
|
|
154
176
|
if (!column || !values || values.length === 0) return;
|
|
@@ -159,7 +181,9 @@ export function extractLiteralFilters(compiledQuery: any): {
|
|
|
159
181
|
? versionIds
|
|
160
182
|
: column === "entity_id"
|
|
161
183
|
? entityIds
|
|
162
|
-
:
|
|
184
|
+
: column === "file_id"
|
|
185
|
+
? fileIds
|
|
186
|
+
: undefined;
|
|
163
187
|
if (!target) return;
|
|
164
188
|
for (const value of values) {
|
|
165
189
|
if (typeof value === "string") {
|
|
@@ -193,24 +217,34 @@ export function extractLiteralFilters(compiledQuery: any): {
|
|
|
193
217
|
return;
|
|
194
218
|
case "BinaryOperationNode": {
|
|
195
219
|
const operator = node.operator?.operator ?? node.operator;
|
|
196
|
-
const leftColumn =
|
|
197
|
-
const rightColumn =
|
|
220
|
+
const leftColumn = extractColumnReference(node.leftOperand);
|
|
221
|
+
const rightColumn = extractColumnReference(node.rightOperand);
|
|
198
222
|
const leftValues = extractLiteralValues(node.leftOperand);
|
|
199
223
|
const rightValues = extractLiteralValues(node.rightOperand);
|
|
200
224
|
|
|
201
225
|
if (
|
|
202
|
-
leftColumn === "schema_key" ||
|
|
203
|
-
leftColumn === "version_id" ||
|
|
204
|
-
leftColumn === "entity_id"
|
|
226
|
+
leftColumn?.column === "schema_key" ||
|
|
227
|
+
leftColumn?.column === "version_id" ||
|
|
228
|
+
leftColumn?.column === "entity_id" ||
|
|
229
|
+
leftColumn?.column === "file_id"
|
|
205
230
|
) {
|
|
206
|
-
record(leftColumn, rightValues);
|
|
231
|
+
record(leftColumn.column, rightValues);
|
|
207
232
|
}
|
|
208
233
|
if (
|
|
209
|
-
rightColumn === "schema_key" ||
|
|
210
|
-
rightColumn === "version_id" ||
|
|
211
|
-
rightColumn === "entity_id"
|
|
234
|
+
rightColumn?.column === "schema_key" ||
|
|
235
|
+
rightColumn?.column === "version_id" ||
|
|
236
|
+
rightColumn?.column === "entity_id" ||
|
|
237
|
+
rightColumn?.column === "file_id"
|
|
212
238
|
) {
|
|
213
|
-
record(rightColumn, leftValues);
|
|
239
|
+
record(rightColumn.column, leftValues);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Heuristic: file queries filter on 'id' column; capture as file_id when file table is present.
|
|
243
|
+
if (fileTablesPresent) {
|
|
244
|
+
if (isFileIdColumn(leftColumn, onlyFileTablesPresent, tableAliases))
|
|
245
|
+
record("file_id", rightValues);
|
|
246
|
+
if (isFileIdColumn(rightColumn, onlyFileTablesPresent, tableAliases))
|
|
247
|
+
record("file_id", leftValues);
|
|
214
248
|
}
|
|
215
249
|
|
|
216
250
|
// For IN/NOT IN operators, both sides can carry literals. Already handled above.
|
|
@@ -288,24 +322,6 @@ export function extractLiteralFilters(compiledQuery: any): {
|
|
|
288
322
|
return undefined;
|
|
289
323
|
};
|
|
290
324
|
|
|
291
|
-
const extractColumnName = (node: any): string | undefined => {
|
|
292
|
-
if (!node) return undefined;
|
|
293
|
-
switch (node.kind) {
|
|
294
|
-
case "ReferenceNode":
|
|
295
|
-
return extractColumnName(node.column);
|
|
296
|
-
case "ColumnNode":
|
|
297
|
-
return extractColumnName(node.column);
|
|
298
|
-
case "IdentifierNode":
|
|
299
|
-
return typeof node.name === "string" ? node.name : undefined;
|
|
300
|
-
case "AliasNode":
|
|
301
|
-
return extractColumnName(node.node);
|
|
302
|
-
case "ParensNode":
|
|
303
|
-
return extractColumnName(node.node);
|
|
304
|
-
default:
|
|
305
|
-
return undefined;
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
|
|
309
325
|
const queryNode = root?.query ?? root;
|
|
310
326
|
if (queryNode?.where) visitFilterNode(queryNode.where);
|
|
311
327
|
if (queryNode?.having) visitFilterNode(queryNode.having);
|
|
@@ -319,6 +335,7 @@ export function extractLiteralFilters(compiledQuery: any): {
|
|
|
319
335
|
schemaKeys: Array.from(schemaKeys),
|
|
320
336
|
versionIds: Array.from(versionIds),
|
|
321
337
|
entityIds: Array.from(entityIds),
|
|
338
|
+
fileIds: Array.from(fileIds),
|
|
322
339
|
};
|
|
323
340
|
}
|
|
324
341
|
|
|
@@ -327,7 +344,8 @@ export function extractLiteralFilters(compiledQuery: any): {
|
|
|
327
344
|
*/
|
|
328
345
|
function extractTableNamesFromQueryNode(
|
|
329
346
|
node: any,
|
|
330
|
-
tableNames: Set<string
|
|
347
|
+
tableNames: Set<string>,
|
|
348
|
+
tableAliases?: Map<string, string>
|
|
331
349
|
): void {
|
|
332
350
|
if (!node) return;
|
|
333
351
|
|
|
@@ -335,8 +353,9 @@ function extractTableNamesFromQueryNode(
|
|
|
335
353
|
switch (node.kind) {
|
|
336
354
|
case "TableNode": {
|
|
337
355
|
// Extract table name from TableNode
|
|
338
|
-
|
|
339
|
-
|
|
356
|
+
const tableName = extractTableIdentifier(node.table);
|
|
357
|
+
if (tableName) {
|
|
358
|
+
tableNames.add(tableName);
|
|
340
359
|
}
|
|
341
360
|
break;
|
|
342
361
|
}
|
|
@@ -388,14 +407,25 @@ function extractTableNamesFromQueryNode(
|
|
|
388
407
|
case "AliasNode": {
|
|
389
408
|
// Look inside the aliased node
|
|
390
409
|
if (node.node) {
|
|
391
|
-
|
|
410
|
+
const aliasedTableName =
|
|
411
|
+
node.node.kind === "TableNode"
|
|
412
|
+
? extractTableIdentifier(node.node.table)
|
|
413
|
+
: extractTableIdentifier(node.node);
|
|
414
|
+
if (aliasedTableName && node.alias?.name) {
|
|
415
|
+
tableAliases?.set(node.alias.name, aliasedTableName);
|
|
416
|
+
}
|
|
417
|
+
extractTableNamesFromQueryNode(node.node, tableNames, tableAliases);
|
|
392
418
|
}
|
|
393
419
|
break;
|
|
394
420
|
}
|
|
395
421
|
|
|
396
422
|
case "SetOperationNode": {
|
|
397
423
|
if (node.expression) {
|
|
398
|
-
extractTableNamesFromQueryNode(
|
|
424
|
+
extractTableNamesFromQueryNode(
|
|
425
|
+
node.expression,
|
|
426
|
+
tableNames,
|
|
427
|
+
tableAliases
|
|
428
|
+
);
|
|
399
429
|
}
|
|
400
430
|
break;
|
|
401
431
|
}
|
|
@@ -409,11 +439,15 @@ function extractTableNamesFromQueryNode(
|
|
|
409
439
|
if (Array.isArray(value)) {
|
|
410
440
|
for (const item of value) {
|
|
411
441
|
if (item && typeof item === "object" && item.kind) {
|
|
412
|
-
extractTableNamesFromQueryNode(
|
|
442
|
+
extractTableNamesFromQueryNode(
|
|
443
|
+
item,
|
|
444
|
+
tableNames,
|
|
445
|
+
tableAliases
|
|
446
|
+
);
|
|
413
447
|
}
|
|
414
448
|
}
|
|
415
449
|
} else if (value.kind) {
|
|
416
|
-
extractTableNamesFromQueryNode(value, tableNames);
|
|
450
|
+
extractTableNamesFromQueryNode(value, tableNames, tableAliases);
|
|
417
451
|
}
|
|
418
452
|
}
|
|
419
453
|
}
|
|
@@ -422,3 +456,68 @@ function extractTableNamesFromQueryNode(
|
|
|
422
456
|
}
|
|
423
457
|
}
|
|
424
458
|
}
|
|
459
|
+
|
|
460
|
+
function isFileTableName(tableName: string): boolean {
|
|
461
|
+
return tableName === "file" || tableName === "file_by_version";
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
type ColumnReference = {
|
|
465
|
+
column?: string;
|
|
466
|
+
table?: string;
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
function extractColumnReference(node: any): ColumnReference | undefined {
|
|
470
|
+
if (!node) return undefined;
|
|
471
|
+
switch (node.kind) {
|
|
472
|
+
case "ReferenceNode": {
|
|
473
|
+
const table = extractTableIdentifier(node.table);
|
|
474
|
+
const nested = extractColumnReference(node.column);
|
|
475
|
+
return {
|
|
476
|
+
table: table ?? nested?.table,
|
|
477
|
+
column: nested?.column,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
case "ColumnNode": {
|
|
481
|
+
const table = extractTableIdentifier(node.table);
|
|
482
|
+
const nested = extractColumnReference(node.column);
|
|
483
|
+
return {
|
|
484
|
+
table: table ?? nested?.table,
|
|
485
|
+
column: nested?.column,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
case "IdentifierNode":
|
|
489
|
+
return { column: typeof node.name === "string" ? node.name : undefined };
|
|
490
|
+
case "AliasNode":
|
|
491
|
+
case "ParensNode":
|
|
492
|
+
return extractColumnReference(node.node);
|
|
493
|
+
default:
|
|
494
|
+
return undefined;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function extractTableIdentifier(node: any): string | undefined {
|
|
499
|
+
if (!node) return undefined;
|
|
500
|
+
switch (node.kind) {
|
|
501
|
+
case "TableNode":
|
|
502
|
+
return extractTableIdentifier(node.table);
|
|
503
|
+
case "SchemableIdentifierNode":
|
|
504
|
+
return extractTableIdentifier(node.identifier);
|
|
505
|
+
case "IdentifierNode":
|
|
506
|
+
return typeof node.name === "string" ? node.name : undefined;
|
|
507
|
+
default:
|
|
508
|
+
return undefined;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function isFileIdColumn(
|
|
513
|
+
columnRef: ColumnReference | undefined,
|
|
514
|
+
onlyFileTablesPresent: boolean,
|
|
515
|
+
tableAliases: Map<string, string>
|
|
516
|
+
): boolean {
|
|
517
|
+
if (!columnRef || columnRef.column !== "id") return false;
|
|
518
|
+
if (columnRef.table) {
|
|
519
|
+
const resolvedTable = tableAliases.get(columnRef.table) ?? columnRef.table;
|
|
520
|
+
return isFileTableName(resolvedTable);
|
|
521
|
+
}
|
|
522
|
+
return onlyFileTablesPresent;
|
|
523
|
+
}
|
|
@@ -883,7 +883,7 @@ test.skip("parent_change_set_ids field shows correct parent relationships", asyn
|
|
|
883
883
|
const history = await lix.db
|
|
884
884
|
.selectFrom("state_history")
|
|
885
885
|
.where("entity_id", "=", "test-entity")
|
|
886
|
-
// .where("
|
|
886
|
+
// .where("root_commit_id", "=", changeSet3.commit_id)
|
|
887
887
|
.orderBy("depth", "asc")
|
|
888
888
|
.selectAll()
|
|
889
889
|
.execute();
|