@untangle-code/storage-local 0.0.0-stage → 0.1.1

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 (65) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +90 -0
  3. package/README.md +226 -2
  4. package/THIRD_PARTY_LICENSES.md +24 -0
  5. package/dist/abort.d.ts +18 -0
  6. package/dist/abort.d.ts.map +1 -0
  7. package/dist/abort.js +14 -0
  8. package/dist/abort.js.map +1 -0
  9. package/dist/analysis-cache.d.ts +81 -0
  10. package/dist/analysis-cache.d.ts.map +1 -0
  11. package/dist/analysis-cache.js +322 -0
  12. package/dist/analysis-cache.js.map +1 -0
  13. package/dist/checkpoint-store.d.ts +22 -0
  14. package/dist/checkpoint-store.d.ts.map +1 -0
  15. package/dist/checkpoint-store.js +123 -0
  16. package/dist/checkpoint-store.js.map +1 -0
  17. package/dist/embedding-cache.d.ts +38 -0
  18. package/dist/embedding-cache.d.ts.map +1 -0
  19. package/dist/embedding-cache.js +229 -0
  20. package/dist/embedding-cache.js.map +1 -0
  21. package/dist/fs-errors.d.ts +4 -0
  22. package/dist/fs-errors.d.ts.map +1 -0
  23. package/dist/fs-errors.js +14 -0
  24. package/dist/fs-errors.js.map +1 -0
  25. package/dist/index.d.ts +15 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +23 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/layout.d.ts +50 -0
  30. package/dist/layout.d.ts.map +1 -0
  31. package/dist/layout.js +99 -0
  32. package/dist/layout.js.map +1 -0
  33. package/dist/local-file-system.d.ts +37 -0
  34. package/dist/local-file-system.d.ts.map +1 -0
  35. package/dist/local-file-system.js +193 -0
  36. package/dist/local-file-system.js.map +1 -0
  37. package/dist/lock-provider.d.ts +59 -0
  38. package/dist/lock-provider.d.ts.map +1 -0
  39. package/dist/lock-provider.js +318 -0
  40. package/dist/lock-provider.js.map +1 -0
  41. package/dist/real-time.d.ts +16 -0
  42. package/dist/real-time.d.ts.map +1 -0
  43. package/dist/real-time.js +19 -0
  44. package/dist/real-time.js.map +1 -0
  45. package/dist/search-journey-store.d.ts +34 -0
  46. package/dist/search-journey-store.d.ts.map +1 -0
  47. package/dist/search-journey-store.js +530 -0
  48. package/dist/search-journey-store.js.map +1 -0
  49. package/dist/storage.d.ts +42 -0
  50. package/dist/storage.d.ts.map +1 -0
  51. package/dist/storage.js +34 -0
  52. package/dist/storage.js.map +1 -0
  53. package/package.json +60 -3
  54. package/src/abort.ts +26 -0
  55. package/src/analysis-cache.ts +404 -0
  56. package/src/checkpoint-store.ts +155 -0
  57. package/src/embedding-cache.ts +282 -0
  58. package/src/fs-errors.ts +19 -0
  59. package/src/index.ts +30 -0
  60. package/src/layout.ts +149 -0
  61. package/src/local-file-system.ts +232 -0
  62. package/src/lock-provider.ts +381 -0
  63. package/src/real-time.ts +18 -0
  64. package/src/search-journey-store.ts +638 -0
  65. package/src/storage.ts +98 -0
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Persistent embedding cache under `cache/embeddings/` (architecture.md §12;
3
+ * spec.md §9.4: cache key `embedding_profile_id + SHA256(prepared_text)`,
4
+ * "Unveränderte vorbereitete Texte DÜRFEN NICHT erneut eingebettet werden,
5
+ * sofern der Cache gültig ist").
6
+ *
7
+ * Layout:
8
+ * - one entry file per cached vector, sharded two hex characters deep
9
+ * (`<hash[0:2]>/<hash>.json`) so a large cache never puts thousands of
10
+ * files in one directory;
11
+ * - one small `index.json` manifest tracking, per logical cache key, which
12
+ * entry file holds it, its size, and its last-access time — the LRU
13
+ * bookkeeping used for size-bounded eviction.
14
+ *
15
+ * Every mutation (`set`, and `get` on a hit, since a hit updates recency)
16
+ * rewrites the manifest via atomic temp+rename. This is O(entry count) per
17
+ * call, a documented, accepted simplification for an MVP-sized local cache
18
+ * (see README "Entscheidungen") — not the right shape for a cache with
19
+ * millions of entries, which would want a real embedded index instead of one
20
+ * JSON file.
21
+ *
22
+ * Reads degrade in a controlled way rather than throwing (docs/BUILD_PLAN.md
23
+ * WP-12 acceptance: "Cache-Löschung ist verlustfrei rekonstruierbar (Reads
24
+ * degradieren kontrolliert)"): a missing entry file, a corrupted entry, or a
25
+ * corrupted manifest are all treated as a cache miss (or an empty cache),
26
+ * self-healing the manifest where relevant — never a thrown error, because
27
+ * the cache is documented as disposable and reconstructible from source
28
+ * (architecture.md §12: "Lokale Caches sind löschbar und rekonstruierbar").
29
+ */
30
+ import * as nodePath from "node:path";
31
+ import { sha256Hex } from "@untangle-code/domain";
32
+ import { assertNotAborted } from "./abort.js";
33
+ import { isNotFoundError } from "./fs-errors.js";
34
+ /** 512 MiB — an Untangle default, not spec-mandated (spec.md §9.4 leaves the
35
+ * limit open); callers pass their own `maxBytes` where config specifies one. */
36
+ export const DEFAULT_EMBEDDING_CACHE_MAX_BYTES = 512 * 1024 * 1024;
37
+ const MANIFEST_FILE_NAME = "index.json";
38
+ function encode(text) {
39
+ return new TextEncoder().encode(text);
40
+ }
41
+ function signalOptions(options) {
42
+ return options?.signal ? { signal: options.signal } : undefined;
43
+ }
44
+ function totalBytesOf(manifest) {
45
+ let total = 0;
46
+ for (const entry of Object.values(manifest.entries)) {
47
+ total += entry.sizeBytes;
48
+ }
49
+ return total;
50
+ }
51
+ function pickEvictionCandidate(manifest, protectedKey) {
52
+ const keys = Object.keys(manifest.entries);
53
+ let best = null;
54
+ let bestAccess = Number.POSITIVE_INFINITY;
55
+ for (const key of keys) {
56
+ if (key === protectedKey && keys.length === 1) {
57
+ // The just-written entry is the sole entry left — evicting it would
58
+ // silently discard the very value `set()` was asked to store. A
59
+ // single entry larger than `maxBytes` is kept anyway (documented edge
60
+ // case, README "Entscheidungen") rather than making `set()` a no-op.
61
+ continue;
62
+ }
63
+ const entry = manifest.entries[key];
64
+ if (entry !== undefined && entry.lastAccessEpochMs < bestAccess) {
65
+ bestAccess = entry.lastAccessEpochMs;
66
+ best = key;
67
+ }
68
+ }
69
+ return best;
70
+ }
71
+ /**
72
+ * Cache key exactly per spec.md §9.4: `embedding_profile_id + SHA256(prepared_text)`.
73
+ * Exported so callers/tests can reason about identity without depending on
74
+ * this class's internal file sharding.
75
+ */
76
+ export function buildEmbeddingCacheKey(embeddingProfileId, preparedText) {
77
+ return `${embeddingProfileId}${sha256Hex(preparedText)}`;
78
+ }
79
+ export class LocalEmbeddingCache {
80
+ fs;
81
+ dir;
82
+ clock;
83
+ random;
84
+ maxBytes;
85
+ manifest = null;
86
+ constructor(fs, dir, clock, random, options) {
87
+ this.fs = fs;
88
+ this.dir = dir;
89
+ this.clock = clock;
90
+ this.random = random;
91
+ this.maxBytes = options?.maxBytes ?? DEFAULT_EMBEDDING_CACHE_MAX_BYTES;
92
+ }
93
+ manifestPath() {
94
+ return nodePath.join(this.dir, MANIFEST_FILE_NAME);
95
+ }
96
+ entryFileName(logicalKey) {
97
+ const hash = sha256Hex(logicalKey);
98
+ return nodePath.join(hash.slice(0, 2), `${hash}.json`);
99
+ }
100
+ entryPath(fileName) {
101
+ return nodePath.join(this.dir, fileName);
102
+ }
103
+ async loadManifest(options) {
104
+ if (this.manifest !== null) {
105
+ return this.manifest;
106
+ }
107
+ let text;
108
+ try {
109
+ text = await this.fs.readTextFile(this.manifestPath(), options);
110
+ }
111
+ catch (error) {
112
+ if (isNotFoundError(error)) {
113
+ this.manifest = { version: 1, entries: {} };
114
+ return this.manifest;
115
+ }
116
+ throw error;
117
+ }
118
+ try {
119
+ const parsed = JSON.parse(text);
120
+ this.manifest =
121
+ parsed.entries !== undefined
122
+ ? { version: 1, entries: parsed.entries }
123
+ : { version: 1, entries: {} };
124
+ }
125
+ catch {
126
+ // Corrupted manifest — degrade to an empty cache (see file doc comment).
127
+ this.manifest = { version: 1, entries: {} };
128
+ }
129
+ return this.manifest;
130
+ }
131
+ async persistManifest(manifest, options) {
132
+ await this.fs.mkdir(this.dir, { recursive: true, ...signalOptions(options) });
133
+ const tempPath = `${this.manifestPath()}.${this.random.uuid()}.tmp`;
134
+ await this.fs.writeFile(tempPath, encode(JSON.stringify(manifest)), options);
135
+ await this.fs.rename(tempPath, this.manifestPath(), options);
136
+ this.manifest = manifest;
137
+ }
138
+ async get(embeddingProfileId, preparedText, options) {
139
+ assertNotAborted(options, "embedding_cache_get");
140
+ const logicalKey = buildEmbeddingCacheKey(embeddingProfileId, preparedText);
141
+ const manifest = await this.loadManifest(options);
142
+ const entry = manifest.entries[logicalKey];
143
+ if (entry === undefined) {
144
+ return null;
145
+ }
146
+ let text;
147
+ try {
148
+ text = await this.fs.readTextFile(this.entryPath(entry.fileName), options);
149
+ }
150
+ catch (error) {
151
+ if (!isNotFoundError(error)) {
152
+ throw error;
153
+ }
154
+ // Manifest references a file that is gone — self-heal and miss.
155
+ delete manifest.entries[logicalKey];
156
+ await this.persistManifest(manifest, options);
157
+ return null;
158
+ }
159
+ let stored;
160
+ try {
161
+ stored = JSON.parse(text);
162
+ }
163
+ catch {
164
+ delete manifest.entries[logicalKey];
165
+ await this.persistManifest(manifest, options);
166
+ return null;
167
+ }
168
+ entry.lastAccessEpochMs = this.clock.nowEpochMs();
169
+ await this.persistManifest(manifest, options);
170
+ return stored.vector;
171
+ }
172
+ async set(embeddingProfileId, preparedText, vector, options) {
173
+ assertNotAborted(options, "embedding_cache_set");
174
+ const logicalKey = buildEmbeddingCacheKey(embeddingProfileId, preparedText);
175
+ const fileName = this.entryFileName(logicalKey);
176
+ const entryPath = this.entryPath(fileName);
177
+ const stored = { profileId: embeddingProfileId, vector: Array.from(vector) };
178
+ const bytes = encode(JSON.stringify(stored));
179
+ await this.fs.mkdir(nodePath.dirname(entryPath), {
180
+ recursive: true,
181
+ ...signalOptions(options),
182
+ });
183
+ const tempPath = `${entryPath}.${this.random.uuid()}.tmp`;
184
+ await this.fs.writeFile(tempPath, bytes, options);
185
+ await this.fs.rename(tempPath, entryPath, options);
186
+ const manifest = await this.loadManifest(options);
187
+ manifest.entries[logicalKey] = {
188
+ fileName,
189
+ sizeBytes: bytes.length,
190
+ lastAccessEpochMs: this.clock.nowEpochMs(),
191
+ };
192
+ await this.evictUntilWithinLimit(manifest, logicalKey, options);
193
+ await this.persistManifest(manifest, options);
194
+ }
195
+ async evictUntilWithinLimit(manifest, justWrittenKey, options) {
196
+ let total = totalBytesOf(manifest);
197
+ while (total > this.maxBytes) {
198
+ const victim = pickEvictionCandidate(manifest, justWrittenKey);
199
+ if (victim === null) {
200
+ break;
201
+ }
202
+ const entry = manifest.entries[victim];
203
+ delete manifest.entries[victim];
204
+ if (entry !== undefined) {
205
+ total -= entry.sizeBytes;
206
+ try {
207
+ await this.fs.remove(this.entryPath(entry.fileName), options);
208
+ }
209
+ catch {
210
+ // Best-effort: the manifest entry is already gone either way, so a
211
+ // future `get()` for this key will miss regardless.
212
+ }
213
+ }
214
+ }
215
+ }
216
+ async stats(options) {
217
+ assertNotAborted(options, "embedding_cache_stats");
218
+ const manifest = await this.loadManifest(options);
219
+ return { entryCount: Object.keys(manifest.entries).length, totalBytes: totalBytesOf(manifest) };
220
+ }
221
+ /** Explicit, whole-cache removal (the one "clear everything" operation this
222
+ * class exposes — always naming its exact target: this cache's own directory). */
223
+ async clear(options) {
224
+ assertNotAborted(options, "embedding_cache_clear");
225
+ await this.fs.remove(this.dir, { recursive: true, ...signalOptions(options) });
226
+ this.manifest = { version: 1, entries: {} };
227
+ }
228
+ }
229
+ //# sourceMappingURL=embedding-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedding-cache.js","sourceRoot":"","sources":["../src/embedding-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** Shared Node `fs` error-code checks used across this package's adapters. */
2
+ export declare function isNotFoundError(error: unknown): boolean;
3
+ export declare function isAlreadyExistsError(error: unknown): boolean;
4
+ //# sourceMappingURL=fs-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs-errors.d.ts","sourceRoot":"","sources":["../src/fs-errors.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAE9E,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOvD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAO5D"}
@@ -0,0 +1,14 @@
1
+ /** Shared Node `fs` error-code checks used across this package's adapters. */
2
+ export function isNotFoundError(error) {
3
+ return (typeof error === "object" &&
4
+ error !== null &&
5
+ "code" in error &&
6
+ error.code === "ENOENT");
7
+ }
8
+ export function isAlreadyExistsError(error) {
9
+ return (typeof error === "object" &&
10
+ error !== null &&
11
+ "code" in error &&
12
+ error.code === "EEXIST");
13
+ }
14
+ //# sourceMappingURL=fs-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs-errors.js","sourceRoot":"","sources":["../src/fs-errors.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAE9E,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACd,KAA4B,CAAC,IAAI,KAAK,QAAQ,CAChD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACd,KAA4B,CAAC,IAAI,KAAK,QAAQ,CAChD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @untangle-code/storage-local — local state under `.untangle/`
3
+ * (architecture.md §12, §21.1–§21.2; spec.md §11.2, §12.3, §24).
4
+ *
5
+ * This module is the ONLY public export of the package.
6
+ */
7
+ export * from "./layout.js";
8
+ export * from "./local-file-system.js";
9
+ export * from "./lock-provider.js";
10
+ export * from "./checkpoint-store.js";
11
+ export * from "./embedding-cache.js";
12
+ export * from "./analysis-cache.js";
13
+ export * from "./search-journey-store.js";
14
+ export * from "./storage.js";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,aAAa,CAAC;AAG5B,cAAc,wBAAwB,CAAC;AAGvC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,uBAAuB,CAAC;AAGtC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,2BAA2B,CAAC;AAG1C,cAAc,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @untangle-code/storage-local — local state under `.untangle/`
3
+ * (architecture.md §12, §21.1–§21.2; spec.md §11.2, §12.3, §24).
4
+ *
5
+ * This module is the ONLY public export of the package.
6
+ */
7
+ // `.untangle/` layout (architecture.md §12)
8
+ export * from "./layout.js";
9
+ // Path-sandboxed `FileSystemPort` adapter (architecture.md §2.1, §23.1)
10
+ export * from "./local-file-system.js";
11
+ // File-based `LockProvider` (architecture.md §21.1–§21.2)
12
+ export * from "./lock-provider.js";
13
+ // Atomic local checkpoint mirror (architecture.md §12, §13.3)
14
+ export * from "./checkpoint-store.js";
15
+ // Persistent, size-bounded embedding cache (spec.md §9.4)
16
+ export * from "./embedding-cache.js";
17
+ // Inhaltsadressierte Caches fuer Parse, Chunk und Komplexitaet (WP-100)
18
+ export * from "./analysis-cache.js";
19
+ // Explizit aktivierte, lokale Dynamic-Product-Complexity-Sessions (Welle 9)
20
+ export * from "./search-journey-store.js";
21
+ // Convenience wiring of the above
22
+ export * from "./storage.js";
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,4CAA4C;AAC5C,cAAc,aAAa,CAAC;AAE5B,wEAAwE;AACxE,cAAc,wBAAwB,CAAC;AAEvC,0DAA0D;AAC1D,cAAc,oBAAoB,CAAC;AAEnC,8DAA8D;AAC9D,cAAc,uBAAuB,CAAC;AAEtC,0DAA0D;AAC1D,cAAc,sBAAsB,CAAC;AAErC,wEAAwE;AACxE,cAAc,qBAAqB,CAAC;AAEpC,4EAA4E;AAC5E,cAAc,2BAA2B,CAAC;AAE1C,kCAAkC;AAClC,cAAc,cAAc,CAAC"}
@@ -0,0 +1,50 @@
1
+ import type { FileSystemPort, PortCallOptions } from "@untangle-code/domain";
2
+ /**
3
+ * Die drei inhaltsadressierten Analyse-Caches (WP-100). Sie liegen unter
4
+ * `cache/analysis/`, getrennt nach Art, damit jeder seine eigene
5
+ * Groessengrenze und seine eigene Trefferquote hat.
6
+ */
7
+ export interface StateAnalysisCacheLayout {
8
+ root: string;
9
+ parse: string;
10
+ chunk: string;
11
+ metrics: string;
12
+ }
13
+ export interface StateCacheLayout {
14
+ root: string;
15
+ embeddings: string;
16
+ analysis: StateAnalysisCacheLayout;
17
+ parser: string;
18
+ gitBlobs: string;
19
+ }
20
+ export interface StateArtifactsLayout {
21
+ root: string;
22
+ diffs: string;
23
+ visualizations: string;
24
+ }
25
+ /** The full `.untangle/` layout rooted at one absolute directory. */
26
+ export interface StateDirLayout {
27
+ /** Absolute path to the state directory itself (e.g. `<workspace>/.untangle`). */
28
+ root: string;
29
+ cache: StateCacheLayout;
30
+ jobs: string;
31
+ locks: string;
32
+ productComplexity: {
33
+ root: string;
34
+ sessions: string;
35
+ };
36
+ artifacts: StateArtifactsLayout;
37
+ logs: string;
38
+ }
39
+ /** Builds the layout's paths under `root`. Pure; performs no I/O. */
40
+ export declare function buildStateDirLayout(root: string): StateDirLayout;
41
+ /** Every directory the layout declares, in an order safe for sequential `mkdir`. */
42
+ export declare function listStateDirLayoutPaths(layout: StateDirLayout): string[];
43
+ /**
44
+ * Creates every directory of the layout (idempotent — safe to call on every
45
+ * startup). Directories are created one at a time with `recursive: true` so
46
+ * a partially-created layout from a previous aborted run is completed
47
+ * without error.
48
+ */
49
+ export declare function ensureStateDirLayout(fs: FileSystemPort, layout: StateDirLayout, options?: PortCallOptions): Promise<void>;
50
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAI7E;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,wBAAwB,CAAC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,gBAAgB,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,EAAE;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,SAAS,EAAE,oBAAoB,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAgChE;AAED,oFAAoF;AACpF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,EAAE,CAoBxE;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,IAAI,CAAC,CAQf"}
package/dist/layout.js ADDED
@@ -0,0 +1,99 @@
1
+ /**
2
+ * `.untangle/` local state directory layout (architecture.md §12).
3
+ *
4
+ * ```text
5
+ * .untangle/
6
+ * ├─ cache/
7
+ * │ ├─ embeddings/
8
+ * │ ├─ analysis/ (WP-100: parse/, chunk/, metrics/)
9
+ * │ ├─ parser/
10
+ * │ └─ git-blobs/
11
+ * ├─ jobs/
12
+ * ├─ locks/
13
+ * ├─ product-complexity/
14
+ * │ └─ sessions/
15
+ * ├─ artifacts/
16
+ * │ ├─ diffs/
17
+ * │ └─ visualizations/
18
+ * └─ logs/
19
+ * ```
20
+ *
21
+ * Pure path arithmetic only — no I/O. `ensureStateDirLayout` is the one
22
+ * function that actually touches the filesystem, via an injected
23
+ * `FileSystemPort` (never `node:fs` directly), so layout construction stays
24
+ * testable without a real disk.
25
+ */
26
+ import path from "node:path";
27
+ import { assertNotAborted } from "./abort.js";
28
+ /** Builds the layout's paths under `root`. Pure; performs no I/O. */
29
+ export function buildStateDirLayout(root) {
30
+ const cacheRoot = path.join(root, "cache");
31
+ const analysisRoot = path.join(cacheRoot, "analysis");
32
+ const artifactsRoot = path.join(root, "artifacts");
33
+ const productComplexityRoot = path.join(root, "product-complexity");
34
+ return {
35
+ root,
36
+ cache: {
37
+ root: cacheRoot,
38
+ embeddings: path.join(cacheRoot, "embeddings"),
39
+ analysis: {
40
+ root: analysisRoot,
41
+ parse: path.join(analysisRoot, "parse"),
42
+ chunk: path.join(analysisRoot, "chunk"),
43
+ metrics: path.join(analysisRoot, "metrics"),
44
+ },
45
+ parser: path.join(cacheRoot, "parser"),
46
+ gitBlobs: path.join(cacheRoot, "git-blobs"),
47
+ },
48
+ jobs: path.join(root, "jobs"),
49
+ locks: path.join(root, "locks"),
50
+ productComplexity: {
51
+ root: productComplexityRoot,
52
+ sessions: path.join(productComplexityRoot, "sessions"),
53
+ },
54
+ artifacts: {
55
+ root: artifactsRoot,
56
+ diffs: path.join(artifactsRoot, "diffs"),
57
+ visualizations: path.join(artifactsRoot, "visualizations"),
58
+ },
59
+ logs: path.join(root, "logs"),
60
+ };
61
+ }
62
+ /** Every directory the layout declares, in an order safe for sequential `mkdir`. */
63
+ export function listStateDirLayoutPaths(layout) {
64
+ return [
65
+ layout.root,
66
+ layout.cache.root,
67
+ layout.cache.embeddings,
68
+ layout.cache.analysis.root,
69
+ layout.cache.analysis.parse,
70
+ layout.cache.analysis.chunk,
71
+ layout.cache.analysis.metrics,
72
+ layout.cache.parser,
73
+ layout.cache.gitBlobs,
74
+ layout.jobs,
75
+ layout.locks,
76
+ layout.productComplexity.root,
77
+ layout.productComplexity.sessions,
78
+ layout.artifacts.root,
79
+ layout.artifacts.diffs,
80
+ layout.artifacts.visualizations,
81
+ layout.logs,
82
+ ];
83
+ }
84
+ /**
85
+ * Creates every directory of the layout (idempotent — safe to call on every
86
+ * startup). Directories are created one at a time with `recursive: true` so
87
+ * a partially-created layout from a previous aborted run is completed
88
+ * without error.
89
+ */
90
+ export async function ensureStateDirLayout(fs, layout, options) {
91
+ for (const dir of listStateDirLayoutPaths(layout)) {
92
+ assertNotAborted(options, "ensure_state_dir_layout");
93
+ await fs.mkdir(dir, {
94
+ recursive: true,
95
+ ...(options?.signal ? { signal: options.signal } : {}),
96
+ });
97
+ }
98
+ }
99
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { DirEntryV1, FileStatV1, FileSystemPort, FileWriteOptions, PortCallOptions } from "@untangle-code/domain";
2
+ /**
3
+ * Node-backed, path-sandboxed `FileSystemPort`. Construct one per set of
4
+ * roots that should be reachable (typically just the `.untangle` state
5
+ * directory for this package's own callers).
6
+ */
7
+ export declare class LocalFileSystem implements FileSystemPort {
8
+ private readonly roots;
9
+ constructor(roots: readonly string[]);
10
+ /**
11
+ * Lexical sandbox check (throws `PATH_OUTSIDE_WORKSPACE` on escape), plus a
12
+ * real-path re-check via {@link resolveAsFarAsPossible} on BOTH the
13
+ * candidate and the configured roots — needed on both sides: the candidate
14
+ * may not exist yet (`writeFile`/`mkdir` of a new entry) and a root itself
15
+ * may contain an unresolved symlink segment (e.g. macOS'
16
+ * `/var` → `/private/var`) that would otherwise make every path under it
17
+ * look like an escape. Recomputed per call rather than cached, since which
18
+ * prefix of a root already exists changes over the adapter's lifetime
19
+ * (the root itself usually does not exist yet on first use).
20
+ */
21
+ private guard;
22
+ stat(path: string, options?: PortCallOptions): Promise<FileStatV1 | null>;
23
+ lstat(path: string, options?: PortCallOptions): Promise<FileStatV1 | null>;
24
+ readFile(path: string, options?: PortCallOptions): Promise<Uint8Array>;
25
+ readTextFile(path: string, options?: PortCallOptions): Promise<string>;
26
+ readDir(path: string, options?: PortCallOptions): Promise<DirEntryV1[]>;
27
+ realPath(path: string, options?: PortCallOptions): Promise<string>;
28
+ writeFile(path: string, data: Uint8Array, options?: FileWriteOptions): Promise<void>;
29
+ mkdir(path: string, options?: PortCallOptions & {
30
+ recursive?: boolean;
31
+ }): Promise<void>;
32
+ rename(from: string, to: string, options?: PortCallOptions): Promise<void>;
33
+ remove(path: string, options?: PortCallOptions & {
34
+ recursive?: boolean;
35
+ }): Promise<void>;
36
+ }
37
+ //# sourceMappingURL=local-file-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-file-system.d.ts","sourceRoot":"","sources":["../src/local-file-system.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,eAAe,EAChB,MAAM,uBAAuB,CAAC;AAwE/B;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,cAAc;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;gBAEvB,KAAK,EAAE,SAAS,MAAM,EAAE;IAS3C;;;;;;;;;;OAUG;YACW,KAAK;IAQN,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAczE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAc1E,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC;IAOtE,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC;IAMtE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAgBvE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC;IAQlE,SAAS,CACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,IAAI,CAAC;IAMH,KAAK,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,eAAe,GAAG;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAClD,OAAO,CAAC,IAAI,CAAC;IAMH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAO1E,MAAM,CACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,eAAe,GAAG;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAClD,OAAO,CAAC,IAAI,CAAC;CAKjB"}