@tanstack/db-sqlite-persistence-core 0.4.3 → 0.4.5

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.
@@ -22,10 +22,61 @@ function serializeSQLiteBigInt(value) {
22
22
  [PERSISTED_VALUE_TAG]: value.toString()
23
23
  };
24
24
  }
25
+ function sqliteTemporalKind(value) {
26
+ if (value === null || typeof value !== `object`) return;
27
+ const tag = value[Symbol.toStringTag];
28
+ if (tag === `Temporal.Instant` || tag === `Temporal.PlainDate`) return tag;
29
+ return void 0;
30
+ }
31
+ function requireSQLiteTemporalConstructor(kind) {
32
+ const name = kind.slice(`Temporal.`.length);
33
+ const constructor = globalThis.Temporal?.[name];
34
+ if (typeof constructor?.from !== `function` || typeof constructor.prototype.toString !== `function`) {
35
+ throw new errors.InvalidPersistedCollectionConfigError(
36
+ `Missing global ${kind} constructor for SQLite persistence`
37
+ );
38
+ }
39
+ return constructor;
40
+ }
41
+ function reviveSQLiteTemporal(kind, value) {
42
+ return requireSQLiteTemporalConstructor(kind).from(value);
43
+ }
44
+ function serializeSQLiteTemporal(value) {
45
+ if (value === null || typeof value !== `object`) return;
46
+ const prototype = Object.getPrototypeOf(value);
47
+ if (prototype === null || prototype === Object.prototype) return;
48
+ const tag = value[Symbol.toStringTag];
49
+ if (typeof tag !== `string` || !tag.startsWith(`Temporal.`)) return;
50
+ const kind = sqliteTemporalKind(value);
51
+ if (!kind)
52
+ throw new errors.InvalidPersistedCollectionConfigError(
53
+ `Unsupported ${tag} value for SQLite persistence`
54
+ );
55
+ const constructor = requireSQLiteTemporalConstructor(kind);
56
+ let text;
57
+ try {
58
+ text = constructor.prototype.toString.call(value);
59
+ } catch {
60
+ throw new errors.InvalidPersistedCollectionConfigError(
61
+ `Invalid ${kind} value for the registered global constructor`
62
+ );
63
+ }
64
+ const native = constructor.from(text);
65
+ const iso = kind === `Temporal.PlainDate` ? native.withCalendar(`iso8601`) : void 0;
66
+ const order = iso ? `${String(iso.year + 1e6).padStart(7, `0`)}${String(iso.month).padStart(2, `0`)}${String(iso.day).padStart(2, `0`)}` : (native.epochNanoseconds + 10000000000000000000000n).toString().padStart(23, `0`);
67
+ return { [PERSISTED_TYPE_TAG]: kind, [PERSISTED_VALUE_TAG]: text, order };
68
+ }
69
+ function sqliteTemporalIdentity(value) {
70
+ return `${value[PERSISTED_TYPE_TAG]}:${value[PERSISTED_VALUE_TAG]}`;
71
+ }
25
72
  exports.PERSISTED_TYPE_TAG = PERSISTED_TYPE_TAG;
26
73
  exports.PERSISTED_VALUE_TAG = PERSISTED_VALUE_TAG;
27
74
  exports.SQLITE_BIGINT_MAX = SQLITE_BIGINT_MAX;
28
75
  exports.SQLITE_BIGINT_MIN = SQLITE_BIGINT_MIN;
29
76
  exports.assertSQLiteBigIntInRange = assertSQLiteBigIntInRange;
77
+ exports.reviveSQLiteTemporal = reviveSQLiteTemporal;
30
78
  exports.serializeSQLiteBigInt = serializeSQLiteBigInt;
79
+ exports.serializeSQLiteTemporal = serializeSQLiteTemporal;
80
+ exports.sqliteTemporalIdentity = sqliteTemporalIdentity;
81
+ exports.sqliteTemporalKind = sqliteTemporalKind;
31
82
  //# sourceMappingURL=sqlite-value.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-value.cjs","sources":["../../src/sqlite-value.ts"],"sourcesContent":["import { SQLiteBigIntOutOfRangeError } from './errors'\n\nexport const SQLITE_BIGINT_MIN = -9_223_372_036_854_775_808n\nexport const SQLITE_BIGINT_MAX = 9_223_372_036_854_775_807n\nexport const PERSISTED_TYPE_TAG = `__tanstack_db_persisted_type__`\nexport const PERSISTED_VALUE_TAG = `value`\n\nexport type SerializedSQLiteBigInt = {\n [PERSISTED_TYPE_TAG]: `bigint`\n [PERSISTED_VALUE_TAG]: string\n}\n\nexport function assertSQLiteBigIntInRange(value: bigint): bigint {\n if (value < SQLITE_BIGINT_MIN || value > SQLITE_BIGINT_MAX) {\n throw new SQLiteBigIntOutOfRangeError(\n value,\n SQLITE_BIGINT_MIN,\n SQLITE_BIGINT_MAX,\n )\n }\n return value\n}\n\nexport function serializeSQLiteBigInt(value: bigint): SerializedSQLiteBigInt {\n assertSQLiteBigIntInRange(value)\n return {\n [PERSISTED_TYPE_TAG]: `bigint`,\n [PERSISTED_VALUE_TAG]: value.toString(),\n }\n}\n"],"names":["SQLiteBigIntOutOfRangeError"],"mappings":";;;AAEO,MAAM,oBAAoB,CAAC;AAC3B,MAAM,oBAAoB;AAC1B,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAO5B,SAAS,0BAA0B,OAAuB;AAC/D,MAAI,QAAQ,qBAAqB,QAAQ,mBAAmB;AAC1D,UAAM,IAAIA,OAAAA;AAAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AACA,SAAO;AACT;AAEO,SAAS,sBAAsB,OAAuC;AAC3E,4BAA0B,KAAK;AAC/B,SAAO;AAAA,IACL,CAAC,kBAAkB,GAAG;AAAA,IACtB,CAAC,mBAAmB,GAAG,MAAM,SAAA;AAAA,EAAS;AAE1C;;;;;;;"}
1
+ {"version":3,"file":"sqlite-value.cjs","sources":["../../src/sqlite-value.ts"],"sourcesContent":["import {\n InvalidPersistedCollectionConfigError,\n SQLiteBigIntOutOfRangeError,\n} from './errors'\n\nexport const SQLITE_BIGINT_MIN = -9_223_372_036_854_775_808n\nexport const SQLITE_BIGINT_MAX = 9_223_372_036_854_775_807n\nexport const PERSISTED_TYPE_TAG = `__tanstack_db_persisted_type__`\nexport const PERSISTED_VALUE_TAG = `value`\n\nexport type SerializedSQLiteBigInt = {\n [PERSISTED_TYPE_TAG]: `bigint`\n [PERSISTED_VALUE_TAG]: string\n}\n\nexport function assertSQLiteBigIntInRange(value: bigint): bigint {\n if (value < SQLITE_BIGINT_MIN || value > SQLITE_BIGINT_MAX) {\n throw new SQLiteBigIntOutOfRangeError(\n value,\n SQLITE_BIGINT_MIN,\n SQLITE_BIGINT_MAX,\n )\n }\n return value\n}\n\nexport function serializeSQLiteBigInt(value: bigint): SerializedSQLiteBigInt {\n assertSQLiteBigIntInRange(value)\n return {\n [PERSISTED_TYPE_TAG]: `bigint`,\n [PERSISTED_VALUE_TAG]: value.toString(),\n }\n}\n\ntype SQLiteTemporalKind = `Temporal.Instant` | `Temporal.PlainDate`\ntype SQLiteTemporal = {\n epochNanoseconds: bigint\n year: number\n month: number\n day: number\n withCalendar: (calendar: string) => SQLiteTemporal\n}\ntype TemporalConstructor = {\n from: (value: string) => SQLiteTemporal\n prototype: { toString: () => string }\n}\n\nexport type SerializedSQLiteTemporal = {\n [PERSISTED_TYPE_TAG]: SQLiteTemporalKind\n [PERSISTED_VALUE_TAG]: string\n order: string\n}\n\nexport function sqliteTemporalKind(\n value: unknown,\n): SQLiteTemporalKind | undefined {\n if (value === null || typeof value !== `object`) return\n const tag = (value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag]\n if (tag === `Temporal.Instant` || tag === `Temporal.PlainDate`) return tag\n return undefined\n}\n\nfunction requireSQLiteTemporalConstructor(\n kind: SQLiteTemporalKind,\n): TemporalConstructor {\n const name = kind.slice(`Temporal.`.length)\n const constructor = (\n globalThis as { Temporal?: Record<string, TemporalConstructor> }\n ).Temporal?.[name]\n if (\n typeof constructor?.from !== `function` ||\n typeof constructor.prototype.toString !== `function`\n ) {\n throw new InvalidPersistedCollectionConfigError(\n `Missing global ${kind} constructor for SQLite persistence`,\n )\n }\n return constructor\n}\n\nexport function reviveSQLiteTemporal(\n kind: SQLiteTemporalKind,\n value: string,\n): unknown {\n return requireSQLiteTemporalConstructor(kind).from(value)\n}\n\n/** Registered prototype methods validate the brand; user toString is ignored. */\nexport function serializeSQLiteTemporal(\n value: unknown,\n): SerializedSQLiteTemporal | undefined {\n if (value === null || typeof value !== `object`) return\n const prototype = Object.getPrototypeOf(value)\n if (prototype === null || prototype === Object.prototype) return\n const tag = (value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag]\n if (typeof tag !== `string` || !tag.startsWith(`Temporal.`)) return\n const kind = sqliteTemporalKind(value)\n if (!kind)\n throw new InvalidPersistedCollectionConfigError(\n `Unsupported ${tag} value for SQLite persistence`,\n )\n const constructor = requireSQLiteTemporalConstructor(kind)\n let text: string\n try {\n text = constructor.prototype.toString.call(value)\n } catch {\n throw new InvalidPersistedCollectionConfigError(\n `Invalid ${kind} value for the registered global constructor`,\n )\n }\n const native = constructor.from(text)\n const iso =\n kind === `Temporal.PlainDate` ? native.withCalendar(`iso8601`) : undefined\n // Text keys avoid both floating-point precision loss and SQLite's int64 cap.\n const order = iso\n ? `${String(iso.year + 1_000_000).padStart(7, `0`)}${String(iso.month).padStart(2, `0`)}${String(iso.day).padStart(2, `0`)}`\n : (native.epochNanoseconds + 10_000_000_000_000_000_000_000n)\n .toString()\n .padStart(23, `0`)\n return { [PERSISTED_TYPE_TAG]: kind, [PERSISTED_VALUE_TAG]: text, order }\n}\n\nexport function sqliteTemporalIdentity(\n value: SerializedSQLiteTemporal,\n): string {\n return `${value[PERSISTED_TYPE_TAG]}:${value[PERSISTED_VALUE_TAG]}`\n}\n"],"names":["SQLiteBigIntOutOfRangeError","InvalidPersistedCollectionConfigError"],"mappings":";;;AAKO,MAAM,oBAAoB,CAAC;AAC3B,MAAM,oBAAoB;AAC1B,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAO5B,SAAS,0BAA0B,OAAuB;AAC/D,MAAI,QAAQ,qBAAqB,QAAQ,mBAAmB;AAC1D,UAAM,IAAIA,OAAAA;AAAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AACA,SAAO;AACT;AAEO,SAAS,sBAAsB,OAAuC;AAC3E,4BAA0B,KAAK;AAC/B,SAAO;AAAA,IACL,CAAC,kBAAkB,GAAG;AAAA,IACtB,CAAC,mBAAmB,GAAG,MAAM,SAAA;AAAA,EAAS;AAE1C;AAqBO,SAAS,mBACd,OACgC;AAChC,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU;AACjD,QAAM,MAAO,MAA6C,OAAO,WAAW;AAC5E,MAAI,QAAQ,sBAAsB,QAAQ,qBAAsB,QAAO;AACvE,SAAO;AACT;AAEA,SAAS,iCACP,MACqB;AACrB,QAAM,OAAO,KAAK,MAAM,YAAY,MAAM;AAC1C,QAAM,cACJ,WACA,WAAW,IAAI;AACjB,MACE,OAAO,aAAa,SAAS,cAC7B,OAAO,YAAY,UAAU,aAAa,YAC1C;AACA,UAAM,IAAIC,OAAAA;AAAAA,MACR,kBAAkB,IAAI;AAAA,IAAA;AAAA,EAE1B;AACA,SAAO;AACT;AAEO,SAAS,qBACd,MACA,OACS;AACT,SAAO,iCAAiC,IAAI,EAAE,KAAK,KAAK;AAC1D;AAGO,SAAS,wBACd,OACsC;AACtC,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU;AACjD,QAAM,YAAY,OAAO,eAAe,KAAK;AAC7C,MAAI,cAAc,QAAQ,cAAc,OAAO,UAAW;AAC1D,QAAM,MAAO,MAA6C,OAAO,WAAW;AAC5E,MAAI,OAAO,QAAQ,YAAY,CAAC,IAAI,WAAW,WAAW,EAAG;AAC7D,QAAM,OAAO,mBAAmB,KAAK;AACrC,MAAI,CAAC;AACH,UAAM,IAAIA,OAAAA;AAAAA,MACR,eAAe,GAAG;AAAA,IAAA;AAEtB,QAAM,cAAc,iCAAiC,IAAI;AACzD,MAAI;AACJ,MAAI;AACF,WAAO,YAAY,UAAU,SAAS,KAAK,KAAK;AAAA,EAClD,QAAQ;AACN,UAAM,IAAIA,OAAAA;AAAAA,MACR,WAAW,IAAI;AAAA,IAAA;AAAA,EAEnB;AACA,QAAM,SAAS,YAAY,KAAK,IAAI;AACpC,QAAM,MACJ,SAAS,uBAAuB,OAAO,aAAa,SAAS,IAAI;AAEnE,QAAM,QAAQ,MACV,GAAG,OAAO,IAAI,OAAO,GAAS,EAAE,SAAS,GAAG,GAAG,CAAC,GAAG,OAAO,IAAI,KAAK,EAAE,SAAS,GAAG,GAAG,CAAC,GAAG,OAAO,IAAI,GAAG,EAAE,SAAS,GAAG,GAAG,CAAC,MACvH,OAAO,mBAAmB,0BACxB,WACA,SAAS,IAAI,GAAG;AACvB,SAAO,EAAE,CAAC,kBAAkB,GAAG,MAAM,CAAC,mBAAmB,GAAG,MAAM,MAAA;AACpE;AAEO,SAAS,uBACd,OACQ;AACR,SAAO,GAAG,MAAM,kBAAkB,CAAC,IAAI,MAAM,mBAAmB,CAAC;AACnE;;;;;;;;;;;"}
@@ -8,3 +8,15 @@ export type SerializedSQLiteBigInt = {
8
8
  };
9
9
  export declare function assertSQLiteBigIntInRange(value: bigint): bigint;
10
10
  export declare function serializeSQLiteBigInt(value: bigint): SerializedSQLiteBigInt;
11
+ type SQLiteTemporalKind = `Temporal.Instant` | `Temporal.PlainDate`;
12
+ export type SerializedSQLiteTemporal = {
13
+ [PERSISTED_TYPE_TAG]: SQLiteTemporalKind;
14
+ [PERSISTED_VALUE_TAG]: string;
15
+ order: string;
16
+ };
17
+ export declare function sqliteTemporalKind(value: unknown): SQLiteTemporalKind | undefined;
18
+ export declare function reviveSQLiteTemporal(kind: SQLiteTemporalKind, value: string): unknown;
19
+ /** Registered prototype methods validate the brand; user toString is ignored. */
20
+ export declare function serializeSQLiteTemporal(value: unknown): SerializedSQLiteTemporal | undefined;
21
+ export declare function sqliteTemporalIdentity(value: SerializedSQLiteTemporal): string;
22
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sources":["../../src/errors.ts"],"sourcesContent":["import { TanStackDBError } from '@tanstack/db'\n\nexport class PersistedCollectionCoreError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `PersistedCollectionCoreError`\n }\n}\n\nexport class InvalidPersistedCollectionConfigError extends PersistedCollectionCoreError {\n constructor(message: string) {\n super(message)\n this.name = `InvalidPersistedCollectionConfigError`\n }\n}\n\nexport class InvalidSyncConfigError extends InvalidPersistedCollectionConfigError {\n constructor(details?: string) {\n super(\n details\n ? `Invalid sync config: ${details}`\n : `Invalid sync config: expected an object with a callable sync function`,\n )\n this.name = `InvalidSyncConfigError`\n }\n}\n\nexport class InvalidPersistedCollectionCoordinatorError extends InvalidPersistedCollectionConfigError {\n constructor(methodName: string) {\n super(\n `Invalid persisted collection coordinator: missing required \"${methodName}\" method`,\n )\n this.name = `InvalidPersistedCollectionCoordinatorError`\n }\n}\n\nexport class InvalidPersistenceAdapterError extends InvalidPersistedCollectionConfigError {\n constructor(methodName: string) {\n super(\n `Invalid persistence adapter: missing required \"${methodName}\" method`,\n )\n this.name = `InvalidPersistenceAdapterError`\n }\n}\n\nexport type PersistedCollectionDurabilityErrorOptions = {\n cause?: unknown\n code?: unknown\n path?: unknown\n}\n\nexport class PersistedCollectionDurabilityError extends PersistedCollectionCoreError {\n override readonly cause: unknown\n readonly code: unknown\n readonly path: unknown\n\n constructor(\n message: string,\n options: PersistedCollectionDurabilityErrorOptions = {},\n ) {\n super(message)\n this.name = `PersistedCollectionDurabilityError`\n this.cause = options.cause\n this.code = options.code\n this.path = options.path\n }\n}\n\nexport function toPersistedCollectionDurabilityError(\n collectionId: string,\n cause: unknown,\n): PersistedCollectionDurabilityError {\n if (cause instanceof PersistedCollectionDurabilityError) {\n return cause\n }\n\n const details =\n typeof cause === `object` && cause !== null\n ? (cause as Record<string, unknown>)\n : undefined\n const message = cause instanceof Error ? cause.message : String(cause)\n return new PersistedCollectionDurabilityError(\n `Failed to durably persist collection \"${collectionId}\": ${message}`,\n {\n cause,\n code: details?.code,\n path: details?.path,\n },\n )\n}\n\nexport type IndeterminateCommitRequestType =\n | `rpc:applyLocalMutations:req`\n | `rpc:applyCommittedTx:req`\n\nexport type IndeterminateCommitErrorOptions = {\n collectionId: string\n requestType: IndeterminateCommitRequestType\n previousLeaderId: string | null\n previousTerm: number | null\n currentLeaderId: string | null\n currentTerm: number | null\n cause: unknown\n}\n\nexport class IndeterminateCommitError extends PersistedCollectionCoreError {\n readonly code: `INDETERMINATE_COMMIT` = `INDETERMINATE_COMMIT`\n readonly collectionId: string\n readonly requestType: IndeterminateCommitRequestType\n readonly previousLeaderId: string | null\n readonly previousTerm: number | null\n readonly currentLeaderId: string | null\n readonly currentTerm: number | null\n override readonly cause: unknown\n\n constructor(options: IndeterminateCommitErrorOptions) {\n super(\n `Commit outcome is indeterminate for collection \"${options.collectionId}\": ${options.requestType} crossed leadership from ${formatLeader(options.previousLeaderId, options.previousTerm)} to ${formatLeader(options.currentLeaderId, options.currentTerm)}`,\n )\n this.name = `IndeterminateCommitError`\n this.collectionId = options.collectionId\n this.requestType = options.requestType\n this.previousLeaderId = options.previousLeaderId\n this.previousTerm = options.previousTerm\n this.currentLeaderId = options.currentLeaderId\n this.currentTerm = options.currentTerm\n this.cause = options.cause\n }\n}\n\nfunction formatLeader(leaderId: string | null, term: number | null): string {\n return `${leaderId ?? `unknown leader`} (term ${term ?? `unknown`})`\n}\n\nexport class DuplicateRemoteSubsetOwnerError extends PersistedCollectionCoreError {\n readonly collectionId: string\n\n constructor(collectionId: string) {\n super(\n `A remote subset owner is already registered for collection \"${collectionId}\"`,\n )\n this.name = `DuplicateRemoteSubsetOwnerError`\n this.collectionId = collectionId\n }\n}\n\nexport class RetryableRemoteSubsetAcquisitionError extends PersistedCollectionCoreError {\n override readonly cause: unknown\n\n constructor(message: string, cause?: unknown) {\n super(message)\n this.name = `RetryableRemoteSubsetAcquisitionError`\n this.cause = cause\n }\n}\n\nexport class InvalidPersistedStorageKeyError extends InvalidPersistedCollectionConfigError {\n constructor(key: string | number) {\n super(\n `Invalid persisted storage key \"${String(key)}\": numeric keys must be finite`,\n )\n this.name = `InvalidPersistedStorageKeyError`\n }\n}\n\nexport class InvalidPersistedStorageKeyEncodingError extends InvalidPersistedCollectionConfigError {\n constructor(encoded: string) {\n super(\n `Invalid persisted storage key encoding \"${encoded}\": expected prefix \"n:\" or \"s:\"`,\n )\n this.name = `InvalidPersistedStorageKeyEncodingError`\n }\n}\n\nexport class SQLiteBigIntOutOfRangeError extends PersistedCollectionCoreError {\n constructor(value: bigint, minimum: bigint, maximum: bigint) {\n super(\n `SQLite BigInt value ${value} is outside the signed 64-bit range [${minimum}, ${maximum}]`,\n )\n this.name = `SQLiteBigIntOutOfRangeError`\n }\n}\n\nexport class PersistenceUnavailableError extends PersistedCollectionCoreError {\n constructor(details?: string) {\n super(\n details\n ? `Persistence unavailable: ${details}`\n : `Persistence unavailable in this runtime`,\n )\n this.name = `PersistenceUnavailableError`\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","sources":["../../src/errors.ts"],"sourcesContent":["import { TanStackDBError } from '@tanstack/db'\n\nexport class PersistedCollectionCoreError extends TanStackDBError {\n constructor(message: string) {\n super(message)\n this.name = `PersistedCollectionCoreError`\n }\n}\n\nexport class InvalidPersistedCollectionConfigError extends PersistedCollectionCoreError {\n constructor(message: string) {\n super(message)\n this.name = `InvalidPersistedCollectionConfigError`\n }\n}\n\nexport class InvalidSyncConfigError extends InvalidPersistedCollectionConfigError {\n constructor(details?: string) {\n super(\n details\n ? `Invalid sync config: ${details}`\n : `Invalid sync config: expected an object with a callable sync function`,\n )\n this.name = `InvalidSyncConfigError`\n }\n}\n\nexport class InvalidPersistedCollectionCoordinatorError extends InvalidPersistedCollectionConfigError {\n constructor(methodName: string) {\n super(\n `Invalid persisted collection coordinator: missing required \"${methodName}\" method`,\n )\n this.name = `InvalidPersistedCollectionCoordinatorError`\n }\n}\n\nexport class InvalidPersistenceAdapterError extends InvalidPersistedCollectionConfigError {\n constructor(methodName: string) {\n super(\n `Invalid persistence adapter: missing required \"${methodName}\" method`,\n )\n this.name = `InvalidPersistenceAdapterError`\n }\n}\n\nexport type PersistedCollectionDurabilityErrorOptions = {\n cause?: unknown\n code?: unknown\n path?: unknown\n}\n\nexport class PersistedCollectionDurabilityError extends PersistedCollectionCoreError {\n override readonly cause: unknown\n readonly code: unknown\n readonly path: unknown\n\n constructor(\n message: string,\n options: PersistedCollectionDurabilityErrorOptions = {},\n ) {\n super(message)\n this.name = `PersistedCollectionDurabilityError`\n this.cause = options.cause\n this.code = options.code\n this.path = options.path\n }\n}\n\nexport function toPersistedCollectionDurabilityError(\n collectionId: string,\n cause: unknown,\n): PersistedCollectionDurabilityError {\n if (cause instanceof PersistedCollectionDurabilityError) {\n return cause\n }\n\n const details =\n typeof cause === `object` && cause !== null\n ? (cause as Record<string, unknown>)\n : undefined\n const message = cause instanceof Error ? cause.message : String(cause)\n return new PersistedCollectionDurabilityError(\n `Failed to durably persist collection \"${collectionId}\": ${message}`,\n {\n cause,\n code: details?.code,\n path: details?.path,\n },\n )\n}\n\nexport type IndeterminateCommitRequestType =\n `rpc:applyLocalMutations:req` | `rpc:applyCommittedTx:req`\n\nexport type IndeterminateCommitErrorOptions = {\n collectionId: string\n requestType: IndeterminateCommitRequestType\n previousLeaderId: string | null\n previousTerm: number | null\n currentLeaderId: string | null\n currentTerm: number | null\n cause: unknown\n}\n\nexport class IndeterminateCommitError extends PersistedCollectionCoreError {\n readonly code: `INDETERMINATE_COMMIT` = `INDETERMINATE_COMMIT`\n readonly collectionId: string\n readonly requestType: IndeterminateCommitRequestType\n readonly previousLeaderId: string | null\n readonly previousTerm: number | null\n readonly currentLeaderId: string | null\n readonly currentTerm: number | null\n override readonly cause: unknown\n\n constructor(options: IndeterminateCommitErrorOptions) {\n super(\n `Commit outcome is indeterminate for collection \"${options.collectionId}\": ${options.requestType} crossed leadership from ${formatLeader(options.previousLeaderId, options.previousTerm)} to ${formatLeader(options.currentLeaderId, options.currentTerm)}`,\n )\n this.name = `IndeterminateCommitError`\n this.collectionId = options.collectionId\n this.requestType = options.requestType\n this.previousLeaderId = options.previousLeaderId\n this.previousTerm = options.previousTerm\n this.currentLeaderId = options.currentLeaderId\n this.currentTerm = options.currentTerm\n this.cause = options.cause\n }\n}\n\nfunction formatLeader(leaderId: string | null, term: number | null): string {\n return `${leaderId ?? `unknown leader`} (term ${term ?? `unknown`})`\n}\n\nexport class DuplicateRemoteSubsetOwnerError extends PersistedCollectionCoreError {\n readonly collectionId: string\n\n constructor(collectionId: string) {\n super(\n `A remote subset owner is already registered for collection \"${collectionId}\"`,\n )\n this.name = `DuplicateRemoteSubsetOwnerError`\n this.collectionId = collectionId\n }\n}\n\nexport class RetryableRemoteSubsetAcquisitionError extends PersistedCollectionCoreError {\n override readonly cause: unknown\n\n constructor(message: string, cause?: unknown) {\n super(message)\n this.name = `RetryableRemoteSubsetAcquisitionError`\n this.cause = cause\n }\n}\n\nexport class InvalidPersistedStorageKeyError extends InvalidPersistedCollectionConfigError {\n constructor(key: string | number) {\n super(\n `Invalid persisted storage key \"${String(key)}\": numeric keys must be finite`,\n )\n this.name = `InvalidPersistedStorageKeyError`\n }\n}\n\nexport class InvalidPersistedStorageKeyEncodingError extends InvalidPersistedCollectionConfigError {\n constructor(encoded: string) {\n super(\n `Invalid persisted storage key encoding \"${encoded}\": expected prefix \"n:\" or \"s:\"`,\n )\n this.name = `InvalidPersistedStorageKeyEncodingError`\n }\n}\n\nexport class SQLiteBigIntOutOfRangeError extends PersistedCollectionCoreError {\n constructor(value: bigint, minimum: bigint, maximum: bigint) {\n super(\n `SQLite BigInt value ${value} is outside the signed 64-bit range [${minimum}, ${maximum}]`,\n )\n this.name = `SQLiteBigIntOutOfRangeError`\n }\n}\n\nexport class PersistenceUnavailableError extends PersistedCollectionCoreError {\n constructor(details?: string) {\n super(\n details\n ? `Persistence unavailable: ${details}`\n : `Persistence unavailable in this runtime`,\n )\n this.name = `PersistenceUnavailableError`\n }\n}\n"],"names":[],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { SQLITE_DRIVER_SHARED_LOGICAL_SCHEDULING_KEY, SingleProcessCoordinator, createPersistedTableName, decodePersistedStorageKey, encodePersistedStorageKey, forwardSQLiteDriverSharedLogicalScheduling, persistedCollectionOptions, validatePersistedCollectionCoordinator } from "./persisted.js";
2
2
  import { DuplicateRemoteSubsetOwnerError, IndeterminateCommitError, InvalidPersistedCollectionConfigError, InvalidPersistedCollectionCoordinatorError, InvalidPersistedStorageKeyEncodingError, InvalidPersistedStorageKeyError, InvalidPersistenceAdapterError, InvalidSyncConfigError, PersistedCollectionCoreError, PersistedCollectionDurabilityError, PersistenceUnavailableError, RetryableRemoteSubsetAcquisitionError, SQLiteBigIntOutOfRangeError, toPersistedCollectionDurabilityError } from "./errors.js";
3
3
  import { DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS, DEFAULT_APPLIED_TX_PRUNE_MAX_ROWS, SQLiteCorePersistenceAdapter, createSQLiteCorePersistenceAdapter } from "./sqlite-core-adapter.js";
4
- import { PERSISTED_TYPE_TAG, PERSISTED_VALUE_TAG, SQLITE_BIGINT_MAX, SQLITE_BIGINT_MIN, assertSQLiteBigIntInRange, serializeSQLiteBigInt } from "./sqlite-value.js";
5
- import { RemoteSubsetWireValueError, toProcessLocalLoadSubsetOptions, toTransportedLoadSubsetOptions } from "./remote-subset-wire.js";
4
+ import { PERSISTED_TYPE_TAG, PERSISTED_VALUE_TAG, SQLITE_BIGINT_MAX, SQLITE_BIGINT_MIN, assertSQLiteBigIntInRange, reviveSQLiteTemporal, serializeSQLiteBigInt, serializeSQLiteTemporal, sqliteTemporalIdentity, sqliteTemporalKind } from "./sqlite-value.js";
5
+ import { RemoteSubsetWireValueError, toProcessLocalLoadSubsetOptions, toTransportedLoadSubsetOptions, validateRemoteSubsetOptions } from "./remote-subset-wire.js";
6
6
  import { safeRandomUUID } from "@tanstack/db";
7
7
  export {
8
8
  DEFAULT_APPLIED_TX_PRUNE_MAX_AGE_SECONDS,
@@ -35,11 +35,16 @@ export {
35
35
  encodePersistedStorageKey,
36
36
  forwardSQLiteDriverSharedLogicalScheduling,
37
37
  persistedCollectionOptions,
38
+ reviveSQLiteTemporal,
38
39
  safeRandomUUID,
39
40
  serializeSQLiteBigInt,
41
+ serializeSQLiteTemporal,
42
+ sqliteTemporalIdentity,
43
+ sqliteTemporalKind,
40
44
  toPersistedCollectionDurabilityError,
41
45
  toProcessLocalLoadSubsetOptions,
42
46
  toTransportedLoadSubsetOptions,
43
- validatePersistedCollectionCoordinator
47
+ validatePersistedCollectionCoordinator,
48
+ validateRemoteSubsetOptions
44
49
  };
45
50
  //# sourceMappingURL=index.js.map
@@ -358,6 +358,12 @@ export interface PersistedCollectionCoordinator {
358
358
  */
359
359
  requestEnsurePersistedIndex: (collectionId: string, signature: string, spec: PersistedIndexSpec, scopedAdapter?: HydrationPersistenceAdapter, localEnsureCompleted?: boolean) => Promise<void>;
360
360
  requestApplyLocalMutations?: (collectionId: string, mutations: Array<PersistedMutationEnvelope>) => Promise<ApplyLocalMutationsResponse>;
361
+ /**
362
+ * Applies a committed transaction through the supplied adapter when present.
363
+ * Wrappers must forward this leader-local loan: scheduling it again can wait
364
+ * on the hydration that is awaiting this call. Never serialize or retain the
365
+ * loan after the call; ordinary work uses the registered collection adapter.
366
+ */
361
367
  requestApplyCommittedTx: (collectionId: string, tx: PersistedTx, scopedAdapter?: HydrationPersistenceAdapter) => Promise<ApplyCommittedTxResponse>;
362
368
  /** The scoped adapter is leader-local and is never serialized to a follower. */
363
369
  pullSince?: (collectionId: string, fromRowVersion: number, scopedAdapter?: HydrationPersistenceAdapter) => Promise<PullSinceResponse>;
@@ -433,7 +439,7 @@ export declare class SingleProcessCoordinator implements PersistedCollectionCoor
433
439
  registerRemoteSubsetOwner(collectionId: string, owner: RemoteSubsetOwner): () => void;
434
440
  requestEnsurePersistedIndex(): Promise<void>;
435
441
  setAdapterForCollection(collectionId: string, adapter: PersistenceAdapter): void;
436
- requestApplyCommittedTx(collectionId: string, tx: PersistedTx): Promise<ApplyCommittedTxResponse>;
442
+ requestApplyCommittedTx(collectionId: string, tx: PersistedTx, scopedAdapter?: HydrationPersistenceAdapter): Promise<ApplyCommittedTxResponse>;
437
443
  pullSince(): Promise<PullSinceResponse>;
438
444
  }
439
445
  export declare function validatePersistedCollectionCoordinator(coordinator: PersistedCollectionCoordinator): void;
@@ -1,7 +1,7 @@
1
- import { safeRandomUUID, withCollectionConfigFactory, SyncTransactionAbortedError, getLoadSubsetDemandKey, compileSingleRowExpression, toBooleanPredicate, SYNC_PERSISTENCE_VERSION, SYNC_PERSISTENCE_PROTOCOL, PERSISTED_READINESS, NoPendingSyncTransactionCommitError, NoPendingSyncTransactionWriteError } from "@tanstack/db";
2
- import { InvalidPersistedCollectionConfigError, DuplicateRemoteSubsetOwnerError, toPersistedCollectionDurabilityError, InvalidPersistedCollectionCoordinatorError, InvalidSyncConfigError, InvalidPersistedStorageKeyError, InvalidPersistedStorageKeyEncodingError, PersistedCollectionDurabilityError, InvalidPersistenceAdapterError } from "./errors.js";
3
- import { serializeSQLiteBigInt } from "./sqlite-value.js";
4
- import { toTransportedLoadSubsetOptions, toProcessLocalLoadSubsetOptions } from "./remote-subset-wire.js";
1
+ import { safeRandomUUID, withCollectionConfigFactory, whenSyncAccepted, SyncTransactionAbortedError, getLoadSubsetDemandKey, compileSingleRowExpression, toBooleanPredicate, SYNC_PERSISTENCE_VERSION, SYNC_PERSISTENCE_PROTOCOL, PERSISTED_READINESS, NoPendingSyncTransactionCommitError, NoPendingSyncTransactionWriteError, withAcceptedReceipt } from "@tanstack/db";
2
+ import { InvalidPersistedCollectionConfigError, DuplicateRemoteSubsetOwnerError, toPersistedCollectionDurabilityError, InvalidPersistedStorageKeyEncodingError, InvalidPersistedStorageKeyError, InvalidSyncConfigError, InvalidPersistedCollectionCoordinatorError, PersistedCollectionDurabilityError, InvalidPersistenceAdapterError } from "./errors.js";
3
+ import { serializeSQLiteBigInt, serializeSQLiteTemporal } from "./sqlite-value.js";
4
+ import { toTransportedLoadSubsetOptions, toProcessLocalLoadSubsetOptions, validateRemoteSubsetOptions } from "./remote-subset-wire.js";
5
5
  import { reportRemoteSubsetOwnerError, unloadRemoteSubsetOwner } from "./remote-subset-owner.js";
6
6
  const SQLITE_DRIVER_SHARED_LOGICAL_SCHEDULING_KEY = /* @__PURE__ */ Symbol.for(
7
7
  `tanstack-db.sqlite-driver-supports-shared-logical-scheduling`
@@ -176,8 +176,8 @@ class SingleProcessCoordinator {
176
176
  setAdapterForCollection(collectionId, adapter) {
177
177
  this.collectionAdapters.set(collectionId, adapter);
178
178
  }
179
- async requestApplyCommittedTx(collectionId, tx) {
180
- const adapter = this.collectionAdapters.get(collectionId);
179
+ async requestApplyCommittedTx(collectionId, tx, scopedAdapter) {
180
+ const adapter = scopedAdapter ?? this.collectionAdapters.get(collectionId);
181
181
  if (!adapter) {
182
182
  throw new InvalidPersistedCollectionConfigError(
183
183
  `SingleProcessCoordinator has no persistence adapter configured for collection "${collectionId}"`
@@ -333,6 +333,8 @@ function toStableSerializable(value) {
333
333
  case `undefined`:
334
334
  return void 0;
335
335
  }
336
+ const temporal = serializeSQLiteTemporal(value);
337
+ if (temporal) return temporal;
336
338
  if (value instanceof Date) {
337
339
  return value.toISOString();
338
340
  }
@@ -440,7 +442,6 @@ class PersistedCollectionRuntime {
440
442
  this.resumeGenerationOwner = /* @__PURE__ */ Symbol(`persisted resume generation owner`);
441
443
  this.lifecycleGeneration = 0;
442
444
  this.internalApplyDepth = 0;
443
- this.sourcePublicationWaitDepth = 0;
444
445
  this.appliedReceiptSequence = 0;
445
446
  this.syncErrorReported = false;
446
447
  this.pendingAppliedReceipts = /* @__PURE__ */ new Map();
@@ -499,11 +500,14 @@ class PersistedCollectionRuntime {
499
500
  void receipt.then(removeReceipt, removeReceipt);
500
501
  return receipt;
501
502
  }
502
- async waitForAppliedReceiptsAfter(cursor) {
503
+ // Startup waits for acceptance: a local mutation's handler can wait for
504
+ // startup, and the rows publish when that mutation settles. Readiness waits
505
+ // for them in core. A subset load waits for visibility.
506
+ async waitForAppliedReceiptsAfter(cursor, until = `visible`) {
503
507
  await Promise.all(
504
508
  Array.from(
505
509
  this.pendingAppliedReceipts,
506
- ([sequence, receipt]) => sequence > cursor ? receipt : void 0
510
+ ([sequence, receipt]) => sequence <= cursor ? void 0 : until === `accepted` ? whenSyncAccepted(receipt) : receipt
507
511
  )
508
512
  );
509
513
  }
@@ -664,7 +668,10 @@ class PersistedCollectionRuntime {
664
668
  startup.completedLocalIndexSignatures
665
669
  );
666
670
  if (startup.appliedCursor !== void 0 && lifecycleGeneration === this.lifecycleGeneration) {
667
- await this.waitForAppliedReceiptsAfter(startup.appliedCursor);
671
+ await this.waitForAppliedReceiptsAfter(
672
+ startup.appliedCursor,
673
+ `accepted`
674
+ );
668
675
  }
669
676
  }
670
677
  if (lifecycleGeneration === this.lifecycleGeneration) {
@@ -699,7 +706,7 @@ class PersistedCollectionRuntime {
699
706
  return result;
700
707
  });
701
708
  if (appliedCursor !== void 0 && lifecycleGeneration === this.lifecycleGeneration) {
702
- await this.waitForAppliedReceiptsAfter(appliedCursor);
709
+ await this.waitForAppliedReceiptsAfter(appliedCursor, `accepted`);
703
710
  }
704
711
  })().catch((error) => {
705
712
  throw this.markTerminalFailure(error, lifecycleGeneration);
@@ -740,7 +747,6 @@ class PersistedCollectionRuntime {
740
747
  await this.hydrateSubsetUnsafe(
741
748
  baseline,
742
749
  {
743
- requestRemoteEnsure: false,
744
750
  lifecycleGeneration,
745
751
  bindKeySetEvidence: true
746
752
  },
@@ -788,7 +794,7 @@ class PersistedCollectionRuntime {
788
794
  const applied = this.replaceCollectionMetadataSnapshot(
789
795
  snapshot.collectionMetadata
790
796
  );
791
- if (applied !== true) await applied;
797
+ await whenSyncAccepted(applied);
792
798
  }
793
799
  async loadCollectionMetadataSnapshot(adapter) {
794
800
  if (!adapter.loadCollectionMetadata) {
@@ -805,7 +811,7 @@ class PersistedCollectionRuntime {
805
811
  );
806
812
  const currentKeys = this.syncControls.metadata.collection.list().map(({ key }) => key);
807
813
  return this.withInternalApply(() => {
808
- this.syncControls.begin?.({ immediate: true });
814
+ this.syncControls.begin?.();
809
815
  currentKeys.forEach((key) => {
810
816
  if (!nextMetadata.has(key)) {
811
817
  this.syncControls.metadata?.collection.delete(key);
@@ -823,6 +829,7 @@ class PersistedCollectionRuntime {
823
829
  const subsetKey = this.getSubsetKey(options);
824
830
  const truncateGeneration = this.sourceTruncateGeneration;
825
831
  const routeRemoteDemandDuringHydration = this.canRouteRemoteDemandThroughCoordinator();
832
+ if (routeRemoteDemandDuringHydration) validateRemoteSubsetOptions(options);
826
833
  this.activeSubsets.set(subsetKey, options);
827
834
  const appliedCursor = this.appliedReceiptSequence;
828
835
  try {
@@ -832,7 +839,6 @@ class PersistedCollectionRuntime {
832
839
  (adapter) => this.hydrateSubsetUnsafe(
833
840
  options,
834
841
  {
835
- requestRemoteEnsure: this.mode === `sync-present` && !routeRemoteDemandDuringHydration,
836
842
  lifecycleGeneration,
837
843
  requestLocalLoadFailure: true,
838
844
  rejectBufferedReplayFailure: true
@@ -848,6 +854,9 @@ class PersistedCollectionRuntime {
848
854
  });
849
855
  if (lifecycleGeneration !== this.lifecycleGeneration) return;
850
856
  await this.waitForAppliedReceiptsAfter(appliedCursor);
857
+ if (!routeRemoteDemandDuringHydration && this.canRouteRemoteDemandThroughCoordinator()) {
858
+ validateRemoteSubsetOptions(options);
859
+ }
851
860
  } catch (error) {
852
861
  if (this.activeSubsets.get(subsetKey) === options) {
853
862
  this.activeSubsets.delete(subsetKey);
@@ -957,7 +966,6 @@ class PersistedCollectionRuntime {
957
966
  (adapter) => this.hydrateSubsetUnsafe(
958
967
  options,
959
968
  {
960
- requestRemoteEnsure: false,
961
969
  lifecycleGeneration,
962
970
  rejectBufferedReplayFailure: true
963
971
  },
@@ -980,13 +988,6 @@ class PersistedCollectionRuntime {
980
988
  applyHydrationBufferedTransaction(transaction) {
981
989
  const failure = this.getCurrentTerminalFailure();
982
990
  if (failure) return Promise.reject(failure.error);
983
- if (transaction.beginOptions?.immediate && this.sourcePublicationWaitDepth > 0) {
984
- return Promise.reject(
985
- new InvalidPersistedCollectionConfigError(
986
- `immediate persisted source replay cannot enter while an earlier source publication is waiting`
987
- )
988
- );
989
- }
990
991
  return this.applyMutex.run(async () => {
991
992
  await this.applyBufferedSyncTransactionUnsafe(transaction);
992
993
  });
@@ -1207,7 +1208,7 @@ class PersistedCollectionRuntime {
1207
1208
  hydrationContext.suppliedRowKeys.add(row.key);
1208
1209
  }
1209
1210
  const applied = this.applyRowsToCollection(rows);
1210
- if (applied !== true) await applied;
1211
+ await whenSyncAccepted(applied);
1211
1212
  }
1212
1213
  } finally {
1213
1214
  if (this.activeHydrationContext === hydrationContext && this.hydratingGeneration === config.lifecycleGeneration) {
@@ -1217,9 +1218,6 @@ class PersistedCollectionRuntime {
1217
1218
  if (config.lifecycleGeneration !== this.lifecycleGeneration) return;
1218
1219
  replayFailure = await this.flushQueuedHydrationTransactionsUnsafe(adapter);
1219
1220
  if (config.lifecycleGeneration !== this.lifecycleGeneration) return;
1220
- if (config.requestRemoteEnsure && !replayFailure) {
1221
- this.queueRemoteSubsetEnsure(options);
1222
- }
1223
1221
  } catch (error) {
1224
1222
  if (config.requestLocalLoadFailure && !rowsLoaded) {
1225
1223
  this.hydratingGeneration = config.lifecycleGeneration;
@@ -1350,7 +1348,7 @@ class PersistedCollectionRuntime {
1350
1348
  return true;
1351
1349
  }
1352
1350
  return this.withInternalApply(() => {
1353
- this.syncControls.begin?.({ immediate: true });
1351
+ this.syncControls.begin?.();
1354
1352
  for (const row of rows) {
1355
1353
  if (this.collection?._hasHydratedKey(row.key)) {
1356
1354
  continue;
@@ -1396,7 +1394,7 @@ class PersistedCollectionRuntime {
1396
1394
  );
1397
1395
  const currentKeys = this.syncControls.metadata.collection.list().map(({ key }) => key);
1398
1396
  return this.withInternalApply(() => {
1399
- this.syncControls.begin?.({ immediate: true });
1397
+ this.syncControls.begin?.();
1400
1398
  this.syncControls.truncate?.();
1401
1399
  for (const row of rows) {
1402
1400
  this.syncControls.write?.({
@@ -1450,14 +1448,9 @@ class PersistedCollectionRuntime {
1450
1448
  const applied = transaction.internal ? this.withInternalApply(transaction.applyToCollection) : transaction.applyToCollection();
1451
1449
  applicationReturned = true;
1452
1450
  abortedDuringApplication = transaction.signal?.aborted === true;
1453
- if (applied !== true) {
1454
- this.sourcePublicationWaitDepth++;
1455
- try {
1456
- await applied;
1457
- } finally {
1458
- this.sourcePublicationWaitDepth--;
1459
- }
1460
- }
1451
+ if (transaction.internal) await whenSyncAccepted(applied);
1452
+ else if (applied !== true && !this.collection?._state.hasPersistingTransaction())
1453
+ await applied;
1461
1454
  this.throwIfLifecycleReplaced(transaction.lifecycleGeneration);
1462
1455
  if (!transaction.internal) {
1463
1456
  await this.persistAndBroadcastExternalSyncTransactionUnsafe(
@@ -1627,7 +1620,7 @@ class PersistedCollectionRuntime {
1627
1620
  return;
1628
1621
  }
1629
1622
  const applied = this.withInternalApply(() => {
1630
- this.syncControls.begin?.({ immediate: true });
1623
+ this.syncControls.begin?.();
1631
1624
  for (const mutation of mutations) {
1632
1625
  if (mutation.type === `delete`) {
1633
1626
  this.syncControls.write?.({
@@ -1643,9 +1636,8 @@ class PersistedCollectionRuntime {
1643
1636
  }
1644
1637
  return this.syncControls.commit?.() ?? true;
1645
1638
  });
1646
- if (applied !== true) {
1647
- await applied;
1648
- }
1639
+ const accepted = whenSyncAccepted(applied);
1640
+ if (accepted !== true) await accepted;
1649
1641
  }
1650
1642
  filterMutationsForCollection(mutations) {
1651
1643
  const collection = this.collection;
@@ -1818,6 +1810,7 @@ class PersistedCollectionRuntime {
1818
1810
  }
1819
1811
  const subsetKey = this.getSubsetKey(options);
1820
1812
  if (this.activeSubsets.get(subsetKey) !== options) return;
1813
+ validateRemoteSubsetOptions(options);
1821
1814
  this.pendingRemoteSubsetEnsures.set(subsetKey, options);
1822
1815
  void this.flushPendingRemoteSubsetEnsures();
1823
1816
  }
@@ -2033,11 +2026,11 @@ class PersistedCollectionRuntime {
2033
2026
  this.resetSequence++;
2034
2027
  if (this.syncControls.begin && this.syncControls.commit) {
2035
2028
  const applied = this.withInternalApply(() => {
2036
- this.syncControls.begin?.({ immediate: true });
2029
+ this.syncControls.begin?.();
2037
2030
  this.syncControls.truncate?.();
2038
2031
  return this.syncControls.commit?.() ?? true;
2039
2032
  });
2040
- if (applied !== true) await applied;
2033
+ await whenSyncAccepted(applied);
2041
2034
  }
2042
2035
  if (lifecycleGeneration !== this.lifecycleGeneration) return;
2043
2036
  await this.reloadActiveSubsetsUnsafe(adapter);
@@ -2070,7 +2063,7 @@ class PersistedCollectionRuntime {
2070
2063
  (opt) => opt.where ? compileSingleRowExpression(opt.where) : null
2071
2064
  );
2072
2065
  const applied = this.withInternalApply(() => {
2073
- this.syncControls.begin?.({ immediate: true });
2066
+ this.syncControls.begin?.();
2074
2067
  for (const {
2075
2068
  key: changedKey,
2076
2069
  value: newValue
@@ -2110,7 +2103,7 @@ class PersistedCollectionRuntime {
2110
2103
  });
2111
2104
  return this.syncControls.commit?.() ?? true;
2112
2105
  });
2113
- if (applied !== true) await applied;
2106
+ await whenSyncAccepted(applied);
2114
2107
  }
2115
2108
  async reloadActiveSubsetsUnsafe(adapter) {
2116
2109
  const lifecycleGeneration = this.lifecycleGeneration;
@@ -2146,7 +2139,7 @@ class PersistedCollectionRuntime {
2146
2139
  })),
2147
2140
  collectionMetadata
2148
2141
  );
2149
- if (applied !== true) await applied;
2142
+ await whenSyncAccepted(applied);
2150
2143
  } finally {
2151
2144
  if (this.activeHydrationContext === hydrationContext && this.hydratingGeneration === lifecycleGeneration) {
2152
2145
  this.hydratingGeneration = null;
@@ -2220,8 +2213,8 @@ class PersistedCollectionRuntime {
2220
2213
  await this.requestCoordinatorPersistedIndex(indexMetadata, completedLocally);
2221
2214
  }
2222
2215
  async ensureLocalPersistedIndex(indexMetadata, adapter) {
2223
- const spec = this.buildPersistedIndexSpec(indexMetadata);
2224
2216
  try {
2217
+ const spec = this.buildPersistedIndexSpec(indexMetadata);
2225
2218
  await adapter.ensureIndex(
2226
2219
  this.collectionId,
2227
2220
  indexMetadata.signature,
@@ -2234,8 +2227,8 @@ class PersistedCollectionRuntime {
2234
2227
  }
2235
2228
  }
2236
2229
  async requestCoordinatorPersistedIndex(indexMetadata, completedLocally) {
2237
- const spec = this.buildPersistedIndexSpec(indexMetadata);
2238
2230
  try {
2231
+ const spec = this.buildPersistedIndexSpec(indexMetadata);
2239
2232
  await this.persistence.coordinator.requestEnsurePersistedIndex(
2240
2233
  this.collectionId,
2241
2234
  indexMetadata.signature,
@@ -2312,6 +2305,14 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime, persistedReadiness)
2312
2305
  const settlePendingTransaction = (transaction) => {
2313
2306
  removePendingPublicationTransaction(transaction);
2314
2307
  };
2308
+ const visibleAfterDurable = (transaction, durable) => {
2309
+ const visible = durable.then(async () => {
2310
+ const receipt = transaction.applicationReceipt;
2311
+ if (receipt !== void 0 && receipt !== true) await receipt;
2312
+ });
2313
+ void visible.catch(() => void 0);
2314
+ return withAcceptedReceipt(visible, durable);
2315
+ };
2315
2316
  const settleRuntimeTransaction = (transaction, applied) => {
2316
2317
  void applied.then(
2317
2318
  () => settlePendingTransaction(transaction),
@@ -2419,9 +2420,7 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime, persistedReadiness)
2419
2420
  }
2420
2421
  }
2421
2422
  transaction.deferredHydrationMetadataDeleteKeys.clear();
2422
- params.begin(
2423
- transaction.queuedBecauseHydrating ? { immediate: true } : transaction.beginOptions
2424
- );
2423
+ params.begin();
2425
2424
  if (transaction.truncate) params.truncate();
2426
2425
  for (const operation of transaction.operations) {
2427
2426
  if (operation.type === `delete`) {
@@ -2447,14 +2446,8 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime, persistedReadiness)
2447
2446
  }
2448
2447
  const applied = params.commit(signal);
2449
2448
  transaction.applicationReceipt = applied;
2450
- if (applied === true) {
2451
- removePendingPublicationTransaction(transaction);
2452
- } else {
2453
- void applied.then(
2454
- () => removePendingPublicationTransaction(transaction),
2455
- () => removePendingPublicationTransaction(transaction)
2456
- );
2457
- }
2449
+ removePendingPublicationTransaction(transaction);
2450
+ if (applied !== true) void applied.catch(() => void 0);
2458
2451
  return applied;
2459
2452
  } catch (error) {
2460
2453
  removePendingPublicationTransaction(transaction);
@@ -2475,6 +2468,11 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime, persistedReadiness)
2475
2468
  const persistenceCapability = {
2476
2469
  protocol: SYNC_PERSISTENCE_PROTOCOL,
2477
2470
  version: SYNC_PERSISTENCE_VERSION,
2471
+ reserveCommitTurn: () => {
2472
+ const openTransaction = getOpenTransaction();
2473
+ if (openTransaction && !openTransaction.internal)
2474
+ openTransaction.reservesCommitTurn = true;
2475
+ },
2478
2476
  hydrateBaseline: async () => {
2479
2477
  if (startupState.cleanedUp) return;
2480
2478
  try {
@@ -2524,7 +2522,7 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime, persistedReadiness)
2524
2522
  params.markReady();
2525
2523
  }).catch(() => void 0);
2526
2524
  },
2527
- begin: (options) => {
2525
+ begin: () => {
2528
2526
  if (startupState.cleanedUp) return void 0;
2529
2527
  const terminalFailure = getTerminalFailure();
2530
2528
  const internal = runtime.isApplyingInternally();
@@ -2537,7 +2535,6 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime, persistedReadiness)
2537
2535
  truncate: false,
2538
2536
  internal,
2539
2537
  lifecycleGeneration: runtime.getLifecycleGeneration(),
2540
- beginOptions: options,
2541
2538
  operationKeys: /* @__PURE__ */ new Set(),
2542
2539
  hasDependentSuccessor: false,
2543
2540
  queuedBecauseHydrating: terminalFailure === void 0 && !internal && runtime.isHydratingNow(),
@@ -2796,7 +2793,6 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime, persistedReadiness)
2796
2793
  truncate: openTransaction.truncate,
2797
2794
  internal: false,
2798
2795
  lifecycleGeneration: openTransaction.lifecycleGeneration,
2799
- beginOptions: openTransaction.beginOptions,
2800
2796
  expectedResumeGenerationOwner: openTransaction.expectedResumeGenerationOwner,
2801
2797
  signal,
2802
2798
  prependHydrationRows: (rows) => {
@@ -2838,8 +2834,9 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime, persistedReadiness)
2838
2834
  });
2839
2835
  settlePublicationAdmissionWaiters(openTransaction);
2840
2836
  settleRuntimeTransaction(openTransaction, applied2);
2841
- return applied2;
2837
+ return visibleAfterDurable(openTransaction, applied2);
2842
2838
  }
2839
+ openTransaction.hydrationSequence = void 0;
2843
2840
  let applied;
2844
2841
  try {
2845
2842
  applied = runtime.applyHydrationBufferedTransaction(transaction);
@@ -2849,7 +2846,7 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime, persistedReadiness)
2849
2846
  }
2850
2847
  settlePublicationAdmissionWaiters(openTransaction);
2851
2848
  settleRuntimeTransaction(openTransaction, applied);
2852
- return applied;
2849
+ return visibleAfterDurable(openTransaction, applied);
2853
2850
  }
2854
2851
  };
2855
2852
  let sourceResult = {};
@@ -2957,7 +2954,7 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime, persistedReadiness)
2957
2954
  }
2958
2955
  };
2959
2956
  const openTransaction = getOpenTransaction();
2960
- const needsPublicationAdmission = openTransaction && !openTransaction.internal && openTransaction.beginOptions?.immediate;
2957
+ const needsPublicationAdmission = openTransaction?.reservesCommitTurn === true;
2961
2958
  const hydrated = sourceResultSettled && !needsPublicationAdmission ? runtime.loadHydratedSubset(
2962
2959
  options,
2963
2960
  (loadOptions) => forwardUpstream(sourceResult, loadOptions)
@@ -2972,7 +2969,7 @@ function createWrappedSyncConfig(sourceSyncConfig, runtime, persistedReadiness)
2972
2969
  return;
2973
2970
  }
2974
2971
  const currentTransaction = getOpenTransaction();
2975
- if (currentTransaction && !currentTransaction.internal && currentTransaction.beginOptions?.immediate) {
2972
+ if (currentTransaction?.reservesCommitTurn) {
2976
2973
  await waitForPublicationAdmission(
2977
2974
  currentTransaction,
2978
2975
  options,