@syncular/client 0.0.6-95 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +713 -11
- package/dist/auth-leases.d.ts +11 -0
- package/dist/auth-leases.d.ts.map +1 -0
- package/dist/auth-leases.js +140 -0
- package/dist/auth-leases.js.map +1 -0
- package/dist/blob-limits.d.ts +15 -0
- package/dist/blob-limits.d.ts.map +1 -0
- package/dist/blob-limits.js +66 -0
- package/dist/blob-limits.js.map +1 -0
- package/dist/bridge-client.d.ts +69 -0
- package/dist/bridge-client.d.ts.map +1 -0
- package/dist/bridge-client.js +282 -0
- package/dist/bridge-client.js.map +1 -0
- package/dist/client-config.d.ts +6 -0
- package/dist/client-config.d.ts.map +1 -0
- package/dist/client-config.js +18 -0
- package/dist/client-config.js.map +1 -0
- package/dist/client.d.ts +65 -397
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +150 -960
- package/dist/client.js.map +1 -1
- package/dist/command-history.d.ts +35 -0
- package/dist/command-history.d.ts.map +1 -0
- package/dist/command-history.js +378 -0
- package/dist/command-history.js.map +1 -0
- package/dist/console-diagnostics.d.ts +33 -0
- package/dist/console-diagnostics.d.ts.map +1 -0
- package/dist/console-diagnostics.js +496 -0
- package/dist/console-diagnostics.js.map +1 -0
- package/dist/crdt-yjs/index.d.ts +4 -0
- package/dist/crdt-yjs/index.d.ts.map +1 -0
- package/dist/crdt-yjs/index.js +4 -0
- package/dist/crdt-yjs/index.js.map +1 -0
- package/dist/crdt-yjs/webview-host-facade.d.ts +126 -0
- package/dist/crdt-yjs/webview-host-facade.d.ts.map +1 -0
- package/dist/crdt-yjs/webview-host-facade.js +284 -0
- package/dist/crdt-yjs/webview-host-facade.js.map +1 -0
- package/dist/crdt-yjs/yjs-document-field-adapter.d.ts +153 -0
- package/dist/crdt-yjs/yjs-document-field-adapter.d.ts.map +1 -0
- package/dist/crdt-yjs/yjs-document-field-adapter.js +406 -0
- package/dist/crdt-yjs/yjs-document-field-adapter.js.map +1 -0
- package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts +73 -0
- package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts.map +1 -0
- package/dist/crdt-yjs/yjs-prosemirror-bridge.js +169 -0
- package/dist/crdt-yjs/yjs-prosemirror-bridge.js.map +1 -0
- package/dist/database.d.ts +83 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +916 -0
- package/dist/database.js.map +1 -0
- package/dist/diagnostics.d.ts +10 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +83 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +211 -0
- package/dist/errors.js.map +1 -0
- package/dist/generated-bridge.d.ts +365 -0
- package/dist/generated-bridge.d.ts.map +1 -0
- package/dist/generated-bridge.js +250 -0
- package/dist/generated-bridge.js.map +1 -0
- package/dist/index.d.ts +13 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -25
- package/dist/index.js.map +1 -1
- package/dist/mutations.d.ts +4 -72
- package/dist/mutations.d.ts.map +1 -1
- package/dist/mutations.js +2 -568
- package/dist/mutations.js.map +1 -1
- package/dist/network.d.ts +3 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +17 -0
- package/dist/network.js.map +1 -0
- package/dist/react/index.d.ts +169 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +628 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react-native/index.d.ts +35 -0
- package/dist/react-native/index.d.ts.map +1 -0
- package/dist/react-native/index.js +49 -0
- package/dist/react-native/index.js.map +1 -0
- package/dist/runtime-contract.d.ts +13 -0
- package/dist/runtime-contract.d.ts.map +1 -0
- package/dist/runtime-contract.js +24 -0
- package/dist/runtime-contract.js.map +1 -0
- package/dist/rust-client.d.ts +106 -0
- package/dist/rust-client.d.ts.map +1 -0
- package/dist/rust-client.js +936 -0
- package/dist/rust-client.js.map +1 -0
- package/dist/sentry.d.ts +35 -0
- package/dist/sentry.d.ts.map +1 -0
- package/dist/sentry.js +155 -0
- package/dist/sentry.js.map +1 -0
- package/dist/sql-safety.d.ts +3 -0
- package/dist/sql-safety.d.ts.map +1 -0
- package/dist/sql-safety.js +62 -0
- package/dist/sql-safety.js.map +1 -0
- package/dist/syncular-runtime-artifacts.json +61 -0
- package/dist/tauri/index.d.ts +35 -0
- package/dist/tauri/index.d.ts.map +1 -0
- package/dist/tauri/index.js +114 -0
- package/dist/tauri/index.js.map +1 -0
- package/dist/types.d.ts +1059 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/wasm/.syncular-wasm-profile +1 -0
- package/dist/wasm/syncular-runtime-artifact.json +21 -0
- package/dist/wasm/syncular.d.ts +207 -0
- package/dist/wasm/syncular.js +2341 -0
- package/dist/wasm/syncular_bg.wasm +0 -0
- package/dist/wasm/syncular_bg.wasm.d.ts +97 -0
- package/dist/wasm-bindings/runtime-contract.d.ts +22 -0
- package/dist/wasm-bindings/runtime-contract.d.ts.map +1 -0
- package/dist/wasm-bindings/runtime-contract.js +112 -0
- package/dist/wasm-bindings/runtime-contract.js.map +1 -0
- package/dist/wasm-core/.syncular-wasm-profile +1 -0
- package/dist/wasm-core/syncular-runtime-artifact.json +17 -0
- package/dist/wasm-core/syncular.d.ts +162 -0
- package/dist/wasm-core/syncular.js +1847 -0
- package/dist/wasm-core/syncular_bg.wasm +0 -0
- package/dist/wasm-core/syncular_bg.wasm.d.ts +77 -0
- package/dist/wasm-perf/.syncular-wasm-profile +1 -0
- package/dist/wasm-perf/syncular-runtime-artifact.json +21 -0
- package/dist/wasm-perf/syncular.d.ts +207 -0
- package/dist/wasm-perf/syncular.js +2341 -0
- package/dist/wasm-perf/syncular_bg.wasm +0 -0
- package/dist/wasm-perf/syncular_bg.wasm.d.ts +97 -0
- package/dist/wasm-runtime.d.ts +18 -0
- package/dist/wasm-runtime.d.ts.map +1 -0
- package/dist/wasm-runtime.js +28 -0
- package/dist/wasm-runtime.js.map +1 -0
- package/dist/worker-client.d.ts +123 -0
- package/dist/worker-client.d.ts.map +1 -0
- package/dist/worker-client.js +1686 -0
- package/dist/worker-client.js.map +1 -0
- package/dist/worker-entry.d.ts +2 -0
- package/dist/worker-entry.d.ts.map +1 -0
- package/dist/worker-entry.js +479 -0
- package/dist/worker-entry.js.map +1 -0
- package/dist/worker-protocol.d.ts +64 -0
- package/dist/worker-protocol.d.ts.map +1 -0
- package/dist/worker-protocol.js +16 -0
- package/dist/worker-protocol.js.map +1 -0
- package/dist/worker-realtime.d.ts +39 -0
- package/dist/worker-realtime.d.ts.map +1 -0
- package/dist/worker-realtime.js +677 -0
- package/dist/worker-realtime.js.map +1 -0
- package/package.json +124 -17
- package/src/auth-leases.ts +251 -0
- package/src/blob-limits.ts +98 -0
- package/src/bridge-client.ts +512 -0
- package/src/client-config.ts +29 -0
- package/src/client.ts +256 -1414
- package/src/command-history.ts +623 -0
- package/src/console-diagnostics.ts +617 -0
- package/src/crdt-yjs/index.ts +3 -0
- package/src/crdt-yjs/webview-host-facade.ts +477 -0
- package/src/crdt-yjs/yjs-document-field-adapter.ts +733 -0
- package/src/crdt-yjs/yjs-prosemirror-bridge.ts +272 -0
- package/src/database.ts +1342 -0
- package/src/diagnostics.ts +116 -0
- package/src/errors.ts +269 -0
- package/src/generated-bridge.ts +741 -0
- package/src/index.ts +21 -25
- package/src/mutations.ts +12 -821
- package/src/network.ts +32 -0
- package/src/react/index.ts +1036 -0
- package/src/react-native/index.ts +152 -0
- package/src/runtime-contract.ts +48 -0
- package/src/rust-client.ts +1488 -0
- package/src/sentry.ts +215 -0
- package/src/sql-safety.ts +61 -0
- package/src/tauri/index.ts +211 -0
- package/src/types.ts +1356 -0
- package/src/wasm-bindings/generated-wasm-bindings.d.ts +70 -0
- package/src/wasm-bindings/runtime-contract.ts +158 -0
- package/src/wasm-runtime.ts +76 -0
- package/src/worker-client.ts +2215 -0
- package/src/worker-entry.ts +584 -0
- package/src/worker-protocol.ts +109 -0
- package/src/worker-realtime.ts +843 -0
- package/dist/blobs/index.d.ts +0 -7
- package/dist/blobs/index.d.ts.map +0 -1
- package/dist/blobs/index.js +0 -7
- package/dist/blobs/index.js.map +0 -1
- package/dist/blobs/manager.d.ts +0 -345
- package/dist/blobs/manager.d.ts.map +0 -1
- package/dist/blobs/manager.js +0 -749
- package/dist/blobs/manager.js.map +0 -1
- package/dist/blobs/migrate.d.ts +0 -14
- package/dist/blobs/migrate.d.ts.map +0 -1
- package/dist/blobs/migrate.js +0 -59
- package/dist/blobs/migrate.js.map +0 -1
- package/dist/blobs/types.d.ts +0 -62
- package/dist/blobs/types.d.ts.map +0 -1
- package/dist/blobs/types.js +0 -5
- package/dist/blobs/types.js.map +0 -1
- package/dist/conflicts.d.ts +0 -31
- package/dist/conflicts.d.ts.map +0 -1
- package/dist/conflicts.js +0 -112
- package/dist/conflicts.js.map +0 -1
- package/dist/create-client.d.ts +0 -118
- package/dist/create-client.d.ts.map +0 -1
- package/dist/create-client.js +0 -178
- package/dist/create-client.js.map +0 -1
- package/dist/engine/SyncEngine.d.ts +0 -272
- package/dist/engine/SyncEngine.d.ts.map +0 -1
- package/dist/engine/SyncEngine.js +0 -1860
- package/dist/engine/SyncEngine.js.map +0 -1
- package/dist/engine/index.d.ts +0 -6
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js +0 -6
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/types.d.ts +0 -345
- package/dist/engine/types.d.ts.map +0 -1
- package/dist/engine/types.js +0 -7
- package/dist/engine/types.js.map +0 -1
- package/dist/handlers/collection.d.ts +0 -6
- package/dist/handlers/collection.d.ts.map +0 -1
- package/dist/handlers/collection.js +0 -21
- package/dist/handlers/collection.js.map +0 -1
- package/dist/handlers/create-handler.d.ts +0 -120
- package/dist/handlers/create-handler.d.ts.map +0 -1
- package/dist/handlers/create-handler.js +0 -145
- package/dist/handlers/create-handler.js.map +0 -1
- package/dist/handlers/types.d.ts +0 -83
- package/dist/handlers/types.d.ts.map +0 -1
- package/dist/handlers/types.js +0 -5
- package/dist/handlers/types.js.map +0 -1
- package/dist/migrate.d.ts +0 -19
- package/dist/migrate.d.ts.map +0 -1
- package/dist/migrate.js +0 -238
- package/dist/migrate.js.map +0 -1
- package/dist/outbox.d.ts +0 -112
- package/dist/outbox.d.ts.map +0 -1
- package/dist/outbox.js +0 -294
- package/dist/outbox.js.map +0 -1
- package/dist/plugins/incrementing-version.d.ts +0 -34
- package/dist/plugins/incrementing-version.d.ts.map +0 -1
- package/dist/plugins/incrementing-version.js +0 -83
- package/dist/plugins/incrementing-version.js.map +0 -1
- package/dist/plugins/index.d.ts +0 -3
- package/dist/plugins/index.d.ts.map +0 -1
- package/dist/plugins/index.js +0 -3
- package/dist/plugins/index.js.map +0 -1
- package/dist/plugins/types.d.ts +0 -49
- package/dist/plugins/types.d.ts.map +0 -1
- package/dist/plugins/types.js +0 -15
- package/dist/plugins/types.js.map +0 -1
- package/dist/proxy/connection.d.ts +0 -33
- package/dist/proxy/connection.d.ts.map +0 -1
- package/dist/proxy/connection.js +0 -153
- package/dist/proxy/connection.js.map +0 -1
- package/dist/proxy/dialect.d.ts +0 -46
- package/dist/proxy/dialect.d.ts.map +0 -1
- package/dist/proxy/dialect.js +0 -58
- package/dist/proxy/dialect.js.map +0 -1
- package/dist/proxy/driver.d.ts +0 -42
- package/dist/proxy/driver.d.ts.map +0 -1
- package/dist/proxy/driver.js +0 -78
- package/dist/proxy/driver.js.map +0 -1
- package/dist/proxy/index.d.ts +0 -10
- package/dist/proxy/index.d.ts.map +0 -1
- package/dist/proxy/index.js +0 -10
- package/dist/proxy/index.js.map +0 -1
- package/dist/proxy/mutations.d.ts +0 -9
- package/dist/proxy/mutations.d.ts.map +0 -1
- package/dist/proxy/mutations.js +0 -11
- package/dist/proxy/mutations.js.map +0 -1
- package/dist/pull-engine.d.ts +0 -48
- package/dist/pull-engine.d.ts.map +0 -1
- package/dist/pull-engine.js +0 -648
- package/dist/pull-engine.js.map +0 -1
- package/dist/push-engine.d.ts +0 -18
- package/dist/push-engine.d.ts.map +0 -1
- package/dist/push-engine.js +0 -167
- package/dist/push-engine.js.map +0 -1
- package/dist/query/FingerprintCollector.d.ts +0 -18
- package/dist/query/FingerprintCollector.d.ts.map +0 -1
- package/dist/query/FingerprintCollector.js +0 -28
- package/dist/query/FingerprintCollector.js.map +0 -1
- package/dist/query/QueryContext.d.ts +0 -33
- package/dist/query/QueryContext.d.ts.map +0 -1
- package/dist/query/QueryContext.js +0 -16
- package/dist/query/QueryContext.js.map +0 -1
- package/dist/query/fingerprint.d.ts +0 -61
- package/dist/query/fingerprint.d.ts.map +0 -1
- package/dist/query/fingerprint.js +0 -91
- package/dist/query/fingerprint.js.map +0 -1
- package/dist/query/index.d.ts +0 -7
- package/dist/query/index.d.ts.map +0 -1
- package/dist/query/index.js +0 -7
- package/dist/query/index.js.map +0 -1
- package/dist/query/tracked-select.d.ts +0 -18
- package/dist/query/tracked-select.d.ts.map +0 -1
- package/dist/query/tracked-select.js +0 -90
- package/dist/query/tracked-select.js.map +0 -1
- package/dist/schema.d.ts +0 -83
- package/dist/schema.d.ts.map +0 -1
- package/dist/schema.js +0 -7
- package/dist/schema.js.map +0 -1
- package/dist/subscription-state.d.ts +0 -46
- package/dist/subscription-state.d.ts.map +0 -1
- package/dist/subscription-state.js +0 -185
- package/dist/subscription-state.js.map +0 -1
- package/dist/sync-loop.d.ts +0 -34
- package/dist/sync-loop.d.ts.map +0 -1
- package/dist/sync-loop.js +0 -253
- package/dist/sync-loop.js.map +0 -1
- package/dist/sync.d.ts +0 -32
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js +0 -55
- package/dist/sync.js.map +0 -1
- package/dist/utils/id.d.ts +0 -17
- package/dist/utils/id.d.ts.map +0 -1
- package/dist/utils/id.js +0 -46
- package/dist/utils/id.js.map +0 -1
- package/src/blobs/index.ts +0 -7
- package/src/blobs/manager.ts +0 -1027
- package/src/blobs/migrate.ts +0 -67
- package/src/blobs/types.ts +0 -84
- package/src/client.test.ts +0 -417
- package/src/conflicts.ts +0 -171
- package/src/create-client.test.ts +0 -83
- package/src/create-client.ts +0 -330
- package/src/engine/SyncEngine.test.ts +0 -254
- package/src/engine/SyncEngine.ts +0 -2375
- package/src/engine/index.ts +0 -6
- package/src/engine/types.ts +0 -419
- package/src/handlers/collection.ts +0 -36
- package/src/handlers/create-handler.ts +0 -330
- package/src/handlers/types.ts +0 -108
- package/src/migrate.ts +0 -318
- package/src/outbox.ts +0 -383
- package/src/plugins/incrementing-version.ts +0 -133
- package/src/plugins/index.ts +0 -2
- package/src/plugins/types.ts +0 -63
- package/src/proxy/connection.ts +0 -191
- package/src/proxy/dialect.ts +0 -76
- package/src/proxy/driver.ts +0 -126
- package/src/proxy/index.ts +0 -10
- package/src/proxy/mutations.ts +0 -18
- package/src/pull-engine.test.ts +0 -151
- package/src/pull-engine.ts +0 -855
- package/src/push-engine.ts +0 -216
- package/src/query/FingerprintCollector.ts +0 -29
- package/src/query/QueryContext.ts +0 -54
- package/src/query/fingerprint.ts +0 -109
- package/src/query/index.ts +0 -10
- package/src/query/tracked-select.ts +0 -139
- package/src/schema.ts +0 -94
- package/src/subscription-state.ts +0 -259
- package/src/sync-loop.ts +0 -379
- package/src/sync.ts +0 -170
- package/src/utils/id.ts +0 -55
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @syncular/client - Subscription state helpers
|
|
3
|
-
*
|
|
4
|
-
* Stable accessors for sync subscription metadata.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { ScopeValues, SyncBootstrapState } from '@syncular/core';
|
|
8
|
-
import { isRecord } from '@syncular/core';
|
|
9
|
-
import type { Kysely } from 'kysely';
|
|
10
|
-
import { sql } from 'kysely';
|
|
11
|
-
import type {
|
|
12
|
-
SubscriptionStatus,
|
|
13
|
-
SyncClientDb,
|
|
14
|
-
SyncSubscriptionStateTable,
|
|
15
|
-
} from './schema';
|
|
16
|
-
|
|
17
|
-
export const DEFAULT_SYNC_STATE_ID = 'default';
|
|
18
|
-
|
|
19
|
-
export interface SubscriptionState {
|
|
20
|
-
stateId: string;
|
|
21
|
-
subscriptionId: string;
|
|
22
|
-
table: string;
|
|
23
|
-
scopes: ScopeValues;
|
|
24
|
-
params: Record<string, unknown>;
|
|
25
|
-
cursor: number;
|
|
26
|
-
bootstrapState: SyncBootstrapState | null;
|
|
27
|
-
status: SubscriptionStatus;
|
|
28
|
-
createdAt: number;
|
|
29
|
-
updatedAt: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface ListSubscriptionStatesOptions {
|
|
33
|
-
stateId?: string;
|
|
34
|
-
table?: string;
|
|
35
|
-
status?: SubscriptionStatus;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface GetSubscriptionStateOptions {
|
|
39
|
-
stateId?: string;
|
|
40
|
-
subscriptionId: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface UpsertSubscriptionStateInput {
|
|
44
|
-
stateId?: string;
|
|
45
|
-
subscriptionId: string;
|
|
46
|
-
table: string;
|
|
47
|
-
scopes: ScopeValues;
|
|
48
|
-
params?: Record<string, unknown>;
|
|
49
|
-
cursor: number;
|
|
50
|
-
bootstrapState?: SyncBootstrapState | null;
|
|
51
|
-
status?: SubscriptionStatus;
|
|
52
|
-
nowMs?: number;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function isScopeValues(value: unknown): value is ScopeValues {
|
|
56
|
-
if (!isRecord(value)) return false;
|
|
57
|
-
|
|
58
|
-
for (const entry of Object.values(value)) {
|
|
59
|
-
if (typeof entry === 'string') continue;
|
|
60
|
-
if (Array.isArray(entry) && entry.every((v) => typeof v === 'string')) {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function parseBootstrapState(
|
|
70
|
-
value: string | object | null | undefined
|
|
71
|
-
): SyncBootstrapState | null {
|
|
72
|
-
if (!value) return null;
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const parsed: unknown =
|
|
76
|
-
typeof value === 'string' ? JSON.parse(value) : value;
|
|
77
|
-
|
|
78
|
-
if (!isRecord(parsed)) return null;
|
|
79
|
-
if (typeof parsed.asOfCommitSeq !== 'number') return null;
|
|
80
|
-
if (!Array.isArray(parsed.tables)) return null;
|
|
81
|
-
if (!parsed.tables.every((table) => typeof table === 'string')) return null;
|
|
82
|
-
if (typeof parsed.tableIndex !== 'number') return null;
|
|
83
|
-
if (parsed.rowCursor !== null && typeof parsed.rowCursor !== 'string') {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
asOfCommitSeq: parsed.asOfCommitSeq,
|
|
89
|
-
tables: parsed.tables,
|
|
90
|
-
tableIndex: parsed.tableIndex,
|
|
91
|
-
rowCursor: parsed.rowCursor,
|
|
92
|
-
};
|
|
93
|
-
} catch {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function parseScopes(value: string): ScopeValues {
|
|
99
|
-
try {
|
|
100
|
-
const parsed: unknown = JSON.parse(value);
|
|
101
|
-
return isScopeValues(parsed) ? parsed : {};
|
|
102
|
-
} catch {
|
|
103
|
-
return {};
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function parseParams(value: string): Record<string, unknown> {
|
|
108
|
-
try {
|
|
109
|
-
const parsed: unknown = JSON.parse(value);
|
|
110
|
-
return isRecord(parsed) ? parsed : {};
|
|
111
|
-
} catch {
|
|
112
|
-
return {};
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function mapSubscriptionState(
|
|
117
|
-
row: SyncSubscriptionStateTable
|
|
118
|
-
): SubscriptionState {
|
|
119
|
-
return {
|
|
120
|
-
stateId: row.state_id,
|
|
121
|
-
subscriptionId: row.subscription_id,
|
|
122
|
-
table: row.table,
|
|
123
|
-
scopes: parseScopes(row.scopes_json),
|
|
124
|
-
params: parseParams(row.params_json),
|
|
125
|
-
cursor: row.cursor,
|
|
126
|
-
bootstrapState: parseBootstrapState(row.bootstrap_state_json),
|
|
127
|
-
status: row.status,
|
|
128
|
-
createdAt: row.created_at,
|
|
129
|
-
updatedAt: row.updated_at,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export async function listSubscriptionStates<DB extends SyncClientDb>(
|
|
134
|
-
db: Kysely<DB>,
|
|
135
|
-
options: ListSubscriptionStatesOptions = {}
|
|
136
|
-
): Promise<SubscriptionState[]> {
|
|
137
|
-
const filters: Array<ReturnType<typeof sql>> = [];
|
|
138
|
-
if (options.stateId) {
|
|
139
|
-
filters.push(sql`${sql.ref('state_id')} = ${sql.val(options.stateId)}`);
|
|
140
|
-
}
|
|
141
|
-
if (options.table) {
|
|
142
|
-
filters.push(sql`${sql.ref('table')} = ${sql.val(options.table)}`);
|
|
143
|
-
}
|
|
144
|
-
if (options.status) {
|
|
145
|
-
filters.push(sql`${sql.ref('status')} = ${sql.val(options.status)}`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const whereClause =
|
|
149
|
-
filters.length > 0 ? sql`where ${sql.join(filters, sql` and `)}` : sql``;
|
|
150
|
-
|
|
151
|
-
const rows = await sql<SyncSubscriptionStateTable>`
|
|
152
|
-
select
|
|
153
|
-
${sql.ref('state_id')},
|
|
154
|
-
${sql.ref('subscription_id')},
|
|
155
|
-
${sql.ref('table')},
|
|
156
|
-
${sql.ref('scopes_json')},
|
|
157
|
-
${sql.ref('params_json')},
|
|
158
|
-
${sql.ref('cursor')},
|
|
159
|
-
${sql.ref('bootstrap_state_json')},
|
|
160
|
-
${sql.ref('status')},
|
|
161
|
-
${sql.ref('created_at')},
|
|
162
|
-
${sql.ref('updated_at')}
|
|
163
|
-
from ${sql.table('sync_subscription_state')}
|
|
164
|
-
${whereClause}
|
|
165
|
-
order by ${sql.ref('state_id')} asc, ${sql.ref('subscription_id')} asc
|
|
166
|
-
`.execute(db);
|
|
167
|
-
|
|
168
|
-
return rows.rows.map((row) => mapSubscriptionState(row));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export async function getSubscriptionState<DB extends SyncClientDb>(
|
|
172
|
-
db: Kysely<DB>,
|
|
173
|
-
options: GetSubscriptionStateOptions
|
|
174
|
-
): Promise<SubscriptionState | null> {
|
|
175
|
-
const stateId = options.stateId ?? DEFAULT_SYNC_STATE_ID;
|
|
176
|
-
|
|
177
|
-
const rows = await sql<SyncSubscriptionStateTable>`
|
|
178
|
-
select
|
|
179
|
-
${sql.ref('state_id')},
|
|
180
|
-
${sql.ref('subscription_id')},
|
|
181
|
-
${sql.ref('table')},
|
|
182
|
-
${sql.ref('scopes_json')},
|
|
183
|
-
${sql.ref('params_json')},
|
|
184
|
-
${sql.ref('cursor')},
|
|
185
|
-
${sql.ref('bootstrap_state_json')},
|
|
186
|
-
${sql.ref('status')},
|
|
187
|
-
${sql.ref('created_at')},
|
|
188
|
-
${sql.ref('updated_at')}
|
|
189
|
-
from ${sql.table('sync_subscription_state')}
|
|
190
|
-
where
|
|
191
|
-
${sql.ref('state_id')} = ${sql.val(stateId)}
|
|
192
|
-
and ${sql.ref('subscription_id')} = ${sql.val(options.subscriptionId)}
|
|
193
|
-
limit 1
|
|
194
|
-
`.execute(db);
|
|
195
|
-
|
|
196
|
-
const row = rows.rows[0];
|
|
197
|
-
return row ? mapSubscriptionState(row) : null;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export async function upsertSubscriptionState<DB extends SyncClientDb>(
|
|
201
|
-
db: Kysely<DB>,
|
|
202
|
-
input: UpsertSubscriptionStateInput
|
|
203
|
-
): Promise<SubscriptionState> {
|
|
204
|
-
const now = input.nowMs ?? Date.now();
|
|
205
|
-
const stateId = input.stateId ?? DEFAULT_SYNC_STATE_ID;
|
|
206
|
-
|
|
207
|
-
const bootstrapStateJson =
|
|
208
|
-
input.bootstrapState === null || input.bootstrapState === undefined
|
|
209
|
-
? null
|
|
210
|
-
: JSON.stringify(input.bootstrapState);
|
|
211
|
-
|
|
212
|
-
await sql`
|
|
213
|
-
insert into ${sql.table('sync_subscription_state')} (
|
|
214
|
-
${sql.ref('state_id')},
|
|
215
|
-
${sql.ref('subscription_id')},
|
|
216
|
-
${sql.ref('table')},
|
|
217
|
-
${sql.ref('scopes_json')},
|
|
218
|
-
${sql.ref('params_json')},
|
|
219
|
-
${sql.ref('cursor')},
|
|
220
|
-
${sql.ref('bootstrap_state_json')},
|
|
221
|
-
${sql.ref('status')},
|
|
222
|
-
${sql.ref('created_at')},
|
|
223
|
-
${sql.ref('updated_at')}
|
|
224
|
-
) values (
|
|
225
|
-
${sql.val(stateId)},
|
|
226
|
-
${sql.val(input.subscriptionId)},
|
|
227
|
-
${sql.val(input.table)},
|
|
228
|
-
${sql.val(JSON.stringify(input.scopes ?? {}))},
|
|
229
|
-
${sql.val(JSON.stringify(input.params ?? {}))},
|
|
230
|
-
${sql.val(input.cursor)},
|
|
231
|
-
${sql.val(bootstrapStateJson)},
|
|
232
|
-
${sql.val(input.status ?? 'active')},
|
|
233
|
-
${sql.val(now)},
|
|
234
|
-
${sql.val(now)}
|
|
235
|
-
)
|
|
236
|
-
on conflict (${sql.join([sql.ref('state_id'), sql.ref('subscription_id')])})
|
|
237
|
-
do update set
|
|
238
|
-
${sql.ref('table')} = ${sql.val(input.table)},
|
|
239
|
-
${sql.ref('scopes_json')} = ${sql.val(JSON.stringify(input.scopes ?? {}))},
|
|
240
|
-
${sql.ref('params_json')} = ${sql.val(JSON.stringify(input.params ?? {}))},
|
|
241
|
-
${sql.ref('cursor')} = ${sql.val(input.cursor)},
|
|
242
|
-
${sql.ref('bootstrap_state_json')} = ${sql.val(bootstrapStateJson)},
|
|
243
|
-
${sql.ref('status')} = ${sql.val(input.status ?? 'active')},
|
|
244
|
-
${sql.ref('updated_at')} = ${sql.val(now)}
|
|
245
|
-
`.execute(db);
|
|
246
|
-
|
|
247
|
-
const next = await getSubscriptionState(db, {
|
|
248
|
-
stateId,
|
|
249
|
-
subscriptionId: input.subscriptionId,
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
if (!next) {
|
|
253
|
-
throw new Error(
|
|
254
|
-
`[subscription-state] Failed to load upserted state for "${input.subscriptionId}"`
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return next;
|
|
259
|
-
}
|
package/src/sync-loop.ts
DELETED
|
@@ -1,379 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @syncular/client - High-level sync loops
|
|
3
|
-
*
|
|
4
|
-
* Helpers that run push/pull repeatedly until the client is caught up.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
SyncPullResponse,
|
|
9
|
-
SyncPullSubscriptionResponse,
|
|
10
|
-
SyncPushRequest,
|
|
11
|
-
SyncPushResponse,
|
|
12
|
-
SyncSubscriptionRequest,
|
|
13
|
-
SyncTransport,
|
|
14
|
-
} from '@syncular/core';
|
|
15
|
-
import type { Kysely } from 'kysely';
|
|
16
|
-
import { upsertConflictsForRejectedCommit } from './conflicts';
|
|
17
|
-
import type { ClientHandlerCollection } from './handlers/collection';
|
|
18
|
-
import {
|
|
19
|
-
getNextSendableOutboxCommit,
|
|
20
|
-
markOutboxCommitAcked,
|
|
21
|
-
markOutboxCommitFailed,
|
|
22
|
-
markOutboxCommitPending,
|
|
23
|
-
} from './outbox';
|
|
24
|
-
import type { SyncClientPluginContext } from './plugins/types';
|
|
25
|
-
import {
|
|
26
|
-
applyPullResponse,
|
|
27
|
-
buildPullRequest,
|
|
28
|
-
createFollowupPullState,
|
|
29
|
-
type SyncPullOnceOptions,
|
|
30
|
-
type SyncPullRequestState,
|
|
31
|
-
syncPullOnce,
|
|
32
|
-
} from './pull-engine';
|
|
33
|
-
import { type SyncPushOnceOptions, syncPushOnce } from './push-engine';
|
|
34
|
-
import type { SyncClientDb } from './schema';
|
|
35
|
-
|
|
36
|
-
interface SyncPushUntilSettledOptions extends SyncPushOnceOptions {
|
|
37
|
-
/** Max outbox commits to attempt per call. Default: 20 */
|
|
38
|
-
maxCommits?: number;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
interface SyncPushUntilSettledResult {
|
|
42
|
-
pushedCount: number;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function syncPushUntilSettled<DB extends SyncClientDb>(
|
|
46
|
-
db: Kysely<DB>,
|
|
47
|
-
transport: SyncTransport,
|
|
48
|
-
options: SyncPushUntilSettledOptions
|
|
49
|
-
): Promise<SyncPushUntilSettledResult> {
|
|
50
|
-
const maxCommits = Math.max(1, Math.min(1000, options.maxCommits ?? 20));
|
|
51
|
-
|
|
52
|
-
let pushedCount = 0;
|
|
53
|
-
for (let i = 0; i < maxCommits; i++) {
|
|
54
|
-
const res = await syncPushOnce(db, transport, {
|
|
55
|
-
clientId: options.clientId,
|
|
56
|
-
actorId: options.actorId,
|
|
57
|
-
plugins: options.plugins,
|
|
58
|
-
});
|
|
59
|
-
if (!res.pushed) break;
|
|
60
|
-
pushedCount += 1;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return { pushedCount };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
interface SyncPullUntilSettledOptions extends SyncPullOnceOptions {
|
|
67
|
-
/** Max pull rounds per call. Default: 20 */
|
|
68
|
-
maxRounds?: number;
|
|
69
|
-
/** Optional prebuilt state from a prior pull round in the same sync cycle. */
|
|
70
|
-
initialPullState?: SyncPullRequestState;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
interface SyncPullUntilSettledResult {
|
|
74
|
-
response: SyncPullResponse;
|
|
75
|
-
rounds: number;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
interface TransportWithWsPush extends SyncTransport {
|
|
79
|
-
pushViaWs(request: SyncPushRequest): Promise<SyncPushResponse | null>;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function hasPushViaWs(
|
|
83
|
-
transport: SyncTransport
|
|
84
|
-
): transport is TransportWithWsPush {
|
|
85
|
-
return 'pushViaWs' in transport && typeof transport.pushViaWs === 'function';
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function needsAnotherPull(res: SyncPullResponse): boolean {
|
|
89
|
-
for (const sub of res.subscriptions ?? []) {
|
|
90
|
-
if (sub.status !== 'active') continue;
|
|
91
|
-
if (sub.bootstrap) return true;
|
|
92
|
-
if ((sub.commits?.length ?? 0) > 0) return true;
|
|
93
|
-
}
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function mergePullResponse(
|
|
98
|
-
targetBySubId: Map<string, SyncPullSubscriptionResponse>,
|
|
99
|
-
res: SyncPullResponse
|
|
100
|
-
): void {
|
|
101
|
-
for (const sub of res.subscriptions ?? []) {
|
|
102
|
-
const prev = targetBySubId.get(sub.id);
|
|
103
|
-
if (!prev) {
|
|
104
|
-
const merged: SyncPullSubscriptionResponse = {
|
|
105
|
-
...sub,
|
|
106
|
-
commits: [...(sub.commits ?? [])],
|
|
107
|
-
snapshots: [...(sub.snapshots ?? [])],
|
|
108
|
-
};
|
|
109
|
-
targetBySubId.set(sub.id, merged);
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const merged: SyncPullSubscriptionResponse = {
|
|
114
|
-
...prev,
|
|
115
|
-
...sub,
|
|
116
|
-
commits: [...(prev.commits ?? []), ...(sub.commits ?? [])],
|
|
117
|
-
snapshots: [...(prev.snapshots ?? []), ...(sub.snapshots ?? [])],
|
|
118
|
-
};
|
|
119
|
-
targetBySubId.set(sub.id, merged);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async function syncPullUntilSettled<DB extends SyncClientDb>(
|
|
124
|
-
db: Kysely<DB>,
|
|
125
|
-
transport: SyncTransport,
|
|
126
|
-
handlers: ClientHandlerCollection<DB>,
|
|
127
|
-
options: SyncPullUntilSettledOptions
|
|
128
|
-
): Promise<SyncPullUntilSettledResult> {
|
|
129
|
-
const maxRounds = Math.max(1, Math.min(1000, options.maxRounds ?? 20));
|
|
130
|
-
|
|
131
|
-
const aggregatedBySubId = new Map<string, SyncPullSubscriptionResponse>();
|
|
132
|
-
let pullState =
|
|
133
|
-
options.initialPullState ?? (await buildPullRequest(db, options));
|
|
134
|
-
let rounds = 0;
|
|
135
|
-
|
|
136
|
-
for (let i = 0; i < maxRounds; i++) {
|
|
137
|
-
rounds += 1;
|
|
138
|
-
const res = await syncPullOnce(db, transport, handlers, options, pullState);
|
|
139
|
-
mergePullResponse(aggregatedBySubId, res);
|
|
140
|
-
|
|
141
|
-
if (!needsAnotherPull(res)) break;
|
|
142
|
-
pullState = createFollowupPullState(pullState, res);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return {
|
|
146
|
-
// Return an aggregate response so callers can see what was applied across
|
|
147
|
-
// all pull rounds (the last round is often empty by design).
|
|
148
|
-
response: {
|
|
149
|
-
ok: true,
|
|
150
|
-
subscriptions: Array.from(aggregatedBySubId.values()),
|
|
151
|
-
},
|
|
152
|
-
rounds,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export interface SyncOnceOptions {
|
|
157
|
-
clientId: string;
|
|
158
|
-
actorId?: string;
|
|
159
|
-
plugins?: SyncPushOnceOptions['plugins'];
|
|
160
|
-
subscriptions: Array<Omit<SyncSubscriptionRequest, 'cursor'>>;
|
|
161
|
-
limitCommits?: number;
|
|
162
|
-
limitSnapshotRows?: number;
|
|
163
|
-
maxSnapshotPages?: number;
|
|
164
|
-
dedupeRows?: boolean;
|
|
165
|
-
stateId?: string;
|
|
166
|
-
maxPushCommits?: number;
|
|
167
|
-
maxPullRounds?: number;
|
|
168
|
-
/** When 'ws', peek outbox first and skip push if empty. */
|
|
169
|
-
trigger?: 'ws' | 'local' | 'poll';
|
|
170
|
-
/** Custom SHA-256 hash function (for platforms without crypto.subtle) */
|
|
171
|
-
sha256?: (bytes: Uint8Array) => Promise<string>;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export interface SyncOnceResult {
|
|
175
|
-
pushedCommits: number;
|
|
176
|
-
pullRounds: number;
|
|
177
|
-
pullResponse: SyncPullResponse;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Sync once using a WS-first push strategy for the first outbox commit.
|
|
182
|
-
*
|
|
183
|
-
* - If transport supports `pushViaWs` and the commit succeeds over WS, this call
|
|
184
|
-
* sends only an HTTP pull request.
|
|
185
|
-
* - Otherwise it falls back to combined HTTP push+pull.
|
|
186
|
-
*
|
|
187
|
-
* Remaining outbox commits are then settled via `syncPushUntilSettled`.
|
|
188
|
-
*/
|
|
189
|
-
async function syncOnceCombined<DB extends SyncClientDb>(
|
|
190
|
-
db: Kysely<DB>,
|
|
191
|
-
transport: SyncTransport,
|
|
192
|
-
handlers: ClientHandlerCollection<DB>,
|
|
193
|
-
options: SyncOnceOptions
|
|
194
|
-
): Promise<SyncOnceResult> {
|
|
195
|
-
const pullOpts: SyncPullOnceOptions = {
|
|
196
|
-
clientId: options.clientId,
|
|
197
|
-
actorId: options.actorId,
|
|
198
|
-
plugins: options.plugins,
|
|
199
|
-
subscriptions: options.subscriptions,
|
|
200
|
-
limitCommits: options.limitCommits,
|
|
201
|
-
limitSnapshotRows: options.limitSnapshotRows,
|
|
202
|
-
maxSnapshotPages: options.maxSnapshotPages,
|
|
203
|
-
dedupeRows: options.dedupeRows,
|
|
204
|
-
stateId: options.stateId,
|
|
205
|
-
sha256: options.sha256,
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
// Build pull request (reads subscription state)
|
|
209
|
-
const pullState = await buildPullRequest(db, pullOpts);
|
|
210
|
-
const { clientId } = pullState.request;
|
|
211
|
-
|
|
212
|
-
// Grab at most one outbox commit
|
|
213
|
-
const outbox = await getNextSendableOutboxCommit(db);
|
|
214
|
-
|
|
215
|
-
const plugins = options.plugins ?? [];
|
|
216
|
-
const ctx: SyncClientPluginContext = {
|
|
217
|
-
actorId: options.actorId ?? 'unknown',
|
|
218
|
-
clientId,
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
// Build push request, running beforePush plugins
|
|
222
|
-
let pushRequest: SyncPushRequest | undefined;
|
|
223
|
-
if (outbox) {
|
|
224
|
-
pushRequest = {
|
|
225
|
-
clientId,
|
|
226
|
-
clientCommitId: outbox.client_commit_id,
|
|
227
|
-
operations: outbox.operations,
|
|
228
|
-
schemaVersion: outbox.schema_version,
|
|
229
|
-
};
|
|
230
|
-
for (const plugin of plugins) {
|
|
231
|
-
if (!plugin.beforePush) continue;
|
|
232
|
-
pushRequest = await plugin.beforePush(ctx, pushRequest);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Try WS push first for the first outbox commit (if realtime transport supports it).
|
|
237
|
-
// Fall back to HTTP push in the combined request when WS is unavailable or fails.
|
|
238
|
-
let wsPushResponse: SyncPushResponse | null = null;
|
|
239
|
-
if (pushRequest && hasPushViaWs(transport)) {
|
|
240
|
-
try {
|
|
241
|
-
wsPushResponse = await transport.pushViaWs(pushRequest);
|
|
242
|
-
} catch {
|
|
243
|
-
wsPushResponse = null;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const combined = await transport.sync({
|
|
248
|
-
clientId,
|
|
249
|
-
...(pushRequest && !wsPushResponse
|
|
250
|
-
? {
|
|
251
|
-
push: {
|
|
252
|
-
clientCommitId: pushRequest.clientCommitId,
|
|
253
|
-
operations: pushRequest.operations,
|
|
254
|
-
schemaVersion: pushRequest.schemaVersion,
|
|
255
|
-
},
|
|
256
|
-
}
|
|
257
|
-
: {}),
|
|
258
|
-
pull: {
|
|
259
|
-
limitCommits: pullState.request.limitCommits,
|
|
260
|
-
limitSnapshotRows: pullState.request.limitSnapshotRows,
|
|
261
|
-
maxSnapshotPages: pullState.request.maxSnapshotPages,
|
|
262
|
-
dedupeRows: pullState.request.dedupeRows,
|
|
263
|
-
subscriptions: pullState.request.subscriptions,
|
|
264
|
-
},
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// Process push response
|
|
268
|
-
let pushedCommits = 0;
|
|
269
|
-
if (outbox && pushRequest) {
|
|
270
|
-
let pushRes = wsPushResponse ?? combined.push;
|
|
271
|
-
if (!pushRes) {
|
|
272
|
-
await markOutboxCommitPending(db, {
|
|
273
|
-
id: outbox.id,
|
|
274
|
-
error: 'MISSING_PUSH_RESPONSE',
|
|
275
|
-
});
|
|
276
|
-
throw new Error('Server returned no push response');
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Run afterPush plugins
|
|
280
|
-
for (const plugin of plugins) {
|
|
281
|
-
if (!plugin.afterPush) continue;
|
|
282
|
-
pushRes = await plugin.afterPush(ctx, {
|
|
283
|
-
request: pushRequest,
|
|
284
|
-
response: pushRes,
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const responseJson = JSON.stringify(pushRes);
|
|
289
|
-
|
|
290
|
-
if (pushRes.status === 'applied' || pushRes.status === 'cached') {
|
|
291
|
-
await markOutboxCommitAcked(db, {
|
|
292
|
-
id: outbox.id,
|
|
293
|
-
commitSeq: pushRes.commitSeq ?? null,
|
|
294
|
-
responseJson,
|
|
295
|
-
});
|
|
296
|
-
pushedCommits = 1;
|
|
297
|
-
} else {
|
|
298
|
-
// Check if all errors are retriable
|
|
299
|
-
const errorResults = pushRes.results.filter((r) => r.status === 'error');
|
|
300
|
-
const allRetriable =
|
|
301
|
-
errorResults.length > 0 &&
|
|
302
|
-
errorResults.every((r) => r.retriable === true);
|
|
303
|
-
|
|
304
|
-
if (allRetriable) {
|
|
305
|
-
await markOutboxCommitPending(db, {
|
|
306
|
-
id: outbox.id,
|
|
307
|
-
error: 'Retriable',
|
|
308
|
-
responseJson,
|
|
309
|
-
});
|
|
310
|
-
pushedCommits = 1;
|
|
311
|
-
} else {
|
|
312
|
-
await upsertConflictsForRejectedCommit(db, {
|
|
313
|
-
outboxCommitId: outbox.id,
|
|
314
|
-
clientCommitId: outbox.client_commit_id,
|
|
315
|
-
response: pushRes,
|
|
316
|
-
});
|
|
317
|
-
await markOutboxCommitFailed(db, {
|
|
318
|
-
id: outbox.id,
|
|
319
|
-
error: 'REJECTED',
|
|
320
|
-
responseJson,
|
|
321
|
-
});
|
|
322
|
-
pushedCommits = 1;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Settle remaining outbox commits
|
|
327
|
-
const remaining = await syncPushUntilSettled(db, transport, {
|
|
328
|
-
clientId: options.clientId,
|
|
329
|
-
actorId: options.actorId,
|
|
330
|
-
plugins: options.plugins,
|
|
331
|
-
maxCommits: (options.maxPushCommits ?? 20) - 1,
|
|
332
|
-
});
|
|
333
|
-
pushedCommits += remaining.pushedCount;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Process pull response
|
|
337
|
-
let pullResponse: SyncPullResponse = { ok: true, subscriptions: [] };
|
|
338
|
-
let pullRounds = 0;
|
|
339
|
-
if (combined.pull) {
|
|
340
|
-
pullResponse = await applyPullResponse(
|
|
341
|
-
db,
|
|
342
|
-
transport,
|
|
343
|
-
handlers,
|
|
344
|
-
pullOpts,
|
|
345
|
-
pullState,
|
|
346
|
-
combined.pull
|
|
347
|
-
);
|
|
348
|
-
pullRounds = 1;
|
|
349
|
-
|
|
350
|
-
// Continue pulling if more data
|
|
351
|
-
if (needsAnotherPull(pullResponse)) {
|
|
352
|
-
const aggregatedBySubId = new Map<string, SyncPullSubscriptionResponse>();
|
|
353
|
-
mergePullResponse(aggregatedBySubId, pullResponse);
|
|
354
|
-
|
|
355
|
-
const more = await syncPullUntilSettled(db, transport, handlers, {
|
|
356
|
-
...pullOpts,
|
|
357
|
-
maxRounds: (options.maxPullRounds ?? 20) - 1,
|
|
358
|
-
initialPullState: createFollowupPullState(pullState, pullResponse),
|
|
359
|
-
});
|
|
360
|
-
pullRounds += more.rounds;
|
|
361
|
-
mergePullResponse(aggregatedBySubId, more.response);
|
|
362
|
-
pullResponse = {
|
|
363
|
-
ok: true,
|
|
364
|
-
subscriptions: Array.from(aggregatedBySubId.values()),
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
return { pushedCommits, pullRounds, pullResponse };
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
export async function syncOnce<DB extends SyncClientDb>(
|
|
373
|
-
db: Kysely<DB>,
|
|
374
|
-
transport: SyncTransport,
|
|
375
|
-
handlers: ClientHandlerCollection<DB>,
|
|
376
|
-
options: SyncOnceOptions
|
|
377
|
-
): Promise<SyncOnceResult> {
|
|
378
|
-
return syncOnceCombined(db, transport, handlers, options);
|
|
379
|
-
}
|