@storacha/clawracha 0.3.14 → 0.4.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 (43) hide show
  1. package/LICENSE.md +204 -0
  2. package/README.md +117 -110
  3. package/dist/commands.d.ts.map +1 -1
  4. package/dist/commands.js +3 -3
  5. package/dist/handlers/apply.d.ts +3 -11
  6. package/dist/handlers/apply.d.ts.map +1 -1
  7. package/dist/handlers/apply.js +5 -32
  8. package/dist/handlers/process.d.ts +3 -6
  9. package/dist/handlers/process.d.ts.map +1 -1
  10. package/dist/handlers/process.js +30 -60
  11. package/dist/handlers/remote.d.ts +1 -3
  12. package/dist/handlers/remote.d.ts.map +1 -1
  13. package/dist/handlers/remote.js +4 -34
  14. package/dist/index.d.ts +0 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +0 -1
  17. package/dist/mdsync/index.d.ts +2 -2
  18. package/dist/mdsync/index.d.ts.map +1 -1
  19. package/dist/mdsync/index.js +14 -17
  20. package/dist/plugin.js +2 -2
  21. package/dist/sync.d.ts +20 -10
  22. package/dist/sync.d.ts.map +1 -1
  23. package/dist/sync.js +76 -105
  24. package/dist/types/index.d.ts +11 -5
  25. package/dist/types/index.d.ts.map +1 -1
  26. package/dist/utils/config.d.ts +26 -0
  27. package/dist/utils/config.d.ts.map +1 -0
  28. package/dist/utils/config.js +89 -0
  29. package/dist/utils/contentfetcher.d.ts +6 -0
  30. package/dist/utils/contentfetcher.d.ts.map +1 -0
  31. package/dist/utils/contentfetcher.js +58 -0
  32. package/dist/utils/crypto.d.ts +0 -6
  33. package/dist/utils/crypto.d.ts.map +1 -1
  34. package/dist/utils/crypto.js +0 -45
  35. package/dist/utils/differ.d.ts.map +1 -1
  36. package/dist/utils/differ.js +6 -0
  37. package/dist/utils/encoder.d.ts +2 -15
  38. package/dist/utils/encoder.d.ts.map +1 -1
  39. package/dist/utils/encoder.js +8 -49
  40. package/dist/utils/tempcar.d.ts +1 -1
  41. package/dist/utils/tempcar.d.ts.map +1 -1
  42. package/dist/utils/tempcar.js +4 -2
  43. package/package.json +3 -2
@@ -8,8 +8,6 @@
8
8
  * before upload via encryptToBlockStream.
9
9
  */
10
10
  import { Revision } from "@storacha/ucn/pail";
11
- import { encodeFiles } from "../utils/encoder.js";
12
- import { encryptToBlockStream } from "../utils/crypto.js";
13
11
  import * as mdsync from "../mdsync/index.js";
14
12
  import * as fs from "node:fs/promises";
15
13
  import * as path from "node:path";
@@ -29,7 +27,7 @@ async function drainBlockStream(stream, sink) {
29
27
  throw new Error("Empty block stream");
30
28
  return root;
31
29
  }
32
- export async function processChanges(changes, workspace, current, blocks, sink, store, encryptionConfig) {
30
+ export async function processChanges(changes, workspace, current, blocks, sink, encoder, contentFetcher) {
33
31
  const pendingOps = [];
34
32
  const mdChanges = changes.filter((c) => isMarkdown(c.path));
35
33
  const regularChanges = changes.filter((c) => !isMarkdown(c.path));
@@ -37,79 +35,51 @@ export async function processChanges(changes, workspace, current, blocks, sink,
37
35
  const toEncode = regularChanges
38
36
  .filter((c) => c.type !== "unlink")
39
37
  .map((c) => c.path);
40
- const files = [];
41
- if (encryptionConfig) {
42
- // Private space: encrypt each file
43
- for (const relPath of toEncode) {
44
- try {
45
- const fileBytes = await fs.readFile(path.join(workspace, relPath));
46
- const encStream = await encryptToBlockStream(new Blob([fileBytes]), encryptionConfig);
47
- const rootCID = await drainBlockStream(encStream, sink);
48
- files.push({ path: relPath, rootCID });
49
- }
50
- catch (err) {
51
- if (err.code === "ENOENT") {
52
- console.warn(`File not found during encoding: ${relPath}`);
53
- continue;
38
+ for (const relPath of toEncode) {
39
+ try {
40
+ const fileBytes = await fs.readFile(path.join(workspace, relPath));
41
+ const existing = current
42
+ ? await Revision.get(blocks, current, relPath)
43
+ : null;
44
+ if (existing) {
45
+ const existingBytes = await contentFetcher(existing);
46
+ if (Buffer.compare(existingBytes, fileBytes) === 0) {
47
+ continue; // No change detected, skip encoding/uploading.
54
48
  }
55
- throw err;
56
49
  }
57
- }
58
- }
59
- else {
60
- // Public space: UnixFS encode
61
- const encoded = await encodeFiles(workspace, toEncode);
62
- for (const file of encoded) {
63
- const rootCID = await drainBlockStream(file.blocks, sink);
64
- files.push({ path: file.path, rootCID });
65
- }
66
- }
67
- for (const file of files) {
68
- const existing = current
69
- ? await Revision.get(blocks, current, file.path)
70
- : null;
71
- if (!existing || !existing.equals(file.rootCID)) {
50
+ const encStream = await encoder(new Blob([fileBytes]));
51
+ const rootCID = await drainBlockStream(encStream, sink);
72
52
  pendingOps.push({
73
53
  type: "put",
74
- key: file.path,
75
- value: file.rootCID,
54
+ key: relPath,
55
+ value: rootCID,
76
56
  });
77
57
  }
58
+ catch (err) {
59
+ if (err.code === "ENOENT") {
60
+ console.warn(`File not found during encoding: ${relPath}`);
61
+ continue;
62
+ }
63
+ throw err;
64
+ }
78
65
  }
79
66
  // --- Markdown files (CRDT merge via mdsync) ---
80
67
  const mdPuts = mdChanges.filter((c) => c.type !== "unlink");
81
68
  for (const change of mdPuts) {
82
69
  const content = await fs.readFile(path.join(workspace, change.path), "utf-8");
83
70
  const block = current
84
- ? await mdsync.put(blocks, current, change.path, content)
71
+ ? await mdsync.put(blocks, contentFetcher, current, change.path, content)
85
72
  : await mdsync.v0Put(content);
86
73
  if (!block) {
87
74
  continue; // No change detected, skip writing a new entry.
88
75
  }
89
- if (encryptionConfig) {
90
- // Private space: encrypt the single-block entry
91
- const encStream = await encryptToBlockStream(new Blob([block.bytes]), encryptionConfig);
92
- const rootCID = await drainBlockStream(encStream, sink);
93
- // Store unencrypted block locally for future resolveValue calls
94
- if (store)
95
- await store(block);
96
- pendingOps.push({
97
- type: "put",
98
- key: change.path,
99
- value: rootCID,
100
- });
101
- }
102
- else {
103
- // Public space: store block directly
104
- await sink(block);
105
- if (store)
106
- await store(block);
107
- pendingOps.push({
108
- type: "put",
109
- key: change.path,
110
- value: block.cid,
111
- });
112
- }
76
+ const encStream = await encoder(new Blob([block.bytes]));
77
+ const rootCID = await drainBlockStream(encStream, sink);
78
+ pendingOps.push({
79
+ type: "put",
80
+ key: change.path,
81
+ value: rootCID,
82
+ });
113
83
  }
114
84
  // --- Deletes (both regular and markdown) ---
115
85
  const toDelete = changes
@@ -8,10 +8,8 @@
8
8
  */
9
9
  import type { CID } from "multiformats/cid";
10
10
  import type { BlockFetcher, ValueView } from "@storacha/ucn/pail/api";
11
- export declare function applyRemoteChanges(changedPaths: string[], entries: Map<string, CID>, workspace: string, options?: {
12
- gateway?: string;
11
+ export declare function applyRemoteChanges(changedPaths: string[], entries: Map<string, CID>, workspace: string, contentFetcher: (cid: CID) => Promise<Uint8Array>, options?: {
13
12
  blocks?: BlockFetcher;
14
13
  current?: ValueView;
15
- decrypt?: (cid: CID) => Promise<Uint8Array>;
16
14
  }): Promise<void>;
17
15
  //# sourceMappingURL=remote.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/handlers/remote.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AA+BtE,wBAAsB,kBAAkB,CACtC,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CAC7C,GACA,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
1
+ {"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/handlers/remote.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAKtE,wBAAsB,kBAAkB,CACtC,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,UAAU,CAAC,EACjD,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB,GACA,OAAO,CAAC,IAAI,CAAC,CAmCf"}
@@ -9,29 +9,8 @@
9
9
  import * as fs from "node:fs/promises";
10
10
  import * as path from "node:path";
11
11
  import * as mdsync from "../mdsync/index.js";
12
- const DEFAULT_GATEWAY = "https://storacha.link";
13
12
  const isMarkdown = (filePath) => filePath.endsWith(".md");
14
- /** Drain a ReadableStream into a single Uint8Array. */
15
- async function drainStream(stream) {
16
- const reader = stream.getReader();
17
- const chunks = [];
18
- while (true) {
19
- const { done, value } = await reader.read();
20
- if (done)
21
- break;
22
- chunks.push(value);
23
- }
24
- const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
25
- const result = new Uint8Array(totalLength);
26
- let offset = 0;
27
- for (const chunk of chunks) {
28
- result.set(chunk, offset);
29
- offset += chunk.length;
30
- }
31
- return result;
32
- }
33
- export async function applyRemoteChanges(changedPaths, entries, workspace, options) {
34
- const gateway = options?.gateway ?? DEFAULT_GATEWAY;
13
+ export async function applyRemoteChanges(changedPaths, entries, workspace, contentFetcher, options) {
35
14
  for (const relativePath of changedPaths) {
36
15
  const cid = entries.get(relativePath);
37
16
  const fullPath = path.join(workspace, relativePath);
@@ -50,25 +29,16 @@ export async function applyRemoteChanges(changedPaths, entries, workspace, optio
50
29
  options?.current) {
51
30
  // Markdown: resolve via mdsync CRDT merge.
52
31
  // For single-device, unencrypted blocks are stored locally.
53
- const content = await mdsync.get(options.blocks, options.current, relativePath, options.decrypt);
32
+ const content = await mdsync.get(options.blocks, contentFetcher, options.current, relativePath);
54
33
  if (content != null) {
55
34
  await fs.mkdir(path.dirname(fullPath), { recursive: true });
56
35
  await fs.writeFile(fullPath, content);
57
36
  }
58
37
  }
59
- else if (options?.decrypt) {
60
- const bytes = await options.decrypt(cid);
61
- await fs.mkdir(path.dirname(fullPath), { recursive: true });
62
- await fs.writeFile(fullPath, bytes);
63
- }
64
38
  else {
65
- // Public space: fetch full file from gateway (handles UnixFS reassembly)
66
- const res = await fetch(`${gateway}/ipfs/${cid}`);
67
- if (!res.ok) {
68
- throw new Error(`Gateway fetch failed for ${cid}: ${res.status}`);
69
- }
39
+ const bytes = await contentFetcher(cid);
70
40
  await fs.mkdir(path.dirname(fullPath), { recursive: true });
71
- await fs.writeFile(fullPath, new Uint8Array(await res.arrayBuffer()));
41
+ await fs.writeFile(fullPath, bytes);
72
42
  }
73
43
  }
74
44
  }
package/dist/index.d.ts CHANGED
@@ -3,7 +3,6 @@
3
3
  */
4
4
  export * from "./types/index.js";
5
5
  export * from "./blockstore/index.js";
6
- export { encodeWorkspaceFile, encodeFiles } from "./utils/encoder.js";
7
6
  export { diffEntries, diffRemoteChanges } from "./utils/differ.js";
8
7
  export { SyncEngine } from "./sync.js";
9
8
  export { FileWatcher } from "./watcher.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -3,7 +3,6 @@
3
3
  */
4
4
  export * from "./types/index.js";
5
5
  export * from "./blockstore/index.js";
6
- export { encodeWorkspaceFile, encodeFiles } from "./utils/encoder.js";
7
6
  export { diffEntries, diffRemoteChanges } from "./utils/differ.js";
8
7
  export { SyncEngine } from "./sync.js";
9
8
  export { FileWatcher } from "./watcher.js";
@@ -78,11 +78,11 @@ export declare const v0Put: (newMarkdown: string) => Promise<Block>;
78
78
  * Returns a single block to store, or null if no changes detected.
79
79
  * Caller is responsible for creating the Pail revision via Revision.put.
80
80
  */
81
- export declare const put: (blocks: BlockFetcher, current: ValueView, key: string, newMarkdown: string) => Promise<Block | null>;
81
+ export declare const put: (blocks: BlockFetcher, contentFetcher: (cid: CID) => Promise<Uint8Array>, current: ValueView, key: string, newMarkdown: string) => Promise<Block | null>;
82
82
  /**
83
83
  * Get the current markdown string for a key, resolving concurrent heads.
84
84
  * Returns undefined if the key doesn't exist.
85
85
  */
86
- export declare const get: (blocks: BlockFetcher, current: ValueView, key: string, decrypt?: (cid: CID) => Promise<Uint8Array>) => Promise<string | undefined>;
86
+ export declare const get: (blocks: BlockFetcher, contentFetcher: (cid: CID) => Promise<Uint8Array>, current: ValueView, key: string) => Promise<string | undefined>;
87
87
  export {};
88
88
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mdsync/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EACL,YAAY,EACZ,SAAS,EAET,SAAS,EACV,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAEL,GAAG,EAGH,WAAW,EACX,YAAY,EAUb,MAAM,oBAAoB,CAAC;AAQ5B;;;;;;;;GAQG;AACH,cAAM,aAAa;IACjB,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;gBAEd,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IAIrC,QAAQ;CAGT;AAyBD,wDAAwD;AACxD,KAAK,QAAQ,GAAG,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAElD,UAAU,6BAA6B;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,IAAI,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;IACjC;;;;OAIG;IACH,MAAM,EAAE,QAAQ,CAAC;CAClB;AAED,UAAU,gCACR,SAAQ,6BAA6B;IACrC,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,UAAU,+BACR,SAAQ,6BAA6B;IACrC,IAAI,EAAE,QAAQ,CAAC;IACf,0EAA0E;IAC1E,SAAS,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC;CACxC;AAED,KAAK,yBAAyB,GAC1B,gCAAgC,GAChC,+BAA+B,CAAC;AA8BpC;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,OAAO,yBAAyB,KAC/B,OAAO,CAAC,KAAK,CAcf,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC9B,OAAO;IAAE,KAAK,EAAE,UAAU,CAAA;CAAE,KAC3B,OAAO,CAAC,yBAAyB,CA+BnC,CAAC;AA4BF;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,KAAK,CAG9D,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,GAAG,GACd,QAAQ,YAAY,EACpB,SAAS,SAAS,EAClB,KAAK,MAAM,EACX,aAAa,MAAM,KAClB,OAAO,CAAC,KAAK,GAAG,IAAI,CAiCtB,CAAC;AA4IF;;;GAGG;AACH,eAAO,MAAM,GAAG,GACd,QAAQ,YAAY,EACpB,SAAS,SAAS,EAClB,KAAK,MAAM,EACX,UAAU,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,UAAU,CAAC,KAC1C,OAAO,CAAC,MAAM,GAAG,SAAS,CAM5B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mdsync/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EACL,YAAY,EACZ,SAAS,EAET,SAAS,EACV,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAEL,GAAG,EAGH,WAAW,EACX,YAAY,EAUb,MAAM,oBAAoB,CAAC;AAQ5B;;;;;;;;GAQG;AACH,cAAM,aAAa;IACjB,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;gBAEd,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IAIrC,QAAQ;CAGT;AAyBD,wDAAwD;AACxD,KAAK,QAAQ,GAAG,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAElD,UAAU,6BAA6B;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,IAAI,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;IACjC;;;;OAIG;IACH,MAAM,EAAE,QAAQ,CAAC;CAClB;AAED,UAAU,gCAAiC,SAAQ,6BAA6B;IAC9E,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,UAAU,+BAAgC,SAAQ,6BAA6B;IAC7E,IAAI,EAAE,QAAQ,CAAC;IACf,0EAA0E;IAC1E,SAAS,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC;CACxC;AAED,KAAK,yBAAyB,GAC1B,gCAAgC,GAChC,+BAA+B,CAAC;AA8BpC;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,OAAO,yBAAyB,KAC/B,OAAO,CAAC,KAAK,CAcf,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAU,OAAO;IAC/C,KAAK,EAAE,UAAU,CAAC;CACnB,KAAG,OAAO,CAAC,yBAAyB,CA+BpC,CAAC;AA0BF;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,KAAK,CAG9D,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,GAAG,GACd,QAAQ,YAAY,EACpB,gBAAgB,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,UAAU,CAAC,EACjD,SAAS,SAAS,EAClB,KAAK,MAAM,EACX,aAAa,MAAM,KAClB,OAAO,CAAC,KAAK,GAAG,IAAI,CAiCtB,CAAC;AA8IF;;;GAGG;AACH,eAAO,MAAM,GAAG,GACd,QAAQ,YAAY,EACpB,gBAAgB,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,UAAU,CAAC,EACjD,SAAS,SAAS,EAClB,KAAK,MAAM,KACV,OAAO,CAAC,MAAM,GAAG,SAAS,CAM5B,CAAC"}
@@ -152,8 +152,8 @@ export const v0Put = async (newMarkdown) => {
152
152
  * Returns a single block to store, or null if no changes detected.
153
153
  * Caller is responsible for creating the Pail revision via Revision.put.
154
154
  */
155
- export const put = async (blocks, current, key, newMarkdown) => {
156
- const mdEntry = await resolveValue(blocks, current, key);
155
+ export const put = async (blocks, contentFetcher, current, key, newMarkdown) => {
156
+ const mdEntry = await resolveValue(blocks, contentFetcher, current, key);
157
157
  if (!mdEntry) {
158
158
  // Key doesn't exist yet — bootstrap with firstPut.
159
159
  const entry = firstPut(newMarkdown, current.revision.map((r) => r.event.cid));
@@ -188,7 +188,7 @@ export const put = async (blocks, current, key, newMarkdown) => {
188
188
  * If there are multiple heads, finds their common ancestor in the Pail's
189
189
  * merkle clock, then replays all events from ancestor → heads in causal order.
190
190
  */
191
- const resolveValue = async (blocks, current, key, decrypt) => {
191
+ const resolveValue = async (blocks, contentFetcher, current, key) => {
192
192
  const mdEntryBlockCid = await Pail.get(blocks, current.root, key);
193
193
  if (!mdEntryBlockCid) {
194
194
  return undefined;
@@ -196,18 +196,11 @@ const resolveValue = async (blocks, current, key, decrypt) => {
196
196
  // Cache event blocks in memory so EventFetcher can find them.
197
197
  blocks = withCache(blocks, new MemoryBlockstore(current.revision.map((r) => r.event)));
198
198
  const events = new EventFetcher(blocks);
199
- // Fetch entry bytes: decrypt callback for private spaces, or raw block fetch.
200
- const getEntryBytes = async (cid) => {
201
- if (decrypt)
202
- return decrypt(cid);
203
- const block = await blocks.get(cid);
204
- if (!block)
205
- throw new Error(`Could not find block for CID ${cid}`);
206
- return block.bytes;
207
- };
208
199
  // Fast path: single head, no merge needed.
209
200
  if (current.revision.length === 1) {
210
- return decodeMarkdownEntry({ bytes: await getEntryBytes(mdEntryBlockCid) });
201
+ return decodeMarkdownEntry({
202
+ bytes: await contentFetcher(mdEntryBlockCid),
203
+ });
211
204
  }
212
205
  // Multi-head: find common ancestor and replay events in causal order.
213
206
  const ancestor = await CRDT.findCommonAncestor(events, current.revision.map((r) => r.event.cid));
@@ -220,7 +213,9 @@ const resolveValue = async (blocks, current, key, decrypt) => {
220
213
  const rootMDEntryCid = await Pail.get(blocks, root, key);
221
214
  let mdEntry;
222
215
  if (rootMDEntryCid) {
223
- mdEntry = await decodeMarkdownEntry({ bytes: await getEntryBytes(rootMDEntryCid) });
216
+ mdEntry = await decodeMarkdownEntry({
217
+ bytes: await contentFetcher(rootMDEntryCid),
218
+ });
224
219
  }
225
220
  // Get all events from ancestor → heads, sorted in deterministic causal order.
226
221
  const sorted = await CRDT.findSortedEvents(events, current.revision.map((r) => r.event.cid), ancestor);
@@ -254,7 +249,9 @@ const resolveValue = async (blocks, current, key, decrypt) => {
254
249
  if (!mdEntryCid) {
255
250
  throw new Error(`Could not find markdown entry for CID ${data.root} and key ${key}`);
256
251
  }
257
- const newMDEntry = await decodeMarkdownEntry({ bytes: await getEntryBytes(mdEntryCid) });
252
+ const newMDEntry = await decodeMarkdownEntry({
253
+ bytes: await contentFetcher(mdEntryCid),
254
+ });
258
255
  if (newMDEntry.type === "initial") {
259
256
  if (mdEntry) {
260
257
  // Concurrent initial — merge event histories and RGA trees.
@@ -295,8 +292,8 @@ const resolveValue = async (blocks, current, key, decrypt) => {
295
292
  * Get the current markdown string for a key, resolving concurrent heads.
296
293
  * Returns undefined if the key doesn't exist.
297
294
  */
298
- export const get = async (blocks, current, key, decrypt) => {
299
- const mdEntry = await resolveValue(blocks, current, key, decrypt);
295
+ export const get = async (blocks, contentFetcher, current, key) => {
296
+ const mdEntry = await resolveValue(blocks, contentFetcher, current, key);
300
297
  if (!mdEntry) {
301
298
  return undefined;
302
299
  }
package/dist/plugin.js CHANGED
@@ -367,8 +367,8 @@ export default function plugin(api) {
367
367
  engine = activeSync.engine;
368
368
  }
369
369
  else {
370
- engine = new SyncEngine(workspace);
371
- await engine.init(deviceConfig);
370
+ engine = await SyncEngine.fromConfig(workspace, deviceConfig);
371
+ await engine.start();
372
372
  }
373
373
  const state = await engine.inspect();
374
374
  console.log(`🔥 Storacha Inspect [${agentId}]`);
package/dist/sync.d.ts CHANGED
@@ -8,8 +8,18 @@
8
8
  * 5. Upload all blocks as CAR
9
9
  * 6. Apply remote changes to local filesystem
10
10
  */
11
- import type { SyncState, FileChange, DeviceConfig } from "./types/index.js";
11
+ import type { NameView, ClockConnection } from "@storacha/ucn/pail/api";
12
+ import type { SyncState, FileChange, DeviceConfig, ContentFetcher, Encoder, StorageClient } from "./types/index.js";
13
+ import { type WorkspaceBlockstore } from "./blockstore/index.js";
12
14
  import { type PailEntries } from "./utils/differ.js";
15
+ export interface SyncDeps {
16
+ blocks: WorkspaceBlockstore;
17
+ name: NameView;
18
+ client: StorageClient;
19
+ encoder: Encoder;
20
+ contentFetcher: ContentFetcher;
21
+ remotes?: ClockConnection[];
22
+ }
13
23
  export declare class SyncEngine {
14
24
  private workspace;
15
25
  private blocks;
@@ -19,23 +29,22 @@ export declare class SyncEngine {
19
29
  private carFile;
20
30
  private lastSync;
21
31
  private syncLock;
22
- private encryptionConfig?;
23
- private decryptionConfig?;
24
- private encryptedClient?;
25
- constructor(workspace: string);
32
+ private client;
33
+ private name;
34
+ private encoder;
35
+ private contentFetcher;
36
+ private remotes?;
37
+ constructor(workspace: string, deps: SyncDeps);
38
+ start(): Promise<void>;
26
39
  /**
27
40
  * Initialize sync engine with device config.
28
41
  * Creates the Storacha client and resolves the UCN name.
29
42
  */
30
- init(config: DeviceConfig): Promise<void>;
43
+ static fromConfig(workspace: string, config: DeviceConfig): Promise<SyncEngine>;
31
44
  /**
32
45
  * Require running state or throw.
33
46
  */
34
47
  private requireRunning;
35
- /**
36
- * Require state is initialized or throw.
37
- */
38
- private requireInitialized;
39
48
  /**
40
49
  * Process a batch of file changes.
41
50
  * Can be called even when not running (accumulates pending ops).
@@ -83,5 +92,6 @@ export declare class SyncEngine {
83
92
  status(): Promise<SyncState>;
84
93
  exportNameArchive(): Promise<string>;
85
94
  private storeBlocks;
95
+ private storeForUpload;
86
96
  }
87
97
  //# sourceMappingURL=sync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EAEV,YAAY,EACb,MAAM,kBAAkB,CAAC;AAS1B,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA6BxE,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAAiD;IAC9D,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,eAAe,CAAC,CAAkB;gBAE9B,SAAS,EAAE,MAAM;IAK7B;;;OAGG;IACG,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAoE/C;;OAEG;IACH,OAAO,CAAC,cAAc;IAOtB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB1D;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAMb,UAAU;IAmExB;;OAEG;YACW,iBAAiB;IAe/B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAc5C;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;YAMrB,gBAAgB;IAkC9B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC;QACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC/B,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,UAAU,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC5D,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC;IAkBI,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC;IAW5B,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;YAM5B,WAAW;CAK1B"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAa,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEnF,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EAEV,YAAY,EACZ,cAAc,EACd,OAAO,EACP,aAAa,EACd,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAiBxE,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;CAC7B;AACD,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,IAAI,CAAW;IACvB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,OAAO,CAAC,CAAoB;gBACxB,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ;IAUvC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB5B;;;OAGG;WACU,UAAU,CACrB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,UAAU,CAAC;IAoBtB;;OAEG;IACH,OAAO,CAAC,cAAc;IAOtB;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB1D;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAMb,UAAU;IAwExB;;OAEG;YACW,iBAAiB;IAe/B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAc5C;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;YAMrB,gBAAgB;IAgC9B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC;QACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC/B,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,UAAU,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC5D,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC;IAkBI,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC;IAW5B,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;YAK5B,WAAW;YAMX,cAAc;CAI7B"}