@fulmenhq/tsfulmen 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -1
- package/README.md +30 -1
- package/config/crucible-ts/taxonomy/metrics.yaml +1 -1
- package/dist/appidentity/index.d.ts +15 -5
- package/dist/appidentity/index.js +9 -2204
- package/dist/appidentity/index.js.map +1 -1
- package/dist/assets/index.d.ts +179 -0
- package/dist/assets/index.js +4 -0
- package/dist/assets/index.js.map +1 -0
- package/dist/bin/prometheus-cli.js +15 -9134
- package/dist/bin/prometheus-cli.js.map +1 -1
- package/dist/bin/schema-cli.js +4 -6098
- package/dist/bin/schema-cli.js.map +1 -1
- package/dist/bin/signals-cli.js +15 -1910
- package/dist/bin/signals-cli.js.map +1 -1
- package/dist/chunk-3HISR2XI.js +36 -0
- package/dist/chunk-3HISR2XI.js.map +1 -0
- package/dist/chunk-5HZEJGZ5.js +725 -0
- package/dist/chunk-5HZEJGZ5.js.map +1 -0
- package/dist/chunk-7SVYOPJ3.js +9412 -0
- package/dist/chunk-7SVYOPJ3.js.map +1 -0
- package/dist/chunk-BCO3FUUO.js +682 -0
- package/dist/chunk-BCO3FUUO.js.map +1 -0
- package/dist/chunk-BJDL6JEX.js +208 -0
- package/dist/chunk-BJDL6JEX.js.map +1 -0
- package/dist/chunk-BTUS6PZZ.js +302 -0
- package/dist/chunk-BTUS6PZZ.js.map +1 -0
- package/dist/chunk-CW4CSNVF.js +410 -0
- package/dist/chunk-CW4CSNVF.js.map +1 -0
- package/dist/chunk-HIBZ2AY7.js +194 -0
- package/dist/chunk-HIBZ2AY7.js.map +1 -0
- package/dist/chunk-HS4F3BRR.js +985 -0
- package/dist/chunk-HS4F3BRR.js.map +1 -0
- package/dist/chunk-HY6JQOFN.js +32 -0
- package/dist/chunk-HY6JQOFN.js.map +1 -0
- package/dist/chunk-K2FYT43R.js +837 -0
- package/dist/chunk-K2FYT43R.js.map +1 -0
- package/dist/chunk-K7BMFGQP.js +43 -0
- package/dist/chunk-K7BMFGQP.js.map +1 -0
- package/dist/chunk-LWAH2UG7.js +103 -0
- package/dist/chunk-LWAH2UG7.js.map +1 -0
- package/dist/chunk-OHE2AOH4.js +273 -0
- package/dist/chunk-OHE2AOH4.js.map +1 -0
- package/dist/chunk-P3DNOZOG.js +1488 -0
- package/dist/chunk-P3DNOZOG.js.map +1 -0
- package/dist/chunk-PQ6TKPUN.js +27 -0
- package/dist/chunk-PQ6TKPUN.js.map +1 -0
- package/dist/chunk-QCCN6POA.js +211 -0
- package/dist/chunk-QCCN6POA.js.map +1 -0
- package/dist/chunk-RSLFR4TO.js +631 -0
- package/dist/chunk-RSLFR4TO.js.map +1 -0
- package/dist/chunk-UP2VWCW5.js +33 -0
- package/dist/chunk-UP2VWCW5.js.map +1 -0
- package/dist/chunk-W6TQFTTY.js +130 -0
- package/dist/chunk-W6TQFTTY.js.map +1 -0
- package/dist/chunk-WSMVLWLB.js +269 -0
- package/dist/chunk-WSMVLWLB.js.map +1 -0
- package/dist/config/index.d.ts +69 -11
- package/dist/config/index.js +24 -1227
- package/dist/config/index.js.map +1 -1
- package/dist/crucible/fulpack/index.js +2 -25
- package/dist/crucible/fulpack/index.js.map +1 -1
- package/dist/crucible/index.js +13 -1129
- package/dist/crucible/index.js.map +1 -1
- package/dist/docscribe/index.js +2 -680
- package/dist/docscribe/index.js.map +1 -1
- package/dist/errors/index.js +5 -1831
- package/dist/errors/index.js.map +1 -1
- package/dist/errors-D7KXV3QW.js +4 -0
- package/dist/errors-D7KXV3QW.js.map +1 -0
- package/dist/errors-RBJX6ETJ.js +8 -0
- package/dist/errors-RBJX6ETJ.js.map +1 -0
- package/dist/export-BWRETGYS.js +6 -0
- package/dist/export-BWRETGYS.js.map +1 -0
- package/dist/foundry/index.d.ts +1 -1
- package/dist/foundry/index.js +340 -3949
- package/dist/foundry/index.js.map +1 -1
- package/dist/foundry/similarity/index.js +3 -134
- package/dist/foundry/similarity/index.js.map +1 -1
- package/dist/fulencode/index.js +3 -319
- package/dist/fulencode/index.js.map +1 -1
- package/dist/fulhash/index.js +3 -611
- package/dist/fulhash/index.js.map +1 -1
- package/dist/fulpack/index.js +3 -1515
- package/dist/fulpack/index.js.map +1 -1
- package/dist/hash-YOJYVOWH.js +4 -0
- package/dist/hash-YOJYVOWH.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +14 -4390
- package/dist/index.js.map +1 -1
- package/dist/loader-H5K7HSO5.js +9 -0
- package/dist/loader-H5K7HSO5.js.map +1 -0
- package/dist/logging/index.js +2 -723
- package/dist/logging/index.js.map +1 -1
- package/dist/pathfinder/index.js +18 -2341
- package/dist/pathfinder/index.js.map +1 -1
- package/dist/prom-client-PNEBGD3G.js +4801 -0
- package/dist/prom-client-PNEBGD3G.js.map +1 -0
- package/dist/reports/license-inventory.csv +1 -1
- package/dist/schema/index.d.ts +19 -6
- package/dist/schema/index.js +4 -6148
- package/dist/schema/index.js.map +1 -1
- package/dist/signals/index.d.ts +1 -1
- package/dist/signals/index.js +8 -2767
- package/dist/signals/index.js.map +1 -1
- package/dist/similarity/index.js +3 -134
- package/dist/similarity/index.js.map +1 -1
- package/dist/telemetry/http/index.js +9 -930
- package/dist/telemetry/http/index.js.map +1 -1
- package/dist/telemetry/index.js +4 -1561
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/prometheus/index.js +7 -8417
- package/dist/telemetry/prometheus/index.js.map +1 -1
- package/dist/{types-Dv5TERCM.d.ts → types-CHDvDRCf.d.ts} +17 -1
- package/dist/validator-U4CKD2Y3.js +6 -0
- package/dist/validator-U4CKD2Y3.js.map +1 -0
- package/package.json +5 -1
- package/schemas/crucible-ts/library/module-manifest/v1.0.0/module-manifest.schema.json +1 -1
- package/schemas/crucible-ts/observability/logging/v1.0.0/definitions.schema.json +1 -1
- package/schemas/crucible-ts/observability/logging/v1.0.0/log-event.schema.json +13 -13
- package/schemas/crucible-ts/observability/logging/v1.0.0/logger-config.schema.json +2 -2
- package/schemas/crucible-ts/observability/logging/v1.0.0/logging-policy.schema.json +1 -1
- package/schemas/crucible-ts/observability/logging/v1.0.0/middleware-config.schema.json +2 -2
- package/schemas/crucible-ts/observability/logging/v1.0.0/severity-filter.schema.json +3 -3
- package/schemas/crucible-ts/upstream/3leaps/crucible/PROVENANCE.md +3 -3
- package/schemas/crucible-ts/upstream/3leaps/crucible/docs/catalog/classifiers/README.md +1 -1
- package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/classifiers-framework.md +1 -1
- package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/data-sensitivity-classification.md +1 -1
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import picomatch from 'picomatch';
|
|
2
|
+
import { readFile, access } from 'fs/promises';
|
|
3
|
+
import { join, dirname, sep } from 'path';
|
|
4
|
+
import glob from 'fast-glob';
|
|
5
|
+
import { accessSync } from 'fs';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
|
|
8
|
+
// src/assets/embedded-resolver.ts
|
|
9
|
+
|
|
10
|
+
// src/assets/errors.ts
|
|
11
|
+
var AssetResolutionError = class _AssetResolutionError extends Error {
|
|
12
|
+
constructor(message, assetPath, cause) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.assetPath = assetPath;
|
|
15
|
+
this.cause = cause;
|
|
16
|
+
this.name = "AssetResolutionError";
|
|
17
|
+
if (Error.captureStackTrace) {
|
|
18
|
+
Error.captureStackTrace(this, _AssetResolutionError);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
static notFound(assetPath, mode) {
|
|
22
|
+
return new _AssetResolutionError(`Asset not found (${mode} resolver): ${assetPath}`, assetPath);
|
|
23
|
+
}
|
|
24
|
+
static readFailed(assetPath, code) {
|
|
25
|
+
return new _AssetResolutionError(`Failed to read asset ${assetPath} (${code})`, assetPath);
|
|
26
|
+
}
|
|
27
|
+
static baseDirUnavailable(detail) {
|
|
28
|
+
return new _AssetResolutionError(`No SSOT asset base directory available: ${detail}`);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/assets/paths.ts
|
|
33
|
+
var ASSET_NAMESPACES = ["schemas", "config", "docs"];
|
|
34
|
+
function rejectCommon(value, kind) {
|
|
35
|
+
if (!value || value.trim() === "") {
|
|
36
|
+
throw new AssetResolutionError(`Asset ${kind} must be a non-empty string`);
|
|
37
|
+
}
|
|
38
|
+
if (value.includes("\\")) {
|
|
39
|
+
throw new AssetResolutionError(`Asset ${kind} must be POSIX (no backslashes): ${value}`, value);
|
|
40
|
+
}
|
|
41
|
+
if (value.startsWith("/") || /^[A-Za-z]:/.test(value)) {
|
|
42
|
+
throw new AssetResolutionError(`Asset ${kind} must be relative, got absolute: ${value}`, value);
|
|
43
|
+
}
|
|
44
|
+
const segments = value.split("/");
|
|
45
|
+
for (const seg of segments) {
|
|
46
|
+
if (seg === "" || seg === "." || seg === "..") {
|
|
47
|
+
throw new AssetResolutionError(
|
|
48
|
+
`Asset ${kind} must not contain empty, '.' or '..' segments: ${value}`,
|
|
49
|
+
value
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return segments;
|
|
54
|
+
}
|
|
55
|
+
function assertNamespace(value, segments, kind) {
|
|
56
|
+
const top = segments[0];
|
|
57
|
+
if (!ASSET_NAMESPACES.includes(top)) {
|
|
58
|
+
throw new AssetResolutionError(
|
|
59
|
+
`Asset ${kind} must start with one of ${ASSET_NAMESPACES.join("/")}/ : ${value}`,
|
|
60
|
+
value
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function assertSafeLogicalPath(logicalPath, enforceNamespace = true) {
|
|
65
|
+
const segments = rejectCommon(logicalPath, "path");
|
|
66
|
+
if (enforceNamespace) {
|
|
67
|
+
assertNamespace(logicalPath, segments, "path");
|
|
68
|
+
}
|
|
69
|
+
return logicalPath;
|
|
70
|
+
}
|
|
71
|
+
function assertSafePattern(pattern, enforceNamespace = true) {
|
|
72
|
+
const segments = rejectCommon(pattern, "pattern");
|
|
73
|
+
if (enforceNamespace) {
|
|
74
|
+
assertNamespace(pattern, segments, "pattern");
|
|
75
|
+
}
|
|
76
|
+
return pattern;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/assets/embedded-resolver.ts
|
|
80
|
+
var registeredManifests = [];
|
|
81
|
+
var registrationVersion = 0;
|
|
82
|
+
function getRegistrationVersion() {
|
|
83
|
+
return registrationVersion;
|
|
84
|
+
}
|
|
85
|
+
function registerEmbeddedAssets(manifest) {
|
|
86
|
+
const existing = registeredManifests.findIndex((m) => m.domain === manifest.domain);
|
|
87
|
+
if (existing >= 0) {
|
|
88
|
+
registeredManifests[existing] = manifest;
|
|
89
|
+
} else {
|
|
90
|
+
registeredManifests.push(manifest);
|
|
91
|
+
}
|
|
92
|
+
registrationVersion++;
|
|
93
|
+
}
|
|
94
|
+
function hasEmbeddedDomain(domain) {
|
|
95
|
+
return registeredManifests.some((m) => m.domain === domain);
|
|
96
|
+
}
|
|
97
|
+
function clearEmbeddedAssets() {
|
|
98
|
+
registeredManifests.length = 0;
|
|
99
|
+
registrationVersion++;
|
|
100
|
+
}
|
|
101
|
+
function hasEmbeddedAssets() {
|
|
102
|
+
return registeredManifests.length > 0;
|
|
103
|
+
}
|
|
104
|
+
function buildIndex() {
|
|
105
|
+
const index = /* @__PURE__ */ new Map();
|
|
106
|
+
for (const manifest of registeredManifests) {
|
|
107
|
+
for (const [logicalPath, content] of Object.entries(manifest.files)) {
|
|
108
|
+
index.set(logicalPath, content);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return index;
|
|
112
|
+
}
|
|
113
|
+
var EmbeddedAssetResolver = class {
|
|
114
|
+
mode = "embedded";
|
|
115
|
+
// Snapshot the index at construction; resolvers are cheap to recreate and the
|
|
116
|
+
// factory rebuilds when mode changes.
|
|
117
|
+
index;
|
|
118
|
+
constructor() {
|
|
119
|
+
this.index = buildIndex();
|
|
120
|
+
}
|
|
121
|
+
async read(logicalPath) {
|
|
122
|
+
const content = this.index.get(assertSafeLogicalPath(logicalPath));
|
|
123
|
+
if (content === void 0) {
|
|
124
|
+
throw AssetResolutionError.notFound(logicalPath, "embedded");
|
|
125
|
+
}
|
|
126
|
+
return content;
|
|
127
|
+
}
|
|
128
|
+
async list(patterns) {
|
|
129
|
+
if (patterns.length === 0) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
for (const pattern of patterns) {
|
|
133
|
+
assertSafePattern(pattern);
|
|
134
|
+
}
|
|
135
|
+
const isMatch = picomatch(patterns, { dot: false });
|
|
136
|
+
return Array.from(this.index.keys()).filter((key) => isMatch(key)).sort();
|
|
137
|
+
}
|
|
138
|
+
async has(logicalPath) {
|
|
139
|
+
try {
|
|
140
|
+
return this.index.has(assertSafeLogicalPath(logicalPath));
|
|
141
|
+
} catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
provenance() {
|
|
146
|
+
return { mode: "embedded", embeddedCount: this.index.size };
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
function toPosix(p) {
|
|
150
|
+
return sep === "/" ? p : p.split(sep).join("/");
|
|
151
|
+
}
|
|
152
|
+
var FsAssetResolver = class {
|
|
153
|
+
/**
|
|
154
|
+
* @param baseDir Directory logical paths resolve against.
|
|
155
|
+
* @param enforceNamespace Restrict to the SSOT namespaces (schemas/config/docs).
|
|
156
|
+
* Default `true` for the package's own asset tree; pass `false` when `baseDir`
|
|
157
|
+
* is a consumer-supplied tree they scope themselves (traversal guards still apply).
|
|
158
|
+
*/
|
|
159
|
+
constructor(baseDir, enforceNamespace = true) {
|
|
160
|
+
this.baseDir = baseDir;
|
|
161
|
+
this.enforceNamespace = enforceNamespace;
|
|
162
|
+
}
|
|
163
|
+
mode = "fs";
|
|
164
|
+
resolve(logicalPath) {
|
|
165
|
+
return join(this.baseDir, assertSafeLogicalPath(logicalPath, this.enforceNamespace));
|
|
166
|
+
}
|
|
167
|
+
async read(logicalPath) {
|
|
168
|
+
const absolute = this.resolve(logicalPath);
|
|
169
|
+
try {
|
|
170
|
+
return await readFile(absolute, "utf-8");
|
|
171
|
+
} catch (error) {
|
|
172
|
+
const err = error;
|
|
173
|
+
if (err.code === "ENOENT") {
|
|
174
|
+
throw AssetResolutionError.notFound(logicalPath, "fs");
|
|
175
|
+
}
|
|
176
|
+
throw AssetResolutionError.readFailed(logicalPath, err.code ?? "unknown");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async list(patterns) {
|
|
180
|
+
if (patterns.length === 0) {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
for (const pattern of patterns) {
|
|
184
|
+
assertSafePattern(pattern, this.enforceNamespace);
|
|
185
|
+
}
|
|
186
|
+
const matches = await glob(patterns, {
|
|
187
|
+
cwd: this.baseDir,
|
|
188
|
+
absolute: false,
|
|
189
|
+
onlyFiles: true,
|
|
190
|
+
followSymbolicLinks: false,
|
|
191
|
+
suppressErrors: true,
|
|
192
|
+
dot: false
|
|
193
|
+
});
|
|
194
|
+
return matches.map(toPosix).sort();
|
|
195
|
+
}
|
|
196
|
+
async has(logicalPath) {
|
|
197
|
+
let absolute;
|
|
198
|
+
try {
|
|
199
|
+
absolute = this.resolve(logicalPath);
|
|
200
|
+
} catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
await access(absolute);
|
|
205
|
+
return true;
|
|
206
|
+
} catch {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
provenance() {
|
|
211
|
+
return { mode: "fs", baseDir: this.baseDir };
|
|
212
|
+
}
|
|
213
|
+
/** The on-disk base directory this resolver reads from. */
|
|
214
|
+
getBaseDir() {
|
|
215
|
+
return this.baseDir;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var ASSET_ROOT_MARKER = join("schemas", "crucible-ts");
|
|
219
|
+
function dirExists(path) {
|
|
220
|
+
try {
|
|
221
|
+
accessSync(path);
|
|
222
|
+
return true;
|
|
223
|
+
} catch {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function findAssetBaseDir() {
|
|
228
|
+
let dir;
|
|
229
|
+
try {
|
|
230
|
+
dir = dirname(fileURLToPath(import.meta.url));
|
|
231
|
+
} catch {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
let prev = "";
|
|
235
|
+
while (dir !== prev) {
|
|
236
|
+
if (dirExists(join(dir, ASSET_ROOT_MARKER))) {
|
|
237
|
+
return dir;
|
|
238
|
+
}
|
|
239
|
+
prev = dir;
|
|
240
|
+
dir = dirname(dir);
|
|
241
|
+
}
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
function readModeFromEnv() {
|
|
245
|
+
const raw = process.env.TSFULMEN_ASSET_MODE?.trim().toLowerCase();
|
|
246
|
+
if (raw === "fs" || raw === "embedded" || raw === "auto") {
|
|
247
|
+
return raw;
|
|
248
|
+
}
|
|
249
|
+
return void 0;
|
|
250
|
+
}
|
|
251
|
+
function resolveAssets(options = {}) {
|
|
252
|
+
if (options.baseDir) {
|
|
253
|
+
return new FsAssetResolver(options.baseDir, false);
|
|
254
|
+
}
|
|
255
|
+
const requested = options.mode ?? readModeFromEnv() ?? "auto";
|
|
256
|
+
if (requested === "fs") {
|
|
257
|
+
const baseDir2 = findAssetBaseDir();
|
|
258
|
+
if (!baseDir2) {
|
|
259
|
+
throw AssetResolutionError.baseDirUnavailable(
|
|
260
|
+
"mode=fs but no on-disk asset tree found (use mode=embedded in a compiled binary)"
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
return new FsAssetResolver(baseDir2);
|
|
264
|
+
}
|
|
265
|
+
if (requested === "embedded") {
|
|
266
|
+
return new EmbeddedAssetResolver();
|
|
267
|
+
}
|
|
268
|
+
const baseDir = findAssetBaseDir();
|
|
269
|
+
if (baseDir) {
|
|
270
|
+
return new FsAssetResolver(baseDir);
|
|
271
|
+
}
|
|
272
|
+
if (hasEmbeddedAssets()) {
|
|
273
|
+
return new EmbeddedAssetResolver();
|
|
274
|
+
}
|
|
275
|
+
throw AssetResolutionError.baseDirUnavailable(
|
|
276
|
+
"auto: no on-disk asset tree and no embedded assets registered"
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
var cached;
|
|
280
|
+
var cachedKey;
|
|
281
|
+
function getAssetResolver(options = {}) {
|
|
282
|
+
const key = JSON.stringify({
|
|
283
|
+
mode: options.mode ?? readModeFromEnv() ?? "auto",
|
|
284
|
+
baseDir: options.baseDir ?? null,
|
|
285
|
+
// Fold in the registration version so a cached embedded resolver rebuilds
|
|
286
|
+
// after a domain is registered (e.g. via ensureEmbeddedDomain).
|
|
287
|
+
reg: getRegistrationVersion()
|
|
288
|
+
});
|
|
289
|
+
if (!cached || cachedKey !== key) {
|
|
290
|
+
cached = resolveAssets(options);
|
|
291
|
+
cachedKey = key;
|
|
292
|
+
}
|
|
293
|
+
return cached;
|
|
294
|
+
}
|
|
295
|
+
function resetAssetResolver() {
|
|
296
|
+
cached = void 0;
|
|
297
|
+
cachedKey = void 0;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export { AssetResolutionError, EmbeddedAssetResolver, FsAssetResolver, clearEmbeddedAssets, findAssetBaseDir, getAssetResolver, hasEmbeddedAssets, hasEmbeddedDomain, registerEmbeddedAssets, resetAssetResolver, resolveAssets };
|
|
301
|
+
//# sourceMappingURL=chunk-BTUS6PZZ.js.map
|
|
302
|
+
//# sourceMappingURL=chunk-BTUS6PZZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/assets/errors.ts","../src/assets/paths.ts","../src/assets/embedded-resolver.ts","../src/assets/fs-resolver.ts","../src/assets/resolver.ts"],"names":["join","baseDir"],"mappings":"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","file":"chunk-BTUS6PZZ.js","sourcesContent":["/**\n * Asset resolution - Error handling\n *\n * Custom error classes for SSOT asset resolution (filesystem or embedded).\n */\n\nexport class AssetResolutionError extends Error {\n constructor(\n message: string,\n public readonly assetPath?: string,\n public readonly cause?: Error,\n ) {\n super(message);\n this.name = \"AssetResolutionError\";\n\n // Maintain proper stack trace for V8\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, AssetResolutionError);\n }\n }\n\n static notFound(assetPath: string, mode: string): AssetResolutionError {\n return new AssetResolutionError(`Asset not found (${mode} resolver): ${assetPath}`, assetPath);\n }\n\n static readFailed(assetPath: string, code: string): AssetResolutionError {\n // Reference the logical path + error code only; never interpolate a raw FS\n // error message, which can carry the absolute host path (info disclosure).\n return new AssetResolutionError(`Failed to read asset ${assetPath} (${code})`, assetPath);\n }\n\n static baseDirUnavailable(detail: string): AssetResolutionError {\n return new AssetResolutionError(`No SSOT asset base directory available: ${detail}`);\n }\n}\n","/**\n * Asset resolution - Logical path & pattern validation\n *\n * `./assets` is a public export, so logical paths and glob patterns are\n * caller-controlled. These guards prevent path traversal (reading outside the\n * package root via `..`, absolute paths, or non-POSIX separators) and restrict\n * resolution to the SSOT asset namespaces tsfulmen actually ships.\n */\n\nimport { AssetResolutionError } from \"./errors.js\";\n\n/** Top-level directories that make up the SSOT asset namespace. */\nexport const ASSET_NAMESPACES = [\"schemas\", \"config\", \"docs\"] as const;\n\nfunction rejectCommon(value: string, kind: \"path\" | \"pattern\"): string[] {\n if (!value || value.trim() === \"\") {\n throw new AssetResolutionError(`Asset ${kind} must be a non-empty string`);\n }\n if (value.includes(\"\\\\\")) {\n throw new AssetResolutionError(`Asset ${kind} must be POSIX (no backslashes): ${value}`, value);\n }\n if (value.startsWith(\"/\") || /^[A-Za-z]:/.test(value)) {\n throw new AssetResolutionError(`Asset ${kind} must be relative, got absolute: ${value}`, value);\n }\n const segments = value.split(\"/\");\n for (const seg of segments) {\n if (seg === \"\" || seg === \".\" || seg === \"..\") {\n throw new AssetResolutionError(\n `Asset ${kind} must not contain empty, '.' or '..' segments: ${value}`,\n value,\n );\n }\n }\n return segments;\n}\n\nfunction assertNamespace(value: string, segments: string[], kind: \"path\" | \"pattern\"): void {\n const top = segments[0];\n if (!ASSET_NAMESPACES.includes(top as (typeof ASSET_NAMESPACES)[number])) {\n throw new AssetResolutionError(\n `Asset ${kind} must start with one of ${ASSET_NAMESPACES.join(\"/\")}/ : ${value}`,\n value,\n );\n }\n}\n\n/**\n * Validate a logical asset path. Always rejects traversal/absolute/non-POSIX\n * inputs (the security guard). When `enforceNamespace` (the default — used by the\n * package's own asset resolver), also restricts to the SSOT namespaces. Resolvers\n * pointed at a consumer-supplied `baseDir` pass `enforceNamespace: false` since\n * the consumer owns and scopes that tree. Returns the (unchanged) safe path.\n */\nexport function assertSafeLogicalPath(logicalPath: string, enforceNamespace = true): string {\n const segments = rejectCommon(logicalPath, \"path\");\n if (enforceNamespace) {\n assertNamespace(logicalPath, segments, \"path\");\n }\n return logicalPath;\n}\n\n/**\n * Validate a glob pattern. Always rejects `..`/absolute/backslash patterns; glob\n * metacharacters (`*`, `{}`, etc.) are allowed within segments. Namespace\n * restriction is applied only when `enforceNamespace` (see\n * {@link assertSafeLogicalPath}).\n */\nexport function assertSafePattern(pattern: string, enforceNamespace = true): string {\n const segments = rejectCommon(pattern, \"pattern\");\n if (enforceNamespace) {\n assertNamespace(pattern, segments, \"pattern\");\n }\n return pattern;\n}\n","/**\n * Asset resolution - Embedded backend\n *\n * Reads SSOT assets from generated, build-embedded manifests instead of the\n * filesystem, so loads work inside a `bun --compile` single-file binary (no\n * asset tree on disk). T3 codegen emits per-domain {@link EmbeddedAssetManifest}\n * modules and registers them via {@link registerEmbeddedAssets}; this resolver\n * composes whatever has been registered.\n *\n * Until T3 lands, no manifests are registered, so the embedded resolver is\n * empty — `read()` throws a clear \"not found\" and `list()` returns `[]`.\n */\n\nimport picomatch from \"picomatch\";\nimport { AssetResolutionError } from \"./errors.js\";\nimport { assertSafeLogicalPath, assertSafePattern } from \"./paths.js\";\nimport type { AssetProvenance, AssetResolver, EmbeddedAssetManifest } from \"./types.js\";\n\n/** Process-level registry of embedded asset manifests (composed by domain). */\nconst registeredManifests: EmbeddedAssetManifest[] = [];\n\n/**\n * Monotonic counter bumped on every registration change. The resolver factory\n * folds this into its cache key so a cached embedded resolver is never stale\n * after a domain is registered (entarch watch-item).\n */\nlet registrationVersion = 0;\n\n/** Current registration version (see {@link registrationVersion}). */\nexport function getRegistrationVersion(): number {\n return registrationVersion;\n}\n\n/**\n * Register a generated embedded-asset manifest. Idempotent per domain\n * (re-registering a domain replaces it). Called by generated modules.\n */\nexport function registerEmbeddedAssets(manifest: EmbeddedAssetManifest): void {\n const existing = registeredManifests.findIndex((m) => m.domain === manifest.domain);\n if (existing >= 0) {\n registeredManifests[existing] = manifest;\n } else {\n registeredManifests.push(manifest);\n }\n registrationVersion++;\n}\n\n/** Whether a specific domain has been registered. */\nexport function hasEmbeddedDomain(domain: string): boolean {\n return registeredManifests.some((m) => m.domain === domain);\n}\n\n/** Clear all registered manifests. For testing only. */\nexport function clearEmbeddedAssets(): void {\n registeredManifests.length = 0;\n registrationVersion++;\n}\n\n/** Whether any embedded manifests have been registered. */\nexport function hasEmbeddedAssets(): boolean {\n return registeredManifests.length > 0;\n}\n\n/** Flatten registered manifests into a single logical-path -> content map. */\nfunction buildIndex(): Map<string, string> {\n const index = new Map<string, string>();\n for (const manifest of registeredManifests) {\n for (const [logicalPath, content] of Object.entries(manifest.files)) {\n index.set(logicalPath, content);\n }\n }\n return index;\n}\n\nexport class EmbeddedAssetResolver implements AssetResolver {\n readonly mode = \"embedded\" as const;\n\n // Snapshot the index at construction; resolvers are cheap to recreate and the\n // factory rebuilds when mode changes.\n private readonly index: Map<string, string>;\n\n constructor() {\n this.index = buildIndex();\n }\n\n async read(logicalPath: string): Promise<string> {\n const content = this.index.get(assertSafeLogicalPath(logicalPath));\n if (content === undefined) {\n throw AssetResolutionError.notFound(logicalPath, \"embedded\");\n }\n return content;\n }\n\n async list(patterns: string[]): Promise<string[]> {\n if (patterns.length === 0) {\n return [];\n }\n for (const pattern of patterns) {\n assertSafePattern(pattern);\n }\n const isMatch = picomatch(patterns, { dot: false });\n return Array.from(this.index.keys())\n .filter((key) => isMatch(key))\n .sort();\n }\n\n async has(logicalPath: string): Promise<boolean> {\n try {\n return this.index.has(assertSafeLogicalPath(logicalPath));\n } catch {\n return false;\n }\n }\n\n provenance(): AssetProvenance {\n return { mode: \"embedded\", embeddedCount: this.index.size };\n }\n}\n","/**\n * Asset resolution - Filesystem backend\n *\n * Reads SSOT assets from the on-disk `schemas/crucible-ts` / `config/crucible-ts`\n * trees shipped in the npm package. This is the current (pre-v0.4.0) behavior,\n * now centralized behind the {@link AssetResolver} contract.\n */\n\nimport { access, readFile } from \"node:fs/promises\";\nimport { join, sep } from \"node:path\";\nimport glob from \"fast-glob\";\nimport { AssetResolutionError } from \"./errors.js\";\nimport { assertSafeLogicalPath, assertSafePattern } from \"./paths.js\";\nimport type { AssetProvenance, AssetResolver } from \"./types.js\";\n\n/** Normalize a logical path to POSIX separators (logical paths are always POSIX). */\nfunction toPosix(p: string): string {\n return sep === \"/\" ? p : p.split(sep).join(\"/\");\n}\n\nexport class FsAssetResolver implements AssetResolver {\n readonly mode = \"fs\" as const;\n\n /**\n * @param baseDir Directory logical paths resolve against.\n * @param enforceNamespace Restrict to the SSOT namespaces (schemas/config/docs).\n * Default `true` for the package's own asset tree; pass `false` when `baseDir`\n * is a consumer-supplied tree they scope themselves (traversal guards still apply).\n */\n constructor(\n private readonly baseDir: string,\n private readonly enforceNamespace = true,\n ) {}\n\n private resolve(logicalPath: string): string {\n // Validate before joining — prevents `..`/absolute/non-POSIX traversal out of\n // baseDir via the public `./assets` surface.\n return join(this.baseDir, assertSafeLogicalPath(logicalPath, this.enforceNamespace));\n }\n\n async read(logicalPath: string): Promise<string> {\n const absolute = this.resolve(logicalPath);\n try {\n return await readFile(absolute, \"utf-8\");\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n if (err.code === \"ENOENT\") {\n throw AssetResolutionError.notFound(logicalPath, \"fs\");\n }\n // Reference the logical path + error code only — avoid leaking the absolute\n // host path that a raw FS error message (e.g. EACCES) would carry (secrev).\n throw AssetResolutionError.readFailed(logicalPath, err.code ?? \"unknown\");\n }\n }\n\n async list(patterns: string[]): Promise<string[]> {\n if (patterns.length === 0) {\n return [];\n }\n for (const pattern of patterns) {\n assertSafePattern(pattern, this.enforceNamespace);\n }\n // Patterns are package-root-relative; glob with `cwd: baseDir` and return\n // logical (relative, POSIX) paths so callers never see absolute paths.\n const matches = await glob(patterns, {\n cwd: this.baseDir,\n absolute: false,\n onlyFiles: true,\n followSymbolicLinks: false,\n suppressErrors: true,\n dot: false,\n });\n return matches.map(toPosix).sort();\n }\n\n async has(logicalPath: string): Promise<boolean> {\n let absolute: string;\n try {\n absolute = this.resolve(logicalPath);\n } catch {\n return false; // invalid/traversing path → not present\n }\n try {\n await access(absolute);\n return true;\n } catch {\n return false;\n }\n }\n\n provenance(): AssetProvenance {\n return { mode: \"fs\", baseDir: this.baseDir };\n }\n\n /** The on-disk base directory this resolver reads from. */\n getBaseDir(): string {\n return this.baseDir;\n }\n}\n","/**\n * Asset resolution - Resolver factory & mode selection\n *\n * Chooses between the filesystem and embedded backends. Default is `auto`:\n * prefer the filesystem when the on-disk asset tree is present/readable, else\n * fall back to embedded generated modules (the `bun --compile` case). The mode\n * can be forced with the `TSFULMEN_ASSET_MODE` env var or an explicit option.\n */\n\nimport { accessSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n EmbeddedAssetResolver,\n getRegistrationVersion,\n hasEmbeddedAssets,\n} from \"./embedded-resolver.js\";\nimport { AssetResolutionError } from \"./errors.js\";\nimport { FsAssetResolver } from \"./fs-resolver.js\";\nimport type { AssetMode, AssetResolver } from \"./types.js\";\n\n/**\n * Marker that identifies the package-root asset tree. Used both to locate the\n * base dir by walking upward and to test FS availability for `auto` selection.\n */\nconst ASSET_ROOT_MARKER = join(\"schemas\", \"crucible-ts\");\n\nfunction dirExists(path: string): boolean {\n try {\n accessSync(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Find the package-root directory that contains the SSOT asset trees by walking\n * upward from this module. Works for both source (`src/assets/`) and bundled\n * (`dist/`) layouts without hard-coded depths — and returns `null` inside a\n * `bun --compile` binary where no such directory exists on disk.\n */\nexport function findAssetBaseDir(): string | null {\n let dir: string;\n try {\n dir = dirname(fileURLToPath(import.meta.url));\n } catch {\n return null;\n }\n\n // Walk up to the filesystem root looking for the asset-root marker.\n let prev = \"\";\n while (dir !== prev) {\n if (dirExists(join(dir, ASSET_ROOT_MARKER))) {\n return dir;\n }\n prev = dir;\n dir = dirname(dir);\n }\n return null;\n}\n\nfunction readModeFromEnv(): AssetMode | undefined {\n const raw = process.env.TSFULMEN_ASSET_MODE?.trim().toLowerCase();\n if (raw === \"fs\" || raw === \"embedded\" || raw === \"auto\") {\n return raw;\n }\n return undefined;\n}\n\nexport interface ResolveAssetsOptions {\n /** Force a mode. Precedence: explicit option > `TSFULMEN_ASSET_MODE` > `auto`. */\n mode?: AssetMode;\n /** Override the FS base dir (e.g. a consumer's own asset tree). Implies `fs`. */\n baseDir?: string;\n}\n\n/**\n * Build an {@link AssetResolver} for the requested (or auto-detected) mode.\n * Stateless — callers that want a cached instance use {@link getAssetResolver}.\n */\nexport function resolveAssets(options: ResolveAssetsOptions = {}): AssetResolver {\n if (options.baseDir) {\n // Consumer-supplied tree: they scope it, so don't impose the SSOT namespace\n // (traversal guards still apply). Used by e.g. a custom schema-registry baseDir.\n return new FsAssetResolver(options.baseDir, false);\n }\n\n const requested: AssetMode = options.mode ?? readModeFromEnv() ?? \"auto\";\n\n if (requested === \"fs\") {\n const baseDir = findAssetBaseDir();\n if (!baseDir) {\n throw AssetResolutionError.baseDirUnavailable(\n \"mode=fs but no on-disk asset tree found (use mode=embedded in a compiled binary)\",\n );\n }\n return new FsAssetResolver(baseDir);\n }\n\n if (requested === \"embedded\") {\n return new EmbeddedAssetResolver();\n }\n\n // auto: prefer FS when the asset tree is present, else embedded.\n const baseDir = findAssetBaseDir();\n if (baseDir) {\n return new FsAssetResolver(baseDir);\n }\n if (hasEmbeddedAssets()) {\n return new EmbeddedAssetResolver();\n }\n throw AssetResolutionError.baseDirUnavailable(\n \"auto: no on-disk asset tree and no embedded assets registered\",\n );\n}\n\nlet cached: AssetResolver | undefined;\nlet cachedKey: string | undefined;\n\n/**\n * Cached resolver keyed on the effective options. Most call sites should use\n * this; pass fresh options (or different env) to rebuild.\n */\nexport function getAssetResolver(options: ResolveAssetsOptions = {}): AssetResolver {\n const key = JSON.stringify({\n mode: options.mode ?? readModeFromEnv() ?? \"auto\",\n baseDir: options.baseDir ?? null,\n // Fold in the registration version so a cached embedded resolver rebuilds\n // after a domain is registered (e.g. via ensureEmbeddedDomain).\n reg: getRegistrationVersion(),\n });\n if (!cached || cachedKey !== key) {\n cached = resolveAssets(options);\n cachedKey = key;\n }\n return cached;\n}\n\n/** Reset the cached resolver. For testing / after registering embedded assets. */\nexport function resetAssetResolver(): void {\n cached = undefined;\n cachedKey = undefined;\n}\n\n// Embedded-asset registration is STATIC PER-SUBPATH (entarch-sanctioned fallback;\n// see planning/tsfulmen/v040-shaping.md §0.7). Each feature subpath statically\n// imports + registers only its own domain manifest, so a foundry/schema/taxonomy\n// consumer's published bundle carries only the assets it uses — and the `./assets`\n// core (this module) references NO generated content, keeping dist/assets/index.js\n// lean. (Dynamic per-domain lazy loading would require global tsup `splitting`,\n// which we deliberately avoid.) Cache stays correct because registerEmbeddedAssets\n// bumps a version that getAssetResolver folds into its cache key.\n"]}
|