@fulmenhq/tsfulmen 0.3.3 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/CHANGELOG.md +76 -1
  2. package/README.md +30 -1
  3. package/config/crucible-ts/taxonomy/metrics.yaml +1 -1
  4. package/dist/appidentity/index.js +9 -2209
  5. package/dist/appidentity/index.js.map +1 -1
  6. package/dist/assets/index.d.ts +184 -0
  7. package/dist/assets/index.js +4 -0
  8. package/dist/assets/index.js.map +1 -0
  9. package/dist/bin/prometheus-cli.js +15 -9139
  10. package/dist/bin/prometheus-cli.js.map +1 -1
  11. package/dist/bin/schema-cli.js +4 -6098
  12. package/dist/bin/schema-cli.js.map +1 -1
  13. package/dist/bin/signals-cli.js +15 -1910
  14. package/dist/bin/signals-cli.js.map +1 -1
  15. package/dist/chunk-24BAF63H.js +194 -0
  16. package/dist/chunk-24BAF63H.js.map +1 -0
  17. package/dist/chunk-3HISR2XI.js +36 -0
  18. package/dist/chunk-3HISR2XI.js.map +1 -0
  19. package/dist/chunk-56F2EEAG.js +328 -0
  20. package/dist/chunk-56F2EEAG.js.map +1 -0
  21. package/dist/chunk-5HZEJGZ5.js +725 -0
  22. package/dist/chunk-5HZEJGZ5.js.map +1 -0
  23. package/dist/chunk-7YWACZEJ.js +837 -0
  24. package/dist/chunk-7YWACZEJ.js.map +1 -0
  25. package/dist/chunk-BCO3FUUO.js +682 -0
  26. package/dist/chunk-BCO3FUUO.js.map +1 -0
  27. package/dist/chunk-GOTEQYAD.js +410 -0
  28. package/dist/chunk-GOTEQYAD.js.map +1 -0
  29. package/dist/chunk-HY6JQOFN.js +32 -0
  30. package/dist/chunk-HY6JQOFN.js.map +1 -0
  31. package/dist/chunk-LMNAUTW7.js +43 -0
  32. package/dist/chunk-LMNAUTW7.js.map +1 -0
  33. package/dist/chunk-LWAH2UG7.js +103 -0
  34. package/dist/chunk-LWAH2UG7.js.map +1 -0
  35. package/dist/chunk-OHE2AOH4.js +273 -0
  36. package/dist/chunk-OHE2AOH4.js.map +1 -0
  37. package/dist/chunk-P3DNOZOG.js +1488 -0
  38. package/dist/chunk-P3DNOZOG.js.map +1 -0
  39. package/dist/chunk-PQ6TKPUN.js +27 -0
  40. package/dist/chunk-PQ6TKPUN.js.map +1 -0
  41. package/dist/chunk-QCCN6POA.js +211 -0
  42. package/dist/chunk-QCCN6POA.js.map +1 -0
  43. package/dist/chunk-QGTNV4U2.js +985 -0
  44. package/dist/chunk-QGTNV4U2.js.map +1 -0
  45. package/dist/chunk-RSLFR4TO.js +631 -0
  46. package/dist/chunk-RSLFR4TO.js.map +1 -0
  47. package/dist/chunk-SNV5WG5V.js +9412 -0
  48. package/dist/chunk-SNV5WG5V.js.map +1 -0
  49. package/dist/chunk-UP2VWCW5.js +33 -0
  50. package/dist/chunk-UP2VWCW5.js.map +1 -0
  51. package/dist/chunk-VJIGX7MH.js +208 -0
  52. package/dist/chunk-VJIGX7MH.js.map +1 -0
  53. package/dist/chunk-WCCMRVZF.js +130 -0
  54. package/dist/chunk-WCCMRVZF.js.map +1 -0
  55. package/dist/chunk-WSMVLWLB.js +269 -0
  56. package/dist/chunk-WSMVLWLB.js.map +1 -0
  57. package/dist/config/index.js +6 -1220
  58. package/dist/config/index.js.map +1 -1
  59. package/dist/crucible/fulpack/index.js +2 -25
  60. package/dist/crucible/fulpack/index.js.map +1 -1
  61. package/dist/crucible/index.js +13 -1129
  62. package/dist/crucible/index.js.map +1 -1
  63. package/dist/docscribe/index.js +2 -680
  64. package/dist/docscribe/index.js.map +1 -1
  65. package/dist/errors/index.js +5 -1831
  66. package/dist/errors/index.js.map +1 -1
  67. package/dist/errors-D7KXV3QW.js +4 -0
  68. package/dist/errors-D7KXV3QW.js.map +1 -0
  69. package/dist/errors-R7ZODJXS.js +8 -0
  70. package/dist/errors-R7ZODJXS.js.map +1 -0
  71. package/dist/export-EA3RNQNS.js +6 -0
  72. package/dist/export-EA3RNQNS.js.map +1 -0
  73. package/dist/foundry/index.js +340 -3949
  74. package/dist/foundry/index.js.map +1 -1
  75. package/dist/foundry/similarity/index.js +3 -134
  76. package/dist/foundry/similarity/index.js.map +1 -1
  77. package/dist/fulencode/index.js +3 -319
  78. package/dist/fulencode/index.js.map +1 -1
  79. package/dist/fulhash/index.js +3 -611
  80. package/dist/fulhash/index.js.map +1 -1
  81. package/dist/fulpack/index.js +3 -1515
  82. package/dist/fulpack/index.js.map +1 -1
  83. package/dist/hash-YOJYVOWH.js +4 -0
  84. package/dist/hash-YOJYVOWH.js.map +1 -0
  85. package/dist/index.d.ts +1 -1
  86. package/dist/index.js +14 -4395
  87. package/dist/index.js.map +1 -1
  88. package/dist/loader-VP2ZIC4O.js +9 -0
  89. package/dist/loader-VP2ZIC4O.js.map +1 -0
  90. package/dist/logging/index.js +2 -723
  91. package/dist/logging/index.js.map +1 -1
  92. package/dist/pathfinder/index.js +18 -2341
  93. package/dist/pathfinder/index.js.map +1 -1
  94. package/dist/prom-client-PNEBGD3G.js +4801 -0
  95. package/dist/prom-client-PNEBGD3G.js.map +1 -0
  96. package/dist/reports/license-inventory.csv +1 -1
  97. package/dist/schema/index.d.ts +19 -6
  98. package/dist/schema/index.js +4 -6148
  99. package/dist/schema/index.js.map +1 -1
  100. package/dist/signals/index.js +8 -2767
  101. package/dist/signals/index.js.map +1 -1
  102. package/dist/similarity/index.js +3 -134
  103. package/dist/similarity/index.js.map +1 -1
  104. package/dist/telemetry/http/index.js +9 -930
  105. package/dist/telemetry/http/index.js.map +1 -1
  106. package/dist/telemetry/index.js +4 -1561
  107. package/dist/telemetry/index.js.map +1 -1
  108. package/dist/telemetry/prometheus/index.js +7 -8422
  109. package/dist/telemetry/prometheus/index.js.map +1 -1
  110. package/dist/validator-I3ASDVDB.js +6 -0
  111. package/dist/validator-I3ASDVDB.js.map +1 -0
  112. package/package.json +5 -1
  113. package/schemas/crucible-ts/library/module-manifest/v1.0.0/module-manifest.schema.json +1 -1
  114. package/schemas/crucible-ts/observability/logging/v1.0.0/definitions.schema.json +1 -1
  115. package/schemas/crucible-ts/observability/logging/v1.0.0/log-event.schema.json +13 -13
  116. package/schemas/crucible-ts/observability/logging/v1.0.0/logger-config.schema.json +2 -2
  117. package/schemas/crucible-ts/observability/logging/v1.0.0/logging-policy.schema.json +1 -1
  118. package/schemas/crucible-ts/observability/logging/v1.0.0/middleware-config.schema.json +2 -2
  119. package/schemas/crucible-ts/observability/logging/v1.0.0/severity-filter.schema.json +3 -3
  120. package/schemas/crucible-ts/upstream/3leaps/crucible/PROVENANCE.md +3 -3
  121. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/catalog/classifiers/README.md +1 -1
  122. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/classifiers-framework.md +1 -1
  123. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/data-sensitivity-classification.md +1 -1
@@ -1,1833 +1,7 @@
1
- import { randomUUID } from 'crypto';
2
- import addFormats from 'ajv-formats';
3
- import 'child_process';
4
- import { readFile, access } from 'fs/promises';
5
- import { parse } from 'yaml';
6
- import { dirname, join, relative, extname } from 'path';
7
- import { fileURLToPath } from 'url';
8
- import glob from 'fast-glob';
9
- import Ajv from 'ajv';
10
- import Ajv2019 from 'ajv/dist/2019.js';
11
- import Ajv2020 from 'ajv/dist/2020.js';
12
- import AjvDraft04 from 'ajv-draft-04';
13
- import 'commander';
14
-
15
- var __getOwnPropNames = Object.getOwnPropertyNames;
16
- var __esm = (fn, res) => function __init() {
17
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
18
- };
19
- function generateCorrelationId() {
20
- return randomUUID();
21
- }
22
- function isValidCorrelationId(id) {
23
- return UUID_V4_PATTERN.test(id);
24
- }
25
- function normalizeCorrelationId(id) {
26
- return id.trim().toLowerCase();
27
- }
28
- function isCorrelationId(value) {
29
- return typeof value === "string" && isValidCorrelationId(value);
30
- }
31
- var UUID_V4_PATTERN;
32
- var init_correlation = __esm({
33
- "src/errors/correlation.ts"() {
34
- UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
35
- }
36
- });
37
-
38
- // src/errors/severity.ts
39
- function severityToLevel(name) {
40
- if (!isSeverityName(name)) {
41
- throw new Error(
42
- `Invalid severity name: "${name}". Must be one of: ${Object.values(Severity).join(", ")}`
43
- );
44
- }
45
- return SEVERITY_LEVELS[name];
46
- }
47
- function levelToSeverity(level) {
48
- if (!isSeverityLevel(level)) {
49
- throw new Error(`Invalid severity level: ${level}. Must be 0-4`);
50
- }
51
- return LEVEL_TO_SEVERITY[level];
52
- }
53
- function isSeverityName(value) {
54
- return typeof value === "string" && Object.values(Severity).includes(value);
55
- }
56
- function isSeverityLevel(value) {
57
- return typeof value === "number" && value >= 0 && value <= 4 && Number.isInteger(value);
58
- }
59
- function getDefaultSeverity() {
60
- return {
61
- name: Severity.MEDIUM,
62
- level: 2
63
- };
64
- }
65
- function compareSeverity(a, b) {
66
- const levelA = typeof a === "string" ? severityToLevel(a) : a;
67
- const levelB = typeof b === "string" ? severityToLevel(b) : b;
68
- return levelA - levelB;
69
- }
70
- var Severity, SEVERITY_LEVELS, LEVEL_TO_SEVERITY;
71
- var init_severity = __esm({
72
- "src/errors/severity.ts"() {
73
- Severity = {
74
- INFO: "info",
75
- LOW: "low",
76
- MEDIUM: "medium",
77
- HIGH: "high",
78
- CRITICAL: "critical"
79
- };
80
- SEVERITY_LEVELS = {
81
- info: 0,
82
- low: 1,
83
- medium: 2,
84
- high: 3,
85
- critical: 4
86
- };
87
- LEVEL_TO_SEVERITY = {
88
- 0: "info",
89
- 1: "low",
90
- 2: "medium",
91
- 3: "high",
92
- 4: "critical"
93
- };
94
- }
95
- });
96
-
97
- // src/errors/serialization.ts
98
- function serializeError(error, code = "UNKNOWN_ERROR", severity = Severity.MEDIUM) {
99
- if (error instanceof Error) {
100
- return {
101
- code,
102
- message: error.message,
103
- severity,
104
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
105
- context: {
106
- name: error.name,
107
- stack: error.stack
108
- },
109
- original: error.stack || error.message
110
- };
111
- }
112
- if (isErrorLike(error)) {
113
- return {
114
- code,
115
- message: error.message,
116
- severity,
117
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
118
- details: error.details,
119
- context: error.context,
120
- original: JSON.stringify(error)
121
- };
122
- }
123
- if (typeof error === "string") {
124
- return {
125
- code,
126
- message: error,
127
- severity,
128
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
129
- };
130
- }
131
- return {
132
- code,
133
- message: String(error),
134
- severity,
135
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
136
- original: typeof error === "object" ? JSON.stringify(error) : String(error)
137
- };
138
- }
139
- function extractErrorMessage(error) {
140
- if (error instanceof Error) {
141
- return error.message;
142
- }
143
- if (isErrorLike(error)) {
144
- return error.message;
145
- }
146
- if (typeof error === "string") {
147
- return error;
148
- }
149
- return String(error);
150
- }
151
- function extractStackTrace(error) {
152
- if (error instanceof Error) {
153
- return error.stack;
154
- }
155
- if (isErrorLike(error) && typeof error.stack === "string") {
156
- return error.stack;
157
- }
158
- return void 0;
159
- }
160
- function isErrorLike(value) {
161
- return typeof value === "object" && value !== null && "message" in value && typeof value.message === "string";
162
- }
163
- var init_serialization = __esm({
164
- "src/errors/serialization.ts"() {
165
- init_severity();
166
- }
167
- });
168
- function applyFulmenAjvFormats(ajv, options = {}) {
169
- const mode = options.mode ?? "fast";
170
- const formats = options.formats ?? DEFAULT_FORMATS;
171
- addFormats(ajv, { mode, formats });
172
- return ajv;
173
- }
174
- var DEFAULT_FORMATS;
175
- var init_ajv_formats = __esm({
176
- "src/schema/ajv-formats.ts"() {
177
- DEFAULT_FORMATS = [
178
- "date-time",
179
- "email",
180
- "hostname",
181
- "ipv4",
182
- "ipv6",
183
- "uri",
184
- "uri-reference",
185
- "uuid"
186
- ];
187
- }
188
- });
189
-
190
- // src/schema/errors.ts
191
- var SchemaValidationError;
192
- var init_errors = __esm({
193
- "src/schema/errors.ts"() {
194
- SchemaValidationError = class _SchemaValidationError extends Error {
195
- constructor(message, schemaId, diagnostics = [], source, cause) {
196
- super(message);
197
- this.schemaId = schemaId;
198
- this.diagnostics = diagnostics;
199
- this.source = source;
200
- this.cause = cause;
201
- this.name = "SchemaValidationError";
202
- if (Error.captureStackTrace) {
203
- Error.captureStackTrace(this, _SchemaValidationError);
204
- }
205
- }
206
- /**
207
- * Create error for schema not found
208
- */
209
- static schemaNotFound(schemaId) {
210
- return new _SchemaValidationError(`Schema not found: ${schemaId}`, schemaId);
211
- }
212
- /**
213
- * Create error for invalid schema input
214
- */
215
- static invalidSchemaInput(source, details) {
216
- return new _SchemaValidationError(`Invalid schema input: ${details}`, void 0, [], source);
217
- }
218
- /**
219
- * Create error for validation failure
220
- */
221
- static validationFailed(schemaId, diagnostics, source) {
222
- const errorCount = diagnostics.filter((d) => d.severity === "ERROR").length;
223
- const warningCount = diagnostics.filter((d) => d.severity === "WARN").length;
224
- const message = `Schema validation failed: ${errorCount} error(s), ${warningCount} warning(s)`;
225
- return new _SchemaValidationError(message, schemaId, diagnostics, source);
226
- }
227
- /**
228
- * Create error for goneat binary not found
229
- */
230
- static goneatNotFound(goneatPath) {
231
- const pathInfo = goneatPath ? ` at ${goneatPath}` : "";
232
- return new _SchemaValidationError(
233
- `Goneat binary not found${pathInfo}. Falling back to AJV validation.`
234
- );
235
- }
236
- /**
237
- * Create error for goneat execution failure
238
- */
239
- static goneatExecutionFailed(error) {
240
- return new _SchemaValidationError(
241
- "Goneat execution failed. Falling back to AJV validation.",
242
- void 0,
243
- [],
244
- void 0,
245
- error
246
- );
247
- }
248
- /**
249
- * Create error for empty schema input
250
- */
251
- static emptySchemaInput(source) {
252
- return new _SchemaValidationError("Schema content is empty", void 0, [], source);
253
- }
254
- /**
255
- * Create error for parse failure
256
- */
257
- static parseFailed(source, error) {
258
- return new _SchemaValidationError(
259
- `Failed to parse schema: ${error.message}`,
260
- void 0,
261
- [],
262
- source,
263
- error
264
- );
265
- }
266
- /**
267
- * Create error for encoding failure
268
- */
269
- static encodingFailed(source, error) {
270
- return new _SchemaValidationError(
271
- `Failed to encode schema: ${error.message}`,
272
- void 0,
273
- [],
274
- source,
275
- error
276
- );
277
- }
278
- /**
279
- * Create error for registry operation failure
280
- */
281
- static registryError(operation, details) {
282
- return new _SchemaValidationError(`Schema registry ${operation} failed: ${details}`);
283
- }
284
- /**
285
- * Format error for display
286
- */
287
- format() {
288
- let output = this.message;
289
- if (this.schemaId) {
290
- output += `
291
- Schema ID: ${this.schemaId}`;
292
- }
293
- if (this.diagnostics.length > 0) {
294
- output += "\n\nValidation Issues:";
295
- this.diagnostics.forEach((diag, index) => {
296
- output += `
297
- ${index + 1}. [${diag.severity}] ${diag.message}`;
298
- if (diag.pointer) {
299
- output += ` at ${diag.pointer}`;
300
- }
301
- if (diag.keyword) {
302
- output += ` (keyword: ${diag.keyword})`;
303
- }
304
- if (diag.source) {
305
- output += ` [${diag.source}]`;
306
- }
307
- });
308
- }
309
- if (this.source) {
310
- output += `
311
-
312
- Source: ${this.source.type}`;
313
- if (this.source.id) {
314
- output += ` (${this.source.id})`;
315
- }
316
- }
317
- return output;
318
- }
319
- /**
320
- * Convert to JSON representation
321
- */
322
- toJSON() {
323
- return {
324
- name: this.name,
325
- message: this.message,
326
- schemaId: this.schemaId,
327
- diagnostics: this.diagnostics,
328
- source: this.source,
329
- cause: this.cause?.message
330
- };
331
- }
332
- };
333
- }
334
- });
335
-
336
- // src/schema/utils.ts
337
- var init_utils = __esm({
338
- "src/schema/utils.ts"() {
339
- init_errors();
340
- }
341
- });
342
- var init_goneat_bridge = __esm({
343
- "src/schema/goneat-bridge.ts"() {
344
- init_utils();
345
- }
346
- });
347
- var init_normalizer = __esm({
348
- "src/schema/normalizer.ts"() {
349
- init_errors();
350
- }
351
- });
352
- function optionsChanged(newOptions) {
353
- if (!globalRegistryOptions) return false;
354
- return true;
355
- }
356
- function getSchemaRegistry(options) {
357
- if (!globalRegistry || optionsChanged()) {
358
- globalRegistry = new SchemaRegistry(options);
359
- globalRegistryOptions = options;
360
- }
361
- return globalRegistry;
362
- }
363
- var DEFAULT_PATTERNS, SchemaRegistry, globalRegistry, globalRegistryOptions;
364
- var init_registry = __esm({
365
- "src/schema/registry.ts"() {
366
- init_errors();
367
- DEFAULT_PATTERNS = ["**/*.schema.json", "**/*.schema.yaml", "**/*.schema.yml"];
368
- SchemaRegistry = class {
369
- schemas = /* @__PURE__ */ new Map();
370
- options;
371
- constructor(options = {}) {
372
- this.options = {
373
- baseDir: options.baseDir || this.getDefaultSchemaDir(),
374
- patterns: options.patterns || DEFAULT_PATTERNS,
375
- followSymlinks: options.followSymlinks ?? false,
376
- maxDepth: options.maxDepth ?? 10
377
- };
378
- }
379
- /**
380
- * Get default schema directory using import.meta.url
381
- */
382
- getDefaultSchemaDir() {
383
- const __filename = fileURLToPath(import.meta.url);
384
- const __dirname2 = dirname(__filename);
385
- return join(__dirname2, "..", "..", "schemas", "crucible-ts");
386
- }
387
- /**
388
- * Build logical schema ID from file path
389
- */
390
- buildSchemaId(filePath, baseDir) {
391
- const relativePath = relative(baseDir, filePath);
392
- const withoutExt = relativePath.replace(/\.(schema\.(json|yaml|yml))$/, "");
393
- return withoutExt.replace(/\\/g, "/");
394
- }
395
- /**
396
- * Extract schema format from file extension
397
- */
398
- getSchemaFormat(filePath) {
399
- const ext = extname(filePath).toLowerCase();
400
- switch (ext) {
401
- case ".json":
402
- return "json";
403
- case ".yaml":
404
- case ".yml":
405
- return "yaml";
406
- default:
407
- return "json";
408
- }
409
- }
410
- /**
411
- * Extract metadata from schema file
412
- */
413
- async extractMetadata(filePath) {
414
- try {
415
- const content = await readFile(filePath, "utf-8");
416
- const format = this.getSchemaFormat(filePath);
417
- let parsed;
418
- if (format === "yaml") {
419
- parsed = parse(content);
420
- } else {
421
- parsed = JSON.parse(content);
422
- }
423
- const baseDir = this.options.baseDir ?? "";
424
- const relativePath = relative(baseDir, filePath);
425
- return {
426
- id: this.buildSchemaId(filePath, baseDir),
427
- path: filePath,
428
- relativePath,
429
- format,
430
- version: parsed.$schema || parsed.version,
431
- description: parsed.title || parsed.description,
432
- schemaDraft: parsed.$schema
433
- };
434
- } catch (error) {
435
- throw SchemaValidationError.registryError(
436
- "metadata extraction",
437
- `Failed to process ${filePath}: ${error.message}`
438
- );
439
- }
440
- }
441
- /**
442
- * Discover and index all available schemas
443
- */
444
- async discoverSchemas() {
445
- try {
446
- const baseDir = this.options.baseDir ?? "";
447
- const patterns = this.options.patterns ?? [];
448
- if (patterns.length === 0) {
449
- this.schemas.clear();
450
- return;
451
- }
452
- const pattern = patterns.map((p) => join(baseDir, p));
453
- try {
454
- await access(baseDir);
455
- } catch {
456
- this.schemas.clear();
457
- return;
458
- }
459
- const files = await glob(pattern, {
460
- absolute: true,
461
- followSymbolicLinks: this.options.followSymlinks,
462
- deep: this.options.maxDepth,
463
- onlyFiles: true,
464
- suppressErrors: true
465
- // Don't throw on permission errors
466
- });
467
- this.schemas.clear();
468
- for (const filePath of files) {
469
- try {
470
- const metadata = await this.extractMetadata(filePath);
471
- this.schemas.set(metadata.id, metadata);
472
- } catch (error) {
473
- console.warn(`Warning: Failed to process schema ${filePath}:`, error);
474
- }
475
- }
476
- } catch (error) {
477
- throw SchemaValidationError.registryError("discovery", error.message);
478
- }
479
- }
480
- /**
481
- * List available schemas with optional prefix filtering
482
- */
483
- async listSchemas(prefix) {
484
- if (this.schemas.size === 0) {
485
- await this.discoverSchemas();
486
- }
487
- const schemas = Array.from(this.schemas.values());
488
- if (prefix) {
489
- return schemas.filter((schema) => schema.id.startsWith(prefix));
490
- }
491
- return schemas;
492
- }
493
- /**
494
- * Get schema by logical ID
495
- */
496
- async getSchema(id) {
497
- if (this.schemas.size === 0) {
498
- await this.discoverSchemas();
499
- }
500
- const schema = this.schemas.get(id);
501
- if (!schema) {
502
- throw SchemaValidationError.schemaNotFound(id);
503
- }
504
- return schema;
505
- }
506
- /**
507
- * Get schema by file path
508
- */
509
- async getSchemaByPath(filePath) {
510
- if (this.schemas.size === 0) {
511
- await this.discoverSchemas();
512
- }
513
- const absolutePath = filePath.startsWith("/") ? filePath : join(process.cwd(), filePath);
514
- for (const schema of this.schemas.values()) {
515
- if (schema.path === absolutePath) {
516
- return schema;
517
- }
518
- }
519
- throw SchemaValidationError.schemaNotFound(filePath);
520
- }
521
- /**
522
- * Check if schema exists
523
- */
524
- async hasSchema(id) {
525
- if (this.schemas.size === 0) {
526
- await this.discoverSchemas();
527
- }
528
- return this.schemas.has(id);
529
- }
530
- /**
531
- * Get registry size
532
- */
533
- get size() {
534
- return this.schemas.size;
535
- }
536
- /**
537
- * Clear registry cache
538
- */
539
- clear() {
540
- this.schemas.clear();
541
- }
542
- };
543
- }
544
- });
545
-
546
- // src/telemetry/counter.ts
547
- var Counter;
548
- var init_counter = __esm({
549
- "src/telemetry/counter.ts"() {
550
- Counter = class {
551
- constructor(name) {
552
- this.name = name;
553
- }
554
- value = 0;
555
- labeledValues = /* @__PURE__ */ new Map();
556
- /**
557
- * Increment counter by delta (default: 1)
558
- *
559
- * @param delta - Amount to increment (must be non-negative)
560
- * @param labels - Optional label dimensions for this observation
561
- * @throws {Error} If delta is negative
562
- *
563
- * @example
564
- * ```typescript
565
- * counter.inc(); // Increment unlabeled by 1
566
- * counter.inc(5); // Increment unlabeled by 5
567
- * counter.inc(1, { status: '200' }); // Increment labeled instance
568
- * counter.inc(1, { result: 'success' }); // Different label set
569
- * ```
570
- */
571
- inc(delta = 1, labels) {
572
- if (delta < 0) {
573
- throw new Error(`Counter delta must be non-negative, got: ${delta}`);
574
- }
575
- if (labels && Object.keys(labels).length > 0) {
576
- const labelKey = this.serializeLabels(labels);
577
- const current = this.labeledValues.get(labelKey) || 0;
578
- this.labeledValues.set(labelKey, current + delta);
579
- } else {
580
- this.value += delta;
581
- }
582
- }
583
- /**
584
- * Get current counter value (unlabeled)
585
- */
586
- getValue() {
587
- return this.value;
588
- }
589
- /**
590
- * Get all labeled values
591
- * @returns Map of serialized label keys to values
592
- */
593
- getLabeledValues() {
594
- return new Map(this.labeledValues);
595
- }
596
- /**
597
- * Get value for specific label combination
598
- */
599
- getValueForLabels(labels) {
600
- const labelKey = this.serializeLabels(labels);
601
- return this.labeledValues.get(labelKey) || 0;
602
- }
603
- /**
604
- * Reset counter to zero (all label combinations)
605
- */
606
- reset() {
607
- this.value = 0;
608
- this.labeledValues.clear();
609
- }
610
- /**
611
- * Serialize labels to deterministic string key
612
- * Format: key1=value1,key2=value2 (sorted by key)
613
- */
614
- serializeLabels(labels) {
615
- return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
616
- }
617
- };
618
- }
619
- });
620
-
621
- // src/telemetry/gauge.ts
622
- var Gauge;
623
- var init_gauge = __esm({
624
- "src/telemetry/gauge.ts"() {
625
- Gauge = class {
626
- constructor(name) {
627
- this.name = name;
628
- }
629
- value = 0;
630
- labeledValues = /* @__PURE__ */ new Map();
631
- /**
632
- * Set gauge to specific value
633
- *
634
- * @param value - New gauge value (can be any number, including negative)
635
- * @param labels - Optional label dimensions for this observation
636
- *
637
- * @example
638
- * ```typescript
639
- * gauge.set(42); // Set unlabeled to 42
640
- * gauge.set(-10); // Negative values allowed
641
- * gauge.set(1, { phase: 'collect' }); // Set labeled instance
642
- * ```
643
- */
644
- set(value, labels) {
645
- if (labels && Object.keys(labels).length > 0) {
646
- const labelKey = this.serializeLabels(labels);
647
- this.labeledValues.set(labelKey, value);
648
- } else {
649
- this.value = value;
650
- }
651
- }
652
- /**
653
- * Increment gauge by delta (default: 1)
654
- *
655
- * @param delta - Amount to increment (can be negative)
656
- * @param labels - Optional label dimensions for this observation
657
- */
658
- inc(delta = 1, labels) {
659
- if (labels && Object.keys(labels).length > 0) {
660
- const labelKey = this.serializeLabels(labels);
661
- const current = this.labeledValues.get(labelKey) || 0;
662
- this.labeledValues.set(labelKey, current + delta);
663
- } else {
664
- this.value += delta;
665
- }
666
- }
667
- /**
668
- * Decrement gauge by delta (default: 1)
669
- *
670
- * @param delta - Amount to decrement (can be negative)
671
- * @param labels - Optional label dimensions for this observation
672
- */
673
- dec(delta = 1, labels) {
674
- if (labels && Object.keys(labels).length > 0) {
675
- const labelKey = this.serializeLabels(labels);
676
- const current = this.labeledValues.get(labelKey) || 0;
677
- this.labeledValues.set(labelKey, current - delta);
678
- } else {
679
- this.value -= delta;
680
- }
681
- }
682
- /**
683
- * Get current gauge value (unlabeled)
684
- */
685
- getValue() {
686
- return this.value;
687
- }
688
- /**
689
- * Get all labeled values
690
- * @returns Map of serialized label keys to values
691
- */
692
- getLabeledValues() {
693
- return new Map(this.labeledValues);
694
- }
695
- /**
696
- * Get value for specific label combination
697
- */
698
- getValueForLabels(labels) {
699
- const labelKey = this.serializeLabels(labels);
700
- return this.labeledValues.get(labelKey) || 0;
701
- }
702
- /**
703
- * Reset gauge to zero (all label combinations)
704
- */
705
- reset() {
706
- this.value = 0;
707
- this.labeledValues.clear();
708
- }
709
- /**
710
- * Serialize labels to deterministic string key
711
- * Format: key1=value1,key2=value2 (sorted by key)
712
- */
713
- serializeLabels(labels) {
714
- return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
715
- }
716
- };
717
- }
718
- });
719
- async function getDefaultUnit(name) {
720
- return TaxonomyLoader.getInstance().getDefaultUnit(name);
721
- }
722
- var DEFAULT_MS_BUCKETS, TaxonomyLoader;
723
- var init_taxonomy = __esm({
724
- "src/telemetry/taxonomy.ts"() {
725
- DEFAULT_MS_BUCKETS = [1, 5, 10, 50, 100, 500, 1e3, 5e3, 1e4];
726
- TaxonomyLoader = class _TaxonomyLoader {
727
- static instance;
728
- taxonomy = null;
729
- loadPromise = null;
730
- loadError = null;
731
- constructor() {
732
- }
733
- /**
734
- * Get singleton instance
735
- */
736
- static getInstance() {
737
- if (!_TaxonomyLoader.instance) {
738
- _TaxonomyLoader.instance = new _TaxonomyLoader();
739
- }
740
- return _TaxonomyLoader.instance;
741
- }
742
- /**
743
- * Load taxonomy from YAML file
744
- */
745
- async load() {
746
- if (this.taxonomy !== null) {
747
- return this.taxonomy;
748
- }
749
- if (this.loadError !== null) {
750
- throw this.loadError;
751
- }
752
- if (this.loadPromise) {
753
- return this.loadPromise;
754
- }
755
- this.loadPromise = (async () => {
756
- try {
757
- const taxonomyPath = join(
758
- __dirname,
759
- "..",
760
- "..",
761
- "config",
762
- "crucible-ts",
763
- "taxonomy",
764
- "metrics.yaml"
765
- );
766
- const content = await readFile(taxonomyPath, "utf-8");
767
- this.taxonomy = parse(content);
768
- return this.taxonomy;
769
- } catch (err) {
770
- this.loadError = err instanceof Error ? err : new Error(String(err));
771
- throw new Error(`Failed to load metrics taxonomy: ${this.loadError.message}`);
772
- }
773
- })();
774
- return this.loadPromise;
775
- }
776
- /**
777
- * Get taxonomy (async)
778
- */
779
- async getTaxonomy() {
780
- return this.load();
781
- }
782
- /**
783
- * Get metric definition by name
784
- */
785
- async getMetric(name) {
786
- const taxonomy = await this.load();
787
- return taxonomy.metrics.find((m) => m.name === name);
788
- }
789
- /**
790
- * Get default unit for metric
791
- */
792
- async getDefaultUnit(name) {
793
- const metric = await this.getMetric(name);
794
- return metric?.unit;
795
- }
796
- /**
797
- * Get default histogram buckets for metric
798
- * Returns ADR-0007 buckets for _ms metrics, undefined for others
799
- */
800
- async getDefaultBuckets(name) {
801
- if (name.endsWith("_ms")) {
802
- const taxonomy = await this.load();
803
- return taxonomy.defaults.histogram_buckets.ms_metrics;
804
- }
805
- return void 0;
806
- }
807
- /**
808
- * Check if metric name is valid (exists in taxonomy)
809
- */
810
- async isValidMetricName(name) {
811
- try {
812
- const taxonomy = await this.load();
813
- return taxonomy.metrics.some((m) => m.name === name);
814
- } catch {
815
- return false;
816
- }
817
- }
818
- /**
819
- * Reset loader state (for testing)
820
- * @internal
821
- */
822
- static _reset() {
823
- _TaxonomyLoader.instance = new _TaxonomyLoader();
824
- }
825
- };
826
- }
827
- });
828
-
829
- // src/telemetry/histogram.ts
830
- var Histogram;
831
- var init_histogram = __esm({
832
- "src/telemetry/histogram.ts"() {
833
- init_taxonomy();
834
- Histogram = class {
835
- constructor(name, options) {
836
- this.name = name;
837
- if (options?.buckets) {
838
- this.buckets = [...options.buckets].sort((a, b) => a - b);
839
- } else if (name.endsWith("_ms") || name.endsWith("_seconds")) {
840
- this.buckets = [...DEFAULT_MS_BUCKETS];
841
- } else {
842
- this.buckets = [];
843
- }
844
- for (const bucket of this.buckets) {
845
- this.bucketCounts.set(bucket, 0);
846
- }
847
- }
848
- count = 0;
849
- sum = 0;
850
- bucketCounts = /* @__PURE__ */ new Map();
851
- labeledStates = /* @__PURE__ */ new Map();
852
- buckets;
853
- /**
854
- * Record an observation
855
- *
856
- * @param value - Value to observe (typically a duration in ms or seconds)
857
- * @param labels - Optional label dimensions for this observation
858
- *
859
- * @example
860
- * ```typescript
861
- * const start = performance.now();
862
- * // ... operation ...
863
- * histogram.observe(performance.now() - start);
864
- * histogram.observe(duration, { phase: 'collect', result: 'success' });
865
- * ```
866
- */
867
- observe(value, labels) {
868
- if (labels && Object.keys(labels).length > 0) {
869
- const labelKey = this.serializeLabels(labels);
870
- let state = this.labeledStates.get(labelKey);
871
- if (!state) {
872
- state = {
873
- count: 0,
874
- sum: 0,
875
- bucketCounts: /* @__PURE__ */ new Map()
876
- };
877
- for (const bucket of this.buckets) {
878
- state.bucketCounts.set(bucket, 0);
879
- }
880
- this.labeledStates.set(labelKey, state);
881
- }
882
- state.count++;
883
- state.sum += value;
884
- for (const bucket of this.buckets) {
885
- if (value <= bucket) {
886
- state.bucketCounts.set(bucket, (state.bucketCounts.get(bucket) || 0) + 1);
887
- }
888
- }
889
- } else {
890
- this.count++;
891
- this.sum += value;
892
- for (const bucket of this.buckets) {
893
- if (value <= bucket) {
894
- this.bucketCounts.set(bucket, (this.bucketCounts.get(bucket) || 0) + 1);
895
- }
896
- }
897
- }
898
- }
899
- /**
900
- * Get histogram summary
901
- *
902
- * Returns OTLP-compatible histogram summary with cumulative bucket counts.
903
- */
904
- getSummary() {
905
- const buckets = this.buckets.map((le) => ({
906
- le,
907
- count: this.bucketCounts.get(le) || 0
908
- }));
909
- return {
910
- count: this.count,
911
- sum: this.sum,
912
- buckets
913
- };
914
- }
915
- /**
916
- * Get current observation count
917
- */
918
- getCount() {
919
- return this.count;
920
- }
921
- /**
922
- * Get sum of all observed values
923
- */
924
- getSum() {
925
- return this.sum;
926
- }
927
- /**
928
- * Get average of observed values
929
- */
930
- getAverage() {
931
- return this.count > 0 ? this.sum / this.count : 0;
932
- }
933
- /**
934
- * Get all labeled summaries
935
- * @returns Map of serialized label keys to histogram summaries
936
- */
937
- getLabeledSummaries() {
938
- const summaries = /* @__PURE__ */ new Map();
939
- for (const [labelKey, state] of this.labeledStates) {
940
- const buckets = this.buckets.map((le) => ({
941
- le,
942
- count: state.bucketCounts.get(le) || 0
943
- }));
944
- summaries.set(labelKey, {
945
- count: state.count,
946
- sum: state.sum,
947
- buckets
948
- });
949
- }
950
- return summaries;
951
- }
952
- /**
953
- * Get summary for specific label combination
954
- */
955
- getSummaryForLabels(labels) {
956
- const labelKey = this.serializeLabels(labels);
957
- const state = this.labeledStates.get(labelKey);
958
- if (!state) {
959
- return null;
960
- }
961
- const buckets = this.buckets.map((le) => ({
962
- le,
963
- count: state.bucketCounts.get(le) || 0
964
- }));
965
- return {
966
- count: state.count,
967
- sum: state.sum,
968
- buckets
969
- };
970
- }
971
- /**
972
- * Reset histogram to initial state (all label combinations)
973
- */
974
- reset() {
975
- this.count = 0;
976
- this.sum = 0;
977
- for (const bucket of this.buckets) {
978
- this.bucketCounts.set(bucket, 0);
979
- }
980
- this.labeledStates.clear();
981
- }
982
- /**
983
- * Serialize labels to deterministic string key
984
- * Format: key1=value1,key2=value2 (sorted by key)
985
- */
986
- serializeLabels(labels) {
987
- return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
988
- }
989
- };
990
- }
991
- });
992
-
993
- // src/telemetry/registry.ts
994
- var MetricsRegistry;
995
- var init_registry2 = __esm({
996
- "src/telemetry/registry.ts"() {
997
- init_counter();
998
- init_gauge();
999
- init_histogram();
1000
- init_taxonomy();
1001
- MetricsRegistry = class {
1002
- counters = /* @__PURE__ */ new Map();
1003
- gauges = /* @__PURE__ */ new Map();
1004
- histograms = /* @__PURE__ */ new Map();
1005
- /**
1006
- * Get or create a counter
1007
- *
1008
- * @param name - Metric name from taxonomy
1009
- * @returns Counter instance
1010
- *
1011
- * @example
1012
- * ```typescript
1013
- * const counter = registry.counter('schema_validations');
1014
- * counter.inc();
1015
- * ```
1016
- */
1017
- counter(name) {
1018
- let counter = this.counters.get(name);
1019
- if (!counter) {
1020
- counter = new Counter(name);
1021
- this.counters.set(name, counter);
1022
- }
1023
- return counter;
1024
- }
1025
- /**
1026
- * Get or create a gauge
1027
- *
1028
- * @param name - Metric name from taxonomy
1029
- * @returns Gauge instance
1030
- *
1031
- * @example
1032
- * ```typescript
1033
- * const gauge = registry.gauge('foundry_lookup_count');
1034
- * gauge.set(42);
1035
- * ```
1036
- */
1037
- gauge(name) {
1038
- let gauge = this.gauges.get(name);
1039
- if (!gauge) {
1040
- gauge = new Gauge(name);
1041
- this.gauges.set(name, gauge);
1042
- }
1043
- return gauge;
1044
- }
1045
- /**
1046
- * Get or create a histogram
1047
- *
1048
- * @param name - Metric name from taxonomy
1049
- * @param options - Optional histogram options
1050
- * @returns Histogram instance
1051
- *
1052
- * @example
1053
- * ```typescript
1054
- * // Auto-applies ADR-0007 buckets for _ms metrics
1055
- * const histogram = registry.histogram('config_load_ms');
1056
- * histogram.observe(42.5);
1057
- *
1058
- * // Custom buckets
1059
- * const custom = registry.histogram('custom_metric', {
1060
- * buckets: [10, 50, 100, 500, 1000]
1061
- * });
1062
- * ```
1063
- */
1064
- histogram(name, options) {
1065
- let histogram = this.histograms.get(name);
1066
- if (!histogram) {
1067
- histogram = new Histogram(name, options);
1068
- this.histograms.set(name, histogram);
1069
- }
1070
- return histogram;
1071
- }
1072
- /**
1073
- * Export all metrics as events
1074
- *
1075
- * Returns array of schema-compliant MetricsEvent objects.
1076
- * Does not clear metrics (use flush() to clear after export).
1077
- *
1078
- * @returns Promise resolving to array of metrics events
1079
- *
1080
- * @example
1081
- * ```typescript
1082
- * const events = await registry.export();
1083
- * console.log(JSON.stringify(events, null, 2));
1084
- * ```
1085
- */
1086
- async export() {
1087
- const events = [];
1088
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1089
- for (const [name, counter] of this.counters) {
1090
- const unit = await getDefaultUnit(name);
1091
- events.push({
1092
- timestamp,
1093
- name,
1094
- value: counter.getValue(),
1095
- unit
1096
- });
1097
- for (const [labelKey, value] of counter.getLabeledValues()) {
1098
- if (value > 0) {
1099
- const tags = this.deserializeLabels(labelKey);
1100
- events.push({
1101
- timestamp,
1102
- name,
1103
- value,
1104
- tags,
1105
- unit
1106
- });
1107
- }
1108
- }
1109
- }
1110
- for (const [name, gauge] of this.gauges) {
1111
- const unit = await getDefaultUnit(name);
1112
- events.push({
1113
- timestamp,
1114
- name,
1115
- value: gauge.getValue(),
1116
- unit
1117
- });
1118
- for (const [labelKey, value] of gauge.getLabeledValues()) {
1119
- const tags = this.deserializeLabels(labelKey);
1120
- events.push({
1121
- timestamp,
1122
- name,
1123
- value,
1124
- tags,
1125
- unit
1126
- });
1127
- }
1128
- }
1129
- for (const [name, histogram] of this.histograms) {
1130
- const unit = await getDefaultUnit(name);
1131
- events.push({
1132
- timestamp,
1133
- name,
1134
- value: histogram.getSummary(),
1135
- unit
1136
- });
1137
- for (const [labelKey, summary] of histogram.getLabeledSummaries()) {
1138
- if (summary.count > 0) {
1139
- const tags = this.deserializeLabels(labelKey);
1140
- events.push({
1141
- timestamp,
1142
- name,
1143
- value: summary,
1144
- tags,
1145
- unit
1146
- });
1147
- }
1148
- }
1149
- }
1150
- return events;
1151
- }
1152
- /**
1153
- * Deserialize label key back to tags object
1154
- * Format: key1=value1,key2=value2 → {key1: "value1", key2: "value2"}
1155
- */
1156
- deserializeLabels(labelKey) {
1157
- if (!labelKey) {
1158
- return {};
1159
- }
1160
- const tags = {};
1161
- for (const pair of labelKey.split(",")) {
1162
- const [key, value] = pair.split("=");
1163
- if (key && value) {
1164
- tags[key] = value;
1165
- }
1166
- }
1167
- return tags;
1168
- }
1169
- /**
1170
- * Export and clear all metrics
1171
- *
1172
- * Exports metrics as events, optionally emits them via logger,
1173
- * then resets all metrics to zero.
1174
- *
1175
- * @param options - Flush options
1176
- * @returns Promise resolving to array of exported events
1177
- *
1178
- * @example
1179
- * ```typescript
1180
- * // Export and clear
1181
- * const events = await registry.flush();
1182
- *
1183
- * // Export, emit to logger, and clear
1184
- * const events = await registry.flush({
1185
- * emit: (events) => console.log(JSON.stringify(events))
1186
- * });
1187
- * ```
1188
- */
1189
- async flush(options) {
1190
- const events = await this.export();
1191
- try {
1192
- if (options?.emit) {
1193
- options.emit(events);
1194
- }
1195
- } finally {
1196
- this.clear();
1197
- }
1198
- return events;
1199
- }
1200
- /**
1201
- * Clear all metrics (reset to zero)
1202
- *
1203
- * Resets all counters, gauges, and histograms to their initial state.
1204
- */
1205
- clear() {
1206
- for (const counter of this.counters.values()) {
1207
- counter.reset();
1208
- }
1209
- for (const gauge of this.gauges.values()) {
1210
- gauge.reset();
1211
- }
1212
- for (const histogram of this.histograms.values()) {
1213
- histogram.reset();
1214
- }
1215
- }
1216
- /**
1217
- * Get all registered metric names
1218
- *
1219
- * Returns array of all metric names that have been accessed
1220
- * (counters, gauges, or histograms).
1221
- */
1222
- getMetricNames() {
1223
- const names = /* @__PURE__ */ new Set();
1224
- for (const name of this.counters.keys()) {
1225
- names.add(name);
1226
- }
1227
- for (const name of this.gauges.keys()) {
1228
- names.add(name);
1229
- }
1230
- for (const name of this.histograms.keys()) {
1231
- names.add(name);
1232
- }
1233
- return Array.from(names);
1234
- }
1235
- /**
1236
- * Get total count of registered metrics
1237
- */
1238
- getMetricCount() {
1239
- return this.counters.size + this.gauges.size + this.histograms.size;
1240
- }
1241
- };
1242
- }
1243
- });
1244
-
1245
- // src/telemetry/types.ts
1246
- var init_types = __esm({
1247
- "src/telemetry/types.ts"() {
1248
- }
1249
- });
1250
-
1251
- // src/telemetry/validators.ts
1252
- var init_validators = __esm({
1253
- "src/telemetry/validators.ts"() {
1254
- init_schema();
1255
- }
1256
- });
1257
-
1258
- // src/telemetry/index.ts
1259
- var metrics;
1260
- var init_telemetry = __esm({
1261
- "src/telemetry/index.ts"() {
1262
- init_registry2();
1263
- init_registry2();
1264
- init_counter();
1265
- init_gauge();
1266
- init_histogram();
1267
- init_taxonomy();
1268
- init_types();
1269
- init_validators();
1270
- metrics = new MetricsRegistry();
1271
- }
1272
- });
1273
- async function loadMetaSchema(draft) {
1274
- const __filename = fileURLToPath(import.meta.url);
1275
- const __dirname2 = dirname(__filename);
1276
- const metaSchemaPath = join(
1277
- __dirname2,
1278
- "..",
1279
- "..",
1280
- "schemas",
1281
- "crucible-ts",
1282
- "meta",
1283
- draft,
1284
- "schema.json"
1285
- );
1286
- const content = await readFile(metaSchemaPath, "utf-8");
1287
- return JSON.parse(content);
1288
- }
1289
- async function loadVocabularySchemas(draft) {
1290
- if (draft !== "draft-2019-09" && draft !== "draft-2020-12") {
1291
- return [];
1292
- }
1293
- const __filename = fileURLToPath(import.meta.url);
1294
- const __dirname2 = dirname(__filename);
1295
- const vocabDir = join(__dirname2, "..", "..", "schemas", "crucible-ts", "meta", draft, "meta");
1296
- const vocabFiles = draft === "draft-2020-12" ? [
1297
- "core.json",
1298
- "applicator.json",
1299
- "unevaluated.json",
1300
- "validation.json",
1301
- "meta-data.json",
1302
- "format-annotation.json",
1303
- "content.json"
1304
- ] : [
1305
- "core.json",
1306
- "applicator.json",
1307
- "validation.json",
1308
- "meta-data.json",
1309
- "format.json",
1310
- "content.json"
1311
- ];
1312
- const schemas = [];
1313
- for (const file of vocabFiles) {
1314
- try {
1315
- const content = await readFile(join(vocabDir, file), "utf-8");
1316
- schemas.push(JSON.parse(content));
1317
- } catch {
1318
- }
1319
- }
1320
- return schemas;
1321
- }
1322
- async function loadReferencedSchema(uri) {
1323
- const __filename = fileURLToPath(import.meta.url);
1324
- const __dirname2 = dirname(__filename);
1325
- const repoRoot = join(__dirname2, "..", "..");
1326
- let resolvedPath;
1327
- if (uri.startsWith("https://schemas.fulmenhq.dev/")) {
1328
- let relativePath = uri.replace("https://schemas.fulmenhq.dev/", "");
1329
- if (relativePath.startsWith("crucible/")) {
1330
- relativePath = relativePath.slice("crucible/".length);
1331
- }
1332
- if (relativePath.startsWith("config/taxonomy/")) {
1333
- resolvedPath = join(
1334
- repoRoot,
1335
- "config",
1336
- "crucible-ts",
1337
- "taxonomy",
1338
- relativePath.split("/").pop() || ""
1339
- );
1340
- } else {
1341
- resolvedPath = join(repoRoot, "schemas", "crucible-ts", relativePath);
1342
- }
1343
- } else if (uri.startsWith("../../") || uri.startsWith("../")) {
1344
- const schemaBase = join(
1345
- repoRoot,
1346
- "schemas",
1347
- "crucible-ts",
1348
- "observability",
1349
- "metrics",
1350
- "v1.0.0"
1351
- );
1352
- resolvedPath = join(schemaBase, uri);
1353
- } else if (uri.startsWith("file://")) {
1354
- resolvedPath = fileURLToPath(uri);
1355
- } else {
1356
- throw new Error(`Cannot load remote schema: ${uri}`);
1357
- }
1358
- const content = await readFile(resolvedPath, "utf-8");
1359
- const ext = resolvedPath.split(".").pop()?.toLowerCase();
1360
- if (ext === "yaml" || ext === "yml") {
1361
- return parse(content);
1362
- }
1363
- return JSON.parse(content);
1364
- }
1365
- function detectDialect(schema) {
1366
- if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1367
- const maybeSchema = schema;
1368
- const declared = maybeSchema.$schema;
1369
- if (typeof declared === "string") {
1370
- if (declared.includes("draft-04")) return "draft-04";
1371
- if (declared.includes("draft-06")) return "draft-06";
1372
- if (declared.includes("draft-07")) return "draft-07";
1373
- if (declared.includes("draft/2019-09")) return "draft-2019-09";
1374
- if (declared.includes("draft/2020-12")) return "draft-2020-12";
1375
- }
1376
- }
1377
- return "draft-2020-12";
1378
- }
1379
- function createAjv(dialect) {
1380
- const AjvCtor = dialect === "draft-2020-12" ? Ajv2020 : dialect === "draft-2019-09" ? Ajv2019 : dialect === "draft-04" ? AjvDraft04 : Ajv;
1381
- const ajv = new AjvCtor({
1382
- strict: false,
1383
- allErrors: true,
1384
- verbose: true,
1385
- // Allow schemas with $id to be added without replacing existing ones
1386
- addUsedSchema: false,
1387
- // draft-04 uses "id"; later drafts use "$id"
1388
- schemaId: dialect === "draft-04" ? "id" : "$id",
1389
- // Enable async schema loading for YAML references
1390
- loadSchema: loadReferencedSchema
1391
- });
1392
- applyFulmenAjvFormats(ajv);
1393
- return ajv;
1394
- }
1395
- async function getAjv(dialect) {
1396
- const existing = ajvInstances.get(dialect);
1397
- if (existing) {
1398
- const ready = metaschemaReady.get(dialect);
1399
- if (ready) await ready;
1400
- return existing;
1401
- }
1402
- const ajv = createAjv(dialect);
1403
- ajvInstances.set(dialect, ajv);
1404
- const readyPromise = Promise.all([loadVocabularySchemas(dialect), loadMetaSchema(dialect)]).then(([vocabSchemas, metaSchema]) => {
1405
- for (const vocabSchema of vocabSchemas) {
1406
- try {
1407
- ajv.addMetaSchema(vocabSchema);
1408
- } catch {
1409
- }
1410
- }
1411
- try {
1412
- ajv.addMetaSchema(metaSchema);
1413
- } catch {
1414
- }
1415
- }).catch((error) => {
1416
- throw new Error(`Failed to load metaschemas (${dialect}): ${error}`);
1417
- });
1418
- metaschemaReady.set(dialect, readyPromise);
1419
- await readyPromise;
1420
- return ajv;
1421
- }
1422
- async function compileSchema(schema, options = {}) {
1423
- const baseKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1424
- let parsedSchema;
1425
- if (typeof schema === "string") {
1426
- try {
1427
- parsedSchema = JSON.parse(schema);
1428
- } catch {
1429
- parsedSchema = parse(schema);
1430
- }
1431
- } else if (Buffer.isBuffer(schema)) {
1432
- const content = schema.toString("utf-8");
1433
- try {
1434
- parsedSchema = JSON.parse(content);
1435
- } catch {
1436
- parsedSchema = parse(content);
1437
- }
1438
- } else {
1439
- parsedSchema = schema;
1440
- }
1441
- const dialect = detectDialect(parsedSchema);
1442
- const ajv = await getAjv(dialect);
1443
- const cacheKey = `${dialect}:${baseKey}`;
1444
- const cached = schemaCache.get(cacheKey);
1445
- if (cached !== void 0) {
1446
- return cached;
1447
- }
1448
- try {
1449
- if (options.aliases && options.aliases.length > 0) {
1450
- for (const alias of options.aliases) {
1451
- if (alias && ajv.getSchema(alias) === void 0) {
1452
- try {
1453
- if (typeof parsedSchema === "object" && parsedSchema !== null) {
1454
- ajv.addSchema(parsedSchema, alias);
1455
- }
1456
- } catch {
1457
- }
1458
- }
1459
- }
1460
- }
1461
- const validator = typeof parsedSchema === "boolean" ? ajv.compile(parsedSchema) : await ajv.compileAsync(parsedSchema);
1462
- schemaCache.set(cacheKey, validator);
1463
- return validator;
1464
- } catch (error) {
1465
- throw SchemaValidationError.parseFailed(
1466
- {
1467
- type: "string",
1468
- content: typeof schema === "string" ? schema : JSON.stringify(schema)
1469
- },
1470
- error
1471
- );
1472
- }
1473
- }
1474
- async function compileSchemaById(schemaId, registryOptions) {
1475
- try {
1476
- const registry = getSchemaRegistry(registryOptions);
1477
- const metadata = await registry.getSchema(schemaId);
1478
- const content = await readFile(metadata.path, "utf-8");
1479
- const aliases = [];
1480
- const normalizedRelativePath = metadata.relativePath.replace(/\\/g, "/");
1481
- if (normalizedRelativePath) {
1482
- aliases.push(
1483
- new URL(`crucible/${normalizedRelativePath}`, "https://schemas.fulmenhq.dev/").toString()
1484
- );
1485
- }
1486
- return compileSchema(content, { aliases });
1487
- } catch (error) {
1488
- metrics.counter("schema_validation_errors").inc();
1489
- throw error;
1490
- }
1491
- }
1492
- var ajvInstances, metaschemaReady, schemaCache;
1493
- var init_validator = __esm({
1494
- "src/schema/validator.ts"() {
1495
- init_telemetry();
1496
- init_ajv_formats();
1497
- init_errors();
1498
- init_registry();
1499
- init_utils();
1500
- ajvInstances = /* @__PURE__ */ new Map();
1501
- metaschemaReady = /* @__PURE__ */ new Map();
1502
- schemaCache = /* @__PURE__ */ new Map();
1503
- }
1504
- });
1505
- var init_cli = __esm({
1506
- "src/schema/cli.ts"() {
1507
- init_goneat_bridge();
1508
- init_normalizer();
1509
- init_registry();
1510
- init_utils();
1511
- init_validator();
1512
- }
1513
- });
1514
- var init_export = __esm({
1515
- "src/schema/export.ts"() {
1516
- init_errors();
1517
- init_registry();
1518
- init_validator();
1519
- }
1520
- });
1521
-
1522
- // src/schema/index.ts
1523
- var init_schema = __esm({
1524
- "src/schema/index.ts"() {
1525
- init_ajv_formats();
1526
- init_cli();
1527
- init_errors();
1528
- init_export();
1529
- init_goneat_bridge();
1530
- init_normalizer();
1531
- init_registry();
1532
- init_utils();
1533
- init_validator();
1534
- }
1535
- });
1536
-
1537
- // src/errors/validators.ts
1538
- async function validateErrorData(data) {
1539
- return ErrorValidator.getInstance().validate(data);
1540
- }
1541
- function getValidationErrors2() {
1542
- return ErrorValidator.getInstance().getErrors();
1543
- }
1544
- function formatValidationErrors2(errors) {
1545
- return errors.map((err) => {
1546
- const path = err.instancePath || "(root)";
1547
- const message = err.message || "validation failed";
1548
- return `${path}: ${message}`;
1549
- }).join("; ");
1550
- }
1551
- async function assertValidErrorData(data) {
1552
- if (!await validateErrorData(data)) {
1553
- const errors = getValidationErrors2();
1554
- const message = errors ? formatValidationErrors2(errors) : "Error data validation failed";
1555
- throw new Error(`Invalid error data: ${message}`);
1556
- }
1557
- }
1558
- var ErrorValidator;
1559
- var init_validators2 = __esm({
1560
- "src/errors/validators.ts"() {
1561
- init_schema();
1562
- ErrorValidator = class _ErrorValidator {
1563
- static instance;
1564
- validateFn = null;
1565
- initPromise = null;
1566
- initError = null;
1567
- constructor() {
1568
- }
1569
- /**
1570
- * Get singleton instance
1571
- */
1572
- static getInstance() {
1573
- if (!_ErrorValidator.instance) {
1574
- _ErrorValidator.instance = new _ErrorValidator();
1575
- }
1576
- return _ErrorValidator.instance;
1577
- }
1578
- /**
1579
- * Initialize validator (lazy load, async)
1580
- */
1581
- async init() {
1582
- if (this.validateFn !== null || this.initError !== null) {
1583
- return;
1584
- }
1585
- if (this.initPromise) {
1586
- return this.initPromise;
1587
- }
1588
- this.initPromise = (async () => {
1589
- try {
1590
- await compileSchemaById("pathfinder/v1.0.0/error-response");
1591
- await compileSchemaById("assessment/v1.0.0/severity-definitions");
1592
- this.validateFn = await compileSchemaById("error-handling/v1.0.0/error-response");
1593
- } catch (err) {
1594
- this.initError = err instanceof Error ? err : new Error(String(err));
1595
- throw new Error(`Failed to initialize error validator: ${this.initError.message}`);
1596
- }
1597
- })();
1598
- return this.initPromise;
1599
- }
1600
- /**
1601
- * Validate error data against schema
1602
- *
1603
- * @param data - Data to validate
1604
- * @returns Promise resolving to true if valid, false otherwise
1605
- * @throws {Error} If validator failed to initialize
1606
- */
1607
- async validate(data) {
1608
- if (this.validateFn === null) {
1609
- await this.init();
1610
- }
1611
- if (this.initError) {
1612
- throw this.initError;
1613
- }
1614
- if (!this.validateFn) {
1615
- throw new Error("Validator not initialized");
1616
- }
1617
- return this.validateFn(data);
1618
- }
1619
- /**
1620
- * Get validation errors from last validation
1621
- *
1622
- * @returns Validation errors or null
1623
- */
1624
- getErrors() {
1625
- if (!this.validateFn) {
1626
- return null;
1627
- }
1628
- return this.validateFn.errors;
1629
- }
1630
- /**
1631
- * Reset validator state (for testing)
1632
- * @internal
1633
- */
1634
- static _reset() {
1635
- _ErrorValidator.instance = new _ErrorValidator();
1636
- }
1637
- };
1638
- }
1639
- });
1640
-
1641
- // src/errors/fulmen-error.ts
1642
- function isFulmenError(value) {
1643
- return value instanceof FulmenError;
1644
- }
1645
- function isFulmenErrorData(value) {
1646
- return typeof value === "object" && value !== null && "code" in value && typeof value.code === "string" && "message" in value && typeof value.message === "string";
1647
- }
1648
- var FulmenError;
1649
- var init_fulmen_error = __esm({
1650
- "src/errors/fulmen-error.ts"() {
1651
- init_serialization();
1652
- init_severity();
1653
- init_validators2();
1654
- FulmenError = class _FulmenError extends Error {
1655
- data;
1656
- constructor(data) {
1657
- super(data.message);
1658
- this.name = "FulmenError";
1659
- this.data = Object.freeze({ ...data });
1660
- Error.captureStackTrace(this, _FulmenError);
1661
- }
1662
- /**
1663
- * Serialize to JSON (schema-compliant)
1664
- */
1665
- toJSON() {
1666
- return this.data;
1667
- }
1668
- /**
1669
- * Check equality with another FulmenError
1670
- */
1671
- equals(other) {
1672
- return JSON.stringify(this.data) === JSON.stringify(other.data);
1673
- }
1674
- /**
1675
- * Get severity level for comparison
1676
- */
1677
- getSeverityLevel() {
1678
- return this.data.severity_level ?? SEVERITY_LEVELS[this.data.severity ?? "medium"];
1679
- }
1680
- /**
1681
- * Wrap an existing error with FulmenError structure
1682
- *
1683
- * @param error - Error to wrap (Error instance or FulmenErrorData)
1684
- * @param options - Additional error options
1685
- * @returns New FulmenError instance
1686
- *
1687
- * @example
1688
- * ```typescript
1689
- * try {
1690
- * throw new Error('Config invalid');
1691
- * } catch (err) {
1692
- * const fulmenErr = FulmenError.wrap(err, {
1693
- * code: 'CONFIG_INVALID',
1694
- * severity: 'high',
1695
- * exit_code: 2
1696
- * });
1697
- * throw fulmenErr;
1698
- * }
1699
- * ```
1700
- */
1701
- static wrap(error, options = {}) {
1702
- if (error instanceof _FulmenError) {
1703
- const effectiveSeverity = options.severity ?? error.data.severity ?? Severity.MEDIUM;
1704
- const effectiveSeverityLevel = SEVERITY_LEVELS[effectiveSeverity];
1705
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1706
- return new _FulmenError({
1707
- ...error.data,
1708
- ...options,
1709
- code: options.code ?? error.data.code,
1710
- message: error.data.message,
1711
- severity: effectiveSeverity,
1712
- // Consistent severity
1713
- severity_level: effectiveSeverityLevel,
1714
- // Recomputed level
1715
- timestamp
1716
- // Updated timestamp
1717
- });
1718
- }
1719
- if (isFulmenErrorData(error)) {
1720
- const defaultSeverity = getDefaultSeverity();
1721
- const effectiveSeverity = options.severity ?? error.severity ?? defaultSeverity.name;
1722
- const effectiveSeverityLevel = SEVERITY_LEVELS[effectiveSeverity];
1723
- return new _FulmenError({
1724
- ...error,
1725
- ...options,
1726
- severity: effectiveSeverity,
1727
- severity_level: effectiveSeverityLevel,
1728
- // Recomputed, not from error.severity_level
1729
- timestamp: error.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
1730
- });
1731
- }
1732
- return _FulmenError.fromError(error, options);
1733
- }
1734
- /**
1735
- * Create FulmenError from native Error object
1736
- *
1737
- * @param err - Native Error instance
1738
- * @param options - Error options
1739
- * @returns New FulmenError instance
1740
- *
1741
- * @example
1742
- * ```typescript
1743
- * const err = new TypeError('Invalid type');
1744
- * const fulmenErr = FulmenError.fromError(err, {
1745
- * code: 'TYPE_ERROR',
1746
- * severity: 'medium'
1747
- * });
1748
- * ```
1749
- */
1750
- static fromError(err, options = {}) {
1751
- const code = options.code ?? "UNKNOWN_ERROR";
1752
- const severity = options.severity ?? Severity.MEDIUM;
1753
- const severityLevel = SEVERITY_LEVELS[severity];
1754
- const message = extractErrorMessage(err);
1755
- const stack = extractStackTrace(err);
1756
- const data = {
1757
- code,
1758
- message,
1759
- severity,
1760
- severity_level: severityLevel,
1761
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1762
- ...options,
1763
- context: {
1764
- ...options.context,
1765
- originalName: err instanceof Error ? err.name : typeof err,
1766
- stack
1767
- },
1768
- original: stack || message
1769
- };
1770
- return new _FulmenError(data);
1771
- }
1772
- /**
1773
- * Validate error data against schema
1774
- *
1775
- * @param data - Error data to validate
1776
- * @returns Promise resolving to true if valid
1777
- *
1778
- * @example
1779
- * ```typescript
1780
- * const data = { code: 'TEST', message: 'Test error' };
1781
- * if (await FulmenError.validate(data)) {
1782
- * const err = new FulmenError(data);
1783
- * }
1784
- * ```
1785
- */
1786
- static async validate(data) {
1787
- return validateErrorData(data);
1788
- }
1789
- /**
1790
- * Exit process with structured error
1791
- *
1792
- * Logs error as JSON and exits with specified exit code.
1793
- * Mockable for testing (override process.exit).
1794
- *
1795
- * @param error - FulmenError instance
1796
- * @param options - Exit options
1797
- *
1798
- * @example
1799
- * ```typescript
1800
- * const err = FulmenError.fromError(new Error('Fatal'), {
1801
- * code: 'FATAL_ERROR',
1802
- * exit_code: 1
1803
- * });
1804
- * FulmenError.exitWithError(err); // Exits with code 1
1805
- * ```
1806
- */
1807
- static exitWithError(error, options = {}) {
1808
- const logger = options.logger ?? console.error;
1809
- const exitCode = error.data.exit_code ?? 1;
1810
- logger(JSON.stringify(error.toJSON(), null, 2));
1811
- process.exit(exitCode);
1812
- }
1813
- };
1814
- }
1815
- });
1816
-
1817
- // src/errors/index.ts
1818
- var VERSION;
1819
- var init_errors2 = __esm({
1820
- "src/errors/index.ts"() {
1821
- init_correlation();
1822
- init_fulmen_error();
1823
- init_serialization();
1824
- init_severity();
1825
- init_validators2();
1826
- VERSION = "0.2.0";
1827
- }
1828
- });
1829
- init_errors2();
1830
-
1831
- export { FulmenError, LEVEL_TO_SEVERITY, SEVERITY_LEVELS, Severity, VERSION, assertValidErrorData, compareSeverity, extractErrorMessage, extractStackTrace, formatValidationErrors2 as formatValidationErrors, generateCorrelationId, getDefaultSeverity, getValidationErrors2 as getValidationErrors, isCorrelationId, isFulmenError, isFulmenErrorData, isSeverityLevel, isSeverityName, isValidCorrelationId, levelToSeverity, normalizeCorrelationId, serializeError, severityToLevel, validateErrorData };
1
+ export { FulmenError, LEVEL_TO_SEVERITY, SEVERITY_LEVELS, Severity, VERSION, assertValidErrorData, compareSeverity, extractErrorMessage, extractStackTrace, formatValidationErrors, generateCorrelationId, getDefaultSeverity, getValidationErrors, isCorrelationId, isFulmenError, isFulmenErrorData, isSeverityLevel, isSeverityName, isValidCorrelationId, levelToSeverity, normalizeCorrelationId, serializeError, severityToLevel, validateErrorData } from '../chunk-GOTEQYAD.js';
2
+ import '../chunk-SNV5WG5V.js';
3
+ import '../chunk-QCCN6POA.js';
4
+ import '../chunk-56F2EEAG.js';
5
+ import '../chunk-UP2VWCW5.js';
1832
6
  //# sourceMappingURL=index.js.map
1833
7
  //# sourceMappingURL=index.js.map