@rocicorp/zero 0.17.2025030701 → 0.17.2025031100

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 (193) hide show
  1. package/out/advanced.js +1 -1
  2. package/out/{chunk-YKI43S5O.js → chunk-2TJNN7K4.js} +507 -211
  3. package/out/chunk-2TJNN7K4.js.map +7 -0
  4. package/out/{chunk-6XQK3UTJ.js → chunk-6CQ6CUML.js} +4 -5
  5. package/out/chunk-6CQ6CUML.js.map +7 -0
  6. package/out/{chunk-HARIWJ2J.js → chunk-HLTH2AYN.js} +6 -2
  7. package/out/chunk-HLTH2AYN.js.map +7 -0
  8. package/out/otel/src/log-options.d.ts +4 -0
  9. package/out/otel/src/log-options.d.ts.map +1 -1
  10. package/out/otel/src/log-options.js +6 -0
  11. package/out/otel/src/log-options.js.map +1 -1
  12. package/out/otel/src/maybe-time.js +1 -1
  13. package/out/react.js +3 -3
  14. package/out/react.js.map +2 -2
  15. package/out/shared/src/asserts.d.ts.map +1 -1
  16. package/out/shared/src/asserts.js +1 -2
  17. package/out/shared/src/asserts.js.map +1 -1
  18. package/out/shared/src/objects.d.ts +4 -0
  19. package/out/shared/src/objects.d.ts.map +1 -0
  20. package/out/shared/src/objects.js +25 -0
  21. package/out/shared/src/objects.js.map +1 -0
  22. package/out/solid.js +5 -4
  23. package/out/solid.js.map +2 -2
  24. package/out/z2s/src/compiler.d.ts.map +1 -1
  25. package/out/z2s/src/compiler.js +1 -1
  26. package/out/z2s/src/compiler.js.map +1 -1
  27. package/out/zero-cache/src/auth/load-permissions.d.ts.map +1 -1
  28. package/out/zero-cache/src/auth/load-permissions.js +0 -1
  29. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  30. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  31. package/out/zero-cache/src/auth/write-authorizer.js +1 -0
  32. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  33. package/out/zero-cache/src/config/zero-config.d.ts +4 -0
  34. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  35. package/out/zero-cache/src/db/lite-tables.d.ts +5 -1
  36. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  37. package/out/zero-cache/src/db/lite-tables.js +8 -1
  38. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  39. package/out/zero-cache/src/db/specs.d.ts +5 -5
  40. package/out/zero-cache/src/scripts/deploy-permissions.js +2 -1
  41. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  42. package/out/zero-cache/src/server/multi/config.d.ts +4 -0
  43. package/out/zero-cache/src/server/multi/config.d.ts.map +1 -1
  44. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  45. package/out/zero-cache/src/server/syncer.js +1 -1
  46. package/out/zero-cache/src/server/syncer.js.map +1 -1
  47. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +10 -10
  48. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts +4 -4
  49. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +1 -1
  50. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +8 -8
  51. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +12 -12
  52. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +4 -4
  53. package/out/zero-cache/src/services/dispatcher/connect-params.d.ts +1 -1
  54. package/out/zero-cache/src/services/dispatcher/connect-params.d.ts.map +1 -1
  55. package/out/zero-cache/src/services/dispatcher/connect-params.js +1 -1
  56. package/out/zero-cache/src/services/dispatcher/connect-params.js.map +1 -1
  57. package/out/zero-cache/src/services/mutagen/mutagen.d.ts +3 -3
  58. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  59. package/out/zero-cache/src/services/mutagen/mutagen.js +7 -3
  60. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  61. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +1 -1
  62. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  63. package/out/zero-cache/src/services/view-syncer/client-handler.js +4 -2
  64. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  65. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts +5 -0
  66. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +1 -0
  67. package/out/zero-cache/src/services/view-syncer/client-schema.js +74 -0
  68. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -0
  69. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +6 -2
  70. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  71. package/out/zero-cache/src/services/view-syncer/cvr-store.js +38 -15
  72. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  73. package/out/zero-cache/src/services/view-syncer/cvr.d.ts +6 -2
  74. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  75. package/out/zero-cache/src/services/view-syncer/cvr.js +36 -15
  76. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  77. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +5 -4
  78. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  79. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +22 -16
  80. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  81. package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts +2 -0
  82. package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts.map +1 -1
  83. package/out/zero-cache/src/services/view-syncer/schema/cvr.js +2 -1
  84. package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
  85. package/out/zero-cache/src/services/view-syncer/schema/init.d.ts.map +1 -1
  86. package/out/zero-cache/src/services/view-syncer/schema/init.js +6 -0
  87. package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
  88. package/out/zero-cache/src/services/view-syncer/schema/types.d.ts +2 -2
  89. package/out/zero-cache/src/services/view-syncer/schema/types.d.ts.map +1 -1
  90. package/out/zero-cache/src/services/view-syncer/schema/types.js +4 -3
  91. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  92. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  93. package/out/zero-cache/src/services/view-syncer/snapshotter.js +4 -1
  94. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  95. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +2 -2
  96. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  97. package/out/zero-cache/src/services/view-syncer/view-syncer.js +27 -15
  98. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  99. package/out/zero-client/src/client/context.d.ts +10 -4
  100. package/out/zero-client/src/client/context.d.ts.map +1 -1
  101. package/out/zero-client/src/client/options.d.ts +8 -0
  102. package/out/zero-client/src/client/options.d.ts.map +1 -1
  103. package/out/zero-client/src/client/query-manager.d.ts +2 -1
  104. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  105. package/out/zero-client/src/client/zero.d.ts +3 -2
  106. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  107. package/out/zero-client/src/mod.d.ts +2 -2
  108. package/out/zero-client/src/mod.d.ts.map +1 -1
  109. package/out/zero-protocol/src/client-schema.d.ts +27 -0
  110. package/out/zero-protocol/src/client-schema.d.ts.map +1 -0
  111. package/out/zero-protocol/src/client-schema.js +28 -0
  112. package/out/zero-protocol/src/client-schema.js.map +1 -0
  113. package/out/zero-protocol/src/connect.d.ts +14 -0
  114. package/out/zero-protocol/src/connect.d.ts.map +1 -1
  115. package/out/zero-protocol/src/connect.js +2 -0
  116. package/out/zero-protocol/src/connect.js.map +1 -1
  117. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  118. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  119. package/out/zero-protocol/src/protocol-version.js +2 -1
  120. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  121. package/out/zero-protocol/src/push.d.ts +2 -2
  122. package/out/zero-protocol/src/push.d.ts.map +1 -1
  123. package/out/zero-protocol/src/push.js +4 -1
  124. package/out/zero-protocol/src/push.js.map +1 -1
  125. package/out/zero-protocol/src/up.d.ts +8 -1
  126. package/out/zero-protocol/src/up.d.ts.map +1 -1
  127. package/out/zero-react/src/use-query.d.ts +4 -3
  128. package/out/zero-react/src/use-query.d.ts.map +1 -1
  129. package/out/zero-schema/src/builder/schema-builder.d.ts +7 -13
  130. package/out/zero-schema/src/builder/schema-builder.d.ts.map +1 -1
  131. package/out/zero-schema/src/builder/schema-builder.js +22 -13
  132. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  133. package/out/zero-schema/src/schema-config.d.ts +2 -4
  134. package/out/zero-schema/src/schema-config.d.ts.map +1 -1
  135. package/out/zero-schema/src/schema-config.js +1 -2
  136. package/out/zero-schema/src/schema-config.js.map +1 -1
  137. package/out/zero-schema/src/table-schema.d.ts +2 -2
  138. package/out/zero-schema/src/table-schema.d.ts.map +1 -1
  139. package/out/zero-solid/src/use-query.d.ts +2 -2
  140. package/out/zero-solid/src/use-query.d.ts.map +1 -1
  141. package/out/zero.js +5 -3
  142. package/out/zql/src/builder/builder.d.ts +3 -2
  143. package/out/zql/src/builder/builder.d.ts.map +1 -1
  144. package/out/zql/src/builder/builder.js +48 -27
  145. package/out/zql/src/builder/builder.js.map +1 -1
  146. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  147. package/out/zql/src/ivm/exists.js +1 -1
  148. package/out/zql/src/ivm/exists.js.map +1 -1
  149. package/out/zql/src/ivm/fan-in.d.ts +1 -0
  150. package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
  151. package/out/zql/src/ivm/fan-in.js +100 -4
  152. package/out/zql/src/ivm/fan-in.js.map +1 -1
  153. package/out/zql/src/ivm/fan-out.d.ts +2 -1
  154. package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
  155. package/out/zql/src/ivm/fan-out.js +6 -12
  156. package/out/zql/src/ivm/fan-out.js.map +1 -1
  157. package/out/zql/src/ivm/join.d.ts.map +1 -1
  158. package/out/zql/src/ivm/join.js +12 -41
  159. package/out/zql/src/ivm/join.js.map +1 -1
  160. package/out/zql/src/ivm/memory-source.d.ts +17 -3
  161. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  162. package/out/zql/src/ivm/memory-source.js +108 -56
  163. package/out/zql/src/ivm/memory-source.js.map +1 -1
  164. package/out/zql/src/ivm/source.d.ts +4 -1
  165. package/out/zql/src/ivm/source.d.ts.map +1 -1
  166. package/out/zql/src/ivm/take.d.ts.map +1 -1
  167. package/out/zql/src/ivm/take.js +2 -20
  168. package/out/zql/src/ivm/take.js.map +1 -1
  169. package/out/zql/src/ivm/view-apply-change.js +2 -2
  170. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  171. package/out/zql/src/query/query-impl.d.ts +3 -1
  172. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  173. package/out/zql/src/query/query-impl.js +4 -3
  174. package/out/zql/src/query/query-impl.js.map +1 -1
  175. package/out/zql/src/query/query-internal.d.ts +3 -2
  176. package/out/zql/src/query/query-internal.d.ts.map +1 -1
  177. package/out/zql/src/query/query.d.ts +2 -1
  178. package/out/zql/src/query/query.d.ts.map +1 -1
  179. package/out/zql/src/query/query.js.map +1 -1
  180. package/out/zql/src/query/ttl.d.ts +18 -0
  181. package/out/zql/src/query/ttl.d.ts.map +1 -0
  182. package/out/zql/src/query/ttl.js +33 -0
  183. package/out/zql/src/query/ttl.js.map +1 -0
  184. package/out/zqlite/src/db.js +1 -1
  185. package/out/zqlite/src/db.js.map +1 -1
  186. package/out/zqlite/src/table-source.d.ts +1 -1
  187. package/out/zqlite/src/table-source.d.ts.map +1 -1
  188. package/out/zqlite/src/table-source.js +10 -53
  189. package/out/zqlite/src/table-source.js.map +1 -1
  190. package/package.json +1 -1
  191. package/out/chunk-6XQK3UTJ.js.map +0 -7
  192. package/out/chunk-HARIWJ2J.js.map +0 -7
  193. package/out/chunk-YKI43S5O.js.map +0 -7
@@ -1,6 +1,7 @@
1
1
  import {
2
+ DEFAULT_TTL,
2
3
  hasOwn
3
- } from "./chunk-HARIWJ2J.js";
4
+ } from "./chunk-HLTH2AYN.js";
4
5
  import {
5
6
  applyChange,
6
7
  assert,
@@ -18,7 +19,7 @@ import {
18
19
  throwInvalidType,
19
20
  unreachable,
20
21
  valuesEqual
21
- } from "./chunk-6XQK3UTJ.js";
22
+ } from "./chunk-6CQ6CUML.js";
22
23
  import {
23
24
  __export,
24
25
  __reExport
@@ -8457,6 +8458,65 @@ function one(...args) {
8457
8458
  }));
8458
8459
  }
8459
8460
 
8461
+ // ../shared/src/hash.ts
8462
+ import { xxHash32 } from "js-xxhash";
8463
+ var h64 = (s) => hash(s, 2);
8464
+ var h128 = (s) => hash(s, 4);
8465
+ function hash(str, words) {
8466
+ let hash2 = 0n;
8467
+ for (let i = 0; i < words; i++) {
8468
+ hash2 = (hash2 << 32n) + BigInt(xxHash32(str, i));
8469
+ }
8470
+ return hash2;
8471
+ }
8472
+
8473
+ // ../shared/src/objects.ts
8474
+ function mapEntries(input, mapper) {
8475
+ const output = {};
8476
+ for (const entry of Object.entries(input)) {
8477
+ const mapped = mapper(entry[0], entry[1]);
8478
+ output[mapped[0]] = mapped[1];
8479
+ }
8480
+ return output;
8481
+ }
8482
+ function mapAllEntries(input, mapper) {
8483
+ const output = {};
8484
+ for (const mapped of mapper(Object.entries(input))) {
8485
+ output[mapped[0]] = mapped[1];
8486
+ }
8487
+ return output;
8488
+ }
8489
+
8490
+ // ../zero-protocol/src/client-schema.ts
8491
+ var valueTypeSchema = valita_exports.union(
8492
+ valita_exports.literal("string"),
8493
+ valita_exports.literal("number"),
8494
+ valita_exports.literal("boolean"),
8495
+ valita_exports.literal("null"),
8496
+ valita_exports.literal("json")
8497
+ );
8498
+ var columnSchemaSchema = valita_exports.object({
8499
+ type: valueTypeSchema
8500
+ });
8501
+ var tableSchemaSchema = valita_exports.object({
8502
+ columns: valita_exports.record(columnSchemaSchema)
8503
+ });
8504
+ var clientSchemaSchema = valita_exports.object({
8505
+ tables: valita_exports.record(tableSchemaSchema)
8506
+ });
8507
+ var keyCmp = ([a], [b]) => a < b ? -1 : a > b ? 1 : 0;
8508
+ function normalizeClientSchema(schema) {
8509
+ return {
8510
+ tables: mapAllEntries(
8511
+ schema.tables,
8512
+ (tables) => tables.sort(keyCmp).map(([name, table2]) => [
8513
+ name,
8514
+ { columns: mapAllEntries(table2.columns, (e) => e.sort(keyCmp)) }
8515
+ ])
8516
+ )
8517
+ };
8518
+ }
8519
+
8460
8520
  // ../zero-schema/src/builder/table-builder.ts
8461
8521
  function table(name) {
8462
8522
  return new TableBuilder({
@@ -8575,7 +8635,7 @@ var ColumnBuilder = class _ColumnBuilder {
8575
8635
  };
8576
8636
 
8577
8637
  // ../zero-schema/src/builder/schema-builder.ts
8578
- function createSchema(version3, options) {
8638
+ function createSchema(options) {
8579
8639
  const retTables = {};
8580
8640
  const retRelationships = {};
8581
8641
  const serverNames = /* @__PURE__ */ new Set();
@@ -8606,7 +8666,6 @@ function createSchema(version3, options) {
8606
8666
  );
8607
8667
  });
8608
8668
  return {
8609
- version: version3,
8610
8669
  tables: retTables,
8611
8670
  relationships: retRelationships
8612
8671
  };
@@ -8629,6 +8688,22 @@ function checkRelationship(relationships2, tableName, tables) {
8629
8688
  });
8630
8689
  });
8631
8690
  }
8691
+ function clientSchemaFrom(schema) {
8692
+ const client = {
8693
+ tables: mapEntries(schema.tables, (name, { serverName, columns }) => [
8694
+ serverName ?? name,
8695
+ {
8696
+ columns: mapEntries(columns, (name2, { serverName: serverName2, type }) => [
8697
+ serverName2 ?? name2,
8698
+ { type }
8699
+ ])
8700
+ }
8701
+ ])
8702
+ };
8703
+ const clientSchema2 = normalizeClientSchema(client);
8704
+ const hash2 = h64(JSON.stringify(clientSchema2)).toString(36);
8705
+ return { clientSchema: clientSchema2, hash: hash2 };
8706
+ }
8632
8707
 
8633
8708
  // ../zero-protocol/src/ast.ts
8634
8709
  import { compareUTF8 as compareUTF83 } from "compare-utf8";
@@ -8976,18 +9051,6 @@ function compareUTF8MaybeNull(a, b) {
8976
9051
  // ../zql/src/query/query-impl.ts
8977
9052
  import { resolver as resolver7 } from "@rocicorp/resolver";
8978
9053
 
8979
- // ../shared/src/hash.ts
8980
- import { xxHash32 } from "js-xxhash";
8981
- var h64 = (s) => hash(s, 2);
8982
- var h128 = (s) => hash(s, 4);
8983
- function hash(str, words) {
8984
- let hash2 = 0n;
8985
- for (let i = 0; i < words; i++) {
8986
- hash2 = (hash2 << 32n) + BigInt(xxHash32(str, i));
8987
- }
8988
- return hash2;
8989
- }
8990
-
8991
9054
  // ../zero-protocol/src/ast-hash.ts
8992
9055
  var hashCache = /* @__PURE__ */ new WeakMap();
8993
9056
  function hashOfAST(ast) {
@@ -9059,7 +9122,10 @@ var Exists = class {
9059
9122
  this.#relationshipName = relationshipName;
9060
9123
  this.#input.setOutput(this);
9061
9124
  this.#storage = storage;
9062
- assert(this.#input.getSchema().relationships[relationshipName]);
9125
+ assert(
9126
+ this.#input.getSchema().relationships[relationshipName],
9127
+ `Input schema missing ${relationshipName}`
9128
+ );
9063
9129
  this.#not = type === "NOT EXISTS";
9064
9130
  this.#parentJoinKey = parentJoinKey;
9065
9131
  this.#noSizeReuse = areEqual(
@@ -9273,17 +9339,17 @@ var Exists = class {
9273
9339
  // ../zql/src/ivm/fan-in.ts
9274
9340
  var FanIn = class {
9275
9341
  #inputs;
9276
- #fanOut;
9277
9342
  #schema;
9278
9343
  #output = throwOutput;
9344
+ #accumulatedPushes;
9279
9345
  constructor(fanOut, inputs) {
9280
9346
  this.#inputs = inputs;
9281
9347
  this.#schema = fanOut.getSchema();
9282
- this.#fanOut = fanOut;
9283
9348
  for (const input of inputs) {
9284
9349
  input.setOutput(this);
9285
9350
  assert(this.#schema === input.getSchema(), `Schema mismatch in fan-in`);
9286
9351
  }
9352
+ this.#accumulatedPushes = [];
9287
9353
  }
9288
9354
  setOutput(output) {
9289
9355
  this.#output = output;
@@ -9311,8 +9377,102 @@ var FanIn = class {
9311
9377
  );
9312
9378
  }
9313
9379
  push(change) {
9314
- this.#fanOut.onFanInReceivedPush();
9315
- this.#output.push(change);
9380
+ this.#accumulatedPushes.push(change);
9381
+ }
9382
+ fanOutDonePushingToAllBranches(fanOutChangeType) {
9383
+ if (this.#inputs.length === 0) {
9384
+ assert(
9385
+ this.#accumulatedPushes.length === 0,
9386
+ "If there are no inputs then fan-in should not receive any pushes."
9387
+ );
9388
+ return;
9389
+ }
9390
+ if (this.#accumulatedPushes.length === 0) {
9391
+ return;
9392
+ }
9393
+ const candidatesToPush = /* @__PURE__ */ new Map();
9394
+ for (const change of this.#accumulatedPushes) {
9395
+ if (fanOutChangeType === "child" && change.type !== "child") {
9396
+ assert(
9397
+ candidatesToPush.has(change.type) === false,
9398
+ () => `Fan-in:child expected at most one ${change.type} when fan-out is of type child`
9399
+ );
9400
+ }
9401
+ candidatesToPush.set(change.type, change);
9402
+ }
9403
+ this.#accumulatedPushes = [];
9404
+ const types = [...candidatesToPush.keys()];
9405
+ switch (fanOutChangeType) {
9406
+ case "remove":
9407
+ assert(
9408
+ types.length === 1 && types[0] === "remove",
9409
+ "Fan-in:remove expected all removes"
9410
+ );
9411
+ this.#output.push(must(candidatesToPush.get("remove")));
9412
+ return;
9413
+ case "add":
9414
+ assert(
9415
+ types.length === 1 && types[0] === "add",
9416
+ "Fan-in:add expected all adds"
9417
+ );
9418
+ this.#output.push(must(candidatesToPush.get("add")));
9419
+ return;
9420
+ case "edit": {
9421
+ assert(
9422
+ types.every(
9423
+ (type) => type === "add" || type === "remove" || type === "edit"
9424
+ ),
9425
+ "Fan-in:edit expected all adds, removes, or edits"
9426
+ );
9427
+ const addChange = candidatesToPush.get("add");
9428
+ const removeChange = candidatesToPush.get("remove");
9429
+ const editChange = candidatesToPush.get("edit");
9430
+ if (editChange) {
9431
+ this.#output.push(editChange);
9432
+ return;
9433
+ }
9434
+ if (addChange && removeChange) {
9435
+ this.#output.push({
9436
+ type: "edit",
9437
+ node: addChange.node,
9438
+ oldNode: removeChange.node
9439
+ });
9440
+ return;
9441
+ }
9442
+ this.#output.push(must(addChange ?? removeChange));
9443
+ return;
9444
+ }
9445
+ case "child": {
9446
+ assert(
9447
+ types.every(
9448
+ (type) => type === "add" || // exists can change child to add or remove
9449
+ type === "remove" || // exists can change child to add or remove
9450
+ type === "child"
9451
+ // other operators may preserve the child change
9452
+ ),
9453
+ "Fan-in:child expected all adds, removes, or children"
9454
+ );
9455
+ assert(
9456
+ types.length <= 2,
9457
+ "Fan-in:child expected at most 2 types on a child change from fan-out"
9458
+ );
9459
+ const childChange = candidatesToPush.get("child");
9460
+ if (childChange) {
9461
+ this.#output.push(childChange);
9462
+ return;
9463
+ }
9464
+ const addChange = candidatesToPush.get("add");
9465
+ const removeChange = candidatesToPush.get("remove");
9466
+ assert(
9467
+ addChange === void 0 || removeChange === void 0,
9468
+ "Fan-in:child expected either add or remove, not both"
9469
+ );
9470
+ this.#output.push(must(addChange ?? removeChange));
9471
+ return;
9472
+ }
9473
+ default:
9474
+ fanOutChangeType;
9475
+ }
9316
9476
  }
9317
9477
  };
9318
9478
 
@@ -9320,16 +9480,15 @@ var FanIn = class {
9320
9480
  var FanOut = class {
9321
9481
  #input;
9322
9482
  #outputs = [];
9323
- // FanOut is paired with a FanIn.
9324
- // Once FanIn has received a push from FanOut along
9325
- // any branch, FanOut no longer needs to push that value
9326
- // across the rest of its outputs..
9327
- #fanInReceivedPush = false;
9483
+ #fanIn;
9328
9484
  #destroyCount = 0;
9329
9485
  constructor(input) {
9330
9486
  this.#input = input;
9331
9487
  input.setOutput(this);
9332
9488
  }
9489
+ setFanIn(fanIn) {
9490
+ this.#fanIn = fanIn;
9491
+ }
9333
9492
  setOutput(output) {
9334
9493
  this.#outputs.push(output);
9335
9494
  }
@@ -9352,17 +9511,14 @@ var FanOut = class {
9352
9511
  cleanup(req) {
9353
9512
  return this.#input.cleanup(req);
9354
9513
  }
9355
- onFanInReceivedPush() {
9356
- this.#fanInReceivedPush = true;
9357
- }
9358
9514
  push(change) {
9359
- this.#fanInReceivedPush = false;
9360
9515
  for (const out of this.#outputs) {
9361
9516
  out.push(change);
9362
- if (this.#fanInReceivedPush) {
9363
- return;
9364
- }
9365
9517
  }
9518
+ must(
9519
+ this.#fanIn,
9520
+ "fan-out must have a corresponding fan-in set!"
9521
+ ).fanOutDonePushingToAllBranches(change.type);
9366
9522
  }
9367
9523
  };
9368
9524
 
@@ -9564,34 +9720,27 @@ var Join = class {
9564
9720
  });
9565
9721
  break;
9566
9722
  case "edit": {
9567
- if (rowEqualsForCompoundKey(
9568
- change.oldNode.row,
9569
- change.node.row,
9570
- this.#parentKey
9571
- )) {
9572
- this.#output.push({
9573
- type: "edit",
9574
- oldNode: this.#processParentNode(
9575
- change.oldNode.row,
9576
- change.oldNode.relationships,
9577
- "cleanup"
9578
- ),
9579
- node: this.#processParentNode(
9580
- change.node.row,
9581
- change.node.relationships,
9582
- "fetch"
9583
- )
9584
- });
9585
- } else {
9586
- this.#pushParent({
9587
- type: "remove",
9588
- node: change.oldNode
9589
- });
9590
- this.#pushParent({
9591
- type: "add",
9592
- node: change.node
9593
- });
9594
- }
9723
+ assert(
9724
+ rowEqualsForCompoundKey(
9725
+ change.oldNode.row,
9726
+ change.node.row,
9727
+ this.#parentKey
9728
+ ),
9729
+ `Parent edit must not change relationship.`
9730
+ );
9731
+ this.#output.push({
9732
+ type: "edit",
9733
+ oldNode: this.#processParentNode(
9734
+ change.oldNode.row,
9735
+ change.oldNode.relationships,
9736
+ "cleanup"
9737
+ ),
9738
+ node: this.#processParentNode(
9739
+ change.node.row,
9740
+ change.node.relationships,
9741
+ "fetch"
9742
+ )
9743
+ });
9595
9744
  break;
9596
9745
  }
9597
9746
  default:
@@ -9632,18 +9781,11 @@ var Join = class {
9632
9781
  case "edit": {
9633
9782
  const childRow = change.node.row;
9634
9783
  const oldChildRow = change.oldNode.row;
9635
- if (rowEqualsForCompoundKey(oldChildRow, childRow, this.#childKey)) {
9636
- pushChildChange(childRow, change);
9637
- } else {
9638
- pushChildChange(oldChildRow, {
9639
- type: "remove",
9640
- node: change.oldNode
9641
- });
9642
- pushChildChange(childRow, {
9643
- type: "add",
9644
- node: change.node
9645
- });
9646
- }
9784
+ assert(
9785
+ rowEqualsForCompoundKey(oldChildRow, childRow, this.#childKey),
9786
+ "Child edit must not change relationship."
9787
+ );
9788
+ pushChildChange(childRow, change);
9647
9789
  break;
9648
9790
  }
9649
9791
  default:
@@ -9719,7 +9861,7 @@ function makeStorageKey(key, primaryKey, row) {
9719
9861
  }
9720
9862
  function rowEqualsForCompoundKey(a, b, key) {
9721
9863
  for (let i = 0; i < key.length; i++) {
9722
- if (!valuesEqual(a[key[i]], b[key[i]])) {
9864
+ if (compareValues(a[key[i]], b[key[i]]) !== 0) {
9723
9865
  return false;
9724
9866
  }
9725
9867
  }
@@ -10092,17 +10234,10 @@ var Take = class {
10092
10234
  }
10093
10235
  }
10094
10236
  #pushEditChange(change) {
10095
- if (this.#partitionKeyComparator && this.#partitionKeyComparator(change.oldNode.row, change.node.row) !== 0) {
10096
- this.push({
10097
- type: "remove",
10098
- node: change.oldNode
10099
- });
10100
- this.push({
10101
- type: "add",
10102
- node: change.node
10103
- });
10104
- return;
10105
- }
10237
+ assert(
10238
+ !this.#partitionKeyComparator || this.#partitionKeyComparator(change.oldNode.row, change.node.row) === 0,
10239
+ "Unexpected change of partition key"
10240
+ );
10106
10241
  const { takeState, takeStateKey, maxBound, constraint } = this.#getStateAndConstraint(change.oldNode.row);
10107
10242
  if (!takeState) {
10108
10243
  return;
@@ -10521,55 +10656,75 @@ function transformFilters(filters) {
10521
10656
 
10522
10657
  // ../zql/src/builder/builder.ts
10523
10658
  function buildPipeline(ast, delegate) {
10524
- return buildPipelineInternal(ast, delegate);
10659
+ return buildPipelineInternal(ast, delegate, "");
10525
10660
  }
10526
- function buildPipelineInternal(ast, delegate, partitionKey) {
10661
+ function buildPipelineInternal(ast, delegate, name, partitionKey) {
10527
10662
  const source = delegate.getSource(ast.table);
10528
10663
  if (!source) {
10529
10664
  throw new Error(`Source not found: ${ast.table}`);
10530
10665
  }
10531
- const conn = source.connect(must(ast.orderBy), ast.where);
10532
- let end = conn;
10533
- const { fullyAppliedFilters } = conn;
10534
10666
  ast = uniquifyCorrelatedSubqueryConditionAliases(ast);
10667
+ const csqsFromCondition = gatherCorrelatedSubqueryQueriesFromCondition(
10668
+ ast.where
10669
+ );
10670
+ const splitEditKeys = partitionKey ? new Set(partitionKey) : /* @__PURE__ */ new Set();
10671
+ for (const csq of csqsFromCondition) {
10672
+ for (const key of csq.correlation.parentField) {
10673
+ splitEditKeys.add(key);
10674
+ }
10675
+ }
10676
+ if (ast.related) {
10677
+ for (const csq of ast.related) {
10678
+ for (const key of csq.correlation.parentField) {
10679
+ splitEditKeys.add(key);
10680
+ }
10681
+ }
10682
+ }
10683
+ const conn = source.connect(must(ast.orderBy), ast.where, splitEditKeys);
10684
+ let end = delegate.decorateInput(conn, `${name}:source(${ast.table})`);
10685
+ const { fullyAppliedFilters } = conn;
10535
10686
  if (ast.start) {
10536
- end = new Skip(end, ast.start);
10687
+ end = delegate.decorateInput(new Skip(end, ast.start), `${name}:skip)`);
10537
10688
  }
10538
- for (const csq of gatherCorrelatedSubqueryQueriesFromCondition(ast.where)) {
10539
- end = applyCorrelatedSubQuery(csq, delegate, end);
10689
+ for (const csq of csqsFromCondition) {
10690
+ end = applyCorrelatedSubQuery(csq, delegate, end, name);
10540
10691
  }
10541
10692
  if (ast.where && !fullyAppliedFilters) {
10542
- end = applyWhere(end, ast.where, delegate);
10693
+ end = applyWhere(end, ast.where, delegate, name);
10543
10694
  }
10544
- if (ast.limit) {
10545
- end = new Take(end, delegate.createStorage(), ast.limit, partitionKey);
10695
+ if (ast.limit !== void 0) {
10696
+ const takeName = `${name}:take`;
10697
+ end = delegate.decorateInput(
10698
+ new Take(end, delegate.createStorage(takeName), ast.limit, partitionKey),
10699
+ takeName
10700
+ );
10546
10701
  }
10547
10702
  if (ast.related) {
10548
10703
  for (const csq of ast.related) {
10549
- end = applyCorrelatedSubQuery(csq, delegate, end);
10704
+ end = applyCorrelatedSubQuery(csq, delegate, end, name);
10550
10705
  }
10551
10706
  }
10552
10707
  return end;
10553
10708
  }
10554
- function applyWhere(input, condition, delegate) {
10709
+ function applyWhere(input, condition, delegate, name) {
10555
10710
  switch (condition.type) {
10556
10711
  case "and":
10557
- return applyAnd(input, condition, delegate);
10712
+ return applyAnd(input, condition, delegate, name);
10558
10713
  case "or":
10559
- return applyOr(input, condition, delegate);
10714
+ return applyOr(input, condition, delegate, name);
10560
10715
  case "correlatedSubquery":
10561
- return applyCorrelatedSubqueryCondition(input, condition, delegate);
10716
+ return applyCorrelatedSubqueryCondition(input, condition, delegate, name);
10562
10717
  case "simple":
10563
10718
  return applySimpleCondition(input, condition);
10564
10719
  }
10565
10720
  }
10566
- function applyAnd(input, condition, delegate) {
10721
+ function applyAnd(input, condition, delegate, name) {
10567
10722
  for (const subCondition of condition.conditions) {
10568
- input = applyWhere(input, subCondition, delegate);
10723
+ input = applyWhere(input, subCondition, delegate, name);
10569
10724
  }
10570
10725
  return input;
10571
10726
  }
10572
- function applyOr(input, condition, delegate) {
10727
+ function applyOr(input, condition, delegate, name) {
10573
10728
  const [subqueryConditions, otherConditions] = groupSubqueryConditions(condition);
10574
10729
  if (subqueryConditions.length === 0) {
10575
10730
  return new Filter(
@@ -10582,7 +10737,7 @@ function applyOr(input, condition, delegate) {
10582
10737
  }
10583
10738
  const fanOut = new FanOut(input);
10584
10739
  const branches = subqueryConditions.map(
10585
- (subCondition) => applyWhere(fanOut, subCondition, delegate)
10740
+ (subCondition) => applyWhere(fanOut, subCondition, delegate, name)
10586
10741
  );
10587
10742
  if (otherConditions.length > 0) {
10588
10743
  branches.push(
@@ -10595,7 +10750,9 @@ function applyOr(input, condition, delegate) {
10595
10750
  )
10596
10751
  );
10597
10752
  }
10598
- return new FanIn(fanOut, branches);
10753
+ const ret = new FanIn(fanOut, branches);
10754
+ fanOut.setFanIn(ret);
10755
+ return ret;
10599
10756
  }
10600
10757
  function groupSubqueryConditions(condition) {
10601
10758
  const partitioned = [[], []];
@@ -10621,33 +10778,39 @@ function isNotAndDoesNotContainSubquery(condition) {
10621
10778
  function applySimpleCondition(input, condition) {
10622
10779
  return new Filter(input, createPredicate(condition));
10623
10780
  }
10624
- function applyCorrelatedSubQuery(sq, delegate, end) {
10781
+ function applyCorrelatedSubQuery(sq, delegate, end, name) {
10625
10782
  assert(sq.subquery.alias, "Subquery must have an alias");
10626
10783
  const child = buildPipelineInternal(
10627
10784
  sq.subquery,
10628
10785
  delegate,
10786
+ `${name}.${sq.subquery.alias}`,
10629
10787
  sq.correlation.childField
10630
10788
  );
10789
+ const joinName = `${name}:join(${sq.subquery.alias})`;
10631
10790
  end = new Join({
10632
10791
  parent: end,
10633
10792
  child,
10634
- storage: delegate.createStorage(),
10793
+ storage: delegate.createStorage(joinName),
10635
10794
  parentKey: sq.correlation.parentField,
10636
10795
  childKey: sq.correlation.childField,
10637
10796
  relationshipName: sq.subquery.alias,
10638
10797
  hidden: sq.hidden ?? false,
10639
10798
  system: sq.system ?? "client"
10640
10799
  });
10641
- return end;
10800
+ return delegate.decorateInput(end, joinName);
10642
10801
  }
10643
- function applyCorrelatedSubqueryCondition(input, condition, delegate) {
10802
+ function applyCorrelatedSubqueryCondition(input, condition, delegate, name) {
10644
10803
  assert(condition.op === "EXISTS" || condition.op === "NOT EXISTS");
10645
- return new Exists(
10646
- input,
10647
- delegate.createStorage(),
10648
- must(condition.related.subquery.alias),
10649
- condition.related.correlation.parentField,
10650
- condition.op
10804
+ const existsName = `${name}:exists(${condition.related.subquery.alias})`;
10805
+ return delegate.decorateInput(
10806
+ new Exists(
10807
+ input,
10808
+ delegate.createStorage(existsName),
10809
+ must(condition.related.subquery.alias),
10810
+ condition.related.correlation.parentField,
10811
+ condition.op
10812
+ ),
10813
+ existsName
10651
10814
  );
10652
10815
  }
10653
10816
  function gatherCorrelatedSubqueryQueriesFromCondition(condition) {
@@ -11044,9 +11207,6 @@ function unwrap(c) {
11044
11207
  return { type: c.type, conditions: flatten(c.type, c.conditions.map(unwrap)) };
11045
11208
  }
11046
11209
 
11047
- // ../zql/src/query/query.ts
11048
- var DEFAULT_TTL = 1e4;
11049
-
11050
11210
  // ../zql/src/query/query-impl.ts
11051
11211
  var astForTestingSymbol = Symbol();
11052
11212
  function newQuery(delegate, schema, table2) {
@@ -11085,9 +11245,7 @@ var AbstractQuery = class {
11085
11245
  }
11086
11246
  hash() {
11087
11247
  if (!this.#hash) {
11088
- const ast = this._completeAst();
11089
- const hash2 = hashOfAST(ast);
11090
- this.#hash = hash2;
11248
+ this.#hash = hashOfAST(this._completeAst());
11091
11249
  }
11092
11250
  return this.#hash;
11093
11251
  }
@@ -11452,6 +11610,7 @@ var QueryImpl = class extends AbstractQuery {
11452
11610
  return newQueryWithDetails(this.#delegate, schema, tableName, ast, format);
11453
11611
  }
11454
11612
  materialize(factoryOrTTL, ttl = DEFAULT_TTL) {
11613
+ const t0 = Date.now();
11455
11614
  let factory;
11456
11615
  if (typeof factoryOrTTL === "function") {
11457
11616
  factory = factoryOrTTL;
@@ -11463,6 +11622,8 @@ var QueryImpl = class extends AbstractQuery {
11463
11622
  let queryGot = false;
11464
11623
  const removeServerQuery = this.#delegate.addServerQuery(ast, ttl, (got) => {
11465
11624
  if (got) {
11625
+ const t1 = Date.now();
11626
+ this.#delegate.onQueryMaterialized(this.hash(), ast, t1 - t0);
11466
11627
  queryGot = true;
11467
11628
  queryCompleteResolver.resolve(true);
11468
11629
  }
@@ -11831,6 +11992,40 @@ function escapeLike(val) {
11831
11992
  return val.replace(/[%_]/g, "\\$&");
11832
11993
  }
11833
11994
 
11995
+ // ../zql/src/query/ttl.ts
11996
+ var DEFAULT_TTL2 = "none";
11997
+ var multiplier = {
11998
+ s: 1e3,
11999
+ m: 60 * 1e3,
12000
+ h: 60 * 60 * 1e3,
12001
+ d: 24 * 60 * 60 * 1e3,
12002
+ y: 365 * 24 * 60 * 60 * 1e3
12003
+ };
12004
+ function parseTTL(ttl) {
12005
+ if (typeof ttl === "number") {
12006
+ return Number.isNaN(ttl) ? 0 : !Number.isFinite(ttl) || ttl < 0 ? -1 : ttl;
12007
+ }
12008
+ if (ttl === "none") {
12009
+ return 0;
12010
+ }
12011
+ if (ttl === "forever") {
12012
+ return -1;
12013
+ }
12014
+ const multi = multiplier[ttl[ttl.length - 1]];
12015
+ return Number(ttl.slice(0, -1)) * multi;
12016
+ }
12017
+ function compareTTL(a, b) {
12018
+ const ap = parseTTL(a);
12019
+ const bp = parseTTL(b);
12020
+ if (ap === -1 && bp !== -1) {
12021
+ return 1;
12022
+ }
12023
+ if (ap !== -1 && bp === -1) {
12024
+ return -1;
12025
+ }
12026
+ return ap - bp;
12027
+ }
12028
+
11834
12029
  // ../zero-client/src/client/zero.ts
11835
12030
  import { LogContext as LogContext4 } from "@rocicorp/logger";
11836
12031
  import { resolver as resolver8 } from "@rocicorp/resolver";
@@ -11875,6 +12070,7 @@ var connectedMessageSchema = valita_exports.tuple([
11875
12070
  ]);
11876
12071
  var initConnectionBodySchema = valita_exports.object({
11877
12072
  desiredQueriesPatch: queriesPatchSchema,
12073
+ clientSchema: clientSchemaSchema.optional(),
11878
12074
  deleted: deleteClientsBodySchema.optional()
11879
12075
  });
11880
12076
  var initConnectionMessageSchema = valita_exports.tuple([
@@ -12103,7 +12299,7 @@ var CRUD = "crud";
12103
12299
  var Custom = "custom";
12104
12300
 
12105
12301
  // ../zero-protocol/src/protocol-version.ts
12106
- var PROTOCOL_VERSION = 6;
12302
+ var PROTOCOL_VERSION = 7;
12107
12303
  var MIN_SERVER_SUPPORTED_SYNC_PROTOCOL = 2;
12108
12304
  assert(MIN_SERVER_SUPPORTED_SYNC_PROTOCOL < PROTOCOL_VERSION);
12109
12305
  var MIN_SERVER_SUPPORTED_PERMISSIONS_PROTOCOL = 4;
@@ -12168,7 +12364,10 @@ var pushBodySchema = valita_exports.object({
12168
12364
  clientGroupID: valita_exports.string(),
12169
12365
  mutations: valita_exports.array(mutationSchema),
12170
12366
  pushVersion: valita_exports.number(),
12171
- schemaVersion: valita_exports.number(),
12367
+ // For legacy (CRUD) mutations, the schema is tied to the client group /
12368
+ // sync connection. For custom mutations, schema versioning is delegated
12369
+ // to the custom protocol / api-server.
12370
+ schemaVersion: valita_exports.number().optional(),
12172
12371
  timestamp: valita_exports.number(),
12173
12372
  requestID: valita_exports.string()
12174
12373
  });
@@ -12811,6 +13010,7 @@ var MemorySource = class _MemorySource {
12811
13010
  #indexes = /* @__PURE__ */ new Map();
12812
13011
  #connections = [];
12813
13012
  #overlay;
13013
+ #splitEditOverlay;
12814
13014
  constructor(tableName, columns, primaryKey, primaryIndexData) {
12815
13015
  this.#tableName = tableName;
12816
13016
  this.#columns = columns;
@@ -12853,7 +13053,7 @@ var MemorySource = class _MemorySource {
12853
13053
  compareRows: connection.compareRows
12854
13054
  };
12855
13055
  }
12856
- connect(sort, filters) {
13056
+ connect(sort, filters, splitEditKeys) {
12857
13057
  const transformedFilters = transformFilters(filters);
12858
13058
  const input = {
12859
13059
  getSchema: () => schema,
@@ -12871,6 +13071,7 @@ var MemorySource = class _MemorySource {
12871
13071
  input,
12872
13072
  output: void 0,
12873
13073
  sort,
13074
+ splitEditKeys,
12874
13075
  compareRows: makeComparator(sort),
12875
13076
  filters: transformedFilters.filters ? {
12876
13077
  condition: transformedFilters.filters,
@@ -12924,10 +13125,9 @@ var MemorySource = class _MemorySource {
12924
13125
  return [...this.#indexes.keys()];
12925
13126
  }
12926
13127
  *#fetch(req, from) {
12927
- let overlay;
12928
- const callingConnectionNum = this.#connections.indexOf(from);
12929
- assert(callingConnectionNum !== -1, "Output not found");
12930
- const conn = this.#connections[callingConnectionNum];
13128
+ const callingConnectionIndex = this.#connections.indexOf(from);
13129
+ assert(callingConnectionIndex !== -1, "Output not found");
13130
+ const conn = this.#connections[callingConnectionIndex];
12931
13131
  const { sort: requestedSort } = conn;
12932
13132
  const indexSort = [];
12933
13133
  if (req.constraint) {
@@ -12941,11 +13141,6 @@ var MemorySource = class _MemorySource {
12941
13141
  const index = this.#getOrCreateIndex(indexSort, from);
12942
13142
  const { data, comparator: compare } = index;
12943
13143
  const comparator2 = (r1, r2) => compare(r1, r2) * (req.reverse ? -1 : 1);
12944
- if (this.#overlay) {
12945
- if (callingConnectionNum <= this.#overlay.outputIndex) {
12946
- overlay = this.#overlay;
12947
- }
12948
- }
12949
13144
  const startAt = req.start?.row;
12950
13145
  let scanStart;
12951
13146
  if (req.constraint) {
@@ -12968,7 +13163,9 @@ var MemorySource = class _MemorySource {
12968
13163
  startAt,
12969
13164
  generateRows(data, scanStart, req.reverse),
12970
13165
  req.constraint,
12971
- overlay,
13166
+ this.#overlay,
13167
+ this.#splitEditOverlay,
13168
+ callingConnectionIndex,
12972
13169
  comparator2,
12973
13170
  conn.filters?.predicate
12974
13171
  );
@@ -12988,56 +13185,18 @@ var MemorySource = class _MemorySource {
12988
13185
  *genPush(change) {
12989
13186
  const primaryIndex = this.#getPrimaryIndex();
12990
13187
  const { data } = primaryIndex;
12991
- switch (change.type) {
12992
- case "add":
12993
- assert(
12994
- !data.has(change.row),
12995
- () => `Row already exists ${JSON.stringify(change)}`
12996
- );
12997
- break;
12998
- case "remove":
12999
- assert(
13000
- data.has(change.row),
13001
- () => `Row not found ${JSON.stringify(change)}`
13002
- );
13003
- break;
13004
- case "edit":
13005
- assert(
13006
- data.has(change.oldRow),
13007
- () => `Row not found ${JSON.stringify(change)}`
13008
- );
13009
- break;
13010
- default:
13011
- unreachable(change);
13012
- }
13013
- const outputChange = change.type === "edit" ? {
13014
- type: change.type,
13015
- oldNode: {
13016
- row: change.oldRow,
13017
- relationships: {}
13018
- },
13019
- node: {
13020
- row: change.row,
13021
- relationships: {}
13022
- }
13023
- } : {
13024
- type: change.type,
13025
- node: {
13026
- row: change.row,
13027
- relationships: {}
13028
- }
13029
- };
13030
- for (const [
13031
- outputIndex,
13032
- { output, filters }
13033
- ] of this.#connections.entries()) {
13034
- if (output) {
13035
- this.#overlay = { outputIndex, change };
13036
- filterPush(outputChange, output, filters?.predicate);
13037
- yield;
13038
- }
13188
+ const exists = (row) => data.has(row);
13189
+ const setOverlay = (o) => this.#overlay = o;
13190
+ const setSplitEditOverlay = (o) => this.#splitEditOverlay = o;
13191
+ for (const x of genPush(
13192
+ change,
13193
+ exists,
13194
+ this.#connections.entries(),
13195
+ setOverlay,
13196
+ setSplitEditOverlay
13197
+ )) {
13198
+ yield x;
13039
13199
  }
13040
- this.#overlay = void 0;
13041
13200
  for (const { data: data2 } of this.#indexes.values()) {
13042
13201
  switch (change.type) {
13043
13202
  case "add": {
@@ -13077,6 +13236,89 @@ function* generateWithFilter(it, filter) {
13077
13236
  }
13078
13237
  }
13079
13238
  }
13239
+ function* genPush(change, exists, connections, setOverlay, setSplitEditOverlay) {
13240
+ switch (change.type) {
13241
+ case "add":
13242
+ assert(
13243
+ !exists(change.row),
13244
+ () => `Row already exists ${stringify(change)}`
13245
+ );
13246
+ break;
13247
+ case "remove":
13248
+ assert(exists(change.row), () => `Row not found ${stringify(change)}`);
13249
+ break;
13250
+ case "edit":
13251
+ assert(exists(change.oldRow), () => `Row not found ${stringify(change)}`);
13252
+ break;
13253
+ default:
13254
+ unreachable(change);
13255
+ }
13256
+ for (const [outputIndex, { output, splitEditKeys, filters }] of connections) {
13257
+ if (output) {
13258
+ let splitEdit = false;
13259
+ if (change.type === "edit" && splitEditKeys) {
13260
+ for (const key of splitEditKeys) {
13261
+ if (!valuesEqual(change.row[key], change.oldRow[key])) {
13262
+ splitEdit = true;
13263
+ break;
13264
+ }
13265
+ }
13266
+ }
13267
+ if (splitEdit) {
13268
+ assert(change.type === "edit");
13269
+ setSplitEditOverlay({
13270
+ outputIndex,
13271
+ change: {
13272
+ type: "remove",
13273
+ row: change.oldRow
13274
+ }
13275
+ });
13276
+ const outputRemove = {
13277
+ type: "remove",
13278
+ node: {
13279
+ row: change.oldRow,
13280
+ relationships: {}
13281
+ }
13282
+ };
13283
+ filterPush(outputRemove, output, filters?.predicate);
13284
+ yield;
13285
+ setSplitEditOverlay(void 0);
13286
+ setOverlay({ outputIndex, change });
13287
+ const outputAdd = {
13288
+ type: "add",
13289
+ node: {
13290
+ row: change.row,
13291
+ relationships: {}
13292
+ }
13293
+ };
13294
+ filterPush(outputAdd, output, filters?.predicate);
13295
+ yield;
13296
+ } else {
13297
+ setOverlay({ outputIndex, change });
13298
+ const outputChange = change.type === "edit" ? {
13299
+ type: change.type,
13300
+ oldNode: {
13301
+ row: change.oldRow,
13302
+ relationships: {}
13303
+ },
13304
+ node: {
13305
+ row: change.row,
13306
+ relationships: {}
13307
+ }
13308
+ } : {
13309
+ type: change.type,
13310
+ node: {
13311
+ row: change.row,
13312
+ relationships: {}
13313
+ }
13314
+ };
13315
+ filterPush(outputChange, output, filters?.predicate);
13316
+ yield;
13317
+ }
13318
+ }
13319
+ }
13320
+ setOverlay(void 0);
13321
+ }
13080
13322
  function* generateWithStart(nodes, start, compare) {
13081
13323
  if (!start) {
13082
13324
  yield* nodes;
@@ -13100,11 +13342,17 @@ function* generateWithStart(nodes, start, compare) {
13100
13342
  }
13101
13343
  }
13102
13344
  }
13103
- function* generateWithOverlay(startAt, rows, constraint, overlay, compare, filterPredicate) {
13345
+ function* generateWithOverlay(startAt, rows, constraint, overlay, splitEditOverlay, connectionIndex, compare, filterPredicate) {
13346
+ let overlayToApply = void 0;
13347
+ if (splitEditOverlay && splitEditOverlay.outputIndex === connectionIndex) {
13348
+ overlayToApply = splitEditOverlay;
13349
+ } else if (overlay && connectionIndex <= overlay.outputIndex) {
13350
+ overlayToApply = overlay;
13351
+ }
13104
13352
  const overlays = computeOverlays(
13105
13353
  startAt,
13106
13354
  constraint,
13107
- overlay,
13355
+ overlayToApply,
13108
13356
  compare,
13109
13357
  filterPredicate
13110
13358
  );
@@ -13228,6 +13476,12 @@ function* generateRows(data, scanStart, reverse) {
13228
13476
  scanStart
13229
13477
  );
13230
13478
  }
13479
+ function stringify(change) {
13480
+ return JSON.stringify(
13481
+ change,
13482
+ (_, v2) => typeof v2 === "bigint" ? v2.toString() : v2
13483
+ );
13484
+ }
13231
13485
 
13232
13486
  // ../zero-client/src/client/keys.ts
13233
13487
  var DESIRED_QUERIES_KEY_PREFIX = "d/";
@@ -13393,11 +13647,15 @@ var ZeroContext = class {
13393
13647
  #addQuery;
13394
13648
  #batchViewUpdates;
13395
13649
  #commitListeners = /* @__PURE__ */ new Set();
13650
+ slowMaterializeThreshold;
13651
+ lc;
13396
13652
  staticQueryParameters = void 0;
13397
- constructor(mainSources, addQuery, batchViewUpdates) {
13653
+ constructor(lc, mainSources, addQuery, batchViewUpdates, slowMaterializeThreshold) {
13398
13654
  this.#mainSources = mainSources;
13399
13655
  this.#addQuery = addQuery;
13400
13656
  this.#batchViewUpdates = batchViewUpdates;
13657
+ this.lc = lc;
13658
+ this.slowMaterializeThreshold = slowMaterializeThreshold;
13401
13659
  }
13402
13660
  getSource(name) {
13403
13661
  return this.#mainSources.getSource(name);
@@ -13405,9 +13663,29 @@ var ZeroContext = class {
13405
13663
  addServerQuery(ast, ttl, gotCallback) {
13406
13664
  return this.#addQuery(ast, ttl, gotCallback);
13407
13665
  }
13666
+ onQueryMaterialized(hash2, ast, duration) {
13667
+ if (this.slowMaterializeThreshold !== void 0 && duration > this.slowMaterializeThreshold) {
13668
+ this.lc.warn?.(
13669
+ "Slow query materialization (including server/network)",
13670
+ hash2,
13671
+ ast,
13672
+ duration
13673
+ );
13674
+ } else {
13675
+ this.lc.debug?.(
13676
+ "Materialized query (including server/network)",
13677
+ hash2,
13678
+ ast,
13679
+ duration
13680
+ );
13681
+ }
13682
+ }
13408
13683
  createStorage() {
13409
13684
  return new MemoryStorage();
13410
13685
  }
13686
+ decorateInput(input) {
13687
+ return input;
13688
+ }
13411
13689
  onTransactionCommit(cb) {
13412
13690
  this.#commitListeners.add(cb);
13413
13691
  return () => {
@@ -14062,7 +14340,7 @@ function makeMessage(message, context, logLevel) {
14062
14340
  }
14063
14341
 
14064
14342
  // ../zero-client/src/client/version.ts
14065
- var version2 = "0.17.2025030701";
14343
+ var version2 = "0.17.2025031100";
14066
14344
 
14067
14345
  // ../zero-client/src/client/log-options.ts
14068
14346
  var LevelFilterLogSink = class {
@@ -14431,7 +14709,7 @@ var QueryManager = class {
14431
14709
  }
14432
14710
  for (const [hash2, { normalized, ttl }] of this.#queries) {
14433
14711
  if (!existingQueryHashes.has(hash2)) {
14434
- patch.set(hash2, { op: "put", hash: hash2, ast: normalized, ttl });
14712
+ patch.set(hash2, { op: "put", hash: hash2, ast: normalized, ttl: parseTTL(ttl) });
14435
14713
  }
14436
14714
  }
14437
14715
  if (lastPatch) {
@@ -14459,7 +14737,7 @@ var QueryManager = class {
14459
14737
  entry = {
14460
14738
  normalized: serverAST,
14461
14739
  count: 1,
14462
- gotCallbacks: gotCallback === void 0 ? [] : [gotCallback],
14740
+ gotCallbacks: gotCallback ? [gotCallback] : [],
14463
14741
  ttl
14464
14742
  };
14465
14743
  this.#queries.set(astHash, entry);
@@ -14467,19 +14745,24 @@ var QueryManager = class {
14467
14745
  "changeDesiredQueries",
14468
14746
  {
14469
14747
  desiredQueriesPatch: [
14470
- { op: "put", hash: astHash, ast: serverAST, ttl }
14748
+ { op: "put", hash: astHash, ast: serverAST, ttl: parseTTL(ttl) }
14471
14749
  ]
14472
14750
  }
14473
14751
  ]);
14474
14752
  } else {
14475
14753
  ++entry.count;
14476
- if (ttl !== void 0 && (entry.ttl === void 0 || ttl > entry.ttl)) {
14754
+ if (compareTTL(ttl, entry.ttl) > 0) {
14477
14755
  entry.ttl = ttl;
14478
14756
  this.#send([
14479
14757
  "changeDesiredQueries",
14480
14758
  {
14481
14759
  desiredQueriesPatch: [
14482
- { op: "put", hash: astHash, ast: entry.normalized, ttl }
14760
+ {
14761
+ op: "put",
14762
+ hash: astHash,
14763
+ ast: entry.normalized,
14764
+ ttl: parseTTL(ttl)
14765
+ }
14483
14766
  ]
14484
14767
  }
14485
14768
  ]);
@@ -14497,11 +14780,10 @@ var QueryManager = class {
14497
14780
  return;
14498
14781
  }
14499
14782
  removed = true;
14500
- this.#remove(astHash, gotCallback);
14783
+ this.#remove(entry, astHash, gotCallback);
14501
14784
  };
14502
14785
  }
14503
- #remove(astHash, gotCallback) {
14504
- const entry = must(this.#queries.get(astHash));
14786
+ #remove(entry, astHash, gotCallback) {
14505
14787
  if (gotCallback) {
14506
14788
  const index = entry.gotCallbacks.indexOf(gotCallback);
14507
14789
  entry.gotCallbacks.splice(index, 1);
@@ -14998,7 +15280,7 @@ function updateNeededReloadReason(reason, serverErrMsg) {
14998
15280
  reasonMsg = "The server no longer supports this client's protocol version.";
14999
15281
  break;
15000
15282
  case "SchemaVersionNotSupported":
15001
- reasonMsg = "The server no longer supports this client's schema version.";
15283
+ reasonMsg = "Client and server schemas incompatible.";
15002
15284
  break;
15003
15285
  default:
15004
15286
  unreachable(type);
@@ -15025,6 +15307,7 @@ var Zero = class {
15025
15307
  #lc;
15026
15308
  #logOptions;
15027
15309
  #enableAnalytics;
15310
+ #clientSchema;
15028
15311
  #pokeHandler;
15029
15312
  #queryManager;
15030
15313
  #ivmMain;
@@ -15122,7 +15405,8 @@ var Zero = class {
15122
15405
  hiddenTabDisconnectDelay = DEFAULT_DISCONNECT_HIDDEN_DELAY_MS,
15123
15406
  schema,
15124
15407
  batchViewUpdates = (applyViewUpdates) => applyViewUpdates(),
15125
- maxRecentQueries = 0
15408
+ maxRecentQueries = 0,
15409
+ slowMaterializeThreshold = 5e3
15126
15410
  } = options;
15127
15411
  if (!userID) {
15128
15412
  throw new Error("ZeroOptions.userID must not be empty.");
@@ -15170,10 +15454,12 @@ var Zero = class {
15170
15454
  }
15171
15455
  }
15172
15456
  this.storageKey = storageKey ?? "";
15457
+ const { clientSchema: clientSchema2, hash: hash2 } = clientSchemaFrom(schema);
15458
+ this.#clientSchema = clientSchema2;
15173
15459
  const replicacheOptions = {
15174
- // The schema stored in IDB is dependent upon both the application schema
15460
+ // The schema stored in IDB is dependent upon both the ClientSchema
15175
15461
  // and the AST schema (i.e. PROTOCOL_VERSION).
15176
- schemaVersion: `${schema.version}.${PROTOCOL_VERSION}`,
15462
+ schemaVersion: `${PROTOCOL_VERSION}.${hash2}`,
15177
15463
  logLevel: logOptions.logLevel,
15178
15464
  logSinks: [logOptions.logSink],
15179
15465
  mutators: replicacheMutators,
@@ -15260,9 +15546,11 @@ var Zero = class {
15260
15546
  this.#lc
15261
15547
  );
15262
15548
  this.#zeroContext = new ZeroContext(
15549
+ this.#lc,
15263
15550
  this.#ivmMain,
15264
15551
  (ast, ttl, gotCallback) => this.#queryManager.add(ast, ttl, gotCallback),
15265
- batchViewUpdates
15552
+ batchViewUpdates,
15553
+ slowMaterializeThreshold
15266
15554
  );
15267
15555
  rep.experimentalWatch(
15268
15556
  (diff3) => this.#zeroContext.processChanges(diff3),
@@ -15470,10 +15758,14 @@ var Zero = class {
15470
15758
  case "error":
15471
15759
  return this.#handleErrorMessage(lc, downMessage);
15472
15760
  case "pong":
15761
+ resetBackoff();
15473
15762
  return this.#onPong();
15474
15763
  case "pokeStart":
15475
15764
  return this.#handlePokeStart(lc, downMessage);
15476
15765
  case "pokePart":
15766
+ if (downMessage[1].rowsPatch) {
15767
+ resetBackoff();
15768
+ }
15477
15769
  return this.#handlePokePart(lc, downMessage);
15478
15770
  case "pokeEnd":
15479
15771
  return this.#handlePokeEnd(lc, downMessage);
@@ -15607,11 +15899,15 @@ var Zero = class {
15607
15899
  }
15608
15900
  ]);
15609
15901
  } else if (this.#initConnectionQueries === void 0) {
15902
+ const clientSchema2 = this.#clientSchema;
15610
15903
  send(socket, [
15611
15904
  "initConnection",
15612
15905
  {
15613
15906
  desiredQueriesPatch: [...queriesPatch.values()],
15614
- deleted: skipEmptyDeletedClients(this.#deletedClients)
15907
+ deleted: skipEmptyDeletedClients(this.#deletedClients),
15908
+ // The clientSchema only needs to be sent for the very first request.
15909
+ // Henceforth it is stored with the CVR and verified automatically.
15910
+ ...this.#connectCookie === null ? { clientSchema: clientSchema2 } : {}
15615
15911
  }
15616
15912
  ]);
15617
15913
  this.#deletedClients = void 0;
@@ -15679,7 +15975,7 @@ var Zero = class {
15679
15975
  this.#connectCookie,
15680
15976
  this.clientID,
15681
15977
  await this.clientGroupID,
15682
- this.#options.schema.version,
15978
+ this.#clientSchema,
15683
15979
  this.userID,
15684
15980
  this.#rep.auth,
15685
15981
  this.#lastMutationIDReceived,
@@ -15775,7 +16071,6 @@ var Zero = class {
15775
16071
  );
15776
16072
  }
15777
16073
  #handlePokeStart(_lc, pokeMessage) {
15778
- resetBackoff();
15779
16074
  this.#abortPingTimeout();
15780
16075
  this.#pokeHandler.handlePokeStart(pokeMessage[1]);
15781
16076
  }
@@ -15860,8 +16155,6 @@ var Zero = class {
15860
16155
  clientGroupID: req.clientGroupID,
15861
16156
  mutations: [zeroM],
15862
16157
  pushVersion: req.pushVersion,
15863
- // Zero schema versions are always numbers.
15864
- schemaVersion: parseInt(req.schemaVersion),
15865
16158
  requestID
15866
16159
  }
15867
16160
  ];
@@ -16106,7 +16399,7 @@ var Zero = class {
16106
16399
  return this.#online;
16107
16400
  }
16108
16401
  /**
16109
- * Starts a a ping and waits for a pong.
16402
+ * Starts a ping and waits for a pong.
16110
16403
  *
16111
16404
  * If it takes too long to get a pong we disconnect and this returns
16112
16405
  * {@code PingResult.TimedOut}.
@@ -16154,14 +16447,13 @@ var Zero = class {
16154
16447
  return rv;
16155
16448
  }
16156
16449
  };
16157
- async function createSocket(rep, queryManager, deleteClientsManager, socketOrigin, baseCookie, clientID, clientGroupID, schemaVersion, userID, auth, lmid, wsid, debugPerf, lc, maxHeaderLength = 1024 * 8, additionalConnectParams) {
16450
+ async function createSocket(rep, queryManager, deleteClientsManager, socketOrigin, baseCookie, clientID, clientGroupID, clientSchema2, userID, auth, lmid, wsid, debugPerf, lc, maxHeaderLength = 1024 * 8, additionalConnectParams) {
16158
16451
  const url = new URL(
16159
16452
  appendPath(socketOrigin, `/sync/v${PROTOCOL_VERSION}/connect`)
16160
16453
  );
16161
16454
  const { searchParams } = url;
16162
16455
  searchParams.set("clientID", clientID);
16163
16456
  searchParams.set("clientGroupID", clientGroupID);
16164
- searchParams.set("schemaVersion", schemaVersion.toString());
16165
16457
  searchParams.set("userID", userID);
16166
16458
  searchParams.set("baseCookie", baseCookie === null ? "" : String(baseCookie));
16167
16459
  searchParams.set("ts", String(performance.now()));
@@ -16189,7 +16481,10 @@ async function createSocket(rep, queryManager, deleteClientsManager, socketOrigi
16189
16481
  "initConnection",
16190
16482
  {
16191
16483
  desiredQueriesPatch: [...queriesPatch.values()],
16192
- deleted: skipEmptyDeletedClients(deletedClients)
16484
+ deleted: skipEmptyDeletedClients(deletedClients),
16485
+ // The clientSchema only needs to be sent for the very first request.
16486
+ // Henceforth it is stored with the CVR and verified automatically.
16487
+ ...baseCookie === null ? { clientSchema: clientSchema2 } : {}
16193
16488
  }
16194
16489
  ],
16195
16490
  auth
@@ -16264,6 +16559,7 @@ export {
16264
16559
  NOBODY_CAN,
16265
16560
  definePermissions,
16266
16561
  escapeLike,
16562
+ DEFAULT_TTL2 as DEFAULT_TTL,
16267
16563
  Zero
16268
16564
  };
16269
- //# sourceMappingURL=chunk-YKI43S5O.js.map
16565
+ //# sourceMappingURL=chunk-2TJNN7K4.js.map