@lensmcp/cluster 1.18.3 → 1.18.6

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 (190) hide show
  1. package/basic-ssl.d.ts +0 -1
  2. package/basic-ssl.js +1 -241
  3. package/build-scope-patterns.d.ts +0 -1
  4. package/build-scope-patterns.js +1 -40
  5. package/create-webpack-dev.d.ts +0 -1
  6. package/create-webpack-dev.js +1 -186
  7. package/create-webpack-prod.d.ts +0 -1
  8. package/create-webpack-prod.js +1 -169
  9. package/executors/build/build.impl.d.ts +0 -1
  10. package/executors/build/build.impl.js +1 -98
  11. package/executors/gateway/gateway-errors.d.ts +0 -1
  12. package/executors/gateway/gateway-errors.js +1 -43
  13. package/executors/gateway/gateway.impl.d.ts +0 -1
  14. package/executors/gateway/gateway.impl.js +1 -53
  15. package/executors/gateway/gateway.lib.d.ts +0 -1
  16. package/executors/gateway/gateway.lib.js +1 -29
  17. package/executors/gateway/health-check.d.ts +0 -1
  18. package/executors/gateway/health-check.js +1 -66
  19. package/executors/gateway/jwks-verify.d.ts +0 -1
  20. package/executors/gateway/jwks-verify.js +1 -121
  21. package/executors/gateway/main.prod-gateway.d.ts +0 -1
  22. package/executors/gateway/main.prod-gateway.js +2 -573
  23. package/executors/gateway/main.rollout.d.ts +0 -1
  24. package/executors/gateway/main.rollout.js +11 -117
  25. package/executors/gateway/manifest.d.ts +0 -1
  26. package/executors/gateway/manifest.js +1 -374
  27. package/executors/gateway/metrics.d.ts +0 -1
  28. package/executors/gateway/metrics.js +1 -56
  29. package/executors/gateway/otel-tracing.d.ts +0 -1
  30. package/executors/gateway/otel-tracing.js +1 -74
  31. package/executors/gateway/prod-gateway.lib.d.ts +0 -1
  32. package/executors/gateway/prod-gateway.lib.js +1 -22
  33. package/executors/gateway/prod-runtime/access-log.d.ts +0 -1
  34. package/executors/gateway/prod-runtime/access-log.js +1 -24
  35. package/executors/gateway/prod-runtime/app.d.ts +0 -1
  36. package/executors/gateway/prod-runtime/app.js +1 -123
  37. package/executors/gateway/prod-runtime/auth.d.ts +0 -1
  38. package/executors/gateway/prod-runtime/auth.js +1 -51
  39. package/executors/gateway/prod-runtime/cors.d.ts +0 -1
  40. package/executors/gateway/prod-runtime/cors.js +1 -40
  41. package/executors/gateway/prod-runtime/edge.d.ts +0 -1
  42. package/executors/gateway/prod-runtime/edge.js +1 -65
  43. package/executors/gateway/prod-runtime/handler.d.ts +0 -1
  44. package/executors/gateway/prod-runtime/handler.js +1 -226
  45. package/executors/gateway/prod-runtime/hooks.d.ts +0 -1
  46. package/executors/gateway/prod-runtime/hooks.js +1 -42
  47. package/executors/gateway/prod-runtime/observability.d.ts +0 -1
  48. package/executors/gateway/prod-runtime/observability.js +1 -125
  49. package/executors/gateway/prod-runtime/rollout.d.ts +0 -1
  50. package/executors/gateway/prod-runtime/rollout.js +1 -103
  51. package/executors/gateway/prod-runtime/routing.d.ts +0 -1
  52. package/executors/gateway/prod-runtime/routing.js +1 -40
  53. package/executors/gateway/prod-runtime/server.d.ts +0 -1
  54. package/executors/gateway/prod-runtime/server.js +1 -79
  55. package/executors/gateway/prod-runtime/trust.d.ts +0 -1
  56. package/executors/gateway/prod-runtime/trust.js +1 -32
  57. package/executors/gateway/prod-runtime/types.d.ts +0 -1
  58. package/executors/gateway/prod-runtime/types.js +1 -2
  59. package/executors/gateway/prod-runtime/upgrade.d.ts +0 -1
  60. package/executors/gateway/prod-runtime/upgrade.js +4 -116
  61. package/executors/gateway/prod-runtime/upstream.d.ts +0 -1
  62. package/executors/gateway/prod-runtime/upstream.js +1 -21
  63. package/executors/gateway/providers-prod.d.ts +0 -1
  64. package/executors/gateway/providers-prod.js +1 -232
  65. package/executors/gateway/rate-limit.d.ts +0 -1
  66. package/executors/gateway/rate-limit.js +2 -75
  67. package/executors/gateway/registry-source.d.ts +0 -1
  68. package/executors/gateway/registry-source.js +1 -131
  69. package/executors/gateway/rollout-ops.d.ts +0 -1
  70. package/executors/gateway/rollout-ops.js +2 -167
  71. package/executors/gateway/runtime/auth.d.ts +0 -1
  72. package/executors/gateway/runtime/auth.js +1 -64
  73. package/executors/gateway/runtime/chooser.d.ts +0 -1
  74. package/executors/gateway/runtime/chooser.js +12 -45
  75. package/executors/gateway/runtime/control.d.ts +0 -1
  76. package/executors/gateway/runtime/control.js +1 -128
  77. package/executors/gateway/runtime/dev-auth.d.ts +0 -1
  78. package/executors/gateway/runtime/dev-auth.js +1 -108
  79. package/executors/gateway/runtime/discovery.d.ts +0 -1
  80. package/executors/gateway/runtime/discovery.js +1 -123
  81. package/executors/gateway/runtime/edge.d.ts +0 -1
  82. package/executors/gateway/runtime/edge.js +1 -47
  83. package/executors/gateway/runtime/handler.d.ts +0 -1
  84. package/executors/gateway/runtime/handler.js +1 -183
  85. package/executors/gateway/runtime/hooks.d.ts +0 -1
  86. package/executors/gateway/runtime/hooks.js +1 -55
  87. package/executors/gateway/runtime/lens-children.d.ts +0 -1
  88. package/executors/gateway/runtime/lens-children.js +1 -651
  89. package/executors/gateway/runtime/lifecycle.d.ts +0 -1
  90. package/executors/gateway/runtime/lifecycle.js +3 -842
  91. package/executors/gateway/runtime/observability.d.ts +0 -1
  92. package/executors/gateway/runtime/observability.js +2 -148
  93. package/executors/gateway/runtime/pod-env.d.ts +0 -1
  94. package/executors/gateway/runtime/pod-env.js +2 -89
  95. package/executors/gateway/runtime/proxy.d.ts +0 -1
  96. package/executors/gateway/runtime/proxy.js +1 -457
  97. package/executors/gateway/runtime/route-registry.d.ts +0 -1
  98. package/executors/gateway/runtime/route-registry.js +1 -72
  99. package/executors/gateway/runtime/scope.d.ts +0 -1
  100. package/executors/gateway/runtime/scope.js +1 -117
  101. package/executors/gateway/runtime/server.d.ts +0 -1
  102. package/executors/gateway/runtime/server.js +3 -487
  103. package/executors/gateway/runtime/service-keys.d.ts +0 -1
  104. package/executors/gateway/runtime/service-keys.js +1 -49
  105. package/executors/gateway/runtime/types.d.ts +0 -1
  106. package/executors/gateway/runtime/types.js +1 -151
  107. package/executors/gateway/runtime/upgrade.d.ts +0 -1
  108. package/executors/gateway/runtime/upgrade.js +1 -71
  109. package/executors/gateway/runtime/workspace-registry.d.ts +0 -1
  110. package/executors/gateway/runtime/workspace-registry.js +1 -99
  111. package/executors/gateway/ssrf-guard.d.ts +0 -1
  112. package/executors/gateway/ssrf-guard.js +1 -190
  113. package/executors/serve/serve.impl.d.ts +0 -1
  114. package/executors/serve/serve.impl.js +1 -280
  115. package/executors/trust/trust.impl.d.ts +0 -1
  116. package/executors/trust/trust.impl.js +4 -162
  117. package/gateway.d.ts +0 -1
  118. package/gateway.js +1 -35
  119. package/index.d.ts +0 -1
  120. package/index.js +1 -16
  121. package/main.devserver.d.ts +0 -1
  122. package/main.devserver.js +10 -1117
  123. package/package.json +4 -3
  124. package/tsgo-check-plugin.d.ts +0 -1
  125. package/tsgo-check-plugin.js +4 -364
  126. package/typecheck-bus.d.ts +0 -1
  127. package/typecheck-bus.js +4 -256
  128. package/basic-ssl.d.ts.map +0 -1
  129. package/build-scope-patterns.d.ts.map +0 -1
  130. package/create-webpack-dev.d.ts.map +0 -1
  131. package/create-webpack-prod.d.ts.map +0 -1
  132. package/executors/build/build.impl.d.ts.map +0 -1
  133. package/executors/gateway/gateway-errors.d.ts.map +0 -1
  134. package/executors/gateway/gateway.impl.d.ts.map +0 -1
  135. package/executors/gateway/gateway.lib.d.ts.map +0 -1
  136. package/executors/gateway/health-check.d.ts.map +0 -1
  137. package/executors/gateway/jwks-verify.d.ts.map +0 -1
  138. package/executors/gateway/main.prod-gateway.d.ts.map +0 -1
  139. package/executors/gateway/main.rollout.d.ts.map +0 -1
  140. package/executors/gateway/manifest.d.ts.map +0 -1
  141. package/executors/gateway/metrics.d.ts.map +0 -1
  142. package/executors/gateway/otel-tracing.d.ts.map +0 -1
  143. package/executors/gateway/prod-gateway.lib.d.ts.map +0 -1
  144. package/executors/gateway/prod-runtime/access-log.d.ts.map +0 -1
  145. package/executors/gateway/prod-runtime/app.d.ts.map +0 -1
  146. package/executors/gateway/prod-runtime/auth.d.ts.map +0 -1
  147. package/executors/gateway/prod-runtime/cors.d.ts.map +0 -1
  148. package/executors/gateway/prod-runtime/edge.d.ts.map +0 -1
  149. package/executors/gateway/prod-runtime/handler.d.ts.map +0 -1
  150. package/executors/gateway/prod-runtime/hooks.d.ts.map +0 -1
  151. package/executors/gateway/prod-runtime/observability.d.ts.map +0 -1
  152. package/executors/gateway/prod-runtime/rollout.d.ts.map +0 -1
  153. package/executors/gateway/prod-runtime/routing.d.ts.map +0 -1
  154. package/executors/gateway/prod-runtime/server.d.ts.map +0 -1
  155. package/executors/gateway/prod-runtime/trust.d.ts.map +0 -1
  156. package/executors/gateway/prod-runtime/types.d.ts.map +0 -1
  157. package/executors/gateway/prod-runtime/upgrade.d.ts.map +0 -1
  158. package/executors/gateway/prod-runtime/upstream.d.ts.map +0 -1
  159. package/executors/gateway/providers-prod.d.ts.map +0 -1
  160. package/executors/gateway/rate-limit.d.ts.map +0 -1
  161. package/executors/gateway/registry-source.d.ts.map +0 -1
  162. package/executors/gateway/rollout-ops.d.ts.map +0 -1
  163. package/executors/gateway/runtime/auth.d.ts.map +0 -1
  164. package/executors/gateway/runtime/chooser.d.ts.map +0 -1
  165. package/executors/gateway/runtime/control.d.ts.map +0 -1
  166. package/executors/gateway/runtime/dev-auth.d.ts.map +0 -1
  167. package/executors/gateway/runtime/discovery.d.ts.map +0 -1
  168. package/executors/gateway/runtime/edge.d.ts.map +0 -1
  169. package/executors/gateway/runtime/handler.d.ts.map +0 -1
  170. package/executors/gateway/runtime/hooks.d.ts.map +0 -1
  171. package/executors/gateway/runtime/lens-children.d.ts.map +0 -1
  172. package/executors/gateway/runtime/lifecycle.d.ts.map +0 -1
  173. package/executors/gateway/runtime/observability.d.ts.map +0 -1
  174. package/executors/gateway/runtime/pod-env.d.ts.map +0 -1
  175. package/executors/gateway/runtime/proxy.d.ts.map +0 -1
  176. package/executors/gateway/runtime/route-registry.d.ts.map +0 -1
  177. package/executors/gateway/runtime/scope.d.ts.map +0 -1
  178. package/executors/gateway/runtime/server.d.ts.map +0 -1
  179. package/executors/gateway/runtime/service-keys.d.ts.map +0 -1
  180. package/executors/gateway/runtime/types.d.ts.map +0 -1
  181. package/executors/gateway/runtime/upgrade.d.ts.map +0 -1
  182. package/executors/gateway/runtime/workspace-registry.d.ts.map +0 -1
  183. package/executors/gateway/ssrf-guard.d.ts.map +0 -1
  184. package/executors/serve/serve.impl.d.ts.map +0 -1
  185. package/executors/trust/trust.impl.d.ts.map +0 -1
  186. package/gateway.d.ts.map +0 -1
  187. package/index.d.ts.map +0 -1
  188. package/main.devserver.d.ts.map +0 -1
  189. package/tsgo-check-plugin.d.ts.map +0 -1
  190. package/typecheck-bus.d.ts.map +0 -1
@@ -1,25 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runRollout = runRollout;
4
- /**
5
- * `lensmcp rollout` CD helper entry — safe, scriptable edits to the gateway
6
- * rollout config. Two targets:
7
- * - a FILE (default): the watched config / mounted ConfigMap. Atomic write
8
- * (tmp + rename) so the gateway watcher never reads a half file.
9
- * - a REDIS registry (`--redis <url>`): writes the service's rollout key and
10
- * PUBLISHES the change channel, so a registry-backed gateway re-reads live
11
- * (zero-touch auto-sync; no file mounts).
12
- *
13
- * node main.rollout.js <config.json> <command> [args]
14
- * node main.rollout.js --redis <url> [--prefix <p>] <command> [args]
15
- *
16
- * Available from the published @lensmcp/cluster package; `lensmcp rollout …`
17
- * wraps it. See docs/design/targeted-rollout.md.
18
- */
19
- const node_fs_1 = require("node:fs");
20
- const rollout_ops_1 = require("./rollout-ops");
21
- const registry_source_1 = require("./registry-source");
22
- const USAGE = `lensmcp rollout — edit the gateway rollout config (file or Redis registry)
1
+ "use strict";var p=Object.defineProperty;var l=(e,r)=>p(e,"name",{value:r,configurable:!0});var a=Object.defineProperty,i=l((e,r)=>a(e,"name",{value:r,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.runRollout=runRollout;const node_fs_1=require("node:fs"),rollout_ops_1=require("./rollout-ops"),registry_source_1=require("./registry-source"),USAGE=`lensmcp rollout \u2014 edit the gateway rollout config (file or Redis registry)
23
2
 
24
3
  Usage: <config.json> <command> [args]
25
4
  --redis <url> [--prefix <p>] <command> [args]
@@ -33,98 +12,13 @@ Commands:
33
12
  abort <svc> <version> set version weight=0 (rollback)
34
13
  add-rule <svc> <name> <attr> <op> <value> <version> add/replace a targeting rule
35
14
  remove-rule <svc> <name> remove a targeting rule
36
- `;
37
- const out = (s) => process.stdout.write(s.endsWith('\n') ? s : s + '\n');
38
- const err = (s) => process.stderr.write(s.endsWith('\n') ? s : s + '\n');
39
- /** Pull `--flag <value>` out of argv, returning its value + the remaining args. */
40
- function extractFlag(argv, name) {
41
- const i = argv.indexOf(name);
42
- if (i < 0)
43
- return { rest: argv };
44
- return { value: argv[i + 1], rest: [...argv.slice(0, i), ...argv.slice(i + 2)] };
45
- }
46
- function runFile(argv) {
47
- const [file, ...rest] = argv;
48
- if (!file || !rest.length) {
49
- err(USAGE);
50
- return 2;
51
- }
52
- let config;
53
- try {
54
- config = JSON.parse((0, node_fs_1.readFileSync)(file, 'utf8'));
55
- }
56
- catch (e) {
57
- err(`cannot read ${file}: ${e.message}`);
58
- return 1;
59
- }
60
- try {
61
- const parsed = (0, rollout_ops_1.parseRolloutCommand)(rest);
62
- if (!parsed.op) {
63
- out((0, rollout_ops_1.summarizeRollout)(config, parsed.status));
64
- return 0;
65
- }
66
- const next = (0, rollout_ops_1.applyRolloutOp)(config, parsed.op);
67
- (0, node_fs_1.writeFileSync)(`${file}.tmp`, JSON.stringify(next, null, 2) + '\n');
68
- (0, node_fs_1.renameSync)(`${file}.tmp`, file); // atomic — the watcher never sees a partial file
69
- out(`✓ ${parsed.op.kind} ${parsed.op.service}\n\n${(0, rollout_ops_1.summarizeRollout)(next, parsed.op.service)}`);
70
- return 0;
71
- }
72
- catch (e) {
73
- err(`✗ ${e.message}`);
74
- return 1;
75
- }
76
- }
77
- async function runRedis(url, prefix, argv) {
78
- if (!argv.length) {
79
- err(USAGE);
80
- return 2;
81
- }
82
- let client;
83
- try {
84
- // eslint-disable-next-line @typescript-eslint/no-require-imports
85
- const Redis = require('ioredis');
86
- client = new Redis(url);
87
- }
88
- catch {
89
- err('--redis needs the "ioredis" package installed (peer dep of @lensmcp/cluster)');
90
- return 1;
91
- }
92
- try {
93
- const { endpoints } = await (0, registry_source_1.redisRegistrySource)(client, { prefix }).read();
94
- const parsed = (0, rollout_ops_1.parseRolloutCommand)(argv);
95
- if (!parsed.op) {
96
- out((0, rollout_ops_1.summarizeRollout)(endpoints, parsed.status));
97
- return 0;
98
- }
99
- const next = (0, rollout_ops_1.applyRolloutOp)(endpoints, parsed.op);
100
- const svc = parsed.op.service;
101
- const entry = next[svc];
102
- if (entry === undefined)
103
- await client.del((0, registry_source_1.rolloutKey)(prefix, svc));
104
- else
105
- await client.set((0, registry_source_1.rolloutKey)(prefix, svc), JSON.stringify(entry));
106
- await client.publish((0, registry_source_1.changeChannel)(prefix), svc); // gateway re-reads live
107
- out(`✓ ${parsed.op.kind} ${svc} → redis ${prefix} (published)\n\n${(0, rollout_ops_1.summarizeRollout)(next, svc)}`);
108
- return 0;
109
- }
110
- catch (e) {
111
- err(`✗ ${e.message}`);
112
- return 1;
113
- }
114
- finally {
115
- try {
116
- await client.quit();
117
- }
118
- catch { /* ignore */ }
119
- }
120
- }
121
- async function runRollout(argv) {
122
- const redis = extractFlag(argv, '--redis');
123
- const pref = extractFlag(redis.rest, '--prefix');
124
- if (redis.value)
125
- return runRedis(redis.value, pref.value ?? registry_source_1.DEFAULT_REGISTRY_PREFIX, pref.rest);
126
- return runFile(pref.rest);
127
- }
128
- // Run only when invoked directly (not when imported, e.g. by the lensmcp CLI).
129
- if (require.main === module)
130
- void runRollout(process.argv.slice(2)).then((code) => process.exit(code));
15
+ `,out=i(e=>process.stdout.write(e.endsWith(`
16
+ `)?e:e+`
17
+ `),"out"),err=i(e=>process.stderr.write(e.endsWith(`
18
+ `)?e:e+`
19
+ `),"err");function extractFlag(e,r){const o=e.indexOf(r);return o<0?{rest:e}:{value:e[o+1],rest:[...e.slice(0,o),...e.slice(o+2)]}}l(extractFlag,"extractFlag"),i(extractFlag,"extractFlag");function runFile(e){const[r,...o]=e;if(!r||!o.length)return err(USAGE),2;let s;try{s=JSON.parse((0,node_fs_1.readFileSync)(r,"utf8"))}catch(t){return err(`cannot read ${r}: ${t.message}`),1}try{const t=(0,rollout_ops_1.parseRolloutCommand)(o);if(!t.op)return out((0,rollout_ops_1.summarizeRollout)(s,t.status)),0;const n=(0,rollout_ops_1.applyRolloutOp)(s,t.op);return(0,node_fs_1.writeFileSync)(`${r}.tmp`,JSON.stringify(n,null,2)+`
20
+ `),(0,node_fs_1.renameSync)(`${r}.tmp`,r),out(`\u2713 ${t.op.kind} ${t.op.service}
21
+
22
+ ${(0,rollout_ops_1.summarizeRollout)(n,t.op.service)}`),0}catch(t){return err(`\u2717 ${t.message}`),1}}l(runFile,"runFile"),i(runFile,"runFile");async function runRedis(e,r,o){if(!o.length)return err(USAGE),2;let s;try{const t=require("ioredis");s=new t(e)}catch{return err('--redis needs the "ioredis" package installed (peer dep of @lensmcp/cluster)'),1}try{const{endpoints:t}=await(0,registry_source_1.redisRegistrySource)(s,{prefix:r}).read(),n=(0,rollout_ops_1.parseRolloutCommand)(o);if(!n.op)return out((0,rollout_ops_1.summarizeRollout)(t,n.status)),0;const c=(0,rollout_ops_1.applyRolloutOp)(t,n.op),u=n.op.service,d=c[u];return d===void 0?await s.del((0,registry_source_1.rolloutKey)(r,u)):await s.set((0,registry_source_1.rolloutKey)(r,u),JSON.stringify(d)),await s.publish((0,registry_source_1.changeChannel)(r),u),out(`\u2713 ${n.op.kind} ${u} \u2192 redis ${r} (published)
23
+
24
+ ${(0,rollout_ops_1.summarizeRollout)(c,u)}`),0}catch(t){return err(`\u2717 ${t.message}`),1}finally{try{await s.quit()}catch{}}}l(runRedis,"runRedis"),i(runRedis,"runRedis");async function runRollout(e){const r=extractFlag(e,"--redis"),o=extractFlag(r.rest,"--prefix");return r.value?runRedis(r.value,o.value??registry_source_1.DEFAULT_REGISTRY_PREFIX,o.rest):runFile(o.rest)}l(runRollout,"runRollout"),i(runRollout,"runRollout"),require.main===module&&runRollout(process.argv.slice(2)).then(e=>process.exit(e));
@@ -282,4 +282,3 @@ export declare function fnv1a(s: string): number;
282
282
  * cohort order. All-zero weights → 0.
283
283
  */
284
284
  export declare function bucketByWeight(key: string, weights: readonly number[]): number;
285
- //# sourceMappingURL=manifest.d.ts.map
@@ -1,374 +1 @@
1
- "use strict";
2
- /**
3
- * Route manifests + the provider seams that make the gateway ONE library
4
- * across dev and prod.
5
- *
6
- * The dev gateway and the production gateway share this routing core. A
7
- * service DESCRIBES itself with a `RouteManifest` (in dev, synthesized from
8
- * the project.json `cluster` field; in prod, published by the service to a
9
- * registry / served at `/.well-known/lensmcp-routes`). The gateway builds a
10
- * flat `Route[]` from the manifests and matches requests against it — the
11
- * SAME `matchRoute` in both environments, so routing can never drift.
12
- *
13
- * What differs between dev and prod is supplied through two interfaces:
14
- * - ManifestProvider — WHERE the manifests come from (workspace scan vs
15
- * registry watch) and how changes arrive.
16
- * - PodProvider — HOW a service name resolves to a concrete upstream
17
- * (unix-socket pool with scale-from-zero vs a TCP endpoint).
18
- *
19
- * This module is PURE (no IO, no process, no http) so it is exhaustively
20
- * unit-testable; the providers carry the environment-specific machinery.
21
- */
22
- Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.smoothWeightedSelector = smoothWeightedSelector;
24
- exports.hostMatches = hostMatches;
25
- exports.matchRoute = matchRoute;
26
- exports.sortBySpecificity = sortBySpecificity;
27
- exports.authForPath = authForPath;
28
- exports.authPathMatches = authPathMatches;
29
- exports.authRuleFor = authRuleFor;
30
- exports.authRuleForPath = authRuleForPath;
31
- exports.buildRouteTable = buildRouteTable;
32
- exports.manifestFromClusterDecl = manifestFromClusterDecl;
33
- exports.matchCidr = matchCidr;
34
- exports.evalRule = evalRule;
35
- exports.pickTargetedVersion = pickTargetedVersion;
36
- exports.fnv1a = fnv1a;
37
- exports.bucketByWeight = bucketByWeight;
38
- /**
39
- * Smooth weighted round-robin (nginx-style). Returns a stateful selector that,
40
- * over time, hands out indices in proportion to `weights` — but INTERLEAVED
41
- * (90/10 yields the rare cohort roughly every tenth pick, not in a burst at
42
- * the end). Deterministic given the weights, so rollout splits are testable.
43
- * A zero/negative total (all cohorts drained) falls back to index 0.
44
- */
45
- function smoothWeightedSelector(weights) {
46
- const cur = weights.map(() => 0);
47
- const total = weights.reduce((s, w) => s + Math.max(0, w), 0);
48
- return () => {
49
- if (total <= 0 || weights.length === 0)
50
- return 0;
51
- let best = 0;
52
- for (let i = 0; i < weights.length; i++) {
53
- cur[i] += Math.max(0, weights[i]);
54
- if (cur[i] > cur[best])
55
- best = i;
56
- }
57
- cur[best] -= total;
58
- return best;
59
- };
60
- }
61
- /** Exact hostname or `*.suffix` wildcard (the bare suffix also matches). */
62
- function hostMatches(pattern, host) {
63
- return pattern.startsWith('*.')
64
- ? host.endsWith(pattern.slice(1)) || host === pattern.slice(2)
65
- : host === pattern;
66
- }
67
- /**
68
- * THE matcher — shared by dev and prod. Generic over the route shape so the
69
- * dev gateway can pass its pod-rich routes and prod its upstream routes; only
70
- * `host`/`prefix` are read here.
71
- *
72
- * Rules (each one a fixed bug):
73
- * - A host CLAIMED by any declaration never falls through to the catch-all
74
- * default; its unmatched paths belong to the gateway's own infra.
75
- * - An UNCLAIMED host may only reach the host-less `default` route — never a
76
- * host route by accident of sort order (unknown host → default or 404).
77
- * - An EXACT host match beats a wildcard (`internal.api.x` is served by its
78
- * own route, not a co-existing `*.x` app).
79
- * - Within the candidates, the most specific path prefix wins (callers sort
80
- * routes by specificity: host+path before host before catch-all).
81
- */
82
- function matchRoute(routes, host, url) {
83
- const claimed = routes.some((r) => r.host && hostMatches(r.host, host));
84
- let candidates;
85
- if (claimed) {
86
- candidates = routes.filter((r) => r.host && hostMatches(r.host, host));
87
- if (candidates.some((r) => r.host === host)) {
88
- candidates = candidates.filter((r) => r.host === host);
89
- }
90
- }
91
- else {
92
- // UNCLAIMED host → the host-less default catch-all(s). ONE default (a single workspace) → today's
93
- // behavior, apex ignored. MULTIPLE defaults (a shared multi-workspace gateway that merged several
94
- // workspaces' fragments) → route by APEX: the default whose apex the request host belongs to, longest
95
- // apex first; a foreign host matching no apex falls to an apex-less GLOBAL default (else nothing → 404).
96
- const defaults = routes.filter((r) => !r.host);
97
- if (defaults.length <= 1) {
98
- candidates = defaults;
99
- }
100
- else {
101
- const apexHit = defaults
102
- .filter((r) => r.apex && (host === r.apex || host.endsWith('.' + r.apex)))
103
- .sort((a, b) => b.apex.length - a.apex.length);
104
- candidates = apexHit.length > 0 ? apexHit : defaults.filter((r) => !r.apex);
105
- }
106
- }
107
- return candidates.find((r) => !r.prefix || url.startsWith(r.prefix));
108
- }
109
- /** Specificity sort: host+path beats host-only beats catch-all. Mutates+returns. */
110
- function sortBySpecificity(routes) {
111
- routes.sort((a, b) => ((b.host ? 2 : 0) + (b.prefix ? 1 : 0)) - ((a.host ? 2 : 0) + (a.prefix ? 1 : 0)));
112
- return routes;
113
- }
114
- /** Resolve the auth mode for a concrete request path against a manifest. */
115
- function authForPath(manifest, path, internal) {
116
- if (internal)
117
- return 'internal';
118
- const overrides = manifest.auth?.overrides ?? [];
119
- for (const o of overrides)
120
- if (path.startsWith(o.path))
121
- return o.mode;
122
- return manifest.auth?.default ?? 'jwt';
123
- }
124
- /**
125
- * Resolve the auth mode + any edge ABAC rule for a concrete (path, method) against an
126
- * {@link AuthPolicy} — the shared core both gateways use. First override whose `path` is a
127
- * prefix and whose `methods` include the method wins (callers emit most-specific-first);
128
- * `permission` is compiled to a `perms contains` condition.
129
- */
130
- /**
131
- * Segment-prefix match where a pattern segment of `*` or `:param` matches ANY one path segment —
132
- * so a route override like `/engage/tenants/:companyId/campaigns` matches the real request
133
- * `/engage/tenants/abc-123/campaigns` (and anything deeper). Stricter than `startsWith` (it won't
134
- * cross a segment boundary), which is also more correct. Lets `@RequirePermission` enforce on
135
- * tenant-scoped (`:param`) routes, not just static prefixes.
136
- */
137
- function authPathMatches(pattern, path) {
138
- const ps = pattern.split('/').filter(Boolean);
139
- const xs = (path.split('?')[0] ?? '').split('/').filter(Boolean);
140
- if (xs.length < ps.length)
141
- return false;
142
- for (let i = 0; i < ps.length; i++) {
143
- const seg = ps[i];
144
- if (seg === '*' || seg.startsWith(':'))
145
- continue;
146
- if (seg !== xs[i])
147
- return false;
148
- }
149
- return true;
150
- }
151
- function authRuleFor(policy, path, method) {
152
- const m = method.toUpperCase();
153
- for (const o of policy?.overrides ?? []) {
154
- if (!authPathMatches(o.path, path))
155
- continue;
156
- if (o.methods && !o.methods.some((x) => x.toUpperCase() === m))
157
- continue;
158
- const rule = o.rule ?? (o.permission ? { attr: 'perms', op: 'hastoken', value: o.permission } : undefined);
159
- // An override that omits `mode` INHERITS the service default — otherwise a
160
- // permission-only override would resolve to mode=undefined and the gateway
161
- // would skip the jwt branch entirely, silently NOT enforcing the permission.
162
- return { mode: o.mode ?? policy?.default ?? 'jwt', ...(rule ? { rule } : {}) };
163
- }
164
- return { mode: policy?.default ?? 'jwt' };
165
- }
166
- /**
167
- * Resolve the auth mode AND any edge ABAC rule for a concrete (path, method) on a manifest —
168
- * the permission-aware companion to {@link authForPath}. Internal routes are always `internal`;
169
- * otherwise delegates to {@link authRuleFor}. The gateway verifies the JWT for `jwt` mode and,
170
- * when a `rule` is present, requires `evalRule(rule, attrs)` (else 403).
171
- */
172
- function authRuleForPath(manifest, path, method, internal) {
173
- if (internal)
174
- return { mode: 'internal' };
175
- return authRuleFor(manifest.auth, path, method);
176
- }
177
- /**
178
- * Build the flat route table from manifests. Each mount becomes a host route;
179
- * a service flagged `internal` also gets an `internal.<host>` route per mount;
180
- * a `default` service contributes the host-less catch-all. Sorted by
181
- * specificity so `matchRoute` picks the most specific.
182
- */
183
- function buildRouteTable(manifests) {
184
- const routes = [];
185
- for (const m of manifests) {
186
- const baseAuth = m.auth?.default ?? 'jwt';
187
- for (const mount of m.mounts) {
188
- const base = {
189
- service: m.service,
190
- ...(mount.path ? { prefix: mount.path } : {}),
191
- ...(m.prependPrefix ? { prependPrefix: m.prependPrefix } : {}),
192
- ...(m.upstream ? { upstream: m.upstream } : {}),
193
- };
194
- routes.push({ ...base, host: mount.host, auth: baseAuth });
195
- if (m.internal && m.internalHost !== false) {
196
- routes.push({
197
- ...base,
198
- host: typeof m.internalHost === 'string' ? m.internalHost : `internal.${mount.host}`,
199
- internal: true,
200
- auth: 'internal',
201
- });
202
- }
203
- }
204
- if (m.default) {
205
- routes.push({
206
- service: m.service,
207
- ...(m.prependPrefix ? { prependPrefix: m.prependPrefix } : {}),
208
- ...(m.upstream ? { upstream: m.upstream } : {}),
209
- default: true,
210
- auth: baseAuth,
211
- });
212
- }
213
- }
214
- return sortBySpecificity(routes);
215
- }
216
- /** Synthesize a manifest from a dev project.json `cluster` declaration —
217
- * the dev ManifestProvider's adapter, and the seed for `lensmcp`-generated
218
- * prod manifests (same fields, prod adds version/rateLimits). */
219
- function manifestFromClusterDecl(project, decl) {
220
- const upstream = decl.upstream ?? (decl.port ? `http://localhost:${decl.port}` : undefined);
221
- // A service must route SOMEWHERE: either a pod service name or a TCP upstream.
222
- if (!decl.service && !upstream)
223
- return undefined;
224
- return {
225
- service: decl.service ?? project,
226
- mounts: decl.host ? [{ host: decl.host, ...(decl.path ? { path: decl.path } : {}) }] : [],
227
- // pod services get an internal east-west host by default; TCP upstreams don't.
228
- internal: !!decl.service,
229
- ...(decl.internalHost !== undefined ? { internalHost: decl.internalHost } : {}),
230
- ...(decl.default ? { default: true } : {}),
231
- ...(decl.prependPrefix ? { prependPrefix: decl.prependPrefix } : {}),
232
- ...(decl.auth ? { auth: decl.auth } : {}),
233
- ...(upstream ? { upstream } : {}),
234
- };
235
- }
236
- const isGroup = (n) => 'all' in n || 'any' in n || 'not' in n;
237
- const reCache = new Map();
238
- const compileRe = (src) => {
239
- if (src.length > 512)
240
- return null; // ReDoS guard — bound pattern length
241
- if (reCache.has(src))
242
- return reCache.get(src);
243
- let re;
244
- try {
245
- re = new RegExp(src);
246
- }
247
- catch {
248
- re = null;
249
- }
250
- reCache.set(src, re);
251
- return re;
252
- };
253
- /** IPv4 dotted-quad → uint32, or null if malformed. */
254
- function ipToInt(ip) {
255
- const v4 = ip.startsWith('::ffff:') ? ip.slice(7) : ip; // IPv4-mapped IPv6
256
- const parts = v4.split('.');
257
- if (parts.length !== 4)
258
- return null;
259
- let n = 0;
260
- for (const p of parts) {
261
- const o = Number(p);
262
- if (!Number.isInteger(o) || o < 0 || o > 255 || (p.length > 1 && p[0] === '0'))
263
- return null;
264
- n = n * 256 + o;
265
- }
266
- return n >>> 0;
267
- }
268
- /** `ip ∈ cidr` for IPv4 (handles IPv4-mapped IPv6). False for IPv6/malformed. */
269
- function matchCidr(ip, cidr) {
270
- const slash = cidr.indexOf('/');
271
- if (slash < 0)
272
- return false;
273
- const bits = Number(cidr.slice(slash + 1));
274
- if (!Number.isInteger(bits) || bits < 0 || bits > 32)
275
- return false;
276
- const ipN = ipToInt(ip);
277
- const rangeN = ipToInt(cidr.slice(0, slash));
278
- if (ipN === null || rangeN === null)
279
- return false;
280
- if (bits === 0)
281
- return true;
282
- const mask = (bits === 32 ? 0xffffffff : ~((1 << (32 - bits)) - 1)) >>> 0;
283
- return ((ipN & mask) >>> 0) === ((rangeN & mask) >>> 0);
284
- }
285
- const str = (v) => (v === null || v === undefined ? '' : String(v));
286
- const eq = (a, b) => str(a) === str(b);
287
- /** Evaluate one condition against the attributes. Undefined attribute → false
288
- * for every op except `ne` (absent ≠ value → true) and `exists` (→ false). */
289
- function evalCondition(c, attrs) {
290
- const v = attrs[c.attr];
291
- const present = v !== undefined && v !== null;
292
- switch (c.op) {
293
- case 'exists': return present;
294
- case 'ne': return !present || !eq(v, c.value);
295
- }
296
- if (!present)
297
- return false;
298
- switch (c.op) {
299
- case 'eq': return eq(v, c.value);
300
- case 'in': return Array.isArray(c.value) && c.value.some((x) => Array.isArray(v) ? v.some((vv) => eq(vv, x)) : eq(v, x));
301
- case 'nin': return !(Array.isArray(c.value) && c.value.some((x) => Array.isArray(v) ? v.some((vv) => eq(vv, x)) : eq(v, x)));
302
- case 'prefix': return str(v).startsWith(str(c.value));
303
- case 'suffix': return str(v).endsWith(str(c.value));
304
- case 'contains': return Array.isArray(v) ? v.some((vv) => eq(vv, c.value)) : str(v).includes(str(c.value)); // substring / array element (general op)
305
- // EXACT token in a list — a `perms`/scope STRING is delimited, so "superadmin"/"billing,adminx"
306
- // must NOT satisfy hastoken:"admin". This is what `permission` compiles to (authz, no substring bypass).
307
- case 'hastoken': {
308
- const cv = str(c.value);
309
- return Array.isArray(v) ? v.some((vv) => eq(vv, c.value)) : str(v).split(/[\s,]+/).filter(Boolean).includes(cv);
310
- }
311
- case 'regex': {
312
- const re = compileRe(str(c.value));
313
- return re ? re.test(str(v)) : false;
314
- }
315
- case 'gt': return Number(v) > Number(c.value);
316
- case 'lt': return Number(v) < Number(c.value);
317
- case 'cidr': return matchCidr(str(v), str(c.value));
318
- default: return false;
319
- }
320
- }
321
- /** Evaluate a rule node (condition or all/any/not group) against attributes. Pure. */
322
- function evalRule(node, attrs) {
323
- if (!isGroup(node))
324
- return evalCondition(node, attrs);
325
- // Only MEANINGFUL clauses count — an empty all:[]/any:[]/not:{} must NOT vacuously admit (fail closed),
326
- // while a real `not:{cond}`-only rule still grants when its veto passes.
327
- const all = Array.isArray(node.all) && node.all.length ? node.all : undefined;
328
- const any = Array.isArray(node.any) && node.any.length ? node.any : undefined;
329
- const not = node.not && Object.keys(node.not).length ? node.not : undefined;
330
- if (not && evalRule(not, attrs))
331
- return false; // veto
332
- if (all && !all.every((n) => evalRule(n, attrs)))
333
- return false;
334
- if (any && !any.some((n) => evalRule(n, attrs)))
335
- return false;
336
- return !!(all || any || not); // grant only if ≥1 meaningful clause was present (and satisfied)
337
- }
338
- /** First matching rule's version (top-to-bottom), or undefined. */
339
- function pickTargetedVersion(rules, attrs) {
340
- for (const r of rules) {
341
- if (evalRule(r.when, attrs))
342
- return { version: r.version, ...(r.name ? { rule: r.name } : {}) };
343
- }
344
- return undefined;
345
- }
346
- /** 32-bit FNV-1a hash → unsigned int. Stable across processes (no seed). */
347
- function fnv1a(s) {
348
- let h = 0x811c9dc5;
349
- for (let i = 0; i < s.length; i++) {
350
- h ^= s.charCodeAt(i);
351
- h = Math.imul(h, 0x01000193);
352
- }
353
- return h >>> 0;
354
- }
355
- /**
356
- * Consistent-hash a stable key to a cohort INDEX by weight. The hash point uses
357
- * a FIXED modulus (10000) so it is stable per key regardless of the weights;
358
- * only the cohort thresholds move when weights change — so ramping a weight
359
- * moves a key across the boundary AT MOST ONCE (no reshuffle/flapping). Fixed
360
- * cohort order. All-zero weights → 0.
361
- */
362
- function bucketByWeight(key, weights) {
363
- const total = weights.reduce((a, w) => a + Math.max(0, w), 0);
364
- if (total <= 0 || weights.length === 0)
365
- return 0;
366
- const point = fnv1a(key) % 10000;
367
- let acc = 0;
368
- for (let i = 0; i < weights.length; i++) {
369
- acc += (Math.max(0, weights[i]) / total) * 10000;
370
- if (point < acc)
371
- return i;
372
- }
373
- return weights.length - 1;
374
- }
1
+ "use strict";var h=Object.defineProperty;var u=(t,e)=>h(t,"name",{value:e,configurable:!0});var f=Object.defineProperty,a=u((t,e)=>f(t,"name",{value:e,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.smoothWeightedSelector=smoothWeightedSelector,exports.hostMatches=hostMatches,exports.matchRoute=matchRoute,exports.sortBySpecificity=sortBySpecificity,exports.authForPath=authForPath,exports.authPathMatches=authPathMatches,exports.authRuleFor=authRuleFor,exports.authRuleForPath=authRuleForPath,exports.buildRouteTable=buildRouteTable,exports.manifestFromClusterDecl=manifestFromClusterDecl,exports.matchCidr=matchCidr,exports.evalRule=evalRule,exports.pickTargetedVersion=pickTargetedVersion,exports.fnv1a=fnv1a,exports.bucketByWeight=bucketByWeight;function smoothWeightedSelector(t){const e=t.map(()=>0),r=t.reduce((o,n)=>o+Math.max(0,n),0);return()=>{if(r<=0||t.length===0)return 0;let o=0;for(let n=0;n<t.length;n++)e[n]+=Math.max(0,t[n]),e[n]>e[o]&&(o=n);return e[o]-=r,o}}u(smoothWeightedSelector,"smoothWeightedSelector"),a(smoothWeightedSelector,"smoothWeightedSelector");function hostMatches(t,e){return t.startsWith("*.")?e.endsWith(t.slice(1))||e===t.slice(2):e===t}u(hostMatches,"hostMatches"),a(hostMatches,"hostMatches");function matchRoute(t,e,r){const o=t.some(s=>s.host&&hostMatches(s.host,e));let n;if(o)n=t.filter(s=>s.host&&hostMatches(s.host,e)),n.some(s=>s.host===e)&&(n=n.filter(s=>s.host===e));else{const s=t.filter(i=>!i.host);if(s.length<=1)n=s;else{const i=s.filter(l=>l.apex&&(e===l.apex||e.endsWith("."+l.apex))).sort((l,c)=>c.apex.length-l.apex.length);n=i.length>0?i:s.filter(l=>!l.apex)}}return n.find(s=>!s.prefix||r.startsWith(s.prefix))}u(matchRoute,"matchRoute"),a(matchRoute,"matchRoute");function sortBySpecificity(t){return t.sort((e,r)=>(r.host?2:0)+(r.prefix?1:0)-((e.host?2:0)+(e.prefix?1:0))),t}u(sortBySpecificity,"sortBySpecificity"),a(sortBySpecificity,"sortBySpecificity");function authForPath(t,e,r){if(r)return"internal";const o=t.auth?.overrides??[];for(const n of o)if(e.startsWith(n.path))return n.mode;return t.auth?.default??"jwt"}u(authForPath,"authForPath"),a(authForPath,"authForPath");function authPathMatches(t,e){const r=t.split("/").filter(Boolean),o=(e.split("?")[0]??"").split("/").filter(Boolean);if(o.length<r.length)return!1;for(let n=0;n<r.length;n++){const s=r[n];if(!(s==="*"||s.startsWith(":"))&&s!==o[n])return!1}return!0}u(authPathMatches,"authPathMatches"),a(authPathMatches,"authPathMatches");function authRuleFor(t,e,r){const o=r.toUpperCase();for(const n of t?.overrides??[]){if(!authPathMatches(n.path,e)||n.methods&&!n.methods.some(i=>i.toUpperCase()===o))continue;const s=n.rule??(n.permission?{attr:"perms",op:"hastoken",value:n.permission}:void 0);return{mode:n.mode??t?.default??"jwt",...s?{rule:s}:{}}}return{mode:t?.default??"jwt"}}u(authRuleFor,"authRuleFor"),a(authRuleFor,"authRuleFor");function authRuleForPath(t,e,r,o){return o?{mode:"internal"}:authRuleFor(t.auth,e,r)}u(authRuleForPath,"authRuleForPath"),a(authRuleForPath,"authRuleForPath");function buildRouteTable(t){const e=[];for(const r of t){const o=r.auth?.default??"jwt";for(const n of r.mounts){const s={service:r.service,...n.path?{prefix:n.path}:{},...r.prependPrefix?{prependPrefix:r.prependPrefix}:{},...r.upstream?{upstream:r.upstream}:{}};e.push({...s,host:n.host,auth:o}),r.internal&&r.internalHost!==!1&&e.push({...s,host:typeof r.internalHost=="string"?r.internalHost:`internal.${n.host}`,internal:!0,auth:"internal"})}r.default&&e.push({service:r.service,...r.prependPrefix?{prependPrefix:r.prependPrefix}:{},...r.upstream?{upstream:r.upstream}:{},default:!0,auth:o})}return sortBySpecificity(e)}u(buildRouteTable,"buildRouteTable"),a(buildRouteTable,"buildRouteTable");function manifestFromClusterDecl(t,e){const r=e.upstream??(e.port?`http://localhost:${e.port}`:void 0);if(!(!e.service&&!r))return{service:e.service??t,mounts:e.host?[{host:e.host,...e.path?{path:e.path}:{}}]:[],internal:!!e.service,...e.internalHost!==void 0?{internalHost:e.internalHost}:{},...e.default?{default:!0}:{},...e.prependPrefix?{prependPrefix:e.prependPrefix}:{},...e.auth?{auth:e.auth}:{},...r?{upstream:r}:{}}}u(manifestFromClusterDecl,"manifestFromClusterDecl"),a(manifestFromClusterDecl,"manifestFromClusterDecl");const isGroup=a(t=>"all"in t||"any"in t||"not"in t,"isGroup"),reCache=new Map,compileRe=a(t=>{if(t.length>512)return null;if(reCache.has(t))return reCache.get(t);let e;try{e=new RegExp(t)}catch{e=null}return reCache.set(t,e),e},"compileRe");function ipToInt(t){const e=(t.startsWith("::ffff:")?t.slice(7):t).split(".");if(e.length!==4)return null;let r=0;for(const o of e){const n=Number(o);if(!Number.isInteger(n)||n<0||n>255||o.length>1&&o[0]==="0")return null;r=r*256+n}return r>>>0}u(ipToInt,"ipToInt"),a(ipToInt,"ipToInt");function matchCidr(t,e){const r=e.indexOf("/");if(r<0)return!1;const o=Number(e.slice(r+1));if(!Number.isInteger(o)||o<0||o>32)return!1;const n=ipToInt(t),s=ipToInt(e.slice(0,r));if(n===null||s===null)return!1;if(o===0)return!0;const i=(o===32?4294967295:~((1<<32-o)-1))>>>0;return(n&i)>>>0===(s&i)>>>0}u(matchCidr,"matchCidr"),a(matchCidr,"matchCidr");const str=a(t=>t==null?"":String(t),"str"),eq=a((t,e)=>str(t)===str(e),"eq");function evalCondition(t,e){const r=e[t.attr],o=r!=null;switch(t.op){case"exists":return o;case"ne":return!o||!eq(r,t.value)}if(!o)return!1;switch(t.op){case"eq":return eq(r,t.value);case"in":return Array.isArray(t.value)&&t.value.some(n=>Array.isArray(r)?r.some(s=>eq(s,n)):eq(r,n));case"nin":return!(Array.isArray(t.value)&&t.value.some(n=>Array.isArray(r)?r.some(s=>eq(s,n)):eq(r,n)));case"prefix":return str(r).startsWith(str(t.value));case"suffix":return str(r).endsWith(str(t.value));case"contains":return Array.isArray(r)?r.some(n=>eq(n,t.value)):str(r).includes(str(t.value));case"hastoken":{const n=str(t.value);return Array.isArray(r)?r.some(s=>eq(s,t.value)):str(r).split(/[\s,]+/).filter(Boolean).includes(n)}case"regex":{const n=compileRe(str(t.value));return n?n.test(str(r)):!1}case"gt":return Number(r)>Number(t.value);case"lt":return Number(r)<Number(t.value);case"cidr":return matchCidr(str(r),str(t.value));default:return!1}}u(evalCondition,"evalCondition"),a(evalCondition,"evalCondition");function evalRule(t,e){if(!isGroup(t))return evalCondition(t,e);const r=Array.isArray(t.all)&&t.all.length?t.all:void 0,o=Array.isArray(t.any)&&t.any.length?t.any:void 0,n=t.not&&Object.keys(t.not).length?t.not:void 0;return n&&evalRule(n,e)||r&&!r.every(s=>evalRule(s,e))||o&&!o.some(s=>evalRule(s,e))?!1:!!(r||o||n)}u(evalRule,"evalRule"),a(evalRule,"evalRule");function pickTargetedVersion(t,e){for(const r of t)if(evalRule(r.when,e))return{version:r.version,...r.name?{rule:r.name}:{}}}u(pickTargetedVersion,"pickTargetedVersion"),a(pickTargetedVersion,"pickTargetedVersion");function fnv1a(t){let e=2166136261;for(let r=0;r<t.length;r++)e^=t.charCodeAt(r),e=Math.imul(e,16777619);return e>>>0}u(fnv1a,"fnv1a"),a(fnv1a,"fnv1a");function bucketByWeight(t,e){const r=e.reduce((s,i)=>s+Math.max(0,i),0);if(r<=0||e.length===0)return 0;const o=fnv1a(t)%1e4;let n=0;for(let s=0;s<e.length;s++)if(n+=Math.max(0,e[s])/r*1e4,o<n)return s;return e.length-1}u(bucketByWeight,"bucketByWeight"),a(bucketByWeight,"bucketByWeight");
@@ -34,4 +34,3 @@ export interface ProcessMetrics {
34
34
  stop(): void;
35
35
  }
36
36
  export declare function createProcessMetrics(): ProcessMetrics;
37
- //# sourceMappingURL=metrics.d.ts.map
@@ -1,56 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createProcessMetrics = createProcessMetrics;
4
- /**
5
- * Process self-metrics for the production gateway — the signals a load/stress test
6
- * needs that can ONLY be measured from inside the process: event-loop delay (lag),
7
- * RSS/heap, CPU time, and request rates. OPT-IN; exposed via the token-gated
8
- * `/metricsz` endpoint and reusable for production monitoring (Prometheus-style scrape).
9
- *
10
- * `snapshot()` returns DELTAS since the previous call (CPU%, requests/sec, and a fresh
11
- * event-loop-lag histogram), so a poller sampling every N seconds reads interval rates —
12
- * the natural shape for a load run's time series.
13
- */
14
- const node_perf_hooks_1 = require("node:perf_hooks");
15
- const MB = 1024 * 1024;
16
- const round = (n, d = 1) => { const f = 10 ** d; return Math.round(n * f) / f; };
17
- function createProcessMetrics() {
18
- const startedAt = Date.now();
19
- const loop = (0, node_perf_hooks_1.monitorEventLoopDelay)({ resolution: 10 });
20
- loop.enable();
21
- let lastCpu = process.cpuUsage();
22
- let lastAt = Date.now();
23
- let total = 0, s2 = 0, s4 = 0, s5 = 0, lastTotal = 0;
24
- const lagMs = (ns) => round(ns / 1e6, 2);
25
- return {
26
- recordRequest(status) {
27
- total += 1;
28
- if (status >= 500 || status === 0)
29
- s5 += 1;
30
- else if (status >= 400)
31
- s4 += 1;
32
- else if (status >= 200)
33
- s2 += 1;
34
- },
35
- snapshot() {
36
- const now = Date.now();
37
- const wallMs = Math.max(1, now - lastAt);
38
- const cpu = process.cpuUsage(lastCpu); // µs since lastCpu
39
- const mem = process.memoryUsage();
40
- const cpuMs = (cpu.user + cpu.system) / 1000;
41
- const snap = {
42
- uptimeSec: Math.round((now - startedAt) / 1000),
43
- rssMB: round(mem.rss / MB), heapUsedMB: round(mem.heapUsed / MB), heapTotalMB: round(mem.heapTotal / MB), externalMB: round(mem.external / MB),
44
- cpu: { userMs: round(cpu.user / 1000), systemMs: round(cpu.system / 1000), percent: round((cpuMs / wallMs) * 100) },
45
- loopLagMs: { mean: lagMs(loop.mean), p50: lagMs(loop.percentile(50)), p90: lagMs(loop.percentile(90)), p99: lagMs(loop.percentile(99)), max: lagMs(loop.max) },
46
- requests: { total, sinceLast: total - lastTotal, perSec: Math.round(((total - lastTotal) / wallMs) * 1000), s2xx: s2, s4xx: s4, s5xx: s5 },
47
- };
48
- lastCpu = process.cpuUsage();
49
- lastAt = now;
50
- lastTotal = total;
51
- loop.reset();
52
- return snap;
53
- },
54
- stop() { loop.disable(); },
55
- };
56
- }
1
+ "use strict";var y=Object.defineProperty;var l=(r,e)=>y(r,"name",{value:e,configurable:!0});var f=Object.defineProperty,p=l((r,e)=>f(r,"name",{value:e,configurable:!0}),"p");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createProcessMetrics=createProcessMetrics;const node_perf_hooks_1=require("node:perf_hooks"),MB=1024*1024,round=p((r,e=1)=>{const t=10**e;return Math.round(r*t)/t},"round");function createProcessMetrics(){const r=Date.now(),e=(0,node_perf_hooks_1.monitorEventLoopDelay)({resolution:10});e.enable();let t=process.cpuUsage(),i=Date.now(),o=0,M=0,d=0,m=0,u=0;const a=p(s=>round(s/1e6,2),"lagMs");return{recordRequest(s){o+=1,s>=500||s===0?m+=1:s>=400?d+=1:s>=200&&(M+=1)},snapshot(){const s=Date.now(),x=Math.max(1,s-i),n=process.cpuUsage(t),c=process.memoryUsage(),h=(n.user+n.system)/1e3,g={uptimeSec:Math.round((s-r)/1e3),rssMB:round(c.rss/MB),heapUsedMB:round(c.heapUsed/MB),heapTotalMB:round(c.heapTotal/MB),externalMB:round(c.external/MB),cpu:{userMs:round(n.user/1e3),systemMs:round(n.system/1e3),percent:round(h/x*100)},loopLagMs:{mean:a(e.mean),p50:a(e.percentile(50)),p90:a(e.percentile(90)),p99:a(e.percentile(99)),max:a(e.max)},requests:{total:o,sinceLast:o-u,perSec:Math.round((o-u)/x*1e3),s2xx:M,s4xx:d,s5xx:m}};return t=process.cpuUsage(),i=s,u=o,e.reset(),g},stop(){e.disable()}}}l(createProcessMetrics,"createProcessMetrics"),p(createProcessMetrics,"createProcessMetrics");
@@ -44,4 +44,3 @@ export interface TracingOptions {
44
44
  /** Build the gateway tracer, or undefined when no OTLP endpoint is configured
45
45
  * (default-off → the lib skips all span work, zero hot-path overhead). */
46
46
  export declare function setupTracing(opts?: TracingOptions): GatewayTracer | undefined;
47
- //# sourceMappingURL=otel-tracing.d.ts.map