@fulmenhq/tsfulmen 0.2.2 → 0.2.4

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.
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from 'crypto';
2
+ import addFormats from 'ajv-formats';
2
3
  import { spawn } from 'child_process';
3
4
  import { readFile, access, mkdir, writeFile } from 'fs/promises';
4
5
  import { parse, stringify } from 'yaml';
@@ -9,7 +10,6 @@ import Ajv from 'ajv';
9
10
  import Ajv2019 from 'ajv/dist/2019';
10
11
  import Ajv2020 from 'ajv/dist/2020';
11
12
  import AjvDraft04 from 'ajv-draft-04';
12
- import addFormats from 'ajv-formats';
13
13
  import { Readable } from 'stream';
14
14
  import picomatch from 'picomatch';
15
15
  import { suggest as suggest$1, substringSimilarity, score as score$1, normalize as normalize$1, jaro_winkler, damerau_levenshtein, osa_distance, levenshtein } from '@3leaps/string-metrics-wasm';
@@ -173,6 +173,27 @@ var init_serialization = __esm({
173
173
  init_severity();
174
174
  }
175
175
  });
176
+ function applyFulmenAjvFormats(ajv, options = {}) {
177
+ const mode = options.mode ?? "fast";
178
+ const formats = options.formats ?? DEFAULT_FORMATS;
179
+ addFormats(ajv, { mode, formats });
180
+ return ajv;
181
+ }
182
+ var DEFAULT_FORMATS;
183
+ var init_ajv_formats = __esm({
184
+ "src/schema/ajv-formats.ts"() {
185
+ DEFAULT_FORMATS = [
186
+ "date-time",
187
+ "email",
188
+ "hostname",
189
+ "ipv4",
190
+ "ipv6",
191
+ "uri",
192
+ "uri-reference",
193
+ "uuid"
194
+ ];
195
+ }
196
+ });
176
197
 
177
198
  // src/schema/errors.ts
178
199
  var errors_exports = {};
@@ -1728,10 +1749,7 @@ function createAjv(dialect) {
1728
1749
  // Enable async schema loading for YAML references
1729
1750
  loadSchema: loadReferencedSchema
1730
1751
  });
1731
- addFormats(ajv, {
1732
- mode: "fast",
1733
- formats: ["date-time", "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference"]
1734
- });
1752
+ applyFulmenAjvFormats(ajv);
1735
1753
  return ajv;
1736
1754
  }
1737
1755
  async function getAjv(dialect) {
@@ -1977,6 +1995,7 @@ var ajvInstances, metaschemaReady, schemaCache;
1977
1995
  var init_validator = __esm({
1978
1996
  "src/schema/validator.ts"() {
1979
1997
  init_telemetry();
1998
+ init_ajv_formats();
1980
1999
  init_errors();
1981
2000
  init_registry();
1982
2001
  init_utils();
@@ -3936,6 +3955,224 @@ var init_capabilities2 = __esm({
3936
3955
  }
3937
3956
  });
3938
3957
 
3958
+ // src/foundry/signals/config-reload-endpoint.ts
3959
+ function createConfigReloadEndpoint(options) {
3960
+ const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
3961
+ return async (payload, req) => {
3962
+ const correlationId = payload.correlation_id ?? generateCorrelationId2();
3963
+ const authResult = await auth(req);
3964
+ if (!authResult.authenticated) {
3965
+ if (logger) {
3966
+ logger.warn("Config reload endpoint: authentication failed", {
3967
+ correlation_id: correlationId,
3968
+ reason: authResult.reason
3969
+ });
3970
+ }
3971
+ if (telemetry) {
3972
+ telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
3973
+ correlation_id: correlationId
3974
+ });
3975
+ }
3976
+ return {
3977
+ status: "error",
3978
+ error: "authentication_failed",
3979
+ message: authResult.reason || "Authentication required",
3980
+ statusCode: 401
3981
+ };
3982
+ }
3983
+ const identity = authResult.identity || "unknown";
3984
+ if (rateLimit) {
3985
+ const rateLimitResult = await rateLimit(identity);
3986
+ if (!rateLimitResult.allowed) {
3987
+ if (logger) {
3988
+ logger.warn("Config reload endpoint: rate limit exceeded", {
3989
+ correlation_id: correlationId,
3990
+ identity
3991
+ });
3992
+ }
3993
+ if (telemetry) {
3994
+ telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
3995
+ correlation_id: correlationId
3996
+ });
3997
+ }
3998
+ return {
3999
+ status: "error",
4000
+ error: "rate_limit_exceeded",
4001
+ message: "Rate limit exceeded. Please try again later.",
4002
+ statusCode: 429
4003
+ };
4004
+ }
4005
+ }
4006
+ if (telemetry) {
4007
+ telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
4008
+ correlation_id: correlationId
4009
+ });
4010
+ }
4011
+ try {
4012
+ const config = await loader();
4013
+ if (validator) {
4014
+ const validation = await validator(config);
4015
+ if (!validation.valid) {
4016
+ if (logger) {
4017
+ logger.warn("Config reload endpoint: validation failed", {
4018
+ correlation_id: correlationId,
4019
+ error_count: validation.errors?.length ?? 0
4020
+ });
4021
+ }
4022
+ if (telemetry) {
4023
+ telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
4024
+ correlation_id: correlationId,
4025
+ reason: "validation_failed"
4026
+ });
4027
+ }
4028
+ return {
4029
+ status: "error",
4030
+ error: "validation_failed",
4031
+ message: "Configuration validation failed",
4032
+ validation_errors: validation.errors,
4033
+ statusCode: 422
4034
+ };
4035
+ }
4036
+ }
4037
+ if (onReload2) {
4038
+ await onReload2(config);
4039
+ }
4040
+ if (telemetry) {
4041
+ telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
4042
+ correlation_id: correlationId
4043
+ });
4044
+ }
4045
+ if (logger) {
4046
+ logger.info("Config reload endpoint: reload accepted", {
4047
+ correlation_id: correlationId,
4048
+ reason: payload.reason
4049
+ });
4050
+ }
4051
+ return {
4052
+ status: "reloaded",
4053
+ correlation_id: correlationId,
4054
+ message: "Configuration reloaded",
4055
+ statusCode: 200
4056
+ };
4057
+ } catch (error) {
4058
+ if (logger) {
4059
+ logger.warn("Config reload endpoint: reload failed", {
4060
+ correlation_id: correlationId,
4061
+ error: error instanceof Error ? error.message : String(error)
4062
+ });
4063
+ }
4064
+ if (telemetry) {
4065
+ telemetry.emit("fulmen.config.http_endpoint.reload_error", {
4066
+ correlation_id: correlationId,
4067
+ error_type: error instanceof Error ? error.constructor.name : "unknown"
4068
+ });
4069
+ }
4070
+ return {
4071
+ status: "error",
4072
+ error: "reload_failed",
4073
+ message: error instanceof Error ? error.message : String(error),
4074
+ statusCode: 500
4075
+ };
4076
+ }
4077
+ };
4078
+ }
4079
+ function generateCorrelationId2() {
4080
+ return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4081
+ }
4082
+ var init_config_reload_endpoint = __esm({
4083
+ "src/foundry/signals/config-reload-endpoint.ts"() {
4084
+ }
4085
+ });
4086
+
4087
+ // src/appidentity/runtime.ts
4088
+ function detectRuntime() {
4089
+ const versions = process.versions;
4090
+ if (typeof versions.bun === "string" && versions.bun.length > 0) {
4091
+ return { name: "bun", version: versions.bun };
4092
+ }
4093
+ if (typeof versions.node === "string" && versions.node.length > 0) {
4094
+ return { name: "node", version: versions.node };
4095
+ }
4096
+ return { name: "unknown" };
4097
+ }
4098
+ function buildRuntimeInfo(options = {}) {
4099
+ const runtime = detectRuntime();
4100
+ const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
4101
+ const vendor = options.vendor ?? options.identity?.app.vendor;
4102
+ return {
4103
+ service: {
4104
+ name: serviceName,
4105
+ vendor,
4106
+ version: options.version
4107
+ },
4108
+ runtime,
4109
+ platform: {
4110
+ os: process.platform,
4111
+ arch: process.arch
4112
+ }
4113
+ };
4114
+ }
4115
+ var init_runtime = __esm({
4116
+ "src/appidentity/runtime.ts"() {
4117
+ }
4118
+ });
4119
+
4120
+ // src/foundry/signals/control-discovery-endpoint.ts
4121
+ function createControlDiscoveryEndpoint(options) {
4122
+ const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
4123
+ return async (req) => {
4124
+ if (auth) {
4125
+ const authResult = await auth(req);
4126
+ if (!authResult.authenticated) {
4127
+ if (logger) {
4128
+ logger.warn("Control discovery endpoint: authentication failed", {
4129
+ reason: authResult.reason
4130
+ });
4131
+ }
4132
+ if (telemetry) {
4133
+ telemetry.emit("fulmen.control.discovery.auth_failed", {
4134
+ service: identity.app.binary_name
4135
+ });
4136
+ }
4137
+ return {
4138
+ status: "error",
4139
+ error: "authentication_failed",
4140
+ message: authResult.reason || "Authentication required",
4141
+ statusCode: 401
4142
+ };
4143
+ }
4144
+ }
4145
+ if (telemetry) {
4146
+ telemetry.emit("fulmen.control.discovery.served", {
4147
+ service: identity.app.binary_name
4148
+ });
4149
+ }
4150
+ const runtime = buildRuntimeInfo({ identity, version });
4151
+ return {
4152
+ status: "ok",
4153
+ service: {
4154
+ name: identity.app.binary_name,
4155
+ vendor: identity.app.vendor,
4156
+ version
4157
+ },
4158
+ runtime: {
4159
+ name: runtime.runtime.name,
4160
+ version: runtime.runtime.version,
4161
+ platform: runtime.platform.os,
4162
+ arch: runtime.platform.arch
4163
+ },
4164
+ auth_summary: authSummary,
4165
+ endpoints,
4166
+ statusCode: 200
4167
+ };
4168
+ };
4169
+ }
4170
+ var init_control_discovery_endpoint = __esm({
4171
+ "src/foundry/signals/control-discovery-endpoint.ts"() {
4172
+ init_runtime();
4173
+ }
4174
+ });
4175
+
3939
4176
  // src/foundry/signals/convenience.ts
3940
4177
  async function onShutdown(manager, handler, options = {}) {
3941
4178
  await manager.register("SIGTERM", handler, options);
@@ -4195,7 +4432,7 @@ var init_guards = __esm({
4195
4432
  function createSignalEndpoint(options) {
4196
4433
  const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
4197
4434
  return async (payload, req) => {
4198
- const correlationId = payload.correlation_id ?? generateCorrelationId2();
4435
+ const correlationId = payload.correlation_id ?? generateCorrelationId3();
4199
4436
  const authResult = await auth(req);
4200
4437
  if (!authResult.authenticated) {
4201
4438
  if (logger) {
@@ -4318,7 +4555,7 @@ function normalizeSignalName(signal) {
4318
4555
  }
4319
4556
  return `SIG${upper}`;
4320
4557
  }
4321
- function generateCorrelationId2() {
4558
+ function generateCorrelationId3() {
4322
4559
  return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4323
4560
  }
4324
4561
  function createBearerTokenAuth(expectedToken) {
@@ -4785,6 +5022,8 @@ var init_signals = __esm({
4785
5022
  "src/foundry/signals/index.ts"() {
4786
5023
  init_capabilities2();
4787
5024
  init_catalog();
5025
+ init_config_reload_endpoint();
5026
+ init_control_discovery_endpoint();
4788
5027
  init_convenience();
4789
5028
  init_double_tap();
4790
5029
  init_guards();
@@ -4930,7 +5169,9 @@ __export(foundry_exports, {
4930
5169
  clearMimeTypeCache: () => clearMimeTypeCache,
4931
5170
  clearPatternCache: () => clearPatternCache,
4932
5171
  createBearerTokenAuth: () => createBearerTokenAuth,
5172
+ createConfigReloadEndpoint: () => createConfigReloadEndpoint,
4933
5173
  createConfigReloadHandler: () => createConfigReloadHandler,
5174
+ createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
4934
5175
  createDoubleTapTracker: () => createDoubleTapTracker,
4935
5176
  createSignalEndpoint: () => createSignalEndpoint,
4936
5177
  createSignalManager: () => createSignalManager,
@@ -5653,6 +5894,7 @@ var init_cli = __esm({
5653
5894
  // src/schema/index.ts
5654
5895
  var init_schema = __esm({
5655
5896
  "src/schema/index.ts"() {
5897
+ init_ajv_formats();
5656
5898
  init_cli();
5657
5899
  init_errors();
5658
5900
  init_export();