@syncular/client 0.0.6-96 → 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,133 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
SyncOperation,
|
|
3
|
-
SyncPushRequest,
|
|
4
|
-
SyncPushResponse,
|
|
5
|
-
} from '@syncular/core';
|
|
6
|
-
import type { SyncClientPlugin, SyncClientPluginContext } from './types';
|
|
7
|
-
|
|
8
|
-
export interface IncrementingVersionPluginOptions {
|
|
9
|
-
/**
|
|
10
|
-
* Plugin name (used for debugging).
|
|
11
|
-
* Defaults to "incrementing-version".
|
|
12
|
-
*/
|
|
13
|
-
name?: string;
|
|
14
|
-
/**
|
|
15
|
-
* Maximum number of rows to track in memory.
|
|
16
|
-
* Defaults to 10_000.
|
|
17
|
-
*/
|
|
18
|
-
maxTrackedRows?: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function makeRowKey(op: Pick<SyncOperation, 'table' | 'row_id'>): string {
|
|
22
|
-
return `${op.table}\u001f${op.row_id}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function touchLru(
|
|
26
|
-
map: Map<string, number>,
|
|
27
|
-
key: string,
|
|
28
|
-
value: number,
|
|
29
|
-
max: number
|
|
30
|
-
): void {
|
|
31
|
-
// Maintain insertion order as an LRU.
|
|
32
|
-
map.delete(key);
|
|
33
|
-
map.set(key, value);
|
|
34
|
-
if (map.size <= max) return;
|
|
35
|
-
const firstKey = map.keys().next().value as string | undefined;
|
|
36
|
-
if (firstKey) map.delete(firstKey);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Automatically advances `base_version` for sequential operations on the same row.
|
|
41
|
-
*
|
|
42
|
-
* Why this exists:
|
|
43
|
-
* - UI code often reads a row once and can enqueue multiple updates quickly.
|
|
44
|
-
* - The server rejects stale `base_version` values (optimistic concurrency).
|
|
45
|
-
* - When those updates all originate from the same client and are pushed in-order,
|
|
46
|
-
* later ops are effectively based on the earlier ops.
|
|
47
|
-
*
|
|
48
|
-
* Assumptions:
|
|
49
|
-
* - The server's row version is an integer that increments by 1 per applied upsert.
|
|
50
|
-
* - Operations are pushed in commit order for a given client (outbox ordering).
|
|
51
|
-
*
|
|
52
|
-
* This plugin:
|
|
53
|
-
* - Tracks the "next expected server version" per (table, row_id) based on
|
|
54
|
-
* successfully applied pushes.
|
|
55
|
-
* - Rewrites outgoing `base_version` to that expected version when it is higher
|
|
56
|
-
* than the caller-provided value, preventing "self-conflicts" on hot rows.
|
|
57
|
-
*/
|
|
58
|
-
export function createIncrementingVersionPlugin(
|
|
59
|
-
options: IncrementingVersionPluginOptions = {}
|
|
60
|
-
): SyncClientPlugin {
|
|
61
|
-
const name = options.name ?? 'incrementing-version';
|
|
62
|
-
const maxTrackedRows = Math.max(
|
|
63
|
-
1,
|
|
64
|
-
Math.min(1_000_000, options.maxTrackedRows ?? 10_000)
|
|
65
|
-
);
|
|
66
|
-
const nextExpectedBaseVersionByRow = new Map<string, number>();
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
name,
|
|
70
|
-
|
|
71
|
-
beforePush(
|
|
72
|
-
_ctx: SyncClientPluginContext,
|
|
73
|
-
request: SyncPushRequest
|
|
74
|
-
): SyncPushRequest {
|
|
75
|
-
const operations = request.operations.map((op) => {
|
|
76
|
-
if (typeof op.base_version !== 'number') return op;
|
|
77
|
-
|
|
78
|
-
const key = makeRowKey(op);
|
|
79
|
-
const nextExpected = nextExpectedBaseVersionByRow.get(key);
|
|
80
|
-
if (typeof nextExpected !== 'number') return op;
|
|
81
|
-
|
|
82
|
-
if (nextExpected > op.base_version) {
|
|
83
|
-
return { ...op, base_version: nextExpected };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return op;
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
return { ...request, operations };
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
afterPush(
|
|
93
|
-
_ctx: SyncClientPluginContext,
|
|
94
|
-
args: { request: SyncPushRequest; response: SyncPushResponse }
|
|
95
|
-
): SyncPushResponse {
|
|
96
|
-
// Rejected commits are rolled back by the server; nothing is applied.
|
|
97
|
-
if (
|
|
98
|
-
args.response.status !== 'applied' &&
|
|
99
|
-
args.response.status !== 'cached'
|
|
100
|
-
) {
|
|
101
|
-
return args.response;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
for (const result of args.response.results ?? []) {
|
|
105
|
-
if (result.status !== 'applied') continue;
|
|
106
|
-
|
|
107
|
-
const op = args.request.operations[result.opIndex];
|
|
108
|
-
if (!op) continue;
|
|
109
|
-
|
|
110
|
-
const key = makeRowKey(op);
|
|
111
|
-
|
|
112
|
-
if (op.op === 'delete') {
|
|
113
|
-
nextExpectedBaseVersionByRow.delete(key);
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (typeof op.base_version === 'number') {
|
|
118
|
-
touchLru(
|
|
119
|
-
nextExpectedBaseVersionByRow,
|
|
120
|
-
key,
|
|
121
|
-
op.base_version + 1,
|
|
122
|
-
maxTrackedRows
|
|
123
|
-
);
|
|
124
|
-
} else {
|
|
125
|
-
// Insert case: most tables start at version 1.
|
|
126
|
-
touchLru(nextExpectedBaseVersionByRow, key, 1, maxTrackedRows);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return args.response;
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
}
|
package/src/plugins/index.ts
DELETED
package/src/plugins/types.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
SyncPullRequest,
|
|
3
|
-
SyncPullResponse,
|
|
4
|
-
SyncPushRequest,
|
|
5
|
-
SyncPushResponse,
|
|
6
|
-
} from '@syncular/core';
|
|
7
|
-
|
|
8
|
-
export interface SyncClientPluginContext {
|
|
9
|
-
actorId: string;
|
|
10
|
-
clientId: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Plugin priority levels for ordering execution.
|
|
15
|
-
* Lower numbers execute first.
|
|
16
|
-
*/
|
|
17
|
-
export const PluginPriority = {
|
|
18
|
-
/** Compression should run early (before encryption) */
|
|
19
|
-
COMPRESSION: 10,
|
|
20
|
-
/** Encryption should run after compression but before transport */
|
|
21
|
-
ENCRYPTION: 20,
|
|
22
|
-
/** Default priority for general plugins */
|
|
23
|
-
DEFAULT: 50,
|
|
24
|
-
/** Logging/telemetry should run last */
|
|
25
|
-
TELEMETRY: 100,
|
|
26
|
-
} as const;
|
|
27
|
-
|
|
28
|
-
export interface SyncClientPlugin {
|
|
29
|
-
name: string;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Plugin priority for ordering. Lower numbers execute first.
|
|
33
|
-
* @default PluginPriority.DEFAULT (50)
|
|
34
|
-
*/
|
|
35
|
-
priority?: number;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Called before sending a push request to the server.
|
|
39
|
-
* Use this for client-side encryption, payload shaping, etc.
|
|
40
|
-
*/
|
|
41
|
-
beforePush?(
|
|
42
|
-
ctx: SyncClientPluginContext,
|
|
43
|
-
request: SyncPushRequest
|
|
44
|
-
): Promise<SyncPushRequest> | SyncPushRequest;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Called after receiving a push response from the server.
|
|
48
|
-
* Receives both the request and the response to allow opIndex correlation.
|
|
49
|
-
*/
|
|
50
|
-
afterPush?(
|
|
51
|
-
ctx: SyncClientPluginContext,
|
|
52
|
-
args: { request: SyncPushRequest; response: SyncPushResponse }
|
|
53
|
-
): Promise<SyncPushResponse> | SyncPushResponse;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Called after receiving a pull response from the server (and after any
|
|
57
|
-
* snapshot chunk materialization), but before applying it to the local DB.
|
|
58
|
-
*/
|
|
59
|
-
afterPull?(
|
|
60
|
-
ctx: SyncClientPluginContext,
|
|
61
|
-
args: { request: SyncPullRequest; response: SyncPullResponse }
|
|
62
|
-
): Promise<SyncPullResponse> | SyncPullResponse;
|
|
63
|
-
}
|
package/src/proxy/connection.ts
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @syncular/client - Proxy Connection
|
|
3
|
-
*
|
|
4
|
-
* DatabaseConnection implementation that proxies queries over WebSocket.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
ProxyHandshake,
|
|
9
|
-
ProxyHandshakeAck,
|
|
10
|
-
ProxyMessage,
|
|
11
|
-
ProxyResponse,
|
|
12
|
-
} from '@syncular/core';
|
|
13
|
-
import type { CompiledQuery, DatabaseConnection, QueryResult } from 'kysely';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* WebSocket wrapper that handles reconnection and message correlation.
|
|
17
|
-
*/
|
|
18
|
-
export class ProxyConnection implements DatabaseConnection {
|
|
19
|
-
private ws: WebSocket;
|
|
20
|
-
private messageId = 0;
|
|
21
|
-
private pendingRequests = new Map<
|
|
22
|
-
string,
|
|
23
|
-
{
|
|
24
|
-
resolve: (response: ProxyResponse) => void;
|
|
25
|
-
reject: (error: Error) => void;
|
|
26
|
-
}
|
|
27
|
-
>();
|
|
28
|
-
private handshakeComplete = false;
|
|
29
|
-
private handshakePromise: Promise<void>;
|
|
30
|
-
private handshakeResolve: (() => void) | null = null;
|
|
31
|
-
private handshakeReject: ((error: Error) => void) | null = null;
|
|
32
|
-
private closed = false;
|
|
33
|
-
|
|
34
|
-
constructor(
|
|
35
|
-
ws: WebSocket,
|
|
36
|
-
private actorId: string,
|
|
37
|
-
private clientId: string
|
|
38
|
-
) {
|
|
39
|
-
this.ws = ws;
|
|
40
|
-
|
|
41
|
-
this.handshakePromise = new Promise((resolve, reject) => {
|
|
42
|
-
this.handshakeResolve = resolve;
|
|
43
|
-
this.handshakeReject = reject;
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
this.ws.onmessage = (event) => {
|
|
47
|
-
this.handleMessage(event.data as string);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
this.ws.onerror = (_event) => {
|
|
51
|
-
const error = new Error('WebSocket error');
|
|
52
|
-
this.rejectAllPending(error);
|
|
53
|
-
if (!this.handshakeComplete) {
|
|
54
|
-
this.handshakeReject?.(error);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
this.ws.onclose = () => {
|
|
59
|
-
this.closed = true;
|
|
60
|
-
const error = new Error('WebSocket closed');
|
|
61
|
-
this.rejectAllPending(error);
|
|
62
|
-
if (!this.handshakeComplete) {
|
|
63
|
-
this.handshakeReject?.(error);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// Send handshake
|
|
68
|
-
const handshake: ProxyHandshake = {
|
|
69
|
-
type: 'handshake',
|
|
70
|
-
actorId: this.actorId,
|
|
71
|
-
clientId: this.clientId,
|
|
72
|
-
};
|
|
73
|
-
this.ws.send(JSON.stringify(handshake));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private handleMessage(data: string): void {
|
|
77
|
-
try {
|
|
78
|
-
const message = JSON.parse(data);
|
|
79
|
-
|
|
80
|
-
// Handle handshake acknowledgement
|
|
81
|
-
if (message.type === 'handshake_ack') {
|
|
82
|
-
const ack = message as ProxyHandshakeAck;
|
|
83
|
-
if (ack.ok) {
|
|
84
|
-
this.handshakeComplete = true;
|
|
85
|
-
this.handshakeResolve?.();
|
|
86
|
-
} else {
|
|
87
|
-
this.handshakeReject?.(new Error(ack.error ?? 'Handshake failed'));
|
|
88
|
-
}
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Handle query responses
|
|
93
|
-
const response = message as ProxyResponse;
|
|
94
|
-
const pending = this.pendingRequests.get(response.id);
|
|
95
|
-
if (pending) {
|
|
96
|
-
this.pendingRequests.delete(response.id);
|
|
97
|
-
pending.resolve(response);
|
|
98
|
-
}
|
|
99
|
-
} catch {
|
|
100
|
-
// Ignore malformed messages
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
private rejectAllPending(error: Error): void {
|
|
105
|
-
for (const pending of this.pendingRequests.values()) {
|
|
106
|
-
pending.reject(error);
|
|
107
|
-
}
|
|
108
|
-
this.pendingRequests.clear();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
private async send(message: ProxyMessage): Promise<ProxyResponse> {
|
|
112
|
-
// Wait for handshake to complete
|
|
113
|
-
await this.handshakePromise;
|
|
114
|
-
|
|
115
|
-
if (this.closed) {
|
|
116
|
-
throw new Error('Connection is closed');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return new Promise((resolve, reject) => {
|
|
120
|
-
this.pendingRequests.set(message.id, { resolve, reject });
|
|
121
|
-
this.ws.send(JSON.stringify(message));
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
private nextMessageId(): string {
|
|
126
|
-
return `${++this.messageId}`;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
async executeQuery<R>(query: CompiledQuery): Promise<QueryResult<R>> {
|
|
130
|
-
const response = await this.send({
|
|
131
|
-
id: this.nextMessageId(),
|
|
132
|
-
type: 'query',
|
|
133
|
-
sql: query.sql,
|
|
134
|
-
parameters: query.parameters,
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
if (response.type === 'error') {
|
|
138
|
-
throw new Error(response.error ?? 'Query failed');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
rows: (response.rows ?? []) as R[],
|
|
143
|
-
numAffectedRows:
|
|
144
|
-
response.rowCount != null ? BigInt(response.rowCount) : undefined,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async beginTransaction(): Promise<void> {
|
|
149
|
-
const response = await this.send({
|
|
150
|
-
id: this.nextMessageId(),
|
|
151
|
-
type: 'begin',
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
if (response.type === 'error') {
|
|
155
|
-
throw new Error(response.error ?? 'Failed to begin transaction');
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async commitTransaction(): Promise<void> {
|
|
160
|
-
const response = await this.send({
|
|
161
|
-
id: this.nextMessageId(),
|
|
162
|
-
type: 'commit',
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
if (response.type === 'error') {
|
|
166
|
-
throw new Error(response.error ?? 'Failed to commit transaction');
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
async rollbackTransaction(): Promise<void> {
|
|
171
|
-
const response = await this.send({
|
|
172
|
-
id: this.nextMessageId(),
|
|
173
|
-
type: 'rollback',
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
if (response.type === 'error') {
|
|
177
|
-
throw new Error(response.error ?? 'Failed to rollback transaction');
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
streamQuery<R>(): AsyncIterableIterator<QueryResult<R>> {
|
|
182
|
-
throw new Error('Streaming queries are not supported over proxy');
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
close(): void {
|
|
186
|
-
if (!this.closed) {
|
|
187
|
-
this.closed = true;
|
|
188
|
-
this.ws.close();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
package/src/proxy/dialect.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @syncular/client - Proxy Dialect
|
|
3
|
-
*
|
|
4
|
-
* Kysely Dialect that proxies queries over WebSocket to a sync server.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
DatabaseIntrospector,
|
|
9
|
-
Dialect,
|
|
10
|
-
DialectAdapter,
|
|
11
|
-
Driver,
|
|
12
|
-
Kysely,
|
|
13
|
-
QueryCompiler,
|
|
14
|
-
} from 'kysely';
|
|
15
|
-
import {
|
|
16
|
-
PostgresAdapter,
|
|
17
|
-
PostgresIntrospector,
|
|
18
|
-
PostgresQueryCompiler,
|
|
19
|
-
} from 'kysely';
|
|
20
|
-
import { ProxyDriver, type ProxyDriverConfigWithFactory } from './driver';
|
|
21
|
-
|
|
22
|
-
interface ProxyDialectConfig extends ProxyDriverConfigWithFactory {}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Creates a Kysely dialect that proxies queries over WebSocket.
|
|
26
|
-
*
|
|
27
|
-
* This dialect uses PostgreSQL query compilation and adapters since
|
|
28
|
-
* the proxy server typically runs against a PostgreSQL database.
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```typescript
|
|
32
|
-
* import { Kysely } from 'kysely';
|
|
33
|
-
* import { createProxyDialect } from '@syncular/client/proxy';
|
|
34
|
-
*
|
|
35
|
-
* const db = new Kysely<MySchema>({
|
|
36
|
-
* dialect: createProxyDialect({
|
|
37
|
-
* endpoint: 'wss://api.example.com/proxy',
|
|
38
|
-
* actorId: 'admin:worker',
|
|
39
|
-
* clientId: 'cf-worker-123',
|
|
40
|
-
* headers: { authorization: 'Bearer ...' },
|
|
41
|
-
* }),
|
|
42
|
-
* });
|
|
43
|
-
*
|
|
44
|
-
* // Full Kysely API works
|
|
45
|
-
* const tasks = await db.selectFrom('tasks').selectAll().execute();
|
|
46
|
-
*
|
|
47
|
-
* // Transactions work
|
|
48
|
-
* await db.transaction().execute(async (trx) => {
|
|
49
|
-
* const user = await trx.selectFrom('users')
|
|
50
|
-
* .where('id', '=', 'u1')
|
|
51
|
-
* .executeTakeFirst();
|
|
52
|
-
* await trx.insertInto('tasks')
|
|
53
|
-
* .values({ id: 'x', user_id: user.id, title: 'New' })
|
|
54
|
-
* .execute();
|
|
55
|
-
* });
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
export function createProxyDialect(config: ProxyDialectConfig): Dialect {
|
|
59
|
-
return {
|
|
60
|
-
createDriver(): Driver {
|
|
61
|
-
return new ProxyDriver(config);
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
createQueryCompiler(): QueryCompiler {
|
|
65
|
-
return new PostgresQueryCompiler();
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
createAdapter(): DialectAdapter {
|
|
69
|
-
return new PostgresAdapter();
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
|
73
|
-
return new PostgresIntrospector(db);
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
}
|
package/src/proxy/driver.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @syncular/client - Proxy Driver
|
|
3
|
-
*
|
|
4
|
-
* Kysely Driver that creates WebSocket connections for query execution.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { DatabaseConnection, Driver } from 'kysely';
|
|
8
|
-
import { ProxyConnection } from './connection';
|
|
9
|
-
|
|
10
|
-
interface ProxyDriverConfig {
|
|
11
|
-
/** WebSocket endpoint URL (wss://...) */
|
|
12
|
-
endpoint: string;
|
|
13
|
-
/** Actor ID for oplog tracking */
|
|
14
|
-
actorId: string;
|
|
15
|
-
/** Client ID for oplog tracking */
|
|
16
|
-
clientId: string;
|
|
17
|
-
/** Optional headers for authentication */
|
|
18
|
-
headers?: Record<string, string>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Custom WebSocket factory type for environments that need it.
|
|
23
|
-
*/
|
|
24
|
-
export type WebSocketFactory = (
|
|
25
|
-
url: string,
|
|
26
|
-
protocols?: string | string[]
|
|
27
|
-
) => WebSocket;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Extended config with optional WebSocket factory.
|
|
31
|
-
*/
|
|
32
|
-
export interface ProxyDriverConfigWithFactory extends ProxyDriverConfig {
|
|
33
|
-
/** Optional WebSocket factory for custom environments */
|
|
34
|
-
createWebSocket?: WebSocketFactory;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export class ProxyDriver implements Driver {
|
|
38
|
-
private connection: ProxyConnection | null = null;
|
|
39
|
-
|
|
40
|
-
constructor(private config: ProxyDriverConfigWithFactory) {}
|
|
41
|
-
|
|
42
|
-
async init(): Promise<void> {
|
|
43
|
-
// No initialization needed
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async acquireConnection(): Promise<DatabaseConnection> {
|
|
47
|
-
// Reuse existing connection if available
|
|
48
|
-
if (this.connection) {
|
|
49
|
-
return this.connection as DatabaseConnection;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const ws = await this.createWebSocket();
|
|
53
|
-
this.connection = new ProxyConnection(
|
|
54
|
-
ws,
|
|
55
|
-
this.config.actorId,
|
|
56
|
-
this.config.clientId
|
|
57
|
-
);
|
|
58
|
-
return this.connection as DatabaseConnection;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
private async createWebSocket(): Promise<WebSocket> {
|
|
62
|
-
return new Promise((resolve, reject) => {
|
|
63
|
-
// Build URL with auth info if needed
|
|
64
|
-
const url = new URL(this.config.endpoint);
|
|
65
|
-
|
|
66
|
-
// Note: WebSocket doesn't support custom headers directly,
|
|
67
|
-
// so auth is typically done via query params or after connection
|
|
68
|
-
// The server should handle auth during the handshake
|
|
69
|
-
if (this.config.headers?.authorization) {
|
|
70
|
-
url.searchParams.set(
|
|
71
|
-
'authorization',
|
|
72
|
-
this.config.headers.authorization
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
let ws: WebSocket;
|
|
77
|
-
if (this.config.createWebSocket) {
|
|
78
|
-
ws = this.config.createWebSocket(url.toString());
|
|
79
|
-
} else {
|
|
80
|
-
ws = new WebSocket(url.toString());
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
ws.onopen = () => {
|
|
84
|
-
resolve(ws);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
ws.onerror = (_event) => {
|
|
88
|
-
reject(new Error('Failed to connect to proxy endpoint'));
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// Set a connection timeout
|
|
92
|
-
const timeout = setTimeout(() => {
|
|
93
|
-
ws.close();
|
|
94
|
-
reject(new Error('Connection timeout'));
|
|
95
|
-
}, 30000);
|
|
96
|
-
|
|
97
|
-
ws.onopen = () => {
|
|
98
|
-
clearTimeout(timeout);
|
|
99
|
-
resolve(ws);
|
|
100
|
-
};
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async beginTransaction(connection: DatabaseConnection): Promise<void> {
|
|
105
|
-
await (connection as ProxyConnection).beginTransaction();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async commitTransaction(connection: DatabaseConnection): Promise<void> {
|
|
109
|
-
await (connection as ProxyConnection).commitTransaction();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async rollbackTransaction(connection: DatabaseConnection): Promise<void> {
|
|
113
|
-
await (connection as ProxyConnection).rollbackTransaction();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async releaseConnection(): Promise<void> {
|
|
117
|
-
// Keep the connection alive for reuse
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async destroy(): Promise<void> {
|
|
121
|
-
if (this.connection) {
|
|
122
|
-
this.connection.close();
|
|
123
|
-
this.connection = null;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
package/src/proxy/index.ts
DELETED
package/src/proxy/mutations.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @syncular/client - Proxy mutations
|
|
3
|
-
*
|
|
4
|
-
* Exposes the same Proxy-based mutation interface as the offline client,
|
|
5
|
-
* but pushes commits to the server immediately (no local outbox/db writes).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
createPushMutations,
|
|
10
|
-
type MutationsApi,
|
|
11
|
-
type PushCommitConfig,
|
|
12
|
-
} from '../mutations';
|
|
13
|
-
|
|
14
|
-
export function createProxyMutations<DB extends Record<string, any>>(
|
|
15
|
-
config: PushCommitConfig
|
|
16
|
-
): MutationsApi<DB, undefined> {
|
|
17
|
-
return createPushMutations(config);
|
|
18
|
-
}
|