@memrosetta/sync-client 0.1.0 → 0.1.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/dist/index.d.ts +19 -1
- package/dist/index.js +42 -7
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ declare class Outbox {
|
|
|
7
7
|
constructor(db: Database.Database);
|
|
8
8
|
addOp(op: SyncOp): void;
|
|
9
9
|
getPending(): readonly SyncOp[];
|
|
10
|
+
countPending(): number;
|
|
10
11
|
markPushed(opIds: readonly string[]): void;
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -27,12 +28,26 @@ interface SyncClientConfig {
|
|
|
27
28
|
readonly serverUrl: string;
|
|
28
29
|
readonly apiKey: string;
|
|
29
30
|
readonly deviceId: string;
|
|
31
|
+
readonly userId: string;
|
|
30
32
|
}
|
|
31
33
|
interface SyncClientPushResponse {
|
|
32
34
|
readonly pushed: number;
|
|
33
35
|
readonly results: readonly SyncPushResult[];
|
|
34
36
|
readonly highWatermark: number;
|
|
35
37
|
}
|
|
38
|
+
interface SyncStatusTimestamps {
|
|
39
|
+
readonly attemptAt: string | null;
|
|
40
|
+
readonly successAt: string | null;
|
|
41
|
+
}
|
|
42
|
+
interface SyncClientStatus {
|
|
43
|
+
readonly enabled: true;
|
|
44
|
+
readonly serverUrl: string;
|
|
45
|
+
readonly deviceId: string;
|
|
46
|
+
readonly pendingOps: number;
|
|
47
|
+
readonly lastPush: SyncStatusTimestamps;
|
|
48
|
+
readonly lastPull: SyncStatusTimestamps;
|
|
49
|
+
readonly cursor: number;
|
|
50
|
+
}
|
|
36
51
|
declare class SyncClient {
|
|
37
52
|
private readonly db;
|
|
38
53
|
private readonly config;
|
|
@@ -42,12 +57,15 @@ declare class SyncClient {
|
|
|
42
57
|
initialize(): void;
|
|
43
58
|
getOutbox(): Outbox;
|
|
44
59
|
getInbox(): Inbox;
|
|
60
|
+
getStatus(): SyncClientStatus;
|
|
45
61
|
push(): Promise<SyncClientPushResponse>;
|
|
46
62
|
pull(): Promise<number>;
|
|
47
63
|
getState(key: string): string | null;
|
|
48
64
|
setState(key: string, value: string): void;
|
|
65
|
+
private getCursor;
|
|
66
|
+
private setCursor;
|
|
49
67
|
}
|
|
50
68
|
|
|
51
69
|
declare function ensureSyncSchema(db: Database.Database): void;
|
|
52
70
|
|
|
53
|
-
export { Inbox, Outbox, SyncClient, ensureSyncSchema };
|
|
71
|
+
export { Inbox, Outbox, SyncClient, type SyncClientConfig, type SyncClientPushResponse, type SyncClientStatus, type SyncStatusTimestamps, ensureSyncSchema };
|
package/dist/index.js
CHANGED
|
@@ -56,6 +56,10 @@ var Outbox = class {
|
|
|
56
56
|
const rows = this.db.prepare("SELECT * FROM sync_outbox WHERE pushed_at IS NULL ORDER BY created_at ASC").all();
|
|
57
57
|
return rows.map(rowToSyncOp);
|
|
58
58
|
}
|
|
59
|
+
countPending() {
|
|
60
|
+
const row = this.db.prepare("SELECT COUNT(*) as count FROM sync_outbox WHERE pushed_at IS NULL").get();
|
|
61
|
+
return row.count;
|
|
62
|
+
}
|
|
59
63
|
markPushed(opIds) {
|
|
60
64
|
if (opIds.length === 0) return;
|
|
61
65
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -129,13 +133,32 @@ var SyncClient = class {
|
|
|
129
133
|
getInbox() {
|
|
130
134
|
return this.inbox;
|
|
131
135
|
}
|
|
136
|
+
getStatus() {
|
|
137
|
+
return {
|
|
138
|
+
enabled: true,
|
|
139
|
+
serverUrl: this.config.serverUrl,
|
|
140
|
+
deviceId: this.config.deviceId,
|
|
141
|
+
pendingOps: this.outbox.countPending(),
|
|
142
|
+
lastPush: {
|
|
143
|
+
attemptAt: this.getState("last_push_attempt_at"),
|
|
144
|
+
successAt: this.getState("last_push_success_at")
|
|
145
|
+
},
|
|
146
|
+
lastPull: {
|
|
147
|
+
attemptAt: this.getState("last_pull_attempt_at"),
|
|
148
|
+
successAt: this.getState("last_pull_success_at")
|
|
149
|
+
},
|
|
150
|
+
cursor: this.getCursor()
|
|
151
|
+
};
|
|
152
|
+
}
|
|
132
153
|
async push() {
|
|
154
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
155
|
+
this.setState("last_push_attempt_at", now);
|
|
133
156
|
const pending = this.outbox.getPending();
|
|
134
157
|
if (pending.length === 0) {
|
|
158
|
+
this.setState("last_push_success_at", now);
|
|
135
159
|
return { pushed: 0, results: [], highWatermark: 0 };
|
|
136
160
|
}
|
|
137
|
-
const
|
|
138
|
-
const baseCursor = baseCursorStr ? parseInt(baseCursorStr, 10) : 0;
|
|
161
|
+
const baseCursor = this.getCursor();
|
|
139
162
|
const wireOps = pending.map((op) => ({
|
|
140
163
|
...op,
|
|
141
164
|
payload: typeof op.payload === "string" ? JSON.parse(op.payload) : op.payload
|
|
@@ -160,7 +183,8 @@ var SyncClient = class {
|
|
|
160
183
|
const { results, highWatermark } = body.data;
|
|
161
184
|
const pushedIds = results.filter((r) => r.status === "accepted" || r.status === "duplicate").map((r) => r.opId);
|
|
162
185
|
this.outbox.markPushed(pushedIds);
|
|
163
|
-
this.
|
|
186
|
+
this.setCursor(highWatermark);
|
|
187
|
+
this.setState("last_push_success_at", (/* @__PURE__ */ new Date()).toISOString());
|
|
164
188
|
return {
|
|
165
189
|
pushed: pushedIds.length,
|
|
166
190
|
results,
|
|
@@ -168,10 +192,11 @@ var SyncClient = class {
|
|
|
168
192
|
};
|
|
169
193
|
}
|
|
170
194
|
async pull() {
|
|
171
|
-
|
|
172
|
-
const since =
|
|
195
|
+
this.setState("last_pull_attempt_at", (/* @__PURE__ */ new Date()).toISOString());
|
|
196
|
+
const since = this.getCursor();
|
|
173
197
|
const params = new URLSearchParams({
|
|
174
|
-
since: String(since)
|
|
198
|
+
since: String(since),
|
|
199
|
+
userId: this.config.userId
|
|
175
200
|
});
|
|
176
201
|
const url = `${this.config.serverUrl}/sync/pull?${params.toString()}`;
|
|
177
202
|
const response = await fetch(url, {
|
|
@@ -188,7 +213,8 @@ var SyncClient = class {
|
|
|
188
213
|
if (ops.length > 0) {
|
|
189
214
|
this.inbox.addOps(ops);
|
|
190
215
|
}
|
|
191
|
-
this.
|
|
216
|
+
this.setCursor(nextCursor);
|
|
217
|
+
this.setState("last_pull_success_at", (/* @__PURE__ */ new Date()).toISOString());
|
|
192
218
|
return ops.length;
|
|
193
219
|
}
|
|
194
220
|
getState(key) {
|
|
@@ -198,6 +224,15 @@ var SyncClient = class {
|
|
|
198
224
|
setState(key, value) {
|
|
199
225
|
this.db.prepare("INSERT OR REPLACE INTO sync_state (key, value) VALUES (?, ?)").run(key, value);
|
|
200
226
|
}
|
|
227
|
+
getCursor() {
|
|
228
|
+
const cursorStr = this.getState("last_cursor") ?? this.getState("pull_cursor");
|
|
229
|
+
return cursorStr ? parseInt(cursorStr, 10) : 0;
|
|
230
|
+
}
|
|
231
|
+
setCursor(cursor) {
|
|
232
|
+
const value = String(cursor);
|
|
233
|
+
this.setState("last_cursor", value);
|
|
234
|
+
this.setState("pull_cursor", value);
|
|
235
|
+
}
|
|
201
236
|
};
|
|
202
237
|
export {
|
|
203
238
|
Inbox,
|