@rebasepro/server 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/dist/{GCSStorageController-C7ARrDAp.js → GCSStorageController-40FyFeSx.js} +8 -4
  2. package/dist/GCSStorageController-40FyFeSx.js.map +1 -0
  3. package/dist/{S3StorageController-qevJ0pVE.js → S3StorageController-Bl5shp94.js} +8 -4
  4. package/dist/S3StorageController-Bl5shp94.js.map +1 -0
  5. package/dist/access-BFAUvsSs.js +1897 -0
  6. package/dist/access-BFAUvsSs.js.map +1 -0
  7. package/dist/api/branch-routes.d.ts +25 -0
  8. package/dist/api/closed-database.d.ts +25 -0
  9. package/dist/api/live-schema-routes.d.ts +10 -0
  10. package/dist/api/rest/api-generator.d.ts +9 -1
  11. package/dist/api/rest/idempotency.d.ts +15 -1
  12. package/dist/api/root-error-handler.d.ts +6 -5
  13. package/dist/api/types.d.ts +12 -0
  14. package/dist/{ast-schema-editor-mCeSC03x.js → ast-schema-editor-madTa9Hh.js} +2 -2
  15. package/dist/{ast-schema-editor-mCeSC03x.js.map → ast-schema-editor-madTa9Hh.js.map} +1 -1
  16. package/dist/auth/access.d.ts +10 -0
  17. package/dist/auth/adapter-middleware.d.ts +2 -1
  18. package/dist/auth/adapter-service-key.d.ts +24 -0
  19. package/dist/auth/api-keys/api-key-middleware.d.ts +21 -1
  20. package/dist/auth/api-keys/api-key-store.d.ts +8 -1
  21. package/dist/auth/api-keys/key-grant.d.ts +18 -1
  22. package/dist/auth/expired-token-sweep.d.ts +5 -0
  23. package/dist/auth/impersonation.d.ts +3 -1
  24. package/dist/auth/jwt.d.ts +8 -2
  25. package/dist/auth/oauth-signin-policy.d.ts +21 -35
  26. package/dist/auth/rls-scope.d.ts +17 -7
  27. package/dist/auth/rls-simulator-routes.d.ts +51 -0
  28. package/dist/auth/simulation-read-only.d.ts +28 -0
  29. package/dist/auth/simulation.d.ts +141 -0
  30. package/dist/auth/sql-rate-limit-store.d.ts +15 -1
  31. package/dist/{auth-CteGWxsL.js → auth-izI7AEzC.js} +391 -1893
  32. package/dist/auth-izI7AEzC.js.map +1 -0
  33. package/dist/auth_adapter-B7RUMk6e.js +89 -0
  34. package/dist/auth_adapter-B7RUMk6e.js.map +1 -0
  35. package/dist/backend/boot-announcements.d.ts +13 -0
  36. package/dist/backend/create-backend.d.ts +10 -0
  37. package/dist/backend/options.d.ts +9 -4
  38. package/dist/{backend-DTAOsLQc.js → backend-CaG_Jyso.js} +9 -2
  39. package/dist/backend-CaG_Jyso.js.map +1 -0
  40. package/dist/{backup-Dq0lyJ_D.js → backup-BVbHoKo8.js} +8 -8
  41. package/dist/{backup-Dq0lyJ_D.js.map → backup-BVbHoKo8.js.map} +1 -1
  42. package/dist/boot/boot.d.ts +2 -15
  43. package/dist/boot/ddl-bootstrap.d.ts +34 -0
  44. package/dist/boot/env.d.ts +6 -0
  45. package/dist/boot/fetch-bundle.d.ts +5 -1
  46. package/dist/boot/provision-only.d.ts +23 -0
  47. package/dist/boot/provision.d.ts +53 -2
  48. package/dist/boot/role.d.ts +2 -0
  49. package/dist/boot/schema-change-recorder.d.ts +45 -0
  50. package/dist/boot/security-headers.d.ts +22 -0
  51. package/dist/branch-routes-B2vvEx53.js +84 -0
  52. package/dist/branch-routes-B2vvEx53.js.map +1 -0
  53. package/dist/{contract-routes-DiFwiLg2.js → contract-routes-CU2A3JnF.js} +3 -3
  54. package/dist/{contract-routes-DiFwiLg2.js.map → contract-routes-CU2A3JnF.js.map} +1 -1
  55. package/dist/cron/cron-store.d.ts +8 -1
  56. package/dist/{cron-loader-NHqRfKF0.js → cron-loader-mMhqbVsl.js} +3 -3
  57. package/dist/{cron-loader-NHqRfKF0.js.map → cron-loader-mMhqbVsl.js.map} +1 -1
  58. package/dist/{cron-routes-Dhf82HzT.js → cron-routes-Cs33j_Kr.js} +5 -5
  59. package/dist/{cron-routes-Dhf82HzT.js.map → cron-routes-Cs33j_Kr.js.map} +1 -1
  60. package/dist/{cron-scheduler-D1DT0nw5.js → cron-scheduler-Be7CNghL.js} +3 -3
  61. package/dist/{cron-scheduler-D1DT0nw5.js.map → cron-scheduler-Be7CNghL.js.map} +1 -1
  62. package/dist/{cron-store-DEpVstUU.js → cron-store-DLn11M8H.js} +11 -10
  63. package/dist/cron-store-DLn11M8H.js.map +1 -0
  64. package/dist/{strings.es-BeZWAodn.js → data_driver-D0mg1Y_x.js} +2 -90
  65. package/dist/data_driver-D0mg1Y_x.js.map +1 -0
  66. package/dist/{ddl-bootstrap-BNqi2NZe.js → ddl-bootstrap-CjRrn12l.js} +92 -3
  67. package/dist/ddl-bootstrap-CjRrn12l.js.map +1 -0
  68. package/dist/deploy/pod-contract.d.ts +1 -1
  69. package/dist/email/dev-sink.d.ts +6 -0
  70. package/dist/email/index.d.ts +1 -1
  71. package/dist/{errors-_ytoGi8A.js → errors-OMWkKSH-.js} +66 -5
  72. package/dist/errors-OMWkKSH-.js.map +1 -0
  73. package/dist/{soft-delete-params-BAgpmBvp.js → field-access-DYsvjI_P.js} +27 -57
  74. package/dist/field-access-DYsvjI_P.js.map +1 -0
  75. package/dist/{filter-dialect-O1HK6Nvr.js → filter-dialect-TrKFWLqT.js} +3 -3
  76. package/dist/{filter-dialect-O1HK6Nvr.js.map → filter-dialect-TrKFWLqT.js.map} +1 -1
  77. package/dist/{function-loader-DbCSoNJj.js → function-loader-B8YzXEsQ.js} +22 -15
  78. package/dist/function-loader-B8YzXEsQ.js.map +1 -0
  79. package/dist/{function-routes-DNV4RUF0.js → function-routes-RxrCHNuM.js} +2 -2
  80. package/dist/{function-routes-DNV4RUF0.js.map → function-routes-RxrCHNuM.js.map} +1 -1
  81. package/dist/functions/hono-like.d.ts +12 -0
  82. package/dist/functions/index.js +37 -2
  83. package/dist/functions/index.js.map +1 -1
  84. package/dist/functions/request-timeout.d.ts +3 -3
  85. package/dist/history/history-routes.d.ts +6 -0
  86. package/dist/history/retention-sweep.d.ts +5 -0
  87. package/dist/{history-recorder-RZ1Ow4YY.js → history-recorder-Bsau7h4X.js} +7 -7
  88. package/dist/history-recorder-Bsau7h4X.js.map +1 -0
  89. package/dist/{history-store-DPofGVDp.js → history-store-BQz5YCkW.js} +26 -2
  90. package/dist/history-store-BQz5YCkW.js.map +1 -0
  91. package/dist/index.d.ts +3 -1
  92. package/dist/index.es.js +3003 -681
  93. package/dist/index.es.js.map +1 -1
  94. package/dist/init/process-safety.d.ts +4 -3
  95. package/dist/init/shutdown.d.ts +8 -0
  96. package/dist/init.d.ts +6 -1
  97. package/dist/jobs/job-store.d.ts +8 -1
  98. package/dist/{jobs-8G-_agld.js → jobs-Co6Ni5HP.js} +7 -7
  99. package/dist/jobs-Co6Ni5HP.js.map +1 -0
  100. package/dist/{jwt-YhciR93H.js → jwt-rDS0884_.js} +5 -4
  101. package/dist/{jwt-YhciR93H.js.map → jwt-rDS0884_.js.map} +1 -1
  102. package/dist/{keys-COzQ2aE8.js → keys-BXcdRNhJ.js} +2 -2
  103. package/dist/{keys-COzQ2aE8.js.map → keys-BXcdRNhJ.js.map} +1 -1
  104. package/dist/{logger-Bt32sHFw.js → logger-CW8pmTS6.js} +35 -3
  105. package/dist/logger-CW8pmTS6.js.map +1 -0
  106. package/dist/{logs-routes-CelTiS7C.js → logs-routes-CBt7mMIL.js} +3 -3
  107. package/dist/{logs-routes-CelTiS7C.js.map → logs-routes-CBt7mMIL.js.map} +1 -1
  108. package/dist/mcp/consent-page.d.ts +38 -1
  109. package/dist/mcp/mcp-routes.d.ts +9 -0
  110. package/dist/mcp/mcp-tools.d.ts +7 -1
  111. package/dist/mcp/oauth-store.d.ts +12 -5
  112. package/dist/metrics/history-recorder.d.ts +4 -1
  113. package/dist/metrics/history-store.d.ts +8 -0
  114. package/dist/{offline-BrqCRgih-BtDB1ecy.js → offline-AvuEcg7u-DFHL98OT.js} +127 -40
  115. package/dist/offline-AvuEcg7u-DFHL98OT.js.map +1 -0
  116. package/dist/{offline-store-BhYXViha-BW5j1We_.js → offline-store-BhYXViha-YTmjBpo2.js} +5 -5
  117. package/dist/{offline-store-BhYXViha-BW5j1We_.js.map → offline-store-BhYXViha-YTmjBpo2.js.map} +1 -1
  118. package/dist/{openapi-generator-BamY6-ga.js → openapi-generator-CyxEtYEX.js} +61 -22
  119. package/dist/openapi-generator-CyxEtYEX.js.map +1 -0
  120. package/dist/{proxy-BEKbsil6.js → proxy-BYir8tWK.js} +5 -7
  121. package/dist/proxy-BYir8tWK.js.map +1 -0
  122. package/dist/{query-parser-CGohjyWI.js → query-parser-CDXWf2hH.js} +7 -6
  123. package/dist/{query-parser-CGohjyWI.js.map → query-parser-CDXWf2hH.js.map} +1 -1
  124. package/dist/{request-timeout-CRny5KD0.js → request-timeout-DVjo-c_8.js} +6 -6
  125. package/dist/request-timeout-DVjo-c_8.js.map +1 -0
  126. package/dist/rls-simulator-routes-bEY7-5-u.js +190 -0
  127. package/dist/rls-simulator-routes-bEY7-5-u.js.map +1 -0
  128. package/dist/schema-edit/deployed-schema-guard.d.ts +47 -0
  129. package/dist/{schema-editor-routes-CnNGCayU.js → schema-editor-routes-Dc3DU_jm.js} +4 -4
  130. package/dist/{schema-editor-routes-CnNGCayU.js.map → schema-editor-routes-Dc3DU_jm.js.map} +1 -1
  131. package/dist/soft-delete-params-BRONCOA2.js +59 -0
  132. package/dist/soft-delete-params-BRONCOA2.js.map +1 -0
  133. package/dist/{sql-export-routes-BgTJ7K3S.js → sql-export-routes-QycX8W8S.js} +40 -6
  134. package/dist/sql-export-routes-QycX8W8S.js.map +1 -0
  135. package/dist/{src-D5Y3N-S4.js → src-Bh9LRPb9.js} +21 -2
  136. package/dist/{src-D5Y3N-S4.js.map → src-Bh9LRPb9.js.map} +1 -1
  137. package/dist/storage/GCSStorageController.d.ts +2 -0
  138. package/dist/storage/LocalStorageController.d.ts +8 -3
  139. package/dist/storage/S3StorageController.d.ts +2 -0
  140. package/dist/storage/maintenance-keys.d.ts +17 -0
  141. package/dist/storage/maintenance-routes.d.ts +28 -5
  142. package/dist/storage/maintenance.d.ts +136 -4
  143. package/dist/storage/orphan-scans.d.ts +120 -0
  144. package/dist/storage/types.d.ts +11 -0
  145. package/dist/strings.es-B7QLnHh2.js +96 -0
  146. package/dist/strings.es-B7QLnHh2.js.map +1 -0
  147. package/dist/{upload-validation-I9cCUBIW.js → upload-validation-Bgo626uY.js} +2 -2
  148. package/dist/upload-validation-Bgo626uY.js.map +1 -0
  149. package/dist/utils/dev-dependency-command.d.ts +15 -0
  150. package/dist/utils/dev-port.d.ts +8 -0
  151. package/dist/utils/logger.d.ts +24 -0
  152. package/package.json +5 -5
  153. package/dist/GCSStorageController-C7ARrDAp.js.map +0 -1
  154. package/dist/S3StorageController-qevJ0pVE.js.map +0 -1
  155. package/dist/auth-CteGWxsL.js.map +0 -1
  156. package/dist/auth_adapter-CIItCTc8.js +0 -21
  157. package/dist/auth_adapter-CIItCTc8.js.map +0 -1
  158. package/dist/backend-DTAOsLQc.js.map +0 -1
  159. package/dist/cron-store-DEpVstUU.js.map +0 -1
  160. package/dist/ddl-bootstrap-BNqi2NZe.js.map +0 -1
  161. package/dist/errors-_ytoGi8A.js.map +0 -1
  162. package/dist/function-loader-DbCSoNJj.js.map +0 -1
  163. package/dist/history-recorder-RZ1Ow4YY.js.map +0 -1
  164. package/dist/history-store-DPofGVDp.js.map +0 -1
  165. package/dist/jobs-8G-_agld.js.map +0 -1
  166. package/dist/logger-Bt32sHFw.js.map +0 -1
  167. package/dist/offline-BrqCRgih-BtDB1ecy.js.map +0 -1
  168. package/dist/openapi-generator-BamY6-ga.js.map +0 -1
  169. package/dist/proxy-BEKbsil6.js.map +0 -1
  170. package/dist/request-timeout-CRny5KD0.js.map +0 -1
  171. package/dist/soft-delete-params-BAgpmBvp.js.map +0 -1
  172. package/dist/sql-export-routes-BgTJ7K3S.js.map +0 -1
  173. package/dist/strings.es-BeZWAodn.js.map +0 -1
  174. package/dist/upload-validation-I9cCUBIW.js.map +0 -1
@@ -2,11 +2,12 @@ import { createRequire as __rebaseCreateRequire } from "module";
2
2
  import __rebaseProcess from "process";
3
3
  globalThis.process ??= __rebaseProcess;
4
4
  __rebaseCreateRequire(import.meta.url);
5
- import { c as RebaseClientError, l as isUnsupported, s as RebaseApiError, t as splitSearchTerms } from "./strings.es-BeZWAodn.js";
6
- import { D as Vector, E as GeoPoint, S as hasFieldOperation, T as EntityRelation, _ as normalizeOrderBy, c as collectAllPages, l as paginateFind, u as resolveFindWindow, w as EntityReference } from "./filter-dialect-O1HK6Nvr.js";
7
- import { $ as parseRelationAggregateSort, tt as toCanonicalOp } from "./src-D5Y3N-S4.js";
8
- import { a as byIdRead, f as rebaseReviver, h as unref, i as buildQueryString, n as MemoryOfflineStore, p as etagOf, r as SDKQueryBuilder, s as createMutationId, t as IndexedDBOfflineStore, u as invalidBulkBody } from "./offline-store-BhYXViha-BW5j1We_.js";
9
- //#region ../client/dist/offline-BrqCRgih.js
5
+ import { a as RebaseClientError, i as RebaseApiError, o as isUnsupported } from "./data_driver-D0mg1Y_x.js";
6
+ import { D as Vector, E as GeoPoint, S as hasFieldOperation, T as EntityRelation, _ as normalizeOrderBy, c as collectAllPages, l as paginateFind, u as resolveFindWindow, w as EntityReference } from "./filter-dialect-TrKFWLqT.js";
7
+ import { et as parseRelationAggregateSort, nt as toCanonicalOp } from "./src-Bh9LRPb9.js";
8
+ import { t as splitSearchTerms } from "./strings.es-B7QLnHh2.js";
9
+ import { a as byIdRead, f as rebaseReviver, h as unref, i as buildQueryString, m as withETag, n as MemoryOfflineStore, p as etagOf, r as SDKQueryBuilder, s as createMutationId, t as IndexedDBOfflineStore, u as invalidBulkBody } from "./offline-store-BhYXViha-YTmjBpo2.js";
10
+ //#region ../client/dist/offline-AvuEcg7u.js
10
11
  /**
11
12
  * Lossless round-tripping of rows through the offline store.
12
13
  *
@@ -901,6 +902,13 @@ function isPlainObject(value) {
901
902
  const proto = Object.getPrototypeOf(value);
902
903
  return proto === null || Object.getPrototypeOf(proto) === null;
903
904
  }
905
+ /**
906
+ * The key a folded run is sent under: its first and last member and its
907
+ * length, which fixes its composition.
908
+ */
909
+ function runKey(run) {
910
+ return `${run[0].mutationId}..${run[run.length - 1].mutationId}:${run.length}`;
911
+ }
904
912
  /** Whether a queued write's payload mentions `id` anywhere — see {@link replaceMintedId}. */
905
913
  function mentionsMintedId(op, id) {
906
914
  return [op.data, ...(op.updates ?? []).map((u) => u.data)].some((payload) => replaceMintedId(payload, id, "").changed);
@@ -1324,19 +1332,45 @@ var OfflineManager = class {
1324
1332
  */
1325
1333
  ticket() {
1326
1334
  if (this.scopeHold) return void 0;
1327
- return {
1328
- scope: this.scope,
1329
- epoch: this.scopeEpoch
1330
- };
1335
+ return this.currentTicket();
1331
1336
  }
1332
1337
  /** The ticket of whoever the held scope turns out to be. */
1333
1338
  async nextTicket() {
1334
1339
  await this.scopeKnown();
1335
- return {
1340
+ return this.currentTicket();
1341
+ }
1342
+ currentTicket() {
1343
+ return this.unverified ? {
1344
+ scope: this.scope,
1345
+ epoch: this.scopeEpoch,
1346
+ unverified: true
1347
+ } : {
1336
1348
  scope: this.scope,
1337
1349
  epoch: this.scopeEpoch
1338
1350
  };
1339
1351
  }
1352
+ /**
1353
+ * Whether a write may go to the server now. Not while the scope is the last
1354
+ * user's, unconfirmed: it would go out with no credentials, as nobody. It
1355
+ * queues for its author instead, as it does offline.
1356
+ */
1357
+ sendsWrites() {
1358
+ return this.connectivity.shouldAttempt() && !this.unverified;
1359
+ }
1360
+ reachedUnconfirmedListeners = /* @__PURE__ */ new Set();
1361
+ /**
1362
+ * `listener` runs when a request made while the scope is unconfirmed
1363
+ * reached the server: the connection is back, so the session can be asked
1364
+ * about. The client wires it to the same confirmation the status listener
1365
+ * runs — the status may never have said the connection was gone.
1366
+ */
1367
+ onReachedUnconfirmed(listener) {
1368
+ this.reachedUnconfirmedListeners.add(listener);
1369
+ return () => this.reachedUnconfirmedListeners.delete(listener);
1370
+ }
1371
+ reachedUnconfirmed() {
1372
+ for (const listener of this.reachedUnconfirmedListeners) callSafely(listener);
1373
+ }
1340
1374
  /** Whether an operation's user is still the signed-in one. */
1341
1375
  isCurrent(ticket) {
1342
1376
  return ticket.epoch === this.scopeEpoch;
@@ -1393,15 +1427,18 @@ var OfflineManager = class {
1393
1427
  const res = await inner.find(params);
1394
1428
  this.connectivity.markSuccess();
1395
1429
  if (!this.isCurrent(ticket)) return res;
1396
- await this.ingest(slug, res.data ?? [], ticket, isProjection(params));
1397
- if (!this.isCurrent(ticket)) return res;
1398
- const snapshot = this.recordSnapshot(slug, params, res, ticket);
1399
- const answer = this.answer(slug, params, snapshot);
1400
- this.notifyCollection(slug, false);
1401
- return {
1402
- data: answer.data,
1403
- meta: answer.meta
1404
- };
1430
+ if (ticket.unverified) this.reachedUnconfirmed();
1431
+ else {
1432
+ await this.ingest(slug, res.data ?? [], ticket, isProjection(params));
1433
+ if (!this.isCurrent(ticket)) return res;
1434
+ const snapshot = this.recordSnapshot(slug, params, res, ticket);
1435
+ const answer = this.answer(slug, params, snapshot);
1436
+ this.notifyCollection(slug, false);
1437
+ return {
1438
+ data: answer.data,
1439
+ meta: answer.meta
1440
+ };
1441
+ }
1405
1442
  } catch (error) {
1406
1443
  if (!isNetworkError(error)) {
1407
1444
  if (isRetryableError(error) && this.hasLocalAnswer(state, slug, params) && !(walk && !this.snapshotFor(slug, params))) {
@@ -1435,10 +1472,16 @@ var OfflineManager = class {
1435
1472
  const row = await inner.findById(id, options);
1436
1473
  this.connectivity.markSuccess();
1437
1474
  if (!this.isCurrent(ticket)) return row;
1475
+ if (ticket.unverified) {
1476
+ this.reachedUnconfirmed();
1477
+ return this.localRow(slug, id);
1478
+ }
1438
1479
  if (row !== void 0) await this.ingest(slug, [row], ticket);
1439
1480
  else if (!this.hasPending(slug, id)) this.removeLocalRow(slug, id, ticket, true);
1440
1481
  this.notifyCollection(slug, false);
1441
- return this.localRow(slug, id);
1482
+ const local = this.localRow(slug, id);
1483
+ const etag = etagOf(row);
1484
+ return etag && local !== void 0 && !this.hasPending(slug, id) ? withETag({ ...local }, etag) : local;
1442
1485
  } catch (error) {
1443
1486
  if (!isNetworkError(error)) throw error;
1444
1487
  this.connectivity.markFailure();
@@ -1461,7 +1504,7 @@ var OfflineManager = class {
1461
1504
  await this.ensureCollection(slug);
1462
1505
  const idempotencyKey = options?.idempotencyKey ?? createMutationId();
1463
1506
  let sent = false;
1464
- if (this.connectivity.shouldAttempt()) try {
1507
+ if (this.sendsWrites()) try {
1465
1508
  const row = await inner.create(data, id, {
1466
1509
  ...options,
1467
1510
  idempotencyKey
@@ -1503,7 +1546,7 @@ var OfflineManager = class {
1503
1546
  if (data.length === 0) return [];
1504
1547
  const idempotencyKey = options?.idempotencyKey ?? createMutationId();
1505
1548
  let sent = false;
1506
- if (this.connectivity.shouldAttempt()) try {
1549
+ if (this.sendsWrites()) try {
1507
1550
  const rows = await inner.createMany(data, {
1508
1551
  ...options,
1509
1552
  idempotencyKey
@@ -1555,7 +1598,7 @@ var OfflineManager = class {
1555
1598
  upsert: async (data, options) => {
1556
1599
  const ticket = this.ticket() ?? await this.nextTicket();
1557
1600
  await this.ensureCollection(slug);
1558
- if (this.connectivity.shouldAttempt()) try {
1601
+ if (this.sendsWrites()) try {
1559
1602
  const row = await inner.upsert(data, options);
1560
1603
  this.connectivity.markSuccess();
1561
1604
  await this.ingest(slug, [row], ticket);
@@ -1586,7 +1629,7 @@ var OfflineManager = class {
1586
1629
  if (!Array.isArray(updates)) throw invalidBulkBody("updateMany expects an array of { id, data } entries.");
1587
1630
  if (updates.length === 0) return [];
1588
1631
  const anyPending = updates.some((u) => this.hasPending(slug, u.id));
1589
- if (this.connectivity.shouldAttempt() && !anyPending) try {
1632
+ if (this.sendsWrites() && !anyPending) try {
1590
1633
  const rows = await inner.updateMany(updates, options);
1591
1634
  this.connectivity.markSuccess();
1592
1635
  await this.ingest(slug, rows, ticket);
@@ -1625,7 +1668,7 @@ var OfflineManager = class {
1625
1668
  if (!Array.isArray(ids)) throw invalidBulkBody("deleteMany expects an array of ids.");
1626
1669
  if (ids.length === 0) return;
1627
1670
  const anyPending = ids.some((id) => this.hasPending(slug, id));
1628
- if (this.connectivity.shouldAttempt() && !anyPending) try {
1671
+ if (this.sendsWrites() && !anyPending) try {
1629
1672
  await inner.deleteMany(ids, options);
1630
1673
  this.connectivity.markSuccess();
1631
1674
  for (const id of ids) this.removeLocalRow(slug, id, ticket, true);
@@ -1653,7 +1696,7 @@ var OfflineManager = class {
1653
1696
  const idempotencyKey = options?.idempotencyKey ?? createMutationId();
1654
1697
  let sent = false;
1655
1698
  let sendFailure;
1656
- if (this.connectivity.shouldAttempt() && !this.hasPending(slug, id)) try {
1699
+ if (this.sendsWrites() && !this.hasPending(slug, id)) try {
1657
1700
  const row = await inner.update(id, data, {
1658
1701
  ...options,
1659
1702
  idempotencyKey
@@ -1678,7 +1721,7 @@ var OfflineManager = class {
1678
1721
  throw new RebaseClientError(`Cannot apply a field operation to "${slug}" while offline: it is evaluated against the stored value, which this device does not have a current copy of. Send the value itself, or retry when the connection is back.`, { code: "OFFLINE_FIELD_OP_UNSUPPORTED" });
1679
1722
  }
1680
1723
  const base = this.rawLocalRow(slug, id);
1681
- const ifMatch = this.onConflict ? options?.ifMatch ?? (this.hasPending(slug, id) ? void 0 : this.etagFor(slug, id)) : void 0;
1724
+ const ifMatch = options?.ifMatch ?? (this.onConflict && !this.hasPending(slug, id) ? this.etagFor(slug, id) : void 0);
1682
1725
  await this.enqueue({
1683
1726
  collection: slug,
1684
1727
  type: "update",
@@ -1701,7 +1744,7 @@ var OfflineManager = class {
1701
1744
  await this.ensureCollection(slug);
1702
1745
  const idempotencyKey = options?.idempotencyKey ?? createMutationId();
1703
1746
  let sent = false;
1704
- if (this.connectivity.shouldAttempt() && !this.hasPending(slug, id)) try {
1747
+ if (this.sendsWrites() && !this.hasPending(slug, id)) try {
1705
1748
  await inner.delete(id, {
1706
1749
  ...options,
1707
1750
  idempotencyKey
@@ -1721,6 +1764,7 @@ var OfflineManager = class {
1721
1764
  type: "delete",
1722
1765
  id,
1723
1766
  ...sent ? { sent: { idempotencyKey } } : {},
1767
+ ...options?.ifMatch ? { ifMatch: options.ifMatch } : {},
1724
1768
  rollback: { rows: { [String(id)]: this.rawLocalRow(slug, id) ?? null } }
1725
1769
  }, ticket);
1726
1770
  this.removeLocalRow(slug, id, ticket);
@@ -1733,6 +1777,12 @@ var OfflineManager = class {
1733
1777
  const n = await inner.count(params);
1734
1778
  this.connectivity.markSuccess();
1735
1779
  if (!this.isCurrent(ticket)) return n;
1780
+ if (ticket.unverified) {
1781
+ this.reachedUnconfirmed();
1782
+ const cached = await this.readCache(this.countKey(slug, params));
1783
+ if (cached !== void 0) return Math.max(0, cached + this.pendingDelta(slug, params));
1784
+ throw offlineError(`No count of "${slug}" for the last user is cached, and their session is not confirmed yet.`);
1785
+ }
1736
1786
  this.writeCache(this.countKey(slug, params), n);
1737
1787
  return Math.max(0, n + this.pendingDelta(slug, params));
1738
1788
  } catch (error) {
@@ -1771,6 +1821,15 @@ var OfflineManager = class {
1771
1821
  onUpdate(response);
1772
1822
  return;
1773
1823
  }
1824
+ if (ticket.unverified) {
1825
+ const answer = this.localFind(slug, params);
1826
+ onUpdate({
1827
+ ...response,
1828
+ data: answer.data,
1829
+ meta: answer.meta
1830
+ });
1831
+ return;
1832
+ }
1774
1833
  this.ingest(slug, response.data ?? [], ticket, isProjection(params)).then(() => {
1775
1834
  if (!this.isCurrent(ticket)) return;
1776
1835
  const snapshot = this.recordSnapshot(slug, params, response, ticket);
@@ -1789,6 +1848,10 @@ var OfflineManager = class {
1789
1848
  onUpdate(row);
1790
1849
  return;
1791
1850
  }
1851
+ if (ticket.unverified) {
1852
+ onUpdate(this.localRow(slug, id));
1853
+ return;
1854
+ }
1792
1855
  if (!row) {
1793
1856
  if (!this.hasPending(slug, id)) this.removeLocalRow(slug, id, ticket, true);
1794
1857
  this.notifyCollection(slug, false);
@@ -1820,7 +1883,7 @@ var OfflineManager = class {
1820
1883
  wrapTrash(slug, inner) {
1821
1884
  const online = async (what, call) => {
1822
1885
  const message = `Offline: ${what} on the trash of "${slug}" needs the server; the trash is never cached on this device.`;
1823
- if (!this.connectivity.shouldAttempt()) throw offlineError(message);
1886
+ if (!this.sendsWrites()) throw offlineError(message);
1824
1887
  try {
1825
1888
  const result = await call();
1826
1889
  this.connectivity.markSuccess();
@@ -1934,7 +1997,7 @@ var OfflineManager = class {
1934
1997
  })();
1935
1998
  if (options?.realtime !== false && !isUnsupported(inner.listen)) unlisten = inner.listen(params, (response) => {
1936
1999
  const ticket = this.ticket();
1937
- if (!ticket) return;
2000
+ if (!ticket || ticket.unverified) return;
1938
2001
  this.ingest(slug, response.data ?? [], ticket, isProjection(params)).then(() => {
1939
2002
  this.recordSnapshot(slug, params, response, ticket);
1940
2003
  this.notifyCollection(slug, false);
@@ -1989,7 +2052,7 @@ var OfflineManager = class {
1989
2052
  })();
1990
2053
  if (options?.realtime !== false && !isUnsupported(inner.listenById)) unlisten = inner.listenById(id, (row) => {
1991
2054
  const ticket = this.ticket();
1992
- if (!ticket) return;
2055
+ if (!ticket || ticket.unverified) return;
1993
2056
  if (!row) {
1994
2057
  if (!this.hasPending(slug, id)) this.removeLocalRow(slug, id, ticket, true);
1995
2058
  this.notifyCollection(slug, false);
@@ -2314,6 +2377,7 @@ var OfflineManager = class {
2314
2377
  */
2315
2378
  removeLocalRow(slug, id, ticket, known = false) {
2316
2379
  if (!this.isCurrent(ticket)) return;
2380
+ if (known && ticket.unverified) return;
2317
2381
  const state = this.collectionState(slug);
2318
2382
  const key = String(id);
2319
2383
  const existed = state.rows.delete(key);
@@ -2346,7 +2410,7 @@ var OfflineManager = class {
2346
2410
  * dropdown elsewhere asked for titles.
2347
2411
  */
2348
2412
  async ingest(slug, rows, ticket, projection = false) {
2349
- if (rows.length === 0 || !this.isCurrent(ticket)) return;
2413
+ if (rows.length === 0 || !this.isCurrent(ticket) || ticket.unverified) return;
2350
2414
  const state = await this.ensureCollection(slug);
2351
2415
  if (!this.isCurrent(ticket)) return;
2352
2416
  const cachedAt = Date.now();
@@ -2450,7 +2514,7 @@ var OfflineManager = class {
2450
2514
  hasMore: meta.hasMore ?? false,
2451
2515
  ...isProjection(params) ? { rows: (result.data ?? []).map((row) => ({ ...row })) } : {}
2452
2516
  };
2453
- if (!this.isCurrent(ticket)) return snapshot;
2517
+ if (!this.isCurrent(ticket) || ticket.unverified) return snapshot;
2454
2518
  const state = this.collectionState(slug);
2455
2519
  const key = buildQueryString(params);
2456
2520
  state.snapshots.set(key, snapshot);
@@ -2548,7 +2612,7 @@ var OfflineManager = class {
2548
2612
  }
2549
2613
  if (mutation.type === "update") {
2550
2614
  const tail = this.queue[this.queue.length - 1];
2551
- if (tail && !this.inFlight.has(tail.mutationId) && tail.sent === void 0 && (tail.schemaVersion ?? 1) <= 1 && tail.collection === mutation.collection && (tail.type === "create" || tail.type === "update") && tail.id === mutation.id) {
2615
+ if (tail && !this.inFlight.has(tail.mutationId) && tail.sent === void 0 && (tail.schemaVersion ?? 1) <= 1 && tail.collection === mutation.collection && (tail.type === "create" || tail.type === "update") && tail.id === mutation.id && (mutation.ifMatch === void 0 || mutation.ifMatch === tail.ifMatch)) {
2552
2616
  if (tail.type === "update") tail.parts = [...tail.parts ?? [{ ...tail.data }], { ...mutation.data }];
2553
2617
  tail.data = {
2554
2618
  ...tail.data,
@@ -2729,7 +2793,7 @@ var OfflineManager = class {
2729
2793
  this.connectivity.markFailure();
2730
2794
  break;
2731
2795
  }
2732
- if (op.type === "update" && op.ifMatch && isPreconditionFailed(error)) {
2796
+ if ((op.type === "update" || op.type === "delete") && op.ifMatch && this.onConflict && isPreconditionFailed(error)) {
2733
2797
  this.connectivity.markSuccess();
2734
2798
  await this.resolveConflict(op, error, ticket);
2735
2799
  continue;
@@ -2800,16 +2864,18 @@ var OfflineManager = class {
2800
2864
  */
2801
2865
  foldableRun(unbatched) {
2802
2866
  if (this.replayBatchSize < 2) return [];
2803
- const foldable = (op, collection) => op !== void 0 && op.type === "create" && op.generatedId === true && typeof op.id === "string" && op.sent === void 0 && !op.upsert && (op.schemaVersion ?? 1) <= 1 && !unbatched.has(op.mutationId) && (collection === void 0 || op.collection === collection);
2804
2867
  const head = this.queue[0];
2868
+ const resent = head?.sent?.run;
2869
+ const foldable = (op, collection) => op !== void 0 && op.type === "create" && op.generatedId === true && typeof op.id === "string" && (resent === void 0 ? op.sent === void 0 : op.sent?.run === resent) && !op.upsert && (op.schemaVersion ?? 1) <= 1 && !unbatched.has(op.mutationId) && (collection === void 0 || op.collection === collection);
2805
2870
  if (!foldable(head)) return [];
2806
2871
  const run = [head];
2807
- for (let i = 1; i < this.queue.length && run.length < this.replayBatchSize; i++) {
2872
+ for (let i = 1; i < this.queue.length && (resent !== void 0 || run.length < this.replayBatchSize); i++) {
2808
2873
  const next = this.queue[i];
2809
2874
  if (!foldable(next, head.collection)) break;
2810
2875
  if (run.some((member) => mentionsMintedId(next, member.id))) break;
2811
2876
  run.push(next);
2812
2877
  }
2878
+ if (resent !== void 0 && runKey(run) !== resent) return [];
2813
2879
  return run;
2814
2880
  }
2815
2881
  /**
@@ -2830,9 +2896,10 @@ var OfflineManager = class {
2830
2896
  */
2831
2897
  async replayRun(run, ticket) {
2832
2898
  const inner = this.innerFor(run[0].collection);
2833
- const idempotencyKey = `${run[0].mutationId}..${run[run.length - 1].mutationId}:${run.length}`;
2899
+ const idempotencyKey = runKey(run);
2834
2900
  for (const member of run) this.inFlight.add(member.mutationId);
2835
2901
  try {
2902
+ for (const member of run) await this.markCreateSent(member, ticket, idempotencyKey);
2836
2903
  let rows;
2837
2904
  try {
2838
2905
  rows = await inner.createMany(run.map((member) => member.data), { idempotencyKey });
@@ -2854,6 +2921,7 @@ var OfflineManager = class {
2854
2921
  async replay(op, ticket) {
2855
2922
  const inner = this.innerFor(op.collection);
2856
2923
  if (op.type === "create") {
2924
+ await this.markCreateSent(op, ticket);
2857
2925
  let row;
2858
2926
  try {
2859
2927
  row = await this.sendQueuedWrite(op, (idempotencyKey) => inner.create(op.data, void 0, { idempotencyKey }), (sent) => inner.create(sent.data, sent.id, { idempotencyKey: sent.idempotencyKey }));
@@ -2906,11 +2974,30 @@ var OfflineManager = class {
2906
2974
  }
2907
2975
  for (const id of ids) this.removeLocalRow(op.collection, id, ticket, true);
2908
2976
  } else if (op.type === "delete") {
2909
- await inner.delete(op.id, { idempotencyKey: op.sent?.idempotencyKey ?? op.mutationId });
2977
+ await inner.delete(op.id, {
2978
+ idempotencyKey: op.sent?.idempotencyKey ?? op.mutationId,
2979
+ ...op.ifMatch ? { ifMatch: op.ifMatch } : {}
2980
+ });
2910
2981
  this.removeLocalRow(op.collection, op.id, ticket, true);
2911
2982
  }
2912
2983
  }
2913
2984
  /**
2985
+ * Record a queued create as sent, with the request about to go out, and
2986
+ * persist it — before the request, so a lost answer cannot leave it
2987
+ * unmarked. One already marked (sent online before it was queued, or by
2988
+ * an earlier replay) keeps what it holds: that is the request the server
2989
+ * may have stored.
2990
+ */
2991
+ async markCreateSent(op, ticket, run) {
2992
+ if (op.sent) return;
2993
+ op.sent = {
2994
+ idempotencyKey: op.mutationId,
2995
+ data: { ...op.data },
2996
+ ...run ? { run } : {}
2997
+ };
2998
+ await this.persistMutation(`${ticket.scope}|${op.mutationId}`, op).catch(() => void 0);
2999
+ }
3000
+ /**
2914
3001
  * Send a queued write under its key.
2915
3002
  *
2916
3003
  * A write the queue sent once already, before it was queued, carries that
@@ -3302,4 +3389,4 @@ var OfflineManager = class {
3302
3389
  //#endregion
3303
3390
  export { OfflineManager };
3304
3391
 
3305
- //# sourceMappingURL=offline-BrqCRgih-BtDB1ecy.js.map
3392
+ //# sourceMappingURL=offline-AvuEcg7u-DFHL98OT.js.map