@rocicorp/zero 0.22.2025063000 → 0.22.2025070600

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 (137) hide show
  1. package/out/analyze-query/src/bin-analyze.js +1 -0
  2. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  3. package/out/{chunk-J67XJRPS.js → chunk-5ZACJSVD.js} +3 -3
  4. package/out/{chunk-CESYRWV4.js → chunk-U52OBQ2U.js} +105 -69
  5. package/out/chunk-U52OBQ2U.js.map +7 -0
  6. package/out/{chunk-WOYR3FAT.js → chunk-ZGVTOORV.js} +303 -87
  7. package/out/{chunk-WOYR3FAT.js.map → chunk-ZGVTOORV.js.map} +4 -4
  8. package/out/{inspector-LF6RTN44.js → inspector-XJ4IL4MG.js} +7 -3
  9. package/out/{inspector-LF6RTN44.js.map → inspector-XJ4IL4MG.js.map} +2 -2
  10. package/out/react.js +3 -3
  11. package/out/react.js.map +2 -2
  12. package/out/replicache/src/btree/node.d.ts.map +1 -1
  13. package/out/replicache/src/subscriptions.d.ts.map +1 -1
  14. package/out/shared/src/binary-search.d.ts.map +1 -0
  15. package/out/solid.js +131 -99
  16. package/out/solid.js.map +4 -4
  17. package/out/zero-cache/src/config/zero-config.d.ts +8 -0
  18. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  19. package/out/zero-cache/src/config/zero-config.js +9 -0
  20. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  21. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  22. package/out/zero-cache/src/server/change-streamer.js +2 -2
  23. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  24. package/out/zero-cache/src/services/change-source/pg/copy-runner.d.ts.map +1 -1
  25. package/out/zero-cache/src/services/change-source/pg/copy-runner.js +4 -3
  26. package/out/zero-cache/src/services/change-source/pg/copy-runner.js.map +1 -1
  27. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  28. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +1 -1
  29. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  30. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +1 -1
  31. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  32. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +4 -4
  33. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  34. package/out/zero-cache/src/services/change-streamer/storer.d.ts +1 -1
  35. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  36. package/out/zero-cache/src/services/change-streamer/storer.js +10 -3
  37. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  38. package/out/zero-cache/src/services/mutagen/pusher.d.ts +6 -2
  39. package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
  40. package/out/zero-cache/src/services/view-syncer/cvr-store.js +8 -8
  41. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  42. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  43. package/out/zero-cache/src/services/view-syncer/cvr.js +13 -23
  44. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  45. package/out/zero-cache/src/services/view-syncer/schema/types.d.ts.map +1 -1
  46. package/out/zero-cache/src/services/view-syncer/schema/types.js +3 -0
  47. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  48. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +0 -3
  49. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  50. package/out/zero-cache/src/services/view-syncer/view-syncer.js +29 -28
  51. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  52. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  53. package/out/zero-cache/src/workers/connection.js +1 -25
  54. package/out/zero-cache/src/workers/connection.js.map +1 -1
  55. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  56. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +1 -1
  57. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  58. package/out/zero-client/src/client/active-clients-manager.d.ts +49 -0
  59. package/out/zero-client/src/client/active-clients-manager.d.ts.map +1 -0
  60. package/out/zero-client/src/client/context.d.ts +3 -1
  61. package/out/zero-client/src/client/context.d.ts.map +1 -1
  62. package/out/zero-client/src/client/custom.d.ts +6 -6
  63. package/out/zero-client/src/client/custom.d.ts.map +1 -1
  64. package/out/zero-client/src/client/inspector/types.d.ts +4 -2
  65. package/out/zero-client/src/client/inspector/types.d.ts.map +1 -1
  66. package/out/zero-client/src/client/options.d.ts +9 -2
  67. package/out/zero-client/src/client/options.d.ts.map +1 -1
  68. package/out/zero-client/src/client/query-manager.d.ts +2 -1
  69. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  70. package/out/zero-client/src/client/zero.d.ts +5 -4
  71. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  72. package/out/zero-protocol/src/close-connection.d.ts +4 -0
  73. package/out/zero-protocol/src/close-connection.d.ts.map +1 -1
  74. package/out/zero-protocol/src/close-connection.js +2 -0
  75. package/out/zero-protocol/src/close-connection.js.map +1 -1
  76. package/out/zero-protocol/src/connect.d.ts +14 -0
  77. package/out/zero-protocol/src/connect.d.ts.map +1 -1
  78. package/out/zero-protocol/src/connect.js +7 -0
  79. package/out/zero-protocol/src/connect.js.map +1 -1
  80. package/out/zero-protocol/src/down.d.ts +3 -1
  81. package/out/zero-protocol/src/down.d.ts.map +1 -1
  82. package/out/zero-protocol/src/inspect-down.d.ts +12 -4
  83. package/out/zero-protocol/src/inspect-down.d.ts.map +1 -1
  84. package/out/zero-protocol/src/inspect-down.js +7 -1
  85. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  86. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  87. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  88. package/out/zero-protocol/src/protocol-version.js +3 -1
  89. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  90. package/out/zero-protocol/src/up.d.ts +1 -0
  91. package/out/zero-protocol/src/up.d.ts.map +1 -1
  92. package/out/zero-react/src/zero-provider.d.ts +1 -1
  93. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  94. package/out/zero-solid/src/mod.d.ts +2 -2
  95. package/out/zero-solid/src/mod.d.ts.map +1 -1
  96. package/out/zero-solid/src/solid-view.d.ts +8 -6
  97. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  98. package/out/zero-solid/src/{create-query.d.ts → use-query.d.ts} +7 -7
  99. package/out/zero-solid/src/use-query.d.ts.map +1 -0
  100. package/out/zero-solid/src/use-zero.d.ts +11 -0
  101. package/out/zero-solid/src/use-zero.d.ts.map +1 -0
  102. package/out/zero.js +3 -3
  103. package/out/zql/src/ivm/take.d.ts.map +1 -1
  104. package/out/zql/src/ivm/take.js +6 -0
  105. package/out/zql/src/ivm/take.js.map +1 -1
  106. package/out/zql/src/ivm/view-apply-change.d.ts +2 -3
  107. package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
  108. package/out/zql/src/ivm/view-apply-change.js +28 -19
  109. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  110. package/out/zql/src/query/query-delegate.d.ts +1 -0
  111. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  112. package/out/zql/src/query/query-impl.d.ts +3 -3
  113. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  114. package/out/zql/src/query/query-impl.js +4 -4
  115. package/out/zql/src/query/query-impl.js.map +1 -1
  116. package/out/zql/src/query/query.d.ts +1 -1
  117. package/out/zql/src/query/ttl.d.ts +4 -0
  118. package/out/zql/src/query/ttl.d.ts.map +1 -1
  119. package/out/zql/src/query/ttl.js +12 -1
  120. package/out/zql/src/query/ttl.js.map +1 -1
  121. package/out/zqlite/src/query-delegate.d.ts +1 -0
  122. package/out/zqlite/src/query-delegate.d.ts.map +1 -1
  123. package/out/zqlite/src/query-delegate.js +1 -0
  124. package/out/zqlite/src/query-delegate.js.map +1 -1
  125. package/out/zqlite/src/table-source.d.ts.map +1 -1
  126. package/out/zqlite/src/table-source.js +12 -8
  127. package/out/zqlite/src/table-source.js.map +1 -1
  128. package/package.json +3 -3
  129. package/out/chunk-CESYRWV4.js.map +0 -7
  130. package/out/replicache/src/binary-search.d.ts.map +0 -1
  131. package/out/shared/src/ref-count.d.ts +0 -16
  132. package/out/shared/src/ref-count.d.ts.map +0 -1
  133. package/out/zero-solid/src/create-query.d.ts.map +0 -1
  134. package/out/zero-solid/src/create-zero.d.ts +0 -3
  135. package/out/zero-solid/src/create-zero.d.ts.map +0 -1
  136. /package/out/{chunk-J67XJRPS.js.map → chunk-5ZACJSVD.js.map} +0 -0
  137. /package/out/{replicache → shared}/src/binary-search.d.ts +0 -0
@@ -126,7 +126,7 @@ import {
126
126
  withWrite,
127
127
  withWriteNoImplicitCommit,
128
128
  wrapIterable
129
- } from "./chunk-CESYRWV4.js";
129
+ } from "./chunk-U52OBQ2U.js";
130
130
  import {
131
131
  __export
132
132
  } from "./chunk-424PT5DM.js";
@@ -5841,7 +5841,7 @@ var SchemaVersionNotSupported2 = "SchemaVersionNotSupported";
5841
5841
 
5842
5842
  // ../zero-client/src/client/zero.ts
5843
5843
  import "@rocicorp/logger";
5844
- import { resolver as resolver8 } from "@rocicorp/resolver";
5844
+ import { resolver as resolver9 } from "@rocicorp/resolver";
5845
5845
 
5846
5846
  // ../zero-protocol/src/delete-clients.ts
5847
5847
  var deleteClientsBodySchema = valita_exports.union(
@@ -5900,7 +5900,14 @@ var initConnectionBodySchema = valita_exports.object({
5900
5900
  desiredQueriesPatch: upQueriesPatchSchema,
5901
5901
  clientSchema: clientSchemaSchema.optional(),
5902
5902
  deleted: deleteClientsBodySchema.optional(),
5903
- userPushParams: userPushParamsSchema.optional()
5903
+ userPushParams: userPushParamsSchema.optional(),
5904
+ /**
5905
+ * `activeClients` is an optional array of client IDs that are currently active
5906
+ * in the client group. This is used to inform the server about the clients
5907
+ * that are currently active (aka running, aka alive), so it can inactive
5908
+ * queries from inactive clients.
5909
+ */
5910
+ activeClients: valita_exports.array(valita_exports.string()).optional()
5904
5911
  });
5905
5912
  var initConnectionMessageSchema = valita_exports.tuple([
5906
5913
  valita_exports.literal("initConnection"),
@@ -6241,7 +6248,7 @@ var downstreamSchema = valita_exports.union(
6241
6248
  );
6242
6249
 
6243
6250
  // ../zero-protocol/src/protocol-version.ts
6244
- var PROTOCOL_VERSION = 18;
6251
+ var PROTOCOL_VERSION = 20;
6245
6252
  var MIN_SERVER_SUPPORTED_SYNC_PROTOCOL = 6;
6246
6253
  assert(MIN_SERVER_SUPPORTED_SYNC_PROTOCOL < PROTOCOL_VERSION);
6247
6254
 
@@ -6338,6 +6345,201 @@ function send(ws, data) {
6338
6345
  ws.send(JSON.stringify(data));
6339
6346
  }
6340
6347
 
6348
+ // ../zero-client/src/client/active-clients-manager.ts
6349
+ import { resolver as resolver7 } from "@rocicorp/resolver";
6350
+ var keyPrefix = "zero-active";
6351
+ function toLockName(clientGroupID, clientID) {
6352
+ return `${keyPrefix}/${clientGroupID}/${clientID}`;
6353
+ }
6354
+ function toBroadcastChannelName(clientGroupID) {
6355
+ return `${keyPrefix}/${clientGroupID}`;
6356
+ }
6357
+ function fromLockName(lockKey) {
6358
+ if (!lockKey || !lockKey.startsWith(keyPrefix)) {
6359
+ return void 0;
6360
+ }
6361
+ const parts = lockKey.slice(keyPrefix.length).split("/");
6362
+ if (parts.length !== 3) {
6363
+ return void 0;
6364
+ }
6365
+ return {
6366
+ clientGroupID: parts[1],
6367
+ clientID: parts[2]
6368
+ };
6369
+ }
6370
+ function ignoreAbortError(e) {
6371
+ if (e instanceof Error && e.name === "AbortError") {
6372
+ return;
6373
+ }
6374
+ throw e;
6375
+ }
6376
+ var ActiveClientsManager = class _ActiveClientsManager {
6377
+ clientGroupID;
6378
+ clientID;
6379
+ #resolver = resolver7();
6380
+ #lockManager;
6381
+ #activeClients = /* @__PURE__ */ new Set();
6382
+ /**
6383
+ * A callback that is called when a client is added to the client group.
6384
+ * It receives the client ID of the added client.
6385
+ */
6386
+ onAdd;
6387
+ /**
6388
+ * A callback that is called when a client is deleted from the client group.
6389
+ * It receives the client ID of the deleted client.
6390
+ */
6391
+ onDelete;
6392
+ /**
6393
+ * Creates an instance of `ActiveClientsManager` for the specified client
6394
+ * group and client ID. It will return a promise that resolves when the
6395
+ * instance is ready to use, which means that it has successfully acquired the
6396
+ * exclusive lock for the client and has retrieved the list of active clients.
6397
+ */
6398
+ static async create(clientGroupID, clientID, signal) {
6399
+ const instance = new _ActiveClientsManager(clientGroupID, clientID, signal);
6400
+ await instance.#init(clientGroupID, clientID, signal);
6401
+ return instance;
6402
+ }
6403
+ constructor(clientGroupID, clientID, signal) {
6404
+ this.clientGroupID = clientGroupID;
6405
+ this.clientID = clientID;
6406
+ this.#lockManager = getClientLockManager(signal);
6407
+ this.#activeClients.add(clientID);
6408
+ }
6409
+ async #init(clientGroupID, clientID, signal) {
6410
+ const name = toLockName(clientGroupID, clientID);
6411
+ const channel = new bc(toBroadcastChannelName(clientGroupID));
6412
+ channel.addEventListener(
6413
+ "message",
6414
+ (e) => {
6415
+ const client = fromLockName(e.data);
6416
+ if (client?.clientGroupID === this.clientGroupID) {
6417
+ this.#addClient(client.clientID);
6418
+ }
6419
+ },
6420
+ { signal }
6421
+ );
6422
+ this.#lockManager.request(name, "exclusive", () => this.#resolver.promise).catch(ignoreAbortError);
6423
+ signal.addEventListener(
6424
+ "abort",
6425
+ () => {
6426
+ this.#lockManager.release(name, () => this.#resolver.resolve());
6427
+ channel.close();
6428
+ },
6429
+ { once: true }
6430
+ );
6431
+ for (const clientID2 of await this.#getActiveClients()) {
6432
+ if (clientID2 !== this.clientID) {
6433
+ this.#addClient(clientID2);
6434
+ }
6435
+ }
6436
+ if (!signal.aborted) {
6437
+ channel.postMessage(name);
6438
+ }
6439
+ }
6440
+ get activeClients() {
6441
+ return this.#activeClients;
6442
+ }
6443
+ async #getActiveClients() {
6444
+ const activeClients = /* @__PURE__ */ new Set();
6445
+ for await (const lockName of this.#lockManager.queryExclusive()) {
6446
+ const client = fromLockName(lockName);
6447
+ if (client?.clientGroupID === this.clientGroupID) {
6448
+ activeClients.add(client.clientID);
6449
+ }
6450
+ }
6451
+ return activeClients;
6452
+ }
6453
+ /**
6454
+ * This gets called when a new client is added to the client group.
6455
+ *
6456
+ * It will request a shared lock for the client, and when the exclusive lock
6457
+ * is released, it will notify that the client has been deactivated.
6458
+ */
6459
+ #addSharedLockForOtherClient(clientID) {
6460
+ const name = toLockName(this.clientGroupID, clientID);
6461
+ this.#lockManager.request(name, "shared", () => this.#removeClient(clientID)).catch(ignoreAbortError);
6462
+ }
6463
+ #addClient(clientID) {
6464
+ if (!this.#activeClients.has(clientID)) {
6465
+ this.#activeClients.add(clientID);
6466
+ this.#addSharedLockForOtherClient(clientID);
6467
+ this.onAdd?.(clientID);
6468
+ }
6469
+ }
6470
+ #removeClient(clientID) {
6471
+ if (this.#activeClients.delete(clientID)) {
6472
+ this.onDelete?.(clientID);
6473
+ }
6474
+ }
6475
+ };
6476
+ function getClientLockManager(signal) {
6477
+ const locks = getBrowserGlobal("navigator")?.locks;
6478
+ if (locks) {
6479
+ return new NativeClientLockManager(locks, signal);
6480
+ }
6481
+ return new MockClientLockManager();
6482
+ }
6483
+ var NativeClientLockManager = class {
6484
+ #locks;
6485
+ #signal;
6486
+ constructor(locks, signal) {
6487
+ this.#locks = locks;
6488
+ this.#signal = signal;
6489
+ }
6490
+ request(name, mode, fn) {
6491
+ return this.#locks.request(name, { mode, signal: this.#signal }, fn);
6492
+ }
6493
+ release(_name, fn) {
6494
+ fn();
6495
+ }
6496
+ async *queryExclusive() {
6497
+ const snapshot = await this.#locks.query();
6498
+ for (const lock of [
6499
+ ...snapshot.held ?? [],
6500
+ ...snapshot.pending ?? []
6501
+ ]) {
6502
+ if (lock.mode === "exclusive" && lock.name) {
6503
+ yield lock.name;
6504
+ }
6505
+ }
6506
+ }
6507
+ };
6508
+ var mockLockNames = /* @__PURE__ */ new Set();
6509
+ var mockListeners = /* @__PURE__ */ new Set();
6510
+ var MockClientLockManager = class {
6511
+ #listeners = /* @__PURE__ */ new Set();
6512
+ request(name, mode, fn) {
6513
+ if (mode === "exclusive") {
6514
+ mockLockNames.add(name);
6515
+ } else {
6516
+ mode;
6517
+ const listener = (removed) => {
6518
+ if (removed === name) {
6519
+ mockListeners.delete(listener);
6520
+ return fn();
6521
+ }
6522
+ };
6523
+ mockListeners.add(listener);
6524
+ this.#listeners.add(listener);
6525
+ }
6526
+ return Promise.resolve();
6527
+ }
6528
+ release(name, fn) {
6529
+ mockLockNames.delete(name);
6530
+ for (const listener of mockListeners) {
6531
+ listener(name);
6532
+ }
6533
+ for (const listener of this.#listeners) {
6534
+ mockListeners.delete(listener);
6535
+ }
6536
+ fn();
6537
+ }
6538
+ async *queryExclusive() {
6539
+ yield* mockLockNames;
6540
+ }
6541
+ };
6542
+
6341
6543
  // ../zero-client/src/client/connection-state-enum.ts
6342
6544
  var Disconnected = 0;
6343
6545
  var Connecting = 1;
@@ -7580,6 +7782,7 @@ var ZeroContext = class {
7580
7782
  #addCustomQuery;
7581
7783
  #updateQuery;
7582
7784
  #updateCustomQuery;
7785
+ #flushQueryChanges;
7583
7786
  #batchViewUpdates;
7584
7787
  #commitListeners = /* @__PURE__ */ new Set();
7585
7788
  #slowMaterializeThreshold;
@@ -7590,7 +7793,7 @@ var ZeroContext = class {
7590
7793
  * "complete" once the server has sent back the query result.
7591
7794
  */
7592
7795
  defaultQueryComplete = false;
7593
- constructor(lc, mainSources, addQuery, addCustomQuery, updateQuery, updateCustomQuery, batchViewUpdates, slowMaterializeThreshold, assertValidRunOptions3) {
7796
+ constructor(lc, mainSources, addQuery, addCustomQuery, updateQuery, updateCustomQuery, flushQueryChanges, batchViewUpdates, slowMaterializeThreshold, assertValidRunOptions3) {
7594
7797
  this.#mainSources = mainSources;
7595
7798
  this.#addQuery = addQuery;
7596
7799
  this.#updateQuery = updateQuery;
@@ -7600,6 +7803,7 @@ var ZeroContext = class {
7600
7803
  this.#slowMaterializeThreshold = slowMaterializeThreshold;
7601
7804
  this.assertValidRunOptions = assertValidRunOptions3;
7602
7805
  this.#addCustomQuery = addCustomQuery;
7806
+ this.#flushQueryChanges = flushQueryChanges;
7603
7807
  }
7604
7808
  getSource(name) {
7605
7809
  return this.#mainSources.getSource(name);
@@ -7621,6 +7825,9 @@ var ZeroContext = class {
7621
7825
  updateCustomQuery(customQueryID, ttl) {
7622
7826
  this.#updateCustomQuery(customQueryID.name, customQueryID.args, ttl);
7623
7827
  }
7828
+ flushQueryChanges() {
7829
+ this.#flushQueryChanges();
7830
+ }
7624
7831
  onQueryMaterialized(hash, ast, duration) {
7625
7832
  if (this.#slowMaterializeThreshold !== void 0 && duration > this.#slowMaterializeThreshold) {
7626
7833
  this.#lc.warn?.(
@@ -7984,6 +8191,7 @@ function makeSchemaQuery(lc, schema, ivmBranch, slowMaterializeThreshold) {
7984
8191
  () => emptyFunction,
7985
8192
  emptyFunction,
7986
8193
  emptyFunction,
8194
+ emptyFunction,
7987
8195
  (applyViewUpdates) => applyViewUpdates(),
7988
8196
  slowMaterializeThreshold,
7989
8197
  assertValidRunOptions
@@ -8331,7 +8539,7 @@ function makeMessage(message, context, logLevel) {
8331
8539
  }
8332
8540
 
8333
8541
  // ../zero-client/src/client/version.ts
8334
- var version2 = "0.22.2025063000";
8542
+ var version2 = "0.22.2025070600";
8335
8543
 
8336
8544
  // ../zero-client/src/client/log-options.ts
8337
8545
  var LevelFilterLogSink = class {
@@ -8626,7 +8834,7 @@ var State = class {
8626
8834
  };
8627
8835
 
8628
8836
  // ../zero-client/src/client/mutation-tracker.ts
8629
- import { resolver as resolver7 } from "@rocicorp/resolver";
8837
+ import { resolver as resolver8 } from "@rocicorp/resolver";
8630
8838
  var transientPushErrorTypes = [
8631
8839
  "zeroPusher",
8632
8840
  "http",
@@ -8656,7 +8864,7 @@ var MutationTracker = class {
8656
8864
  }
8657
8865
  trackMutation() {
8658
8866
  const id = nextEphemeralID();
8659
- const mutationResolver = resolver7();
8867
+ const mutationResolver = resolver8();
8660
8868
  this.#outstandingMutations.set(id, {
8661
8869
  resolver: mutationResolver
8662
8870
  });
@@ -8820,13 +9028,17 @@ var QueryManager = class {
8820
9028
  #recentQueries = /* @__PURE__ */ new Set();
8821
9029
  #gotQueries = /* @__PURE__ */ new Set();
8822
9030
  #mutationTracker;
9031
+ #pendingQueryChanges = [];
9032
+ #queryChangeThrottleMs;
8823
9033
  #pendingRemovals = [];
8824
- constructor(mutationTracker, clientID, tables, send2, experimentalWatch, recentQueriesMaxSize) {
9034
+ #batchTimer;
9035
+ constructor(mutationTracker, clientID, tables, send2, experimentalWatch, recentQueriesMaxSize, queryChangeThrottleMs) {
8825
9036
  this.#clientID = clientID;
8826
9037
  this.#clientToServer = clientToServer(tables);
8827
9038
  this.#recentQueriesMaxSize = recentQueriesMaxSize;
8828
9039
  this.#send = send2;
8829
9040
  this.#mutationTracker = mutationTracker;
9041
+ this.#queryChangeThrottleMs = queryChangeThrottleMs;
8830
9042
  this.#mutationTracker.onAllMutationsConfirmed(() => {
8831
9043
  if (this.#pendingRemovals.length === 0) {
8832
9044
  return;
@@ -8965,21 +9177,14 @@ var QueryManager = class {
8965
9177
  ttl
8966
9178
  };
8967
9179
  this.#queries.set(queryId, entry);
8968
- this.#send([
8969
- "changeDesiredQueries",
8970
- {
8971
- desiredQueriesPatch: [
8972
- {
8973
- op: "put",
8974
- hash: queryId,
8975
- ast: normalized,
8976
- name,
8977
- args,
8978
- ttl: parseTTL(ttl)
8979
- }
8980
- ]
8981
- }
8982
- ]);
9180
+ this.#queueQueryChange({
9181
+ op: "put",
9182
+ hash: queryId,
9183
+ ast: normalized,
9184
+ name,
9185
+ args,
9186
+ ttl: parseTTL(ttl)
9187
+ });
8983
9188
  } else {
8984
9189
  ++entry.count;
8985
9190
  this.#updateEntry(entry, queryId, ttl);
@@ -9019,21 +9224,41 @@ var QueryManager = class {
9019
9224
  #updateEntry(entry, hash, ttl) {
9020
9225
  if (compareTTL(ttl, entry.ttl) > 0) {
9021
9226
  entry.ttl = ttl;
9227
+ this.#queueQueryChange({
9228
+ op: "put",
9229
+ hash,
9230
+ ast: entry.normalized,
9231
+ name: entry.name,
9232
+ args: entry.args,
9233
+ ttl: parseTTL(ttl)
9234
+ });
9235
+ }
9236
+ }
9237
+ #queueQueryChange(op) {
9238
+ this.#pendingQueryChanges.push(op);
9239
+ this.#scheduleBatch();
9240
+ }
9241
+ #scheduleBatch() {
9242
+ if (this.#batchTimer === void 0) {
9243
+ this.#batchTimer = setTimeout(
9244
+ () => this.flushBatch(),
9245
+ this.#queryChangeThrottleMs
9246
+ );
9247
+ }
9248
+ }
9249
+ flushBatch() {
9250
+ if (this.#batchTimer !== void 0) {
9251
+ clearTimeout(this.#batchTimer);
9252
+ this.#batchTimer = void 0;
9253
+ }
9254
+ if (this.#pendingQueryChanges.length > 0) {
9022
9255
  this.#send([
9023
9256
  "changeDesiredQueries",
9024
9257
  {
9025
- desiredQueriesPatch: [
9026
- {
9027
- op: "put",
9028
- hash,
9029
- ast: entry.normalized,
9030
- name: entry.name,
9031
- args: entry.args,
9032
- ttl: parseTTL(ttl)
9033
- }
9034
- ]
9258
+ desiredQueriesPatch: [...this.#pendingQueryChanges]
9035
9259
  }
9036
9260
  ]);
9261
+ this.#pendingQueryChanges.length = 0;
9037
9262
  }
9038
9263
  }
9039
9264
  #remove(entry, astHash, gotCallback) {
@@ -9049,12 +9274,7 @@ var QueryManager = class {
9049
9274
  assert(lruAstHash);
9050
9275
  this.#queries.delete(lruAstHash);
9051
9276
  this.#recentQueries.delete(lruAstHash);
9052
- this.#send([
9053
- "changeDesiredQueries",
9054
- {
9055
- desiredQueriesPatch: [{ op: "del", hash: lruAstHash }]
9056
- }
9057
- ]);
9277
+ this.#queueQueryChange({ op: "del", hash: lruAstHash });
9058
9278
  }
9059
9279
  }
9060
9280
  }
@@ -9600,6 +9820,7 @@ var DEFAULT_DISCONNECT_HIDDEN_DELAY_MS = 5e3;
9600
9820
  var CONNECT_TIMEOUT_MS = 1e4;
9601
9821
  var CHECK_CONNECTIVITY_ON_ERROR_FREQUENCY = 6;
9602
9822
  var NULL_LAST_MUTATION_ID_SENT = { clientID: "", id: -1 };
9823
+ var DEFAULT_QUERY_CHANGE_THROTTLE_MS = 10;
9603
9824
  function convertOnUpdateNeededReason(reason) {
9604
9825
  return { type: reason.type };
9605
9826
  }
@@ -9684,12 +9905,12 @@ var Zero = class _Zero {
9684
9905
  };
9685
9906
  #zeroContext;
9686
9907
  queryDelegate;
9687
- #connectResolver = resolver8();
9908
+ #connectResolver = resolver9();
9688
9909
  #pendingPullsByRequestID = /* @__PURE__ */ new Map();
9689
9910
  #lastMutationIDReceived = 0;
9690
9911
  #socket = void 0;
9691
- #socketResolver = resolver8();
9692
- #connectionStateChangeResolver = resolver8();
9912
+ #socketResolver = resolver9();
9913
+ #connectionStateChangeResolver = resolver9();
9693
9914
  /**
9694
9915
  * This resolver is only used for rejections. It is awaited in the connected
9695
9916
  * state (including when waiting for a pong). It is rejected when we get an
@@ -9700,13 +9921,14 @@ var Zero = class _Zero {
9700
9921
  #visibilityWatcher;
9701
9922
  // We use an accessor pair to allow the subclass to override the setter.
9702
9923
  #connectionState = Disconnected;
9924
+ #activeClientsManager;
9703
9925
  #setConnectionState(state) {
9704
9926
  if (state === this.#connectionState) {
9705
9927
  return;
9706
9928
  }
9707
9929
  this.#connectionState = state;
9708
9930
  this.#connectionStateChangeResolver.resolve(state);
9709
- this.#connectionStateChangeResolver = resolver8();
9931
+ this.#connectionStateChangeResolver = resolver9();
9710
9932
  if (false) {
9711
9933
  asTestZero(this)[onSetConnectionStateSymbol]?.(state);
9712
9934
  }
@@ -9866,6 +10088,7 @@ var Zero = class _Zero {
9866
10088
  (queryName, queryArgs, ttl, gotCallback) => this.#queryManager.addCustom(queryName, queryArgs, ttl, gotCallback),
9867
10089
  (ast, ttl) => this.#queryManager.updateLegacy(ast, ttl),
9868
10090
  (name, args, ttl) => this.#queryManager.updateCustom(name, args, ttl),
10091
+ () => this.#queryManager.flushBatch(),
9869
10092
  batchViewUpdates,
9870
10093
  slowMaterializeThreshold,
9871
10094
  assertValidRunOptions2
@@ -9893,6 +10116,12 @@ var Zero = class _Zero {
9893
10116
  this.userID = userID;
9894
10117
  this.#lc = lc.withContext("clientID", rep.clientID);
9895
10118
  this.#mutationTracker.clientID = rep.clientID;
10119
+ this.#activeClientsManager = makeActiveClientsManager(
10120
+ rep.clientGroupID,
10121
+ this.clientID,
10122
+ this.#closeAbortController.signal,
10123
+ (clientID) => this.#deleteClientsManager.onClientsDeleted([clientID], [])
10124
+ );
9896
10125
  const onUpdateNeededCallback = (reason, serverErrorMsg) => {
9897
10126
  if (onUpdateNeeded) {
9898
10127
  onUpdateNeeded(reason);
@@ -9948,7 +10177,8 @@ var Zero = class _Zero {
9948
10177
  schema.tables,
9949
10178
  (msg) => this.#send(msg),
9950
10179
  rep.experimentalWatch.bind(rep),
9951
- maxRecentQueries
10180
+ maxRecentQueries,
10181
+ options.queryChangeThrottleMs ?? DEFAULT_QUERY_CHANGE_THROTTLE_MS
9952
10182
  );
9953
10183
  this.#clientToServer = clientToServer(schema.tables);
9954
10184
  this.#deleteClientsManager = new DeleteClientsManager(
@@ -10124,21 +10354,12 @@ var Zero = class _Zero {
10124
10354
  const lc = this.#lc.withContext("close");
10125
10355
  lc.debug?.("Closing Zero instance. Stack:", new Error().stack);
10126
10356
  if (this.#connectionState !== Disconnected) {
10127
- let closeReason = JSON.stringify([
10128
- "closeConnection",
10129
- []
10130
- ]);
10131
- if (closeReason.length > 123) {
10132
- lc.warn?.("Close reason is too long. Removing it.");
10133
- closeReason = "";
10134
- }
10135
10357
  this.#disconnect(
10136
10358
  lc,
10137
10359
  {
10138
10360
  client: "ClientClosed"
10139
10361
  },
10140
- CLOSE_CODE_NORMAL,
10141
- closeReason
10362
+ CLOSE_CODE_NORMAL
10142
10363
  );
10143
10364
  }
10144
10365
  lc.debug?.("Aborting closeAbortController due to close()");
@@ -10148,15 +10369,6 @@ var Zero = class _Zero {
10148
10369
  this.#unexpose();
10149
10370
  return ret;
10150
10371
  }
10151
- #onPageHide = (e) => {
10152
- if (e.persisted) {
10153
- this.#lc.debug?.("Ignoring pagehide event because it was persisted");
10154
- } else {
10155
- this.#lc.debug?.("Closing client because we got a clean close");
10156
- this.close().catch(() => {
10157
- });
10158
- }
10159
- };
10160
10372
  #onMessage = (e) => {
10161
10373
  const lc = this.#lc;
10162
10374
  lc.debug?.("received message", e.data);
@@ -10417,7 +10629,8 @@ var Zero = class _Zero {
10417
10629
  lc,
10418
10630
  this.#options.push,
10419
10631
  this.#options.maxHeaderLength,
10420
- additionalConnectParams
10632
+ additionalConnectParams,
10633
+ await this.#activeClientsManager
10421
10634
  );
10422
10635
  if (this.closed) {
10423
10636
  return;
@@ -10429,10 +10642,6 @@ var Zero = class _Zero {
10429
10642
  ws.addEventListener("close", this.#onClose);
10430
10643
  this.#socket = ws;
10431
10644
  this.#socketResolver.resolve(ws);
10432
- getBrowserGlobal("window")?.addEventListener?.(
10433
- "pagehide",
10434
- this.#onPageHide
10435
- );
10436
10645
  try {
10437
10646
  lc.debug?.("Waiting for connection to be acknowledged");
10438
10647
  await this.#connectResolver.promise;
@@ -10447,7 +10656,7 @@ var Zero = class _Zero {
10447
10656
  );
10448
10657
  }
10449
10658
  }
10450
- #disconnect(lc, reason, closeCode, closeReason) {
10659
+ #disconnect(lc, reason, closeCode) {
10451
10660
  if (this.#connectionState === Connecting) {
10452
10661
  this.#connectErrorCount++;
10453
10662
  }
@@ -10491,9 +10700,9 @@ var Zero = class _Zero {
10491
10700
  lc.error?.("disconnect() called while disconnected");
10492
10701
  break;
10493
10702
  }
10494
- this.#socketResolver = resolver8();
10703
+ this.#socketResolver = resolver9();
10495
10704
  lc.debug?.("Creating new connect resolver");
10496
- this.#connectResolver = resolver8();
10705
+ this.#connectResolver = resolver9();
10497
10706
  this.#setConnectionState(Disconnected);
10498
10707
  this.#messageCount = 0;
10499
10708
  this.#connectStart = void 0;
@@ -10501,14 +10710,10 @@ var Zero = class _Zero {
10501
10710
  this.#socket?.removeEventListener("message", this.#onMessage);
10502
10711
  this.#socket?.removeEventListener("open", this.#onOpen);
10503
10712
  this.#socket?.removeEventListener("close", this.#onClose);
10504
- this.#socket?.close(closeCode, closeReason);
10713
+ this.#socket?.close(closeCode);
10505
10714
  this.#socket = void 0;
10506
10715
  this.#lastMutationIDSent = NULL_LAST_MUTATION_ID_SENT;
10507
10716
  this.#pokeHandler.handleDisconnect();
10508
- getBrowserGlobal("window")?.removeEventListener?.(
10509
- "pagehide",
10510
- this.#onPageHide
10511
- );
10512
10717
  }
10513
10718
  #handlePokeStart(_lc, pokeMessage) {
10514
10719
  this.#abortPingTimeout();
@@ -10544,12 +10749,12 @@ var Zero = class _Zero {
10544
10749
  const body = pullResponseMessage[1];
10545
10750
  lc = lc.withContext("requestID", body.requestID);
10546
10751
  lc.debug?.("Handling pull response", body);
10547
- const resolver9 = this.#pendingPullsByRequestID.get(body.requestID);
10548
- if (!resolver9) {
10752
+ const resolver10 = this.#pendingPullsByRequestID.get(body.requestID);
10753
+ if (!resolver10) {
10549
10754
  lc.debug?.("No resolver found");
10550
10755
  return;
10551
10756
  }
10552
- resolver9.resolve(pullResponseMessage[1]);
10757
+ resolver10.resolve(pullResponseMessage[1]);
10553
10758
  }
10554
10759
  async #pusher(req, requestID) {
10555
10760
  assert(req.pushVersion === 1);
@@ -10680,7 +10885,7 @@ var Zero = class _Zero {
10680
10885
  PING_INTERVAL_MS,
10681
10886
  controller.signal
10682
10887
  );
10683
- this.#rejectMessageError = resolver8();
10888
+ this.#rejectMessageError = resolver9();
10684
10889
  const PING = 0;
10685
10890
  const HIDDEN = 2;
10686
10891
  const raceResult = await promiseRace([
@@ -10790,7 +10995,7 @@ var Zero = class _Zero {
10790
10995
  }
10791
10996
  ];
10792
10997
  send(socket, pullRequestMessage);
10793
- const pullResponseResolver = resolver8();
10998
+ const pullResponseResolver = resolver9();
10794
10999
  this.#pendingPullsByRequestID.set(requestID, pullResponseResolver);
10795
11000
  try {
10796
11001
  const TIMEOUT = 0;
@@ -10848,7 +11053,7 @@ var Zero = class _Zero {
10848
11053
  */
10849
11054
  async #ping(lc, messageErrorRejectionPromise) {
10850
11055
  lc.debug?.("pinging");
10851
- const { promise, resolve } = resolver8();
11056
+ const { promise, resolve } = resolver9();
10852
11057
  this.#onPong = resolve;
10853
11058
  const pingMessage = ["ping", {}];
10854
11059
  const t0 = performance.now();
@@ -10894,7 +11099,7 @@ var Zero = class _Zero {
10894
11099
  */
10895
11100
  async inspect() {
10896
11101
  BUNDLE_SIZE: {
10897
- const m = await import("./inspector-LF6RTN44.js");
11102
+ const m = await import("./inspector-XJ4IL4MG.js");
10898
11103
  return m.newInspector(this.#rep, this.#schema, async () => {
10899
11104
  await this.#connectResolver.promise;
10900
11105
  return this.#socket;
@@ -10902,7 +11107,7 @@ var Zero = class _Zero {
10902
11107
  }
10903
11108
  }
10904
11109
  };
10905
- async function createSocket(rep, queryManager, deleteClientsManager, socketOrigin, baseCookie, clientID, clientGroupID, clientSchema, userID, auth, lmid, wsid, debugPerf, lc, userPushParams, maxHeaderLength = 1024 * 8, additionalConnectParams) {
11110
+ async function createSocket(rep, queryManager, deleteClientsManager, socketOrigin, baseCookie, clientID, clientGroupID, clientSchema, userID, auth, lmid, wsid, debugPerf, lc, userPushParams, maxHeaderLength = 1024 * 8, additionalConnectParams, activeClientsManager) {
10906
11111
  const url = new URL(
10907
11112
  appendPath(socketOrigin, `/sync/v${PROTOCOL_VERSION}/connect`)
10908
11113
  );
@@ -10931,6 +11136,7 @@ async function createSocket(rep, queryManager, deleteClientsManager, socketOrigi
10931
11136
  const queriesPatchP = rep.query((tx) => queryManager.getQueriesPatch(tx));
10932
11137
  let deletedClients = await deleteClientsManager.getDeletedClients();
10933
11138
  let queriesPatch = await queriesPatchP;
11139
+ const { activeClients } = activeClientsManager;
10934
11140
  let secProtocol = encodeSecProtocols(
10935
11141
  [
10936
11142
  "initConnection",
@@ -10940,7 +11146,8 @@ async function createSocket(rep, queryManager, deleteClientsManager, socketOrigi
10940
11146
  // The clientSchema only needs to be sent for the very first request.
10941
11147
  // Henceforth it is stored with the CVR and verified automatically.
10942
11148
  ...baseCookie === null ? { clientSchema } : {},
10943
- userPushParams
11149
+ userPushParams,
11150
+ activeClients: [...activeClients]
10944
11151
  }
10945
11152
  ],
10946
11153
  auth
@@ -11001,6 +11208,15 @@ var CloseError = class extends Error {
11001
11208
  };
11002
11209
  function assertValidRunOptions2(_options) {
11003
11210
  }
11211
+ async function makeActiveClientsManager(clientGroupID, clientID, signal, onDelete) {
11212
+ const manager = await ActiveClientsManager.create(
11213
+ await clientGroupID,
11214
+ clientID,
11215
+ signal
11216
+ );
11217
+ manager.onDelete = onDelete;
11218
+ return manager;
11219
+ }
11004
11220
 
11005
11221
  export {
11006
11222
  getDefaultPuller,
@@ -11021,4 +11237,4 @@ export {
11021
11237
  update_needed_reason_type_enum_exports,
11022
11238
  Zero
11023
11239
  };
11024
- //# sourceMappingURL=chunk-WOYR3FAT.js.map
11240
+ //# sourceMappingURL=chunk-ZGVTOORV.js.map