@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,1499 +1,41 @@
1
- import { readFile, access } from 'fs/promises';
2
- import { dirname, join, relative, extname } from 'path';
1
+ export { ConfigReloadTracker, SignalManager, createBearerTokenAuth, createConfigReloadEndpoint, createConfigReloadHandler, createControlDiscoveryEndpoint, createDoubleTapTracker, createSignalEndpoint, createSignalManager, createSimpleRateLimiter, ensurePOSIX, ensureSignalExitCodesSupported, ensureSupported, ensureWindows, getFallbackMetadata, getHttpFallbackGuidance, getWindowTimeRemaining, handleDoubleTap, handleWindowsFallback, isWithinWindow, onAnyShutdown, onEmergencyQuit, onReload, onShutdown, onUSR1, onUSR2, requiresFallback, resetDoubleTap } from '../chunk-HS4F3BRR.js';
2
+ export { EXIT_CODES_VERSION, SimplifiedMode, exitCodeMetadata, exitCodes, getExitCodeInfo, getPlatform, getPlatformCapabilities, getSimplifiedCodeDescription, getSimplifiedCodes, isPOSIX, isWindows, mapExitCodeToSimplified, supportsSignalExitCodes } from '../chunk-RSLFR4TO.js';
3
+ import { ensureFoundryAssetsRegistered } from '../chunk-HIBZ2AY7.js';
4
+ export { getBehavior, getSignal, getSignalCatalog, getSignalNumber, getPlatformCapabilities as getSignalPlatformCapabilities, getSignalsVersion, getWindowsEvent, isPOSIX as isSignalPOSIX, isWindows as isSignalWindows, listBehaviors, listSignals, supportsSignal, supportsSignalExitCodes as supportsSignalBasedExitCodes } from '../chunk-HIBZ2AY7.js';
5
+ import '../chunk-HY6JQOFN.js';
6
+ export { casefold, distance, equalsIgnoreCase, normalize, score, stripAccents, suggest } from '../chunk-LWAH2UG7.js';
7
+ import { FoundryCatalogError } from '../chunk-3HISR2XI.js';
8
+ export { FoundryCatalogError } from '../chunk-3HISR2XI.js';
9
+ import { validateDataBySchemaId } from '../chunk-7SVYOPJ3.js';
10
+ import '../chunk-QCCN6POA.js';
11
+ import { getAssetResolver } from '../chunk-BTUS6PZZ.js';
12
+ import '../chunk-UP2VWCW5.js';
3
13
  import { parse } from 'yaml';
4
- import addFormats from 'ajv-formats';
5
- import 'child_process';
6
- import { fileURLToPath } from 'url';
7
- import glob from 'fast-glob';
8
- import 'commander';
9
- import Ajv from 'ajv';
10
- import Ajv2019 from 'ajv/dist/2019.js';
11
- import Ajv2020 from 'ajv/dist/2020.js';
12
- import AjvDraft04 from 'ajv-draft-04';
14
+ import { readFile } from 'fs/promises';
13
15
  import { Readable } from 'stream';
14
16
  import picomatch from 'picomatch';
15
- import { substringSimilarity, jaro_winkler, damerau_levenshtein, osa_distance, levenshtein, normalize as normalize$1, score as score$1, suggest as suggest$1 } from '@3leaps/string-metrics-wasm';
16
17
 
17
- var __getOwnPropNames = Object.getOwnPropertyNames;
18
- var __esm = (fn, res) => function __init() {
19
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
18
+ var SSOT_PATHS = {
19
+ patterns: "config/crucible-ts/library/foundry/patterns.yaml",
20
+ httpStatuses: "config/crucible-ts/library/foundry/http-statuses.yaml",
21
+ mimeTypes: "config/crucible-ts/library/foundry/mime-types.yaml",
22
+ countryCodes: "config/crucible-ts/library/foundry/country-codes.yaml"
20
23
  };
21
-
22
- // src/telemetry/counter.ts
23
- var Counter;
24
- var init_counter = __esm({
25
- "src/telemetry/counter.ts"() {
26
- Counter = class {
27
- constructor(name) {
28
- this.name = name;
29
- }
30
- value = 0;
31
- labeledValues = /* @__PURE__ */ new Map();
32
- /**
33
- * Increment counter by delta (default: 1)
34
- *
35
- * @param delta - Amount to increment (must be non-negative)
36
- * @param labels - Optional label dimensions for this observation
37
- * @throws {Error} If delta is negative
38
- *
39
- * @example
40
- * ```typescript
41
- * counter.inc(); // Increment unlabeled by 1
42
- * counter.inc(5); // Increment unlabeled by 5
43
- * counter.inc(1, { status: '200' }); // Increment labeled instance
44
- * counter.inc(1, { result: 'success' }); // Different label set
45
- * ```
46
- */
47
- inc(delta = 1, labels) {
48
- if (delta < 0) {
49
- throw new Error(`Counter delta must be non-negative, got: ${delta}`);
50
- }
51
- if (labels && Object.keys(labels).length > 0) {
52
- const labelKey = this.serializeLabels(labels);
53
- const current = this.labeledValues.get(labelKey) || 0;
54
- this.labeledValues.set(labelKey, current + delta);
55
- } else {
56
- this.value += delta;
57
- }
58
- }
59
- /**
60
- * Get current counter value (unlabeled)
61
- */
62
- getValue() {
63
- return this.value;
64
- }
65
- /**
66
- * Get all labeled values
67
- * @returns Map of serialized label keys to values
68
- */
69
- getLabeledValues() {
70
- return new Map(this.labeledValues);
71
- }
72
- /**
73
- * Get value for specific label combination
74
- */
75
- getValueForLabels(labels) {
76
- const labelKey = this.serializeLabels(labels);
77
- return this.labeledValues.get(labelKey) || 0;
78
- }
79
- /**
80
- * Reset counter to zero (all label combinations)
81
- */
82
- reset() {
83
- this.value = 0;
84
- this.labeledValues.clear();
85
- }
86
- /**
87
- * Serialize labels to deterministic string key
88
- * Format: key1=value1,key2=value2 (sorted by key)
89
- */
90
- serializeLabels(labels) {
91
- return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
92
- }
93
- };
94
- }
95
- });
96
-
97
- // src/telemetry/gauge.ts
98
- var Gauge;
99
- var init_gauge = __esm({
100
- "src/telemetry/gauge.ts"() {
101
- Gauge = class {
102
- constructor(name) {
103
- this.name = name;
104
- }
105
- value = 0;
106
- labeledValues = /* @__PURE__ */ new Map();
107
- /**
108
- * Set gauge to specific value
109
- *
110
- * @param value - New gauge value (can be any number, including negative)
111
- * @param labels - Optional label dimensions for this observation
112
- *
113
- * @example
114
- * ```typescript
115
- * gauge.set(42); // Set unlabeled to 42
116
- * gauge.set(-10); // Negative values allowed
117
- * gauge.set(1, { phase: 'collect' }); // Set labeled instance
118
- * ```
119
- */
120
- set(value, labels) {
121
- if (labels && Object.keys(labels).length > 0) {
122
- const labelKey = this.serializeLabels(labels);
123
- this.labeledValues.set(labelKey, value);
124
- } else {
125
- this.value = value;
126
- }
127
- }
128
- /**
129
- * Increment gauge by delta (default: 1)
130
- *
131
- * @param delta - Amount to increment (can be negative)
132
- * @param labels - Optional label dimensions for this observation
133
- */
134
- inc(delta = 1, labels) {
135
- if (labels && Object.keys(labels).length > 0) {
136
- const labelKey = this.serializeLabels(labels);
137
- const current = this.labeledValues.get(labelKey) || 0;
138
- this.labeledValues.set(labelKey, current + delta);
139
- } else {
140
- this.value += delta;
141
- }
142
- }
143
- /**
144
- * Decrement gauge by delta (default: 1)
145
- *
146
- * @param delta - Amount to decrement (can be negative)
147
- * @param labels - Optional label dimensions for this observation
148
- */
149
- dec(delta = 1, labels) {
150
- if (labels && Object.keys(labels).length > 0) {
151
- const labelKey = this.serializeLabels(labels);
152
- const current = this.labeledValues.get(labelKey) || 0;
153
- this.labeledValues.set(labelKey, current - delta);
154
- } else {
155
- this.value -= delta;
156
- }
157
- }
158
- /**
159
- * Get current gauge value (unlabeled)
160
- */
161
- getValue() {
162
- return this.value;
163
- }
164
- /**
165
- * Get all labeled values
166
- * @returns Map of serialized label keys to values
167
- */
168
- getLabeledValues() {
169
- return new Map(this.labeledValues);
170
- }
171
- /**
172
- * Get value for specific label combination
173
- */
174
- getValueForLabels(labels) {
175
- const labelKey = this.serializeLabels(labels);
176
- return this.labeledValues.get(labelKey) || 0;
177
- }
178
- /**
179
- * Reset gauge to zero (all label combinations)
180
- */
181
- reset() {
182
- this.value = 0;
183
- this.labeledValues.clear();
184
- }
185
- /**
186
- * Serialize labels to deterministic string key
187
- * Format: key1=value1,key2=value2 (sorted by key)
188
- */
189
- serializeLabels(labels) {
190
- return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
191
- }
192
- };
193
- }
194
- });
195
- async function getDefaultUnit(name) {
196
- return TaxonomyLoader.getInstance().getDefaultUnit(name);
197
- }
198
- var DEFAULT_MS_BUCKETS, TaxonomyLoader;
199
- var init_taxonomy = __esm({
200
- "src/telemetry/taxonomy.ts"() {
201
- DEFAULT_MS_BUCKETS = [1, 5, 10, 50, 100, 500, 1e3, 5e3, 1e4];
202
- TaxonomyLoader = class _TaxonomyLoader {
203
- static instance;
204
- taxonomy = null;
205
- loadPromise = null;
206
- loadError = null;
207
- constructor() {
208
- }
209
- /**
210
- * Get singleton instance
211
- */
212
- static getInstance() {
213
- if (!_TaxonomyLoader.instance) {
214
- _TaxonomyLoader.instance = new _TaxonomyLoader();
215
- }
216
- return _TaxonomyLoader.instance;
217
- }
218
- /**
219
- * Load taxonomy from YAML file
220
- */
221
- async load() {
222
- if (this.taxonomy !== null) {
223
- return this.taxonomy;
224
- }
225
- if (this.loadError !== null) {
226
- throw this.loadError;
227
- }
228
- if (this.loadPromise) {
229
- return this.loadPromise;
230
- }
231
- this.loadPromise = (async () => {
232
- try {
233
- const taxonomyPath = join(
234
- __dirname,
235
- "..",
236
- "..",
237
- "config",
238
- "crucible-ts",
239
- "taxonomy",
240
- "metrics.yaml"
241
- );
242
- const content = await readFile(taxonomyPath, "utf-8");
243
- this.taxonomy = parse(content);
244
- return this.taxonomy;
245
- } catch (err) {
246
- this.loadError = err instanceof Error ? err : new Error(String(err));
247
- throw new Error(`Failed to load metrics taxonomy: ${this.loadError.message}`);
248
- }
249
- })();
250
- return this.loadPromise;
251
- }
252
- /**
253
- * Get taxonomy (async)
254
- */
255
- async getTaxonomy() {
256
- return this.load();
257
- }
258
- /**
259
- * Get metric definition by name
260
- */
261
- async getMetric(name) {
262
- const taxonomy = await this.load();
263
- return taxonomy.metrics.find((m) => m.name === name);
264
- }
265
- /**
266
- * Get default unit for metric
267
- */
268
- async getDefaultUnit(name) {
269
- const metric = await this.getMetric(name);
270
- return metric?.unit;
271
- }
272
- /**
273
- * Get default histogram buckets for metric
274
- * Returns ADR-0007 buckets for _ms metrics, undefined for others
275
- */
276
- async getDefaultBuckets(name) {
277
- if (name.endsWith("_ms")) {
278
- const taxonomy = await this.load();
279
- return taxonomy.defaults.histogram_buckets.ms_metrics;
280
- }
281
- return void 0;
282
- }
283
- /**
284
- * Check if metric name is valid (exists in taxonomy)
285
- */
286
- async isValidMetricName(name) {
287
- try {
288
- const taxonomy = await this.load();
289
- return taxonomy.metrics.some((m) => m.name === name);
290
- } catch {
291
- return false;
292
- }
293
- }
294
- /**
295
- * Reset loader state (for testing)
296
- * @internal
297
- */
298
- static _reset() {
299
- _TaxonomyLoader.instance = new _TaxonomyLoader();
300
- }
301
- };
302
- }
303
- });
304
-
305
- // src/telemetry/histogram.ts
306
- var Histogram;
307
- var init_histogram = __esm({
308
- "src/telemetry/histogram.ts"() {
309
- init_taxonomy();
310
- Histogram = class {
311
- constructor(name, options) {
312
- this.name = name;
313
- if (options?.buckets) {
314
- this.buckets = [...options.buckets].sort((a, b) => a - b);
315
- } else if (name.endsWith("_ms") || name.endsWith("_seconds")) {
316
- this.buckets = [...DEFAULT_MS_BUCKETS];
317
- } else {
318
- this.buckets = [];
319
- }
320
- for (const bucket of this.buckets) {
321
- this.bucketCounts.set(bucket, 0);
322
- }
323
- }
324
- count = 0;
325
- sum = 0;
326
- bucketCounts = /* @__PURE__ */ new Map();
327
- labeledStates = /* @__PURE__ */ new Map();
328
- buckets;
329
- /**
330
- * Record an observation
331
- *
332
- * @param value - Value to observe (typically a duration in ms or seconds)
333
- * @param labels - Optional label dimensions for this observation
334
- *
335
- * @example
336
- * ```typescript
337
- * const start = performance.now();
338
- * // ... operation ...
339
- * histogram.observe(performance.now() - start);
340
- * histogram.observe(duration, { phase: 'collect', result: 'success' });
341
- * ```
342
- */
343
- observe(value, labels) {
344
- if (labels && Object.keys(labels).length > 0) {
345
- const labelKey = this.serializeLabels(labels);
346
- let state = this.labeledStates.get(labelKey);
347
- if (!state) {
348
- state = {
349
- count: 0,
350
- sum: 0,
351
- bucketCounts: /* @__PURE__ */ new Map()
352
- };
353
- for (const bucket of this.buckets) {
354
- state.bucketCounts.set(bucket, 0);
355
- }
356
- this.labeledStates.set(labelKey, state);
357
- }
358
- state.count++;
359
- state.sum += value;
360
- for (const bucket of this.buckets) {
361
- if (value <= bucket) {
362
- state.bucketCounts.set(bucket, (state.bucketCounts.get(bucket) || 0) + 1);
363
- }
364
- }
365
- } else {
366
- this.count++;
367
- this.sum += value;
368
- for (const bucket of this.buckets) {
369
- if (value <= bucket) {
370
- this.bucketCounts.set(bucket, (this.bucketCounts.get(bucket) || 0) + 1);
371
- }
372
- }
373
- }
374
- }
375
- /**
376
- * Get histogram summary
377
- *
378
- * Returns OTLP-compatible histogram summary with cumulative bucket counts.
379
- */
380
- getSummary() {
381
- const buckets = this.buckets.map((le) => ({
382
- le,
383
- count: this.bucketCounts.get(le) || 0
384
- }));
385
- return {
386
- count: this.count,
387
- sum: this.sum,
388
- buckets
389
- };
390
- }
391
- /**
392
- * Get current observation count
393
- */
394
- getCount() {
395
- return this.count;
396
- }
397
- /**
398
- * Get sum of all observed values
399
- */
400
- getSum() {
401
- return this.sum;
402
- }
403
- /**
404
- * Get average of observed values
405
- */
406
- getAverage() {
407
- return this.count > 0 ? this.sum / this.count : 0;
408
- }
409
- /**
410
- * Get all labeled summaries
411
- * @returns Map of serialized label keys to histogram summaries
412
- */
413
- getLabeledSummaries() {
414
- const summaries = /* @__PURE__ */ new Map();
415
- for (const [labelKey, state] of this.labeledStates) {
416
- const buckets = this.buckets.map((le) => ({
417
- le,
418
- count: state.bucketCounts.get(le) || 0
419
- }));
420
- summaries.set(labelKey, {
421
- count: state.count,
422
- sum: state.sum,
423
- buckets
424
- });
425
- }
426
- return summaries;
427
- }
428
- /**
429
- * Get summary for specific label combination
430
- */
431
- getSummaryForLabels(labels) {
432
- const labelKey = this.serializeLabels(labels);
433
- const state = this.labeledStates.get(labelKey);
434
- if (!state) {
435
- return null;
436
- }
437
- const buckets = this.buckets.map((le) => ({
438
- le,
439
- count: state.bucketCounts.get(le) || 0
440
- }));
441
- return {
442
- count: state.count,
443
- sum: state.sum,
444
- buckets
445
- };
446
- }
447
- /**
448
- * Reset histogram to initial state (all label combinations)
449
- */
450
- reset() {
451
- this.count = 0;
452
- this.sum = 0;
453
- for (const bucket of this.buckets) {
454
- this.bucketCounts.set(bucket, 0);
455
- }
456
- this.labeledStates.clear();
457
- }
458
- /**
459
- * Serialize labels to deterministic string key
460
- * Format: key1=value1,key2=value2 (sorted by key)
461
- */
462
- serializeLabels(labels) {
463
- return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
464
- }
465
- };
466
- }
467
- });
468
-
469
- // src/telemetry/registry.ts
470
- var MetricsRegistry;
471
- var init_registry = __esm({
472
- "src/telemetry/registry.ts"() {
473
- init_counter();
474
- init_gauge();
475
- init_histogram();
476
- init_taxonomy();
477
- MetricsRegistry = class {
478
- counters = /* @__PURE__ */ new Map();
479
- gauges = /* @__PURE__ */ new Map();
480
- histograms = /* @__PURE__ */ new Map();
481
- /**
482
- * Get or create a counter
483
- *
484
- * @param name - Metric name from taxonomy
485
- * @returns Counter instance
486
- *
487
- * @example
488
- * ```typescript
489
- * const counter = registry.counter('schema_validations');
490
- * counter.inc();
491
- * ```
492
- */
493
- counter(name) {
494
- let counter = this.counters.get(name);
495
- if (!counter) {
496
- counter = new Counter(name);
497
- this.counters.set(name, counter);
498
- }
499
- return counter;
500
- }
501
- /**
502
- * Get or create a gauge
503
- *
504
- * @param name - Metric name from taxonomy
505
- * @returns Gauge instance
506
- *
507
- * @example
508
- * ```typescript
509
- * const gauge = registry.gauge('foundry_lookup_count');
510
- * gauge.set(42);
511
- * ```
512
- */
513
- gauge(name) {
514
- let gauge = this.gauges.get(name);
515
- if (!gauge) {
516
- gauge = new Gauge(name);
517
- this.gauges.set(name, gauge);
518
- }
519
- return gauge;
520
- }
521
- /**
522
- * Get or create a histogram
523
- *
524
- * @param name - Metric name from taxonomy
525
- * @param options - Optional histogram options
526
- * @returns Histogram instance
527
- *
528
- * @example
529
- * ```typescript
530
- * // Auto-applies ADR-0007 buckets for _ms metrics
531
- * const histogram = registry.histogram('config_load_ms');
532
- * histogram.observe(42.5);
533
- *
534
- * // Custom buckets
535
- * const custom = registry.histogram('custom_metric', {
536
- * buckets: [10, 50, 100, 500, 1000]
537
- * });
538
- * ```
539
- */
540
- histogram(name, options) {
541
- let histogram = this.histograms.get(name);
542
- if (!histogram) {
543
- histogram = new Histogram(name, options);
544
- this.histograms.set(name, histogram);
545
- }
546
- return histogram;
547
- }
548
- /**
549
- * Export all metrics as events
550
- *
551
- * Returns array of schema-compliant MetricsEvent objects.
552
- * Does not clear metrics (use flush() to clear after export).
553
- *
554
- * @returns Promise resolving to array of metrics events
555
- *
556
- * @example
557
- * ```typescript
558
- * const events = await registry.export();
559
- * console.log(JSON.stringify(events, null, 2));
560
- * ```
561
- */
562
- async export() {
563
- const events = [];
564
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
565
- for (const [name, counter] of this.counters) {
566
- const unit = await getDefaultUnit(name);
567
- events.push({
568
- timestamp,
569
- name,
570
- value: counter.getValue(),
571
- unit
572
- });
573
- for (const [labelKey, value] of counter.getLabeledValues()) {
574
- if (value > 0) {
575
- const tags = this.deserializeLabels(labelKey);
576
- events.push({
577
- timestamp,
578
- name,
579
- value,
580
- tags,
581
- unit
582
- });
583
- }
584
- }
585
- }
586
- for (const [name, gauge] of this.gauges) {
587
- const unit = await getDefaultUnit(name);
588
- events.push({
589
- timestamp,
590
- name,
591
- value: gauge.getValue(),
592
- unit
593
- });
594
- for (const [labelKey, value] of gauge.getLabeledValues()) {
595
- const tags = this.deserializeLabels(labelKey);
596
- events.push({
597
- timestamp,
598
- name,
599
- value,
600
- tags,
601
- unit
602
- });
603
- }
604
- }
605
- for (const [name, histogram] of this.histograms) {
606
- const unit = await getDefaultUnit(name);
607
- events.push({
608
- timestamp,
609
- name,
610
- value: histogram.getSummary(),
611
- unit
612
- });
613
- for (const [labelKey, summary] of histogram.getLabeledSummaries()) {
614
- if (summary.count > 0) {
615
- const tags = this.deserializeLabels(labelKey);
616
- events.push({
617
- timestamp,
618
- name,
619
- value: summary,
620
- tags,
621
- unit
622
- });
623
- }
624
- }
625
- }
626
- return events;
627
- }
628
- /**
629
- * Deserialize label key back to tags object
630
- * Format: key1=value1,key2=value2 → {key1: "value1", key2: "value2"}
631
- */
632
- deserializeLabels(labelKey) {
633
- if (!labelKey) {
634
- return {};
635
- }
636
- const tags = {};
637
- for (const pair of labelKey.split(",")) {
638
- const [key, value] = pair.split("=");
639
- if (key && value) {
640
- tags[key] = value;
641
- }
642
- }
643
- return tags;
644
- }
645
- /**
646
- * Export and clear all metrics
647
- *
648
- * Exports metrics as events, optionally emits them via logger,
649
- * then resets all metrics to zero.
650
- *
651
- * @param options - Flush options
652
- * @returns Promise resolving to array of exported events
653
- *
654
- * @example
655
- * ```typescript
656
- * // Export and clear
657
- * const events = await registry.flush();
658
- *
659
- * // Export, emit to logger, and clear
660
- * const events = await registry.flush({
661
- * emit: (events) => console.log(JSON.stringify(events))
662
- * });
663
- * ```
664
- */
665
- async flush(options) {
666
- const events = await this.export();
667
- try {
668
- if (options?.emit) {
669
- options.emit(events);
670
- }
671
- } finally {
672
- this.clear();
673
- }
674
- return events;
675
- }
676
- /**
677
- * Clear all metrics (reset to zero)
678
- *
679
- * Resets all counters, gauges, and histograms to their initial state.
680
- */
681
- clear() {
682
- for (const counter of this.counters.values()) {
683
- counter.reset();
684
- }
685
- for (const gauge of this.gauges.values()) {
686
- gauge.reset();
687
- }
688
- for (const histogram of this.histograms.values()) {
689
- histogram.reset();
690
- }
691
- }
692
- /**
693
- * Get all registered metric names
694
- *
695
- * Returns array of all metric names that have been accessed
696
- * (counters, gauges, or histograms).
697
- */
698
- getMetricNames() {
699
- const names = /* @__PURE__ */ new Set();
700
- for (const name of this.counters.keys()) {
701
- names.add(name);
702
- }
703
- for (const name of this.gauges.keys()) {
704
- names.add(name);
705
- }
706
- for (const name of this.histograms.keys()) {
707
- names.add(name);
708
- }
709
- return Array.from(names);
710
- }
711
- /**
712
- * Get total count of registered metrics
713
- */
714
- getMetricCount() {
715
- return this.counters.size + this.gauges.size + this.histograms.size;
716
- }
717
- };
718
- }
719
- });
720
-
721
- // src/telemetry/types.ts
722
- var init_types = __esm({
723
- "src/telemetry/types.ts"() {
724
- }
725
- });
726
- function applyFulmenAjvFormats(ajv, options = {}) {
727
- const mode = options.mode ?? "fast";
728
- const formats = options.formats ?? DEFAULT_FORMATS;
729
- addFormats(ajv, { mode, formats });
730
- return ajv;
731
- }
732
- var DEFAULT_FORMATS;
733
- var init_ajv_formats = __esm({
734
- "src/schema/ajv-formats.ts"() {
735
- DEFAULT_FORMATS = [
736
- "date-time",
737
- "email",
738
- "hostname",
739
- "ipv4",
740
- "ipv6",
741
- "uri",
742
- "uri-reference",
743
- "uuid"
744
- ];
745
- }
746
- });
747
-
748
- // src/schema/errors.ts
749
- var SchemaValidationError;
750
- var init_errors = __esm({
751
- "src/schema/errors.ts"() {
752
- SchemaValidationError = class _SchemaValidationError extends Error {
753
- constructor(message, schemaId, diagnostics = [], source, cause) {
754
- super(message);
755
- this.schemaId = schemaId;
756
- this.diagnostics = diagnostics;
757
- this.source = source;
758
- this.cause = cause;
759
- this.name = "SchemaValidationError";
760
- if (Error.captureStackTrace) {
761
- Error.captureStackTrace(this, _SchemaValidationError);
762
- }
763
- }
764
- /**
765
- * Create error for schema not found
766
- */
767
- static schemaNotFound(schemaId) {
768
- return new _SchemaValidationError(`Schema not found: ${schemaId}`, schemaId);
769
- }
770
- /**
771
- * Create error for invalid schema input
772
- */
773
- static invalidSchemaInput(source, details) {
774
- return new _SchemaValidationError(`Invalid schema input: ${details}`, void 0, [], source);
775
- }
776
- /**
777
- * Create error for validation failure
778
- */
779
- static validationFailed(schemaId, diagnostics, source) {
780
- const errorCount = diagnostics.filter((d) => d.severity === "ERROR").length;
781
- const warningCount = diagnostics.filter((d) => d.severity === "WARN").length;
782
- const message = `Schema validation failed: ${errorCount} error(s), ${warningCount} warning(s)`;
783
- return new _SchemaValidationError(message, schemaId, diagnostics, source);
784
- }
785
- /**
786
- * Create error for goneat binary not found
787
- */
788
- static goneatNotFound(goneatPath) {
789
- const pathInfo = goneatPath ? ` at ${goneatPath}` : "";
790
- return new _SchemaValidationError(
791
- `Goneat binary not found${pathInfo}. Falling back to AJV validation.`
792
- );
793
- }
794
- /**
795
- * Create error for goneat execution failure
796
- */
797
- static goneatExecutionFailed(error) {
798
- return new _SchemaValidationError(
799
- "Goneat execution failed. Falling back to AJV validation.",
800
- void 0,
801
- [],
802
- void 0,
803
- error
804
- );
805
- }
806
- /**
807
- * Create error for empty schema input
808
- */
809
- static emptySchemaInput(source) {
810
- return new _SchemaValidationError("Schema content is empty", void 0, [], source);
811
- }
812
- /**
813
- * Create error for parse failure
814
- */
815
- static parseFailed(source, error) {
816
- return new _SchemaValidationError(
817
- `Failed to parse schema: ${error.message}`,
818
- void 0,
819
- [],
820
- source,
821
- error
822
- );
823
- }
824
- /**
825
- * Create error for encoding failure
826
- */
827
- static encodingFailed(source, error) {
828
- return new _SchemaValidationError(
829
- `Failed to encode schema: ${error.message}`,
830
- void 0,
831
- [],
832
- source,
833
- error
834
- );
835
- }
836
- /**
837
- * Create error for registry operation failure
838
- */
839
- static registryError(operation, details) {
840
- return new _SchemaValidationError(`Schema registry ${operation} failed: ${details}`);
841
- }
842
- /**
843
- * Format error for display
844
- */
845
- format() {
846
- let output = this.message;
847
- if (this.schemaId) {
848
- output += `
849
- Schema ID: ${this.schemaId}`;
850
- }
851
- if (this.diagnostics.length > 0) {
852
- output += "\n\nValidation Issues:";
853
- this.diagnostics.forEach((diag, index) => {
854
- output += `
855
- ${index + 1}. [${diag.severity}] ${diag.message}`;
856
- if (diag.pointer) {
857
- output += ` at ${diag.pointer}`;
858
- }
859
- if (diag.keyword) {
860
- output += ` (keyword: ${diag.keyword})`;
861
- }
862
- if (diag.source) {
863
- output += ` [${diag.source}]`;
864
- }
865
- });
866
- }
867
- if (this.source) {
868
- output += `
869
-
870
- Source: ${this.source.type}`;
871
- if (this.source.id) {
872
- output += ` (${this.source.id})`;
873
- }
874
- }
875
- return output;
876
- }
877
- /**
878
- * Convert to JSON representation
879
- */
880
- toJSON() {
881
- return {
882
- name: this.name,
883
- message: this.message,
884
- schemaId: this.schemaId,
885
- diagnostics: this.diagnostics,
886
- source: this.source,
887
- cause: this.cause?.message
888
- };
889
- }
890
- };
891
- }
892
- });
893
-
894
- // src/schema/utils.ts
895
- function createDiagnostic(pointer, message, keyword, severity = "ERROR", source = "ajv", data) {
896
- return {
897
- pointer,
898
- message,
899
- keyword,
900
- severity,
901
- source,
902
- data
903
- };
904
- }
905
- var init_utils = __esm({
906
- "src/schema/utils.ts"() {
907
- init_errors();
908
- }
909
- });
910
- var init_goneat_bridge = __esm({
911
- "src/schema/goneat-bridge.ts"() {
912
- init_utils();
913
- }
914
- });
915
- var init_normalizer = __esm({
916
- "src/schema/normalizer.ts"() {
917
- init_errors();
918
- }
919
- });
920
- function optionsChanged(newOptions) {
921
- if (!globalRegistryOptions) return false;
922
- return true;
923
- }
924
- function getSchemaRegistry(options) {
925
- if (!globalRegistry || optionsChanged()) {
926
- globalRegistry = new SchemaRegistry(options);
927
- globalRegistryOptions = options;
928
- }
929
- return globalRegistry;
930
- }
931
- var DEFAULT_PATTERNS, SchemaRegistry, globalRegistry, globalRegistryOptions;
932
- var init_registry2 = __esm({
933
- "src/schema/registry.ts"() {
934
- init_errors();
935
- DEFAULT_PATTERNS = ["**/*.schema.json", "**/*.schema.yaml", "**/*.schema.yml"];
936
- SchemaRegistry = class {
937
- schemas = /* @__PURE__ */ new Map();
938
- options;
939
- constructor(options = {}) {
940
- this.options = {
941
- baseDir: options.baseDir || this.getDefaultSchemaDir(),
942
- patterns: options.patterns || DEFAULT_PATTERNS,
943
- followSymlinks: options.followSymlinks ?? false,
944
- maxDepth: options.maxDepth ?? 10
945
- };
946
- }
947
- /**
948
- * Get default schema directory using import.meta.url
949
- */
950
- getDefaultSchemaDir() {
951
- const __filename3 = fileURLToPath(import.meta.url);
952
- const __dirname4 = dirname(__filename3);
953
- return join(__dirname4, "..", "..", "schemas", "crucible-ts");
954
- }
955
- /**
956
- * Build logical schema ID from file path
957
- */
958
- buildSchemaId(filePath, baseDir) {
959
- const relativePath = relative(baseDir, filePath);
960
- const withoutExt = relativePath.replace(/\.(schema\.(json|yaml|yml))$/, "");
961
- return withoutExt.replace(/\\/g, "/");
962
- }
963
- /**
964
- * Extract schema format from file extension
965
- */
966
- getSchemaFormat(filePath) {
967
- const ext = extname(filePath).toLowerCase();
968
- switch (ext) {
969
- case ".json":
970
- return "json";
971
- case ".yaml":
972
- case ".yml":
973
- return "yaml";
974
- default:
975
- return "json";
976
- }
977
- }
978
- /**
979
- * Extract metadata from schema file
980
- */
981
- async extractMetadata(filePath) {
982
- try {
983
- const content = await readFile(filePath, "utf-8");
984
- const format = this.getSchemaFormat(filePath);
985
- let parsed;
986
- if (format === "yaml") {
987
- parsed = parse(content);
988
- } else {
989
- parsed = JSON.parse(content);
990
- }
991
- const baseDir = this.options.baseDir ?? "";
992
- const relativePath = relative(baseDir, filePath);
993
- return {
994
- id: this.buildSchemaId(filePath, baseDir),
995
- path: filePath,
996
- relativePath,
997
- format,
998
- version: parsed.$schema || parsed.version,
999
- description: parsed.title || parsed.description,
1000
- schemaDraft: parsed.$schema
1001
- };
1002
- } catch (error) {
1003
- throw SchemaValidationError.registryError(
1004
- "metadata extraction",
1005
- `Failed to process ${filePath}: ${error.message}`
1006
- );
1007
- }
1008
- }
1009
- /**
1010
- * Discover and index all available schemas
1011
- */
1012
- async discoverSchemas() {
1013
- try {
1014
- const baseDir = this.options.baseDir ?? "";
1015
- const patterns = this.options.patterns ?? [];
1016
- if (patterns.length === 0) {
1017
- this.schemas.clear();
1018
- return;
1019
- }
1020
- const pattern = patterns.map((p) => join(baseDir, p));
1021
- try {
1022
- await access(baseDir);
1023
- } catch {
1024
- this.schemas.clear();
1025
- return;
1026
- }
1027
- const files = await glob(pattern, {
1028
- absolute: true,
1029
- followSymbolicLinks: this.options.followSymlinks,
1030
- deep: this.options.maxDepth,
1031
- onlyFiles: true,
1032
- suppressErrors: true
1033
- // Don't throw on permission errors
1034
- });
1035
- this.schemas.clear();
1036
- for (const filePath of files) {
1037
- try {
1038
- const metadata = await this.extractMetadata(filePath);
1039
- this.schemas.set(metadata.id, metadata);
1040
- } catch (error) {
1041
- console.warn(`Warning: Failed to process schema ${filePath}:`, error);
1042
- }
1043
- }
1044
- } catch (error) {
1045
- throw SchemaValidationError.registryError("discovery", error.message);
1046
- }
1047
- }
1048
- /**
1049
- * List available schemas with optional prefix filtering
1050
- */
1051
- async listSchemas(prefix) {
1052
- if (this.schemas.size === 0) {
1053
- await this.discoverSchemas();
1054
- }
1055
- const schemas = Array.from(this.schemas.values());
1056
- if (prefix) {
1057
- return schemas.filter((schema) => schema.id.startsWith(prefix));
1058
- }
1059
- return schemas;
1060
- }
1061
- /**
1062
- * Get schema by logical ID
1063
- */
1064
- async getSchema(id) {
1065
- if (this.schemas.size === 0) {
1066
- await this.discoverSchemas();
1067
- }
1068
- const schema = this.schemas.get(id);
1069
- if (!schema) {
1070
- throw SchemaValidationError.schemaNotFound(id);
1071
- }
1072
- return schema;
1073
- }
1074
- /**
1075
- * Get schema by file path
1076
- */
1077
- async getSchemaByPath(filePath) {
1078
- if (this.schemas.size === 0) {
1079
- await this.discoverSchemas();
1080
- }
1081
- const absolutePath = filePath.startsWith("/") ? filePath : join(process.cwd(), filePath);
1082
- for (const schema of this.schemas.values()) {
1083
- if (schema.path === absolutePath) {
1084
- return schema;
1085
- }
1086
- }
1087
- throw SchemaValidationError.schemaNotFound(filePath);
1088
- }
1089
- /**
1090
- * Check if schema exists
1091
- */
1092
- async hasSchema(id) {
1093
- if (this.schemas.size === 0) {
1094
- await this.discoverSchemas();
1095
- }
1096
- return this.schemas.has(id);
1097
- }
1098
- /**
1099
- * Get registry size
1100
- */
1101
- get size() {
1102
- return this.schemas.size;
1103
- }
1104
- /**
1105
- * Clear registry cache
1106
- */
1107
- clear() {
1108
- this.schemas.clear();
1109
- }
1110
- };
1111
- }
1112
- });
1113
- var init_cli = __esm({
1114
- "src/schema/cli.ts"() {
1115
- init_goneat_bridge();
1116
- init_normalizer();
1117
- init_registry2();
1118
- init_utils();
1119
- init_validator();
1120
- }
1121
- });
1122
- var init_export = __esm({
1123
- "src/schema/export.ts"() {
1124
- init_errors();
1125
- init_registry2();
1126
- init_validator();
1127
- }
1128
- });
1129
-
1130
- // src/schema/index.ts
1131
- var init_schema = __esm({
1132
- "src/schema/index.ts"() {
1133
- init_ajv_formats();
1134
- init_cli();
1135
- init_errors();
1136
- init_export();
1137
- init_goneat_bridge();
1138
- init_normalizer();
1139
- init_registry2();
1140
- init_utils();
1141
- init_validator();
1142
- }
1143
- });
1144
-
1145
- // src/telemetry/validators.ts
1146
- var init_validators = __esm({
1147
- "src/telemetry/validators.ts"() {
1148
- init_schema();
1149
- }
1150
- });
1151
-
1152
- // src/telemetry/index.ts
1153
- var metrics;
1154
- var init_telemetry = __esm({
1155
- "src/telemetry/index.ts"() {
1156
- init_registry();
1157
- init_registry();
1158
- init_counter();
1159
- init_gauge();
1160
- init_histogram();
1161
- init_taxonomy();
1162
- init_types();
1163
- init_validators();
1164
- metrics = new MetricsRegistry();
1165
- }
1166
- });
1167
- async function loadMetaSchema(draft) {
1168
- const __filename3 = fileURLToPath(import.meta.url);
1169
- const __dirname4 = dirname(__filename3);
1170
- const metaSchemaPath = join(
1171
- __dirname4,
1172
- "..",
1173
- "..",
1174
- "schemas",
1175
- "crucible-ts",
1176
- "meta",
1177
- draft,
1178
- "schema.json"
1179
- );
1180
- const content = await readFile(metaSchemaPath, "utf-8");
1181
- return JSON.parse(content);
1182
- }
1183
- async function loadVocabularySchemas(draft) {
1184
- if (draft !== "draft-2019-09" && draft !== "draft-2020-12") {
1185
- return [];
1186
- }
1187
- const __filename3 = fileURLToPath(import.meta.url);
1188
- const __dirname4 = dirname(__filename3);
1189
- const vocabDir = join(__dirname4, "..", "..", "schemas", "crucible-ts", "meta", draft, "meta");
1190
- const vocabFiles = draft === "draft-2020-12" ? [
1191
- "core.json",
1192
- "applicator.json",
1193
- "unevaluated.json",
1194
- "validation.json",
1195
- "meta-data.json",
1196
- "format-annotation.json",
1197
- "content.json"
1198
- ] : [
1199
- "core.json",
1200
- "applicator.json",
1201
- "validation.json",
1202
- "meta-data.json",
1203
- "format.json",
1204
- "content.json"
1205
- ];
1206
- const schemas = [];
1207
- for (const file of vocabFiles) {
1208
- try {
1209
- const content = await readFile(join(vocabDir, file), "utf-8");
1210
- schemas.push(JSON.parse(content));
1211
- } catch {
1212
- }
1213
- }
1214
- return schemas;
1215
- }
1216
- async function loadReferencedSchema(uri) {
1217
- const __filename3 = fileURLToPath(import.meta.url);
1218
- const __dirname4 = dirname(__filename3);
1219
- const repoRoot = join(__dirname4, "..", "..");
1220
- let resolvedPath;
1221
- if (uri.startsWith("https://schemas.fulmenhq.dev/")) {
1222
- let relativePath = uri.replace("https://schemas.fulmenhq.dev/", "");
1223
- if (relativePath.startsWith("crucible/")) {
1224
- relativePath = relativePath.slice("crucible/".length);
1225
- }
1226
- if (relativePath.startsWith("config/taxonomy/")) {
1227
- resolvedPath = join(
1228
- repoRoot,
1229
- "config",
1230
- "crucible-ts",
1231
- "taxonomy",
1232
- relativePath.split("/").pop() || ""
1233
- );
1234
- } else {
1235
- resolvedPath = join(repoRoot, "schemas", "crucible-ts", relativePath);
1236
- }
1237
- } else if (uri.startsWith("../../") || uri.startsWith("../")) {
1238
- const schemaBase = join(
1239
- repoRoot,
1240
- "schemas",
1241
- "crucible-ts",
1242
- "observability",
1243
- "metrics",
1244
- "v1.0.0"
1245
- );
1246
- resolvedPath = join(schemaBase, uri);
1247
- } else if (uri.startsWith("file://")) {
1248
- resolvedPath = fileURLToPath(uri);
1249
- } else {
1250
- throw new Error(`Cannot load remote schema: ${uri}`);
1251
- }
1252
- const content = await readFile(resolvedPath, "utf-8");
1253
- const ext = resolvedPath.split(".").pop()?.toLowerCase();
1254
- if (ext === "yaml" || ext === "yml") {
1255
- return parse(content);
1256
- }
1257
- return JSON.parse(content);
1258
- }
1259
- function detectDialect(schema) {
1260
- if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1261
- const maybeSchema = schema;
1262
- const declared = maybeSchema.$schema;
1263
- if (typeof declared === "string") {
1264
- if (declared.includes("draft-04")) return "draft-04";
1265
- if (declared.includes("draft-06")) return "draft-06";
1266
- if (declared.includes("draft-07")) return "draft-07";
1267
- if (declared.includes("draft/2019-09")) return "draft-2019-09";
1268
- if (declared.includes("draft/2020-12")) return "draft-2020-12";
1269
- }
1270
- }
1271
- return "draft-2020-12";
1272
- }
1273
- function createAjv(dialect) {
1274
- const AjvCtor = dialect === "draft-2020-12" ? Ajv2020 : dialect === "draft-2019-09" ? Ajv2019 : dialect === "draft-04" ? AjvDraft04 : Ajv;
1275
- const ajv = new AjvCtor({
1276
- strict: false,
1277
- allErrors: true,
1278
- verbose: true,
1279
- // Allow schemas with $id to be added without replacing existing ones
1280
- addUsedSchema: false,
1281
- // draft-04 uses "id"; later drafts use "$id"
1282
- schemaId: dialect === "draft-04" ? "id" : "$id",
1283
- // Enable async schema loading for YAML references
1284
- loadSchema: loadReferencedSchema
1285
- });
1286
- applyFulmenAjvFormats(ajv);
1287
- return ajv;
1288
- }
1289
- async function getAjv(dialect) {
1290
- const existing = ajvInstances.get(dialect);
1291
- if (existing) {
1292
- const ready = metaschemaReady.get(dialect);
1293
- if (ready) await ready;
1294
- return existing;
1295
- }
1296
- const ajv = createAjv(dialect);
1297
- ajvInstances.set(dialect, ajv);
1298
- const readyPromise = Promise.all([loadVocabularySchemas(dialect), loadMetaSchema(dialect)]).then(([vocabSchemas, metaSchema]) => {
1299
- for (const vocabSchema of vocabSchemas) {
1300
- try {
1301
- ajv.addMetaSchema(vocabSchema);
1302
- } catch {
1303
- }
1304
- }
1305
- try {
1306
- ajv.addMetaSchema(metaSchema);
1307
- } catch {
1308
- }
1309
- }).catch((error) => {
1310
- throw new Error(`Failed to load metaschemas (${dialect}): ${error}`);
1311
- });
1312
- metaschemaReady.set(dialect, readyPromise);
1313
- await readyPromise;
1314
- return ajv;
1315
- }
1316
- async function compileSchema(schema, options = {}) {
1317
- const baseKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1318
- let parsedSchema;
1319
- if (typeof schema === "string") {
1320
- try {
1321
- parsedSchema = JSON.parse(schema);
1322
- } catch {
1323
- parsedSchema = parse(schema);
1324
- }
1325
- } else if (Buffer.isBuffer(schema)) {
1326
- const content = schema.toString("utf-8");
1327
- try {
1328
- parsedSchema = JSON.parse(content);
1329
- } catch {
1330
- parsedSchema = parse(content);
1331
- }
1332
- } else {
1333
- parsedSchema = schema;
1334
- }
1335
- const dialect = detectDialect(parsedSchema);
1336
- const ajv = await getAjv(dialect);
1337
- const cacheKey = `${dialect}:${baseKey}`;
1338
- const cached = schemaCache.get(cacheKey);
1339
- if (cached !== void 0) {
1340
- return cached;
1341
- }
1342
- try {
1343
- if (options.aliases && options.aliases.length > 0) {
1344
- for (const alias of options.aliases) {
1345
- if (alias && ajv.getSchema(alias) === void 0) {
1346
- try {
1347
- if (typeof parsedSchema === "object" && parsedSchema !== null) {
1348
- ajv.addSchema(parsedSchema, alias);
1349
- }
1350
- } catch {
1351
- }
1352
- }
1353
- }
1354
- }
1355
- const validator = typeof parsedSchema === "boolean" ? ajv.compile(parsedSchema) : await ajv.compileAsync(parsedSchema);
1356
- schemaCache.set(cacheKey, validator);
1357
- return validator;
1358
- } catch (error) {
1359
- throw SchemaValidationError.parseFailed(
1360
- {
1361
- type: "string",
1362
- content: typeof schema === "string" ? schema : JSON.stringify(schema)
1363
- },
1364
- error
1365
- );
1366
- }
1367
- }
1368
- function validateData(data, validator) {
1369
- const valid = validator(data);
1370
- const result = {
1371
- valid,
1372
- diagnostics: [],
1373
- source: "ajv"
1374
- };
1375
- if (!valid && validator.errors) {
1376
- const errors = validator.errors;
1377
- if (Array.isArray(errors)) {
1378
- result.diagnostics = errors.map(
1379
- (error) => createDiagnostic(
1380
- error.instancePath || "",
1381
- error.message || "Validation failed",
1382
- error.keyword || "unknown",
1383
- "ERROR",
1384
- "ajv"
1385
- )
1386
- );
1387
- }
1388
- metrics.counter("schema_validation_errors").inc();
1389
- } else {
1390
- metrics.counter("schema_validations").inc();
1391
- }
1392
- return result;
1393
- }
1394
- async function compileSchemaById(schemaId, registryOptions) {
1395
- try {
1396
- const registry = getSchemaRegistry(registryOptions);
1397
- const metadata = await registry.getSchema(schemaId);
1398
- const content = await readFile(metadata.path, "utf-8");
1399
- const aliases = [];
1400
- const normalizedRelativePath = metadata.relativePath.replace(/\\/g, "/");
1401
- if (normalizedRelativePath) {
1402
- aliases.push(
1403
- new URL(`crucible/${normalizedRelativePath}`, "https://schemas.fulmenhq.dev/").toString()
1404
- );
1405
- }
1406
- return compileSchema(content, { aliases });
1407
- } catch (error) {
1408
- metrics.counter("schema_validation_errors").inc();
1409
- throw error;
1410
- }
1411
- }
1412
- async function validateDataBySchemaId(data, schemaId, registryOptions) {
1413
- try {
1414
- const validator = await compileSchemaById(schemaId, registryOptions);
1415
- return validateData(data, validator);
1416
- } catch (error) {
1417
- metrics.counter("schema_validation_errors").inc();
1418
- throw error;
1419
- }
1420
- }
1421
- var ajvInstances, metaschemaReady, schemaCache;
1422
- var init_validator = __esm({
1423
- "src/schema/validator.ts"() {
1424
- init_telemetry();
1425
- init_ajv_formats();
1426
- init_errors();
1427
- init_registry2();
1428
- init_utils();
1429
- ajvInstances = /* @__PURE__ */ new Map();
1430
- metaschemaReady = /* @__PURE__ */ new Map();
1431
- schemaCache = /* @__PURE__ */ new Map();
1432
- }
1433
- });
1434
-
1435
- // src/foundry/errors.ts
1436
- var FoundryCatalogError;
1437
- var init_errors2 = __esm({
1438
- "src/foundry/errors.ts"() {
1439
- FoundryCatalogError = class _FoundryCatalogError extends Error {
1440
- constructor(message, catalog, cause) {
1441
- super(message);
1442
- this.catalog = catalog;
1443
- this.cause = cause;
1444
- this.name = "FoundryCatalogError";
1445
- if (Error.captureStackTrace) {
1446
- Error.captureStackTrace(this, _FoundryCatalogError);
1447
- }
1448
- }
1449
- static invalidSchema(catalog, details, cause) {
1450
- return new _FoundryCatalogError(
1451
- `Invalid schema in ${catalog} catalog: ${details}`,
1452
- catalog,
1453
- cause
1454
- );
1455
- }
1456
- static missingCatalog(catalog) {
1457
- return new _FoundryCatalogError(`Catalog ${catalog} not found or could not be loaded`, catalog);
1458
- }
1459
- static invalidPattern(patternId, details) {
1460
- return new _FoundryCatalogError(`Invalid pattern ${patternId}: ${details}`, "patterns");
1461
- }
1462
- static compilationError(patternId, details, cause) {
1463
- return new _FoundryCatalogError(
1464
- `Failed to compile pattern ${patternId}: ${details}`,
1465
- "patterns",
1466
- cause
1467
- );
1468
- }
1469
- };
1470
- }
1471
- });
1472
- async function loadCatalog(filePath, catalogName, schemaId) {
24
+ var SCHEMA_IDS = {
25
+ patterns: "library/foundry/v1.0.0/patterns",
26
+ httpStatuses: "library/foundry/v1.0.0/http-status-groups",
27
+ mimeTypes: "library/foundry/v1.0.0/mime-types",
28
+ countryCodes: "library/foundry/v1.0.0/country-codes"
29
+ };
30
+ async function loadCatalog(logicalPath, catalogName, schemaId) {
1473
31
  try {
32
+ ensureFoundryAssetsRegistered();
33
+ const resolver = getAssetResolver();
1474
34
  let content;
1475
- if (typeof Bun !== "undefined") {
1476
- try {
1477
- const file = Bun.file(filePath);
1478
- if (!await file.exists()) {
1479
- throw FoundryCatalogError.missingCatalog(catalogName);
1480
- }
1481
- content = await file.text();
1482
- } catch (error) {
1483
- if (error instanceof Error && error.message.includes("No such file")) {
1484
- throw FoundryCatalogError.missingCatalog(catalogName);
1485
- }
1486
- throw error;
1487
- }
1488
- } else {
1489
- try {
1490
- content = await readFile(filePath, "utf-8");
1491
- } catch (error) {
1492
- if (error.code === "ENOENT") {
1493
- throw FoundryCatalogError.missingCatalog(catalogName);
1494
- }
1495
- throw error;
1496
- }
35
+ try {
36
+ content = await resolver.read(logicalPath);
37
+ } catch {
38
+ throw FoundryCatalogError.missingCatalog(catalogName);
1497
39
  }
1498
40
  const data = parse(content);
1499
41
  const result = await validateDataBySchemaId(data, schemaId);
@@ -1509,25 +51,11 @@ async function loadCatalog(filePath, catalogName, schemaId) {
1509
51
  if (error instanceof FoundryCatalogError) {
1510
52
  throw error;
1511
53
  }
1512
- const err = error;
1513
- if (err.code === "ENOENT") {
1514
- throw FoundryCatalogError.missingCatalog(catalogName);
1515
- } else if (err.code === "EACCES") {
1516
- throw FoundryCatalogError.invalidSchema(
1517
- catalogName,
1518
- "Permission denied accessing catalog file",
1519
- err
1520
- );
1521
- } else if (err.code === "EISDIR") {
1522
- throw FoundryCatalogError.invalidSchema(
1523
- catalogName,
1524
- "Expected file but found directory",
1525
- err
1526
- );
1527
- } else if (err.code === "EMFILE" || err.code === "ENFILE") {
1528
- throw FoundryCatalogError.invalidSchema(catalogName, "Too many open files", err);
1529
- }
1530
- throw FoundryCatalogError.invalidSchema(catalogName, "Failed to load catalog", err);
54
+ throw FoundryCatalogError.invalidSchema(
55
+ catalogName,
56
+ `Failed to load catalog: ${error.message}`,
57
+ error
58
+ );
1531
59
  }
1532
60
  }
1533
61
  async function loadPatternCatalog() {
@@ -1559,29 +87,12 @@ async function loadAllCatalogs() {
1559
87
  ]);
1560
88
  return { patterns, httpStatuses, mimeTypes, countryCodes };
1561
89
  }
1562
- var __filename, __dirname2, SSOT_PATHS, SCHEMA_IDS;
1563
- var init_loader = __esm({
1564
- "src/foundry/loader.ts"() {
1565
- init_validator();
1566
- init_errors2();
1567
- __filename = fileURLToPath(import.meta.url);
1568
- __dirname2 = dirname(__filename);
1569
- SSOT_PATHS = {
1570
- patterns: join(__dirname2, "../../config/crucible-ts/library/foundry/patterns.yaml"),
1571
- httpStatuses: join(__dirname2, "../../config/crucible-ts/library/foundry/http-statuses.yaml"),
1572
- mimeTypes: join(__dirname2, "../../config/crucible-ts/library/foundry/mime-types.yaml"),
1573
- countryCodes: join(__dirname2, "../../config/crucible-ts/library/foundry/country-codes.yaml")
1574
- };
1575
- SCHEMA_IDS = {
1576
- patterns: "library/foundry/v1.0.0/patterns",
1577
- httpStatuses: "library/foundry/v1.0.0/http-status-groups",
1578
- mimeTypes: "library/foundry/v1.0.0/mime-types",
1579
- countryCodes: "library/foundry/v1.0.0/country-codes"
1580
- };
1581
- }
1582
- });
1583
90
 
1584
91
  // src/foundry/country-codes.ts
92
+ var catalogCache = null;
93
+ var alpha2Index = /* @__PURE__ */ new Map();
94
+ var alpha3Index = /* @__PURE__ */ new Map();
95
+ var numericIndex = /* @__PURE__ */ new Map();
1585
96
  function deepClone(obj) {
1586
97
  if (obj === null || typeof obj !== "object") {
1587
98
  return obj;
@@ -1653,673 +164,13 @@ function clearCountryCodeCache() {
1653
164
  alpha3Index.clear();
1654
165
  numericIndex.clear();
1655
166
  }
1656
- var catalogCache, alpha2Index, alpha3Index, numericIndex;
1657
- var init_country_codes = __esm({
1658
- "src/foundry/country-codes.ts"() {
1659
- init_loader();
1660
- catalogCache = null;
1661
- alpha2Index = /* @__PURE__ */ new Map();
1662
- alpha3Index = /* @__PURE__ */ new Map();
1663
- numericIndex = /* @__PURE__ */ new Map();
1664
- }
1665
- });
1666
167
 
1667
- // src/crucible/foundry/exitCodes.ts
1668
- function getExitCodeInfo(code) {
1669
- return exitCodeMetadata[code];
1670
- }
1671
- var exitCodes, exitCodeMetadata, EXIT_CODES_VERSION;
1672
- var init_exitCodes = __esm({
1673
- "src/crucible/foundry/exitCodes.ts"() {
1674
- exitCodes = {
1675
- // Standard Exit Codes (0-1)
1676
- // POSIX standard success and generic failure codes
1677
- EXIT_SUCCESS: 0,
1678
- EXIT_FAILURE: 1,
1679
- // Networking & Port Management (10-19)
1680
- // Network-related failures (ports, connectivity, etc.)
1681
- EXIT_PORT_IN_USE: 10,
1682
- EXIT_PORT_RANGE_EXHAUSTED: 11,
1683
- EXIT_INSTANCE_ALREADY_RUNNING: 12,
1684
- EXIT_NETWORK_UNREACHABLE: 13,
1685
- EXIT_CONNECTION_REFUSED: 14,
1686
- EXIT_CONNECTION_TIMEOUT: 15,
1687
- // Configuration & Validation (20-29)
1688
- // Configuration errors, validation failures, version mismatches
1689
- EXIT_CONFIG_INVALID: 20,
1690
- EXIT_MISSING_DEPENDENCY: 21,
1691
- EXIT_SSOT_VERSION_MISMATCH: 22,
1692
- EXIT_CONFIG_FILE_NOT_FOUND: 23,
1693
- EXIT_ENVIRONMENT_INVALID: 24,
1694
- // Runtime Errors (30-39)
1695
- // Errors during normal operation (health checks, database, etc.)
1696
- EXIT_HEALTH_CHECK_FAILED: 30,
1697
- EXIT_DATABASE_UNAVAILABLE: 31,
1698
- EXIT_EXTERNAL_SERVICE_UNAVAILABLE: 32,
1699
- EXIT_RESOURCE_EXHAUSTED: 33,
1700
- EXIT_OPERATION_TIMEOUT: 34,
1701
- // Command-Line Usage Errors (40-49)
1702
- // Invalid arguments, missing required flags, usage errors
1703
- EXIT_INVALID_ARGUMENT: 40,
1704
- EXIT_MISSING_REQUIRED_ARGUMENT: 41,
1705
- EXIT_USAGE: 64,
1706
- // Permissions & File Access (50-59)
1707
- // Permission denied, file not found, access errors
1708
- EXIT_PERMISSION_DENIED: 50,
1709
- EXIT_FILE_NOT_FOUND: 51,
1710
- EXIT_DIRECTORY_NOT_FOUND: 52,
1711
- EXIT_FILE_READ_ERROR: 53,
1712
- EXIT_FILE_WRITE_ERROR: 54,
1713
- // Data & Processing Errors (60-69)
1714
- // Data validation, parsing, transformation failures
1715
- EXIT_DATA_INVALID: 60,
1716
- EXIT_PARSE_ERROR: 61,
1717
- EXIT_TRANSFORMATION_FAILED: 62,
1718
- EXIT_DATA_CORRUPT: 63,
1719
- // Security & Authentication (70-79)
1720
- // Authentication failures, authorization errors, security violations
1721
- EXIT_AUTHENTICATION_FAILED: 70,
1722
- EXIT_AUTHORIZATION_FAILED: 71,
1723
- EXIT_SECURITY_VIOLATION: 72,
1724
- EXIT_CERTIFICATE_INVALID: 73,
1725
- // Observability & Monitoring (80-89)
1726
- // Observability infrastructure failures. Use when observability is CRITICAL to operation (e.g., monitoring agents, telemetry exporters). For workhorses where observability is auxiliary: - Log warning and continue (don't exit) - Emit degraded health status - Only exit if explicitly configured (fail_on_observability_error: true)
1727
- EXIT_METRICS_UNAVAILABLE: 80,
1728
- EXIT_TRACING_FAILED: 81,
1729
- EXIT_LOGGING_FAILED: 82,
1730
- EXIT_ALERT_SYSTEM_FAILED: 83,
1731
- EXIT_STRUCTURED_LOGGING_FAILED: 84,
1732
- // Testing & Validation (91-99)
1733
- // Test execution outcomes and validation failures. NOTE: Test harnesses MUST use EXIT_SUCCESS (0) for successful test runs per ecosystem conventions (pytest, Go testing, Jest all use 0 for success). Codes in this category are for FAILURES and exceptional conditions only.
1734
- EXIT_TEST_FAILURE: 91,
1735
- EXIT_TEST_ERROR: 92,
1736
- EXIT_TEST_INTERRUPTED: 93,
1737
- EXIT_TEST_USAGE_ERROR: 94,
1738
- EXIT_TEST_NO_TESTS_COLLECTED: 95,
1739
- EXIT_COVERAGE_THRESHOLD_NOT_MET: 96,
1740
- // Shell & Process Control (124-127)
1741
- // Exit codes from shell conventions and process control utilities (timeout, exec)
1742
- EXIT_TIMEOUT: 124,
1743
- EXIT_TIMEOUT_INTERNAL: 125,
1744
- EXIT_CANNOT_EXECUTE: 126,
1745
- EXIT_NOT_FOUND: 127,
1746
- // Signal-Induced Exits (128-165)
1747
- // Process terminated by Unix signals (128+N pattern per POSIX). Signal codes follow Linux numbering; macOS/FreeBSD differ for SIGUSR1/SIGUSR2. For full signal semantics, see config/library/foundry/signals.yaml. For signal handling patterns, see docs/standards/library/modules/signal-handling.md.
1748
- EXIT_SIGNAL_HUP: 129,
1749
- EXIT_SIGNAL_INT: 130,
1750
- EXIT_SIGNAL_QUIT: 131,
1751
- EXIT_SIGNAL_KILL: 137,
1752
- EXIT_SIGNAL_PIPE: 141,
1753
- EXIT_SIGNAL_ALRM: 142,
1754
- EXIT_SIGNAL_TERM: 143,
1755
- EXIT_SIGNAL_USR1: 138,
1756
- EXIT_SIGNAL_USR2: 140
1757
- };
1758
- exitCodeMetadata = {
1759
- 0: {
1760
- code: 0,
1761
- name: "EXIT_SUCCESS",
1762
- description: "Successful execution",
1763
- context: "Command completed without errors",
1764
- category: "standard"
1765
- },
1766
- 1: {
1767
- code: 1,
1768
- name: "EXIT_FAILURE",
1769
- description: "Generic failure (unspecified error)",
1770
- context: "Use when no more specific exit code applies",
1771
- category: "standard"
1772
- },
1773
- 10: {
1774
- code: 10,
1775
- name: "EXIT_PORT_IN_USE",
1776
- description: "Specified port is already in use",
1777
- context: "Server startup when port unavailable and fail_if_unavailable strategy",
1778
- category: "networking"
1779
- },
1780
- 11: {
1781
- code: 11,
1782
- name: "EXIT_PORT_RANGE_EXHAUSTED",
1783
- description: "No available ports in configured range",
1784
- context: "Server startup when all ports in environment range occupied",
1785
- category: "networking"
1786
- },
1787
- 12: {
1788
- code: 12,
1789
- name: "EXIT_INSTANCE_ALREADY_RUNNING",
1790
- description: "Another instance already running on target port",
1791
- context: "Server startup when PID registry shows active process on port",
1792
- category: "networking"
1793
- },
1794
- 13: {
1795
- code: 13,
1796
- name: "EXIT_NETWORK_UNREACHABLE",
1797
- description: "Network destination unreachable",
1798
- context: "Client connections, health checks, external service validation",
1799
- category: "networking"
1800
- },
1801
- 14: {
1802
- code: 14,
1803
- name: "EXIT_CONNECTION_REFUSED",
1804
- description: "Connection refused by remote host",
1805
- context: "Database connections, API endpoints, upstream services",
1806
- category: "networking"
1807
- },
1808
- 15: {
1809
- code: 15,
1810
- name: "EXIT_CONNECTION_TIMEOUT",
1811
- description: "Connection attempt timed out",
1812
- context: "Slow networks, unresponsive services, firewall blocks",
1813
- category: "networking"
1814
- },
1815
- 20: {
1816
- code: 20,
1817
- name: "EXIT_CONFIG_INVALID",
1818
- description: "Configuration file failed validation",
1819
- context: "Startup validation, schema mismatches, invalid YAML/JSON",
1820
- category: "configuration",
1821
- retryHint: "no_retry"
1822
- },
1823
- 21: {
1824
- code: 21,
1825
- name: "EXIT_MISSING_DEPENDENCY",
1826
- description: "Required dependency not found",
1827
- context: "Missing binaries, libraries, or runtime requirements",
1828
- category: "configuration",
1829
- retryHint: "investigate"
1830
- },
1831
- 22: {
1832
- code: 22,
1833
- name: "EXIT_SSOT_VERSION_MISMATCH",
1834
- description: "SSOT (Crucible) version incompatible",
1835
- context: "Helper library detects unsupported Crucible version",
1836
- category: "configuration",
1837
- retryHint: "no_retry"
1838
- },
1839
- 23: {
1840
- code: 23,
1841
- name: "EXIT_CONFIG_FILE_NOT_FOUND",
1842
- description: "Required configuration file not found",
1843
- context: "Explicitly specified config path doesn't exist",
1844
- category: "configuration"
1845
- },
1846
- 24: {
1847
- code: 24,
1848
- name: "EXIT_ENVIRONMENT_INVALID",
1849
- description: "Invalid or unsupported environment specification",
1850
- context: "Unknown environment name, missing environment config",
1851
- category: "configuration"
1852
- },
1853
- 30: {
1854
- code: 30,
1855
- name: "EXIT_HEALTH_CHECK_FAILED",
1856
- description: "Health check endpoint returned non-healthy status",
1857
- context: "Startup health validation, readiness probes",
1858
- category: "runtime",
1859
- retryHint: "retry"
1860
- },
1861
- 31: {
1862
- code: 31,
1863
- name: "EXIT_DATABASE_UNAVAILABLE",
1864
- description: "Database connection failed or unavailable",
1865
- context: "Startup connection checks, critical query failures",
1866
- category: "runtime",
1867
- retryHint: "retry"
1868
- },
1869
- 32: {
1870
- code: 32,
1871
- name: "EXIT_EXTERNAL_SERVICE_UNAVAILABLE",
1872
- description: "Required external service unavailable",
1873
- context: "API dependencies, message queues, cache servers",
1874
- category: "runtime"
1875
- },
1876
- 33: {
1877
- code: 33,
1878
- name: "EXIT_RESOURCE_EXHAUSTED",
1879
- description: "System resources exhausted (memory, disk, file descriptors)",
1880
- context: "Out-of-memory, disk full, too many open files",
1881
- category: "runtime",
1882
- retryHint: "investigate"
1883
- },
1884
- 34: {
1885
- code: 34,
1886
- name: "EXIT_OPERATION_TIMEOUT",
1887
- description: "Operation exceeded timeout threshold",
1888
- context: "Long-running tasks, async operations, batch processing",
1889
- category: "runtime",
1890
- retryHint: "retry"
1891
- },
1892
- 40: {
1893
- code: 40,
1894
- name: "EXIT_INVALID_ARGUMENT",
1895
- description: "Invalid command-line argument or flag value",
1896
- context: "Type errors, out-of-range values, malformed input",
1897
- category: "usage"
1898
- },
1899
- 41: {
1900
- code: 41,
1901
- name: "EXIT_MISSING_REQUIRED_ARGUMENT",
1902
- description: "Required command-line argument not provided",
1903
- context: "Missing --config, --port, or other required flags",
1904
- category: "usage"
1905
- },
1906
- 64: {
1907
- code: 64,
1908
- name: "EXIT_USAGE",
1909
- description: "Command-line usage error",
1910
- context: "BSD sysexits.h EX_USAGE - wrong number of arguments, bad syntax",
1911
- category: "usage",
1912
- bsdEquivalent: "EX_USAGE"
1913
- },
1914
- 50: {
1915
- code: 50,
1916
- name: "EXIT_PERMISSION_DENIED",
1917
- description: "Insufficient permissions for operation",
1918
- context: "File access, port binding (<1024), privileged operations",
1919
- category: "permissions"
1920
- },
1921
- 51: {
1922
- code: 51,
1923
- name: "EXIT_FILE_NOT_FOUND",
1924
- description: "Required file not found",
1925
- context: "Assets, templates, data files (not config - use 23)",
1926
- category: "permissions"
1927
- },
1928
- 52: {
1929
- code: 52,
1930
- name: "EXIT_DIRECTORY_NOT_FOUND",
1931
- description: "Required directory not found",
1932
- context: "State directories, log paths, data directories",
1933
- category: "permissions"
1934
- },
1935
- 53: {
1936
- code: 53,
1937
- name: "EXIT_FILE_READ_ERROR",
1938
- description: "Error reading file",
1939
- context: "Corrupt files, I/O errors, encoding issues",
1940
- category: "permissions"
1941
- },
1942
- 54: {
1943
- code: 54,
1944
- name: "EXIT_FILE_WRITE_ERROR",
1945
- description: "Error writing file",
1946
- context: "Disk full, read-only filesystem, permission errors",
1947
- category: "permissions"
1948
- },
1949
- 60: {
1950
- code: 60,
1951
- name: "EXIT_DATA_INVALID",
1952
- description: "Input data failed validation",
1953
- context: "Schema validation, business rule violations",
1954
- category: "data"
1955
- },
1956
- 61: {
1957
- code: 61,
1958
- name: "EXIT_PARSE_ERROR",
1959
- description: "Error parsing input data",
1960
- context: "Malformed JSON/YAML/XML, syntax errors",
1961
- category: "data"
1962
- },
1963
- 62: {
1964
- code: 62,
1965
- name: "EXIT_TRANSFORMATION_FAILED",
1966
- description: "Data transformation or conversion failed",
1967
- context: "Type conversions, format transformations, encoding changes",
1968
- category: "data"
1969
- },
1970
- 63: {
1971
- code: 63,
1972
- name: "EXIT_DATA_CORRUPT",
1973
- description: "Data corruption detected",
1974
- context: "Checksum failures, integrity violations",
1975
- category: "data"
1976
- },
1977
- 70: {
1978
- code: 70,
1979
- name: "EXIT_AUTHENTICATION_FAILED",
1980
- description: "Authentication failed",
1981
- context: "Invalid credentials, expired tokens, auth service unavailable",
1982
- category: "security"
1983
- },
1984
- 71: {
1985
- code: 71,
1986
- name: "EXIT_AUTHORIZATION_FAILED",
1987
- description: "Authorization failed (authenticated but insufficient permissions)",
1988
- context: "RBAC failures, scope violations, resource access denied",
1989
- category: "security"
1990
- },
1991
- 72: {
1992
- code: 72,
1993
- name: "EXIT_SECURITY_VIOLATION",
1994
- description: "Security policy violation detected",
1995
- context: "Suspicious activity, rate limit exceeded, IP blocklist",
1996
- category: "security"
1997
- },
1998
- 73: {
1999
- code: 73,
2000
- name: "EXIT_CERTIFICATE_INVALID",
2001
- description: "TLS/SSL certificate validation failed",
2002
- context: "Expired certs, untrusted CAs, hostname mismatches",
2003
- category: "security",
2004
- bsdEquivalent: "EX_PROTOCOL"
2005
- },
2006
- 80: {
2007
- code: 80,
2008
- name: "EXIT_METRICS_UNAVAILABLE",
2009
- description: "Metrics endpoint or collection system unavailable",
2010
- context: "Use for observability-focused tools (Prometheus exporters, StatsD agents).\nWorkhorses SHOULD log warning and continue unless configured to fail-fast.\n",
2011
- category: "observability"
2012
- },
2013
- 81: {
2014
- code: 81,
2015
- name: "EXIT_TRACING_FAILED",
2016
- description: "Distributed tracing system unavailable",
2017
- context: "OTLP exporter failed, Jaeger collector unreachable",
2018
- category: "observability"
2019
- },
2020
- 82: {
2021
- code: 82,
2022
- name: "EXIT_LOGGING_FAILED",
2023
- description: "Logging system unavailable or misconfigured",
2024
- context: "Log aggregator unreachable, log file unwritable",
2025
- category: "observability"
2026
- },
2027
- 83: {
2028
- code: 83,
2029
- name: "EXIT_ALERT_SYSTEM_FAILED",
2030
- description: "Alerting system unavailable",
2031
- context: "PagerDuty API failed, Slack webhook unreachable",
2032
- category: "observability"
2033
- },
2034
- 84: {
2035
- code: 84,
2036
- name: "EXIT_STRUCTURED_LOGGING_FAILED",
2037
- description: "Structured logging system unavailable",
2038
- context: "JSON log aggregator unreachable, log schema validation failed",
2039
- category: "observability"
2040
- },
2041
- 91: {
2042
- code: 91,
2043
- name: "EXIT_TEST_FAILURE",
2044
- description: "One or more tests failed",
2045
- context: "Test assertions failed, expected behavior not met.\nMaps to pytest exit code 1, Go test failure, Jest failure.\n",
2046
- category: "testing"
2047
- },
2048
- 92: {
2049
- code: 92,
2050
- name: "EXIT_TEST_ERROR",
2051
- description: "Test execution error (not test failure)",
2052
- context: "Test setup failed, fixture unavailable, test harness error.\nMaps to pytest exit code 3 (internal error).\n",
2053
- category: "testing"
2054
- },
2055
- 93: {
2056
- code: 93,
2057
- name: "EXIT_TEST_INTERRUPTED",
2058
- description: "Test run interrupted by user or system",
2059
- context: "Ctrl+C during tests, system signal, user cancellation.\nMaps to pytest exit code 2.\n",
2060
- category: "testing"
2061
- },
2062
- 94: {
2063
- code: 94,
2064
- name: "EXIT_TEST_USAGE_ERROR",
2065
- description: "Test command usage error",
2066
- context: "Invalid test arguments, bad configuration.\nMaps to pytest exit code 4.\n",
2067
- category: "testing"
2068
- },
2069
- 95: {
2070
- code: 95,
2071
- name: "EXIT_TEST_NO_TESTS_COLLECTED",
2072
- description: "No tests found or all tests skipped",
2073
- context: "Empty test suite, all tests deselected or skipped.\nMaps to pytest exit code 5.\n",
2074
- category: "testing"
2075
- },
2076
- 96: {
2077
- code: 96,
2078
- name: "EXIT_COVERAGE_THRESHOLD_NOT_MET",
2079
- description: "Test coverage below required threshold",
2080
- context: "Code coverage validation, quality gate failure",
2081
- category: "testing"
2082
- },
2083
- 124: {
2084
- code: 124,
2085
- name: "EXIT_TIMEOUT",
2086
- description: "Command timed out before completion",
2087
- context: "GNU timeout or similar utility terminated command after deadline",
2088
- category: "shell"
2089
- },
2090
- 125: {
2091
- code: 125,
2092
- name: "EXIT_TIMEOUT_INTERNAL",
2093
- description: "Timeout utility itself failed",
2094
- context: "Error in timeout tool before or during command execution (not command failure)",
2095
- category: "shell"
2096
- },
2097
- 126: {
2098
- code: 126,
2099
- name: "EXIT_CANNOT_EXECUTE",
2100
- description: "Command found but could not be executed",
2101
- context: "Permission denied, not executable, exec format error",
2102
- category: "shell",
2103
- bsdEquivalent: "EX_NOPERM (partial)"
2104
- },
2105
- 127: {
2106
- code: 127,
2107
- name: "EXIT_NOT_FOUND",
2108
- description: "Command not found",
2109
- context: "Executable not found in PATH or specified path does not exist",
2110
- category: "shell",
2111
- bsdEquivalent: "EX_UNAVAILABLE (partial)"
2112
- },
2113
- 129: {
2114
- code: 129,
2115
- name: "EXIT_SIGNAL_HUP",
2116
- description: "Hangup signal (SIGHUP) - config reload via restart",
2117
- context: "Config reload via restart-based pattern (mandatory schema validation).\nProcess exits with 129, supervisor restarts with new config.\nSee signals.yaml for reload behavior definition.",
2118
- category: "signals",
2119
- bsdEquivalent: "128 + 1"
2120
- },
2121
- 130: {
2122
- code: 130,
2123
- name: "EXIT_SIGNAL_INT",
2124
- description: "Interrupt signal (SIGINT) - user interrupt with Ctrl+C double-tap",
2125
- context: "Ctrl+C pressed. First tap initiates graceful shutdown, second within 2s forces immediate exit.\nSame exit code (130) for both graceful and force modes.\nSee signals.yaml for double-tap behavior definition.",
2126
- category: "signals",
2127
- bsdEquivalent: "128 + 2"
2128
- },
2129
- 131: {
2130
- code: 131,
2131
- name: "EXIT_SIGNAL_QUIT",
2132
- description: "Quit signal (SIGQUIT) - immediate exit",
2133
- context: "Ctrl+\\ on Unix, Ctrl+Break on Windows. Immediate termination without cleanup.\nUse for emergency shutdown or debugging (core dumps).",
2134
- category: "signals",
2135
- bsdEquivalent: "128 + 3"
2136
- },
2137
- 137: {
2138
- code: 137,
2139
- name: "EXIT_SIGNAL_KILL",
2140
- description: "Kill signal (SIGKILL)",
2141
- context: "Forceful termination, non-graceful shutdown (not catchable)",
2142
- category: "signals",
2143
- bsdEquivalent: "128 + 9",
2144
- pythonNote: "Cannot be caught in Python (OS-level)"
2145
- },
2146
- 141: {
2147
- code: 141,
2148
- name: "EXIT_SIGNAL_PIPE",
2149
- description: "Broken pipe (SIGPIPE) - observe only",
2150
- context: "Writing to closed pipe/socket. Fulmen default is observe_only (log + graceful exit).\nApplications may override to ignore for network services.\nSee signals.yaml for SIGPIPE handling guidance.",
2151
- category: "signals",
2152
- bsdEquivalent: "128 + 13",
2153
- pythonNote: "Raised as BrokenPipeError exception"
2154
- },
2155
- 142: {
2156
- code: 142,
2157
- name: "EXIT_SIGNAL_ALRM",
2158
- description: "Alarm signal (SIGALRM) - watchdog timeout",
2159
- context: "Watchdog timer expired. Treat as timeout-induced exit.\nWatchdog pattern out of scope for v1.0.0 module implementations.",
2160
- category: "signals",
2161
- bsdEquivalent: "128 + 14",
2162
- pythonNote: "Supported by signal module, rarely used in practice"
2163
- },
2164
- 143: {
2165
- code: 143,
2166
- name: "EXIT_SIGNAL_TERM",
2167
- description: "Termination signal (SIGTERM) - graceful shutdown",
2168
- context: "Graceful shutdown requested by container orchestrator or process supervisor.\nStandard 30-second timeout for cleanup. Applications run cleanup handlers before exit.\nSee signals.yaml for graceful shutdown behavior definition.",
2169
- category: "signals",
2170
- bsdEquivalent: "128 + 15",
2171
- pythonNote: "Default signal for graceful shutdown"
2172
- },
2173
- 138: {
2174
- code: 138,
2175
- name: "EXIT_SIGNAL_USR1",
2176
- description: "User-defined signal 1 (SIGUSR1) - custom handler",
2177
- context: "Application-specific signal (e.g., reopen logs, dump stats, trigger profiling).\nApplications register custom handlers. Exit code 138 on Linux (128+10).\nPlatform differences: macOS/FreeBSD use signal 30 (exit 158), not 10.",
2178
- category: "signals"
2179
- },
2180
- 140: {
2181
- code: 140,
2182
- name: "EXIT_SIGNAL_USR2",
2183
- description: "User-defined signal 2 (SIGUSR2) - custom handler",
2184
- context: "Application-specific signal (e.g., toggle debug mode, rotate credentials).\nApplications register custom handlers. Exit code 140 on Linux (128+12).\nPlatform differences: macOS/FreeBSD use signal 31 (exit 159), not 12.",
2185
- category: "signals"
2186
- }
2187
- };
2188
- EXIT_CODES_VERSION = "v1.0.0";
2189
- }
2190
- });
2191
-
2192
- // src/foundry/exit-codes/capabilities.ts
2193
- function supportsSignalExitCodes() {
2194
- return process.platform !== "win32";
2195
- }
2196
- function getPlatform() {
2197
- return process.platform;
2198
- }
2199
- function isWindows() {
2200
- return process.platform === "win32";
2201
- }
2202
- function isPOSIX() {
2203
- return !isWindows();
2204
- }
2205
- function getPlatformCapabilities() {
2206
- return {
2207
- platform: getPlatform(),
2208
- supportsSignalExitCodes: supportsSignalExitCodes(),
2209
- isPOSIX: isPOSIX(),
2210
- isWindows: isWindows()
2211
- };
2212
- }
2213
- var init_capabilities = __esm({
2214
- "src/foundry/exit-codes/capabilities.ts"() {
2215
- }
2216
- });
2217
-
2218
- // src/foundry/exit-codes/simplified.ts
2219
- function mapExitCodeToSimplified(code, mode = "basic" /* BASIC */) {
2220
- if (code === 0) {
2221
- return 0;
2222
- }
2223
- if (mode === "basic" /* BASIC */) {
2224
- return 1;
2225
- }
2226
- const info = exitCodeMetadata[code];
2227
- if (!info) {
2228
- return 3;
2229
- }
2230
- if (info.retryHint === "retry") {
2231
- return 1;
2232
- }
2233
- if (info.retryHint === "no_retry") {
2234
- return 2;
2235
- }
2236
- if (info.retryHint === "investigate") {
2237
- return 3;
2238
- }
2239
- switch (info.category) {
2240
- case "configuration":
2241
- case "usage":
2242
- return 2;
2243
- case "networking":
2244
- case "runtime":
2245
- return 1;
2246
- case "permissions":
2247
- case "data":
2248
- return 2;
2249
- case "security":
2250
- return 3;
2251
- case "observability":
2252
- return 1;
2253
- case "testing":
2254
- return 1;
2255
- case "signals":
2256
- return 3;
2257
- default:
2258
- return 1;
2259
- }
2260
- }
2261
- function getSimplifiedCodes(mode) {
2262
- switch (mode) {
2263
- case "basic" /* BASIC */:
2264
- return [0, 1];
2265
- case "severity" /* SEVERITY */:
2266
- return [0, 1, 2, 3];
2267
- default:
2268
- return [0, 1];
2269
- }
2270
- }
2271
- function getSimplifiedCodeDescription(code, mode) {
2272
- if (mode === "basic" /* BASIC */) {
2273
- switch (code) {
2274
- case 0:
2275
- return "Success";
2276
- case 1:
2277
- return "Failure";
2278
- default:
2279
- return "Unknown";
2280
- }
2281
- }
2282
- if (mode === "severity" /* SEVERITY */) {
2283
- switch (code) {
2284
- case 0:
2285
- return "Success";
2286
- case 1:
2287
- return "Recoverable error (retry possible)";
2288
- case 2:
2289
- return "Configuration error (fix config, don't retry)";
2290
- case 3:
2291
- return "Fatal error (investigate required)";
2292
- default:
2293
- return "Unknown";
2294
- }
2295
- }
2296
- return "Unknown mode";
2297
- }
2298
- var SimplifiedMode;
2299
- var init_simplified = __esm({
2300
- "src/foundry/exit-codes/simplified.ts"() {
2301
- init_exitCodes();
2302
- SimplifiedMode = /* @__PURE__ */ ((SimplifiedMode2) => {
2303
- SimplifiedMode2["BASIC"] = "basic";
2304
- SimplifiedMode2["SEVERITY"] = "severity";
2305
- return SimplifiedMode2;
2306
- })(SimplifiedMode || {});
2307
- }
2308
- });
2309
-
2310
- // src/foundry/exit-codes/index.ts
2311
- var init_exit_codes = __esm({
2312
- "src/foundry/exit-codes/index.ts"() {
2313
- init_exitCodes();
2314
- init_capabilities();
2315
- init_simplified();
2316
- }
2317
- });
2318
-
2319
- // src/foundry/http-statuses.ts
2320
- function deepClone2(obj) {
2321
- if (obj === null || typeof obj !== "object") {
2322
- return obj;
168
+ // src/foundry/http-statuses.ts
169
+ var catalogCache2 = null;
170
+ var statusCodeIndex = /* @__PURE__ */ new Map();
171
+ function deepClone2(obj) {
172
+ if (obj === null || typeof obj !== "object") {
173
+ return obj;
2323
174
  }
2324
175
  if (Array.isArray(obj)) {
2325
176
  return obj.map((item) => deepClone2(item));
@@ -2392,16 +243,109 @@ function clearHttpStatusCache() {
2392
243
  catalogCache2 = null;
2393
244
  statusCodeIndex.clear();
2394
245
  }
2395
- var catalogCache2, statusCodeIndex;
2396
- var init_http_statuses = __esm({
2397
- "src/foundry/http-statuses.ts"() {
2398
- init_loader();
2399
- catalogCache2 = null;
2400
- statusCodeIndex = /* @__PURE__ */ new Map();
2401
- }
2402
- });
2403
246
 
2404
247
  // src/foundry/magic-numbers.ts
248
+ var UTF8_BOM = [239, 187, 191];
249
+ var XML_PATTERNS = [
250
+ {
251
+ offset: 0,
252
+ bytes: [60, 63, 120, 109, 108],
253
+ description: "XML declaration: <?xml"
254
+ },
255
+ {
256
+ offset: 0,
257
+ bytes: [239, 187, 191, 60, 63, 120, 109, 108],
258
+ description: "XML with UTF-8 BOM: BOM + <?xml"
259
+ }
260
+ ];
261
+ var JSON_PATTERNS = [
262
+ {
263
+ offset: 0,
264
+ bytes: [123],
265
+ description: "JSON object start: {"
266
+ },
267
+ {
268
+ offset: 0,
269
+ bytes: [91],
270
+ description: "JSON array start: ["
271
+ },
272
+ {
273
+ offset: 0,
274
+ bytes: [239, 187, 191, 123],
275
+ description: "JSON object with BOM: BOM + {"
276
+ },
277
+ {
278
+ offset: 0,
279
+ bytes: [239, 187, 191, 91],
280
+ description: "JSON array with BOM: BOM + ["
281
+ }
282
+ ];
283
+ var YAML_PATTERNS = [
284
+ {
285
+ offset: 0,
286
+ bytes: [45, 45, 45],
287
+ description: "YAML document marker: ---"
288
+ },
289
+ {
290
+ offset: 0,
291
+ bytes: [37, 89, 65, 77, 76],
292
+ description: "YAML directive: %YAML"
293
+ }
294
+ ];
295
+ var NDJSON_PATTERNS = [];
296
+ var CSV_PATTERNS = [];
297
+ var PROTOBUF_PATTERNS = [];
298
+ var TEXT_PATTERNS = [];
299
+ var MAGIC_NUMBER_DATABASE = [
300
+ {
301
+ mimeType: "application/xml",
302
+ patterns: XML_PATTERNS,
303
+ priority: 10,
304
+ matchStrategy: "exact"
305
+ },
306
+ {
307
+ mimeType: "application/x-ndjson",
308
+ patterns: NDJSON_PATTERNS,
309
+ priority: 9,
310
+ matchStrategy: "heuristic"
311
+ },
312
+ {
313
+ mimeType: "application/json",
314
+ patterns: JSON_PATTERNS,
315
+ priority: 8,
316
+ matchStrategy: "exact"
317
+ },
318
+ {
319
+ mimeType: "application/yaml",
320
+ patterns: YAML_PATTERNS,
321
+ priority: 7,
322
+ matchStrategy: "exact"
323
+ },
324
+ {
325
+ mimeType: "application/yaml",
326
+ patterns: [],
327
+ priority: 6.5,
328
+ matchStrategy: "heuristic"
329
+ },
330
+ {
331
+ mimeType: "text/csv",
332
+ patterns: CSV_PATTERNS,
333
+ priority: 6,
334
+ matchStrategy: "heuristic"
335
+ },
336
+ {
337
+ mimeType: "application/x-protobuf",
338
+ patterns: PROTOBUF_PATTERNS,
339
+ priority: 5,
340
+ matchStrategy: "heuristic"
341
+ },
342
+ {
343
+ mimeType: "text/plain",
344
+ patterns: TEXT_PATTERNS,
345
+ priority: 1,
346
+ matchStrategy: "heuristic"
347
+ }
348
+ ];
2405
349
  function hasBOM(buffer) {
2406
350
  if (buffer.length < 3) return false;
2407
351
  return buffer[0] === UTF8_BOM[0] && buffer[1] === UTF8_BOM[1] && buffer[2] === UTF8_BOM[2];
@@ -2409,313 +353,207 @@ function hasBOM(buffer) {
2409
353
  function getBOMOffset(buffer) {
2410
354
  return hasBOM(buffer) ? 3 : 0;
2411
355
  }
2412
- var UTF8_BOM, XML_PATTERNS, JSON_PATTERNS, YAML_PATTERNS, NDJSON_PATTERNS, CSV_PATTERNS, PROTOBUF_PATTERNS, TEXT_PATTERNS, MAGIC_NUMBER_DATABASE;
2413
- var init_magic_numbers = __esm({
2414
- "src/foundry/magic-numbers.ts"() {
2415
- UTF8_BOM = [239, 187, 191];
2416
- XML_PATTERNS = [
2417
- {
2418
- offset: 0,
2419
- bytes: [60, 63, 120, 109, 108],
2420
- description: "XML declaration: <?xml"
2421
- },
2422
- {
2423
- offset: 0,
2424
- bytes: [239, 187, 191, 60, 63, 120, 109, 108],
2425
- description: "XML with UTF-8 BOM: BOM + <?xml"
2426
- }
2427
- ];
2428
- JSON_PATTERNS = [
2429
- {
2430
- offset: 0,
2431
- bytes: [123],
2432
- description: "JSON object start: {"
2433
- },
2434
- {
2435
- offset: 0,
2436
- bytes: [91],
2437
- description: "JSON array start: ["
2438
- },
2439
- {
2440
- offset: 0,
2441
- bytes: [239, 187, 191, 123],
2442
- description: "JSON object with BOM: BOM + {"
2443
- },
2444
- {
2445
- offset: 0,
2446
- bytes: [239, 187, 191, 91],
2447
- description: "JSON array with BOM: BOM + ["
2448
- }
2449
- ];
2450
- YAML_PATTERNS = [
2451
- {
2452
- offset: 0,
2453
- bytes: [45, 45, 45],
2454
- description: "YAML document marker: ---"
2455
- },
2456
- {
2457
- offset: 0,
2458
- bytes: [37, 89, 65, 77, 76],
2459
- description: "YAML directive: %YAML"
2460
- }
2461
- ];
2462
- NDJSON_PATTERNS = [];
2463
- CSV_PATTERNS = [];
2464
- PROTOBUF_PATTERNS = [];
2465
- TEXT_PATTERNS = [];
2466
- MAGIC_NUMBER_DATABASE = [
2467
- {
2468
- mimeType: "application/xml",
2469
- patterns: XML_PATTERNS,
2470
- priority: 10,
2471
- matchStrategy: "exact"
2472
- },
2473
- {
2474
- mimeType: "application/x-ndjson",
2475
- patterns: NDJSON_PATTERNS,
2476
- priority: 9,
2477
- matchStrategy: "heuristic"
2478
- },
2479
- {
2480
- mimeType: "application/json",
2481
- patterns: JSON_PATTERNS,
2482
- priority: 8,
2483
- matchStrategy: "exact"
2484
- },
2485
- {
2486
- mimeType: "application/yaml",
2487
- patterns: YAML_PATTERNS,
2488
- priority: 7,
2489
- matchStrategy: "exact"
2490
- },
2491
- {
2492
- mimeType: "application/yaml",
2493
- patterns: [],
2494
- priority: 6.5,
2495
- matchStrategy: "heuristic"
2496
- },
2497
- {
2498
- mimeType: "text/csv",
2499
- patterns: CSV_PATTERNS,
2500
- priority: 6,
2501
- matchStrategy: "heuristic"
2502
- },
2503
- {
2504
- mimeType: "application/x-protobuf",
2505
- patterns: PROTOBUF_PATTERNS,
2506
- priority: 5,
2507
- matchStrategy: "heuristic"
2508
- },
2509
- {
2510
- mimeType: "text/plain",
2511
- patterns: TEXT_PATTERNS,
2512
- priority: 1,
2513
- matchStrategy: "heuristic"
2514
- }
2515
- ];
2516
- }
2517
- });
2518
356
 
2519
357
  // src/foundry/detector.ts
2520
- function createDetector(catalog) {
2521
- return new MimeTypeDetector(MAGIC_NUMBER_DATABASE, catalog);
2522
- }
2523
- var MimeTypeDetector;
2524
- var init_detector = __esm({
2525
- "src/foundry/detector.ts"() {
2526
- init_magic_numbers();
2527
- MimeTypeDetector = class {
2528
- patterns;
2529
- catalog;
2530
- constructor(patterns, catalog) {
2531
- this.patterns = [...patterns].sort((a, b) => b.priority - a.priority);
2532
- this.catalog = catalog;
2533
- }
2534
- /**
2535
- * Detect MIME type from buffer content
2536
- */
2537
- detect(buffer, _options = {}) {
2538
- const offset = getBOMOffset(buffer);
2539
- const workingBuffer = offset > 0 ? buffer.subarray(offset) : buffer;
2540
- for (const pattern of this.patterns) {
2541
- if (pattern.matchStrategy === "exact" && this.matchPattern(workingBuffer, pattern)) {
2542
- return this.catalog.get(pattern.mimeType) || null;
2543
- }
2544
- if (pattern.matchStrategy === "heuristic" && this.matchHeuristic(workingBuffer, pattern)) {
2545
- return this.catalog.get(pattern.mimeType) || null;
2546
- }
2547
- }
2548
- return null;
2549
- }
2550
- /**
2551
- * Match buffer against a pattern signature
2552
- */
2553
- matchPattern(buffer, signature) {
2554
- for (const pattern of signature.patterns) {
2555
- if (this.matchBytes(buffer, pattern)) {
2556
- return true;
2557
- }
2558
- }
2559
- return false;
358
+ var MimeTypeDetector = class {
359
+ patterns;
360
+ catalog;
361
+ constructor(patterns, catalog) {
362
+ this.patterns = [...patterns].sort((a, b) => b.priority - a.priority);
363
+ this.catalog = catalog;
364
+ }
365
+ /**
366
+ * Detect MIME type from buffer content
367
+ */
368
+ detect(buffer, _options = {}) {
369
+ const offset = getBOMOffset(buffer);
370
+ const workingBuffer = offset > 0 ? buffer.subarray(offset) : buffer;
371
+ for (const pattern of this.patterns) {
372
+ if (pattern.matchStrategy === "exact" && this.matchPattern(workingBuffer, pattern)) {
373
+ return this.catalog.get(pattern.mimeType) || null;
374
+ }
375
+ if (pattern.matchStrategy === "heuristic" && this.matchHeuristic(workingBuffer, pattern)) {
376
+ return this.catalog.get(pattern.mimeType) || null;
2560
377
  }
2561
- /**
2562
- * Match bytes at specified offset with optional masking
2563
- */
2564
- matchBytes(buffer, pattern) {
2565
- const { offset, bytes, mask } = pattern;
2566
- if (buffer.length < offset + bytes.length) {
2567
- return false;
2568
- }
2569
- for (let i = 0; i < bytes.length; i++) {
2570
- const bufferByte = buffer[offset + i];
2571
- const patternByte = bytes[i];
2572
- const maskByte = mask ? mask[i] : 255;
2573
- if ((bufferByte & maskByte) !== (patternByte & maskByte)) {
2574
- return false;
2575
- }
2576
- }
378
+ }
379
+ return null;
380
+ }
381
+ /**
382
+ * Match buffer against a pattern signature
383
+ */
384
+ matchPattern(buffer, signature) {
385
+ for (const pattern of signature.patterns) {
386
+ if (this.matchBytes(buffer, pattern)) {
2577
387
  return true;
2578
388
  }
2579
- /**
2580
- * Heuristic detection for formats without magic numbers
2581
- */
2582
- matchHeuristic(buffer, signature) {
2583
- switch (signature.mimeType) {
2584
- case "application/x-ndjson":
2585
- return this.detectNDJSON(buffer);
2586
- case "application/yaml":
2587
- return this.detectYAML(buffer);
2588
- case "text/csv":
2589
- return this.detectCSV(buffer);
2590
- case "application/x-protobuf":
2591
- return this.detectProtobuf(buffer);
2592
- case "text/plain":
2593
- return this.detectPlainText(buffer);
2594
- default:
2595
- return false;
2596
- }
2597
- }
2598
- /**
2599
- * Detect NDJSON (newline-delimited JSON)
2600
- */
2601
- detectNDJSON(buffer) {
2602
- try {
2603
- const text = buffer.toString("utf-8", 0, Math.min(buffer.length, 512));
2604
- const lines = text.split("\n").filter((line) => line.trim().length > 0);
2605
- if (lines.length < 2) return false;
2606
- const linesToCheck = Math.min(3, lines.length);
2607
- let validJsonLines = 0;
2608
- for (let i = 0; i < linesToCheck; i++) {
2609
- try {
2610
- JSON.parse(lines[i]);
2611
- validJsonLines++;
2612
- } catch {
2613
- return false;
2614
- }
2615
- }
2616
- return validJsonLines >= 2;
2617
- } catch {
2618
- return false;
2619
- }
2620
- }
2621
- /**
2622
- * Detect YAML format (heuristic for files without --- header)
2623
- */
2624
- detectYAML(buffer) {
2625
- try {
2626
- const text = buffer.toString("utf-8", 0, Math.min(buffer.length, 512));
2627
- const lines = text.split("\n").filter((line) => line.trim().length > 0);
2628
- if (lines.length === 0) return false;
2629
- let yamlIndicators = 0;
2630
- let nonYamlIndicators = 0;
2631
- for (const line of lines.slice(0, 10)) {
2632
- const trimmed = line.trim();
2633
- if (trimmed.startsWith("#")) continue;
2634
- if (/^[\w"'-]+\s*:\s*.+$/.test(trimmed)) {
2635
- yamlIndicators++;
2636
- continue;
2637
- }
2638
- if (/^-\s+/.test(trimmed)) {
2639
- yamlIndicators++;
2640
- continue;
2641
- }
2642
- if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.endsWith(",")) {
2643
- nonYamlIndicators++;
2644
- }
2645
- }
2646
- return yamlIndicators >= 2 && nonYamlIndicators === 0;
2647
- } catch {
2648
- return false;
2649
- }
389
+ }
390
+ return false;
391
+ }
392
+ /**
393
+ * Match bytes at specified offset with optional masking
394
+ */
395
+ matchBytes(buffer, pattern) {
396
+ const { offset, bytes, mask } = pattern;
397
+ if (buffer.length < offset + bytes.length) {
398
+ return false;
399
+ }
400
+ for (let i = 0; i < bytes.length; i++) {
401
+ const bufferByte = buffer[offset + i];
402
+ const patternByte = bytes[i];
403
+ const maskByte = mask ? mask[i] : 255;
404
+ if ((bufferByte & maskByte) !== (patternByte & maskByte)) {
405
+ return false;
2650
406
  }
2651
- /**
2652
- * Detect CSV format
2653
- */
2654
- detectCSV(buffer) {
407
+ }
408
+ return true;
409
+ }
410
+ /**
411
+ * Heuristic detection for formats without magic numbers
412
+ */
413
+ matchHeuristic(buffer, signature) {
414
+ switch (signature.mimeType) {
415
+ case "application/x-ndjson":
416
+ return this.detectNDJSON(buffer);
417
+ case "application/yaml":
418
+ return this.detectYAML(buffer);
419
+ case "text/csv":
420
+ return this.detectCSV(buffer);
421
+ case "application/x-protobuf":
422
+ return this.detectProtobuf(buffer);
423
+ case "text/plain":
424
+ return this.detectPlainText(buffer);
425
+ default:
426
+ return false;
427
+ }
428
+ }
429
+ /**
430
+ * Detect NDJSON (newline-delimited JSON)
431
+ */
432
+ detectNDJSON(buffer) {
433
+ try {
434
+ const text = buffer.toString("utf-8", 0, Math.min(buffer.length, 512));
435
+ const lines = text.split("\n").filter((line) => line.trim().length > 0);
436
+ if (lines.length < 2) return false;
437
+ const linesToCheck = Math.min(3, lines.length);
438
+ let validJsonLines = 0;
439
+ for (let i = 0; i < linesToCheck; i++) {
2655
440
  try {
2656
- const text = buffer.toString("utf-8", 0, Math.min(buffer.length, 512));
2657
- const lines = text.split("\n").filter((line) => line.trim().length > 0);
2658
- if (lines.length < 2) return false;
2659
- const delimiters = [",", ";", " "];
2660
- for (const delimiter of delimiters) {
2661
- const counts = lines.map((line) => {
2662
- const escaped = delimiter.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2663
- const matches = line.match(new RegExp(escaped, "g"));
2664
- return matches ? matches.length : 0;
2665
- });
2666
- const firstCount = counts[0];
2667
- if (firstCount > 0 && counts.every((count) => count === firstCount)) {
2668
- return true;
2669
- }
2670
- }
2671
- return false;
441
+ JSON.parse(lines[i]);
442
+ validJsonLines++;
2672
443
  } catch {
2673
444
  return false;
2674
445
  }
2675
446
  }
2676
- /**
2677
- * Detect protobuf binary format
2678
- */
2679
- detectProtobuf(buffer) {
2680
- if (buffer.length < 4) return false;
2681
- const firstByte = buffer[0];
2682
- const wireType = firstByte & 7;
2683
- const fieldNumber = firstByte >> 3;
2684
- const hasValidWireType = wireType === 0 || wireType === 1 || wireType === 2 || wireType === 5;
2685
- const hasValidFieldNumber = fieldNumber > 0 && fieldNumber < 100;
2686
- const isBinary = this.isBinaryContent(buffer);
2687
- return hasValidWireType && hasValidFieldNumber && isBinary;
2688
- }
2689
- /**
2690
- * Detect plain text format
2691
- */
2692
- detectPlainText(buffer) {
2693
- const sample = buffer.subarray(0, Math.min(buffer.length, 512));
2694
- if (sample.length === 0) return false;
2695
- let binaryBytes = 0;
2696
- for (const byte of sample) {
2697
- if (byte === 0 || byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
2698
- binaryBytes++;
2699
- }
2700
- }
2701
- return binaryBytes / sample.length < 0.05;
2702
- }
2703
- /**
2704
- * Check if buffer contains binary content
2705
- */
2706
- isBinaryContent(buffer) {
2707
- const sample = buffer.subarray(0, Math.min(buffer.length, 512));
2708
- let binaryBytes = 0;
2709
- for (const byte of sample) {
2710
- if (byte === 0 || byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
2711
- binaryBytes++;
2712
- }
447
+ return validJsonLines >= 2;
448
+ } catch {
449
+ return false;
450
+ }
451
+ }
452
+ /**
453
+ * Detect YAML format (heuristic for files without --- header)
454
+ */
455
+ detectYAML(buffer) {
456
+ try {
457
+ const text = buffer.toString("utf-8", 0, Math.min(buffer.length, 512));
458
+ const lines = text.split("\n").filter((line) => line.trim().length > 0);
459
+ if (lines.length === 0) return false;
460
+ let yamlIndicators = 0;
461
+ let nonYamlIndicators = 0;
462
+ for (const line of lines.slice(0, 10)) {
463
+ const trimmed = line.trim();
464
+ if (trimmed.startsWith("#")) continue;
465
+ if (/^[\w"'-]+\s*:\s*.+$/.test(trimmed)) {
466
+ yamlIndicators++;
467
+ continue;
468
+ }
469
+ if (/^-\s+/.test(trimmed)) {
470
+ yamlIndicators++;
471
+ continue;
472
+ }
473
+ if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.endsWith(",")) {
474
+ nonYamlIndicators++;
475
+ }
476
+ }
477
+ return yamlIndicators >= 2 && nonYamlIndicators === 0;
478
+ } catch {
479
+ return false;
480
+ }
481
+ }
482
+ /**
483
+ * Detect CSV format
484
+ */
485
+ detectCSV(buffer) {
486
+ try {
487
+ const text = buffer.toString("utf-8", 0, Math.min(buffer.length, 512));
488
+ const lines = text.split("\n").filter((line) => line.trim().length > 0);
489
+ if (lines.length < 2) return false;
490
+ const delimiters = [",", ";", " "];
491
+ for (const delimiter of delimiters) {
492
+ const counts = lines.map((line) => {
493
+ const escaped = delimiter.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
494
+ const matches = line.match(new RegExp(escaped, "g"));
495
+ return matches ? matches.length : 0;
496
+ });
497
+ const firstCount = counts[0];
498
+ if (firstCount > 0 && counts.every((count) => count === firstCount)) {
499
+ return true;
2713
500
  }
2714
- return binaryBytes / sample.length > 0.1;
2715
501
  }
2716
- };
502
+ return false;
503
+ } catch {
504
+ return false;
505
+ }
506
+ }
507
+ /**
508
+ * Detect protobuf binary format
509
+ */
510
+ detectProtobuf(buffer) {
511
+ if (buffer.length < 4) return false;
512
+ const firstByte = buffer[0];
513
+ const wireType = firstByte & 7;
514
+ const fieldNumber = firstByte >> 3;
515
+ const hasValidWireType = wireType === 0 || wireType === 1 || wireType === 2 || wireType === 5;
516
+ const hasValidFieldNumber = fieldNumber > 0 && fieldNumber < 100;
517
+ const isBinary = this.isBinaryContent(buffer);
518
+ return hasValidWireType && hasValidFieldNumber && isBinary;
519
+ }
520
+ /**
521
+ * Detect plain text format
522
+ */
523
+ detectPlainText(buffer) {
524
+ const sample = buffer.subarray(0, Math.min(buffer.length, 512));
525
+ if (sample.length === 0) return false;
526
+ let binaryBytes = 0;
527
+ for (const byte of sample) {
528
+ if (byte === 0 || byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
529
+ binaryBytes++;
530
+ }
531
+ }
532
+ return binaryBytes / sample.length < 0.05;
533
+ }
534
+ /**
535
+ * Check if buffer contains binary content
536
+ */
537
+ isBinaryContent(buffer) {
538
+ const sample = buffer.subarray(0, Math.min(buffer.length, 512));
539
+ let binaryBytes = 0;
540
+ for (const byte of sample) {
541
+ if (byte === 0 || byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
542
+ binaryBytes++;
543
+ }
544
+ }
545
+ return binaryBytes / sample.length > 0.1;
2717
546
  }
2718
- });
547
+ };
548
+ function createDetector(catalog) {
549
+ return new MimeTypeDetector(MAGIC_NUMBER_DATABASE, catalog);
550
+ }
551
+
552
+ // src/foundry/mime-types.ts
553
+ var catalogCache3 = null;
554
+ var mimeStringIndex = /* @__PURE__ */ new Map();
555
+ var extensionIndex = /* @__PURE__ */ new Map();
556
+ var detectorInstance = null;
2719
557
  function deepClone3(obj) {
2720
558
  if (obj === null || typeof obj !== "object") {
2721
559
  return obj;
@@ -2858,17 +696,10 @@ function clearMimeTypeCache() {
2858
696
  extensionIndex.clear();
2859
697
  detectorInstance = null;
2860
698
  }
2861
- var catalogCache3, mimeStringIndex, extensionIndex, detectorInstance;
2862
- var init_mime_types = __esm({
2863
- "src/foundry/mime-types.ts"() {
2864
- init_detector();
2865
- init_loader();
2866
- catalogCache3 = null;
2867
- mimeStringIndex = /* @__PURE__ */ new Map();
2868
- extensionIndex = /* @__PURE__ */ new Map();
2869
- detectorInstance = null;
2870
- }
2871
- });
699
+ var catalogCache4 = null;
700
+ var patternIndex = /* @__PURE__ */ new Map();
701
+ var compiledRegexCache = /* @__PURE__ */ new Map();
702
+ var compiledGlobCache = /* @__PURE__ */ new Map();
2872
703
  function deepClone4(obj) {
2873
704
  if (obj === null || typeof obj !== "object") {
2874
705
  return obj;
@@ -2980,1450 +811,10 @@ function clearPatternCache() {
2980
811
  compiledRegexCache.clear();
2981
812
  compiledGlobCache.clear();
2982
813
  }
2983
- var catalogCache4, patternIndex, compiledRegexCache, compiledGlobCache;
2984
- var init_patterns = __esm({
2985
- "src/foundry/patterns.ts"() {
2986
- init_errors2();
2987
- init_loader();
2988
- catalogCache4 = null;
2989
- patternIndex = /* @__PURE__ */ new Map();
2990
- compiledRegexCache = /* @__PURE__ */ new Map();
2991
- compiledGlobCache = /* @__PURE__ */ new Map();
2992
- }
2993
- });
2994
- function getConfigPath() {
2995
- if (__dirname3.includes("/dist/")) {
2996
- return join(__dirname3, "../../config/crucible-ts/library/foundry/signals.yaml");
2997
- }
2998
- return join(__dirname3, "../../../config/crucible-ts/library/foundry/signals.yaml");
2999
- }
3000
- async function loadCatalog2() {
3001
- const filePath = SSOT_PATHS2.signals;
3002
- const catalogName = "signals";
3003
- try {
3004
- let content;
3005
- if (typeof Bun !== "undefined") {
3006
- try {
3007
- const file = Bun.file(filePath);
3008
- if (!await file.exists()) {
3009
- throw FoundryCatalogError.missingCatalog(catalogName);
3010
- }
3011
- content = await file.text();
3012
- } catch (error) {
3013
- if (error instanceof Error && error.message.includes("No such file")) {
3014
- throw FoundryCatalogError.missingCatalog(catalogName);
3015
- }
3016
- throw error;
3017
- }
3018
- } else {
3019
- try {
3020
- content = await readFile(filePath, "utf-8");
3021
- } catch (error) {
3022
- if (error.code === "ENOENT") {
3023
- throw FoundryCatalogError.missingCatalog(catalogName);
3024
- }
3025
- throw error;
3026
- }
3027
- }
3028
- const data = parse(content);
3029
- const result = await validateDataBySchemaId(data, SCHEMA_ID);
3030
- if (!result.valid) {
3031
- const errorMessages = result.diagnostics.map((d) => `${d.pointer}: ${d.message}`).join("; ");
3032
- throw FoundryCatalogError.invalidSchema(
3033
- catalogName,
3034
- `Schema validation failed: ${errorMessages}`
3035
- );
3036
- }
3037
- return data;
3038
- } catch (error) {
3039
- if (error instanceof FoundryCatalogError) {
3040
- throw error;
3041
- }
3042
- const err = error;
3043
- if (err.code === "ENOENT") {
3044
- throw FoundryCatalogError.missingCatalog(catalogName);
3045
- } else if (err.code === "EACCES") {
3046
- throw FoundryCatalogError.invalidSchema(
3047
- catalogName,
3048
- "Permission denied accessing catalog file",
3049
- err
3050
- );
3051
- } else if (err.code === "EISDIR") {
3052
- throw FoundryCatalogError.invalidSchema(
3053
- catalogName,
3054
- "Expected file but found directory",
3055
- err
3056
- );
3057
- } else if (err.code === "EMFILE" || err.code === "ENFILE") {
3058
- throw FoundryCatalogError.invalidSchema(catalogName, "Too many open files", err);
3059
- }
3060
- throw FoundryCatalogError.invalidSchema(catalogName, "Failed to load catalog", err);
3061
- }
3062
- }
3063
- async function getCatalog() {
3064
- if (!cachedCatalog) {
3065
- cachedCatalog = await loadCatalog2();
3066
- }
3067
- return cachedCatalog;
3068
- }
3069
- async function getSignalsVersion() {
3070
- const catalog = await getCatalog();
3071
- return catalog.version;
3072
- }
3073
- async function listSignals() {
3074
- const catalog = await getCatalog();
3075
- return catalog.signals.map((signal) => ({ ...signal }));
3076
- }
3077
- async function getSignal(identifier) {
3078
- const catalog = await getCatalog();
3079
- const signal = catalog.signals.find((s) => s.id === identifier || s.name === identifier);
3080
- return signal ? { ...signal } : null;
3081
- }
3082
- async function listBehaviors() {
3083
- const catalog = await getCatalog();
3084
- return catalog.behaviors.map((behavior) => ({ ...behavior }));
3085
- }
3086
- async function getBehavior(id) {
3087
- const catalog = await getCatalog();
3088
- const behavior = catalog.behaviors.find((b) => b.id === id);
3089
- return behavior ? { ...behavior } : null;
3090
- }
3091
- async function getSignalCatalog() {
3092
- return await getCatalog();
3093
- }
3094
- var __filename2, __dirname3, SSOT_PATHS2, SCHEMA_ID, cachedCatalog;
3095
- var init_catalog = __esm({
3096
- "src/foundry/signals/catalog.ts"() {
3097
- init_validator();
3098
- init_errors2();
3099
- __filename2 = fileURLToPath(import.meta.url);
3100
- __dirname3 = dirname(__filename2);
3101
- SSOT_PATHS2 = {
3102
- signals: getConfigPath()
3103
- };
3104
- SCHEMA_ID = "library/foundry/v1.0.0/signals";
3105
- cachedCatalog = null;
3106
- }
3107
- });
3108
-
3109
- // src/foundry/signals/capabilities.ts
3110
- function getPlatform2() {
3111
- const platform = process.platform;
3112
- switch (platform) {
3113
- case "linux":
3114
- return "linux";
3115
- case "darwin":
3116
- return "darwin";
3117
- case "win32":
3118
- return "win32";
3119
- case "freebsd":
3120
- return "freebsd";
3121
- default:
3122
- return "unknown";
3123
- }
3124
- }
3125
- function isPOSIX2() {
3126
- const platform = getPlatform2();
3127
- return platform === "linux" || platform === "darwin" || platform === "freebsd";
3128
- }
3129
- function isWindows2() {
3130
- return getPlatform2() === "win32";
3131
- }
3132
- async function supportsSignal(signalName) {
3133
- const signal = await getSignal(signalName);
3134
- if (!signal) {
3135
- return false;
3136
- }
3137
- if (isPOSIX2()) {
3138
- return true;
3139
- }
3140
- if (isWindows2()) {
3141
- return signal.windows_event !== null;
3142
- }
3143
- return false;
3144
- }
3145
- function supportsSignalExitCodes2() {
3146
- return isPOSIX2();
3147
- }
3148
- async function getPlatformCapabilities2() {
3149
- const platform = getPlatform2();
3150
- const isPosix = isPOSIX2();
3151
- const isWin = isWindows2();
3152
- const catalog = await getSignalCatalog();
3153
- const supported = [];
3154
- const unsupported = [];
3155
- const mapped = [];
3156
- for (const signal of catalog.signals) {
3157
- if (isPosix) {
3158
- supported.push(signal.name);
3159
- } else if (isWin) {
3160
- if (signal.windows_event !== null) {
3161
- supported.push(signal.name);
3162
- mapped.push(signal.name);
3163
- } else {
3164
- unsupported.push(signal.name);
3165
- }
3166
- } else {
3167
- unsupported.push(signal.name);
3168
- }
3169
- }
3170
- return {
3171
- platform,
3172
- isPOSIX: isPosix,
3173
- isWindows: isWin,
3174
- supportsNativeSignals: isPosix,
3175
- supportsSignalExitCodes: supportsSignalExitCodes2(),
3176
- supportedSignals: supported,
3177
- unsupportedSignals: unsupported,
3178
- mappedSignals: mapped
3179
- };
3180
- }
3181
- async function getSignalNumber(signalName) {
3182
- const signal = await getSignal(signalName);
3183
- if (!signal) {
3184
- return null;
3185
- }
3186
- const platform = getPlatform2();
3187
- if (signal.platform_overrides) {
3188
- if (platform === "darwin" && signal.platform_overrides.darwin !== void 0) {
3189
- return signal.platform_overrides.darwin;
3190
- }
3191
- if (platform === "freebsd" && signal.platform_overrides.freebsd !== void 0) {
3192
- return signal.platform_overrides.freebsd;
3193
- }
3194
- }
3195
- return signal.unix_number;
3196
- }
3197
- async function getWindowsEvent(signalName) {
3198
- const signal = await getSignal(signalName);
3199
- if (!signal) {
3200
- return null;
3201
- }
3202
- return signal.windows_event;
3203
- }
3204
- var init_capabilities2 = __esm({
3205
- "src/foundry/signals/capabilities.ts"() {
3206
- init_catalog();
3207
- }
3208
- });
3209
-
3210
- // src/foundry/signals/config-reload-endpoint.ts
3211
- function createConfigReloadEndpoint(options) {
3212
- const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
3213
- return async (payload, req) => {
3214
- const correlationId = payload.correlation_id ?? generateCorrelationId();
3215
- const authResult = await auth(req);
3216
- if (!authResult.authenticated) {
3217
- if (logger) {
3218
- logger.warn("Config reload endpoint: authentication failed", {
3219
- correlation_id: correlationId,
3220
- reason: authResult.reason
3221
- });
3222
- }
3223
- if (telemetry) {
3224
- telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
3225
- correlation_id: correlationId
3226
- });
3227
- }
3228
- return {
3229
- status: "error",
3230
- error: "authentication_failed",
3231
- message: authResult.reason || "Authentication required",
3232
- statusCode: 401
3233
- };
3234
- }
3235
- const identity = authResult.identity || "unknown";
3236
- if (rateLimit) {
3237
- const rateLimitResult = await rateLimit(identity);
3238
- if (!rateLimitResult.allowed) {
3239
- if (logger) {
3240
- logger.warn("Config reload endpoint: rate limit exceeded", {
3241
- correlation_id: correlationId,
3242
- identity
3243
- });
3244
- }
3245
- if (telemetry) {
3246
- telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
3247
- correlation_id: correlationId
3248
- });
3249
- }
3250
- return {
3251
- status: "error",
3252
- error: "rate_limit_exceeded",
3253
- message: "Rate limit exceeded. Please try again later.",
3254
- statusCode: 429
3255
- };
3256
- }
3257
- }
3258
- if (telemetry) {
3259
- telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
3260
- correlation_id: correlationId
3261
- });
3262
- }
3263
- try {
3264
- const config = await loader();
3265
- if (validator) {
3266
- const validation = await validator(config);
3267
- if (!validation.valid) {
3268
- if (logger) {
3269
- logger.warn("Config reload endpoint: validation failed", {
3270
- correlation_id: correlationId,
3271
- error_count: validation.errors?.length ?? 0
3272
- });
3273
- }
3274
- if (telemetry) {
3275
- telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
3276
- correlation_id: correlationId,
3277
- reason: "validation_failed"
3278
- });
3279
- }
3280
- return {
3281
- status: "error",
3282
- error: "validation_failed",
3283
- message: "Configuration validation failed",
3284
- validation_errors: validation.errors,
3285
- statusCode: 422
3286
- };
3287
- }
3288
- }
3289
- if (onReload2) {
3290
- await onReload2(config);
3291
- }
3292
- if (telemetry) {
3293
- telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
3294
- correlation_id: correlationId
3295
- });
3296
- }
3297
- if (logger) {
3298
- logger.info("Config reload endpoint: reload accepted", {
3299
- correlation_id: correlationId,
3300
- reason: payload.reason
3301
- });
3302
- }
3303
- return {
3304
- status: "reloaded",
3305
- correlation_id: correlationId,
3306
- message: "Configuration reloaded",
3307
- statusCode: 200
3308
- };
3309
- } catch (error) {
3310
- if (logger) {
3311
- logger.warn("Config reload endpoint: reload failed", {
3312
- correlation_id: correlationId,
3313
- error: error instanceof Error ? error.message : String(error)
3314
- });
3315
- }
3316
- if (telemetry) {
3317
- telemetry.emit("fulmen.config.http_endpoint.reload_error", {
3318
- correlation_id: correlationId,
3319
- error_type: error instanceof Error ? error.constructor.name : "unknown"
3320
- });
3321
- }
3322
- return {
3323
- status: "error",
3324
- error: "reload_failed",
3325
- message: error instanceof Error ? error.message : String(error),
3326
- statusCode: 500
3327
- };
3328
- }
3329
- };
3330
- }
3331
- function generateCorrelationId() {
3332
- return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
3333
- }
3334
- var init_config_reload_endpoint = __esm({
3335
- "src/foundry/signals/config-reload-endpoint.ts"() {
3336
- }
3337
- });
3338
-
3339
- // src/appidentity/runtime.ts
3340
- function detectRuntime() {
3341
- const versions = process.versions;
3342
- if (typeof versions.bun === "string" && versions.bun.length > 0) {
3343
- return { name: "bun", version: versions.bun };
3344
- }
3345
- if (typeof versions.node === "string" && versions.node.length > 0) {
3346
- return { name: "node", version: versions.node };
3347
- }
3348
- return { name: "unknown" };
3349
- }
3350
- function buildRuntimeInfo(options = {}) {
3351
- const runtime = detectRuntime();
3352
- const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
3353
- const vendor = options.vendor ?? options.identity?.app.vendor;
3354
- return {
3355
- service: {
3356
- name: serviceName,
3357
- vendor,
3358
- version: options.version
3359
- },
3360
- runtime,
3361
- platform: {
3362
- os: process.platform,
3363
- arch: process.arch
3364
- }
3365
- };
3366
- }
3367
- var init_runtime = __esm({
3368
- "src/appidentity/runtime.ts"() {
3369
- }
3370
- });
3371
-
3372
- // src/foundry/signals/control-discovery-endpoint.ts
3373
- function createControlDiscoveryEndpoint(options) {
3374
- const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
3375
- return async (req) => {
3376
- if (auth) {
3377
- const authResult = await auth(req);
3378
- if (!authResult.authenticated) {
3379
- if (logger) {
3380
- logger.warn("Control discovery endpoint: authentication failed", {
3381
- reason: authResult.reason
3382
- });
3383
- }
3384
- if (telemetry) {
3385
- telemetry.emit("fulmen.control.discovery.auth_failed", {
3386
- service: identity.app.binary_name
3387
- });
3388
- }
3389
- return {
3390
- status: "error",
3391
- error: "authentication_failed",
3392
- message: authResult.reason || "Authentication required",
3393
- statusCode: 401
3394
- };
3395
- }
3396
- }
3397
- if (telemetry) {
3398
- telemetry.emit("fulmen.control.discovery.served", {
3399
- service: identity.app.binary_name
3400
- });
3401
- }
3402
- const runtime = buildRuntimeInfo({ identity, version });
3403
- return {
3404
- status: "ok",
3405
- service: {
3406
- name: identity.app.binary_name,
3407
- vendor: identity.app.vendor,
3408
- version
3409
- },
3410
- runtime: {
3411
- name: runtime.runtime.name,
3412
- version: runtime.runtime.version,
3413
- platform: runtime.platform.os,
3414
- arch: runtime.platform.arch
3415
- },
3416
- auth_summary: authSummary,
3417
- endpoints,
3418
- statusCode: 200
3419
- };
3420
- };
3421
- }
3422
- var init_control_discovery_endpoint = __esm({
3423
- "src/foundry/signals/control-discovery-endpoint.ts"() {
3424
- init_runtime();
3425
- }
3426
- });
3427
-
3428
- // src/foundry/signals/convenience.ts
3429
- async function onShutdown(manager, handler, options = {}) {
3430
- await manager.register("SIGTERM", handler, options);
3431
- await manager.register("SIGINT", handler, options);
3432
- }
3433
- async function onReload(manager, handler, options = {}) {
3434
- await manager.register("SIGHUP", handler, options);
3435
- }
3436
- async function onUSR1(manager, handler, options = {}) {
3437
- await manager.register("SIGUSR1", handler, options);
3438
- }
3439
- async function onUSR2(manager, handler, options = {}) {
3440
- await manager.register("SIGUSR2", handler, options);
3441
- }
3442
- async function onEmergencyQuit(manager, handler, options = {}) {
3443
- await manager.register("SIGQUIT", handler, options);
3444
- }
3445
- async function onAnyShutdown(manager, handler, options = {}) {
3446
- await manager.register("SIGTERM", handler, options);
3447
- await manager.register("SIGINT", handler, options);
3448
- await manager.register("SIGQUIT", handler, options);
3449
- }
3450
- var init_convenience = __esm({
3451
- "src/foundry/signals/convenience.ts"() {
3452
- }
3453
- });
3454
-
3455
- // src/foundry/signals/double-tap.ts
3456
- async function createDoubleTapTracker(signalName, config = {}) {
3457
- const signal = await getSignal(signalName);
3458
- const defaultWindowMs = signal?.double_tap_window_seconds ? signal.double_tap_window_seconds * 1e3 : 2e3;
3459
- const defaultExitCode = signal?.double_tap_exit_code || 130;
3460
- const defaultHintMessage = signal?.double_tap_message || "Press Ctrl+C again within 2s to force quit";
3461
- return {
3462
- firstTapTime: null,
3463
- windowMs: config.windowMs ?? defaultWindowMs,
3464
- exitCode: config.exitCode ?? defaultExitCode,
3465
- hintMessage: config.hintMessage ?? defaultHintMessage,
3466
- logger: config.logger,
3467
- testMode: config.testMode ?? false
3468
- };
3469
- }
3470
- function handleDoubleTap(state) {
3471
- const now = Date.now();
3472
- if (state.firstTapTime === null) {
3473
- state.firstTapTime = now;
3474
- if (state.logger) {
3475
- state.logger.info(state.hintMessage);
3476
- } else {
3477
- console.log(state.hintMessage);
3478
- }
3479
- return false;
3480
- }
3481
- const elapsed = now - state.firstTapTime;
3482
- if (elapsed < state.windowMs) {
3483
- if (state.logger) {
3484
- state.logger.info("Force quitting...");
3485
- } else {
3486
- console.log("Force quitting...");
3487
- }
3488
- if (!state.testMode) {
3489
- process.exit(state.exitCode);
3490
- }
3491
- return true;
3492
- }
3493
- state.firstTapTime = now;
3494
- if (state.logger) {
3495
- state.logger.info(state.hintMessage);
3496
- } else {
3497
- console.log(state.hintMessage);
3498
- }
3499
- return false;
3500
- }
3501
- function resetDoubleTap(state) {
3502
- state.firstTapTime = null;
3503
- }
3504
- function isWithinWindow(state) {
3505
- if (state.firstTapTime === null) {
3506
- return false;
3507
- }
3508
- const elapsed = Date.now() - state.firstTapTime;
3509
- return elapsed < state.windowMs;
3510
- }
3511
- function getWindowTimeRemaining(state) {
3512
- if (state.firstTapTime === null) {
3513
- return null;
3514
- }
3515
- const elapsed = Date.now() - state.firstTapTime;
3516
- const remaining = state.windowMs - elapsed;
3517
- return remaining > 0 ? remaining : null;
3518
- }
3519
- var init_double_tap = __esm({
3520
- "src/foundry/signals/double-tap.ts"() {
3521
- init_catalog();
3522
- }
3523
- });
3524
-
3525
- // src/foundry/signals/windows.ts
3526
- async function handleWindowsFallback(signalName, options = {}) {
3527
- const signal = await getSignal(signalName);
3528
- if (!signal) {
3529
- return {
3530
- supported: false,
3531
- logged: false
3532
- };
3533
- }
3534
- if (signal.windows_event !== null) {
3535
- return {
3536
- supported: true,
3537
- logged: false
3538
- };
3539
- }
3540
- const fallback = signal.windows_fallback;
3541
- if (!fallback) {
3542
- return {
3543
- supported: false,
3544
- logged: false
3545
- };
3546
- }
3547
- if (options.silent) {
3548
- return {
3549
- supported: false,
3550
- fallback,
3551
- logged: false
3552
- };
3553
- }
3554
- const logger = options.logger || defaultLogger;
3555
- const telemetry = options.telemetry;
3556
- const logMeta = {
3557
- signal: signal.name,
3558
- platform: "windows",
3559
- fallback: fallback.fallback_behavior,
3560
- operation_hint: fallback.operation_hint
3561
- };
3562
- logger.info(fallback.log_message, logMeta);
3563
- if (telemetry) {
3564
- telemetry.emit(fallback.telemetry_event, fallback.telemetry_tags);
3565
- }
3566
- return {
3567
- supported: false,
3568
- fallback,
3569
- logged: true
3570
- };
3571
- }
3572
- async function getFallbackMetadata(signalName) {
3573
- const signal = await getSignal(signalName);
3574
- if (!signal || !signal.windows_fallback) {
3575
- return null;
3576
- }
3577
- return signal.windows_fallback;
3578
- }
3579
- async function requiresFallback(signalName) {
3580
- const signal = await getSignal(signalName);
3581
- if (!signal) {
3582
- return false;
3583
- }
3584
- return signal.windows_event === null && signal.windows_fallback !== void 0;
3585
- }
3586
- async function getHttpFallbackGuidance(signalName) {
3587
- const fallback = await getFallbackMetadata(signalName);
3588
- if (!fallback || fallback.fallback_behavior !== "http_admin_endpoint") {
3589
- return null;
3590
- }
3591
- return fallback.operation_hint;
3592
- }
3593
- var defaultLogger;
3594
- var init_windows = __esm({
3595
- "src/foundry/signals/windows.ts"() {
3596
- init_catalog();
3597
- defaultLogger = {
3598
- info(message, meta) {
3599
- if (meta) {
3600
- console.info(message, meta);
3601
- } else {
3602
- console.info(message);
3603
- }
3604
- },
3605
- warn(message, meta) {
3606
- if (meta) {
3607
- console.warn(message, meta);
3608
- } else {
3609
- console.warn(message);
3610
- }
3611
- }
3612
- };
3613
- }
3614
- });
3615
-
3616
- // src/foundry/signals/guards.ts
3617
- async function ensureSupported(signalName, options = {}) {
3618
- const { includeGuidance = true } = options;
3619
- const signal = await getSignal(signalName);
3620
- if (!signal) {
3621
- throw FoundryCatalogError.invalidSchema(
3622
- "signals",
3623
- `Signal "${signalName}" not found in catalog. Valid signals: SIGTERM, SIGINT, SIGHUP, SIGQUIT, SIGPIPE, SIGALRM, SIGUSR1, SIGUSR2`
3624
- );
3625
- }
3626
- const supported = await supportsSignal(signalName);
3627
- if (supported) {
3628
- return;
3629
- }
3630
- let message = `Signal ${signal.name} is not supported on this platform`;
3631
- if (isWindows2() && includeGuidance) {
3632
- const fallback = await getFallbackMetadata(signalName);
3633
- if (fallback) {
3634
- message += `. ${fallback.log_message}`;
3635
- switch (fallback.fallback_behavior) {
3636
- case "http_admin_endpoint":
3637
- message += `. Use HTTP endpoint: ${fallback.operation_hint}`;
3638
- break;
3639
- case "exception_handling":
3640
- message += `. Alternative: ${fallback.operation_hint}`;
3641
- break;
3642
- case "timer_api":
3643
- message += `. Alternative: ${fallback.operation_hint}`;
3644
- break;
3645
- }
3646
- }
3647
- }
3648
- throw FoundryCatalogError.invalidSchema("signals", message);
3649
- }
3650
- function ensureSignalExitCodesSupported() {
3651
- if (!isPOSIX2()) {
3652
- throw FoundryCatalogError.invalidSchema(
3653
- "signals",
3654
- "Signal-based exit codes (128+N pattern) are not supported on this platform. Windows does not propagate signal numbers via exit codes. Use explicit exit codes or monitor via HTTP admin endpoint."
3655
- );
3656
- }
3657
- }
3658
- function ensurePOSIX() {
3659
- if (!isPOSIX2()) {
3660
- throw FoundryCatalogError.invalidSchema(
3661
- "signals",
3662
- "This operation requires a POSIX-compliant platform (Linux, macOS, FreeBSD). Current platform does not support native signal handling."
3663
- );
3664
- }
3665
- }
3666
- function ensureWindows() {
3667
- if (!isWindows2()) {
3668
- throw FoundryCatalogError.invalidSchema(
3669
- "signals",
3670
- "This operation requires Windows platform. Current platform uses native signals."
3671
- );
3672
- }
3673
- }
3674
- var init_guards = __esm({
3675
- "src/foundry/signals/guards.ts"() {
3676
- init_errors2();
3677
- init_capabilities2();
3678
- init_catalog();
3679
- init_windows();
3680
- }
3681
- });
3682
-
3683
- // src/foundry/signals/http-helper.ts
3684
- function createSignalEndpoint(options) {
3685
- const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
3686
- return async (payload, req) => {
3687
- const correlationId = payload.correlation_id ?? generateCorrelationId2();
3688
- const authResult = await auth(req);
3689
- if (!authResult.authenticated) {
3690
- if (logger) {
3691
- logger.warn("Signal endpoint: authentication failed", {
3692
- correlation_id: correlationId,
3693
- reason: authResult.reason
3694
- });
3695
- }
3696
- if (telemetry) {
3697
- telemetry.emit("fulmen.signal.http_endpoint.auth_failed", {
3698
- correlation_id: correlationId
3699
- });
3700
- }
3701
- return {
3702
- status: "error",
3703
- error: "authentication_failed",
3704
- message: authResult.reason || "Authentication required",
3705
- statusCode: 401
3706
- };
3707
- }
3708
- const identity = authResult.identity || "unknown";
3709
- if (!payload.signal) {
3710
- return {
3711
- status: "error",
3712
- error: "invalid_request",
3713
- message: "Signal name is required",
3714
- statusCode: 400
3715
- };
3716
- }
3717
- const signalName = normalizeSignalName(payload.signal);
3718
- const defaultAllowed = [
3719
- "SIGHUP",
3720
- "SIGTERM",
3721
- "SIGINT",
3722
- "SIGQUIT",
3723
- "SIGUSR1",
3724
- "SIGUSR2",
3725
- "SIGPIPE",
3726
- "SIGALRM"
3727
- ];
3728
- const allowed = (allowedSignals || defaultAllowed).map((s) => normalizeSignalName(s));
3729
- if (!allowed.includes(signalName)) {
3730
- return {
3731
- status: "error",
3732
- error: "invalid_signal",
3733
- message: `Signal '${payload.signal}' is not recognized. Valid signals: ${allowed.join(", ")}`,
3734
- valid_signals: allowed,
3735
- statusCode: 400
3736
- };
3737
- }
3738
- const supported = await supportsSignal(signalName);
3739
- if (!supported) {
3740
- return {
3741
- status: "error",
3742
- error: "signal_not_supported",
3743
- message: `Signal ${signalName} is not supported on this platform`,
3744
- statusCode: 400
3745
- };
3746
- }
3747
- if (rateLimit) {
3748
- const rateLimitResult = await rateLimit(identity, signalName);
3749
- if (!rateLimitResult.allowed) {
3750
- if (logger) {
3751
- logger.warn("Signal endpoint: rate limit exceeded", {
3752
- correlation_id: correlationId,
3753
- identity,
3754
- signal: signalName
3755
- });
3756
- }
3757
- if (telemetry) {
3758
- telemetry.emit("fulmen.signal.http_endpoint.rate_limited", {
3759
- correlation_id: correlationId,
3760
- signal: signalName
3761
- });
3762
- }
3763
- return {
3764
- status: "error",
3765
- error: "rate_limit_exceeded",
3766
- message: "Rate limit exceeded. Please try again later.",
3767
- statusCode: 429
3768
- };
3769
- }
3770
- }
3771
- if (logger) {
3772
- logger.info("Signal endpoint: signal received", {
3773
- correlation_id: correlationId,
3774
- identity,
3775
- signal: signalName,
3776
- reason: payload.reason
3777
- });
3778
- }
3779
- if (telemetry) {
3780
- telemetry.emit("fulmen.signal.http_endpoint.signal_received", {
3781
- correlation_id: correlationId,
3782
- signal: signalName
3783
- });
3784
- }
3785
- void manager.trigger(signalName).catch((error) => {
3786
- if (logger) {
3787
- logger.warn("Signal handler execution failed", {
3788
- correlation_id: correlationId,
3789
- signal: signalName,
3790
- error: error instanceof Error ? error.message : String(error)
3791
- });
3792
- }
3793
- });
3794
- return {
3795
- status: "accepted",
3796
- signal: signalName,
3797
- correlation_id: correlationId,
3798
- message: "Signal will be processed asynchronously",
3799
- statusCode: 202
3800
- };
3801
- };
3802
- }
3803
- function normalizeSignalName(signal) {
3804
- const upper = signal.toUpperCase();
3805
- if (upper.startsWith("SIG")) {
3806
- return upper;
3807
- }
3808
- return `SIG${upper}`;
3809
- }
3810
- function generateCorrelationId2() {
3811
- return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
3812
- }
3813
- function createBearerTokenAuth(expectedToken) {
3814
- return (req) => {
3815
- const headers = req.headers;
3816
- const authHeader = headers?.authorization || headers?.Authorization;
3817
- if (!authHeader) {
3818
- return {
3819
- authenticated: false,
3820
- reason: "Missing Authorization header"
3821
- };
3822
- }
3823
- const [scheme, token] = authHeader.split(" ");
3824
- if (scheme !== "Bearer" || token !== expectedToken) {
3825
- return {
3826
- authenticated: false,
3827
- reason: "Invalid bearer token"
3828
- };
3829
- }
3830
- return {
3831
- authenticated: true,
3832
- identity: "bearer-token-user"
3833
- };
3834
- };
3835
- }
3836
- function createSimpleRateLimiter(requestsPerMinute) {
3837
- const requests = /* @__PURE__ */ new Map();
3838
- const windowMs = 6e4;
3839
- return (identity) => {
3840
- const now = Date.now();
3841
- const key = identity;
3842
- let timestamps = requests.get(key) || [];
3843
- timestamps = timestamps.filter((ts) => now - ts < windowMs);
3844
- if (timestamps.length >= requestsPerMinute) {
3845
- const oldestTimestamp = Math.min(...timestamps);
3846
- const resetAt = oldestTimestamp + windowMs;
3847
- return {
3848
- allowed: false,
3849
- remaining: 0,
3850
- reset_at: resetAt
3851
- };
3852
- }
3853
- timestamps.push(now);
3854
- requests.set(key, timestamps);
3855
- return {
3856
- allowed: true,
3857
- remaining: requestsPerMinute - timestamps.length,
3858
- reset_at: now + windowMs
3859
- };
3860
- };
3861
- }
3862
- var init_http_helper = __esm({
3863
- "src/foundry/signals/http-helper.ts"() {
3864
- init_capabilities2();
3865
- }
3866
- });
3867
-
3868
- // src/foundry/signals/manager.ts
3869
- function createSignalManager(options = {}) {
3870
- return new SignalManager(options);
3871
- }
3872
- var SignalManager;
3873
- var init_manager = __esm({
3874
- "src/foundry/signals/manager.ts"() {
3875
- init_capabilities2();
3876
- init_catalog();
3877
- init_double_tap();
3878
- init_windows();
3879
- SignalManager = class {
3880
- handlers = /* @__PURE__ */ new Map();
3881
- nativeListeners = /* @__PURE__ */ new Map();
3882
- options;
3883
- handlerIdCounter = 0;
3884
- doubleTapTrackers = /* @__PURE__ */ new Map();
3885
- shuttingDown = false;
3886
- constructor(options = {}) {
3887
- this.options = {
3888
- defaultTimeoutMs: options.defaultTimeoutMs ?? 3e4,
3889
- timeoutBehavior: options.timeoutBehavior ?? "log_and_continue",
3890
- testMode: options.testMode ?? false,
3891
- doubleTapWindowMs: options.doubleTapWindowMs ?? 2e3,
3892
- doubleTapExitCode: options.doubleTapExitCode ?? 130,
3893
- logger: options.logger,
3894
- telemetry: options.telemetry
3895
- };
3896
- }
3897
- /**
3898
- * Register a signal handler
3899
- *
3900
- * @param signal - Signal name (e.g., "SIGTERM") or NodeJS.Signals
3901
- * @param handler - Handler function
3902
- * @param options - Handler options
3903
- */
3904
- async register(signal, handler, options = {}) {
3905
- const signalName = typeof signal === "string" ? signal : signal;
3906
- const supported = await supportsSignal(signalName);
3907
- if (!supported) {
3908
- if (isWindows2()) {
3909
- await handleWindowsFallback(signalName, {
3910
- logger: this.options.logger,
3911
- telemetry: this.options.telemetry
3912
- });
3913
- }
3914
- return;
3915
- }
3916
- if (!this.handlers.has(signalName)) {
3917
- this.handlers.set(signalName, []);
3918
- }
3919
- const registration = {
3920
- signal: signalName,
3921
- handler,
3922
- priority: options.priority ?? 0,
3923
- timeoutMs: options.timeoutMs ?? this.options.defaultTimeoutMs,
3924
- id: options.id ?? `handler-${++this.handlerIdCounter}`,
3925
- registeredAt: Date.now()
3926
- };
3927
- const handlers = this.handlers.get(signalName);
3928
- if (!handlers) {
3929
- throw new Error(`Handler list not initialized for signal ${signalName}`);
3930
- }
3931
- handlers.push(registration);
3932
- handlers.sort((a, b) => {
3933
- if (a.priority !== b.priority) {
3934
- return b.priority - a.priority;
3935
- }
3936
- return a.registeredAt - b.registeredAt;
3937
- });
3938
- if (!this.nativeListeners.has(signalName)) {
3939
- await this.registerNativeListener(signalName);
3940
- }
3941
- if (this.options.logger) {
3942
- this.options.logger.info(`Signal handler registered: ${signalName}`, {
3943
- handler_id: registration.id,
3944
- priority: registration.priority,
3945
- timeout_ms: registration.timeoutMs
3946
- });
3947
- }
3948
- if (this.options.telemetry) {
3949
- this.options.telemetry.emit("fulmen.signal.handler_registered", {
3950
- signal: signalName,
3951
- handler_id: registration.id,
3952
- priority: String(registration.priority)
3953
- });
3954
- }
3955
- }
3956
- /**
3957
- * Register native Node.js signal listener
3958
- */
3959
- async registerNativeListener(signalName) {
3960
- const signal = await getSignal(signalName);
3961
- if (!signal) {
3962
- return;
3963
- }
3964
- const useDoubleTap = signal.default_behavior === "graceful_shutdown_with_double_tap";
3965
- if (useDoubleTap) {
3966
- const tracker = await createDoubleTapTracker(signalName, {
3967
- windowMs: this.options.doubleTapWindowMs,
3968
- exitCode: this.options.doubleTapExitCode,
3969
- logger: this.options.logger,
3970
- testMode: this.options.testMode
3971
- });
3972
- this.doubleTapTrackers.set(signalName, tracker);
3973
- }
3974
- const listener = async (sig) => {
3975
- if (useDoubleTap) {
3976
- const tracker = this.doubleTapTrackers.get(signalName);
3977
- if (tracker) {
3978
- const forceQuit = handleDoubleTap(tracker);
3979
- if (forceQuit) {
3980
- return;
3981
- }
3982
- }
3983
- }
3984
- await this.executeHandlers(signalName, sig);
3985
- };
3986
- process.on(signalName, listener);
3987
- this.nativeListeners.set(signalName, listener);
3988
- }
3989
- /**
3990
- * Execute all registered handlers for a signal
3991
- */
3992
- async executeHandlers(signalName, signal) {
3993
- const handlers = this.handlers.get(signalName);
3994
- if (!handlers || handlers.length === 0) {
3995
- return;
3996
- }
3997
- this.shuttingDown = true;
3998
- if (this.options.logger) {
3999
- this.options.logger.info(`Signal received: ${signalName}`, {
4000
- handler_count: handlers.length
4001
- });
4002
- }
4003
- for (const registration of handlers) {
4004
- try {
4005
- await this.executeWithTimeout(registration, signal);
4006
- } catch (error) {
4007
- if (this.options.logger) {
4008
- this.options.logger.warn(`Signal handler failed: ${signalName}`, {
4009
- handler_id: registration.id,
4010
- error: error instanceof Error ? error.message : String(error)
4011
- });
4012
- }
4013
- if (this.options.telemetry) {
4014
- this.options.telemetry.emit("fulmen.signal.handler_error", {
4015
- signal: signalName,
4016
- handler_id: registration.id,
4017
- error_type: error instanceof Error ? error.constructor.name : "unknown"
4018
- });
4019
- }
4020
- if (this.options.timeoutBehavior === "force_exit") {
4021
- const exitCode = (await getSignal(signalName))?.exit_code ?? 1;
4022
- if (!this.options.testMode) {
4023
- process.exit(exitCode);
4024
- }
4025
- return;
4026
- }
4027
- }
4028
- }
4029
- const tracker = this.doubleTapTrackers.get(signalName);
4030
- if (tracker) {
4031
- resetDoubleTap(tracker);
4032
- }
4033
- }
4034
- /**
4035
- * Execute a handler with timeout enforcement
4036
- */
4037
- async executeWithTimeout(registration, signal) {
4038
- const timeoutPromise = new Promise((_, reject) => {
4039
- setTimeout(() => {
4040
- reject(new Error(`Handler timeout exceeded: ${registration.timeoutMs}ms`));
4041
- }, registration.timeoutMs);
4042
- });
4043
- const handlerPromise = Promise.resolve(registration.handler(signal));
4044
- try {
4045
- await Promise.race([handlerPromise, timeoutPromise]);
4046
- if (this.options.telemetry) {
4047
- this.options.telemetry.emit("fulmen.signal.handler_completed", {
4048
- signal: registration.signal,
4049
- handler_id: registration.id
4050
- });
4051
- }
4052
- } catch (error) {
4053
- if (this.options.telemetry) {
4054
- this.options.telemetry.emit("fulmen.signal.handler_timeout", {
4055
- signal: registration.signal,
4056
- handler_id: registration.id,
4057
- timeout_ms: String(registration.timeoutMs)
4058
- });
4059
- }
4060
- throw error;
4061
- }
4062
- }
4063
- /**
4064
- * Unregister a signal handler
4065
- *
4066
- * @param signal - Signal name
4067
- * @param handler - Handler to remove (if not provided, removes all handlers)
4068
- */
4069
- unregister(signal, handler) {
4070
- const handlers = this.handlers.get(signal);
4071
- if (!handlers) {
4072
- return;
4073
- }
4074
- if (handler) {
4075
- const index = handlers.findIndex((h) => h.handler === handler);
4076
- if (index !== -1) {
4077
- handlers.splice(index, 1);
4078
- }
4079
- } else {
4080
- handlers.length = 0;
4081
- }
4082
- if (handlers.length === 0) {
4083
- const listener = this.nativeListeners.get(signal);
4084
- if (listener) {
4085
- process.off(signal, listener);
4086
- this.nativeListeners.delete(signal);
4087
- }
4088
- this.handlers.delete(signal);
4089
- }
4090
- }
4091
- /**
4092
- * Check if a signal has registered handlers
4093
- */
4094
- isRegistered(signal) {
4095
- const handlers = this.handlers.get(signal);
4096
- return handlers !== void 0 && handlers.length > 0;
4097
- }
4098
- /**
4099
- * Get handler count for a signal
4100
- */
4101
- getHandlerCount(signal) {
4102
- const handlers = this.handlers.get(signal);
4103
- return handlers?.length ?? 0;
4104
- }
4105
- /**
4106
- * Trigger signal handlers manually (for testing)
4107
- *
4108
- * @param signal - Signal name
4109
- */
4110
- async trigger(signal) {
4111
- const handlers = this.handlers.get(signal);
4112
- if (!handlers || handlers.length === 0) {
4113
- return;
4114
- }
4115
- await this.executeHandlers(signal, signal);
4116
- }
4117
- /**
4118
- * Shutdown the signal manager and cleanup all handlers
4119
- */
4120
- async shutdown() {
4121
- for (const [signal, listener] of this.nativeListeners.entries()) {
4122
- process.off(signal, listener);
4123
- }
4124
- this.nativeListeners.clear();
4125
- this.handlers.clear();
4126
- this.doubleTapTrackers.clear();
4127
- this.shuttingDown = false;
4128
- }
4129
- /**
4130
- * Check if manager is currently shutting down
4131
- */
4132
- isShuttingDown() {
4133
- return this.shuttingDown;
4134
- }
4135
- };
4136
- }
4137
- });
4138
-
4139
- // src/foundry/signals/reload.ts
4140
- function createConfigReloadHandler(options) {
4141
- const {
4142
- loader,
4143
- validator,
4144
- onValidated,
4145
- exitCode = 129,
4146
- logger,
4147
- telemetry,
4148
- testMode = false
4149
- } = options;
4150
- return async () => {
4151
- if (logger) {
4152
- logger.info("Config reload requested (SIGHUP)");
4153
- }
4154
- if (telemetry) {
4155
- telemetry.emit("fulmen.signal.config_reload_requested", {
4156
- signal: "SIGHUP"
4157
- });
4158
- }
4159
- try {
4160
- const newConfig = await loader();
4161
- const result = await validator(newConfig);
4162
- if (!result.valid) {
4163
- if (logger) {
4164
- logger.warn("Config validation failed - continuing with current config", {
4165
- error_count: result.errors?.length ?? 0,
4166
- errors: result.errors
4167
- });
4168
- }
4169
- if (telemetry) {
4170
- telemetry.emit("fulmen.signal.config_reload_rejected", {
4171
- signal: "SIGHUP",
4172
- reason: "validation_failed",
4173
- error_count: String(result.errors?.length ?? 0)
4174
- });
4175
- }
4176
- return;
4177
- }
4178
- if (logger) {
4179
- logger.info("Config validation succeeded - exiting for restart");
4180
- }
4181
- if (telemetry) {
4182
- telemetry.emit("fulmen.signal.config_reload_accepted", {
4183
- signal: "SIGHUP"
4184
- });
4185
- }
4186
- if (onValidated) {
4187
- await onValidated(newConfig);
4188
- }
4189
- if (!testMode) {
4190
- process.exit(exitCode);
4191
- }
4192
- } catch (error) {
4193
- if (logger) {
4194
- logger.warn("Config reload failed with error - continuing with current config", {
4195
- error: error instanceof Error ? error.message : String(error)
4196
- });
4197
- }
4198
- if (telemetry) {
4199
- telemetry.emit("fulmen.signal.config_reload_error", {
4200
- signal: "SIGHUP",
4201
- error_type: error instanceof Error ? error.constructor.name : "unknown"
4202
- });
4203
- }
4204
- }
4205
- };
4206
- }
4207
- var ConfigReloadTracker;
4208
- var init_reload = __esm({
4209
- "src/foundry/signals/reload.ts"() {
4210
- ConfigReloadTracker = class {
4211
- failures = 0;
4212
- lastFailureTime = null;
4213
- maxFailures;
4214
- logger;
4215
- telemetry;
4216
- constructor(options) {
4217
- this.maxFailures = options.maxFailures ?? 3;
4218
- this.logger = options.logger;
4219
- this.telemetry = options.telemetry;
4220
- }
4221
- /**
4222
- * Record a reload failure
4223
- *
4224
- * @returns true if threshold exceeded, false otherwise
4225
- */
4226
- recordFailure() {
4227
- this.failures++;
4228
- this.lastFailureTime = Date.now();
4229
- if (this.failures >= this.maxFailures) {
4230
- if (this.logger) {
4231
- this.logger.warn(
4232
- `${this.failures} consecutive config reload failures - check config source`,
4233
- {
4234
- failure_count: this.failures,
4235
- threshold: this.maxFailures
4236
- }
4237
- );
4238
- }
4239
- if (this.telemetry) {
4240
- this.telemetry.emit("fulmen.signal.config_reload_threshold_exceeded", {
4241
- failure_count: String(this.failures),
4242
- threshold: String(this.maxFailures)
4243
- });
4244
- }
4245
- return true;
4246
- }
4247
- return false;
4248
- }
4249
- /**
4250
- * Record a successful reload (resets counter)
4251
- */
4252
- recordSuccess() {
4253
- this.failures = 0;
4254
- this.lastFailureTime = null;
4255
- }
4256
- /**
4257
- * Get current failure count
4258
- */
4259
- getFailureCount() {
4260
- return this.failures;
4261
- }
4262
- /**
4263
- * Get last failure timestamp
4264
- */
4265
- getLastFailureTime() {
4266
- return this.lastFailureTime;
4267
- }
4268
- };
4269
- }
4270
- });
4271
-
4272
- // src/foundry/signals/index.ts
4273
- var init_signals = __esm({
4274
- "src/foundry/signals/index.ts"() {
4275
- init_capabilities2();
4276
- init_catalog();
4277
- init_config_reload_endpoint();
4278
- init_control_discovery_endpoint();
4279
- init_convenience();
4280
- init_double_tap();
4281
- init_guards();
4282
- init_http_helper();
4283
- init_manager();
4284
- init_reload();
4285
- init_windows();
4286
- }
4287
- });
4288
- function distance(a, b, metric = "levenshtein") {
4289
- switch (metric) {
4290
- case "levenshtein":
4291
- return levenshtein(a, b);
4292
- case "damerau_osa":
4293
- return osa_distance(a, b);
4294
- case "damerau_unrestricted":
4295
- return damerau_levenshtein(a, b);
4296
- case "jaro_winkler":
4297
- return jaro_winkler(a, b);
4298
- case "substring":
4299
- return substringSimilarity(a, b).score;
4300
- default:
4301
- throw new Error(
4302
- `Invalid metric '${metric}': must be one of: levenshtein, damerau_osa, damerau_unrestricted, jaro_winkler, substring`
4303
- );
4304
- }
4305
- }
4306
- var init_distance = __esm({
4307
- "src/foundry/similarity/distance.ts"() {
4308
- }
4309
- });
4310
-
4311
- // src/foundry/similarity/errors.ts
4312
- var init_errors3 = __esm({
4313
- "src/foundry/similarity/errors.ts"() {
4314
- init_errors2();
4315
- }
4316
- });
4317
- function toNormalizationLocale(locale) {
4318
- if (!locale) {
4319
- return void 0;
4320
- }
4321
- if (locale === "tr" || locale === "az" || locale === "lt") {
4322
- return locale;
4323
- }
4324
- return void 0;
4325
- }
4326
- function normalize(value, preset = "default", locale) {
4327
- if (typeof preset === "object") {
4328
- const targetPreset = preset.stripAccents ? "aggressive" : "default";
4329
- const targetLocale = toNormalizationLocale(preset.locale ?? locale);
4330
- return normalize$1(value, targetPreset, targetLocale);
4331
- }
4332
- return normalize$1(value, preset, toNormalizationLocale(locale));
4333
- }
4334
- function casefold(value, locale) {
4335
- if (locale === "tr") {
4336
- return value.toLocaleLowerCase("tr-TR");
4337
- }
4338
- return value.toLowerCase();
4339
- }
4340
- function stripAccents(value) {
4341
- return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
4342
- }
4343
- function equalsIgnoreCase(a, b, options) {
4344
- return normalize(a, options) === normalize(b, options);
4345
- }
4346
- var init_normalization = __esm({
4347
- "src/foundry/similarity/normalization.ts"() {
4348
- }
4349
- });
4350
- function score(a, b, metric = "levenshtein") {
4351
- if (metric === "substring") {
4352
- return substringSimilarity(a, b).score;
4353
- }
4354
- return score$1(a, b, metric);
4355
- }
4356
- var init_score = __esm({
4357
- "src/foundry/similarity/score.ts"() {
4358
- }
4359
- });
4360
- function suggest(input, candidates, options) {
4361
- const metric = options?.metric ?? DEFAULT_METRIC;
4362
- const minScore = options?.minScore ?? DEFAULT_MIN_SCORE;
4363
- const maxSuggestions = options?.maxSuggestions ?? DEFAULT_MAX_SUGGESTIONS;
4364
- let normalizePreset = options?.normalizePreset ?? DEFAULT_NORMALIZE_PRESET;
4365
- if (options?.normalize === false) {
4366
- normalizePreset = "none";
4367
- } else if (options?.normalize === true && !options?.normalizePreset) {
4368
- normalizePreset = "default";
4369
- }
4370
- const wasmOptions = {
4371
- metric,
4372
- normalizePreset,
4373
- minScore,
4374
- maxSuggestions,
4375
- preferPrefix: options?.preferPrefix,
4376
- jaroPrefixScale: options?.jaroPrefixScale,
4377
- jaroMaxPrefix: options?.jaroMaxPrefix
4378
- };
4379
- const results = suggest$1(input, candidates, wasmOptions);
4380
- return results.map((r) => ({
4381
- value: r.value,
4382
- score: r.score,
4383
- matchedRange: r.matchedRange,
4384
- reason: r.reason,
4385
- normalizedValue: r.normalizedValue
4386
- }));
4387
- }
4388
- var DEFAULT_MIN_SCORE, DEFAULT_MAX_SUGGESTIONS, DEFAULT_METRIC, DEFAULT_NORMALIZE_PRESET;
4389
- var init_suggest = __esm({
4390
- "src/foundry/similarity/suggest.ts"() {
4391
- DEFAULT_MIN_SCORE = 0.6;
4392
- DEFAULT_MAX_SUGGESTIONS = 3;
4393
- DEFAULT_METRIC = "levenshtein";
4394
- DEFAULT_NORMALIZE_PRESET = "default";
4395
- }
4396
- });
4397
-
4398
- // src/foundry/similarity/index.ts
4399
- var init_similarity = __esm({
4400
- "src/foundry/similarity/index.ts"() {
4401
- init_distance();
4402
- init_errors3();
4403
- init_normalization();
4404
- init_score();
4405
- init_suggest();
4406
- }
4407
- });
4408
814
 
4409
815
  // src/foundry/index.ts
4410
- var VERSION;
4411
- var init_foundry = __esm({
4412
- "src/foundry/index.ts"() {
4413
- init_country_codes();
4414
- init_errors2();
4415
- init_exit_codes();
4416
- init_http_statuses();
4417
- init_loader();
4418
- init_mime_types();
4419
- init_patterns();
4420
- init_signals();
4421
- init_similarity();
4422
- VERSION = "0.1.1";
4423
- }
4424
- });
4425
- init_foundry();
816
+ var VERSION = "0.1.1";
4426
817
 
4427
- export { ConfigReloadTracker, EXIT_CODES_VERSION, FoundryCatalogError, SignalManager, SimplifiedMode, VERSION, casefold, clearCountryCodeCache, clearHttpStatusCache, clearMimeTypeCache, clearPatternCache, createBearerTokenAuth, createConfigReloadEndpoint, createConfigReloadHandler, createControlDiscoveryEndpoint, createDoubleTapTracker, createSignalEndpoint, createSignalManager, createSimpleRateLimiter, describePattern, detectMimeType, detectMimeTypeFromBuffer, detectMimeTypeFromFile, detectMimeTypeFromStream, distance, ensurePOSIX, ensureSignalExitCodesSupported, ensureSupported, ensureWindows, equalsIgnoreCase, exitCodeMetadata, exitCodes, getBehavior, getCountryByAlpha2, getCountryByAlpha3, getCountryByNumeric, getExitCodeInfo, getFallbackMetadata, getHttpFallbackGuidance, getHttpStatus, getMimeType, getMimeTypeByExtension, getPattern, getPatternRegex, getPlatform, getPlatformCapabilities, getSignal, getSignalCatalog, getSignalNumber, getPlatformCapabilities2 as getSignalPlatformCapabilities, getSignalsVersion, getSimplifiedCodeDescription, getSimplifiedCodes, getStatusReason, getWindowTimeRemaining, getWindowsEvent, handleDoubleTap, handleWindowsFallback, isClientError, isInformational, isPOSIX, isRedirection, isServerError, isPOSIX2 as isSignalPOSIX, isWindows2 as isSignalWindows, isSuccess, isSupportedMimeType, isWindows, isWithinWindow, listBehaviors, listCountries, listHttpStatuses, listMimeTypes, listPatterns, listSignals, loadAllCatalogs, loadCountryCodeCatalog, loadHttpStatusCatalog, loadMimeTypeCatalog, loadPatternCatalog, mapExitCodeToSimplified, matchMagicNumber, matchPattern, normalize, onAnyShutdown, onEmergencyQuit, onReload, onShutdown, onUSR1, onUSR2, requiresFallback, resetDoubleTap, score, stripAccents, suggest, supportsSignal, supportsSignalExitCodes2 as supportsSignalBasedExitCodes, supportsSignalExitCodes };
818
+ export { VERSION, clearCountryCodeCache, clearHttpStatusCache, clearMimeTypeCache, clearPatternCache, describePattern, detectMimeType, detectMimeTypeFromBuffer, detectMimeTypeFromFile, detectMimeTypeFromStream, getCountryByAlpha2, getCountryByAlpha3, getCountryByNumeric, getHttpStatus, getMimeType, getMimeTypeByExtension, getPattern, getPatternRegex, getStatusReason, isClientError, isInformational, isRedirection, isServerError, isSuccess, isSupportedMimeType, listCountries, listHttpStatuses, listMimeTypes, listPatterns, loadAllCatalogs, loadCountryCodeCatalog, loadHttpStatusCatalog, loadMimeTypeCatalog, loadPatternCatalog, matchMagicNumber, matchPattern };
4428
819
  //# sourceMappingURL=index.js.map
4429
820
  //# sourceMappingURL=index.js.map