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