@firtoz/collection-sync 1.0.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/package.json +82 -0
- package/src/cache-manager.ts +208 -0
- package/src/connect-partial-sync.ts +349 -0
- package/src/connect-sync.ts +82 -0
- package/src/create-partial-synced-collection.ts +32 -0
- package/src/create-synced-collection.ts +32 -0
- package/src/index.ts +119 -0
- package/src/partial-sync-client-bridge.ts +1025 -0
- package/src/partial-sync-interest.ts +200 -0
- package/src/partial-sync-mutation-handler.ts +152 -0
- package/src/partial-sync-predicate-match.ts +65 -0
- package/src/partial-sync-row-key.ts +57 -0
- package/src/partial-sync-server-bridge.ts +859 -0
- package/src/react/constants.ts +11 -0
- package/src/react/index.ts +50 -0
- package/src/react/partial-sync-adapter.ts +73 -0
- package/src/react/partial-sync-utils.ts +115 -0
- package/src/react/range-conditions-expression.ts +70 -0
- package/src/react/types.ts +232 -0
- package/src/react/usePartialSyncCollection.ts +140 -0
- package/src/react/usePartialSyncViewport.ts +230 -0
- package/src/react/usePartialSyncWindow.ts +807 -0
- package/src/react/usePredicateFilteredRows.ts +169 -0
- package/src/sync-client-bridge.ts +458 -0
- package/src/sync-protocol.ts +362 -0
- package/src/sync-server-bridge.ts +267 -0
- package/src/with-sync.ts +368 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ZodWebSocketClient } from "@firtoz/websocket-do/zod-client";
|
|
2
|
+
import type { SyncClientBridge } from "./sync-client-bridge";
|
|
3
|
+
import type { PartialSyncRowShape } from "./partial-sync-row-key";
|
|
4
|
+
import {
|
|
5
|
+
createClientMessageSchema,
|
|
6
|
+
createServerMessageSchema,
|
|
7
|
+
type SyncClientMessage,
|
|
8
|
+
type SyncServerMessage,
|
|
9
|
+
} from "./sync-protocol";
|
|
10
|
+
|
|
11
|
+
export type ConnectSyncTransport = "json" | "msgpack";
|
|
12
|
+
|
|
13
|
+
export type ConnectSyncOptions<TItem = unknown> = {
|
|
14
|
+
/** WebSocket URL (e.g. `wss://host/room/x/websocket`). */
|
|
15
|
+
url: string;
|
|
16
|
+
transport?: ConnectSyncTransport;
|
|
17
|
+
/** Prefer a module-level function or `useCallback`; a new inline function each render can churn effects. */
|
|
18
|
+
serializeJson?: (value: unknown) => string;
|
|
19
|
+
/** Prefer a module-level function or `useCallback`; a new inline function each render can churn effects. */
|
|
20
|
+
deserializeJson?: (raw: string) => unknown;
|
|
21
|
+
/** Wire transport after {@link SyncClientBridge} is created (from {@link withSync}). */
|
|
22
|
+
setTransportSend: (send: (msg: SyncClientMessage) => void) => void;
|
|
23
|
+
/** Optional tap before messages reach the bridge (e.g. debug / inspector). */
|
|
24
|
+
onServerMessage?: (msg: SyncServerMessage<TItem>) => void;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Connects a {@link SyncClientBridge} to a WebSocket using the same codec as {@link ZodSession} on the server.
|
|
29
|
+
*/
|
|
30
|
+
export function connectSync<TItem extends PartialSyncRowShape>(
|
|
31
|
+
bridge: SyncClientBridge<TItem>,
|
|
32
|
+
options: ConnectSyncOptions<TItem>,
|
|
33
|
+
): () => void {
|
|
34
|
+
const clientSchema = createClientMessageSchema();
|
|
35
|
+
const serverSchema = createServerMessageSchema<TItem>();
|
|
36
|
+
const useMsgpack = options.transport === "msgpack";
|
|
37
|
+
|
|
38
|
+
const zodClient = new ZodWebSocketClient({
|
|
39
|
+
url: options.url,
|
|
40
|
+
clientSchema,
|
|
41
|
+
serverSchema,
|
|
42
|
+
enableBufferMessages: useMsgpack,
|
|
43
|
+
...(useMsgpack
|
|
44
|
+
? {}
|
|
45
|
+
: {
|
|
46
|
+
serializeJson: options.serializeJson ?? JSON.stringify,
|
|
47
|
+
deserializeJson: options.deserializeJson ?? JSON.parse,
|
|
48
|
+
}),
|
|
49
|
+
onMessage: (msg) => {
|
|
50
|
+
options.onServerMessage?.(msg);
|
|
51
|
+
void bridge.handleServerMessage(msg);
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
options.setTransportSend((message) => {
|
|
56
|
+
zodClient.send(message);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
bridge.setConnected(false);
|
|
60
|
+
|
|
61
|
+
const onOpen = () => {
|
|
62
|
+
bridge.setConnected(true);
|
|
63
|
+
};
|
|
64
|
+
const onClose = () => {
|
|
65
|
+
bridge.setConnected(false);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
zodClient.socket.addEventListener("open", onOpen);
|
|
69
|
+
zodClient.socket.addEventListener("close", onClose);
|
|
70
|
+
|
|
71
|
+
if (zodClient.socket.readyState === WebSocket.OPEN) {
|
|
72
|
+
onOpen();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return () => {
|
|
76
|
+
zodClient.socket.removeEventListener("open", onOpen);
|
|
77
|
+
zodClient.socket.removeEventListener("close", onClose);
|
|
78
|
+
bridge.setConnected(false);
|
|
79
|
+
options.setTransportSend(() => {});
|
|
80
|
+
zodClient.close();
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Collection } from "@tanstack/db";
|
|
2
|
+
import { createCollection } from "@tanstack/db";
|
|
3
|
+
import {
|
|
4
|
+
withSync,
|
|
5
|
+
type AnyWithSyncableCollectionConfig,
|
|
6
|
+
type InferItemFromCollectionOptions,
|
|
7
|
+
type WithSyncOptions,
|
|
8
|
+
} from "./with-sync";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Like {@link createSyncedCollection}, but defaults `sendSyncHelloOnConnect` to `false` and
|
|
12
|
+
* `forwardTruncateToMutations` to `false` for use with {@link connectPartialSync} +
|
|
13
|
+
* {@link usePartialSyncWindow} (`mutateBatch` without `syncHello`). Local window resets call
|
|
14
|
+
* `truncate()` on the collection; those must not be sent to the server or they can batch with user
|
|
15
|
+
* edits and wipe authoritative data.
|
|
16
|
+
*/
|
|
17
|
+
export function createPartialSyncedCollection<
|
|
18
|
+
TConfig extends AnyWithSyncableCollectionConfig,
|
|
19
|
+
>(baseOptions: TConfig, syncOptions?: WithSyncOptions) {
|
|
20
|
+
type TItem = InferItemFromCollectionOptions<TConfig>;
|
|
21
|
+
const { options, bridge, setTransportSend } = withSync(baseOptions, {
|
|
22
|
+
...syncOptions,
|
|
23
|
+
sendSyncHelloOnConnect: syncOptions?.sendSyncHelloOnConnect ?? false,
|
|
24
|
+
forwardTruncateToMutations:
|
|
25
|
+
syncOptions?.forwardTruncateToMutations ?? false,
|
|
26
|
+
});
|
|
27
|
+
const collection = createCollection(
|
|
28
|
+
options as never,
|
|
29
|
+
) as unknown as Collection<TItem>;
|
|
30
|
+
bridge.setRowGet((key) => collection.get(key));
|
|
31
|
+
return { collection, bridge, setTransportSend };
|
|
32
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Collection } from "@tanstack/db";
|
|
2
|
+
import { createCollection } from "@tanstack/db";
|
|
3
|
+
import {
|
|
4
|
+
withSync,
|
|
5
|
+
type AnyWithSyncableCollectionConfig,
|
|
6
|
+
type InferItemFromCollectionOptions,
|
|
7
|
+
type WithSyncOptions,
|
|
8
|
+
} from "./with-sync";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Builds a {@link withSync} wrapper and a TanStack {@link createCollection} in one step.
|
|
12
|
+
* Row type is inferred from the collection config (see {@link InferItemFromCollectionOptions}).
|
|
13
|
+
* (The internal `as unknown as Collection<TItem>` bridges `createCollection`'s overloads to the inferred row type.)
|
|
14
|
+
*
|
|
15
|
+
* Pass `syncOptions` for durable backends (IndexedDB, sqlite-wasm): use `syncStateKey` so incremental
|
|
16
|
+
* reconnect works. Omit `syncOptions` (or omit `syncStateKey`) for in-memory collections that need a
|
|
17
|
+
* full snapshot every load — see the module doc on {@link withSync} / `with-sync.ts`.
|
|
18
|
+
*/
|
|
19
|
+
export function createSyncedCollection<
|
|
20
|
+
TConfig extends AnyWithSyncableCollectionConfig,
|
|
21
|
+
>(baseOptions: TConfig, syncOptions?: WithSyncOptions) {
|
|
22
|
+
type TItem = InferItemFromCollectionOptions<TConfig>;
|
|
23
|
+
const { options, bridge, setTransportSend } = withSync(
|
|
24
|
+
baseOptions,
|
|
25
|
+
syncOptions,
|
|
26
|
+
);
|
|
27
|
+
const collection = createCollection(
|
|
28
|
+
options as never,
|
|
29
|
+
) as unknown as Collection<TItem>;
|
|
30
|
+
bridge.setRowGet((key) => collection.get(key));
|
|
31
|
+
return { collection, bridge, setTransportSend };
|
|
32
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export {
|
|
2
|
+
DEFAULT_SYNC_COLLECTION_ID,
|
|
3
|
+
createClientMessageSchema,
|
|
4
|
+
createServerMessageSchema,
|
|
5
|
+
clientMessageSchema,
|
|
6
|
+
serverMessageSchema,
|
|
7
|
+
mutationIntentSchema,
|
|
8
|
+
syncRangeSchema,
|
|
9
|
+
toSyncMessage,
|
|
10
|
+
createClientMutationId,
|
|
11
|
+
type MutationIntent,
|
|
12
|
+
type IndexRangeCursor,
|
|
13
|
+
type IndexRangeOffset,
|
|
14
|
+
type PredicateRange,
|
|
15
|
+
type RangeCondition,
|
|
16
|
+
type RangeConditionOp,
|
|
17
|
+
type RangeFingerprint,
|
|
18
|
+
type SyncBackfillMode,
|
|
19
|
+
type SyncClientMessage,
|
|
20
|
+
type SyncClientMessageBody,
|
|
21
|
+
type SyncRange,
|
|
22
|
+
type SyncRangeSort,
|
|
23
|
+
type SyncServerMessage,
|
|
24
|
+
type SyncServerMessageBody,
|
|
25
|
+
type SyncSortDirection,
|
|
26
|
+
} from "./sync-protocol";
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
SyncClientBridge,
|
|
30
|
+
type SyncClientBridgeOptions,
|
|
31
|
+
} from "./sync-client-bridge";
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
PartialSyncClientBridge,
|
|
35
|
+
type PartialSyncClientBridgeOptions,
|
|
36
|
+
type PartialSyncRangePatchAppliedEvent,
|
|
37
|
+
type PartialSyncRangeResult,
|
|
38
|
+
type PartialSyncReconcileResult,
|
|
39
|
+
type PartialSyncState,
|
|
40
|
+
type PartialSyncViewTransitionEvent,
|
|
41
|
+
} from "./partial-sync-client-bridge";
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
CacheManager,
|
|
45
|
+
type CacheEntry,
|
|
46
|
+
type CacheManagerOptions,
|
|
47
|
+
type CacheStorageEstimate,
|
|
48
|
+
type CacheViewport,
|
|
49
|
+
} from "./cache-manager";
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
SyncServerBridge,
|
|
53
|
+
type SyncServerBridgeStore,
|
|
54
|
+
type SyncServerBridgeOptions,
|
|
55
|
+
} from "./sync-server-bridge";
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
PartialSyncServerBridge,
|
|
59
|
+
type ClientQueryState,
|
|
60
|
+
type DeliveredRange,
|
|
61
|
+
type PartialSyncPushServerChangesOptions,
|
|
62
|
+
type PartialSyncServerBridgeOptions,
|
|
63
|
+
type PartialSyncServerBridgeStore,
|
|
64
|
+
} from "./partial-sync-server-bridge";
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
PartialSyncMutationHandler,
|
|
68
|
+
type PartialSyncMutationHandlerOptions,
|
|
69
|
+
type PartialSyncMutationHandlerStore,
|
|
70
|
+
} from "./partial-sync-mutation-handler";
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
withSync,
|
|
74
|
+
getBrowserLocalStorageSyncStateStorage,
|
|
75
|
+
type AnyWithSyncableCollectionConfig,
|
|
76
|
+
type InferItemFromCollectionOptions,
|
|
77
|
+
type SyncStateStorage,
|
|
78
|
+
type SyncableCollectionItem,
|
|
79
|
+
type WithSyncOptions,
|
|
80
|
+
type WithSyncableCollectionConfig,
|
|
81
|
+
} from "./with-sync";
|
|
82
|
+
|
|
83
|
+
export { createSyncedCollection } from "./create-synced-collection";
|
|
84
|
+
|
|
85
|
+
export { createPartialSyncedCollection } from "./create-partial-synced-collection";
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
connectSync,
|
|
89
|
+
type ConnectSyncOptions,
|
|
90
|
+
type ConnectSyncTransport,
|
|
91
|
+
} from "./connect-sync";
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
connectPartialSync,
|
|
95
|
+
type ConnectPartialSyncOptions,
|
|
96
|
+
type ConnectPartialSyncTransport,
|
|
97
|
+
} from "./connect-partial-sync";
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
partialSyncRowKey,
|
|
101
|
+
partialSyncRowVersionWatermarkMs,
|
|
102
|
+
partialSyncRowVersionWatermarkMsUnknown,
|
|
103
|
+
type PartialSyncRowId,
|
|
104
|
+
type PartialSyncRowRef,
|
|
105
|
+
type PartialSyncRowShape,
|
|
106
|
+
type PartialSyncRowVersion,
|
|
107
|
+
} from "./partial-sync-row-key";
|
|
108
|
+
|
|
109
|
+
export type {
|
|
110
|
+
PartialSyncPatchResult,
|
|
111
|
+
PartialSyncViewTransition,
|
|
112
|
+
} from "./partial-sync-interest";
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
compareInterestValues,
|
|
116
|
+
filterSyncMessagesForPredicateRange,
|
|
117
|
+
rowMatchesClientInterest,
|
|
118
|
+
type ClassifyPartialSyncRangePatchOptions,
|
|
119
|
+
} from "./partial-sync-interest";
|