@memrosetta/sync-client 0.1.3 → 0.1.5
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 +1 -0
- package/dist/index.js +68 -29
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ declare class Inbox {
|
|
|
32
32
|
* - `relation_created` INSERT OR IGNORE on the (src, dst, type) PK
|
|
33
33
|
* - `memory_invalidated` UPDATE of `invalidated_at`
|
|
34
34
|
* - `feedback_given` additive UPDATE of `use_count` / `success_count`
|
|
35
|
+
* plus the same salience recompute rule used by core
|
|
35
36
|
* - `memory_tier_set` UPDATE of `tier`
|
|
36
37
|
*/
|
|
37
38
|
interface ApplyResult {
|
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@ var Outbox = class {
|
|
|
48
48
|
addOp(op) {
|
|
49
49
|
const payloadStr = typeof op.payload === "string" ? op.payload : JSON.stringify(op.payload);
|
|
50
50
|
this.db.prepare(
|
|
51
|
-
`INSERT INTO sync_outbox (op_id, op_type, device_id, user_id, payload, created_at, pushed_at)
|
|
51
|
+
`INSERT OR IGNORE INTO sync_outbox (op_id, op_type, device_id, user_id, payload, created_at, pushed_at)
|
|
52
52
|
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
53
53
|
).run(op.opId, op.opType, op.deviceId, op.userId, payloadStr, op.createdAt, null);
|
|
54
54
|
}
|
|
@@ -119,6 +119,10 @@ function parsePayload(payload) {
|
|
|
119
119
|
}
|
|
120
120
|
return payload;
|
|
121
121
|
}
|
|
122
|
+
function serializeKeywords(keywords) {
|
|
123
|
+
if (!keywords || keywords.length === 0) return null;
|
|
124
|
+
return keywords.join(" ");
|
|
125
|
+
}
|
|
122
126
|
function applyMemoryCreated(db, op) {
|
|
123
127
|
const p = parsePayload(op.payload);
|
|
124
128
|
const stmt = db.prepare(
|
|
@@ -148,7 +152,7 @@ function applyMemoryCreated(db, op) {
|
|
|
148
152
|
source_id: p.sourceId ?? null,
|
|
149
153
|
confidence: p.confidence ?? 1,
|
|
150
154
|
salience: p.salience ?? 1,
|
|
151
|
-
keywords:
|
|
155
|
+
keywords: serializeKeywords(p.keywords),
|
|
152
156
|
event_date_start: p.eventDateStart ?? null,
|
|
153
157
|
event_date_end: p.eventDateEnd ?? null,
|
|
154
158
|
invalidated_at: p.invalidatedAt ?? null
|
|
@@ -184,6 +188,17 @@ function applyFeedbackGiven(db, op) {
|
|
|
184
188
|
"UPDATE memories SET use_count = use_count + 1 WHERE memory_id = ?"
|
|
185
189
|
).run(p.memoryId);
|
|
186
190
|
}
|
|
191
|
+
const row = db.prepare(
|
|
192
|
+
"SELECT use_count, success_count FROM memories WHERE memory_id = ?"
|
|
193
|
+
).get(p.memoryId);
|
|
194
|
+
if (row && row.use_count > 0) {
|
|
195
|
+
const successRate = row.success_count / row.use_count;
|
|
196
|
+
const newSalience = Math.min(1, Math.max(0.1, 0.5 + 0.5 * successRate));
|
|
197
|
+
db.prepare("UPDATE memories SET salience = ? WHERE memory_id = ?").run(
|
|
198
|
+
newSalience,
|
|
199
|
+
p.memoryId
|
|
200
|
+
);
|
|
201
|
+
}
|
|
187
202
|
}
|
|
188
203
|
function applyMemoryTierSet(db, op) {
|
|
189
204
|
const p = parsePayload(op.payload);
|
|
@@ -232,6 +247,7 @@ function applyInboxOps(db, ops) {
|
|
|
232
247
|
}
|
|
233
248
|
|
|
234
249
|
// src/sync-client.ts
|
|
250
|
+
var MAX_OPS_PER_PUSH = 400;
|
|
235
251
|
var SyncClient = class {
|
|
236
252
|
db;
|
|
237
253
|
config;
|
|
@@ -278,36 +294,47 @@ var SyncClient = class {
|
|
|
278
294
|
this.setState("last_push_success_at", now);
|
|
279
295
|
return { pushed: 0, results: [], highWatermark: 0 };
|
|
280
296
|
}
|
|
281
|
-
const baseCursor = this.getCursor();
|
|
282
|
-
const wireOps = pending.map((op) => ({
|
|
283
|
-
...op,
|
|
284
|
-
payload: typeof op.payload === "string" ? JSON.parse(op.payload) : op.payload
|
|
285
|
-
}));
|
|
286
297
|
const url = `${this.config.serverUrl}/sync/push`;
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
298
|
+
const aggregatedResults = [];
|
|
299
|
+
let totalPushed = 0;
|
|
300
|
+
let highWatermark = 0;
|
|
301
|
+
for (let start = 0; start < pending.length; start += MAX_OPS_PER_PUSH) {
|
|
302
|
+
const chunk = pending.slice(start, start + MAX_OPS_PER_PUSH);
|
|
303
|
+
const baseCursor = this.getCursor();
|
|
304
|
+
const wireOps = chunk.map((op) => ({
|
|
305
|
+
...op,
|
|
306
|
+
payload: typeof op.payload === "string" ? JSON.parse(op.payload) : op.payload
|
|
307
|
+
}));
|
|
308
|
+
const response = await fetch(url, {
|
|
309
|
+
method: "POST",
|
|
310
|
+
headers: {
|
|
311
|
+
"Content-Type": "application/json",
|
|
312
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
313
|
+
},
|
|
314
|
+
body: JSON.stringify({
|
|
315
|
+
deviceId: this.config.deviceId,
|
|
316
|
+
baseCursor,
|
|
317
|
+
ops: wireOps
|
|
318
|
+
})
|
|
319
|
+
});
|
|
320
|
+
if (!response.ok) {
|
|
321
|
+
throw new Error(
|
|
322
|
+
`Push failed: ${response.status} ${response.statusText}`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
const body = await response.json();
|
|
326
|
+
const { results, highWatermark: batchHigh } = body.data;
|
|
327
|
+
const pushedIds = results.filter((r) => r.status === "accepted" || r.status === "duplicate").map((r) => r.opId);
|
|
328
|
+
this.outbox.markPushed(pushedIds);
|
|
329
|
+
this.setCursor(batchHigh);
|
|
330
|
+
aggregatedResults.push(...results);
|
|
331
|
+
totalPushed += pushedIds.length;
|
|
332
|
+
highWatermark = batchHigh;
|
|
301
333
|
}
|
|
302
|
-
const body = await response.json();
|
|
303
|
-
const { results, highWatermark } = body.data;
|
|
304
|
-
const pushedIds = results.filter((r) => r.status === "accepted" || r.status === "duplicate").map((r) => r.opId);
|
|
305
|
-
this.outbox.markPushed(pushedIds);
|
|
306
|
-
this.setCursor(highWatermark);
|
|
307
334
|
this.setState("last_push_success_at", (/* @__PURE__ */ new Date()).toISOString());
|
|
308
335
|
return {
|
|
309
|
-
pushed:
|
|
310
|
-
results,
|
|
336
|
+
pushed: totalPushed,
|
|
337
|
+
results: aggregatedResults,
|
|
311
338
|
highWatermark
|
|
312
339
|
};
|
|
313
340
|
}
|
|
@@ -334,14 +361,26 @@ var SyncClient = class {
|
|
|
334
361
|
this.inbox.addOps(ops);
|
|
335
362
|
}
|
|
336
363
|
const pending = this.inbox.getPending();
|
|
364
|
+
let skippedCount = 0;
|
|
337
365
|
if (pending.length > 0) {
|
|
338
366
|
const result = applyInboxOps(this.db, pending);
|
|
339
367
|
if (result.applied.length > 0) {
|
|
340
368
|
this.inbox.markApplied(result.applied);
|
|
341
369
|
}
|
|
370
|
+
skippedCount = result.skipped.length;
|
|
371
|
+
if (skippedCount > 0) {
|
|
372
|
+
for (const s of result.skipped) {
|
|
373
|
+
process.stderr.write(
|
|
374
|
+
`[sync] apply skipped op ${s.opId}: ${s.reason}
|
|
375
|
+
`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
342
379
|
}
|
|
343
380
|
this.setCursor(nextCursor);
|
|
344
|
-
|
|
381
|
+
if (skippedCount === 0) {
|
|
382
|
+
this.setState("last_pull_success_at", (/* @__PURE__ */ new Date()).toISOString());
|
|
383
|
+
}
|
|
345
384
|
return ops.length;
|
|
346
385
|
}
|
|
347
386
|
/** For tests / advanced callers: apply currently-pending inbox ops manually. */
|