@untangle-code/storage-local 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/NOTICE +90 -0
- package/README.md +226 -2
- package/THIRD_PARTY_LICENSES.md +24 -0
- package/dist/abort.d.ts +18 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/abort.js +14 -0
- package/dist/abort.js.map +1 -0
- package/dist/analysis-cache.d.ts +81 -0
- package/dist/analysis-cache.d.ts.map +1 -0
- package/dist/analysis-cache.js +322 -0
- package/dist/analysis-cache.js.map +1 -0
- package/dist/checkpoint-store.d.ts +22 -0
- package/dist/checkpoint-store.d.ts.map +1 -0
- package/dist/checkpoint-store.js +123 -0
- package/dist/checkpoint-store.js.map +1 -0
- package/dist/embedding-cache.d.ts +38 -0
- package/dist/embedding-cache.d.ts.map +1 -0
- package/dist/embedding-cache.js +229 -0
- package/dist/embedding-cache.js.map +1 -0
- package/dist/fs-errors.d.ts +4 -0
- package/dist/fs-errors.d.ts.map +1 -0
- package/dist/fs-errors.js +14 -0
- package/dist/fs-errors.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +50 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +99 -0
- package/dist/layout.js.map +1 -0
- package/dist/local-file-system.d.ts +37 -0
- package/dist/local-file-system.d.ts.map +1 -0
- package/dist/local-file-system.js +193 -0
- package/dist/local-file-system.js.map +1 -0
- package/dist/lock-provider.d.ts +59 -0
- package/dist/lock-provider.d.ts.map +1 -0
- package/dist/lock-provider.js +318 -0
- package/dist/lock-provider.js.map +1 -0
- package/dist/real-time.d.ts +16 -0
- package/dist/real-time.d.ts.map +1 -0
- package/dist/real-time.js +19 -0
- package/dist/real-time.js.map +1 -0
- package/dist/search-journey-store.d.ts +34 -0
- package/dist/search-journey-store.d.ts.map +1 -0
- package/dist/search-journey-store.js +530 -0
- package/dist/search-journey-store.js.map +1 -0
- package/dist/storage.d.ts +42 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +34 -0
- package/dist/storage.js.map +1 -0
- package/package.json +60 -3
- package/src/abort.ts +26 -0
- package/src/analysis-cache.ts +404 -0
- package/src/checkpoint-store.ts +155 -0
- package/src/embedding-cache.ts +282 -0
- package/src/fs-errors.ts +19 -0
- package/src/index.ts +30 -0
- package/src/layout.ts +149 -0
- package/src/local-file-system.ts +232 -0
- package/src/lock-provider.ts +381 -0
- package/src/real-time.ts +18 -0
- package/src/search-journey-store.ts +638 -0
- 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 @@
|
|
|
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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|
package/dist/layout.d.ts
ADDED
|
@@ -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"}
|