@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.
- package/README.md +14 -6
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/index.cjs +5 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/persisted.cjs +59 -62
- package/dist/cjs/persisted.cjs.map +1 -1
- package/dist/cjs/persisted.d.cts +7 -1
- package/dist/cjs/remote-subset-wire.cjs +100 -73
- package/dist/cjs/remote-subset-wire.cjs.map +1 -1
- package/dist/cjs/remote-subset-wire.d.cts +2 -0
- package/dist/cjs/sqlite-core-adapter.cjs +433 -184
- package/dist/cjs/sqlite-core-adapter.cjs.map +1 -1
- package/dist/cjs/sqlite-value.cjs +51 -0
- package/dist/cjs/sqlite-value.cjs.map +1 -1
- package/dist/cjs/sqlite-value.d.cts +12 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.js +8 -3
- package/dist/esm/persisted.d.ts +7 -1
- package/dist/esm/persisted.js +63 -66
- package/dist/esm/persisted.js.map +1 -1
- package/dist/esm/remote-subset-wire.d.ts +2 -0
- package/dist/esm/remote-subset-wire.js +101 -74
- package/dist/esm/remote-subset-wire.js.map +1 -1
- package/dist/esm/sqlite-core-adapter.js +435 -186
- package/dist/esm/sqlite-core-adapter.js.map +1 -1
- package/dist/esm/sqlite-value.d.ts +12 -0
- package/dist/esm/sqlite-value.js +53 -2
- package/dist/esm/sqlite-value.js.map +1 -1
- package/package.json +4 -3
- package/src/errors.ts +1 -2
- package/src/persisted.ts +111 -91
- package/src/remote-subset-wire.ts +150 -85
- package/src/sqlite-core-adapter.ts +686 -235
- package/src/sqlite-value.ts +98 -1
|
@@ -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 {
|
|
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 {};
|
package/dist/esm/errors.js.map
CHANGED
|
@@ -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
|
|
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
|
package/dist/esm/persisted.d.ts
CHANGED
|
@@ -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;
|
package/dist/esm/persisted.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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?.(
|
|
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
|
-
|
|
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?.(
|
|
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?.(
|
|
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 (
|
|
1454
|
-
|
|
1455
|
-
|
|
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?.(
|
|
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
|
-
|
|
1647
|
-
|
|
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?.(
|
|
2029
|
+
this.syncControls.begin?.();
|
|
2037
2030
|
this.syncControls.truncate?.();
|
|
2038
2031
|
return this.syncControls.commit?.() ?? true;
|
|
2039
2032
|
});
|
|
2040
|
-
|
|
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?.(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2451
|
-
|
|
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: (
|
|
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
|
|
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
|
|
2972
|
+
if (currentTransaction?.reservesCommitTurn) {
|
|
2976
2973
|
await waitForPublicationAdmission(
|
|
2977
2974
|
currentTransaction,
|
|
2978
2975
|
options,
|