pushwork 1.0.5 → 1.0.11

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 (196) hide show
  1. package/README.md +87 -335
  2. package/babel.config.js +5 -0
  3. package/dist/cli/commands.d.ts +9 -15
  4. package/dist/cli/commands.d.ts.map +1 -1
  5. package/dist/cli/commands.js +37 -170
  6. package/dist/cli/commands.js.map +1 -1
  7. package/dist/cli/output.d.ts +11 -25
  8. package/dist/cli/output.d.ts.map +1 -1
  9. package/dist/cli/output.js +55 -61
  10. package/dist/cli/output.js.map +1 -1
  11. package/dist/cli.js +208 -213
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands.d.ts +51 -0
  14. package/dist/commands.d.ts.map +1 -0
  15. package/dist/commands.js +799 -0
  16. package/dist/commands.js.map +1 -0
  17. package/dist/core/change-detection.d.ts +7 -23
  18. package/dist/core/change-detection.d.ts.map +1 -1
  19. package/dist/core/change-detection.js +108 -122
  20. package/dist/core/change-detection.js.map +1 -1
  21. package/dist/core/config.d.ts +81 -0
  22. package/dist/core/config.d.ts.map +1 -0
  23. package/dist/core/config.js +296 -0
  24. package/dist/core/config.js.map +1 -0
  25. package/dist/core/index.d.ts +1 -0
  26. package/dist/core/index.d.ts.map +1 -1
  27. package/dist/core/index.js +1 -1
  28. package/dist/core/index.js.map +1 -1
  29. package/dist/core/move-detection.d.ts +4 -3
  30. package/dist/core/move-detection.d.ts.map +1 -1
  31. package/dist/core/move-detection.js +8 -7
  32. package/dist/core/move-detection.js.map +1 -1
  33. package/dist/core/snapshot.d.ts +0 -4
  34. package/dist/core/snapshot.d.ts.map +1 -1
  35. package/dist/core/snapshot.js +2 -11
  36. package/dist/core/snapshot.js.map +1 -1
  37. package/dist/core/sync-engine.d.ts +41 -12
  38. package/dist/core/sync-engine.d.ts.map +1 -1
  39. package/dist/core/sync-engine.js +522 -359
  40. package/dist/core/sync-engine.js.map +1 -1
  41. package/dist/index.d.ts +0 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +0 -6
  44. package/dist/index.js.map +1 -1
  45. package/dist/types/config.d.ts +24 -88
  46. package/dist/types/config.d.ts.map +1 -1
  47. package/dist/types/config.js +6 -0
  48. package/dist/types/config.js.map +1 -1
  49. package/dist/types/documents.d.ts +15 -2
  50. package/dist/types/documents.d.ts.map +1 -1
  51. package/dist/types/documents.js.map +1 -1
  52. package/dist/types/index.d.ts.map +1 -1
  53. package/dist/types/index.js +0 -3
  54. package/dist/types/index.js.map +1 -1
  55. package/dist/types/snapshot.d.ts +0 -21
  56. package/dist/types/snapshot.d.ts.map +1 -1
  57. package/dist/types/snapshot.js +0 -14
  58. package/dist/types/snapshot.js.map +1 -1
  59. package/dist/utils/content.d.ts.map +1 -1
  60. package/dist/utils/content.js +2 -6
  61. package/dist/utils/content.js.map +1 -1
  62. package/dist/utils/directory.d.ts +24 -0
  63. package/dist/utils/directory.d.ts.map +1 -0
  64. package/dist/utils/directory.js +56 -0
  65. package/dist/utils/directory.js.map +1 -0
  66. package/dist/utils/fs.d.ts +15 -2
  67. package/dist/utils/fs.d.ts.map +1 -1
  68. package/dist/utils/fs.js +53 -20
  69. package/dist/utils/fs.js.map +1 -1
  70. package/dist/utils/index.d.ts +1 -0
  71. package/dist/utils/index.d.ts.map +1 -1
  72. package/dist/utils/index.js +1 -3
  73. package/dist/utils/index.js.map +1 -1
  74. package/dist/utils/keyhive.d.ts +9 -0
  75. package/dist/utils/keyhive.d.ts.map +1 -0
  76. package/dist/utils/keyhive.js +26 -0
  77. package/dist/utils/keyhive.js.map +1 -0
  78. package/dist/utils/mime-types.d.ts.map +1 -1
  79. package/dist/utils/mime-types.js +11 -4
  80. package/dist/utils/mime-types.js.map +1 -1
  81. package/dist/utils/network-sync.d.ts +16 -7
  82. package/dist/utils/network-sync.d.ts.map +1 -1
  83. package/dist/utils/network-sync.js +158 -99
  84. package/dist/utils/network-sync.js.map +1 -1
  85. package/dist/utils/output.d.ts +129 -0
  86. package/dist/utils/output.d.ts.map +1 -0
  87. package/dist/utils/output.js +375 -0
  88. package/dist/utils/output.js.map +1 -0
  89. package/dist/utils/repo-factory.d.ts +2 -6
  90. package/dist/utils/repo-factory.d.ts.map +1 -1
  91. package/dist/utils/repo-factory.js +8 -31
  92. package/dist/utils/repo-factory.js.map +1 -1
  93. package/dist/utils/string-similarity.js +2 -2
  94. package/dist/utils/string-similarity.js.map +1 -1
  95. package/dist/utils/trace.d.ts +19 -0
  96. package/dist/utils/trace.d.ts.map +1 -0
  97. package/dist/utils/trace.js +68 -0
  98. package/dist/utils/trace.js.map +1 -0
  99. package/package.json +21 -11
  100. package/src/cli.ts +276 -308
  101. package/src/commands.ts +988 -0
  102. package/src/core/change-detection.ts +226 -246
  103. package/src/{config/index.ts → core/config.ts} +65 -82
  104. package/src/core/index.ts +1 -1
  105. package/src/core/move-detection.ts +10 -8
  106. package/src/core/snapshot.ts +2 -12
  107. package/src/core/sync-engine.ts +630 -478
  108. package/src/index.ts +0 -10
  109. package/src/types/config.ts +28 -93
  110. package/src/types/documents.ts +16 -2
  111. package/src/types/index.ts +0 -5
  112. package/src/types/snapshot.ts +0 -23
  113. package/src/utils/content.ts +2 -6
  114. package/src/utils/directory.ts +73 -0
  115. package/src/utils/fs.ts +57 -23
  116. package/src/utils/index.ts +1 -5
  117. package/src/utils/mime-types.ts +12 -4
  118. package/src/utils/network-sync.ts +216 -138
  119. package/src/utils/output.ts +450 -0
  120. package/src/utils/repo-factory.ts +13 -44
  121. package/src/utils/string-similarity.ts +2 -2
  122. package/src/utils/trace.ts +70 -0
  123. package/test/integration/exclude-patterns.test.ts +6 -15
  124. package/test/integration/fuzzer.test.ts +308 -391
  125. package/test/integration/in-memory-sync.test.ts +435 -0
  126. package/test/integration/init-sync.test.ts +89 -0
  127. package/test/integration/sync-deletion.test.ts +2 -61
  128. package/test/integration/sync-flow.test.ts +4 -24
  129. package/test/jest.setup.ts +34 -0
  130. package/test/unit/deletion-behavior.test.ts +3 -14
  131. package/test/unit/enhanced-mime-detection.test.ts +0 -22
  132. package/test/unit/snapshot.test.ts +2 -29
  133. package/test/unit/sync-convergence.test.ts +3 -198
  134. package/test/unit/sync-timing.test.ts +0 -44
  135. package/test/unit/utils.test.ts +0 -2
  136. package/tsconfig.json +3 -3
  137. package/bench/filesystem.bench.ts +0 -78
  138. package/bench/hashing.bench.ts +0 -60
  139. package/bench/move-detection.bench.ts +0 -130
  140. package/bench/runner.ts +0 -49
  141. package/dist/browser/browser-sync-engine.d.ts +0 -64
  142. package/dist/browser/browser-sync-engine.d.ts.map +0 -1
  143. package/dist/browser/browser-sync-engine.js +0 -303
  144. package/dist/browser/browser-sync-engine.js.map +0 -1
  145. package/dist/browser/filesystem-adapter.d.ts +0 -84
  146. package/dist/browser/filesystem-adapter.d.ts.map +0 -1
  147. package/dist/browser/filesystem-adapter.js +0 -413
  148. package/dist/browser/filesystem-adapter.js.map +0 -1
  149. package/dist/browser/index.d.ts +0 -36
  150. package/dist/browser/index.d.ts.map +0 -1
  151. package/dist/browser/index.js +0 -90
  152. package/dist/browser/index.js.map +0 -1
  153. package/dist/browser/types.d.ts +0 -70
  154. package/dist/browser/types.d.ts.map +0 -1
  155. package/dist/browser/types.js +0 -6
  156. package/dist/browser/types.js.map +0 -1
  157. package/dist/config/remote-manager.d.ts +0 -65
  158. package/dist/config/remote-manager.d.ts.map +0 -1
  159. package/dist/config/remote-manager.js +0 -243
  160. package/dist/config/remote-manager.js.map +0 -1
  161. package/dist/core/isomorphic-snapshot.d.ts +0 -58
  162. package/dist/core/isomorphic-snapshot.d.ts.map +0 -1
  163. package/dist/core/isomorphic-snapshot.js +0 -204
  164. package/dist/core/isomorphic-snapshot.js.map +0 -1
  165. package/dist/platform/browser-filesystem.d.ts +0 -26
  166. package/dist/platform/browser-filesystem.d.ts.map +0 -1
  167. package/dist/platform/browser-filesystem.js +0 -91
  168. package/dist/platform/browser-filesystem.js.map +0 -1
  169. package/dist/platform/filesystem.d.ts +0 -29
  170. package/dist/platform/filesystem.d.ts.map +0 -1
  171. package/dist/platform/filesystem.js +0 -65
  172. package/dist/platform/filesystem.js.map +0 -1
  173. package/dist/platform/node-filesystem.d.ts +0 -21
  174. package/dist/platform/node-filesystem.d.ts.map +0 -1
  175. package/dist/platform/node-filesystem.js +0 -93
  176. package/dist/platform/node-filesystem.js.map +0 -1
  177. package/dist/utils/fs-browser.d.ts +0 -57
  178. package/dist/utils/fs-browser.d.ts.map +0 -1
  179. package/dist/utils/fs-browser.js +0 -311
  180. package/dist/utils/fs-browser.js.map +0 -1
  181. package/dist/utils/fs-node.d.ts +0 -53
  182. package/dist/utils/fs-node.d.ts.map +0 -1
  183. package/dist/utils/fs-node.js +0 -220
  184. package/dist/utils/fs-node.js.map +0 -1
  185. package/dist/utils/isomorphic.d.ts +0 -29
  186. package/dist/utils/isomorphic.d.ts.map +0 -1
  187. package/dist/utils/isomorphic.js +0 -139
  188. package/dist/utils/isomorphic.js.map +0 -1
  189. package/dist/utils/pure.d.ts +0 -25
  190. package/dist/utils/pure.d.ts.map +0 -1
  191. package/dist/utils/pure.js +0 -112
  192. package/dist/utils/pure.js.map +0 -1
  193. package/src/cli/commands.ts +0 -1030
  194. package/src/cli/index.ts +0 -2
  195. package/src/cli/output.ts +0 -244
  196. package/test/README-TESTING-GAPS.md +0 -174
@@ -1,169 +1,247 @@
1
- import { DocHandle, StorageId } from "@automerge/automerge-repo";
1
+ import { DocHandle, StorageId, Repo, AutomergeUrl } from "@automerge/automerge-repo";
2
2
  import * as A from "@automerge/automerge";
3
+ import { out } from "./output";
4
+ import { DirectoryDocument } from "../types";
5
+ import { getPlainUrl } from "./directory";
6
+
7
+ /**
8
+ * Wait for bidirectional sync to stabilize.
9
+ * This function waits until document heads stop changing, indicating that
10
+ * both outgoing and incoming sync has completed.
11
+ *
12
+ * @param repo - The Automerge repository
13
+ * @param rootDirectoryUrl - The root directory URL to start traversal from
14
+ * @param syncServerStorageId - The sync server storage ID
15
+ * @param options - Configuration options
16
+ */
17
+ export async function waitForBidirectionalSync(
18
+ repo: Repo,
19
+ rootDirectoryUrl: AutomergeUrl | undefined,
20
+ syncServerStorageId: StorageId | undefined,
21
+ options: {
22
+ timeoutMs?: number;
23
+ pollIntervalMs?: number;
24
+ stableChecksRequired?: number;
25
+ } = {}
26
+ ): Promise<void> {
27
+ const {
28
+ timeoutMs = 10000,
29
+ pollIntervalMs = 100,
30
+ stableChecksRequired = 3,
31
+ } = options;
32
+
33
+ if (!syncServerStorageId || !rootDirectoryUrl) {
34
+ return;
35
+ }
36
+
37
+ const startTime = Date.now();
38
+ let lastSeenHeads = new Map<string, string>();
39
+ let stableCount = 0;
40
+
41
+ while (Date.now() - startTime < timeoutMs) {
42
+ // Get current heads for all documents in the directory hierarchy
43
+ const currentHeads = await getAllDocumentHeads(repo, rootDirectoryUrl);
44
+
45
+ // Check if heads are stable (no changes since last check)
46
+ const isStable = headsMapEqual(lastSeenHeads, currentHeads);
47
+
48
+ if (isStable) {
49
+ stableCount++;
50
+ if (stableCount >= stableChecksRequired) {
51
+ return; // Converged!
52
+ }
53
+ } else {
54
+ stableCount = 0;
55
+ lastSeenHeads = currentHeads;
56
+ }
57
+
58
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
59
+ }
60
+
61
+ // Timeout - but don't throw, just log a warning
62
+ // The sync may still work, we just couldn't confirm stability
63
+ out.taskLine(`Sync stability check timed out after ${timeoutMs}ms`, true);
64
+ }
65
+
66
+ /**
67
+ * Get all document heads in the directory hierarchy.
68
+ * Returns a map of document URL -> serialized heads.
69
+ * Uses plain URLs (without heads) to ensure we see current document state.
70
+ */
71
+ async function getAllDocumentHeads(
72
+ repo: Repo,
73
+ rootDirectoryUrl: AutomergeUrl
74
+ ): Promise<Map<string, string>> {
75
+ const heads = new Map<string, string>();
76
+ // Pass URL as-is; collectHeadsRecursive will strip heads
77
+ await collectHeadsRecursive(repo, rootDirectoryUrl, heads);
78
+ return heads;
79
+ }
80
+
81
+ /**
82
+ * Recursively collect document heads from the directory hierarchy.
83
+ * Uses getPlainUrl to strip heads and always see the CURRENT state of documents.
84
+ */
85
+ async function collectHeadsRecursive(
86
+ repo: Repo,
87
+ directoryUrl: AutomergeUrl,
88
+ heads: Map<string, string>
89
+ ): Promise<void> {
90
+ try {
91
+ const plainUrl = getPlainUrl(directoryUrl);
92
+ const handle = await repo.find<DirectoryDocument>(plainUrl);
93
+ const doc = await handle.doc();
94
+
95
+ // Record this directory's heads (use plain URL as key for consistency)
96
+ heads.set(plainUrl, JSON.stringify(handle.heads()));
97
+
98
+ if (!doc || !doc.docs) {
99
+ return;
100
+ }
101
+
102
+ // Process all entries in the directory
103
+ for (const entry of doc.docs) {
104
+ if (entry.type === "folder") {
105
+ // Recurse into subdirectory (entry.url may have stale heads)
106
+ await collectHeadsRecursive(repo, entry.url, heads);
107
+ } else if (entry.type === "file") {
108
+ // Get file document heads (strip heads from entry.url)
109
+ try {
110
+ const fileUrl = getPlainUrl(entry.url);
111
+ const fileHandle = await repo.find(fileUrl);
112
+ heads.set(fileUrl, JSON.stringify(fileHandle.heads()));
113
+ } catch {
114
+ // File document may not exist yet
115
+ }
116
+ }
117
+ }
118
+ } catch {
119
+ // Directory may not exist yet
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Compare two heads maps for equality.
125
+ */
126
+ function headsMapEqual(
127
+ a: Map<string, string>,
128
+ b: Map<string, string>
129
+ ): boolean {
130
+ if (a.size !== b.size) {
131
+ return false;
132
+ }
133
+ for (const [key, value] of a) {
134
+ if (b.get(key) !== value) {
135
+ return false;
136
+ }
137
+ }
138
+ return true;
139
+ }
3
140
 
4
141
  /**
5
142
  * Wait for documents to sync to the remote server
6
- * Based on patchwork-cli implementation with timeout for debugging
7
143
  */
8
144
  export async function waitForSync(
9
145
  handlesToWaitOn: DocHandle<unknown>[],
10
146
  syncServerStorageId?: StorageId,
11
- timeoutMs: number = 60000 // 60 second timeout for debugging
147
+ timeoutMs: number = 60000
12
148
  ): Promise<void> {
13
149
  const startTime = Date.now();
14
150
 
15
151
  if (!syncServerStorageId) {
16
- console.warn(
17
- "No sync server storage ID provided. Skipping network sync wait."
18
- );
152
+ // No sync server storage ID - skip network sync
19
153
  return;
20
154
  }
21
155
 
22
156
  if (handlesToWaitOn.length === 0) {
23
- console.log("🔄 No documents to sync");
157
+ // No documents to sync
24
158
  return;
25
159
  }
26
160
 
27
- // Debug logging only in verbose mode (can be controlled via env var later)
28
- const verbose = false;
29
-
30
- if (verbose) {
31
- console.log(
32
- `🔄 Waiting for ${handlesToWaitOn.length} documents to sync...`
33
- );
34
- console.log(`📡 Using sync server storage ID: ${syncServerStorageId}`);
35
-
36
- handlesToWaitOn.forEach((handle, i) => {
37
- const localHeads = handle.heads();
38
- const syncInfo = handle.getSyncInfo(syncServerStorageId);
39
- const remoteHeads = syncInfo?.lastHeads;
40
- console.log(` 📄 Document ${i + 1}: ${handle.url}`);
41
- console.log(` 🏠 Local heads: ${JSON.stringify(localHeads)}`);
42
- console.log(` 🌐 Remote heads: ${JSON.stringify(remoteHeads)}`);
43
- console.log(
44
- ` ✅ Already synced: ${A.equals(localHeads, remoteHeads)}`
45
- );
46
- });
47
- }
161
+ let alreadySynced = 0;
162
+
163
+ const promises = handlesToWaitOn.map((handle) => {
164
+ // Check if already synced
165
+ const heads = handle.heads();
166
+ const syncInfo = handle.getSyncInfo(syncServerStorageId);
167
+ const remoteHeads = syncInfo?.lastHeads;
168
+ const wasAlreadySynced = A.equals(heads, remoteHeads);
48
169
 
49
- const promises = handlesToWaitOn.map(
50
- (handle, index) =>
51
- new Promise<void>((resolve, reject) => {
52
- let pollInterval: NodeJS.Timeout;
53
-
54
- const timeout = setTimeout(() => {
55
- clearInterval(pollInterval);
56
- const localHeads = handle.heads();
57
- const syncInfo = handle.getSyncInfo(syncServerStorageId);
58
- const remoteHeads = syncInfo?.lastHeads;
59
- console.log(`⏰ TIMEOUT for document ${index + 1}: ${handle.url}`);
60
- console.log(` Final local heads: ${JSON.stringify(localHeads)}`);
61
- console.log(` Final remote heads: ${JSON.stringify(remoteHeads)}`);
62
- reject(
63
- new Error(
64
- `Sync timeout after ${timeoutMs}ms for document ${handle.url}`
65
- )
66
- );
67
- }, timeoutMs);
68
-
69
- const checkSync = () => {
70
- const newHeads = handle.heads();
71
- const syncInfo = handle.getSyncInfo(syncServerStorageId);
72
- const remoteHeads = syncInfo?.lastHeads;
73
-
74
- if (verbose) {
75
- console.log(`🔍 Checking sync for ${handle.url}:`);
76
- console.log(` Local heads: ${JSON.stringify(newHeads)}`);
77
- console.log(` Remote heads: ${JSON.stringify(remoteHeads)}`);
78
- console.log(` Heads equal: ${A.equals(newHeads, remoteHeads)}`);
79
- }
80
-
81
- // If the remote heads are already up to date, we can resolve immediately
82
- if (A.equals(newHeads, remoteHeads)) {
83
- if (verbose) {
84
- console.log(`✅ Document ${index + 1} synced: ${handle.url}`);
85
- }
86
- clearTimeout(timeout);
87
- clearInterval(pollInterval);
88
- resolve();
89
- return true;
90
- }
91
- return false;
92
- };
93
-
94
- // Check if already synced
95
- if (checkSync()) {
96
- return;
170
+ if (wasAlreadySynced) {
171
+ alreadySynced++;
172
+ return Promise.resolve();
173
+ }
174
+
175
+ // Wait for convergence
176
+ return new Promise<void>((resolve, reject) => {
177
+ // TODO: can we delete this polling?
178
+ let pollInterval: NodeJS.Timeout;
179
+
180
+ const cleanup = () => {
181
+ clearTimeout(timeout);
182
+ clearInterval(pollInterval);
183
+ handle.off("remote-heads", onRemoteHeads);
184
+ };
185
+
186
+ const onConverged = () => {
187
+ cleanup();
188
+ resolve();
189
+ };
190
+
191
+ const timeout = setTimeout(() => {
192
+ cleanup();
193
+ reject(
194
+ new Error(
195
+ `Sync timeout after ${timeoutMs}ms for document ${handle.url}`
196
+ )
197
+ );
198
+ }, timeoutMs);
199
+
200
+ const isConverged = () => {
201
+ const localHeads = handle.heads();
202
+ const info = handle.getSyncInfo(syncServerStorageId);
203
+ return A.equals(localHeads, info?.lastHeads);
204
+ };
205
+
206
+ const onRemoteHeads = ({
207
+ storageId,
208
+ }: {
209
+ storageId: StorageId;
210
+ heads: any;
211
+ }) => {
212
+ if (storageId === syncServerStorageId && isConverged()) {
213
+ onConverged();
97
214
  }
215
+ };
98
216
 
99
- // Periodically re-check if heads have converged (polling fallback)
100
- pollInterval = setInterval(() => {
101
- if (checkSync()) {
102
- clearInterval(pollInterval);
103
- }
104
- }, 100); // Check every 100ms for faster response
105
-
106
- // Also wait for remote-heads event (faster when events work)
107
- const onRemoteHeads = ({
108
- storageId,
109
- heads,
110
- }: {
111
- storageId: StorageId;
112
- heads: any;
113
- }) => {
114
- if (verbose) {
115
- console.log(`📡 Received remote heads event for ${handle.url}:`);
116
- console.log(` Event storage ID: ${storageId}`);
117
- console.log(` Expected storage ID: ${syncServerStorageId}`);
118
- console.log(` Event heads: ${JSON.stringify(heads)}`);
119
- console.log(
120
- ` Current local heads: ${JSON.stringify(handle.heads())}`
121
- );
122
- }
123
-
124
- if (
125
- storageId === syncServerStorageId &&
126
- A.equals(handle.heads(), heads)
127
- ) {
128
- if (verbose) {
129
- console.log(
130
- `✅ Document ${index + 1} synced via event: ${handle.url}`
131
- );
132
- }
133
- clearTimeout(timeout);
134
- clearInterval(pollInterval);
135
- handle.off("remote-heads", onRemoteHeads);
136
- resolve();
137
- } else if (verbose) {
138
- console.log(`❌ Heads/storage mismatch for ${handle.url}`);
139
- }
140
- };
141
-
142
- if (verbose) {
143
- console.log(`👂 Listening for remote-heads events on ${handle.url}`);
217
+ const poll = () => {
218
+ if (isConverged()) {
219
+ onConverged();
220
+ return true;
144
221
  }
145
- handle.on("remote-heads", onRemoteHeads);
146
- })
147
- );
222
+ return false;
223
+ };
224
+
225
+ // Initial check
226
+ if (poll()) {
227
+ return;
228
+ }
229
+
230
+ // Start polling and event listening
231
+ pollInterval = setInterval(() => {
232
+ poll();
233
+ }, 100);
234
+
235
+ handle.on("remote-heads", onRemoteHeads);
236
+ });
237
+ });
148
238
 
149
239
  try {
150
240
  await Promise.all(promises);
151
- const elapsed = Date.now() - startTime;
152
- if (verbose) {
153
- console.log(`✅ All documents synced to network (took ${elapsed}ms)`);
154
- }
155
241
  } catch (error) {
156
242
  const elapsed = Date.now() - startTime;
157
- console.error(`❌ Sync wait failed after ${elapsed}ms: ${error}`);
243
+ out.errorBlock("FAILED", `after ${elapsed}ms`);
244
+ out.crash(error);
158
245
  throw error;
159
246
  }
160
247
  }
161
-
162
- /**
163
- * Get the storage ID for the sync server
164
- * Using the same ID as patchwork-cli for consistency
165
- */
166
- export function getSyncServerStorageId(customStorageId?: string): StorageId {
167
- return (customStorageId ||
168
- "3760df37-a4c6-4f66-9ecd-732039a9385d") as StorageId;
169
- }