@fulmenhq/tsfulmen 0.3.0 → 0.3.2
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 +31 -0
- package/config/crucible-ts/repository/app-identity/app-identity.example.yaml +8 -0
- package/config/crucible-ts/repository/app-identity/fixtures/valid/typescript-package.yaml +17 -0
- package/config/crucible-ts/repository/app-identity/parity-snapshot.json +28 -0
- package/config/crucible-ts/taxonomy/metrics.yaml +1 -1
- package/dist/appidentity/index.js +156 -4150
- package/dist/appidentity/index.js.map +1 -1
- package/dist/bin/prometheus-cli.d.ts +1 -0
- package/dist/bin/prometheus-cli.js +9331 -0
- package/dist/bin/prometheus-cli.js.map +1 -0
- package/dist/bin/schema-cli.d.ts +1 -0
- package/dist/bin/schema-cli.js +6104 -0
- package/dist/bin/schema-cli.js.map +1 -0
- package/dist/bin/signals-cli.d.ts +1 -0
- package/dist/bin/signals-cli.js +2104 -0
- package/dist/bin/signals-cli.js.map +1 -0
- package/dist/config/index.js +29 -4915
- package/dist/config/index.js.map +1 -1
- package/dist/crucible/index.js +120 -5336
- package/dist/crucible/index.js.map +1 -1
- package/dist/errors/index.js +52 -4434
- package/dist/errors/index.js.map +1 -1
- package/dist/foundry/index.js +197 -1874
- package/dist/foundry/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +161 -4154
- package/dist/index.js.map +1 -1
- package/dist/pathfinder/index.js +47 -4378
- package/dist/pathfinder/index.js.map +1 -1
- package/dist/reports/license-inventory.csv +2 -2
- package/dist/schema/index.js +0 -4
- package/dist/schema/index.js.map +1 -1
- package/dist/signals/index.js +231 -3570
- package/dist/signals/index.js.map +1 -1
- package/dist/telemetry/http/index.js +94 -5280
- package/dist/telemetry/http/index.js.map +1 -1
- package/dist/telemetry/index.js +70 -4786
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/prometheus/index.js +461 -4450
- package/dist/telemetry/prometheus/index.js.map +1 -1
- package/package.json +8 -2
- package/schemas/crucible-ts/config/repository/app-identity/v1.0.0/app-identity.schema.json +34 -0
package/dist/index.js
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import addFormats from 'ajv-formats';
|
|
2
|
-
import
|
|
3
|
-
import { readFile,
|
|
4
|
-
import { parse,
|
|
5
|
-
import { dirname,
|
|
2
|
+
import 'child_process';
|
|
3
|
+
import { readFile, access } from 'fs/promises';
|
|
4
|
+
import { parse, parseDocument } from 'yaml';
|
|
5
|
+
import { join, dirname, basename, relative, extname } from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import glob from 'fast-glob';
|
|
8
8
|
import Ajv from 'ajv';
|
|
9
9
|
import Ajv2019 from 'ajv/dist/2019.js';
|
|
10
10
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
11
11
|
import AjvDraft04 from 'ajv-draft-04';
|
|
12
|
-
import
|
|
13
|
-
import picomatch from 'picomatch';
|
|
14
|
-
import { suggest as suggest$1, substringSimilarity, score as score$1, normalize as normalize$1, jaro_winkler, damerau_levenshtein, osa_distance, levenshtein } from '@3leaps/string-metrics-wasm';
|
|
12
|
+
import 'commander';
|
|
15
13
|
import 'crypto';
|
|
16
|
-
import { Command } from 'commander';
|
|
17
14
|
import { existsSync, mkdirSync, statSync, createReadStream, createWriteStream, readdirSync, lstatSync } from 'fs';
|
|
15
|
+
import { pipeline } from 'stream';
|
|
18
16
|
import { createGunzip, createGzip } from 'zlib';
|
|
19
17
|
import { TarArchive, ZipArchive } from 'archiver';
|
|
20
18
|
|
|
21
|
-
var __defProp = Object.defineProperty;
|
|
22
19
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
23
20
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
24
21
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
@@ -29,10 +26,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
29
26
|
var __esm = (fn, res) => function __init() {
|
|
30
27
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
31
28
|
};
|
|
32
|
-
var __export = (target, all) => {
|
|
33
|
-
for (var name in all)
|
|
34
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
35
|
-
};
|
|
36
29
|
|
|
37
30
|
// src/appidentity/constants.ts
|
|
38
31
|
var APP_IDENTITY_FILENAME, APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_SCHEMA_ID, MAX_ANCESTOR_SEARCH_DEPTH;
|
|
@@ -68,23 +61,9 @@ var init_ajv_formats = __esm({
|
|
|
68
61
|
});
|
|
69
62
|
|
|
70
63
|
// src/schema/errors.ts
|
|
71
|
-
var
|
|
72
|
-
__export(errors_exports, {
|
|
73
|
-
ExportErrorReason: () => ExportErrorReason,
|
|
74
|
-
SchemaExportError: () => SchemaExportError,
|
|
75
|
-
SchemaValidationError: () => SchemaValidationError
|
|
76
|
-
});
|
|
77
|
-
var ExportErrorReason, SchemaValidationError, SchemaExportError;
|
|
64
|
+
var SchemaValidationError;
|
|
78
65
|
var init_errors = __esm({
|
|
79
66
|
"src/schema/errors.ts"() {
|
|
80
|
-
ExportErrorReason = /* @__PURE__ */ ((ExportErrorReason2) => {
|
|
81
|
-
ExportErrorReason2["FILE_EXISTS"] = "FILE_EXISTS";
|
|
82
|
-
ExportErrorReason2["WRITE_FAILED"] = "WRITE_FAILED";
|
|
83
|
-
ExportErrorReason2["INVALID_FORMAT"] = "INVALID_FORMAT";
|
|
84
|
-
ExportErrorReason2["PROVENANCE_FAILED"] = "PROVENANCE_FAILED";
|
|
85
|
-
ExportErrorReason2["UNKNOWN"] = "UNKNOWN";
|
|
86
|
-
return ExportErrorReason2;
|
|
87
|
-
})(ExportErrorReason || {});
|
|
88
67
|
SchemaValidationError = class _SchemaValidationError extends Error {
|
|
89
68
|
constructor(message, schemaId, diagnostics = [], source, cause) {
|
|
90
69
|
super(message);
|
|
@@ -224,100 +203,10 @@ Source: ${this.source.type}`;
|
|
|
224
203
|
};
|
|
225
204
|
}
|
|
226
205
|
};
|
|
227
|
-
SchemaExportError = class _SchemaExportError extends SchemaValidationError {
|
|
228
|
-
constructor(message, reason, schemaId, outPath, cause) {
|
|
229
|
-
super(message, schemaId, [], void 0, cause);
|
|
230
|
-
this.reason = reason;
|
|
231
|
-
this.schemaId = schemaId;
|
|
232
|
-
this.outPath = outPath;
|
|
233
|
-
this.cause = cause;
|
|
234
|
-
this.name = "SchemaExportError";
|
|
235
|
-
if (Error.captureStackTrace) {
|
|
236
|
-
Error.captureStackTrace(this, _SchemaExportError);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Create error for file already exists
|
|
241
|
-
*/
|
|
242
|
-
static fileExists(outPath) {
|
|
243
|
-
return new _SchemaExportError(
|
|
244
|
-
`Output file already exists: ${outPath}. Use overwrite option to replace.`,
|
|
245
|
-
"FILE_EXISTS" /* FILE_EXISTS */,
|
|
246
|
-
void 0,
|
|
247
|
-
outPath
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Create error for invalid export format
|
|
252
|
-
*/
|
|
253
|
-
static invalidFormat(format, outPath) {
|
|
254
|
-
return new _SchemaExportError(
|
|
255
|
-
`Invalid export format: ${format}. Must be 'json' or 'yaml'.`,
|
|
256
|
-
"INVALID_FORMAT" /* INVALID_FORMAT */,
|
|
257
|
-
void 0,
|
|
258
|
-
outPath
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Create error for write failure
|
|
263
|
-
*/
|
|
264
|
-
static writeFailed(outPath, error) {
|
|
265
|
-
return new _SchemaExportError(
|
|
266
|
-
`Failed to write schema to ${outPath}: ${error.message}`,
|
|
267
|
-
"WRITE_FAILED" /* WRITE_FAILED */,
|
|
268
|
-
void 0,
|
|
269
|
-
outPath,
|
|
270
|
-
error
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* Create error for provenance extraction failure
|
|
275
|
-
*/
|
|
276
|
-
static provenanceFailed(details, error) {
|
|
277
|
-
return new _SchemaExportError(
|
|
278
|
-
`Failed to extract provenance metadata: ${details}`,
|
|
279
|
-
"PROVENANCE_FAILED" /* PROVENANCE_FAILED */,
|
|
280
|
-
void 0,
|
|
281
|
-
void 0,
|
|
282
|
-
error
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
206
|
}
|
|
287
207
|
});
|
|
288
208
|
|
|
289
209
|
// src/schema/utils.ts
|
|
290
|
-
function formatDiagnostics(diagnostics) {
|
|
291
|
-
if (diagnostics.length === 0) {
|
|
292
|
-
return "No validation issues found.";
|
|
293
|
-
}
|
|
294
|
-
const lines = [];
|
|
295
|
-
const errors = diagnostics.filter((d) => d.severity === "ERROR");
|
|
296
|
-
const warnings = diagnostics.filter((d) => d.severity === "WARN");
|
|
297
|
-
if (errors.length > 0) {
|
|
298
|
-
lines.push(`\u274C ${errors.length} error(s) found:`);
|
|
299
|
-
errors.forEach((diag, index) => {
|
|
300
|
-
lines.push(` ${index + 1}. ${diag.message}`);
|
|
301
|
-
if (diag.pointer) {
|
|
302
|
-
lines.push(` at ${diag.pointer}`);
|
|
303
|
-
}
|
|
304
|
-
if (diag.keyword) {
|
|
305
|
-
lines.push(` keyword: ${diag.keyword}`);
|
|
306
|
-
}
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
if (warnings.length > 0) {
|
|
310
|
-
lines.push("");
|
|
311
|
-
lines.push(`\u26A0\uFE0F ${warnings.length} warning(s) found:`);
|
|
312
|
-
warnings.forEach((diag, index) => {
|
|
313
|
-
lines.push(` ${index + 1}. ${diag.message}`);
|
|
314
|
-
if (diag.pointer) {
|
|
315
|
-
lines.push(` at ${diag.pointer}`);
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
return lines.join("\n");
|
|
320
|
-
}
|
|
321
210
|
function createDiagnostic(pointer, message, keyword, severity = "ERROR", source = "ajv", data) {
|
|
322
211
|
return {
|
|
323
212
|
pointer,
|
|
@@ -333,261 +222,27 @@ var init_utils = __esm({
|
|
|
333
222
|
init_errors();
|
|
334
223
|
}
|
|
335
224
|
});
|
|
336
|
-
async function detectGoneat(customPath) {
|
|
337
|
-
if (customPath) {
|
|
338
|
-
try {
|
|
339
|
-
await access(customPath);
|
|
340
|
-
return customPath;
|
|
341
|
-
} catch {
|
|
342
|
-
return null;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
if (process.env.GONEAT_PATH) {
|
|
346
|
-
try {
|
|
347
|
-
await access(process.env.GONEAT_PATH);
|
|
348
|
-
return process.env.GONEAT_PATH;
|
|
349
|
-
} catch {
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
try {
|
|
353
|
-
await access("./bin/goneat");
|
|
354
|
-
return "./bin/goneat";
|
|
355
|
-
} catch {
|
|
356
|
-
}
|
|
357
|
-
return "goneat";
|
|
358
|
-
}
|
|
359
|
-
async function isGoneatAvailable(goneatPath) {
|
|
360
|
-
let pathToTest;
|
|
361
|
-
if (goneatPath) {
|
|
362
|
-
pathToTest = goneatPath;
|
|
363
|
-
} else {
|
|
364
|
-
pathToTest = await detectGoneat();
|
|
365
|
-
if (!pathToTest) return false;
|
|
366
|
-
}
|
|
367
|
-
return new Promise((resolve) => {
|
|
368
|
-
const proc = spawn(pathToTest, ["version"], { stdio: "ignore" });
|
|
369
|
-
const timeout = setTimeout(() => {
|
|
370
|
-
proc.kill();
|
|
371
|
-
resolve(false);
|
|
372
|
-
}, 5e3);
|
|
373
|
-
proc.on("close", (code) => {
|
|
374
|
-
clearTimeout(timeout);
|
|
375
|
-
resolve(code === 0);
|
|
376
|
-
});
|
|
377
|
-
proc.on("error", () => {
|
|
378
|
-
clearTimeout(timeout);
|
|
379
|
-
resolve(false);
|
|
380
|
-
});
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
async function runGoneatValidation(schemaPath, dataPath, goneatPath) {
|
|
384
|
-
const detected = await detectGoneat(goneatPath);
|
|
385
|
-
if (!detected) {
|
|
386
|
-
return {
|
|
387
|
-
valid: false,
|
|
388
|
-
diagnostics: [
|
|
389
|
-
createDiagnostic(
|
|
390
|
-
"",
|
|
391
|
-
"goneat binary not found. Install goneat or specify path with --goneat-path",
|
|
392
|
-
"goneat-unavailable",
|
|
393
|
-
"ERROR",
|
|
394
|
-
"goneat"
|
|
395
|
-
)
|
|
396
|
-
],
|
|
397
|
-
source: "goneat"
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
if (!await isGoneatAvailable(detected)) {
|
|
401
|
-
return {
|
|
402
|
-
valid: false,
|
|
403
|
-
diagnostics: [
|
|
404
|
-
createDiagnostic(
|
|
405
|
-
"",
|
|
406
|
-
`goneat binary found at '${detected}' but not executable or version check failed`,
|
|
407
|
-
"goneat-not-executable",
|
|
408
|
-
"ERROR",
|
|
409
|
-
"goneat"
|
|
410
|
-
)
|
|
411
|
-
],
|
|
412
|
-
source: "goneat"
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
return new Promise((resolve) => {
|
|
416
|
-
const args = [
|
|
417
|
-
"schema",
|
|
418
|
-
"validate",
|
|
419
|
-
"--schema",
|
|
420
|
-
schemaPath,
|
|
421
|
-
"--data",
|
|
422
|
-
dataPath,
|
|
423
|
-
"--format",
|
|
424
|
-
"json"
|
|
425
|
-
];
|
|
426
|
-
const proc = spawn(detected, args);
|
|
427
|
-
let stdout = "";
|
|
428
|
-
let stderr = "";
|
|
429
|
-
proc.stdout.on("data", (data) => {
|
|
430
|
-
stdout += data.toString();
|
|
431
|
-
});
|
|
432
|
-
proc.stderr.on("data", (data) => {
|
|
433
|
-
stderr += data.toString();
|
|
434
|
-
});
|
|
435
|
-
proc.on("close", (code) => {
|
|
436
|
-
let output;
|
|
437
|
-
try {
|
|
438
|
-
output = JSON.parse(stdout);
|
|
439
|
-
} catch {
|
|
440
|
-
resolve({
|
|
441
|
-
valid: false,
|
|
442
|
-
diagnostics: [
|
|
443
|
-
createDiagnostic(
|
|
444
|
-
"",
|
|
445
|
-
`goneat validation failed: ${stderr || "unknown error"}`,
|
|
446
|
-
"goneat-error",
|
|
447
|
-
"ERROR",
|
|
448
|
-
"goneat"
|
|
449
|
-
)
|
|
450
|
-
],
|
|
451
|
-
source: "goneat"
|
|
452
|
-
});
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
const diagnostics = output.errors?.map(
|
|
456
|
-
(error) => createDiagnostic(
|
|
457
|
-
error.path || "",
|
|
458
|
-
error.message,
|
|
459
|
-
error.keyword || "validation",
|
|
460
|
-
"ERROR",
|
|
461
|
-
"goneat"
|
|
462
|
-
)
|
|
463
|
-
) || [];
|
|
464
|
-
resolve({
|
|
465
|
-
valid: code === 0 && output.valid,
|
|
466
|
-
diagnostics,
|
|
467
|
-
source: "goneat"
|
|
468
|
-
});
|
|
469
|
-
});
|
|
470
|
-
proc.on("error", (error) => {
|
|
471
|
-
resolve({
|
|
472
|
-
valid: false,
|
|
473
|
-
diagnostics: [
|
|
474
|
-
createDiagnostic(
|
|
475
|
-
"",
|
|
476
|
-
`Failed to execute goneat: ${error.message}`,
|
|
477
|
-
"goneat-spawn-error",
|
|
478
|
-
"ERROR",
|
|
479
|
-
"goneat"
|
|
480
|
-
)
|
|
481
|
-
],
|
|
482
|
-
source: "goneat"
|
|
483
|
-
});
|
|
484
|
-
});
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
225
|
var init_goneat_bridge = __esm({
|
|
488
226
|
"src/schema/goneat-bridge.ts"() {
|
|
489
227
|
init_utils();
|
|
490
228
|
}
|
|
491
229
|
});
|
|
492
|
-
function parseSchemaInput(input) {
|
|
493
|
-
if (!input) {
|
|
494
|
-
throw SchemaValidationError.parseFailed(
|
|
495
|
-
{ type: "string", content: "" },
|
|
496
|
-
new Error("schema content is empty")
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
try {
|
|
500
|
-
if (typeof input === "string") {
|
|
501
|
-
try {
|
|
502
|
-
return JSON.parse(input);
|
|
503
|
-
} catch {
|
|
504
|
-
return parse(input);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
if (Buffer.isBuffer(input)) {
|
|
508
|
-
const content = input.toString("utf-8");
|
|
509
|
-
try {
|
|
510
|
-
return JSON.parse(content);
|
|
511
|
-
} catch {
|
|
512
|
-
return parse(content);
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
return input;
|
|
516
|
-
} catch (error) {
|
|
517
|
-
throw SchemaValidationError.parseFailed(
|
|
518
|
-
{
|
|
519
|
-
type: typeof input === "string" ? "string" : "object",
|
|
520
|
-
content: typeof input === "string" ? input : JSON.stringify(input)
|
|
521
|
-
},
|
|
522
|
-
error
|
|
523
|
-
);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
function sortObjectKeys(obj) {
|
|
527
|
-
if (obj === null || typeof obj !== "object") {
|
|
528
|
-
return obj;
|
|
529
|
-
}
|
|
530
|
-
if (Array.isArray(obj)) {
|
|
531
|
-
return obj.map(sortObjectKeys);
|
|
532
|
-
}
|
|
533
|
-
const sorted = {};
|
|
534
|
-
const keys = Object.keys(obj).sort();
|
|
535
|
-
for (const key of keys) {
|
|
536
|
-
sorted[key] = sortObjectKeys(obj[key]);
|
|
537
|
-
}
|
|
538
|
-
return sorted;
|
|
539
|
-
}
|
|
540
|
-
function normalizeSchema(input, options = {}) {
|
|
541
|
-
try {
|
|
542
|
-
const parsed = parseSchemaInput(input);
|
|
543
|
-
const sorted = sortObjectKeys(parsed);
|
|
544
|
-
if (options.compact) {
|
|
545
|
-
return JSON.stringify(sorted);
|
|
546
|
-
}
|
|
547
|
-
return JSON.stringify(sorted, null, 2);
|
|
548
|
-
} catch (error) {
|
|
549
|
-
if (error instanceof SchemaValidationError) {
|
|
550
|
-
throw error;
|
|
551
|
-
}
|
|
552
|
-
throw SchemaValidationError.parseFailed(
|
|
553
|
-
{
|
|
554
|
-
type: typeof input === "string" ? "string" : "object",
|
|
555
|
-
content: typeof input === "string" ? input : JSON.stringify(input)
|
|
556
|
-
},
|
|
557
|
-
error
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
function compareSchemas(schemaA, schemaB, options = {}) {
|
|
562
|
-
const normalizedA = normalizeSchema(schemaA, options);
|
|
563
|
-
const normalizedB = normalizeSchema(schemaB, options);
|
|
564
|
-
return {
|
|
565
|
-
equal: normalizedA === normalizedB,
|
|
566
|
-
normalizedA,
|
|
567
|
-
normalizedB
|
|
568
|
-
};
|
|
569
|
-
}
|
|
570
230
|
var init_normalizer = __esm({
|
|
571
231
|
"src/schema/normalizer.ts"() {
|
|
572
232
|
init_errors();
|
|
573
233
|
}
|
|
574
234
|
});
|
|
575
235
|
function optionsChanged(newOptions) {
|
|
576
|
-
if (!
|
|
577
|
-
|
|
578
|
-
return newOptions.baseDir !== globalRegistryOptions.baseDir || JSON.stringify(newOptions.patterns) !== JSON.stringify(globalRegistryOptions.patterns) || newOptions.followSymlinks !== globalRegistryOptions.followSymlinks || newOptions.maxDepth !== globalRegistryOptions.maxDepth;
|
|
236
|
+
if (!globalRegistryOptions) return false;
|
|
237
|
+
return true;
|
|
579
238
|
}
|
|
580
239
|
function getSchemaRegistry(options) {
|
|
581
|
-
if (!globalRegistry || optionsChanged(
|
|
240
|
+
if (!globalRegistry || optionsChanged()) {
|
|
582
241
|
globalRegistry = new SchemaRegistry(options);
|
|
583
242
|
globalRegistryOptions = options;
|
|
584
243
|
}
|
|
585
244
|
return globalRegistry;
|
|
586
245
|
}
|
|
587
|
-
async function listSchemas(prefix, options) {
|
|
588
|
-
const registry = getSchemaRegistry(options);
|
|
589
|
-
return registry.listSchemas(prefix);
|
|
590
|
-
}
|
|
591
246
|
var DEFAULT_PATTERNS, SchemaRegistry, globalRegistry, globalRegistryOptions;
|
|
592
247
|
var init_registry = __esm({
|
|
593
248
|
"src/schema/registry.ts"() {
|
|
@@ -608,9 +263,9 @@ var init_registry = __esm({
|
|
|
608
263
|
* Get default schema directory using import.meta.url
|
|
609
264
|
*/
|
|
610
265
|
getDefaultSchemaDir() {
|
|
611
|
-
const
|
|
612
|
-
const
|
|
613
|
-
return join(
|
|
266
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
267
|
+
const __dirname2 = dirname(__filename);
|
|
268
|
+
return join(__dirname2, "..", "..", "schemas", "crucible-ts");
|
|
614
269
|
}
|
|
615
270
|
/**
|
|
616
271
|
* Build logical schema ID from file path
|
|
@@ -1498,25 +1153,11 @@ var init_telemetry = __esm({
|
|
|
1498
1153
|
metrics = new MetricsRegistry();
|
|
1499
1154
|
}
|
|
1500
1155
|
});
|
|
1501
|
-
|
|
1502
|
-
// src/schema/validator.ts
|
|
1503
|
-
var validator_exports = {};
|
|
1504
|
-
__export(validator_exports, {
|
|
1505
|
-
clearCache: () => clearCache,
|
|
1506
|
-
compileSchema: () => compileSchema,
|
|
1507
|
-
compileSchemaById: () => compileSchemaById,
|
|
1508
|
-
getCacheSize: () => getCacheSize,
|
|
1509
|
-
validateData: () => validateData,
|
|
1510
|
-
validateDataBySchemaId: () => validateDataBySchemaId,
|
|
1511
|
-
validateFile: () => validateFile,
|
|
1512
|
-
validateFileBySchemaId: () => validateFileBySchemaId,
|
|
1513
|
-
validateSchema: () => validateSchema
|
|
1514
|
-
});
|
|
1515
1156
|
async function loadMetaSchema(draft) {
|
|
1516
|
-
const
|
|
1517
|
-
const
|
|
1157
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
1158
|
+
const __dirname2 = dirname(__filename);
|
|
1518
1159
|
const metaSchemaPath = join(
|
|
1519
|
-
|
|
1160
|
+
__dirname2,
|
|
1520
1161
|
"..",
|
|
1521
1162
|
"..",
|
|
1522
1163
|
"schemas",
|
|
@@ -1532,9 +1173,9 @@ async function loadVocabularySchemas(draft) {
|
|
|
1532
1173
|
if (draft !== "draft-2019-09" && draft !== "draft-2020-12") {
|
|
1533
1174
|
return [];
|
|
1534
1175
|
}
|
|
1535
|
-
const
|
|
1536
|
-
const
|
|
1537
|
-
const vocabDir = join(
|
|
1176
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
1177
|
+
const __dirname2 = dirname(__filename);
|
|
1178
|
+
const vocabDir = join(__dirname2, "..", "..", "schemas", "crucible-ts", "meta", draft, "meta");
|
|
1538
1179
|
const vocabFiles = draft === "draft-2020-12" ? [
|
|
1539
1180
|
"core.json",
|
|
1540
1181
|
"applicator.json",
|
|
@@ -1562,9 +1203,9 @@ async function loadVocabularySchemas(draft) {
|
|
|
1562
1203
|
return schemas;
|
|
1563
1204
|
}
|
|
1564
1205
|
async function loadReferencedSchema(uri) {
|
|
1565
|
-
const
|
|
1566
|
-
const
|
|
1567
|
-
const repoRoot = join(
|
|
1206
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
1207
|
+
const __dirname2 = dirname(__filename);
|
|
1208
|
+
const repoRoot = join(__dirname2, "..", "..");
|
|
1568
1209
|
let resolvedPath;
|
|
1569
1210
|
if (uri.startsWith("https://schemas.fulmenhq.dev/")) {
|
|
1570
1211
|
let relativePath = uri.replace("https://schemas.fulmenhq.dev/", "");
|
|
@@ -1739,104 +1380,6 @@ function validateData(data, validator) {
|
|
|
1739
1380
|
}
|
|
1740
1381
|
return result;
|
|
1741
1382
|
}
|
|
1742
|
-
async function validateFile(filePath, validator) {
|
|
1743
|
-
try {
|
|
1744
|
-
const content = await readFile(filePath, "utf-8");
|
|
1745
|
-
let data;
|
|
1746
|
-
try {
|
|
1747
|
-
data = JSON.parse(content);
|
|
1748
|
-
} catch {
|
|
1749
|
-
data = parse(content);
|
|
1750
|
-
}
|
|
1751
|
-
return validateData(data, validator);
|
|
1752
|
-
} catch (error) {
|
|
1753
|
-
if (error instanceof SchemaValidationError) {
|
|
1754
|
-
throw error;
|
|
1755
|
-
}
|
|
1756
|
-
throw SchemaValidationError.validationFailed(
|
|
1757
|
-
filePath,
|
|
1758
|
-
[
|
|
1759
|
-
createDiagnostic(
|
|
1760
|
-
"",
|
|
1761
|
-
`Failed to read or parse file: ${error.message}`,
|
|
1762
|
-
"file-read",
|
|
1763
|
-
"ERROR",
|
|
1764
|
-
"ajv"
|
|
1765
|
-
)
|
|
1766
|
-
],
|
|
1767
|
-
{ type: "file", id: filePath }
|
|
1768
|
-
);
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
async function validateSchema(schema) {
|
|
1772
|
-
try {
|
|
1773
|
-
let parsedSchema;
|
|
1774
|
-
if (typeof schema === "string") {
|
|
1775
|
-
try {
|
|
1776
|
-
parsedSchema = JSON.parse(schema);
|
|
1777
|
-
} catch {
|
|
1778
|
-
parsedSchema = parse(schema);
|
|
1779
|
-
}
|
|
1780
|
-
} else if (Buffer.isBuffer(schema)) {
|
|
1781
|
-
const content = schema.toString("utf-8");
|
|
1782
|
-
try {
|
|
1783
|
-
parsedSchema = JSON.parse(content);
|
|
1784
|
-
} catch {
|
|
1785
|
-
parsedSchema = parse(content);
|
|
1786
|
-
}
|
|
1787
|
-
} else {
|
|
1788
|
-
parsedSchema = schema;
|
|
1789
|
-
}
|
|
1790
|
-
const dialect = detectDialect(parsedSchema);
|
|
1791
|
-
const ajv = await getAjv(dialect);
|
|
1792
|
-
const metaValid = ajv.validateSchema(parsedSchema);
|
|
1793
|
-
if (!metaValid && ajv.errors) {
|
|
1794
|
-
const diagnostics = ajv.errors.map(
|
|
1795
|
-
(error) => createDiagnostic(
|
|
1796
|
-
error.instancePath || "",
|
|
1797
|
-
error.message || "Schema meta-validation failed",
|
|
1798
|
-
error.keyword || "unknown",
|
|
1799
|
-
"ERROR",
|
|
1800
|
-
"ajv"
|
|
1801
|
-
)
|
|
1802
|
-
);
|
|
1803
|
-
return { valid: false, diagnostics, source: "ajv" };
|
|
1804
|
-
}
|
|
1805
|
-
await compileSchema(parsedSchema);
|
|
1806
|
-
return {
|
|
1807
|
-
valid: true,
|
|
1808
|
-
diagnostics: [],
|
|
1809
|
-
source: "ajv"
|
|
1810
|
-
};
|
|
1811
|
-
} catch (error) {
|
|
1812
|
-
if (error instanceof SchemaValidationError) {
|
|
1813
|
-
return {
|
|
1814
|
-
valid: false,
|
|
1815
|
-
diagnostics: error.diagnostics,
|
|
1816
|
-
source: "ajv"
|
|
1817
|
-
};
|
|
1818
|
-
}
|
|
1819
|
-
return {
|
|
1820
|
-
valid: false,
|
|
1821
|
-
diagnostics: [
|
|
1822
|
-
createDiagnostic(
|
|
1823
|
-
"",
|
|
1824
|
-
`Schema validation failed: ${error.message}`,
|
|
1825
|
-
"schema-validation",
|
|
1826
|
-
"ERROR",
|
|
1827
|
-
"ajv"
|
|
1828
|
-
)
|
|
1829
|
-
],
|
|
1830
|
-
source: "ajv"
|
|
1831
|
-
};
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
function clearCache() {
|
|
1835
|
-
schemaCache.clear();
|
|
1836
|
-
}
|
|
1837
|
-
function getCacheSize() {
|
|
1838
|
-
return schemaCache.size;
|
|
1839
|
-
}
|
|
1840
1383
|
async function compileSchemaById(schemaId, registryOptions) {
|
|
1841
1384
|
try {
|
|
1842
1385
|
const registry = getSchemaRegistry(registryOptions);
|
|
@@ -1864,15 +1407,6 @@ async function validateDataBySchemaId(data, schemaId, registryOptions) {
|
|
|
1864
1407
|
throw error;
|
|
1865
1408
|
}
|
|
1866
1409
|
}
|
|
1867
|
-
async function validateFileBySchemaId(filePath, schemaId, registryOptions) {
|
|
1868
|
-
try {
|
|
1869
|
-
const validator = await compileSchemaById(schemaId, registryOptions);
|
|
1870
|
-
return validateFile(filePath, validator);
|
|
1871
|
-
} catch (error) {
|
|
1872
|
-
metrics.counter("schema_validation_errors").inc();
|
|
1873
|
-
throw error;
|
|
1874
|
-
}
|
|
1875
|
-
}
|
|
1876
1410
|
var ajvInstances, metaschemaReady, schemaCache;
|
|
1877
1411
|
var init_validator = __esm({
|
|
1878
1412
|
"src/schema/validator.ts"() {
|
|
@@ -1886,174 +1420,15 @@ var init_validator = __esm({
|
|
|
1886
1420
|
schemaCache = /* @__PURE__ */ new Map();
|
|
1887
1421
|
}
|
|
1888
1422
|
});
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
async function extractProvenanceMetadata(schemaId) {
|
|
1897
|
-
try {
|
|
1898
|
-
const __filename3 = fileURLToPath(import.meta.url);
|
|
1899
|
-
const __dirname4 = dirname(__filename3);
|
|
1900
|
-
const metadataPath = join(__dirname4, "..", "..", ".crucible", "metadata", "metadata.yaml");
|
|
1901
|
-
const metadataContent = await readFile(metadataPath, "utf-8");
|
|
1902
|
-
const metadata = parse(metadataContent);
|
|
1903
|
-
const crucibleSource = metadata.sources?.[0];
|
|
1904
|
-
const crucibleVersion = crucibleSource?.version || "unknown";
|
|
1905
|
-
const revision = crucibleSource?.commit;
|
|
1906
|
-
const pkgPath = join(__dirname4, "..", "..", "package.json");
|
|
1907
|
-
const pkgContent = await readFile(pkgPath, "utf-8");
|
|
1908
|
-
const pkg = JSON.parse(pkgContent);
|
|
1909
|
-
return {
|
|
1910
|
-
schema_id: schemaId,
|
|
1911
|
-
crucible_version: crucibleVersion,
|
|
1912
|
-
library_version: pkg.version,
|
|
1913
|
-
revision,
|
|
1914
|
-
exported_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1915
|
-
export_source: "tsfulmen"
|
|
1916
|
-
};
|
|
1917
|
-
} catch (error) {
|
|
1918
|
-
throw SchemaExportError.provenanceFailed(error.message, error);
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
function embedProvenance(schemaContent, provenance, format) {
|
|
1922
|
-
if (format === "json") {
|
|
1923
|
-
const withProvenance = {
|
|
1924
|
-
...schemaContent,
|
|
1925
|
-
$comment: {
|
|
1926
|
-
...typeof schemaContent.$comment === "object" ? schemaContent.$comment : {},
|
|
1927
|
-
"x-crucible-source": provenance
|
|
1928
|
-
}
|
|
1929
|
-
};
|
|
1930
|
-
return JSON.stringify(withProvenance, null, 2);
|
|
1931
|
-
}
|
|
1932
|
-
const yamlContent = stringify(schemaContent, {
|
|
1933
|
-
indent: 2,
|
|
1934
|
-
lineWidth: 0
|
|
1935
|
-
});
|
|
1936
|
-
const provenanceComment = [
|
|
1937
|
-
"# x-crucible-source:",
|
|
1938
|
-
`# schema_id: ${provenance.schema_id}`,
|
|
1939
|
-
`# crucible_version: ${provenance.crucible_version}`,
|
|
1940
|
-
`# library_version: ${provenance.library_version}`,
|
|
1941
|
-
...provenance.revision ? [`# revision: ${provenance.revision}`] : [],
|
|
1942
|
-
`# exported_at: ${provenance.exported_at}`,
|
|
1943
|
-
`# export_source: ${provenance.export_source}`,
|
|
1944
|
-
""
|
|
1945
|
-
].join("\n");
|
|
1946
|
-
return provenanceComment + yamlContent;
|
|
1947
|
-
}
|
|
1948
|
-
function detectFormat(outPath, formatOption) {
|
|
1949
|
-
if (formatOption && formatOption !== "auto") {
|
|
1950
|
-
return formatOption;
|
|
1951
|
-
}
|
|
1952
|
-
const ext = extname(outPath).toLowerCase();
|
|
1953
|
-
switch (ext) {
|
|
1954
|
-
case ".json":
|
|
1955
|
-
return "json";
|
|
1956
|
-
case ".yaml":
|
|
1957
|
-
case ".yml":
|
|
1958
|
-
return "yaml";
|
|
1959
|
-
default:
|
|
1960
|
-
throw SchemaExportError.invalidFormat(ext, outPath);
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
async function exportSchema(options) {
|
|
1964
|
-
const {
|
|
1965
|
-
schemaId,
|
|
1966
|
-
outPath,
|
|
1967
|
-
includeProvenance = true,
|
|
1968
|
-
validate = true,
|
|
1969
|
-
overwrite = false,
|
|
1970
|
-
format: formatOption,
|
|
1971
|
-
baseDir
|
|
1972
|
-
} = options;
|
|
1973
|
-
const format = detectFormat(outPath, formatOption);
|
|
1974
|
-
if (!overwrite) {
|
|
1975
|
-
try {
|
|
1976
|
-
await access(outPath);
|
|
1977
|
-
throw SchemaExportError.fileExists(outPath);
|
|
1978
|
-
} catch (error) {
|
|
1979
|
-
if (error.code !== "ENOENT") {
|
|
1980
|
-
throw error;
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
const registry = getSchemaRegistry({ baseDir });
|
|
1985
|
-
const schema = await registry.getSchema(schemaId);
|
|
1986
|
-
const schemaContent = await readFile(schema.path, "utf-8");
|
|
1987
|
-
if (validate) {
|
|
1988
|
-
const validationResult = await validateSchema(schemaContent);
|
|
1989
|
-
if (!validationResult.valid) {
|
|
1990
|
-
throw SchemaValidationError.validationFailed(schemaId, validationResult.diagnostics, {
|
|
1991
|
-
type: "file",
|
|
1992
|
-
id: schema.path
|
|
1993
|
-
});
|
|
1994
|
-
}
|
|
1995
|
-
}
|
|
1996
|
-
let schemaObject;
|
|
1997
|
-
try {
|
|
1998
|
-
schemaObject = JSON.parse(schemaContent);
|
|
1999
|
-
} catch {
|
|
2000
|
-
schemaObject = parse(schemaContent);
|
|
2001
|
-
}
|
|
2002
|
-
Object.freeze(schemaObject);
|
|
2003
|
-
let provenance;
|
|
2004
|
-
let outputContent;
|
|
2005
|
-
if (includeProvenance) {
|
|
2006
|
-
provenance = await extractProvenanceMetadata(schemaId);
|
|
2007
|
-
outputContent = embedProvenance(schemaObject, provenance, format);
|
|
2008
|
-
} else {
|
|
2009
|
-
if (format === "json") {
|
|
2010
|
-
outputContent = JSON.stringify(schemaObject, null, 2);
|
|
2011
|
-
} else {
|
|
2012
|
-
outputContent = stringify(schemaObject, { indent: 2, lineWidth: 0 });
|
|
2013
|
-
}
|
|
2014
|
-
}
|
|
2015
|
-
await mkdir(dirname(outPath), { recursive: true });
|
|
2016
|
-
try {
|
|
2017
|
-
await writeFile(outPath, outputContent, "utf-8");
|
|
2018
|
-
} catch (error) {
|
|
2019
|
-
throw SchemaExportError.writeFailed(outPath, error);
|
|
2020
|
-
}
|
|
2021
|
-
return {
|
|
2022
|
-
success: true,
|
|
2023
|
-
schemaId,
|
|
2024
|
-
outPath,
|
|
2025
|
-
format,
|
|
2026
|
-
includeProvenance,
|
|
2027
|
-
provenance
|
|
2028
|
-
};
|
|
2029
|
-
}
|
|
2030
|
-
function stripProvenance(content) {
|
|
2031
|
-
try {
|
|
2032
|
-
const parsed = JSON.parse(content);
|
|
2033
|
-
if (parsed.$comment && typeof parsed.$comment === "object") {
|
|
2034
|
-
const comment = { ...parsed.$comment };
|
|
2035
|
-
delete comment["x-crucible-source"];
|
|
2036
|
-
if (Object.keys(comment).length === 0) {
|
|
2037
|
-
delete parsed.$comment;
|
|
2038
|
-
} else {
|
|
2039
|
-
parsed.$comment = comment;
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
return JSON.stringify(parsed, null, 2);
|
|
2043
|
-
} catch {
|
|
2044
|
-
const lines = content.split("\n");
|
|
2045
|
-
const filtered = lines.filter((line) => {
|
|
2046
|
-
const trimmed = line.trim();
|
|
2047
|
-
return !(trimmed.startsWith("# x-crucible-source:") || trimmed.startsWith("# ") && /^#\s+(schema_id|crucible_version|library_version|revision|exported_at|export_source):/.test(
|
|
2048
|
-
trimmed
|
|
2049
|
-
));
|
|
2050
|
-
});
|
|
2051
|
-
while (filtered.length > 0 && filtered[0]?.trim() === "") {
|
|
2052
|
-
filtered.shift();
|
|
2053
|
-
}
|
|
2054
|
-
return filtered.join("\n");
|
|
1423
|
+
var init_cli = __esm({
|
|
1424
|
+
"src/schema/cli.ts"() {
|
|
1425
|
+
init_goneat_bridge();
|
|
1426
|
+
init_normalizer();
|
|
1427
|
+
init_registry();
|
|
1428
|
+
init_utils();
|
|
1429
|
+
init_validator();
|
|
2055
1430
|
}
|
|
2056
|
-
}
|
|
1431
|
+
});
|
|
2057
1432
|
var init_export = __esm({
|
|
2058
1433
|
"src/schema/export.ts"() {
|
|
2059
1434
|
init_errors();
|
|
@@ -2062,3112 +1437,18 @@ var init_export = __esm({
|
|
|
2062
1437
|
}
|
|
2063
1438
|
});
|
|
2064
1439
|
|
|
2065
|
-
// src/
|
|
2066
|
-
var
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
|
-
static invalidSchema(catalog, details, cause) {
|
|
2080
|
-
return new _FoundryCatalogError(
|
|
2081
|
-
`Invalid schema in ${catalog} catalog: ${details}`,
|
|
2082
|
-
catalog,
|
|
2083
|
-
cause
|
|
2084
|
-
);
|
|
2085
|
-
}
|
|
2086
|
-
static missingCatalog(catalog) {
|
|
2087
|
-
return new _FoundryCatalogError(`Catalog ${catalog} not found or could not be loaded`, catalog);
|
|
2088
|
-
}
|
|
2089
|
-
static invalidPattern(patternId, details) {
|
|
2090
|
-
return new _FoundryCatalogError(`Invalid pattern ${patternId}: ${details}`, "patterns");
|
|
2091
|
-
}
|
|
2092
|
-
static compilationError(patternId, details, cause) {
|
|
2093
|
-
return new _FoundryCatalogError(
|
|
2094
|
-
`Failed to compile pattern ${patternId}: ${details}`,
|
|
2095
|
-
"patterns",
|
|
2096
|
-
cause
|
|
2097
|
-
);
|
|
2098
|
-
}
|
|
2099
|
-
};
|
|
2100
|
-
}
|
|
2101
|
-
});
|
|
2102
|
-
async function loadCatalog(filePath, catalogName, schemaId) {
|
|
2103
|
-
try {
|
|
2104
|
-
let content;
|
|
2105
|
-
if (typeof Bun !== "undefined") {
|
|
2106
|
-
try {
|
|
2107
|
-
const file = Bun.file(filePath);
|
|
2108
|
-
if (!await file.exists()) {
|
|
2109
|
-
throw FoundryCatalogError.missingCatalog(catalogName);
|
|
2110
|
-
}
|
|
2111
|
-
content = await file.text();
|
|
2112
|
-
} catch (error) {
|
|
2113
|
-
if (error instanceof Error && error.message.includes("No such file")) {
|
|
2114
|
-
throw FoundryCatalogError.missingCatalog(catalogName);
|
|
2115
|
-
}
|
|
2116
|
-
throw error;
|
|
2117
|
-
}
|
|
2118
|
-
} else {
|
|
2119
|
-
try {
|
|
2120
|
-
content = await readFile(filePath, "utf-8");
|
|
2121
|
-
} catch (error) {
|
|
2122
|
-
if (error.code === "ENOENT") {
|
|
2123
|
-
throw FoundryCatalogError.missingCatalog(catalogName);
|
|
2124
|
-
}
|
|
2125
|
-
throw error;
|
|
2126
|
-
}
|
|
2127
|
-
}
|
|
2128
|
-
const data = parse(content);
|
|
2129
|
-
const result = await validateDataBySchemaId(data, schemaId);
|
|
2130
|
-
if (!result.valid) {
|
|
2131
|
-
const errorMessages = result.diagnostics.map((d) => `${d.pointer}: ${d.message}`).join("; ");
|
|
2132
|
-
throw FoundryCatalogError.invalidSchema(
|
|
2133
|
-
catalogName,
|
|
2134
|
-
`Schema validation failed: ${errorMessages}`
|
|
2135
|
-
);
|
|
2136
|
-
}
|
|
2137
|
-
return data;
|
|
2138
|
-
} catch (error) {
|
|
2139
|
-
if (error instanceof FoundryCatalogError) {
|
|
2140
|
-
throw error;
|
|
2141
|
-
}
|
|
2142
|
-
const err = error;
|
|
2143
|
-
if (err.code === "ENOENT") {
|
|
2144
|
-
throw FoundryCatalogError.missingCatalog(catalogName);
|
|
2145
|
-
} else if (err.code === "EACCES") {
|
|
2146
|
-
throw FoundryCatalogError.invalidSchema(
|
|
2147
|
-
catalogName,
|
|
2148
|
-
"Permission denied accessing catalog file",
|
|
2149
|
-
err
|
|
2150
|
-
);
|
|
2151
|
-
} else if (err.code === "EISDIR") {
|
|
2152
|
-
throw FoundryCatalogError.invalidSchema(
|
|
2153
|
-
catalogName,
|
|
2154
|
-
"Expected file but found directory",
|
|
2155
|
-
err
|
|
2156
|
-
);
|
|
2157
|
-
} else if (err.code === "EMFILE" || err.code === "ENFILE") {
|
|
2158
|
-
throw FoundryCatalogError.invalidSchema(catalogName, "Too many open files", err);
|
|
2159
|
-
}
|
|
2160
|
-
throw FoundryCatalogError.invalidSchema(catalogName, "Failed to load catalog", err);
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
async function loadPatternCatalog() {
|
|
2164
|
-
return loadCatalog(SSOT_PATHS.patterns, "patterns", SCHEMA_IDS.patterns);
|
|
2165
|
-
}
|
|
2166
|
-
async function loadHttpStatusCatalog() {
|
|
2167
|
-
return loadCatalog(
|
|
2168
|
-
SSOT_PATHS.httpStatuses,
|
|
2169
|
-
"httpStatuses",
|
|
2170
|
-
SCHEMA_IDS.httpStatuses
|
|
2171
|
-
);
|
|
2172
|
-
}
|
|
2173
|
-
async function loadMimeTypeCatalog() {
|
|
2174
|
-
return loadCatalog(SSOT_PATHS.mimeTypes, "mimeTypes", SCHEMA_IDS.mimeTypes);
|
|
2175
|
-
}
|
|
2176
|
-
async function loadCountryCodeCatalog() {
|
|
2177
|
-
return loadCatalog(
|
|
2178
|
-
SSOT_PATHS.countryCodes,
|
|
2179
|
-
"countryCodes",
|
|
2180
|
-
SCHEMA_IDS.countryCodes
|
|
2181
|
-
);
|
|
2182
|
-
}
|
|
2183
|
-
async function loadAllCatalogs() {
|
|
2184
|
-
const [patterns, httpStatuses, mimeTypes, countryCodes] = await Promise.all([
|
|
2185
|
-
loadPatternCatalog(),
|
|
2186
|
-
loadHttpStatusCatalog(),
|
|
2187
|
-
loadMimeTypeCatalog(),
|
|
2188
|
-
loadCountryCodeCatalog()
|
|
2189
|
-
]);
|
|
2190
|
-
return { patterns, httpStatuses, mimeTypes, countryCodes };
|
|
2191
|
-
}
|
|
2192
|
-
var __filename, __dirname2, SSOT_PATHS, SCHEMA_IDS;
|
|
2193
|
-
var init_loader = __esm({
|
|
2194
|
-
"src/foundry/loader.ts"() {
|
|
2195
|
-
init_validator();
|
|
2196
|
-
init_errors2();
|
|
2197
|
-
__filename = fileURLToPath(import.meta.url);
|
|
2198
|
-
__dirname2 = dirname(__filename);
|
|
2199
|
-
SSOT_PATHS = {
|
|
2200
|
-
patterns: join(__dirname2, "../../config/crucible-ts/library/foundry/patterns.yaml"),
|
|
2201
|
-
httpStatuses: join(__dirname2, "../../config/crucible-ts/library/foundry/http-statuses.yaml"),
|
|
2202
|
-
mimeTypes: join(__dirname2, "../../config/crucible-ts/library/foundry/mime-types.yaml"),
|
|
2203
|
-
countryCodes: join(__dirname2, "../../config/crucible-ts/library/foundry/country-codes.yaml")
|
|
2204
|
-
};
|
|
2205
|
-
SCHEMA_IDS = {
|
|
2206
|
-
patterns: "library/foundry/v1.0.0/patterns",
|
|
2207
|
-
httpStatuses: "library/foundry/v1.0.0/http-status-groups",
|
|
2208
|
-
mimeTypes: "library/foundry/v1.0.0/mime-types",
|
|
2209
|
-
countryCodes: "library/foundry/v1.0.0/country-codes"
|
|
2210
|
-
};
|
|
2211
|
-
}
|
|
2212
|
-
});
|
|
2213
|
-
|
|
2214
|
-
// src/foundry/country-codes.ts
|
|
2215
|
-
function deepClone(obj) {
|
|
2216
|
-
if (obj === null || typeof obj !== "object") {
|
|
2217
|
-
return obj;
|
|
2218
|
-
}
|
|
2219
|
-
if (Array.isArray(obj)) {
|
|
2220
|
-
return obj.map((item) => deepClone(item));
|
|
2221
|
-
}
|
|
2222
|
-
const cloned = {};
|
|
2223
|
-
for (const key in obj) {
|
|
2224
|
-
if (Object.hasOwn(obj, key)) {
|
|
2225
|
-
cloned[key] = deepClone(obj[key]);
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
return cloned;
|
|
2229
|
-
}
|
|
2230
|
-
function deepFreeze(obj) {
|
|
2231
|
-
Object.freeze(obj);
|
|
2232
|
-
for (const key in obj) {
|
|
2233
|
-
if (Object.hasOwn(obj, key)) {
|
|
2234
|
-
const value = obj[key];
|
|
2235
|
-
if (value !== null && typeof value === "object") {
|
|
2236
|
-
deepFreeze(value);
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
return obj;
|
|
2241
|
-
}
|
|
2242
|
-
function normalizeNumeric(code) {
|
|
2243
|
-
const str = typeof code === "number" ? code.toString() : code;
|
|
2244
|
-
return str.padStart(3, "0");
|
|
2245
|
-
}
|
|
2246
|
-
async function ensureCatalogLoaded() {
|
|
2247
|
-
if (catalogCache !== null) {
|
|
2248
|
-
return;
|
|
2249
|
-
}
|
|
2250
|
-
catalogCache = await loadCountryCodeCatalog();
|
|
2251
|
-
for (const country of catalogCache.countries) {
|
|
2252
|
-
alpha2Index.set(country.alpha2.toUpperCase(), country);
|
|
2253
|
-
alpha3Index.set(country.alpha3.toUpperCase(), country);
|
|
2254
|
-
const normalized = normalizeNumeric(country.numeric);
|
|
2255
|
-
numericIndex.set(normalized, country);
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
async function getCountryByAlpha2(code) {
|
|
2259
|
-
await ensureCatalogLoaded();
|
|
2260
|
-
const normalized = code.toUpperCase();
|
|
2261
|
-
const country = alpha2Index.get(normalized);
|
|
2262
|
-
return country ? deepFreeze(deepClone(country)) : null;
|
|
2263
|
-
}
|
|
2264
|
-
async function getCountryByAlpha3(code) {
|
|
2265
|
-
await ensureCatalogLoaded();
|
|
2266
|
-
const normalized = code.toUpperCase();
|
|
2267
|
-
const country = alpha3Index.get(normalized);
|
|
2268
|
-
return country ? deepFreeze(deepClone(country)) : null;
|
|
2269
|
-
}
|
|
2270
|
-
async function getCountryByNumeric(code) {
|
|
2271
|
-
await ensureCatalogLoaded();
|
|
2272
|
-
const normalized = normalizeNumeric(code);
|
|
2273
|
-
const country = numericIndex.get(normalized);
|
|
2274
|
-
return country ? deepFreeze(deepClone(country)) : null;
|
|
2275
|
-
}
|
|
2276
|
-
async function listCountries() {
|
|
2277
|
-
await ensureCatalogLoaded();
|
|
2278
|
-
return Array.from(alpha2Index.values()).map((c) => deepFreeze(deepClone(c)));
|
|
2279
|
-
}
|
|
2280
|
-
function clearCountryCodeCache() {
|
|
2281
|
-
catalogCache = null;
|
|
2282
|
-
alpha2Index.clear();
|
|
2283
|
-
alpha3Index.clear();
|
|
2284
|
-
numericIndex.clear();
|
|
2285
|
-
}
|
|
2286
|
-
var catalogCache, alpha2Index, alpha3Index, numericIndex;
|
|
2287
|
-
var init_country_codes = __esm({
|
|
2288
|
-
"src/foundry/country-codes.ts"() {
|
|
2289
|
-
init_loader();
|
|
2290
|
-
catalogCache = null;
|
|
2291
|
-
alpha2Index = /* @__PURE__ */ new Map();
|
|
2292
|
-
alpha3Index = /* @__PURE__ */ new Map();
|
|
2293
|
-
numericIndex = /* @__PURE__ */ new Map();
|
|
2294
|
-
}
|
|
2295
|
-
});
|
|
2296
|
-
|
|
2297
|
-
// src/crucible/foundry/exitCodes.ts
|
|
2298
|
-
function getExitCodeInfo(code) {
|
|
2299
|
-
return exitCodeMetadata[code];
|
|
2300
|
-
}
|
|
2301
|
-
var exitCodes, exitCodeMetadata, EXIT_CODES_VERSION;
|
|
2302
|
-
var init_exitCodes = __esm({
|
|
2303
|
-
"src/crucible/foundry/exitCodes.ts"() {
|
|
2304
|
-
exitCodes = {
|
|
2305
|
-
// Standard Exit Codes (0-1)
|
|
2306
|
-
// POSIX standard success and generic failure codes
|
|
2307
|
-
EXIT_SUCCESS: 0,
|
|
2308
|
-
EXIT_FAILURE: 1,
|
|
2309
|
-
// Networking & Port Management (10-19)
|
|
2310
|
-
// Network-related failures (ports, connectivity, etc.)
|
|
2311
|
-
EXIT_PORT_IN_USE: 10,
|
|
2312
|
-
EXIT_PORT_RANGE_EXHAUSTED: 11,
|
|
2313
|
-
EXIT_INSTANCE_ALREADY_RUNNING: 12,
|
|
2314
|
-
EXIT_NETWORK_UNREACHABLE: 13,
|
|
2315
|
-
EXIT_CONNECTION_REFUSED: 14,
|
|
2316
|
-
EXIT_CONNECTION_TIMEOUT: 15,
|
|
2317
|
-
// Configuration & Validation (20-29)
|
|
2318
|
-
// Configuration errors, validation failures, version mismatches
|
|
2319
|
-
EXIT_CONFIG_INVALID: 20,
|
|
2320
|
-
EXIT_MISSING_DEPENDENCY: 21,
|
|
2321
|
-
EXIT_SSOT_VERSION_MISMATCH: 22,
|
|
2322
|
-
EXIT_CONFIG_FILE_NOT_FOUND: 23,
|
|
2323
|
-
EXIT_ENVIRONMENT_INVALID: 24,
|
|
2324
|
-
// Runtime Errors (30-39)
|
|
2325
|
-
// Errors during normal operation (health checks, database, etc.)
|
|
2326
|
-
EXIT_HEALTH_CHECK_FAILED: 30,
|
|
2327
|
-
EXIT_DATABASE_UNAVAILABLE: 31,
|
|
2328
|
-
EXIT_EXTERNAL_SERVICE_UNAVAILABLE: 32,
|
|
2329
|
-
EXIT_RESOURCE_EXHAUSTED: 33,
|
|
2330
|
-
EXIT_OPERATION_TIMEOUT: 34,
|
|
2331
|
-
// Command-Line Usage Errors (40-49)
|
|
2332
|
-
// Invalid arguments, missing required flags, usage errors
|
|
2333
|
-
EXIT_INVALID_ARGUMENT: 40,
|
|
2334
|
-
EXIT_MISSING_REQUIRED_ARGUMENT: 41,
|
|
2335
|
-
EXIT_USAGE: 64,
|
|
2336
|
-
// Permissions & File Access (50-59)
|
|
2337
|
-
// Permission denied, file not found, access errors
|
|
2338
|
-
EXIT_PERMISSION_DENIED: 50,
|
|
2339
|
-
EXIT_FILE_NOT_FOUND: 51,
|
|
2340
|
-
EXIT_DIRECTORY_NOT_FOUND: 52,
|
|
2341
|
-
EXIT_FILE_READ_ERROR: 53,
|
|
2342
|
-
EXIT_FILE_WRITE_ERROR: 54,
|
|
2343
|
-
// Data & Processing Errors (60-69)
|
|
2344
|
-
// Data validation, parsing, transformation failures
|
|
2345
|
-
EXIT_DATA_INVALID: 60,
|
|
2346
|
-
EXIT_PARSE_ERROR: 61,
|
|
2347
|
-
EXIT_TRANSFORMATION_FAILED: 62,
|
|
2348
|
-
EXIT_DATA_CORRUPT: 63,
|
|
2349
|
-
// Security & Authentication (70-79)
|
|
2350
|
-
// Authentication failures, authorization errors, security violations
|
|
2351
|
-
EXIT_AUTHENTICATION_FAILED: 70,
|
|
2352
|
-
EXIT_AUTHORIZATION_FAILED: 71,
|
|
2353
|
-
EXIT_SECURITY_VIOLATION: 72,
|
|
2354
|
-
EXIT_CERTIFICATE_INVALID: 73,
|
|
2355
|
-
// Observability & Monitoring (80-89)
|
|
2356
|
-
// Observability infrastructure failures. Use when observability is CRITICAL to operation (e.g., monitoring agents, telemetry exporters). For workhorses where observability is auxiliary: - Log warning and continue (don't exit) - Emit degraded health status - Only exit if explicitly configured (fail_on_observability_error: true)
|
|
2357
|
-
EXIT_METRICS_UNAVAILABLE: 80,
|
|
2358
|
-
EXIT_TRACING_FAILED: 81,
|
|
2359
|
-
EXIT_LOGGING_FAILED: 82,
|
|
2360
|
-
EXIT_ALERT_SYSTEM_FAILED: 83,
|
|
2361
|
-
EXIT_STRUCTURED_LOGGING_FAILED: 84,
|
|
2362
|
-
// Testing & Validation (91-99)
|
|
2363
|
-
// Test execution outcomes and validation failures. NOTE: Test harnesses MUST use EXIT_SUCCESS (0) for successful test runs per ecosystem conventions (pytest, Go testing, Jest all use 0 for success). Codes in this category are for FAILURES and exceptional conditions only.
|
|
2364
|
-
EXIT_TEST_FAILURE: 91,
|
|
2365
|
-
EXIT_TEST_ERROR: 92,
|
|
2366
|
-
EXIT_TEST_INTERRUPTED: 93,
|
|
2367
|
-
EXIT_TEST_USAGE_ERROR: 94,
|
|
2368
|
-
EXIT_TEST_NO_TESTS_COLLECTED: 95,
|
|
2369
|
-
EXIT_COVERAGE_THRESHOLD_NOT_MET: 96,
|
|
2370
|
-
// Shell & Process Control (124-127)
|
|
2371
|
-
// Exit codes from shell conventions and process control utilities (timeout, exec)
|
|
2372
|
-
EXIT_TIMEOUT: 124,
|
|
2373
|
-
EXIT_TIMEOUT_INTERNAL: 125,
|
|
2374
|
-
EXIT_CANNOT_EXECUTE: 126,
|
|
2375
|
-
EXIT_NOT_FOUND: 127,
|
|
2376
|
-
// Signal-Induced Exits (128-165)
|
|
2377
|
-
// Process terminated by Unix signals (128+N pattern per POSIX). Signal codes follow Linux numbering; macOS/FreeBSD differ for SIGUSR1/SIGUSR2. For full signal semantics, see config/library/foundry/signals.yaml. For signal handling patterns, see docs/standards/library/modules/signal-handling.md.
|
|
2378
|
-
EXIT_SIGNAL_HUP: 129,
|
|
2379
|
-
EXIT_SIGNAL_INT: 130,
|
|
2380
|
-
EXIT_SIGNAL_QUIT: 131,
|
|
2381
|
-
EXIT_SIGNAL_KILL: 137,
|
|
2382
|
-
EXIT_SIGNAL_PIPE: 141,
|
|
2383
|
-
EXIT_SIGNAL_ALRM: 142,
|
|
2384
|
-
EXIT_SIGNAL_TERM: 143,
|
|
2385
|
-
EXIT_SIGNAL_USR1: 138,
|
|
2386
|
-
EXIT_SIGNAL_USR2: 140
|
|
2387
|
-
};
|
|
2388
|
-
exitCodeMetadata = {
|
|
2389
|
-
0: {
|
|
2390
|
-
code: 0,
|
|
2391
|
-
name: "EXIT_SUCCESS",
|
|
2392
|
-
description: "Successful execution",
|
|
2393
|
-
context: "Command completed without errors",
|
|
2394
|
-
category: "standard"
|
|
2395
|
-
},
|
|
2396
|
-
1: {
|
|
2397
|
-
code: 1,
|
|
2398
|
-
name: "EXIT_FAILURE",
|
|
2399
|
-
description: "Generic failure (unspecified error)",
|
|
2400
|
-
context: "Use when no more specific exit code applies",
|
|
2401
|
-
category: "standard"
|
|
2402
|
-
},
|
|
2403
|
-
10: {
|
|
2404
|
-
code: 10,
|
|
2405
|
-
name: "EXIT_PORT_IN_USE",
|
|
2406
|
-
description: "Specified port is already in use",
|
|
2407
|
-
context: "Server startup when port unavailable and fail_if_unavailable strategy",
|
|
2408
|
-
category: "networking"
|
|
2409
|
-
},
|
|
2410
|
-
11: {
|
|
2411
|
-
code: 11,
|
|
2412
|
-
name: "EXIT_PORT_RANGE_EXHAUSTED",
|
|
2413
|
-
description: "No available ports in configured range",
|
|
2414
|
-
context: "Server startup when all ports in environment range occupied",
|
|
2415
|
-
category: "networking"
|
|
2416
|
-
},
|
|
2417
|
-
12: {
|
|
2418
|
-
code: 12,
|
|
2419
|
-
name: "EXIT_INSTANCE_ALREADY_RUNNING",
|
|
2420
|
-
description: "Another instance already running on target port",
|
|
2421
|
-
context: "Server startup when PID registry shows active process on port",
|
|
2422
|
-
category: "networking"
|
|
2423
|
-
},
|
|
2424
|
-
13: {
|
|
2425
|
-
code: 13,
|
|
2426
|
-
name: "EXIT_NETWORK_UNREACHABLE",
|
|
2427
|
-
description: "Network destination unreachable",
|
|
2428
|
-
context: "Client connections, health checks, external service validation",
|
|
2429
|
-
category: "networking"
|
|
2430
|
-
},
|
|
2431
|
-
14: {
|
|
2432
|
-
code: 14,
|
|
2433
|
-
name: "EXIT_CONNECTION_REFUSED",
|
|
2434
|
-
description: "Connection refused by remote host",
|
|
2435
|
-
context: "Database connections, API endpoints, upstream services",
|
|
2436
|
-
category: "networking"
|
|
2437
|
-
},
|
|
2438
|
-
15: {
|
|
2439
|
-
code: 15,
|
|
2440
|
-
name: "EXIT_CONNECTION_TIMEOUT",
|
|
2441
|
-
description: "Connection attempt timed out",
|
|
2442
|
-
context: "Slow networks, unresponsive services, firewall blocks",
|
|
2443
|
-
category: "networking"
|
|
2444
|
-
},
|
|
2445
|
-
20: {
|
|
2446
|
-
code: 20,
|
|
2447
|
-
name: "EXIT_CONFIG_INVALID",
|
|
2448
|
-
description: "Configuration file failed validation",
|
|
2449
|
-
context: "Startup validation, schema mismatches, invalid YAML/JSON",
|
|
2450
|
-
category: "configuration",
|
|
2451
|
-
retryHint: "no_retry"
|
|
2452
|
-
},
|
|
2453
|
-
21: {
|
|
2454
|
-
code: 21,
|
|
2455
|
-
name: "EXIT_MISSING_DEPENDENCY",
|
|
2456
|
-
description: "Required dependency not found",
|
|
2457
|
-
context: "Missing binaries, libraries, or runtime requirements",
|
|
2458
|
-
category: "configuration",
|
|
2459
|
-
retryHint: "investigate"
|
|
2460
|
-
},
|
|
2461
|
-
22: {
|
|
2462
|
-
code: 22,
|
|
2463
|
-
name: "EXIT_SSOT_VERSION_MISMATCH",
|
|
2464
|
-
description: "SSOT (Crucible) version incompatible",
|
|
2465
|
-
context: "Helper library detects unsupported Crucible version",
|
|
2466
|
-
category: "configuration",
|
|
2467
|
-
retryHint: "no_retry"
|
|
2468
|
-
},
|
|
2469
|
-
23: {
|
|
2470
|
-
code: 23,
|
|
2471
|
-
name: "EXIT_CONFIG_FILE_NOT_FOUND",
|
|
2472
|
-
description: "Required configuration file not found",
|
|
2473
|
-
context: "Explicitly specified config path doesn't exist",
|
|
2474
|
-
category: "configuration"
|
|
2475
|
-
},
|
|
2476
|
-
24: {
|
|
2477
|
-
code: 24,
|
|
2478
|
-
name: "EXIT_ENVIRONMENT_INVALID",
|
|
2479
|
-
description: "Invalid or unsupported environment specification",
|
|
2480
|
-
context: "Unknown environment name, missing environment config",
|
|
2481
|
-
category: "configuration"
|
|
2482
|
-
},
|
|
2483
|
-
30: {
|
|
2484
|
-
code: 30,
|
|
2485
|
-
name: "EXIT_HEALTH_CHECK_FAILED",
|
|
2486
|
-
description: "Health check endpoint returned non-healthy status",
|
|
2487
|
-
context: "Startup health validation, readiness probes",
|
|
2488
|
-
category: "runtime",
|
|
2489
|
-
retryHint: "retry"
|
|
2490
|
-
},
|
|
2491
|
-
31: {
|
|
2492
|
-
code: 31,
|
|
2493
|
-
name: "EXIT_DATABASE_UNAVAILABLE",
|
|
2494
|
-
description: "Database connection failed or unavailable",
|
|
2495
|
-
context: "Startup connection checks, critical query failures",
|
|
2496
|
-
category: "runtime",
|
|
2497
|
-
retryHint: "retry"
|
|
2498
|
-
},
|
|
2499
|
-
32: {
|
|
2500
|
-
code: 32,
|
|
2501
|
-
name: "EXIT_EXTERNAL_SERVICE_UNAVAILABLE",
|
|
2502
|
-
description: "Required external service unavailable",
|
|
2503
|
-
context: "API dependencies, message queues, cache servers",
|
|
2504
|
-
category: "runtime"
|
|
2505
|
-
},
|
|
2506
|
-
33: {
|
|
2507
|
-
code: 33,
|
|
2508
|
-
name: "EXIT_RESOURCE_EXHAUSTED",
|
|
2509
|
-
description: "System resources exhausted (memory, disk, file descriptors)",
|
|
2510
|
-
context: "Out-of-memory, disk full, too many open files",
|
|
2511
|
-
category: "runtime",
|
|
2512
|
-
retryHint: "investigate"
|
|
2513
|
-
},
|
|
2514
|
-
34: {
|
|
2515
|
-
code: 34,
|
|
2516
|
-
name: "EXIT_OPERATION_TIMEOUT",
|
|
2517
|
-
description: "Operation exceeded timeout threshold",
|
|
2518
|
-
context: "Long-running tasks, async operations, batch processing",
|
|
2519
|
-
category: "runtime",
|
|
2520
|
-
retryHint: "retry"
|
|
2521
|
-
},
|
|
2522
|
-
40: {
|
|
2523
|
-
code: 40,
|
|
2524
|
-
name: "EXIT_INVALID_ARGUMENT",
|
|
2525
|
-
description: "Invalid command-line argument or flag value",
|
|
2526
|
-
context: "Type errors, out-of-range values, malformed input",
|
|
2527
|
-
category: "usage"
|
|
2528
|
-
},
|
|
2529
|
-
41: {
|
|
2530
|
-
code: 41,
|
|
2531
|
-
name: "EXIT_MISSING_REQUIRED_ARGUMENT",
|
|
2532
|
-
description: "Required command-line argument not provided",
|
|
2533
|
-
context: "Missing --config, --port, or other required flags",
|
|
2534
|
-
category: "usage"
|
|
2535
|
-
},
|
|
2536
|
-
64: {
|
|
2537
|
-
code: 64,
|
|
2538
|
-
name: "EXIT_USAGE",
|
|
2539
|
-
description: "Command-line usage error",
|
|
2540
|
-
context: "BSD sysexits.h EX_USAGE - wrong number of arguments, bad syntax",
|
|
2541
|
-
category: "usage",
|
|
2542
|
-
bsdEquivalent: "EX_USAGE"
|
|
2543
|
-
},
|
|
2544
|
-
50: {
|
|
2545
|
-
code: 50,
|
|
2546
|
-
name: "EXIT_PERMISSION_DENIED",
|
|
2547
|
-
description: "Insufficient permissions for operation",
|
|
2548
|
-
context: "File access, port binding (<1024), privileged operations",
|
|
2549
|
-
category: "permissions"
|
|
2550
|
-
},
|
|
2551
|
-
51: {
|
|
2552
|
-
code: 51,
|
|
2553
|
-
name: "EXIT_FILE_NOT_FOUND",
|
|
2554
|
-
description: "Required file not found",
|
|
2555
|
-
context: "Assets, templates, data files (not config - use 23)",
|
|
2556
|
-
category: "permissions"
|
|
2557
|
-
},
|
|
2558
|
-
52: {
|
|
2559
|
-
code: 52,
|
|
2560
|
-
name: "EXIT_DIRECTORY_NOT_FOUND",
|
|
2561
|
-
description: "Required directory not found",
|
|
2562
|
-
context: "State directories, log paths, data directories",
|
|
2563
|
-
category: "permissions"
|
|
2564
|
-
},
|
|
2565
|
-
53: {
|
|
2566
|
-
code: 53,
|
|
2567
|
-
name: "EXIT_FILE_READ_ERROR",
|
|
2568
|
-
description: "Error reading file",
|
|
2569
|
-
context: "Corrupt files, I/O errors, encoding issues",
|
|
2570
|
-
category: "permissions"
|
|
2571
|
-
},
|
|
2572
|
-
54: {
|
|
2573
|
-
code: 54,
|
|
2574
|
-
name: "EXIT_FILE_WRITE_ERROR",
|
|
2575
|
-
description: "Error writing file",
|
|
2576
|
-
context: "Disk full, read-only filesystem, permission errors",
|
|
2577
|
-
category: "permissions"
|
|
2578
|
-
},
|
|
2579
|
-
60: {
|
|
2580
|
-
code: 60,
|
|
2581
|
-
name: "EXIT_DATA_INVALID",
|
|
2582
|
-
description: "Input data failed validation",
|
|
2583
|
-
context: "Schema validation, business rule violations",
|
|
2584
|
-
category: "data"
|
|
2585
|
-
},
|
|
2586
|
-
61: {
|
|
2587
|
-
code: 61,
|
|
2588
|
-
name: "EXIT_PARSE_ERROR",
|
|
2589
|
-
description: "Error parsing input data",
|
|
2590
|
-
context: "Malformed JSON/YAML/XML, syntax errors",
|
|
2591
|
-
category: "data"
|
|
2592
|
-
},
|
|
2593
|
-
62: {
|
|
2594
|
-
code: 62,
|
|
2595
|
-
name: "EXIT_TRANSFORMATION_FAILED",
|
|
2596
|
-
description: "Data transformation or conversion failed",
|
|
2597
|
-
context: "Type conversions, format transformations, encoding changes",
|
|
2598
|
-
category: "data"
|
|
2599
|
-
},
|
|
2600
|
-
63: {
|
|
2601
|
-
code: 63,
|
|
2602
|
-
name: "EXIT_DATA_CORRUPT",
|
|
2603
|
-
description: "Data corruption detected",
|
|
2604
|
-
context: "Checksum failures, integrity violations",
|
|
2605
|
-
category: "data"
|
|
2606
|
-
},
|
|
2607
|
-
70: {
|
|
2608
|
-
code: 70,
|
|
2609
|
-
name: "EXIT_AUTHENTICATION_FAILED",
|
|
2610
|
-
description: "Authentication failed",
|
|
2611
|
-
context: "Invalid credentials, expired tokens, auth service unavailable",
|
|
2612
|
-
category: "security"
|
|
2613
|
-
},
|
|
2614
|
-
71: {
|
|
2615
|
-
code: 71,
|
|
2616
|
-
name: "EXIT_AUTHORIZATION_FAILED",
|
|
2617
|
-
description: "Authorization failed (authenticated but insufficient permissions)",
|
|
2618
|
-
context: "RBAC failures, scope violations, resource access denied",
|
|
2619
|
-
category: "security"
|
|
2620
|
-
},
|
|
2621
|
-
72: {
|
|
2622
|
-
code: 72,
|
|
2623
|
-
name: "EXIT_SECURITY_VIOLATION",
|
|
2624
|
-
description: "Security policy violation detected",
|
|
2625
|
-
context: "Suspicious activity, rate limit exceeded, IP blocklist",
|
|
2626
|
-
category: "security"
|
|
2627
|
-
},
|
|
2628
|
-
73: {
|
|
2629
|
-
code: 73,
|
|
2630
|
-
name: "EXIT_CERTIFICATE_INVALID",
|
|
2631
|
-
description: "TLS/SSL certificate validation failed",
|
|
2632
|
-
context: "Expired certs, untrusted CAs, hostname mismatches",
|
|
2633
|
-
category: "security",
|
|
2634
|
-
bsdEquivalent: "EX_PROTOCOL"
|
|
2635
|
-
},
|
|
2636
|
-
80: {
|
|
2637
|
-
code: 80,
|
|
2638
|
-
name: "EXIT_METRICS_UNAVAILABLE",
|
|
2639
|
-
description: "Metrics endpoint or collection system unavailable",
|
|
2640
|
-
context: "Use for observability-focused tools (Prometheus exporters, StatsD agents).\nWorkhorses SHOULD log warning and continue unless configured to fail-fast.\n",
|
|
2641
|
-
category: "observability"
|
|
2642
|
-
},
|
|
2643
|
-
81: {
|
|
2644
|
-
code: 81,
|
|
2645
|
-
name: "EXIT_TRACING_FAILED",
|
|
2646
|
-
description: "Distributed tracing system unavailable",
|
|
2647
|
-
context: "OTLP exporter failed, Jaeger collector unreachable",
|
|
2648
|
-
category: "observability"
|
|
2649
|
-
},
|
|
2650
|
-
82: {
|
|
2651
|
-
code: 82,
|
|
2652
|
-
name: "EXIT_LOGGING_FAILED",
|
|
2653
|
-
description: "Logging system unavailable or misconfigured",
|
|
2654
|
-
context: "Log aggregator unreachable, log file unwritable",
|
|
2655
|
-
category: "observability"
|
|
2656
|
-
},
|
|
2657
|
-
83: {
|
|
2658
|
-
code: 83,
|
|
2659
|
-
name: "EXIT_ALERT_SYSTEM_FAILED",
|
|
2660
|
-
description: "Alerting system unavailable",
|
|
2661
|
-
context: "PagerDuty API failed, Slack webhook unreachable",
|
|
2662
|
-
category: "observability"
|
|
2663
|
-
},
|
|
2664
|
-
84: {
|
|
2665
|
-
code: 84,
|
|
2666
|
-
name: "EXIT_STRUCTURED_LOGGING_FAILED",
|
|
2667
|
-
description: "Structured logging system unavailable",
|
|
2668
|
-
context: "JSON log aggregator unreachable, log schema validation failed",
|
|
2669
|
-
category: "observability"
|
|
2670
|
-
},
|
|
2671
|
-
91: {
|
|
2672
|
-
code: 91,
|
|
2673
|
-
name: "EXIT_TEST_FAILURE",
|
|
2674
|
-
description: "One or more tests failed",
|
|
2675
|
-
context: "Test assertions failed, expected behavior not met.\nMaps to pytest exit code 1, Go test failure, Jest failure.\n",
|
|
2676
|
-
category: "testing"
|
|
2677
|
-
},
|
|
2678
|
-
92: {
|
|
2679
|
-
code: 92,
|
|
2680
|
-
name: "EXIT_TEST_ERROR",
|
|
2681
|
-
description: "Test execution error (not test failure)",
|
|
2682
|
-
context: "Test setup failed, fixture unavailable, test harness error.\nMaps to pytest exit code 3 (internal error).\n",
|
|
2683
|
-
category: "testing"
|
|
2684
|
-
},
|
|
2685
|
-
93: {
|
|
2686
|
-
code: 93,
|
|
2687
|
-
name: "EXIT_TEST_INTERRUPTED",
|
|
2688
|
-
description: "Test run interrupted by user or system",
|
|
2689
|
-
context: "Ctrl+C during tests, system signal, user cancellation.\nMaps to pytest exit code 2.\n",
|
|
2690
|
-
category: "testing"
|
|
2691
|
-
},
|
|
2692
|
-
94: {
|
|
2693
|
-
code: 94,
|
|
2694
|
-
name: "EXIT_TEST_USAGE_ERROR",
|
|
2695
|
-
description: "Test command usage error",
|
|
2696
|
-
context: "Invalid test arguments, bad configuration.\nMaps to pytest exit code 4.\n",
|
|
2697
|
-
category: "testing"
|
|
2698
|
-
},
|
|
2699
|
-
95: {
|
|
2700
|
-
code: 95,
|
|
2701
|
-
name: "EXIT_TEST_NO_TESTS_COLLECTED",
|
|
2702
|
-
description: "No tests found or all tests skipped",
|
|
2703
|
-
context: "Empty test suite, all tests deselected or skipped.\nMaps to pytest exit code 5.\n",
|
|
2704
|
-
category: "testing"
|
|
2705
|
-
},
|
|
2706
|
-
96: {
|
|
2707
|
-
code: 96,
|
|
2708
|
-
name: "EXIT_COVERAGE_THRESHOLD_NOT_MET",
|
|
2709
|
-
description: "Test coverage below required threshold",
|
|
2710
|
-
context: "Code coverage validation, quality gate failure",
|
|
2711
|
-
category: "testing"
|
|
2712
|
-
},
|
|
2713
|
-
124: {
|
|
2714
|
-
code: 124,
|
|
2715
|
-
name: "EXIT_TIMEOUT",
|
|
2716
|
-
description: "Command timed out before completion",
|
|
2717
|
-
context: "GNU timeout or similar utility terminated command after deadline",
|
|
2718
|
-
category: "shell"
|
|
2719
|
-
},
|
|
2720
|
-
125: {
|
|
2721
|
-
code: 125,
|
|
2722
|
-
name: "EXIT_TIMEOUT_INTERNAL",
|
|
2723
|
-
description: "Timeout utility itself failed",
|
|
2724
|
-
context: "Error in timeout tool before or during command execution (not command failure)",
|
|
2725
|
-
category: "shell"
|
|
2726
|
-
},
|
|
2727
|
-
126: {
|
|
2728
|
-
code: 126,
|
|
2729
|
-
name: "EXIT_CANNOT_EXECUTE",
|
|
2730
|
-
description: "Command found but could not be executed",
|
|
2731
|
-
context: "Permission denied, not executable, exec format error",
|
|
2732
|
-
category: "shell",
|
|
2733
|
-
bsdEquivalent: "EX_NOPERM (partial)"
|
|
2734
|
-
},
|
|
2735
|
-
127: {
|
|
2736
|
-
code: 127,
|
|
2737
|
-
name: "EXIT_NOT_FOUND",
|
|
2738
|
-
description: "Command not found",
|
|
2739
|
-
context: "Executable not found in PATH or specified path does not exist",
|
|
2740
|
-
category: "shell",
|
|
2741
|
-
bsdEquivalent: "EX_UNAVAILABLE (partial)"
|
|
2742
|
-
},
|
|
2743
|
-
129: {
|
|
2744
|
-
code: 129,
|
|
2745
|
-
name: "EXIT_SIGNAL_HUP",
|
|
2746
|
-
description: "Hangup signal (SIGHUP) - config reload via restart",
|
|
2747
|
-
context: "Config reload via restart-based pattern (mandatory schema validation).\nProcess exits with 129, supervisor restarts with new config.\nSee signals.yaml for reload behavior definition.",
|
|
2748
|
-
category: "signals",
|
|
2749
|
-
bsdEquivalent: "128 + 1"
|
|
2750
|
-
},
|
|
2751
|
-
130: {
|
|
2752
|
-
code: 130,
|
|
2753
|
-
name: "EXIT_SIGNAL_INT",
|
|
2754
|
-
description: "Interrupt signal (SIGINT) - user interrupt with Ctrl+C double-tap",
|
|
2755
|
-
context: "Ctrl+C pressed. First tap initiates graceful shutdown, second within 2s forces immediate exit.\nSame exit code (130) for both graceful and force modes.\nSee signals.yaml for double-tap behavior definition.",
|
|
2756
|
-
category: "signals",
|
|
2757
|
-
bsdEquivalent: "128 + 2"
|
|
2758
|
-
},
|
|
2759
|
-
131: {
|
|
2760
|
-
code: 131,
|
|
2761
|
-
name: "EXIT_SIGNAL_QUIT",
|
|
2762
|
-
description: "Quit signal (SIGQUIT) - immediate exit",
|
|
2763
|
-
context: "Ctrl+\\ on Unix, Ctrl+Break on Windows. Immediate termination without cleanup.\nUse for emergency shutdown or debugging (core dumps).",
|
|
2764
|
-
category: "signals",
|
|
2765
|
-
bsdEquivalent: "128 + 3"
|
|
2766
|
-
},
|
|
2767
|
-
137: {
|
|
2768
|
-
code: 137,
|
|
2769
|
-
name: "EXIT_SIGNAL_KILL",
|
|
2770
|
-
description: "Kill signal (SIGKILL)",
|
|
2771
|
-
context: "Forceful termination, non-graceful shutdown (not catchable)",
|
|
2772
|
-
category: "signals",
|
|
2773
|
-
bsdEquivalent: "128 + 9",
|
|
2774
|
-
pythonNote: "Cannot be caught in Python (OS-level)"
|
|
2775
|
-
},
|
|
2776
|
-
141: {
|
|
2777
|
-
code: 141,
|
|
2778
|
-
name: "EXIT_SIGNAL_PIPE",
|
|
2779
|
-
description: "Broken pipe (SIGPIPE) - observe only",
|
|
2780
|
-
context: "Writing to closed pipe/socket. Fulmen default is observe_only (log + graceful exit).\nApplications may override to ignore for network services.\nSee signals.yaml for SIGPIPE handling guidance.",
|
|
2781
|
-
category: "signals",
|
|
2782
|
-
bsdEquivalent: "128 + 13",
|
|
2783
|
-
pythonNote: "Raised as BrokenPipeError exception"
|
|
2784
|
-
},
|
|
2785
|
-
142: {
|
|
2786
|
-
code: 142,
|
|
2787
|
-
name: "EXIT_SIGNAL_ALRM",
|
|
2788
|
-
description: "Alarm signal (SIGALRM) - watchdog timeout",
|
|
2789
|
-
context: "Watchdog timer expired. Treat as timeout-induced exit.\nWatchdog pattern out of scope for v1.0.0 module implementations.",
|
|
2790
|
-
category: "signals",
|
|
2791
|
-
bsdEquivalent: "128 + 14",
|
|
2792
|
-
pythonNote: "Supported by signal module, rarely used in practice"
|
|
2793
|
-
},
|
|
2794
|
-
143: {
|
|
2795
|
-
code: 143,
|
|
2796
|
-
name: "EXIT_SIGNAL_TERM",
|
|
2797
|
-
description: "Termination signal (SIGTERM) - graceful shutdown",
|
|
2798
|
-
context: "Graceful shutdown requested by container orchestrator or process supervisor.\nStandard 30-second timeout for cleanup. Applications run cleanup handlers before exit.\nSee signals.yaml for graceful shutdown behavior definition.",
|
|
2799
|
-
category: "signals",
|
|
2800
|
-
bsdEquivalent: "128 + 15",
|
|
2801
|
-
pythonNote: "Default signal for graceful shutdown"
|
|
2802
|
-
},
|
|
2803
|
-
138: {
|
|
2804
|
-
code: 138,
|
|
2805
|
-
name: "EXIT_SIGNAL_USR1",
|
|
2806
|
-
description: "User-defined signal 1 (SIGUSR1) - custom handler",
|
|
2807
|
-
context: "Application-specific signal (e.g., reopen logs, dump stats, trigger profiling).\nApplications register custom handlers. Exit code 138 on Linux (128+10).\nPlatform differences: macOS/FreeBSD use signal 30 (exit 158), not 10.",
|
|
2808
|
-
category: "signals"
|
|
2809
|
-
},
|
|
2810
|
-
140: {
|
|
2811
|
-
code: 140,
|
|
2812
|
-
name: "EXIT_SIGNAL_USR2",
|
|
2813
|
-
description: "User-defined signal 2 (SIGUSR2) - custom handler",
|
|
2814
|
-
context: "Application-specific signal (e.g., toggle debug mode, rotate credentials).\nApplications register custom handlers. Exit code 140 on Linux (128+12).\nPlatform differences: macOS/FreeBSD use signal 31 (exit 159), not 12.",
|
|
2815
|
-
category: "signals"
|
|
2816
|
-
}
|
|
2817
|
-
};
|
|
2818
|
-
EXIT_CODES_VERSION = "v1.0.0";
|
|
2819
|
-
}
|
|
2820
|
-
});
|
|
2821
|
-
|
|
2822
|
-
// src/foundry/exit-codes/capabilities.ts
|
|
2823
|
-
function supportsSignalExitCodes() {
|
|
2824
|
-
return process.platform !== "win32";
|
|
2825
|
-
}
|
|
2826
|
-
function getPlatform() {
|
|
2827
|
-
return process.platform;
|
|
2828
|
-
}
|
|
2829
|
-
function isWindows() {
|
|
2830
|
-
return process.platform === "win32";
|
|
2831
|
-
}
|
|
2832
|
-
function isPOSIX() {
|
|
2833
|
-
return !isWindows();
|
|
2834
|
-
}
|
|
2835
|
-
function getPlatformCapabilities() {
|
|
2836
|
-
return {
|
|
2837
|
-
platform: getPlatform(),
|
|
2838
|
-
supportsSignalExitCodes: supportsSignalExitCodes(),
|
|
2839
|
-
isPOSIX: isPOSIX(),
|
|
2840
|
-
isWindows: isWindows()
|
|
2841
|
-
};
|
|
2842
|
-
}
|
|
2843
|
-
var init_capabilities = __esm({
|
|
2844
|
-
"src/foundry/exit-codes/capabilities.ts"() {
|
|
2845
|
-
}
|
|
2846
|
-
});
|
|
2847
|
-
|
|
2848
|
-
// src/foundry/exit-codes/simplified.ts
|
|
2849
|
-
function mapExitCodeToSimplified(code, mode = "basic" /* BASIC */) {
|
|
2850
|
-
if (code === 0) {
|
|
2851
|
-
return 0;
|
|
2852
|
-
}
|
|
2853
|
-
if (mode === "basic" /* BASIC */) {
|
|
2854
|
-
return 1;
|
|
2855
|
-
}
|
|
2856
|
-
const info2 = exitCodeMetadata[code];
|
|
2857
|
-
if (!info2) {
|
|
2858
|
-
return 3;
|
|
2859
|
-
}
|
|
2860
|
-
if (info2.retryHint === "retry") {
|
|
2861
|
-
return 1;
|
|
2862
|
-
}
|
|
2863
|
-
if (info2.retryHint === "no_retry") {
|
|
2864
|
-
return 2;
|
|
2865
|
-
}
|
|
2866
|
-
if (info2.retryHint === "investigate") {
|
|
2867
|
-
return 3;
|
|
2868
|
-
}
|
|
2869
|
-
switch (info2.category) {
|
|
2870
|
-
case "configuration":
|
|
2871
|
-
case "usage":
|
|
2872
|
-
return 2;
|
|
2873
|
-
case "networking":
|
|
2874
|
-
case "runtime":
|
|
2875
|
-
return 1;
|
|
2876
|
-
case "permissions":
|
|
2877
|
-
case "data":
|
|
2878
|
-
return 2;
|
|
2879
|
-
case "security":
|
|
2880
|
-
return 3;
|
|
2881
|
-
case "observability":
|
|
2882
|
-
return 1;
|
|
2883
|
-
case "testing":
|
|
2884
|
-
return 1;
|
|
2885
|
-
case "signals":
|
|
2886
|
-
return 3;
|
|
2887
|
-
default:
|
|
2888
|
-
return 1;
|
|
2889
|
-
}
|
|
2890
|
-
}
|
|
2891
|
-
function getSimplifiedCodes(mode) {
|
|
2892
|
-
switch (mode) {
|
|
2893
|
-
case "basic" /* BASIC */:
|
|
2894
|
-
return [0, 1];
|
|
2895
|
-
case "severity" /* SEVERITY */:
|
|
2896
|
-
return [0, 1, 2, 3];
|
|
2897
|
-
default:
|
|
2898
|
-
return [0, 1];
|
|
2899
|
-
}
|
|
2900
|
-
}
|
|
2901
|
-
function getSimplifiedCodeDescription(code, mode) {
|
|
2902
|
-
if (mode === "basic" /* BASIC */) {
|
|
2903
|
-
switch (code) {
|
|
2904
|
-
case 0:
|
|
2905
|
-
return "Success";
|
|
2906
|
-
case 1:
|
|
2907
|
-
return "Failure";
|
|
2908
|
-
default:
|
|
2909
|
-
return "Unknown";
|
|
2910
|
-
}
|
|
2911
|
-
}
|
|
2912
|
-
if (mode === "severity" /* SEVERITY */) {
|
|
2913
|
-
switch (code) {
|
|
2914
|
-
case 0:
|
|
2915
|
-
return "Success";
|
|
2916
|
-
case 1:
|
|
2917
|
-
return "Recoverable error (retry possible)";
|
|
2918
|
-
case 2:
|
|
2919
|
-
return "Configuration error (fix config, don't retry)";
|
|
2920
|
-
case 3:
|
|
2921
|
-
return "Fatal error (investigate required)";
|
|
2922
|
-
default:
|
|
2923
|
-
return "Unknown";
|
|
2924
|
-
}
|
|
2925
|
-
}
|
|
2926
|
-
return "Unknown mode";
|
|
2927
|
-
}
|
|
2928
|
-
var SimplifiedMode;
|
|
2929
|
-
var init_simplified = __esm({
|
|
2930
|
-
"src/foundry/exit-codes/simplified.ts"() {
|
|
2931
|
-
init_exitCodes();
|
|
2932
|
-
SimplifiedMode = /* @__PURE__ */ ((SimplifiedMode2) => {
|
|
2933
|
-
SimplifiedMode2["BASIC"] = "basic";
|
|
2934
|
-
SimplifiedMode2["SEVERITY"] = "severity";
|
|
2935
|
-
return SimplifiedMode2;
|
|
2936
|
-
})(SimplifiedMode || {});
|
|
2937
|
-
}
|
|
2938
|
-
});
|
|
2939
|
-
|
|
2940
|
-
// src/foundry/exit-codes/index.ts
|
|
2941
|
-
var init_exit_codes = __esm({
|
|
2942
|
-
"src/foundry/exit-codes/index.ts"() {
|
|
2943
|
-
init_exitCodes();
|
|
2944
|
-
init_capabilities();
|
|
2945
|
-
init_simplified();
|
|
2946
|
-
}
|
|
2947
|
-
});
|
|
2948
|
-
|
|
2949
|
-
// src/foundry/http-statuses.ts
|
|
2950
|
-
function deepClone2(obj) {
|
|
2951
|
-
if (obj === null || typeof obj !== "object") {
|
|
2952
|
-
return obj;
|
|
2953
|
-
}
|
|
2954
|
-
if (Array.isArray(obj)) {
|
|
2955
|
-
return obj.map((item) => deepClone2(item));
|
|
2956
|
-
}
|
|
2957
|
-
const cloned = {};
|
|
2958
|
-
for (const key in obj) {
|
|
2959
|
-
if (Object.hasOwn(obj, key)) {
|
|
2960
|
-
cloned[key] = deepClone2(obj[key]);
|
|
2961
|
-
}
|
|
2962
|
-
}
|
|
2963
|
-
return cloned;
|
|
2964
|
-
}
|
|
2965
|
-
function deepFreeze2(obj) {
|
|
2966
|
-
Object.freeze(obj);
|
|
2967
|
-
for (const key in obj) {
|
|
2968
|
-
if (Object.hasOwn(obj, key)) {
|
|
2969
|
-
const value = obj[key];
|
|
2970
|
-
if (value !== null && typeof value === "object") {
|
|
2971
|
-
deepFreeze2(value);
|
|
2972
|
-
}
|
|
2973
|
-
}
|
|
2974
|
-
}
|
|
2975
|
-
return obj;
|
|
2976
|
-
}
|
|
2977
|
-
async function ensureCatalogLoaded2() {
|
|
2978
|
-
if (catalogCache2 !== null) {
|
|
2979
|
-
return;
|
|
2980
|
-
}
|
|
2981
|
-
catalogCache2 = await loadHttpStatusCatalog();
|
|
2982
|
-
for (const group of catalogCache2.groups) {
|
|
2983
|
-
const groupId = group.id;
|
|
2984
|
-
for (const code of group.codes) {
|
|
2985
|
-
statusCodeIndex.set(code.value, {
|
|
2986
|
-
value: code.value,
|
|
2987
|
-
reason: code.reason,
|
|
2988
|
-
group: groupId
|
|
2989
|
-
});
|
|
2990
|
-
}
|
|
2991
|
-
}
|
|
2992
|
-
}
|
|
2993
|
-
async function getHttpStatus(code) {
|
|
2994
|
-
await ensureCatalogLoaded2();
|
|
2995
|
-
const status = statusCodeIndex.get(code);
|
|
2996
|
-
return status ? deepFreeze2(deepClone2(status)) : null;
|
|
2997
|
-
}
|
|
2998
|
-
function isInformational(code) {
|
|
2999
|
-
return code >= 100 && code < 200;
|
|
3000
|
-
}
|
|
3001
|
-
function isSuccess(code) {
|
|
3002
|
-
return code >= 200 && code < 300;
|
|
3003
|
-
}
|
|
3004
|
-
function isRedirection(code) {
|
|
3005
|
-
return code >= 300 && code < 400;
|
|
3006
|
-
}
|
|
3007
|
-
function isClientError(code) {
|
|
3008
|
-
return code >= 400 && code < 500;
|
|
3009
|
-
}
|
|
3010
|
-
function isServerError(code) {
|
|
3011
|
-
return code >= 500 && code < 600;
|
|
3012
|
-
}
|
|
3013
|
-
async function listHttpStatuses() {
|
|
3014
|
-
await ensureCatalogLoaded2();
|
|
3015
|
-
return Array.from(statusCodeIndex.values()).map((s) => deepFreeze2(deepClone2(s)));
|
|
3016
|
-
}
|
|
3017
|
-
async function getStatusReason(code) {
|
|
3018
|
-
const status = await getHttpStatus(code);
|
|
3019
|
-
return status?.reason ?? null;
|
|
3020
|
-
}
|
|
3021
|
-
function clearHttpStatusCache() {
|
|
3022
|
-
catalogCache2 = null;
|
|
3023
|
-
statusCodeIndex.clear();
|
|
3024
|
-
}
|
|
3025
|
-
var catalogCache2, statusCodeIndex;
|
|
3026
|
-
var init_http_statuses = __esm({
|
|
3027
|
-
"src/foundry/http-statuses.ts"() {
|
|
3028
|
-
init_loader();
|
|
3029
|
-
catalogCache2 = null;
|
|
3030
|
-
statusCodeIndex = /* @__PURE__ */ new Map();
|
|
3031
|
-
}
|
|
3032
|
-
});
|
|
3033
|
-
|
|
3034
|
-
// src/foundry/magic-numbers.ts
|
|
3035
|
-
function hasBOM(buffer) {
|
|
3036
|
-
if (buffer.length < 3) return false;
|
|
3037
|
-
return buffer[0] === UTF8_BOM[0] && buffer[1] === UTF8_BOM[1] && buffer[2] === UTF8_BOM[2];
|
|
3038
|
-
}
|
|
3039
|
-
function getBOMOffset(buffer) {
|
|
3040
|
-
return hasBOM(buffer) ? 3 : 0;
|
|
3041
|
-
}
|
|
3042
|
-
var UTF8_BOM, XML_PATTERNS, JSON_PATTERNS, YAML_PATTERNS, NDJSON_PATTERNS, CSV_PATTERNS, PROTOBUF_PATTERNS, TEXT_PATTERNS, MAGIC_NUMBER_DATABASE;
|
|
3043
|
-
var init_magic_numbers = __esm({
|
|
3044
|
-
"src/foundry/magic-numbers.ts"() {
|
|
3045
|
-
UTF8_BOM = [239, 187, 191];
|
|
3046
|
-
XML_PATTERNS = [
|
|
3047
|
-
{
|
|
3048
|
-
offset: 0,
|
|
3049
|
-
bytes: [60, 63, 120, 109, 108],
|
|
3050
|
-
description: "XML declaration: <?xml"
|
|
3051
|
-
},
|
|
3052
|
-
{
|
|
3053
|
-
offset: 0,
|
|
3054
|
-
bytes: [239, 187, 191, 60, 63, 120, 109, 108],
|
|
3055
|
-
description: "XML with UTF-8 BOM: BOM + <?xml"
|
|
3056
|
-
}
|
|
3057
|
-
];
|
|
3058
|
-
JSON_PATTERNS = [
|
|
3059
|
-
{
|
|
3060
|
-
offset: 0,
|
|
3061
|
-
bytes: [123],
|
|
3062
|
-
description: "JSON object start: {"
|
|
3063
|
-
},
|
|
3064
|
-
{
|
|
3065
|
-
offset: 0,
|
|
3066
|
-
bytes: [91],
|
|
3067
|
-
description: "JSON array start: ["
|
|
3068
|
-
},
|
|
3069
|
-
{
|
|
3070
|
-
offset: 0,
|
|
3071
|
-
bytes: [239, 187, 191, 123],
|
|
3072
|
-
description: "JSON object with BOM: BOM + {"
|
|
3073
|
-
},
|
|
3074
|
-
{
|
|
3075
|
-
offset: 0,
|
|
3076
|
-
bytes: [239, 187, 191, 91],
|
|
3077
|
-
description: "JSON array with BOM: BOM + ["
|
|
3078
|
-
}
|
|
3079
|
-
];
|
|
3080
|
-
YAML_PATTERNS = [
|
|
3081
|
-
{
|
|
3082
|
-
offset: 0,
|
|
3083
|
-
bytes: [45, 45, 45],
|
|
3084
|
-
description: "YAML document marker: ---"
|
|
3085
|
-
},
|
|
3086
|
-
{
|
|
3087
|
-
offset: 0,
|
|
3088
|
-
bytes: [37, 89, 65, 77, 76],
|
|
3089
|
-
description: "YAML directive: %YAML"
|
|
3090
|
-
}
|
|
3091
|
-
];
|
|
3092
|
-
NDJSON_PATTERNS = [];
|
|
3093
|
-
CSV_PATTERNS = [];
|
|
3094
|
-
PROTOBUF_PATTERNS = [];
|
|
3095
|
-
TEXT_PATTERNS = [];
|
|
3096
|
-
MAGIC_NUMBER_DATABASE = [
|
|
3097
|
-
{
|
|
3098
|
-
mimeType: "application/xml",
|
|
3099
|
-
patterns: XML_PATTERNS,
|
|
3100
|
-
priority: 10,
|
|
3101
|
-
matchStrategy: "exact"
|
|
3102
|
-
},
|
|
3103
|
-
{
|
|
3104
|
-
mimeType: "application/x-ndjson",
|
|
3105
|
-
patterns: NDJSON_PATTERNS,
|
|
3106
|
-
priority: 9,
|
|
3107
|
-
matchStrategy: "heuristic"
|
|
3108
|
-
},
|
|
3109
|
-
{
|
|
3110
|
-
mimeType: "application/json",
|
|
3111
|
-
patterns: JSON_PATTERNS,
|
|
3112
|
-
priority: 8,
|
|
3113
|
-
matchStrategy: "exact"
|
|
3114
|
-
},
|
|
3115
|
-
{
|
|
3116
|
-
mimeType: "application/yaml",
|
|
3117
|
-
patterns: YAML_PATTERNS,
|
|
3118
|
-
priority: 7,
|
|
3119
|
-
matchStrategy: "exact"
|
|
3120
|
-
},
|
|
3121
|
-
{
|
|
3122
|
-
mimeType: "application/yaml",
|
|
3123
|
-
patterns: [],
|
|
3124
|
-
priority: 6.5,
|
|
3125
|
-
matchStrategy: "heuristic"
|
|
3126
|
-
},
|
|
3127
|
-
{
|
|
3128
|
-
mimeType: "text/csv",
|
|
3129
|
-
patterns: CSV_PATTERNS,
|
|
3130
|
-
priority: 6,
|
|
3131
|
-
matchStrategy: "heuristic"
|
|
3132
|
-
},
|
|
3133
|
-
{
|
|
3134
|
-
mimeType: "application/x-protobuf",
|
|
3135
|
-
patterns: PROTOBUF_PATTERNS,
|
|
3136
|
-
priority: 5,
|
|
3137
|
-
matchStrategy: "heuristic"
|
|
3138
|
-
},
|
|
3139
|
-
{
|
|
3140
|
-
mimeType: "text/plain",
|
|
3141
|
-
patterns: TEXT_PATTERNS,
|
|
3142
|
-
priority: 1,
|
|
3143
|
-
matchStrategy: "heuristic"
|
|
3144
|
-
}
|
|
3145
|
-
];
|
|
3146
|
-
}
|
|
3147
|
-
});
|
|
3148
|
-
|
|
3149
|
-
// src/foundry/detector.ts
|
|
3150
|
-
function createDetector(catalog) {
|
|
3151
|
-
return new MimeTypeDetector(MAGIC_NUMBER_DATABASE, catalog);
|
|
3152
|
-
}
|
|
3153
|
-
var MimeTypeDetector;
|
|
3154
|
-
var init_detector = __esm({
|
|
3155
|
-
"src/foundry/detector.ts"() {
|
|
3156
|
-
init_magic_numbers();
|
|
3157
|
-
MimeTypeDetector = class {
|
|
3158
|
-
patterns;
|
|
3159
|
-
catalog;
|
|
3160
|
-
constructor(patterns, catalog) {
|
|
3161
|
-
this.patterns = [...patterns].sort((a, b) => b.priority - a.priority);
|
|
3162
|
-
this.catalog = catalog;
|
|
3163
|
-
}
|
|
3164
|
-
/**
|
|
3165
|
-
* Detect MIME type from buffer content
|
|
3166
|
-
*/
|
|
3167
|
-
detect(buffer, _options = {}) {
|
|
3168
|
-
const offset = getBOMOffset(buffer);
|
|
3169
|
-
const workingBuffer = offset > 0 ? buffer.subarray(offset) : buffer;
|
|
3170
|
-
for (const pattern of this.patterns) {
|
|
3171
|
-
if (pattern.matchStrategy === "exact" && this.matchPattern(workingBuffer, pattern)) {
|
|
3172
|
-
return this.catalog.get(pattern.mimeType) || null;
|
|
3173
|
-
}
|
|
3174
|
-
if (pattern.matchStrategy === "heuristic" && this.matchHeuristic(workingBuffer, pattern)) {
|
|
3175
|
-
return this.catalog.get(pattern.mimeType) || null;
|
|
3176
|
-
}
|
|
3177
|
-
}
|
|
3178
|
-
return null;
|
|
3179
|
-
}
|
|
3180
|
-
/**
|
|
3181
|
-
* Match buffer against a pattern signature
|
|
3182
|
-
*/
|
|
3183
|
-
matchPattern(buffer, signature) {
|
|
3184
|
-
for (const pattern of signature.patterns) {
|
|
3185
|
-
if (this.matchBytes(buffer, pattern)) {
|
|
3186
|
-
return true;
|
|
3187
|
-
}
|
|
3188
|
-
}
|
|
3189
|
-
return false;
|
|
3190
|
-
}
|
|
3191
|
-
/**
|
|
3192
|
-
* Match bytes at specified offset with optional masking
|
|
3193
|
-
*/
|
|
3194
|
-
matchBytes(buffer, pattern) {
|
|
3195
|
-
const { offset, bytes, mask } = pattern;
|
|
3196
|
-
if (buffer.length < offset + bytes.length) {
|
|
3197
|
-
return false;
|
|
3198
|
-
}
|
|
3199
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
3200
|
-
const bufferByte = buffer[offset + i];
|
|
3201
|
-
const patternByte = bytes[i];
|
|
3202
|
-
const maskByte = mask ? mask[i] : 255;
|
|
3203
|
-
if ((bufferByte & maskByte) !== (patternByte & maskByte)) {
|
|
3204
|
-
return false;
|
|
3205
|
-
}
|
|
3206
|
-
}
|
|
3207
|
-
return true;
|
|
3208
|
-
}
|
|
3209
|
-
/**
|
|
3210
|
-
* Heuristic detection for formats without magic numbers
|
|
3211
|
-
*/
|
|
3212
|
-
matchHeuristic(buffer, signature) {
|
|
3213
|
-
switch (signature.mimeType) {
|
|
3214
|
-
case "application/x-ndjson":
|
|
3215
|
-
return this.detectNDJSON(buffer);
|
|
3216
|
-
case "application/yaml":
|
|
3217
|
-
return this.detectYAML(buffer);
|
|
3218
|
-
case "text/csv":
|
|
3219
|
-
return this.detectCSV(buffer);
|
|
3220
|
-
case "application/x-protobuf":
|
|
3221
|
-
return this.detectProtobuf(buffer);
|
|
3222
|
-
case "text/plain":
|
|
3223
|
-
return this.detectPlainText(buffer);
|
|
3224
|
-
default:
|
|
3225
|
-
return false;
|
|
3226
|
-
}
|
|
3227
|
-
}
|
|
3228
|
-
/**
|
|
3229
|
-
* Detect NDJSON (newline-delimited JSON)
|
|
3230
|
-
*/
|
|
3231
|
-
detectNDJSON(buffer) {
|
|
3232
|
-
try {
|
|
3233
|
-
const text = buffer.toString("utf-8", 0, Math.min(buffer.length, 512));
|
|
3234
|
-
const lines = text.split("\n").filter((line) => line.trim().length > 0);
|
|
3235
|
-
if (lines.length < 2) return false;
|
|
3236
|
-
const linesToCheck = Math.min(3, lines.length);
|
|
3237
|
-
let validJsonLines = 0;
|
|
3238
|
-
for (let i = 0; i < linesToCheck; i++) {
|
|
3239
|
-
try {
|
|
3240
|
-
JSON.parse(lines[i]);
|
|
3241
|
-
validJsonLines++;
|
|
3242
|
-
} catch {
|
|
3243
|
-
return false;
|
|
3244
|
-
}
|
|
3245
|
-
}
|
|
3246
|
-
return validJsonLines >= 2;
|
|
3247
|
-
} catch {
|
|
3248
|
-
return false;
|
|
3249
|
-
}
|
|
3250
|
-
}
|
|
3251
|
-
/**
|
|
3252
|
-
* Detect YAML format (heuristic for files without --- header)
|
|
3253
|
-
*/
|
|
3254
|
-
detectYAML(buffer) {
|
|
3255
|
-
try {
|
|
3256
|
-
const text = buffer.toString("utf-8", 0, Math.min(buffer.length, 512));
|
|
3257
|
-
const lines = text.split("\n").filter((line) => line.trim().length > 0);
|
|
3258
|
-
if (lines.length === 0) return false;
|
|
3259
|
-
let yamlIndicators = 0;
|
|
3260
|
-
let nonYamlIndicators = 0;
|
|
3261
|
-
for (const line of lines.slice(0, 10)) {
|
|
3262
|
-
const trimmed = line.trim();
|
|
3263
|
-
if (trimmed.startsWith("#")) continue;
|
|
3264
|
-
if (/^[\w"'-]+\s*:\s*.+$/.test(trimmed)) {
|
|
3265
|
-
yamlIndicators++;
|
|
3266
|
-
continue;
|
|
3267
|
-
}
|
|
3268
|
-
if (/^-\s+/.test(trimmed)) {
|
|
3269
|
-
yamlIndicators++;
|
|
3270
|
-
continue;
|
|
3271
|
-
}
|
|
3272
|
-
if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.endsWith(",")) {
|
|
3273
|
-
nonYamlIndicators++;
|
|
3274
|
-
}
|
|
3275
|
-
}
|
|
3276
|
-
return yamlIndicators >= 2 && nonYamlIndicators === 0;
|
|
3277
|
-
} catch {
|
|
3278
|
-
return false;
|
|
3279
|
-
}
|
|
3280
|
-
}
|
|
3281
|
-
/**
|
|
3282
|
-
* Detect CSV format
|
|
3283
|
-
*/
|
|
3284
|
-
detectCSV(buffer) {
|
|
3285
|
-
try {
|
|
3286
|
-
const text = buffer.toString("utf-8", 0, Math.min(buffer.length, 512));
|
|
3287
|
-
const lines = text.split("\n").filter((line) => line.trim().length > 0);
|
|
3288
|
-
if (lines.length < 2) return false;
|
|
3289
|
-
const delimiters = [",", ";", " "];
|
|
3290
|
-
for (const delimiter of delimiters) {
|
|
3291
|
-
const counts = lines.map((line) => {
|
|
3292
|
-
const escaped = delimiter.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3293
|
-
const matches = line.match(new RegExp(escaped, "g"));
|
|
3294
|
-
return matches ? matches.length : 0;
|
|
3295
|
-
});
|
|
3296
|
-
const firstCount = counts[0];
|
|
3297
|
-
if (firstCount > 0 && counts.every((count) => count === firstCount)) {
|
|
3298
|
-
return true;
|
|
3299
|
-
}
|
|
3300
|
-
}
|
|
3301
|
-
return false;
|
|
3302
|
-
} catch {
|
|
3303
|
-
return false;
|
|
3304
|
-
}
|
|
3305
|
-
}
|
|
3306
|
-
/**
|
|
3307
|
-
* Detect protobuf binary format
|
|
3308
|
-
*/
|
|
3309
|
-
detectProtobuf(buffer) {
|
|
3310
|
-
if (buffer.length < 4) return false;
|
|
3311
|
-
const firstByte = buffer[0];
|
|
3312
|
-
const wireType = firstByte & 7;
|
|
3313
|
-
const fieldNumber = firstByte >> 3;
|
|
3314
|
-
const hasValidWireType = wireType === 0 || wireType === 1 || wireType === 2 || wireType === 5;
|
|
3315
|
-
const hasValidFieldNumber = fieldNumber > 0 && fieldNumber < 100;
|
|
3316
|
-
const isBinary = this.isBinaryContent(buffer);
|
|
3317
|
-
return hasValidWireType && hasValidFieldNumber && isBinary;
|
|
3318
|
-
}
|
|
3319
|
-
/**
|
|
3320
|
-
* Detect plain text format
|
|
3321
|
-
*/
|
|
3322
|
-
detectPlainText(buffer) {
|
|
3323
|
-
const sample = buffer.subarray(0, Math.min(buffer.length, 512));
|
|
3324
|
-
if (sample.length === 0) return false;
|
|
3325
|
-
let binaryBytes = 0;
|
|
3326
|
-
for (const byte of sample) {
|
|
3327
|
-
if (byte === 0 || byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
|
|
3328
|
-
binaryBytes++;
|
|
3329
|
-
}
|
|
3330
|
-
}
|
|
3331
|
-
return binaryBytes / sample.length < 0.05;
|
|
3332
|
-
}
|
|
3333
|
-
/**
|
|
3334
|
-
* Check if buffer contains binary content
|
|
3335
|
-
*/
|
|
3336
|
-
isBinaryContent(buffer) {
|
|
3337
|
-
const sample = buffer.subarray(0, Math.min(buffer.length, 512));
|
|
3338
|
-
let binaryBytes = 0;
|
|
3339
|
-
for (const byte of sample) {
|
|
3340
|
-
if (byte === 0 || byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
|
|
3341
|
-
binaryBytes++;
|
|
3342
|
-
}
|
|
3343
|
-
}
|
|
3344
|
-
return binaryBytes / sample.length > 0.1;
|
|
3345
|
-
}
|
|
3346
|
-
};
|
|
3347
|
-
}
|
|
3348
|
-
});
|
|
3349
|
-
function deepClone3(obj) {
|
|
3350
|
-
if (obj === null || typeof obj !== "object") {
|
|
3351
|
-
return obj;
|
|
3352
|
-
}
|
|
3353
|
-
if (Array.isArray(obj)) {
|
|
3354
|
-
return obj.map((item) => deepClone3(item));
|
|
3355
|
-
}
|
|
3356
|
-
const cloned = {};
|
|
3357
|
-
for (const key in obj) {
|
|
3358
|
-
if (Object.hasOwn(obj, key)) {
|
|
3359
|
-
cloned[key] = deepClone3(obj[key]);
|
|
3360
|
-
}
|
|
3361
|
-
}
|
|
3362
|
-
return cloned;
|
|
3363
|
-
}
|
|
3364
|
-
function deepFreeze3(obj) {
|
|
3365
|
-
Object.freeze(obj);
|
|
3366
|
-
for (const key in obj) {
|
|
3367
|
-
if (Object.hasOwn(obj, key)) {
|
|
3368
|
-
const value = obj[key];
|
|
3369
|
-
if (value !== null && typeof value === "object") {
|
|
3370
|
-
deepFreeze3(value);
|
|
3371
|
-
}
|
|
3372
|
-
}
|
|
3373
|
-
}
|
|
3374
|
-
return obj;
|
|
3375
|
-
}
|
|
3376
|
-
async function ensureCatalogLoaded3() {
|
|
3377
|
-
if (catalogCache3 !== null) {
|
|
3378
|
-
return;
|
|
3379
|
-
}
|
|
3380
|
-
catalogCache3 = await loadMimeTypeCatalog();
|
|
3381
|
-
for (const mimeType of catalogCache3.types) {
|
|
3382
|
-
mimeStringIndex.set(mimeType.mime.toLowerCase(), mimeType);
|
|
3383
|
-
for (const ext of mimeType.extensions) {
|
|
3384
|
-
const normalized = ext.toLowerCase().replace(/^\./, "");
|
|
3385
|
-
extensionIndex.set(normalized, mimeType);
|
|
3386
|
-
}
|
|
3387
|
-
}
|
|
3388
|
-
detectorInstance = createDetector(mimeStringIndex);
|
|
3389
|
-
}
|
|
3390
|
-
async function getMimeType(mimeString) {
|
|
3391
|
-
await ensureCatalogLoaded3();
|
|
3392
|
-
const normalized = mimeString.toLowerCase();
|
|
3393
|
-
const mimeType = mimeStringIndex.get(normalized);
|
|
3394
|
-
return mimeType ? deepFreeze3(deepClone3(mimeType)) : null;
|
|
3395
|
-
}
|
|
3396
|
-
async function getMimeTypeByExtension(extension) {
|
|
3397
|
-
await ensureCatalogLoaded3();
|
|
3398
|
-
const normalized = extension.toLowerCase().replace(/^\./, "");
|
|
3399
|
-
const mimeType = extensionIndex.get(normalized);
|
|
3400
|
-
return mimeType ? deepFreeze3(deepClone3(mimeType)) : null;
|
|
3401
|
-
}
|
|
3402
|
-
async function isSupportedMimeType(mime) {
|
|
3403
|
-
await ensureCatalogLoaded3();
|
|
3404
|
-
return mimeStringIndex.has(mime.toLowerCase());
|
|
3405
|
-
}
|
|
3406
|
-
async function detectMimeType(input, options) {
|
|
3407
|
-
await ensureCatalogLoaded3();
|
|
3408
|
-
if (!detectorInstance) {
|
|
3409
|
-
throw new Error("Detector not initialized");
|
|
3410
|
-
}
|
|
3411
|
-
if (typeof input === "string") {
|
|
3412
|
-
return detectMimeTypeFromFile(input, options);
|
|
3413
|
-
}
|
|
3414
|
-
if (Buffer.isBuffer(input)) {
|
|
3415
|
-
return detectMimeTypeFromBuffer(input, options);
|
|
3416
|
-
}
|
|
3417
|
-
return detectMimeTypeFromStream(input, options);
|
|
3418
|
-
}
|
|
3419
|
-
async function detectMimeTypeFromFile(filePath, options = {}) {
|
|
3420
|
-
await ensureCatalogLoaded3();
|
|
3421
|
-
if (!detectorInstance) {
|
|
3422
|
-
throw new Error("Detector not initialized");
|
|
3423
|
-
}
|
|
3424
|
-
const bytesToRead = options.bytesToRead || 512;
|
|
3425
|
-
if (typeof Bun !== "undefined") {
|
|
3426
|
-
const file = Bun.file(filePath);
|
|
3427
|
-
if (!await file.exists()) {
|
|
3428
|
-
return null;
|
|
3429
|
-
}
|
|
3430
|
-
const slice = file.slice(0, bytesToRead);
|
|
3431
|
-
const arrayBuffer = await slice.arrayBuffer();
|
|
3432
|
-
const buffer = Buffer.from(arrayBuffer);
|
|
3433
|
-
return detectMimeTypeFromBuffer(buffer, options);
|
|
3434
|
-
}
|
|
3435
|
-
try {
|
|
3436
|
-
const buffer = await readFile(filePath, { encoding: null });
|
|
3437
|
-
const sample = buffer.subarray(0, Math.min(buffer.length, bytesToRead));
|
|
3438
|
-
return detectMimeTypeFromBuffer(sample, options);
|
|
3439
|
-
} catch (error) {
|
|
3440
|
-
if (error.code === "ENOENT") {
|
|
3441
|
-
return null;
|
|
3442
|
-
}
|
|
3443
|
-
throw error;
|
|
3444
|
-
}
|
|
3445
|
-
}
|
|
3446
|
-
function detectMimeTypeFromBuffer(buffer, options = {}) {
|
|
3447
|
-
if (!detectorInstance) {
|
|
3448
|
-
throw new Error("Detector not initialized");
|
|
3449
|
-
}
|
|
3450
|
-
const result = detectorInstance.detect(buffer, options);
|
|
3451
|
-
return result ? deepFreeze3(deepClone3(result)) : null;
|
|
3452
|
-
}
|
|
3453
|
-
async function detectMimeTypeFromStream(stream, options = {}) {
|
|
3454
|
-
await ensureCatalogLoaded3();
|
|
3455
|
-
const bytesToRead = options.bytesToRead || 512;
|
|
3456
|
-
const chunks = [];
|
|
3457
|
-
let totalBytes = 0;
|
|
3458
|
-
const webStream = typeof stream.getReader === "function" ? stream : Readable.toWeb(stream);
|
|
3459
|
-
const reader = webStream.getReader();
|
|
3460
|
-
try {
|
|
3461
|
-
while (totalBytes < bytesToRead) {
|
|
3462
|
-
const { value, done } = await reader.read();
|
|
3463
|
-
if (done) break;
|
|
3464
|
-
chunks.push(value);
|
|
3465
|
-
totalBytes += value.length;
|
|
3466
|
-
}
|
|
3467
|
-
const buffer = Buffer.concat(chunks.map((c) => Buffer.from(c)));
|
|
3468
|
-
const sample = buffer.subarray(0, Math.min(buffer.length, bytesToRead));
|
|
3469
|
-
return detectMimeTypeFromBuffer(sample, options);
|
|
3470
|
-
} finally {
|
|
3471
|
-
reader.releaseLock();
|
|
3472
|
-
}
|
|
3473
|
-
}
|
|
3474
|
-
function matchMagicNumber(buffer, mimeType) {
|
|
3475
|
-
if (!detectorInstance) {
|
|
3476
|
-
throw new Error("Detector not initialized");
|
|
3477
|
-
}
|
|
3478
|
-
const result = detectorInstance.detect(buffer);
|
|
3479
|
-
return result?.mime === mimeType;
|
|
3480
|
-
}
|
|
3481
|
-
async function listMimeTypes() {
|
|
3482
|
-
await ensureCatalogLoaded3();
|
|
3483
|
-
return Array.from(mimeStringIndex.values()).map((m) => deepFreeze3(deepClone3(m)));
|
|
3484
|
-
}
|
|
3485
|
-
function clearMimeTypeCache() {
|
|
3486
|
-
catalogCache3 = null;
|
|
3487
|
-
mimeStringIndex.clear();
|
|
3488
|
-
extensionIndex.clear();
|
|
3489
|
-
detectorInstance = null;
|
|
3490
|
-
}
|
|
3491
|
-
var catalogCache3, mimeStringIndex, extensionIndex, detectorInstance;
|
|
3492
|
-
var init_mime_types = __esm({
|
|
3493
|
-
"src/foundry/mime-types.ts"() {
|
|
3494
|
-
init_detector();
|
|
3495
|
-
init_loader();
|
|
3496
|
-
catalogCache3 = null;
|
|
3497
|
-
mimeStringIndex = /* @__PURE__ */ new Map();
|
|
3498
|
-
extensionIndex = /* @__PURE__ */ new Map();
|
|
3499
|
-
detectorInstance = null;
|
|
3500
|
-
}
|
|
3501
|
-
});
|
|
3502
|
-
function deepClone4(obj) {
|
|
3503
|
-
if (obj === null || typeof obj !== "object") {
|
|
3504
|
-
return obj;
|
|
3505
|
-
}
|
|
3506
|
-
if (Array.isArray(obj)) {
|
|
3507
|
-
return obj.map((item) => deepClone4(item));
|
|
3508
|
-
}
|
|
3509
|
-
const cloned = {};
|
|
3510
|
-
for (const key in obj) {
|
|
3511
|
-
if (Object.hasOwn(obj, key)) {
|
|
3512
|
-
cloned[key] = deepClone4(obj[key]);
|
|
3513
|
-
}
|
|
3514
|
-
}
|
|
3515
|
-
return cloned;
|
|
3516
|
-
}
|
|
3517
|
-
function deepFreeze4(obj) {
|
|
3518
|
-
Object.freeze(obj);
|
|
3519
|
-
for (const key in obj) {
|
|
3520
|
-
if (Object.hasOwn(obj, key)) {
|
|
3521
|
-
const value = obj[key];
|
|
3522
|
-
if (value !== null && typeof value === "object") {
|
|
3523
|
-
deepFreeze4(value);
|
|
3524
|
-
}
|
|
3525
|
-
}
|
|
3526
|
-
}
|
|
3527
|
-
return obj;
|
|
3528
|
-
}
|
|
3529
|
-
async function ensureCatalogLoaded4() {
|
|
3530
|
-
if (catalogCache4 !== null) {
|
|
3531
|
-
return;
|
|
3532
|
-
}
|
|
3533
|
-
catalogCache4 = await loadPatternCatalog();
|
|
3534
|
-
for (const pattern of catalogCache4.patterns) {
|
|
3535
|
-
patternIndex.set(pattern.id, pattern);
|
|
3536
|
-
}
|
|
3537
|
-
}
|
|
3538
|
-
async function getPattern(id) {
|
|
3539
|
-
await ensureCatalogLoaded4();
|
|
3540
|
-
const pattern = patternIndex.get(id);
|
|
3541
|
-
return pattern ? deepFreeze4(deepClone4(pattern)) : null;
|
|
3542
|
-
}
|
|
3543
|
-
async function getPatternRegex(id) {
|
|
3544
|
-
await ensureCatalogLoaded4();
|
|
3545
|
-
const pattern = patternIndex.get(id);
|
|
3546
|
-
if (!pattern) {
|
|
3547
|
-
return null;
|
|
3548
|
-
}
|
|
3549
|
-
if (pattern.kind !== "regex") {
|
|
3550
|
-
throw FoundryCatalogError.invalidSchema(
|
|
3551
|
-
"patterns",
|
|
3552
|
-
`Pattern '${id}' is not a regex pattern (kind: ${pattern.kind})`
|
|
3553
|
-
);
|
|
3554
|
-
}
|
|
3555
|
-
const cached = compiledRegexCache.get(id);
|
|
3556
|
-
if (cached !== void 0) {
|
|
3557
|
-
return cached;
|
|
3558
|
-
}
|
|
3559
|
-
let flags = "";
|
|
3560
|
-
if (pattern.flags?.typescript?.ignoreCase) {
|
|
3561
|
-
flags += "i";
|
|
3562
|
-
}
|
|
3563
|
-
const hasUnicodePropertyEscapes = /\\p\{/.test(pattern.pattern);
|
|
3564
|
-
if (pattern.flags?.typescript?.unicode || hasUnicodePropertyEscapes) {
|
|
3565
|
-
flags += "u";
|
|
3566
|
-
}
|
|
3567
|
-
const regex = new RegExp(pattern.pattern, flags);
|
|
3568
|
-
compiledRegexCache.set(id, regex);
|
|
3569
|
-
return regex;
|
|
3570
|
-
}
|
|
3571
|
-
function getCompiledGlob(id, pattern) {
|
|
3572
|
-
const cached = compiledGlobCache.get(id);
|
|
3573
|
-
if (cached !== void 0) {
|
|
3574
|
-
return cached;
|
|
3575
|
-
}
|
|
3576
|
-
const matcher = picomatch(pattern);
|
|
3577
|
-
compiledGlobCache.set(id, matcher);
|
|
3578
|
-
return matcher;
|
|
3579
|
-
}
|
|
3580
|
-
async function matchPattern(id, value) {
|
|
3581
|
-
await ensureCatalogLoaded4();
|
|
3582
|
-
const pattern = patternIndex.get(id);
|
|
3583
|
-
if (!pattern) {
|
|
3584
|
-
throw FoundryCatalogError.missingCatalog(`Pattern '${id}' not found`);
|
|
3585
|
-
}
|
|
3586
|
-
if (pattern.kind === "regex") {
|
|
3587
|
-
const regex = await getPatternRegex(id);
|
|
3588
|
-
return regex ? regex.test(value) : false;
|
|
3589
|
-
}
|
|
3590
|
-
if (pattern.kind === "glob") {
|
|
3591
|
-
const matcher = getCompiledGlob(id, pattern.pattern);
|
|
3592
|
-
return matcher(value);
|
|
3593
|
-
}
|
|
3594
|
-
if (pattern.kind === "literal") {
|
|
3595
|
-
return pattern.pattern === value;
|
|
3596
|
-
}
|
|
3597
|
-
throw FoundryCatalogError.invalidSchema("patterns", `Unknown pattern kind: ${pattern.kind}`);
|
|
3598
|
-
}
|
|
3599
|
-
async function listPatterns() {
|
|
3600
|
-
await ensureCatalogLoaded4();
|
|
3601
|
-
return Array.from(patternIndex.values()).map((p) => deepFreeze4(deepClone4(p)));
|
|
3602
|
-
}
|
|
3603
|
-
async function describePattern(id) {
|
|
3604
|
-
const pattern = await getPattern(id);
|
|
3605
|
-
return pattern?.description ?? null;
|
|
3606
|
-
}
|
|
3607
|
-
function clearPatternCache() {
|
|
3608
|
-
catalogCache4 = null;
|
|
3609
|
-
patternIndex.clear();
|
|
3610
|
-
compiledRegexCache.clear();
|
|
3611
|
-
compiledGlobCache.clear();
|
|
3612
|
-
}
|
|
3613
|
-
var catalogCache4, patternIndex, compiledRegexCache, compiledGlobCache;
|
|
3614
|
-
var init_patterns = __esm({
|
|
3615
|
-
"src/foundry/patterns.ts"() {
|
|
3616
|
-
init_errors2();
|
|
3617
|
-
init_loader();
|
|
3618
|
-
catalogCache4 = null;
|
|
3619
|
-
patternIndex = /* @__PURE__ */ new Map();
|
|
3620
|
-
compiledRegexCache = /* @__PURE__ */ new Map();
|
|
3621
|
-
compiledGlobCache = /* @__PURE__ */ new Map();
|
|
3622
|
-
}
|
|
3623
|
-
});
|
|
3624
|
-
function getConfigPath() {
|
|
3625
|
-
if (__dirname3.includes("/dist/")) {
|
|
3626
|
-
return join(__dirname3, "../../config/crucible-ts/library/foundry/signals.yaml");
|
|
3627
|
-
}
|
|
3628
|
-
return join(__dirname3, "../../../config/crucible-ts/library/foundry/signals.yaml");
|
|
3629
|
-
}
|
|
3630
|
-
async function loadCatalog2() {
|
|
3631
|
-
const filePath = SSOT_PATHS2.signals;
|
|
3632
|
-
const catalogName = "signals";
|
|
3633
|
-
try {
|
|
3634
|
-
let content;
|
|
3635
|
-
if (typeof Bun !== "undefined") {
|
|
3636
|
-
try {
|
|
3637
|
-
const file = Bun.file(filePath);
|
|
3638
|
-
if (!await file.exists()) {
|
|
3639
|
-
throw FoundryCatalogError.missingCatalog(catalogName);
|
|
3640
|
-
}
|
|
3641
|
-
content = await file.text();
|
|
3642
|
-
} catch (error) {
|
|
3643
|
-
if (error instanceof Error && error.message.includes("No such file")) {
|
|
3644
|
-
throw FoundryCatalogError.missingCatalog(catalogName);
|
|
3645
|
-
}
|
|
3646
|
-
throw error;
|
|
3647
|
-
}
|
|
3648
|
-
} else {
|
|
3649
|
-
try {
|
|
3650
|
-
content = await readFile(filePath, "utf-8");
|
|
3651
|
-
} catch (error) {
|
|
3652
|
-
if (error.code === "ENOENT") {
|
|
3653
|
-
throw FoundryCatalogError.missingCatalog(catalogName);
|
|
3654
|
-
}
|
|
3655
|
-
throw error;
|
|
3656
|
-
}
|
|
3657
|
-
}
|
|
3658
|
-
const data = parse(content);
|
|
3659
|
-
const result = await validateDataBySchemaId(data, SCHEMA_ID);
|
|
3660
|
-
if (!result.valid) {
|
|
3661
|
-
const errorMessages = result.diagnostics.map((d) => `${d.pointer}: ${d.message}`).join("; ");
|
|
3662
|
-
throw FoundryCatalogError.invalidSchema(
|
|
3663
|
-
catalogName,
|
|
3664
|
-
`Schema validation failed: ${errorMessages}`
|
|
3665
|
-
);
|
|
3666
|
-
}
|
|
3667
|
-
return data;
|
|
3668
|
-
} catch (error) {
|
|
3669
|
-
if (error instanceof FoundryCatalogError) {
|
|
3670
|
-
throw error;
|
|
3671
|
-
}
|
|
3672
|
-
const err = error;
|
|
3673
|
-
if (err.code === "ENOENT") {
|
|
3674
|
-
throw FoundryCatalogError.missingCatalog(catalogName);
|
|
3675
|
-
} else if (err.code === "EACCES") {
|
|
3676
|
-
throw FoundryCatalogError.invalidSchema(
|
|
3677
|
-
catalogName,
|
|
3678
|
-
"Permission denied accessing catalog file",
|
|
3679
|
-
err
|
|
3680
|
-
);
|
|
3681
|
-
} else if (err.code === "EISDIR") {
|
|
3682
|
-
throw FoundryCatalogError.invalidSchema(
|
|
3683
|
-
catalogName,
|
|
3684
|
-
"Expected file but found directory",
|
|
3685
|
-
err
|
|
3686
|
-
);
|
|
3687
|
-
} else if (err.code === "EMFILE" || err.code === "ENFILE") {
|
|
3688
|
-
throw FoundryCatalogError.invalidSchema(catalogName, "Too many open files", err);
|
|
3689
|
-
}
|
|
3690
|
-
throw FoundryCatalogError.invalidSchema(catalogName, "Failed to load catalog", err);
|
|
3691
|
-
}
|
|
3692
|
-
}
|
|
3693
|
-
async function getCatalog() {
|
|
3694
|
-
if (!cachedCatalog) {
|
|
3695
|
-
cachedCatalog = await loadCatalog2();
|
|
3696
|
-
}
|
|
3697
|
-
return cachedCatalog;
|
|
3698
|
-
}
|
|
3699
|
-
async function getSignalsVersion() {
|
|
3700
|
-
const catalog = await getCatalog();
|
|
3701
|
-
return catalog.version;
|
|
3702
|
-
}
|
|
3703
|
-
async function listSignals() {
|
|
3704
|
-
const catalog = await getCatalog();
|
|
3705
|
-
return catalog.signals.map((signal) => ({ ...signal }));
|
|
3706
|
-
}
|
|
3707
|
-
async function getSignal(identifier) {
|
|
3708
|
-
const catalog = await getCatalog();
|
|
3709
|
-
const signal = catalog.signals.find((s) => s.id === identifier || s.name === identifier);
|
|
3710
|
-
return signal ? { ...signal } : null;
|
|
3711
|
-
}
|
|
3712
|
-
async function listBehaviors() {
|
|
3713
|
-
const catalog = await getCatalog();
|
|
3714
|
-
return catalog.behaviors.map((behavior) => ({ ...behavior }));
|
|
3715
|
-
}
|
|
3716
|
-
async function getBehavior(id) {
|
|
3717
|
-
const catalog = await getCatalog();
|
|
3718
|
-
const behavior = catalog.behaviors.find((b) => b.id === id);
|
|
3719
|
-
return behavior ? { ...behavior } : null;
|
|
3720
|
-
}
|
|
3721
|
-
async function getSignalCatalog() {
|
|
3722
|
-
return await getCatalog();
|
|
3723
|
-
}
|
|
3724
|
-
var __filename2, __dirname3, SSOT_PATHS2, SCHEMA_ID, cachedCatalog;
|
|
3725
|
-
var init_catalog = __esm({
|
|
3726
|
-
"src/foundry/signals/catalog.ts"() {
|
|
3727
|
-
init_validator();
|
|
3728
|
-
init_errors2();
|
|
3729
|
-
__filename2 = fileURLToPath(import.meta.url);
|
|
3730
|
-
__dirname3 = dirname(__filename2);
|
|
3731
|
-
SSOT_PATHS2 = {
|
|
3732
|
-
signals: getConfigPath()
|
|
3733
|
-
};
|
|
3734
|
-
SCHEMA_ID = "library/foundry/v1.0.0/signals";
|
|
3735
|
-
cachedCatalog = null;
|
|
3736
|
-
}
|
|
3737
|
-
});
|
|
3738
|
-
|
|
3739
|
-
// src/foundry/signals/capabilities.ts
|
|
3740
|
-
function getPlatform2() {
|
|
3741
|
-
const platform = process.platform;
|
|
3742
|
-
switch (platform) {
|
|
3743
|
-
case "linux":
|
|
3744
|
-
return "linux";
|
|
3745
|
-
case "darwin":
|
|
3746
|
-
return "darwin";
|
|
3747
|
-
case "win32":
|
|
3748
|
-
return "win32";
|
|
3749
|
-
case "freebsd":
|
|
3750
|
-
return "freebsd";
|
|
3751
|
-
default:
|
|
3752
|
-
return "unknown";
|
|
3753
|
-
}
|
|
3754
|
-
}
|
|
3755
|
-
function isPOSIX2() {
|
|
3756
|
-
const platform = getPlatform2();
|
|
3757
|
-
return platform === "linux" || platform === "darwin" || platform === "freebsd";
|
|
3758
|
-
}
|
|
3759
|
-
function isWindows2() {
|
|
3760
|
-
return getPlatform2() === "win32";
|
|
3761
|
-
}
|
|
3762
|
-
async function supportsSignal(signalName) {
|
|
3763
|
-
const signal = await getSignal(signalName);
|
|
3764
|
-
if (!signal) {
|
|
3765
|
-
return false;
|
|
3766
|
-
}
|
|
3767
|
-
if (isPOSIX2()) {
|
|
3768
|
-
return true;
|
|
3769
|
-
}
|
|
3770
|
-
if (isWindows2()) {
|
|
3771
|
-
return signal.windows_event !== null;
|
|
3772
|
-
}
|
|
3773
|
-
return false;
|
|
3774
|
-
}
|
|
3775
|
-
function supportsSignalExitCodes2() {
|
|
3776
|
-
return isPOSIX2();
|
|
3777
|
-
}
|
|
3778
|
-
async function getPlatformCapabilities2() {
|
|
3779
|
-
const platform = getPlatform2();
|
|
3780
|
-
const isPosix = isPOSIX2();
|
|
3781
|
-
const isWin = isWindows2();
|
|
3782
|
-
const catalog = await getSignalCatalog();
|
|
3783
|
-
const supported = [];
|
|
3784
|
-
const unsupported = [];
|
|
3785
|
-
const mapped = [];
|
|
3786
|
-
for (const signal of catalog.signals) {
|
|
3787
|
-
if (isPosix) {
|
|
3788
|
-
supported.push(signal.name);
|
|
3789
|
-
} else if (isWin) {
|
|
3790
|
-
if (signal.windows_event !== null) {
|
|
3791
|
-
supported.push(signal.name);
|
|
3792
|
-
mapped.push(signal.name);
|
|
3793
|
-
} else {
|
|
3794
|
-
unsupported.push(signal.name);
|
|
3795
|
-
}
|
|
3796
|
-
} else {
|
|
3797
|
-
unsupported.push(signal.name);
|
|
3798
|
-
}
|
|
3799
|
-
}
|
|
3800
|
-
return {
|
|
3801
|
-
platform,
|
|
3802
|
-
isPOSIX: isPosix,
|
|
3803
|
-
isWindows: isWin,
|
|
3804
|
-
supportsNativeSignals: isPosix,
|
|
3805
|
-
supportsSignalExitCodes: supportsSignalExitCodes2(),
|
|
3806
|
-
supportedSignals: supported,
|
|
3807
|
-
unsupportedSignals: unsupported,
|
|
3808
|
-
mappedSignals: mapped
|
|
3809
|
-
};
|
|
3810
|
-
}
|
|
3811
|
-
async function getSignalNumber(signalName) {
|
|
3812
|
-
const signal = await getSignal(signalName);
|
|
3813
|
-
if (!signal) {
|
|
3814
|
-
return null;
|
|
3815
|
-
}
|
|
3816
|
-
const platform = getPlatform2();
|
|
3817
|
-
if (signal.platform_overrides) {
|
|
3818
|
-
if (platform === "darwin" && signal.platform_overrides.darwin !== void 0) {
|
|
3819
|
-
return signal.platform_overrides.darwin;
|
|
3820
|
-
}
|
|
3821
|
-
if (platform === "freebsd" && signal.platform_overrides.freebsd !== void 0) {
|
|
3822
|
-
return signal.platform_overrides.freebsd;
|
|
3823
|
-
}
|
|
3824
|
-
}
|
|
3825
|
-
return signal.unix_number;
|
|
3826
|
-
}
|
|
3827
|
-
async function getWindowsEvent(signalName) {
|
|
3828
|
-
const signal = await getSignal(signalName);
|
|
3829
|
-
if (!signal) {
|
|
3830
|
-
return null;
|
|
3831
|
-
}
|
|
3832
|
-
return signal.windows_event;
|
|
3833
|
-
}
|
|
3834
|
-
var init_capabilities2 = __esm({
|
|
3835
|
-
"src/foundry/signals/capabilities.ts"() {
|
|
3836
|
-
init_catalog();
|
|
3837
|
-
}
|
|
3838
|
-
});
|
|
3839
|
-
|
|
3840
|
-
// src/foundry/signals/config-reload-endpoint.ts
|
|
3841
|
-
function createConfigReloadEndpoint(options) {
|
|
3842
|
-
const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
|
|
3843
|
-
return async (payload, req) => {
|
|
3844
|
-
const correlationId = payload.correlation_id ?? generateCorrelationId();
|
|
3845
|
-
const authResult = await auth(req);
|
|
3846
|
-
if (!authResult.authenticated) {
|
|
3847
|
-
if (logger) {
|
|
3848
|
-
logger.warn("Config reload endpoint: authentication failed", {
|
|
3849
|
-
correlation_id: correlationId,
|
|
3850
|
-
reason: authResult.reason
|
|
3851
|
-
});
|
|
3852
|
-
}
|
|
3853
|
-
if (telemetry) {
|
|
3854
|
-
telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
|
|
3855
|
-
correlation_id: correlationId
|
|
3856
|
-
});
|
|
3857
|
-
}
|
|
3858
|
-
return {
|
|
3859
|
-
status: "error",
|
|
3860
|
-
error: "authentication_failed",
|
|
3861
|
-
message: authResult.reason || "Authentication required",
|
|
3862
|
-
statusCode: 401
|
|
3863
|
-
};
|
|
3864
|
-
}
|
|
3865
|
-
const identity = authResult.identity || "unknown";
|
|
3866
|
-
if (rateLimit) {
|
|
3867
|
-
const rateLimitResult = await rateLimit(identity);
|
|
3868
|
-
if (!rateLimitResult.allowed) {
|
|
3869
|
-
if (logger) {
|
|
3870
|
-
logger.warn("Config reload endpoint: rate limit exceeded", {
|
|
3871
|
-
correlation_id: correlationId,
|
|
3872
|
-
identity
|
|
3873
|
-
});
|
|
3874
|
-
}
|
|
3875
|
-
if (telemetry) {
|
|
3876
|
-
telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
|
|
3877
|
-
correlation_id: correlationId
|
|
3878
|
-
});
|
|
3879
|
-
}
|
|
3880
|
-
return {
|
|
3881
|
-
status: "error",
|
|
3882
|
-
error: "rate_limit_exceeded",
|
|
3883
|
-
message: "Rate limit exceeded. Please try again later.",
|
|
3884
|
-
statusCode: 429
|
|
3885
|
-
};
|
|
3886
|
-
}
|
|
3887
|
-
}
|
|
3888
|
-
if (telemetry) {
|
|
3889
|
-
telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
|
|
3890
|
-
correlation_id: correlationId
|
|
3891
|
-
});
|
|
3892
|
-
}
|
|
3893
|
-
try {
|
|
3894
|
-
const config = await loader();
|
|
3895
|
-
if (validator) {
|
|
3896
|
-
const validation = await validator(config);
|
|
3897
|
-
if (!validation.valid) {
|
|
3898
|
-
if (logger) {
|
|
3899
|
-
logger.warn("Config reload endpoint: validation failed", {
|
|
3900
|
-
correlation_id: correlationId,
|
|
3901
|
-
error_count: validation.errors?.length ?? 0
|
|
3902
|
-
});
|
|
3903
|
-
}
|
|
3904
|
-
if (telemetry) {
|
|
3905
|
-
telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
|
|
3906
|
-
correlation_id: correlationId,
|
|
3907
|
-
reason: "validation_failed"
|
|
3908
|
-
});
|
|
3909
|
-
}
|
|
3910
|
-
return {
|
|
3911
|
-
status: "error",
|
|
3912
|
-
error: "validation_failed",
|
|
3913
|
-
message: "Configuration validation failed",
|
|
3914
|
-
validation_errors: validation.errors,
|
|
3915
|
-
statusCode: 422
|
|
3916
|
-
};
|
|
3917
|
-
}
|
|
3918
|
-
}
|
|
3919
|
-
if (onReload2) {
|
|
3920
|
-
await onReload2(config);
|
|
3921
|
-
}
|
|
3922
|
-
if (telemetry) {
|
|
3923
|
-
telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
|
|
3924
|
-
correlation_id: correlationId
|
|
3925
|
-
});
|
|
3926
|
-
}
|
|
3927
|
-
if (logger) {
|
|
3928
|
-
logger.info("Config reload endpoint: reload accepted", {
|
|
3929
|
-
correlation_id: correlationId,
|
|
3930
|
-
reason: payload.reason
|
|
3931
|
-
});
|
|
3932
|
-
}
|
|
3933
|
-
return {
|
|
3934
|
-
status: "reloaded",
|
|
3935
|
-
correlation_id: correlationId,
|
|
3936
|
-
message: "Configuration reloaded",
|
|
3937
|
-
statusCode: 200
|
|
3938
|
-
};
|
|
3939
|
-
} catch (error) {
|
|
3940
|
-
if (logger) {
|
|
3941
|
-
logger.warn("Config reload endpoint: reload failed", {
|
|
3942
|
-
correlation_id: correlationId,
|
|
3943
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3944
|
-
});
|
|
3945
|
-
}
|
|
3946
|
-
if (telemetry) {
|
|
3947
|
-
telemetry.emit("fulmen.config.http_endpoint.reload_error", {
|
|
3948
|
-
correlation_id: correlationId,
|
|
3949
|
-
error_type: error instanceof Error ? error.constructor.name : "unknown"
|
|
3950
|
-
});
|
|
3951
|
-
}
|
|
3952
|
-
return {
|
|
3953
|
-
status: "error",
|
|
3954
|
-
error: "reload_failed",
|
|
3955
|
-
message: error instanceof Error ? error.message : String(error),
|
|
3956
|
-
statusCode: 500
|
|
3957
|
-
};
|
|
3958
|
-
}
|
|
3959
|
-
};
|
|
3960
|
-
}
|
|
3961
|
-
function generateCorrelationId() {
|
|
3962
|
-
return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
3963
|
-
}
|
|
3964
|
-
var init_config_reload_endpoint = __esm({
|
|
3965
|
-
"src/foundry/signals/config-reload-endpoint.ts"() {
|
|
3966
|
-
}
|
|
3967
|
-
});
|
|
3968
|
-
|
|
3969
|
-
// src/appidentity/runtime.ts
|
|
3970
|
-
function detectRuntime() {
|
|
3971
|
-
const versions = process.versions;
|
|
3972
|
-
if (typeof versions.bun === "string" && versions.bun.length > 0) {
|
|
3973
|
-
return { name: "bun", version: versions.bun };
|
|
3974
|
-
}
|
|
3975
|
-
if (typeof versions.node === "string" && versions.node.length > 0) {
|
|
3976
|
-
return { name: "node", version: versions.node };
|
|
3977
|
-
}
|
|
3978
|
-
return { name: "unknown" };
|
|
3979
|
-
}
|
|
3980
|
-
function buildRuntimeInfo(options = {}) {
|
|
3981
|
-
const runtime = detectRuntime();
|
|
3982
|
-
const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
|
|
3983
|
-
const vendor = options.vendor ?? options.identity?.app.vendor;
|
|
3984
|
-
return {
|
|
3985
|
-
service: {
|
|
3986
|
-
name: serviceName,
|
|
3987
|
-
vendor,
|
|
3988
|
-
version: options.version
|
|
3989
|
-
},
|
|
3990
|
-
runtime,
|
|
3991
|
-
platform: {
|
|
3992
|
-
os: process.platform,
|
|
3993
|
-
arch: process.arch
|
|
3994
|
-
}
|
|
3995
|
-
};
|
|
3996
|
-
}
|
|
3997
|
-
var init_runtime = __esm({
|
|
3998
|
-
"src/appidentity/runtime.ts"() {
|
|
3999
|
-
}
|
|
4000
|
-
});
|
|
4001
|
-
|
|
4002
|
-
// src/foundry/signals/control-discovery-endpoint.ts
|
|
4003
|
-
function createControlDiscoveryEndpoint(options) {
|
|
4004
|
-
const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
|
|
4005
|
-
return async (req) => {
|
|
4006
|
-
if (auth) {
|
|
4007
|
-
const authResult = await auth(req);
|
|
4008
|
-
if (!authResult.authenticated) {
|
|
4009
|
-
if (logger) {
|
|
4010
|
-
logger.warn("Control discovery endpoint: authentication failed", {
|
|
4011
|
-
reason: authResult.reason
|
|
4012
|
-
});
|
|
4013
|
-
}
|
|
4014
|
-
if (telemetry) {
|
|
4015
|
-
telemetry.emit("fulmen.control.discovery.auth_failed", {
|
|
4016
|
-
service: identity.app.binary_name
|
|
4017
|
-
});
|
|
4018
|
-
}
|
|
4019
|
-
return {
|
|
4020
|
-
status: "error",
|
|
4021
|
-
error: "authentication_failed",
|
|
4022
|
-
message: authResult.reason || "Authentication required",
|
|
4023
|
-
statusCode: 401
|
|
4024
|
-
};
|
|
4025
|
-
}
|
|
4026
|
-
}
|
|
4027
|
-
if (telemetry) {
|
|
4028
|
-
telemetry.emit("fulmen.control.discovery.served", {
|
|
4029
|
-
service: identity.app.binary_name
|
|
4030
|
-
});
|
|
4031
|
-
}
|
|
4032
|
-
const runtime = buildRuntimeInfo({ identity, version });
|
|
4033
|
-
return {
|
|
4034
|
-
status: "ok",
|
|
4035
|
-
service: {
|
|
4036
|
-
name: identity.app.binary_name,
|
|
4037
|
-
vendor: identity.app.vendor,
|
|
4038
|
-
version
|
|
4039
|
-
},
|
|
4040
|
-
runtime: {
|
|
4041
|
-
name: runtime.runtime.name,
|
|
4042
|
-
version: runtime.runtime.version,
|
|
4043
|
-
platform: runtime.platform.os,
|
|
4044
|
-
arch: runtime.platform.arch
|
|
4045
|
-
},
|
|
4046
|
-
auth_summary: authSummary,
|
|
4047
|
-
endpoints,
|
|
4048
|
-
statusCode: 200
|
|
4049
|
-
};
|
|
4050
|
-
};
|
|
4051
|
-
}
|
|
4052
|
-
var init_control_discovery_endpoint = __esm({
|
|
4053
|
-
"src/foundry/signals/control-discovery-endpoint.ts"() {
|
|
4054
|
-
init_runtime();
|
|
4055
|
-
}
|
|
4056
|
-
});
|
|
4057
|
-
|
|
4058
|
-
// src/foundry/signals/convenience.ts
|
|
4059
|
-
async function onShutdown(manager, handler, options = {}) {
|
|
4060
|
-
await manager.register("SIGTERM", handler, options);
|
|
4061
|
-
await manager.register("SIGINT", handler, options);
|
|
4062
|
-
}
|
|
4063
|
-
async function onReload(manager, handler, options = {}) {
|
|
4064
|
-
await manager.register("SIGHUP", handler, options);
|
|
4065
|
-
}
|
|
4066
|
-
async function onUSR1(manager, handler, options = {}) {
|
|
4067
|
-
await manager.register("SIGUSR1", handler, options);
|
|
4068
|
-
}
|
|
4069
|
-
async function onUSR2(manager, handler, options = {}) {
|
|
4070
|
-
await manager.register("SIGUSR2", handler, options);
|
|
4071
|
-
}
|
|
4072
|
-
async function onEmergencyQuit(manager, handler, options = {}) {
|
|
4073
|
-
await manager.register("SIGQUIT", handler, options);
|
|
4074
|
-
}
|
|
4075
|
-
async function onAnyShutdown(manager, handler, options = {}) {
|
|
4076
|
-
await manager.register("SIGTERM", handler, options);
|
|
4077
|
-
await manager.register("SIGINT", handler, options);
|
|
4078
|
-
await manager.register("SIGQUIT", handler, options);
|
|
4079
|
-
}
|
|
4080
|
-
var init_convenience = __esm({
|
|
4081
|
-
"src/foundry/signals/convenience.ts"() {
|
|
4082
|
-
}
|
|
4083
|
-
});
|
|
4084
|
-
|
|
4085
|
-
// src/foundry/signals/double-tap.ts
|
|
4086
|
-
async function createDoubleTapTracker(signalName, config = {}) {
|
|
4087
|
-
const signal = await getSignal(signalName);
|
|
4088
|
-
const defaultWindowMs = signal?.double_tap_window_seconds ? signal.double_tap_window_seconds * 1e3 : 2e3;
|
|
4089
|
-
const defaultExitCode = signal?.double_tap_exit_code || 130;
|
|
4090
|
-
const defaultHintMessage = signal?.double_tap_message || "Press Ctrl+C again within 2s to force quit";
|
|
4091
|
-
return {
|
|
4092
|
-
firstTapTime: null,
|
|
4093
|
-
windowMs: config.windowMs ?? defaultWindowMs,
|
|
4094
|
-
exitCode: config.exitCode ?? defaultExitCode,
|
|
4095
|
-
hintMessage: config.hintMessage ?? defaultHintMessage,
|
|
4096
|
-
logger: config.logger,
|
|
4097
|
-
testMode: config.testMode ?? false
|
|
4098
|
-
};
|
|
4099
|
-
}
|
|
4100
|
-
function handleDoubleTap(state) {
|
|
4101
|
-
const now = Date.now();
|
|
4102
|
-
if (state.firstTapTime === null) {
|
|
4103
|
-
state.firstTapTime = now;
|
|
4104
|
-
if (state.logger) {
|
|
4105
|
-
state.logger.info(state.hintMessage);
|
|
4106
|
-
} else {
|
|
4107
|
-
console.log(state.hintMessage);
|
|
4108
|
-
}
|
|
4109
|
-
return false;
|
|
4110
|
-
}
|
|
4111
|
-
const elapsed = now - state.firstTapTime;
|
|
4112
|
-
if (elapsed < state.windowMs) {
|
|
4113
|
-
if (state.logger) {
|
|
4114
|
-
state.logger.info("Force quitting...");
|
|
4115
|
-
} else {
|
|
4116
|
-
console.log("Force quitting...");
|
|
4117
|
-
}
|
|
4118
|
-
if (!state.testMode) {
|
|
4119
|
-
process.exit(state.exitCode);
|
|
4120
|
-
}
|
|
4121
|
-
return true;
|
|
4122
|
-
}
|
|
4123
|
-
state.firstTapTime = now;
|
|
4124
|
-
if (state.logger) {
|
|
4125
|
-
state.logger.info(state.hintMessage);
|
|
4126
|
-
} else {
|
|
4127
|
-
console.log(state.hintMessage);
|
|
4128
|
-
}
|
|
4129
|
-
return false;
|
|
4130
|
-
}
|
|
4131
|
-
function resetDoubleTap(state) {
|
|
4132
|
-
state.firstTapTime = null;
|
|
4133
|
-
}
|
|
4134
|
-
function isWithinWindow(state) {
|
|
4135
|
-
if (state.firstTapTime === null) {
|
|
4136
|
-
return false;
|
|
4137
|
-
}
|
|
4138
|
-
const elapsed = Date.now() - state.firstTapTime;
|
|
4139
|
-
return elapsed < state.windowMs;
|
|
4140
|
-
}
|
|
4141
|
-
function getWindowTimeRemaining(state) {
|
|
4142
|
-
if (state.firstTapTime === null) {
|
|
4143
|
-
return null;
|
|
4144
|
-
}
|
|
4145
|
-
const elapsed = Date.now() - state.firstTapTime;
|
|
4146
|
-
const remaining = state.windowMs - elapsed;
|
|
4147
|
-
return remaining > 0 ? remaining : null;
|
|
4148
|
-
}
|
|
4149
|
-
var init_double_tap = __esm({
|
|
4150
|
-
"src/foundry/signals/double-tap.ts"() {
|
|
4151
|
-
init_catalog();
|
|
4152
|
-
}
|
|
4153
|
-
});
|
|
4154
|
-
|
|
4155
|
-
// src/foundry/signals/windows.ts
|
|
4156
|
-
async function handleWindowsFallback(signalName, options = {}) {
|
|
4157
|
-
const signal = await getSignal(signalName);
|
|
4158
|
-
if (!signal) {
|
|
4159
|
-
return {
|
|
4160
|
-
supported: false,
|
|
4161
|
-
logged: false
|
|
4162
|
-
};
|
|
4163
|
-
}
|
|
4164
|
-
if (signal.windows_event !== null) {
|
|
4165
|
-
return {
|
|
4166
|
-
supported: true,
|
|
4167
|
-
logged: false
|
|
4168
|
-
};
|
|
4169
|
-
}
|
|
4170
|
-
const fallback = signal.windows_fallback;
|
|
4171
|
-
if (!fallback) {
|
|
4172
|
-
return {
|
|
4173
|
-
supported: false,
|
|
4174
|
-
logged: false
|
|
4175
|
-
};
|
|
4176
|
-
}
|
|
4177
|
-
if (options.silent) {
|
|
4178
|
-
return {
|
|
4179
|
-
supported: false,
|
|
4180
|
-
fallback,
|
|
4181
|
-
logged: false
|
|
4182
|
-
};
|
|
4183
|
-
}
|
|
4184
|
-
const logger = options.logger || defaultLogger;
|
|
4185
|
-
const telemetry = options.telemetry;
|
|
4186
|
-
const logMeta = {
|
|
4187
|
-
signal: signal.name,
|
|
4188
|
-
platform: "windows",
|
|
4189
|
-
fallback: fallback.fallback_behavior,
|
|
4190
|
-
operation_hint: fallback.operation_hint
|
|
4191
|
-
};
|
|
4192
|
-
logger.info(fallback.log_message, logMeta);
|
|
4193
|
-
if (telemetry) {
|
|
4194
|
-
telemetry.emit(fallback.telemetry_event, fallback.telemetry_tags);
|
|
4195
|
-
}
|
|
4196
|
-
return {
|
|
4197
|
-
supported: false,
|
|
4198
|
-
fallback,
|
|
4199
|
-
logged: true
|
|
4200
|
-
};
|
|
4201
|
-
}
|
|
4202
|
-
async function getFallbackMetadata(signalName) {
|
|
4203
|
-
const signal = await getSignal(signalName);
|
|
4204
|
-
if (!signal || !signal.windows_fallback) {
|
|
4205
|
-
return null;
|
|
4206
|
-
}
|
|
4207
|
-
return signal.windows_fallback;
|
|
4208
|
-
}
|
|
4209
|
-
async function requiresFallback(signalName) {
|
|
4210
|
-
const signal = await getSignal(signalName);
|
|
4211
|
-
if (!signal) {
|
|
4212
|
-
return false;
|
|
4213
|
-
}
|
|
4214
|
-
return signal.windows_event === null && signal.windows_fallback !== void 0;
|
|
4215
|
-
}
|
|
4216
|
-
async function getHttpFallbackGuidance(signalName) {
|
|
4217
|
-
const fallback = await getFallbackMetadata(signalName);
|
|
4218
|
-
if (!fallback || fallback.fallback_behavior !== "http_admin_endpoint") {
|
|
4219
|
-
return null;
|
|
4220
|
-
}
|
|
4221
|
-
return fallback.operation_hint;
|
|
4222
|
-
}
|
|
4223
|
-
var defaultLogger;
|
|
4224
|
-
var init_windows = __esm({
|
|
4225
|
-
"src/foundry/signals/windows.ts"() {
|
|
4226
|
-
init_catalog();
|
|
4227
|
-
defaultLogger = {
|
|
4228
|
-
info(message, meta) {
|
|
4229
|
-
if (meta) {
|
|
4230
|
-
console.info(message, meta);
|
|
4231
|
-
} else {
|
|
4232
|
-
console.info(message);
|
|
4233
|
-
}
|
|
4234
|
-
},
|
|
4235
|
-
warn(message, meta) {
|
|
4236
|
-
if (meta) {
|
|
4237
|
-
console.warn(message, meta);
|
|
4238
|
-
} else {
|
|
4239
|
-
console.warn(message);
|
|
4240
|
-
}
|
|
4241
|
-
}
|
|
4242
|
-
};
|
|
4243
|
-
}
|
|
4244
|
-
});
|
|
4245
|
-
|
|
4246
|
-
// src/foundry/signals/guards.ts
|
|
4247
|
-
async function ensureSupported(signalName, options = {}) {
|
|
4248
|
-
const { includeGuidance = true } = options;
|
|
4249
|
-
const signal = await getSignal(signalName);
|
|
4250
|
-
if (!signal) {
|
|
4251
|
-
throw FoundryCatalogError.invalidSchema(
|
|
4252
|
-
"signals",
|
|
4253
|
-
`Signal "${signalName}" not found in catalog. Valid signals: SIGTERM, SIGINT, SIGHUP, SIGQUIT, SIGPIPE, SIGALRM, SIGUSR1, SIGUSR2`
|
|
4254
|
-
);
|
|
4255
|
-
}
|
|
4256
|
-
const supported = await supportsSignal(signalName);
|
|
4257
|
-
if (supported) {
|
|
4258
|
-
return;
|
|
4259
|
-
}
|
|
4260
|
-
let message = `Signal ${signal.name} is not supported on this platform`;
|
|
4261
|
-
if (isWindows2() && includeGuidance) {
|
|
4262
|
-
const fallback = await getFallbackMetadata(signalName);
|
|
4263
|
-
if (fallback) {
|
|
4264
|
-
message += `. ${fallback.log_message}`;
|
|
4265
|
-
switch (fallback.fallback_behavior) {
|
|
4266
|
-
case "http_admin_endpoint":
|
|
4267
|
-
message += `. Use HTTP endpoint: ${fallback.operation_hint}`;
|
|
4268
|
-
break;
|
|
4269
|
-
case "exception_handling":
|
|
4270
|
-
message += `. Alternative: ${fallback.operation_hint}`;
|
|
4271
|
-
break;
|
|
4272
|
-
case "timer_api":
|
|
4273
|
-
message += `. Alternative: ${fallback.operation_hint}`;
|
|
4274
|
-
break;
|
|
4275
|
-
}
|
|
4276
|
-
}
|
|
4277
|
-
}
|
|
4278
|
-
throw FoundryCatalogError.invalidSchema("signals", message);
|
|
4279
|
-
}
|
|
4280
|
-
function ensureSignalExitCodesSupported() {
|
|
4281
|
-
if (!isPOSIX2()) {
|
|
4282
|
-
throw FoundryCatalogError.invalidSchema(
|
|
4283
|
-
"signals",
|
|
4284
|
-
"Signal-based exit codes (128+N pattern) are not supported on this platform. Windows does not propagate signal numbers via exit codes. Use explicit exit codes or monitor via HTTP admin endpoint."
|
|
4285
|
-
);
|
|
4286
|
-
}
|
|
4287
|
-
}
|
|
4288
|
-
function ensurePOSIX() {
|
|
4289
|
-
if (!isPOSIX2()) {
|
|
4290
|
-
throw FoundryCatalogError.invalidSchema(
|
|
4291
|
-
"signals",
|
|
4292
|
-
"This operation requires a POSIX-compliant platform (Linux, macOS, FreeBSD). Current platform does not support native signal handling."
|
|
4293
|
-
);
|
|
4294
|
-
}
|
|
4295
|
-
}
|
|
4296
|
-
function ensureWindows() {
|
|
4297
|
-
if (!isWindows2()) {
|
|
4298
|
-
throw FoundryCatalogError.invalidSchema(
|
|
4299
|
-
"signals",
|
|
4300
|
-
"This operation requires Windows platform. Current platform uses native signals."
|
|
4301
|
-
);
|
|
4302
|
-
}
|
|
4303
|
-
}
|
|
4304
|
-
var init_guards = __esm({
|
|
4305
|
-
"src/foundry/signals/guards.ts"() {
|
|
4306
|
-
init_errors2();
|
|
4307
|
-
init_capabilities2();
|
|
4308
|
-
init_catalog();
|
|
4309
|
-
init_windows();
|
|
4310
|
-
}
|
|
4311
|
-
});
|
|
4312
|
-
|
|
4313
|
-
// src/foundry/signals/http-helper.ts
|
|
4314
|
-
function createSignalEndpoint(options) {
|
|
4315
|
-
const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
|
|
4316
|
-
return async (payload, req) => {
|
|
4317
|
-
const correlationId = payload.correlation_id ?? generateCorrelationId2();
|
|
4318
|
-
const authResult = await auth(req);
|
|
4319
|
-
if (!authResult.authenticated) {
|
|
4320
|
-
if (logger) {
|
|
4321
|
-
logger.warn("Signal endpoint: authentication failed", {
|
|
4322
|
-
correlation_id: correlationId,
|
|
4323
|
-
reason: authResult.reason
|
|
4324
|
-
});
|
|
4325
|
-
}
|
|
4326
|
-
if (telemetry) {
|
|
4327
|
-
telemetry.emit("fulmen.signal.http_endpoint.auth_failed", {
|
|
4328
|
-
correlation_id: correlationId
|
|
4329
|
-
});
|
|
4330
|
-
}
|
|
4331
|
-
return {
|
|
4332
|
-
status: "error",
|
|
4333
|
-
error: "authentication_failed",
|
|
4334
|
-
message: authResult.reason || "Authentication required",
|
|
4335
|
-
statusCode: 401
|
|
4336
|
-
};
|
|
4337
|
-
}
|
|
4338
|
-
const identity = authResult.identity || "unknown";
|
|
4339
|
-
if (!payload.signal) {
|
|
4340
|
-
return {
|
|
4341
|
-
status: "error",
|
|
4342
|
-
error: "invalid_request",
|
|
4343
|
-
message: "Signal name is required",
|
|
4344
|
-
statusCode: 400
|
|
4345
|
-
};
|
|
4346
|
-
}
|
|
4347
|
-
const signalName = normalizeSignalName(payload.signal);
|
|
4348
|
-
const defaultAllowed = [
|
|
4349
|
-
"SIGHUP",
|
|
4350
|
-
"SIGTERM",
|
|
4351
|
-
"SIGINT",
|
|
4352
|
-
"SIGQUIT",
|
|
4353
|
-
"SIGUSR1",
|
|
4354
|
-
"SIGUSR2",
|
|
4355
|
-
"SIGPIPE",
|
|
4356
|
-
"SIGALRM"
|
|
4357
|
-
];
|
|
4358
|
-
const allowed = (allowedSignals || defaultAllowed).map((s) => normalizeSignalName(s));
|
|
4359
|
-
if (!allowed.includes(signalName)) {
|
|
4360
|
-
return {
|
|
4361
|
-
status: "error",
|
|
4362
|
-
error: "invalid_signal",
|
|
4363
|
-
message: `Signal '${payload.signal}' is not recognized. Valid signals: ${allowed.join(", ")}`,
|
|
4364
|
-
valid_signals: allowed,
|
|
4365
|
-
statusCode: 400
|
|
4366
|
-
};
|
|
4367
|
-
}
|
|
4368
|
-
const supported = await supportsSignal(signalName);
|
|
4369
|
-
if (!supported) {
|
|
4370
|
-
return {
|
|
4371
|
-
status: "error",
|
|
4372
|
-
error: "signal_not_supported",
|
|
4373
|
-
message: `Signal ${signalName} is not supported on this platform`,
|
|
4374
|
-
statusCode: 400
|
|
4375
|
-
};
|
|
4376
|
-
}
|
|
4377
|
-
if (rateLimit) {
|
|
4378
|
-
const rateLimitResult = await rateLimit(identity, signalName);
|
|
4379
|
-
if (!rateLimitResult.allowed) {
|
|
4380
|
-
if (logger) {
|
|
4381
|
-
logger.warn("Signal endpoint: rate limit exceeded", {
|
|
4382
|
-
correlation_id: correlationId,
|
|
4383
|
-
identity,
|
|
4384
|
-
signal: signalName
|
|
4385
|
-
});
|
|
4386
|
-
}
|
|
4387
|
-
if (telemetry) {
|
|
4388
|
-
telemetry.emit("fulmen.signal.http_endpoint.rate_limited", {
|
|
4389
|
-
correlation_id: correlationId,
|
|
4390
|
-
signal: signalName
|
|
4391
|
-
});
|
|
4392
|
-
}
|
|
4393
|
-
return {
|
|
4394
|
-
status: "error",
|
|
4395
|
-
error: "rate_limit_exceeded",
|
|
4396
|
-
message: "Rate limit exceeded. Please try again later.",
|
|
4397
|
-
statusCode: 429
|
|
4398
|
-
};
|
|
4399
|
-
}
|
|
4400
|
-
}
|
|
4401
|
-
if (logger) {
|
|
4402
|
-
logger.info("Signal endpoint: signal received", {
|
|
4403
|
-
correlation_id: correlationId,
|
|
4404
|
-
identity,
|
|
4405
|
-
signal: signalName,
|
|
4406
|
-
reason: payload.reason
|
|
4407
|
-
});
|
|
4408
|
-
}
|
|
4409
|
-
if (telemetry) {
|
|
4410
|
-
telemetry.emit("fulmen.signal.http_endpoint.signal_received", {
|
|
4411
|
-
correlation_id: correlationId,
|
|
4412
|
-
signal: signalName
|
|
4413
|
-
});
|
|
4414
|
-
}
|
|
4415
|
-
void manager.trigger(signalName).catch((error) => {
|
|
4416
|
-
if (logger) {
|
|
4417
|
-
logger.warn("Signal handler execution failed", {
|
|
4418
|
-
correlation_id: correlationId,
|
|
4419
|
-
signal: signalName,
|
|
4420
|
-
error: error instanceof Error ? error.message : String(error)
|
|
4421
|
-
});
|
|
4422
|
-
}
|
|
4423
|
-
});
|
|
4424
|
-
return {
|
|
4425
|
-
status: "accepted",
|
|
4426
|
-
signal: signalName,
|
|
4427
|
-
correlation_id: correlationId,
|
|
4428
|
-
message: "Signal will be processed asynchronously",
|
|
4429
|
-
statusCode: 202
|
|
4430
|
-
};
|
|
4431
|
-
};
|
|
4432
|
-
}
|
|
4433
|
-
function normalizeSignalName(signal) {
|
|
4434
|
-
const upper = signal.toUpperCase();
|
|
4435
|
-
if (upper.startsWith("SIG")) {
|
|
4436
|
-
return upper;
|
|
4437
|
-
}
|
|
4438
|
-
return `SIG${upper}`;
|
|
4439
|
-
}
|
|
4440
|
-
function generateCorrelationId2() {
|
|
4441
|
-
return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
4442
|
-
}
|
|
4443
|
-
function createBearerTokenAuth(expectedToken) {
|
|
4444
|
-
return (req) => {
|
|
4445
|
-
const headers = req.headers;
|
|
4446
|
-
const authHeader = headers?.authorization || headers?.Authorization;
|
|
4447
|
-
if (!authHeader) {
|
|
4448
|
-
return {
|
|
4449
|
-
authenticated: false,
|
|
4450
|
-
reason: "Missing Authorization header"
|
|
4451
|
-
};
|
|
4452
|
-
}
|
|
4453
|
-
const [scheme, token] = authHeader.split(" ");
|
|
4454
|
-
if (scheme !== "Bearer" || token !== expectedToken) {
|
|
4455
|
-
return {
|
|
4456
|
-
authenticated: false,
|
|
4457
|
-
reason: "Invalid bearer token"
|
|
4458
|
-
};
|
|
4459
|
-
}
|
|
4460
|
-
return {
|
|
4461
|
-
authenticated: true,
|
|
4462
|
-
identity: "bearer-token-user"
|
|
4463
|
-
};
|
|
4464
|
-
};
|
|
4465
|
-
}
|
|
4466
|
-
function createSimpleRateLimiter(requestsPerMinute) {
|
|
4467
|
-
const requests = /* @__PURE__ */ new Map();
|
|
4468
|
-
const windowMs = 6e4;
|
|
4469
|
-
return (identity) => {
|
|
4470
|
-
const now = Date.now();
|
|
4471
|
-
const key = identity;
|
|
4472
|
-
let timestamps = requests.get(key) || [];
|
|
4473
|
-
timestamps = timestamps.filter((ts) => now - ts < windowMs);
|
|
4474
|
-
if (timestamps.length >= requestsPerMinute) {
|
|
4475
|
-
const oldestTimestamp = Math.min(...timestamps);
|
|
4476
|
-
const resetAt = oldestTimestamp + windowMs;
|
|
4477
|
-
return {
|
|
4478
|
-
allowed: false,
|
|
4479
|
-
remaining: 0,
|
|
4480
|
-
reset_at: resetAt
|
|
4481
|
-
};
|
|
4482
|
-
}
|
|
4483
|
-
timestamps.push(now);
|
|
4484
|
-
requests.set(key, timestamps);
|
|
4485
|
-
return {
|
|
4486
|
-
allowed: true,
|
|
4487
|
-
remaining: requestsPerMinute - timestamps.length,
|
|
4488
|
-
reset_at: now + windowMs
|
|
4489
|
-
};
|
|
4490
|
-
};
|
|
4491
|
-
}
|
|
4492
|
-
var init_http_helper = __esm({
|
|
4493
|
-
"src/foundry/signals/http-helper.ts"() {
|
|
4494
|
-
init_capabilities2();
|
|
4495
|
-
}
|
|
4496
|
-
});
|
|
4497
|
-
|
|
4498
|
-
// src/foundry/signals/manager.ts
|
|
4499
|
-
function createSignalManager(options = {}) {
|
|
4500
|
-
return new SignalManager(options);
|
|
4501
|
-
}
|
|
4502
|
-
var SignalManager;
|
|
4503
|
-
var init_manager = __esm({
|
|
4504
|
-
"src/foundry/signals/manager.ts"() {
|
|
4505
|
-
init_capabilities2();
|
|
4506
|
-
init_catalog();
|
|
4507
|
-
init_double_tap();
|
|
4508
|
-
init_windows();
|
|
4509
|
-
SignalManager = class {
|
|
4510
|
-
handlers = /* @__PURE__ */ new Map();
|
|
4511
|
-
nativeListeners = /* @__PURE__ */ new Map();
|
|
4512
|
-
options;
|
|
4513
|
-
handlerIdCounter = 0;
|
|
4514
|
-
doubleTapTrackers = /* @__PURE__ */ new Map();
|
|
4515
|
-
shuttingDown = false;
|
|
4516
|
-
constructor(options = {}) {
|
|
4517
|
-
this.options = {
|
|
4518
|
-
defaultTimeoutMs: options.defaultTimeoutMs ?? 3e4,
|
|
4519
|
-
timeoutBehavior: options.timeoutBehavior ?? "log_and_continue",
|
|
4520
|
-
testMode: options.testMode ?? false,
|
|
4521
|
-
doubleTapWindowMs: options.doubleTapWindowMs ?? 2e3,
|
|
4522
|
-
doubleTapExitCode: options.doubleTapExitCode ?? 130,
|
|
4523
|
-
logger: options.logger,
|
|
4524
|
-
telemetry: options.telemetry
|
|
4525
|
-
};
|
|
4526
|
-
}
|
|
4527
|
-
/**
|
|
4528
|
-
* Register a signal handler
|
|
4529
|
-
*
|
|
4530
|
-
* @param signal - Signal name (e.g., "SIGTERM") or NodeJS.Signals
|
|
4531
|
-
* @param handler - Handler function
|
|
4532
|
-
* @param options - Handler options
|
|
4533
|
-
*/
|
|
4534
|
-
async register(signal, handler, options = {}) {
|
|
4535
|
-
const signalName = typeof signal === "string" ? signal : signal;
|
|
4536
|
-
const supported = await supportsSignal(signalName);
|
|
4537
|
-
if (!supported) {
|
|
4538
|
-
if (isWindows2()) {
|
|
4539
|
-
await handleWindowsFallback(signalName, {
|
|
4540
|
-
logger: this.options.logger,
|
|
4541
|
-
telemetry: this.options.telemetry
|
|
4542
|
-
});
|
|
4543
|
-
}
|
|
4544
|
-
return;
|
|
4545
|
-
}
|
|
4546
|
-
if (!this.handlers.has(signalName)) {
|
|
4547
|
-
this.handlers.set(signalName, []);
|
|
4548
|
-
}
|
|
4549
|
-
const registration = {
|
|
4550
|
-
signal: signalName,
|
|
4551
|
-
handler,
|
|
4552
|
-
priority: options.priority ?? 0,
|
|
4553
|
-
timeoutMs: options.timeoutMs ?? this.options.defaultTimeoutMs,
|
|
4554
|
-
id: options.id ?? `handler-${++this.handlerIdCounter}`,
|
|
4555
|
-
registeredAt: Date.now()
|
|
4556
|
-
};
|
|
4557
|
-
const handlers = this.handlers.get(signalName);
|
|
4558
|
-
if (!handlers) {
|
|
4559
|
-
throw new Error(`Handler list not initialized for signal ${signalName}`);
|
|
4560
|
-
}
|
|
4561
|
-
handlers.push(registration);
|
|
4562
|
-
handlers.sort((a, b) => {
|
|
4563
|
-
if (a.priority !== b.priority) {
|
|
4564
|
-
return b.priority - a.priority;
|
|
4565
|
-
}
|
|
4566
|
-
return a.registeredAt - b.registeredAt;
|
|
4567
|
-
});
|
|
4568
|
-
if (!this.nativeListeners.has(signalName)) {
|
|
4569
|
-
await this.registerNativeListener(signalName);
|
|
4570
|
-
}
|
|
4571
|
-
if (this.options.logger) {
|
|
4572
|
-
this.options.logger.info(`Signal handler registered: ${signalName}`, {
|
|
4573
|
-
handler_id: registration.id,
|
|
4574
|
-
priority: registration.priority,
|
|
4575
|
-
timeout_ms: registration.timeoutMs
|
|
4576
|
-
});
|
|
4577
|
-
}
|
|
4578
|
-
if (this.options.telemetry) {
|
|
4579
|
-
this.options.telemetry.emit("fulmen.signal.handler_registered", {
|
|
4580
|
-
signal: signalName,
|
|
4581
|
-
handler_id: registration.id,
|
|
4582
|
-
priority: String(registration.priority)
|
|
4583
|
-
});
|
|
4584
|
-
}
|
|
4585
|
-
}
|
|
4586
|
-
/**
|
|
4587
|
-
* Register native Node.js signal listener
|
|
4588
|
-
*/
|
|
4589
|
-
async registerNativeListener(signalName) {
|
|
4590
|
-
const signal = await getSignal(signalName);
|
|
4591
|
-
if (!signal) {
|
|
4592
|
-
return;
|
|
4593
|
-
}
|
|
4594
|
-
const useDoubleTap = signal.default_behavior === "graceful_shutdown_with_double_tap";
|
|
4595
|
-
if (useDoubleTap) {
|
|
4596
|
-
const tracker = await createDoubleTapTracker(signalName, {
|
|
4597
|
-
windowMs: this.options.doubleTapWindowMs,
|
|
4598
|
-
exitCode: this.options.doubleTapExitCode,
|
|
4599
|
-
logger: this.options.logger,
|
|
4600
|
-
testMode: this.options.testMode
|
|
4601
|
-
});
|
|
4602
|
-
this.doubleTapTrackers.set(signalName, tracker);
|
|
4603
|
-
}
|
|
4604
|
-
const listener = async (sig) => {
|
|
4605
|
-
if (useDoubleTap) {
|
|
4606
|
-
const tracker = this.doubleTapTrackers.get(signalName);
|
|
4607
|
-
if (tracker) {
|
|
4608
|
-
const forceQuit = handleDoubleTap(tracker);
|
|
4609
|
-
if (forceQuit) {
|
|
4610
|
-
return;
|
|
4611
|
-
}
|
|
4612
|
-
}
|
|
4613
|
-
}
|
|
4614
|
-
await this.executeHandlers(signalName, sig);
|
|
4615
|
-
};
|
|
4616
|
-
process.on(signalName, listener);
|
|
4617
|
-
this.nativeListeners.set(signalName, listener);
|
|
4618
|
-
}
|
|
4619
|
-
/**
|
|
4620
|
-
* Execute all registered handlers for a signal
|
|
4621
|
-
*/
|
|
4622
|
-
async executeHandlers(signalName, signal) {
|
|
4623
|
-
const handlers = this.handlers.get(signalName);
|
|
4624
|
-
if (!handlers || handlers.length === 0) {
|
|
4625
|
-
return;
|
|
4626
|
-
}
|
|
4627
|
-
this.shuttingDown = true;
|
|
4628
|
-
if (this.options.logger) {
|
|
4629
|
-
this.options.logger.info(`Signal received: ${signalName}`, {
|
|
4630
|
-
handler_count: handlers.length
|
|
4631
|
-
});
|
|
4632
|
-
}
|
|
4633
|
-
for (const registration of handlers) {
|
|
4634
|
-
try {
|
|
4635
|
-
await this.executeWithTimeout(registration, signal);
|
|
4636
|
-
} catch (error) {
|
|
4637
|
-
if (this.options.logger) {
|
|
4638
|
-
this.options.logger.warn(`Signal handler failed: ${signalName}`, {
|
|
4639
|
-
handler_id: registration.id,
|
|
4640
|
-
error: error instanceof Error ? error.message : String(error)
|
|
4641
|
-
});
|
|
4642
|
-
}
|
|
4643
|
-
if (this.options.telemetry) {
|
|
4644
|
-
this.options.telemetry.emit("fulmen.signal.handler_error", {
|
|
4645
|
-
signal: signalName,
|
|
4646
|
-
handler_id: registration.id,
|
|
4647
|
-
error_type: error instanceof Error ? error.constructor.name : "unknown"
|
|
4648
|
-
});
|
|
4649
|
-
}
|
|
4650
|
-
if (this.options.timeoutBehavior === "force_exit") {
|
|
4651
|
-
const exitCode = (await getSignal(signalName))?.exit_code ?? 1;
|
|
4652
|
-
if (!this.options.testMode) {
|
|
4653
|
-
process.exit(exitCode);
|
|
4654
|
-
}
|
|
4655
|
-
return;
|
|
4656
|
-
}
|
|
4657
|
-
}
|
|
4658
|
-
}
|
|
4659
|
-
const tracker = this.doubleTapTrackers.get(signalName);
|
|
4660
|
-
if (tracker) {
|
|
4661
|
-
resetDoubleTap(tracker);
|
|
4662
|
-
}
|
|
4663
|
-
}
|
|
4664
|
-
/**
|
|
4665
|
-
* Execute a handler with timeout enforcement
|
|
4666
|
-
*/
|
|
4667
|
-
async executeWithTimeout(registration, signal) {
|
|
4668
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
4669
|
-
setTimeout(() => {
|
|
4670
|
-
reject(new Error(`Handler timeout exceeded: ${registration.timeoutMs}ms`));
|
|
4671
|
-
}, registration.timeoutMs);
|
|
4672
|
-
});
|
|
4673
|
-
const handlerPromise = Promise.resolve(registration.handler(signal));
|
|
4674
|
-
try {
|
|
4675
|
-
await Promise.race([handlerPromise, timeoutPromise]);
|
|
4676
|
-
if (this.options.telemetry) {
|
|
4677
|
-
this.options.telemetry.emit("fulmen.signal.handler_completed", {
|
|
4678
|
-
signal: registration.signal,
|
|
4679
|
-
handler_id: registration.id
|
|
4680
|
-
});
|
|
4681
|
-
}
|
|
4682
|
-
} catch (error) {
|
|
4683
|
-
if (this.options.telemetry) {
|
|
4684
|
-
this.options.telemetry.emit("fulmen.signal.handler_timeout", {
|
|
4685
|
-
signal: registration.signal,
|
|
4686
|
-
handler_id: registration.id,
|
|
4687
|
-
timeout_ms: String(registration.timeoutMs)
|
|
4688
|
-
});
|
|
4689
|
-
}
|
|
4690
|
-
throw error;
|
|
4691
|
-
}
|
|
4692
|
-
}
|
|
4693
|
-
/**
|
|
4694
|
-
* Unregister a signal handler
|
|
4695
|
-
*
|
|
4696
|
-
* @param signal - Signal name
|
|
4697
|
-
* @param handler - Handler to remove (if not provided, removes all handlers)
|
|
4698
|
-
*/
|
|
4699
|
-
unregister(signal, handler) {
|
|
4700
|
-
const handlers = this.handlers.get(signal);
|
|
4701
|
-
if (!handlers) {
|
|
4702
|
-
return;
|
|
4703
|
-
}
|
|
4704
|
-
if (handler) {
|
|
4705
|
-
const index = handlers.findIndex((h) => h.handler === handler);
|
|
4706
|
-
if (index !== -1) {
|
|
4707
|
-
handlers.splice(index, 1);
|
|
4708
|
-
}
|
|
4709
|
-
} else {
|
|
4710
|
-
handlers.length = 0;
|
|
4711
|
-
}
|
|
4712
|
-
if (handlers.length === 0) {
|
|
4713
|
-
const listener = this.nativeListeners.get(signal);
|
|
4714
|
-
if (listener) {
|
|
4715
|
-
process.off(signal, listener);
|
|
4716
|
-
this.nativeListeners.delete(signal);
|
|
4717
|
-
}
|
|
4718
|
-
this.handlers.delete(signal);
|
|
4719
|
-
}
|
|
4720
|
-
}
|
|
4721
|
-
/**
|
|
4722
|
-
* Check if a signal has registered handlers
|
|
4723
|
-
*/
|
|
4724
|
-
isRegistered(signal) {
|
|
4725
|
-
const handlers = this.handlers.get(signal);
|
|
4726
|
-
return handlers !== void 0 && handlers.length > 0;
|
|
4727
|
-
}
|
|
4728
|
-
/**
|
|
4729
|
-
* Get handler count for a signal
|
|
4730
|
-
*/
|
|
4731
|
-
getHandlerCount(signal) {
|
|
4732
|
-
const handlers = this.handlers.get(signal);
|
|
4733
|
-
return handlers?.length ?? 0;
|
|
4734
|
-
}
|
|
4735
|
-
/**
|
|
4736
|
-
* Trigger signal handlers manually (for testing)
|
|
4737
|
-
*
|
|
4738
|
-
* @param signal - Signal name
|
|
4739
|
-
*/
|
|
4740
|
-
async trigger(signal) {
|
|
4741
|
-
const handlers = this.handlers.get(signal);
|
|
4742
|
-
if (!handlers || handlers.length === 0) {
|
|
4743
|
-
return;
|
|
4744
|
-
}
|
|
4745
|
-
await this.executeHandlers(signal, signal);
|
|
4746
|
-
}
|
|
4747
|
-
/**
|
|
4748
|
-
* Shutdown the signal manager and cleanup all handlers
|
|
4749
|
-
*/
|
|
4750
|
-
async shutdown() {
|
|
4751
|
-
for (const [signal, listener] of this.nativeListeners.entries()) {
|
|
4752
|
-
process.off(signal, listener);
|
|
4753
|
-
}
|
|
4754
|
-
this.nativeListeners.clear();
|
|
4755
|
-
this.handlers.clear();
|
|
4756
|
-
this.doubleTapTrackers.clear();
|
|
4757
|
-
this.shuttingDown = false;
|
|
4758
|
-
}
|
|
4759
|
-
/**
|
|
4760
|
-
* Check if manager is currently shutting down
|
|
4761
|
-
*/
|
|
4762
|
-
isShuttingDown() {
|
|
4763
|
-
return this.shuttingDown;
|
|
4764
|
-
}
|
|
4765
|
-
};
|
|
4766
|
-
}
|
|
4767
|
-
});
|
|
4768
|
-
|
|
4769
|
-
// src/foundry/signals/reload.ts
|
|
4770
|
-
function createConfigReloadHandler(options) {
|
|
4771
|
-
const {
|
|
4772
|
-
loader,
|
|
4773
|
-
validator,
|
|
4774
|
-
onValidated,
|
|
4775
|
-
exitCode = 129,
|
|
4776
|
-
logger,
|
|
4777
|
-
telemetry,
|
|
4778
|
-
testMode = false
|
|
4779
|
-
} = options;
|
|
4780
|
-
return async () => {
|
|
4781
|
-
if (logger) {
|
|
4782
|
-
logger.info("Config reload requested (SIGHUP)");
|
|
4783
|
-
}
|
|
4784
|
-
if (telemetry) {
|
|
4785
|
-
telemetry.emit("fulmen.signal.config_reload_requested", {
|
|
4786
|
-
signal: "SIGHUP"
|
|
4787
|
-
});
|
|
4788
|
-
}
|
|
4789
|
-
try {
|
|
4790
|
-
const newConfig = await loader();
|
|
4791
|
-
const result = await validator(newConfig);
|
|
4792
|
-
if (!result.valid) {
|
|
4793
|
-
if (logger) {
|
|
4794
|
-
logger.warn("Config validation failed - continuing with current config", {
|
|
4795
|
-
error_count: result.errors?.length ?? 0,
|
|
4796
|
-
errors: result.errors
|
|
4797
|
-
});
|
|
4798
|
-
}
|
|
4799
|
-
if (telemetry) {
|
|
4800
|
-
telemetry.emit("fulmen.signal.config_reload_rejected", {
|
|
4801
|
-
signal: "SIGHUP",
|
|
4802
|
-
reason: "validation_failed",
|
|
4803
|
-
error_count: String(result.errors?.length ?? 0)
|
|
4804
|
-
});
|
|
4805
|
-
}
|
|
4806
|
-
return;
|
|
4807
|
-
}
|
|
4808
|
-
if (logger) {
|
|
4809
|
-
logger.info("Config validation succeeded - exiting for restart");
|
|
4810
|
-
}
|
|
4811
|
-
if (telemetry) {
|
|
4812
|
-
telemetry.emit("fulmen.signal.config_reload_accepted", {
|
|
4813
|
-
signal: "SIGHUP"
|
|
4814
|
-
});
|
|
4815
|
-
}
|
|
4816
|
-
if (onValidated) {
|
|
4817
|
-
await onValidated(newConfig);
|
|
4818
|
-
}
|
|
4819
|
-
if (!testMode) {
|
|
4820
|
-
process.exit(exitCode);
|
|
4821
|
-
}
|
|
4822
|
-
} catch (error) {
|
|
4823
|
-
if (logger) {
|
|
4824
|
-
logger.warn("Config reload failed with error - continuing with current config", {
|
|
4825
|
-
error: error instanceof Error ? error.message : String(error)
|
|
4826
|
-
});
|
|
4827
|
-
}
|
|
4828
|
-
if (telemetry) {
|
|
4829
|
-
telemetry.emit("fulmen.signal.config_reload_error", {
|
|
4830
|
-
signal: "SIGHUP",
|
|
4831
|
-
error_type: error instanceof Error ? error.constructor.name : "unknown"
|
|
4832
|
-
});
|
|
4833
|
-
}
|
|
4834
|
-
}
|
|
4835
|
-
};
|
|
4836
|
-
}
|
|
4837
|
-
var ConfigReloadTracker;
|
|
4838
|
-
var init_reload = __esm({
|
|
4839
|
-
"src/foundry/signals/reload.ts"() {
|
|
4840
|
-
ConfigReloadTracker = class {
|
|
4841
|
-
failures = 0;
|
|
4842
|
-
lastFailureTime = null;
|
|
4843
|
-
maxFailures;
|
|
4844
|
-
logger;
|
|
4845
|
-
telemetry;
|
|
4846
|
-
constructor(options) {
|
|
4847
|
-
this.maxFailures = options.maxFailures ?? 3;
|
|
4848
|
-
this.logger = options.logger;
|
|
4849
|
-
this.telemetry = options.telemetry;
|
|
4850
|
-
}
|
|
4851
|
-
/**
|
|
4852
|
-
* Record a reload failure
|
|
4853
|
-
*
|
|
4854
|
-
* @returns true if threshold exceeded, false otherwise
|
|
4855
|
-
*/
|
|
4856
|
-
recordFailure() {
|
|
4857
|
-
this.failures++;
|
|
4858
|
-
this.lastFailureTime = Date.now();
|
|
4859
|
-
if (this.failures >= this.maxFailures) {
|
|
4860
|
-
if (this.logger) {
|
|
4861
|
-
this.logger.warn(
|
|
4862
|
-
`${this.failures} consecutive config reload failures - check config source`,
|
|
4863
|
-
{
|
|
4864
|
-
failure_count: this.failures,
|
|
4865
|
-
threshold: this.maxFailures
|
|
4866
|
-
}
|
|
4867
|
-
);
|
|
4868
|
-
}
|
|
4869
|
-
if (this.telemetry) {
|
|
4870
|
-
this.telemetry.emit("fulmen.signal.config_reload_threshold_exceeded", {
|
|
4871
|
-
failure_count: String(this.failures),
|
|
4872
|
-
threshold: String(this.maxFailures)
|
|
4873
|
-
});
|
|
4874
|
-
}
|
|
4875
|
-
return true;
|
|
4876
|
-
}
|
|
4877
|
-
return false;
|
|
4878
|
-
}
|
|
4879
|
-
/**
|
|
4880
|
-
* Record a successful reload (resets counter)
|
|
4881
|
-
*/
|
|
4882
|
-
recordSuccess() {
|
|
4883
|
-
this.failures = 0;
|
|
4884
|
-
this.lastFailureTime = null;
|
|
4885
|
-
}
|
|
4886
|
-
/**
|
|
4887
|
-
* Get current failure count
|
|
4888
|
-
*/
|
|
4889
|
-
getFailureCount() {
|
|
4890
|
-
return this.failures;
|
|
4891
|
-
}
|
|
4892
|
-
/**
|
|
4893
|
-
* Get last failure timestamp
|
|
4894
|
-
*/
|
|
4895
|
-
getLastFailureTime() {
|
|
4896
|
-
return this.lastFailureTime;
|
|
4897
|
-
}
|
|
4898
|
-
};
|
|
4899
|
-
}
|
|
4900
|
-
});
|
|
4901
|
-
|
|
4902
|
-
// src/foundry/signals/index.ts
|
|
4903
|
-
var init_signals = __esm({
|
|
4904
|
-
"src/foundry/signals/index.ts"() {
|
|
4905
|
-
init_capabilities2();
|
|
4906
|
-
init_catalog();
|
|
4907
|
-
init_config_reload_endpoint();
|
|
4908
|
-
init_control_discovery_endpoint();
|
|
4909
|
-
init_convenience();
|
|
4910
|
-
init_double_tap();
|
|
4911
|
-
init_guards();
|
|
4912
|
-
init_http_helper();
|
|
4913
|
-
init_manager();
|
|
4914
|
-
init_reload();
|
|
4915
|
-
init_windows();
|
|
4916
|
-
}
|
|
4917
|
-
});
|
|
4918
|
-
function distance(a, b, metric = "levenshtein") {
|
|
4919
|
-
switch (metric) {
|
|
4920
|
-
case "levenshtein":
|
|
4921
|
-
return levenshtein(a, b);
|
|
4922
|
-
case "damerau_osa":
|
|
4923
|
-
return osa_distance(a, b);
|
|
4924
|
-
case "damerau_unrestricted":
|
|
4925
|
-
return damerau_levenshtein(a, b);
|
|
4926
|
-
case "jaro_winkler":
|
|
4927
|
-
return jaro_winkler(a, b);
|
|
4928
|
-
case "substring":
|
|
4929
|
-
return substringSimilarity(a, b).score;
|
|
4930
|
-
default:
|
|
4931
|
-
throw new Error(
|
|
4932
|
-
`Invalid metric '${metric}': must be one of: levenshtein, damerau_osa, damerau_unrestricted, jaro_winkler, substring`
|
|
4933
|
-
);
|
|
4934
|
-
}
|
|
4935
|
-
}
|
|
4936
|
-
var init_distance = __esm({
|
|
4937
|
-
"src/foundry/similarity/distance.ts"() {
|
|
4938
|
-
}
|
|
4939
|
-
});
|
|
4940
|
-
|
|
4941
|
-
// src/foundry/similarity/errors.ts
|
|
4942
|
-
var init_errors3 = __esm({
|
|
4943
|
-
"src/foundry/similarity/errors.ts"() {
|
|
4944
|
-
init_errors2();
|
|
4945
|
-
}
|
|
4946
|
-
});
|
|
4947
|
-
function toNormalizationLocale(locale) {
|
|
4948
|
-
if (!locale) {
|
|
4949
|
-
return void 0;
|
|
4950
|
-
}
|
|
4951
|
-
if (locale === "tr" || locale === "az" || locale === "lt") {
|
|
4952
|
-
return locale;
|
|
4953
|
-
}
|
|
4954
|
-
return void 0;
|
|
4955
|
-
}
|
|
4956
|
-
function normalize(value, preset = "default", locale) {
|
|
4957
|
-
if (typeof preset === "object") {
|
|
4958
|
-
const targetPreset = preset.stripAccents ? "aggressive" : "default";
|
|
4959
|
-
const targetLocale = toNormalizationLocale(preset.locale ?? locale);
|
|
4960
|
-
return normalize$1(value, targetPreset, targetLocale);
|
|
4961
|
-
}
|
|
4962
|
-
return normalize$1(value, preset, toNormalizationLocale(locale));
|
|
4963
|
-
}
|
|
4964
|
-
function casefold(value, locale) {
|
|
4965
|
-
if (locale === "tr") {
|
|
4966
|
-
return value.toLocaleLowerCase("tr-TR");
|
|
4967
|
-
}
|
|
4968
|
-
return value.toLowerCase();
|
|
4969
|
-
}
|
|
4970
|
-
function stripAccents(value) {
|
|
4971
|
-
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
4972
|
-
}
|
|
4973
|
-
function equalsIgnoreCase(a, b, options) {
|
|
4974
|
-
return normalize(a, options) === normalize(b, options);
|
|
4975
|
-
}
|
|
4976
|
-
var init_normalization = __esm({
|
|
4977
|
-
"src/foundry/similarity/normalization.ts"() {
|
|
4978
|
-
}
|
|
4979
|
-
});
|
|
4980
|
-
function score(a, b, metric = "levenshtein") {
|
|
4981
|
-
if (metric === "substring") {
|
|
4982
|
-
return substringSimilarity(a, b).score;
|
|
4983
|
-
}
|
|
4984
|
-
return score$1(a, b, metric);
|
|
4985
|
-
}
|
|
4986
|
-
var init_score = __esm({
|
|
4987
|
-
"src/foundry/similarity/score.ts"() {
|
|
4988
|
-
}
|
|
4989
|
-
});
|
|
4990
|
-
function suggest(input, candidates, options) {
|
|
4991
|
-
const metric = options?.metric ?? DEFAULT_METRIC;
|
|
4992
|
-
const minScore = options?.minScore ?? DEFAULT_MIN_SCORE;
|
|
4993
|
-
const maxSuggestions = options?.maxSuggestions ?? DEFAULT_MAX_SUGGESTIONS;
|
|
4994
|
-
let normalizePreset = options?.normalizePreset ?? DEFAULT_NORMALIZE_PRESET;
|
|
4995
|
-
if (options?.normalize === false) {
|
|
4996
|
-
normalizePreset = "none";
|
|
4997
|
-
} else if (options?.normalize === true && !options?.normalizePreset) {
|
|
4998
|
-
normalizePreset = "default";
|
|
4999
|
-
}
|
|
5000
|
-
const wasmOptions = {
|
|
5001
|
-
metric,
|
|
5002
|
-
normalizePreset,
|
|
5003
|
-
minScore,
|
|
5004
|
-
maxSuggestions,
|
|
5005
|
-
preferPrefix: options?.preferPrefix,
|
|
5006
|
-
jaroPrefixScale: options?.jaroPrefixScale,
|
|
5007
|
-
jaroMaxPrefix: options?.jaroMaxPrefix
|
|
5008
|
-
};
|
|
5009
|
-
const results = suggest$1(input, candidates, wasmOptions);
|
|
5010
|
-
return results.map((r) => ({
|
|
5011
|
-
value: r.value,
|
|
5012
|
-
score: r.score,
|
|
5013
|
-
matchedRange: r.matchedRange,
|
|
5014
|
-
reason: r.reason,
|
|
5015
|
-
normalizedValue: r.normalizedValue
|
|
5016
|
-
}));
|
|
5017
|
-
}
|
|
5018
|
-
var DEFAULT_MIN_SCORE, DEFAULT_MAX_SUGGESTIONS, DEFAULT_METRIC, DEFAULT_NORMALIZE_PRESET;
|
|
5019
|
-
var init_suggest = __esm({
|
|
5020
|
-
"src/foundry/similarity/suggest.ts"() {
|
|
5021
|
-
DEFAULT_MIN_SCORE = 0.6;
|
|
5022
|
-
DEFAULT_MAX_SUGGESTIONS = 3;
|
|
5023
|
-
DEFAULT_METRIC = "levenshtein";
|
|
5024
|
-
DEFAULT_NORMALIZE_PRESET = "default";
|
|
5025
|
-
}
|
|
5026
|
-
});
|
|
5027
|
-
|
|
5028
|
-
// src/foundry/similarity/index.ts
|
|
5029
|
-
var init_similarity = __esm({
|
|
5030
|
-
"src/foundry/similarity/index.ts"() {
|
|
5031
|
-
init_distance();
|
|
5032
|
-
init_errors3();
|
|
5033
|
-
init_normalization();
|
|
5034
|
-
init_score();
|
|
5035
|
-
init_suggest();
|
|
5036
|
-
}
|
|
5037
|
-
});
|
|
5038
|
-
|
|
5039
|
-
// src/foundry/index.ts
|
|
5040
|
-
var foundry_exports = {};
|
|
5041
|
-
__export(foundry_exports, {
|
|
5042
|
-
ConfigReloadTracker: () => ConfigReloadTracker,
|
|
5043
|
-
EXIT_CODES_VERSION: () => EXIT_CODES_VERSION,
|
|
5044
|
-
FoundryCatalogError: () => FoundryCatalogError,
|
|
5045
|
-
SignalManager: () => SignalManager,
|
|
5046
|
-
SimplifiedMode: () => SimplifiedMode,
|
|
5047
|
-
VERSION: () => VERSION,
|
|
5048
|
-
casefold: () => casefold,
|
|
5049
|
-
clearCountryCodeCache: () => clearCountryCodeCache,
|
|
5050
|
-
clearHttpStatusCache: () => clearHttpStatusCache,
|
|
5051
|
-
clearMimeTypeCache: () => clearMimeTypeCache,
|
|
5052
|
-
clearPatternCache: () => clearPatternCache,
|
|
5053
|
-
createBearerTokenAuth: () => createBearerTokenAuth,
|
|
5054
|
-
createConfigReloadEndpoint: () => createConfigReloadEndpoint,
|
|
5055
|
-
createConfigReloadHandler: () => createConfigReloadHandler,
|
|
5056
|
-
createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
|
|
5057
|
-
createDoubleTapTracker: () => createDoubleTapTracker,
|
|
5058
|
-
createSignalEndpoint: () => createSignalEndpoint,
|
|
5059
|
-
createSignalManager: () => createSignalManager,
|
|
5060
|
-
createSimpleRateLimiter: () => createSimpleRateLimiter,
|
|
5061
|
-
describePattern: () => describePattern,
|
|
5062
|
-
detectMimeType: () => detectMimeType,
|
|
5063
|
-
detectMimeTypeFromBuffer: () => detectMimeTypeFromBuffer,
|
|
5064
|
-
detectMimeTypeFromFile: () => detectMimeTypeFromFile,
|
|
5065
|
-
detectMimeTypeFromStream: () => detectMimeTypeFromStream,
|
|
5066
|
-
distance: () => distance,
|
|
5067
|
-
ensurePOSIX: () => ensurePOSIX,
|
|
5068
|
-
ensureSignalExitCodesSupported: () => ensureSignalExitCodesSupported,
|
|
5069
|
-
ensureSupported: () => ensureSupported,
|
|
5070
|
-
ensureWindows: () => ensureWindows,
|
|
5071
|
-
equalsIgnoreCase: () => equalsIgnoreCase,
|
|
5072
|
-
exitCodeMetadata: () => exitCodeMetadata,
|
|
5073
|
-
exitCodes: () => exitCodes,
|
|
5074
|
-
getBehavior: () => getBehavior,
|
|
5075
|
-
getCountryByAlpha2: () => getCountryByAlpha2,
|
|
5076
|
-
getCountryByAlpha3: () => getCountryByAlpha3,
|
|
5077
|
-
getCountryByNumeric: () => getCountryByNumeric,
|
|
5078
|
-
getExitCodeInfo: () => getExitCodeInfo,
|
|
5079
|
-
getFallbackMetadata: () => getFallbackMetadata,
|
|
5080
|
-
getHttpFallbackGuidance: () => getHttpFallbackGuidance,
|
|
5081
|
-
getHttpStatus: () => getHttpStatus,
|
|
5082
|
-
getMimeType: () => getMimeType,
|
|
5083
|
-
getMimeTypeByExtension: () => getMimeTypeByExtension,
|
|
5084
|
-
getPattern: () => getPattern,
|
|
5085
|
-
getPatternRegex: () => getPatternRegex,
|
|
5086
|
-
getPlatform: () => getPlatform,
|
|
5087
|
-
getPlatformCapabilities: () => getPlatformCapabilities,
|
|
5088
|
-
getSignal: () => getSignal,
|
|
5089
|
-
getSignalCatalog: () => getSignalCatalog,
|
|
5090
|
-
getSignalNumber: () => getSignalNumber,
|
|
5091
|
-
getSignalPlatformCapabilities: () => getPlatformCapabilities2,
|
|
5092
|
-
getSignalsVersion: () => getSignalsVersion,
|
|
5093
|
-
getSimplifiedCodeDescription: () => getSimplifiedCodeDescription,
|
|
5094
|
-
getSimplifiedCodes: () => getSimplifiedCodes,
|
|
5095
|
-
getStatusReason: () => getStatusReason,
|
|
5096
|
-
getWindowTimeRemaining: () => getWindowTimeRemaining,
|
|
5097
|
-
getWindowsEvent: () => getWindowsEvent,
|
|
5098
|
-
handleDoubleTap: () => handleDoubleTap,
|
|
5099
|
-
handleWindowsFallback: () => handleWindowsFallback,
|
|
5100
|
-
isClientError: () => isClientError,
|
|
5101
|
-
isInformational: () => isInformational,
|
|
5102
|
-
isPOSIX: () => isPOSIX,
|
|
5103
|
-
isRedirection: () => isRedirection,
|
|
5104
|
-
isServerError: () => isServerError,
|
|
5105
|
-
isSignalPOSIX: () => isPOSIX2,
|
|
5106
|
-
isSignalWindows: () => isWindows2,
|
|
5107
|
-
isSuccess: () => isSuccess,
|
|
5108
|
-
isSupportedMimeType: () => isSupportedMimeType,
|
|
5109
|
-
isWindows: () => isWindows,
|
|
5110
|
-
isWithinWindow: () => isWithinWindow,
|
|
5111
|
-
listBehaviors: () => listBehaviors,
|
|
5112
|
-
listCountries: () => listCountries,
|
|
5113
|
-
listHttpStatuses: () => listHttpStatuses,
|
|
5114
|
-
listMimeTypes: () => listMimeTypes,
|
|
5115
|
-
listPatterns: () => listPatterns,
|
|
5116
|
-
listSignals: () => listSignals,
|
|
5117
|
-
loadAllCatalogs: () => loadAllCatalogs,
|
|
5118
|
-
loadCountryCodeCatalog: () => loadCountryCodeCatalog,
|
|
5119
|
-
loadHttpStatusCatalog: () => loadHttpStatusCatalog,
|
|
5120
|
-
loadMimeTypeCatalog: () => loadMimeTypeCatalog,
|
|
5121
|
-
loadPatternCatalog: () => loadPatternCatalog,
|
|
5122
|
-
mapExitCodeToSimplified: () => mapExitCodeToSimplified,
|
|
5123
|
-
matchMagicNumber: () => matchMagicNumber,
|
|
5124
|
-
matchPattern: () => matchPattern,
|
|
5125
|
-
normalize: () => normalize,
|
|
5126
|
-
onAnyShutdown: () => onAnyShutdown,
|
|
5127
|
-
onEmergencyQuit: () => onEmergencyQuit,
|
|
5128
|
-
onReload: () => onReload,
|
|
5129
|
-
onShutdown: () => onShutdown,
|
|
5130
|
-
onUSR1: () => onUSR1,
|
|
5131
|
-
onUSR2: () => onUSR2,
|
|
5132
|
-
requiresFallback: () => requiresFallback,
|
|
5133
|
-
resetDoubleTap: () => resetDoubleTap,
|
|
5134
|
-
score: () => score,
|
|
5135
|
-
stripAccents: () => stripAccents,
|
|
5136
|
-
suggest: () => suggest,
|
|
5137
|
-
supportsSignal: () => supportsSignal,
|
|
5138
|
-
supportsSignalBasedExitCodes: () => supportsSignalExitCodes2,
|
|
5139
|
-
supportsSignalExitCodes: () => supportsSignalExitCodes
|
|
5140
|
-
});
|
|
5141
|
-
var VERSION;
|
|
5142
|
-
var init_foundry = __esm({
|
|
5143
|
-
"src/foundry/index.ts"() {
|
|
5144
|
-
init_country_codes();
|
|
5145
|
-
init_errors2();
|
|
5146
|
-
init_exit_codes();
|
|
5147
|
-
init_http_statuses();
|
|
5148
|
-
init_loader();
|
|
5149
|
-
init_mime_types();
|
|
5150
|
-
init_patterns();
|
|
5151
|
-
init_signals();
|
|
5152
|
-
init_similarity();
|
|
5153
|
-
VERSION = "0.1.1";
|
|
5154
|
-
}
|
|
5155
|
-
});
|
|
5156
|
-
|
|
5157
|
-
// src/appidentity/cache.ts
|
|
5158
|
-
function getCachedIdentity() {
|
|
5159
|
-
return cachedIdentity;
|
|
5160
|
-
}
|
|
5161
|
-
function setCachedIdentity(identity) {
|
|
5162
|
-
cachedIdentity = identity;
|
|
5163
|
-
}
|
|
5164
|
-
function clearIdentityCache() {
|
|
5165
|
-
cachedIdentity = null;
|
|
5166
|
-
}
|
|
5167
|
-
var cachedIdentity;
|
|
5168
|
-
var init_cache = __esm({
|
|
5169
|
-
"src/appidentity/cache.ts"() {
|
|
5170
|
-
cachedIdentity = null;
|
|
1440
|
+
// src/schema/index.ts
|
|
1441
|
+
var init_schema = __esm({
|
|
1442
|
+
"src/schema/index.ts"() {
|
|
1443
|
+
init_ajv_formats();
|
|
1444
|
+
init_cli();
|
|
1445
|
+
init_errors();
|
|
1446
|
+
init_export();
|
|
1447
|
+
init_goneat_bridge();
|
|
1448
|
+
init_normalizer();
|
|
1449
|
+
init_registry();
|
|
1450
|
+
init_utils();
|
|
1451
|
+
init_validator();
|
|
5171
1452
|
}
|
|
5172
1453
|
});
|
|
5173
1454
|
var init_correlation = __esm({
|
|
@@ -5494,7 +1775,7 @@ var init_fulmen_error = __esm({
|
|
|
5494
1775
|
});
|
|
5495
1776
|
|
|
5496
1777
|
// src/errors/index.ts
|
|
5497
|
-
var
|
|
1778
|
+
var init_errors2 = __esm({
|
|
5498
1779
|
"src/errors/index.ts"() {
|
|
5499
1780
|
init_correlation();
|
|
5500
1781
|
init_fulmen_error();
|
|
@@ -5505,14 +1786,10 @@ var init_errors4 = __esm({
|
|
|
5505
1786
|
});
|
|
5506
1787
|
|
|
5507
1788
|
// src/appidentity/errors.ts
|
|
5508
|
-
var errors_exports2 = {};
|
|
5509
|
-
__export(errors_exports2, {
|
|
5510
|
-
AppIdentityError: () => AppIdentityError
|
|
5511
|
-
});
|
|
5512
1789
|
var AppIdentityError;
|
|
5513
|
-
var
|
|
1790
|
+
var init_errors3 = __esm({
|
|
5514
1791
|
"src/appidentity/errors.ts"() {
|
|
5515
|
-
|
|
1792
|
+
init_errors2();
|
|
5516
1793
|
AppIdentityError = class _AppIdentityError extends FulmenError {
|
|
5517
1794
|
identityPath;
|
|
5518
1795
|
constructor(message, identityPath, cause) {
|
|
@@ -5638,6 +1915,82 @@ ${cause.message}`;
|
|
|
5638
1915
|
};
|
|
5639
1916
|
}
|
|
5640
1917
|
});
|
|
1918
|
+
function deepFreeze(obj) {
|
|
1919
|
+
Object.freeze(obj);
|
|
1920
|
+
Object.getOwnPropertyNames(obj).forEach((prop) => {
|
|
1921
|
+
const value = obj[prop];
|
|
1922
|
+
if (value !== null && (typeof value === "object" || typeof value === "function") && !Object.isFrozen(value)) {
|
|
1923
|
+
deepFreeze(value);
|
|
1924
|
+
}
|
|
1925
|
+
});
|
|
1926
|
+
return obj;
|
|
1927
|
+
}
|
|
1928
|
+
async function registerEmbeddedIdentity(data) {
|
|
1929
|
+
if (isRegistered) {
|
|
1930
|
+
throw AppIdentityError.alreadyRegistered();
|
|
1931
|
+
}
|
|
1932
|
+
let identity;
|
|
1933
|
+
if (typeof data === "string") {
|
|
1934
|
+
let parsed;
|
|
1935
|
+
try {
|
|
1936
|
+
parsed = parse(data);
|
|
1937
|
+
} catch (error) {
|
|
1938
|
+
throw AppIdentityError.embeddedParseFailed(
|
|
1939
|
+
error instanceof Error ? error : new Error(String(error))
|
|
1940
|
+
);
|
|
1941
|
+
}
|
|
1942
|
+
const result = await validateDataBySchemaId(parsed, APP_IDENTITY_SCHEMA_ID);
|
|
1943
|
+
if (!result.valid) {
|
|
1944
|
+
throw AppIdentityError.embeddedValidationFailed(result.diagnostics);
|
|
1945
|
+
}
|
|
1946
|
+
identity = parsed;
|
|
1947
|
+
} else {
|
|
1948
|
+
const result = await validateDataBySchemaId(data, APP_IDENTITY_SCHEMA_ID);
|
|
1949
|
+
if (!result.valid) {
|
|
1950
|
+
throw AppIdentityError.embeddedValidationFailed(result.diagnostics);
|
|
1951
|
+
}
|
|
1952
|
+
identity = data;
|
|
1953
|
+
}
|
|
1954
|
+
embeddedIdentity = deepFreeze(structuredClone(identity));
|
|
1955
|
+
isRegistered = true;
|
|
1956
|
+
}
|
|
1957
|
+
function hasEmbeddedIdentity() {
|
|
1958
|
+
return isRegistered;
|
|
1959
|
+
}
|
|
1960
|
+
function getEmbeddedIdentity() {
|
|
1961
|
+
return embeddedIdentity;
|
|
1962
|
+
}
|
|
1963
|
+
function clearEmbeddedIdentity() {
|
|
1964
|
+
embeddedIdentity = null;
|
|
1965
|
+
isRegistered = false;
|
|
1966
|
+
}
|
|
1967
|
+
var embeddedIdentity, isRegistered;
|
|
1968
|
+
var init_embedded = __esm({
|
|
1969
|
+
"src/appidentity/embedded.ts"() {
|
|
1970
|
+
init_schema();
|
|
1971
|
+
init_constants();
|
|
1972
|
+
init_errors3();
|
|
1973
|
+
embeddedIdentity = null;
|
|
1974
|
+
isRegistered = false;
|
|
1975
|
+
}
|
|
1976
|
+
});
|
|
1977
|
+
|
|
1978
|
+
// src/appidentity/cache.ts
|
|
1979
|
+
function getCachedIdentity() {
|
|
1980
|
+
return cachedIdentity;
|
|
1981
|
+
}
|
|
1982
|
+
function setCachedIdentity(identity) {
|
|
1983
|
+
cachedIdentity = identity;
|
|
1984
|
+
}
|
|
1985
|
+
function clearIdentityCache() {
|
|
1986
|
+
cachedIdentity = null;
|
|
1987
|
+
}
|
|
1988
|
+
var cachedIdentity;
|
|
1989
|
+
var init_cache = __esm({
|
|
1990
|
+
"src/appidentity/cache.ts"() {
|
|
1991
|
+
cachedIdentity = null;
|
|
1992
|
+
}
|
|
1993
|
+
});
|
|
5641
1994
|
async function discoverIdentityPath(options) {
|
|
5642
1995
|
if (options?.path) {
|
|
5643
1996
|
const exists = await fileExists(options.path);
|
|
@@ -5689,30 +2042,22 @@ async function fileExists(path) {
|
|
|
5689
2042
|
var init_discovery = __esm({
|
|
5690
2043
|
"src/appidentity/discovery.ts"() {
|
|
5691
2044
|
init_constants();
|
|
5692
|
-
|
|
2045
|
+
init_errors3();
|
|
5693
2046
|
}
|
|
5694
2047
|
});
|
|
5695
|
-
|
|
5696
|
-
// src/appidentity/loader.ts
|
|
5697
|
-
var loader_exports = {};
|
|
5698
|
-
__export(loader_exports, {
|
|
5699
|
-
clearIdentityCache: () => clearIdentityCache,
|
|
5700
|
-
getCachedIdentity: () => getCachedIdentity,
|
|
5701
|
-
loadIdentity: () => loadIdentity
|
|
5702
|
-
});
|
|
5703
|
-
function deepFreeze5(obj) {
|
|
2048
|
+
function deepFreeze2(obj) {
|
|
5704
2049
|
Object.freeze(obj);
|
|
5705
2050
|
Object.getOwnPropertyNames(obj).forEach((prop) => {
|
|
5706
2051
|
const value = obj[prop];
|
|
5707
2052
|
if (value !== null && (typeof value === "object" || typeof value === "function") && !Object.isFrozen(value)) {
|
|
5708
|
-
|
|
2053
|
+
deepFreeze2(value);
|
|
5709
2054
|
}
|
|
5710
2055
|
});
|
|
5711
2056
|
return obj;
|
|
5712
2057
|
}
|
|
5713
2058
|
async function loadIdentity(options) {
|
|
5714
2059
|
if (options?.identity) {
|
|
5715
|
-
return
|
|
2060
|
+
return deepFreeze2(structuredClone(options.identity));
|
|
5716
2061
|
}
|
|
5717
2062
|
if (!options?.skipCache) {
|
|
5718
2063
|
const cached = getCachedIdentity();
|
|
@@ -5770,399 +2115,61 @@ async function loadIdentity(options) {
|
|
|
5770
2115
|
throw AppIdentityError.validationFailed(discovery.path, result.diagnostics);
|
|
5771
2116
|
}
|
|
5772
2117
|
}
|
|
5773
|
-
const identity =
|
|
2118
|
+
const identity = deepFreeze2(structuredClone(parsed));
|
|
5774
2119
|
setCachedIdentity(identity);
|
|
5775
2120
|
return identity;
|
|
5776
2121
|
}
|
|
5777
|
-
var
|
|
2122
|
+
var init_loader = __esm({
|
|
5778
2123
|
"src/appidentity/loader.ts"() {
|
|
5779
2124
|
init_schema();
|
|
5780
2125
|
init_cache();
|
|
5781
2126
|
init_constants();
|
|
5782
2127
|
init_discovery();
|
|
5783
2128
|
init_embedded();
|
|
5784
|
-
|
|
5785
|
-
}
|
|
5786
|
-
});
|
|
5787
|
-
function createCLI(options = {}) {
|
|
5788
|
-
const program = new Command();
|
|
5789
|
-
program.name("tsfulmen-schema").description("Schema validation and discovery CLI for Fulmen (developer tool)").version("0.1.0");
|
|
5790
|
-
program.command("list").description("List available schemas from registry").argument("[prefix]", "Filter schemas by prefix").option("--base-dir <path>", "Override schema base directory").action(async (prefix, cmdOptions) => {
|
|
5791
|
-
try {
|
|
5792
|
-
const schemas = await listSchemas(prefix, {
|
|
5793
|
-
baseDir: cmdOptions?.baseDir || options.baseDir
|
|
5794
|
-
});
|
|
5795
|
-
if (schemas.length === 0) {
|
|
5796
|
-
console.log("No schemas found");
|
|
5797
|
-
return;
|
|
5798
|
-
}
|
|
5799
|
-
console.log(`Found ${schemas.length} schema(s):
|
|
5800
|
-
`);
|
|
5801
|
-
for (const schema of schemas) {
|
|
5802
|
-
console.log(` ${schema.id}`);
|
|
5803
|
-
console.log(` Format: ${schema.format}`);
|
|
5804
|
-
console.log(` Path: ${schema.relativePath}`);
|
|
5805
|
-
if (schema.description) {
|
|
5806
|
-
console.log(` Description: ${schema.description}`);
|
|
5807
|
-
}
|
|
5808
|
-
console.log();
|
|
5809
|
-
}
|
|
5810
|
-
} catch (error) {
|
|
5811
|
-
console.error("Error listing schemas:", error.message);
|
|
5812
|
-
process.exit(1);
|
|
5813
|
-
}
|
|
5814
|
-
});
|
|
5815
|
-
program.command("show").description("Show schema details").requiredOption("--schema-id <id>", "Schema ID to show").option("--base-dir <path>", "Override schema base directory").action(async (cmdOptions) => {
|
|
5816
|
-
try {
|
|
5817
|
-
const registry = getSchemaRegistry({
|
|
5818
|
-
baseDir: cmdOptions.baseDir || options.baseDir
|
|
5819
|
-
});
|
|
5820
|
-
const schema = await registry.getSchema(cmdOptions.schemaId);
|
|
5821
|
-
console.log("Schema Details:\n");
|
|
5822
|
-
console.log(` ID: ${schema.id}`);
|
|
5823
|
-
console.log(` Format: ${schema.format}`);
|
|
5824
|
-
console.log(` Path: ${schema.path}`);
|
|
5825
|
-
console.log(` Relative Path: ${schema.relativePath}`);
|
|
5826
|
-
if (schema.version) {
|
|
5827
|
-
console.log(` Version: ${schema.version}`);
|
|
5828
|
-
}
|
|
5829
|
-
if (schema.description) {
|
|
5830
|
-
console.log(` Description: ${schema.description}`);
|
|
5831
|
-
}
|
|
5832
|
-
if (schema.schemaDraft) {
|
|
5833
|
-
console.log(` Schema Draft: ${schema.schemaDraft}`);
|
|
5834
|
-
}
|
|
5835
|
-
const content = await readFile(schema.path, "utf-8");
|
|
5836
|
-
console.log("\nSchema Content:");
|
|
5837
|
-
console.log(content);
|
|
5838
|
-
} catch (error) {
|
|
5839
|
-
console.error("Error showing schema:", error.message);
|
|
5840
|
-
process.exit(1);
|
|
5841
|
-
}
|
|
5842
|
-
});
|
|
5843
|
-
program.command("validate").description("Validate data file against schema").requiredOption("--schema-id <id>", "Schema ID to validate against").argument("<file>", "Data file to validate").option("--use-goneat", "Use goneat for validation (requires goneat binary)").option("--goneat-path <path>", "Path to goneat binary").option("--base-dir <path>", "Override schema base directory").action(
|
|
5844
|
-
async (file, cmdOptions) => {
|
|
5845
|
-
try {
|
|
5846
|
-
let result;
|
|
5847
|
-
if (cmdOptions.useGoneat) {
|
|
5848
|
-
const available = await isGoneatAvailable(cmdOptions.goneatPath);
|
|
5849
|
-
if (!available) {
|
|
5850
|
-
console.error("\u274C goneat not available. Install goneat or remove --use-goneat flag.");
|
|
5851
|
-
console.error(" AJV validation (default) works without external dependencies.");
|
|
5852
|
-
process.exit(1);
|
|
5853
|
-
}
|
|
5854
|
-
const registry = getSchemaRegistry({
|
|
5855
|
-
baseDir: cmdOptions.baseDir || options.baseDir
|
|
5856
|
-
});
|
|
5857
|
-
const schema = await registry.getSchema(cmdOptions.schemaId);
|
|
5858
|
-
console.log("Using goneat validation...");
|
|
5859
|
-
result = await runGoneatValidation(schema.path, file, cmdOptions.goneatPath);
|
|
5860
|
-
} else {
|
|
5861
|
-
console.log("Using AJV validation...");
|
|
5862
|
-
result = await validateFileBySchemaId(file, cmdOptions.schemaId, {
|
|
5863
|
-
baseDir: cmdOptions.baseDir || options.baseDir
|
|
5864
|
-
});
|
|
5865
|
-
}
|
|
5866
|
-
if (result.valid) {
|
|
5867
|
-
console.log(`\u2705 Validation passed (${result.source})`);
|
|
5868
|
-
process.exit(0);
|
|
5869
|
-
} else {
|
|
5870
|
-
console.log(`\u274C Validation failed (${result.source})`);
|
|
5871
|
-
console.log("\nDiagnostics:");
|
|
5872
|
-
console.log(formatDiagnostics(result.diagnostics));
|
|
5873
|
-
process.exit(1);
|
|
5874
|
-
}
|
|
5875
|
-
} catch (error) {
|
|
5876
|
-
console.error("Error validating file:", error.message);
|
|
5877
|
-
process.exit(1);
|
|
5878
|
-
}
|
|
5879
|
-
}
|
|
5880
|
-
);
|
|
5881
|
-
program.command("validate-schema").description("Validate a schema file itself").argument("<file>", "Schema file to validate").action(async (file) => {
|
|
5882
|
-
try {
|
|
5883
|
-
const content = await readFile(file, "utf-8");
|
|
5884
|
-
const { validateSchema: validateSchema2 } = await Promise.resolve().then(() => (init_validator(), validator_exports));
|
|
5885
|
-
const result = await validateSchema2(content);
|
|
5886
|
-
if (result.valid) {
|
|
5887
|
-
console.log("\u2705 Schema is valid");
|
|
5888
|
-
process.exit(0);
|
|
5889
|
-
} else {
|
|
5890
|
-
console.log("\u274C Schema is invalid");
|
|
5891
|
-
console.log("\nDiagnostics:");
|
|
5892
|
-
console.log(formatDiagnostics(result.diagnostics));
|
|
5893
|
-
process.exit(1);
|
|
5894
|
-
}
|
|
5895
|
-
} catch (error) {
|
|
5896
|
-
console.error("Error validating schema:", error.message);
|
|
5897
|
-
process.exit(1);
|
|
5898
|
-
}
|
|
5899
|
-
});
|
|
5900
|
-
program.command("normalize").description("Normalize schema to canonical JSON format").argument("<file>", "Schema file to normalize").option("--compact", "Output compact JSON (no formatting)").option("-o, --output <file>", "Write to output file instead of stdout").action(async (file, cmdOptions) => {
|
|
5901
|
-
try {
|
|
5902
|
-
const content = await readFile(file, "utf-8");
|
|
5903
|
-
const normalized = normalizeSchema(content, {
|
|
5904
|
-
compact: cmdOptions.compact
|
|
5905
|
-
});
|
|
5906
|
-
if (cmdOptions.output) {
|
|
5907
|
-
await writeFile(cmdOptions.output, normalized, "utf-8");
|
|
5908
|
-
console.log(`\u2705 Normalized schema written to ${cmdOptions.output}`);
|
|
5909
|
-
} else {
|
|
5910
|
-
console.log(normalized);
|
|
5911
|
-
}
|
|
5912
|
-
} catch (error) {
|
|
5913
|
-
console.error("Error normalizing schema:", error.message);
|
|
5914
|
-
process.exit(1);
|
|
5915
|
-
}
|
|
5916
|
-
});
|
|
5917
|
-
program.command("compare").description("Compare two schemas for semantic equality").argument("<file1>", "First schema file").argument("<file2>", "Second schema file").option("--show-normalized", "Show normalized outputs").action(async (file1, file2, cmdOptions) => {
|
|
5918
|
-
try {
|
|
5919
|
-
const content1 = await readFile(file1, "utf-8");
|
|
5920
|
-
const content2 = await readFile(file2, "utf-8");
|
|
5921
|
-
const result = compareSchemas(content1, content2);
|
|
5922
|
-
if (result.equal) {
|
|
5923
|
-
console.log("\u2705 Schemas are semantically equal");
|
|
5924
|
-
} else {
|
|
5925
|
-
console.log("\u274C Schemas differ");
|
|
5926
|
-
}
|
|
5927
|
-
if (cmdOptions.showNormalized) {
|
|
5928
|
-
console.log("\nNormalized Schema 1:");
|
|
5929
|
-
console.log(result.normalizedA);
|
|
5930
|
-
console.log("\nNormalized Schema 2:");
|
|
5931
|
-
console.log(result.normalizedB);
|
|
5932
|
-
}
|
|
5933
|
-
process.exit(result.equal ? 0 : 1);
|
|
5934
|
-
} catch (error) {
|
|
5935
|
-
console.error("Error comparing schemas:", error.message);
|
|
5936
|
-
process.exit(1);
|
|
5937
|
-
}
|
|
5938
|
-
});
|
|
5939
|
-
program.command("export").description("Export schema from registry to file with provenance").requiredOption("--schema-id <id>", "Schema ID to export").requiredOption("--out <path>", "Output file path").option("--force", "Overwrite existing file", false).option("--no-provenance", "Exclude provenance metadata").option("--no-validate", "Skip schema validation before export").option("--format <format>", "Export format (json|yaml|auto)", "auto").option("--base-dir <path>", "Override schema base directory").action(
|
|
5940
|
-
async (cmdOptions) => {
|
|
5941
|
-
try {
|
|
5942
|
-
const { exportSchema: exportSchema2 } = await Promise.resolve().then(() => (init_export(), export_exports));
|
|
5943
|
-
const { exitCodes: exitCodes2 } = await Promise.resolve().then(() => (init_foundry(), foundry_exports));
|
|
5944
|
-
const result = await exportSchema2({
|
|
5945
|
-
schemaId: cmdOptions.schemaId,
|
|
5946
|
-
outPath: cmdOptions.out,
|
|
5947
|
-
includeProvenance: cmdOptions.provenance ?? true,
|
|
5948
|
-
validate: cmdOptions.validate ?? true,
|
|
5949
|
-
overwrite: cmdOptions.force ?? false,
|
|
5950
|
-
format: cmdOptions.format ?? "auto",
|
|
5951
|
-
baseDir: cmdOptions.baseDir || options.baseDir
|
|
5952
|
-
});
|
|
5953
|
-
console.log("\u2705 Schema exported successfully");
|
|
5954
|
-
console.log(` Schema ID: ${result.schemaId}`);
|
|
5955
|
-
console.log(` Output: ${result.outPath}`);
|
|
5956
|
-
console.log(` Format: ${result.format}`);
|
|
5957
|
-
if (result.provenance) {
|
|
5958
|
-
console.log("\nProvenance:");
|
|
5959
|
-
console.log(` Crucible: ${result.provenance.crucible_version}`);
|
|
5960
|
-
console.log(` Library: ${result.provenance.library_version}`);
|
|
5961
|
-
if (result.provenance.revision) {
|
|
5962
|
-
console.log(` Revision: ${result.provenance.revision}`);
|
|
5963
|
-
}
|
|
5964
|
-
console.log(` Exported: ${result.provenance.exported_at}`);
|
|
5965
|
-
}
|
|
5966
|
-
process.exit(exitCodes2.EXIT_SUCCESS);
|
|
5967
|
-
} catch (error) {
|
|
5968
|
-
const { exitCodes: exitCodes2 } = await Promise.resolve().then(() => (init_foundry(), foundry_exports));
|
|
5969
|
-
const { SchemaExportError: SchemaExportError2, SchemaValidationError: SchemaValidationError2, ExportErrorReason: ExportErrorReason2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
5970
|
-
console.error("\u274C Schema export failed:", error.message);
|
|
5971
|
-
if (error instanceof SchemaExportError2) {
|
|
5972
|
-
if (error.outPath) {
|
|
5973
|
-
console.error(` Output path: ${error.outPath}`);
|
|
5974
|
-
}
|
|
5975
|
-
switch (error.reason) {
|
|
5976
|
-
case ExportErrorReason2.FILE_EXISTS:
|
|
5977
|
-
case ExportErrorReason2.WRITE_FAILED:
|
|
5978
|
-
process.exit(exitCodes2.EXIT_FILE_WRITE_ERROR);
|
|
5979
|
-
break;
|
|
5980
|
-
case ExportErrorReason2.INVALID_FORMAT:
|
|
5981
|
-
process.exit(exitCodes2.EXIT_INVALID_ARGUMENT);
|
|
5982
|
-
break;
|
|
5983
|
-
default:
|
|
5984
|
-
process.exit(exitCodes2.EXIT_FAILURE);
|
|
5985
|
-
}
|
|
5986
|
-
}
|
|
5987
|
-
if (error instanceof SchemaValidationError2) {
|
|
5988
|
-
const errorMsg = error.message.toLowerCase();
|
|
5989
|
-
if (errorMsg.includes("not found")) {
|
|
5990
|
-
process.exit(exitCodes2.EXIT_FILE_NOT_FOUND);
|
|
5991
|
-
}
|
|
5992
|
-
process.exit(exitCodes2.EXIT_DATA_INVALID);
|
|
5993
|
-
}
|
|
5994
|
-
process.exit(exitCodes2.EXIT_FAILURE);
|
|
5995
|
-
}
|
|
5996
|
-
}
|
|
5997
|
-
);
|
|
5998
|
-
program.command("identity-show").description("Show application identity from .fulmen/app.yaml").option("--path <path>", "Explicit path to app.yaml").option("--json", "Output as JSON").action(async (cmdOptions) => {
|
|
5999
|
-
try {
|
|
6000
|
-
const { loadIdentity: loadIdentity2 } = await Promise.resolve().then(() => (init_loader2(), loader_exports));
|
|
6001
|
-
const { exitCodes: exitCodes2 } = await Promise.resolve().then(() => (init_foundry(), foundry_exports));
|
|
6002
|
-
const identity = await loadIdentity2({ path: cmdOptions.path });
|
|
6003
|
-
if (cmdOptions.json) {
|
|
6004
|
-
console.log(JSON.stringify(identity, null, 2));
|
|
6005
|
-
} else {
|
|
6006
|
-
console.log("Application Identity:\n");
|
|
6007
|
-
console.log(` Binary Name: ${identity.app.binary_name}`);
|
|
6008
|
-
console.log(` Vendor: ${identity.app.vendor}`);
|
|
6009
|
-
console.log(` Env Prefix: ${identity.app.env_prefix}`);
|
|
6010
|
-
console.log(` Config Name: ${identity.app.config_name}`);
|
|
6011
|
-
console.log(` Description: ${identity.app.description}`);
|
|
6012
|
-
if (identity.metadata) {
|
|
6013
|
-
console.log("\nMetadata:");
|
|
6014
|
-
if (identity.metadata.license) {
|
|
6015
|
-
console.log(` License: ${identity.metadata.license}`);
|
|
6016
|
-
}
|
|
6017
|
-
if (identity.metadata.repository_category) {
|
|
6018
|
-
console.log(` Category: ${identity.metadata.repository_category}`);
|
|
6019
|
-
}
|
|
6020
|
-
if (identity.metadata.telemetry_namespace) {
|
|
6021
|
-
console.log(` Telemetry: ${identity.metadata.telemetry_namespace}`);
|
|
6022
|
-
}
|
|
6023
|
-
if (identity.metadata.project_url) {
|
|
6024
|
-
console.log(` Project URL: ${identity.metadata.project_url}`);
|
|
6025
|
-
}
|
|
6026
|
-
}
|
|
6027
|
-
}
|
|
6028
|
-
process.exit(exitCodes2.EXIT_SUCCESS);
|
|
6029
|
-
} catch (error) {
|
|
6030
|
-
const { exitCodes: exitCodes2 } = await Promise.resolve().then(() => (init_foundry(), foundry_exports));
|
|
6031
|
-
const { AppIdentityError: AppIdentityError2 } = await Promise.resolve().then(() => (init_errors5(), errors_exports2));
|
|
6032
|
-
console.error("\u274C Failed to load identity:", error.message);
|
|
6033
|
-
if (error instanceof AppIdentityError2) {
|
|
6034
|
-
if (error.message.includes("not found")) {
|
|
6035
|
-
process.exit(exitCodes2.EXIT_FILE_NOT_FOUND);
|
|
6036
|
-
}
|
|
6037
|
-
if (error.message.includes("Invalid") || error.message.includes("validation")) {
|
|
6038
|
-
process.exit(exitCodes2.EXIT_DATA_INVALID);
|
|
6039
|
-
}
|
|
6040
|
-
}
|
|
6041
|
-
process.exit(exitCodes2.EXIT_FAILURE);
|
|
6042
|
-
}
|
|
6043
|
-
});
|
|
6044
|
-
program.command("identity-validate").description("Validate application identity against schema").argument("[file]", "Path to app.yaml (defaults to discovery)").action(async (file) => {
|
|
6045
|
-
try {
|
|
6046
|
-
const { loadIdentity: loadIdentity2 } = await Promise.resolve().then(() => (init_loader2(), loader_exports));
|
|
6047
|
-
const { exitCodes: exitCodes2 } = await Promise.resolve().then(() => (init_foundry(), foundry_exports));
|
|
6048
|
-
console.log("Validating application identity...");
|
|
6049
|
-
const identity = await loadIdentity2({ path: file });
|
|
6050
|
-
console.log("\u2705 Identity is valid");
|
|
6051
|
-
console.log(` Binary: ${identity.app.binary_name}`);
|
|
6052
|
-
console.log(` Vendor: ${identity.app.vendor}`);
|
|
6053
|
-
process.exit(exitCodes2.EXIT_SUCCESS);
|
|
6054
|
-
} catch (error) {
|
|
6055
|
-
const { exitCodes: exitCodes2 } = await Promise.resolve().then(() => (init_foundry(), foundry_exports));
|
|
6056
|
-
const { AppIdentityError: AppIdentityError2 } = await Promise.resolve().then(() => (init_errors5(), errors_exports2));
|
|
6057
|
-
console.error("\u274C Identity validation failed:", error.message);
|
|
6058
|
-
if (error instanceof AppIdentityError2) {
|
|
6059
|
-
if (error.message.includes("not found")) {
|
|
6060
|
-
process.exit(exitCodes2.EXIT_FILE_NOT_FOUND);
|
|
6061
|
-
}
|
|
6062
|
-
if (error.message.includes("Invalid") || error.message.includes("validation")) {
|
|
6063
|
-
process.exit(exitCodes2.EXIT_DATA_INVALID);
|
|
6064
|
-
}
|
|
6065
|
-
}
|
|
6066
|
-
process.exit(exitCodes2.EXIT_FAILURE);
|
|
6067
|
-
}
|
|
6068
|
-
});
|
|
6069
|
-
return program;
|
|
6070
|
-
}
|
|
6071
|
-
var init_cli = __esm({
|
|
6072
|
-
"src/schema/cli.ts"() {
|
|
6073
|
-
init_goneat_bridge();
|
|
6074
|
-
init_normalizer();
|
|
6075
|
-
init_registry();
|
|
6076
|
-
init_utils();
|
|
6077
|
-
init_validator();
|
|
6078
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
6079
|
-
const program = createCLI();
|
|
6080
|
-
program.parse(process.argv);
|
|
6081
|
-
}
|
|
2129
|
+
init_errors3();
|
|
6082
2130
|
}
|
|
6083
2131
|
});
|
|
6084
2132
|
|
|
6085
|
-
// src/
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
init_errors();
|
|
6091
|
-
init_export();
|
|
6092
|
-
init_goneat_bridge();
|
|
6093
|
-
init_normalizer();
|
|
6094
|
-
init_registry();
|
|
6095
|
-
init_utils();
|
|
6096
|
-
init_validator();
|
|
6097
|
-
}
|
|
6098
|
-
});
|
|
6099
|
-
function deepFreeze6(obj) {
|
|
6100
|
-
Object.freeze(obj);
|
|
6101
|
-
Object.getOwnPropertyNames(obj).forEach((prop) => {
|
|
6102
|
-
const value = obj[prop];
|
|
6103
|
-
if (value !== null && (typeof value === "object" || typeof value === "function") && !Object.isFrozen(value)) {
|
|
6104
|
-
deepFreeze6(value);
|
|
6105
|
-
}
|
|
6106
|
-
});
|
|
6107
|
-
return obj;
|
|
6108
|
-
}
|
|
6109
|
-
async function registerEmbeddedIdentity(data) {
|
|
6110
|
-
if (isRegistered) {
|
|
6111
|
-
throw AppIdentityError.alreadyRegistered();
|
|
2133
|
+
// src/appidentity/runtime.ts
|
|
2134
|
+
function detectRuntime() {
|
|
2135
|
+
const versions = process.versions;
|
|
2136
|
+
if (typeof versions.bun === "string" && versions.bun.length > 0) {
|
|
2137
|
+
return { name: "bun", version: versions.bun };
|
|
6112
2138
|
}
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
let parsed;
|
|
6116
|
-
try {
|
|
6117
|
-
parsed = parse(data);
|
|
6118
|
-
} catch (error) {
|
|
6119
|
-
throw AppIdentityError.embeddedParseFailed(
|
|
6120
|
-
error instanceof Error ? error : new Error(String(error))
|
|
6121
|
-
);
|
|
6122
|
-
}
|
|
6123
|
-
const result = await validateDataBySchemaId(parsed, APP_IDENTITY_SCHEMA_ID);
|
|
6124
|
-
if (!result.valid) {
|
|
6125
|
-
throw AppIdentityError.embeddedValidationFailed(result.diagnostics);
|
|
6126
|
-
}
|
|
6127
|
-
identity = parsed;
|
|
6128
|
-
} else {
|
|
6129
|
-
const result = await validateDataBySchemaId(data, APP_IDENTITY_SCHEMA_ID);
|
|
6130
|
-
if (!result.valid) {
|
|
6131
|
-
throw AppIdentityError.embeddedValidationFailed(result.diagnostics);
|
|
6132
|
-
}
|
|
6133
|
-
identity = data;
|
|
2139
|
+
if (typeof versions.node === "string" && versions.node.length > 0) {
|
|
2140
|
+
return { name: "node", version: versions.node };
|
|
6134
2141
|
}
|
|
6135
|
-
|
|
6136
|
-
isRegistered = true;
|
|
6137
|
-
}
|
|
6138
|
-
function hasEmbeddedIdentity() {
|
|
6139
|
-
return isRegistered;
|
|
6140
|
-
}
|
|
6141
|
-
function getEmbeddedIdentity() {
|
|
6142
|
-
return embeddedIdentity;
|
|
2142
|
+
return { name: "unknown" };
|
|
6143
2143
|
}
|
|
6144
|
-
function
|
|
6145
|
-
|
|
6146
|
-
|
|
2144
|
+
function buildRuntimeInfo(options = {}) {
|
|
2145
|
+
const runtime = detectRuntime();
|
|
2146
|
+
const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
|
|
2147
|
+
const vendor = options.vendor ?? options.identity?.app.vendor;
|
|
2148
|
+
return {
|
|
2149
|
+
service: {
|
|
2150
|
+
name: serviceName,
|
|
2151
|
+
vendor,
|
|
2152
|
+
version: options.version
|
|
2153
|
+
},
|
|
2154
|
+
runtime,
|
|
2155
|
+
platform: {
|
|
2156
|
+
os: process.platform,
|
|
2157
|
+
arch: process.arch
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
6147
2160
|
}
|
|
6148
|
-
var
|
|
6149
|
-
|
|
6150
|
-
"src/appidentity/embedded.ts"() {
|
|
6151
|
-
init_schema();
|
|
6152
|
-
init_constants();
|
|
6153
|
-
init_errors5();
|
|
6154
|
-
embeddedIdentity = null;
|
|
6155
|
-
isRegistered = false;
|
|
2161
|
+
var init_runtime = __esm({
|
|
2162
|
+
"src/appidentity/runtime.ts"() {
|
|
6156
2163
|
}
|
|
6157
2164
|
});
|
|
6158
2165
|
|
|
6159
2166
|
// src/appidentity/index.ts
|
|
6160
2167
|
init_constants();
|
|
6161
2168
|
init_embedded();
|
|
6162
|
-
|
|
2169
|
+
init_errors3();
|
|
6163
2170
|
|
|
6164
2171
|
// src/appidentity/helpers.ts
|
|
6165
|
-
|
|
2172
|
+
init_loader();
|
|
6166
2173
|
async function getBinaryName(options) {
|
|
6167
2174
|
const identity = await loadIdentity(options);
|
|
6168
2175
|
return identity.app.binary_name;
|
|
@@ -6201,7 +2208,7 @@ async function getEnvVar(key, options) {
|
|
|
6201
2208
|
}
|
|
6202
2209
|
|
|
6203
2210
|
// src/appidentity/index.ts
|
|
6204
|
-
|
|
2211
|
+
init_loader();
|
|
6205
2212
|
init_runtime();
|
|
6206
2213
|
|
|
6207
2214
|
// src/docscribe/normalize.ts
|
|
@@ -6534,7 +2541,7 @@ var YAML_KEY_PATTERN = /^\s*[\w"'-]+\s*:\s*.+$/;
|
|
|
6534
2541
|
var MARKDOWN_HEADING = /(\n|^)\s{0,3}#{1,6}\s+/;
|
|
6535
2542
|
var MARKDOWN_LIST = /(\n|^)\s{0,3}(?:-|\*|\+)\s+/;
|
|
6536
2543
|
var MARKDOWN_SETEXT = /(\n|^).+\n\s{0,3}(?:=+|-+)\s*(\n|$)/;
|
|
6537
|
-
function
|
|
2544
|
+
function detectFormat(input) {
|
|
6538
2545
|
const { content } = normalizeInput(input);
|
|
6539
2546
|
return detectFormatFromContent(content);
|
|
6540
2547
|
}
|
|
@@ -6686,7 +2693,7 @@ function splitDocuments(input, options) {
|
|
|
6686
2693
|
return [];
|
|
6687
2694
|
}
|
|
6688
2695
|
const maxDocuments = options?.maxDocuments ?? Number.POSITIVE_INFINITY;
|
|
6689
|
-
const format =
|
|
2696
|
+
const format = detectFormat(content);
|
|
6690
2697
|
let splits;
|
|
6691
2698
|
if (format === "yaml-stream") {
|
|
6692
2699
|
splits = withIndices(splitYamlStream(content));
|
|
@@ -8381,8 +4388,8 @@ async function scanZip(archive, options) {
|
|
|
8381
4388
|
var FULPACK_VERSION = "1.0.0";
|
|
8382
4389
|
|
|
8383
4390
|
// src/index.ts
|
|
8384
|
-
var
|
|
4391
|
+
var VERSION = "0.3.2";
|
|
8385
4392
|
|
|
8386
|
-
export { APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_FILENAME, APP_IDENTITY_SCHEMA_ID, AppIdentityError, ArchiveFormat, DocScribeError, DocScribeParseError, DocScribeUnsupportedFormatError, ERROR_CODES, EntryType, FULPACK_VERSION, FulpackOperationError, MAX_ANCESTOR_SEARCH_DEPTH, Operation,
|
|
4393
|
+
export { APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_FILENAME, APP_IDENTITY_SCHEMA_ID, AppIdentityError, ArchiveFormat, DocScribeError, DocScribeParseError, DocScribeUnsupportedFormatError, ERROR_CODES, EntryType, FULPACK_VERSION, FulpackOperationError, MAX_ANCESTOR_SEARCH_DEPTH, Operation, VERSION, __internal, buildEnvVar, buildRuntimeInfo, checkDecompressionBomb, clearEmbeddedIdentity, clearIdentityCache, create, createFulpackError, detectFormat, extract, extractHeaders, extractMetadata, getBinaryName, getCachedIdentity, getConfigIdentifiers, getConfigName, getEmbeddedIdentity, getEnvPrefix, getEnvVar, getTelemetryNamespace, getVendor, hasEmbeddedIdentity, hasPathTraversal, info, inspectDocument, isAbsolutePath, loadIdentity, normalizeInput, parseFrontmatter, registerEmbeddedIdentity, scan, splitDocuments, stripFrontmatter, validatePath, verify };
|
|
8387
4394
|
//# sourceMappingURL=index.js.map
|
|
8388
4395
|
//# sourceMappingURL=index.js.map
|