@korajs/sync 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.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SyncScopeMap, a as ScopeSnapshotOptions, D as DeltaCursor, b as SyncMessage, c as SerializedOperation, W as WireFormat, d as SyncTransport, T as TransportOptions, e as TransportMessageHandler, f as TransportCloseHandler, g as TransportErrorHandler, B as BlobChunkRequestMessage, h as BlobChunkResponseMessage, i as BlobChunkPushMessage, j as WrappedKeyRecord, k as SyncEncryptionConfig, V as VersionedKey, Y as YjsDocUpdateMessage, Q as QueueStorage, l as SyncState, m as SyncStatusInfo, n as SyncConfig, R as RejectedOperationStorage, o as SyncStatePersistence, p as SyncQuerySubset, q as QuarantinedOperation, H as HeldNodeInfo, r as RejectedOperation } from './transport-Bemtqwep.cjs';
2
- export { A as AcknowledgmentMessage, s as ActiveApplyFailure, t as AdoptionScheduleInfo, u as AwarenessStateWire, v as AwarenessUpdateMessage, w as DEFAULT_KEYRING, E as EncryptedPayload, x as EncryptionKeyMessage, y as EncryptionKeyPutMessage, z as EncryptionKeyRequestMessage, C as EncryptionKeyResponseMessage, F as EncryptionKeyResponseStatus, G as ErrorMessage, I as HandshakeMessage, J as HandshakeResponseMessage, K as HeartbeatMessage, L as HeldReason, M as INVALID_OPERATION_ID, N as InvalidScopeError, O as KEY_RECORD_FORMAT, P as KeyRecordKdf, U as KeyRecordRecovery, X as KeyRecordValidation, Z as LEGACY_SYNC_PROTOCOL_VERSION, _ as LocalNodeInfo, $ as MAX_KDF_ITERATIONS, a0 as MAX_KEY_RECORD_BYTES, a1 as MemoryRejectedOperationStorage, a2 as OperationBatchMessage, a3 as OperationRejectedMessage, a4 as PLAINTEXT_REJECTED, a5 as PROTOCOL_V1_DEPRECATED, a6 as SYNC_PROTOCOL_VERSION, a7 as SYNC_STATES, a8 as SYNC_STATUSES, a9 as ScopeViolationError, aa as SyncAuthRequest, ab as SyncEncryptionAlgorithm, ac as SyncPhase, ad as SyncScopeContext, ae as SyncSettlementOptions, af as SyncSettlementResult, ag as SyncStatus, ah as TerminalRejectionRecord, ai as WrappedDataKey, aj as WrappedMasterKey, ak as declaredProtocolVersion, al as dedupeQuerySubsets, am as isAcknowledgmentMessage, an as isAwarenessUpdateMessage, ao as isBlobChunkPushMessage, ap as isBlobChunkRequestMessage, aq as isBlobChunkResponseMessage, ar as isEncryptionKeyMessage, as as isErrorMessage, at as isHandshakeMessage, au as isHandshakeResponseMessage, av as isKeyId, aw as isKeyRecordSuccessor, ax as isOperationBatchMessage, ay as isOperationRejectedMessage, az as isSyncMessage, aA as isValidKeyringName, aB as isYjsDocUpdateMessage, aC as operationMatchesQuerySubsets, aD as querySubsetContains, aE as validateKeyRecord } from './transport-Bemtqwep.cjs';
1
+ import { S as SyncScopeMap, a as ScopeSnapshotOptions, D as DeltaCursor, b as SyncMessage, c as SerializedOperation, W as WireFormat, d as SyncTransport, T as TransportOptions, e as TransportMessageHandler, f as TransportCloseHandler, g as TransportErrorHandler, B as BlobChunkRequestMessage, h as BlobChunkResponseMessage, i as BlobChunkPushMessage, j as WrappedKeyRecord, k as SyncEncryptionConfig, V as VersionedKey, Y as YjsDocUpdateMessage, Q as QueueStorage, l as SyncState, m as SyncStatusInfo, n as SyncConfig, R as RejectedOperationStorage, o as SyncStatePersistence, p as SyncQuerySubset, q as QuarantinedOperation, H as HeldNodeInfo, r as RejectedOperation } from './transport-CoPFFarn.cjs';
2
+ export { A as AcknowledgmentMessage, s as ActiveApplyFailure, t as AdoptionScheduleInfo, u as AwarenessStateWire, v as AwarenessUpdateMessage, w as DEFAULT_KEYRING, E as EncryptedPayload, x as EncryptionKeyMessage, y as EncryptionKeyPutMessage, z as EncryptionKeyRequestMessage, C as EncryptionKeyResponseMessage, F as EncryptionKeyResponseStatus, G as ErrorMessage, I as HandshakeMessage, J as HandshakeResponseMessage, K as HeartbeatMessage, L as HeldReason, M as INVALID_OPERATION_ID, N as InvalidScopeError, O as KEY_RECORD_FORMAT, P as KeyRecordKdf, U as KeyRecordRecovery, X as KeyRecordValidation, Z as LEGACY_SYNC_PROTOCOL_VERSION, _ as LocalNodeInfo, $ as MAX_KDF_ITERATIONS, a0 as MAX_KEY_RECORD_BYTES, a1 as MemoryRejectedOperationStorage, a2 as OperationBatchMessage, a3 as OperationRejectedMessage, a4 as PLAINTEXT_REJECTED, a5 as PROTOCOL_V1_DEPRECATED, a6 as SYNC_PROTOCOL_VERSION, a7 as SYNC_STATES, a8 as SYNC_STATUSES, a9 as ScopeViolationError, aa as SyncAuthRequest, ab as SyncEncryptionAlgorithm, ac as SyncPhase, ad as SyncScopeContext, ae as SyncSettlementOptions, af as SyncSettlementResult, ag as SyncStatus, ah as TerminalRejectionRecord, ai as WrappedDataKey, aj as WrappedMasterKey, ak as declaredProtocolVersion, al as dedupeQuerySubsets, am as isAcknowledgmentMessage, an as isAwarenessUpdateMessage, ao as isBlobChunkPushMessage, ap as isBlobChunkRequestMessage, aq as isBlobChunkResponseMessage, ar as isEncryptionKeyMessage, as as isErrorMessage, at as isHandshakeMessage, au as isHandshakeResponseMessage, av as isKeyId, aw as isKeyRecordSuccessor, ax as isOperationBatchMessage, ay as isOperationRejectedMessage, az as isSyncMessage, aA as isValidKeyringName, aB as isYjsDocUpdateMessage, aC as operationMatchesQuerySubsets, aD as querySubsetContains, aE as validateKeyRecord } from './transport-CoPFFarn.cjs';
3
3
  import * as _korajs_core from '@korajs/core';
4
4
  import { Operation, VersionVector, ApplyResult, OperationTransform, KoraEventEmitter, TimeSource, SyncError, ConnectionQuality, KoraError, SchemaDefinition } from '@korajs/core';
5
5
  export { ApplyFailureReason, ApplyResult } from '@korajs/core';
@@ -108,6 +108,21 @@ interface SyncStore {
108
108
  nodeId: string;
109
109
  operations: Operation[];
110
110
  }>;
111
+ /**
112
+ * Optional: the server refused the store's pinned node id as another user's (`true`),
113
+ * or accepted it again (`false`). A pinned store refuses local writes meanwhile (F9).
114
+ */
115
+ setPinnedNodeOwnedElsewhere?(owned: boolean): void;
116
+ /**
117
+ * Optional: re-author another local node's given unsynced operations under a fresh
118
+ * node id bound to `principal`, without moving the store's own node. Called when the
119
+ * server refused an adopted node whose writes belong to the signed-in user but that
120
+ * no claims-aware server ever accepted (a beta.12 node with unowned history).
121
+ */
122
+ reauthorLocalNode?(fromNodeId: string, ids: string[], principal: string): Promise<{
123
+ nodeId: string;
124
+ operations: Operation[];
125
+ }>;
111
126
  /**
112
127
  * Optional: move the device back to a node id this database authored under before
113
128
  * (RT-38), without rewriting anything. Used after the server refused the current node
@@ -2310,6 +2325,19 @@ declare class SyncEngine {
2310
2325
  * - An adopted node (RT-40) is held; the store's own node is left as it is.
2311
2326
  */
2312
2327
  private rotateNodeIdentity;
2328
+ /**
2329
+ * The server refused an adopted node that belongs to the signed-in user (the app
2330
+ * assigned its held writes, or it was created for them) but that no claims-aware
2331
+ * server ever accepted: typically a beta.12 node whose history the server recorded no
2332
+ * owner for, and whose id is not this device's verified id, so no handover applies.
2333
+ * Holding it would hold the user's own writes forever. Like the store's own node
2334
+ * (RT-21), its never-sent writes are re-authored under a fresh node of the user's,
2335
+ * which a later session adopts and uploads; what a beta.12 server acknowledged under
2336
+ * the node is not re-authored (it is stored there already).
2337
+ *
2338
+ * @returns Whether the node's writes were re-authored (false: the caller holds it)
2339
+ */
2340
+ private reauthorRefusedAdoption;
2313
2341
  /** RT-38: hold a refused, previously accepted node's writes and move to another node. */
2314
2342
  private holdNodeAndSwitch;
2315
2343
  /** RT-21: re-author a never-accepted node's never-sent writes under a fresh node. */
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SyncScopeMap, a as ScopeSnapshotOptions, D as DeltaCursor, b as SyncMessage, c as SerializedOperation, W as WireFormat, d as SyncTransport, T as TransportOptions, e as TransportMessageHandler, f as TransportCloseHandler, g as TransportErrorHandler, B as BlobChunkRequestMessage, h as BlobChunkResponseMessage, i as BlobChunkPushMessage, j as WrappedKeyRecord, k as SyncEncryptionConfig, V as VersionedKey, Y as YjsDocUpdateMessage, Q as QueueStorage, l as SyncState, m as SyncStatusInfo, n as SyncConfig, R as RejectedOperationStorage, o as SyncStatePersistence, p as SyncQuerySubset, q as QuarantinedOperation, H as HeldNodeInfo, r as RejectedOperation } from './transport-Bemtqwep.js';
2
- export { A as AcknowledgmentMessage, s as ActiveApplyFailure, t as AdoptionScheduleInfo, u as AwarenessStateWire, v as AwarenessUpdateMessage, w as DEFAULT_KEYRING, E as EncryptedPayload, x as EncryptionKeyMessage, y as EncryptionKeyPutMessage, z as EncryptionKeyRequestMessage, C as EncryptionKeyResponseMessage, F as EncryptionKeyResponseStatus, G as ErrorMessage, I as HandshakeMessage, J as HandshakeResponseMessage, K as HeartbeatMessage, L as HeldReason, M as INVALID_OPERATION_ID, N as InvalidScopeError, O as KEY_RECORD_FORMAT, P as KeyRecordKdf, U as KeyRecordRecovery, X as KeyRecordValidation, Z as LEGACY_SYNC_PROTOCOL_VERSION, _ as LocalNodeInfo, $ as MAX_KDF_ITERATIONS, a0 as MAX_KEY_RECORD_BYTES, a1 as MemoryRejectedOperationStorage, a2 as OperationBatchMessage, a3 as OperationRejectedMessage, a4 as PLAINTEXT_REJECTED, a5 as PROTOCOL_V1_DEPRECATED, a6 as SYNC_PROTOCOL_VERSION, a7 as SYNC_STATES, a8 as SYNC_STATUSES, a9 as ScopeViolationError, aa as SyncAuthRequest, ab as SyncEncryptionAlgorithm, ac as SyncPhase, ad as SyncScopeContext, ae as SyncSettlementOptions, af as SyncSettlementResult, ag as SyncStatus, ah as TerminalRejectionRecord, ai as WrappedDataKey, aj as WrappedMasterKey, ak as declaredProtocolVersion, al as dedupeQuerySubsets, am as isAcknowledgmentMessage, an as isAwarenessUpdateMessage, ao as isBlobChunkPushMessage, ap as isBlobChunkRequestMessage, aq as isBlobChunkResponseMessage, ar as isEncryptionKeyMessage, as as isErrorMessage, at as isHandshakeMessage, au as isHandshakeResponseMessage, av as isKeyId, aw as isKeyRecordSuccessor, ax as isOperationBatchMessage, ay as isOperationRejectedMessage, az as isSyncMessage, aA as isValidKeyringName, aB as isYjsDocUpdateMessage, aC as operationMatchesQuerySubsets, aD as querySubsetContains, aE as validateKeyRecord } from './transport-Bemtqwep.js';
1
+ import { S as SyncScopeMap, a as ScopeSnapshotOptions, D as DeltaCursor, b as SyncMessage, c as SerializedOperation, W as WireFormat, d as SyncTransport, T as TransportOptions, e as TransportMessageHandler, f as TransportCloseHandler, g as TransportErrorHandler, B as BlobChunkRequestMessage, h as BlobChunkResponseMessage, i as BlobChunkPushMessage, j as WrappedKeyRecord, k as SyncEncryptionConfig, V as VersionedKey, Y as YjsDocUpdateMessage, Q as QueueStorage, l as SyncState, m as SyncStatusInfo, n as SyncConfig, R as RejectedOperationStorage, o as SyncStatePersistence, p as SyncQuerySubset, q as QuarantinedOperation, H as HeldNodeInfo, r as RejectedOperation } from './transport-CoPFFarn.js';
2
+ export { A as AcknowledgmentMessage, s as ActiveApplyFailure, t as AdoptionScheduleInfo, u as AwarenessStateWire, v as AwarenessUpdateMessage, w as DEFAULT_KEYRING, E as EncryptedPayload, x as EncryptionKeyMessage, y as EncryptionKeyPutMessage, z as EncryptionKeyRequestMessage, C as EncryptionKeyResponseMessage, F as EncryptionKeyResponseStatus, G as ErrorMessage, I as HandshakeMessage, J as HandshakeResponseMessage, K as HeartbeatMessage, L as HeldReason, M as INVALID_OPERATION_ID, N as InvalidScopeError, O as KEY_RECORD_FORMAT, P as KeyRecordKdf, U as KeyRecordRecovery, X as KeyRecordValidation, Z as LEGACY_SYNC_PROTOCOL_VERSION, _ as LocalNodeInfo, $ as MAX_KDF_ITERATIONS, a0 as MAX_KEY_RECORD_BYTES, a1 as MemoryRejectedOperationStorage, a2 as OperationBatchMessage, a3 as OperationRejectedMessage, a4 as PLAINTEXT_REJECTED, a5 as PROTOCOL_V1_DEPRECATED, a6 as SYNC_PROTOCOL_VERSION, a7 as SYNC_STATES, a8 as SYNC_STATUSES, a9 as ScopeViolationError, aa as SyncAuthRequest, ab as SyncEncryptionAlgorithm, ac as SyncPhase, ad as SyncScopeContext, ae as SyncSettlementOptions, af as SyncSettlementResult, ag as SyncStatus, ah as TerminalRejectionRecord, ai as WrappedDataKey, aj as WrappedMasterKey, ak as declaredProtocolVersion, al as dedupeQuerySubsets, am as isAcknowledgmentMessage, an as isAwarenessUpdateMessage, ao as isBlobChunkPushMessage, ap as isBlobChunkRequestMessage, aq as isBlobChunkResponseMessage, ar as isEncryptionKeyMessage, as as isErrorMessage, at as isHandshakeMessage, au as isHandshakeResponseMessage, av as isKeyId, aw as isKeyRecordSuccessor, ax as isOperationBatchMessage, ay as isOperationRejectedMessage, az as isSyncMessage, aA as isValidKeyringName, aB as isYjsDocUpdateMessage, aC as operationMatchesQuerySubsets, aD as querySubsetContains, aE as validateKeyRecord } from './transport-CoPFFarn.js';
3
3
  import * as _korajs_core from '@korajs/core';
4
4
  import { Operation, VersionVector, ApplyResult, OperationTransform, KoraEventEmitter, TimeSource, SyncError, ConnectionQuality, KoraError, SchemaDefinition } from '@korajs/core';
5
5
  export { ApplyFailureReason, ApplyResult } from '@korajs/core';
@@ -108,6 +108,21 @@ interface SyncStore {
108
108
  nodeId: string;
109
109
  operations: Operation[];
110
110
  }>;
111
+ /**
112
+ * Optional: the server refused the store's pinned node id as another user's (`true`),
113
+ * or accepted it again (`false`). A pinned store refuses local writes meanwhile (F9).
114
+ */
115
+ setPinnedNodeOwnedElsewhere?(owned: boolean): void;
116
+ /**
117
+ * Optional: re-author another local node's given unsynced operations under a fresh
118
+ * node id bound to `principal`, without moving the store's own node. Called when the
119
+ * server refused an adopted node whose writes belong to the signed-in user but that
120
+ * no claims-aware server ever accepted (a beta.12 node with unowned history).
121
+ */
122
+ reauthorLocalNode?(fromNodeId: string, ids: string[], principal: string): Promise<{
123
+ nodeId: string;
124
+ operations: Operation[];
125
+ }>;
111
126
  /**
112
127
  * Optional: move the device back to a node id this database authored under before
113
128
  * (RT-38), without rewriting anything. Used after the server refused the current node
@@ -2310,6 +2325,19 @@ declare class SyncEngine {
2310
2325
  * - An adopted node (RT-40) is held; the store's own node is left as it is.
2311
2326
  */
2312
2327
  private rotateNodeIdentity;
2328
+ /**
2329
+ * The server refused an adopted node that belongs to the signed-in user (the app
2330
+ * assigned its held writes, or it was created for them) but that no claims-aware
2331
+ * server ever accepted: typically a beta.12 node whose history the server recorded no
2332
+ * owner for, and whose id is not this device's verified id, so no handover applies.
2333
+ * Holding it would hold the user's own writes forever. Like the store's own node
2334
+ * (RT-21), its never-sent writes are re-authored under a fresh node of the user's,
2335
+ * which a later session adopts and uploads; what a beta.12 server acknowledged under
2336
+ * the node is not re-authored (it is stored there already).
2337
+ *
2338
+ * @returns Whether the node's writes were re-authored (false: the caller holds it)
2339
+ */
2340
+ private reauthorRefusedAdoption;
2313
2341
  /** RT-38: hold a refused, previously accepted node's writes and move to another node. */
2314
2342
  private holdNodeAndSwitch;
2315
2343
  /** RT-21: re-author a never-accepted node's never-sent writes under a fresh node. */
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  recordMatchesScopePredicates,
11
11
  scopeViewKey,
12
12
  verifyInboundOperation
13
- } from "./chunk-5S6U7EVU.js";
13
+ } from "./chunk-YMPQ53EI.js";
14
14
 
15
15
  // src/types.ts
16
16
  import { KoraError } from "@korajs/core";
@@ -7504,10 +7504,12 @@ var SyncEngine = class {
7504
7504
  this.returnAllInFlightUploads();
7505
7505
  try {
7506
7506
  const principal = this.sessionPrincipal;
7507
+ const before = refused !== storeNode ? (await this.syncState?.listLocalNodes?.())?.find((entry) => entry.nodeId === refused) : void 0;
7507
7508
  if (typeof principal === "string") {
7508
7509
  await this.syncState?.recordLocalNodeRefusedFor?.(refused, principal);
7509
7510
  }
7510
7511
  if (refused !== storeNode) {
7512
+ if (before && await this.reauthorRefusedAdoption(refused, before)) return;
7511
7513
  const node = (await this.syncState?.listLocalNodes?.())?.find(
7512
7514
  (entry) => entry.nodeId === refused
7513
7515
  );
@@ -7537,6 +7539,31 @@ var SyncEngine = class {
7537
7539
  });
7538
7540
  }
7539
7541
  }
7542
+ /**
7543
+ * The server refused an adopted node that belongs to the signed-in user (the app
7544
+ * assigned its held writes, or it was created for them) but that no claims-aware
7545
+ * server ever accepted: typically a beta.12 node whose history the server recorded no
7546
+ * owner for, and whose id is not this device's verified id, so no handover applies.
7547
+ * Holding it would hold the user's own writes forever. Like the store's own node
7548
+ * (RT-21), its never-sent writes are re-authored under a fresh node of the user's,
7549
+ * which a later session adopts and uploads; what a beta.12 server acknowledged under
7550
+ * the node is not re-authored (it is stored there already).
7551
+ *
7552
+ * @returns Whether the node's writes were re-authored (false: the caller holds it)
7553
+ */
7554
+ async reauthorRefusedAdoption(refused, node) {
7555
+ const reauthor = this.store.reauthorLocalNode?.bind(this.store);
7556
+ const principal = this.sessionPrincipal;
7557
+ if (!reauthor || typeof principal !== "string") return false;
7558
+ if ((node.principal ?? null) !== principal || node.held) return false;
7559
+ if (node.binding !== "app" && node.binding !== "fresh") return false;
7560
+ if (node.accepted || await this.wasNodeAccepted(refused)) return false;
7561
+ await this.syncState?.markLocalNodeRefused?.(refused, false);
7562
+ this.endAdoption();
7563
+ await this.rotateUnsent(refused, (ids) => reauthor(refused, ids, principal));
7564
+ this.emitter?.emit({ type: "sync:local-node", nodeId: refused, action: "adoption-refused" });
7565
+ return true;
7566
+ }
7540
7567
  /** RT-38: hold a refused, previously accepted node's writes and move to another node. */
7541
7568
  async holdNodeAndSwitch(refused, rotate) {
7542
7569
  await this.syncState?.markLocalNodeRefused?.(refused, true);
@@ -8438,6 +8465,9 @@ var SyncEngine = class {
8438
8465
  if (isCredentialEndingCode(msg.code)) {
8439
8466
  this.credentialRefreshRequired = true;
8440
8467
  }
8468
+ if (msg.code === "NODE_ID_CLAIMED" && msg.nodeOwnership === "other-principal" && this.currentNodeId() === this.store.getNodeId()) {
8469
+ this.store.setPinnedNodeOwnedElsewhere?.(true);
8470
+ }
8441
8471
  if (msg.code === "NODE_ID_CLAIMED" && !this.nodeRotation) {
8442
8472
  this.nodeRotation = this.rotateNodeIdentity().finally(() => {
8443
8473
  this.nodeRotation = null;
@@ -9307,6 +9337,7 @@ var SyncEngine = class {
9307
9337
  async recordNodeAccepted(nodeId) {
9308
9338
  this.memoryAcceptedNodes.add(nodeId);
9309
9339
  this.heldNodeIds.delete(nodeId);
9340
+ if (nodeId === this.store.getNodeId()) this.store.setPinnedNodeOwnedElsewhere?.(false);
9310
9341
  try {
9311
9342
  await this.syncState?.markLocalNodeAccepted?.(nodeId);
9312
9343
  const principal = this.sessionPrincipal;