@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.
Files changed (37) hide show
  1. package/README.md +38 -289
  2. package/dist/environment/load-from-string.d.ts.map +1 -1
  3. package/dist/environment/load-from-string.js +9 -2
  4. package/dist/environment/load-from-string.js.map +1 -1
  5. package/dist/filesystem/file/schema.d.ts +7 -0
  6. package/dist/filesystem/file/schema.d.ts.map +1 -1
  7. package/dist/filesystem/file/schema.js +11 -1
  8. package/dist/filesystem/file/schema.js.map +1 -1
  9. package/dist/lix/open-lix.d.ts +18 -5
  10. package/dist/lix/open-lix.d.ts.map +1 -1
  11. package/dist/lix/open-lix.js.map +1 -1
  12. package/dist/observe/create-observe.d.ts.map +1 -1
  13. package/dist/observe/create-observe.js +19 -0
  14. package/dist/observe/create-observe.js.map +1 -1
  15. package/dist/observe/create-observe.test.js +73 -0
  16. package/dist/observe/create-observe.test.js.map +1 -1
  17. package/dist/observe/determine-schema-keys.d.ts +1 -0
  18. package/dist/observe/determine-schema-keys.d.ts.map +1 -1
  19. package/dist/observe/determine-schema-keys.js +110 -37
  20. package/dist/observe/determine-schema-keys.js.map +1 -1
  21. package/dist/observe/determine-schema-keys.test.js +23 -0
  22. package/dist/observe/determine-schema-keys.test.js.map +1 -1
  23. package/dist/services/env-variables/index.d.ts +1 -1
  24. package/dist/services/env-variables/index.js +2 -2
  25. package/dist/services/env-variables/index.js.map +1 -1
  26. package/dist/state-history/schema.test.js +1 -1
  27. package/dist/state-history/schema.test.js.map +1 -1
  28. package/package.json +3 -3
  29. package/src/environment/load-from-string.ts +11 -2
  30. package/src/filesystem/file/schema.ts +11 -1
  31. package/src/lix/open-lix.ts +18 -5
  32. package/src/observe/create-observe.test.ts +89 -0
  33. package/src/observe/create-observe.ts +25 -0
  34. package/src/observe/determine-schema-keys.test.ts +31 -0
  35. package/src/observe/determine-schema-keys.ts +136 -37
  36. package/src/state-history/schema.test.ts +1 -1
  37. 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
- : undefined;
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 = extractColumnName(node.leftOperand);
197
- const rightColumn = extractColumnName(node.rightOperand);
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
- if (node.table && node.table.identifier && node.table.identifier.name) {
339
- tableNames.add(node.table.identifier.name);
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
- extractTableNamesFromQueryNode(node.node, tableNames);
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(node.expression, tableNames);
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(item, tableNames);
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("root_change_set_id", "=", changeSet3.change_set_id)
886
+ // .where("root_commit_id", "=", changeSet3.commit_id)
887
887
  .orderBy("depth", "asc")
888
888
  .selectAll()
889
889
  .execute();