@syncular/client 0.1.3 → 0.2.1
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 +227 -709
- package/dist/apply.d.ts +80 -0
- package/dist/apply.js +301 -0
- package/dist/blob.d.ts +127 -0
- package/dist/blob.js +201 -0
- package/dist/bun-database.d.ts +22 -0
- package/dist/bun-database.js +61 -0
- package/dist/client.d.ts +320 -69
- package/dist/client.js +1627 -163
- package/dist/content-type.d.ts +2 -0
- package/dist/content-type.js +2 -0
- package/dist/database.d.ts +35 -80
- package/dist/database.js +32 -947
- package/dist/encryption.d.ts +47 -0
- package/dist/encryption.js +75 -0
- package/dist/errors.d.ts +8 -22
- package/dist/errors.js +10 -207
- package/dist/http.d.ts +32 -0
- package/dist/http.js +196 -0
- package/dist/index.d.ts +28 -16
- package/dist/index.js +25 -12
- package/dist/invalidation.d.ts +69 -0
- package/dist/invalidation.js +84 -0
- package/dist/leader-lock.d.ts +28 -0
- package/dist/leader-lock.js +38 -0
- package/dist/multi-tab.d.ts +134 -0
- package/dist/multi-tab.js +399 -0
- package/dist/node-database.d.ts +41 -0
- package/dist/node-database.js +133 -0
- package/dist/outbox.d.ts +56 -0
- package/dist/outbox.js +140 -0
- package/dist/schema.d.ts +107 -0
- package/dist/schema.js +267 -0
- package/dist/state.d.ts +40 -0
- package/dist/state.js +77 -0
- package/dist/transport.d.ts +44 -0
- package/dist/transport.js +7 -0
- package/dist/wasm-database.d.ts +30 -0
- package/dist/wasm-database.js +170 -0
- package/dist/window.d.ts +64 -0
- package/dist/window.js +0 -0
- package/dist/worker-entry.d.ts +16 -2
- package/dist/worker-entry.js +300 -456
- package/dist/worker-host.d.ts +174 -0
- package/dist/worker-host.js +536 -0
- package/dist/worker-protocol.d.ts +173 -61
- package/dist/worker-protocol.js +7 -16
- package/package.json +45 -112
- package/src/apply.ts +435 -0
- package/src/blob.ts +333 -0
- package/src/bun-database.ts +75 -0
- package/src/client.ts +2247 -277
- package/src/content-type.ts +2 -0
- package/src/database.ts +63 -1389
- package/src/encryption.ts +123 -0
- package/src/errors.ts +11 -265
- package/src/http.ts +254 -0
- package/src/index.ts +25 -20
- package/src/invalidation.ts +128 -0
- package/src/leader-lock.ts +68 -0
- package/src/multi-tab.ts +550 -0
- package/src/node-database.ts +180 -0
- package/src/outbox.ts +206 -0
- package/src/schema.ts +397 -0
- package/src/state.ts +126 -0
- package/src/transport.ts +52 -0
- package/src/wasm-database.ts +279 -0
- package/src/window.ts +0 -0
- package/src/worker-entry.ts +391 -545
- package/src/worker-host.ts +800 -0
- package/src/worker-protocol.ts +204 -99
- package/dist/auth-leases.d.ts +0 -11
- package/dist/auth-leases.d.ts.map +0 -1
- package/dist/auth-leases.js +0 -140
- package/dist/auth-leases.js.map +0 -1
- package/dist/blob-limits.d.ts +0 -15
- package/dist/blob-limits.d.ts.map +0 -1
- package/dist/blob-limits.js +0 -66
- package/dist/blob-limits.js.map +0 -1
- package/dist/bridge-client.d.ts +0 -69
- package/dist/bridge-client.d.ts.map +0 -1
- package/dist/bridge-client.js +0 -282
- package/dist/bridge-client.js.map +0 -1
- package/dist/client-config.d.ts +0 -6
- package/dist/client-config.d.ts.map +0 -1
- package/dist/client-config.js +0 -18
- package/dist/client-config.js.map +0 -1
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/command-history.d.ts +0 -35
- package/dist/command-history.d.ts.map +0 -1
- package/dist/command-history.js +0 -378
- package/dist/command-history.js.map +0 -1
- package/dist/console-diagnostics.d.ts +0 -33
- package/dist/console-diagnostics.d.ts.map +0 -1
- package/dist/console-diagnostics.js +0 -496
- package/dist/console-diagnostics.js.map +0 -1
- package/dist/crdt-yjs/index.d.ts +0 -4
- package/dist/crdt-yjs/index.d.ts.map +0 -1
- package/dist/crdt-yjs/index.js +0 -4
- package/dist/crdt-yjs/index.js.map +0 -1
- package/dist/crdt-yjs/webview-host-facade.d.ts +0 -126
- package/dist/crdt-yjs/webview-host-facade.d.ts.map +0 -1
- package/dist/crdt-yjs/webview-host-facade.js +0 -284
- package/dist/crdt-yjs/webview-host-facade.js.map +0 -1
- package/dist/crdt-yjs/yjs-document-field-adapter.d.ts +0 -153
- package/dist/crdt-yjs/yjs-document-field-adapter.d.ts.map +0 -1
- package/dist/crdt-yjs/yjs-document-field-adapter.js +0 -406
- package/dist/crdt-yjs/yjs-document-field-adapter.js.map +0 -1
- package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts +0 -73
- package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts.map +0 -1
- package/dist/crdt-yjs/yjs-prosemirror-bridge.js +0 -169
- package/dist/crdt-yjs/yjs-prosemirror-bridge.js.map +0 -1
- package/dist/database.d.ts.map +0 -1
- package/dist/database.js.map +0 -1
- package/dist/diagnostics.d.ts +0 -10
- package/dist/diagnostics.d.ts.map +0 -1
- package/dist/diagnostics.js +0 -83
- package/dist/diagnostics.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/generated-bridge.d.ts +0 -365
- package/dist/generated-bridge.d.ts.map +0 -1
- package/dist/generated-bridge.js +0 -250
- package/dist/generated-bridge.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/mutations.d.ts +0 -72
- package/dist/mutations.d.ts.map +0 -1
- package/dist/mutations.js +0 -63
- package/dist/mutations.js.map +0 -1
- package/dist/network.d.ts +0 -3
- package/dist/network.d.ts.map +0 -1
- package/dist/network.js +0 -17
- package/dist/network.js.map +0 -1
- package/dist/react/index.d.ts +0 -169
- package/dist/react/index.d.ts.map +0 -1
- package/dist/react/index.js +0 -628
- package/dist/react/index.js.map +0 -1
- package/dist/react-native/index.d.ts +0 -35
- package/dist/react-native/index.d.ts.map +0 -1
- package/dist/react-native/index.js +0 -49
- package/dist/react-native/index.js.map +0 -1
- package/dist/runtime-contract.d.ts +0 -13
- package/dist/runtime-contract.d.ts.map +0 -1
- package/dist/runtime-contract.js +0 -24
- package/dist/runtime-contract.js.map +0 -1
- package/dist/rust-client.d.ts +0 -106
- package/dist/rust-client.d.ts.map +0 -1
- package/dist/rust-client.js +0 -938
- package/dist/rust-client.js.map +0 -1
- package/dist/sentry.d.ts +0 -35
- package/dist/sentry.d.ts.map +0 -1
- package/dist/sentry.js +0 -155
- package/dist/sentry.js.map +0 -1
- package/dist/sql-safety.d.ts +0 -3
- package/dist/sql-safety.d.ts.map +0 -1
- package/dist/sql-safety.js +0 -62
- package/dist/sql-safety.js.map +0 -1
- package/dist/syncular-runtime-artifacts.json +0 -61
- package/dist/tauri/index.d.ts +0 -35
- package/dist/tauri/index.d.ts.map +0 -1
- package/dist/tauri/index.js +0 -114
- package/dist/tauri/index.js.map +0 -1
- package/dist/types.d.ts +0 -1096
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/wasm/.syncular-wasm-profile +0 -1
- package/dist/wasm/syncular-runtime-artifact.json +0 -21
- package/dist/wasm/syncular.d.ts +0 -207
- package/dist/wasm/syncular.js +0 -2341
- package/dist/wasm/syncular_bg.wasm +0 -0
- package/dist/wasm/syncular_bg.wasm.d.ts +0 -97
- package/dist/wasm-bindings/runtime-contract.d.ts +0 -22
- package/dist/wasm-bindings/runtime-contract.d.ts.map +0 -1
- package/dist/wasm-bindings/runtime-contract.js +0 -112
- package/dist/wasm-bindings/runtime-contract.js.map +0 -1
- package/dist/wasm-core/.syncular-wasm-profile +0 -1
- package/dist/wasm-core/syncular-runtime-artifact.json +0 -17
- package/dist/wasm-core/syncular.d.ts +0 -162
- package/dist/wasm-core/syncular.js +0 -1847
- package/dist/wasm-core/syncular_bg.wasm +0 -0
- package/dist/wasm-core/syncular_bg.wasm.d.ts +0 -77
- package/dist/wasm-perf/.syncular-wasm-profile +0 -1
- package/dist/wasm-perf/syncular-runtime-artifact.json +0 -21
- package/dist/wasm-perf/syncular.d.ts +0 -207
- package/dist/wasm-perf/syncular.js +0 -2341
- package/dist/wasm-perf/syncular_bg.wasm +0 -0
- package/dist/wasm-perf/syncular_bg.wasm.d.ts +0 -97
- package/dist/wasm-runtime.d.ts +0 -23
- package/dist/wasm-runtime.d.ts.map +0 -1
- package/dist/wasm-runtime.js +0 -69
- package/dist/wasm-runtime.js.map +0 -1
- package/dist/worker-client.d.ts +0 -123
- package/dist/worker-client.d.ts.map +0 -1
- package/dist/worker-client.js +0 -1735
- package/dist/worker-client.js.map +0 -1
- package/dist/worker-entry.d.ts.map +0 -1
- package/dist/worker-entry.js.map +0 -1
- package/dist/worker-protocol.d.ts.map +0 -1
- package/dist/worker-protocol.js.map +0 -1
- package/dist/worker-realtime.d.ts +0 -39
- package/dist/worker-realtime.d.ts.map +0 -1
- package/dist/worker-realtime.js +0 -677
- package/dist/worker-realtime.js.map +0 -1
- package/src/auth-leases.ts +0 -251
- package/src/blob-limits.ts +0 -98
- package/src/bridge-client.ts +0 -512
- package/src/client-config.ts +0 -29
- package/src/command-history.ts +0 -623
- package/src/console-diagnostics.ts +0 -617
- package/src/crdt-yjs/index.ts +0 -3
- package/src/crdt-yjs/webview-host-facade.ts +0 -477
- package/src/crdt-yjs/yjs-document-field-adapter.ts +0 -733
- package/src/crdt-yjs/yjs-prosemirror-bridge.ts +0 -272
- package/src/diagnostics.ts +0 -116
- package/src/generated-bridge.ts +0 -741
- package/src/mutations.ts +0 -168
- package/src/network.ts +0 -32
- package/src/react/index.ts +0 -1036
- package/src/react-native/index.ts +0 -152
- package/src/runtime-contract.ts +0 -48
- package/src/rust-client.ts +0 -1491
- package/src/sentry.ts +0 -215
- package/src/sql-safety.ts +0 -61
- package/src/tauri/index.ts +0 -211
- package/src/types.ts +0 -1397
- package/src/wasm-bindings/generated-wasm-bindings.d.ts +0 -70
- package/src/wasm-bindings/runtime-contract.ts +0 -158
- package/src/wasm-runtime.ts +0 -145
- package/src/worker-client.ts +0 -2289
- package/src/worker-realtime.ts +0 -843
package/src/bridge-client.ts
DELETED
|
@@ -1,512 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
randomId,
|
|
3
|
-
type SyncAuthLeaseIssueRequest,
|
|
4
|
-
type SyncOperation,
|
|
5
|
-
} from '@syncular/core';
|
|
6
|
-
import {
|
|
7
|
-
type CompiledQuery,
|
|
8
|
-
type DatabaseConnection,
|
|
9
|
-
type Dialect,
|
|
10
|
-
Kysely,
|
|
11
|
-
type QueryResult,
|
|
12
|
-
} from 'kysely';
|
|
13
|
-
import { BaseSqliteDialect, BaseSqliteDriver } from 'kysely-generic-sqlite';
|
|
14
|
-
import type {
|
|
15
|
-
SyncularBlobClientLike,
|
|
16
|
-
SyncularClientLike,
|
|
17
|
-
SyncularClientStatus,
|
|
18
|
-
} from './client';
|
|
19
|
-
import {
|
|
20
|
-
createMutationsApi,
|
|
21
|
-
type MutationReceipt,
|
|
22
|
-
type MutationsApi,
|
|
23
|
-
type MutationsCommitFn,
|
|
24
|
-
type MutationsTx,
|
|
25
|
-
} from './mutations';
|
|
26
|
-
import { assertSyncularReadonlySql } from './sql-safety';
|
|
27
|
-
import type {
|
|
28
|
-
SyncularAuthLeaseRecord,
|
|
29
|
-
SyncularClientEventSink,
|
|
30
|
-
SyncularClientEventType,
|
|
31
|
-
SyncularConflictResolution,
|
|
32
|
-
SyncularConflictStats,
|
|
33
|
-
SyncularConflictSummary,
|
|
34
|
-
SyncularConnectionState,
|
|
35
|
-
SyncularDiagnosticSnapshot,
|
|
36
|
-
SyncularLifecycleState,
|
|
37
|
-
SyncularOutboxStats,
|
|
38
|
-
SyncularPresenceEntry,
|
|
39
|
-
SyncularPresenceSink,
|
|
40
|
-
SyncularSubscriptionSpec,
|
|
41
|
-
SyncularSyncRequestOptions,
|
|
42
|
-
SyncularSyncResult,
|
|
43
|
-
} from './types';
|
|
44
|
-
|
|
45
|
-
export interface SyncularBridgeQueryRequest {
|
|
46
|
-
sql: string;
|
|
47
|
-
parameters: readonly unknown[];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface SyncularBridgeQueryResult<
|
|
51
|
-
Row extends Record<string, unknown> = Record<string, unknown>,
|
|
52
|
-
> {
|
|
53
|
-
rows: Row[];
|
|
54
|
-
numAffectedRows?: bigint | number | string;
|
|
55
|
-
insertId?: bigint | number | string | null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface SyncularBridgeMutationBatch {
|
|
59
|
-
operations: SyncOperation[];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export interface SyncularBridgeStatus {
|
|
63
|
-
lifecycle?: SyncularLifecycleState;
|
|
64
|
-
connection?: SyncularConnectionState;
|
|
65
|
-
outbox?: SyncularOutboxStats | null;
|
|
66
|
-
conflicts?: SyncularConflictStats | null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface SyncularBridgePresence {
|
|
70
|
-
get<TMetadata = Record<string, unknown>>(
|
|
71
|
-
scopeKey: string
|
|
72
|
-
): SyncularPresenceEntry<TMetadata>[];
|
|
73
|
-
join(scopeKey: string, metadata?: Record<string, unknown>): void;
|
|
74
|
-
leave(scopeKey: string): void;
|
|
75
|
-
updateMetadata(scopeKey: string, metadata: Record<string, unknown>): void;
|
|
76
|
-
onChange<TMetadata = Record<string, unknown>>(
|
|
77
|
-
listener: SyncularPresenceSink<TMetadata>
|
|
78
|
-
): () => void;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export interface SyncularBridgeConflicts {
|
|
82
|
-
list(): Promise<SyncularConflictSummary[]>;
|
|
83
|
-
retryKeepLocal(id: string): Promise<string>;
|
|
84
|
-
resolve(id: string, resolution: SyncularConflictResolution): Promise<void>;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export interface SyncularBridge {
|
|
88
|
-
open?(): Promise<void> | void;
|
|
89
|
-
close?(): Promise<void> | void;
|
|
90
|
-
executeSql<Row extends Record<string, unknown> = Record<string, unknown>>(
|
|
91
|
-
request: SyncularBridgeQueryRequest
|
|
92
|
-
): Promise<SyncularBridgeQueryResult<Row>> | SyncularBridgeQueryResult<Row>;
|
|
93
|
-
applyMutationsCommit(
|
|
94
|
-
batch: SyncularBridgeMutationBatch
|
|
95
|
-
): Promise<string | MutationReceipt> | string | MutationReceipt;
|
|
96
|
-
applyLeasedMutationsCommit?(
|
|
97
|
-
batch: SyncularBridgeMutationBatch
|
|
98
|
-
): Promise<string | MutationReceipt> | string | MutationReceipt;
|
|
99
|
-
sync?(): Promise<SyncularSyncResult>;
|
|
100
|
-
resumeFromBackground?(
|
|
101
|
-
options?: SyncularSyncRequestOptions
|
|
102
|
-
): Promise<SyncularSyncResult>;
|
|
103
|
-
start?(): Promise<void>;
|
|
104
|
-
stop?(): Promise<void>;
|
|
105
|
-
setSubscriptions?(
|
|
106
|
-
subscriptions: readonly SyncularSubscriptionSpec[]
|
|
107
|
-
): Promise<void>;
|
|
108
|
-
getStatus?(): Promise<SyncularBridgeStatus> | SyncularBridgeStatus;
|
|
109
|
-
issueAuthLease?(
|
|
110
|
-
request: SyncAuthLeaseIssueRequest
|
|
111
|
-
): Promise<SyncularAuthLeaseRecord>;
|
|
112
|
-
upsertAuthLease?(lease: SyncularAuthLeaseRecord): Promise<void>;
|
|
113
|
-
authLease?(leaseId: string): Promise<SyncularAuthLeaseRecord | null>;
|
|
114
|
-
activeAuthLeases?(
|
|
115
|
-
actorId?: string | null,
|
|
116
|
-
nowMs?: number
|
|
117
|
-
): Promise<SyncularAuthLeaseRecord[]>;
|
|
118
|
-
diagnosticSnapshot?(): Promise<SyncularDiagnosticSnapshot>;
|
|
119
|
-
on?<T extends SyncularClientEventType>(
|
|
120
|
-
event: T,
|
|
121
|
-
listener: SyncularClientEventSink<T>
|
|
122
|
-
): () => void;
|
|
123
|
-
presence?: SyncularBridgePresence;
|
|
124
|
-
conflicts?: SyncularBridgeConflicts;
|
|
125
|
-
blobs?: Partial<SyncularBlobClientLike>;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export interface CreateSyncularBridgeClientOptions {
|
|
129
|
-
bridge: SyncularBridge;
|
|
130
|
-
sync?: {
|
|
131
|
-
autoSyncAfterMutation?: boolean;
|
|
132
|
-
};
|
|
133
|
-
idColumn?: string;
|
|
134
|
-
versionColumn?: string | null;
|
|
135
|
-
omitColumns?: readonly string[];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function createSyncularBridgeDialect(bridge: SyncularBridge): Dialect {
|
|
139
|
-
return new BaseSqliteDialect(() => new SyncularBridgeDriver(bridge));
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export async function createSyncularBridgeClient<DB>(
|
|
143
|
-
options: CreateSyncularBridgeClientOptions
|
|
144
|
-
): Promise<SyncularClientLike<DB>> {
|
|
145
|
-
await options.bridge.open?.();
|
|
146
|
-
const dialect = createSyncularBridgeDialect(options.bridge);
|
|
147
|
-
const db = new Kysely<DB>({ dialect });
|
|
148
|
-
let closed = false;
|
|
149
|
-
const syncAfterMutation = options.sync?.autoSyncAfterMutation !== false;
|
|
150
|
-
|
|
151
|
-
const client: SyncularClientLike<DB> = {
|
|
152
|
-
db,
|
|
153
|
-
dialect,
|
|
154
|
-
mutations: createSyncularBridgeMutations<DB>(options, async () => {
|
|
155
|
-
if (syncAfterMutation) await client.sync().catch(() => undefined);
|
|
156
|
-
}),
|
|
157
|
-
leasedMutations: createSyncularBridgeMutations<DB>(
|
|
158
|
-
options,
|
|
159
|
-
async () => {
|
|
160
|
-
if (syncAfterMutation) await client.sync().catch(() => undefined);
|
|
161
|
-
},
|
|
162
|
-
{ leased: true }
|
|
163
|
-
),
|
|
164
|
-
blobs: createBridgeBlobs(options.bridge),
|
|
165
|
-
on: (event, listener) => options.bridge.on?.(event, listener) ?? noop,
|
|
166
|
-
getStatus: () => resolveBridgeStatusSnapshot(options.bridge),
|
|
167
|
-
setSubscriptions: async (subscriptions) => {
|
|
168
|
-
await options.bridge.setSubscriptions?.(subscriptions);
|
|
169
|
-
},
|
|
170
|
-
resumeFromBackground: (syncOptions) =>
|
|
171
|
-
requireBridgeMethod(
|
|
172
|
-
options.bridge.resumeFromBackground,
|
|
173
|
-
'resumeFromBackground'
|
|
174
|
-
)(syncOptions),
|
|
175
|
-
issueAuthLease: (request) =>
|
|
176
|
-
requireBridgeMethod(
|
|
177
|
-
options.bridge.issueAuthLease,
|
|
178
|
-
'issueAuthLease'
|
|
179
|
-
)(request),
|
|
180
|
-
upsertAuthLease: (lease) =>
|
|
181
|
-
requireBridgeMethod(
|
|
182
|
-
options.bridge.upsertAuthLease,
|
|
183
|
-
'upsertAuthLease'
|
|
184
|
-
)(lease),
|
|
185
|
-
authLease: (leaseId) =>
|
|
186
|
-
requireBridgeMethod(options.bridge.authLease, 'authLease')(leaseId),
|
|
187
|
-
activeAuthLeases: (actorId, nowMs) =>
|
|
188
|
-
requireBridgeMethod(options.bridge.activeAuthLeases, 'activeAuthLeases')(
|
|
189
|
-
actorId,
|
|
190
|
-
nowMs
|
|
191
|
-
),
|
|
192
|
-
diagnosticSnapshot: () =>
|
|
193
|
-
requireBridgeMethod(
|
|
194
|
-
options.bridge.diagnosticSnapshot,
|
|
195
|
-
'diagnosticSnapshot'
|
|
196
|
-
)(),
|
|
197
|
-
presence: createBridgePresence(options.bridge),
|
|
198
|
-
conflicts: createBridgeConflicts(options.bridge),
|
|
199
|
-
start: async () => {
|
|
200
|
-
await options.bridge.start?.();
|
|
201
|
-
},
|
|
202
|
-
stop: async () => {
|
|
203
|
-
await options.bridge.stop?.();
|
|
204
|
-
},
|
|
205
|
-
sync: async () => {
|
|
206
|
-
if (!options.bridge.sync) {
|
|
207
|
-
throw new Error('Syncular bridge does not implement sync().');
|
|
208
|
-
}
|
|
209
|
-
return await options.bridge.sync();
|
|
210
|
-
},
|
|
211
|
-
async close() {
|
|
212
|
-
if (closed) return;
|
|
213
|
-
closed = true;
|
|
214
|
-
await options.bridge.stop?.();
|
|
215
|
-
await db.destroy();
|
|
216
|
-
await options.bridge.close?.();
|
|
217
|
-
},
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
return client;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function createSyncularBridgeMutations<DB>(
|
|
224
|
-
options: CreateSyncularBridgeClientOptions,
|
|
225
|
-
afterCommit: () => Promise<void>,
|
|
226
|
-
mode: { leased?: boolean } = {}
|
|
227
|
-
): MutationsApi<DB, undefined> {
|
|
228
|
-
const commit = createSyncularBridgeCommit<DB>(options, afterCommit, mode);
|
|
229
|
-
return createMutationsApi(commit);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function createSyncularBridgeCommit<DB>(
|
|
233
|
-
options: CreateSyncularBridgeClientOptions,
|
|
234
|
-
afterCommit: () => Promise<void>,
|
|
235
|
-
mode: { leased?: boolean } = {}
|
|
236
|
-
): MutationsCommitFn<DB, { operations: SyncOperation[] }, undefined> {
|
|
237
|
-
const idColumn = options.idColumn ?? 'id';
|
|
238
|
-
const versionColumn = options.versionColumn ?? 'server_version';
|
|
239
|
-
const omitColumns = options.omitColumns ?? [];
|
|
240
|
-
|
|
241
|
-
return async (fn) => {
|
|
242
|
-
const operations: SyncOperation[] = [];
|
|
243
|
-
const makeTxTable = (table: string) => ({
|
|
244
|
-
async insert(values: unknown) {
|
|
245
|
-
const raw = objectRecord(values);
|
|
246
|
-
const id =
|
|
247
|
-
typeof raw[idColumn] === 'string' && raw[idColumn]
|
|
248
|
-
? raw[idColumn]
|
|
249
|
-
: randomId();
|
|
250
|
-
operations.push({
|
|
251
|
-
table,
|
|
252
|
-
row_id: id,
|
|
253
|
-
op: 'upsert',
|
|
254
|
-
payload: sanitizeOperationPayload({ ...raw, [idColumn]: id }, [
|
|
255
|
-
idColumn,
|
|
256
|
-
...(versionColumn ? [versionColumn] : []),
|
|
257
|
-
...omitColumns,
|
|
258
|
-
]),
|
|
259
|
-
base_version: null,
|
|
260
|
-
});
|
|
261
|
-
return id;
|
|
262
|
-
},
|
|
263
|
-
async insertMany(rows: unknown[]) {
|
|
264
|
-
const ids: string[] = [];
|
|
265
|
-
for (const row of rows) ids.push(await this.insert(row));
|
|
266
|
-
return ids;
|
|
267
|
-
},
|
|
268
|
-
async update(
|
|
269
|
-
id: string,
|
|
270
|
-
patch: unknown,
|
|
271
|
-
opts?: { baseVersion?: number | null }
|
|
272
|
-
) {
|
|
273
|
-
operations.push(upsertOperation(table, id, patch, opts));
|
|
274
|
-
},
|
|
275
|
-
async delete(id: string, opts?: { baseVersion?: number | null }) {
|
|
276
|
-
operations.push({
|
|
277
|
-
table,
|
|
278
|
-
row_id: id,
|
|
279
|
-
op: 'delete',
|
|
280
|
-
payload: null,
|
|
281
|
-
base_version: opts?.baseVersion ?? null,
|
|
282
|
-
});
|
|
283
|
-
},
|
|
284
|
-
async upsert(
|
|
285
|
-
id: string,
|
|
286
|
-
patch: unknown,
|
|
287
|
-
opts?: { baseVersion?: number | null }
|
|
288
|
-
) {
|
|
289
|
-
operations.push(upsertOperation(table, id, patch, opts));
|
|
290
|
-
},
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
const tx = new Proxy(
|
|
294
|
-
{},
|
|
295
|
-
{
|
|
296
|
-
get(_target, prop) {
|
|
297
|
-
if (prop === 'then') return undefined;
|
|
298
|
-
return typeof prop === 'string' ? makeTxTable(prop) : undefined;
|
|
299
|
-
},
|
|
300
|
-
}
|
|
301
|
-
) as MutationsTx<DB>;
|
|
302
|
-
|
|
303
|
-
const result = await fn(tx);
|
|
304
|
-
if (operations.length === 0) throw new Error('No mutations were enqueued');
|
|
305
|
-
const commitFn = mode.leased
|
|
306
|
-
? requireBridgeMethod(
|
|
307
|
-
options.bridge.applyLeasedMutationsCommit,
|
|
308
|
-
'applyLeasedMutationsCommit'
|
|
309
|
-
)
|
|
310
|
-
: options.bridge.applyMutationsCommit.bind(options.bridge);
|
|
311
|
-
const receipt = normalizeMutationReceipt(await commitFn({ operations }));
|
|
312
|
-
await afterCommit();
|
|
313
|
-
return { result, receipt, meta: { operations } };
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
function upsertOperation(
|
|
317
|
-
table: string,
|
|
318
|
-
id: string,
|
|
319
|
-
patch: unknown,
|
|
320
|
-
opts?: { baseVersion?: number | null }
|
|
321
|
-
): SyncOperation {
|
|
322
|
-
return {
|
|
323
|
-
table,
|
|
324
|
-
row_id: id,
|
|
325
|
-
op: 'upsert',
|
|
326
|
-
payload: sanitizeOperationPayload(objectRecord(patch), [
|
|
327
|
-
idColumn,
|
|
328
|
-
...(versionColumn ? [versionColumn] : []),
|
|
329
|
-
...omitColumns,
|
|
330
|
-
]),
|
|
331
|
-
base_version: opts?.baseVersion ?? null,
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
class SyncularBridgeDriver extends BaseSqliteDriver {
|
|
337
|
-
constructor(bridge: SyncularBridge) {
|
|
338
|
-
super(async () => {
|
|
339
|
-
this.conn = new SyncularBridgeConnection(bridge);
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
async destroy(): Promise<void> {
|
|
344
|
-
this.conn = undefined;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
class SyncularBridgeConnection implements DatabaseConnection {
|
|
349
|
-
constructor(private readonly bridge: SyncularBridge) {}
|
|
350
|
-
|
|
351
|
-
async executeQuery<R>(compiledQuery: CompiledQuery): Promise<QueryResult<R>> {
|
|
352
|
-
assertSyncularReadonlySql(compiledQuery.sql);
|
|
353
|
-
const result = await this.bridge.executeSql<R & Record<string, unknown>>({
|
|
354
|
-
sql: compiledQuery.sql,
|
|
355
|
-
parameters: compiledQuery.parameters,
|
|
356
|
-
});
|
|
357
|
-
return {
|
|
358
|
-
rows: result.rows as R[],
|
|
359
|
-
numAffectedRows:
|
|
360
|
-
result.numAffectedRows == null
|
|
361
|
-
? undefined
|
|
362
|
-
: BigInt(result.numAffectedRows),
|
|
363
|
-
insertId: result.insertId == null ? undefined : BigInt(result.insertId),
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
streamQuery<R>(
|
|
368
|
-
_compiledQuery: CompiledQuery,
|
|
369
|
-
_chunkSize?: number
|
|
370
|
-
): AsyncIterableIterator<QueryResult<R>> {
|
|
371
|
-
throw new Error('Syncular bridge dialect does not support streaming.');
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
function createBridgePresence(bridge: SyncularBridge): SyncularBridgePresence {
|
|
376
|
-
return (
|
|
377
|
-
bridge.presence ?? {
|
|
378
|
-
get: () => [],
|
|
379
|
-
join: unsupportedFn('presence.join'),
|
|
380
|
-
leave: unsupportedFn('presence.leave'),
|
|
381
|
-
updateMetadata: unsupportedFn('presence.updateMetadata'),
|
|
382
|
-
onChange: () => noop,
|
|
383
|
-
}
|
|
384
|
-
);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function createBridgeConflicts(
|
|
388
|
-
bridge: SyncularBridge
|
|
389
|
-
): SyncularBridgeConflicts {
|
|
390
|
-
return (
|
|
391
|
-
bridge.conflicts ?? {
|
|
392
|
-
list: async () => [],
|
|
393
|
-
retryKeepLocal: unsupportedFn('conflicts.retryKeepLocal'),
|
|
394
|
-
resolve: unsupportedFn('conflicts.resolve'),
|
|
395
|
-
}
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function createBridgeBlobs(bridge: SyncularBridge): SyncularBlobClientLike {
|
|
400
|
-
return {
|
|
401
|
-
getUploadQueueStats: async () =>
|
|
402
|
-
bridge.blobs?.getUploadQueueStats?.() ?? {
|
|
403
|
-
pending: 0,
|
|
404
|
-
uploading: 0,
|
|
405
|
-
failed: 0,
|
|
406
|
-
},
|
|
407
|
-
processUploadQueue: async (options) =>
|
|
408
|
-
bridge.blobs?.processUploadQueue?.(options) ?? { uploaded: 0, failed: 0 },
|
|
409
|
-
retrieve:
|
|
410
|
-
bridge.blobs?.retrieve ??
|
|
411
|
-
(async () => {
|
|
412
|
-
throw new Error('Syncular bridge does not implement blob retrieval.');
|
|
413
|
-
}),
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
function resolveBridgeStatusSnapshot(
|
|
418
|
-
bridge: SyncularBridge
|
|
419
|
-
): SyncularClientStatus {
|
|
420
|
-
const snapshot = readSynchronousStatus(bridge);
|
|
421
|
-
const connection = snapshot.connection ?? {
|
|
422
|
-
closed: false,
|
|
423
|
-
pendingRequests: 0,
|
|
424
|
-
realtime: 'disconnected',
|
|
425
|
-
};
|
|
426
|
-
const lifecycle = snapshot.lifecycle ?? {
|
|
427
|
-
phase: connection.closed ? 'closed' : 'offline',
|
|
428
|
-
realtime: connection.realtime,
|
|
429
|
-
online: connection.realtime === 'connected',
|
|
430
|
-
requiresAction: false,
|
|
431
|
-
pendingRequests: connection.pendingRequests,
|
|
432
|
-
...(snapshot.outbox ? { outbox: snapshot.outbox } : {}),
|
|
433
|
-
...(snapshot.conflicts ? { conflicts: snapshot.conflicts } : {}),
|
|
434
|
-
};
|
|
435
|
-
const outbox = snapshot.outbox ?? lifecycle.outbox ?? null;
|
|
436
|
-
const conflicts = snapshot.conflicts ?? lifecycle.conflicts ?? null;
|
|
437
|
-
return {
|
|
438
|
-
lifecycle,
|
|
439
|
-
connection,
|
|
440
|
-
outbox,
|
|
441
|
-
conflicts,
|
|
442
|
-
isConnected: connection.realtime === 'connected' && !connection.closed,
|
|
443
|
-
isSyncing:
|
|
444
|
-
lifecycle.phase === 'syncing' || lifecycle.phase === 'recovering',
|
|
445
|
-
hasPendingMutations: (outbox?.pending ?? 0) + (outbox?.sending ?? 0) > 0,
|
|
446
|
-
hasConflicts: (conflicts?.unresolved ?? 0) > 0,
|
|
447
|
-
requiresAction: lifecycle.requiresAction,
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
function readSynchronousStatus(bridge: SyncularBridge): SyncularBridgeStatus {
|
|
452
|
-
if (!bridge.getStatus) return {};
|
|
453
|
-
const snapshot = bridge.getStatus();
|
|
454
|
-
if (isPromiseLike(snapshot)) {
|
|
455
|
-
throw new Error(
|
|
456
|
-
'Syncular bridge getStatus() must be synchronous for React snapshots.'
|
|
457
|
-
);
|
|
458
|
-
}
|
|
459
|
-
return snapshot;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
function normalizeMutationReceipt(
|
|
463
|
-
value: string | MutationReceipt
|
|
464
|
-
): MutationReceipt {
|
|
465
|
-
if (typeof value === 'string')
|
|
466
|
-
return { commitId: value, clientCommitId: value };
|
|
467
|
-
return value;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function sanitizeOperationPayload(
|
|
471
|
-
payload: Record<string, unknown>,
|
|
472
|
-
omitColumns: readonly string[]
|
|
473
|
-
): Record<string, unknown> {
|
|
474
|
-
const omit = new Set(omitColumns);
|
|
475
|
-
const out: Record<string, unknown> = {};
|
|
476
|
-
for (const [key, value] of Object.entries(payload)) {
|
|
477
|
-
if (!omit.has(key)) out[key] = value;
|
|
478
|
-
}
|
|
479
|
-
return out;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
function objectRecord(value: unknown): Record<string, unknown> {
|
|
483
|
-
return value && typeof value === 'object' && !Array.isArray(value)
|
|
484
|
-
? { ...(value as Record<string, unknown>) }
|
|
485
|
-
: {};
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
function unsupportedFn<Args extends unknown[], Result>(
|
|
489
|
-
method: string
|
|
490
|
-
): (...args: Args) => Result {
|
|
491
|
-
return () => {
|
|
492
|
-
throw new Error(`Syncular bridge does not implement ${method}.`);
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
function requireBridgeMethod<Fn extends (...args: any[]) => any>(
|
|
497
|
-
fn: Fn | undefined,
|
|
498
|
-
method: string
|
|
499
|
-
): Fn {
|
|
500
|
-
if (typeof fn === 'function') return fn;
|
|
501
|
-
return unsupportedFn(method) as Fn;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
function isPromiseLike(value: unknown): value is Promise<unknown> {
|
|
505
|
-
return (
|
|
506
|
-
value != null &&
|
|
507
|
-
typeof value === 'object' &&
|
|
508
|
-
typeof (value as { then?: unknown }).then === 'function'
|
|
509
|
-
);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
function noop(): void {}
|
package/src/client-config.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ResolvedSyncularClientConfig,
|
|
3
|
-
SyncularClientConfig,
|
|
4
|
-
SyncularStorage,
|
|
5
|
-
} from './types';
|
|
6
|
-
|
|
7
|
-
export const SYNCULAR_DEFAULT_STORAGE = 'opfsSahPool' satisfies SyncularStorage;
|
|
8
|
-
export const SYNCULAR_LOCAL_DISABLED_BASE_URL = 'syncular-local://disabled';
|
|
9
|
-
|
|
10
|
-
export function resolveSyncularClientConfig(
|
|
11
|
-
config: SyncularClientConfig
|
|
12
|
-
): ResolvedSyncularClientConfig {
|
|
13
|
-
const mode = config.mode ?? 'remote';
|
|
14
|
-
if (mode === 'remote' && !config.baseUrl) {
|
|
15
|
-
throw new Error('Syncular remote clients require config.baseUrl');
|
|
16
|
-
}
|
|
17
|
-
return {
|
|
18
|
-
...config,
|
|
19
|
-
mode,
|
|
20
|
-
baseUrl: config.baseUrl ?? SYNCULAR_LOCAL_DISABLED_BASE_URL,
|
|
21
|
-
storage: config.storage ?? SYNCULAR_DEFAULT_STORAGE,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function isSyncularRemoteMode(
|
|
26
|
-
config: Pick<ResolvedSyncularClientConfig, 'mode'>
|
|
27
|
-
): boolean {
|
|
28
|
-
return config.mode === 'remote';
|
|
29
|
-
}
|