@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.
- package/dist/packages/core/src/base58.d.ts +5 -0
- package/dist/packages/core/src/base58.d.ts.map +1 -0
- package/dist/packages/core/src/base58.js +42 -0
- package/dist/packages/core/src/base58.js.map +1 -0
- package/dist/packages/core/src/browser/filesystem.d.ts +34 -0
- package/dist/packages/core/src/browser/filesystem.d.ts.map +1 -0
- package/dist/packages/core/src/browser/filesystem.js +169 -0
- package/dist/packages/core/src/browser/filesystem.js.map +1 -0
- package/dist/packages/core/src/browser/index.d.ts +4 -0
- package/dist/packages/core/src/browser/index.d.ts.map +1 -0
- package/dist/packages/core/src/browser/index.js +6 -0
- package/dist/packages/core/src/browser/index.js.map +1 -0
- package/dist/packages/core/src/browser/store.d.ts +28 -0
- package/dist/packages/core/src/browser/store.d.ts.map +1 -0
- package/dist/packages/core/src/browser/store.js +142 -0
- package/dist/packages/core/src/browser/store.js.map +1 -0
- package/dist/packages/core/src/browser/worker.d.ts +34 -0
- package/dist/packages/core/src/browser/worker.d.ts.map +1 -0
- package/dist/packages/core/src/browser/worker.js +96 -0
- package/dist/packages/core/src/browser/worker.js.map +1 -0
- package/dist/packages/core/src/decoder.d.ts +23 -0
- package/dist/packages/core/src/decoder.d.ts.map +1 -0
- package/dist/packages/core/src/decoder.js +431 -0
- package/dist/packages/core/src/decoder.js.map +1 -0
- package/dist/packages/core/src/diff.d.ts +48 -0
- package/dist/packages/core/src/diff.d.ts.map +1 -0
- package/dist/packages/core/src/diff.js +169 -0
- package/dist/packages/core/src/diff.js.map +1 -0
- package/dist/packages/core/src/encoder.d.ts +13 -0
- package/dist/packages/core/src/encoder.d.ts.map +1 -0
- package/dist/packages/core/src/encoder.js +125 -0
- package/dist/packages/core/src/encoder.js.map +1 -0
- package/dist/packages/core/src/entry.d.ts +59 -0
- package/dist/packages/core/src/entry.d.ts.map +1 -0
- package/dist/packages/core/src/entry.js +266 -0
- package/dist/packages/core/src/entry.js.map +1 -0
- package/dist/packages/core/src/errors.d.ts +29 -0
- package/dist/packages/core/src/errors.d.ts.map +1 -0
- package/dist/packages/core/src/errors.js +56 -0
- package/dist/packages/core/src/errors.js.map +1 -0
- package/dist/packages/core/src/filesystem.d.ts +68 -0
- package/dist/packages/core/src/filesystem.d.ts.map +1 -0
- package/dist/packages/core/src/filesystem.js +62 -0
- package/dist/packages/core/src/filesystem.js.map +1 -0
- package/dist/packages/core/src/id.d.ts +33 -0
- package/dist/packages/core/src/id.d.ts.map +1 -0
- package/dist/packages/core/src/id.js +126 -0
- package/dist/packages/core/src/id.js.map +1 -0
- package/dist/packages/core/src/identify-content.d.ts +17 -0
- package/dist/packages/core/src/identify-content.d.ts.map +1 -0
- package/dist/packages/core/src/identify-content.js +70 -0
- package/dist/packages/core/src/identify-content.js.map +1 -0
- package/dist/packages/core/src/index.d.ts +23 -0
- package/dist/packages/core/src/index.d.ts.map +1 -0
- package/dist/packages/core/src/index.js +41 -0
- package/dist/packages/core/src/index.js.map +1 -0
- package/dist/packages/core/src/manifest.d.ts +68 -0
- package/dist/packages/core/src/manifest.d.ts.map +1 -0
- package/dist/packages/core/src/manifest.js +463 -0
- package/dist/packages/core/src/manifest.js.map +1 -0
- package/dist/packages/core/src/memory-fs.d.ts +33 -0
- package/dist/packages/core/src/memory-fs.d.ts.map +1 -0
- package/dist/packages/core/src/memory-fs.js +187 -0
- package/dist/packages/core/src/memory-fs.js.map +1 -0
- package/dist/packages/core/src/memory-store.d.ts +21 -0
- package/dist/packages/core/src/memory-store.d.ts.map +1 -0
- package/dist/packages/core/src/memory-store.js +57 -0
- package/dist/packages/core/src/memory-store.js.map +1 -0
- package/dist/packages/core/src/naturalsort.d.ts +2 -0
- package/dist/packages/core/src/naturalsort.d.ts.map +1 -0
- package/dist/packages/core/src/naturalsort.js +88 -0
- package/dist/packages/core/src/naturalsort.js.map +1 -0
- package/dist/packages/core/src/observable.d.ts +54 -0
- package/dist/packages/core/src/observable.d.ts.map +1 -0
- package/dist/packages/core/src/observable.js +150 -0
- package/dist/packages/core/src/observable.js.map +1 -0
- package/dist/packages/core/src/pool.d.ts +38 -0
- package/dist/packages/core/src/pool.d.ts.map +1 -0
- package/dist/packages/core/src/pool.js +113 -0
- package/dist/packages/core/src/pool.js.map +1 -0
- package/dist/packages/core/src/reconcile.d.ts +43 -0
- package/dist/packages/core/src/reconcile.d.ts.map +1 -0
- package/dist/packages/core/src/reconcile.js +172 -0
- package/dist/packages/core/src/reconcile.js.map +1 -0
- package/dist/packages/core/src/resolver.d.ts +67 -0
- package/dist/packages/core/src/resolver.d.ts.map +1 -0
- package/dist/packages/core/src/resolver.js +110 -0
- package/dist/packages/core/src/resolver.js.map +1 -0
- package/dist/packages/core/src/safename.d.ts +7 -0
- package/dist/packages/core/src/safename.d.ts.map +1 -0
- package/dist/packages/core/src/safename.js +354 -0
- package/dist/packages/core/src/safename.js.map +1 -0
- package/dist/packages/core/src/scanner.d.ts +25 -0
- package/dist/packages/core/src/scanner.d.ts.map +1 -0
- package/dist/packages/core/src/scanner.js +97 -0
- package/dist/packages/core/src/scanner.js.map +1 -0
- package/dist/packages/core/src/store.d.ts +39 -0
- package/dist/packages/core/src/store.d.ts.map +1 -0
- package/dist/packages/core/src/store.js +53 -0
- package/dist/packages/core/src/store.js.map +1 -0
- package/dist/packages/core/src/tree.d.ts +16 -0
- package/dist/packages/core/src/tree.d.ts.map +1 -0
- package/dist/packages/core/src/tree.js +45 -0
- package/dist/packages/core/src/tree.js.map +1 -0
- package/dist/packages/core/src/verify.d.ts +29 -0
- package/dist/packages/core/src/verify.d.ts.map +1 -0
- package/dist/packages/core/src/verify.js +85 -0
- package/dist/packages/core/src/verify.js.map +1 -0
- package/dist/packages/core/src/workspace.d.ts +72 -0
- package/dist/packages/core/src/workspace.d.ts.map +1 -0
- package/dist/packages/core/src/workspace.js +135 -0
- package/dist/packages/core/src/workspace.js.map +1 -0
- package/dist/packages/node/src/index.d.ts +4 -0
- package/dist/packages/node/src/index.d.ts.map +1 -0
- package/dist/packages/node/src/index.js +6 -0
- package/dist/packages/node/src/index.js.map +1 -0
- package/dist/packages/node/src/node-fs.d.ts +24 -0
- package/dist/packages/node/src/node-fs.d.ts.map +1 -0
- package/dist/packages/node/src/node-fs.js +84 -0
- package/dist/packages/node/src/node-fs.js.map +1 -0
- package/dist/packages/node/src/tree-store.d.ts +22 -0
- package/dist/packages/node/src/tree-store.d.ts.map +1 -0
- package/dist/packages/node/src/tree-store.js +77 -0
- package/dist/packages/node/src/tree-store.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { C4ID } from "./id.js";
|
|
2
|
+
/**
|
|
3
|
+
* Compute the tree (Merkle) ID for a set of C4IDs.
|
|
4
|
+
*
|
|
5
|
+
* Algorithm:
|
|
6
|
+
* 1. Sort IDs by digest bytes (big-endian).
|
|
7
|
+
* 2. Deduplicate adjacent equal IDs.
|
|
8
|
+
* 3. Build a Merkle tree bottom-up:
|
|
9
|
+
* - Pair adjacent IDs and sum each pair (order-independent via C4ID.sum).
|
|
10
|
+
* - An odd trailing ID promotes unchanged to the next level.
|
|
11
|
+
* - Repeat until a single root remains.
|
|
12
|
+
* 4. Empty input returns the nil ID.
|
|
13
|
+
* 5. Single element returns itself.
|
|
14
|
+
*/
|
|
15
|
+
export async function treeId(ids) {
|
|
16
|
+
if (ids.length === 0)
|
|
17
|
+
return C4ID.nil();
|
|
18
|
+
// Sort by digest bytes
|
|
19
|
+
const sorted = ids.slice().sort((a, b) => a.compareTo(b));
|
|
20
|
+
// Deduplicate
|
|
21
|
+
const deduped = [sorted[0]];
|
|
22
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
23
|
+
if (!sorted[i].equals(sorted[i - 1])) {
|
|
24
|
+
deduped.push(sorted[i]);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (deduped.length === 1)
|
|
28
|
+
return deduped[0];
|
|
29
|
+
// Build Merkle tree bottom-up
|
|
30
|
+
let level = deduped;
|
|
31
|
+
while (level.length > 1) {
|
|
32
|
+
const next = [];
|
|
33
|
+
for (let i = 0; i < level.length; i += 2) {
|
|
34
|
+
if (i + 1 < level.length) {
|
|
35
|
+
next.push(await level[i].sum(level[i + 1]));
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
next.push(level[i]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
level = next;
|
|
42
|
+
}
|
|
43
|
+
return level[0];
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.js","sourceRoot":"","sources":["../../../../../core/src/tree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,GAAW;IACtC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;IAExC,uBAAuB;IACvB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1D,cAAc;IACd,MAAM,OAAO,GAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;IAE5C,8BAA8B;IAC9B,IAAI,KAAK,GAAG,OAAO,CAAC;IACpB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,GAAW,EAAE,CAAC;QACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;gBACzB,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9C,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QACD,KAAK,GAAG,IAAI,CAAC;IACf,CAAC;IAED,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { FileSystem } from './filesystem.js';
|
|
2
|
+
import type { Manifest } from './manifest.js';
|
|
3
|
+
/** A file whose content does not match the manifest's expected C4 ID. */
|
|
4
|
+
export interface CorruptEntry {
|
|
5
|
+
path: string;
|
|
6
|
+
expected: string;
|
|
7
|
+
actual: string;
|
|
8
|
+
}
|
|
9
|
+
/** Result of verifying a manifest against a real filesystem. */
|
|
10
|
+
export interface VerifyReport {
|
|
11
|
+
ok: string[];
|
|
12
|
+
missing: string[];
|
|
13
|
+
corrupt: CorruptEntry[];
|
|
14
|
+
extra: string[];
|
|
15
|
+
get isOk(): boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Options for the verify function. */
|
|
18
|
+
export interface VerifyOptions {
|
|
19
|
+
progress?: (path: string, index: number, total: number) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Compare a manifest against a real filesystem.
|
|
23
|
+
*
|
|
24
|
+
* Walks every file entry in the manifest and checks that the on-disk
|
|
25
|
+
* content produces the same C4 ID. Then walks the actual directory tree
|
|
26
|
+
* to find files present on disk but absent from the manifest.
|
|
27
|
+
*/
|
|
28
|
+
export declare function verify(manifest: Manifest, fs: FileSystem, rootPath: string, options?: VerifyOptions): Promise<VerifyReport>;
|
|
29
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../../../core/src/verify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAK7C,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,EAAE,CAAA;IACZ,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,IAAI,IAAI,IAAI,OAAO,CAAA;CACpB;AAED,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAChE;AAED;;;;;;GAMG;AACH,wBAAsB,MAAM,CAC1B,QAAQ,EAAE,QAAQ,EAClB,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,YAAY,CAAC,CAiEvB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { identifyContent } from './identify-content.js';
|
|
2
|
+
import { isDir } from './entry.js';
|
|
3
|
+
import { streamToBytes, joinPath, FileNotFoundError } from './filesystem.js';
|
|
4
|
+
/**
|
|
5
|
+
* Compare a manifest against a real filesystem.
|
|
6
|
+
*
|
|
7
|
+
* Walks every file entry in the manifest and checks that the on-disk
|
|
8
|
+
* content produces the same C4 ID. Then walks the actual directory tree
|
|
9
|
+
* to find files present on disk but absent from the manifest.
|
|
10
|
+
*/
|
|
11
|
+
export async function verify(manifest, fs, rootPath, options) {
|
|
12
|
+
const ok = [];
|
|
13
|
+
const missing = [];
|
|
14
|
+
const corrupt = [];
|
|
15
|
+
// Collect expected file paths from the manifest.
|
|
16
|
+
const expectedFiles = []; // [relativePath, c4idString]
|
|
17
|
+
for (const [path, entry] of manifest) {
|
|
18
|
+
if (isDir(entry))
|
|
19
|
+
continue;
|
|
20
|
+
const idStr = entry.c4id && !entry.c4id.isNil() ? entry.c4id.toString() : '';
|
|
21
|
+
expectedFiles.push([path, idStr]);
|
|
22
|
+
}
|
|
23
|
+
const expectedSet = new Set();
|
|
24
|
+
const total = expectedFiles.length;
|
|
25
|
+
// Check each manifest entry against the filesystem.
|
|
26
|
+
for (let i = 0; i < expectedFiles.length; i++) {
|
|
27
|
+
const [relPath, expectedId] = expectedFiles[i];
|
|
28
|
+
expectedSet.add(relPath);
|
|
29
|
+
const fullPath = joinPath(rootPath, relPath);
|
|
30
|
+
options?.progress?.(relPath, i, total);
|
|
31
|
+
try {
|
|
32
|
+
await fs.stat(fullPath);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
if (err instanceof FileNotFoundError) {
|
|
36
|
+
missing.push(relPath);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
41
|
+
// No expected C4 ID — can only confirm existence, not integrity.
|
|
42
|
+
if (!expectedId) {
|
|
43
|
+
ok.push(relPath);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const stream = await fs.readFile(fullPath);
|
|
47
|
+
const bytes = await streamToBytes(stream);
|
|
48
|
+
const actualId = await identifyContent(bytes);
|
|
49
|
+
const actualStr = actualId.toString();
|
|
50
|
+
if (actualStr === expectedId) {
|
|
51
|
+
ok.push(relPath);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
corrupt.push({ path: relPath, expected: expectedId, actual: actualStr });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Walk the real filesystem to find extra files.
|
|
58
|
+
const extra = [];
|
|
59
|
+
await walkDir(fs, rootPath, '', expectedSet, extra);
|
|
60
|
+
return {
|
|
61
|
+
ok,
|
|
62
|
+
missing,
|
|
63
|
+
corrupt,
|
|
64
|
+
extra,
|
|
65
|
+
get isOk() {
|
|
66
|
+
return missing.length === 0 && corrupt.length === 0 && extra.length === 0;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Recursively walk a directory, collecting paths not in the expected set. */
|
|
71
|
+
async function walkDir(fs, rootPath, relDir, expected, extra) {
|
|
72
|
+
const absDir = relDir ? joinPath(rootPath, relDir) : rootPath;
|
|
73
|
+
for await (const entry of fs.readDir(absDir)) {
|
|
74
|
+
const relPath = relDir ? relDir + entry.name : entry.name;
|
|
75
|
+
if (entry.isDirectory) {
|
|
76
|
+
const dirRel = relPath.endsWith('/') ? relPath : relPath + '/';
|
|
77
|
+
await walkDir(fs, rootPath, dirRel, expected, extra);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!expected.has(relPath)) {
|
|
81
|
+
extra.push(relPath);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../../../core/src/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { FileSystem } from './filesystem.js';
|
|
2
|
+
import type { Store } from './store.js';
|
|
3
|
+
import { Manifest } from './manifest.js';
|
|
4
|
+
import { type DiffResult } from './diff.js';
|
|
5
|
+
import { type ReconcileResult, type ReconcilePlan } from './reconcile.js';
|
|
6
|
+
/** Workspace status information. */
|
|
7
|
+
export interface WorkspaceStatus {
|
|
8
|
+
path: string;
|
|
9
|
+
exists: boolean;
|
|
10
|
+
hasManifest: boolean;
|
|
11
|
+
manifestC4ID: string | null;
|
|
12
|
+
created: string | null;
|
|
13
|
+
lastCheckout: string | null;
|
|
14
|
+
}
|
|
15
|
+
export interface CheckoutOptions {
|
|
16
|
+
progress?: (op: string, path: string, index: number, total: number) => void;
|
|
17
|
+
dryRun?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface SnapshotOptions {
|
|
20
|
+
storeContent?: boolean;
|
|
21
|
+
progress?: (path: string, filesProcessed: number) => void;
|
|
22
|
+
skipHidden?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Declarative directory management backed by a content store.
|
|
26
|
+
*
|
|
27
|
+
* A workspace ties a directory (via FileSystem) to a Store, enabling
|
|
28
|
+
* checkout (materialize a manifest), snapshot (capture current state),
|
|
29
|
+
* reset (revert to last checkout), and diff.
|
|
30
|
+
*
|
|
31
|
+
* Generic over FileSystem and Store — works in browser, Node, or memory.
|
|
32
|
+
*/
|
|
33
|
+
export declare class Workspace {
|
|
34
|
+
readonly rootPath: string;
|
|
35
|
+
readonly fs: FileSystem;
|
|
36
|
+
readonly store: Store;
|
|
37
|
+
private state;
|
|
38
|
+
private currentManifest;
|
|
39
|
+
constructor(rootPath: string, fs: FileSystem, store: Store);
|
|
40
|
+
/**
|
|
41
|
+
* Load persisted workspace state from disk.
|
|
42
|
+
* Call this after construction to restore previous session state.
|
|
43
|
+
*/
|
|
44
|
+
load(): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Make the directory match a manifest.
|
|
47
|
+
* Only transfers content that differs from what's on disk.
|
|
48
|
+
*/
|
|
49
|
+
checkout(manifest: Manifest, options?: CheckoutOptions): Promise<ReconcileResult | ReconcilePlan>;
|
|
50
|
+
/**
|
|
51
|
+
* Capture the current directory state as a manifest.
|
|
52
|
+
*/
|
|
53
|
+
snapshot(options?: SnapshotOptions): Promise<Manifest>;
|
|
54
|
+
/**
|
|
55
|
+
* Restore the directory to the last checked-out manifest.
|
|
56
|
+
*/
|
|
57
|
+
reset(options?: {
|
|
58
|
+
progress?: CheckoutOptions['progress'];
|
|
59
|
+
}): Promise<ReconcileResult>;
|
|
60
|
+
/**
|
|
61
|
+
* Compare current directory state against the checked-out manifest.
|
|
62
|
+
*/
|
|
63
|
+
diffFromCurrent(options?: {
|
|
64
|
+
progress?: SnapshotOptions['progress'];
|
|
65
|
+
}): Promise<DiffResult>;
|
|
66
|
+
/** Get workspace status. */
|
|
67
|
+
status(): WorkspaceStatus;
|
|
68
|
+
/** The currently checked-out manifest, or null. */
|
|
69
|
+
get manifest(): Manifest | null;
|
|
70
|
+
private saveState;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../../../core/src/workspace.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,WAAW,CAAA;AAEjD,OAAO,EAAe,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAYtF,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,CAAA;IACf,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,IAAI,CAAA;IACzD,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED;;;;;;;;GAQG;AACH,qBAAa,SAAS;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IAErB,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,eAAe,CAAwB;gBAEnC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK;IAM1D;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;OAGG;IACG,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,GAAG,aAAa,CAAC;IAsBvG;;OAEG;IACG,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC;IAU5D;;OAEG;IACG,KAAK,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,eAAe,CAAC,UAAU,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC;IAU3F;;OAEG;IACG,eAAe,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,eAAe,CAAC,UAAU,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAQhG,4BAA4B;IAC5B,MAAM,IAAI,eAAe;IAWzB,mDAAmD;IACnD,IAAI,QAAQ,IAAI,QAAQ,GAAG,IAAI,CAE9B;YAEa,SAAS;CAexB"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { streamToBytes, joinPath } from './filesystem.js';
|
|
2
|
+
import { Manifest } from './manifest.js';
|
|
3
|
+
import { diff } from './diff.js';
|
|
4
|
+
import { scan } from './scanner.js';
|
|
5
|
+
import { plan, apply } from './reconcile.js';
|
|
6
|
+
const STATE_FILE = '.c4-workspace.json';
|
|
7
|
+
const MANIFEST_FILE = '.c4-workspace-manifest.c4m';
|
|
8
|
+
/**
|
|
9
|
+
* Declarative directory management backed by a content store.
|
|
10
|
+
*
|
|
11
|
+
* A workspace ties a directory (via FileSystem) to a Store, enabling
|
|
12
|
+
* checkout (materialize a manifest), snapshot (capture current state),
|
|
13
|
+
* reset (revert to last checkout), and diff.
|
|
14
|
+
*
|
|
15
|
+
* Generic over FileSystem and Store — works in browser, Node, or memory.
|
|
16
|
+
*/
|
|
17
|
+
export class Workspace {
|
|
18
|
+
constructor(rootPath, fs, store) {
|
|
19
|
+
this.state = null;
|
|
20
|
+
this.currentManifest = null;
|
|
21
|
+
this.rootPath = rootPath;
|
|
22
|
+
this.fs = fs;
|
|
23
|
+
this.store = store;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Load persisted workspace state from disk.
|
|
27
|
+
* Call this after construction to restore previous session state.
|
|
28
|
+
*/
|
|
29
|
+
async load() {
|
|
30
|
+
try {
|
|
31
|
+
const stateStream = await this.fs.readFile(joinPath(this.rootPath, STATE_FILE));
|
|
32
|
+
const stateBytes = await streamToBytes(stateStream);
|
|
33
|
+
const stateText = new TextDecoder().decode(stateBytes);
|
|
34
|
+
this.state = JSON.parse(stateText);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
this.state = null;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const manifestStream = await this.fs.readFile(joinPath(this.rootPath, MANIFEST_FILE));
|
|
41
|
+
const manifestBytes = await streamToBytes(manifestStream);
|
|
42
|
+
const manifestText = new TextDecoder().decode(manifestBytes);
|
|
43
|
+
this.currentManifest = await Manifest.parse(manifestText);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
this.currentManifest = null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Make the directory match a manifest.
|
|
51
|
+
* Only transfers content that differs from what's on disk.
|
|
52
|
+
*/
|
|
53
|
+
async checkout(manifest, options) {
|
|
54
|
+
const p = await plan(manifest, this.fs, this.rootPath, this.store);
|
|
55
|
+
if (options?.dryRun)
|
|
56
|
+
return p;
|
|
57
|
+
const result = await apply(p, this.fs, this.rootPath, this.store, {
|
|
58
|
+
progress: options?.progress,
|
|
59
|
+
});
|
|
60
|
+
// Persist state
|
|
61
|
+
this.currentManifest = manifest;
|
|
62
|
+
const c4id = await manifest.computeC4ID();
|
|
63
|
+
this.state = {
|
|
64
|
+
manifestC4ID: c4id.toString(),
|
|
65
|
+
created: this.state?.created ?? new Date().toISOString(),
|
|
66
|
+
lastCheckout: new Date().toISOString(),
|
|
67
|
+
};
|
|
68
|
+
await this.saveState();
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Capture the current directory state as a manifest.
|
|
73
|
+
*/
|
|
74
|
+
async snapshot(options) {
|
|
75
|
+
const scanOpts = {
|
|
76
|
+
store: options?.storeContent ? this.store : undefined,
|
|
77
|
+
progress: options?.progress,
|
|
78
|
+
skipHidden: options?.skipHidden ?? true,
|
|
79
|
+
computeIds: true,
|
|
80
|
+
};
|
|
81
|
+
return scan(this.fs, this.rootPath, scanOpts);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Restore the directory to the last checked-out manifest.
|
|
85
|
+
*/
|
|
86
|
+
async reset(options) {
|
|
87
|
+
if (!this.currentManifest) {
|
|
88
|
+
throw new Error('no manifest checked out — cannot reset');
|
|
89
|
+
}
|
|
90
|
+
const p = await plan(this.currentManifest, this.fs, this.rootPath, this.store);
|
|
91
|
+
return apply(p, this.fs, this.rootPath, this.store, {
|
|
92
|
+
progress: options?.progress,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Compare current directory state against the checked-out manifest.
|
|
97
|
+
*/
|
|
98
|
+
async diffFromCurrent(options) {
|
|
99
|
+
if (!this.currentManifest) {
|
|
100
|
+
throw new Error('no manifest checked out — cannot diff');
|
|
101
|
+
}
|
|
102
|
+
const current = await this.snapshot({ progress: options?.progress });
|
|
103
|
+
return diff(this.currentManifest, current);
|
|
104
|
+
}
|
|
105
|
+
/** Get workspace status. */
|
|
106
|
+
status() {
|
|
107
|
+
return {
|
|
108
|
+
path: this.rootPath,
|
|
109
|
+
exists: this.state !== null,
|
|
110
|
+
hasManifest: this.currentManifest !== null,
|
|
111
|
+
manifestC4ID: this.state?.manifestC4ID ?? null,
|
|
112
|
+
created: this.state?.created ?? null,
|
|
113
|
+
lastCheckout: this.state?.lastCheckout ?? null,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** The currently checked-out manifest, or null. */
|
|
117
|
+
get manifest() {
|
|
118
|
+
return this.currentManifest;
|
|
119
|
+
}
|
|
120
|
+
async saveState() {
|
|
121
|
+
if (!this.state)
|
|
122
|
+
return;
|
|
123
|
+
// Save state JSON
|
|
124
|
+
const stateJson = JSON.stringify(this.state, null, 2);
|
|
125
|
+
const stateBytes = new TextEncoder().encode(stateJson);
|
|
126
|
+
await this.fs.writeFile(joinPath(this.rootPath, STATE_FILE), stateBytes);
|
|
127
|
+
// Save manifest c4m
|
|
128
|
+
if (this.currentManifest) {
|
|
129
|
+
const c4mText = this.currentManifest.encode();
|
|
130
|
+
const c4mBytes = new TextEncoder().encode(c4mText);
|
|
131
|
+
await this.fs.writeFile(joinPath(this.rootPath, MANIFEST_FILE), c4mBytes);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../../../core/src/workspace.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAiB,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAExE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,OAAO,EAAE,IAAI,EAAmB,MAAM,WAAW,CAAA;AACjD,OAAO,EAAE,IAAI,EAAoB,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,IAAI,EAAE,KAAK,EAA4C,MAAM,gBAAgB,CAAA;AAEtF,MAAM,UAAU,GAAG,oBAAoB,CAAA;AACvC,MAAM,aAAa,GAAG,4BAA4B,CAAA;AA8BlD;;;;;;;;GAQG;AACH,MAAM,OAAO,SAAS;IAQpB,YAAY,QAAgB,EAAE,EAAc,EAAE,KAAY;QAHlD,UAAK,GAA0B,IAAI,CAAA;QACnC,oBAAe,GAAoB,IAAI,CAAA;QAG7C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;QACZ,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAA;YAC/E,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,CAAA;YACnD,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;YACtD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACnB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAA;YACrF,MAAM,aAAa,GAAG,MAAM,aAAa,CAAC,cAAc,CAAC,CAAA;YACzD,MAAM,YAAY,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;YAC5D,IAAI,CAAC,eAAe,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,eAAe,GAAG,IAAI,CAAA;QAC7B,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ,CAAC,QAAkB,EAAE,OAAyB;QAC1D,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QAElE,IAAI,OAAO,EAAE,MAAM;YAAE,OAAO,CAAC,CAAA;QAE7B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,EAAE;YAChE,QAAQ,EAAE,OAAO,EAAE,QAAQ;SAC5B,CAAC,CAAA;QAEF,gBAAgB;QAChB,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAA;QAC/B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAA;QACzC,IAAI,CAAC,KAAK,GAAG;YACX,YAAY,EAAE,IAAI,CAAC,QAAQ,EAAE;YAC7B,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACxD,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACvC,CAAA;QACD,MAAM,IAAI,CAAC,SAAS,EAAE,CAAA;QAEtB,OAAO,MAAM,CAAA;IACf,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ,CAAC,OAAyB;QACtC,MAAM,QAAQ,GAAgB;YAC5B,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;YACrD,QAAQ,EAAE,OAAO,EAAE,QAAQ;YAC3B,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,IAAI;YACvC,UAAU,EAAE,IAAI;SACjB,CAAA;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC/C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK,CAAC,OAAoD;QAC9D,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;QAC3D,CAAC;QACD,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QAC9E,OAAO,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,EAAE;YAClD,QAAQ,EAAE,OAAO,EAAE,QAAQ;SAC5B,CAAC,CAAA;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,OAAoD;QACxE,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;QAC1D,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;QACpE,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;IAC5C,CAAC;IAED,4BAA4B;IAC5B,MAAM;QACJ,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,QAAQ;YACnB,MAAM,EAAE,IAAI,CAAC,KAAK,KAAK,IAAI;YAC3B,WAAW,EAAE,IAAI,CAAC,eAAe,KAAK,IAAI;YAC1C,YAAY,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY,IAAI,IAAI;YAC9C,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI,IAAI;YACpC,YAAY,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY,IAAI,IAAI;SAC/C,CAAA;IACH,CAAC;IAED,mDAAmD;IACnD,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,eAAe,CAAA;IAC7B,CAAC;IAEO,KAAK,CAAC,SAAS;QACrB,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAM;QAEvB,kBAAkB;QAClB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QACrD,MAAM,UAAU,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QACtD,MAAM,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,UAAU,CAAC,CAAA;QAExE,oBAAoB;QACpB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAA;YAC7C,MAAM,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;YAClD,MAAM,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,QAAQ,CAAC,CAAA;QAC3E,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @avalanche-io/c4-node — Node.js extensions for C4
|
|
2
|
+
// Re-exports core plus Node-specific implementations
|
|
3
|
+
export * from '../../core/src/index.js';
|
|
4
|
+
export { NodeFS } from './node-fs.js';
|
|
5
|
+
export { TreeStore, openStore } from './tree-store.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/index.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,qDAAqD;AAErD,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FileSystem, DirEntry, FileStat } from '../../core/src/filesystem.js';
|
|
2
|
+
/**
|
|
3
|
+
* FileSystem implementation backed by Node.js fs module.
|
|
4
|
+
* Wraps node:fs/promises to provide the standard FileSystem interface.
|
|
5
|
+
*/
|
|
6
|
+
export declare class NodeFS implements FileSystem {
|
|
7
|
+
readDir(path: string): AsyncIterable<DirEntry>;
|
|
8
|
+
stat(path: string): Promise<FileStat>;
|
|
9
|
+
readFile(path: string): Promise<ReadableStream<Uint8Array>>;
|
|
10
|
+
readFileBytes(path: string): Promise<Uint8Array>;
|
|
11
|
+
writeFile(path: string, data: ReadableStream<Uint8Array> | Uint8Array): Promise<void>;
|
|
12
|
+
mkdir(path: string, options?: {
|
|
13
|
+
recursive?: boolean;
|
|
14
|
+
}): Promise<void>;
|
|
15
|
+
remove(path: string, options?: {
|
|
16
|
+
recursive?: boolean;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
rename(oldPath: string, newPath: string): Promise<void>;
|
|
19
|
+
setMeta(path: string, meta: {
|
|
20
|
+
mode?: number;
|
|
21
|
+
mtime?: Date;
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=node-fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-fs.d.ts","sourceRoot":"","sources":["../../../../src/node-fs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AAGlF;;;GAGG;AACH,qBAAa,MAAO,YAAW,UAAU;IAChC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC;IAW/C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAgBrC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IAU3D,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAUhD,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrF,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAStE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAIlF"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { readdir, stat, readFile, writeFile, mkdir, rm, rename, chmod, utimes } from 'node:fs/promises';
|
|
2
|
+
import { FileNotFoundError, bytesToStream, streamToBytes } from '../../core/src/filesystem.js';
|
|
3
|
+
/**
|
|
4
|
+
* FileSystem implementation backed by Node.js fs module.
|
|
5
|
+
* Wraps node:fs/promises to provide the standard FileSystem interface.
|
|
6
|
+
*/
|
|
7
|
+
export class NodeFS {
|
|
8
|
+
async *readDir(path) {
|
|
9
|
+
const entries = await readdir(path, { withFileTypes: true });
|
|
10
|
+
for (const entry of entries) {
|
|
11
|
+
yield {
|
|
12
|
+
name: entry.name,
|
|
13
|
+
isDirectory: entry.isDirectory(),
|
|
14
|
+
isSymlink: entry.isSymbolicLink(),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async stat(path) {
|
|
19
|
+
try {
|
|
20
|
+
const s = await stat(path);
|
|
21
|
+
return {
|
|
22
|
+
size: s.size,
|
|
23
|
+
mode: s.mode & 0o7777,
|
|
24
|
+
mtime: s.mtime,
|
|
25
|
+
isDirectory: s.isDirectory(),
|
|
26
|
+
isSymlink: s.isSymbolicLink(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
if (err.code === 'ENOENT')
|
|
31
|
+
throw new FileNotFoundError(path);
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async readFile(path) {
|
|
36
|
+
try {
|
|
37
|
+
const data = await readFile(path);
|
|
38
|
+
return bytesToStream(new Uint8Array(data));
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (err.code === 'ENOENT')
|
|
42
|
+
throw new FileNotFoundError(path);
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async readFileBytes(path) {
|
|
47
|
+
try {
|
|
48
|
+
const data = await readFile(path);
|
|
49
|
+
return new Uint8Array(data);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (err.code === 'ENOENT')
|
|
53
|
+
throw new FileNotFoundError(path);
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async writeFile(path, data) {
|
|
58
|
+
const bytes = data instanceof Uint8Array ? data : await streamToBytes(data);
|
|
59
|
+
await writeFile(path, bytes);
|
|
60
|
+
}
|
|
61
|
+
async mkdir(path, options) {
|
|
62
|
+
await mkdir(path, { recursive: options?.recursive });
|
|
63
|
+
}
|
|
64
|
+
async remove(path, options) {
|
|
65
|
+
try {
|
|
66
|
+
await rm(path, { recursive: options?.recursive, force: true });
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
if (err.code === 'ENOENT')
|
|
70
|
+
throw new FileNotFoundError(path);
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async rename(oldPath, newPath) {
|
|
75
|
+
await rename(oldPath, newPath);
|
|
76
|
+
}
|
|
77
|
+
async setMeta(path, meta) {
|
|
78
|
+
if (meta.mode !== undefined)
|
|
79
|
+
await chmod(path, meta.mode);
|
|
80
|
+
if (meta.mtime !== undefined)
|
|
81
|
+
await utimes(path, meta.mtime, meta.mtime);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=node-fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-fs.js","sourceRoot":"","sources":["../../../../src/node-fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAEvG,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAE9F;;;GAGG;AACH,MAAM,OAAO,MAAM;IACjB,KAAK,CAAC,CAAC,OAAO,CAAC,IAAY;QACzB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QAC5D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM;gBACJ,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE;gBAChC,SAAS,EAAE,KAAK,CAAC,cAAc,EAAE;aAClC,CAAA;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAY;QACrB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAA;YAC1B,OAAO;gBACL,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI,EAAE,CAAC,CAAC,IAAI,GAAG,MAAM;gBACrB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE;gBAC5B,SAAS,EAAE,CAAC,CAAC,cAAc,EAAE;aAC9B,CAAA;QACH,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAA;YAC5D,MAAM,GAAG,CAAA;QACX,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY;QACzB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAA;YACjC,OAAO,aAAa,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;QAC5C,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAA;YAC5D,MAAM,GAAG,CAAA;QACX,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,IAAY;QAC9B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAA;YACjC,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,CAAA;QAC7B,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAA;YAC5D,MAAM,GAAG,CAAA;QACX,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAY,EAAE,IAA6C;QACzE,MAAM,KAAK,GAAG,IAAI,YAAY,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,aAAa,CAAC,IAAI,CAAC,CAAA;QAC3E,MAAM,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY,EAAE,OAAiC;QACzD,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAA;IACtD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY,EAAE,OAAiC;QAC1D,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QAChE,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAA;YAC5D,MAAM,GAAG,CAAA;QACX,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAe,EAAE,OAAe;QAC3C,MAAM,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAChC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,IAAqC;QAC/D,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QACzD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,MAAM,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;IAC1E,CAAC;CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { C4ID } from '../../core/src/id.js';
|
|
2
|
+
import type { Store } from '../../core/src/store.js';
|
|
3
|
+
/**
|
|
4
|
+
* Filesystem content store compatible with Go's store.TreeStore
|
|
5
|
+
* and Python's FSStore.
|
|
6
|
+
*
|
|
7
|
+
* Layout: store/c4/REMAINING_88_CHARS
|
|
8
|
+
* All C4 IDs start with "c4", so top level has a single "c4" directory.
|
|
9
|
+
*/
|
|
10
|
+
export declare class TreeStore implements Store {
|
|
11
|
+
readonly path: string;
|
|
12
|
+
constructor(path: string);
|
|
13
|
+
has(id: C4ID): Promise<boolean>;
|
|
14
|
+
get(id: C4ID): Promise<ReadableStream<Uint8Array>>;
|
|
15
|
+
put(data: ReadableStream<Uint8Array> | Uint8Array): Promise<C4ID>;
|
|
16
|
+
remove(id: C4ID): Promise<void>;
|
|
17
|
+
private idToPath;
|
|
18
|
+
private idToDir;
|
|
19
|
+
}
|
|
20
|
+
/** Open the default content store (C4_STORE env or ~/.c4/store). */
|
|
21
|
+
export declare function openStore(path?: string): Promise<TreeStore>;
|
|
22
|
+
//# sourceMappingURL=tree-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-store.d.ts","sourceRoot":"","sources":["../../../../src/tree-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAGhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAGpD;;;;;;GAMG;AACH,qBAAa,SAAU,YAAW,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;gBAET,IAAI,EAAE,MAAM;IAIlB,GAAG,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAS/B,GAAG,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IASlD,GAAG,CAAC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBjE,MAAM,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,OAAO;CAIhB;AAED,oEAAoE;AACpE,wBAAsB,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAQjE"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir, rm, stat, rename as fsRename } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
4
|
+
import { identifyBytes } from '../../core/src/id.js';
|
|
5
|
+
import { bytesToStream, streamToBytes } from '../../core/src/filesystem.js';
|
|
6
|
+
import { ContentNotFoundError } from '../../core/src/store.js';
|
|
7
|
+
/**
|
|
8
|
+
* Filesystem content store compatible with Go's store.TreeStore
|
|
9
|
+
* and Python's FSStore.
|
|
10
|
+
*
|
|
11
|
+
* Layout: store/c4/REMAINING_88_CHARS
|
|
12
|
+
* All C4 IDs start with "c4", so top level has a single "c4" directory.
|
|
13
|
+
*/
|
|
14
|
+
export class TreeStore {
|
|
15
|
+
constructor(path) {
|
|
16
|
+
this.path = path;
|
|
17
|
+
}
|
|
18
|
+
async has(id) {
|
|
19
|
+
try {
|
|
20
|
+
await stat(this.idToPath(id));
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async get(id) {
|
|
28
|
+
try {
|
|
29
|
+
const data = await readFile(this.idToPath(id));
|
|
30
|
+
return bytesToStream(new Uint8Array(data));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
throw new ContentNotFoundError(id);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async put(data) {
|
|
37
|
+
const bytes = data instanceof Uint8Array ? data : await streamToBytes(data);
|
|
38
|
+
const id = await identifyBytes(bytes);
|
|
39
|
+
if (await this.has(id))
|
|
40
|
+
return id;
|
|
41
|
+
const dir = this.idToDir(id);
|
|
42
|
+
await mkdir(dir, { recursive: true });
|
|
43
|
+
const tmpName = `.ingest.${randomBytes(8).toString('hex')}`;
|
|
44
|
+
const tmpPath = join(dir, tmpName);
|
|
45
|
+
const finalPath = this.idToPath(id);
|
|
46
|
+
await writeFile(tmpPath, bytes);
|
|
47
|
+
await fsRename(tmpPath, finalPath);
|
|
48
|
+
return id;
|
|
49
|
+
}
|
|
50
|
+
async remove(id) {
|
|
51
|
+
try {
|
|
52
|
+
await rm(this.idToPath(id));
|
|
53
|
+
}
|
|
54
|
+
catch { /* ignore */ }
|
|
55
|
+
}
|
|
56
|
+
idToPath(id) {
|
|
57
|
+
const s = id.toString();
|
|
58
|
+
return join(this.path, s.substring(0, 2), s.substring(2));
|
|
59
|
+
}
|
|
60
|
+
idToDir(id) {
|
|
61
|
+
const s = id.toString();
|
|
62
|
+
return join(this.path, s.substring(0, 2));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Open the default content store (C4_STORE env or ~/.c4/store). */
|
|
66
|
+
export async function openStore(path) {
|
|
67
|
+
if (path)
|
|
68
|
+
return new TreeStore(path);
|
|
69
|
+
const envStore = process.env.C4_STORE;
|
|
70
|
+
if (envStore)
|
|
71
|
+
return new TreeStore(envStore);
|
|
72
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? '';
|
|
73
|
+
const defaultPath = join(home, '.c4', 'store');
|
|
74
|
+
await mkdir(defaultPath, { recursive: true });
|
|
75
|
+
return new TreeStore(defaultPath);
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=tree-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-store.js","sourceRoot":"","sources":["../../../../src/tree-store.ts"],"names":[],"mappings":"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"}
|