@fulmenhq/tsfulmen 0.2.2 → 0.2.3

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,5 +1,6 @@
1
1
  import { crc32, xxhash128, createXXHash128, createCRC32 } from 'hash-wasm';
2
2
  import { createHash, randomUUID } from 'crypto';
3
+ import addFormats from 'ajv-formats';
3
4
  import { spawn } from 'child_process';
4
5
  import fs2, { readFile, writeFile, access, mkdir, lstat, realpath } from 'fs/promises';
5
6
  import { parse, stringify } from 'yaml';
@@ -10,7 +11,6 @@ import Ajv from 'ajv';
10
11
  import Ajv2019 from 'ajv/dist/2019';
11
12
  import Ajv2020 from 'ajv/dist/2020';
12
13
  import AjvDraft04 from 'ajv-draft-04';
13
- import addFormats from 'ajv-formats';
14
14
  import { Readable } from 'stream';
15
15
  import picomatch from 'picomatch';
16
16
  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';
@@ -409,6 +409,27 @@ var init_serialization = __esm({
409
409
  init_severity();
410
410
  }
411
411
  });
412
+ function applyFulmenAjvFormats(ajv, options = {}) {
413
+ const mode = options.mode ?? "fast";
414
+ const formats = options.formats ?? DEFAULT_FORMATS;
415
+ addFormats(ajv, { mode, formats });
416
+ return ajv;
417
+ }
418
+ var DEFAULT_FORMATS;
419
+ var init_ajv_formats = __esm({
420
+ "src/schema/ajv-formats.ts"() {
421
+ DEFAULT_FORMATS = [
422
+ "date-time",
423
+ "email",
424
+ "hostname",
425
+ "ipv4",
426
+ "ipv6",
427
+ "uri",
428
+ "uri-reference",
429
+ "uuid"
430
+ ];
431
+ }
432
+ });
412
433
 
413
434
  // src/schema/errors.ts
414
435
  var errors_exports = {};
@@ -1964,10 +1985,7 @@ function createAjv(dialect) {
1964
1985
  // Enable async schema loading for YAML references
1965
1986
  loadSchema: loadReferencedSchema
1966
1987
  });
1967
- addFormats(ajv, {
1968
- mode: "fast",
1969
- formats: ["date-time", "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference"]
1970
- });
1988
+ applyFulmenAjvFormats(ajv);
1971
1989
  return ajv;
1972
1990
  }
1973
1991
  async function getAjv(dialect) {
@@ -2213,6 +2231,7 @@ var ajvInstances, metaschemaReady, schemaCache;
2213
2231
  var init_validator = __esm({
2214
2232
  "src/schema/validator.ts"() {
2215
2233
  init_telemetry();
2234
+ init_ajv_formats();
2216
2235
  init_errors2();
2217
2236
  init_registry();
2218
2237
  init_utils();
@@ -4172,6 +4191,224 @@ var init_capabilities2 = __esm({
4172
4191
  }
4173
4192
  });
4174
4193
 
4194
+ // src/foundry/signals/config-reload-endpoint.ts
4195
+ function createConfigReloadEndpoint(options) {
4196
+ const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
4197
+ return async (payload, req) => {
4198
+ const correlationId = payload.correlation_id ?? generateCorrelationId2();
4199
+ const authResult = await auth(req);
4200
+ if (!authResult.authenticated) {
4201
+ if (logger) {
4202
+ logger.warn("Config reload endpoint: authentication failed", {
4203
+ correlation_id: correlationId,
4204
+ reason: authResult.reason
4205
+ });
4206
+ }
4207
+ if (telemetry) {
4208
+ telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
4209
+ correlation_id: correlationId
4210
+ });
4211
+ }
4212
+ return {
4213
+ status: "error",
4214
+ error: "authentication_failed",
4215
+ message: authResult.reason || "Authentication required",
4216
+ statusCode: 401
4217
+ };
4218
+ }
4219
+ const identity = authResult.identity || "unknown";
4220
+ if (rateLimit) {
4221
+ const rateLimitResult = await rateLimit(identity);
4222
+ if (!rateLimitResult.allowed) {
4223
+ if (logger) {
4224
+ logger.warn("Config reload endpoint: rate limit exceeded", {
4225
+ correlation_id: correlationId,
4226
+ identity
4227
+ });
4228
+ }
4229
+ if (telemetry) {
4230
+ telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
4231
+ correlation_id: correlationId
4232
+ });
4233
+ }
4234
+ return {
4235
+ status: "error",
4236
+ error: "rate_limit_exceeded",
4237
+ message: "Rate limit exceeded. Please try again later.",
4238
+ statusCode: 429
4239
+ };
4240
+ }
4241
+ }
4242
+ if (telemetry) {
4243
+ telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
4244
+ correlation_id: correlationId
4245
+ });
4246
+ }
4247
+ try {
4248
+ const config = await loader();
4249
+ if (validator) {
4250
+ const validation = await validator(config);
4251
+ if (!validation.valid) {
4252
+ if (logger) {
4253
+ logger.warn("Config reload endpoint: validation failed", {
4254
+ correlation_id: correlationId,
4255
+ error_count: validation.errors?.length ?? 0
4256
+ });
4257
+ }
4258
+ if (telemetry) {
4259
+ telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
4260
+ correlation_id: correlationId,
4261
+ reason: "validation_failed"
4262
+ });
4263
+ }
4264
+ return {
4265
+ status: "error",
4266
+ error: "validation_failed",
4267
+ message: "Configuration validation failed",
4268
+ validation_errors: validation.errors,
4269
+ statusCode: 422
4270
+ };
4271
+ }
4272
+ }
4273
+ if (onReload2) {
4274
+ await onReload2(config);
4275
+ }
4276
+ if (telemetry) {
4277
+ telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
4278
+ correlation_id: correlationId
4279
+ });
4280
+ }
4281
+ if (logger) {
4282
+ logger.info("Config reload endpoint: reload accepted", {
4283
+ correlation_id: correlationId,
4284
+ reason: payload.reason
4285
+ });
4286
+ }
4287
+ return {
4288
+ status: "reloaded",
4289
+ correlation_id: correlationId,
4290
+ message: "Configuration reloaded",
4291
+ statusCode: 200
4292
+ };
4293
+ } catch (error) {
4294
+ if (logger) {
4295
+ logger.warn("Config reload endpoint: reload failed", {
4296
+ correlation_id: correlationId,
4297
+ error: error instanceof Error ? error.message : String(error)
4298
+ });
4299
+ }
4300
+ if (telemetry) {
4301
+ telemetry.emit("fulmen.config.http_endpoint.reload_error", {
4302
+ correlation_id: correlationId,
4303
+ error_type: error instanceof Error ? error.constructor.name : "unknown"
4304
+ });
4305
+ }
4306
+ return {
4307
+ status: "error",
4308
+ error: "reload_failed",
4309
+ message: error instanceof Error ? error.message : String(error),
4310
+ statusCode: 500
4311
+ };
4312
+ }
4313
+ };
4314
+ }
4315
+ function generateCorrelationId2() {
4316
+ return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4317
+ }
4318
+ var init_config_reload_endpoint = __esm({
4319
+ "src/foundry/signals/config-reload-endpoint.ts"() {
4320
+ }
4321
+ });
4322
+
4323
+ // src/appidentity/runtime.ts
4324
+ function detectRuntime() {
4325
+ const versions = process.versions;
4326
+ if (typeof versions.bun === "string" && versions.bun.length > 0) {
4327
+ return { name: "bun", version: versions.bun };
4328
+ }
4329
+ if (typeof versions.node === "string" && versions.node.length > 0) {
4330
+ return { name: "node", version: versions.node };
4331
+ }
4332
+ return { name: "unknown" };
4333
+ }
4334
+ function buildRuntimeInfo(options = {}) {
4335
+ const runtime = detectRuntime();
4336
+ const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
4337
+ const vendor = options.vendor ?? options.identity?.app.vendor;
4338
+ return {
4339
+ service: {
4340
+ name: serviceName,
4341
+ vendor,
4342
+ version: options.version
4343
+ },
4344
+ runtime,
4345
+ platform: {
4346
+ os: process.platform,
4347
+ arch: process.arch
4348
+ }
4349
+ };
4350
+ }
4351
+ var init_runtime = __esm({
4352
+ "src/appidentity/runtime.ts"() {
4353
+ }
4354
+ });
4355
+
4356
+ // src/foundry/signals/control-discovery-endpoint.ts
4357
+ function createControlDiscoveryEndpoint(options) {
4358
+ const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
4359
+ return async (req) => {
4360
+ if (auth) {
4361
+ const authResult = await auth(req);
4362
+ if (!authResult.authenticated) {
4363
+ if (logger) {
4364
+ logger.warn("Control discovery endpoint: authentication failed", {
4365
+ reason: authResult.reason
4366
+ });
4367
+ }
4368
+ if (telemetry) {
4369
+ telemetry.emit("fulmen.control.discovery.auth_failed", {
4370
+ service: identity.app.binary_name
4371
+ });
4372
+ }
4373
+ return {
4374
+ status: "error",
4375
+ error: "authentication_failed",
4376
+ message: authResult.reason || "Authentication required",
4377
+ statusCode: 401
4378
+ };
4379
+ }
4380
+ }
4381
+ if (telemetry) {
4382
+ telemetry.emit("fulmen.control.discovery.served", {
4383
+ service: identity.app.binary_name
4384
+ });
4385
+ }
4386
+ const runtime = buildRuntimeInfo({ identity, version });
4387
+ return {
4388
+ status: "ok",
4389
+ service: {
4390
+ name: identity.app.binary_name,
4391
+ vendor: identity.app.vendor,
4392
+ version
4393
+ },
4394
+ runtime: {
4395
+ name: runtime.runtime.name,
4396
+ version: runtime.runtime.version,
4397
+ platform: runtime.platform.os,
4398
+ arch: runtime.platform.arch
4399
+ },
4400
+ auth_summary: authSummary,
4401
+ endpoints,
4402
+ statusCode: 200
4403
+ };
4404
+ };
4405
+ }
4406
+ var init_control_discovery_endpoint = __esm({
4407
+ "src/foundry/signals/control-discovery-endpoint.ts"() {
4408
+ init_runtime();
4409
+ }
4410
+ });
4411
+
4175
4412
  // src/foundry/signals/convenience.ts
4176
4413
  async function onShutdown(manager, handler, options = {}) {
4177
4414
  await manager.register("SIGTERM", handler, options);
@@ -4431,7 +4668,7 @@ var init_guards = __esm({
4431
4668
  function createSignalEndpoint(options) {
4432
4669
  const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
4433
4670
  return async (payload, req) => {
4434
- const correlationId = payload.correlation_id ?? generateCorrelationId2();
4671
+ const correlationId = payload.correlation_id ?? generateCorrelationId3();
4435
4672
  const authResult = await auth(req);
4436
4673
  if (!authResult.authenticated) {
4437
4674
  if (logger) {
@@ -4554,7 +4791,7 @@ function normalizeSignalName(signal) {
4554
4791
  }
4555
4792
  return `SIG${upper}`;
4556
4793
  }
4557
- function generateCorrelationId2() {
4794
+ function generateCorrelationId3() {
4558
4795
  return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4559
4796
  }
4560
4797
  function createBearerTokenAuth(expectedToken) {
@@ -5021,6 +5258,8 @@ var init_signals = __esm({
5021
5258
  "src/foundry/signals/index.ts"() {
5022
5259
  init_capabilities2();
5023
5260
  init_catalog();
5261
+ init_config_reload_endpoint();
5262
+ init_control_discovery_endpoint();
5024
5263
  init_convenience();
5025
5264
  init_double_tap();
5026
5265
  init_guards();
@@ -5166,7 +5405,9 @@ __export(foundry_exports, {
5166
5405
  clearMimeTypeCache: () => clearMimeTypeCache,
5167
5406
  clearPatternCache: () => clearPatternCache,
5168
5407
  createBearerTokenAuth: () => createBearerTokenAuth,
5408
+ createConfigReloadEndpoint: () => createConfigReloadEndpoint,
5169
5409
  createConfigReloadHandler: () => createConfigReloadHandler,
5410
+ createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
5170
5411
  createDoubleTapTracker: () => createDoubleTapTracker,
5171
5412
  createSignalEndpoint: () => createSignalEndpoint,
5172
5413
  createSignalManager: () => createSignalManager,
@@ -5889,6 +6130,7 @@ var init_cli = __esm({
5889
6130
  // src/schema/index.ts
5890
6131
  var init_schema = __esm({
5891
6132
  "src/schema/index.ts"() {
6133
+ init_ajv_formats();
5892
6134
  init_cli();
5893
6135
  init_errors2();
5894
6136
  init_export();