@korajs/test 0.0.0-canary-20261008174129
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/LICENSE +21 -0
- package/dist/index.cjs +973 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +493 -0
- package/dist/index.d.ts +493 -0
- package/dist/index.js +942 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import * as _korajs_sync from '@korajs/sync';
|
|
2
|
+
import { SyncTransport, SyncEncryptionConfig, SyncEngine, ChaosConfig } from '@korajs/sync';
|
|
3
|
+
export { ChaosConfig, ChaosTransport } from '@korajs/sync';
|
|
4
|
+
import * as _korajs_server from '@korajs/server';
|
|
5
|
+
import { ServerTransport, ServerStore, MemoryServerStore, KoraSyncServerConfig, OperationValidator, KoraSyncServer } from '@korajs/server';
|
|
6
|
+
import { KoraEventEmitter, SchemaDefinition, OperationTransform, BlobRef, VersionVector, Operation } from '@korajs/core';
|
|
7
|
+
import { Store, MemoryBlobStore, CollectionAccessor, BlobManifest, ReceiveBlobResult, ContentAddressedBlobStore } from '@korajs/store';
|
|
8
|
+
import { AuthSyncBinding, AuthSyncState } from '@korajs/core/bindings';
|
|
9
|
+
|
|
10
|
+
/** What a device needs from its server: a way to open a session on it. */
|
|
11
|
+
interface TestDeviceServer {
|
|
12
|
+
handleConnection(transport: _korajs_server.ServerTransport): string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* End-to-end encryption of a test device's sync traffic. Devices that must read each
|
|
16
|
+
* other's operations share the passphrase AND the key-derivation salt (shared key
|
|
17
|
+
* distribution, ENC-1, is not built yet).
|
|
18
|
+
*/
|
|
19
|
+
interface TestDeviceEncryption {
|
|
20
|
+
config: SyncEncryptionConfig;
|
|
21
|
+
salt: Uint8Array;
|
|
22
|
+
/** PBKDF2 iterations (lower in tests for speed). */
|
|
23
|
+
iterations?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Options for creating a TestDevice.
|
|
27
|
+
*/
|
|
28
|
+
interface TestDeviceOptions {
|
|
29
|
+
/** Unique device name (used for DB file naming) */
|
|
30
|
+
name: string;
|
|
31
|
+
/** Schema definition */
|
|
32
|
+
schema: SchemaDefinition;
|
|
33
|
+
/** Test server to connect to */
|
|
34
|
+
server: TestDeviceServer;
|
|
35
|
+
/** Transport factory — creates a linked client/server transport pair */
|
|
36
|
+
createTransportPair: () => {
|
|
37
|
+
client: SyncTransport;
|
|
38
|
+
serverTransport: _korajs_server.ServerTransport;
|
|
39
|
+
};
|
|
40
|
+
/** Optional directory for temp DB files */
|
|
41
|
+
tmpDir: string;
|
|
42
|
+
/** Client handshake schema version. Defaults to `schema.version`. */
|
|
43
|
+
syncSchemaVersion?: number;
|
|
44
|
+
/** Schema transforms: the store folds every operation's view (transforms at fold time). */
|
|
45
|
+
operationTransforms?: OperationTransform[];
|
|
46
|
+
/** The store's local `maxOperationBytes` (RT-86). Default: the store default (256 KiB). */
|
|
47
|
+
maxOperationBytes?: number;
|
|
48
|
+
/** A pinned store node id (`StoreConfig.nodeId`), as `createKoraAuthSync` sets the device id. */
|
|
49
|
+
nodeId?: string;
|
|
50
|
+
/** What the device does with records that leave its scope. Defaults to the engine default ('retain'). */
|
|
51
|
+
scopeExit?: 'retain' | 'retract';
|
|
52
|
+
/**
|
|
53
|
+
* The signed-in user, as an auth-aware app knows it (RT-42). When set, the store binds
|
|
54
|
+
* its node to that user at open and on {@link TestDevice.authChanged}, and the sync
|
|
55
|
+
* engine never uploads another user's node on this user's session.
|
|
56
|
+
*/
|
|
57
|
+
principal?: () => string | null;
|
|
58
|
+
/**
|
|
59
|
+
* Let the sync engine reconnect on its own, as over a real WebSocket: a `connect()`
|
|
60
|
+
* after the session closed opens a new transport pair to the server. Off by default
|
|
61
|
+
* (a closed pair stays closed until the next {@link TestDevice.sync}).
|
|
62
|
+
*/
|
|
63
|
+
reconnectable?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Run the device on the beta.12 pairwise pipeline (`experimental.legacyMerge`)
|
|
66
|
+
* instead of the W7 fold. Used by the comparison harness.
|
|
67
|
+
*/
|
|
68
|
+
legacyMerge?: boolean;
|
|
69
|
+
/** Encrypt sync traffic end to end (protocol v2 envelope). */
|
|
70
|
+
encryption?: TestDeviceEncryption;
|
|
71
|
+
/** Upload batch size of the sync engine (default: the engine's). */
|
|
72
|
+
batchSize?: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A virtual device in a test network.
|
|
76
|
+
* Each device has its own Store (with real SQLite), SyncEngine, and MergeEngine.
|
|
77
|
+
* Provides high-level methods for syncing, disconnecting, and inspecting state.
|
|
78
|
+
*/
|
|
79
|
+
declare class TestDevice {
|
|
80
|
+
readonly name: string;
|
|
81
|
+
readonly store: Store;
|
|
82
|
+
readonly emitter: KoraEventEmitter & {
|
|
83
|
+
clear(): void;
|
|
84
|
+
};
|
|
85
|
+
/** Content-addressed blob store for out-of-band blob bytes (chunks + full blobs). */
|
|
86
|
+
readonly blobStore: MemoryBlobStore;
|
|
87
|
+
private readonly schema;
|
|
88
|
+
private readonly server;
|
|
89
|
+
private readonly encryption;
|
|
90
|
+
private readonly batchSize;
|
|
91
|
+
private encryptor;
|
|
92
|
+
private readonly mergeEngine;
|
|
93
|
+
private readonly createTransportPair;
|
|
94
|
+
private readonly adapter;
|
|
95
|
+
private readonly dbPath;
|
|
96
|
+
private readonly syncSchemaVersion;
|
|
97
|
+
private readonly operationTransforms;
|
|
98
|
+
private readonly scopeExit;
|
|
99
|
+
private readonly principal;
|
|
100
|
+
private readonly reconnectable;
|
|
101
|
+
private applyPipeline;
|
|
102
|
+
private syncEngine;
|
|
103
|
+
private blobChunkProvider;
|
|
104
|
+
private currentTransport;
|
|
105
|
+
private unsubscribeSync;
|
|
106
|
+
private unsubscribeAudit;
|
|
107
|
+
private closing;
|
|
108
|
+
constructor(options: TestDeviceOptions);
|
|
109
|
+
/**
|
|
110
|
+
* Open the store (must be called before sync or collection operations).
|
|
111
|
+
*/
|
|
112
|
+
open(): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* The signed-in user changed (RT-42), as an auth binding reports it: like createApp,
|
|
115
|
+
* end the live session and bind the store to the new user before the next write.
|
|
116
|
+
*/
|
|
117
|
+
authChanged(): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Connect to the test server and perform initial sync.
|
|
120
|
+
* If already connected, flushes any pending operations.
|
|
121
|
+
*/
|
|
122
|
+
sync(): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Disconnect from the test server.
|
|
125
|
+
*/
|
|
126
|
+
disconnect(): Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Reconnect to the test server after a disconnect.
|
|
129
|
+
*/
|
|
130
|
+
reconnect(): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Get a collection accessor for performing CRUD operations.
|
|
133
|
+
*/
|
|
134
|
+
collection(name: string): CollectionAccessor;
|
|
135
|
+
/**
|
|
136
|
+
* Get all records from a collection (convenience method).
|
|
137
|
+
*/
|
|
138
|
+
getState(collectionName: string): Promise<Record<string, unknown>[]>;
|
|
139
|
+
/**
|
|
140
|
+
* Get the device's node ID.
|
|
141
|
+
*/
|
|
142
|
+
getNodeId(): string;
|
|
143
|
+
/** Exposes the sync engine for integration tests (e.g. doc channel, chaos). */
|
|
144
|
+
getSyncEngine(): SyncEngine | null;
|
|
145
|
+
/**
|
|
146
|
+
* Operations the server rejected for this device that have not been reconciled.
|
|
147
|
+
* Empty when the device has never connected.
|
|
148
|
+
*/
|
|
149
|
+
getRejectedOperations(): Promise<_korajs_sync.RejectedOperation[]>;
|
|
150
|
+
/**
|
|
151
|
+
* Stage a blob's bytes into this device's blob store, splitting them into
|
|
152
|
+
* content-addressed chunks this device can then serve to peers over the sync
|
|
153
|
+
* connection. Returns the manifest (blob hash + ordered chunk hashes) needed
|
|
154
|
+
* to pull the blob elsewhere.
|
|
155
|
+
*/
|
|
156
|
+
stageBlob(bytes: Uint8Array, options?: {
|
|
157
|
+
chunkSize?: number;
|
|
158
|
+
}): Promise<BlobManifest>;
|
|
159
|
+
/**
|
|
160
|
+
* Store a blob for transfer the way `app.blobs.put` does: stage chunks, store
|
|
161
|
+
* the full blob, and store the manifest as its own content-addressed object.
|
|
162
|
+
* The returned reference carries a `manifestHash`, so a peer can pull the bytes
|
|
163
|
+
* knowing only the reference.
|
|
164
|
+
*/
|
|
165
|
+
putBlob(bytes: Uint8Array, options?: {
|
|
166
|
+
chunkSize?: number;
|
|
167
|
+
mimeType?: string;
|
|
168
|
+
filename?: string;
|
|
169
|
+
}): Promise<{
|
|
170
|
+
ref: BlobRef;
|
|
171
|
+
manifest: BlobManifest;
|
|
172
|
+
}>;
|
|
173
|
+
/**
|
|
174
|
+
* Pull a blob's bytes over the live connection knowing only its reference: the
|
|
175
|
+
* manifest is resolved by `ref.manifestHash` first, then the chunks are fetched.
|
|
176
|
+
*/
|
|
177
|
+
pullBlobByRef(ref: BlobRef): Promise<ReceiveBlobResult>;
|
|
178
|
+
/**
|
|
179
|
+
* Pull a blob's bytes from peers over the live sync connection. Requests only
|
|
180
|
+
* the chunks this device is missing, reassembles them, and verifies integrity
|
|
181
|
+
* against the manifest's blob hash before storing the full blob locally.
|
|
182
|
+
*/
|
|
183
|
+
pullBlob(manifest: BlobManifest): Promise<ReceiveBlobResult>;
|
|
184
|
+
/** Read fully-assembled blob bytes from this device's store, or null if absent. */
|
|
185
|
+
getBlobBytes(hash: string): Promise<Uint8Array | null>;
|
|
186
|
+
/**
|
|
187
|
+
* Get the device's version vector.
|
|
188
|
+
*/
|
|
189
|
+
getVersionVector(): VersionVector;
|
|
190
|
+
/**
|
|
191
|
+
* Check if the device is currently connected to the server.
|
|
192
|
+
*/
|
|
193
|
+
isConnected(): boolean;
|
|
194
|
+
/**
|
|
195
|
+
* Close the device, releasing all resources.
|
|
196
|
+
*/
|
|
197
|
+
close(): Promise<void>;
|
|
198
|
+
/**
|
|
199
|
+
* Wait for in-flight sync operations to settle.
|
|
200
|
+
* In-memory transports are near-synchronous, but apply/relay work is async.
|
|
201
|
+
*/
|
|
202
|
+
private waitForSettled;
|
|
203
|
+
/**
|
|
204
|
+
* Wait for all pending outbound operations to be acknowledged.
|
|
205
|
+
*/
|
|
206
|
+
private waitForPendingOps;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* A linked client/server transport pair, as consumed by {@link TestDevice}.
|
|
211
|
+
*/
|
|
212
|
+
interface TransportPair {
|
|
213
|
+
client: SyncTransport;
|
|
214
|
+
serverTransport: ServerTransport;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Wraps a transport pair so that every sync message exchanged between the two
|
|
218
|
+
* ends is encoded to protobuf bytes and decoded back — a faithful stand-in for
|
|
219
|
+
* a real network hop that uses the protobuf wire format.
|
|
220
|
+
*
|
|
221
|
+
* Both directions are wrapped, so operations, handshakes, batches, and acks all
|
|
222
|
+
* travel as bytes rather than shared object references.
|
|
223
|
+
*
|
|
224
|
+
* @param pair - The underlying (in-memory) transport pair to wrap
|
|
225
|
+
* @returns A transport pair that routes messages through the protobuf codec
|
|
226
|
+
*/
|
|
227
|
+
declare function wrapTransportPairWithProtobufWire(pair: TransportPair): TransportPair;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* In-memory test server wrapping KoraSyncServer with MemoryServerStore.
|
|
231
|
+
* Handles client connections via memory transports.
|
|
232
|
+
*/
|
|
233
|
+
interface TestServerOptions<S extends ServerStore = MemoryServerStore> {
|
|
234
|
+
/**
|
|
235
|
+
* The server store (SQLite, Postgres, a memory store with options). Defaults to a
|
|
236
|
+
* fresh {@link MemoryServerStore}. Await {@link TestServer.ready} before use: the
|
|
237
|
+
* schema is set asynchronously.
|
|
238
|
+
*/
|
|
239
|
+
store?: S;
|
|
240
|
+
/** End-to-end encryption policy of the sync server (protocol v2). */
|
|
241
|
+
encryption?: KoraSyncServerConfig['encryption'];
|
|
242
|
+
/** Handshake schema version advertised by the server. Defaults to `schema.version`. */
|
|
243
|
+
schemaVersion?: number;
|
|
244
|
+
/** Inclusive client schema versions accepted at handshake. */
|
|
245
|
+
supportedSchemaVersions?: {
|
|
246
|
+
min: number;
|
|
247
|
+
max: number;
|
|
248
|
+
};
|
|
249
|
+
/** Transform accepted legacy operations into the server schema before validation. */
|
|
250
|
+
operationTransforms?: OperationTransform[];
|
|
251
|
+
/** Enable central blob storage: the server persists and serves uploaded blob bytes. */
|
|
252
|
+
blobStorage?: boolean;
|
|
253
|
+
/** Adjudicate untrusted client operations before materialization. */
|
|
254
|
+
validateOperation?: OperationValidator;
|
|
255
|
+
}
|
|
256
|
+
declare class TestServer<S extends ServerStore = MemoryServerStore> {
|
|
257
|
+
readonly store: S;
|
|
258
|
+
/** Resolves once the store has the schema (needed before a SQL store is used). */
|
|
259
|
+
readonly ready: Promise<void>;
|
|
260
|
+
/** The server's central blob store, present when `blobStorage` was enabled. */
|
|
261
|
+
readonly blobStore: ContentAddressedBlobStore | null;
|
|
262
|
+
private readonly syncServer;
|
|
263
|
+
constructor(schema: SchemaDefinition, options?: TestServerOptions<S>);
|
|
264
|
+
/**
|
|
265
|
+
* Register a client connection transport with the server.
|
|
266
|
+
* Returns the session ID assigned by the server.
|
|
267
|
+
*/
|
|
268
|
+
handleConnection(transport: ServerTransport): string;
|
|
269
|
+
/**
|
|
270
|
+
* Get all operations stored on the server.
|
|
271
|
+
*/
|
|
272
|
+
getAllOperations(): Operation[];
|
|
273
|
+
/** The trusted server-side write API (route context): writes authored by the server node. */
|
|
274
|
+
getKoraContext(): ReturnType<KoraSyncServer['getKoraContext']>;
|
|
275
|
+
/** The node ids the server's stores fold with and advertise in the handshake. */
|
|
276
|
+
get authoritativeNodeIds(): string[];
|
|
277
|
+
/**
|
|
278
|
+
* Get the number of connected clients.
|
|
279
|
+
*/
|
|
280
|
+
getConnectionCount(): number;
|
|
281
|
+
/**
|
|
282
|
+
* Retransmit relay batches connected clients have not acknowledged. Lets a test
|
|
283
|
+
* deterministically trigger the redelivery that the server also runs on a timer,
|
|
284
|
+
* without depending on wall-clock time. `staleMs` of 0 resends every pending relay.
|
|
285
|
+
*/
|
|
286
|
+
retransmitPendingRelays(staleMs?: number): void;
|
|
287
|
+
/** Every blob reference still reachable from live records on the server. */
|
|
288
|
+
getLiveBlobRefs(): Promise<BlobRef[]>;
|
|
289
|
+
/**
|
|
290
|
+
* Shut down the server and close all sessions.
|
|
291
|
+
*/
|
|
292
|
+
close(): Promise<void>;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Options for creating a test network.
|
|
297
|
+
*/
|
|
298
|
+
interface TestNetworkOptions {
|
|
299
|
+
/** Number of devices to create. Defaults to 2. */
|
|
300
|
+
devices?: number;
|
|
301
|
+
/** Custom device names. If not provided, uses 'device-0', 'device-1', etc. */
|
|
302
|
+
deviceNames?: string[];
|
|
303
|
+
/** Optional chaos transport settings applied to each device link. */
|
|
304
|
+
chaos?: ChaosConfig;
|
|
305
|
+
/**
|
|
306
|
+
* Optional wrapper applied to each device's transport pair after the base
|
|
307
|
+
* (in-memory, optionally chaos-wrapped) pair is created. Use it to route
|
|
308
|
+
* messages through the protobuf wire codec
|
|
309
|
+
* ({@link wrapTransportPairWithProtobufWire}) or to inject a controllable
|
|
310
|
+
* server clock. Called once per device connection.
|
|
311
|
+
*/
|
|
312
|
+
wrapTransport?: (pair: TransportPair) => TransportPair;
|
|
313
|
+
/** Enable central blob storage on the server (persist + serve uploaded blob bytes). */
|
|
314
|
+
blobStorage?: boolean;
|
|
315
|
+
/** Adjudicate untrusted client operations on the server before materialization. */
|
|
316
|
+
validateOperation?: _korajs_server.OperationValidator;
|
|
317
|
+
/** Devices use the beta.12 pairwise pipeline (`experimental.legacyMerge`). */
|
|
318
|
+
legacyMerge?: boolean;
|
|
319
|
+
/**
|
|
320
|
+
* The server's store (SQLite, Postgres). Defaults to a fresh memory store. The
|
|
321
|
+
* network closes it.
|
|
322
|
+
*/
|
|
323
|
+
serverStore?: _korajs_server.ServerStore;
|
|
324
|
+
/** Every device encrypts its sync traffic with this shared key material. */
|
|
325
|
+
encryption?: TestDeviceEncryption;
|
|
326
|
+
/** The sync server's encryption policy. */
|
|
327
|
+
serverEncryption?: _korajs_server.KoraSyncServerConfig['encryption'];
|
|
328
|
+
/** Every device store's local `maxOperationBytes` (RT-86). */
|
|
329
|
+
deviceMaxOperationBytes?: number;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* A test network with a server and multiple devices.
|
|
333
|
+
*/
|
|
334
|
+
interface TestNetwork {
|
|
335
|
+
/** The test server */
|
|
336
|
+
server: TestServer<_korajs_server.ServerStore>;
|
|
337
|
+
/** All devices in the network */
|
|
338
|
+
devices: TestDevice[];
|
|
339
|
+
/** Temporary directory for DB files */
|
|
340
|
+
tmpDir: string;
|
|
341
|
+
/** Close all devices and the server, clean up temp files */
|
|
342
|
+
close(): Promise<void>;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Create a test network with a server and multiple virtual devices.
|
|
346
|
+
*
|
|
347
|
+
* Each device has its own local SQLite store and SyncEngine. Devices
|
|
348
|
+
* communicate with the server via in-memory transports.
|
|
349
|
+
*
|
|
350
|
+
* @param schema - The schema all devices share
|
|
351
|
+
* @param options - Network configuration
|
|
352
|
+
* @returns A test network with server and devices ready for use
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* ```typescript
|
|
356
|
+
* const network = await createTestNetwork(schema, { devices: 2 })
|
|
357
|
+
* const [deviceA, deviceB] = network.devices
|
|
358
|
+
*
|
|
359
|
+
* await deviceA.collection('todos').insert({ title: 'Hello' })
|
|
360
|
+
* await deviceA.sync()
|
|
361
|
+
* await deviceB.sync()
|
|
362
|
+
*
|
|
363
|
+
* const todos = await deviceB.getState('todos')
|
|
364
|
+
* expect(todos).toHaveLength(1)
|
|
365
|
+
*
|
|
366
|
+
* await network.close()
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
declare function createTestNetwork(schema: SchemaDefinition, options?: TestNetworkOptions): Promise<TestNetwork>;
|
|
370
|
+
/**
|
|
371
|
+
* Per-device configuration when devices use different local schemas or sync settings.
|
|
372
|
+
*/
|
|
373
|
+
interface MixedTestDeviceConfig {
|
|
374
|
+
name: string;
|
|
375
|
+
schema: SchemaDefinition;
|
|
376
|
+
syncSchemaVersion?: number;
|
|
377
|
+
operationTransforms?: OperationTransform[];
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Create a test network where devices may use different schema versions and transforms.
|
|
381
|
+
* The server uses `serverSchema` for materialization and handshake bounds.
|
|
382
|
+
*/
|
|
383
|
+
declare function createMixedTestNetwork(serverSchema: SchemaDefinition, serverOptions: TestServerOptions, deviceConfigs: MixedTestDeviceConfig[]): Promise<TestNetwork>;
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Result of a convergence check.
|
|
387
|
+
*/
|
|
388
|
+
interface ConvergenceResult {
|
|
389
|
+
/** Whether all devices have converged to the same state */
|
|
390
|
+
converged: boolean;
|
|
391
|
+
/** Per-collection comparison details (only populated on failure) */
|
|
392
|
+
differences: CollectionDifference[];
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Describes a difference in a collection between devices.
|
|
396
|
+
*/
|
|
397
|
+
interface CollectionDifference {
|
|
398
|
+
collection: string;
|
|
399
|
+
deviceA: string;
|
|
400
|
+
deviceB: string;
|
|
401
|
+
/** Records present in deviceA but not deviceB */
|
|
402
|
+
missingInB: string[];
|
|
403
|
+
/** Records present in deviceB but not deviceA */
|
|
404
|
+
missingInA: string[];
|
|
405
|
+
/** Records present in both but with different values */
|
|
406
|
+
fieldDifferences: FieldDifference[];
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* A specific field-level difference between two devices.
|
|
410
|
+
*/
|
|
411
|
+
interface FieldDifference {
|
|
412
|
+
recordId: string;
|
|
413
|
+
field: string;
|
|
414
|
+
valueInA: unknown;
|
|
415
|
+
valueInB: unknown;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Assert that all devices have converged to identical collection states.
|
|
419
|
+
*
|
|
420
|
+
* Compares every collection across all device pairs. Throws an error
|
|
421
|
+
* with detailed diagnostics if any differences are found.
|
|
422
|
+
*
|
|
423
|
+
* @param devices - The devices to check for convergence
|
|
424
|
+
* @param schema - The schema (used to enumerate collections)
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* ```typescript
|
|
428
|
+
* await deviceA.sync()
|
|
429
|
+
* await deviceB.sync()
|
|
430
|
+
* await expectConverged([deviceA, deviceB], schema)
|
|
431
|
+
* ```
|
|
432
|
+
*/
|
|
433
|
+
declare function expectConverged(devices: TestDevice[], schema: SchemaDefinition): Promise<void>;
|
|
434
|
+
/**
|
|
435
|
+
* Poll until all devices converge or timeout. Use after sync in integration tests
|
|
436
|
+
* where relay/apply work may still be in flight under parallel CI load.
|
|
437
|
+
*/
|
|
438
|
+
declare function expectConvergedEventually(devices: TestDevice[], schema: SchemaDefinition, options?: {
|
|
439
|
+
timeoutMs?: number;
|
|
440
|
+
intervalMs?: number;
|
|
441
|
+
}): Promise<void>;
|
|
442
|
+
/**
|
|
443
|
+
* Check whether all devices have converged without throwing.
|
|
444
|
+
*
|
|
445
|
+
* @param devices - The devices to check
|
|
446
|
+
* @param schema - The schema (for collection enumeration)
|
|
447
|
+
* @returns Convergence result with details
|
|
448
|
+
*/
|
|
449
|
+
declare function checkConvergence(devices: TestDevice[], schema: SchemaDefinition): Promise<ConvergenceResult>;
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Wraps a transport pair so the server side reports `serverNow()` as its
|
|
453
|
+
* handshake wall-clock time.
|
|
454
|
+
*
|
|
455
|
+
* @param pair - The underlying transport pair
|
|
456
|
+
* @param serverNow - Returns the server's true current time (ms since epoch)
|
|
457
|
+
* @returns A transport pair whose handshake responses carry the injected time
|
|
458
|
+
*/
|
|
459
|
+
declare function wrapTransportPairWithServerClock(pair: TransportPair, serverNow: () => number): TransportPair;
|
|
460
|
+
|
|
461
|
+
/** Controllable auth binding for lifecycle and authenticated-only sync tests. */
|
|
462
|
+
interface TestAuthBinding extends AuthSyncBinding {
|
|
463
|
+
readonly state: AuthSyncState;
|
|
464
|
+
setLoading(): void;
|
|
465
|
+
signIn(userId: string, token?: string): void;
|
|
466
|
+
refreshToken(token?: string): void;
|
|
467
|
+
signOut(): void;
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Create a deterministic auth binding that can transition between loading,
|
|
471
|
+
* signed-out, and multiple authenticated users without a real auth server.
|
|
472
|
+
*/
|
|
473
|
+
declare function createTestAuthBinding(options?: {
|
|
474
|
+
initialUserId?: string;
|
|
475
|
+
anonymous?: boolean;
|
|
476
|
+
}): TestAuthBinding;
|
|
477
|
+
|
|
478
|
+
interface TestTransferMetrics {
|
|
479
|
+
connectionAttempts: number;
|
|
480
|
+
disconnections: number;
|
|
481
|
+
clientMessages: number;
|
|
482
|
+
serverMessages: number;
|
|
483
|
+
clientBytes: number;
|
|
484
|
+
serverBytes: number;
|
|
485
|
+
reset(): void;
|
|
486
|
+
}
|
|
487
|
+
/** Create a transport wrapper and counters for asserting reconnect and transfer cost. */
|
|
488
|
+
declare function createTransportMetrics(): {
|
|
489
|
+
metrics: TestTransferMetrics;
|
|
490
|
+
wrapTransport(pair: TransportPair): TransportPair;
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
export { type CollectionDifference, type ConvergenceResult, type FieldDifference, type MixedTestDeviceConfig, type TestAuthBinding, TestDevice, type TestDeviceEncryption, type TestDeviceOptions, type TestDeviceServer, type TestNetwork, type TestNetworkOptions, TestServer, type TestServerOptions, type TestTransferMetrics, type TransportPair, checkConvergence, createMixedTestNetwork, createTestAuthBinding, createTestNetwork, createTransportMetrics, expectConverged, expectConvergedEventually, wrapTransportPairWithProtobufWire, wrapTransportPairWithServerClock };
|