@korajs/store 1.0.0-beta.13 → 1.0.0-beta.14

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 (53) hide show
  1. package/dist/adapters/better-sqlite3.cjs.map +1 -1
  2. package/dist/adapters/better-sqlite3.js +1 -1
  3. package/dist/adapters/indexeddb.cjs.map +1 -1
  4. package/dist/adapters/indexeddb.js +4 -4
  5. package/dist/adapters/sqlite-wasm-worker.cjs +67 -15
  6. package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
  7. package/dist/adapters/sqlite-wasm-worker.js +65 -17
  8. package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
  9. package/dist/adapters/sqlite-wasm.cjs.map +1 -1
  10. package/dist/adapters/sqlite-wasm.js +5 -5
  11. package/dist/{backup-4NUPDEMB.js → backup-IHKVMUAM.js} +5 -5
  12. package/dist/{chunk-Y5LRH7KI.js → chunk-5AQSGMJV.js} +151 -21
  13. package/dist/chunk-5AQSGMJV.js.map +1 -0
  14. package/dist/{chunk-W5F2LWG6.js → chunk-5JMGMPUE.js} +2 -2
  15. package/dist/{chunk-HZTDBLDB.js → chunk-BILSA6XZ.js} +4 -3
  16. package/dist/{chunk-HZTDBLDB.js.map → chunk-BILSA6XZ.js.map} +1 -1
  17. package/dist/{chunk-QDUSJMFV.js → chunk-BQSIL6TT.js} +5 -5
  18. package/dist/{chunk-MCQKEHOD.js → chunk-KK3GBONL.js} +2 -2
  19. package/dist/{chunk-32EDKWMI.js → chunk-KTVNANUH.js} +2 -2
  20. package/dist/{chunk-SJP2HTYZ.js → chunk-QW4UU7NG.js} +2 -2
  21. package/dist/{chunk-DUKTWVZW.js → chunk-SRHDJF5B.js} +13 -2
  22. package/dist/{chunk-DUKTWVZW.js.map → chunk-SRHDJF5B.js.map} +1 -1
  23. package/dist/{chunk-P4V375FY.js → chunk-UC2LGFPV.js} +4 -4
  24. package/dist/index.cjs +203 -47
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +96 -6
  27. package/dist/index.d.ts +96 -6
  28. package/dist/index.js +55 -39
  29. package/dist/index.js.map +1 -1
  30. package/dist/internal.cjs +1 -0
  31. package/dist/internal.cjs.map +1 -1
  32. package/dist/internal.d.cts +2 -2
  33. package/dist/internal.d.ts +2 -2
  34. package/dist/internal.js +3 -3
  35. package/dist/{restore-FR7XIKT2.js → restore-MNDPR557.js} +4 -4
  36. package/dist/{sql-builder-C4u55jKW.d.cts → sql-builder-D0tC3sv9.d.cts} +8 -1
  37. package/dist/{sql-builder-CGckdBDL.d.ts → sql-builder-D5UMzHEo.d.ts} +8 -1
  38. package/dist/sqlite-wasm-channel-QCCNBYCU.js +12 -0
  39. package/dist/store-HNGX4KTT.js +16 -0
  40. package/package.json +2 -2
  41. package/dist/chunk-Y5LRH7KI.js.map +0 -1
  42. package/dist/sqlite-wasm-channel-MLHLKEED.js +0 -12
  43. package/dist/store-IMWKZLWB.js +0 -16
  44. /package/dist/{backup-4NUPDEMB.js.map → backup-IHKVMUAM.js.map} +0 -0
  45. /package/dist/{chunk-W5F2LWG6.js.map → chunk-5JMGMPUE.js.map} +0 -0
  46. /package/dist/{chunk-QDUSJMFV.js.map → chunk-BQSIL6TT.js.map} +0 -0
  47. /package/dist/{chunk-MCQKEHOD.js.map → chunk-KK3GBONL.js.map} +0 -0
  48. /package/dist/{chunk-32EDKWMI.js.map → chunk-KTVNANUH.js.map} +0 -0
  49. /package/dist/{chunk-SJP2HTYZ.js.map → chunk-QW4UU7NG.js.map} +0 -0
  50. /package/dist/{chunk-P4V375FY.js.map → chunk-UC2LGFPV.js.map} +0 -0
  51. /package/dist/{restore-FR7XIKT2.js.map → restore-MNDPR557.js.map} +0 -0
  52. /package/dist/{sqlite-wasm-channel-MLHLKEED.js.map → sqlite-wasm-channel-QCCNBYCU.js.map} +0 -0
  53. /package/dist/{store-IMWKZLWB.js.map → store-HNGX4KTT.js.map} +0 -0
@@ -6,22 +6,22 @@ import {
6
6
  loadDumpFromIndexedDB,
7
7
  readManifestRecord,
8
8
  restoreDumpStatements
9
- } from "../chunk-QDUSJMFV.js";
9
+ } from "../chunk-BQSIL6TT.js";
10
10
  import {
11
11
  WebWorkerBridge
12
- } from "../chunk-MCQKEHOD.js";
12
+ } from "../chunk-KK3GBONL.js";
13
13
  import {
14
14
  Mutex
15
15
  } from "../chunk-TVJEC5S2.js";
16
16
  import {
17
17
  leaderLockName,
18
18
  opfsPoolNameFor
19
- } from "../chunk-W5F2LWG6.js";
19
+ } from "../chunk-5JMGMPUE.js";
20
20
  import {
21
21
  AdapterError,
22
22
  StorageInUseError,
23
23
  UnsyncedDataError
24
- } from "../chunk-DUKTWVZW.js";
24
+ } from "../chunk-SRHDJF5B.js";
25
25
 
26
26
  // src/adapters/local-databases.ts
27
27
  async function listLocalDatabases() {
@@ -52,7 +52,7 @@ async function deleteLocalDatabase(dbName, options) {
52
52
  const record = await readManifestRecord(dbName);
53
53
  const backend = record?.backend ?? "opfs";
54
54
  return withExclusiveDatabase(dbName, async () => {
55
- const { WebWorkerBridge: WebWorkerBridge2 } = await import("../sqlite-wasm-channel-MLHLKEED.js");
55
+ const { WebWorkerBridge: WebWorkerBridge2 } = await import("../sqlite-wasm-channel-QCCNBYCU.js");
56
56
  const worker = new WebWorkerBridge2(options.workerUrl, options.workerResponseTimeoutMs ?? 3e4);
57
57
  const send = async (request) => worker.send(request);
58
58
  const reader = {
@@ -7,11 +7,11 @@ import {
7
7
  readBackupManifest,
8
8
  restoreBackup,
9
9
  verifyBackupChecksum
10
- } from "./chunk-P4V375FY.js";
11
- import "./chunk-32EDKWMI.js";
12
- import "./chunk-SJP2HTYZ.js";
10
+ } from "./chunk-UC2LGFPV.js";
11
+ import "./chunk-KTVNANUH.js";
12
+ import "./chunk-QW4UU7NG.js";
13
13
  import "./chunk-J5AGXF3D.js";
14
- import "./chunk-DUKTWVZW.js";
14
+ import "./chunk-SRHDJF5B.js";
15
15
  export {
16
16
  BACKUP_VERSION,
17
17
  BackupFormatError,
@@ -22,4 +22,4 @@ export {
22
22
  restoreBackup,
23
23
  verifyBackupChecksum
24
24
  };
25
- //# sourceMappingURL=backup-4NUPDEMB.js.map
25
+ //# sourceMappingURL=backup-IHKVMUAM.js.map
@@ -16,7 +16,7 @@ import {
16
16
  writeDeleteInTx,
17
17
  writeInsertInTx,
18
18
  writeUpdateInTx
19
- } from "./chunk-HZTDBLDB.js";
19
+ } from "./chunk-BILSA6XZ.js";
20
20
  import {
21
21
  STORED_SCHEMA_VERSION_SQL,
22
22
  parseSchemaAhead,
@@ -27,7 +27,7 @@ import {
27
27
  LOG_QUARANTINE_TABLE,
28
28
  readBackupManifest,
29
29
  scanLogIntegrity
30
- } from "./chunk-P4V375FY.js";
30
+ } from "./chunk-UC2LGFPV.js";
31
31
  import {
32
32
  COMPACTED_THROUGH_TABLE,
33
33
  FOLD_MATERIALIZATION_CURRENT,
@@ -47,7 +47,7 @@ import {
47
47
  renumberOperationRow,
48
48
  repairSequenceUniqueness,
49
49
  rewriteDependentsInTx
50
- } from "./chunk-32EDKWMI.js";
50
+ } from "./chunk-KTVNANUH.js";
51
51
  import {
52
52
  DELIVERY_WATERMARK_META_KEY,
53
53
  DELTA_CURSOR_META_KEY,
@@ -99,7 +99,7 @@ import {
99
99
  seedTerminalRejectionsOnce,
100
100
  serializeRowVersion,
101
101
  setLocalNodePrincipal
102
- } from "./chunk-SJP2HTYZ.js";
102
+ } from "./chunk-QW4UU7NG.js";
103
103
  import {
104
104
  decodeRichtextFieldsFromOpData,
105
105
  deserializeOperationWithCollection,
@@ -110,12 +110,13 @@ import {
110
110
  toRichtextReadShape
111
111
  } from "./chunk-J5AGXF3D.js";
112
112
  import {
113
+ NodeOwnedByAnotherUserError,
113
114
  OptimisticLockError,
114
115
  QueryError,
115
116
  RecordNotFoundError,
116
117
  SchemaVersionAheadError,
117
118
  StoreNotOpenError
118
- } from "./chunk-DUKTWVZW.js";
119
+ } from "./chunk-SRHDJF5B.js";
119
120
 
120
121
  // src/store/store.ts
121
122
  import {
@@ -140,7 +141,7 @@ import {
140
141
  // src/collection/collection.ts
141
142
  import { quoteIdent } from "@korajs/core";
142
143
  var Collection = class {
143
- constructor(name, definition, schema, adapter, clock, nodeId, onMutation, relationEnforcer, mutationHandler, causalTracker, secretKeyProvider, onStorageError, fold, maxOperationBytes) {
144
+ constructor(name, definition, schema, adapter, clock, nodeId, onMutation, relationEnforcer, mutationHandler, causalTracker, secretKeyProvider, onStorageError, fold, maxOperationBytes, assertLocalWriteAllowed) {
144
145
  this.name = name;
145
146
  this.definition = definition;
146
147
  this.schema = schema;
@@ -155,6 +156,7 @@ var Collection = class {
155
156
  this.onStorageError = onStorageError;
156
157
  this.fold = fold;
157
158
  this.maxOperationBytes = maxOperationBytes;
159
+ this.assertLocalWriteAllowed = assertLocalWriteAllowed;
158
160
  }
159
161
  name;
160
162
  definition;
@@ -170,6 +172,7 @@ var Collection = class {
170
172
  onStorageError;
171
173
  fold;
172
174
  maxOperationBytes;
175
+ assertLocalWriteAllowed;
173
176
  /**
174
177
  * Point this collection at the store's new device identity after a node-id
175
178
  * rotation (RT-21). Internal: called by the owning Store only.
@@ -197,7 +200,8 @@ var Collection = class {
197
200
  causalTracker: this.causalTracker,
198
201
  ...this.secretKeyProvider ? { secretKeyProvider: this.secretKeyProvider } : {},
199
202
  ...this.onStorageError ? { onStorageError: this.onStorageError } : {},
200
- ...this.maxOperationBytes !== void 0 ? { maxOperationBytes: this.maxOperationBytes } : {}
203
+ ...this.maxOperationBytes !== void 0 ? { maxOperationBytes: this.maxOperationBytes } : {},
204
+ ...this.assertLocalWriteAllowed ? { assertLocalWriteAllowed: this.assertLocalWriteAllowed } : {}
201
205
  };
202
206
  }
203
207
  async insert(data) {
@@ -2250,7 +2254,8 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
2250
2254
 
2251
2255
  // src/sync/rotate-node-id.ts
2252
2256
  import { quoteIdent as quoteIdent11 } from "@korajs/core";
2253
- async function rotateUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, oldNodeId, newNodeId) {
2257
+ async function rotateUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, oldNodeId, newNodeId, options = {}) {
2258
+ const moveDatabaseNode = options.moveDatabaseNode !== false;
2254
2259
  const rotateIds = new Set(unsyncedOpIds);
2255
2260
  const idMapping = {};
2256
2261
  const rewritten = [];
@@ -2320,10 +2325,12 @@ async function rotateUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, old
2320
2325
  );
2321
2326
  }
2322
2327
  }
2323
- await tx.execute("INSERT OR REPLACE INTO _kora_meta (key, value) VALUES ('node_id', ?)", [
2324
- newNodeId
2325
- ]);
2326
- await tx.execute("DELETE FROM _kora_meta WHERE key = ?", [NODE_TOKEN_META_KEY]);
2328
+ if (moveDatabaseNode) {
2329
+ await tx.execute("INSERT OR REPLACE INTO _kora_meta (key, value) VALUES ('node_id', ?)", [
2330
+ newNodeId
2331
+ ]);
2332
+ await tx.execute("DELETE FROM _kora_meta WHERE key = ?", [NODE_TOKEN_META_KEY]);
2333
+ }
2327
2334
  if (rewritten.length > 0) {
2328
2335
  await tx.execute(
2329
2336
  `INSERT INTO _kora_version_vector (node_id, sequence_number) VALUES (?, ?)
@@ -2469,7 +2476,9 @@ var TransactionContext = class {
2469
2476
  ...this.config.secretKeyProvider ? { secretKeyProvider: this.config.secretKeyProvider } : {},
2470
2477
  ...this.mutationName !== void 0 ? { mutationName: this.mutationName } : {},
2471
2478
  ...this.config.beforeLocalDelete ? { beforeLocalDelete: this.config.beforeLocalDelete } : {},
2472
- ...this.config.fold ? { fold: this.config.fold } : {}
2479
+ ...this.config.fold ? { fold: this.config.fold } : {},
2480
+ ...this.config.maxOperationBytes !== void 0 ? { maxOperationBytes: this.config.maxOperationBytes } : {},
2481
+ ...this.config.assertLocalWriteAllowed ? { assertLocalWriteAllowed: this.config.assertLocalWriteAllowed } : {}
2473
2482
  };
2474
2483
  }
2475
2484
  ensureActive() {
@@ -2831,6 +2840,12 @@ var Store = class {
2831
2840
  schema;
2832
2841
  adapter;
2833
2842
  configNodeId;
2843
+ /** The owner of the pinned node while it belongs to another user than the signed-in one (F9). */
2844
+ pinnedNodeOwnedBy = null;
2845
+ /** The sync server refused the pinned node as another user's (F9). */
2846
+ pinnedNodeRefusedByServer = false;
2847
+ /** The user of the last {@link bindPrincipal}. */
2848
+ signedInPrincipal = null;
2834
2849
  dbName;
2835
2850
  isolation;
2836
2851
  emitter;
@@ -2905,8 +2920,10 @@ var Store = class {
2905
2920
  this.foldActive = false;
2906
2921
  await relaxValueDomainConstraints(this.adapter, this.schema);
2907
2922
  await dropLegacyIndexes(this.adapter, this.schema);
2923
+ const persistedNodeId = await this.readPersistedNodeId();
2908
2924
  this.nodeId = await this.loadOrGenerateNodeId();
2909
2925
  await registerLocalNode(this.adapter, this.nodeId);
2926
+ await this.registerUnregisteredAuthors(persistedNodeId);
2910
2927
  await this.scanLog("quick");
2911
2928
  await repairSequenceUniqueness(this.adapter, this.schema, this.nodeId);
2912
2929
  await seedTerminalRejectionsOnce(this.adapter);
@@ -2950,7 +2967,8 @@ var Store = class {
2950
2967
  this.secretKeyProvider,
2951
2968
  (error) => this.reportStorageError(error),
2952
2969
  () => this.activeFold(),
2953
- this.maxOperationBytes
2970
+ this.maxOperationBytes,
2971
+ () => this.assertLocalWriteAllowed()
2954
2972
  );
2955
2973
  this.collections.set(name, col);
2956
2974
  }
@@ -4242,6 +4260,7 @@ var Store = class {
4242
4260
  ...options?.extraCausalDeps ? { extraCausalDeps: options.extraCausalDeps } : {},
4243
4261
  ...this.secretKeyProvider ? { secretKeyProvider: this.secretKeyProvider } : {},
4244
4262
  ...this.maxOperationBytes !== void 0 ? { maxOperationBytes: this.maxOperationBytes } : {},
4263
+ assertLocalWriteAllowed: () => this.assertLocalWriteAllowed(),
4245
4264
  ...beforeLocalDelete ? { beforeLocalDelete: beforeLocalDelete.bind(this.localMutationHandler) } : {},
4246
4265
  onStorageError: (error) => this.reportStorageError(error)
4247
4266
  };
@@ -4511,6 +4530,46 @@ var Store = class {
4511
4530
  await this.refoldRecordsOf(result.operations.map((op) => op.id));
4512
4531
  return result;
4513
4532
  }
4533
+ /**
4534
+ * Re-author another local node's never-acknowledged operations under a fresh node id
4535
+ * bound to `principal` (the user they belong to). Used when the sync server refuses an
4536
+ * adopted node whose writes belong to the signed-in user (the app assigned them, or
4537
+ * the node was created for them) but which no claims-aware server ever accepted: a
4538
+ * beta.12 database's node with history the server recorded no owner for. The store's
4539
+ * own node id, clock and counters are untouched; the fresh node is registered, so the
4540
+ * sync engine adopts it like any other local node.
4541
+ *
4542
+ * @param fromNodeId - The refused local node (never the store's own)
4543
+ * @param unsyncedOpIds - Ids of its operations the server never acknowledged
4544
+ * @param principal - The user the writes belong to (the fresh node's binding)
4545
+ * @returns The fresh node id and the rewritten operations (for the outbound queue)
4546
+ * @throws {KoraError} `NODE_ID_UNKNOWN` when `fromNodeId` is the store's own node or
4547
+ * not a local node
4548
+ */
4549
+ async reauthorLocalNode(fromNodeId, unsyncedOpIds, principal) {
4550
+ this.ensureOpen();
4551
+ const from = (await listLocalNodes(this.adapter)).find((node) => node.nodeId === fromNodeId);
4552
+ if (fromNodeId === this.nodeId || !from) {
4553
+ throw new KoraError4(
4554
+ `Node id "${fromNodeId}" is not another local node of this database, so its writes cannot be re-authored.`,
4555
+ "NODE_ID_UNKNOWN",
4556
+ { nodeId: fromNodeId, storeNodeId: this.nodeId }
4557
+ );
4558
+ }
4559
+ const result = await rotateUnsyncedOperationsInLog(
4560
+ this.adapter,
4561
+ this.schema,
4562
+ unsyncedOpIds,
4563
+ fromNodeId,
4564
+ generateUUIDv73(),
4565
+ { moveDatabaseNode: false }
4566
+ );
4567
+ await registerLocalNode(this.adapter, result.nodeId);
4568
+ await setLocalNodePrincipal(this.adapter, result.nodeId, principal, "fresh");
4569
+ this.versionVector = await this.loadVersionVector();
4570
+ await this.refoldRecordsOf(result.operations.map((op) => op.id));
4571
+ return result;
4572
+ }
4514
4573
  /**
4515
4574
  * Move this database back to a node id it authored under before (RT-38): after the
4516
4575
  * sync server refused the current node, the device tries a node a returning user
@@ -4590,6 +4649,31 @@ var Store = class {
4590
4649
  * @returns The node now in use and whether it changed
4591
4650
  */
4592
4651
  async bindPrincipal(principal) {
4652
+ const binding = await this.bindPrincipalToNode(principal);
4653
+ if (principal !== this.signedInPrincipal) this.pinnedNodeRefusedByServer = false;
4654
+ this.pinnedNodeOwnedBy = binding.conflict ? (await listLocalNodes(this.adapter)).find((node) => node.nodeId === this.nodeId)?.principal ?? null : null;
4655
+ this.signedInPrincipal = principal;
4656
+ return binding;
4657
+ }
4658
+ /**
4659
+ * The sync server refused this store's pinned node id because another user owns it
4660
+ * (`owned: true`), or later accepted it (`false`). A pinned node cannot move to a fresh
4661
+ * id, so writes made under it could only ever upload as its owner: while the server
4662
+ * says so, local writes are refused (F9). A no-op for a store whose node is not pinned
4663
+ * (it moves to a fresh node instead).
4664
+ *
4665
+ * @param owned - Whether the server reported the node owned by another user
4666
+ */
4667
+ setPinnedNodeOwnedElsewhere(owned) {
4668
+ if (!this.configNodeId) return;
4669
+ this.pinnedNodeRefusedByServer = owned;
4670
+ }
4671
+ /** Refuse a local write while the pinned node belongs to another user (F9). */
4672
+ assertLocalWriteAllowed() {
4673
+ if (this.pinnedNodeOwnedBy === null && !this.pinnedNodeRefusedByServer) return;
4674
+ throw new NodeOwnedByAnotherUserError(this.nodeId, this.signedInPrincipal);
4675
+ }
4676
+ async bindPrincipalToNode(principal) {
4593
4677
  this.ensureOpen();
4594
4678
  const previousNodeId = this.nodeId;
4595
4679
  const nodes = await listLocalNodes(this.adapter);
@@ -4969,6 +5053,8 @@ var Store = class {
4969
5053
  const beforeLocalDelete = this.localMutationHandler?.beforeLocalDelete;
4970
5054
  const fold = this.activeFold();
4971
5055
  return new TransactionContext({
5056
+ ...this.maxOperationBytes !== void 0 ? { maxOperationBytes: this.maxOperationBytes } : {},
5057
+ assertLocalWriteAllowed: () => this.assertLocalWriteAllowed(),
4972
5058
  ...fold ? { fold } : {},
4973
5059
  schema: this.schema,
4974
5060
  adapter: this.adapter,
@@ -5014,7 +5100,7 @@ var Store = class {
5014
5100
  */
5015
5101
  async exportBackup(options) {
5016
5102
  this.ensureOpen();
5017
- const { exportBackup: doExport } = await import("./backup-4NUPDEMB.js");
5103
+ const { exportBackup: doExport } = await import("./backup-IHKVMUAM.js");
5018
5104
  return doExport(this.adapter, this.schema, this.nodeId, this.schema.version, options);
5019
5105
  }
5020
5106
  /**
@@ -5028,8 +5114,9 @@ var Store = class {
5028
5114
  * move backwards, and unsynced own writes are kept (see
5029
5115
  * `RestoreOptions.keepUnsyncedWrites`). Then {@link reloadFromDisk} runs.
5030
5116
  *
5031
- * A version-1 file is refused (`errorCode: 'BACKUP_FORMAT_OUTDATED'`); convert it with
5032
- * `convertBackupV1`.
5117
+ * A version-1 file (Kora 1.0.0-beta.12 and earlier) is converted first, exactly as
5118
+ * `convertBackupV1` does (`result.convertedFromVersion: 1`); one holding an operation
5119
+ * whose timestamp cannot be recovered is refused (`BACKUP_OPERATION_INVALID`).
5033
5120
  *
5034
5121
  * @param data - The backup data
5035
5122
  * @param options - Restore options (merge, collections, keepUnsyncedWrites, onProgress)
@@ -5042,13 +5129,19 @@ var Store = class {
5042
5129
  const started = Date.now();
5043
5130
  const onProgress = options?.onProgress ?? (() => {
5044
5131
  });
5045
- const { BackupFormatError, parseBackup } = await import("./backup-4NUPDEMB.js");
5046
- const { restoreMerge, restoreReplace } = await import("./restore-FR7XIKT2.js");
5132
+ const { BackupFormatError, convertBackupV1, parseBackup, readBackupManifest: readBackupManifest2 } = await import("./backup-IHKVMUAM.js");
5133
+ const { restoreMerge, restoreReplace } = await import("./restore-MNDPR557.js");
5047
5134
  onProgress({ phase: "verifying", progress: 0, message: "Verifying backup" });
5048
5135
  let parsed;
5136
+ let convertedFromVersion;
5049
5137
  try {
5138
+ let bytes = data;
5139
+ if (readBackupManifest2(data).version === 1) {
5140
+ bytes = await convertBackupV1(data);
5141
+ convertedFromVersion = 1;
5142
+ }
5050
5143
  parsed = await parseBackup(
5051
- data,
5144
+ bytes,
5052
5145
  options?.collections ? { collections: options.collections } : void 0
5053
5146
  );
5054
5147
  } catch (error) {
@@ -5100,6 +5193,7 @@ var Store = class {
5100
5193
  recordsRestored: counts.recordsRestored,
5101
5194
  ...options?.merge ? {} : { unsyncedWritesKept: counts.unsyncedWritesKept },
5102
5195
  ...counts.serverOperationsSkipped ? { serverOperationsSkipped: counts.serverOperationsSkipped } : {},
5196
+ ...convertedFromVersion ? { convertedFromVersion } : {},
5103
5197
  success: true,
5104
5198
  duration: Date.now() - started
5105
5199
  };
@@ -5262,6 +5356,42 @@ var Store = class {
5262
5356
  onOperation: (operation) => this.publishLocalOperation(operation.collection, operation)
5263
5357
  });
5264
5358
  }
5359
+ /** The node id the database persisted before this open, if any. */
5360
+ async readPersistedNodeId() {
5361
+ const rows = await this.adapter.query(
5362
+ "SELECT value FROM _kora_meta WHERE key = 'node_id'"
5363
+ );
5364
+ return rows[0]?.value ?? null;
5365
+ }
5366
+ /**
5367
+ * Register node ids this database authored under that the registry does not know
5368
+ * yet: the node id a pinned `nodeId` (the auth device id) just replaced, and the
5369
+ * authors of queued operations. A beta.12 database had no registry, and a template
5370
+ * app created before its user signed in authored under a random node; once the
5371
+ * store is pinned to the device id, that node's queued writes would otherwise sit in
5372
+ * the queue forever, neither uploaded nor reported. Registered, they are adopted or
5373
+ * held (`status.heldNodes`) like any other local node's writes. Only the outbound
5374
+ * queue is read: received operations never enter it.
5375
+ */
5376
+ async registerUnregisteredAuthors(persistedNodeId) {
5377
+ const authors = /* @__PURE__ */ new Set();
5378
+ if (persistedNodeId !== null && persistedNodeId !== this.nodeId) authors.add(persistedNodeId);
5379
+ const queued = await this.adapter.query(
5380
+ "SELECT payload FROM _kora_sync_queue"
5381
+ );
5382
+ for (const row of queued) {
5383
+ try {
5384
+ const nodeId = JSON.parse(row.payload).node_id;
5385
+ if (typeof nodeId === "string" && nodeId.length > 0) authors.add(nodeId);
5386
+ } catch {
5387
+ }
5388
+ }
5389
+ authors.delete(this.nodeId);
5390
+ for (const nodeId of authors) {
5391
+ if (isReservedNodeId(nodeId)) continue;
5392
+ await registerLocalNode(this.adapter, nodeId);
5393
+ }
5394
+ }
5265
5395
  async loadOrGenerateNodeId() {
5266
5396
  if (this.configNodeId && isReservedNodeId(this.configNodeId)) {
5267
5397
  throw new ReservedNodeIdError(this.configNodeId, "StoreConfig.nodeId");
@@ -5338,4 +5468,4 @@ export {
5338
5468
  ReservedNodeIdError,
5339
5469
  Store
5340
5470
  };
5341
- //# sourceMappingURL=chunk-Y5LRH7KI.js.map
5471
+ //# sourceMappingURL=chunk-5AQSGMJV.js.map