@fulmenhq/tsfulmen 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/appidentity/index.js +6 -6
  3. package/dist/assets/index.d.ts +5 -0
  4. package/dist/assets/index.js +1 -1
  5. package/dist/bin/prometheus-cli.js +6 -6
  6. package/dist/bin/schema-cli.js +2 -2
  7. package/dist/bin/signals-cli.js +4 -4
  8. package/dist/{chunk-HIBZ2AY7.js → chunk-24BAF63H.js} +4 -4
  9. package/dist/{chunk-HIBZ2AY7.js.map → chunk-24BAF63H.js.map} +1 -1
  10. package/dist/{chunk-BTUS6PZZ.js → chunk-56F2EEAG.js} +33 -7
  11. package/dist/chunk-56F2EEAG.js.map +1 -0
  12. package/dist/{chunk-K2FYT43R.js → chunk-7YWACZEJ.js} +4 -4
  13. package/dist/{chunk-K2FYT43R.js.map → chunk-7YWACZEJ.js.map} +1 -1
  14. package/dist/{chunk-CW4CSNVF.js → chunk-GOTEQYAD.js} +3 -3
  15. package/dist/{chunk-CW4CSNVF.js.map → chunk-GOTEQYAD.js.map} +1 -1
  16. package/dist/{chunk-K7BMFGQP.js → chunk-LMNAUTW7.js} +3 -3
  17. package/dist/{chunk-K7BMFGQP.js.map → chunk-LMNAUTW7.js.map} +1 -1
  18. package/dist/{chunk-HS4F3BRR.js → chunk-QGTNV4U2.js} +3 -3
  19. package/dist/{chunk-HS4F3BRR.js.map → chunk-QGTNV4U2.js.map} +1 -1
  20. package/dist/{chunk-7SVYOPJ3.js → chunk-SNV5WG5V.js} +9 -9
  21. package/dist/{chunk-7SVYOPJ3.js.map → chunk-SNV5WG5V.js.map} +1 -1
  22. package/dist/{chunk-BJDL6JEX.js → chunk-VJIGX7MH.js} +4 -4
  23. package/dist/{chunk-BJDL6JEX.js.map → chunk-VJIGX7MH.js.map} +1 -1
  24. package/dist/{chunk-W6TQFTTY.js → chunk-WCCMRVZF.js} +3 -3
  25. package/dist/{chunk-W6TQFTTY.js.map → chunk-WCCMRVZF.js.map} +1 -1
  26. package/dist/config/index.js +2 -2
  27. package/dist/crucible/index.js +2 -2
  28. package/dist/errors/index.js +3 -3
  29. package/dist/errors-R7ZODJXS.js +8 -0
  30. package/dist/{errors-RBJX6ETJ.js.map → errors-R7ZODJXS.js.map} +1 -1
  31. package/dist/export-EA3RNQNS.js +6 -0
  32. package/dist/{export-BWRETGYS.js.map → export-EA3RNQNS.js.map} +1 -1
  33. package/dist/foundry/index.js +5 -5
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.js +7 -7
  36. package/dist/index.js.map +1 -1
  37. package/dist/loader-VP2ZIC4O.js +9 -0
  38. package/dist/{loader-H5K7HSO5.js.map → loader-VP2ZIC4O.js.map} +1 -1
  39. package/dist/pathfinder/index.js +3 -3
  40. package/dist/reports/license-inventory.csv +1 -1
  41. package/dist/schema/index.js +2 -2
  42. package/dist/signals/index.js +4 -4
  43. package/dist/telemetry/http/index.js +6 -6
  44. package/dist/telemetry/index.js +2 -2
  45. package/dist/telemetry/prometheus/index.js +4 -4
  46. package/dist/{validator-U4CKD2Y3.js → validator-I3ASDVDB.js} +4 -4
  47. package/dist/{validator-U4CKD2Y3.js.map → validator-I3ASDVDB.js.map} +1 -1
  48. package/package.json +1 -1
  49. package/dist/chunk-BTUS6PZZ.js.map +0 -1
  50. package/dist/errors-RBJX6ETJ.js +0 -8
  51. package/dist/export-BWRETGYS.js +0 -6
  52. package/dist/loader-H5K7HSO5.js +0 -9
package/CHANGELOG.md CHANGED
@@ -14,6 +14,31 @@ _No unreleased changes._
14
14
 
15
15
  ---
16
16
 
17
+ ## [0.4.1] - 2026-06-26
18
+
19
+ > **Patch — asset-resolver security hardening + CI tooling.** Hardens the public
20
+ > `./assets` surface (secrev follow-ups) and fixes the goneat format gate so Markdown
21
+ > drift is actually caught. No API removals; behavior change is limited to rejecting
22
+ > inputs that were never valid assets. Engine floor unchanged (`>=22.12.0`).
23
+
24
+ ### Security
25
+
26
+ - **`AssetResolver` namespace tightened to the shipped subtree** — logical paths/patterns
27
+ must resolve under `<namespace>/crucible-ts/` (the only subtree the package ships), not
28
+ just a `schemas`/`config`/`docs` top segment. Consumer `baseDir` overrides
29
+ (`enforceNamespace: false`) are unaffected.
30
+ - **`FsAssetResolver` rejects symlink escapes** — `read`/`has` now resolve symlinks
31
+ (`realpath`) and verify the target stays within `baseDir`, so a symlink in the asset
32
+ tree cannot read outside the asset root.
33
+
34
+ ### Changed
35
+
36
+ - **CI/tooling:** `.goneat/assess.yaml` gained a `format:` block so the format gate
37
+ (pre-commit/pre-push hooks + `goneat assess`) actually scopes Markdown/JSON/YAML;
38
+ `.goneatignore` expanded to exclude the Crucible-synced SSOT trees, generated modules,
39
+ and fixtures; and `make fmt` no longer silently no-ops (the `$GONEAT` subshell bug).
40
+ This closes the gap that let Markdown formatting drift slip past the gates in v0.4.0.
41
+
17
42
  ## [0.4.0] - 2026-06-24
18
43
 
19
44
  > **Minor — compile-safe SSOT asset embedding.** Makes every bundled SSOT asset
@@ -1,11 +1,11 @@
1
- export { buildEnvVar, getBinaryName, getConfigIdentifiers, getConfigName, getEnvPrefix, getEnvVar, getTelemetryNamespace, getVendor } from '../chunk-K7BMFGQP.js';
2
- export { APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_FILENAME, APP_IDENTITY_SCHEMA_ID, MAX_ANCESTOR_SEARCH_DEPTH, clearEmbeddedIdentity, clearIdentityCache, getCachedIdentity, getEmbeddedIdentity, hasEmbeddedIdentity, loadIdentity, registerEmbeddedIdentity } from '../chunk-BJDL6JEX.js';
3
- export { AppIdentityError } from '../chunk-W6TQFTTY.js';
1
+ export { buildEnvVar, getBinaryName, getConfigIdentifiers, getConfigName, getEnvPrefix, getEnvVar, getTelemetryNamespace, getVendor } from '../chunk-LMNAUTW7.js';
2
+ export { APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_FILENAME, APP_IDENTITY_SCHEMA_ID, MAX_ANCESTOR_SEARCH_DEPTH, clearEmbeddedIdentity, clearIdentityCache, getCachedIdentity, getEmbeddedIdentity, hasEmbeddedIdentity, loadIdentity, registerEmbeddedIdentity } from '../chunk-VJIGX7MH.js';
3
+ export { AppIdentityError } from '../chunk-WCCMRVZF.js';
4
4
  export { buildRuntimeInfo } from '../chunk-HY6JQOFN.js';
5
- import '../chunk-CW4CSNVF.js';
6
- import '../chunk-7SVYOPJ3.js';
5
+ import '../chunk-GOTEQYAD.js';
6
+ import '../chunk-SNV5WG5V.js';
7
7
  import '../chunk-QCCN6POA.js';
8
- import '../chunk-BTUS6PZZ.js';
8
+ import '../chunk-56F2EEAG.js';
9
9
  import '../chunk-UP2VWCW5.js';
10
10
  //# sourceMappingURL=index.js.map
11
11
  //# sourceMappingURL=index.js.map
@@ -134,6 +134,11 @@ declare class FsAssetResolver implements AssetResolver {
134
134
  constructor(baseDir: string, enforceNamespace?: boolean);
135
135
  private resolve;
136
136
  read(logicalPath: string): Promise<string>;
137
+ /** Cached realpath of baseDir (resolves symlinks once). */
138
+ private baseRealCache?;
139
+ private baseReal;
140
+ /** Whether a resolved (realpath) target stays within the resolver's baseDir. */
141
+ private isWithinBase;
137
142
  list(patterns: string[]): Promise<string[]>;
138
143
  has(logicalPath: string): Promise<boolean>;
139
144
  provenance(): AssetProvenance;
@@ -1,4 +1,4 @@
1
- export { AssetResolutionError, EmbeddedAssetResolver, FsAssetResolver, clearEmbeddedAssets, findAssetBaseDir, getAssetResolver, hasEmbeddedAssets, hasEmbeddedDomain, registerEmbeddedAssets, resetAssetResolver, resolveAssets } from '../chunk-BTUS6PZZ.js';
1
+ export { AssetResolutionError, EmbeddedAssetResolver, FsAssetResolver, clearEmbeddedAssets, findAssetBaseDir, getAssetResolver, hasEmbeddedAssets, hasEmbeddedDomain, registerEmbeddedAssets, resetAssetResolver, resolveAssets } from '../chunk-56F2EEAG.js';
2
2
  import '../chunk-UP2VWCW5.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { PrometheusExporter, startMetricsServer, registerPrometheusShutdown, stopMetricsServer, PromClientNotFoundError } from '../chunk-K2FYT43R.js';
3
- import { createSignalManager } from '../chunk-HS4F3BRR.js';
2
+ import { PrometheusExporter, startMetricsServer, registerPrometheusShutdown, stopMetricsServer, PromClientNotFoundError } from '../chunk-7YWACZEJ.js';
3
+ import { createSignalManager } from '../chunk-QGTNV4U2.js';
4
4
  import { exitCodes } from '../chunk-RSLFR4TO.js';
5
- import '../chunk-HIBZ2AY7.js';
5
+ import '../chunk-24BAF63H.js';
6
6
  import { createLogger } from '../chunk-5HZEJGZ5.js';
7
7
  import '../chunk-HY6JQOFN.js';
8
8
  import '../chunk-3HISR2XI.js';
9
- import '../chunk-CW4CSNVF.js';
10
- import { metrics } from '../chunk-7SVYOPJ3.js';
9
+ import '../chunk-GOTEQYAD.js';
10
+ import { metrics } from '../chunk-SNV5WG5V.js';
11
11
  import '../chunk-QCCN6POA.js';
12
- import '../chunk-BTUS6PZZ.js';
12
+ import '../chunk-56F2EEAG.js';
13
13
  import '../chunk-UP2VWCW5.js';
14
14
  import { Command } from 'commander';
15
15
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { createCLI } from '../chunk-7SVYOPJ3.js';
2
+ import { createCLI } from '../chunk-SNV5WG5V.js';
3
3
  import '../chunk-QCCN6POA.js';
4
- import '../chunk-BTUS6PZZ.js';
4
+ import '../chunk-56F2EEAG.js';
5
5
  import '../chunk-UP2VWCW5.js';
6
6
 
7
7
  // src/bin/schema-cli.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { exitCodes } from '../chunk-RSLFR4TO.js';
3
- import { getBehavior, listBehaviors, getSignal, supportsSignal, getPlatformCapabilities, listSignals, getSignalsVersion, supportsSignalExitCodes } from '../chunk-HIBZ2AY7.js';
3
+ import { getBehavior, listBehaviors, getSignal, supportsSignal, getPlatformCapabilities, listSignals, getSignalsVersion, supportsSignalExitCodes } from '../chunk-24BAF63H.js';
4
4
  import '../chunk-3HISR2XI.js';
5
- import '../chunk-7SVYOPJ3.js';
5
+ import '../chunk-SNV5WG5V.js';
6
6
  import '../chunk-QCCN6POA.js';
7
- import '../chunk-BTUS6PZZ.js';
7
+ import '../chunk-56F2EEAG.js';
8
8
  import '../chunk-UP2VWCW5.js';
9
9
  import { readFile } from 'fs/promises';
10
10
  import { Command } from 'commander';
@@ -131,7 +131,7 @@ function createSignalsCLI() {
131
131
  } else {
132
132
  throw new Error("Unsupported file format. Use .json, .yaml, or .yml");
133
133
  }
134
- const { validateDataBySchemaId } = await import('../validator-U4CKD2Y3.js');
134
+ const { validateDataBySchemaId } = await import('../validator-I3ASDVDB.js');
135
135
  const result = await validateDataBySchemaId(
136
136
  "library/foundry/v1.0.0/signals",
137
137
  data
@@ -1,6 +1,6 @@
1
1
  import { FoundryCatalogError } from './chunk-3HISR2XI.js';
2
- import { validateDataBySchemaId } from './chunk-7SVYOPJ3.js';
3
- import { registerEmbeddedAssets, getAssetResolver } from './chunk-BTUS6PZZ.js';
2
+ import { validateDataBySchemaId } from './chunk-SNV5WG5V.js';
3
+ import { registerEmbeddedAssets, getAssetResolver } from './chunk-56F2EEAG.js';
4
4
  import { parse } from 'yaml';
5
5
 
6
6
  // src/assets/generated/foundry.generated.ts
@@ -190,5 +190,5 @@ async function getWindowsEvent(signalName) {
190
190
  }
191
191
 
192
192
  export { ensureFoundryAssetsRegistered, getBehavior, getPlatform, getPlatformCapabilities, getSignal, getSignalCatalog, getSignalNumber, getSignalsVersion, getWindowsEvent, isPOSIX, isWindows, listBehaviors, listSignals, supportsSignal, supportsSignalExitCodes };
193
- //# sourceMappingURL=chunk-HIBZ2AY7.js.map
194
- //# sourceMappingURL=chunk-HIBZ2AY7.js.map
193
+ //# sourceMappingURL=chunk-24BAF63H.js.map
194
+ //# sourceMappingURL=chunk-24BAF63H.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/assets/generated/foundry.generated.ts","../src/foundry/embedded-assets.ts","../src/foundry/signals/catalog.ts","../src/foundry/signals/capabilities.ts"],"names":["parseYaml"],"mappings":"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","file":"chunk-HIBZ2AY7.js","sourcesContent":["// AUTO-GENERATED by scripts/embed-assets.ts. DO NOT EDIT.\n// Run `make embed-assets` to regenerate after `make sync-ssot`.\n// Domain: foundry (5 assets)\nimport type { EmbeddedAssetManifest } from \"../types.js\";\n\nexport const manifest: EmbeddedAssetManifest = {\n domain: \"foundry\",\n files: {\n \"config/crucible-ts/library/foundry/country-codes.yaml\": \"$schema: https://schemas.fulmenhq.dev/library/foundry/v1.0.0/country-codes.schema.json\\ndescription: Sample ISO 3166 country codes for helper-library lookups.\\nversion: v0.1.0\\ncountries:\\n - alpha2: US\\n alpha3: USA\\n numeric: '840'\\n name: United States of America\\n officialName: United States of America\\n - alpha2: CA\\n alpha3: CAN\\n numeric: '124'\\n name: Canada\\n officialName: Canada\\n - alpha2: JP\\n alpha3: JPN\\n numeric: '392'\\n name: Japan\\n officialName: Japan\\n - alpha2: DE\\n alpha3: DEU\\n numeric: '276'\\n name: Germany\\n officialName: Federal Republic of Germany\\n - alpha2: BR\\n alpha3: BRA\\n numeric: '076'\\n name: Brazil\\n officialName: Federative Republic of Brazil\\n\",\n \"config/crucible-ts/library/foundry/http-statuses.yaml\": \"$schema: https://schemas.fulmenhq.dev/library/foundry/v1.0.0/http-status-groups.schema.json\\ndescription: HTTP status code groups shared across helper libraries.\\nversion: v0.1.0\\ngroups:\\n - id: informational\\n name: Informational Responses\\n description: '1xx HTTP status codes indicating provisional responses.'\\n codes:\\n - value: 100\\n reason: Continue\\n - value: 101\\n reason: Switching Protocols\\n - value: 102\\n reason: Processing\\n - value: 103\\n reason: Early Hints\\n - id: success\\n name: Successful Responses\\n description: '2xx HTTP status codes indicating successful requests.'\\n codes:\\n - value: 200\\n reason: OK\\n - value: 201\\n reason: Created\\n - value: 202\\n reason: Accepted\\n - value: 203\\n reason: Non-Authoritative Information\\n - value: 204\\n reason: No Content\\n - value: 205\\n reason: Reset Content\\n - value: 206\\n reason: Partial Content\\n - value: 207\\n reason: Multi-Status\\n - value: 208\\n reason: Already Reported\\n - value: 226\\n reason: IM Used\\n - id: redirect\\n name: Redirection Responses\\n description: '3xx HTTP status codes indicating redirects.'\\n codes:\\n - value: 300\\n reason: Multiple Choices\\n - value: 301\\n reason: Moved Permanently\\n - value: 302\\n reason: Found\\n - value: 303\\n reason: See Other\\n - value: 304\\n reason: Not Modified\\n - value: 305\\n reason: Use Proxy\\n - value: 307\\n reason: Temporary Redirect\\n - value: 308\\n reason: Permanent Redirect\\n - id: client-error\\n name: Client Error Responses\\n description: '4xx HTTP status codes indicating client errors.'\\n codes:\\n - value: 400\\n reason: Bad Request\\n - value: 401\\n reason: Unauthorized\\n - value: 402\\n reason: Payment Required\\n - value: 403\\n reason: Forbidden\\n - value: 404\\n reason: Not Found\\n - value: 405\\n reason: Method Not Allowed\\n - value: 406\\n reason: Not Acceptable\\n - value: 407\\n reason: Proxy Authentication Required\\n - value: 408\\n reason: Request Timeout\\n - value: 409\\n reason: Conflict\\n - value: 410\\n reason: Gone\\n - value: 411\\n reason: Length Required\\n - value: 412\\n reason: Precondition Failed\\n - value: 413\\n reason: Content Too Large\\n - value: 414\\n reason: URI Too Long\\n - value: 415\\n reason: Unsupported Media Type\\n - value: 416\\n reason: Range Not Satisfiable\\n - value: 417\\n reason: Expectation Failed\\n - value: 418\\n reason: I'm a teapot\\n - value: 421\\n reason: Misdirected Request\\n - value: 422\\n reason: Unprocessable Content\\n - value: 423\\n reason: Locked\\n - value: 424\\n reason: Failed Dependency\\n - value: 425\\n reason: Too Early\\n - value: 426\\n reason: Upgrade Required\\n - value: 428\\n reason: Precondition Required\\n - value: 429\\n reason: Too Many Requests\\n - value: 431\\n reason: Request Header Fields Too Large\\n - value: 451\\n reason: Unavailable For Legal Reasons\\n - id: server-error\\n name: Server Error Responses\\n description: '5xx HTTP status codes indicating server errors.'\\n codes:\\n - value: 500\\n reason: Internal Server Error\\n - value: 501\\n reason: Not Implemented\\n - value: 502\\n reason: Bad Gateway\\n - value: 503\\n reason: Service Unavailable\\n - value: 504\\n reason: Gateway Timeout\\n - value: 505\\n reason: HTTP Version Not Supported\\n - value: 506\\n reason: Variant Also Negotiates\\n - value: 507\\n reason: Insufficient Storage\\n - value: 508\\n reason: Loop Detected\\n - value: 510\\n reason: Not Extended\\n - value: 511\\n reason: Network Authentication Required\\n\",\n \"config/crucible-ts/library/foundry/mime-types.yaml\": \"$schema: https://schemas.fulmenhq.dev/library/foundry/v1.0.0/mime-types.schema.json\\ndescription: Common MIME types that helper libraries should recognize.\\nversion: v0.1.0\\ntypes:\\n - id: json\\n mime: application/json\\n name: JSON\\n extensions: [json, map]\\n description: JavaScript Object Notation\\n - id: yaml\\n mime: application/yaml\\n name: YAML\\n extensions: [yaml, yml]\\n description: YAML Ain't Markup Language\\n - id: ndjson\\n mime: application/x-ndjson\\n name: NDJSON\\n extensions: [ndjson]\\n description: Newline-delimited JSON\\n - id: csv\\n mime: text/csv\\n name: CSV\\n extensions: [csv]\\n description: Comma-separated values\\n - id: xml\\n mime: application/xml\\n name: XML\\n extensions: [xml]\\n description: Extensible Markup Language\\n - id: protobuf\\n mime: application/x-protobuf\\n name: Protocol Buffers\\n extensions: [pb, proto]\\n description: Google Protocol Buffers binary format\\n - id: plain-text\\n mime: text/plain\\n name: Plain Text\\n extensions: [txt]\\n description: UTF-8 plain text\\n\",\n \"config/crucible-ts/library/foundry/patterns.yaml\": \"$schema: https://schemas.fulmenhq.dev/library/foundry/v1.0.0/patterns.schema.json\\ndescription: Canonical regex/glob/literal patterns shared across Fulmen helper libraries.\\nversion: v0.1.0\\npatterns:\\n - id: ansi-email\\n name: RFC 5322 Email\\n kind: regex\\n pattern: '^[\\\\p{Letter}\\\\p{Number}._%+-]+@[\\\\p{Letter}\\\\p{Number}.-]+\\\\.[\\\\p{Letter}]{2,}$'\\n flags:\\n python:\\n unicode: true\\n ignoreCase: true\\n go:\\n unicode: true\\n ignoreCase: true\\n typescript:\\n ignoreCase: true\\n rust:\\n unicode: true\\n ignoreCase: true\\n csharp:\\n unicode: true\\n ignoreCase: true\\n description: 'Internationalized email address with Unicode letters; case-insensitive.'\\n examples:\\n - 'user@example.com'\\n - '你好@例子.公司'\\n - id: slug\\n name: Slug (kebab/underscore)\\n kind: regex\\n pattern: '^[a-z0-9]+(?:[-_][a-z0-9]+)*$'\\n description: 'Lowercase slug with hyphen/underscore separators.'\\n examples:\\n - 'fulmen-hq'\\n - 'data_pipeline'\\n - id: path-segment\\n name: Path Segment\\n kind: regex\\n pattern: '^[a-z0-9]+(?:[/_-][a-z0-9]+)*$'\\n description: 'Lowercase path fragment allowing slash, underscore, hyphen separators.'\\n examples:\\n - 'foo/bar'\\n - 'docs_v1'\\n - id: identifier\\n name: Identifier (Alpha + underscore)\\n kind: regex\\n pattern: '^[A-Za-z][A-Za-z0-9_]*[A-Za-z0-9]$'\\n description: 'Camel/snake compatible identifier, starts with letter.'\\n examples:\\n - 'ConfigValue'\\n - 'config_value1'\\n - id: uri-scheme\\n name: URI Scheme (RFC 3986)\\n kind: regex\\n pattern: '^[a-z][a-z0-9+.-]*$'\\n description: 'Lowercase URI scheme (e.g., http, git+ssh).'\\n examples:\\n - 'https'\\n - 'git+ssh'\\n - id: logger-name\\n name: Logger Name\\n kind: regex\\n pattern: '^[A-Za-z][A-Za-z0-9_]*(?:\\\\.[A-Za-z][A-Za-z0-9_]*)*$'\\n description: 'Dot-separated logger names (e.g., package.module.component).'\\n examples:\\n - 'fulmen.logger'\\n - 'app.core.worker'\\n - id: env-prefix\\n name: Environment Prefix\\n kind: regex\\n pattern: '^[A-Z][A-Z0-9_]*_$'\\n description: 'Uppercase environment variable prefix ending with underscore.'\\n examples:\\n - 'FULMEN_'\\n - 'CRUCIBLE_'\\n - id: tag\\n name: Tag (ASCII)\\n kind: regex\\n pattern: '^[A-Za-z0-9][A-Za-z0-9_\\\\-\\\\s]*[A-Za-z0-9]$'\\n description: 'Label allowing spaces, underscores, hyphens; no leading/trailing separators.'\\n examples:\\n - 'stable release'\\n - 'beta-1'\\n - id: unicode-tag\\n name: Tag (Unicode)\\n kind: regex\\n pattern: '^[\\\\p{L}\\\\p{N}][\\\\p{L}\\\\p{N}_\\\\-\\\\s]*[\\\\p{L}\\\\p{N}]$'\\n flags:\\n python:\\n unicode: true\\n go:\\n unicode: true\\n rust:\\n unicode: true\\n csharp:\\n unicode: true\\n description: 'Unicode-aware tag allowing letters/numbers from any script.'\\n examples:\\n - '国际化 标签'\\n - 'データ-01'\\n - id: domain-name\\n name: Fully Qualified Domain Name\\n kind: regex\\n pattern: '^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\\\.)+[a-z]{2,63}$'\\n description: 'Lowercase ASCII domain labels per RFC 1035 (no wildcard).'\\n examples:\\n - 'example.com'\\n - 'api.fulmenhq.net'\\n - id: ipv6\\n name: IPv6 Address (Full/Compressed)\\n kind: regex\\n pattern: '^(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}$|^(?:[A-Fa-f0-9]{1,4}:){1,7}:$|^:(?::[A-Fa-f0-9]{1,4}){1,7}$|^(?:[A-Fa-f0-9]{1,4}:){1,6}:[A-Fa-f0-9]{1,4}$|^(?:[A-Fa-f0-9]{1,4}:){1,5}(?::[A-Fa-f0-9]{1,4}){1,2}$|^(?:[A-Fa-f0-9]{1,4}:){1,4}(?::[A-Fa-f0-9]{1,4}){1,3}$|^(?:[A-Fa-f0-9]{1,4}:){1,3}(?::[A-Fa-f0-9]{1,4}){1,4}$|^(?:[A-Fa-f0-9]{1,4}:){1,2}(?::[A-Fa-f0-9]{1,4}){1,5}$|^[A-Fa-f0-9]{1,4}:(?:(?::[A-Fa-f0-9]{1,4}){1,6})$|^:(?::[A-Fa-f0-9]{1,4}){1,7}$|^(?:[A-Fa-f0-9]{1,4}:){1,4}:(?:[A-Fa-f0-9]{1,4}:){1,2}[A-Fa-f0-9]{1,4}$'\\n description: 'IPv6 address with support for shorthand compression (RFC 4291).'\\n examples:\\n - '2001:0db8:85a3:0000:0000:8a2e:0370:7334'\\n - '2001:db8::1428:57ab'\\n - id: hex-lower\\n name: Lowercase Hex String\\n kind: regex\\n pattern: '^[0-9a-f]+$'\\n description: 'Lowercase hexadecimal characters (length validated by consumer).'\\n examples:\\n - 'deadbeef'\\n - '0123456789abcdef'\\n - id: base64\\n name: Base64 Encoded String\\n kind: regex\\n pattern: '^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'\\n description: 'Standard Base64 encoding with padding.'\\n examples:\\n - 'SGVsbG8gd29ybGQ='\\n - id: jwt\\n name: JSON Web Token (compact)\\n kind: regex\\n pattern: '^[A-Za-z0-9-_]+\\\\.[A-Za-z0-9-_]+\\\\.[A-Za-z0-9-_]+$'\\n description: 'JWT compact serialization (header.payload.signature).'\\n examples:\\n - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U'\\n - id: strict-iso-date\\n name: ISO Date (YYYY-MM-DD)\\n kind: regex\\n pattern: '^\\\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\\\d|3[01])$'\\n description: 'ISO 8601 calendar date with validated month/day ranges.'\\n examples:\\n - '2025-10-09'\\n - id: iso-timestamp-z\\n name: ISO Timestamp (UTC)\\n kind: regex\\n pattern: '^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d{1,6})?Z$'\\n description: 'UTC timestamp with optional fractional seconds.'\\n examples:\\n - '2025-10-09T14:15:16Z'\\n - '2025-10-09T14:15:16.123456Z'\\n - id: uuid-v4\\n name: UUID v4\\n kind: regex\\n pattern: '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$'\\n description: 'RFC 4122 version 4 UUID.'\\n examples:\\n - '123e4567-e89b-42d3-a456-426614174000'\\n - id: ulid\\n name: ULID\\n kind: regex\\n pattern: '^[0-9A-HJKMNP-TV-Z]{26}$'\\n description: 'Crockford-base32 ULID (26 chars, upper-case).'\\n examples:\\n - '01HCP5MZF1H84D0CW7V5T9FQ0P'\\n - id: semantic-version\\n name: Semantic Version\\n kind: regex\\n pattern: '^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-[0-9A-Za-z-]+(?:\\\\.[0-9A-Za-z-]+)*)?(?:\\\\+[0-9A-Za-z-]+(?:\\\\.[0-9A-Za-z-]+)*)?$'\\n description: 'SemVer 2.0.0 specification-compliant version string.'\\n examples:\\n - '1.2.3'\\n - '1.2.3-beta.1+build.5'\\n - id: glob-any-json\\n name: Glob – JSON Files\\n kind: glob\\n pattern: '**/*.json'\\n description: 'Recursive glob matching all JSON files.'\\n - id: glob-any-yaml\\n name: Glob – YAML Files\\n kind: glob\\n pattern: '**/*.{yml,yaml}'\\n description: 'Recursive glob matching YAML files.'\\n\",\n \"config/crucible-ts/library/foundry/signals.yaml\": \"$schema: https://schemas.fulmenhq.dev/library/foundry/v1.0.0/signals.schema.json\\ndescription: Standard signal handling semantics for Fulmen ecosystem\\nversion: v1.0.0\\nsignals:\\n - id: term\\n name: SIGTERM\\n unix_number: 15\\n windows_event: CTRL_CLOSE_EVENT\\n description: Graceful termination signal\\n default_behavior: graceful_shutdown\\n exit_code: 143 # 128 + 15\\n timeout_seconds: 30\\n cleanup_actions:\\n - close_connections\\n - flush_buffers\\n - remove_pid_file\\n - log_shutdown\\n usage_notes: Standard termination signal sent by container orchestrators (Kubernetes, Docker). Applications should perform cleanup within timeout period before exiting.\\n - id: int\\n name: SIGINT\\n unix_number: 2\\n windows_event: CTRL_C_EVENT\\n description: User interrupt (Ctrl+C)\\n default_behavior: graceful_shutdown_with_double_tap\\n exit_code: 130 # 128 + 2\\n timeout_seconds: 5\\n double_tap_window_seconds: 2\\n double_tap_message: \\\"Press Ctrl+C again within 2s to force quit\\\"\\n double_tap_behavior: immediate_exit\\n double_tap_exit_code: 130\\n cleanup_actions:\\n - close_connections\\n - flush_buffers\\n - remove_pid_file\\n - log_interrupt\\n usage_notes: Interactive interrupt signal (Ctrl+C). First press initiates graceful shutdown, second press within 2s forces immediate exit for hung processes.\\n - id: hup\\n name: SIGHUP\\n unix_number: 1\\n windows_event: null\\n description: Hangup signal (config reload via restart)\\n default_behavior: reload_via_restart\\n exit_code: 129 # 128 + 1\\n reload_strategy: restart_based\\n validation_required: true\\n timeout_seconds: 30\\n cleanup_actions:\\n - validate_new_config_against_schema\\n - graceful_shutdown\\n - restart_with_new_config\\n - log_reload\\n usage_notes: Historically \\\"hangup\\\" (terminal disconnect). In Fulmen, triggers config reload via restart. MANDATORY schema validation before restart. On Windows, use HTTP /admin/signal endpoint as fallback.\\n windows_fallback:\\n fallback_behavior: http_admin_endpoint\\n log_level: INFO\\n log_message: \\\"SIGHUP unavailable on Windows - use HTTP endpoint for config reload\\\"\\n log_template: \\\"signal=${signal} platform=${platform} fallback=${fallback_behavior} message='${operation_hint}'\\\"\\n operation_hint: \\\"POST /admin/signal with signal=HUP\\\"\\n telemetry_event: fulmen.signal.unsupported\\n telemetry_tags:\\n signal: SIGHUP\\n platform: windows\\n fallback_behavior: http_admin_endpoint\\n - id: quit\\n name: SIGQUIT\\n unix_number: 3\\n windows_event: CTRL_BREAK_EVENT\\n description: Quit with core dump (immediate termination)\\n default_behavior: immediate_exit\\n exit_code: 131 # 128 + 3\\n timeout_seconds: 0\\n usage_notes: Immediate termination signal (Ctrl+\\\\\\\\ on Unix, Ctrl+Break on Windows). Does not perform cleanup. Use for emergency shutdown or debugging (core dumps).\\n - id: pipe\\n name: SIGPIPE\\n unix_number: 13\\n windows_event: null\\n description: Broken pipe (write to closed socket/pipe)\\n default_behavior: observe_only\\n exit_code: 141 # 128 + 13\\n usage_notes: Default POSIX behavior terminates process immediately. Fulmen default is observe_only (log at INFO level + exit gracefully with code 141). Applications may override to ignore SIGPIPE for network services that expect broken pipes. Long-running servers typically ignore; CLI tools typically exit.\\n windows_fallback:\\n fallback_behavior: exception_handling\\n log_level: INFO\\n log_message: \\\"SIGPIPE unavailable on Windows - broken pipes raise exceptions instead\\\"\\n log_template: \\\"signal=${signal} platform=${platform} fallback=${fallback_behavior} message='${operation_hint}'\\\"\\n operation_hint: \\\"Handle BrokenPipeError (Python), EPIPE (Go), EPIPE (C)\\\"\\n telemetry_event: fulmen.signal.unsupported\\n telemetry_tags:\\n signal: SIGPIPE\\n platform: windows\\n fallback_behavior: exception_handling\\n - id: alrm\\n name: SIGALRM\\n unix_number: 14\\n windows_event: null\\n description: Alarm timer expired (watchdog timeout)\\n default_behavior: immediate_exit\\n exit_code: 142 # 128 + 14\\n timeout_seconds: 0\\n usage_notes: Used by watchdog timers. Treat as timeout-induced exit unless application overrides. Watchdog pattern - call signal.alarm(N) to set timer, reset periodically with progress; if no reset within N seconds, SIGALRM fires and process exits. Out of scope for v1.0.0 module implementations.\\n windows_fallback:\\n fallback_behavior: timer_api\\n log_level: INFO\\n log_message: \\\"SIGALRM unavailable on Windows - use native timer APIs instead\\\"\\n log_template: \\\"signal=${signal} platform=${platform} fallback=${fallback_behavior} message='${operation_hint}'\\\"\\n operation_hint: \\\"Use threading.Timer (Python), time.AfterFunc (Go), setTimeout (TypeScript)\\\"\\n telemetry_event: fulmen.signal.unsupported\\n telemetry_tags:\\n signal: SIGALRM\\n platform: windows\\n fallback_behavior: timer_api\\n - id: usr1\\n name: SIGUSR1\\n unix_number: 10\\n platform_overrides:\\n darwin: 30\\n freebsd: 30\\n windows_event: null\\n description: User-defined signal 1\\n default_behavior: custom\\n exit_code: 138 # 128 + 10\\n usage_notes: Application-specific signal (e.g., reopen log files, dump statistics, toggle verbose logging). Helper libraries provide registration API; application provides custom handler.\\n windows_fallback:\\n fallback_behavior: http_admin_endpoint\\n log_level: INFO\\n log_message: \\\"SIGUSR1 unavailable on Windows - use HTTP endpoint for custom operations\\\"\\n log_template: \\\"signal=${signal} platform=${platform} fallback=${fallback_behavior} message='${operation_hint}'\\\"\\n operation_hint: \\\"POST /admin/signal with signal=USR1\\\"\\n telemetry_event: fulmen.signal.unsupported\\n telemetry_tags:\\n signal: SIGUSR1\\n platform: windows\\n fallback_behavior: http_admin_endpoint\\n - id: usr2\\n name: SIGUSR2\\n unix_number: 12\\n platform_overrides:\\n darwin: 31\\n freebsd: 31\\n windows_event: null\\n description: User-defined signal 2\\n default_behavior: custom\\n exit_code: 140 # 128 + 12\\n usage_notes: Application-specific signal (e.g., toggle debug mode, trigger profiling, rotate credentials). Helper libraries provide registration API; application provides custom handler.\\n windows_fallback:\\n fallback_behavior: http_admin_endpoint\\n log_level: INFO\\n log_message: \\\"SIGUSR2 unavailable on Windows - use HTTP endpoint for custom operations\\\"\\n log_template: \\\"signal=${signal} platform=${platform} fallback=${fallback_behavior} message='${operation_hint}'\\\"\\n operation_hint: \\\"POST /admin/signal with signal=USR2\\\"\\n telemetry_event: fulmen.signal.unsupported\\n telemetry_tags:\\n signal: SIGUSR2\\n platform: windows\\n fallback_behavior: http_admin_endpoint\\n - id: kill\\n name: SIGKILL\\n unix_number: 9\\n windows_event: null\\n description: Force immediate termination - cannot be caught, blocked, or ignored\\n default_behavior: immediate_exit\\n exit_code: 137 # 128 + 9\\n timeout_seconds: 0\\n cleanup_actions: []\\n usage_notes: |\\n SIGKILL terminates a process immediately without allowing cleanup.\\n Use only as a last resort after SIGTERM grace period expires. The process\\n has no opportunity to save state, close files, or release resources.\\n On Windows, use TerminateProcess() for single process or TerminateJobObject() for groups.\\nbehaviors:\\n - id: graceful_shutdown\\n name: Graceful Shutdown\\n description: Clean shutdown with timeout and cleanup chain\\n phases:\\n - name: notify\\n description: Log signal receipt with correlation ID\\n - name: stop_accepting\\n description: Stop accepting new work (close listeners, reject requests)\\n - name: drain\\n description: Complete in-flight operations within timeout\\n - name: cleanup\\n description: Run registered cleanup handlers in order\\n - name: exit\\n description: Exit with appropriate code (128+N pattern)\\n - id: graceful_shutdown_with_double_tap\\n name: Graceful Shutdown with Double-Tap\\n description: Graceful shutdown with force-quit option (Ctrl+C pattern)\\n phases:\\n - name: first_signal\\n description: First signal starts graceful shutdown and prints hint message\\n - name: notify\\n description: Log signal receipt with correlation ID\\n - name: stop_accepting\\n description: Stop accepting new work\\n - name: drain\\n description: Complete in-flight operations within timeout\\n - name: cleanup\\n description: Run registered cleanup handlers in order\\n - name: second_signal_check\\n description: If second signal within window, force immediate exit\\n - name: exit\\n description: Exit with appropriate code\\n - id: reload_via_restart\\n name: Configuration Reload via Restart\\n description: Reload configuration by validating and restarting process\\n phases:\\n - name: notify\\n description: Log reload request with correlation ID\\n - name: validate\\n description: Validate new configuration against Crucible schema (MANDATORY)\\n - name: reject_invalid\\n description: If invalid, log error and continue with current config (no restart)\\n - name: graceful_shutdown\\n description: If valid, initiate graceful shutdown\\n - name: exit\\n description: Exit with code 129 (SIGHUP) - process supervisor restarts\\n - name: restart\\n description: Process supervisor (systemd, K8s, etc.) restarts process with new config\\n - id: immediate_exit\\n name: Immediate Exit\\n description: Exit immediately without cleanup (emergency shutdown)\\n phases:\\n - name: notify\\n description: Log signal receipt (no correlation ID needed - too fast)\\n - name: exit\\n description: Exit immediately with appropriate code\\n - id: custom\\n name: Custom Handler\\n description: Application-specific behavior defined by handler registration\\n phases:\\n - name: delegate\\n description: Call application-provided handler function\\n - name: log_completion\\n description: Log custom handler completion (do not exit unless handler exits)\\n - id: observe_only\\n name: Observe Only\\n description: Log the signal and increment telemetry without exiting\\n phases:\\n - name: notify\\n description: Log signal receipt at INFO level\\n - name: telemetry\\n description: Increment signal counter in telemetry system\\n - name: continue\\n description: Continue execution (do not exit)\\nos_mappings:\\n unix:\\n SIGTERM: 15\\n SIGINT: 2\\n SIGHUP: 1\\n SIGQUIT: 3\\n SIGPIPE: 13\\n SIGALRM: 14\\n SIGUSR1: 10 # Linux standard\\n SIGUSR2: 12 # Linux standard\\n SIGKILL: 9 # Not catchable, documented for completeness\\n windows:\\n CTRL_C_EVENT: 0\\n CTRL_BREAK_EVENT: 1\\n CTRL_CLOSE_EVENT: 2\\n CTRL_LOGOFF_EVENT: 5\\n CTRL_SHUTDOWN_EVENT: 6\\n # Platform-specific signal number overrides\\n platform_overrides:\\n darwin: # macOS\\n SIGUSR1: 30\\n SIGUSR2: 31\\n freebsd:\\n SIGUSR1: 30\\n SIGUSR2: 31\\n # Mapping Unix signals to Windows console events\\n signal_to_event:\\n SIGTERM: CTRL_CLOSE_EVENT\\n SIGINT: CTRL_C_EVENT\\n SIGQUIT: CTRL_BREAK_EVENT\\n SIGHUP: null # No Windows equivalent - use HTTP fallback\\n SIGPIPE: null # No Windows equivalent - handled via exceptions\\n SIGALRM: null # No Windows equivalent - use timers\\n SIGUSR1: null # No Windows equivalent - use HTTP fallback\\n SIGUSR2: null # No Windows equivalent - use HTTP fallback\\n# Platform support matrix for helper library implementations\\nplatform_support:\\n - signal: SIGTERM\\n linux: native\\n macos: native\\n freebsd: native\\n windows: mapped # CTRL_CLOSE_EVENT\\n notes: Fully supported across all platforms\\n - signal: SIGINT\\n linux: native\\n macos: native\\n freebsd: native\\n windows: mapped # CTRL_C_EVENT\\n notes: Fully supported across all platforms\\n - signal: SIGHUP\\n linux: native\\n macos: native\\n freebsd: native\\n windows: unsupported\\n fallback: http_admin_endpoint\\n notes: On Windows, use HTTP POST /admin/signal with signal=HUP\\n - signal: SIGQUIT\\n linux: native\\n macos: native\\n freebsd: native\\n windows: mapped # CTRL_BREAK_EVENT\\n notes: Fully supported across all platforms\\n - signal: SIGPIPE\\n linux: native\\n macos: native\\n freebsd: native\\n windows: unsupported\\n fallback: exception_handling\\n notes: On Windows, broken pipes generate exceptions instead of signals\\n - signal: SIGALRM\\n linux: native\\n macos: native\\n freebsd: native\\n windows: unsupported\\n fallback: timer_api\\n notes: On Windows, use threading.Timer (Python) or setTimeout (TypeScript)\\n - signal: SIGUSR1\\n linux: native\\n macos: native # signal 30\\n freebsd: native # signal 30\\n windows: unsupported\\n fallback: http_admin_endpoint\\n notes: Signal number varies by platform. On Windows, use HTTP POST /admin/signal with signal=USR1\\n - signal: SIGUSR2\\n linux: native\\n macos: native # signal 31\\n freebsd: native # signal 31\\n windows: unsupported\\n fallback: http_admin_endpoint\\n notes: Signal number varies by platform. On Windows, use HTTP POST /admin/signal with signal=USR2\\n - signal: SIGKILL\\n linux: native\\n macos: native\\n freebsd: native\\n windows: mapped\\n notes: Cannot be caught or ignored. Windows uses TerminateProcess/TerminateJobObject.\\n# Exit code mappings (128+N pattern per POSIX)\\nexit_codes:\\n SIGTERM: 143 # 128 + 15\\n SIGINT: 130 # 128 + 2\\n SIGHUP: 129 # 128 + 1\\n SIGQUIT: 131 # 128 + 3\\n SIGKILL: 137 # 128 + 9\\n SIGPIPE: 141 # 128 + 13\\n SIGALRM: 142 # 128 + 14\\n SIGUSR1: 138 # 128 + 10\\n SIGUSR2: 140 # 128 + 12\\n note: Exit codes follow POSIX 128+N pattern where N is the signal number. These codes are cross-referenced in config/library/foundry/exit-codes.yaml.\\n\",\n },\n};\n\nexport default manifest;\n","/**\n * Static per-subpath embedded-asset registration for the foundry subpath (v0.4.0).\n *\n * Registers only the `foundry` domain (catalogs) by importing its per-domain\n * generated module directly (not `generated/index`). Note: the foundry subpath\n * still transitively reaches the schema corpus via `validateDataBySchemaId`; under\n * tsup `splitting:true` that corpus lives once in a shared chunk (deduped), so the\n * invariant is \"public entries stay lean; corpus lives once in shared chunks\" — not\n * \"foundry never references the schema corpus\". Unused in filesystem mode; required\n * for foundry catalog loading inside a `bun --compile` binary.\n */\n\nimport { manifest as foundryManifest } from \"../assets/generated/foundry.generated.js\";\nimport { registerEmbeddedAssets } from \"../assets/index.js\";\n\nlet registered = false;\n\n/** Register the foundry catalog domain (idempotent). */\nexport function ensureFoundryAssetsRegistered(): void {\n if (registered) {\n return;\n }\n registerEmbeddedAssets(foundryManifest);\n registered = true;\n}\n","/**\n * Signal Catalog Loader\n *\n * Loads and validates the signal handling catalog from Crucible SSOT assets\n * following the same pattern as other Foundry catalogs.\n */\n\nimport { parse as parseYaml } from \"yaml\";\nimport { getAssetResolver } from \"../../assets/index.js\";\nimport { validateDataBySchemaId } from \"../../schema/validator.js\";\nimport { ensureFoundryAssetsRegistered } from \"../embedded-assets.js\";\nimport { FoundryCatalogError } from \"../errors.js\";\nimport type { BehaviorInfo, SignalCatalog, SignalInfo } from \"./types.js\";\n\n// SSOT asset logical path (resolved via the AssetResolver — filesystem or embedded).\nconst SIGNALS_LOGICAL_PATH = \"config/crucible-ts/library/foundry/signals.yaml\";\n\n// Schema ID for signals catalog (from Crucible SSOT)\nconst SCHEMA_ID = \"library/foundry/v1.0.0/signals\";\n\n/**\n * Load and validate the Signal Catalog from SSOT (filesystem or embedded).\n */\nasync function loadCatalog(): Promise<SignalCatalog> {\n const catalogName = \"signals\";\n\n try {\n ensureFoundryAssetsRegistered();\n const resolver = getAssetResolver();\n\n let content: string;\n try {\n content = await resolver.read(SIGNALS_LOGICAL_PATH);\n } catch {\n throw FoundryCatalogError.missingCatalog(catalogName);\n }\n\n // Parse YAML content\n const data = parseYaml(content) as SignalCatalog;\n\n // Validate against JSON Schema from Crucible SSOT\n const result = await validateDataBySchemaId(data, SCHEMA_ID);\n if (!result.valid) {\n const errorMessages = result.diagnostics.map((d) => `${d.pointer}: ${d.message}`).join(\"; \");\n throw FoundryCatalogError.invalidSchema(\n catalogName,\n `Schema validation failed: ${errorMessages}`,\n );\n }\n\n return data;\n } catch (error) {\n if (error instanceof FoundryCatalogError) {\n throw error;\n }\n // Preserve the underlying cause (e.g. a registry/schema failure) for diagnostics.\n throw FoundryCatalogError.invalidSchema(\n catalogName,\n `Failed to load catalog: ${(error as Error).message}`,\n error as Error,\n );\n }\n}\n\n// Cached catalog instance\nlet cachedCatalog: SignalCatalog | null = null;\n\n/**\n * Get the signal catalog, loading it if necessary\n */\nasync function getCatalog(): Promise<SignalCatalog> {\n if (!cachedCatalog) {\n cachedCatalog = await loadCatalog();\n }\n return cachedCatalog;\n}\n\n/**\n * Get the signals catalog version\n */\nexport async function getSignalsVersion(): Promise<string> {\n const catalog = await getCatalog();\n return catalog.version;\n}\n\n/**\n * Get all signals\n */\nexport async function listSignals(): Promise<SignalInfo[]> {\n const catalog = await getCatalog();\n return catalog.signals.map((signal) => ({ ...signal }));\n}\n\n/**\n * Get a specific signal by ID or name\n */\nexport async function getSignal(identifier: string): Promise<SignalInfo | null> {\n const catalog = await getCatalog();\n const signal = catalog.signals.find((s) => s.id === identifier || s.name === identifier);\n return signal ? { ...signal } : null;\n}\n\n/**\n * Get all behaviors\n */\nexport async function listBehaviors(): Promise<BehaviorInfo[]> {\n const catalog = await getCatalog();\n return catalog.behaviors.map((behavior) => ({ ...behavior }));\n}\n\n/**\n * Get a specific behavior by ID\n */\nexport async function getBehavior(id: string): Promise<BehaviorInfo | null> {\n const catalog = await getCatalog();\n const behavior = catalog.behaviors.find((b) => b.id === id);\n return behavior ? { ...behavior } : null;\n}\n\n/**\n * Get the complete catalog (for advanced use cases)\n */\nexport async function getSignalCatalog(): Promise<SignalCatalog> {\n return await getCatalog();\n}\n","/**\n * Signal Capability Detection\n *\n * Platform-aware signal support detection driven by catalog metadata.\n * Ensures cross-language parity by querying windows_event/fallback fields\n * rather than hardcoding signal names.\n */\n\nimport { getSignal, getSignalCatalog } from \"./catalog.js\";\n\n/**\n * Platform types\n */\nexport type Platform = \"linux\" | \"darwin\" | \"win32\" | \"freebsd\" | \"unknown\";\n\n/**\n * Platform capabilities summary\n */\nexport interface PlatformCapabilities {\n platform: Platform;\n isPOSIX: boolean;\n isWindows: boolean;\n supportsNativeSignals: boolean;\n supportsSignalExitCodes: boolean;\n supportedSignals: string[];\n unsupportedSignals: string[];\n mappedSignals: string[]; // Windows console events\n}\n\n/**\n * Detect current platform\n */\nexport function getPlatform(): Platform {\n const platform = process.platform;\n\n switch (platform) {\n case \"linux\":\n return \"linux\";\n case \"darwin\":\n return \"darwin\";\n case \"win32\":\n return \"win32\";\n case \"freebsd\":\n return \"freebsd\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Check if current platform is POSIX-compliant\n */\nexport function isPOSIX(): boolean {\n const platform = getPlatform();\n return platform === \"linux\" || platform === \"darwin\" || platform === \"freebsd\";\n}\n\n/**\n * Check if current platform is Windows\n */\nexport function isWindows(): boolean {\n return getPlatform() === \"win32\";\n}\n\n/**\n * Check if a signal is supported on the current platform\n *\n * Uses catalog metadata (windows_event field) to determine support.\n * Returns true if:\n * - Platform is POSIX (all signals natively supported)\n * - Platform is Windows AND signal has non-null windows_event\n *\n * @param signalName - Signal name (e.g., \"SIGTERM\") or id (e.g., \"term\")\n */\nexport async function supportsSignal(signalName: string): Promise<boolean> {\n const signal = await getSignal(signalName);\n if (!signal) {\n return false;\n }\n\n // POSIX platforms support all signals natively\n if (isPOSIX()) {\n return true;\n }\n\n // Windows: check if signal has a console event mapping\n if (isWindows()) {\n // Signal is supported if windows_event is non-null\n return signal.windows_event !== null;\n }\n\n // Unknown platform: conservative approach (assume unsupported)\n return false;\n}\n\n/**\n * Check if platform supports signal-based exit codes (128+N pattern)\n *\n * Windows doesn't propagate signal numbers via exit codes in the same way\n * as POSIX systems. This function helps applications decide whether to\n * rely on signal exit codes for process monitoring.\n */\nexport function supportsSignalExitCodes(): boolean {\n // Only POSIX systems reliably support 128+N exit code pattern\n return isPOSIX();\n}\n\n/**\n * Get comprehensive platform capabilities\n *\n * Queries catalog to build a complete picture of signal support.\n * Useful for capability reporting, documentation generation, and testing.\n */\nexport async function getPlatformCapabilities(): Promise<PlatformCapabilities> {\n const platform = getPlatform();\n const isPosix = isPOSIX();\n const isWin = isWindows();\n const catalog = await getSignalCatalog();\n\n const supported: string[] = [];\n const unsupported: string[] = [];\n const mapped: string[] = [];\n\n // Categorize signals based on platform\n for (const signal of catalog.signals) {\n if (isPosix) {\n // All signals supported on POSIX\n supported.push(signal.name);\n } else if (isWin) {\n if (signal.windows_event !== null) {\n // Signal has Windows console event mapping\n supported.push(signal.name);\n mapped.push(signal.name);\n } else {\n // No Windows support - requires fallback\n unsupported.push(signal.name);\n }\n } else {\n // Unknown platform - mark as unsupported\n unsupported.push(signal.name);\n }\n }\n\n return {\n platform,\n isPOSIX: isPosix,\n isWindows: isWin,\n supportsNativeSignals: isPosix,\n supportsSignalExitCodes: supportsSignalExitCodes(),\n supportedSignals: supported,\n unsupportedSignals: unsupported,\n mappedSignals: mapped,\n };\n}\n\n/**\n * Get the signal number for the current platform\n *\n * Handles platform-specific overrides (e.g., SIGUSR1/SIGUSR2 on macOS/FreeBSD).\n * Returns the appropriate signal number based on platform_overrides.\n *\n * @param signalName - Signal name (e.g., \"SIGTERM\") or id (e.g., \"term\")\n * @returns Signal number for current platform, or null if signal not found\n */\nexport async function getSignalNumber(signalName: string): Promise<number | null> {\n const signal = await getSignal(signalName);\n if (!signal) {\n return null;\n }\n\n const platform = getPlatform();\n\n // Check for platform-specific override\n if (signal.platform_overrides) {\n if (platform === \"darwin\" && signal.platform_overrides.darwin !== undefined) {\n return signal.platform_overrides.darwin;\n }\n if (platform === \"freebsd\" && signal.platform_overrides.freebsd !== undefined) {\n return signal.platform_overrides.freebsd;\n }\n }\n\n // Return standard Unix number\n return signal.unix_number;\n}\n\n/**\n * Get the Windows console event name for a signal\n *\n * Returns the Windows console event that corresponds to a Unix signal,\n * or null if the signal is not supported on Windows.\n *\n * @param signalName - Signal name (e.g., \"SIGTERM\") or id (e.g., \"term\")\n */\nexport async function getWindowsEvent(signalName: string): Promise<string | null> {\n const signal = await getSignal(signalName);\n if (!signal) {\n return null;\n }\n\n return signal.windows_event;\n}\n"]}
1
+ {"version":3,"sources":["../src/assets/generated/foundry.generated.ts","../src/foundry/embedded-assets.ts","../src/foundry/signals/catalog.ts","../src/foundry/signals/capabilities.ts"],"names":["parseYaml"],"mappings":"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","file":"chunk-24BAF63H.js","sourcesContent":["// AUTO-GENERATED by scripts/embed-assets.ts. DO NOT EDIT.\n// Run `make embed-assets` to regenerate after `make sync-ssot`.\n// Domain: foundry (5 assets)\nimport type { EmbeddedAssetManifest } from \"../types.js\";\n\nexport const manifest: EmbeddedAssetManifest = {\n domain: \"foundry\",\n files: {\n \"config/crucible-ts/library/foundry/country-codes.yaml\": \"$schema: https://schemas.fulmenhq.dev/library/foundry/v1.0.0/country-codes.schema.json\\ndescription: Sample ISO 3166 country codes for helper-library lookups.\\nversion: v0.1.0\\ncountries:\\n - alpha2: US\\n alpha3: USA\\n numeric: '840'\\n name: United States of America\\n officialName: United States of America\\n - alpha2: CA\\n alpha3: CAN\\n numeric: '124'\\n name: Canada\\n officialName: Canada\\n - alpha2: JP\\n alpha3: JPN\\n numeric: '392'\\n name: Japan\\n officialName: Japan\\n - alpha2: DE\\n alpha3: DEU\\n numeric: '276'\\n name: Germany\\n officialName: Federal Republic of Germany\\n - alpha2: BR\\n alpha3: BRA\\n numeric: '076'\\n name: Brazil\\n officialName: Federative Republic of Brazil\\n\",\n \"config/crucible-ts/library/foundry/http-statuses.yaml\": \"$schema: https://schemas.fulmenhq.dev/library/foundry/v1.0.0/http-status-groups.schema.json\\ndescription: HTTP status code groups shared across helper libraries.\\nversion: v0.1.0\\ngroups:\\n - id: informational\\n name: Informational Responses\\n description: '1xx HTTP status codes indicating provisional responses.'\\n codes:\\n - value: 100\\n reason: Continue\\n - value: 101\\n reason: Switching Protocols\\n - value: 102\\n reason: Processing\\n - value: 103\\n reason: Early Hints\\n - id: success\\n name: Successful Responses\\n description: '2xx HTTP status codes indicating successful requests.'\\n codes:\\n - value: 200\\n reason: OK\\n - value: 201\\n reason: Created\\n - value: 202\\n reason: Accepted\\n - value: 203\\n reason: Non-Authoritative Information\\n - value: 204\\n reason: No Content\\n - value: 205\\n reason: Reset Content\\n - value: 206\\n reason: Partial Content\\n - value: 207\\n reason: Multi-Status\\n - value: 208\\n reason: Already Reported\\n - value: 226\\n reason: IM Used\\n - id: redirect\\n name: Redirection Responses\\n description: '3xx HTTP status codes indicating redirects.'\\n codes:\\n - value: 300\\n reason: Multiple Choices\\n - value: 301\\n reason: Moved Permanently\\n - value: 302\\n reason: Found\\n - value: 303\\n reason: See Other\\n - value: 304\\n reason: Not Modified\\n - value: 305\\n reason: Use Proxy\\n - value: 307\\n reason: Temporary Redirect\\n - value: 308\\n reason: Permanent Redirect\\n - id: client-error\\n name: Client Error Responses\\n description: '4xx HTTP status codes indicating client errors.'\\n codes:\\n - value: 400\\n reason: Bad Request\\n - value: 401\\n reason: Unauthorized\\n - value: 402\\n reason: Payment Required\\n - value: 403\\n reason: Forbidden\\n - value: 404\\n reason: Not Found\\n - value: 405\\n reason: Method Not Allowed\\n - value: 406\\n reason: Not Acceptable\\n - value: 407\\n reason: Proxy Authentication Required\\n - value: 408\\n reason: Request Timeout\\n - value: 409\\n reason: Conflict\\n - value: 410\\n reason: Gone\\n - value: 411\\n reason: Length Required\\n - value: 412\\n reason: Precondition Failed\\n - value: 413\\n reason: Content Too Large\\n - value: 414\\n reason: URI Too Long\\n - value: 415\\n reason: Unsupported Media Type\\n - value: 416\\n reason: Range Not Satisfiable\\n - value: 417\\n reason: Expectation Failed\\n - value: 418\\n reason: I'm a teapot\\n - value: 421\\n reason: Misdirected Request\\n - value: 422\\n reason: Unprocessable Content\\n - value: 423\\n reason: Locked\\n - value: 424\\n reason: Failed Dependency\\n - value: 425\\n reason: Too Early\\n - value: 426\\n reason: Upgrade Required\\n - value: 428\\n reason: Precondition Required\\n - value: 429\\n reason: Too Many Requests\\n - value: 431\\n reason: Request Header Fields Too Large\\n - value: 451\\n reason: Unavailable For Legal Reasons\\n - id: server-error\\n name: Server Error Responses\\n description: '5xx HTTP status codes indicating server errors.'\\n codes:\\n - value: 500\\n reason: Internal Server Error\\n - value: 501\\n reason: Not Implemented\\n - value: 502\\n reason: Bad Gateway\\n - value: 503\\n reason: Service Unavailable\\n - value: 504\\n reason: Gateway Timeout\\n - value: 505\\n reason: HTTP Version Not Supported\\n - value: 506\\n reason: Variant Also Negotiates\\n - value: 507\\n reason: Insufficient Storage\\n - value: 508\\n reason: Loop Detected\\n - value: 510\\n reason: Not Extended\\n - value: 511\\n reason: Network Authentication Required\\n\",\n \"config/crucible-ts/library/foundry/mime-types.yaml\": \"$schema: https://schemas.fulmenhq.dev/library/foundry/v1.0.0/mime-types.schema.json\\ndescription: Common MIME types that helper libraries should recognize.\\nversion: v0.1.0\\ntypes:\\n - id: json\\n mime: application/json\\n name: JSON\\n extensions: [json, map]\\n description: JavaScript Object Notation\\n - id: yaml\\n mime: application/yaml\\n name: YAML\\n extensions: [yaml, yml]\\n description: YAML Ain't Markup Language\\n - id: ndjson\\n mime: application/x-ndjson\\n name: NDJSON\\n extensions: [ndjson]\\n description: Newline-delimited JSON\\n - id: csv\\n mime: text/csv\\n name: CSV\\n extensions: [csv]\\n description: Comma-separated values\\n - id: xml\\n mime: application/xml\\n name: XML\\n extensions: [xml]\\n description: Extensible Markup Language\\n - id: protobuf\\n mime: application/x-protobuf\\n name: Protocol Buffers\\n extensions: [pb, proto]\\n description: Google Protocol Buffers binary format\\n - id: plain-text\\n mime: text/plain\\n name: Plain Text\\n extensions: [txt]\\n description: UTF-8 plain text\\n\",\n \"config/crucible-ts/library/foundry/patterns.yaml\": \"$schema: https://schemas.fulmenhq.dev/library/foundry/v1.0.0/patterns.schema.json\\ndescription: Canonical regex/glob/literal patterns shared across Fulmen helper libraries.\\nversion: v0.1.0\\npatterns:\\n - id: ansi-email\\n name: RFC 5322 Email\\n kind: regex\\n pattern: '^[\\\\p{Letter}\\\\p{Number}._%+-]+@[\\\\p{Letter}\\\\p{Number}.-]+\\\\.[\\\\p{Letter}]{2,}$'\\n flags:\\n python:\\n unicode: true\\n ignoreCase: true\\n go:\\n unicode: true\\n ignoreCase: true\\n typescript:\\n ignoreCase: true\\n rust:\\n unicode: true\\n ignoreCase: true\\n csharp:\\n unicode: true\\n ignoreCase: true\\n description: 'Internationalized email address with Unicode letters; case-insensitive.'\\n examples:\\n - 'user@example.com'\\n - '你好@例子.公司'\\n - id: slug\\n name: Slug (kebab/underscore)\\n kind: regex\\n pattern: '^[a-z0-9]+(?:[-_][a-z0-9]+)*$'\\n description: 'Lowercase slug with hyphen/underscore separators.'\\n examples:\\n - 'fulmen-hq'\\n - 'data_pipeline'\\n - id: path-segment\\n name: Path Segment\\n kind: regex\\n pattern: '^[a-z0-9]+(?:[/_-][a-z0-9]+)*$'\\n description: 'Lowercase path fragment allowing slash, underscore, hyphen separators.'\\n examples:\\n - 'foo/bar'\\n - 'docs_v1'\\n - id: identifier\\n name: Identifier (Alpha + underscore)\\n kind: regex\\n pattern: '^[A-Za-z][A-Za-z0-9_]*[A-Za-z0-9]$'\\n description: 'Camel/snake compatible identifier, starts with letter.'\\n examples:\\n - 'ConfigValue'\\n - 'config_value1'\\n - id: uri-scheme\\n name: URI Scheme (RFC 3986)\\n kind: regex\\n pattern: '^[a-z][a-z0-9+.-]*$'\\n description: 'Lowercase URI scheme (e.g., http, git+ssh).'\\n examples:\\n - 'https'\\n - 'git+ssh'\\n - id: logger-name\\n name: Logger Name\\n kind: regex\\n pattern: '^[A-Za-z][A-Za-z0-9_]*(?:\\\\.[A-Za-z][A-Za-z0-9_]*)*$'\\n description: 'Dot-separated logger names (e.g., package.module.component).'\\n examples:\\n - 'fulmen.logger'\\n - 'app.core.worker'\\n - id: env-prefix\\n name: Environment Prefix\\n kind: regex\\n pattern: '^[A-Z][A-Z0-9_]*_$'\\n description: 'Uppercase environment variable prefix ending with underscore.'\\n examples:\\n - 'FULMEN_'\\n - 'CRUCIBLE_'\\n - id: tag\\n name: Tag (ASCII)\\n kind: regex\\n pattern: '^[A-Za-z0-9][A-Za-z0-9_\\\\-\\\\s]*[A-Za-z0-9]$'\\n description: 'Label allowing spaces, underscores, hyphens; no leading/trailing separators.'\\n examples:\\n - 'stable release'\\n - 'beta-1'\\n - id: unicode-tag\\n name: Tag (Unicode)\\n kind: regex\\n pattern: '^[\\\\p{L}\\\\p{N}][\\\\p{L}\\\\p{N}_\\\\-\\\\s]*[\\\\p{L}\\\\p{N}]$'\\n flags:\\n python:\\n unicode: true\\n go:\\n unicode: true\\n rust:\\n unicode: true\\n csharp:\\n unicode: true\\n description: 'Unicode-aware tag allowing letters/numbers from any script.'\\n examples:\\n - '国际化 标签'\\n - 'データ-01'\\n - id: domain-name\\n name: Fully Qualified Domain Name\\n kind: regex\\n pattern: '^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\\\.)+[a-z]{2,63}$'\\n description: 'Lowercase ASCII domain labels per RFC 1035 (no wildcard).'\\n examples:\\n - 'example.com'\\n - 'api.fulmenhq.net'\\n - id: ipv6\\n name: IPv6 Address (Full/Compressed)\\n kind: regex\\n pattern: '^(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}$|^(?:[A-Fa-f0-9]{1,4}:){1,7}:$|^:(?::[A-Fa-f0-9]{1,4}){1,7}$|^(?:[A-Fa-f0-9]{1,4}:){1,6}:[A-Fa-f0-9]{1,4}$|^(?:[A-Fa-f0-9]{1,4}:){1,5}(?::[A-Fa-f0-9]{1,4}){1,2}$|^(?:[A-Fa-f0-9]{1,4}:){1,4}(?::[A-Fa-f0-9]{1,4}){1,3}$|^(?:[A-Fa-f0-9]{1,4}:){1,3}(?::[A-Fa-f0-9]{1,4}){1,4}$|^(?:[A-Fa-f0-9]{1,4}:){1,2}(?::[A-Fa-f0-9]{1,4}){1,5}$|^[A-Fa-f0-9]{1,4}:(?:(?::[A-Fa-f0-9]{1,4}){1,6})$|^:(?::[A-Fa-f0-9]{1,4}){1,7}$|^(?:[A-Fa-f0-9]{1,4}:){1,4}:(?:[A-Fa-f0-9]{1,4}:){1,2}[A-Fa-f0-9]{1,4}$'\\n description: 'IPv6 address with support for shorthand compression (RFC 4291).'\\n examples:\\n - '2001:0db8:85a3:0000:0000:8a2e:0370:7334'\\n - '2001:db8::1428:57ab'\\n - id: hex-lower\\n name: Lowercase Hex String\\n kind: regex\\n pattern: '^[0-9a-f]+$'\\n description: 'Lowercase hexadecimal characters (length validated by consumer).'\\n examples:\\n - 'deadbeef'\\n - '0123456789abcdef'\\n - id: base64\\n name: Base64 Encoded String\\n kind: regex\\n pattern: '^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'\\n description: 'Standard Base64 encoding with padding.'\\n examples:\\n - 'SGVsbG8gd29ybGQ='\\n - id: jwt\\n name: JSON Web Token (compact)\\n kind: regex\\n pattern: '^[A-Za-z0-9-_]+\\\\.[A-Za-z0-9-_]+\\\\.[A-Za-z0-9-_]+$'\\n description: 'JWT compact serialization (header.payload.signature).'\\n examples:\\n - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U'\\n - id: strict-iso-date\\n name: ISO Date (YYYY-MM-DD)\\n kind: regex\\n pattern: '^\\\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\\\d|3[01])$'\\n description: 'ISO 8601 calendar date with validated month/day ranges.'\\n examples:\\n - '2025-10-09'\\n - id: iso-timestamp-z\\n name: ISO Timestamp (UTC)\\n kind: regex\\n pattern: '^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d{1,6})?Z$'\\n description: 'UTC timestamp with optional fractional seconds.'\\n examples:\\n - '2025-10-09T14:15:16Z'\\n - '2025-10-09T14:15:16.123456Z'\\n - id: uuid-v4\\n name: UUID v4\\n kind: regex\\n pattern: '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$'\\n description: 'RFC 4122 version 4 UUID.'\\n examples:\\n - '123e4567-e89b-42d3-a456-426614174000'\\n - id: ulid\\n name: ULID\\n kind: regex\\n pattern: '^[0-9A-HJKMNP-TV-Z]{26}$'\\n description: 'Crockford-base32 ULID (26 chars, upper-case).'\\n examples:\\n - '01HCP5MZF1H84D0CW7V5T9FQ0P'\\n - id: semantic-version\\n name: Semantic Version\\n kind: regex\\n pattern: '^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-[0-9A-Za-z-]+(?:\\\\.[0-9A-Za-z-]+)*)?(?:\\\\+[0-9A-Za-z-]+(?:\\\\.[0-9A-Za-z-]+)*)?$'\\n description: 'SemVer 2.0.0 specification-compliant version string.'\\n examples:\\n - '1.2.3'\\n - '1.2.3-beta.1+build.5'\\n - id: glob-any-json\\n name: Glob – JSON Files\\n kind: glob\\n pattern: '**/*.json'\\n description: 'Recursive glob matching all JSON files.'\\n - id: glob-any-yaml\\n name: Glob – YAML Files\\n kind: glob\\n pattern: '**/*.{yml,yaml}'\\n description: 'Recursive glob matching YAML files.'\\n\",\n \"config/crucible-ts/library/foundry/signals.yaml\": \"$schema: https://schemas.fulmenhq.dev/library/foundry/v1.0.0/signals.schema.json\\ndescription: Standard signal handling semantics for Fulmen ecosystem\\nversion: v1.0.0\\nsignals:\\n - id: term\\n name: SIGTERM\\n unix_number: 15\\n windows_event: CTRL_CLOSE_EVENT\\n description: Graceful termination signal\\n default_behavior: graceful_shutdown\\n exit_code: 143 # 128 + 15\\n timeout_seconds: 30\\n cleanup_actions:\\n - close_connections\\n - flush_buffers\\n - remove_pid_file\\n - log_shutdown\\n usage_notes: Standard termination signal sent by container orchestrators (Kubernetes, Docker). Applications should perform cleanup within timeout period before exiting.\\n - id: int\\n name: SIGINT\\n unix_number: 2\\n windows_event: CTRL_C_EVENT\\n description: User interrupt (Ctrl+C)\\n default_behavior: graceful_shutdown_with_double_tap\\n exit_code: 130 # 128 + 2\\n timeout_seconds: 5\\n double_tap_window_seconds: 2\\n double_tap_message: \\\"Press Ctrl+C again within 2s to force quit\\\"\\n double_tap_behavior: immediate_exit\\n double_tap_exit_code: 130\\n cleanup_actions:\\n - close_connections\\n - flush_buffers\\n - remove_pid_file\\n - log_interrupt\\n usage_notes: Interactive interrupt signal (Ctrl+C). First press initiates graceful shutdown, second press within 2s forces immediate exit for hung processes.\\n - id: hup\\n name: SIGHUP\\n unix_number: 1\\n windows_event: null\\n description: Hangup signal (config reload via restart)\\n default_behavior: reload_via_restart\\n exit_code: 129 # 128 + 1\\n reload_strategy: restart_based\\n validation_required: true\\n timeout_seconds: 30\\n cleanup_actions:\\n - validate_new_config_against_schema\\n - graceful_shutdown\\n - restart_with_new_config\\n - log_reload\\n usage_notes: Historically \\\"hangup\\\" (terminal disconnect). In Fulmen, triggers config reload via restart. MANDATORY schema validation before restart. On Windows, use HTTP /admin/signal endpoint as fallback.\\n windows_fallback:\\n fallback_behavior: http_admin_endpoint\\n log_level: INFO\\n log_message: \\\"SIGHUP unavailable on Windows - use HTTP endpoint for config reload\\\"\\n log_template: \\\"signal=${signal} platform=${platform} fallback=${fallback_behavior} message='${operation_hint}'\\\"\\n operation_hint: \\\"POST /admin/signal with signal=HUP\\\"\\n telemetry_event: fulmen.signal.unsupported\\n telemetry_tags:\\n signal: SIGHUP\\n platform: windows\\n fallback_behavior: http_admin_endpoint\\n - id: quit\\n name: SIGQUIT\\n unix_number: 3\\n windows_event: CTRL_BREAK_EVENT\\n description: Quit with core dump (immediate termination)\\n default_behavior: immediate_exit\\n exit_code: 131 # 128 + 3\\n timeout_seconds: 0\\n usage_notes: Immediate termination signal (Ctrl+\\\\\\\\ on Unix, Ctrl+Break on Windows). Does not perform cleanup. Use for emergency shutdown or debugging (core dumps).\\n - id: pipe\\n name: SIGPIPE\\n unix_number: 13\\n windows_event: null\\n description: Broken pipe (write to closed socket/pipe)\\n default_behavior: observe_only\\n exit_code: 141 # 128 + 13\\n usage_notes: Default POSIX behavior terminates process immediately. Fulmen default is observe_only (log at INFO level + exit gracefully with code 141). Applications may override to ignore SIGPIPE for network services that expect broken pipes. Long-running servers typically ignore; CLI tools typically exit.\\n windows_fallback:\\n fallback_behavior: exception_handling\\n log_level: INFO\\n log_message: \\\"SIGPIPE unavailable on Windows - broken pipes raise exceptions instead\\\"\\n log_template: \\\"signal=${signal} platform=${platform} fallback=${fallback_behavior} message='${operation_hint}'\\\"\\n operation_hint: \\\"Handle BrokenPipeError (Python), EPIPE (Go), EPIPE (C)\\\"\\n telemetry_event: fulmen.signal.unsupported\\n telemetry_tags:\\n signal: SIGPIPE\\n platform: windows\\n fallback_behavior: exception_handling\\n - id: alrm\\n name: SIGALRM\\n unix_number: 14\\n windows_event: null\\n description: Alarm timer expired (watchdog timeout)\\n default_behavior: immediate_exit\\n exit_code: 142 # 128 + 14\\n timeout_seconds: 0\\n usage_notes: Used by watchdog timers. Treat as timeout-induced exit unless application overrides. Watchdog pattern - call signal.alarm(N) to set timer, reset periodically with progress; if no reset within N seconds, SIGALRM fires and process exits. Out of scope for v1.0.0 module implementations.\\n windows_fallback:\\n fallback_behavior: timer_api\\n log_level: INFO\\n log_message: \\\"SIGALRM unavailable on Windows - use native timer APIs instead\\\"\\n log_template: \\\"signal=${signal} platform=${platform} fallback=${fallback_behavior} message='${operation_hint}'\\\"\\n operation_hint: \\\"Use threading.Timer (Python), time.AfterFunc (Go), setTimeout (TypeScript)\\\"\\n telemetry_event: fulmen.signal.unsupported\\n telemetry_tags:\\n signal: SIGALRM\\n platform: windows\\n fallback_behavior: timer_api\\n - id: usr1\\n name: SIGUSR1\\n unix_number: 10\\n platform_overrides:\\n darwin: 30\\n freebsd: 30\\n windows_event: null\\n description: User-defined signal 1\\n default_behavior: custom\\n exit_code: 138 # 128 + 10\\n usage_notes: Application-specific signal (e.g., reopen log files, dump statistics, toggle verbose logging). Helper libraries provide registration API; application provides custom handler.\\n windows_fallback:\\n fallback_behavior: http_admin_endpoint\\n log_level: INFO\\n log_message: \\\"SIGUSR1 unavailable on Windows - use HTTP endpoint for custom operations\\\"\\n log_template: \\\"signal=${signal} platform=${platform} fallback=${fallback_behavior} message='${operation_hint}'\\\"\\n operation_hint: \\\"POST /admin/signal with signal=USR1\\\"\\n telemetry_event: fulmen.signal.unsupported\\n telemetry_tags:\\n signal: SIGUSR1\\n platform: windows\\n fallback_behavior: http_admin_endpoint\\n - id: usr2\\n name: SIGUSR2\\n unix_number: 12\\n platform_overrides:\\n darwin: 31\\n freebsd: 31\\n windows_event: null\\n description: User-defined signal 2\\n default_behavior: custom\\n exit_code: 140 # 128 + 12\\n usage_notes: Application-specific signal (e.g., toggle debug mode, trigger profiling, rotate credentials). Helper libraries provide registration API; application provides custom handler.\\n windows_fallback:\\n fallback_behavior: http_admin_endpoint\\n log_level: INFO\\n log_message: \\\"SIGUSR2 unavailable on Windows - use HTTP endpoint for custom operations\\\"\\n log_template: \\\"signal=${signal} platform=${platform} fallback=${fallback_behavior} message='${operation_hint}'\\\"\\n operation_hint: \\\"POST /admin/signal with signal=USR2\\\"\\n telemetry_event: fulmen.signal.unsupported\\n telemetry_tags:\\n signal: SIGUSR2\\n platform: windows\\n fallback_behavior: http_admin_endpoint\\n - id: kill\\n name: SIGKILL\\n unix_number: 9\\n windows_event: null\\n description: Force immediate termination - cannot be caught, blocked, or ignored\\n default_behavior: immediate_exit\\n exit_code: 137 # 128 + 9\\n timeout_seconds: 0\\n cleanup_actions: []\\n usage_notes: |\\n SIGKILL terminates a process immediately without allowing cleanup.\\n Use only as a last resort after SIGTERM grace period expires. The process\\n has no opportunity to save state, close files, or release resources.\\n On Windows, use TerminateProcess() for single process or TerminateJobObject() for groups.\\nbehaviors:\\n - id: graceful_shutdown\\n name: Graceful Shutdown\\n description: Clean shutdown with timeout and cleanup chain\\n phases:\\n - name: notify\\n description: Log signal receipt with correlation ID\\n - name: stop_accepting\\n description: Stop accepting new work (close listeners, reject requests)\\n - name: drain\\n description: Complete in-flight operations within timeout\\n - name: cleanup\\n description: Run registered cleanup handlers in order\\n - name: exit\\n description: Exit with appropriate code (128+N pattern)\\n - id: graceful_shutdown_with_double_tap\\n name: Graceful Shutdown with Double-Tap\\n description: Graceful shutdown with force-quit option (Ctrl+C pattern)\\n phases:\\n - name: first_signal\\n description: First signal starts graceful shutdown and prints hint message\\n - name: notify\\n description: Log signal receipt with correlation ID\\n - name: stop_accepting\\n description: Stop accepting new work\\n - name: drain\\n description: Complete in-flight operations within timeout\\n - name: cleanup\\n description: Run registered cleanup handlers in order\\n - name: second_signal_check\\n description: If second signal within window, force immediate exit\\n - name: exit\\n description: Exit with appropriate code\\n - id: reload_via_restart\\n name: Configuration Reload via Restart\\n description: Reload configuration by validating and restarting process\\n phases:\\n - name: notify\\n description: Log reload request with correlation ID\\n - name: validate\\n description: Validate new configuration against Crucible schema (MANDATORY)\\n - name: reject_invalid\\n description: If invalid, log error and continue with current config (no restart)\\n - name: graceful_shutdown\\n description: If valid, initiate graceful shutdown\\n - name: exit\\n description: Exit with code 129 (SIGHUP) - process supervisor restarts\\n - name: restart\\n description: Process supervisor (systemd, K8s, etc.) restarts process with new config\\n - id: immediate_exit\\n name: Immediate Exit\\n description: Exit immediately without cleanup (emergency shutdown)\\n phases:\\n - name: notify\\n description: Log signal receipt (no correlation ID needed - too fast)\\n - name: exit\\n description: Exit immediately with appropriate code\\n - id: custom\\n name: Custom Handler\\n description: Application-specific behavior defined by handler registration\\n phases:\\n - name: delegate\\n description: Call application-provided handler function\\n - name: log_completion\\n description: Log custom handler completion (do not exit unless handler exits)\\n - id: observe_only\\n name: Observe Only\\n description: Log the signal and increment telemetry without exiting\\n phases:\\n - name: notify\\n description: Log signal receipt at INFO level\\n - name: telemetry\\n description: Increment signal counter in telemetry system\\n - name: continue\\n description: Continue execution (do not exit)\\nos_mappings:\\n unix:\\n SIGTERM: 15\\n SIGINT: 2\\n SIGHUP: 1\\n SIGQUIT: 3\\n SIGPIPE: 13\\n SIGALRM: 14\\n SIGUSR1: 10 # Linux standard\\n SIGUSR2: 12 # Linux standard\\n SIGKILL: 9 # Not catchable, documented for completeness\\n windows:\\n CTRL_C_EVENT: 0\\n CTRL_BREAK_EVENT: 1\\n CTRL_CLOSE_EVENT: 2\\n CTRL_LOGOFF_EVENT: 5\\n CTRL_SHUTDOWN_EVENT: 6\\n # Platform-specific signal number overrides\\n platform_overrides:\\n darwin: # macOS\\n SIGUSR1: 30\\n SIGUSR2: 31\\n freebsd:\\n SIGUSR1: 30\\n SIGUSR2: 31\\n # Mapping Unix signals to Windows console events\\n signal_to_event:\\n SIGTERM: CTRL_CLOSE_EVENT\\n SIGINT: CTRL_C_EVENT\\n SIGQUIT: CTRL_BREAK_EVENT\\n SIGHUP: null # No Windows equivalent - use HTTP fallback\\n SIGPIPE: null # No Windows equivalent - handled via exceptions\\n SIGALRM: null # No Windows equivalent - use timers\\n SIGUSR1: null # No Windows equivalent - use HTTP fallback\\n SIGUSR2: null # No Windows equivalent - use HTTP fallback\\n# Platform support matrix for helper library implementations\\nplatform_support:\\n - signal: SIGTERM\\n linux: native\\n macos: native\\n freebsd: native\\n windows: mapped # CTRL_CLOSE_EVENT\\n notes: Fully supported across all platforms\\n - signal: SIGINT\\n linux: native\\n macos: native\\n freebsd: native\\n windows: mapped # CTRL_C_EVENT\\n notes: Fully supported across all platforms\\n - signal: SIGHUP\\n linux: native\\n macos: native\\n freebsd: native\\n windows: unsupported\\n fallback: http_admin_endpoint\\n notes: On Windows, use HTTP POST /admin/signal with signal=HUP\\n - signal: SIGQUIT\\n linux: native\\n macos: native\\n freebsd: native\\n windows: mapped # CTRL_BREAK_EVENT\\n notes: Fully supported across all platforms\\n - signal: SIGPIPE\\n linux: native\\n macos: native\\n freebsd: native\\n windows: unsupported\\n fallback: exception_handling\\n notes: On Windows, broken pipes generate exceptions instead of signals\\n - signal: SIGALRM\\n linux: native\\n macos: native\\n freebsd: native\\n windows: unsupported\\n fallback: timer_api\\n notes: On Windows, use threading.Timer (Python) or setTimeout (TypeScript)\\n - signal: SIGUSR1\\n linux: native\\n macos: native # signal 30\\n freebsd: native # signal 30\\n windows: unsupported\\n fallback: http_admin_endpoint\\n notes: Signal number varies by platform. On Windows, use HTTP POST /admin/signal with signal=USR1\\n - signal: SIGUSR2\\n linux: native\\n macos: native # signal 31\\n freebsd: native # signal 31\\n windows: unsupported\\n fallback: http_admin_endpoint\\n notes: Signal number varies by platform. On Windows, use HTTP POST /admin/signal with signal=USR2\\n - signal: SIGKILL\\n linux: native\\n macos: native\\n freebsd: native\\n windows: mapped\\n notes: Cannot be caught or ignored. Windows uses TerminateProcess/TerminateJobObject.\\n# Exit code mappings (128+N pattern per POSIX)\\nexit_codes:\\n SIGTERM: 143 # 128 + 15\\n SIGINT: 130 # 128 + 2\\n SIGHUP: 129 # 128 + 1\\n SIGQUIT: 131 # 128 + 3\\n SIGKILL: 137 # 128 + 9\\n SIGPIPE: 141 # 128 + 13\\n SIGALRM: 142 # 128 + 14\\n SIGUSR1: 138 # 128 + 10\\n SIGUSR2: 140 # 128 + 12\\n note: Exit codes follow POSIX 128+N pattern where N is the signal number. These codes are cross-referenced in config/library/foundry/exit-codes.yaml.\\n\",\n },\n};\n\nexport default manifest;\n","/**\n * Static per-subpath embedded-asset registration for the foundry subpath (v0.4.0).\n *\n * Registers only the `foundry` domain (catalogs) by importing its per-domain\n * generated module directly (not `generated/index`). Note: the foundry subpath\n * still transitively reaches the schema corpus via `validateDataBySchemaId`; under\n * tsup `splitting:true` that corpus lives once in a shared chunk (deduped), so the\n * invariant is \"public entries stay lean; corpus lives once in shared chunks\" — not\n * \"foundry never references the schema corpus\". Unused in filesystem mode; required\n * for foundry catalog loading inside a `bun --compile` binary.\n */\n\nimport { manifest as foundryManifest } from \"../assets/generated/foundry.generated.js\";\nimport { registerEmbeddedAssets } from \"../assets/index.js\";\n\nlet registered = false;\n\n/** Register the foundry catalog domain (idempotent). */\nexport function ensureFoundryAssetsRegistered(): void {\n if (registered) {\n return;\n }\n registerEmbeddedAssets(foundryManifest);\n registered = true;\n}\n","/**\n * Signal Catalog Loader\n *\n * Loads and validates the signal handling catalog from Crucible SSOT assets\n * following the same pattern as other Foundry catalogs.\n */\n\nimport { parse as parseYaml } from \"yaml\";\nimport { getAssetResolver } from \"../../assets/index.js\";\nimport { validateDataBySchemaId } from \"../../schema/validator.js\";\nimport { ensureFoundryAssetsRegistered } from \"../embedded-assets.js\";\nimport { FoundryCatalogError } from \"../errors.js\";\nimport type { BehaviorInfo, SignalCatalog, SignalInfo } from \"./types.js\";\n\n// SSOT asset logical path (resolved via the AssetResolver — filesystem or embedded).\nconst SIGNALS_LOGICAL_PATH = \"config/crucible-ts/library/foundry/signals.yaml\";\n\n// Schema ID for signals catalog (from Crucible SSOT)\nconst SCHEMA_ID = \"library/foundry/v1.0.0/signals\";\n\n/**\n * Load and validate the Signal Catalog from SSOT (filesystem or embedded).\n */\nasync function loadCatalog(): Promise<SignalCatalog> {\n const catalogName = \"signals\";\n\n try {\n ensureFoundryAssetsRegistered();\n const resolver = getAssetResolver();\n\n let content: string;\n try {\n content = await resolver.read(SIGNALS_LOGICAL_PATH);\n } catch {\n throw FoundryCatalogError.missingCatalog(catalogName);\n }\n\n // Parse YAML content\n const data = parseYaml(content) as SignalCatalog;\n\n // Validate against JSON Schema from Crucible SSOT\n const result = await validateDataBySchemaId(data, SCHEMA_ID);\n if (!result.valid) {\n const errorMessages = result.diagnostics.map((d) => `${d.pointer}: ${d.message}`).join(\"; \");\n throw FoundryCatalogError.invalidSchema(\n catalogName,\n `Schema validation failed: ${errorMessages}`,\n );\n }\n\n return data;\n } catch (error) {\n if (error instanceof FoundryCatalogError) {\n throw error;\n }\n // Preserve the underlying cause (e.g. a registry/schema failure) for diagnostics.\n throw FoundryCatalogError.invalidSchema(\n catalogName,\n `Failed to load catalog: ${(error as Error).message}`,\n error as Error,\n );\n }\n}\n\n// Cached catalog instance\nlet cachedCatalog: SignalCatalog | null = null;\n\n/**\n * Get the signal catalog, loading it if necessary\n */\nasync function getCatalog(): Promise<SignalCatalog> {\n if (!cachedCatalog) {\n cachedCatalog = await loadCatalog();\n }\n return cachedCatalog;\n}\n\n/**\n * Get the signals catalog version\n */\nexport async function getSignalsVersion(): Promise<string> {\n const catalog = await getCatalog();\n return catalog.version;\n}\n\n/**\n * Get all signals\n */\nexport async function listSignals(): Promise<SignalInfo[]> {\n const catalog = await getCatalog();\n return catalog.signals.map((signal) => ({ ...signal }));\n}\n\n/**\n * Get a specific signal by ID or name\n */\nexport async function getSignal(identifier: string): Promise<SignalInfo | null> {\n const catalog = await getCatalog();\n const signal = catalog.signals.find((s) => s.id === identifier || s.name === identifier);\n return signal ? { ...signal } : null;\n}\n\n/**\n * Get all behaviors\n */\nexport async function listBehaviors(): Promise<BehaviorInfo[]> {\n const catalog = await getCatalog();\n return catalog.behaviors.map((behavior) => ({ ...behavior }));\n}\n\n/**\n * Get a specific behavior by ID\n */\nexport async function getBehavior(id: string): Promise<BehaviorInfo | null> {\n const catalog = await getCatalog();\n const behavior = catalog.behaviors.find((b) => b.id === id);\n return behavior ? { ...behavior } : null;\n}\n\n/**\n * Get the complete catalog (for advanced use cases)\n */\nexport async function getSignalCatalog(): Promise<SignalCatalog> {\n return await getCatalog();\n}\n","/**\n * Signal Capability Detection\n *\n * Platform-aware signal support detection driven by catalog metadata.\n * Ensures cross-language parity by querying windows_event/fallback fields\n * rather than hardcoding signal names.\n */\n\nimport { getSignal, getSignalCatalog } from \"./catalog.js\";\n\n/**\n * Platform types\n */\nexport type Platform = \"linux\" | \"darwin\" | \"win32\" | \"freebsd\" | \"unknown\";\n\n/**\n * Platform capabilities summary\n */\nexport interface PlatformCapabilities {\n platform: Platform;\n isPOSIX: boolean;\n isWindows: boolean;\n supportsNativeSignals: boolean;\n supportsSignalExitCodes: boolean;\n supportedSignals: string[];\n unsupportedSignals: string[];\n mappedSignals: string[]; // Windows console events\n}\n\n/**\n * Detect current platform\n */\nexport function getPlatform(): Platform {\n const platform = process.platform;\n\n switch (platform) {\n case \"linux\":\n return \"linux\";\n case \"darwin\":\n return \"darwin\";\n case \"win32\":\n return \"win32\";\n case \"freebsd\":\n return \"freebsd\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Check if current platform is POSIX-compliant\n */\nexport function isPOSIX(): boolean {\n const platform = getPlatform();\n return platform === \"linux\" || platform === \"darwin\" || platform === \"freebsd\";\n}\n\n/**\n * Check if current platform is Windows\n */\nexport function isWindows(): boolean {\n return getPlatform() === \"win32\";\n}\n\n/**\n * Check if a signal is supported on the current platform\n *\n * Uses catalog metadata (windows_event field) to determine support.\n * Returns true if:\n * - Platform is POSIX (all signals natively supported)\n * - Platform is Windows AND signal has non-null windows_event\n *\n * @param signalName - Signal name (e.g., \"SIGTERM\") or id (e.g., \"term\")\n */\nexport async function supportsSignal(signalName: string): Promise<boolean> {\n const signal = await getSignal(signalName);\n if (!signal) {\n return false;\n }\n\n // POSIX platforms support all signals natively\n if (isPOSIX()) {\n return true;\n }\n\n // Windows: check if signal has a console event mapping\n if (isWindows()) {\n // Signal is supported if windows_event is non-null\n return signal.windows_event !== null;\n }\n\n // Unknown platform: conservative approach (assume unsupported)\n return false;\n}\n\n/**\n * Check if platform supports signal-based exit codes (128+N pattern)\n *\n * Windows doesn't propagate signal numbers via exit codes in the same way\n * as POSIX systems. This function helps applications decide whether to\n * rely on signal exit codes for process monitoring.\n */\nexport function supportsSignalExitCodes(): boolean {\n // Only POSIX systems reliably support 128+N exit code pattern\n return isPOSIX();\n}\n\n/**\n * Get comprehensive platform capabilities\n *\n * Queries catalog to build a complete picture of signal support.\n * Useful for capability reporting, documentation generation, and testing.\n */\nexport async function getPlatformCapabilities(): Promise<PlatformCapabilities> {\n const platform = getPlatform();\n const isPosix = isPOSIX();\n const isWin = isWindows();\n const catalog = await getSignalCatalog();\n\n const supported: string[] = [];\n const unsupported: string[] = [];\n const mapped: string[] = [];\n\n // Categorize signals based on platform\n for (const signal of catalog.signals) {\n if (isPosix) {\n // All signals supported on POSIX\n supported.push(signal.name);\n } else if (isWin) {\n if (signal.windows_event !== null) {\n // Signal has Windows console event mapping\n supported.push(signal.name);\n mapped.push(signal.name);\n } else {\n // No Windows support - requires fallback\n unsupported.push(signal.name);\n }\n } else {\n // Unknown platform - mark as unsupported\n unsupported.push(signal.name);\n }\n }\n\n return {\n platform,\n isPOSIX: isPosix,\n isWindows: isWin,\n supportsNativeSignals: isPosix,\n supportsSignalExitCodes: supportsSignalExitCodes(),\n supportedSignals: supported,\n unsupportedSignals: unsupported,\n mappedSignals: mapped,\n };\n}\n\n/**\n * Get the signal number for the current platform\n *\n * Handles platform-specific overrides (e.g., SIGUSR1/SIGUSR2 on macOS/FreeBSD).\n * Returns the appropriate signal number based on platform_overrides.\n *\n * @param signalName - Signal name (e.g., \"SIGTERM\") or id (e.g., \"term\")\n * @returns Signal number for current platform, or null if signal not found\n */\nexport async function getSignalNumber(signalName: string): Promise<number | null> {\n const signal = await getSignal(signalName);\n if (!signal) {\n return null;\n }\n\n const platform = getPlatform();\n\n // Check for platform-specific override\n if (signal.platform_overrides) {\n if (platform === \"darwin\" && signal.platform_overrides.darwin !== undefined) {\n return signal.platform_overrides.darwin;\n }\n if (platform === \"freebsd\" && signal.platform_overrides.freebsd !== undefined) {\n return signal.platform_overrides.freebsd;\n }\n }\n\n // Return standard Unix number\n return signal.unix_number;\n}\n\n/**\n * Get the Windows console event name for a signal\n *\n * Returns the Windows console event that corresponds to a Unix signal,\n * or null if the signal is not supported on Windows.\n *\n * @param signalName - Signal name (e.g., \"SIGTERM\") or id (e.g., \"term\")\n */\nexport async function getWindowsEvent(signalName: string): Promise<string | null> {\n const signal = await getSignal(signalName);\n if (!signal) {\n return null;\n }\n\n return signal.windows_event;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import picomatch from 'picomatch';
2
- import { readFile, access } from 'fs/promises';
3
- import { join, dirname, sep } from 'path';
2
+ import { realpath, readFile } from 'fs/promises';
3
+ import { join, sep, dirname } from 'path';
4
4
  import glob from 'fast-glob';
5
5
  import { accessSync } from 'fs';
6
6
  import { fileURLToPath } from 'url';
@@ -60,6 +60,12 @@ function assertNamespace(value, segments, kind) {
60
60
  value
61
61
  );
62
62
  }
63
+ if (segments[1] !== "crucible-ts") {
64
+ throw new AssetResolutionError(
65
+ `Asset ${kind} must be under <namespace>/crucible-ts/ : ${value}`,
66
+ value
67
+ );
68
+ }
63
69
  }
64
70
  function assertSafeLogicalPath(logicalPath, enforceNamespace = true) {
65
71
  const segments = rejectCommon(logicalPath, "path");
@@ -167,8 +173,15 @@ var FsAssetResolver = class {
167
173
  async read(logicalPath) {
168
174
  const absolute = this.resolve(logicalPath);
169
175
  try {
170
- return await readFile(absolute, "utf-8");
176
+ const real = await realpath(absolute);
177
+ if (!await this.isWithinBase(real)) {
178
+ throw AssetResolutionError.notFound(logicalPath, "fs");
179
+ }
180
+ return await readFile(real, "utf-8");
171
181
  } catch (error) {
182
+ if (error instanceof AssetResolutionError) {
183
+ throw error;
184
+ }
172
185
  const err = error;
173
186
  if (err.code === "ENOENT") {
174
187
  throw AssetResolutionError.notFound(logicalPath, "fs");
@@ -176,6 +189,19 @@ var FsAssetResolver = class {
176
189
  throw AssetResolutionError.readFailed(logicalPath, err.code ?? "unknown");
177
190
  }
178
191
  }
192
+ /** Cached realpath of baseDir (resolves symlinks once). */
193
+ baseRealCache;
194
+ baseReal() {
195
+ if (!this.baseRealCache) {
196
+ this.baseRealCache = realpath(this.baseDir);
197
+ }
198
+ return this.baseRealCache;
199
+ }
200
+ /** Whether a resolved (realpath) target stays within the resolver's baseDir. */
201
+ async isWithinBase(realPath) {
202
+ const base = await this.baseReal();
203
+ return realPath === base || realPath.startsWith(base + sep);
204
+ }
179
205
  async list(patterns) {
180
206
  if (patterns.length === 0) {
181
207
  return [];
@@ -201,8 +227,8 @@ var FsAssetResolver = class {
201
227
  return false;
202
228
  }
203
229
  try {
204
- await access(absolute);
205
- return true;
230
+ const real = await realpath(absolute);
231
+ return await this.isWithinBase(real);
206
232
  } catch {
207
233
  return false;
208
234
  }
@@ -298,5 +324,5 @@ function resetAssetResolver() {
298
324
  }
299
325
 
300
326
  export { AssetResolutionError, EmbeddedAssetResolver, FsAssetResolver, clearEmbeddedAssets, findAssetBaseDir, getAssetResolver, hasEmbeddedAssets, hasEmbeddedDomain, registerEmbeddedAssets, resetAssetResolver, resolveAssets };
301
- //# sourceMappingURL=chunk-BTUS6PZZ.js.map
302
- //# sourceMappingURL=chunk-BTUS6PZZ.js.map
327
+ //# sourceMappingURL=chunk-56F2EEAG.js.map
328
+ //# sourceMappingURL=chunk-56F2EEAG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/assets/errors.ts","../src/assets/paths.ts","../src/assets/embedded-resolver.ts","../src/assets/fs-resolver.ts","../src/assets/resolver.ts"],"names":["join","baseDir"],"mappings":"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","file":"chunk-56F2EEAG.js","sourcesContent":["/**\n * Asset resolution - Error handling\n *\n * Custom error classes for SSOT asset resolution (filesystem or embedded).\n */\n\nexport class AssetResolutionError extends Error {\n constructor(\n message: string,\n public readonly assetPath?: string,\n public readonly cause?: Error,\n ) {\n super(message);\n this.name = \"AssetResolutionError\";\n\n // Maintain proper stack trace for V8\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, AssetResolutionError);\n }\n }\n\n static notFound(assetPath: string, mode: string): AssetResolutionError {\n return new AssetResolutionError(`Asset not found (${mode} resolver): ${assetPath}`, assetPath);\n }\n\n static readFailed(assetPath: string, code: string): AssetResolutionError {\n // Reference the logical path + error code only; never interpolate a raw FS\n // error message, which can carry the absolute host path (info disclosure).\n return new AssetResolutionError(`Failed to read asset ${assetPath} (${code})`, assetPath);\n }\n\n static baseDirUnavailable(detail: string): AssetResolutionError {\n return new AssetResolutionError(`No SSOT asset base directory available: ${detail}`);\n }\n}\n","/**\n * Asset resolution - Logical path & pattern validation\n *\n * `./assets` is a public export, so logical paths and glob patterns are\n * caller-controlled. These guards prevent path traversal (reading outside the\n * package root via `..`, absolute paths, or non-POSIX separators) and restrict\n * resolution to the SSOT asset namespaces tsfulmen actually ships.\n */\n\nimport { AssetResolutionError } from \"./errors.js\";\n\n/** Top-level directories that make up the SSOT asset namespace. */\nexport const ASSET_NAMESPACES = [\"schemas\", \"config\", \"docs\"] as const;\n\nfunction rejectCommon(value: string, kind: \"path\" | \"pattern\"): string[] {\n if (!value || value.trim() === \"\") {\n throw new AssetResolutionError(`Asset ${kind} must be a non-empty string`);\n }\n if (value.includes(\"\\\\\")) {\n throw new AssetResolutionError(`Asset ${kind} must be POSIX (no backslashes): ${value}`, value);\n }\n if (value.startsWith(\"/\") || /^[A-Za-z]:/.test(value)) {\n throw new AssetResolutionError(`Asset ${kind} must be relative, got absolute: ${value}`, value);\n }\n const segments = value.split(\"/\");\n for (const seg of segments) {\n if (seg === \"\" || seg === \".\" || seg === \"..\") {\n throw new AssetResolutionError(\n `Asset ${kind} must not contain empty, '.' or '..' segments: ${value}`,\n value,\n );\n }\n }\n return segments;\n}\n\nfunction assertNamespace(value: string, segments: string[], kind: \"path\" | \"pattern\"): void {\n const top = segments[0];\n if (!ASSET_NAMESPACES.includes(top as (typeof ASSET_NAMESPACES)[number])) {\n throw new AssetResolutionError(\n `Asset ${kind} must start with one of ${ASSET_NAMESPACES.join(\"/\")}/ : ${value}`,\n value,\n );\n }\n // Tighten to the actually-shipped subtree (secrev): the package only ships the\n // `crucible-ts` subtree of each namespace (config/crucible-ts, schemas/crucible-ts,\n // docs/crucible-ts), so a path/pattern resolving elsewhere can never be a real asset.\n if (segments[1] !== \"crucible-ts\") {\n throw new AssetResolutionError(\n `Asset ${kind} must be under <namespace>/crucible-ts/ : ${value}`,\n value,\n );\n }\n}\n\n/**\n * Validate a logical asset path. Always rejects traversal/absolute/non-POSIX\n * inputs (the security guard). When `enforceNamespace` (the default — used by the\n * package's own asset resolver), also restricts to the SSOT namespaces. Resolvers\n * pointed at a consumer-supplied `baseDir` pass `enforceNamespace: false` since\n * the consumer owns and scopes that tree. Returns the (unchanged) safe path.\n */\nexport function assertSafeLogicalPath(logicalPath: string, enforceNamespace = true): string {\n const segments = rejectCommon(logicalPath, \"path\");\n if (enforceNamespace) {\n assertNamespace(logicalPath, segments, \"path\");\n }\n return logicalPath;\n}\n\n/**\n * Validate a glob pattern. Always rejects `..`/absolute/backslash patterns; glob\n * metacharacters (`*`, `{}`, etc.) are allowed within segments. Namespace\n * restriction is applied only when `enforceNamespace` (see\n * {@link assertSafeLogicalPath}).\n */\nexport function assertSafePattern(pattern: string, enforceNamespace = true): string {\n const segments = rejectCommon(pattern, \"pattern\");\n if (enforceNamespace) {\n assertNamespace(pattern, segments, \"pattern\");\n }\n return pattern;\n}\n","/**\n * Asset resolution - Embedded backend\n *\n * Reads SSOT assets from generated, build-embedded manifests instead of the\n * filesystem, so loads work inside a `bun --compile` single-file binary (no\n * asset tree on disk). T3 codegen emits per-domain {@link EmbeddedAssetManifest}\n * modules and registers them via {@link registerEmbeddedAssets}; this resolver\n * composes whatever has been registered.\n *\n * Until T3 lands, no manifests are registered, so the embedded resolver is\n * empty — `read()` throws a clear \"not found\" and `list()` returns `[]`.\n */\n\nimport picomatch from \"picomatch\";\nimport { AssetResolutionError } from \"./errors.js\";\nimport { assertSafeLogicalPath, assertSafePattern } from \"./paths.js\";\nimport type { AssetProvenance, AssetResolver, EmbeddedAssetManifest } from \"./types.js\";\n\n/** Process-level registry of embedded asset manifests (composed by domain). */\nconst registeredManifests: EmbeddedAssetManifest[] = [];\n\n/**\n * Monotonic counter bumped on every registration change. The resolver factory\n * folds this into its cache key so a cached embedded resolver is never stale\n * after a domain is registered (entarch watch-item).\n */\nlet registrationVersion = 0;\n\n/** Current registration version (see {@link registrationVersion}). */\nexport function getRegistrationVersion(): number {\n return registrationVersion;\n}\n\n/**\n * Register a generated embedded-asset manifest. Idempotent per domain\n * (re-registering a domain replaces it). Called by generated modules.\n */\nexport function registerEmbeddedAssets(manifest: EmbeddedAssetManifest): void {\n const existing = registeredManifests.findIndex((m) => m.domain === manifest.domain);\n if (existing >= 0) {\n registeredManifests[existing] = manifest;\n } else {\n registeredManifests.push(manifest);\n }\n registrationVersion++;\n}\n\n/** Whether a specific domain has been registered. */\nexport function hasEmbeddedDomain(domain: string): boolean {\n return registeredManifests.some((m) => m.domain === domain);\n}\n\n/** Clear all registered manifests. For testing only. */\nexport function clearEmbeddedAssets(): void {\n registeredManifests.length = 0;\n registrationVersion++;\n}\n\n/** Whether any embedded manifests have been registered. */\nexport function hasEmbeddedAssets(): boolean {\n return registeredManifests.length > 0;\n}\n\n/** Flatten registered manifests into a single logical-path -> content map. */\nfunction buildIndex(): Map<string, string> {\n const index = new Map<string, string>();\n for (const manifest of registeredManifests) {\n for (const [logicalPath, content] of Object.entries(manifest.files)) {\n index.set(logicalPath, content);\n }\n }\n return index;\n}\n\nexport class EmbeddedAssetResolver implements AssetResolver {\n readonly mode = \"embedded\" as const;\n\n // Snapshot the index at construction; resolvers are cheap to recreate and the\n // factory rebuilds when mode changes.\n private readonly index: Map<string, string>;\n\n constructor() {\n this.index = buildIndex();\n }\n\n async read(logicalPath: string): Promise<string> {\n const content = this.index.get(assertSafeLogicalPath(logicalPath));\n if (content === undefined) {\n throw AssetResolutionError.notFound(logicalPath, \"embedded\");\n }\n return content;\n }\n\n async list(patterns: string[]): Promise<string[]> {\n if (patterns.length === 0) {\n return [];\n }\n for (const pattern of patterns) {\n assertSafePattern(pattern);\n }\n const isMatch = picomatch(patterns, { dot: false });\n return Array.from(this.index.keys())\n .filter((key) => isMatch(key))\n .sort();\n }\n\n async has(logicalPath: string): Promise<boolean> {\n try {\n return this.index.has(assertSafeLogicalPath(logicalPath));\n } catch {\n return false;\n }\n }\n\n provenance(): AssetProvenance {\n return { mode: \"embedded\", embeddedCount: this.index.size };\n }\n}\n","/**\n * Asset resolution - Filesystem backend\n *\n * Reads SSOT assets from the on-disk `schemas/crucible-ts` / `config/crucible-ts`\n * trees shipped in the npm package. This is the current (pre-v0.4.0) behavior,\n * now centralized behind the {@link AssetResolver} contract.\n */\n\nimport { readFile, realpath } from \"node:fs/promises\";\nimport { join, sep } from \"node:path\";\nimport glob from \"fast-glob\";\nimport { AssetResolutionError } from \"./errors.js\";\nimport { assertSafeLogicalPath, assertSafePattern } from \"./paths.js\";\nimport type { AssetProvenance, AssetResolver } from \"./types.js\";\n\n/** Normalize a logical path to POSIX separators (logical paths are always POSIX). */\nfunction toPosix(p: string): string {\n return sep === \"/\" ? p : p.split(sep).join(\"/\");\n}\n\nexport class FsAssetResolver implements AssetResolver {\n readonly mode = \"fs\" as const;\n\n /**\n * @param baseDir Directory logical paths resolve against.\n * @param enforceNamespace Restrict to the SSOT namespaces (schemas/config/docs).\n * Default `true` for the package's own asset tree; pass `false` when `baseDir`\n * is a consumer-supplied tree they scope themselves (traversal guards still apply).\n */\n constructor(\n private readonly baseDir: string,\n private readonly enforceNamespace = true,\n ) {}\n\n private resolve(logicalPath: string): string {\n // Validate before joining — prevents `..`/absolute/non-POSIX traversal out of\n // baseDir via the public `./assets` surface.\n return join(this.baseDir, assertSafeLogicalPath(logicalPath, this.enforceNamespace));\n }\n\n async read(logicalPath: string): Promise<string> {\n const absolute = this.resolve(logicalPath);\n try {\n // Resolve symlinks and confirm the target stays within baseDir — a symlink\n // in the shipped tree must not let a read escape the asset root (secrev).\n const real = await realpath(absolute);\n if (!(await this.isWithinBase(real))) {\n throw AssetResolutionError.notFound(logicalPath, \"fs\");\n }\n return await readFile(real, \"utf-8\");\n } catch (error) {\n if (error instanceof AssetResolutionError) {\n throw error;\n }\n const err = error as NodeJS.ErrnoException;\n if (err.code === \"ENOENT\") {\n throw AssetResolutionError.notFound(logicalPath, \"fs\");\n }\n // Reference the logical path + error code only — avoid leaking the absolute\n // host path that a raw FS error message (e.g. EACCES) would carry (secrev).\n throw AssetResolutionError.readFailed(logicalPath, err.code ?? \"unknown\");\n }\n }\n\n /** Cached realpath of baseDir (resolves symlinks once). */\n private baseRealCache?: Promise<string>;\n private baseReal(): Promise<string> {\n if (!this.baseRealCache) {\n this.baseRealCache = realpath(this.baseDir);\n }\n return this.baseRealCache;\n }\n\n /** Whether a resolved (realpath) target stays within the resolver's baseDir. */\n private async isWithinBase(realPath: string): Promise<boolean> {\n const base = await this.baseReal();\n return realPath === base || realPath.startsWith(base + sep);\n }\n\n async list(patterns: string[]): Promise<string[]> {\n if (patterns.length === 0) {\n return [];\n }\n for (const pattern of patterns) {\n assertSafePattern(pattern, this.enforceNamespace);\n }\n // Patterns are package-root-relative; glob with `cwd: baseDir` and return\n // logical (relative, POSIX) paths so callers never see absolute paths.\n const matches = await glob(patterns, {\n cwd: this.baseDir,\n absolute: false,\n onlyFiles: true,\n followSymbolicLinks: false,\n suppressErrors: true,\n dot: false,\n });\n return matches.map(toPosix).sort();\n }\n\n async has(logicalPath: string): Promise<boolean> {\n let absolute: string;\n try {\n absolute = this.resolve(logicalPath);\n } catch {\n return false; // invalid/traversing path → not present\n }\n try {\n const real = await realpath(absolute);\n return await this.isWithinBase(real);\n } catch {\n return false;\n }\n }\n\n provenance(): AssetProvenance {\n return { mode: \"fs\", baseDir: this.baseDir };\n }\n\n /** The on-disk base directory this resolver reads from. */\n getBaseDir(): string {\n return this.baseDir;\n }\n}\n","/**\n * Asset resolution - Resolver factory & mode selection\n *\n * Chooses between the filesystem and embedded backends. Default is `auto`:\n * prefer the filesystem when the on-disk asset tree is present/readable, else\n * fall back to embedded generated modules (the `bun --compile` case). The mode\n * can be forced with the `TSFULMEN_ASSET_MODE` env var or an explicit option.\n */\n\nimport { accessSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n EmbeddedAssetResolver,\n getRegistrationVersion,\n hasEmbeddedAssets,\n} from \"./embedded-resolver.js\";\nimport { AssetResolutionError } from \"./errors.js\";\nimport { FsAssetResolver } from \"./fs-resolver.js\";\nimport type { AssetMode, AssetResolver } from \"./types.js\";\n\n/**\n * Marker that identifies the package-root asset tree. Used both to locate the\n * base dir by walking upward and to test FS availability for `auto` selection.\n */\nconst ASSET_ROOT_MARKER = join(\"schemas\", \"crucible-ts\");\n\nfunction dirExists(path: string): boolean {\n try {\n accessSync(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Find the package-root directory that contains the SSOT asset trees by walking\n * upward from this module. Works for both source (`src/assets/`) and bundled\n * (`dist/`) layouts without hard-coded depths — and returns `null` inside a\n * `bun --compile` binary where no such directory exists on disk.\n */\nexport function findAssetBaseDir(): string | null {\n let dir: string;\n try {\n dir = dirname(fileURLToPath(import.meta.url));\n } catch {\n return null;\n }\n\n // Walk up to the filesystem root looking for the asset-root marker.\n let prev = \"\";\n while (dir !== prev) {\n if (dirExists(join(dir, ASSET_ROOT_MARKER))) {\n return dir;\n }\n prev = dir;\n dir = dirname(dir);\n }\n return null;\n}\n\nfunction readModeFromEnv(): AssetMode | undefined {\n const raw = process.env.TSFULMEN_ASSET_MODE?.trim().toLowerCase();\n if (raw === \"fs\" || raw === \"embedded\" || raw === \"auto\") {\n return raw;\n }\n return undefined;\n}\n\nexport interface ResolveAssetsOptions {\n /** Force a mode. Precedence: explicit option > `TSFULMEN_ASSET_MODE` > `auto`. */\n mode?: AssetMode;\n /** Override the FS base dir (e.g. a consumer's own asset tree). Implies `fs`. */\n baseDir?: string;\n}\n\n/**\n * Build an {@link AssetResolver} for the requested (or auto-detected) mode.\n * Stateless — callers that want a cached instance use {@link getAssetResolver}.\n */\nexport function resolveAssets(options: ResolveAssetsOptions = {}): AssetResolver {\n if (options.baseDir) {\n // Consumer-supplied tree: they scope it, so don't impose the SSOT namespace\n // (traversal guards still apply). Used by e.g. a custom schema-registry baseDir.\n return new FsAssetResolver(options.baseDir, false);\n }\n\n const requested: AssetMode = options.mode ?? readModeFromEnv() ?? \"auto\";\n\n if (requested === \"fs\") {\n const baseDir = findAssetBaseDir();\n if (!baseDir) {\n throw AssetResolutionError.baseDirUnavailable(\n \"mode=fs but no on-disk asset tree found (use mode=embedded in a compiled binary)\",\n );\n }\n return new FsAssetResolver(baseDir);\n }\n\n if (requested === \"embedded\") {\n return new EmbeddedAssetResolver();\n }\n\n // auto: prefer FS when the asset tree is present, else embedded.\n const baseDir = findAssetBaseDir();\n if (baseDir) {\n return new FsAssetResolver(baseDir);\n }\n if (hasEmbeddedAssets()) {\n return new EmbeddedAssetResolver();\n }\n throw AssetResolutionError.baseDirUnavailable(\n \"auto: no on-disk asset tree and no embedded assets registered\",\n );\n}\n\nlet cached: AssetResolver | undefined;\nlet cachedKey: string | undefined;\n\n/**\n * Cached resolver keyed on the effective options. Most call sites should use\n * this; pass fresh options (or different env) to rebuild.\n */\nexport function getAssetResolver(options: ResolveAssetsOptions = {}): AssetResolver {\n const key = JSON.stringify({\n mode: options.mode ?? readModeFromEnv() ?? \"auto\",\n baseDir: options.baseDir ?? null,\n // Fold in the registration version so a cached embedded resolver rebuilds\n // after a domain is registered (e.g. via ensureEmbeddedDomain).\n reg: getRegistrationVersion(),\n });\n if (!cached || cachedKey !== key) {\n cached = resolveAssets(options);\n cachedKey = key;\n }\n return cached;\n}\n\n/** Reset the cached resolver. For testing / after registering embedded assets. */\nexport function resetAssetResolver(): void {\n cached = undefined;\n cachedKey = undefined;\n}\n\n// Embedded-asset registration is STATIC PER-SUBPATH (entarch-sanctioned fallback;\n// see planning/tsfulmen/v040-shaping.md §0.7). Each feature subpath statically\n// imports + registers only its own domain manifest, so a foundry/schema/taxonomy\n// consumer's published bundle carries only the assets it uses — and the `./assets`\n// core (this module) references NO generated content, keeping dist/assets/index.js\n// lean. (Dynamic per-domain lazy loading would require global tsup `splitting`,\n// which we deliberately avoid.) Cache stays correct because registerEmbeddedAssets\n// bumps a version that getAssetResolver folds into its cache key.\n"]}
@@ -1,6 +1,6 @@
1
1
  import { createLogger } from './chunk-5HZEJGZ5.js';
2
- import { FulmenError } from './chunk-CW4CSNVF.js';
3
- import { metrics, isHistogramSummary } from './chunk-7SVYOPJ3.js';
2
+ import { FulmenError } from './chunk-GOTEQYAD.js';
3
+ import { metrics, isHistogramSummary } from './chunk-SNV5WG5V.js';
4
4
  import { createServer } from 'http';
5
5
 
6
6
  // src/telemetry/prometheus/constants.ts
@@ -833,5 +833,5 @@ async function stopMetricsServer(server, timeoutMs = 5e3, exporter) {
833
833
  }
834
834
 
835
835
  export { ADR0007_BUCKETS_MS, ADR0007_BUCKETS_SECONDS, ERROR_HANDLING_METRICS, EXPORTER_LABELS, FOUNDRY_METRICS, FULHASH_METRICS, InvalidLabelNameError, InvalidMetricNameError, MetricRegistrationError, PROMETHEUS_EXPORTER_METRICS, PromClientNotFoundError, PrometheusExporter, PrometheusExporterError, RefreshError, createMetricsHandler, msToSeconds, registerPrometheusShutdown, startMetricsServer, stopMetricsServer };
836
- //# sourceMappingURL=chunk-K2FYT43R.js.map
837
- //# sourceMappingURL=chunk-K2FYT43R.js.map
836
+ //# sourceMappingURL=chunk-7YWACZEJ.js.map
837
+ //# sourceMappingURL=chunk-7YWACZEJ.js.map