@undefineds.co/xpod 0.3.22 → 0.3.24

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,553 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.WorkspaceJournaledSolidFsSyncer = exports.JournaledSolidFsSyncer = exports.SqliteSolidFsSyncJournal = void 0;
7
+ exports.resolveSolidFsJournalPath = resolveSolidFsJournalPath;
8
+ const node_crypto_1 = require("node:crypto");
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const SqliteRuntime_1 = require("../storage/SqliteRuntime");
11
+ const RdfContentTypes_1 = require("../storage/rdf/RdfContentTypes");
12
+ const SolidFsPathUtils_1 = require("./SolidFsPathUtils");
13
+ const DAY_MS = 24 * 60 * 60 * 1000;
14
+ const DEFAULT_DONE_RETENTION_MS = 7 * DAY_MS;
15
+ const DEFAULT_TOMBSTONE_RETENTION_MS = 30 * DAY_MS;
16
+ const DEFAULT_FAILED_PERMANENT_RETENTION_MS = 30 * DAY_MS;
17
+ /**
18
+ * Per-Pod SolidFS recovery journal.
19
+ *
20
+ * This is an outbox for derived work after the authority file is already
21
+ * committed. It stores enough metadata to replay index/remote refreshes, but
22
+ * never stores file bodies.
23
+ */
24
+ class SqliteSolidFsSyncJournal {
25
+ constructor(options) {
26
+ this.now = options.now ?? Date.now;
27
+ this.doneRetentionMs = options.doneRetentionMs ?? DEFAULT_DONE_RETENTION_MS;
28
+ this.tombstoneRetentionMs = options.tombstoneRetentionMs ?? DEFAULT_TOMBSTONE_RETENTION_MS;
29
+ this.failedPermanentRetentionMs = options.failedPermanentRetentionMs ?? DEFAULT_FAILED_PERMANENT_RETENTION_MS;
30
+ this.db = (0, SqliteRuntime_1.getSqliteRuntime)().openDatabase(options.path);
31
+ this.db.pragma('journal_mode = WAL');
32
+ this.db.pragma('busy_timeout = 5000');
33
+ this.initializeSchema();
34
+ }
35
+ close() {
36
+ this.db.close();
37
+ }
38
+ async recordLocalCommitted(change, workspace, txId) {
39
+ const normalizedChange = normalizeChange(change);
40
+ const afterHash = normalizedChange.type === 'deleted'
41
+ ? undefined
42
+ : await (0, SolidFsPathUtils_1.maybeFileVersion)(normalizedChange.sourcePath);
43
+ const opId = operationId(workspace.workspace, normalizedChange, afterHash);
44
+ const now = this.now();
45
+ const existing = this.getOperation(opId);
46
+ if (existing) {
47
+ return existing;
48
+ }
49
+ const journalWorkspace = journalWorkspaceSnapshot(workspace);
50
+ this.db.prepare(`
51
+ INSERT INTO sync_ops (
52
+ id,
53
+ tx_id,
54
+ workspace,
55
+ path,
56
+ op_type,
57
+ stage,
58
+ source_path,
59
+ resource,
60
+ content_type,
61
+ source,
62
+ projection,
63
+ source_version,
64
+ after_hash,
65
+ workspace_json,
66
+ change_json,
67
+ retry_count,
68
+ created_at,
69
+ updated_at
70
+ )
71
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)
72
+ `).run(opId, txId ?? null, workspace.workspace, normalizedChange.path, normalizedChange.type, 'local_committed', normalizedChange.sourcePath, normalizedChange.resource ?? null, normalizedChange.contentType ?? null, normalizedChange.source, normalizedChange.projection, normalizedChange.sourceVersion ?? null, afterHash ?? null, JSON.stringify(journalWorkspace), JSON.stringify(normalizedChange), now, now);
73
+ return this.getOperation(opId);
74
+ }
75
+ getOperation(id) {
76
+ const row = this.db.prepare(`
77
+ SELECT id, tx_id, workspace_json, change_json, stage, after_hash, retry_count,
78
+ last_error, created_at, updated_at, done_at
79
+ FROM sync_ops
80
+ WHERE id = ?
81
+ `).get(id);
82
+ return row ? rowToOperation(row) : undefined;
83
+ }
84
+ listOperations(stages) {
85
+ const rows = stages && stages.length > 0
86
+ ? this.db.prepare(`
87
+ SELECT id, tx_id, workspace_json, change_json, stage, after_hash, retry_count,
88
+ last_error, created_at, updated_at, done_at
89
+ FROM sync_ops
90
+ WHERE stage IN (${stages.map(() => '?').join(', ')})
91
+ ORDER BY created_at ASC, id ASC
92
+ `).all(...stages)
93
+ : this.db.prepare(`
94
+ SELECT id, tx_id, workspace_json, change_json, stage, after_hash, retry_count,
95
+ last_error, created_at, updated_at, done_at
96
+ FROM sync_ops
97
+ ORDER BY created_at ASC, id ASC
98
+ `).all();
99
+ return rows.map(rowToOperation);
100
+ }
101
+ listPending() {
102
+ return this.listOperations(['local_committed', 'failed_retryable']);
103
+ }
104
+ async markDone(id) {
105
+ const op = this.getOperation(id);
106
+ if (!op) {
107
+ return;
108
+ }
109
+ const now = this.now();
110
+ this.db.transaction(() => {
111
+ this.db.prepare(`
112
+ UPDATE sync_ops
113
+ SET stage = 'done',
114
+ last_error = NULL,
115
+ done_at = ?,
116
+ updated_at = ?,
117
+ tombstone_confirmed_at = CASE WHEN op_type = 'deleted' THEN ? ELSE tombstone_confirmed_at END
118
+ WHERE id = ?
119
+ `).run(now, now, now, id);
120
+ this.upsertCheckpoint(op, now);
121
+ })();
122
+ }
123
+ async markRetryableFailure(id, error) {
124
+ const now = this.now();
125
+ this.db.prepare(`
126
+ UPDATE sync_ops
127
+ SET stage = 'failed_retryable',
128
+ retry_count = retry_count + 1,
129
+ last_error = ?,
130
+ updated_at = ?
131
+ WHERE id = ?
132
+ `).run(errorMessage(error), now, id);
133
+ }
134
+ async markReconcileRequired(id, reason) {
135
+ const now = this.now();
136
+ this.db.prepare(`
137
+ UPDATE sync_ops
138
+ SET stage = 'reconcile_required',
139
+ last_error = ?,
140
+ updated_at = ?
141
+ WHERE id = ?
142
+ `).run(reason, now, id);
143
+ }
144
+ async markFailedPermanent(id, error) {
145
+ const now = this.now();
146
+ this.db.prepare(`
147
+ UPDATE sync_ops
148
+ SET stage = 'failed_permanent',
149
+ last_error = ?,
150
+ updated_at = ?
151
+ WHERE id = ?
152
+ `).run(errorMessage(error), now, id);
153
+ }
154
+ async replayPending(syncer, context) {
155
+ const result = {
156
+ attempted: 0,
157
+ completed: 0,
158
+ failed: 0,
159
+ reconcileRequired: 0,
160
+ };
161
+ for (const op of this.listPending()) {
162
+ result.attempted += 1;
163
+ const validation = await this.validateOperationForReplay(op);
164
+ if (validation) {
165
+ await this.markReconcileRequired(op.id, validation);
166
+ result.reconcileRequired += 1;
167
+ continue;
168
+ }
169
+ try {
170
+ await syncer.sync(op.change, op.workspace, context);
171
+ await this.markDone(op.id);
172
+ result.completed += 1;
173
+ }
174
+ catch (error) {
175
+ await this.markRetryableFailure(op.id, error);
176
+ result.failed += 1;
177
+ }
178
+ }
179
+ return result;
180
+ }
181
+ async bootstrapWorkspace(input) {
182
+ const projection = input.projection ?? 'direct';
183
+ const source = input.source ?? (0, SolidFsPathUtils_1.sourceForProjection)(projection, input.workspace);
184
+ const manifest = {
185
+ workspace: input.workspace,
186
+ cwd: input.cwd,
187
+ projection,
188
+ entries: [],
189
+ };
190
+ const snapshots = await (0, SolidFsPathUtils_1.snapshotDirectory)(input.cwd, input.shouldTrackPath);
191
+ const currentPaths = new Set(snapshots.map((snapshot) => snapshot.relativePath));
192
+ const result = {
193
+ scanned: snapshots.length,
194
+ enqueued: 0,
195
+ skipped: 0,
196
+ };
197
+ for (const snapshot of snapshots) {
198
+ const checkpoint = this.getCheckpoint(input.workspace, snapshot.relativePath);
199
+ if (checkpoint?.source_version === snapshot.version && !checkpoint.deleted_at) {
200
+ result.skipped += 1;
201
+ continue;
202
+ }
203
+ const op = await this.recordLocalCommitted({
204
+ path: snapshot.relativePath,
205
+ resource: (0, SolidFsPathUtils_1.resolveWorkspaceResource)(input.workspace, snapshot.relativePath),
206
+ source,
207
+ sourcePath: snapshot.absolutePath,
208
+ contentType: (0, SolidFsPathUtils_1.contentTypeForPath)(snapshot.relativePath),
209
+ projection,
210
+ type: 'created',
211
+ }, manifest);
212
+ result.enqueued += op.stage === 'done' ? 0 : 1;
213
+ }
214
+ const checkpoints = this.listCheckpoints(input.workspace);
215
+ for (const checkpoint of checkpoints) {
216
+ if (currentPaths.has(checkpoint.path) || checkpoint.deleted_at) {
217
+ continue;
218
+ }
219
+ const op = await this.recordLocalCommitted({
220
+ path: checkpoint.path,
221
+ resource: (0, SolidFsPathUtils_1.resolveWorkspaceResource)(input.workspace, checkpoint.path),
222
+ source,
223
+ sourcePath: node_path_1.default.join(input.cwd, checkpoint.path),
224
+ contentType: (0, SolidFsPathUtils_1.contentTypeForPath)(checkpoint.path),
225
+ projection,
226
+ type: 'deleted',
227
+ sourceVersion: checkpoint.source_version ?? undefined,
228
+ }, manifest);
229
+ result.enqueued += op.stage === 'done' ? 0 : 1;
230
+ }
231
+ return result;
232
+ }
233
+ async compact() {
234
+ const now = this.now();
235
+ const rows = this.db.prepare(`
236
+ SELECT id, workspace, path, op_type, after_hash, stage, done_at, updated_at, tombstone_confirmed_at
237
+ FROM sync_ops
238
+ WHERE stage IN ('done', 'failed_permanent')
239
+ ORDER BY created_at ASC
240
+ `).all();
241
+ const deletable = [];
242
+ for (const row of rows) {
243
+ if (row.stage === 'failed_permanent') {
244
+ if (row.updated_at <= now - this.failedPermanentRetentionMs) {
245
+ deletable.push(row.id);
246
+ }
247
+ continue;
248
+ }
249
+ if (!row.done_at) {
250
+ continue;
251
+ }
252
+ const checkpoint = this.getCheckpoint(row.workspace, row.path);
253
+ if (!checkpoint) {
254
+ continue;
255
+ }
256
+ if (row.op_type === 'deleted') {
257
+ if (row.done_at <= now - this.tombstoneRetentionMs &&
258
+ row.tombstone_confirmed_at &&
259
+ checkpoint.deleted_at) {
260
+ deletable.push(row.id);
261
+ }
262
+ continue;
263
+ }
264
+ if (row.done_at <= now - this.doneRetentionMs && checkpoint.source_version === row.after_hash) {
265
+ deletable.push(row.id);
266
+ }
267
+ }
268
+ if (deletable.length === 0) {
269
+ return { deletedOps: 0 };
270
+ }
271
+ this.db.transaction(() => {
272
+ const deleteStatement = this.db.prepare('DELETE FROM sync_ops WHERE id = ?');
273
+ for (const id of deletable) {
274
+ deleteStatement.run(id);
275
+ }
276
+ })();
277
+ return { deletedOps: deletable.length };
278
+ }
279
+ async validateOperationForReplay(op) {
280
+ if (op.change.type === 'deleted') {
281
+ return undefined;
282
+ }
283
+ const currentHash = await (0, SolidFsPathUtils_1.maybeFileVersion)(op.change.sourcePath);
284
+ if (!currentHash) {
285
+ return `SolidFS journal source is missing: ${op.change.sourcePath}`;
286
+ }
287
+ if (op.afterHash && currentHash !== op.afterHash) {
288
+ return `SolidFS journal source changed before replay: ${op.change.path}`;
289
+ }
290
+ return undefined;
291
+ }
292
+ getCheckpoint(workspace, relativePath) {
293
+ return this.db.prepare(`
294
+ SELECT source_version, deleted_at
295
+ FROM sync_checkpoints
296
+ WHERE workspace = ? AND path = ?
297
+ `).get(workspace, relativePath);
298
+ }
299
+ listCheckpoints(workspace) {
300
+ return this.db.prepare(`
301
+ SELECT path, source_version, deleted_at
302
+ FROM sync_checkpoints
303
+ WHERE workspace = ?
304
+ ORDER BY path ASC
305
+ `).all(workspace);
306
+ }
307
+ upsertCheckpoint(op, now) {
308
+ if (op.change.type === 'deleted') {
309
+ this.db.prepare(`
310
+ INSERT INTO sync_checkpoints (workspace, path, source_version, deleted_at, updated_at, last_op_id)
311
+ VALUES (?, ?, NULL, ?, ?, ?)
312
+ ON CONFLICT(workspace, path) DO UPDATE SET
313
+ source_version = NULL,
314
+ deleted_at = excluded.deleted_at,
315
+ updated_at = excluded.updated_at,
316
+ last_op_id = excluded.last_op_id
317
+ `).run(op.workspace.workspace, op.change.path, now, now, op.id);
318
+ return;
319
+ }
320
+ this.db.prepare(`
321
+ INSERT INTO sync_checkpoints (workspace, path, source_version, deleted_at, updated_at, last_op_id)
322
+ VALUES (?, ?, ?, NULL, ?, ?)
323
+ ON CONFLICT(workspace, path) DO UPDATE SET
324
+ source_version = excluded.source_version,
325
+ deleted_at = NULL,
326
+ updated_at = excluded.updated_at,
327
+ last_op_id = excluded.last_op_id
328
+ `).run(op.workspace.workspace, op.change.path, op.afterHash ?? null, now, op.id);
329
+ }
330
+ initializeSchema() {
331
+ this.db.exec(`
332
+ CREATE TABLE IF NOT EXISTS sync_ops (
333
+ id TEXT PRIMARY KEY,
334
+ tx_id TEXT,
335
+ workspace TEXT NOT NULL,
336
+ path TEXT NOT NULL,
337
+ op_type TEXT NOT NULL,
338
+ stage TEXT NOT NULL,
339
+ source_path TEXT NOT NULL,
340
+ resource TEXT,
341
+ content_type TEXT,
342
+ source TEXT NOT NULL,
343
+ projection TEXT NOT NULL,
344
+ source_version TEXT,
345
+ after_hash TEXT,
346
+ workspace_json TEXT NOT NULL,
347
+ change_json TEXT NOT NULL,
348
+ retry_count INTEGER NOT NULL DEFAULT 0,
349
+ last_error TEXT,
350
+ created_at INTEGER NOT NULL,
351
+ updated_at INTEGER NOT NULL,
352
+ done_at INTEGER,
353
+ tombstone_confirmed_at INTEGER
354
+ );
355
+
356
+ CREATE INDEX IF NOT EXISTS sync_ops_stage_created_idx
357
+ ON sync_ops(stage, created_at);
358
+ CREATE INDEX IF NOT EXISTS sync_ops_workspace_path_idx
359
+ ON sync_ops(workspace, path);
360
+
361
+ CREATE TABLE IF NOT EXISTS sync_checkpoints (
362
+ workspace TEXT NOT NULL,
363
+ path TEXT NOT NULL,
364
+ source_version TEXT,
365
+ deleted_at INTEGER,
366
+ updated_at INTEGER NOT NULL,
367
+ last_op_id TEXT,
368
+ PRIMARY KEY (workspace, path)
369
+ );
370
+ `);
371
+ }
372
+ }
373
+ exports.SqliteSolidFsSyncJournal = SqliteSolidFsSyncJournal;
374
+ class JournaledSolidFsSyncer {
375
+ constructor(options) {
376
+ this.syncer = options.syncer;
377
+ this.journal = options.journal;
378
+ }
379
+ shouldTrack(input) {
380
+ return this.syncer.shouldTrack?.(input) ?? true;
381
+ }
382
+ shouldTrackPath(relativePath) {
383
+ return this.syncer.shouldTrackPath?.(relativePath) ?? (0, RdfContentTypes_1.isLineAddressableRdfPath)(relativePath);
384
+ }
385
+ async sync(change, workspace, context) {
386
+ const op = await this.journal.recordLocalCommitted(change, workspace);
387
+ if (op.stage === 'done') {
388
+ return;
389
+ }
390
+ try {
391
+ await this.syncer.sync(change, workspace, context);
392
+ await this.journal.markDone(op.id);
393
+ }
394
+ catch (error) {
395
+ await this.journal.markRetryableFailure(op.id, error);
396
+ throw error;
397
+ }
398
+ }
399
+ async replayPending(context) {
400
+ return this.journal.replayPending(this.syncer, context);
401
+ }
402
+ async compact() {
403
+ return this.journal.compact();
404
+ }
405
+ async bootstrapWorkspace(input) {
406
+ return this.journal.bootstrapWorkspace({
407
+ ...input,
408
+ shouldTrackPath: input.shouldTrackPath ?? this.shouldTrackPath.bind(this),
409
+ });
410
+ }
411
+ }
412
+ exports.JournaledSolidFsSyncer = JournaledSolidFsSyncer;
413
+ class WorkspaceJournaledSolidFsSyncer {
414
+ constructor(options) {
415
+ this.options = options;
416
+ this.journals = new Map();
417
+ this.syncer = options.syncer;
418
+ }
419
+ shouldTrack(input) {
420
+ return this.syncer.shouldTrack?.(input) ?? true;
421
+ }
422
+ shouldTrackPath(relativePath) {
423
+ return this.syncer.shouldTrackPath?.(relativePath) ?? (0, RdfContentTypes_1.isLineAddressableRdfPath)(relativePath);
424
+ }
425
+ async initializeWorkspace(workspace, context) {
426
+ const journal = this.journalFor(workspace);
427
+ await journal.bootstrapWorkspace({
428
+ workspace: workspace.workspace,
429
+ cwd: workspace.cwd,
430
+ projection: workspace.projection,
431
+ source: (0, SolidFsPathUtils_1.sourceForProjection)(workspace.projection, workspace.workspace),
432
+ shouldTrackPath: this.shouldTrackPath.bind(this),
433
+ });
434
+ await journal.replayPending(this.syncer, context);
435
+ await journal.compact();
436
+ }
437
+ async sync(change, workspace, context) {
438
+ const journal = this.journalFor(workspace);
439
+ const op = await journal.recordLocalCommitted(change, workspace);
440
+ if (op.stage === 'done') {
441
+ return;
442
+ }
443
+ try {
444
+ await this.syncer.sync(change, workspace, context);
445
+ await journal.markDone(op.id);
446
+ }
447
+ catch (error) {
448
+ await journal.markRetryableFailure(op.id, error);
449
+ throw error;
450
+ }
451
+ }
452
+ async replayPending(workspace, context) {
453
+ return this.journalFor(workspace).replayPending(this.syncer, context);
454
+ }
455
+ async compact(workspace) {
456
+ return this.journalFor(workspace).compact();
457
+ }
458
+ journalPathFor(workspace) {
459
+ return this.options.resolveJournalPath?.(workspace)
460
+ ?? resolveSolidFsJournalPath(workspace, this.options.journalRoot ?? process.env.XPOD_SOLIDFS_JOURNAL_ROOT);
461
+ }
462
+ close() {
463
+ for (const journal of this.journals.values()) {
464
+ journal.close();
465
+ }
466
+ this.journals.clear();
467
+ }
468
+ journalFor(workspace) {
469
+ const journalPath = this.journalPathFor(workspace);
470
+ const existing = this.journals.get(journalPath);
471
+ if (existing) {
472
+ return existing;
473
+ }
474
+ const journal = new SqliteSolidFsSyncJournal({
475
+ path: journalPath,
476
+ now: this.options.now,
477
+ doneRetentionMs: this.options.doneRetentionMs,
478
+ tombstoneRetentionMs: this.options.tombstoneRetentionMs,
479
+ failedPermanentRetentionMs: this.options.failedPermanentRetentionMs,
480
+ });
481
+ this.journals.set(journalPath, journal);
482
+ return journal;
483
+ }
484
+ }
485
+ exports.WorkspaceJournaledSolidFsSyncer = WorkspaceJournaledSolidFsSyncer;
486
+ function resolveSolidFsJournalPath(workspace, journalRoot) {
487
+ const root = journalRoot
488
+ ? node_path_1.default.resolve(journalRoot)
489
+ : node_path_1.default.join(node_path_1.default.dirname(node_path_1.default.resolve(workspace.cwd)), '.xpod-control', 'solidfs-journals');
490
+ const basename = safeJournalSegment(node_path_1.default.basename(workspace.cwd) || 'workspace');
491
+ const key = (0, node_crypto_1.createHash)('sha256')
492
+ .update(workspace.workspace)
493
+ .digest('hex')
494
+ .slice(0, 16);
495
+ return node_path_1.default.join(root, `${basename}-${key}`, 'sync-journal.sqlite');
496
+ }
497
+ function rowToOperation(row) {
498
+ return {
499
+ id: row.id,
500
+ txId: row.tx_id ?? undefined,
501
+ workspace: JSON.parse(row.workspace_json),
502
+ change: JSON.parse(row.change_json),
503
+ stage: row.stage,
504
+ afterHash: row.after_hash ?? undefined,
505
+ retryCount: row.retry_count,
506
+ lastError: row.last_error ?? undefined,
507
+ createdAt: row.created_at,
508
+ updatedAt: row.updated_at,
509
+ doneAt: row.done_at ?? undefined,
510
+ };
511
+ }
512
+ function journalWorkspaceSnapshot(workspace) {
513
+ return {
514
+ workspace: workspace.workspace,
515
+ cwd: workspace.cwd,
516
+ projection: workspace.projection,
517
+ entries: [],
518
+ };
519
+ }
520
+ function normalizeChange(change) {
521
+ return {
522
+ ...change,
523
+ path: change.path.split(/[\\/]+/u).join(node_path_1.default.sep),
524
+ };
525
+ }
526
+ function operationId(workspace, change, afterHash) {
527
+ const digest = (0, node_crypto_1.createHash)('sha256')
528
+ .update(JSON.stringify({
529
+ workspace,
530
+ path: change.path,
531
+ type: change.type,
532
+ resource: change.resource,
533
+ source: change.source,
534
+ sourcePath: change.sourcePath,
535
+ projection: change.projection,
536
+ sourceVersion: change.sourceVersion,
537
+ afterHash,
538
+ }))
539
+ .digest('hex')
540
+ .slice(0, 32);
541
+ return `sync_${digest}`;
542
+ }
543
+ function safeJournalSegment(value) {
544
+ const safe = value.replace(/[^a-zA-Z0-9._-]+/gu, '_').slice(0, 64);
545
+ return safe || 'workspace';
546
+ }
547
+ function errorMessage(error) {
548
+ if (error instanceof Error) {
549
+ return error.message;
550
+ }
551
+ return String(error);
552
+ }
553
+ //# sourceMappingURL=SolidFsSyncJournal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SolidFsSyncJournal.js","sourceRoot":"","sources":["../../src/solidfs/SolidFsSyncJournal.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport path from 'node:path';\n\nimport { getSqliteRuntime, type SqliteDatabase } from '../storage/SqliteRuntime';\nimport type {\n SolidFsChange,\n SolidFsEntrySource,\n SolidFsManifest,\n SolidFsPrepareInput,\n SolidFsProjection,\n SolidFsSyncer,\n} from './types';\nimport {\n isLineAddressableRdfPath,\n} from '../storage/rdf/RdfContentTypes';\nimport {\n contentTypeForPath,\n maybeFileVersion,\n resolveWorkspaceResource,\n snapshotDirectory,\n sourceForProjection,\n} from './SolidFsPathUtils';\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_DONE_RETENTION_MS = 7 * DAY_MS;\nconst DEFAULT_TOMBSTONE_RETENTION_MS = 30 * DAY_MS;\nconst DEFAULT_FAILED_PERMANENT_RETENTION_MS = 30 * DAY_MS;\n\nexport type SolidFsSyncJournalStage =\n | 'local_committed'\n | 'failed_retryable'\n | 'failed_permanent'\n | 'reconcile_required'\n | 'done';\n\nexport interface SolidFsSyncJournalOptions {\n path: string;\n now?: () => number;\n doneRetentionMs?: number;\n tombstoneRetentionMs?: number;\n failedPermanentRetentionMs?: number;\n}\n\nexport interface SolidFsSyncJournalOperation {\n id: string;\n txId?: string;\n workspace: SolidFsManifest;\n change: SolidFsChange;\n stage: SolidFsSyncJournalStage;\n afterHash?: string;\n retryCount: number;\n lastError?: string;\n createdAt: number;\n updatedAt: number;\n doneAt?: number;\n}\n\nexport interface SolidFsJournalBootstrapInput {\n workspace: string;\n cwd: string;\n projection?: SolidFsProjection;\n source?: SolidFsEntrySource;\n shouldTrackPath?: (relativePath: string) => boolean;\n}\n\nexport interface SolidFsJournalBootstrapResult {\n scanned: number;\n enqueued: number;\n skipped: number;\n}\n\nexport interface SolidFsJournalReplayResult {\n attempted: number;\n completed: number;\n failed: number;\n reconcileRequired: number;\n}\n\nexport interface SolidFsJournalCompactResult {\n deletedOps: number;\n}\n\ninterface SyncOpRow {\n id: string;\n tx_id: string | null;\n workspace_json: string;\n change_json: string;\n stage: SolidFsSyncJournalStage;\n after_hash: string | null;\n retry_count: number;\n last_error: string | null;\n created_at: number;\n updated_at: number;\n done_at: number | null;\n}\n\ninterface CheckpointRow {\n source_version: string | null;\n deleted_at: number | null;\n}\n\n/**\n * Per-Pod SolidFS recovery journal.\n *\n * This is an outbox for derived work after the authority file is already\n * committed. It stores enough metadata to replay index/remote refreshes, but\n * never stores file bodies.\n */\nexport class SqliteSolidFsSyncJournal {\n private readonly db: SqliteDatabase;\n private readonly now: () => number;\n private readonly doneRetentionMs: number;\n private readonly tombstoneRetentionMs: number;\n private readonly failedPermanentRetentionMs: number;\n\n public constructor(options: SolidFsSyncJournalOptions) {\n this.now = options.now ?? Date.now;\n this.doneRetentionMs = options.doneRetentionMs ?? DEFAULT_DONE_RETENTION_MS;\n this.tombstoneRetentionMs = options.tombstoneRetentionMs ?? DEFAULT_TOMBSTONE_RETENTION_MS;\n this.failedPermanentRetentionMs = options.failedPermanentRetentionMs ?? DEFAULT_FAILED_PERMANENT_RETENTION_MS;\n this.db = getSqliteRuntime().openDatabase(options.path);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('busy_timeout = 5000');\n this.initializeSchema();\n }\n\n public close(): void {\n this.db.close();\n }\n\n public async recordLocalCommitted(\n change: SolidFsChange,\n workspace: SolidFsManifest,\n txId?: string,\n ): Promise<SolidFsSyncJournalOperation> {\n const normalizedChange = normalizeChange(change);\n const afterHash = normalizedChange.type === 'deleted'\n ? undefined\n : await maybeFileVersion(normalizedChange.sourcePath);\n const opId = operationId(workspace.workspace, normalizedChange, afterHash);\n const now = this.now();\n\n const existing = this.getOperation(opId);\n if (existing) {\n return existing;\n }\n\n const journalWorkspace = journalWorkspaceSnapshot(workspace);\n this.db.prepare(`\n INSERT INTO sync_ops (\n id,\n tx_id,\n workspace,\n path,\n op_type,\n stage,\n source_path,\n resource,\n content_type,\n source,\n projection,\n source_version,\n after_hash,\n workspace_json,\n change_json,\n retry_count,\n created_at,\n updated_at\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)\n `).run(\n opId,\n txId ?? null,\n workspace.workspace,\n normalizedChange.path,\n normalizedChange.type,\n 'local_committed',\n normalizedChange.sourcePath,\n normalizedChange.resource ?? null,\n normalizedChange.contentType ?? null,\n normalizedChange.source,\n normalizedChange.projection,\n normalizedChange.sourceVersion ?? null,\n afterHash ?? null,\n JSON.stringify(journalWorkspace),\n JSON.stringify(normalizedChange),\n now,\n now,\n );\n\n return this.getOperation(opId)!;\n }\n\n public getOperation(id: string): SolidFsSyncJournalOperation | undefined {\n const row = this.db.prepare<SyncOpRow>(`\n SELECT id, tx_id, workspace_json, change_json, stage, after_hash, retry_count,\n last_error, created_at, updated_at, done_at\n FROM sync_ops\n WHERE id = ?\n `).get(id);\n return row ? rowToOperation(row) : undefined;\n }\n\n public listOperations(stages?: SolidFsSyncJournalStage[]): SolidFsSyncJournalOperation[] {\n const rows = stages && stages.length > 0\n ? this.db.prepare<SyncOpRow>(`\n SELECT id, tx_id, workspace_json, change_json, stage, after_hash, retry_count,\n last_error, created_at, updated_at, done_at\n FROM sync_ops\n WHERE stage IN (${stages.map(() => '?').join(', ')})\n ORDER BY created_at ASC, id ASC\n `).all(...stages)\n : this.db.prepare<SyncOpRow>(`\n SELECT id, tx_id, workspace_json, change_json, stage, after_hash, retry_count,\n last_error, created_at, updated_at, done_at\n FROM sync_ops\n ORDER BY created_at ASC, id ASC\n `).all();\n return rows.map(rowToOperation);\n }\n\n public listPending(): SolidFsSyncJournalOperation[] {\n return this.listOperations(['local_committed', 'failed_retryable']);\n }\n\n public async markDone(id: string): Promise<void> {\n const op = this.getOperation(id);\n if (!op) {\n return;\n }\n\n const now = this.now();\n this.db.transaction(() => {\n this.db.prepare(`\n UPDATE sync_ops\n SET stage = 'done',\n last_error = NULL,\n done_at = ?,\n updated_at = ?,\n tombstone_confirmed_at = CASE WHEN op_type = 'deleted' THEN ? ELSE tombstone_confirmed_at END\n WHERE id = ?\n `).run(now, now, now, id);\n\n this.upsertCheckpoint(op, now);\n })();\n }\n\n public async markRetryableFailure(id: string, error: unknown): Promise<void> {\n const now = this.now();\n this.db.prepare(`\n UPDATE sync_ops\n SET stage = 'failed_retryable',\n retry_count = retry_count + 1,\n last_error = ?,\n updated_at = ?\n WHERE id = ?\n `).run(errorMessage(error), now, id);\n }\n\n public async markReconcileRequired(id: string, reason: string): Promise<void> {\n const now = this.now();\n this.db.prepare(`\n UPDATE sync_ops\n SET stage = 'reconcile_required',\n last_error = ?,\n updated_at = ?\n WHERE id = ?\n `).run(reason, now, id);\n }\n\n public async markFailedPermanent(id: string, error: unknown): Promise<void> {\n const now = this.now();\n this.db.prepare(`\n UPDATE sync_ops\n SET stage = 'failed_permanent',\n last_error = ?,\n updated_at = ?\n WHERE id = ?\n `).run(errorMessage(error), now, id);\n }\n\n public async replayPending(syncer: SolidFsSyncer, context?: unknown): Promise<SolidFsJournalReplayResult> {\n const result: SolidFsJournalReplayResult = {\n attempted: 0,\n completed: 0,\n failed: 0,\n reconcileRequired: 0,\n };\n\n for (const op of this.listPending()) {\n result.attempted += 1;\n const validation = await this.validateOperationForReplay(op);\n if (validation) {\n await this.markReconcileRequired(op.id, validation);\n result.reconcileRequired += 1;\n continue;\n }\n\n try {\n await syncer.sync(op.change, op.workspace, context);\n await this.markDone(op.id);\n result.completed += 1;\n } catch (error) {\n await this.markRetryableFailure(op.id, error);\n result.failed += 1;\n }\n }\n\n return result;\n }\n\n public async bootstrapWorkspace(input: SolidFsJournalBootstrapInput): Promise<SolidFsJournalBootstrapResult> {\n const projection = input.projection ?? 'direct';\n const source = input.source ?? sourceForProjection(projection, input.workspace);\n const manifest: SolidFsManifest = {\n workspace: input.workspace,\n cwd: input.cwd,\n projection,\n entries: [],\n };\n\n const snapshots = await snapshotDirectory(input.cwd, input.shouldTrackPath);\n const currentPaths = new Set(snapshots.map((snapshot) => snapshot.relativePath));\n const result: SolidFsJournalBootstrapResult = {\n scanned: snapshots.length,\n enqueued: 0,\n skipped: 0,\n };\n\n for (const snapshot of snapshots) {\n const checkpoint = this.getCheckpoint(input.workspace, snapshot.relativePath);\n if (checkpoint?.source_version === snapshot.version && !checkpoint.deleted_at) {\n result.skipped += 1;\n continue;\n }\n\n const op = await this.recordLocalCommitted({\n path: snapshot.relativePath,\n resource: resolveWorkspaceResource(input.workspace, snapshot.relativePath),\n source,\n sourcePath: snapshot.absolutePath,\n contentType: contentTypeForPath(snapshot.relativePath),\n projection,\n type: 'created',\n }, manifest);\n result.enqueued += op.stage === 'done' ? 0 : 1;\n }\n\n const checkpoints = this.listCheckpoints(input.workspace);\n for (const checkpoint of checkpoints) {\n if (currentPaths.has(checkpoint.path) || checkpoint.deleted_at) {\n continue;\n }\n const op = await this.recordLocalCommitted({\n path: checkpoint.path,\n resource: resolveWorkspaceResource(input.workspace, checkpoint.path),\n source,\n sourcePath: path.join(input.cwd, checkpoint.path),\n contentType: contentTypeForPath(checkpoint.path),\n projection,\n type: 'deleted',\n sourceVersion: checkpoint.source_version ?? undefined,\n }, manifest);\n result.enqueued += op.stage === 'done' ? 0 : 1;\n }\n\n return result;\n }\n\n public async compact(): Promise<SolidFsJournalCompactResult> {\n const now = this.now();\n const rows = this.db.prepare<{\n id: string;\n workspace: string;\n path: string;\n op_type: SolidFsChange['type'];\n after_hash: string | null;\n stage: SolidFsSyncJournalStage;\n done_at: number | null;\n updated_at: number;\n tombstone_confirmed_at: number | null;\n }>(`\n SELECT id, workspace, path, op_type, after_hash, stage, done_at, updated_at, tombstone_confirmed_at\n FROM sync_ops\n WHERE stage IN ('done', 'failed_permanent')\n ORDER BY created_at ASC\n `).all();\n\n const deletable: string[] = [];\n for (const row of rows) {\n if (row.stage === 'failed_permanent') {\n if (row.updated_at <= now - this.failedPermanentRetentionMs) {\n deletable.push(row.id);\n }\n continue;\n }\n\n if (!row.done_at) {\n continue;\n }\n const checkpoint = this.getCheckpoint(row.workspace, row.path);\n if (!checkpoint) {\n continue;\n }\n if (row.op_type === 'deleted') {\n if (\n row.done_at <= now - this.tombstoneRetentionMs &&\n row.tombstone_confirmed_at &&\n checkpoint.deleted_at\n ) {\n deletable.push(row.id);\n }\n continue;\n }\n\n if (row.done_at <= now - this.doneRetentionMs && checkpoint.source_version === row.after_hash) {\n deletable.push(row.id);\n }\n }\n\n if (deletable.length === 0) {\n return { deletedOps: 0 };\n }\n\n this.db.transaction(() => {\n const deleteStatement = this.db.prepare('DELETE FROM sync_ops WHERE id = ?');\n for (const id of deletable) {\n deleteStatement.run(id);\n }\n })();\n\n return { deletedOps: deletable.length };\n }\n\n private async validateOperationForReplay(op: SolidFsSyncJournalOperation): Promise<string | undefined> {\n if (op.change.type === 'deleted') {\n return undefined;\n }\n\n const currentHash = await maybeFileVersion(op.change.sourcePath);\n if (!currentHash) {\n return `SolidFS journal source is missing: ${op.change.sourcePath}`;\n }\n if (op.afterHash && currentHash !== op.afterHash) {\n return `SolidFS journal source changed before replay: ${op.change.path}`;\n }\n return undefined;\n }\n\n private getCheckpoint(workspace: string, relativePath: string): CheckpointRow | undefined {\n return this.db.prepare<CheckpointRow>(`\n SELECT source_version, deleted_at\n FROM sync_checkpoints\n WHERE workspace = ? AND path = ?\n `).get(workspace, relativePath);\n }\n\n private listCheckpoints(workspace: string): Array<CheckpointRow & { path: string }> {\n return this.db.prepare<CheckpointRow & { path: string }>(`\n SELECT path, source_version, deleted_at\n FROM sync_checkpoints\n WHERE workspace = ?\n ORDER BY path ASC\n `).all(workspace);\n }\n\n private upsertCheckpoint(op: SolidFsSyncJournalOperation, now: number): void {\n if (op.change.type === 'deleted') {\n this.db.prepare(`\n INSERT INTO sync_checkpoints (workspace, path, source_version, deleted_at, updated_at, last_op_id)\n VALUES (?, ?, NULL, ?, ?, ?)\n ON CONFLICT(workspace, path) DO UPDATE SET\n source_version = NULL,\n deleted_at = excluded.deleted_at,\n updated_at = excluded.updated_at,\n last_op_id = excluded.last_op_id\n `).run(op.workspace.workspace, op.change.path, now, now, op.id);\n return;\n }\n\n this.db.prepare(`\n INSERT INTO sync_checkpoints (workspace, path, source_version, deleted_at, updated_at, last_op_id)\n VALUES (?, ?, ?, NULL, ?, ?)\n ON CONFLICT(workspace, path) DO UPDATE SET\n source_version = excluded.source_version,\n deleted_at = NULL,\n updated_at = excluded.updated_at,\n last_op_id = excluded.last_op_id\n `).run(op.workspace.workspace, op.change.path, op.afterHash ?? null, now, op.id);\n }\n\n private initializeSchema(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS sync_ops (\n id TEXT PRIMARY KEY,\n tx_id TEXT,\n workspace TEXT NOT NULL,\n path TEXT NOT NULL,\n op_type TEXT NOT NULL,\n stage TEXT NOT NULL,\n source_path TEXT NOT NULL,\n resource TEXT,\n content_type TEXT,\n source TEXT NOT NULL,\n projection TEXT NOT NULL,\n source_version TEXT,\n after_hash TEXT,\n workspace_json TEXT NOT NULL,\n change_json TEXT NOT NULL,\n retry_count INTEGER NOT NULL DEFAULT 0,\n last_error TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n done_at INTEGER,\n tombstone_confirmed_at INTEGER\n );\n\n CREATE INDEX IF NOT EXISTS sync_ops_stage_created_idx\n ON sync_ops(stage, created_at);\n CREATE INDEX IF NOT EXISTS sync_ops_workspace_path_idx\n ON sync_ops(workspace, path);\n\n CREATE TABLE IF NOT EXISTS sync_checkpoints (\n workspace TEXT NOT NULL,\n path TEXT NOT NULL,\n source_version TEXT,\n deleted_at INTEGER,\n updated_at INTEGER NOT NULL,\n last_op_id TEXT,\n PRIMARY KEY (workspace, path)\n );\n `);\n }\n}\n\nexport interface JournaledSolidFsSyncerOptions {\n syncer: SolidFsSyncer;\n journal: SqliteSolidFsSyncJournal;\n}\n\nexport interface WorkspaceJournaledSolidFsSyncerOptions {\n syncer: SolidFsSyncer;\n journalRoot?: string;\n resolveJournalPath?: (workspace: SolidFsManifest) => string;\n now?: () => number;\n doneRetentionMs?: number;\n tombstoneRetentionMs?: number;\n failedPermanentRetentionMs?: number;\n}\n\nexport class JournaledSolidFsSyncer implements SolidFsSyncer {\n private readonly syncer: SolidFsSyncer;\n private readonly journal: SqliteSolidFsSyncJournal;\n\n public constructor(options: JournaledSolidFsSyncerOptions) {\n this.syncer = options.syncer;\n this.journal = options.journal;\n }\n\n public shouldTrack(input: SolidFsPrepareInput): boolean {\n return this.syncer.shouldTrack?.(input) ?? true;\n }\n\n public shouldTrackPath(relativePath: string): boolean {\n return this.syncer.shouldTrackPath?.(relativePath) ?? isLineAddressableRdfPath(relativePath);\n }\n\n public async sync(change: SolidFsChange, workspace: SolidFsManifest, context?: unknown): Promise<void> {\n const op = await this.journal.recordLocalCommitted(change, workspace);\n if (op.stage === 'done') {\n return;\n }\n\n try {\n await this.syncer.sync(change, workspace, context);\n await this.journal.markDone(op.id);\n } catch (error) {\n await this.journal.markRetryableFailure(op.id, error);\n throw error;\n }\n }\n\n public async replayPending(context?: unknown): Promise<SolidFsJournalReplayResult> {\n return this.journal.replayPending(this.syncer, context);\n }\n\n public async compact(): Promise<SolidFsJournalCompactResult> {\n return this.journal.compact();\n }\n\n public async bootstrapWorkspace(\n input: Omit<SolidFsJournalBootstrapInput, 'shouldTrackPath'> & {\n shouldTrackPath?: (relativePath: string) => boolean;\n },\n ): Promise<SolidFsJournalBootstrapResult> {\n return this.journal.bootstrapWorkspace({\n ...input,\n shouldTrackPath: input.shouldTrackPath ?? this.shouldTrackPath.bind(this),\n });\n }\n}\n\nexport class WorkspaceJournaledSolidFsSyncer implements SolidFsSyncer {\n private readonly syncer: SolidFsSyncer;\n private readonly journals = new Map<string, SqliteSolidFsSyncJournal>();\n\n public constructor(private readonly options: WorkspaceJournaledSolidFsSyncerOptions) {\n this.syncer = options.syncer;\n }\n\n public shouldTrack(input: SolidFsPrepareInput): boolean {\n return this.syncer.shouldTrack?.(input) ?? true;\n }\n\n public shouldTrackPath(relativePath: string): boolean {\n return this.syncer.shouldTrackPath?.(relativePath) ?? isLineAddressableRdfPath(relativePath);\n }\n\n public async initializeWorkspace(workspace: SolidFsManifest, context?: unknown): Promise<void> {\n const journal = this.journalFor(workspace);\n await journal.bootstrapWorkspace({\n workspace: workspace.workspace,\n cwd: workspace.cwd,\n projection: workspace.projection,\n source: sourceForProjection(workspace.projection, workspace.workspace),\n shouldTrackPath: this.shouldTrackPath.bind(this),\n });\n await journal.replayPending(this.syncer, context);\n await journal.compact();\n }\n\n public async sync(change: SolidFsChange, workspace: SolidFsManifest, context?: unknown): Promise<void> {\n const journal = this.journalFor(workspace);\n const op = await journal.recordLocalCommitted(change, workspace);\n if (op.stage === 'done') {\n return;\n }\n\n try {\n await this.syncer.sync(change, workspace, context);\n await journal.markDone(op.id);\n } catch (error) {\n await journal.markRetryableFailure(op.id, error);\n throw error;\n }\n }\n\n public async replayPending(workspace: SolidFsManifest, context?: unknown): Promise<SolidFsJournalReplayResult> {\n return this.journalFor(workspace).replayPending(this.syncer, context);\n }\n\n public async compact(workspace: SolidFsManifest): Promise<SolidFsJournalCompactResult> {\n return this.journalFor(workspace).compact();\n }\n\n public journalPathFor(workspace: SolidFsManifest): string {\n return this.options.resolveJournalPath?.(workspace)\n ?? resolveSolidFsJournalPath(workspace, this.options.journalRoot ?? process.env.XPOD_SOLIDFS_JOURNAL_ROOT);\n }\n\n public close(): void {\n for (const journal of this.journals.values()) {\n journal.close();\n }\n this.journals.clear();\n }\n\n private journalFor(workspace: SolidFsManifest): SqliteSolidFsSyncJournal {\n const journalPath = this.journalPathFor(workspace);\n const existing = this.journals.get(journalPath);\n if (existing) {\n return existing;\n }\n\n const journal = new SqliteSolidFsSyncJournal({\n path: journalPath,\n now: this.options.now,\n doneRetentionMs: this.options.doneRetentionMs,\n tombstoneRetentionMs: this.options.tombstoneRetentionMs,\n failedPermanentRetentionMs: this.options.failedPermanentRetentionMs,\n });\n this.journals.set(journalPath, journal);\n return journal;\n }\n}\n\nexport function resolveSolidFsJournalPath(workspace: SolidFsManifest, journalRoot?: string): string {\n const root = journalRoot\n ? path.resolve(journalRoot)\n : path.join(path.dirname(path.resolve(workspace.cwd)), '.xpod-control', 'solidfs-journals');\n const basename = safeJournalSegment(path.basename(workspace.cwd) || 'workspace');\n const key = createHash('sha256')\n .update(workspace.workspace)\n .digest('hex')\n .slice(0, 16);\n return path.join(root, `${basename}-${key}`, 'sync-journal.sqlite');\n}\n\nfunction rowToOperation(row: SyncOpRow): SolidFsSyncJournalOperation {\n return {\n id: row.id,\n txId: row.tx_id ?? undefined,\n workspace: JSON.parse(row.workspace_json) as SolidFsManifest,\n change: JSON.parse(row.change_json) as SolidFsChange,\n stage: row.stage,\n afterHash: row.after_hash ?? undefined,\n retryCount: row.retry_count,\n lastError: row.last_error ?? undefined,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n doneAt: row.done_at ?? undefined,\n };\n}\n\nfunction journalWorkspaceSnapshot(workspace: SolidFsManifest): SolidFsManifest {\n return {\n workspace: workspace.workspace,\n cwd: workspace.cwd,\n projection: workspace.projection,\n entries: [],\n };\n}\n\nfunction normalizeChange(change: SolidFsChange): SolidFsChange {\n return {\n ...change,\n path: change.path.split(/[\\\\/]+/u).join(path.sep),\n };\n}\n\nfunction operationId(workspace: string, change: SolidFsChange, afterHash?: string): string {\n const digest = createHash('sha256')\n .update(JSON.stringify({\n workspace,\n path: change.path,\n type: change.type,\n resource: change.resource,\n source: change.source,\n sourcePath: change.sourcePath,\n projection: change.projection,\n sourceVersion: change.sourceVersion,\n afterHash,\n }))\n .digest('hex')\n .slice(0, 32);\n return `sync_${digest}`;\n}\n\nfunction safeJournalSegment(value: string): string {\n const safe = value.replace(/[^a-zA-Z0-9._-]+/gu, '_').slice(0, 64);\n return safe || 'workspace';\n}\n\nfunction errorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n"]}
@@ -5,3 +5,4 @@ export * from './PodSolidFsHydrator';
5
5
  export * from './RdfIndexSolidFsSyncer';
6
6
  export * from './PodSolidFsSyncer';
7
7
  export * from './LocalFirstRdfRepresentationResolver';
8
+ export * from './SolidFsSyncJournal';
@@ -21,4 +21,5 @@ __exportStar(require("./PodSolidFsHydrator"), exports);
21
21
  __exportStar(require("./RdfIndexSolidFsSyncer"), exports);
22
22
  __exportStar(require("./PodSolidFsSyncer"), exports);
23
23
  __exportStar(require("./LocalFirstRdfRepresentationResolver"), exports);
24
+ __exportStar(require("./SolidFsSyncJournal"), exports);
24
25
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/solidfs/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,iDAA+B;AAC/B,yDAAuC;AACvC,uDAAqC;AACrC,0DAAwC;AACxC,qDAAmC;AACnC,wEAAsD","sourcesContent":["export * from './types';\nexport * from './LocalSolidFS';\nexport * from './PodSolidFsHttpClient';\nexport * from './PodSolidFsHydrator';\nexport * from './RdfIndexSolidFsSyncer';\nexport * from './PodSolidFsSyncer';\nexport * from './LocalFirstRdfRepresentationResolver';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/solidfs/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,iDAA+B;AAC/B,yDAAuC;AACvC,uDAAqC;AACrC,0DAAwC;AACxC,qDAAmC;AACnC,wEAAsD;AACtD,uDAAqC","sourcesContent":["export * from './types';\nexport * from './LocalSolidFS';\nexport * from './PodSolidFsHttpClient';\nexport * from './PodSolidFsHydrator';\nexport * from './RdfIndexSolidFsSyncer';\nexport * from './PodSolidFsSyncer';\nexport * from './LocalFirstRdfRepresentationResolver';\nexport * from './SolidFsSyncJournal';\n"]}
@@ -96,6 +96,7 @@ export interface SolidFS {
96
96
  export interface SolidFsSyncer {
97
97
  shouldTrack?(input: SolidFsPrepareInput): boolean;
98
98
  shouldTrackPath?(relativePath: string): boolean;
99
+ initializeWorkspace?(workspace: SolidFsManifest, context?: unknown): Promise<void>;
99
100
  sync(change: SolidFsChange, workspace: SolidFsManifest, context?: unknown): Promise<void>;
100
101
  }
101
102
  export interface SolidFsHydrateInput {