@avalanche-io/c4-node 1.0.10

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 (125) hide show
  1. package/dist/packages/core/src/base58.d.ts +5 -0
  2. package/dist/packages/core/src/base58.d.ts.map +1 -0
  3. package/dist/packages/core/src/base58.js +42 -0
  4. package/dist/packages/core/src/base58.js.map +1 -0
  5. package/dist/packages/core/src/browser/filesystem.d.ts +34 -0
  6. package/dist/packages/core/src/browser/filesystem.d.ts.map +1 -0
  7. package/dist/packages/core/src/browser/filesystem.js +169 -0
  8. package/dist/packages/core/src/browser/filesystem.js.map +1 -0
  9. package/dist/packages/core/src/browser/index.d.ts +4 -0
  10. package/dist/packages/core/src/browser/index.d.ts.map +1 -0
  11. package/dist/packages/core/src/browser/index.js +6 -0
  12. package/dist/packages/core/src/browser/index.js.map +1 -0
  13. package/dist/packages/core/src/browser/store.d.ts +28 -0
  14. package/dist/packages/core/src/browser/store.d.ts.map +1 -0
  15. package/dist/packages/core/src/browser/store.js +142 -0
  16. package/dist/packages/core/src/browser/store.js.map +1 -0
  17. package/dist/packages/core/src/browser/worker.d.ts +34 -0
  18. package/dist/packages/core/src/browser/worker.d.ts.map +1 -0
  19. package/dist/packages/core/src/browser/worker.js +96 -0
  20. package/dist/packages/core/src/browser/worker.js.map +1 -0
  21. package/dist/packages/core/src/decoder.d.ts +23 -0
  22. package/dist/packages/core/src/decoder.d.ts.map +1 -0
  23. package/dist/packages/core/src/decoder.js +431 -0
  24. package/dist/packages/core/src/decoder.js.map +1 -0
  25. package/dist/packages/core/src/diff.d.ts +48 -0
  26. package/dist/packages/core/src/diff.d.ts.map +1 -0
  27. package/dist/packages/core/src/diff.js +169 -0
  28. package/dist/packages/core/src/diff.js.map +1 -0
  29. package/dist/packages/core/src/encoder.d.ts +13 -0
  30. package/dist/packages/core/src/encoder.d.ts.map +1 -0
  31. package/dist/packages/core/src/encoder.js +125 -0
  32. package/dist/packages/core/src/encoder.js.map +1 -0
  33. package/dist/packages/core/src/entry.d.ts +59 -0
  34. package/dist/packages/core/src/entry.d.ts.map +1 -0
  35. package/dist/packages/core/src/entry.js +266 -0
  36. package/dist/packages/core/src/entry.js.map +1 -0
  37. package/dist/packages/core/src/errors.d.ts +29 -0
  38. package/dist/packages/core/src/errors.d.ts.map +1 -0
  39. package/dist/packages/core/src/errors.js +56 -0
  40. package/dist/packages/core/src/errors.js.map +1 -0
  41. package/dist/packages/core/src/filesystem.d.ts +68 -0
  42. package/dist/packages/core/src/filesystem.d.ts.map +1 -0
  43. package/dist/packages/core/src/filesystem.js +62 -0
  44. package/dist/packages/core/src/filesystem.js.map +1 -0
  45. package/dist/packages/core/src/id.d.ts +33 -0
  46. package/dist/packages/core/src/id.d.ts.map +1 -0
  47. package/dist/packages/core/src/id.js +126 -0
  48. package/dist/packages/core/src/id.js.map +1 -0
  49. package/dist/packages/core/src/identify-content.d.ts +17 -0
  50. package/dist/packages/core/src/identify-content.d.ts.map +1 -0
  51. package/dist/packages/core/src/identify-content.js +70 -0
  52. package/dist/packages/core/src/identify-content.js.map +1 -0
  53. package/dist/packages/core/src/index.d.ts +23 -0
  54. package/dist/packages/core/src/index.d.ts.map +1 -0
  55. package/dist/packages/core/src/index.js +41 -0
  56. package/dist/packages/core/src/index.js.map +1 -0
  57. package/dist/packages/core/src/manifest.d.ts +68 -0
  58. package/dist/packages/core/src/manifest.d.ts.map +1 -0
  59. package/dist/packages/core/src/manifest.js +463 -0
  60. package/dist/packages/core/src/manifest.js.map +1 -0
  61. package/dist/packages/core/src/memory-fs.d.ts +33 -0
  62. package/dist/packages/core/src/memory-fs.d.ts.map +1 -0
  63. package/dist/packages/core/src/memory-fs.js +187 -0
  64. package/dist/packages/core/src/memory-fs.js.map +1 -0
  65. package/dist/packages/core/src/memory-store.d.ts +21 -0
  66. package/dist/packages/core/src/memory-store.d.ts.map +1 -0
  67. package/dist/packages/core/src/memory-store.js +57 -0
  68. package/dist/packages/core/src/memory-store.js.map +1 -0
  69. package/dist/packages/core/src/naturalsort.d.ts +2 -0
  70. package/dist/packages/core/src/naturalsort.d.ts.map +1 -0
  71. package/dist/packages/core/src/naturalsort.js +88 -0
  72. package/dist/packages/core/src/naturalsort.js.map +1 -0
  73. package/dist/packages/core/src/observable.d.ts +54 -0
  74. package/dist/packages/core/src/observable.d.ts.map +1 -0
  75. package/dist/packages/core/src/observable.js +150 -0
  76. package/dist/packages/core/src/observable.js.map +1 -0
  77. package/dist/packages/core/src/pool.d.ts +38 -0
  78. package/dist/packages/core/src/pool.d.ts.map +1 -0
  79. package/dist/packages/core/src/pool.js +113 -0
  80. package/dist/packages/core/src/pool.js.map +1 -0
  81. package/dist/packages/core/src/reconcile.d.ts +43 -0
  82. package/dist/packages/core/src/reconcile.d.ts.map +1 -0
  83. package/dist/packages/core/src/reconcile.js +172 -0
  84. package/dist/packages/core/src/reconcile.js.map +1 -0
  85. package/dist/packages/core/src/resolver.d.ts +67 -0
  86. package/dist/packages/core/src/resolver.d.ts.map +1 -0
  87. package/dist/packages/core/src/resolver.js +110 -0
  88. package/dist/packages/core/src/resolver.js.map +1 -0
  89. package/dist/packages/core/src/safename.d.ts +7 -0
  90. package/dist/packages/core/src/safename.d.ts.map +1 -0
  91. package/dist/packages/core/src/safename.js +354 -0
  92. package/dist/packages/core/src/safename.js.map +1 -0
  93. package/dist/packages/core/src/scanner.d.ts +25 -0
  94. package/dist/packages/core/src/scanner.d.ts.map +1 -0
  95. package/dist/packages/core/src/scanner.js +97 -0
  96. package/dist/packages/core/src/scanner.js.map +1 -0
  97. package/dist/packages/core/src/store.d.ts +39 -0
  98. package/dist/packages/core/src/store.d.ts.map +1 -0
  99. package/dist/packages/core/src/store.js +53 -0
  100. package/dist/packages/core/src/store.js.map +1 -0
  101. package/dist/packages/core/src/tree.d.ts +16 -0
  102. package/dist/packages/core/src/tree.d.ts.map +1 -0
  103. package/dist/packages/core/src/tree.js +45 -0
  104. package/dist/packages/core/src/tree.js.map +1 -0
  105. package/dist/packages/core/src/verify.d.ts +29 -0
  106. package/dist/packages/core/src/verify.d.ts.map +1 -0
  107. package/dist/packages/core/src/verify.js +85 -0
  108. package/dist/packages/core/src/verify.js.map +1 -0
  109. package/dist/packages/core/src/workspace.d.ts +72 -0
  110. package/dist/packages/core/src/workspace.d.ts.map +1 -0
  111. package/dist/packages/core/src/workspace.js +135 -0
  112. package/dist/packages/core/src/workspace.js.map +1 -0
  113. package/dist/packages/node/src/index.d.ts +4 -0
  114. package/dist/packages/node/src/index.d.ts.map +1 -0
  115. package/dist/packages/node/src/index.js +6 -0
  116. package/dist/packages/node/src/index.js.map +1 -0
  117. package/dist/packages/node/src/node-fs.d.ts +24 -0
  118. package/dist/packages/node/src/node-fs.d.ts.map +1 -0
  119. package/dist/packages/node/src/node-fs.js +84 -0
  120. package/dist/packages/node/src/node-fs.js.map +1 -0
  121. package/dist/packages/node/src/tree-store.d.ts +22 -0
  122. package/dist/packages/node/src/tree-store.d.ts.map +1 -0
  123. package/dist/packages/node/src/tree-store.js +77 -0
  124. package/dist/packages/node/src/tree-store.js.map +1 -0
  125. package/package.json +44 -0
@@ -0,0 +1,113 @@
1
+ import { parse as parseC4ID } from './id.js';
2
+ import { joinPath } from './filesystem.js';
3
+ import { isDir } from './entry.js';
4
+ /**
5
+ * Bundle a manifest with its referenced content for portable transfer.
6
+ *
7
+ * Creates:
8
+ * outputDir/
9
+ * <name>.c4m — the manifest
10
+ * objects/ — content store (flat, keyed by C4 ID string)
11
+ */
12
+ export async function pool(manifest, outputDir, fs, store, options) {
13
+ const name = options?.manifestName ?? 'manifest.c4m';
14
+ const objectsDir = joinPath(outputDir, 'objects');
15
+ let copied = 0;
16
+ let skipped = 0;
17
+ let missing = 0;
18
+ // Create output structure
19
+ await fs.mkdir(outputDir, { recursive: true });
20
+ await fs.mkdir(objectsDir, { recursive: true });
21
+ // Write manifest
22
+ const c4mText = manifest.encode();
23
+ const c4mBytes = new TextEncoder().encode(c4mText);
24
+ const manifestPath = joinPath(outputDir, name);
25
+ await fs.writeFile(manifestPath, c4mBytes);
26
+ // Collect unique C4 IDs from manifest
27
+ const ids = new Set();
28
+ for (const [, entry] of manifest) {
29
+ if (!isDir(entry) && entry.c4id && !entry.c4id.isNil()) {
30
+ ids.add(entry.c4id.toString());
31
+ }
32
+ }
33
+ // Copy objects
34
+ const idList = [...ids];
35
+ for (let i = 0; i < idList.length; i++) {
36
+ const idStr = idList[i];
37
+ options?.progress?.(idStr, i, idList.length);
38
+ const objectPath = joinPath(objectsDir, idStr);
39
+ // Check if already in bundle
40
+ try {
41
+ await fs.stat(objectPath);
42
+ skipped++;
43
+ continue;
44
+ }
45
+ catch { /* doesn't exist, proceed */ }
46
+ // Copy from store
47
+ try {
48
+ const c4id = parseC4ID(idStr);
49
+ if (await store.has(c4id)) {
50
+ const stream = await store.get(c4id);
51
+ await fs.writeFile(objectPath, stream);
52
+ copied++;
53
+ }
54
+ else {
55
+ missing++;
56
+ }
57
+ }
58
+ catch {
59
+ missing++;
60
+ }
61
+ }
62
+ return { copied, skipped, missing, manifestPath };
63
+ }
64
+ /**
65
+ * Absorb a pool bundle into a local store.
66
+ * Reads objects from bundleDir/objects/ and puts them in the store.
67
+ * Returns paths to any .c4m files found in the bundle.
68
+ */
69
+ export async function ingest(bundleDir, fs, store, options) {
70
+ let copied = 0;
71
+ let skipped = 0;
72
+ const manifests = [];
73
+ // Find .c4m files in bundle root
74
+ for await (const entry of fs.readDir(bundleDir)) {
75
+ if (entry.name.endsWith('.c4m')) {
76
+ manifests.push(entry.name);
77
+ }
78
+ }
79
+ // Ingest objects
80
+ const objectsDir = joinPath(bundleDir, 'objects');
81
+ const objects = [];
82
+ try {
83
+ for await (const entry of fs.readDir(objectsDir)) {
84
+ if (!entry.isDirectory && entry.name.startsWith('c4')) {
85
+ objects.push(entry.name);
86
+ }
87
+ }
88
+ }
89
+ catch {
90
+ // No objects directory — just manifests
91
+ }
92
+ for (let i = 0; i < objects.length; i++) {
93
+ const name = objects[i];
94
+ options?.progress?.(name, i, objects.length);
95
+ try {
96
+ const c4id = parseC4ID(name);
97
+ if (await store.has(c4id)) {
98
+ skipped++;
99
+ continue;
100
+ }
101
+ }
102
+ catch {
103
+ // Invalid C4 ID filename — skip
104
+ continue;
105
+ }
106
+ const objectPath = joinPath(objectsDir, name);
107
+ const stream = await fs.readFile(objectPath);
108
+ await store.put(stream);
109
+ copied++;
110
+ }
111
+ return { copied, skipped, manifests };
112
+ }
113
+ //# sourceMappingURL=pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.js","sourceRoot":"","sources":["../../../../../core/src/pool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,SAAS,CAAA;AAE5C,OAAO,EAAE,QAAQ,EAAgC,MAAM,iBAAiB,CAAA;AAGxE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAsBlC;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CACxB,QAAkB,EAClB,SAAiB,EACjB,EAAc,EACd,KAAY,EACZ,OAAqB;IAErB,MAAM,IAAI,GAAG,OAAO,EAAE,YAAY,IAAI,cAAc,CAAA;IACpD,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;IACjD,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,IAAI,OAAO,GAAG,CAAC,CAAA;IAEf,0BAA0B;IAC1B,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC9C,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE/C,iBAAiB;IACjB,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAA;IACjC,MAAM,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAClD,MAAM,YAAY,GAAG,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;IAC9C,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;IAE1C,sCAAsC;IACtC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAA;IAC7B,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,QAAQ,EAAE,CAAC;QACjC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YACvD,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QAChC,CAAC;IACH,CAAC;IAED,eAAe;IACf,MAAM,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,CAAA;IACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;QACvB,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;QAE5C,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC,CAAA;QAE9C,6BAA6B;QAC7B,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;YACzB,OAAO,EAAE,CAAA;YACT,SAAQ;QACV,CAAC;QAAC,MAAM,CAAC,CAAC,4BAA4B,CAAC,CAAC;QAExC,kBAAkB;QAClB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,CAAA;YAC7B,IAAI,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;gBACpC,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;gBACtC,MAAM,EAAE,CAAA;YACV,CAAC;iBAAM,CAAC;gBACN,OAAO,EAAE,CAAA;YACX,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAA;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,SAAiB,EACjB,EAAc,EACd,KAAY,EACZ,OAA6E;IAE7E,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,MAAM,SAAS,GAAa,EAAE,CAAA;IAE9B,iCAAiC;IACjC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAChD,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC5B,CAAC;IACH,CAAC;IAED,iBAAiB;IACjB,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;IACjD,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,IAAI,CAAC;QACH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;IAC1C,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;QACvB,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;QAE5C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;YAC5B,IAAI,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,OAAO,EAAE,CAAA;gBACT,SAAQ;YACV,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,gCAAgC;YAChC,SAAQ;QACV,CAAC;QAED,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;QAC7C,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;QAC5C,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACvB,MAAM,EAAE,CAAA;IACV,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAA;AACvC,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { FileSystem } from './filesystem.js';
2
+ import type { Store } from './store.js';
3
+ import type { Manifest } from './manifest.js';
4
+ import { type Entry } from './entry.js';
5
+ /** Types of operations the reconciler can perform. */
6
+ export type ReconcileOpType = 'mkdir' | 'create' | 'update' | 'remove' | 'rmdir';
7
+ /** A single reconciliation operation. */
8
+ export interface ReconcileOp {
9
+ type: ReconcileOpType;
10
+ path: string;
11
+ entry?: Entry;
12
+ }
13
+ /** Plan computed by the reconciler before execution. */
14
+ export interface ReconcilePlan {
15
+ operations: ReconcileOp[];
16
+ missing: string[];
17
+ skipped: string[];
18
+ }
19
+ /** Result of executing a reconciliation plan. */
20
+ export interface ReconcileResult {
21
+ created: number;
22
+ updated: number;
23
+ removed: number;
24
+ skipped: number;
25
+ errors: Array<{
26
+ path: string;
27
+ error: Error;
28
+ }>;
29
+ }
30
+ export interface ReconcileOptions {
31
+ progress?: (op: ReconcileOpType, path: string, index: number, total: number) => void;
32
+ dryRun?: boolean;
33
+ }
34
+ /**
35
+ * Plan reconciliation: determine what operations are needed to make
36
+ * the filesystem at rootPath match the manifest.
37
+ */
38
+ export declare function plan(manifest: Manifest, fs: FileSystem, rootPath: string, store?: Store): Promise<ReconcilePlan>;
39
+ /**
40
+ * Execute a reconciliation plan.
41
+ */
42
+ export declare function apply(reconcilePlan: ReconcilePlan, fs: FileSystem, rootPath: string, store?: Store, options?: ReconcileOptions): Promise<ReconcileResult>;
43
+ //# sourceMappingURL=reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../../../../core/src/reconcile.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,KAAK,KAAK,EAAS,MAAM,YAAY,CAAA;AAE9C,sDAAsD;AACtD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEhF,yCAAyC;AACzC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,KAAK,CAAA;CACd;AAED,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,WAAW,EAAE,CAAA;IACzB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC,CAAA;CAC9C;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpF,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;;GAGG;AACH,wBAAsB,IAAI,CACxB,QAAQ,EAAE,QAAQ,EAClB,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,KAAK,GACZ,OAAO,CAAC,aAAa,CAAC,CAyFxB;AAED;;GAEG;AACH,wBAAsB,KAAK,CACzB,aAAa,EAAE,aAAa,EAC5B,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,KAAK,EACb,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,eAAe,CAAC,CAwD1B"}
@@ -0,0 +1,172 @@
1
+ import { identifyContent } from './identify-content.js';
2
+ import { streamToBytes, joinPath } from './filesystem.js';
3
+ import { isDir } from './entry.js';
4
+ /**
5
+ * Plan reconciliation: determine what operations are needed to make
6
+ * the filesystem at rootPath match the manifest.
7
+ */
8
+ export async function plan(manifest, fs, rootPath, store) {
9
+ const operations = [];
10
+ const missing = [];
11
+ const skipped = [];
12
+ // Build desired state from manifest
13
+ const desired = new Map();
14
+ const desiredDirs = new Set();
15
+ for (const [path, entry] of manifest) {
16
+ if (isDir(entry)) {
17
+ desiredDirs.add(path);
18
+ }
19
+ else {
20
+ desired.set(path, entry);
21
+ }
22
+ }
23
+ // Collect what actually exists on disk
24
+ const actual = new Map(); // path -> isDir
25
+ await collectExisting(fs, rootPath, '', actual);
26
+ // Phase 1: directories to create (sorted by depth for correct ordering)
27
+ const sortedDirs = [...desiredDirs].sort();
28
+ for (const dir of sortedDirs) {
29
+ const fullPath = joinPath(rootPath, dir);
30
+ try {
31
+ const stat = await fs.stat(fullPath);
32
+ if (!stat.isDirectory) {
33
+ operations.push({ type: 'remove', path: dir });
34
+ operations.push({ type: 'mkdir', path: dir });
35
+ }
36
+ }
37
+ catch {
38
+ operations.push({ type: 'mkdir', path: dir });
39
+ }
40
+ }
41
+ // Phase 2: files to create or update
42
+ for (const [path, entry] of desired) {
43
+ const fullPath = joinPath(rootPath, path);
44
+ try {
45
+ const stat = await fs.stat(fullPath);
46
+ if (stat.isDirectory) {
47
+ // Directory exists where file should be — remove first
48
+ operations.push({ type: 'remove', path });
49
+ operations.push({ type: 'create', path, entry });
50
+ }
51
+ else if (entry.c4id) {
52
+ // File exists — check if content matches
53
+ const stream = await fs.readFile(fullPath);
54
+ const bytes = await streamToBytes(stream);
55
+ const actualId = await identifyContent(bytes);
56
+ if (actualId.equals(entry.c4id)) {
57
+ skipped.push(path);
58
+ }
59
+ else {
60
+ // Check store has the content
61
+ if (store && !(await store.has(entry.c4id))) {
62
+ missing.push(entry.c4id.toString());
63
+ }
64
+ operations.push({ type: 'update', path, entry });
65
+ }
66
+ }
67
+ else {
68
+ skipped.push(path); // no C4 ID to compare against
69
+ }
70
+ }
71
+ catch {
72
+ // File doesn't exist — create it
73
+ if (entry.c4id && store && !(await store.has(entry.c4id))) {
74
+ missing.push(entry.c4id.toString());
75
+ }
76
+ operations.push({ type: 'create', path, entry });
77
+ }
78
+ }
79
+ // Phase 3: files/dirs to remove (on disk but not in manifest)
80
+ const desiredPaths = new Set([...desired.keys(), ...desiredDirs]);
81
+ const toRemove = [];
82
+ for (const [path, pathIsDir] of actual) {
83
+ // Normalize: directory paths in manifest end with /
84
+ const manifestPath = pathIsDir ? (path.endsWith('/') ? path : path + '/') : path;
85
+ if (!desiredPaths.has(manifestPath) && !desiredPaths.has(path)) {
86
+ toRemove.push(path);
87
+ }
88
+ }
89
+ // Sort removals deepest-first so children are removed before parents
90
+ toRemove.sort((a, b) => b.split('/').length - a.split('/').length);
91
+ for (const path of toRemove) {
92
+ const isDirectory = actual.get(path);
93
+ operations.push({ type: isDirectory ? 'rmdir' : 'remove', path });
94
+ }
95
+ return { operations, missing, skipped };
96
+ }
97
+ /**
98
+ * Execute a reconciliation plan.
99
+ */
100
+ export async function apply(reconcilePlan, fs, rootPath, store, options) {
101
+ const result = {
102
+ created: 0,
103
+ updated: 0,
104
+ removed: 0,
105
+ skipped: reconcilePlan.skipped.length,
106
+ errors: [],
107
+ };
108
+ const ops = reconcilePlan.operations;
109
+ for (let i = 0; i < ops.length; i++) {
110
+ const op = ops[i];
111
+ const fullPath = joinPath(rootPath, op.path);
112
+ options?.progress?.(op.type, op.path, i, ops.length);
113
+ try {
114
+ switch (op.type) {
115
+ case 'mkdir':
116
+ await fs.mkdir(fullPath, { recursive: true });
117
+ break;
118
+ case 'create':
119
+ case 'update': {
120
+ if (op.entry?.c4id && store) {
121
+ const stream = await store.get(op.entry.c4id);
122
+ await fs.writeFile(fullPath, stream);
123
+ }
124
+ // Apply metadata if supported
125
+ if (op.entry && fs.setMeta) {
126
+ await fs.setMeta(fullPath, {
127
+ mode: op.entry.mode,
128
+ mtime: op.entry.timestamp,
129
+ });
130
+ }
131
+ if (op.type === 'create')
132
+ result.created++;
133
+ else
134
+ result.updated++;
135
+ break;
136
+ }
137
+ case 'remove':
138
+ await fs.remove(fullPath);
139
+ result.removed++;
140
+ break;
141
+ case 'rmdir':
142
+ await fs.remove(fullPath, { recursive: true });
143
+ result.removed++;
144
+ break;
145
+ }
146
+ }
147
+ catch (err) {
148
+ result.errors.push({ path: op.path, error: err });
149
+ }
150
+ }
151
+ return result;
152
+ }
153
+ /** Recursively collect all paths that exist on disk. */
154
+ async function collectExisting(fs, rootPath, relativePath, result) {
155
+ const fullPath = relativePath ? joinPath(rootPath, relativePath) : rootPath;
156
+ try {
157
+ for await (const entry of fs.readDir(fullPath)) {
158
+ // Skip hidden files/dirs
159
+ if (entry.name.startsWith('.'))
160
+ continue;
161
+ const childRelative = relativePath ? `${relativePath}/${entry.name}` : entry.name;
162
+ result.set(childRelative, entry.isDirectory);
163
+ if (entry.isDirectory) {
164
+ await collectExisting(fs, rootPath, childRelative, result);
165
+ }
166
+ }
167
+ }
168
+ catch {
169
+ // Directory doesn't exist or isn't readable — skip
170
+ }
171
+ }
172
+ //# sourceMappingURL=reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../../../../core/src/reconcile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import type { C4ID } from './id.js';
2
+ import type { Store } from './store.js';
3
+ /**
4
+ * A source of content addressable by C4 ID.
5
+ * Every Store is a ContentSource, but sources can also be
6
+ * HTTP endpoints, peer connections, or anything that serves bytes by ID.
7
+ */
8
+ export interface ContentSource {
9
+ /** Human-readable name for this source. */
10
+ readonly name: string;
11
+ /** Priority (lower = tried first in sequential mode). */
12
+ readonly priority: number;
13
+ /** Check if content is available. */
14
+ has(id: C4ID): Promise<boolean>;
15
+ /** Retrieve content. Throws ContentNotFoundError if unavailable. */
16
+ get(id: C4ID): Promise<ReadableStream<Uint8Array>>;
17
+ }
18
+ /** Result of a resolved content fetch. */
19
+ export interface ResolveResult {
20
+ stream: ReadableStream<Uint8Array>;
21
+ source: string;
22
+ }
23
+ /** Wrap any Store as a ContentSource. */
24
+ export declare function storeAsSource(store: Store, name: string, priority?: number): ContentSource;
25
+ /**
26
+ * Multi-source content resolver. Tries sources in parallel (race mode)
27
+ * or sequential (priority mode) to find content by C4 ID.
28
+ *
29
+ * Usage:
30
+ * const resolver = new ContentResolver()
31
+ * resolver.addSource(storeAsSource(localStore, 'local', 0))
32
+ * resolver.addSource(storeAsSource(remoteStore, 'remote', 10))
33
+ * const stream = await resolver.get(id)
34
+ */
35
+ export declare class ContentResolver {
36
+ private sources;
37
+ /** Add a content source. */
38
+ addSource(source: ContentSource): void;
39
+ /** Remove a source by name. */
40
+ removeSource(name: string): void;
41
+ /** List registered sources. */
42
+ listSources(): ReadonlyArray<{
43
+ name: string;
44
+ priority: number;
45
+ }>;
46
+ /** Check if any source has the content. Tries all in parallel. */
47
+ has(id: C4ID): Promise<boolean>;
48
+ /**
49
+ * Retrieve content from the fastest available source (race mode).
50
+ * All sources that claim to have the content are raced;
51
+ * the first to deliver wins.
52
+ */
53
+ get(id: C4ID): Promise<ReadableStream<Uint8Array>>;
54
+ /**
55
+ * Resolve content with metadata about which source served it.
56
+ * Uses a two-phase approach:
57
+ * 1. Check which sources have the content (parallel).
58
+ * 2. Race the available sources for the actual data.
59
+ */
60
+ resolve(id: C4ID): Promise<ResolveResult>;
61
+ /**
62
+ * Sequential resolution — try sources in priority order, return first success.
63
+ * More predictable than race mode; useful when source cost varies.
64
+ */
65
+ getSequential(id: C4ID): Promise<ResolveResult>;
66
+ }
67
+ //# sourceMappingURL=resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../../../core/src/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAGvC;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IAEzB,qCAAqC;IACrC,GAAG,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAE/B,oEAAoE;IACpE,GAAG,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAA;CACnD;AAED,0CAA0C;AAC1C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;IAClC,MAAM,EAAE,MAAM,CAAA;CACf;AAED,yCAAyC;AACzC,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,GAAG,aAAa,CAO7F;AAED;;;;;;;;;GASG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAsB;IAErC,4BAA4B;IAC5B,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAKtC,+BAA+B;IAC/B,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIhC,+BAA+B;IAC/B,WAAW,IAAI,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAIhE,kEAAkE;IAC5D,GAAG,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAQrC;;;;OAIG;IACG,GAAG,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IAKxD;;;;;OAKG;IACG,OAAO,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC;IAqC/C;;;OAGG;IACG,aAAa,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC;CAatD"}
@@ -0,0 +1,110 @@
1
+ import { ContentNotFoundError } from './store.js';
2
+ /** Wrap any Store as a ContentSource. */
3
+ export function storeAsSource(store, name, priority = 0) {
4
+ return {
5
+ name,
6
+ priority,
7
+ has: (id) => store.has(id),
8
+ get: (id) => store.get(id),
9
+ };
10
+ }
11
+ /**
12
+ * Multi-source content resolver. Tries sources in parallel (race mode)
13
+ * or sequential (priority mode) to find content by C4 ID.
14
+ *
15
+ * Usage:
16
+ * const resolver = new ContentResolver()
17
+ * resolver.addSource(storeAsSource(localStore, 'local', 0))
18
+ * resolver.addSource(storeAsSource(remoteStore, 'remote', 10))
19
+ * const stream = await resolver.get(id)
20
+ */
21
+ export class ContentResolver {
22
+ constructor() {
23
+ this.sources = [];
24
+ }
25
+ /** Add a content source. */
26
+ addSource(source) {
27
+ this.sources.push(source);
28
+ this.sources.sort((a, b) => a.priority - b.priority);
29
+ }
30
+ /** Remove a source by name. */
31
+ removeSource(name) {
32
+ this.sources = this.sources.filter(s => s.name !== name);
33
+ }
34
+ /** List registered sources. */
35
+ listSources() {
36
+ return this.sources.map(s => ({ name: s.name, priority: s.priority }));
37
+ }
38
+ /** Check if any source has the content. Tries all in parallel. */
39
+ async has(id) {
40
+ if (this.sources.length === 0)
41
+ return false;
42
+ const results = await Promise.all(this.sources.map(s => s.has(id).catch(() => false)));
43
+ return results.some(r => r);
44
+ }
45
+ /**
46
+ * Retrieve content from the fastest available source (race mode).
47
+ * All sources that claim to have the content are raced;
48
+ * the first to deliver wins.
49
+ */
50
+ async get(id) {
51
+ const result = await this.resolve(id);
52
+ return result.stream;
53
+ }
54
+ /**
55
+ * Resolve content with metadata about which source served it.
56
+ * Uses a two-phase approach:
57
+ * 1. Check which sources have the content (parallel).
58
+ * 2. Race the available sources for the actual data.
59
+ */
60
+ async resolve(id) {
61
+ if (this.sources.length === 0) {
62
+ throw new ContentNotFoundError(id);
63
+ }
64
+ // Phase 1: find which sources have it
65
+ const availability = await Promise.all(this.sources.map(async (s) => {
66
+ try {
67
+ return { source: s, available: await s.has(id) };
68
+ }
69
+ catch {
70
+ return { source: s, available: false };
71
+ }
72
+ }));
73
+ const available = availability
74
+ .filter(a => a.available)
75
+ .map(a => a.source);
76
+ if (available.length === 0) {
77
+ throw new ContentNotFoundError(id);
78
+ }
79
+ // Phase 2: race available sources
80
+ if (available.length === 1) {
81
+ const s = available[0];
82
+ return { stream: await s.get(id), source: s.name };
83
+ }
84
+ // Race multiple sources
85
+ const result = await Promise.any(available.map(async (s) => {
86
+ const stream = await s.get(id);
87
+ return { stream, source: s.name };
88
+ }));
89
+ return result;
90
+ }
91
+ /**
92
+ * Sequential resolution — try sources in priority order, return first success.
93
+ * More predictable than race mode; useful when source cost varies.
94
+ */
95
+ async getSequential(id) {
96
+ for (const source of this.sources) {
97
+ try {
98
+ if (await source.has(id)) {
99
+ const stream = await source.get(id);
100
+ return { stream, source: source.name };
101
+ }
102
+ }
103
+ catch {
104
+ continue;
105
+ }
106
+ }
107
+ throw new ContentNotFoundError(id);
108
+ }
109
+ }
110
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../../../../core/src/resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare function safeName(raw: string): string;
2
+ export declare function unsafeName(encoded: string): string;
3
+ export declare function escapeC4MName(s: string, isSequence: boolean): string;
4
+ export declare function unescapeC4MName(s: string): string;
5
+ export declare function formatName(name: string, isSequence: boolean): string;
6
+ export declare function formatTarget(target: string): string;
7
+ //# sourceMappingURL=safename.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safename.d.ts","sourceRoot":"","sources":["../../../../../core/src/safename.ts"],"names":[],"mappings":"AAqDA,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAyE5C;AAID,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA4ElD;AAwDD,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CA6CpE;AAID,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAoBjD;AAKD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CASpE;AAID,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAqBnD"}