@slorenzot/memento-core 2.0.0 → 2.0.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/ConfigManager.d.ts +44 -59
- package/dist/ConfigManager.d.ts.map +1 -1
- package/dist/ConfigManager.js +78 -279
- package/dist/ConfigManager.js.map +1 -1
- package/dist/MemoryEngine.d.ts +30 -0
- package/dist/MemoryEngine.d.ts.map +1 -1
- package/dist/MemoryEngine.js +136 -11
- package/dist/MemoryEngine.js.map +1 -1
- package/dist/auth/device-flow-client.d.ts +48 -0
- package/dist/auth/device-flow-client.d.ts.map +1 -0
- package/dist/auth/device-flow-client.js +143 -0
- package/dist/auth/device-flow-client.js.map +1 -0
- package/dist/auth/index.d.ts +4 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +9 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/token-store.d.ts +46 -0
- package/dist/auth/token-store.d.ts.map +1 -0
- package/dist/auth/token-store.js +117 -0
- package/dist/auth/token-store.js.map +1 -0
- package/dist/auth/types.d.ts +83 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +4 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/sync/conflict-resolution.d.ts +26 -0
- package/dist/sync/conflict-resolution.d.ts.map +1 -0
- package/dist/sync/conflict-resolution.js +64 -0
- package/dist/sync/conflict-resolution.js.map +1 -0
- package/dist/sync/index.d.ts +5 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +12 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/sync-client.d.ts +41 -0
- package/dist/sync/sync-client.d.ts.map +1 -0
- package/dist/sync/sync-client.js +101 -0
- package/dist/sync/sync-client.js.map +1 -0
- package/dist/sync/sync-engine.d.ts +58 -0
- package/dist/sync/sync-engine.d.ts.map +1 -0
- package/dist/sync/sync-engine.js +370 -0
- package/dist/sync/sync-engine.js.map +1 -0
- package/dist/sync/types.d.ts +107 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +20 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/token-savings.d.ts +42 -0
- package/dist/token-savings.d.ts.map +1 -0
- package/dist/token-savings.js +104 -0
- package/dist/token-savings.js.map +1 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { MemoryEngine } from '../MemoryEngine.js';
|
|
2
|
+
import type { SyncConfig, SyncMeta, SyncResult, SyncStatusResponse } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* SyncEngine — orchestrates bidirectional sync between local DB and memento-web.
|
|
5
|
+
*
|
|
6
|
+
* Protocol: cursor-based, version-tracked, last-write-wins
|
|
7
|
+
*
|
|
8
|
+
* Flow:
|
|
9
|
+
* 1. Pull: fetch remote changes since cursor
|
|
10
|
+
* 2. Apply remote changes locally
|
|
11
|
+
* 3. Push: send local changes since last sync (scope=project only)
|
|
12
|
+
* 4. Save new cursor
|
|
13
|
+
*/
|
|
14
|
+
export declare class SyncEngine {
|
|
15
|
+
private config;
|
|
16
|
+
private metaPath;
|
|
17
|
+
private client;
|
|
18
|
+
private tokenStore;
|
|
19
|
+
private deviceFingerprint;
|
|
20
|
+
constructor(config: SyncConfig);
|
|
21
|
+
/**
|
|
22
|
+
* Full bidirectional sync.
|
|
23
|
+
*/
|
|
24
|
+
sync(engine: MemoryEngine): Promise<SyncResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Pull-only: download remote changes to local DB.
|
|
27
|
+
*/
|
|
28
|
+
pull(engine: MemoryEngine): Promise<SyncResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Push-only: upload local changes to server.
|
|
31
|
+
*/
|
|
32
|
+
push(engine: MemoryEngine): Promise<SyncResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Get sync status.
|
|
35
|
+
*/
|
|
36
|
+
getStatus(engine: MemoryEngine): Promise<{
|
|
37
|
+
meta: SyncMeta | null;
|
|
38
|
+
remote?: SyncStatusResponse;
|
|
39
|
+
local: {
|
|
40
|
+
totalObservations: number;
|
|
41
|
+
activeObservations: number;
|
|
42
|
+
};
|
|
43
|
+
authenticated: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
private doPull;
|
|
46
|
+
private doPush;
|
|
47
|
+
/**
|
|
48
|
+
* Apply a SyncMemento to the local DB.
|
|
49
|
+
*/
|
|
50
|
+
private applyToLocal;
|
|
51
|
+
/**
|
|
52
|
+
* Convert a local Observation to a SyncMemento for the wire.
|
|
53
|
+
*/
|
|
54
|
+
private observationToMemento;
|
|
55
|
+
loadMeta(): SyncMeta;
|
|
56
|
+
saveMeta(meta: SyncMeta): void;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=sync-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-engine.d.ts","sourceRoot":"","sources":["../../src/sync/sync-engine.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,UAAU,EAEV,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAOpB;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,iBAAiB,CAAS;gBAEtB,MAAM,EAAE,UAAU;IAgB9B;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAiCrD;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IA0BrD;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IA0BrD;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC;QAC7C,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;QACtB,MAAM,CAAC,EAAE,kBAAkB,CAAC;QAC5B,KAAK,EAAE;YAAE,iBAAiB,EAAE,MAAM,CAAC;YAAC,kBAAkB,EAAE,MAAM,CAAA;SAAE,CAAC;QACjE,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;YA6BY,MAAM;YAmCN,MAAM;IA2DpB;;OAEG;YACW,YAAY;IAiD1B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAsB5B,QAAQ,IAAI,QAAQ;IAsBpB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;CAO/B"}
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.SyncEngine = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const crypto = __importStar(require("crypto"));
|
|
41
|
+
const sync_client_js_1 = require("./sync-client.js");
|
|
42
|
+
const token_store_js_1 = require("../auth/token-store.js");
|
|
43
|
+
const DEFAULT_META_PATH = path.join(os.homedir(), '.memento', 'sync-meta.json');
|
|
44
|
+
/**
|
|
45
|
+
* SyncEngine — orchestrates bidirectional sync between local DB and memento-web.
|
|
46
|
+
*
|
|
47
|
+
* Protocol: cursor-based, version-tracked, last-write-wins
|
|
48
|
+
*
|
|
49
|
+
* Flow:
|
|
50
|
+
* 1. Pull: fetch remote changes since cursor
|
|
51
|
+
* 2. Apply remote changes locally
|
|
52
|
+
* 3. Push: send local changes since last sync (scope=project only)
|
|
53
|
+
* 4. Save new cursor
|
|
54
|
+
*/
|
|
55
|
+
class SyncEngine {
|
|
56
|
+
config;
|
|
57
|
+
metaPath;
|
|
58
|
+
client;
|
|
59
|
+
tokenStore;
|
|
60
|
+
deviceFingerprint;
|
|
61
|
+
constructor(config) {
|
|
62
|
+
this.config = config;
|
|
63
|
+
this.metaPath = config.metaFilePath || DEFAULT_META_PATH;
|
|
64
|
+
this.tokenStore = new token_store_js_1.TokenStore();
|
|
65
|
+
this.deviceFingerprint = generateDeviceFingerprint();
|
|
66
|
+
this.client = new sync_client_js_1.SyncClient({
|
|
67
|
+
serverUrl: config.serverUrl,
|
|
68
|
+
getToken: () => {
|
|
69
|
+
const t = this.tokenStore.getToken();
|
|
70
|
+
return Promise.resolve(t?.accessToken ?? null);
|
|
71
|
+
},
|
|
72
|
+
fetchFunction: config.fetchFunction,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Full bidirectional sync.
|
|
77
|
+
*/
|
|
78
|
+
async sync(engine) {
|
|
79
|
+
const start = Date.now();
|
|
80
|
+
const meta = this.loadMeta();
|
|
81
|
+
// Pull first
|
|
82
|
+
const pullResult = await this.doPull(engine, meta.lastCursor);
|
|
83
|
+
// Then push
|
|
84
|
+
const pushResult = await this.doPush(engine, meta.lastCursor);
|
|
85
|
+
// Save meta with new cursor
|
|
86
|
+
const newCursor = pullResult.newCursor || pushResult.newCursor || meta.lastCursor;
|
|
87
|
+
this.saveMeta({
|
|
88
|
+
lastSyncAt: Date.now(),
|
|
89
|
+
lastSyncDirection: 'bidirectional',
|
|
90
|
+
lastCursor: newCursor,
|
|
91
|
+
lastPullCount: pullResult.applied,
|
|
92
|
+
lastPushCount: pushResult.pushed,
|
|
93
|
+
lastConflictCount: pullResult.conflicts.length + pushResult.conflicts.length,
|
|
94
|
+
serverUrl: this.config.serverUrl,
|
|
95
|
+
updatedAt: Date.now(),
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
direction: 'bidirectional',
|
|
99
|
+
pulled: pullResult.applied,
|
|
100
|
+
pushed: pushResult.pushed,
|
|
101
|
+
conflicts: [...pullResult.conflicts, ...pushResult.conflicts],
|
|
102
|
+
errors: [...pullResult.errors, ...pushResult.errors],
|
|
103
|
+
durationMs: Date.now() - start,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Pull-only: download remote changes to local DB.
|
|
108
|
+
*/
|
|
109
|
+
async pull(engine) {
|
|
110
|
+
const start = Date.now();
|
|
111
|
+
const meta = this.loadMeta();
|
|
112
|
+
const result = await this.doPull(engine, meta.lastCursor);
|
|
113
|
+
this.saveMeta({
|
|
114
|
+
lastSyncAt: Date.now(),
|
|
115
|
+
lastSyncDirection: 'pull',
|
|
116
|
+
lastCursor: result.newCursor || meta.lastCursor,
|
|
117
|
+
lastPullCount: result.applied,
|
|
118
|
+
lastPushCount: 0,
|
|
119
|
+
lastConflictCount: result.conflicts.length,
|
|
120
|
+
serverUrl: this.config.serverUrl,
|
|
121
|
+
updatedAt: Date.now(),
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
direction: 'pull',
|
|
125
|
+
pulled: result.applied,
|
|
126
|
+
pushed: 0,
|
|
127
|
+
conflicts: result.conflicts,
|
|
128
|
+
errors: result.errors,
|
|
129
|
+
durationMs: Date.now() - start,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Push-only: upload local changes to server.
|
|
134
|
+
*/
|
|
135
|
+
async push(engine) {
|
|
136
|
+
const start = Date.now();
|
|
137
|
+
const meta = this.loadMeta();
|
|
138
|
+
const result = await this.doPush(engine, meta.lastCursor);
|
|
139
|
+
this.saveMeta({
|
|
140
|
+
lastSyncAt: Date.now(),
|
|
141
|
+
lastSyncDirection: 'push',
|
|
142
|
+
lastCursor: result.newCursor || meta.lastCursor,
|
|
143
|
+
lastPullCount: 0,
|
|
144
|
+
lastPushCount: result.pushed,
|
|
145
|
+
lastConflictCount: result.conflicts.length,
|
|
146
|
+
serverUrl: this.config.serverUrl,
|
|
147
|
+
updatedAt: Date.now(),
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
direction: 'push',
|
|
151
|
+
pulled: 0,
|
|
152
|
+
pushed: result.pushed,
|
|
153
|
+
conflicts: result.conflicts,
|
|
154
|
+
errors: result.errors,
|
|
155
|
+
durationMs: Date.now() - start,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get sync status.
|
|
160
|
+
*/
|
|
161
|
+
async getStatus(engine) {
|
|
162
|
+
const meta = this.loadMeta();
|
|
163
|
+
const token = this.tokenStore.getToken();
|
|
164
|
+
const authenticated = !!token;
|
|
165
|
+
// Local stats
|
|
166
|
+
const localActive = (await engine.search({ projectId: this.config.projectId, limit: 0 })).total;
|
|
167
|
+
const localDeleted = (await engine.listDeleted({ limit: 0 })).total;
|
|
168
|
+
let remote;
|
|
169
|
+
if (authenticated) {
|
|
170
|
+
try {
|
|
171
|
+
remote = await this.client.status(this.config.projectId);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Remote unreachable
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
meta: meta.lastSyncAt ? meta : null,
|
|
179
|
+
remote,
|
|
180
|
+
local: { totalObservations: localActive + localDeleted, activeObservations: localActive },
|
|
181
|
+
authenticated,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// ── Internal ────────────────────────────────────────────────
|
|
185
|
+
async doPull(engine, cursor) {
|
|
186
|
+
const errors = [];
|
|
187
|
+
const conflicts = [];
|
|
188
|
+
let hasMore = true;
|
|
189
|
+
let totalApplied = 0;
|
|
190
|
+
let latestCursor = cursor;
|
|
191
|
+
// Paginated pull
|
|
192
|
+
while (hasMore) {
|
|
193
|
+
const response = await this.client.pull({
|
|
194
|
+
projectId: this.config.projectId,
|
|
195
|
+
cursor: latestCursor,
|
|
196
|
+
limit: 100,
|
|
197
|
+
});
|
|
198
|
+
for (const change of response.changes) {
|
|
199
|
+
try {
|
|
200
|
+
await this.applyToLocal(engine, change);
|
|
201
|
+
totalApplied++;
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
errors.push(`Failed to apply ${change.uuid}: ${err}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
latestCursor = response.newCursor;
|
|
208
|
+
hasMore = response.hasMore;
|
|
209
|
+
}
|
|
210
|
+
return { applied: totalApplied, conflicts, errors, newCursor: latestCursor || '' };
|
|
211
|
+
}
|
|
212
|
+
async doPush(engine, cursor) {
|
|
213
|
+
// Get local observations with scope=project (personal stays local)
|
|
214
|
+
const active = await engine.search({
|
|
215
|
+
projectId: this.config.projectId,
|
|
216
|
+
limit: 100000,
|
|
217
|
+
});
|
|
218
|
+
const deleted = await engine.listDeleted({
|
|
219
|
+
projectId: this.config.projectId,
|
|
220
|
+
limit: 100000,
|
|
221
|
+
});
|
|
222
|
+
const all = [...active.observations, ...deleted.observations];
|
|
223
|
+
// All items (project + personal scope) get pushed
|
|
224
|
+
// Filter: only items modified since cursor
|
|
225
|
+
let itemsToSync = all;
|
|
226
|
+
if (cursor) {
|
|
227
|
+
const cursorDate = new Date(cursor);
|
|
228
|
+
itemsToSync = all.filter(obs => obs.updatedAt > cursorDate);
|
|
229
|
+
}
|
|
230
|
+
if (itemsToSync.length === 0) {
|
|
231
|
+
return { pushed: 0, conflicts: [], errors: [], newCursor: cursor || '' };
|
|
232
|
+
}
|
|
233
|
+
// Convert to SyncMemento wire format
|
|
234
|
+
const items = itemsToSync.map(obs => this.observationToMemento(obs));
|
|
235
|
+
const result = await this.client.push({
|
|
236
|
+
projectId: this.config.projectId,
|
|
237
|
+
cursor,
|
|
238
|
+
deviceFingerprint: this.deviceFingerprint,
|
|
239
|
+
clientVersion: '1.0.0',
|
|
240
|
+
items,
|
|
241
|
+
});
|
|
242
|
+
// Handle conflicts — apply server data for server_wins
|
|
243
|
+
for (const conflict of result.conflicts) {
|
|
244
|
+
if (conflict.resolution === 'server_wins' && conflict.serverData) {
|
|
245
|
+
try {
|
|
246
|
+
await this.applyToLocal(engine, conflict.serverData);
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
// Best effort
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
pushed: result.synced,
|
|
255
|
+
conflicts: result.conflicts,
|
|
256
|
+
errors: [],
|
|
257
|
+
newCursor: result.newCursor,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Apply a SyncMemento to the local DB.
|
|
262
|
+
*/
|
|
263
|
+
async applyToLocal(engine, memento) {
|
|
264
|
+
const existing = engine.getObservationByUuid(memento.uuid);
|
|
265
|
+
if (existing) {
|
|
266
|
+
if (existing.readOnly)
|
|
267
|
+
return;
|
|
268
|
+
await engine.updateObservation(existing.id, {
|
|
269
|
+
title: memento.title,
|
|
270
|
+
content: memento.content,
|
|
271
|
+
type: memento.type,
|
|
272
|
+
topicKey: memento.topicKey,
|
|
273
|
+
metadata: memento.metadata,
|
|
274
|
+
pinned: memento.pinned,
|
|
275
|
+
readOnly: memento.readOnly,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
// Find or create session
|
|
280
|
+
const sessions = await engine.listSessions({
|
|
281
|
+
projectId: memento.projectId,
|
|
282
|
+
limit: 1,
|
|
283
|
+
activeOnly: false,
|
|
284
|
+
});
|
|
285
|
+
let sessionId;
|
|
286
|
+
if (sessions.sessions.length > 0) {
|
|
287
|
+
sessionId = sessions.sessions[0].id;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
const session = await engine.createSession({
|
|
291
|
+
projectId: memento.projectId,
|
|
292
|
+
endedAt: null,
|
|
293
|
+
metadata: { source: 'sync', syncedAt: Date.now() },
|
|
294
|
+
});
|
|
295
|
+
sessionId = session.id;
|
|
296
|
+
}
|
|
297
|
+
await engine.createObservation({
|
|
298
|
+
sessionId,
|
|
299
|
+
title: memento.title,
|
|
300
|
+
content: memento.content,
|
|
301
|
+
type: memento.type,
|
|
302
|
+
topicKey: memento.topicKey,
|
|
303
|
+
projectId: memento.projectId,
|
|
304
|
+
scope: memento.scope,
|
|
305
|
+
pinned: memento.pinned,
|
|
306
|
+
readOnly: memento.readOnly,
|
|
307
|
+
metadata: { ...memento.metadata, syncedUuid: memento.uuid },
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Convert a local Observation to a SyncMemento for the wire.
|
|
313
|
+
*/
|
|
314
|
+
observationToMemento(obs) {
|
|
315
|
+
return {
|
|
316
|
+
uuid: obs.uuid,
|
|
317
|
+
title: obs.title,
|
|
318
|
+
content: obs.content,
|
|
319
|
+
type: obs.type,
|
|
320
|
+
topicKey: obs.topicKey,
|
|
321
|
+
scope: obs.scope,
|
|
322
|
+
pinned: obs.pinned,
|
|
323
|
+
readOnly: obs.readOnly,
|
|
324
|
+
revisionCount: obs.revisionCount,
|
|
325
|
+
projectId: obs.projectId,
|
|
326
|
+
metadata: obs.metadata,
|
|
327
|
+
localCreatedAt: obs.createdAt.toISOString(),
|
|
328
|
+
localUpdatedAt: obs.updatedAt.toISOString(),
|
|
329
|
+
version: obs.revisionCount + 1, // Version = revisionCount + 1 (first version is 1)
|
|
330
|
+
deletedAt: obs.deletedAt ? obs.deletedAt.toISOString() : null,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// ── Sync Meta Persistence ────────────────────────────────────
|
|
334
|
+
loadMeta() {
|
|
335
|
+
try {
|
|
336
|
+
if (fs.existsSync(this.metaPath)) {
|
|
337
|
+
const raw = fs.readFileSync(this.metaPath, 'utf-8');
|
|
338
|
+
return JSON.parse(raw);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
// Corrupted — start fresh
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
lastSyncAt: null,
|
|
346
|
+
lastSyncDirection: null,
|
|
347
|
+
lastCursor: null,
|
|
348
|
+
lastPullCount: 0,
|
|
349
|
+
lastPushCount: 0,
|
|
350
|
+
lastConflictCount: 0,
|
|
351
|
+
serverUrl: this.config.serverUrl,
|
|
352
|
+
updatedAt: Date.now(),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
saveMeta(meta) {
|
|
356
|
+
const dir = path.dirname(this.metaPath);
|
|
357
|
+
if (!fs.existsSync(dir)) {
|
|
358
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
359
|
+
}
|
|
360
|
+
fs.writeFileSync(this.metaPath, JSON.stringify(meta, null, 2), 'utf-8');
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
exports.SyncEngine = SyncEngine;
|
|
364
|
+
/** Generate a stable device fingerprint based on hostname + username */
|
|
365
|
+
function generateDeviceFingerprint() {
|
|
366
|
+
const hostname = os.hostname();
|
|
367
|
+
const username = os.userInfo().username;
|
|
368
|
+
return crypto.createHash('sha256').update(`${hostname}:${username}`).digest('hex').slice(0, 16);
|
|
369
|
+
}
|
|
370
|
+
//# sourceMappingURL=sync-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-engine.js","sourceRoot":"","sources":["../../src/sync/sync-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/** Sync direction */
|
|
2
|
+
export type SyncDirection = 'pull' | 'push' | 'bidirectional';
|
|
3
|
+
/**
|
|
4
|
+
* A memento item as transferred over the wire.
|
|
5
|
+
* Matches memento-web SyncMemento exactly.
|
|
6
|
+
*/
|
|
7
|
+
export interface SyncMemento {
|
|
8
|
+
uuid: string;
|
|
9
|
+
title: string;
|
|
10
|
+
content: string;
|
|
11
|
+
type: string;
|
|
12
|
+
topicKey: string | null;
|
|
13
|
+
scope: 'project' | 'personal';
|
|
14
|
+
pinned: boolean;
|
|
15
|
+
readOnly: boolean;
|
|
16
|
+
revisionCount: number;
|
|
17
|
+
projectId: string;
|
|
18
|
+
metadata: Record<string, unknown>;
|
|
19
|
+
localCreatedAt: string;
|
|
20
|
+
localUpdatedAt: string;
|
|
21
|
+
version: number;
|
|
22
|
+
deletedAt: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** Push request body — matches memento-web SyncPushRequest */
|
|
25
|
+
export interface SyncPushRequest {
|
|
26
|
+
projectId: string;
|
|
27
|
+
cursor: string | null;
|
|
28
|
+
deviceFingerprint?: string;
|
|
29
|
+
clientVersion?: string;
|
|
30
|
+
items: SyncMemento[];
|
|
31
|
+
}
|
|
32
|
+
/** Push response from memento-web */
|
|
33
|
+
export interface SyncPushResponse {
|
|
34
|
+
synced: number;
|
|
35
|
+
created: number;
|
|
36
|
+
updated: number;
|
|
37
|
+
conflicts: SyncConflict[];
|
|
38
|
+
newCursor: string;
|
|
39
|
+
}
|
|
40
|
+
/** Pull response from memento-web */
|
|
41
|
+
export interface SyncPullResponse {
|
|
42
|
+
changes: SyncMemento[];
|
|
43
|
+
hasMore: boolean;
|
|
44
|
+
totalChanges: number;
|
|
45
|
+
newCursor: string;
|
|
46
|
+
}
|
|
47
|
+
/** Pull query params */
|
|
48
|
+
export interface SyncPullParams {
|
|
49
|
+
projectId: string;
|
|
50
|
+
cursor: string | null;
|
|
51
|
+
limit?: number;
|
|
52
|
+
}
|
|
53
|
+
/** Conflict during push */
|
|
54
|
+
export interface SyncConflict {
|
|
55
|
+
uuid: string;
|
|
56
|
+
localVersion: number;
|
|
57
|
+
serverVersion: number;
|
|
58
|
+
resolution: 'client_wins' | 'server_wins';
|
|
59
|
+
serverData?: SyncMemento;
|
|
60
|
+
}
|
|
61
|
+
/** Status response from memento-web */
|
|
62
|
+
export interface SyncStatusResponse {
|
|
63
|
+
projectId: string;
|
|
64
|
+
lastSyncAt: string | null;
|
|
65
|
+
totalMementos: number;
|
|
66
|
+
pendingChanges: number | null;
|
|
67
|
+
conflicts: number;
|
|
68
|
+
device: {
|
|
69
|
+
fingerprint: string;
|
|
70
|
+
lastSyncAt: string | null;
|
|
71
|
+
} | null;
|
|
72
|
+
}
|
|
73
|
+
/** Sync metadata persisted locally */
|
|
74
|
+
export interface SyncMeta {
|
|
75
|
+
lastSyncAt: number | null;
|
|
76
|
+
lastSyncDirection: SyncDirection | null;
|
|
77
|
+
lastCursor: string | null;
|
|
78
|
+
lastPullCount: number;
|
|
79
|
+
lastPushCount: number;
|
|
80
|
+
lastConflictCount: number;
|
|
81
|
+
serverUrl: string;
|
|
82
|
+
updatedAt: number;
|
|
83
|
+
}
|
|
84
|
+
/** Result of a sync operation */
|
|
85
|
+
export interface SyncResult {
|
|
86
|
+
direction: SyncDirection;
|
|
87
|
+
pulled: number;
|
|
88
|
+
pushed: number;
|
|
89
|
+
conflicts: SyncConflict[];
|
|
90
|
+
errors: string[];
|
|
91
|
+
durationMs: number;
|
|
92
|
+
}
|
|
93
|
+
/** Configuration for the sync engine */
|
|
94
|
+
export interface SyncConfig {
|
|
95
|
+
serverUrl: string;
|
|
96
|
+
projectId: string;
|
|
97
|
+
fetchFunction?: typeof fetch;
|
|
98
|
+
metaFilePath?: string;
|
|
99
|
+
}
|
|
100
|
+
/** HTTP error from sync API */
|
|
101
|
+
export declare class SyncApiError extends Error {
|
|
102
|
+
status: number;
|
|
103
|
+
code?: string | undefined;
|
|
104
|
+
body?: string | undefined;
|
|
105
|
+
constructor(message: string, status: number, code?: string | undefined, body?: string | undefined);
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ─── Sync Types — Memento v1 Protocol ──────────────────────────
|
|
3
|
+
// Matches memento-web/src/types/sync.ts
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.SyncApiError = void 0;
|
|
6
|
+
/** HTTP error from sync API */
|
|
7
|
+
class SyncApiError extends Error {
|
|
8
|
+
status;
|
|
9
|
+
code;
|
|
10
|
+
body;
|
|
11
|
+
constructor(message, status, code, body) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.status = status;
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.body = body;
|
|
16
|
+
this.name = 'SyncApiError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.SyncApiError = SyncApiError;
|
|
20
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":";AAAA,kEAAkE;AAClE,wCAAwC;;;AAgHxC,+BAA+B;AAC/B,MAAa,YAAa,SAAQ,KAAK;IAG5B;IACA;IACA;IAJT,YACE,OAAe,EACR,MAAc,EACd,IAAa,EACb,IAAa;QAEpB,KAAK,CAAC,OAAO,CAAC,CAAC;QAJR,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAS;QACb,SAAI,GAAJ,IAAI,CAAS;QAGpB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAVD,oCAUC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Observation } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Estimate the number of tokens in a string.
|
|
4
|
+
* Uses simple chars/4 ratio — no tokenizer dependency needed.
|
|
5
|
+
*/
|
|
6
|
+
export declare function estimateTokens(text: string): number;
|
|
7
|
+
/**
|
|
8
|
+
* Get the multiplier for an observation type.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getTypeMultiplier(type: Observation['type']): number;
|
|
11
|
+
/**
|
|
12
|
+
* Estimate token savings for a single observation.
|
|
13
|
+
* Formula: content_tokens × type_multiplier
|
|
14
|
+
*/
|
|
15
|
+
export declare function estimateSavingsForObservation(obs: {
|
|
16
|
+
content: string;
|
|
17
|
+
type: Observation['type'];
|
|
18
|
+
}): number;
|
|
19
|
+
/**
|
|
20
|
+
* Estimate total token savings for a list of observations.
|
|
21
|
+
* Returns raw savings without multiplier (contentTokens) and total with multiplier.
|
|
22
|
+
*/
|
|
23
|
+
export declare function estimateTotalSavings(observations: Array<{
|
|
24
|
+
content: string;
|
|
25
|
+
type: Observation['type'];
|
|
26
|
+
}>): {
|
|
27
|
+
/** Sum of raw content tokens across all observations */
|
|
28
|
+
contentTokens: number;
|
|
29
|
+
/** Sum of (content_tokens × type_multiplier) across all observations */
|
|
30
|
+
estimatedTokensSaved: number;
|
|
31
|
+
/** Number of observations used in the calculation */
|
|
32
|
+
observationCount: number;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Format token savings as a human-readable string for MCP responses.
|
|
36
|
+
*/
|
|
37
|
+
export declare function formatTokenSavings(savings: {
|
|
38
|
+
estimatedTokensSaved: number;
|
|
39
|
+
observationCount: number;
|
|
40
|
+
contentTokens: number;
|
|
41
|
+
}): string;
|
|
42
|
+
//# sourceMappingURL=token-savings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-savings.d.ts","sourceRoot":"","sources":["../src/token-savings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAsC3C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAEnE;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,GAAG,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,GAAG,MAAM,CAIzG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,GAAG;IACzG,wDAAwD;IACxD,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAgBA;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,CAsBT"}
|