@fulmenhq/tsfulmen 0.3.2 → 0.4.0

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