@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,1220 +1,12 @@
1
- import addFormats from 'ajv-formats';
2
- import 'child_process';
3
- import { readFile, access, mkdir } from 'fs/promises';
1
+ import { metrics, compileSchema, validateData } from '../chunk-7SVYOPJ3.js';
2
+ import '../chunk-QCCN6POA.js';
3
+ import '../chunk-BTUS6PZZ.js';
4
+ import '../chunk-UP2VWCW5.js';
5
+ import { access, mkdir, readFile } from 'fs/promises';
6
+ import { join, isAbsolute, resolve, extname } from 'path';
4
7
  import { parse } from 'yaml';
5
- import { join, isAbsolute, resolve, extname, dirname } from 'path';
6
- import { fileURLToPath } from 'url';
7
- import '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
8
  import { homedir } from 'os';
14
9
 
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 applyFulmenAjvFormats(ajv, options = {}) {
20
- const mode = options.mode ?? "fast";
21
- const formats = options.formats ?? DEFAULT_FORMATS;
22
- addFormats(ajv, { mode, formats });
23
- return ajv;
24
- }
25
- var DEFAULT_FORMATS;
26
- var init_ajv_formats = __esm({
27
- "src/schema/ajv-formats.ts"() {
28
- DEFAULT_FORMATS = [
29
- "date-time",
30
- "email",
31
- "hostname",
32
- "ipv4",
33
- "ipv6",
34
- "uri",
35
- "uri-reference",
36
- "uuid"
37
- ];
38
- }
39
- });
40
-
41
- // src/schema/errors.ts
42
- var SchemaValidationError;
43
- var init_errors = __esm({
44
- "src/schema/errors.ts"() {
45
- SchemaValidationError = class _SchemaValidationError extends Error {
46
- constructor(message, schemaId, diagnostics = [], source, cause) {
47
- super(message);
48
- this.schemaId = schemaId;
49
- this.diagnostics = diagnostics;
50
- this.source = source;
51
- this.cause = cause;
52
- this.name = "SchemaValidationError";
53
- if (Error.captureStackTrace) {
54
- Error.captureStackTrace(this, _SchemaValidationError);
55
- }
56
- }
57
- /**
58
- * Create error for schema not found
59
- */
60
- static schemaNotFound(schemaId) {
61
- return new _SchemaValidationError(`Schema not found: ${schemaId}`, schemaId);
62
- }
63
- /**
64
- * Create error for invalid schema input
65
- */
66
- static invalidSchemaInput(source, details) {
67
- return new _SchemaValidationError(`Invalid schema input: ${details}`, void 0, [], source);
68
- }
69
- /**
70
- * Create error for validation failure
71
- */
72
- static validationFailed(schemaId, diagnostics, source) {
73
- const errorCount = diagnostics.filter((d) => d.severity === "ERROR").length;
74
- const warningCount = diagnostics.filter((d) => d.severity === "WARN").length;
75
- const message = `Schema validation failed: ${errorCount} error(s), ${warningCount} warning(s)`;
76
- return new _SchemaValidationError(message, schemaId, diagnostics, source);
77
- }
78
- /**
79
- * Create error for goneat binary not found
80
- */
81
- static goneatNotFound(goneatPath) {
82
- const pathInfo = goneatPath ? ` at ${goneatPath}` : "";
83
- return new _SchemaValidationError(
84
- `Goneat binary not found${pathInfo}. Falling back to AJV validation.`
85
- );
86
- }
87
- /**
88
- * Create error for goneat execution failure
89
- */
90
- static goneatExecutionFailed(error) {
91
- return new _SchemaValidationError(
92
- "Goneat execution failed. Falling back to AJV validation.",
93
- void 0,
94
- [],
95
- void 0,
96
- error
97
- );
98
- }
99
- /**
100
- * Create error for empty schema input
101
- */
102
- static emptySchemaInput(source) {
103
- return new _SchemaValidationError("Schema content is empty", void 0, [], source);
104
- }
105
- /**
106
- * Create error for parse failure
107
- */
108
- static parseFailed(source, error) {
109
- return new _SchemaValidationError(
110
- `Failed to parse schema: ${error.message}`,
111
- void 0,
112
- [],
113
- source,
114
- error
115
- );
116
- }
117
- /**
118
- * Create error for encoding failure
119
- */
120
- static encodingFailed(source, error) {
121
- return new _SchemaValidationError(
122
- `Failed to encode schema: ${error.message}`,
123
- void 0,
124
- [],
125
- source,
126
- error
127
- );
128
- }
129
- /**
130
- * Create error for registry operation failure
131
- */
132
- static registryError(operation, details) {
133
- return new _SchemaValidationError(`Schema registry ${operation} failed: ${details}`);
134
- }
135
- /**
136
- * Format error for display
137
- */
138
- format() {
139
- let output = this.message;
140
- if (this.schemaId) {
141
- output += `
142
- Schema ID: ${this.schemaId}`;
143
- }
144
- if (this.diagnostics.length > 0) {
145
- output += "\n\nValidation Issues:";
146
- this.diagnostics.forEach((diag, index) => {
147
- output += `
148
- ${index + 1}. [${diag.severity}] ${diag.message}`;
149
- if (diag.pointer) {
150
- output += ` at ${diag.pointer}`;
151
- }
152
- if (diag.keyword) {
153
- output += ` (keyword: ${diag.keyword})`;
154
- }
155
- if (diag.source) {
156
- output += ` [${diag.source}]`;
157
- }
158
- });
159
- }
160
- if (this.source) {
161
- output += `
162
-
163
- Source: ${this.source.type}`;
164
- if (this.source.id) {
165
- output += ` (${this.source.id})`;
166
- }
167
- }
168
- return output;
169
- }
170
- /**
171
- * Convert to JSON representation
172
- */
173
- toJSON() {
174
- return {
175
- name: this.name,
176
- message: this.message,
177
- schemaId: this.schemaId,
178
- diagnostics: this.diagnostics,
179
- source: this.source,
180
- cause: this.cause?.message
181
- };
182
- }
183
- };
184
- }
185
- });
186
-
187
- // src/schema/utils.ts
188
- function createDiagnostic(pointer, message, keyword, severity = "ERROR", source = "ajv", data) {
189
- return {
190
- pointer,
191
- message,
192
- keyword,
193
- severity,
194
- source,
195
- data
196
- };
197
- }
198
- var init_utils = __esm({
199
- "src/schema/utils.ts"() {
200
- init_errors();
201
- }
202
- });
203
- var init_goneat_bridge = __esm({
204
- "src/schema/goneat-bridge.ts"() {
205
- init_utils();
206
- }
207
- });
208
- var init_normalizer = __esm({
209
- "src/schema/normalizer.ts"() {
210
- init_errors();
211
- }
212
- });
213
- var init_registry = __esm({
214
- "src/schema/registry.ts"() {
215
- init_errors();
216
- }
217
- });
218
-
219
- // src/telemetry/counter.ts
220
- var Counter;
221
- var init_counter = __esm({
222
- "src/telemetry/counter.ts"() {
223
- Counter = class {
224
- constructor(name) {
225
- this.name = name;
226
- }
227
- value = 0;
228
- labeledValues = /* @__PURE__ */ new Map();
229
- /**
230
- * Increment counter by delta (default: 1)
231
- *
232
- * @param delta - Amount to increment (must be non-negative)
233
- * @param labels - Optional label dimensions for this observation
234
- * @throws {Error} If delta is negative
235
- *
236
- * @example
237
- * ```typescript
238
- * counter.inc(); // Increment unlabeled by 1
239
- * counter.inc(5); // Increment unlabeled by 5
240
- * counter.inc(1, { status: '200' }); // Increment labeled instance
241
- * counter.inc(1, { result: 'success' }); // Different label set
242
- * ```
243
- */
244
- inc(delta = 1, labels) {
245
- if (delta < 0) {
246
- throw new Error(`Counter delta must be non-negative, got: ${delta}`);
247
- }
248
- if (labels && Object.keys(labels).length > 0) {
249
- const labelKey = this.serializeLabels(labels);
250
- const current = this.labeledValues.get(labelKey) || 0;
251
- this.labeledValues.set(labelKey, current + delta);
252
- } else {
253
- this.value += delta;
254
- }
255
- }
256
- /**
257
- * Get current counter value (unlabeled)
258
- */
259
- getValue() {
260
- return this.value;
261
- }
262
- /**
263
- * Get all labeled values
264
- * @returns Map of serialized label keys to values
265
- */
266
- getLabeledValues() {
267
- return new Map(this.labeledValues);
268
- }
269
- /**
270
- * Get value for specific label combination
271
- */
272
- getValueForLabels(labels) {
273
- const labelKey = this.serializeLabels(labels);
274
- return this.labeledValues.get(labelKey) || 0;
275
- }
276
- /**
277
- * Reset counter to zero (all label combinations)
278
- */
279
- reset() {
280
- this.value = 0;
281
- this.labeledValues.clear();
282
- }
283
- /**
284
- * Serialize labels to deterministic string key
285
- * Format: key1=value1,key2=value2 (sorted by key)
286
- */
287
- serializeLabels(labels) {
288
- return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
289
- }
290
- };
291
- }
292
- });
293
-
294
- // src/telemetry/gauge.ts
295
- var Gauge;
296
- var init_gauge = __esm({
297
- "src/telemetry/gauge.ts"() {
298
- Gauge = class {
299
- constructor(name) {
300
- this.name = name;
301
- }
302
- value = 0;
303
- labeledValues = /* @__PURE__ */ new Map();
304
- /**
305
- * Set gauge to specific value
306
- *
307
- * @param value - New gauge value (can be any number, including negative)
308
- * @param labels - Optional label dimensions for this observation
309
- *
310
- * @example
311
- * ```typescript
312
- * gauge.set(42); // Set unlabeled to 42
313
- * gauge.set(-10); // Negative values allowed
314
- * gauge.set(1, { phase: 'collect' }); // Set labeled instance
315
- * ```
316
- */
317
- set(value, labels) {
318
- if (labels && Object.keys(labels).length > 0) {
319
- const labelKey = this.serializeLabels(labels);
320
- this.labeledValues.set(labelKey, value);
321
- } else {
322
- this.value = value;
323
- }
324
- }
325
- /**
326
- * Increment gauge by delta (default: 1)
327
- *
328
- * @param delta - Amount to increment (can be negative)
329
- * @param labels - Optional label dimensions for this observation
330
- */
331
- inc(delta = 1, labels) {
332
- if (labels && Object.keys(labels).length > 0) {
333
- const labelKey = this.serializeLabels(labels);
334
- const current = this.labeledValues.get(labelKey) || 0;
335
- this.labeledValues.set(labelKey, current + delta);
336
- } else {
337
- this.value += delta;
338
- }
339
- }
340
- /**
341
- * Decrement gauge by delta (default: 1)
342
- *
343
- * @param delta - Amount to decrement (can be negative)
344
- * @param labels - Optional label dimensions for this observation
345
- */
346
- dec(delta = 1, labels) {
347
- if (labels && Object.keys(labels).length > 0) {
348
- const labelKey = this.serializeLabels(labels);
349
- const current = this.labeledValues.get(labelKey) || 0;
350
- this.labeledValues.set(labelKey, current - delta);
351
- } else {
352
- this.value -= delta;
353
- }
354
- }
355
- /**
356
- * Get current gauge value (unlabeled)
357
- */
358
- getValue() {
359
- return this.value;
360
- }
361
- /**
362
- * Get all labeled values
363
- * @returns Map of serialized label keys to values
364
- */
365
- getLabeledValues() {
366
- return new Map(this.labeledValues);
367
- }
368
- /**
369
- * Get value for specific label combination
370
- */
371
- getValueForLabels(labels) {
372
- const labelKey = this.serializeLabels(labels);
373
- return this.labeledValues.get(labelKey) || 0;
374
- }
375
- /**
376
- * Reset gauge to zero (all label combinations)
377
- */
378
- reset() {
379
- this.value = 0;
380
- this.labeledValues.clear();
381
- }
382
- /**
383
- * Serialize labels to deterministic string key
384
- * Format: key1=value1,key2=value2 (sorted by key)
385
- */
386
- serializeLabels(labels) {
387
- return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
388
- }
389
- };
390
- }
391
- });
392
- async function getDefaultUnit(name) {
393
- return TaxonomyLoader.getInstance().getDefaultUnit(name);
394
- }
395
- var DEFAULT_MS_BUCKETS, TaxonomyLoader;
396
- var init_taxonomy = __esm({
397
- "src/telemetry/taxonomy.ts"() {
398
- DEFAULT_MS_BUCKETS = [1, 5, 10, 50, 100, 500, 1e3, 5e3, 1e4];
399
- TaxonomyLoader = class _TaxonomyLoader {
400
- static instance;
401
- taxonomy = null;
402
- loadPromise = null;
403
- loadError = null;
404
- constructor() {
405
- }
406
- /**
407
- * Get singleton instance
408
- */
409
- static getInstance() {
410
- if (!_TaxonomyLoader.instance) {
411
- _TaxonomyLoader.instance = new _TaxonomyLoader();
412
- }
413
- return _TaxonomyLoader.instance;
414
- }
415
- /**
416
- * Load taxonomy from YAML file
417
- */
418
- async load() {
419
- if (this.taxonomy !== null) {
420
- return this.taxonomy;
421
- }
422
- if (this.loadError !== null) {
423
- throw this.loadError;
424
- }
425
- if (this.loadPromise) {
426
- return this.loadPromise;
427
- }
428
- this.loadPromise = (async () => {
429
- try {
430
- const taxonomyPath = join(
431
- __dirname,
432
- "..",
433
- "..",
434
- "config",
435
- "crucible-ts",
436
- "taxonomy",
437
- "metrics.yaml"
438
- );
439
- const content = await readFile(taxonomyPath, "utf-8");
440
- this.taxonomy = parse(content);
441
- return this.taxonomy;
442
- } catch (err) {
443
- this.loadError = err instanceof Error ? err : new Error(String(err));
444
- throw new Error(`Failed to load metrics taxonomy: ${this.loadError.message}`);
445
- }
446
- })();
447
- return this.loadPromise;
448
- }
449
- /**
450
- * Get taxonomy (async)
451
- */
452
- async getTaxonomy() {
453
- return this.load();
454
- }
455
- /**
456
- * Get metric definition by name
457
- */
458
- async getMetric(name) {
459
- const taxonomy = await this.load();
460
- return taxonomy.metrics.find((m) => m.name === name);
461
- }
462
- /**
463
- * Get default unit for metric
464
- */
465
- async getDefaultUnit(name) {
466
- const metric = await this.getMetric(name);
467
- return metric?.unit;
468
- }
469
- /**
470
- * Get default histogram buckets for metric
471
- * Returns ADR-0007 buckets for _ms metrics, undefined for others
472
- */
473
- async getDefaultBuckets(name) {
474
- if (name.endsWith("_ms")) {
475
- const taxonomy = await this.load();
476
- return taxonomy.defaults.histogram_buckets.ms_metrics;
477
- }
478
- return void 0;
479
- }
480
- /**
481
- * Check if metric name is valid (exists in taxonomy)
482
- */
483
- async isValidMetricName(name) {
484
- try {
485
- const taxonomy = await this.load();
486
- return taxonomy.metrics.some((m) => m.name === name);
487
- } catch {
488
- return false;
489
- }
490
- }
491
- /**
492
- * Reset loader state (for testing)
493
- * @internal
494
- */
495
- static _reset() {
496
- _TaxonomyLoader.instance = new _TaxonomyLoader();
497
- }
498
- };
499
- }
500
- });
501
-
502
- // src/telemetry/histogram.ts
503
- var Histogram;
504
- var init_histogram = __esm({
505
- "src/telemetry/histogram.ts"() {
506
- init_taxonomy();
507
- Histogram = class {
508
- constructor(name, options) {
509
- this.name = name;
510
- if (options?.buckets) {
511
- this.buckets = [...options.buckets].sort((a, b) => a - b);
512
- } else if (name.endsWith("_ms") || name.endsWith("_seconds")) {
513
- this.buckets = [...DEFAULT_MS_BUCKETS];
514
- } else {
515
- this.buckets = [];
516
- }
517
- for (const bucket of this.buckets) {
518
- this.bucketCounts.set(bucket, 0);
519
- }
520
- }
521
- count = 0;
522
- sum = 0;
523
- bucketCounts = /* @__PURE__ */ new Map();
524
- labeledStates = /* @__PURE__ */ new Map();
525
- buckets;
526
- /**
527
- * Record an observation
528
- *
529
- * @param value - Value to observe (typically a duration in ms or seconds)
530
- * @param labels - Optional label dimensions for this observation
531
- *
532
- * @example
533
- * ```typescript
534
- * const start = performance.now();
535
- * // ... operation ...
536
- * histogram.observe(performance.now() - start);
537
- * histogram.observe(duration, { phase: 'collect', result: 'success' });
538
- * ```
539
- */
540
- observe(value, labels) {
541
- if (labels && Object.keys(labels).length > 0) {
542
- const labelKey = this.serializeLabels(labels);
543
- let state = this.labeledStates.get(labelKey);
544
- if (!state) {
545
- state = {
546
- count: 0,
547
- sum: 0,
548
- bucketCounts: /* @__PURE__ */ new Map()
549
- };
550
- for (const bucket of this.buckets) {
551
- state.bucketCounts.set(bucket, 0);
552
- }
553
- this.labeledStates.set(labelKey, state);
554
- }
555
- state.count++;
556
- state.sum += value;
557
- for (const bucket of this.buckets) {
558
- if (value <= bucket) {
559
- state.bucketCounts.set(bucket, (state.bucketCounts.get(bucket) || 0) + 1);
560
- }
561
- }
562
- } else {
563
- this.count++;
564
- this.sum += value;
565
- for (const bucket of this.buckets) {
566
- if (value <= bucket) {
567
- this.bucketCounts.set(bucket, (this.bucketCounts.get(bucket) || 0) + 1);
568
- }
569
- }
570
- }
571
- }
572
- /**
573
- * Get histogram summary
574
- *
575
- * Returns OTLP-compatible histogram summary with cumulative bucket counts.
576
- */
577
- getSummary() {
578
- const buckets = this.buckets.map((le) => ({
579
- le,
580
- count: this.bucketCounts.get(le) || 0
581
- }));
582
- return {
583
- count: this.count,
584
- sum: this.sum,
585
- buckets
586
- };
587
- }
588
- /**
589
- * Get current observation count
590
- */
591
- getCount() {
592
- return this.count;
593
- }
594
- /**
595
- * Get sum of all observed values
596
- */
597
- getSum() {
598
- return this.sum;
599
- }
600
- /**
601
- * Get average of observed values
602
- */
603
- getAverage() {
604
- return this.count > 0 ? this.sum / this.count : 0;
605
- }
606
- /**
607
- * Get all labeled summaries
608
- * @returns Map of serialized label keys to histogram summaries
609
- */
610
- getLabeledSummaries() {
611
- const summaries = /* @__PURE__ */ new Map();
612
- for (const [labelKey, state] of this.labeledStates) {
613
- const buckets = this.buckets.map((le) => ({
614
- le,
615
- count: state.bucketCounts.get(le) || 0
616
- }));
617
- summaries.set(labelKey, {
618
- count: state.count,
619
- sum: state.sum,
620
- buckets
621
- });
622
- }
623
- return summaries;
624
- }
625
- /**
626
- * Get summary for specific label combination
627
- */
628
- getSummaryForLabels(labels) {
629
- const labelKey = this.serializeLabels(labels);
630
- const state = this.labeledStates.get(labelKey);
631
- if (!state) {
632
- return null;
633
- }
634
- const buckets = this.buckets.map((le) => ({
635
- le,
636
- count: state.bucketCounts.get(le) || 0
637
- }));
638
- return {
639
- count: state.count,
640
- sum: state.sum,
641
- buckets
642
- };
643
- }
644
- /**
645
- * Reset histogram to initial state (all label combinations)
646
- */
647
- reset() {
648
- this.count = 0;
649
- this.sum = 0;
650
- for (const bucket of this.buckets) {
651
- this.bucketCounts.set(bucket, 0);
652
- }
653
- this.labeledStates.clear();
654
- }
655
- /**
656
- * Serialize labels to deterministic string key
657
- * Format: key1=value1,key2=value2 (sorted by key)
658
- */
659
- serializeLabels(labels) {
660
- return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
661
- }
662
- };
663
- }
664
- });
665
-
666
- // src/telemetry/registry.ts
667
- var MetricsRegistry;
668
- var init_registry2 = __esm({
669
- "src/telemetry/registry.ts"() {
670
- init_counter();
671
- init_gauge();
672
- init_histogram();
673
- init_taxonomy();
674
- MetricsRegistry = class {
675
- counters = /* @__PURE__ */ new Map();
676
- gauges = /* @__PURE__ */ new Map();
677
- histograms = /* @__PURE__ */ new Map();
678
- /**
679
- * Get or create a counter
680
- *
681
- * @param name - Metric name from taxonomy
682
- * @returns Counter instance
683
- *
684
- * @example
685
- * ```typescript
686
- * const counter = registry.counter('schema_validations');
687
- * counter.inc();
688
- * ```
689
- */
690
- counter(name) {
691
- let counter = this.counters.get(name);
692
- if (!counter) {
693
- counter = new Counter(name);
694
- this.counters.set(name, counter);
695
- }
696
- return counter;
697
- }
698
- /**
699
- * Get or create a gauge
700
- *
701
- * @param name - Metric name from taxonomy
702
- * @returns Gauge instance
703
- *
704
- * @example
705
- * ```typescript
706
- * const gauge = registry.gauge('foundry_lookup_count');
707
- * gauge.set(42);
708
- * ```
709
- */
710
- gauge(name) {
711
- let gauge = this.gauges.get(name);
712
- if (!gauge) {
713
- gauge = new Gauge(name);
714
- this.gauges.set(name, gauge);
715
- }
716
- return gauge;
717
- }
718
- /**
719
- * Get or create a histogram
720
- *
721
- * @param name - Metric name from taxonomy
722
- * @param options - Optional histogram options
723
- * @returns Histogram instance
724
- *
725
- * @example
726
- * ```typescript
727
- * // Auto-applies ADR-0007 buckets for _ms metrics
728
- * const histogram = registry.histogram('config_load_ms');
729
- * histogram.observe(42.5);
730
- *
731
- * // Custom buckets
732
- * const custom = registry.histogram('custom_metric', {
733
- * buckets: [10, 50, 100, 500, 1000]
734
- * });
735
- * ```
736
- */
737
- histogram(name, options) {
738
- let histogram = this.histograms.get(name);
739
- if (!histogram) {
740
- histogram = new Histogram(name, options);
741
- this.histograms.set(name, histogram);
742
- }
743
- return histogram;
744
- }
745
- /**
746
- * Export all metrics as events
747
- *
748
- * Returns array of schema-compliant MetricsEvent objects.
749
- * Does not clear metrics (use flush() to clear after export).
750
- *
751
- * @returns Promise resolving to array of metrics events
752
- *
753
- * @example
754
- * ```typescript
755
- * const events = await registry.export();
756
- * console.log(JSON.stringify(events, null, 2));
757
- * ```
758
- */
759
- async export() {
760
- const events = [];
761
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
762
- for (const [name, counter] of this.counters) {
763
- const unit = await getDefaultUnit(name);
764
- events.push({
765
- timestamp,
766
- name,
767
- value: counter.getValue(),
768
- unit
769
- });
770
- for (const [labelKey, value] of counter.getLabeledValues()) {
771
- if (value > 0) {
772
- const tags = this.deserializeLabels(labelKey);
773
- events.push({
774
- timestamp,
775
- name,
776
- value,
777
- tags,
778
- unit
779
- });
780
- }
781
- }
782
- }
783
- for (const [name, gauge] of this.gauges) {
784
- const unit = await getDefaultUnit(name);
785
- events.push({
786
- timestamp,
787
- name,
788
- value: gauge.getValue(),
789
- unit
790
- });
791
- for (const [labelKey, value] of gauge.getLabeledValues()) {
792
- const tags = this.deserializeLabels(labelKey);
793
- events.push({
794
- timestamp,
795
- name,
796
- value,
797
- tags,
798
- unit
799
- });
800
- }
801
- }
802
- for (const [name, histogram] of this.histograms) {
803
- const unit = await getDefaultUnit(name);
804
- events.push({
805
- timestamp,
806
- name,
807
- value: histogram.getSummary(),
808
- unit
809
- });
810
- for (const [labelKey, summary] of histogram.getLabeledSummaries()) {
811
- if (summary.count > 0) {
812
- const tags = this.deserializeLabels(labelKey);
813
- events.push({
814
- timestamp,
815
- name,
816
- value: summary,
817
- tags,
818
- unit
819
- });
820
- }
821
- }
822
- }
823
- return events;
824
- }
825
- /**
826
- * Deserialize label key back to tags object
827
- * Format: key1=value1,key2=value2 → {key1: "value1", key2: "value2"}
828
- */
829
- deserializeLabels(labelKey) {
830
- if (!labelKey) {
831
- return {};
832
- }
833
- const tags = {};
834
- for (const pair of labelKey.split(",")) {
835
- const [key, value] = pair.split("=");
836
- if (key && value) {
837
- tags[key] = value;
838
- }
839
- }
840
- return tags;
841
- }
842
- /**
843
- * Export and clear all metrics
844
- *
845
- * Exports metrics as events, optionally emits them via logger,
846
- * then resets all metrics to zero.
847
- *
848
- * @param options - Flush options
849
- * @returns Promise resolving to array of exported events
850
- *
851
- * @example
852
- * ```typescript
853
- * // Export and clear
854
- * const events = await registry.flush();
855
- *
856
- * // Export, emit to logger, and clear
857
- * const events = await registry.flush({
858
- * emit: (events) => console.log(JSON.stringify(events))
859
- * });
860
- * ```
861
- */
862
- async flush(options) {
863
- const events = await this.export();
864
- try {
865
- if (options?.emit) {
866
- options.emit(events);
867
- }
868
- } finally {
869
- this.clear();
870
- }
871
- return events;
872
- }
873
- /**
874
- * Clear all metrics (reset to zero)
875
- *
876
- * Resets all counters, gauges, and histograms to their initial state.
877
- */
878
- clear() {
879
- for (const counter of this.counters.values()) {
880
- counter.reset();
881
- }
882
- for (const gauge of this.gauges.values()) {
883
- gauge.reset();
884
- }
885
- for (const histogram of this.histograms.values()) {
886
- histogram.reset();
887
- }
888
- }
889
- /**
890
- * Get all registered metric names
891
- *
892
- * Returns array of all metric names that have been accessed
893
- * (counters, gauges, or histograms).
894
- */
895
- getMetricNames() {
896
- const names = /* @__PURE__ */ new Set();
897
- for (const name of this.counters.keys()) {
898
- names.add(name);
899
- }
900
- for (const name of this.gauges.keys()) {
901
- names.add(name);
902
- }
903
- for (const name of this.histograms.keys()) {
904
- names.add(name);
905
- }
906
- return Array.from(names);
907
- }
908
- /**
909
- * Get total count of registered metrics
910
- */
911
- getMetricCount() {
912
- return this.counters.size + this.gauges.size + this.histograms.size;
913
- }
914
- };
915
- }
916
- });
917
-
918
- // src/telemetry/types.ts
919
- var init_types = __esm({
920
- "src/telemetry/types.ts"() {
921
- }
922
- });
923
-
924
- // src/telemetry/validators.ts
925
- var init_validators = __esm({
926
- "src/telemetry/validators.ts"() {
927
- init_schema();
928
- }
929
- });
930
-
931
- // src/telemetry/index.ts
932
- var metrics;
933
- var init_telemetry = __esm({
934
- "src/telemetry/index.ts"() {
935
- init_registry2();
936
- init_registry2();
937
- init_counter();
938
- init_gauge();
939
- init_histogram();
940
- init_taxonomy();
941
- init_types();
942
- init_validators();
943
- metrics = new MetricsRegistry();
944
- }
945
- });
946
- async function loadMetaSchema(draft) {
947
- const __filename = fileURLToPath(import.meta.url);
948
- const __dirname2 = dirname(__filename);
949
- const metaSchemaPath = join(
950
- __dirname2,
951
- "..",
952
- "..",
953
- "schemas",
954
- "crucible-ts",
955
- "meta",
956
- draft,
957
- "schema.json"
958
- );
959
- const content = await readFile(metaSchemaPath, "utf-8");
960
- return JSON.parse(content);
961
- }
962
- async function loadVocabularySchemas(draft) {
963
- if (draft !== "draft-2019-09" && draft !== "draft-2020-12") {
964
- return [];
965
- }
966
- const __filename = fileURLToPath(import.meta.url);
967
- const __dirname2 = dirname(__filename);
968
- const vocabDir = join(__dirname2, "..", "..", "schemas", "crucible-ts", "meta", draft, "meta");
969
- const vocabFiles = draft === "draft-2020-12" ? [
970
- "core.json",
971
- "applicator.json",
972
- "unevaluated.json",
973
- "validation.json",
974
- "meta-data.json",
975
- "format-annotation.json",
976
- "content.json"
977
- ] : [
978
- "core.json",
979
- "applicator.json",
980
- "validation.json",
981
- "meta-data.json",
982
- "format.json",
983
- "content.json"
984
- ];
985
- const schemas = [];
986
- for (const file of vocabFiles) {
987
- try {
988
- const content = await readFile(join(vocabDir, file), "utf-8");
989
- schemas.push(JSON.parse(content));
990
- } catch {
991
- }
992
- }
993
- return schemas;
994
- }
995
- async function loadReferencedSchema(uri) {
996
- const __filename = fileURLToPath(import.meta.url);
997
- const __dirname2 = dirname(__filename);
998
- const repoRoot = join(__dirname2, "..", "..");
999
- let resolvedPath;
1000
- if (uri.startsWith("https://schemas.fulmenhq.dev/")) {
1001
- let relativePath = uri.replace("https://schemas.fulmenhq.dev/", "");
1002
- if (relativePath.startsWith("crucible/")) {
1003
- relativePath = relativePath.slice("crucible/".length);
1004
- }
1005
- if (relativePath.startsWith("config/taxonomy/")) {
1006
- resolvedPath = join(
1007
- repoRoot,
1008
- "config",
1009
- "crucible-ts",
1010
- "taxonomy",
1011
- relativePath.split("/").pop() || ""
1012
- );
1013
- } else {
1014
- resolvedPath = join(repoRoot, "schemas", "crucible-ts", relativePath);
1015
- }
1016
- } else if (uri.startsWith("../../") || uri.startsWith("../")) {
1017
- const schemaBase = join(
1018
- repoRoot,
1019
- "schemas",
1020
- "crucible-ts",
1021
- "observability",
1022
- "metrics",
1023
- "v1.0.0"
1024
- );
1025
- resolvedPath = join(schemaBase, uri);
1026
- } else if (uri.startsWith("file://")) {
1027
- resolvedPath = fileURLToPath(uri);
1028
- } else {
1029
- throw new Error(`Cannot load remote schema: ${uri}`);
1030
- }
1031
- const content = await readFile(resolvedPath, "utf-8");
1032
- const ext = resolvedPath.split(".").pop()?.toLowerCase();
1033
- if (ext === "yaml" || ext === "yml") {
1034
- return parse(content);
1035
- }
1036
- return JSON.parse(content);
1037
- }
1038
- function detectDialect(schema) {
1039
- if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1040
- const maybeSchema = schema;
1041
- const declared = maybeSchema.$schema;
1042
- if (typeof declared === "string") {
1043
- if (declared.includes("draft-04")) return "draft-04";
1044
- if (declared.includes("draft-06")) return "draft-06";
1045
- if (declared.includes("draft-07")) return "draft-07";
1046
- if (declared.includes("draft/2019-09")) return "draft-2019-09";
1047
- if (declared.includes("draft/2020-12")) return "draft-2020-12";
1048
- }
1049
- }
1050
- return "draft-2020-12";
1051
- }
1052
- function createAjv(dialect) {
1053
- const AjvCtor = dialect === "draft-2020-12" ? Ajv2020 : dialect === "draft-2019-09" ? Ajv2019 : dialect === "draft-04" ? AjvDraft04 : Ajv;
1054
- const ajv = new AjvCtor({
1055
- strict: false,
1056
- allErrors: true,
1057
- verbose: true,
1058
- // Allow schemas with $id to be added without replacing existing ones
1059
- addUsedSchema: false,
1060
- // draft-04 uses "id"; later drafts use "$id"
1061
- schemaId: dialect === "draft-04" ? "id" : "$id",
1062
- // Enable async schema loading for YAML references
1063
- loadSchema: loadReferencedSchema
1064
- });
1065
- applyFulmenAjvFormats(ajv);
1066
- return ajv;
1067
- }
1068
- async function getAjv(dialect) {
1069
- const existing = ajvInstances.get(dialect);
1070
- if (existing) {
1071
- const ready = metaschemaReady.get(dialect);
1072
- if (ready) await ready;
1073
- return existing;
1074
- }
1075
- const ajv = createAjv(dialect);
1076
- ajvInstances.set(dialect, ajv);
1077
- const readyPromise = Promise.all([loadVocabularySchemas(dialect), loadMetaSchema(dialect)]).then(([vocabSchemas, metaSchema]) => {
1078
- for (const vocabSchema of vocabSchemas) {
1079
- try {
1080
- ajv.addMetaSchema(vocabSchema);
1081
- } catch {
1082
- }
1083
- }
1084
- try {
1085
- ajv.addMetaSchema(metaSchema);
1086
- } catch {
1087
- }
1088
- }).catch((error) => {
1089
- throw new Error(`Failed to load metaschemas (${dialect}): ${error}`);
1090
- });
1091
- metaschemaReady.set(dialect, readyPromise);
1092
- await readyPromise;
1093
- return ajv;
1094
- }
1095
- async function compileSchema(schema, options = {}) {
1096
- const baseKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1097
- let parsedSchema;
1098
- if (typeof schema === "string") {
1099
- try {
1100
- parsedSchema = JSON.parse(schema);
1101
- } catch {
1102
- parsedSchema = parse(schema);
1103
- }
1104
- } else if (Buffer.isBuffer(schema)) {
1105
- const content = schema.toString("utf-8");
1106
- try {
1107
- parsedSchema = JSON.parse(content);
1108
- } catch {
1109
- parsedSchema = parse(content);
1110
- }
1111
- } else {
1112
- parsedSchema = schema;
1113
- }
1114
- const dialect = detectDialect(parsedSchema);
1115
- const ajv = await getAjv(dialect);
1116
- const cacheKey = `${dialect}:${baseKey}`;
1117
- const cached = schemaCache.get(cacheKey);
1118
- if (cached !== void 0) {
1119
- return cached;
1120
- }
1121
- try {
1122
- if (options.aliases && options.aliases.length > 0) {
1123
- for (const alias of options.aliases) {
1124
- if (alias && ajv.getSchema(alias) === void 0) {
1125
- try {
1126
- if (typeof parsedSchema === "object" && parsedSchema !== null) {
1127
- ajv.addSchema(parsedSchema, alias);
1128
- }
1129
- } catch {
1130
- }
1131
- }
1132
- }
1133
- }
1134
- const validator = typeof parsedSchema === "boolean" ? ajv.compile(parsedSchema) : await ajv.compileAsync(parsedSchema);
1135
- schemaCache.set(cacheKey, validator);
1136
- return validator;
1137
- } catch (error) {
1138
- throw SchemaValidationError.parseFailed(
1139
- {
1140
- type: "string",
1141
- content: typeof schema === "string" ? schema : JSON.stringify(schema)
1142
- },
1143
- error
1144
- );
1145
- }
1146
- }
1147
- function validateData(data, validator) {
1148
- const valid = validator(data);
1149
- const result = {
1150
- valid,
1151
- diagnostics: [],
1152
- source: "ajv"
1153
- };
1154
- if (!valid && validator.errors) {
1155
- const errors = validator.errors;
1156
- if (Array.isArray(errors)) {
1157
- result.diagnostics = errors.map(
1158
- (error) => createDiagnostic(
1159
- error.instancePath || "",
1160
- error.message || "Validation failed",
1161
- error.keyword || "unknown",
1162
- "ERROR",
1163
- "ajv"
1164
- )
1165
- );
1166
- }
1167
- metrics.counter("schema_validation_errors").inc();
1168
- } else {
1169
- metrics.counter("schema_validations").inc();
1170
- }
1171
- return result;
1172
- }
1173
- var ajvInstances, metaschemaReady, schemaCache;
1174
- var init_validator = __esm({
1175
- "src/schema/validator.ts"() {
1176
- init_telemetry();
1177
- init_ajv_formats();
1178
- init_errors();
1179
- init_registry();
1180
- init_utils();
1181
- ajvInstances = /* @__PURE__ */ new Map();
1182
- metaschemaReady = /* @__PURE__ */ new Map();
1183
- schemaCache = /* @__PURE__ */ new Map();
1184
- }
1185
- });
1186
- var init_cli = __esm({
1187
- "src/schema/cli.ts"() {
1188
- init_goneat_bridge();
1189
- init_normalizer();
1190
- init_registry();
1191
- init_utils();
1192
- init_validator();
1193
- }
1194
- });
1195
- var init_export = __esm({
1196
- "src/schema/export.ts"() {
1197
- init_errors();
1198
- init_registry();
1199
- init_validator();
1200
- }
1201
- });
1202
-
1203
- // src/schema/index.ts
1204
- var init_schema = __esm({
1205
- "src/schema/index.ts"() {
1206
- init_ajv_formats();
1207
- init_cli();
1208
- init_errors();
1209
- init_export();
1210
- init_goneat_bridge();
1211
- init_normalizer();
1212
- init_registry();
1213
- init_utils();
1214
- init_validator();
1215
- }
1216
- });
1217
-
1218
10
  // src/config/env-alias.ts
1219
11
  function resolveEnvAliases(env, aliasToCanonical) {
1220
12
  const out = { ...env };
@@ -1301,12 +93,6 @@ var ConfigValidationError = class _ConfigValidationError extends Error {
1301
93
  }
1302
94
  }
1303
95
  };
1304
-
1305
- // src/config/loader.ts
1306
- init_schema();
1307
-
1308
- // src/config/paths.ts
1309
- init_telemetry();
1310
96
  function getPlatformInfo() {
1311
97
  const osPlatform = process.platform;
1312
98
  const homeDir = homedir();
@@ -1631,9 +417,16 @@ async function parseConfigFile(path) {
1631
417
  throw new Error(`Unsupported config file extension: ${ext}`);
1632
418
  }
1633
419
  async function loadConfig(options) {
1634
- const { identity, defaultsPath, userConfigName } = options;
420
+ const { identity, defaultsPath, defaults, userConfigName } = options;
1635
421
  const activeLayers = ["defaults"];
1636
- let mergedConfig = await parseConfigFile(defaultsPath);
422
+ let mergedConfig;
423
+ if (defaults !== void 0) {
424
+ mergedConfig = structuredClone(defaults);
425
+ } else if (defaultsPath) {
426
+ mergedConfig = await parseConfigFile(defaultsPath);
427
+ } else {
428
+ throw new Error("loadConfig requires either `defaults` (inline) or `defaultsPath`");
429
+ }
1637
430
  const configName = userConfigName || identity.app;
1638
431
  const extensions = [".yaml", ".yml", ".json"];
1639
432
  let userConfigPath = null;
@@ -1659,9 +452,10 @@ async function loadConfig(options) {
1659
452
  mergedConfig = deepMerge(mergedConfig, envConfig);
1660
453
  activeLayers.push("env");
1661
454
  }
1662
- if (options.schemaPath) {
455
+ const schemaProvided = options.schema !== void 0 || options.schemaPath !== void 0;
456
+ if (schemaProvided) {
1663
457
  try {
1664
- const schemaContent = await readFile(options.schemaPath, "utf-8");
458
+ const schemaContent = options.schema !== void 0 ? options.schema : await readFile(options.schemaPath, "utf-8");
1665
459
  const validator = await compileSchema(schemaContent);
1666
460
  const result = validateData(mergedConfig, validator);
1667
461
  if (!result.valid) {
@@ -1681,15 +475,18 @@ async function loadConfig(options) {
1681
475
  return {
1682
476
  config: mergedConfig,
1683
477
  metadata: {
1684
- defaultsPath,
478
+ defaultsPath: defaults !== void 0 ? "" : defaultsPath,
479
+ defaultsSource: defaults !== void 0 ? "inline" : "path",
1685
480
  userConfigPath,
1686
481
  envPrefix,
1687
482
  envVarsConsumed: includeEnvVarReport ? envVars?.consumedKeys : void 0,
1688
483
  envVarsConsumedCount: includeEnvVarReport ? envVars?.consumedKeys.length : void 0,
1689
484
  activeLayers,
1690
485
  schema: {
1691
- path: options.schemaPath || null,
1692
- validated: !!options.schemaPath
486
+ // Inline schema wins over schemaPath, and has no backing file → report null.
487
+ path: options.schema !== void 0 ? null : options.schemaPath ?? null,
488
+ source: options.schema !== void 0 ? "inline" : options.schemaPath ? "path" : null,
489
+ validated: schemaProvided
1693
490
  }
1694
491
  }
1695
492
  };