@softwareseva/sync 0.1.0 → 0.2.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.
@@ -0,0 +1,82 @@
1
+ /** @softwareseva/sync/client — offline-first push/pull against a syncRouter, backed by RxDB for local durability. Mirrors kashi_sync's Dart engine: an outbox of ops and a pull cursor, both owned by the engine inside the app's RxDB database. */
2
+ import type { RxDatabase } from "rxdb";
3
+ import type { ApiClient } from "@softwareseva/core/client";
4
+ export type LocalSyncEntity = {
5
+ /** Server rows -> local rows. Server wins; the engine skips rows whose id has a pending outbox op. */
6
+ upsert: (rows: Array<Record<string, unknown>>) => Promise<void>;
7
+ delete: (ids: string[]) => Promise<void>;
8
+ clear: () => Promise<void>;
9
+ };
10
+ export type SyncPhase = "idle" | "syncing" | "offline" | "failed";
11
+ export type SyncStatus = {
12
+ phase: SyncPhase;
13
+ pending: number;
14
+ needsAttention: number;
15
+ lastSyncAt: string | null;
16
+ message?: string;
17
+ };
18
+ export type SyncEngineOptions = {
19
+ /** The app's own RxDB database; the engine adds its `kashi_outbox` and `kashi_sync_state` collections to it. */
20
+ db: RxDatabase;
21
+ /** The same client used for the rest of the app; `/sync/push` and `/sync/pull` are appended to its base path. */
22
+ api: ApiClient;
23
+ entities: Record<string, LocalSyncEntity>;
24
+ /** How often to sync while started (default 5 minutes). */
25
+ intervalMs?: number;
26
+ /** Debounce after `enqueue()` before syncing (default 400ms). */
27
+ debounceMs?: number;
28
+ maxAttempts?: number;
29
+ pageSize?: number;
30
+ };
31
+ /** `sync()`: push due ops in batches of 50, then pull pages until `hasMore` is false. Concurrent calls share one run. */
32
+ export declare class SyncEngine {
33
+ private readonly db;
34
+ private readonly api;
35
+ private readonly entities;
36
+ private readonly intervalMs;
37
+ private readonly debounceMs;
38
+ private readonly maxAttempts;
39
+ private readonly pageSize;
40
+ private outbox;
41
+ private cursor;
42
+ private readonly ready;
43
+ private timer;
44
+ private debounceTimer;
45
+ private inFlight;
46
+ private readonly listeners;
47
+ private status;
48
+ constructor(options: SyncEngineOptions);
49
+ private init;
50
+ /** `{subscribe}` shape so it plugs straight into `useSyncExternalStore`. */
51
+ get statusStream(): {
52
+ subscribe: (fn: () => void) => () => void;
53
+ };
54
+ getStatus: () => SyncStatus;
55
+ private setStatus;
56
+ private refreshStatus;
57
+ /** Write the local row first, then enqueue the op — in the same transaction where your storage supports one. */
58
+ enqueue(type: string, payload: unknown, target: {
59
+ entity: string;
60
+ entityId: string;
61
+ }): Promise<void>;
62
+ /** Sync now, every `intervalMs`, and whenever the browser comes back online. */
63
+ start(): void;
64
+ stop(): void;
65
+ private onOnline;
66
+ sync(): Promise<void>;
67
+ private run;
68
+ private push;
69
+ private pull;
70
+ needsAttention(): Promise<Array<{
71
+ opId: string;
72
+ type: string;
73
+ entity: string;
74
+ entityId: string;
75
+ attempts: number;
76
+ }>>;
77
+ retry(opId: string): Promise<void>;
78
+ discard(opId: string): Promise<void>;
79
+ /** Wipe local synced data and the outbox/cursor. Call on sign-out before another user signs in on this device. */
80
+ clearAll(): Promise<void>;
81
+ }
82
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,230 @@
1
+ const outboxSchema = {
2
+ version: 0,
3
+ primaryKey: "opId",
4
+ type: "object",
5
+ properties: {
6
+ opId: { type: "string", maxLength: 64 },
7
+ type: { type: "string" },
8
+ payload: {},
9
+ entity: { type: "string" },
10
+ entityId: { type: "string" },
11
+ status: { type: "string" },
12
+ attempts: { type: "number" },
13
+ nextAttemptAt: { type: "string" },
14
+ createdAt: { type: "string" },
15
+ },
16
+ required: ["opId", "type", "payload", "entity", "entityId", "status", "attempts", "nextAttemptAt", "createdAt"],
17
+ };
18
+ const cursorSchema = {
19
+ version: 0,
20
+ primaryKey: "id",
21
+ type: "object",
22
+ properties: { id: { type: "string", maxLength: 16 }, since: { type: "number" } },
23
+ required: ["id", "since"],
24
+ };
25
+ const CURSOR_ID = "cursor";
26
+ /** `sync()`: push due ops in batches of 50, then pull pages until `hasMore` is false. Concurrent calls share one run. */
27
+ export class SyncEngine {
28
+ db;
29
+ api;
30
+ entities;
31
+ intervalMs;
32
+ debounceMs;
33
+ maxAttempts;
34
+ pageSize;
35
+ outbox = null;
36
+ cursor = null;
37
+ ready;
38
+ timer = null;
39
+ debounceTimer = null;
40
+ inFlight = null;
41
+ listeners = new Set();
42
+ status = { phase: "idle", pending: 0, needsAttention: 0, lastSyncAt: null };
43
+ constructor(options) {
44
+ this.db = options.db;
45
+ this.api = options.api;
46
+ this.entities = options.entities;
47
+ this.intervalMs = options.intervalMs ?? 5 * 60_000;
48
+ this.debounceMs = options.debounceMs ?? 400;
49
+ this.maxAttempts = options.maxAttempts ?? 8;
50
+ this.pageSize = options.pageSize ?? 500;
51
+ this.ready = this.init();
52
+ }
53
+ async init() {
54
+ const existing = this.db.collections;
55
+ const toAdd = {};
56
+ if (!existing.kashi_outbox)
57
+ toAdd.kashi_outbox = { schema: outboxSchema };
58
+ if (!existing.kashi_sync_state)
59
+ toAdd.kashi_sync_state = { schema: cursorSchema };
60
+ if (Object.keys(toAdd).length)
61
+ await this.db.addCollections(toAdd);
62
+ this.outbox = this.db.collections.kashi_outbox;
63
+ this.cursor = this.db.collections.kashi_sync_state;
64
+ await this.refreshStatus();
65
+ }
66
+ /** `{subscribe}` shape so it plugs straight into `useSyncExternalStore`. */
67
+ get statusStream() {
68
+ return { subscribe: (fn) => { this.listeners.add(fn); return () => this.listeners.delete(fn); } };
69
+ }
70
+ getStatus = () => this.status;
71
+ setStatus(patch) {
72
+ this.status = { ...this.status, ...patch };
73
+ for (const listener of this.listeners)
74
+ listener();
75
+ }
76
+ async refreshStatus() {
77
+ const pending = await this.outbox.count({ selector: { status: "pending" } }).exec();
78
+ const needsAttention = await this.outbox.count({ selector: { status: "needs-attention" } }).exec();
79
+ this.setStatus({ pending, needsAttention });
80
+ }
81
+ /** Write the local row first, then enqueue the op — in the same transaction where your storage supports one. */
82
+ async enqueue(type, payload, target) {
83
+ await this.ready;
84
+ await this.outbox.insert({
85
+ opId: newOpId(),
86
+ type,
87
+ payload,
88
+ entity: target.entity,
89
+ entityId: target.entityId,
90
+ status: "pending",
91
+ attempts: 0,
92
+ nextAttemptAt: new Date(0).toISOString(),
93
+ createdAt: new Date().toISOString(),
94
+ });
95
+ await this.refreshStatus();
96
+ if (this.debounceTimer)
97
+ clearTimeout(this.debounceTimer);
98
+ this.debounceTimer = setTimeout(() => void this.sync(), this.debounceMs);
99
+ }
100
+ /** Sync now, every `intervalMs`, and whenever the browser comes back online. */
101
+ start() {
102
+ void this.sync();
103
+ if (this.timer)
104
+ clearInterval(this.timer);
105
+ this.timer = setInterval(() => void this.sync(), this.intervalMs);
106
+ if (typeof window !== "undefined")
107
+ window.addEventListener("online", this.onOnline);
108
+ }
109
+ stop() {
110
+ if (this.timer)
111
+ clearInterval(this.timer);
112
+ this.timer = null;
113
+ if (typeof window !== "undefined")
114
+ window.removeEventListener("online", this.onOnline);
115
+ }
116
+ onOnline = () => void this.sync();
117
+ sync() {
118
+ this.inFlight ??= this.run().finally(() => { this.inFlight = null; });
119
+ return this.inFlight;
120
+ }
121
+ async run() {
122
+ await this.ready;
123
+ if (typeof navigator !== "undefined" && navigator.onLine === false) {
124
+ this.setStatus({ phase: "offline" });
125
+ return;
126
+ }
127
+ this.setStatus({ phase: "syncing" });
128
+ try {
129
+ await this.push();
130
+ await this.pull();
131
+ this.setStatus({ phase: "idle", lastSyncAt: new Date().toISOString() });
132
+ }
133
+ catch (error) {
134
+ this.setStatus({ phase: "failed", message: error instanceof Error ? error.message : "Sync failed." });
135
+ }
136
+ await this.refreshStatus();
137
+ }
138
+ async push() {
139
+ for (;;) {
140
+ const now = new Date().toISOString();
141
+ const due = await this.outbox.find({ selector: { status: "pending", nextAttemptAt: { $lte: now } }, limit: 50 }).exec();
142
+ if (!due.length)
143
+ return;
144
+ const ops = due.map((d) => ({ opId: d.opId, type: d.type, payload: d.payload }));
145
+ const { results } = await this.api.post("/sync/push", { ops });
146
+ for (const result of results) {
147
+ const doc = due.find((d) => d.opId === result.opId);
148
+ if (!doc)
149
+ continue;
150
+ if (result.status === "applied" || result.status === "replayed") {
151
+ await doc.remove();
152
+ continue;
153
+ }
154
+ const attempts = doc.attempts + 1;
155
+ if (result.error?.retryable && attempts < this.maxAttempts) {
156
+ const delay = Math.min(5_000 * 2 ** attempts, 5 * 60_000);
157
+ await doc.incrementalPatch({ attempts, nextAttemptAt: new Date(Date.now() + delay).toISOString() });
158
+ }
159
+ else {
160
+ await doc.incrementalPatch({ attempts, status: "needs-attention" });
161
+ }
162
+ }
163
+ if (due.length < 50)
164
+ return;
165
+ }
166
+ }
167
+ async pull() {
168
+ for (;;) {
169
+ const cursorDoc = await this.cursor.findOne(CURSOR_ID).exec();
170
+ const since = cursorDoc?.since ?? 0;
171
+ const page = await this.api.get(`/sync/pull?since=${since}&limit=${this.pageSize}`);
172
+ if (page.reset) {
173
+ for (const entity of Object.values(this.entities))
174
+ await entity.clear();
175
+ await this.cursor.upsert({ id: CURSOR_ID, since: 0 });
176
+ continue;
177
+ }
178
+ const pendingOps = await this.outbox.find({ selector: { status: "pending" } }).exec();
179
+ const pendingKeys = new Set(pendingOps.map((d) => `${d.entity}:${d.entityId}`));
180
+ for (const [entityName, change] of Object.entries(page.changes)) {
181
+ const entity = this.entities[entityName];
182
+ if (!entity)
183
+ continue;
184
+ const upserts = change.upserts.filter((row) => !pendingKeys.has(`${entityName}:${row.id}`));
185
+ const deletes = change.deletes.filter((id) => !pendingKeys.has(`${entityName}:${id}`));
186
+ if (upserts.length)
187
+ await entity.upsert(upserts);
188
+ if (deletes.length)
189
+ await entity.delete(deletes);
190
+ }
191
+ await this.cursor.upsert({ id: CURSOR_ID, since: page.next });
192
+ if (!page.hasMore)
193
+ return;
194
+ }
195
+ }
196
+ async needsAttention() {
197
+ await this.ready;
198
+ const docs = await this.outbox.find({ selector: { status: "needs-attention" } }).exec();
199
+ return docs.map((d) => ({ opId: d.opId, type: d.type, entity: d.entity, entityId: d.entityId, attempts: d.attempts }));
200
+ }
201
+ async retry(opId) {
202
+ await this.ready;
203
+ const doc = await this.outbox.findOne(opId).exec();
204
+ if (doc)
205
+ await doc.incrementalPatch({ status: "pending", attempts: 0, nextAttemptAt: new Date(0).toISOString() });
206
+ await this.refreshStatus();
207
+ void this.sync();
208
+ }
209
+ async discard(opId) {
210
+ await this.ready;
211
+ const doc = await this.outbox.findOne(opId).exec();
212
+ if (doc)
213
+ await doc.remove();
214
+ await this.refreshStatus();
215
+ }
216
+ /** Wipe local synced data and the outbox/cursor. Call on sign-out before another user signs in on this device. */
217
+ async clearAll() {
218
+ await this.ready;
219
+ this.stop();
220
+ for (const entity of Object.values(this.entities))
221
+ await entity.clear();
222
+ await this.outbox.find().remove();
223
+ await this.cursor.find().remove();
224
+ this.setStatus({ phase: "idle", pending: 0, needsAttention: 0, lastSyncAt: null });
225
+ }
226
+ }
227
+ function newOpId() {
228
+ return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
229
+ }
230
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { RxCollection } from "rxdb";
2
+ import type { SyncEngine, SyncStatus } from "../client/index";
3
+ export type SyncedCollectionOptions<T extends {
4
+ id: string;
5
+ }> = {
6
+ /** Unique id for this collection within the app, e.g. "notes". */
7
+ id: string;
8
+ /** Op type prefix; upserts send `${entity}.upsert`, deletes send `${entity}.delete`. */
9
+ entity: string;
10
+ rxCollection: RxCollection<T>;
11
+ engine: SyncEngine;
12
+ /** Build the op payload sent to the server for an insert/update (default: the row itself). */
13
+ toUpsertPayload?: (row: T) => unknown;
14
+ };
15
+ /** Local-first: `collection.insert/update/delete` write to RxDB immediately (optimistic, durable) and enqueue the matching sync op; the live RxDB query keeps every other read of the collection current, including rows pulled in from the server. */
16
+ export declare function createSyncedCollection<T extends {
17
+ id: string;
18
+ }>(options: SyncedCollectionOptions<T>): import("@tanstack/db").Collection<T, string | number, import("@tanstack/db").UtilsRecord, never, T> & import("@tanstack/db").NonSingleResult;
19
+ /** Re-render whenever the engine's phase, pending count or needs-attention count changes. Feed to a status badge. */
20
+ export declare function useSyncStatus(engine: SyncEngine): SyncStatus;
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE9D,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,IAAI;IAC9D,kEAAkE;IAClE,EAAE,EAAE,MAAM,CAAC;IACX,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,EAAE,UAAU,CAAC;IACnB,8FAA8F;IAC9F,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC;CACvC,CAAC;AAEF,uPAAuP;AACvP,wBAAgB,sBAAsB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,gJA2CnG;AAED,qHAAqH;AACrH,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAE5D"}
@@ -0,0 +1,56 @@
1
+ /** @softwareseva/sync/react — a TanStack DB collection whose durable source is an RxDB collection, and a status hook for SyncEngine. Reads are live (RxDB's own reactive query drives the collection); writes go through RxDB and enqueue an op on the SyncEngine in the same call. */
2
+ import { useSyncExternalStore } from "react";
3
+ import { createCollection } from "@tanstack/db";
4
+ /** Local-first: `collection.insert/update/delete` write to RxDB immediately (optimistic, durable) and enqueue the matching sync op; the live RxDB query keeps every other read of the collection current, including rows pulled in from the server. */
5
+ export function createSyncedCollection(options) {
6
+ const { id, entity, rxCollection, engine, toUpsertPayload = (row) => row } = options;
7
+ return createCollection({
8
+ id,
9
+ getKey: (row) => row.id,
10
+ sync: {
11
+ sync: ({ begin, write, commit, markReady, truncate }) => {
12
+ let ready = false;
13
+ // Full-refresh on every RxDB emission: truncate then re-insert, so this never has to reconcile against what the collection already holds.
14
+ const subscription = rxCollection.find().$.subscribe((docs) => {
15
+ begin();
16
+ truncate();
17
+ for (const doc of docs)
18
+ write({ type: "insert", value: doc.toJSON() });
19
+ commit();
20
+ if (!ready) {
21
+ markReady();
22
+ ready = true;
23
+ }
24
+ });
25
+ return () => subscription.unsubscribe();
26
+ },
27
+ },
28
+ onInsert: async ({ transaction }) => {
29
+ for (const mutation of transaction.mutations) {
30
+ const row = mutation.modified;
31
+ await rxCollection.upsert(row);
32
+ await engine.enqueue(`${entity}.upsert`, toUpsertPayload(row), { entity, entityId: row.id });
33
+ }
34
+ },
35
+ onUpdate: async ({ transaction }) => {
36
+ for (const mutation of transaction.mutations) {
37
+ const row = mutation.modified;
38
+ await rxCollection.upsert(row);
39
+ await engine.enqueue(`${entity}.upsert`, toUpsertPayload(row), { entity, entityId: row.id });
40
+ }
41
+ },
42
+ onDelete: async ({ transaction }) => {
43
+ for (const mutation of transaction.mutations) {
44
+ const row = mutation.original;
45
+ const doc = await rxCollection.findOne(row.id).exec();
46
+ await doc?.remove();
47
+ await engine.enqueue(`${entity}.delete`, { id: row.id }, { entity, entityId: row.id });
48
+ }
49
+ },
50
+ });
51
+ }
52
+ /** Re-render whenever the engine's phase, pending count or needs-attention count changes. Feed to a status badge. */
53
+ export function useSyncStatus(engine) {
54
+ return useSyncExternalStore(engine.statusStream.subscribe, engine.getStatus, engine.getStatus);
55
+ }
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwareseva/sync",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Offline-first sync for Hono on Cloudflare Workers + D1: idempotent batched push of client operations with replay, and a change log with a monotonic cursor for pulling upserts and deletes per scope. Pairs with the kashi_sync Flutter package.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,18 +21,54 @@
21
21
  "types": "./dist/index.d.ts",
22
22
  "import": "./dist/index.js"
23
23
  },
24
+ "./client": {
25
+ "types": "./dist/client/index.d.ts",
26
+ "import": "./dist/client/index.js"
27
+ },
28
+ "./react": {
29
+ "types": "./dist/react/index.d.ts",
30
+ "import": "./dist/react/index.js"
31
+ },
24
32
  "./package.json": "./package.json"
25
33
  },
26
34
  "dependencies": {
27
- "@softwareseva/core": "^0.1.0"
35
+ "@softwareseva/core": "^1.0.0"
28
36
  },
29
37
  "peerDependencies": {
30
38
  "hono": "^4.13.0",
31
- "zod": "^4.0.0"
39
+ "zod": "^4.0.0",
40
+ "rxdb": "^17.0.0",
41
+ "rxjs": "^7.8.0",
42
+ "@tanstack/db": "^0.9.0",
43
+ "@tanstack/react-db": "^0.4.0",
44
+ "react": "^19.0.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "rxdb": {
48
+ "optional": true
49
+ },
50
+ "rxjs": {
51
+ "optional": true
52
+ },
53
+ "@tanstack/db": {
54
+ "optional": true
55
+ },
56
+ "@tanstack/react-db": {
57
+ "optional": true
58
+ },
59
+ "react": {
60
+ "optional": true
61
+ }
32
62
  },
33
63
  "devDependencies": {
34
64
  "@cloudflare/workers-types": "^5.0.0",
35
65
  "hono": "^4.13.9",
66
+ "rxdb": "^17.0.0",
67
+ "rxjs": "^7.8.0",
68
+ "@tanstack/db": "^0.9.0",
69
+ "@tanstack/react-db": "^0.4.0",
70
+ "@types/react": "^19.0.0",
71
+ "react": "^19.0.0",
36
72
  "typescript": "^5.9.0",
37
73
  "vitest": "^4.1.0",
38
74
  "zod": "^4.6.0"