@sync-subscribe/client 0.3.2

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.
@@ -0,0 +1,131 @@
1
+ import type { SyncRecord, SyncPatch, SyncToken, SubscriptionFilter, Subscription, StreamEvent } from "@sync-subscribe/core";
2
+ export type SubscriptionStatus = "pending_gap_fill" | "active";
3
+ export interface ClientSubscriptionOptions {
4
+ filter: SubscriptionFilter;
5
+ /** Stable client-side name for this subscription. Used to persist and restore state across sessions. */
6
+ name?: string;
7
+ /**
8
+ * If provided, the new subscription replaces the existing one with this ID.
9
+ * Used locally only — no server call is made. Triggers gap/eviction analysis.
10
+ */
11
+ previousSubscriptionId?: string;
12
+ }
13
+ export interface ClientSubscription extends Subscription {
14
+ /** Stable client-side name, if one was given when subscribing. */
15
+ name?: string;
16
+ status?: SubscriptionStatus;
17
+ gapSubscriptionId?: string;
18
+ }
19
+ /** Subscription state persisted to the local store, keyed by name. */
20
+ export interface PersistedSubscription extends Subscription {
21
+ name?: string;
22
+ /**
23
+ * Omitted or "active" — subscription participates in pull and stream normally.
24
+ * "pending_gap_fill" — a gap sub is in progress; only the gap sub is pulled,
25
+ * and this subscription is excluded from the stream until it transitions to active.
26
+ */
27
+ status?: SubscriptionStatus;
28
+ /**
29
+ * Set when status === "pending_gap_fill". The subscriptionId of the gap subscription
30
+ * that is filling in records not covered by any existing subscription.
31
+ */
32
+ gapSubscriptionId?: string;
33
+ }
34
+ /** One entry sent in a pull or stream request. key is echoed back in syncTokens responses. */
35
+ export interface SyncSubscriptionRequest {
36
+ key: string;
37
+ filter: SubscriptionFilter;
38
+ syncToken: SyncToken;
39
+ }
40
+ /** Minimal HTTP transport interface — swap in fetch, axios, etc. */
41
+ export interface SyncTransport {
42
+ /**
43
+ * Pull patches for all active subscriptions in a single request.
44
+ * Returns deduplicated patches and one sync token per affected subscription key.
45
+ */
46
+ pull(subscriptions: SyncSubscriptionRequest[]): Promise<{
47
+ patches: SyncPatch<SyncRecord>[];
48
+ syncTokens: Record<string, SyncToken>;
49
+ }>;
50
+ /**
51
+ * Push locally-mutated records to the server.
52
+ * Returns serverUpdatedAt on success so the client can stamp local copies.
53
+ */
54
+ push(records: SyncRecord[]): Promise<{
55
+ ok: true;
56
+ serverUpdatedAt: number;
57
+ } | {
58
+ conflict: true;
59
+ serverRecord: SyncRecord;
60
+ }>;
61
+ /**
62
+ * Optional POST-based SSE streaming for all active subscriptions.
63
+ * Returns a cleanup function that closes the connection.
64
+ */
65
+ stream?(subscriptions: SyncSubscriptionRequest[], onMessage: (event: StreamEvent) => void, onError?: (err: Error) => void): () => void;
66
+ }
67
+ /**
68
+ * Async interface for local record storage.
69
+ * Both InMemoryStore and IdbLocalStore implement this.
70
+ */
71
+ export interface ILocalStore<T extends SyncRecord> {
72
+ /**
73
+ * Apply a batch of patches from the server.
74
+ * On upsert, copies `record.updatedAt` into `record.serverUpdatedAt` before storing
75
+ * (the server's clock is authoritative).
76
+ * Returns the patches that were actually applied (conflict resolution may drop some).
77
+ */
78
+ applyPatches(patches: SyncPatch<T>[]): Promise<SyncPatch<T>[]>;
79
+ write(record: T): Promise<void>;
80
+ getAll(): Promise<T[]>;
81
+ query(filter: SubscriptionFilter<T>): Promise<T[]>;
82
+ count(filter: SubscriptionFilter<T>): Promise<number>;
83
+ delete(fiter: SubscriptionFilter<T>): Promise<void>;
84
+ getById(recordId: string): Promise<T | undefined>;
85
+ /** Remove all records — called by SyncClient.reset(). */
86
+ clear(): Promise<void>;
87
+ /**
88
+ * Removes items from our local store that match the filter.
89
+ * Does not delete them from other stores/devices.
90
+ */
91
+ evict(evictFilter: SubscriptionFilter<T>): Promise<void>;
92
+ /**
93
+ * Scan local records matching filter, find the max (serverUpdatedAt, revisionCount, recordId),
94
+ * and return encodeSyncToken(that record). Returns EMPTY_SYNC_TOKEN if no records have
95
+ * serverUpdatedAt set (i.e. none have been confirmed by the server yet).
96
+ */
97
+ reconstructSyncToken(filter: SubscriptionFilter<T>): Promise<SyncToken>;
98
+ /**
99
+ * Stamp a server-authoritative timestamp on a local record after a successful push.
100
+ * This keeps reconstructSyncToken accurate without a full record rewrite.
101
+ */
102
+ setServerUpdatedAt(recordId: string, serverUpdatedAt: number): Promise<void>;
103
+ /** Persist the latest sync token for an unnamed subscription across sessions. */
104
+ setSyncToken(subscriptionId: string, token: SyncToken): Promise<void>;
105
+ getSyncToken(subscriptionId: string): Promise<SyncToken | undefined>;
106
+ /** Persist full subscription state under a stable client-side name (or subscriptionId). */
107
+ setSubscription(name: string, sub: PersistedSubscription): Promise<void>;
108
+ getSubscription(name: string): Promise<PersistedSubscription | undefined>;
109
+ getSubscriptionById(id: string): Promise<PersistedSubscription | undefined>;
110
+ removeSubscription(name: string): Promise<void>;
111
+ listSubscriptions(): Promise<PersistedSubscription[]>;
112
+ /** Remove all persisted subscriptions — called by SyncClient.reset(). */
113
+ clearSubscriptions(): Promise<void>;
114
+ }
115
+ /** Called whenever the local store changes due to incoming patches. */
116
+ export type PatchListener<T extends SyncRecord> = (patches: SyncPatch<T>[]) => void;
117
+ export interface QueryEntries<T extends SyncRecord> {
118
+ data: T[];
119
+ loading: boolean;
120
+ }
121
+ /**
122
+ * A reactive handle to a filtered view of the local store.
123
+ * Follows the Svelte store contract — subscribe(run) returns unsubscribe.
124
+ *
125
+ * `loading` is true until the first local read (or first pull for liveQuery) completes.
126
+ * Compatible with Svelte's `$store` syntax and React's `useSyncExternalStore`.
127
+ */
128
+ export interface SyncQuery<T extends SyncRecord> {
129
+ subscribe(run: (value: QueryEntries<T>) => void, invalidate?: () => void): () => void;
130
+ }
131
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@sync-subscribe/client",
3
+ "version": "0.3.2",
4
+ "type": "module",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "license": "MIT",
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "import": "./dist/index.js",
19
+ "types": "./dist/index.d.ts"
20
+ }
21
+ },
22
+ "dependencies": {
23
+ "@sync-subscribe/core": "0.3.2"
24
+ },
25
+ "devDependencies": {
26
+ "typescript": "*",
27
+ "vitest": "*"
28
+ },
29
+ "scripts": {
30
+ "build": "tsc --build",
31
+ "dev": "tsc --build --watch",
32
+ "typecheck": "tsc --noEmit",
33
+ "test": "vitest run",
34
+ "clean": "rm -rf dist *.tsbuildinfo"
35
+ }
36
+ }