@rocicorp/zero 0.24.2025092500 → 0.24.2025093000

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 (104) hide show
  1. package/out/analyze-query/src/run-ast.d.ts.map +1 -1
  2. package/out/analyze-query/src/run-ast.js +8 -0
  3. package/out/analyze-query/src/run-ast.js.map +1 -1
  4. package/out/ast-to-zql/src/ast-to-zql.js +5 -4
  5. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  6. package/out/{chunk-N4QOJO4J.js → chunk-CWBP2EI4.js} +94 -151
  7. package/out/{chunk-N4QOJO4J.js.map → chunk-CWBP2EI4.js.map} +4 -4
  8. package/out/{chunk-576ADFNS.js → chunk-HMS3EZCQ.js} +3 -3
  9. package/out/{chunk-576ADFNS.js.map → chunk-HMS3EZCQ.js.map} +2 -2
  10. package/out/{chunk-QZPMFA73.js → chunk-YXYKEMHQ.js} +893 -263
  11. package/out/chunk-YXYKEMHQ.js.map +7 -0
  12. package/out/{lazy-inspector-TOTYUTBC.js → lazy-inspector-A5VQLZYJ.js} +7 -6
  13. package/out/lazy-inspector-A5VQLZYJ.js.map +7 -0
  14. package/out/react.js +2 -2
  15. package/out/solid.js +3 -3
  16. package/out/zero/package.json +1 -1
  17. package/out/zero-cache/src/services/replicator/replication-status.d.ts.map +1 -1
  18. package/out/zero-cache/src/services/replicator/replication-status.js +10 -6
  19. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  20. package/out/zero-events/src/status.d.ts +44 -18
  21. package/out/zero-events/src/status.d.ts.map +1 -1
  22. package/out/zero-events/src/status.js +8 -3
  23. package/out/zero-events/src/status.js.map +1 -1
  24. package/out/zero-protocol/src/ast.d.ts +1 -2
  25. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  26. package/out/zero-protocol/src/ast.js +1 -2
  27. package/out/zero-protocol/src/ast.js.map +1 -1
  28. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  29. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  30. package/out/zero-protocol/src/protocol-version.js +2 -1
  31. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  32. package/out/zero.js +3 -3
  33. package/out/zql/src/builder/builder.d.ts +2 -0
  34. package/out/zql/src/builder/builder.d.ts.map +1 -1
  35. package/out/zql/src/builder/builder.js +123 -42
  36. package/out/zql/src/builder/builder.js.map +1 -1
  37. package/out/zql/src/ivm/constraint.d.ts +6 -0
  38. package/out/zql/src/ivm/constraint.d.ts.map +1 -1
  39. package/out/zql/src/ivm/constraint.js +13 -0
  40. package/out/zql/src/ivm/constraint.js.map +1 -1
  41. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  42. package/out/zql/src/ivm/exists.js +5 -5
  43. package/out/zql/src/ivm/exists.js.map +1 -1
  44. package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
  45. package/out/zql/src/ivm/fan-in.js +1 -1
  46. package/out/zql/src/ivm/fan-in.js.map +1 -1
  47. package/out/zql/src/ivm/fan-out.js +1 -1
  48. package/out/zql/src/ivm/fan-out.js.map +1 -1
  49. package/out/zql/src/ivm/filter-operators.js +2 -2
  50. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  51. package/out/zql/src/ivm/filter-push.d.ts +2 -2
  52. package/out/zql/src/ivm/filter-push.d.ts.map +1 -1
  53. package/out/zql/src/ivm/filter-push.js +5 -5
  54. package/out/zql/src/ivm/filter-push.js.map +1 -1
  55. package/out/zql/src/ivm/filter.js +1 -1
  56. package/out/zql/src/ivm/filter.js.map +1 -1
  57. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  58. package/out/zql/src/ivm/flipped-join.js +42 -49
  59. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  60. package/out/zql/src/ivm/join.d.ts.map +1 -1
  61. package/out/zql/src/ivm/join.js +5 -5
  62. package/out/zql/src/ivm/join.js.map +1 -1
  63. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +2 -2
  64. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
  65. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +4 -4
  66. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  67. package/out/zql/src/ivm/memory-source.d.ts +2 -2
  68. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  69. package/out/zql/src/ivm/memory-source.js +55 -66
  70. package/out/zql/src/ivm/memory-source.js.map +1 -1
  71. package/out/zql/src/ivm/operator.d.ts +1 -1
  72. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  73. package/out/zql/src/ivm/push-accumulated.d.ts +2 -2
  74. package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
  75. package/out/zql/src/ivm/push-accumulated.js +8 -8
  76. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  77. package/out/zql/src/ivm/skip.js +2 -2
  78. package/out/zql/src/ivm/skip.js.map +1 -1
  79. package/out/zql/src/ivm/take.d.ts.map +1 -1
  80. package/out/zql/src/ivm/take.js +17 -17
  81. package/out/zql/src/ivm/take.js.map +1 -1
  82. package/out/zql/src/ivm/union-fan-in.d.ts +19 -0
  83. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -0
  84. package/out/zql/src/ivm/union-fan-in.js +146 -0
  85. package/out/zql/src/ivm/union-fan-in.js.map +1 -0
  86. package/out/zql/src/ivm/union-fan-out.d.ts +18 -0
  87. package/out/zql/src/ivm/union-fan-out.d.ts.map +1 -0
  88. package/out/zql/src/ivm/union-fan-out.js +48 -0
  89. package/out/zql/src/ivm/union-fan-out.js.map +1 -0
  90. package/out/zql/src/query/measure-push-operator.js +1 -1
  91. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  92. package/out/zql/src/query/named.d.ts +2 -1
  93. package/out/zql/src/query/named.d.ts.map +1 -1
  94. package/out/zql/src/query/named.js.map +1 -1
  95. package/out/zql/src/query/query-impl.js +3 -3
  96. package/out/zql/src/query/query-impl.js.map +1 -1
  97. package/out/zql/src/query/query.d.ts +1 -1
  98. package/out/zql/src/query/query.d.ts.map +1 -1
  99. package/out/zqlite/src/table-source.d.ts.map +1 -1
  100. package/out/zqlite/src/table-source.js +6 -7
  101. package/out/zqlite/src/table-source.js.map +1 -1
  102. package/package.json +1 -1
  103. package/out/chunk-QZPMFA73.js.map +0 -7
  104. package/out/lazy-inspector-TOTYUTBC.js.map +0 -7
@@ -532,6 +532,38 @@ var IterWrapper = class _IterWrapper {
532
532
  function wrapIterable(iter) {
533
533
  return new IterWrapper(iter);
534
534
  }
535
+ function* mergeIterables(iterables, comparator, distinct = false) {
536
+ const iterators = iterables.map((i) => i[Symbol.iterator]());
537
+ try {
538
+ const current = iterators.map((i) => i.next());
539
+ let lastYielded;
540
+ while (current.some((c) => !c.done)) {
541
+ const min = current.reduce(
542
+ (acc, c, i) => {
543
+ if (c.done) {
544
+ return acc;
545
+ }
546
+ if (acc === void 0 || comparator(c.value, acc[0]) < 0) {
547
+ return [c.value, i];
548
+ }
549
+ return acc;
550
+ },
551
+ void 0
552
+ );
553
+ assert(min !== void 0, "min is undefined");
554
+ current[min[1]] = iterators[min[1]].next();
555
+ if (lastYielded !== void 0 && distinct && comparator(lastYielded, min[0]) === 0) {
556
+ continue;
557
+ }
558
+ lastYielded = min[0];
559
+ yield min[0];
560
+ }
561
+ } finally {
562
+ for (const it of iterators) {
563
+ it.return?.();
564
+ }
565
+ }
566
+ }
535
567
 
536
568
  // ../replicache/src/btree/node.ts
537
569
  var NODE_LEVEL = 0;
@@ -3429,7 +3461,8 @@ var correlatedSubqueryConditionOperatorSchema = literalUnion("EXISTS", "NOT EXIS
3429
3461
  var correlatedSubqueryConditionSchema = readonlyObject({
3430
3462
  type: valita_exports.literal("correlatedSubquery"),
3431
3463
  related: valita_exports.lazy(() => correlatedSubquerySchema),
3432
- op: correlatedSubqueryConditionOperatorSchema
3464
+ op: correlatedSubqueryConditionOperatorSchema,
3465
+ flip: valita_exports.boolean().optional()
3433
3466
  });
3434
3467
  var conditionSchema = valita_exports.union(
3435
3468
  simpleConditionSchema,
@@ -3459,8 +3492,7 @@ var correlationSchema = readonlyObject({
3459
3492
  var correlatedSubquerySchemaOmitSubquery = readonlyObject({
3460
3493
  correlation: correlationSchema,
3461
3494
  hidden: valita_exports.boolean().optional(),
3462
- system: literalUnion("permissions", "client", "test").optional(),
3463
- flip: valita_exports.boolean().optional()
3495
+ system: literalUnion("permissions", "client", "test").optional()
3464
3496
  });
3465
3497
  var correlatedSubquerySchema = correlatedSubquerySchemaOmitSubquery.extend({
3466
3498
  subquery: valita_exports.lazy(() => astSchema)
@@ -3500,8 +3532,7 @@ function transformAST(ast, transform) {
3500
3532
  },
3501
3533
  hidden: r.hidden,
3502
3534
  subquery: transformAST(r.subquery, transform),
3503
- system: r.system,
3504
- flip: r.flip
3535
+ system: r.system
3505
3536
  })
3506
3537
  )
3507
3538
  ) : void 0,
@@ -4180,7 +4211,7 @@ var Inspector = class {
4180
4211
  },
4181
4212
  rep,
4182
4213
  getSocket,
4183
- lazy: import("./lazy-inspector-TOTYUTBC.js")
4214
+ lazy: import("./lazy-inspector-A5VQLZYJ.js")
4184
4215
  };
4185
4216
  this.client = new Client(this.#delegate, rep.clientID, rep.clientGroupID);
4186
4217
  this.clientGroup = this.client.clientGroup;
@@ -4334,7 +4365,7 @@ var FilterStart = class {
4334
4365
  return this.#input.getSchema();
4335
4366
  }
4336
4367
  push(change) {
4337
- this.#output.push(change);
4368
+ this.#output.push(change, this);
4338
4369
  }
4339
4370
  *fetch(req) {
4340
4371
  for (const node of this.#input.fetch(req)) {
@@ -4385,7 +4416,7 @@ var FilterEnd = class {
4385
4416
  return this.#input.getSchema();
4386
4417
  }
4387
4418
  push(change) {
4388
- this.#output.push(change);
4419
+ this.#output.push(change, this);
4389
4420
  }
4390
4421
  };
4391
4422
  function buildFilterPipeline(input, delegate, pipeline) {
@@ -4512,21 +4543,27 @@ var Exists = class {
4512
4543
  }
4513
4544
  if (size === 1) {
4514
4545
  if (this.#not) {
4515
- this.#output.push({
4516
- type: "remove",
4517
- node: {
4518
- row: change.node.row,
4519
- relationships: {
4520
- ...change.node.relationships,
4521
- [this.#relationshipName]: () => []
4546
+ this.#output.push(
4547
+ {
4548
+ type: "remove",
4549
+ node: {
4550
+ row: change.node.row,
4551
+ relationships: {
4552
+ ...change.node.relationships,
4553
+ [this.#relationshipName]: () => []
4554
+ }
4522
4555
  }
4523
- }
4524
- });
4556
+ },
4557
+ this
4558
+ );
4525
4559
  } else {
4526
- this.#output.push({
4527
- type: "add",
4528
- node: change.node
4529
- });
4560
+ this.#output.push(
4561
+ {
4562
+ type: "add",
4563
+ node: change.node
4564
+ },
4565
+ this
4566
+ );
4530
4567
  }
4531
4568
  } else {
4532
4569
  this.#pushWithFilter(change, size);
@@ -4544,23 +4581,29 @@ var Exists = class {
4544
4581
  }
4545
4582
  if (size === 0) {
4546
4583
  if (this.#not) {
4547
- this.#output.push({
4548
- type: "add",
4549
- node: change.node
4550
- });
4584
+ this.#output.push(
4585
+ {
4586
+ type: "add",
4587
+ node: change.node
4588
+ },
4589
+ this
4590
+ );
4551
4591
  } else {
4552
- this.#output.push({
4553
- type: "remove",
4554
- node: {
4555
- row: change.node.row,
4556
- relationships: {
4557
- ...change.node.relationships,
4558
- [this.#relationshipName]: () => [
4559
- change.child.change.node
4560
- ]
4592
+ this.#output.push(
4593
+ {
4594
+ type: "remove",
4595
+ node: {
4596
+ row: change.node.row,
4597
+ relationships: {
4598
+ ...change.node.relationships,
4599
+ [this.#relationshipName]: () => [
4600
+ change.child.change.node
4601
+ ]
4602
+ }
4561
4603
  }
4562
- }
4563
- });
4604
+ },
4605
+ this
4606
+ );
4564
4607
  }
4565
4608
  } else {
4566
4609
  this.#pushWithFilter(change, size);
@@ -4594,7 +4637,7 @@ var Exists = class {
4594
4637
  */
4595
4638
  #pushWithFilter(change, size) {
4596
4639
  if (this.#filter(change.node, size)) {
4597
- this.#output.push(change);
4640
+ this.#output.push(change, this);
4598
4641
  }
4599
4642
  }
4600
4643
  #getSize(node) {
@@ -4652,7 +4695,7 @@ var Exists = class {
4652
4695
  };
4653
4696
 
4654
4697
  // ../zql/src/ivm/push-accumulated.ts
4655
- function pushAccumulatedChanges(accumulatedPushes, output, fanOutChangeType, mergeRelationships, addEmptyRelationships) {
4698
+ function pushAccumulatedChanges(accumulatedPushes, output, pusher, fanOutChangeType, mergeRelationships2, addEmptyRelationships) {
4656
4699
  if (accumulatedPushes.length === 0) {
4657
4700
  return;
4658
4701
  }
@@ -4667,7 +4710,7 @@ function pushAccumulatedChanges(accumulatedPushes, output, fanOutChangeType, mer
4667
4710
  const existing = candidatesToPush.get(change.type);
4668
4711
  let mergedChange = change;
4669
4712
  if (existing) {
4670
- mergedChange = mergeRelationships(existing, change);
4713
+ mergedChange = mergeRelationships2(existing, change);
4671
4714
  }
4672
4715
  candidatesToPush.set(change.type, mergedChange);
4673
4716
  }
@@ -4679,14 +4722,20 @@ function pushAccumulatedChanges(accumulatedPushes, output, fanOutChangeType, mer
4679
4722
  types.length === 1 && types[0] === "remove",
4680
4723
  "Fan-in:remove expected all removes"
4681
4724
  );
4682
- output.push(addEmptyRelationships(must(candidatesToPush.get("remove"))));
4725
+ output.push(
4726
+ addEmptyRelationships(must(candidatesToPush.get("remove"))),
4727
+ pusher
4728
+ );
4683
4729
  return;
4684
4730
  case "add":
4685
4731
  assert(
4686
4732
  types.length === 1 && types[0] === "add",
4687
4733
  "Fan-in:add expected all adds"
4688
4734
  );
4689
- output.push(addEmptyRelationships(must(candidatesToPush.get("add"))));
4735
+ output.push(
4736
+ addEmptyRelationships(must(candidatesToPush.get("add"))),
4737
+ pusher
4738
+ );
4690
4739
  return;
4691
4740
  case "edit": {
4692
4741
  assert(
@@ -4700,12 +4749,12 @@ function pushAccumulatedChanges(accumulatedPushes, output, fanOutChangeType, mer
4700
4749
  let editChange = candidatesToPush.get("edit");
4701
4750
  if (editChange) {
4702
4751
  if (addChange) {
4703
- editChange = mergeRelationships(editChange, addChange);
4752
+ editChange = mergeRelationships2(editChange, addChange);
4704
4753
  }
4705
4754
  if (removeChange) {
4706
- editChange = mergeRelationships(editChange, removeChange);
4755
+ editChange = mergeRelationships2(editChange, removeChange);
4707
4756
  }
4708
- output.push(addEmptyRelationships(editChange));
4757
+ output.push(addEmptyRelationships(editChange), pusher);
4709
4758
  return;
4710
4759
  }
4711
4760
  if (addChange && removeChange) {
@@ -4714,11 +4763,15 @@ function pushAccumulatedChanges(accumulatedPushes, output, fanOutChangeType, mer
4714
4763
  type: "edit",
4715
4764
  node: addChange.node,
4716
4765
  oldNode: removeChange.node
4717
- })
4766
+ }),
4767
+ pusher
4718
4768
  );
4719
4769
  return;
4720
4770
  }
4721
- output.push(addEmptyRelationships(must(addChange ?? removeChange)));
4771
+ output.push(
4772
+ addEmptyRelationships(must(addChange ?? removeChange)),
4773
+ pusher
4774
+ );
4722
4775
  return;
4723
4776
  }
4724
4777
  case "child": {
@@ -4737,7 +4790,7 @@ function pushAccumulatedChanges(accumulatedPushes, output, fanOutChangeType, mer
4737
4790
  );
4738
4791
  const childChange = candidatesToPush.get("child");
4739
4792
  if (childChange) {
4740
- output.push(childChange);
4793
+ output.push(childChange, pusher);
4741
4794
  return;
4742
4795
  }
4743
4796
  const addChange = candidatesToPush.get("add");
@@ -4746,13 +4799,151 @@ function pushAccumulatedChanges(accumulatedPushes, output, fanOutChangeType, mer
4746
4799
  addChange === void 0 || removeChange === void 0,
4747
4800
  "Fan-in:child expected either add or remove, not both"
4748
4801
  );
4749
- output.push(addEmptyRelationships(must(addChange ?? removeChange)));
4802
+ output.push(
4803
+ addEmptyRelationships(must(addChange ?? removeChange)),
4804
+ pusher
4805
+ );
4750
4806
  return;
4751
4807
  }
4752
4808
  default:
4753
4809
  fanOutChangeType;
4754
4810
  }
4755
4811
  }
4812
+ function mergeRelationships(left, right) {
4813
+ if (left.type === right.type) {
4814
+ switch (left.type) {
4815
+ case "add": {
4816
+ return {
4817
+ type: "add",
4818
+ node: {
4819
+ row: left.node.row,
4820
+ relationships: {
4821
+ ...right.node.relationships,
4822
+ ...left.node.relationships
4823
+ }
4824
+ }
4825
+ };
4826
+ }
4827
+ case "remove": {
4828
+ return {
4829
+ type: "remove",
4830
+ node: {
4831
+ row: left.node.row,
4832
+ relationships: {
4833
+ ...right.node.relationships,
4834
+ ...left.node.relationships
4835
+ }
4836
+ }
4837
+ };
4838
+ }
4839
+ case "edit": {
4840
+ assert(right.type === "edit");
4841
+ return {
4842
+ type: "edit",
4843
+ node: {
4844
+ row: left.node.row,
4845
+ relationships: {
4846
+ ...right.node.relationships,
4847
+ ...left.node.relationships
4848
+ }
4849
+ },
4850
+ oldNode: {
4851
+ row: left.oldNode.row,
4852
+ relationships: {
4853
+ ...right.oldNode.relationships,
4854
+ ...left.oldNode.relationships
4855
+ }
4856
+ }
4857
+ };
4858
+ }
4859
+ }
4860
+ }
4861
+ assert(left.type === "edit");
4862
+ switch (right.type) {
4863
+ case "add": {
4864
+ return {
4865
+ type: "edit",
4866
+ node: {
4867
+ ...left.node,
4868
+ relationships: {
4869
+ ...right.node.relationships,
4870
+ ...left.node.relationships
4871
+ }
4872
+ },
4873
+ oldNode: left.oldNode
4874
+ };
4875
+ }
4876
+ case "remove": {
4877
+ return {
4878
+ type: "edit",
4879
+ node: left.node,
4880
+ oldNode: {
4881
+ ...left.oldNode,
4882
+ relationships: {
4883
+ ...right.node.relationships,
4884
+ ...left.oldNode.relationships
4885
+ }
4886
+ }
4887
+ };
4888
+ }
4889
+ }
4890
+ unreachable();
4891
+ }
4892
+ function makeAddEmptyRelationships(schema) {
4893
+ return (change) => {
4894
+ if (Object.keys(schema.relationships).length === 0) {
4895
+ return change;
4896
+ }
4897
+ switch (change.type) {
4898
+ case "add":
4899
+ case "remove": {
4900
+ const ret = {
4901
+ ...change,
4902
+ node: {
4903
+ ...change.node,
4904
+ relationships: {
4905
+ ...change.node.relationships
4906
+ }
4907
+ }
4908
+ };
4909
+ mergeEmpty(ret.node.relationships, Object.keys(schema.relationships));
4910
+ return ret;
4911
+ }
4912
+ case "edit": {
4913
+ const ret = {
4914
+ ...change,
4915
+ node: {
4916
+ ...change.node,
4917
+ relationships: {
4918
+ ...change.node.relationships
4919
+ }
4920
+ },
4921
+ oldNode: {
4922
+ ...change.oldNode,
4923
+ relationships: {
4924
+ ...change.oldNode.relationships
4925
+ }
4926
+ }
4927
+ };
4928
+ mergeEmpty(ret.node.relationships, Object.keys(schema.relationships));
4929
+ mergeEmpty(
4930
+ ret.oldNode.relationships,
4931
+ Object.keys(schema.relationships)
4932
+ );
4933
+ return ret;
4934
+ }
4935
+ case "child":
4936
+ return change;
4937
+ }
4938
+ };
4939
+ }
4940
+ function mergeEmpty(relationships, relationshipNames) {
4941
+ for (const relName of relationshipNames) {
4942
+ if (relationships[relName] === void 0) {
4943
+ relationships[relName] = () => emptyArray;
4944
+ }
4945
+ }
4946
+ }
4756
4947
 
4757
4948
  // ../zql/src/ivm/fan-in.ts
4758
4949
  var FanIn = class {
@@ -4796,6 +4987,7 @@ var FanIn = class {
4796
4987
  pushAccumulatedChanges(
4797
4988
  this.#accumulatedPushes,
4798
4989
  this.#output,
4990
+ this,
4799
4991
  fanOutChangeType,
4800
4992
  identity,
4801
4993
  identity
@@ -4844,7 +5036,7 @@ var FanOut = class {
4844
5036
  }
4845
5037
  push(change) {
4846
5038
  for (const out of this.#outputs) {
4847
- out.push(change);
5039
+ out.push(change, this);
4848
5040
  }
4849
5041
  must(
4850
5042
  this.#fanIn,
@@ -4854,44 +5046,50 @@ var FanOut = class {
4854
5046
  };
4855
5047
 
4856
5048
  // ../zql/src/ivm/maybe-split-and-push-edit-change.ts
4857
- function maybeSplitAndPushEditChange(change, predicate, output) {
5049
+ function maybeSplitAndPushEditChange(change, predicate, output, pusher) {
4858
5050
  const oldWasPresent = predicate(change.oldNode.row);
4859
5051
  const newIsPresent = predicate(change.node.row);
4860
5052
  if (oldWasPresent && newIsPresent) {
4861
- output.push(change);
5053
+ output.push(change, pusher);
4862
5054
  } else if (oldWasPresent && !newIsPresent) {
4863
- output.push({
4864
- type: "remove",
4865
- node: change.oldNode
4866
- });
5055
+ output.push(
5056
+ {
5057
+ type: "remove",
5058
+ node: change.oldNode
5059
+ },
5060
+ pusher
5061
+ );
4867
5062
  } else if (!oldWasPresent && newIsPresent) {
4868
- output.push({
4869
- type: "add",
4870
- node: change.node
4871
- });
5063
+ output.push(
5064
+ {
5065
+ type: "add",
5066
+ node: change.node
5067
+ },
5068
+ pusher
5069
+ );
4872
5070
  }
4873
5071
  }
4874
5072
 
4875
5073
  // ../zql/src/ivm/filter-push.ts
4876
- function filterPush(change, output, predicate) {
5074
+ function filterPush(change, output, pusher, predicate) {
4877
5075
  if (!predicate) {
4878
- output.push(change);
5076
+ output.push(change, pusher);
4879
5077
  return;
4880
5078
  }
4881
5079
  switch (change.type) {
4882
5080
  case "add":
4883
5081
  case "remove":
4884
5082
  if (predicate(change.node.row)) {
4885
- output.push(change);
5083
+ output.push(change, pusher);
4886
5084
  }
4887
5085
  break;
4888
5086
  case "child":
4889
5087
  if (predicate(change.node.row)) {
4890
- output.push(change);
5088
+ output.push(change, pusher);
4891
5089
  }
4892
5090
  break;
4893
5091
  case "edit":
4894
- maybeSplitAndPushEditChange(change, predicate, output);
5092
+ maybeSplitAndPushEditChange(change, predicate, output, pusher);
4895
5093
  break;
4896
5094
  default:
4897
5095
  unreachable(change);
@@ -4921,10 +5119,85 @@ var Filter = class {
4921
5119
  return this.#input.getSchema();
4922
5120
  }
4923
5121
  push(change) {
4924
- filterPush(change, this.#output, this.#predicate);
5122
+ filterPush(change, this.#output, this, this.#predicate);
4925
5123
  }
4926
5124
  };
4927
5125
 
5126
+ // ../zql/src/ivm/constraint.ts
5127
+ function constraintMatchesRow(constraint, row) {
5128
+ for (const key in constraint) {
5129
+ if (!valuesEqual(row[key], constraint[key])) {
5130
+ return false;
5131
+ }
5132
+ }
5133
+ return true;
5134
+ }
5135
+ function constraintsAreCompatible(left, right) {
5136
+ for (const key in left) {
5137
+ if (key in right && !valuesEqual(left[key], right[key])) {
5138
+ return false;
5139
+ }
5140
+ }
5141
+ return true;
5142
+ }
5143
+ function constraintMatchesPrimaryKey(constraint, primary) {
5144
+ const constraintKeys = Object.keys(constraint);
5145
+ if (constraintKeys.length !== primary.length) {
5146
+ return false;
5147
+ }
5148
+ constraintKeys.sort(stringCompare);
5149
+ for (let i = 0; i < constraintKeys.length; i++) {
5150
+ if (constraintKeys[i] !== primary[i]) {
5151
+ return false;
5152
+ }
5153
+ }
5154
+ return true;
5155
+ }
5156
+ function pullSimpleAndComponents(condition) {
5157
+ if (condition.type === "and") {
5158
+ return condition.conditions.flatMap(pullSimpleAndComponents);
5159
+ }
5160
+ if (condition.type === "simple") {
5161
+ return [condition];
5162
+ }
5163
+ if (condition.type === "or" && condition.conditions.length === 1) {
5164
+ return pullSimpleAndComponents(condition.conditions[0]);
5165
+ }
5166
+ return [];
5167
+ }
5168
+ function primaryKeyConstraintFromFilters(condition, primary) {
5169
+ if (condition === void 0) {
5170
+ return void 0;
5171
+ }
5172
+ const conditions = pullSimpleAndComponents(condition);
5173
+ if (conditions.length === 0) {
5174
+ return void 0;
5175
+ }
5176
+ const ret = {};
5177
+ for (const subCondition of conditions) {
5178
+ if (subCondition.op === "=") {
5179
+ const column = extractColumn(subCondition);
5180
+ if (column !== void 0) {
5181
+ if (!primary.includes(column.name)) {
5182
+ continue;
5183
+ }
5184
+ ret[column.name] = column.value;
5185
+ }
5186
+ }
5187
+ }
5188
+ if (Object.keys(ret).length !== primary.length) {
5189
+ return void 0;
5190
+ }
5191
+ return ret;
5192
+ }
5193
+ function extractColumn(condition) {
5194
+ if (condition.left.type === "column") {
5195
+ assert(condition.right.type === "literal");
5196
+ return { name: condition.left.name, value: condition.right.value };
5197
+ }
5198
+ return void 0;
5199
+ }
5200
+
4928
5201
  // ../zql/src/ivm/join-utils.ts
4929
5202
  function* generateWithOverlay(stream, overlay, schema) {
4930
5203
  let applied = false;
@@ -5089,7 +5362,7 @@ var FlippedJoin = class {
5089
5362
  const compare = this.#child.getSchema().compareRows;
5090
5363
  const insertPos = binarySearch(
5091
5364
  childNodes.length,
5092
- (i) => compare(childNodes[i].row, removedNode.row)
5365
+ (i) => compare(removedNode.row, childNodes[i].row)
5093
5366
  );
5094
5367
  childNodes.splice(insertPos, 0, removedNode);
5095
5368
  }
@@ -5097,20 +5370,23 @@ var FlippedJoin = class {
5097
5370
  let threw = false;
5098
5371
  try {
5099
5372
  for (const childNode of childNodes) {
5100
- const stream = this.#parent.fetch({
5101
- ...req,
5102
- constraint: {
5103
- ...req.constraint,
5104
- ...Object.fromEntries(
5105
- this.#parentKey.map((key, i) => [
5106
- key,
5107
- childNode.row[this.#childKey[i]]
5108
- ])
5109
- )
5110
- }
5111
- });
5112
- const iterator = stream[Symbol.iterator]();
5113
- parentIterators.push(iterator);
5373
+ const constraintFromChild = {};
5374
+ for (let i = 0; i < this.#parentKey.length; i++) {
5375
+ constraintFromChild[this.#parentKey[i]] = childNode.row[this.#childKey[i]];
5376
+ }
5377
+ if (req.constraint && !constraintsAreCompatible(constraintFromChild, req.constraint)) {
5378
+ parentIterators.push(emptyArray[Symbol.iterator]());
5379
+ } else {
5380
+ const stream = this.#parent.fetch({
5381
+ ...req,
5382
+ constraint: {
5383
+ ...req.constraint,
5384
+ ...constraintFromChild
5385
+ }
5386
+ });
5387
+ const iterator = stream[Symbol.iterator]();
5388
+ parentIterators.push(iterator);
5389
+ }
5114
5390
  }
5115
5391
  const nextParentNodes = [];
5116
5392
  for (let i = 0; i < parentIterators.length; i++) {
@@ -5156,19 +5432,17 @@ var FlippedJoin = class {
5156
5432
  minParentNode.row,
5157
5433
  this.#parentKey
5158
5434
  )) {
5435
+ const hasInprogressChildChangeBeenPushedForMinParentNode = this.#parent.getSchema().compareRows(
5436
+ minParentNode.row,
5437
+ this.#inprogressChildChange.position
5438
+ ) <= 0;
5159
5439
  if (this.#inprogressChildChange.change.type === "remove") {
5160
- if (this.#parent.getSchema().compareRows(
5161
- minParentNode.row,
5162
- this.#inprogressChildChange.position
5163
- ) <= 0) {
5440
+ if (hasInprogressChildChangeBeenPushedForMinParentNode) {
5164
5441
  overlaidRelatedChildNodes = relatedChildNodes.filter(
5165
5442
  (n) => n !== this.#inprogressChildChange?.change.node
5166
5443
  );
5167
5444
  }
5168
- } else if (this.#parent.getSchema().compareRows(
5169
- minParentNode.row,
5170
- this.#inprogressChildChange.position
5171
- ) > 0) {
5445
+ } else if (!hasInprogressChildChangeBeenPushedForMinParentNode) {
5172
5446
  overlaidRelatedChildNodes = [
5173
5447
  ...generateWithOverlay(
5174
5448
  relatedChildNodes,
@@ -5247,31 +5521,37 @@ var FlippedJoin = class {
5247
5521
  }
5248
5522
  }
5249
5523
  if (exists) {
5250
- this.#output.push({
5251
- type: "child",
5252
- node: {
5253
- ...parentNode,
5254
- relationships: {
5255
- ...parentNode.relationships,
5256
- [this.#relationshipName]: childNodeStream
5524
+ this.#output.push(
5525
+ {
5526
+ type: "child",
5527
+ node: {
5528
+ ...parentNode,
5529
+ relationships: {
5530
+ ...parentNode.relationships,
5531
+ [this.#relationshipName]: childNodeStream
5532
+ }
5533
+ },
5534
+ child: {
5535
+ relationshipName: this.#relationshipName,
5536
+ change
5257
5537
  }
5258
5538
  },
5259
- child: {
5260
- relationshipName: this.#relationshipName,
5261
- change
5262
- }
5263
- });
5539
+ this
5540
+ );
5264
5541
  } else {
5265
- this.#output.push({
5266
- ...change,
5267
- node: {
5268
- ...parentNode,
5269
- relationships: {
5270
- ...parentNode.relationships,
5271
- [this.#relationshipName]: () => [change.node]
5542
+ this.#output.push(
5543
+ {
5544
+ ...change,
5545
+ node: {
5546
+ ...parentNode,
5547
+ relationships: {
5548
+ ...parentNode.relationships,
5549
+ [this.#relationshipName]: () => [change.node]
5550
+ }
5272
5551
  }
5273
- }
5274
- });
5552
+ },
5553
+ this
5554
+ );
5275
5555
  }
5276
5556
  }
5277
5557
  } finally {
@@ -5313,22 +5593,23 @@ var FlippedJoin = class {
5313
5593
  [this.#relationshipName]: childNodeStream(node)
5314
5594
  }
5315
5595
  });
5596
+ if (first(childNodeStream(change.node)()) === void 0) {
5597
+ return;
5598
+ }
5316
5599
  switch (change.type) {
5317
5600
  case "add":
5318
5601
  case "remove":
5319
5602
  case "child": {
5320
- if (first(childNodeStream(change.node)()) === void 0) {
5321
- return;
5322
- }
5323
- this.#output.push({
5324
- ...change,
5325
- node: flip(change.node)
5326
- });
5603
+ this.#output.push(
5604
+ {
5605
+ ...change,
5606
+ node: flip(change.node)
5607
+ },
5608
+ this
5609
+ );
5327
5610
  break;
5328
5611
  }
5329
5612
  case "edit": {
5330
- const oldHasChild = first(childNodeStream(change.oldNode)()) !== void 0;
5331
- const hasChild = first(childNodeStream(change.node)()) !== void 0;
5332
5613
  assert(
5333
5614
  rowEqualsForCompoundKey(
5334
5615
  change.oldNode.row,
@@ -5337,26 +5618,14 @@ var FlippedJoin = class {
5337
5618
  ),
5338
5619
  `Parent edit must not change relationship.`
5339
5620
  );
5340
- if (oldHasChild && hasChild) {
5341
- this.#output.push({
5621
+ this.#output.push(
5622
+ {
5342
5623
  type: "edit",
5343
5624
  oldNode: flip(change.oldNode),
5344
5625
  node: flip(change.node)
5345
- });
5346
- break;
5347
- }
5348
- if (oldHasChild) {
5349
- this.#output.push({
5350
- type: "remove",
5351
- node: flip(change.node)
5352
- });
5353
- }
5354
- if (hasChild) {
5355
- this.#output.push({
5356
- type: "add",
5357
- node: flip(change.node)
5358
- });
5359
- }
5626
+ },
5627
+ this
5628
+ );
5360
5629
  break;
5361
5630
  }
5362
5631
  default:
@@ -5448,35 +5717,44 @@ var Join = class {
5448
5717
  #pushParent(change) {
5449
5718
  switch (change.type) {
5450
5719
  case "add":
5451
- this.#output.push({
5452
- type: "add",
5453
- node: this.#processParentNode(
5454
- change.node.row,
5455
- change.node.relationships,
5456
- "fetch"
5457
- )
5458
- });
5720
+ this.#output.push(
5721
+ {
5722
+ type: "add",
5723
+ node: this.#processParentNode(
5724
+ change.node.row,
5725
+ change.node.relationships,
5726
+ "fetch"
5727
+ )
5728
+ },
5729
+ this
5730
+ );
5459
5731
  break;
5460
5732
  case "remove":
5461
- this.#output.push({
5462
- type: "remove",
5463
- node: this.#processParentNode(
5464
- change.node.row,
5465
- change.node.relationships,
5466
- "cleanup"
5467
- )
5468
- });
5733
+ this.#output.push(
5734
+ {
5735
+ type: "remove",
5736
+ node: this.#processParentNode(
5737
+ change.node.row,
5738
+ change.node.relationships,
5739
+ "cleanup"
5740
+ )
5741
+ },
5742
+ this
5743
+ );
5469
5744
  break;
5470
5745
  case "child":
5471
- this.#output.push({
5472
- type: "child",
5473
- node: this.#processParentNode(
5474
- change.node.row,
5475
- change.node.relationships,
5476
- "fetch"
5477
- ),
5478
- child: change.child
5479
- });
5746
+ this.#output.push(
5747
+ {
5748
+ type: "child",
5749
+ node: this.#processParentNode(
5750
+ change.node.row,
5751
+ change.node.relationships,
5752
+ "fetch"
5753
+ ),
5754
+ child: change.child
5755
+ },
5756
+ this
5757
+ );
5480
5758
  break;
5481
5759
  case "edit": {
5482
5760
  assert(
@@ -5487,19 +5765,22 @@ var Join = class {
5487
5765
  ),
5488
5766
  `Parent edit must not change relationship.`
5489
5767
  );
5490
- this.#output.push({
5491
- type: "edit",
5492
- oldNode: this.#processParentNode(
5493
- change.oldNode.row,
5494
- change.oldNode.relationships,
5495
- "cleanup"
5496
- ),
5497
- node: this.#processParentNode(
5498
- change.node.row,
5499
- change.node.relationships,
5500
- "fetch"
5501
- )
5502
- });
5768
+ this.#output.push(
5769
+ {
5770
+ type: "edit",
5771
+ oldNode: this.#processParentNode(
5772
+ change.oldNode.row,
5773
+ change.oldNode.relationships,
5774
+ "cleanup"
5775
+ ),
5776
+ node: this.#processParentNode(
5777
+ change.node.row,
5778
+ change.node.relationships,
5779
+ "fetch"
5780
+ )
5781
+ },
5782
+ this
5783
+ );
5503
5784
  break;
5504
5785
  }
5505
5786
  default:
@@ -5532,7 +5813,7 @@ var Join = class {
5532
5813
  change: change2
5533
5814
  }
5534
5815
  };
5535
- this.#output.push(childChange);
5816
+ this.#output.push(childChange, this);
5536
5817
  }
5537
5818
  } finally {
5538
5819
  this.#inprogressChildChange = void 0;
@@ -5695,12 +5976,12 @@ var Skip = class {
5695
5976
  push(change) {
5696
5977
  const shouldBePresent = (row) => this.#shouldBePresent(row);
5697
5978
  if (change.type === "edit") {
5698
- maybeSplitAndPushEditChange(change, shouldBePresent, this.#output);
5979
+ maybeSplitAndPushEditChange(change, shouldBePresent, this.#output, this);
5699
5980
  return;
5700
5981
  }
5701
5982
  change;
5702
5983
  if (shouldBePresent(change.node.row)) {
5703
- this.#output.push(change);
5984
+ this.#output.push(change, this);
5704
5985
  }
5705
5986
  }
5706
5987
  #getStart(req) {
@@ -5900,7 +6181,7 @@ var Take = class {
5900
6181
  takeState.bound === void 0 || compareRows(takeState.bound, change.node.row) < 0 ? change.node.row : takeState.bound,
5901
6182
  maxBound
5902
6183
  );
5903
- this.#output.push(change);
6184
+ this.#output.push(change, this);
5904
6185
  return;
5905
6186
  }
5906
6187
  if (takeState.bound === void 0 || compareRows(change.node.row, takeState.bound) >= 0) {
@@ -5944,9 +6225,9 @@ var Take = class {
5944
6225
  maxBound
5945
6226
  );
5946
6227
  this.#withRowHiddenFromFetch(change.node.row, () => {
5947
- this.#output.push(removeChange);
6228
+ this.#output.push(removeChange, this);
5948
6229
  });
5949
- this.#output.push(change);
6230
+ this.#output.push(change, this);
5950
6231
  } else if (change.type === "remove") {
5951
6232
  if (takeState.bound === void 0) {
5952
6233
  return;
@@ -5993,17 +6274,20 @@ var Take = class {
5993
6274
  }
5994
6275
  }
5995
6276
  if (newBound?.push) {
5996
- this.#output.push(change);
6277
+ this.#output.push(change, this);
5997
6278
  this.#setTakeState(
5998
6279
  takeStateKey,
5999
6280
  takeState.size,
6000
6281
  newBound.node.row,
6001
6282
  maxBound
6002
6283
  );
6003
- this.#output.push({
6004
- type: "add",
6005
- node: newBound.node
6006
- });
6284
+ this.#output.push(
6285
+ {
6286
+ type: "add",
6287
+ node: newBound.node
6288
+ },
6289
+ this
6290
+ );
6007
6291
  return;
6008
6292
  }
6009
6293
  this.#setTakeState(
@@ -6012,10 +6296,10 @@ var Take = class {
6012
6296
  newBound?.node.row,
6013
6297
  maxBound
6014
6298
  );
6015
- this.#output.push(change);
6299
+ this.#output.push(change, this);
6016
6300
  } else if (change.type === "child") {
6017
6301
  if (takeState.bound && compareRows(change.node.row, takeState.bound) <= 0) {
6018
- this.#output.push(change);
6302
+ this.#output.push(change, this);
6019
6303
  }
6020
6304
  }
6021
6305
  }
@@ -6039,11 +6323,11 @@ var Take = class {
6039
6323
  change.node.row,
6040
6324
  maxBound
6041
6325
  );
6042
- this.#output.push(change);
6326
+ this.#output.push(change, this);
6043
6327
  };
6044
6328
  if (oldCmp === 0) {
6045
6329
  if (newCmp === 0) {
6046
- this.#output.push(change);
6330
+ this.#output.push(change, this);
6047
6331
  return;
6048
6332
  }
6049
6333
  if (newCmp < 0) {
@@ -6069,7 +6353,7 @@ var Take = class {
6069
6353
  beforeBoundNode.row,
6070
6354
  maxBound
6071
6355
  );
6072
- this.#output.push(change);
6356
+ this.#output.push(change, this);
6073
6357
  return;
6074
6358
  }
6075
6359
  assert(newCmp > 0);
@@ -6095,15 +6379,21 @@ var Take = class {
6095
6379
  maxBound
6096
6380
  );
6097
6381
  this.#withRowHiddenFromFetch(newBoundNode.row, () => {
6098
- this.#output.push({
6099
- type: "remove",
6100
- node: change.oldNode
6101
- });
6102
- });
6103
- this.#output.push({
6104
- type: "add",
6105
- node: newBoundNode
6382
+ this.#output.push(
6383
+ {
6384
+ type: "remove",
6385
+ node: change.oldNode
6386
+ },
6387
+ this
6388
+ );
6106
6389
  });
6390
+ this.#output.push(
6391
+ {
6392
+ type: "add",
6393
+ node: newBoundNode
6394
+ },
6395
+ this
6396
+ );
6107
6397
  return;
6108
6398
  }
6109
6399
  if (oldCmp > 0) {
@@ -6130,21 +6420,27 @@ var Take = class {
6130
6420
  maxBound
6131
6421
  );
6132
6422
  this.#withRowHiddenFromFetch(change.node.row, () => {
6133
- this.#output.push({
6134
- type: "remove",
6135
- node: oldBoundNode
6136
- });
6137
- });
6138
- this.#output.push({
6139
- type: "add",
6140
- node: change.node
6423
+ this.#output.push(
6424
+ {
6425
+ type: "remove",
6426
+ node: oldBoundNode
6427
+ },
6428
+ this
6429
+ );
6141
6430
  });
6431
+ this.#output.push(
6432
+ {
6433
+ type: "add",
6434
+ node: change.node
6435
+ },
6436
+ this
6437
+ );
6142
6438
  return;
6143
6439
  }
6144
6440
  if (oldCmp < 0) {
6145
6441
  assert(newCmp !== 0, "Invalid state. Row has duplicate primary key");
6146
6442
  if (newCmp < 0) {
6147
- this.#output.push(change);
6443
+ this.#output.push(change, this);
6148
6444
  return;
6149
6445
  }
6150
6446
  assert(newCmp > 0);
@@ -6163,20 +6459,26 @@ var Take = class {
6163
6459
  replaceBoundAndForwardChange();
6164
6460
  return;
6165
6461
  }
6166
- this.#output.push({
6167
- type: "remove",
6168
- node: change.oldNode
6169
- });
6462
+ this.#output.push(
6463
+ {
6464
+ type: "remove",
6465
+ node: change.oldNode
6466
+ },
6467
+ this
6468
+ );
6170
6469
  this.#setTakeState(
6171
6470
  takeStateKey,
6172
6471
  takeState.size,
6173
6472
  afterBoundNode.row,
6174
6473
  maxBound
6175
6474
  );
6176
- this.#output.push({
6177
- type: "add",
6178
- node: afterBoundNode
6179
- });
6475
+ this.#output.push(
6476
+ {
6477
+ type: "add",
6478
+ node: afterBoundNode
6479
+ },
6480
+ this
6481
+ );
6180
6482
  return;
6181
6483
  }
6182
6484
  unreachable();
@@ -6237,6 +6539,212 @@ function makePartitionKeyComparator(partitionKey) {
6237
6539
  };
6238
6540
  }
6239
6541
 
6542
+ // ../zql/src/ivm/union-fan-in.ts
6543
+ var UnionFanIn = class {
6544
+ #inputs;
6545
+ #schema;
6546
+ #fanOutPushStarted = false;
6547
+ #output = throwOutput;
6548
+ #accumulatedPushes = [];
6549
+ constructor(fanOut, inputs) {
6550
+ this.#inputs = inputs;
6551
+ const fanOutSchema = fanOut.getSchema();
6552
+ fanOut.setFanIn(this);
6553
+ const schema = {
6554
+ tableName: fanOutSchema.tableName,
6555
+ columns: fanOutSchema.columns,
6556
+ primaryKey: fanOutSchema.primaryKey,
6557
+ relationships: {
6558
+ ...fanOutSchema.relationships
6559
+ },
6560
+ isHidden: fanOutSchema.isHidden,
6561
+ system: fanOutSchema.system,
6562
+ compareRows: fanOutSchema.compareRows,
6563
+ sort: fanOutSchema.sort
6564
+ };
6565
+ const relationshipsFromBranches = /* @__PURE__ */ new Set();
6566
+ for (const input of inputs) {
6567
+ const inputSchema = input.getSchema();
6568
+ assert(
6569
+ schema.tableName === inputSchema.tableName,
6570
+ `Table name mismatch in union fan-in: ${schema.tableName} !== ${inputSchema.tableName}`
6571
+ );
6572
+ assert(
6573
+ schema.primaryKey === inputSchema.primaryKey,
6574
+ `Primary key mismatch in union fan-in`
6575
+ );
6576
+ assert(
6577
+ schema.system === inputSchema.system,
6578
+ `System mismatch in union fan-in: ${schema.system} !== ${inputSchema.system}`
6579
+ );
6580
+ assert(
6581
+ schema.compareRows === inputSchema.compareRows,
6582
+ `compareRows mismatch in union fan-in`
6583
+ );
6584
+ assert(schema.sort === inputSchema.sort, `Sort mismatch in union fan-in`);
6585
+ for (const [relName, relSchema] of Object.entries(
6586
+ inputSchema.relationships
6587
+ )) {
6588
+ if (relName in fanOutSchema.relationships) {
6589
+ continue;
6590
+ }
6591
+ assert(
6592
+ !relationshipsFromBranches.has(relName),
6593
+ `Relationship ${relName} exists in multiple upstream inputs to union fan-in`
6594
+ );
6595
+ schema.relationships[relName] = relSchema;
6596
+ relationshipsFromBranches.add(relName);
6597
+ }
6598
+ input.setOutput(this);
6599
+ }
6600
+ this.#schema = schema;
6601
+ this.#inputs = inputs;
6602
+ }
6603
+ cleanup(_req) {
6604
+ return [];
6605
+ }
6606
+ destroy() {
6607
+ for (const input of this.#inputs) {
6608
+ input.destroy();
6609
+ }
6610
+ }
6611
+ fetch(req) {
6612
+ const iterables = this.#inputs.map((input) => input.fetch(req));
6613
+ return mergeIterables(
6614
+ iterables,
6615
+ (l, r) => this.#schema.compareRows(l.row, r.row),
6616
+ true
6617
+ );
6618
+ }
6619
+ getSchema() {
6620
+ return this.#schema;
6621
+ }
6622
+ push(change, pusher) {
6623
+ if (!this.#fanOutPushStarted) {
6624
+ this.#pushInternalChange(change, pusher);
6625
+ } else {
6626
+ this.#accumulatedPushes.push(change);
6627
+ }
6628
+ }
6629
+ /**
6630
+ * An internal change means that a change was received inside the fan-out/fan-in sub-graph.
6631
+ *
6632
+ * These changes always come from children of a flip-join as no other push generating operators
6633
+ * currently exist between union-fan-in and union-fan-out. All other pushes
6634
+ * enter into union-fan-out before reaching union-fan-in.
6635
+ *
6636
+ * - normal joins for `exists` come before `union-fan-out`
6637
+ * - joins for `related` come after `union-fan-out`
6638
+ * - take comes after `union-fan-out`
6639
+ *
6640
+ * The algorithm for deciding whether or not to forward a push that came from inside the ufo/ufi sub-graph:
6641
+ * 1. If the change is a `child` change we can forward it. This is because all child branches in the ufo/ufi sub-graph are unique.
6642
+ * 2. If the change is `add` we can forward it iff no `fetches` for the row return any results.
6643
+ * If another branch has it, the add was already emitted in the past.
6644
+ * 3. If the change is `remove` we can forward it iff no `fetches` for the row return any results.
6645
+ * If no other branches have the change, the remove can be sent as the value is no longer present.
6646
+ * If other branches have it, the last branch the processes the remove will send the remove.
6647
+ * 4. Edits will always come through as child changes as flip join will flip them into children.
6648
+ * An edit that would result in a remove or add will have been split into an add/remove pair rather than being an edit.
6649
+ */
6650
+ #pushInternalChange(change, pusher) {
6651
+ if (change.type === "child") {
6652
+ this.#output.push(change, this);
6653
+ return;
6654
+ }
6655
+ assert(change.type === "add" || change.type === "remove");
6656
+ let hadMatch = false;
6657
+ for (const input of this.#inputs) {
6658
+ if (input === pusher) {
6659
+ hadMatch = true;
6660
+ continue;
6661
+ }
6662
+ const constraint = {};
6663
+ for (const key of this.#schema.primaryKey) {
6664
+ constraint[key] = change.node.row[key];
6665
+ }
6666
+ const fetchResult = input.fetch({
6667
+ constraint
6668
+ });
6669
+ if (first(fetchResult) !== void 0) {
6670
+ return;
6671
+ }
6672
+ }
6673
+ assert(hadMatch, "Pusher was not one of the inputs to union-fan-in!");
6674
+ this.#output.push(change, this);
6675
+ }
6676
+ fanOutStartedPushing() {
6677
+ assert(this.#fanOutPushStarted === false);
6678
+ this.#fanOutPushStarted = true;
6679
+ }
6680
+ fanOutDonePushing(fanOutChangeType) {
6681
+ assert(this.#fanOutPushStarted);
6682
+ this.#fanOutPushStarted = false;
6683
+ if (this.#inputs.length === 0) {
6684
+ return;
6685
+ }
6686
+ if (this.#accumulatedPushes.length === 0) {
6687
+ return;
6688
+ }
6689
+ pushAccumulatedChanges(
6690
+ this.#accumulatedPushes,
6691
+ this.#output,
6692
+ this,
6693
+ fanOutChangeType,
6694
+ mergeRelationships,
6695
+ makeAddEmptyRelationships(this.#schema)
6696
+ );
6697
+ }
6698
+ setOutput(output) {
6699
+ this.#output = output;
6700
+ }
6701
+ };
6702
+
6703
+ // ../zql/src/ivm/union-fan-out.ts
6704
+ var UnionFanOut = class {
6705
+ #destroyCount = 0;
6706
+ #unionFanIn;
6707
+ #input;
6708
+ #outputs = [];
6709
+ constructor(input) {
6710
+ this.#input = input;
6711
+ input.setOutput(this);
6712
+ }
6713
+ setFanIn(fanIn) {
6714
+ assert(!this.#unionFanIn, "FanIn already set for this FanOut");
6715
+ this.#unionFanIn = fanIn;
6716
+ }
6717
+ push(change) {
6718
+ must(this.#unionFanIn).fanOutStartedPushing();
6719
+ for (const output of this.#outputs) {
6720
+ output.push(change, this);
6721
+ }
6722
+ must(this.#unionFanIn).fanOutDonePushing(change.type);
6723
+ }
6724
+ setOutput(output) {
6725
+ this.#outputs.push(output);
6726
+ }
6727
+ getSchema() {
6728
+ return this.#input.getSchema();
6729
+ }
6730
+ fetch(req) {
6731
+ return this.#input.fetch(req);
6732
+ }
6733
+ cleanup(_req) {
6734
+ return [];
6735
+ }
6736
+ destroy() {
6737
+ if (this.#destroyCount < this.#outputs.length) {
6738
+ ++this.#destroyCount;
6739
+ if (this.#destroyCount === this.#outputs.length) {
6740
+ this.#input.destroy();
6741
+ }
6742
+ } else {
6743
+ throw new Error("FanOut already destroyed once for each output");
6744
+ }
6745
+ }
6746
+ };
6747
+
6240
6748
  // ../zql/src/query/expression.ts
6241
6749
  var ExpressionBuilder = class {
6242
6750
  #exists;
@@ -6602,27 +7110,23 @@ function transformFilters(filters) {
6602
7110
 
6603
7111
  // ../zql/src/builder/builder.ts
6604
7112
  function buildPipeline(ast, delegate, queryID) {
6605
- return buildPipelineInternal(
6606
- delegate.mapAst ? delegate.mapAst(ast) : ast,
6607
- delegate,
6608
- queryID,
6609
- ""
6610
- );
7113
+ ast = delegate.mapAst ? delegate.mapAst(ast) : ast;
7114
+ return buildPipelineInternal(ast, delegate, queryID, "");
6611
7115
  }
7116
+ var EXISTS_LIMIT = 3;
7117
+ var PERMISSIONS_EXISTS_LIMIT = 1;
6612
7118
  function buildPipelineInternal(ast, delegate, queryID, name, partitionKey) {
6613
7119
  const source = delegate.getSource(ast.table);
6614
7120
  if (!source) {
6615
7121
  throw new Error(`Source not found: ${ast.table}`);
6616
7122
  }
6617
7123
  ast = uniquifyCorrelatedSubqueryConditionAliases(ast);
6618
- const csqsFromCondition = gatherCorrelatedSubqueryQueriesFromCondition(
6619
- ast.where
6620
- );
7124
+ const csqConditions = gatherCorrelatedSubqueryQueryConditions(ast.where);
6621
7125
  const splitEditKeys = partitionKey ? new Set(partitionKey) : /* @__PURE__ */ new Set();
6622
7126
  const aliases = /* @__PURE__ */ new Set();
6623
- for (const csq of csqsFromCondition) {
6624
- aliases.add(csq.subquery.alias || "");
6625
- for (const key of csq.correlation.parentField) {
7127
+ for (const csq of csqConditions) {
7128
+ aliases.add(csq.related.subquery.alias || "");
7129
+ for (const key of csq.related.correlation.parentField) {
6626
7130
  splitEditKeys.add(key);
6627
7131
  }
6628
7132
  }
@@ -6647,8 +7151,23 @@ function buildPipelineInternal(ast, delegate, queryID, name, partitionKey) {
6647
7151
  delegate.addEdge(end, skip);
6648
7152
  end = delegate.decorateInput(skip, `${name}:skip)`);
6649
7153
  }
6650
- for (const csq of csqsFromCondition) {
6651
- end = applyCorrelatedSubQuery(csq, delegate, queryID, end, name, true);
7154
+ for (const csqCondition of csqConditions) {
7155
+ if (!csqCondition.flip) {
7156
+ end = applyCorrelatedSubQuery(
7157
+ {
7158
+ ...csqCondition.related,
7159
+ subquery: {
7160
+ ...csqCondition.related.subquery,
7161
+ limit: csqCondition.related.system === "permissions" ? PERMISSIONS_EXISTS_LIMIT : EXISTS_LIMIT
7162
+ }
7163
+ },
7164
+ delegate,
7165
+ queryID,
7166
+ end,
7167
+ name,
7168
+ true
7169
+ );
7170
+ }
6652
7171
  }
6653
7172
  if (ast.where && (!fullyAppliedFilters || delegate.applyFiltersAnyway)) {
6654
7173
  end = applyWhere(end, ast.where, delegate, name);
@@ -6672,11 +7191,113 @@ function buildPipelineInternal(ast, delegate, queryID, name, partitionKey) {
6672
7191
  return end;
6673
7192
  }
6674
7193
  function applyWhere(input, condition, delegate, name) {
6675
- return buildFilterPipeline(
6676
- input,
6677
- delegate,
6678
- (filterInput) => applyFilter(filterInput, condition, delegate, name)
6679
- );
7194
+ if (!conditionIncludesFlippedSubqueryAtAnyLevel(condition)) {
7195
+ return buildFilterPipeline(
7196
+ input,
7197
+ delegate,
7198
+ (filterInput) => applyFilter(filterInput, condition, delegate, name)
7199
+ );
7200
+ }
7201
+ return applyFilterWithFlips(input, condition, delegate, name);
7202
+ }
7203
+ function applyFilterWithFlips(input, condition, delegate, name) {
7204
+ let end = input;
7205
+ assert(condition.type !== "simple", "Simple conditions cannot have flips");
7206
+ switch (condition.type) {
7207
+ case "and": {
7208
+ const [withFlipped, withoutFlipped] = partitionBranches(
7209
+ condition.conditions,
7210
+ conditionIncludesFlippedSubqueryAtAnyLevel
7211
+ );
7212
+ if (withoutFlipped.length > 0) {
7213
+ end = buildFilterPipeline(
7214
+ input,
7215
+ delegate,
7216
+ (filterInput) => applyAnd(
7217
+ filterInput,
7218
+ {
7219
+ type: "and",
7220
+ conditions: withoutFlipped
7221
+ },
7222
+ delegate,
7223
+ name
7224
+ )
7225
+ );
7226
+ }
7227
+ assert(withFlipped.length > 0, "Impossible to have no flips here");
7228
+ for (const cond of withFlipped) {
7229
+ end = applyFilterWithFlips(end, cond, delegate, name);
7230
+ }
7231
+ break;
7232
+ }
7233
+ case "or": {
7234
+ const [withFlipped, withoutFlipped] = partitionBranches(
7235
+ condition.conditions,
7236
+ conditionIncludesFlippedSubqueryAtAnyLevel
7237
+ );
7238
+ assert(withFlipped.length > 0, "Impossible to have no flips here");
7239
+ const ufo = new UnionFanOut(end);
7240
+ delegate.addEdge(end, ufo);
7241
+ end = delegate.decorateInput(ufo, `${name}:ufo`);
7242
+ const branches = [];
7243
+ if (withoutFlipped.length > 0) {
7244
+ branches.push(
7245
+ buildFilterPipeline(
7246
+ end,
7247
+ delegate,
7248
+ (filterInput) => applyOr(
7249
+ filterInput,
7250
+ {
7251
+ type: "or",
7252
+ conditions: withoutFlipped
7253
+ },
7254
+ delegate,
7255
+ name
7256
+ )
7257
+ )
7258
+ );
7259
+ }
7260
+ for (const cond of withFlipped) {
7261
+ branches.push(applyFilterWithFlips(end, cond, delegate, name));
7262
+ }
7263
+ const ufi = new UnionFanIn(ufo, branches);
7264
+ for (const branch of branches) {
7265
+ delegate.addEdge(branch, ufi);
7266
+ }
7267
+ end = delegate.decorateInput(ufi, `${name}:ufi`);
7268
+ break;
7269
+ }
7270
+ case "correlatedSubquery": {
7271
+ const sq = condition.related;
7272
+ const child = buildPipelineInternal(
7273
+ sq.subquery,
7274
+ delegate,
7275
+ "",
7276
+ `${name}.${sq.subquery.alias}`,
7277
+ sq.correlation.childField
7278
+ );
7279
+ const flippedJoin = new FlippedJoin({
7280
+ parent: end,
7281
+ child,
7282
+ parentKey: sq.correlation.parentField,
7283
+ childKey: sq.correlation.childField,
7284
+ relationshipName: must(
7285
+ sq.subquery.alias,
7286
+ "Subquery must have an alias"
7287
+ ),
7288
+ hidden: sq.hidden ?? false,
7289
+ system: sq.system ?? "client"
7290
+ });
7291
+ delegate.addEdge(end, flippedJoin);
7292
+ delegate.addEdge(child, flippedJoin);
7293
+ end = delegate.decorateInput(
7294
+ flippedJoin,
7295
+ `${name}:flipped-join(${sq.subquery.alias})`
7296
+ );
7297
+ break;
7298
+ }
7299
+ }
7300
+ return end;
6680
7301
  }
6681
7302
  function applyFilter(input, condition, delegate, name) {
6682
7303
  switch (condition.type) {
@@ -6783,16 +7404,8 @@ function applyCorrelatedSubQuery(sq, delegate, queryID, end, name, fromCondition
6783
7404
  `${name}.${sq.subquery.alias}`,
6784
7405
  sq.correlation.childField
6785
7406
  );
6786
- const joinName = `${name}:${sq.flip ? "flipped-join" : "join"}(${sq.subquery.alias})`;
6787
- const join = sq.flip ? new FlippedJoin({
6788
- parent: end,
6789
- child,
6790
- parentKey: sq.correlation.parentField,
6791
- childKey: sq.correlation.childField,
6792
- relationshipName: sq.subquery.alias,
6793
- hidden: sq.hidden ?? false,
6794
- system: sq.system ?? "client"
6795
- }) : new Join({
7407
+ const joinName = `${name}:join(${sq.subquery.alias})`;
7408
+ const join = new Join({
6796
7409
  parent: end,
6797
7410
  child,
6798
7411
  storage: delegate.createStorage(joinName),
@@ -6829,18 +7442,11 @@ function applyCorrelatedSubqueryCondition(input, condition, delegate, name) {
6829
7442
  delegate.addEdge(input, exists);
6830
7443
  return delegate.decorateFilterInput(exists, existsName);
6831
7444
  }
6832
- function gatherCorrelatedSubqueryQueriesFromCondition(condition) {
7445
+ function gatherCorrelatedSubqueryQueryConditions(condition) {
6833
7446
  const csqs = [];
6834
7447
  const gather = (condition2) => {
6835
7448
  if (condition2.type === "correlatedSubquery") {
6836
- assert(condition2.op === "EXISTS" || condition2.op === "NOT EXISTS");
6837
- csqs.push({
6838
- ...condition2.related,
6839
- subquery: {
6840
- ...condition2.related.subquery,
6841
- limit: condition2.related.system === "permissions" ? PERMISSIONS_EXISTS_LIMIT : EXISTS_LIMIT
6842
- }
6843
- });
7449
+ csqs.push(condition2);
6844
7450
  return;
6845
7451
  }
6846
7452
  if (condition2.type === "and" || condition2.type === "or") {
@@ -6855,8 +7461,6 @@ function gatherCorrelatedSubqueryQueriesFromCondition(condition) {
6855
7461
  }
6856
7462
  return csqs;
6857
7463
  }
6858
- var EXISTS_LIMIT = 3;
6859
- var PERMISSIONS_EXISTS_LIMIT = 1;
6860
7464
  function assertOrderingIncludesPK(ordering, pk) {
6861
7465
  const orderingFields = ordering.map(([field]) => field);
6862
7466
  const missingFields = pk.filter((pkField) => !orderingFields.includes(pkField));
@@ -6912,6 +7516,29 @@ function uniquifyCorrelatedSubqueryConditionAliases(ast) {
6912
7516
  };
6913
7517
  return result;
6914
7518
  }
7519
+ function conditionIncludesFlippedSubqueryAtAnyLevel(cond) {
7520
+ if (cond.type === "correlatedSubquery") {
7521
+ return !!cond.flip;
7522
+ }
7523
+ if (cond.type === "and" || cond.type === "or") {
7524
+ return cond.conditions.some(
7525
+ (c) => conditionIncludesFlippedSubqueryAtAnyLevel(c)
7526
+ );
7527
+ }
7528
+ return false;
7529
+ }
7530
+ function partitionBranches(conditions, predicate) {
7531
+ const matched = [];
7532
+ const notMatched = [];
7533
+ for (const c of conditions) {
7534
+ if (predicate(c)) {
7535
+ matched.push(c);
7536
+ } else {
7537
+ notMatched.push(c);
7538
+ }
7539
+ }
7540
+ return [matched, notMatched];
7541
+ }
6915
7542
 
6916
7543
  // ../zql/src/error.ts
6917
7544
  var NotImplementedError = class extends Error {
@@ -7415,10 +8042,10 @@ var AbstractQuery = class {
7415
8042
  subquery: addPrimaryKeysToAst(
7416
8043
  this.#schema.tables[destSchema],
7417
8044
  sq._ast
7418
- ),
7419
- flip
8045
+ )
7420
8046
  },
7421
- op: "EXISTS"
8047
+ op: "EXISTS",
8048
+ flip
7422
8049
  };
7423
8050
  }
7424
8051
  if (isTwoHop(related)) {
@@ -7451,7 +8078,6 @@ var AbstractQuery = class {
7451
8078
  parentField: firstRelation.sourceField,
7452
8079
  childField: firstRelation.destField
7453
8080
  },
7454
- flip,
7455
8081
  subquery: {
7456
8082
  table: junctionSchema,
7457
8083
  alias: `${SUBQ_PREFIX}${relationship}`,
@@ -7470,14 +8096,15 @@ var AbstractQuery = class {
7470
8096
  subquery: addPrimaryKeysToAst(
7471
8097
  this.#schema.tables[destSchema],
7472
8098
  queryToDest._ast
7473
- ),
7474
- flip
8099
+ )
7475
8100
  },
7476
- op: "EXISTS"
8101
+ op: "EXISTS",
8102
+ flip
7477
8103
  }
7478
8104
  }
7479
8105
  },
7480
- op: "EXISTS"
8106
+ op: "EXISTS",
8107
+ flip
7481
8108
  };
7482
8109
  }
7483
8110
  throw new Error(`Invalid relationship ${relationship}`);
@@ -7817,6 +8444,9 @@ export {
7817
8444
  emptyFunction,
7818
8445
  emptyObject,
7819
8446
  filterPush,
8447
+ constraintMatchesRow,
8448
+ constraintMatchesPrimaryKey,
8449
+ primaryKeyConstraintFromFilters,
7820
8450
  ExpressionBuilder,
7821
8451
  createPredicate,
7822
8452
  transformFilters,
@@ -7858,4 +8488,4 @@ export {
7858
8488
  TDigest,
7859
8489
  Inspector
7860
8490
  };
7861
- //# sourceMappingURL=chunk-QZPMFA73.js.map
8491
+ //# sourceMappingURL=chunk-YXYKEMHQ.js.map