pushwork 1.1.4 → 2.0.0-a.sub.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.
Files changed (100) hide show
  1. package/CLAUDE.md +9 -5
  2. package/dist/cli.js +48 -55
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands.d.ts +5 -1
  5. package/dist/commands.d.ts.map +1 -1
  6. package/dist/commands.js +262 -263
  7. package/dist/commands.js.map +1 -1
  8. package/dist/core/change-detection.d.ts +1 -1
  9. package/dist/core/change-detection.d.ts.map +1 -1
  10. package/dist/core/change-detection.js +66 -103
  11. package/dist/core/change-detection.js.map +1 -1
  12. package/dist/core/config.d.ts +1 -1
  13. package/dist/core/config.d.ts.map +1 -1
  14. package/dist/core/config.js +14 -57
  15. package/dist/core/config.js.map +1 -1
  16. package/dist/core/index.d.ts +5 -5
  17. package/dist/core/index.d.ts.map +1 -1
  18. package/dist/core/index.js +5 -21
  19. package/dist/core/index.js.map +1 -1
  20. package/dist/core/move-detection.d.ts +2 -2
  21. package/dist/core/move-detection.d.ts.map +1 -1
  22. package/dist/core/move-detection.js +9 -13
  23. package/dist/core/move-detection.js.map +1 -1
  24. package/dist/core/snapshot.d.ts +1 -1
  25. package/dist/core/snapshot.d.ts.map +1 -1
  26. package/dist/core/snapshot.js +9 -46
  27. package/dist/core/snapshot.js.map +1 -1
  28. package/dist/core/sync-engine.d.ts +1 -1
  29. package/dist/core/sync-engine.d.ts.map +1 -1
  30. package/dist/core/sync-engine.js +113 -150
  31. package/dist/core/sync-engine.js.map +1 -1
  32. package/dist/index.d.ts +4 -4
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +4 -20
  35. package/dist/index.js.map +1 -1
  36. package/dist/types/config.d.ts +7 -6
  37. package/dist/types/config.d.ts.map +1 -1
  38. package/dist/types/config.js +1 -5
  39. package/dist/types/config.js.map +1 -1
  40. package/dist/types/documents.js +4 -7
  41. package/dist/types/documents.js.map +1 -1
  42. package/dist/types/index.d.ts +3 -3
  43. package/dist/types/index.d.ts.map +1 -1
  44. package/dist/types/index.js +3 -19
  45. package/dist/types/index.js.map +1 -1
  46. package/dist/types/snapshot.js +1 -2
  47. package/dist/utils/content.js +4 -8
  48. package/dist/utils/content.js.map +1 -1
  49. package/dist/utils/directory.js +5 -9
  50. package/dist/utils/directory.js.map +1 -1
  51. package/dist/utils/fs.d.ts +1 -1
  52. package/dist/utils/fs.d.ts.map +1 -1
  53. package/dist/utils/fs.js +34 -84
  54. package/dist/utils/fs.js.map +1 -1
  55. package/dist/utils/index.d.ts +4 -4
  56. package/dist/utils/index.d.ts.map +1 -1
  57. package/dist/utils/index.js +4 -20
  58. package/dist/utils/index.js.map +1 -1
  59. package/dist/utils/mime-types.js +5 -43
  60. package/dist/utils/mime-types.js.map +1 -1
  61. package/dist/utils/network-sync.d.ts +13 -8
  62. package/dist/utils/network-sync.d.ts.map +1 -1
  63. package/dist/utils/network-sync.js +65 -137
  64. package/dist/utils/network-sync.js.map +1 -1
  65. package/dist/utils/node-polyfills.d.ts +9 -0
  66. package/dist/utils/node-polyfills.d.ts.map +1 -0
  67. package/dist/utils/node-polyfills.js +9 -0
  68. package/dist/utils/node-polyfills.js.map +1 -0
  69. package/dist/utils/output.js +32 -39
  70. package/dist/utils/output.js.map +1 -1
  71. package/dist/utils/repo-factory.d.ts +8 -2
  72. package/dist/utils/repo-factory.d.ts.map +1 -1
  73. package/dist/utils/repo-factory.js +38 -47
  74. package/dist/utils/repo-factory.js.map +1 -1
  75. package/dist/utils/string-similarity.js +1 -5
  76. package/dist/utils/string-similarity.js.map +1 -1
  77. package/dist/utils/text-diff.js +5 -43
  78. package/dist/utils/text-diff.js.map +1 -1
  79. package/dist/utils/trace.js +6 -11
  80. package/dist/utils/trace.js.map +1 -1
  81. package/package.json +7 -5
  82. package/src/cli.ts +25 -34
  83. package/src/commands.ts +75 -11
  84. package/src/core/change-detection.ts +4 -4
  85. package/src/core/config.ts +2 -12
  86. package/src/core/index.ts +5 -5
  87. package/src/core/move-detection.ts +4 -4
  88. package/src/core/snapshot.ts +3 -3
  89. package/src/core/sync-engine.ts +11 -16
  90. package/src/index.ts +4 -4
  91. package/src/types/config.ts +8 -8
  92. package/src/types/index.ts +3 -3
  93. package/src/utils/directory.ts +1 -1
  94. package/src/utils/fs.ts +6 -4
  95. package/src/utils/index.ts +4 -4
  96. package/src/utils/network-sync.ts +62 -115
  97. package/src/utils/node-polyfills.ts +8 -0
  98. package/src/utils/repo-factory.ts +55 -10
  99. package/src/utils/trace.ts +1 -1
  100. package/tsconfig.json +2 -1
@@ -1,50 +1,14 @@
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 automerge_repo_1 = require("@automerge/automerge-repo");
38
- const A = __importStar(require("@automerge/automerge"));
39
- const types_1 = require("../types");
40
- const utils_1 = require("../utils");
41
- const content_1 = require("../utils/content");
42
- const network_sync_1 = require("../utils/network-sync");
43
- const snapshot_1 = require("./snapshot");
44
- const change_detection_1 = require("./change-detection");
45
- const move_detection_1 = require("./move-detection");
46
- const output_1 = require("../utils/output");
47
- const path = __importStar(require("path"));
1
+ import { parseAutomergeUrl, stringifyAutomergeUrl, } from "@automerge/automerge-repo";
2
+ import * as A from "@automerge/automerge";
3
+ import { ChangeType, FileType, } from "../types/index.js";
4
+ import { writeFileContent, removePath, getFileExtension, getEnhancedMimeType, formatRelativePath, findFileInDirectoryHierarchy, joinAndNormalizePath, getPlainUrl, updateTextContent, readDocContent, } from "../utils/index.js";
5
+ import { isContentEqual, contentHash } from "../utils/content.js";
6
+ import { waitForSync, waitForBidirectionalSync } from "../utils/network-sync.js";
7
+ import { SnapshotManager } from "./snapshot.js";
8
+ import { ChangeDetector } from "./change-detection.js";
9
+ import { MoveDetector } from "./move-detection.js";
10
+ import { out } from "../utils/output.js";
11
+ import * as path from "path";
48
12
  const isDebug = !!process.env.DEBUG;
49
13
  function debug(...args) {
50
14
  if (isDebug)
@@ -69,7 +33,7 @@ const BIDIRECTIONAL_SYNC_TIMEOUT_MS = 5000; // Timeout for bidirectional sync st
69
33
  /**
70
34
  * Bidirectional sync engine implementing two-phase sync
71
35
  */
72
- class SyncEngine {
36
+ export class SyncEngine {
73
37
  constructor(repo, rootPath, config) {
74
38
  this.repo = repo;
75
39
  this.rootPath = rootPath;
@@ -77,9 +41,9 @@ class SyncEngine {
77
41
  // Path depth determines sync order (deepest first)
78
42
  this.handlesByPath = new Map();
79
43
  this.config = config;
80
- this.snapshotManager = new snapshot_1.SnapshotManager(rootPath);
81
- this.changeDetector = new change_detection_1.ChangeDetector(repo, rootPath, config.exclude_patterns, config.artifact_directories || []);
82
- this.moveDetector = new move_detection_1.MoveDetector(config.sync.move_detection_threshold);
44
+ this.snapshotManager = new SnapshotManager(rootPath);
45
+ this.changeDetector = new ChangeDetector(repo, rootPath, config.exclude_patterns, config.artifact_directories || []);
46
+ this.moveDetector = new MoveDetector(config.sync.move_detection_threshold);
83
47
  }
84
48
  /**
85
49
  * Determine if content should be treated as text for Automerge text operations
@@ -95,9 +59,9 @@ class SyncEngine {
95
59
  * This ensures clients can fetch the exact version of the document.
96
60
  */
97
61
  getVersionedUrl(handle) {
98
- const { documentId } = (0, automerge_repo_1.parseAutomergeUrl)(handle.url);
62
+ const { documentId } = parseAutomergeUrl(handle.url);
99
63
  const heads = handle.heads();
100
- return (0, automerge_repo_1.stringifyAutomergeUrl)({ documentId, heads });
64
+ return stringifyAutomergeUrl({ documentId, heads });
101
65
  }
102
66
  /**
103
67
  * Determine if a file path is inside an artifact directory.
@@ -117,7 +81,7 @@ class SyncEngine {
117
81
  if (this.isArtifactPath(filePath)) {
118
82
  return this.getVersionedUrl(handle);
119
83
  }
120
- return (0, utils_1.getPlainUrl)(handle.url);
84
+ return getPlainUrl(handle.url);
121
85
  }
122
86
  /**
123
87
  * Set the root directory URL in the snapshot
@@ -153,7 +117,7 @@ class SyncEngine {
153
117
  return;
154
118
  // Clear the root directory document's entries
155
119
  if (snapshot.rootDirectoryUrl) {
156
- const rootHandle = await this.repo.find((0, utils_1.getPlainUrl)(snapshot.rootDirectoryUrl));
120
+ const rootHandle = await this.repo.find(getPlainUrl(snapshot.rootDirectoryUrl));
157
121
  rootHandle.change((doc) => {
158
122
  doc.docs.splice(0, doc.docs.length);
159
123
  });
@@ -216,15 +180,15 @@ class SyncEngine {
216
180
  * Returns new handles that should be retried for sync.
217
181
  */
218
182
  async recreateFailedDocuments(failedHandles, snapshot) {
219
- const failedUrls = new Set(failedHandles.map(h => (0, utils_1.getPlainUrl)(h.url)));
183
+ const failedUrls = new Set(failedHandles.map(h => getPlainUrl(h.url)));
220
184
  const newHandles = [];
221
185
  // Find which paths correspond to the failed handles
222
186
  for (const [filePath, entry] of snapshot.files.entries()) {
223
- const plainUrl = (0, utils_1.getPlainUrl)(entry.url);
187
+ const plainUrl = getPlainUrl(entry.url);
224
188
  if (!failedUrls.has(plainUrl))
225
189
  continue;
226
190
  debug(`recreate: recreating document for ${filePath} (${plainUrl})`);
227
- output_1.out.taskLine(`Recreating document for ${filePath}`);
191
+ out.taskLine(`Recreating document for ${filePath}`);
228
192
  try {
229
193
  // Read the current content from the old handle
230
194
  const oldHandle = await this.repo.find(plainUrl);
@@ -233,7 +197,7 @@ class SyncEngine {
233
197
  debug(`recreate: could not read doc for ${filePath}, skipping`);
234
198
  continue;
235
199
  }
236
- const content = (0, utils_1.readDocContent)(doc.content);
200
+ const content = readDocContent(doc.content);
237
201
  if (content === null) {
238
202
  debug(`recreate: null content for ${filePath}, skipping`);
239
203
  continue;
@@ -241,8 +205,8 @@ class SyncEngine {
241
205
  // Create a fresh document
242
206
  const fakeChange = {
243
207
  path: filePath,
244
- changeType: types_1.ChangeType.LOCAL_ONLY,
245
- fileType: this.isTextContent(content) ? types_1.FileType.TEXT : types_1.FileType.BINARY,
208
+ changeType: ChangeType.LOCAL_ONLY,
209
+ fileType: this.isTextContent(content) ? FileType.TEXT : FileType.BINARY,
246
210
  localContent: content,
247
211
  remoteContent: null,
248
212
  };
@@ -255,7 +219,7 @@ class SyncEngine {
255
219
  ...entry,
256
220
  url: entryUrl,
257
221
  head: newHandle.heads(),
258
- ...(this.isArtifactPath(filePath) ? { contentHash: (0, content_1.contentHash)(content) } : {}),
222
+ ...(this.isArtifactPath(filePath) ? { contentHash: contentHash(content) } : {}),
259
223
  });
260
224
  // Update parent directory entry to point to new document
261
225
  const pathParts = filePath.split("/");
@@ -271,7 +235,7 @@ class SyncEngine {
271
235
  continue;
272
236
  dirUrl = dirEntry.url;
273
237
  }
274
- const dirHandle = await this.repo.find((0, utils_1.getPlainUrl)(dirUrl));
238
+ const dirHandle = await this.repo.find(getPlainUrl(dirUrl));
275
239
  dirHandle.change((d) => {
276
240
  const idx = d.docs.findIndex(e => e.name === fileName && e.type === "file");
277
241
  if (idx !== -1) {
@@ -287,18 +251,18 @@ class SyncEngine {
287
251
  }
288
252
  catch (error) {
289
253
  debug(`recreate: failed for ${filePath}: ${error}`);
290
- output_1.out.taskLine(`Failed to recreate ${filePath}: ${error}`, true);
254
+ out.taskLine(`Failed to recreate ${filePath}: ${error}`, true);
291
255
  }
292
256
  }
293
257
  // Also check directory documents
294
258
  for (const [dirPath, entry] of snapshot.directories.entries()) {
295
- const plainUrl = (0, utils_1.getPlainUrl)(entry.url);
259
+ const plainUrl = getPlainUrl(entry.url);
296
260
  if (!failedUrls.has(plainUrl))
297
261
  continue;
298
262
  // Directory docs can't be easily recreated (they reference children).
299
263
  // Just log a warning — the child recreation above should handle most cases.
300
264
  debug(`recreate: directory ${dirPath || "(root)"} failed to sync, cannot recreate`);
301
- output_1.out.taskLine(`Warning: directory ${dirPath || "(root)"} failed to sync`, true);
265
+ out.taskLine(`Warning: directory ${dirPath || "(root)"} failed to sync`, true);
302
266
  }
303
267
  return newHandles;
304
268
  }
@@ -324,12 +288,12 @@ class SyncEngine {
324
288
  // Wait for initial sync to receive any pending remote changes
325
289
  if (this.config.sync_enabled && snapshot.rootDirectoryUrl) {
326
290
  debug("sync: waiting for root document to be ready");
327
- output_1.out.update("Waiting for root document from server");
291
+ out.update("Waiting for root document from server");
328
292
  // Wait for the root document to be fetched from the network.
329
293
  // repo.find() rejects with "unavailable" if the server doesn't
330
294
  // have the document yet, so we retry with backoff.
331
295
  // This is critical for clone scenarios.
332
- const plainRootUrl = (0, utils_1.getPlainUrl)(snapshot.rootDirectoryUrl);
296
+ const plainRootUrl = getPlainUrl(snapshot.rootDirectoryUrl);
333
297
  const maxAttempts = 6;
334
298
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
335
299
  try {
@@ -343,32 +307,32 @@ class SyncEngine {
343
307
  if (isUnavailable && attempt < maxAttempts) {
344
308
  const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
345
309
  debug(`sync: root document not available (attempt ${attempt}/${maxAttempts}), retrying in ${delay}ms`);
346
- output_1.out.update(`Waiting for root document (attempt ${attempt}/${maxAttempts})`);
310
+ out.update(`Waiting for root document (attempt ${attempt}/${maxAttempts})`);
347
311
  await new Promise(r => setTimeout(r, delay));
348
312
  }
349
313
  else {
350
314
  debug(`sync: root document unavailable after ${maxAttempts} attempts: ${error}`);
351
- output_1.out.taskLine(`Root document unavailable: ${error}`, true);
315
+ out.taskLine(`Root document unavailable: ${error}`, true);
352
316
  break;
353
317
  }
354
318
  }
355
319
  }
356
320
  debug("sync: waiting for initial bidirectional sync");
357
- output_1.out.update("Waiting for initial sync from server");
321
+ out.update("Waiting for initial sync from server");
358
322
  try {
359
- await (0, network_sync_1.waitForBidirectionalSync)(this.repo, snapshot.rootDirectoryUrl, this.config.sync_server_storage_id, {
323
+ await waitForBidirectionalSync(this.repo, snapshot.rootDirectoryUrl, {
360
324
  timeoutMs: 5000, // Increased timeout for initial sync
361
325
  pollIntervalMs: 100,
362
326
  stableChecksRequired: 3,
363
327
  });
364
328
  }
365
329
  catch (error) {
366
- output_1.out.taskLine(`Initial sync: ${error}`, true);
330
+ out.taskLine(`Initial sync: ${error}`, true);
367
331
  }
368
332
  }
369
333
  // Detect all changes
370
334
  debug("sync: detecting changes");
371
- output_1.out.update("Detecting local and remote changes");
335
+ out.update("Detecting local and remote changes");
372
336
  // Capture pre-push snapshot file paths to detect deletions after push
373
337
  const prePushFilePaths = new Set(snapshot.files.keys());
374
338
  const changes = await this.changeDetector.detectChanges(snapshot);
@@ -396,17 +360,17 @@ class SyncEngine {
396
360
  const allHandles = Array.from(this.handlesByPath.values());
397
361
  const handlePaths = Array.from(this.handlesByPath.keys());
398
362
  debug(`sync: waiting for ${allHandles.length} handles to sync to server: ${handlePaths.slice(0, 10).map(p => p || "(root)").join(", ")}${handlePaths.length > 10 ? ` ...and ${handlePaths.length - 10} more` : ""}`);
399
- output_1.out.update(`Uploading ${allHandles.length} documents to sync server`);
400
- const { failed } = await (0, network_sync_1.waitForSync)(allHandles, this.config.sync_server_storage_id);
363
+ out.update(`Uploading ${allHandles.length} documents to sync server`);
364
+ const { failed } = await waitForSync(allHandles);
401
365
  // Recreate failed documents and retry once
402
366
  if (failed.length > 0) {
403
367
  debug(`sync: ${failed.length} documents failed, recreating`);
404
- output_1.out.update(`Recreating ${failed.length} failed documents`);
368
+ out.update(`Recreating ${failed.length} failed documents`);
405
369
  const retryHandles = await this.recreateFailedDocuments(failed, snapshot);
406
370
  if (retryHandles.length > 0) {
407
371
  debug(`sync: retrying ${retryHandles.length} recreated handles`);
408
- output_1.out.update(`Retrying ${retryHandles.length} recreated documents`);
409
- const retry = await (0, network_sync_1.waitForSync)(retryHandles, this.config.sync_server_storage_id);
372
+ out.update(`Retrying ${retryHandles.length} recreated documents`);
373
+ const retry = await waitForSync(retryHandles);
410
374
  if (retry.failed.length > 0) {
411
375
  const msg = `${retry.failed.length} documents failed to sync to server after recreation`;
412
376
  debug(`sync: ${msg}`);
@@ -425,8 +389,8 @@ class SyncEngine {
425
389
  // Use tracked handles for post-push check (cheaper than full tree scan)
426
390
  const changedHandles = Array.from(this.handlesByPath.values());
427
391
  debug(`sync: waiting for bidirectional sync to stabilize (${changedHandles.length} tracked handles)`);
428
- output_1.out.update("Waiting for bidirectional sync to stabilize");
429
- await (0, network_sync_1.waitForBidirectionalSync)(this.repo, snapshot.rootDirectoryUrl, this.config.sync_server_storage_id, {
392
+ out.update("Waiting for bidirectional sync to stabilize");
393
+ await waitForBidirectionalSync(this.repo, snapshot.rootDirectoryUrl, {
430
394
  timeoutMs: BIDIRECTIONAL_SYNC_TIMEOUT_MS,
431
395
  pollIntervalMs: 100,
432
396
  stableChecksRequired: 3,
@@ -441,13 +405,13 @@ class SyncEngine {
441
405
  await this.touchRootDirectory(snapshot);
442
406
  const rootHandle = await this.repo.find(snapshot.rootDirectoryUrl);
443
407
  debug("sync: syncing root directory touch to server");
444
- output_1.out.update("Syncing root directory update");
445
- await (0, network_sync_1.waitForSync)([rootHandle], this.config.sync_server_storage_id);
408
+ out.update("Syncing root directory update");
409
+ await waitForSync([rootHandle]);
446
410
  }
447
411
  }
448
412
  catch (error) {
449
413
  debug(`sync: network sync error: ${error}`);
450
- output_1.out.taskLine(`Network sync failed: ${error}`, true);
414
+ out.taskLine(`Network sync failed: ${error}`, true);
451
415
  result.errors.push({
452
416
  path: "sync",
453
417
  operation: "network-sync",
@@ -469,11 +433,11 @@ class SyncEngine {
469
433
  }
470
434
  debug("sync: re-detecting changes after network sync");
471
435
  const freshChanges = await this.changeDetector.detectChanges(snapshot, deletedPaths);
472
- const freshRemoteChanges = freshChanges.filter(c => c.changeType === types_1.ChangeType.REMOTE_ONLY ||
473
- c.changeType === types_1.ChangeType.BOTH_CHANGED);
436
+ const freshRemoteChanges = freshChanges.filter(c => c.changeType === ChangeType.REMOTE_ONLY ||
437
+ c.changeType === ChangeType.BOTH_CHANGED);
474
438
  debug(`sync: phase 2 - pulling ${freshRemoteChanges.length} remote changes`);
475
439
  if (freshRemoteChanges.length > 0) {
476
- output_1.out.update(`Pulling ${freshRemoteChanges.length} remote changes`);
440
+ out.update(`Pulling ${freshRemoteChanges.length} remote changes`);
477
441
  }
478
442
  // Phase 2: Pull remote changes to local using fresh detection
479
443
  const phase2Result = await this.pullRemoteChanges(freshRemoteChanges, snapshot);
@@ -492,7 +456,7 @@ class SyncEngine {
492
456
  // can't find the entries to splice out).
493
457
  for (const [filePath, snapshotEntry] of snapshot.files.entries()) {
494
458
  try {
495
- const handle = await this.repo.find((0, utils_1.getPlainUrl)(snapshotEntry.url));
459
+ const handle = await this.repo.find(getPlainUrl(snapshotEntry.url));
496
460
  const currentHeads = handle.heads();
497
461
  if (!A.equals(currentHeads, snapshotEntry.head)) {
498
462
  // Update snapshot with current heads after pulling changes
@@ -509,7 +473,7 @@ class SyncEngine {
509
473
  // Update directory document heads
510
474
  for (const [dirPath, snapshotEntry] of snapshot.directories.entries()) {
511
475
  try {
512
- const handle = await this.repo.find((0, utils_1.getPlainUrl)(snapshotEntry.url));
476
+ const handle = await this.repo.find(getPlainUrl(snapshotEntry.url));
513
477
  const currentHeads = handle.heads();
514
478
  if (!A.equals(currentHeads, snapshotEntry.head)) {
515
479
  // Update snapshot with current heads after pulling changes
@@ -557,13 +521,13 @@ class SyncEngine {
557
521
  // Process moves first - all detected moves are applied
558
522
  if (moves.length > 0) {
559
523
  debug(`push: processing ${moves.length} moves`);
560
- output_1.out.update(`Processing ${moves.length} move${moves.length > 1 ? "s" : ""}`);
524
+ out.update(`Processing ${moves.length} move${moves.length > 1 ? "s" : ""}`);
561
525
  }
562
526
  for (let i = 0; i < moves.length; i++) {
563
527
  const move = moves[i];
564
528
  try {
565
529
  debug(`push: move ${i + 1}/${moves.length}: ${move.fromPath} -> ${move.toPath}`);
566
- output_1.out.taskLine(`Moving ${move.fromPath} -> ${move.toPath}`);
530
+ out.taskLine(`Moving ${move.fromPath} -> ${move.toPath}`);
567
531
  await this.applyMoveToRemote(move, snapshot);
568
532
  result.filesChanged++;
569
533
  }
@@ -578,8 +542,8 @@ class SyncEngine {
578
542
  }
579
543
  }
580
544
  // Filter to local changes only
581
- const localChanges = changes.filter(c => c.changeType === types_1.ChangeType.LOCAL_ONLY ||
582
- c.changeType === types_1.ChangeType.BOTH_CHANGED);
545
+ const localChanges = changes.filter(c => c.changeType === ChangeType.LOCAL_ONLY ||
546
+ c.changeType === ChangeType.BOTH_CHANGED);
583
547
  if (localChanges.length === 0) {
584
548
  debug("push: no local changes to push");
585
549
  return result;
@@ -588,7 +552,7 @@ class SyncEngine {
588
552
  const modifiedFiles = localChanges.filter(c => snapshot.files.has(c.path) && c.localContent !== null);
589
553
  const deletedFiles = localChanges.filter(c => c.localContent === null && snapshot.files.has(c.path));
590
554
  debug(`push: ${localChanges.length} local changes (${newFiles.length} new, ${modifiedFiles.length} modified, ${deletedFiles.length} deleted)`);
591
- output_1.out.update(`Pushing ${localChanges.length} local changes (${newFiles.length} new, ${modifiedFiles.length} modified, ${deletedFiles.length} deleted)`);
555
+ out.update(`Pushing ${localChanges.length} local changes (${newFiles.length} new, ${modifiedFiles.length} modified, ${deletedFiles.length} deleted)`);
592
556
  // Group changes by parent directory path
593
557
  const changesByDir = new Map();
594
558
  for (const change of localChanges) {
@@ -647,7 +611,7 @@ class SyncEngine {
647
611
  if (change.localContent === null && snapshotEntry) {
648
612
  // Delete file
649
613
  debug(`push: [${filesProcessed}/${totalFiles}] delete ${change.path}`);
650
- output_1.out.update(`Pushing local changes [${filesProcessed}/${totalFiles}] deleting ${change.path}`);
614
+ out.update(`Pushing local changes [${filesProcessed}/${totalFiles}] deleting ${change.path}`);
651
615
  await this.deleteRemoteFile(snapshotEntry.url, snapshot, change.path);
652
616
  deletedNames.push(fileName);
653
617
  this.snapshotManager.removeFileEntry(snapshot, change.path);
@@ -656,19 +620,19 @@ class SyncEngine {
656
620
  else if (!snapshotEntry) {
657
621
  // New file
658
622
  debug(`push: [${filesProcessed}/${totalFiles}] create ${change.path} (${change.fileType})`);
659
- output_1.out.update(`Pushing local changes [${filesProcessed}/${totalFiles}] creating ${change.path}`);
623
+ out.update(`Pushing local changes [${filesProcessed}/${totalFiles}] creating ${change.path}`);
660
624
  const handle = await this.createRemoteFile(change);
661
625
  if (handle) {
662
626
  const entryUrl = this.getEntryUrl(handle, change.path);
663
627
  newEntries.push({ name: fileName, url: entryUrl });
664
628
  this.snapshotManager.updateFileEntry(snapshot, change.path, {
665
- path: (0, utils_1.joinAndNormalizePath)(this.rootPath, change.path),
629
+ path: joinAndNormalizePath(this.rootPath, change.path),
666
630
  url: entryUrl,
667
631
  head: handle.heads(),
668
- extension: (0, utils_1.getFileExtension)(change.path),
669
- mimeType: (0, utils_1.getEnhancedMimeType)(change.path),
632
+ extension: getFileExtension(change.path),
633
+ mimeType: getEnhancedMimeType(change.path),
670
634
  ...(this.isArtifactPath(change.path) && change.localContent
671
- ? { contentHash: (0, content_1.contentHash)(change.localContent) }
635
+ ? { contentHash: contentHash(change.localContent) }
672
636
  : {}),
673
637
  });
674
638
  result.filesChanged++;
@@ -681,12 +645,12 @@ class SyncEngine {
681
645
  ? `${change.localContent.length} chars`
682
646
  : `${change.localContent.length} bytes`;
683
647
  debug(`push: [${filesProcessed}/${totalFiles}] update ${change.path} (${contentSize})`);
684
- output_1.out.update(`Pushing local changes [${filesProcessed}/${totalFiles}] updating ${change.path}`);
648
+ out.update(`Pushing local changes [${filesProcessed}/${totalFiles}] updating ${change.path}`);
685
649
  await this.updateRemoteFile(snapshotEntry.url, change.localContent, snapshot, change.path);
686
650
  // Get current entry URL (updateRemoteFile updates snapshot)
687
651
  const updatedFileEntry = snapshot.files.get(change.path);
688
652
  if (updatedFileEntry) {
689
- const fileHandle = await this.repo.find((0, utils_1.getPlainUrl)(updatedFileEntry.url));
653
+ const fileHandle = await this.repo.find(getPlainUrl(updatedFileEntry.url));
690
654
  updatedEntries.push({
691
655
  name: fileName,
692
656
  url: this.getEntryUrl(fileHandle, change.path),
@@ -697,7 +661,7 @@ class SyncEngine {
697
661
  }
698
662
  catch (error) {
699
663
  debug(`push: error processing ${change.path}: ${error}`);
700
- output_1.out.taskLine(`Error pushing ${change.path}: ${error}`, true);
664
+ out.taskLine(`Error pushing ${change.path}: ${error}`, true);
701
665
  result.errors.push({
702
666
  path: change.path,
703
667
  operation: "local-to-remote",
@@ -716,7 +680,7 @@ class SyncEngine {
716
680
  if (parentOfModified === dirPath) {
717
681
  const dirEntry = snapshot.directories.get(modifiedDir);
718
682
  if (dirEntry) {
719
- const childHandle = await this.repo.find((0, utils_1.getPlainUrl)(dirEntry.url));
683
+ const childHandle = await this.repo.find(getPlainUrl(dirEntry.url));
720
684
  subdirUpdates.push({
721
685
  name: childName,
722
686
  url: this.getEntryUrl(childHandle, modifiedDir),
@@ -751,8 +715,8 @@ class SyncEngine {
751
715
  warnings: [],
752
716
  };
753
717
  // Process remote changes
754
- const remoteChanges = changes.filter(c => c.changeType === types_1.ChangeType.REMOTE_ONLY ||
755
- c.changeType === types_1.ChangeType.BOTH_CHANGED);
718
+ const remoteChanges = changes.filter(c => c.changeType === ChangeType.REMOTE_ONLY ||
719
+ c.changeType === ChangeType.BOTH_CHANGED);
756
720
  // Sort changes by dependency order (parents before children)
757
721
  const sortedChanges = this.sortChangesByDependency(remoteChanges);
758
722
  for (const change of sortedChanges) {
@@ -775,19 +739,19 @@ class SyncEngine {
775
739
  * Apply remote change to local filesystem
776
740
  */
777
741
  async applyRemoteChangeToLocal(change, snapshot) {
778
- const localPath = (0, utils_1.joinAndNormalizePath)(this.rootPath, change.path);
742
+ const localPath = joinAndNormalizePath(this.rootPath, change.path);
779
743
  if (!change.remoteHead) {
780
744
  throw new Error(`No remote head found for remote change to ${change.path}`);
781
745
  }
782
746
  // Check for null (empty string/Uint8Array are valid content)
783
747
  if (change.remoteContent === null) {
784
748
  // File was deleted remotely
785
- await (0, utils_1.removePath)(localPath);
749
+ await removePath(localPath);
786
750
  this.snapshotManager.removeFileEntry(snapshot, change.path);
787
751
  return;
788
752
  }
789
753
  // Create or update local file
790
- await (0, utils_1.writeFileContent)(localPath, change.remoteContent);
754
+ await writeFileContent(localPath, change.remoteContent);
791
755
  // Update or create snapshot entry for this file
792
756
  const snapshotEntry = snapshot.files.get(change.path);
793
757
  if (snapshotEntry) {
@@ -804,7 +768,7 @@ class SyncEngine {
804
768
  // We need to find the remote file's URL from the directory hierarchy
805
769
  if (snapshot.rootDirectoryUrl) {
806
770
  try {
807
- const fileEntry = await (0, utils_1.findFileInDirectoryHierarchy)(this.repo, snapshot.rootDirectoryUrl, change.path);
771
+ const fileEntry = await findFileInDirectoryHierarchy(this.repo, snapshot.rootDirectoryUrl, change.path);
808
772
  if (fileEntry) {
809
773
  const fileHandle = await this.repo.find(fileEntry.url);
810
774
  const entryUrl = this.getEntryUrl(fileHandle, change.path);
@@ -812,14 +776,14 @@ class SyncEngine {
812
776
  path: localPath,
813
777
  url: entryUrl,
814
778
  head: change.remoteHead,
815
- extension: (0, utils_1.getFileExtension)(change.path),
816
- mimeType: (0, utils_1.getEnhancedMimeType)(change.path),
779
+ extension: getFileExtension(change.path),
780
+ mimeType: getEnhancedMimeType(change.path),
817
781
  });
818
782
  }
819
783
  }
820
784
  catch (error) {
821
785
  // Failed to update snapshot - file may have been deleted
822
- output_1.out.taskLine(`Warning: Failed to update snapshot for remote file ${change.path}`, true);
786
+ out.taskLine(`Warning: Failed to update snapshot for remote file ${change.path}`, true);
823
787
  }
824
788
  }
825
789
  }
@@ -849,11 +813,11 @@ class SyncEngine {
849
813
  // Artifact files use RawString — no diffing needed, just create a fresh doc
850
814
  const content = move.newContent !== undefined
851
815
  ? move.newContent
852
- : (0, utils_1.readDocContent)((await (await this.repo.find((0, utils_1.getPlainUrl)(fromEntry.url))).doc())?.content);
816
+ : readDocContent((await (await this.repo.find(getPlainUrl(fromEntry.url))).doc())?.content);
853
817
  const fakeChange = {
854
818
  path: move.toPath,
855
- changeType: types_1.ChangeType.LOCAL_ONLY,
856
- fileType: content != null && typeof content === "string" ? types_1.FileType.TEXT : types_1.FileType.BINARY,
819
+ changeType: ChangeType.LOCAL_ONLY,
820
+ fileType: content != null && typeof content === "string" ? FileType.TEXT : FileType.BINARY,
857
821
  localContent: content,
858
822
  remoteContent: null,
859
823
  };
@@ -865,7 +829,7 @@ class SyncEngine {
865
829
  }
866
830
  else {
867
831
  // Use plain URL for mutable handle
868
- const handle = await this.repo.find((0, utils_1.getPlainUrl)(fromEntry.url));
832
+ const handle = await this.repo.find(getPlainUrl(fromEntry.url));
869
833
  const heads = fromEntry.head;
870
834
  // Update both name and content (if content changed during move)
871
835
  changeWithOptionalHeads(handle, heads, (doc) => {
@@ -873,7 +837,7 @@ class SyncEngine {
873
837
  // If new content is provided, update it (handles move + modification case)
874
838
  if (move.newContent !== undefined) {
875
839
  if (typeof move.newContent === "string") {
876
- (0, utils_1.updateTextContent)(doc, ["content"], move.newContent);
840
+ updateTextContent(doc, ["content"], move.newContent);
877
841
  }
878
842
  else {
879
843
  doc.content = move.newContent;
@@ -891,17 +855,17 @@ class SyncEngine {
891
855
  this.snapshotManager.removeFileEntry(snapshot, move.fromPath);
892
856
  this.snapshotManager.updateFileEntry(snapshot, move.toPath, {
893
857
  ...fromEntry,
894
- path: (0, utils_1.joinAndNormalizePath)(this.rootPath, move.toPath),
858
+ path: joinAndNormalizePath(this.rootPath, move.toPath),
895
859
  url: entryUrl,
896
860
  head: finalHeads,
897
861
  ...(this.isArtifactPath(move.toPath) && move.newContent != null
898
- ? { contentHash: (0, content_1.contentHash)(move.newContent) }
862
+ ? { contentHash: contentHash(move.newContent) }
899
863
  : {}),
900
864
  });
901
865
  }
902
866
  catch (e) {
903
867
  // Failed to update file name - file may have been deleted
904
- output_1.out.taskLine(`Warning: Failed to rename ${move.fromPath} to ${move.toPath}`, true);
868
+ out.taskLine(`Warning: Failed to rename ${move.fromPath} to ${move.toPath}`, true);
905
869
  }
906
870
  }
907
871
  /**
@@ -917,8 +881,8 @@ class SyncEngine {
917
881
  const fileDoc = {
918
882
  "@patchwork": { type: "file" },
919
883
  name: change.path.split("/").pop() || "",
920
- extension: (0, utils_1.getFileExtension)(change.path),
921
- mimeType: (0, utils_1.getEnhancedMimeType)(change.path),
884
+ extension: getFileExtension(change.path),
885
+ mimeType: getEnhancedMimeType(change.path),
922
886
  content: isText && isArtifact
923
887
  ? new A.RawString(change.localContent)
924
888
  : isText
@@ -932,7 +896,7 @@ class SyncEngine {
932
896
  // For non-artifact text files, splice in the content so it's stored as collaborative text
933
897
  if (isText && !isArtifact && typeof change.localContent === "string") {
934
898
  handle.change((doc) => {
935
- (0, utils_1.updateTextContent)(doc, ["content"], change.localContent);
899
+ updateTextContent(doc, ["content"], change.localContent);
936
900
  });
937
901
  }
938
902
  // Always track newly created files for network sync
@@ -945,7 +909,7 @@ class SyncEngine {
945
909
  */
946
910
  async updateRemoteFile(url, content, snapshot, filePath) {
947
911
  // Use plain URL for mutable handle
948
- const handle = await this.repo.find((0, utils_1.getPlainUrl)(url));
912
+ const handle = await this.repo.find(getPlainUrl(url));
949
913
  // Check if content actually changed before tracking for sync
950
914
  const doc = await handle.doc();
951
915
  const rawContent = doc?.content;
@@ -957,14 +921,14 @@ class SyncEngine {
957
921
  !doc ||
958
922
  (rawContent != null && A.isImmutableString(rawContent))) {
959
923
  if (!isArtifact) {
960
- output_1.out.taskLine(`Replacing ${!doc ? 'unavailable' : 'immutable string'} document for ${filePath}`, true);
924
+ out.taskLine(`Replacing ${!doc ? 'unavailable' : 'immutable string'} document for ${filePath}`, true);
961
925
  }
962
926
  const fakeChange = {
963
927
  path: filePath,
964
- changeType: types_1.ChangeType.LOCAL_ONLY,
928
+ changeType: ChangeType.LOCAL_ONLY,
965
929
  fileType: this.isTextContent(content)
966
- ? types_1.FileType.TEXT
967
- : types_1.FileType.BINARY,
930
+ ? FileType.TEXT
931
+ : FileType.BINARY,
968
932
  localContent: content,
969
933
  remoteContent: null,
970
934
  };
@@ -972,20 +936,20 @@ class SyncEngine {
972
936
  if (newHandle) {
973
937
  const entryUrl = this.getEntryUrl(newHandle, filePath);
974
938
  this.snapshotManager.updateFileEntry(snapshot, filePath, {
975
- path: (0, utils_1.joinAndNormalizePath)(this.rootPath, filePath),
939
+ path: joinAndNormalizePath(this.rootPath, filePath),
976
940
  url: entryUrl,
977
941
  head: newHandle.heads(),
978
- extension: (0, utils_1.getFileExtension)(filePath),
979
- mimeType: (0, utils_1.getEnhancedMimeType)(filePath),
942
+ extension: getFileExtension(filePath),
943
+ mimeType: getEnhancedMimeType(filePath),
980
944
  ...(this.isArtifactPath(filePath)
981
- ? { contentHash: (0, content_1.contentHash)(content) }
945
+ ? { contentHash: contentHash(content) }
982
946
  : {}),
983
947
  });
984
948
  }
985
949
  return;
986
950
  }
987
- const currentContent = (0, utils_1.readDocContent)(rawContent);
988
- const contentChanged = !(0, content_1.isContentEqual)(content, currentContent);
951
+ const currentContent = readDocContent(rawContent);
952
+ const contentChanged = !isContentEqual(content, currentContent);
989
953
  // Update snapshot heads even when content is identical
990
954
  const snapshotEntry = snapshot.files.get(filePath);
991
955
  if (snapshotEntry) {
@@ -1006,7 +970,7 @@ class SyncEngine {
1006
970
  }
1007
971
  handle.changeAt(heads, (doc) => {
1008
972
  if (typeof content === "string") {
1009
- (0, utils_1.updateTextContent)(doc, ["content"], content);
973
+ updateTextContent(doc, ["content"], content);
1010
974
  }
1011
975
  else {
1012
976
  doc.content = content;
@@ -1043,7 +1007,7 @@ class SyncEngine {
1043
1007
  // Get or create the parent directory document
1044
1008
  const parentDirUrl = await this.ensureDirectoryDocument(snapshot, directoryPath);
1045
1009
  // Use plain URL for mutable handle
1046
- const dirHandle = await this.repo.find((0, utils_1.getPlainUrl)(parentDirUrl));
1010
+ const dirHandle = await this.repo.find(getPlainUrl(parentDirUrl));
1047
1011
  let didChange = false;
1048
1012
  const snapshotEntry = snapshot.directories.get(directoryPath);
1049
1013
  const heads = snapshotEntry?.head;
@@ -1101,7 +1065,7 @@ class SyncEngine {
1101
1065
  const entryUrl = this.getEntryUrl(childDirHandle, directoryPath);
1102
1066
  // Update snapshot with discovered directory
1103
1067
  this.snapshotManager.updateDirectoryEntry(snapshot, directoryPath, {
1104
- path: (0, utils_1.joinAndNormalizePath)(this.rootPath, directoryPath),
1068
+ path: joinAndNormalizePath(this.rootPath, directoryPath),
1105
1069
  url: entryUrl,
1106
1070
  head: childDirHandle.heads(),
1107
1071
  entries: [],
@@ -1129,7 +1093,7 @@ class SyncEngine {
1129
1093
  const dirEntryUrl = this.getEntryUrl(dirHandle, directoryPath);
1130
1094
  // Add this directory to its parent
1131
1095
  // Use plain URL for mutable handle
1132
- const parentHandle = await this.repo.find((0, utils_1.getPlainUrl)(parentDirUrl));
1096
+ const parentHandle = await this.repo.find(getPlainUrl(parentDirUrl));
1133
1097
  let didChange = false;
1134
1098
  parentHandle.change((doc) => {
1135
1099
  // Double-check that entry doesn't exist (race condition protection)
@@ -1154,7 +1118,7 @@ class SyncEngine {
1154
1118
  }
1155
1119
  // Update snapshot with new directory
1156
1120
  this.snapshotManager.updateDirectoryEntry(snapshot, directoryPath, {
1157
- path: (0, utils_1.joinAndNormalizePath)(this.rootPath, directoryPath),
1121
+ path: joinAndNormalizePath(this.rootPath, directoryPath),
1158
1122
  url: dirEntryUrl,
1159
1123
  head: dirHandle.heads(),
1160
1124
  entries: [],
@@ -1185,7 +1149,7 @@ class SyncEngine {
1185
1149
  }
1186
1150
  try {
1187
1151
  // Use plain URL for mutable handle
1188
- const dirHandle = await this.repo.find((0, utils_1.getPlainUrl)(parentDirUrl));
1152
+ const dirHandle = await this.repo.find(getPlainUrl(parentDirUrl));
1189
1153
  // Track this handle for network sync waiting
1190
1154
  this.handlesByPath.set(directoryPath, dirHandle);
1191
1155
  const snapshotEntry = snapshot.directories.get(directoryPath);
@@ -1196,7 +1160,7 @@ class SyncEngine {
1196
1160
  if (indexToRemove !== -1) {
1197
1161
  doc.docs.splice(indexToRemove, 1);
1198
1162
  didChange = true;
1199
- output_1.out.taskLine(`Removed ${fileName} from ${(0, utils_1.formatRelativePath)(directoryPath) || "root"}`);
1163
+ out.taskLine(`Removed ${fileName} from ${formatRelativePath(directoryPath) || "root"}`);
1200
1164
  }
1201
1165
  });
1202
1166
  if (didChange && snapshotEntry) {
@@ -1224,7 +1188,7 @@ class SyncEngine {
1224
1188
  return;
1225
1189
  dirUrl = dirEntry.url;
1226
1190
  }
1227
- const dirHandle = await this.repo.find((0, utils_1.getPlainUrl)(dirUrl));
1191
+ const dirHandle = await this.repo.find(getPlainUrl(dirUrl));
1228
1192
  const snapshotEntry = snapshot.directories.get(dirPath);
1229
1193
  const heads = snapshotEntry?.head;
1230
1194
  // Determine directory name
@@ -1240,7 +1204,7 @@ class SyncEngine {
1240
1204
  const idx = doc.docs.findIndex(entry => entry.name === name && entry.type === "file");
1241
1205
  if (idx !== -1) {
1242
1206
  doc.docs.splice(idx, 1);
1243
- output_1.out.taskLine(`Removed ${name} from ${(0, utils_1.formatRelativePath)(dirPath) || "root"}`);
1207
+ out.taskLine(`Removed ${name} from ${formatRelativePath(dirPath) || "root"}`);
1244
1208
  }
1245
1209
  }
1246
1210
  // Update URLs for modified files
@@ -1328,11 +1292,11 @@ class SyncEngine {
1328
1292
  * Generate human-readable summary of changes
1329
1293
  */
1330
1294
  generateChangeSummary(changes, moves) {
1331
- const localChanges = changes.filter(c => c.changeType === types_1.ChangeType.LOCAL_ONLY ||
1332
- c.changeType === types_1.ChangeType.BOTH_CHANGED).length;
1333
- const remoteChanges = changes.filter(c => c.changeType === types_1.ChangeType.REMOTE_ONLY ||
1334
- c.changeType === types_1.ChangeType.BOTH_CHANGED).length;
1335
- const conflicts = changes.filter(c => c.changeType === types_1.ChangeType.BOTH_CHANGED).length;
1295
+ const localChanges = changes.filter(c => c.changeType === ChangeType.LOCAL_ONLY ||
1296
+ c.changeType === ChangeType.BOTH_CHANGED).length;
1297
+ const remoteChanges = changes.filter(c => c.changeType === ChangeType.REMOTE_ONLY ||
1298
+ c.changeType === ChangeType.BOTH_CHANGED).length;
1299
+ const conflicts = changes.filter(c => c.changeType === ChangeType.BOTH_CHANGED).length;
1336
1300
  const parts = [];
1337
1301
  if (localChanges > 0) {
1338
1302
  parts.push(`${localChanges} local change${localChanges > 1 ? "s" : ""}`);
@@ -1379,5 +1343,4 @@ class SyncEngine {
1379
1343
  }
1380
1344
  }
1381
1345
  }
1382
- exports.SyncEngine = SyncEngine;
1383
1346
  //# sourceMappingURL=sync-engine.js.map