@jarenjs/db 0.56.0 → 0.67.0

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 (79) hide show
  1. package/ARCHITECTURE.md +412 -56
  2. package/README.md +600 -57
  3. package/docs/HOSTS.md +269 -0
  4. package/docs/JOBS-FORMAT.md +293 -45
  5. package/docs/LIVE-FORMAT.md +169 -20
  6. package/docs/MIGRATION-FORMAT.md +142 -17
  7. package/docs/MODEL-FORMAT.md +752 -64
  8. package/docs/REPLICATION-FORMAT.md +208 -0
  9. package/package.json +21 -7
  10. package/schemas/jaren-model.draft-07.schema.json +224 -162
  11. package/schemas/jaren-model.schema.json +224 -162
  12. package/schemas/jaren-replication-snapshot.draft-07.schema.json +83 -0
  13. package/schemas/jaren-replication-snapshot.schema.json +83 -0
  14. package/schemas/jaren-replication.draft-07.schema.json +82 -0
  15. package/schemas/jaren-replication.schema.json +82 -0
  16. package/src/algebra.js +227 -9
  17. package/src/backup.js +161 -0
  18. package/src/cancellation.js +48 -0
  19. package/src/capture.js +230 -47
  20. package/src/cli.js +165 -59
  21. package/src/cursor.js +417 -0
  22. package/src/dag-job.js +154 -21
  23. package/src/ddl.js +102 -8
  24. package/src/dialect.js +268 -113
  25. package/src/dialects/expression-read.js +158 -0
  26. package/src/dialects/postgres.js +618 -0
  27. package/src/dialects/rtree-ddl.js +129 -0
  28. package/src/dialects/sqlite.js +244 -11
  29. package/src/document-files.js +311 -0
  30. package/src/document-steps.js +422 -0
  31. package/src/documents.js +335 -0
  32. package/src/driver.js +448 -61
  33. package/src/drivers/bun.js +37 -1
  34. package/src/drivers/indexeddb-snapshot.js +149 -0
  35. package/src/drivers/node-pool.js +11 -0
  36. package/src/drivers/node-worker-endpoint.js +105 -0
  37. package/src/drivers/node-worker.js +204 -0
  38. package/src/drivers/node.js +41 -7
  39. package/src/drivers/postgres.js +331 -0
  40. package/src/drivers/wasm-oo1.js +97 -0
  41. package/src/drivers/wasm-session.js +67 -0
  42. package/src/drivers/wasm.js +17 -83
  43. package/src/drivers/worker-pool.js +183 -0
  44. package/src/drivers/worker-protocol.js +79 -0
  45. package/src/drivers/worker-queue.js +60 -0
  46. package/src/emit.js +339 -48
  47. package/src/entity.js +20 -22
  48. package/src/errors.js +430 -19
  49. package/src/expression.js +284 -0
  50. package/src/graph.js +64 -8
  51. package/src/index.js +48 -17
  52. package/src/introspect.js +583 -0
  53. package/src/jobs.js +843 -107
  54. package/src/json-bytes.js +58 -0
  55. package/src/live-join.js +250 -0
  56. package/src/live-nested.js +120 -0
  57. package/src/live.js +18 -4
  58. package/src/logical-rows.js +90 -0
  59. package/src/maintenance.js +175 -0
  60. package/src/migrate.js +248 -181
  61. package/src/model.js +68 -0
  62. package/src/plan.js +1119 -138
  63. package/src/pragmas.js +314 -0
  64. package/src/profile.js +151 -3
  65. package/src/query.js +1634 -323
  66. package/src/replication-format.js +115 -0
  67. package/src/replication.js +332 -0
  68. package/src/residual.js +17 -0
  69. package/src/series.js +12 -4
  70. package/src/store.js +1567 -273
  71. package/src/tracker.js +203 -29
  72. package/src/udf.js +88 -7
  73. package/types/index.d.ts +1158 -27
  74. package/types/node-pool.d.ts +28 -0
  75. package/types/node-worker.d.ts +54 -0
  76. package/types/node.d.ts +69 -2
  77. package/types/postgres.d.ts +46 -0
  78. package/types/typed.d.ts +27 -4
  79. package/types/wasm.d.ts +14 -0
package/src/model.js CHANGED
@@ -516,6 +516,12 @@ export function relationTables(entities) {
516
516
  kind: relation.kind,
517
517
  joinTable: relation.joinTable,
518
518
  targetKey: entities.get(relation.to).keys[0],
519
+ // the join row's two columns and the key each references, so a
520
+ // hop can lower through the join ROOT (§10.7) rather than
521
+ // refusing for want of one
522
+ ownColumn: `${entity.name}_key`,
523
+ ownKey: entity.keys[0],
524
+ targetColumn: `${relation.to}_key`,
519
525
  }
520
526
  : {
521
527
  to: relation.to,
@@ -628,6 +634,13 @@ export function explainMapping(model) {
628
634
  for (const declaring of entity.relations) {
629
635
  const relation = declaring.relation;
630
636
  if (relation.kind !== 'manyToMany' || seenJoins.has(relation.joinTable)) continue;
637
+ // a join table is a queryable ROOT (§10.7), so its name shares one
638
+ // namespace with the entities: a collision would make `$.X[*]`
639
+ // mean two things
640
+ if (entities.has(relation.joinTable)) {
641
+ throw modelError(`the join table '${relation.joinTable}' has the name of a declared `
642
+ + 'entity, and both are query roots', declaring.docPath ?? entity.docPath);
643
+ }
631
644
  seenJoins.add(relation.joinTable);
632
645
  const [a, b] = [entity.name, relation.to].sort();
633
646
  mapping.joinTables[relation.joinTable] = {
@@ -639,3 +652,58 @@ export function explainMapping(model) {
639
652
  }
640
653
  return mapping;
641
654
  }
655
+
656
+ /**
657
+ * The read-only query ROOTS a model's join tables contribute (§10.7):
658
+ * one pseudo-entity per declared many-to-many join table, carrying
659
+ * exactly its two key columns and no document of its own. They are
660
+ * queryable — `$.<JoinTable>[*]` binds like any entity array — and they
661
+ * are NOT writable: `store.entity(name)` reads the model's own entity
662
+ * map, which these are deliberately not in, so a membership is still
663
+ * written through `link`/`unlink` and the tracker's join rows.
664
+ * @param {Map<string, any>} entities - the normalized entities
665
+ * @param {any} mapping - `explainMapping(...)`
666
+ * @returns {{ entities: Map<string, any>, mappings: Record<string, any> }}
667
+ */
668
+ export function joinTableRoots(entities, mapping) {
669
+ /** @type {Map<string, any>} */
670
+ const roots = new Map();
671
+ /** @type {any} */
672
+ const mappings = {};
673
+ for (const [name, join] of Object.entries(mapping.joinTables ?? {})) {
674
+ const sides = [join.left, join.right];
675
+ /** @type {any} */
676
+ const properties = new Map();
677
+ /** @type {any} */
678
+ const schemaProperties = {};
679
+ const columns = [];
680
+ for (const side of sides) {
681
+ const referenced = entities.get(side.entity).properties.get(side.referencesKey);
682
+ properties.set(side.column, { name: side.column, type: referenced.type, key: true });
683
+ schemaProperties[side.column] = { type: referenced.type };
684
+ columns.push({ name: side.column, storage: referenced.type, source: 'column' });
685
+ }
686
+ roots.set(name, {
687
+ name,
688
+ docPath: `/entities/${sides[0].entity}` ,
689
+ schema: { type: 'object', required: sides.map((side) => side.column),
690
+ properties: schemaProperties },
691
+ properties,
692
+ keys: sides.map((side) => side.column),
693
+ relations: [],
694
+ version: null,
695
+ joinTable: true,
696
+ });
697
+ mappings[name] = {
698
+ table: name,
699
+ columns,
700
+ foreignKeys: [],
701
+ indexes: [],
702
+ keys: sides.map((side) => side.column),
703
+ // a join row IS its two keys: there is no document column to read,
704
+ // and the merge is handed an empty one
705
+ document: false,
706
+ };
707
+ }
708
+ return { entities: roots, mappings };
709
+ }