@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,3 +1,4 @@
1
+ import addFormats from 'ajv-formats';
1
2
  import { spawn } from 'child_process';
2
3
  import { readFile, writeFile, access, mkdir } from 'fs/promises';
3
4
  import { parse, stringify } from 'yaml';
@@ -8,7 +9,6 @@ import Ajv from 'ajv';
8
9
  import Ajv2019 from 'ajv/dist/2019';
9
10
  import Ajv2020 from 'ajv/dist/2020';
10
11
  import AjvDraft04 from 'ajv-draft-04';
11
- import addFormats from 'ajv-formats';
12
12
  import { Readable } from 'stream';
13
13
  import picomatch from 'picomatch';
14
14
  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';
@@ -36,6 +36,27 @@ var init_constants = __esm({
36
36
  MAX_ANCESTOR_SEARCH_DEPTH = 20;
37
37
  }
38
38
  });
39
+ function applyFulmenAjvFormats(ajv, options = {}) {
40
+ const mode = options.mode ?? "fast";
41
+ const formats = options.formats ?? DEFAULT_FORMATS;
42
+ addFormats(ajv, { mode, formats });
43
+ return ajv;
44
+ }
45
+ var DEFAULT_FORMATS;
46
+ var init_ajv_formats = __esm({
47
+ "src/schema/ajv-formats.ts"() {
48
+ DEFAULT_FORMATS = [
49
+ "date-time",
50
+ "email",
51
+ "hostname",
52
+ "ipv4",
53
+ "ipv6",
54
+ "uri",
55
+ "uri-reference",
56
+ "uuid"
57
+ ];
58
+ }
59
+ });
39
60
 
40
61
  // src/schema/errors.ts
41
62
  var errors_exports = {};
@@ -1591,10 +1612,7 @@ function createAjv(dialect) {
1591
1612
  // Enable async schema loading for YAML references
1592
1613
  loadSchema: loadReferencedSchema
1593
1614
  });
1594
- addFormats(ajv, {
1595
- mode: "fast",
1596
- formats: ["date-time", "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference"]
1597
- });
1615
+ applyFulmenAjvFormats(ajv);
1598
1616
  return ajv;
1599
1617
  }
1600
1618
  async function getAjv(dialect) {
@@ -1840,6 +1858,7 @@ var ajvInstances, metaschemaReady, schemaCache;
1840
1858
  var init_validator = __esm({
1841
1859
  "src/schema/validator.ts"() {
1842
1860
  init_telemetry();
1861
+ init_ajv_formats();
1843
1862
  init_errors();
1844
1863
  init_registry();
1845
1864
  init_utils();
@@ -3799,6 +3818,224 @@ var init_capabilities2 = __esm({
3799
3818
  }
3800
3819
  });
3801
3820
 
3821
+ // src/foundry/signals/config-reload-endpoint.ts
3822
+ function createConfigReloadEndpoint(options) {
3823
+ const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
3824
+ return async (payload, req) => {
3825
+ const correlationId = payload.correlation_id ?? generateCorrelationId();
3826
+ const authResult = await auth(req);
3827
+ if (!authResult.authenticated) {
3828
+ if (logger) {
3829
+ logger.warn("Config reload endpoint: authentication failed", {
3830
+ correlation_id: correlationId,
3831
+ reason: authResult.reason
3832
+ });
3833
+ }
3834
+ if (telemetry) {
3835
+ telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
3836
+ correlation_id: correlationId
3837
+ });
3838
+ }
3839
+ return {
3840
+ status: "error",
3841
+ error: "authentication_failed",
3842
+ message: authResult.reason || "Authentication required",
3843
+ statusCode: 401
3844
+ };
3845
+ }
3846
+ const identity = authResult.identity || "unknown";
3847
+ if (rateLimit) {
3848
+ const rateLimitResult = await rateLimit(identity);
3849
+ if (!rateLimitResult.allowed) {
3850
+ if (logger) {
3851
+ logger.warn("Config reload endpoint: rate limit exceeded", {
3852
+ correlation_id: correlationId,
3853
+ identity
3854
+ });
3855
+ }
3856
+ if (telemetry) {
3857
+ telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
3858
+ correlation_id: correlationId
3859
+ });
3860
+ }
3861
+ return {
3862
+ status: "error",
3863
+ error: "rate_limit_exceeded",
3864
+ message: "Rate limit exceeded. Please try again later.",
3865
+ statusCode: 429
3866
+ };
3867
+ }
3868
+ }
3869
+ if (telemetry) {
3870
+ telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
3871
+ correlation_id: correlationId
3872
+ });
3873
+ }
3874
+ try {
3875
+ const config = await loader();
3876
+ if (validator) {
3877
+ const validation = await validator(config);
3878
+ if (!validation.valid) {
3879
+ if (logger) {
3880
+ logger.warn("Config reload endpoint: validation failed", {
3881
+ correlation_id: correlationId,
3882
+ error_count: validation.errors?.length ?? 0
3883
+ });
3884
+ }
3885
+ if (telemetry) {
3886
+ telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
3887
+ correlation_id: correlationId,
3888
+ reason: "validation_failed"
3889
+ });
3890
+ }
3891
+ return {
3892
+ status: "error",
3893
+ error: "validation_failed",
3894
+ message: "Configuration validation failed",
3895
+ validation_errors: validation.errors,
3896
+ statusCode: 422
3897
+ };
3898
+ }
3899
+ }
3900
+ if (onReload2) {
3901
+ await onReload2(config);
3902
+ }
3903
+ if (telemetry) {
3904
+ telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
3905
+ correlation_id: correlationId
3906
+ });
3907
+ }
3908
+ if (logger) {
3909
+ logger.info("Config reload endpoint: reload accepted", {
3910
+ correlation_id: correlationId,
3911
+ reason: payload.reason
3912
+ });
3913
+ }
3914
+ return {
3915
+ status: "reloaded",
3916
+ correlation_id: correlationId,
3917
+ message: "Configuration reloaded",
3918
+ statusCode: 200
3919
+ };
3920
+ } catch (error) {
3921
+ if (logger) {
3922
+ logger.warn("Config reload endpoint: reload failed", {
3923
+ correlation_id: correlationId,
3924
+ error: error instanceof Error ? error.message : String(error)
3925
+ });
3926
+ }
3927
+ if (telemetry) {
3928
+ telemetry.emit("fulmen.config.http_endpoint.reload_error", {
3929
+ correlation_id: correlationId,
3930
+ error_type: error instanceof Error ? error.constructor.name : "unknown"
3931
+ });
3932
+ }
3933
+ return {
3934
+ status: "error",
3935
+ error: "reload_failed",
3936
+ message: error instanceof Error ? error.message : String(error),
3937
+ statusCode: 500
3938
+ };
3939
+ }
3940
+ };
3941
+ }
3942
+ function generateCorrelationId() {
3943
+ return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
3944
+ }
3945
+ var init_config_reload_endpoint = __esm({
3946
+ "src/foundry/signals/config-reload-endpoint.ts"() {
3947
+ }
3948
+ });
3949
+
3950
+ // src/appidentity/runtime.ts
3951
+ function detectRuntime() {
3952
+ const versions = process.versions;
3953
+ if (typeof versions.bun === "string" && versions.bun.length > 0) {
3954
+ return { name: "bun", version: versions.bun };
3955
+ }
3956
+ if (typeof versions.node === "string" && versions.node.length > 0) {
3957
+ return { name: "node", version: versions.node };
3958
+ }
3959
+ return { name: "unknown" };
3960
+ }
3961
+ function buildRuntimeInfo(options = {}) {
3962
+ const runtime = detectRuntime();
3963
+ const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
3964
+ const vendor = options.vendor ?? options.identity?.app.vendor;
3965
+ return {
3966
+ service: {
3967
+ name: serviceName,
3968
+ vendor,
3969
+ version: options.version
3970
+ },
3971
+ runtime,
3972
+ platform: {
3973
+ os: process.platform,
3974
+ arch: process.arch
3975
+ }
3976
+ };
3977
+ }
3978
+ var init_runtime = __esm({
3979
+ "src/appidentity/runtime.ts"() {
3980
+ }
3981
+ });
3982
+
3983
+ // src/foundry/signals/control-discovery-endpoint.ts
3984
+ function createControlDiscoveryEndpoint(options) {
3985
+ const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
3986
+ return async (req) => {
3987
+ if (auth) {
3988
+ const authResult = await auth(req);
3989
+ if (!authResult.authenticated) {
3990
+ if (logger) {
3991
+ logger.warn("Control discovery endpoint: authentication failed", {
3992
+ reason: authResult.reason
3993
+ });
3994
+ }
3995
+ if (telemetry) {
3996
+ telemetry.emit("fulmen.control.discovery.auth_failed", {
3997
+ service: identity.app.binary_name
3998
+ });
3999
+ }
4000
+ return {
4001
+ status: "error",
4002
+ error: "authentication_failed",
4003
+ message: authResult.reason || "Authentication required",
4004
+ statusCode: 401
4005
+ };
4006
+ }
4007
+ }
4008
+ if (telemetry) {
4009
+ telemetry.emit("fulmen.control.discovery.served", {
4010
+ service: identity.app.binary_name
4011
+ });
4012
+ }
4013
+ const runtime = buildRuntimeInfo({ identity, version });
4014
+ return {
4015
+ status: "ok",
4016
+ service: {
4017
+ name: identity.app.binary_name,
4018
+ vendor: identity.app.vendor,
4019
+ version
4020
+ },
4021
+ runtime: {
4022
+ name: runtime.runtime.name,
4023
+ version: runtime.runtime.version,
4024
+ platform: runtime.platform.os,
4025
+ arch: runtime.platform.arch
4026
+ },
4027
+ auth_summary: authSummary,
4028
+ endpoints,
4029
+ statusCode: 200
4030
+ };
4031
+ };
4032
+ }
4033
+ var init_control_discovery_endpoint = __esm({
4034
+ "src/foundry/signals/control-discovery-endpoint.ts"() {
4035
+ init_runtime();
4036
+ }
4037
+ });
4038
+
3802
4039
  // src/foundry/signals/convenience.ts
3803
4040
  async function onShutdown(manager, handler, options = {}) {
3804
4041
  await manager.register("SIGTERM", handler, options);
@@ -4058,7 +4295,7 @@ var init_guards = __esm({
4058
4295
  function createSignalEndpoint(options) {
4059
4296
  const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
4060
4297
  return async (payload, req) => {
4061
- const correlationId = payload.correlation_id ?? generateCorrelationId();
4298
+ const correlationId = payload.correlation_id ?? generateCorrelationId2();
4062
4299
  const authResult = await auth(req);
4063
4300
  if (!authResult.authenticated) {
4064
4301
  if (logger) {
@@ -4181,7 +4418,7 @@ function normalizeSignalName(signal) {
4181
4418
  }
4182
4419
  return `SIG${upper}`;
4183
4420
  }
4184
- function generateCorrelationId() {
4421
+ function generateCorrelationId2() {
4185
4422
  return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4186
4423
  }
4187
4424
  function createBearerTokenAuth(expectedToken) {
@@ -4648,6 +4885,8 @@ var init_signals = __esm({
4648
4885
  "src/foundry/signals/index.ts"() {
4649
4886
  init_capabilities2();
4650
4887
  init_catalog();
4888
+ init_config_reload_endpoint();
4889
+ init_control_discovery_endpoint();
4651
4890
  init_convenience();
4652
4891
  init_double_tap();
4653
4892
  init_guards();
@@ -4793,7 +5032,9 @@ __export(foundry_exports, {
4793
5032
  clearMimeTypeCache: () => clearMimeTypeCache,
4794
5033
  clearPatternCache: () => clearPatternCache,
4795
5034
  createBearerTokenAuth: () => createBearerTokenAuth,
5035
+ createConfigReloadEndpoint: () => createConfigReloadEndpoint,
4796
5036
  createConfigReloadHandler: () => createConfigReloadHandler,
5037
+ createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
4797
5038
  createDoubleTapTracker: () => createDoubleTapTracker,
4798
5039
  createSignalEndpoint: () => createSignalEndpoint,
4799
5040
  createSignalManager: () => createSignalManager,
@@ -5825,6 +6066,7 @@ var init_cli = __esm({
5825
6066
  // src/schema/index.ts
5826
6067
  var init_schema = __esm({
5827
6068
  "src/schema/index.ts"() {
6069
+ init_ajv_formats();
5828
6070
  init_cli();
5829
6071
  init_errors();
5830
6072
  init_export();
@@ -5858,6 +6100,7 @@ init_loader2();
5858
6100
 
5859
6101
  // src/appidentity/index.ts
5860
6102
  init_loader2();
6103
+ init_runtime();
5861
6104
 
5862
6105
  // src/telemetry/http/index.ts
5863
6106
  init_telemetry();