@vivero/stoma 0.1.0-rc.10

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 (254) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/LICENSE +21 -0
  3. package/README.md +325 -0
  4. package/dist/adapters/bun.d.ts +9 -0
  5. package/dist/adapters/bun.js +8 -0
  6. package/dist/adapters/bun.js.map +1 -0
  7. package/dist/adapters/cloudflare.d.ts +49 -0
  8. package/dist/adapters/cloudflare.js +85 -0
  9. package/dist/adapters/cloudflare.js.map +1 -0
  10. package/dist/adapters/deno.d.ts +9 -0
  11. package/dist/adapters/deno.js +8 -0
  12. package/dist/adapters/deno.js.map +1 -0
  13. package/dist/adapters/durable-object.d.ts +63 -0
  14. package/dist/adapters/durable-object.js +46 -0
  15. package/dist/adapters/durable-object.js.map +1 -0
  16. package/dist/adapters/index.d.ts +13 -0
  17. package/dist/adapters/index.js +53 -0
  18. package/dist/adapters/index.js.map +1 -0
  19. package/dist/adapters/memory.d.ts +9 -0
  20. package/dist/adapters/memory.js +14 -0
  21. package/dist/adapters/memory.js.map +1 -0
  22. package/dist/adapters/node.d.ts +9 -0
  23. package/dist/adapters/node.js +8 -0
  24. package/dist/adapters/node.js.map +1 -0
  25. package/dist/adapters/postgres.d.ts +109 -0
  26. package/dist/adapters/postgres.js +242 -0
  27. package/dist/adapters/postgres.js.map +1 -0
  28. package/dist/adapters/redis.d.ts +116 -0
  29. package/dist/adapters/redis.js +194 -0
  30. package/dist/adapters/redis.js.map +1 -0
  31. package/dist/adapters/testing.d.ts +32 -0
  32. package/dist/adapters/testing.js +33 -0
  33. package/dist/adapters/testing.js.map +1 -0
  34. package/dist/adapters/types.d.ts +4 -0
  35. package/dist/adapters/types.js +1 -0
  36. package/dist/adapters/types.js.map +1 -0
  37. package/dist/config/index.d.ts +11 -0
  38. package/dist/config/index.js +21 -0
  39. package/dist/config/index.js.map +1 -0
  40. package/dist/config/merge.d.ts +48 -0
  41. package/dist/config/merge.js +83 -0
  42. package/dist/config/merge.js.map +1 -0
  43. package/dist/config/schema.d.ts +254 -0
  44. package/dist/config/schema.js +109 -0
  45. package/dist/config/schema.js.map +1 -0
  46. package/dist/core/errors.d.ts +66 -0
  47. package/dist/core/errors.js +47 -0
  48. package/dist/core/errors.js.map +1 -0
  49. package/dist/core/gateway.d.ts +44 -0
  50. package/dist/core/gateway.js +400 -0
  51. package/dist/core/gateway.js.map +1 -0
  52. package/dist/core/health.d.ts +78 -0
  53. package/dist/core/health.js +65 -0
  54. package/dist/core/health.js.map +1 -0
  55. package/dist/core/pipeline.d.ts +62 -0
  56. package/dist/core/pipeline.js +214 -0
  57. package/dist/core/pipeline.js.map +1 -0
  58. package/dist/core/protocol.d.ts +4 -0
  59. package/dist/core/protocol.js +1 -0
  60. package/dist/core/protocol.js.map +1 -0
  61. package/dist/core/scope.d.ts +67 -0
  62. package/dist/core/scope.js +44 -0
  63. package/dist/core/scope.js.map +1 -0
  64. package/dist/core/types.d.ts +252 -0
  65. package/dist/core/types.js +1 -0
  66. package/dist/core/types.js.map +1 -0
  67. package/dist/index.d.ts +57 -0
  68. package/dist/index.js +158 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/observability/admin.d.ts +32 -0
  71. package/dist/observability/admin.js +85 -0
  72. package/dist/observability/admin.js.map +1 -0
  73. package/dist/observability/metrics.d.ts +78 -0
  74. package/dist/observability/metrics.js +107 -0
  75. package/dist/observability/metrics.js.map +1 -0
  76. package/dist/observability/tracing.d.ts +149 -0
  77. package/dist/observability/tracing.js +191 -0
  78. package/dist/observability/tracing.js.map +1 -0
  79. package/dist/policies/auth/api-key-auth.d.ts +64 -0
  80. package/dist/policies/auth/api-key-auth.js +93 -0
  81. package/dist/policies/auth/api-key-auth.js.map +1 -0
  82. package/dist/policies/auth/basic-auth.d.ts +33 -0
  83. package/dist/policies/auth/basic-auth.js +96 -0
  84. package/dist/policies/auth/basic-auth.js.map +1 -0
  85. package/dist/policies/auth/crypto.d.ts +29 -0
  86. package/dist/policies/auth/crypto.js +100 -0
  87. package/dist/policies/auth/crypto.js.map +1 -0
  88. package/dist/policies/auth/generate-http-signature.d.ts +30 -0
  89. package/dist/policies/auth/generate-http-signature.js +79 -0
  90. package/dist/policies/auth/generate-http-signature.js.map +1 -0
  91. package/dist/policies/auth/generate-jwt.d.ts +44 -0
  92. package/dist/policies/auth/generate-jwt.js +99 -0
  93. package/dist/policies/auth/generate-jwt.js.map +1 -0
  94. package/dist/policies/auth/http-signature-base.d.ts +55 -0
  95. package/dist/policies/auth/http-signature-base.js +140 -0
  96. package/dist/policies/auth/http-signature-base.js.map +1 -0
  97. package/dist/policies/auth/jws.d.ts +46 -0
  98. package/dist/policies/auth/jws.js +317 -0
  99. package/dist/policies/auth/jws.js.map +1 -0
  100. package/dist/policies/auth/jwt-auth.d.ts +64 -0
  101. package/dist/policies/auth/jwt-auth.js +266 -0
  102. package/dist/policies/auth/jwt-auth.js.map +1 -0
  103. package/dist/policies/auth/oauth2.d.ts +38 -0
  104. package/dist/policies/auth/oauth2.js +254 -0
  105. package/dist/policies/auth/oauth2.js.map +1 -0
  106. package/dist/policies/auth/rbac.d.ts +30 -0
  107. package/dist/policies/auth/rbac.js +115 -0
  108. package/dist/policies/auth/rbac.js.map +1 -0
  109. package/dist/policies/auth/verify-http-signature.d.ts +30 -0
  110. package/dist/policies/auth/verify-http-signature.js +147 -0
  111. package/dist/policies/auth/verify-http-signature.js.map +1 -0
  112. package/dist/policies/index.d.ts +51 -0
  113. package/dist/policies/index.js +109 -0
  114. package/dist/policies/index.js.map +1 -0
  115. package/dist/policies/mock.d.ts +60 -0
  116. package/dist/policies/mock.js +29 -0
  117. package/dist/policies/mock.js.map +1 -0
  118. package/dist/policies/observability/assign-metrics.d.ts +37 -0
  119. package/dist/policies/observability/assign-metrics.js +29 -0
  120. package/dist/policies/observability/assign-metrics.js.map +1 -0
  121. package/dist/policies/observability/metrics-reporter.d.ts +25 -0
  122. package/dist/policies/observability/metrics-reporter.js +62 -0
  123. package/dist/policies/observability/metrics-reporter.js.map +1 -0
  124. package/dist/policies/observability/request-log.d.ts +135 -0
  125. package/dist/policies/observability/request-log.js +134 -0
  126. package/dist/policies/observability/request-log.js.map +1 -0
  127. package/dist/policies/observability/server-timing.d.ts +35 -0
  128. package/dist/policies/observability/server-timing.js +89 -0
  129. package/dist/policies/observability/server-timing.js.map +1 -0
  130. package/dist/policies/proxy.d.ts +59 -0
  131. package/dist/policies/proxy.js +47 -0
  132. package/dist/policies/proxy.js.map +1 -0
  133. package/dist/policies/resilience/circuit-breaker.d.ts +4 -0
  134. package/dist/policies/resilience/circuit-breaker.js +280 -0
  135. package/dist/policies/resilience/circuit-breaker.js.map +1 -0
  136. package/dist/policies/resilience/latency-injection.d.ts +35 -0
  137. package/dist/policies/resilience/latency-injection.js +26 -0
  138. package/dist/policies/resilience/latency-injection.js.map +1 -0
  139. package/dist/policies/resilience/retry.d.ts +71 -0
  140. package/dist/policies/resilience/retry.js +79 -0
  141. package/dist/policies/resilience/retry.js.map +1 -0
  142. package/dist/policies/resilience/timeout.d.ts +32 -0
  143. package/dist/policies/resilience/timeout.js +46 -0
  144. package/dist/policies/resilience/timeout.js.map +1 -0
  145. package/dist/policies/sdk/define-policy.d.ts +176 -0
  146. package/dist/policies/sdk/define-policy.js +42 -0
  147. package/dist/policies/sdk/define-policy.js.map +1 -0
  148. package/dist/policies/sdk/helpers.d.ts +132 -0
  149. package/dist/policies/sdk/helpers.js +87 -0
  150. package/dist/policies/sdk/helpers.js.map +1 -0
  151. package/dist/policies/sdk/index.d.ts +10 -0
  152. package/dist/policies/sdk/index.js +35 -0
  153. package/dist/policies/sdk/index.js.map +1 -0
  154. package/dist/policies/sdk/priority.d.ts +44 -0
  155. package/dist/policies/sdk/priority.js +36 -0
  156. package/dist/policies/sdk/priority.js.map +1 -0
  157. package/dist/policies/sdk/testing.d.ts +53 -0
  158. package/dist/policies/sdk/testing.js +41 -0
  159. package/dist/policies/sdk/testing.js.map +1 -0
  160. package/dist/policies/sdk/trace.d.ts +73 -0
  161. package/dist/policies/sdk/trace.js +25 -0
  162. package/dist/policies/sdk/trace.js.map +1 -0
  163. package/dist/policies/traffic/cache.d.ts +4 -0
  164. package/dist/policies/traffic/cache.js +224 -0
  165. package/dist/policies/traffic/cache.js.map +1 -0
  166. package/dist/policies/traffic/dynamic-routing.d.ts +54 -0
  167. package/dist/policies/traffic/dynamic-routing.js +36 -0
  168. package/dist/policies/traffic/dynamic-routing.js.map +1 -0
  169. package/dist/policies/traffic/geo-ip-filter.d.ts +37 -0
  170. package/dist/policies/traffic/geo-ip-filter.js +74 -0
  171. package/dist/policies/traffic/geo-ip-filter.js.map +1 -0
  172. package/dist/policies/traffic/http-callout.d.ts +59 -0
  173. package/dist/policies/traffic/http-callout.js +69 -0
  174. package/dist/policies/traffic/http-callout.js.map +1 -0
  175. package/dist/policies/traffic/interrupt.d.ts +46 -0
  176. package/dist/policies/traffic/interrupt.js +38 -0
  177. package/dist/policies/traffic/interrupt.js.map +1 -0
  178. package/dist/policies/traffic/ip-filter.d.ts +47 -0
  179. package/dist/policies/traffic/ip-filter.js +57 -0
  180. package/dist/policies/traffic/ip-filter.js.map +1 -0
  181. package/dist/policies/traffic/json-threat-protection.d.ts +51 -0
  182. package/dist/policies/traffic/json-threat-protection.js +173 -0
  183. package/dist/policies/traffic/json-threat-protection.js.map +1 -0
  184. package/dist/policies/traffic/rate-limit.d.ts +4 -0
  185. package/dist/policies/traffic/rate-limit.js +145 -0
  186. package/dist/policies/traffic/rate-limit.js.map +1 -0
  187. package/dist/policies/traffic/regex-threat-protection.d.ts +54 -0
  188. package/dist/policies/traffic/regex-threat-protection.js +109 -0
  189. package/dist/policies/traffic/regex-threat-protection.js.map +1 -0
  190. package/dist/policies/traffic/request-limit.d.ts +27 -0
  191. package/dist/policies/traffic/request-limit.js +41 -0
  192. package/dist/policies/traffic/request-limit.js.map +1 -0
  193. package/dist/policies/traffic/resource-filter.d.ts +38 -0
  194. package/dist/policies/traffic/resource-filter.js +184 -0
  195. package/dist/policies/traffic/resource-filter.js.map +1 -0
  196. package/dist/policies/traffic/ssl-enforce.d.ts +27 -0
  197. package/dist/policies/traffic/ssl-enforce.js +38 -0
  198. package/dist/policies/traffic/ssl-enforce.js.map +1 -0
  199. package/dist/policies/traffic/traffic-shadow.d.ts +40 -0
  200. package/dist/policies/traffic/traffic-shadow.js +87 -0
  201. package/dist/policies/traffic/traffic-shadow.js.map +1 -0
  202. package/dist/policies/transform/assign-attributes.d.ts +33 -0
  203. package/dist/policies/transform/assign-attributes.js +38 -0
  204. package/dist/policies/transform/assign-attributes.js.map +1 -0
  205. package/dist/policies/transform/assign-content.d.ts +40 -0
  206. package/dist/policies/transform/assign-content.js +185 -0
  207. package/dist/policies/transform/assign-content.js.map +1 -0
  208. package/dist/policies/transform/cors.d.ts +57 -0
  209. package/dist/policies/transform/cors.js +23 -0
  210. package/dist/policies/transform/cors.js.map +1 -0
  211. package/dist/policies/transform/json-validation.d.ts +50 -0
  212. package/dist/policies/transform/json-validation.js +125 -0
  213. package/dist/policies/transform/json-validation.js.map +1 -0
  214. package/dist/policies/transform/override-method.d.ts +33 -0
  215. package/dist/policies/transform/override-method.js +48 -0
  216. package/dist/policies/transform/override-method.js.map +1 -0
  217. package/dist/policies/transform/request-validation.d.ts +59 -0
  218. package/dist/policies/transform/request-validation.js +121 -0
  219. package/dist/policies/transform/request-validation.js.map +1 -0
  220. package/dist/policies/transform/transform.d.ts +75 -0
  221. package/dist/policies/transform/transform.js +116 -0
  222. package/dist/policies/transform/transform.js.map +1 -0
  223. package/dist/policies/types.d.ts +4 -0
  224. package/dist/policies/types.js +1 -0
  225. package/dist/policies/types.js.map +1 -0
  226. package/dist/protocol-2fD3DJrL.d.ts +725 -0
  227. package/dist/utils/cidr.d.ts +58 -0
  228. package/dist/utils/cidr.js +107 -0
  229. package/dist/utils/cidr.js.map +1 -0
  230. package/dist/utils/debug.d.ts +1 -0
  231. package/dist/utils/debug.js +13 -0
  232. package/dist/utils/debug.js.map +1 -0
  233. package/dist/utils/headers.d.ts +68 -0
  234. package/dist/utils/headers.js +25 -0
  235. package/dist/utils/headers.js.map +1 -0
  236. package/dist/utils/ip.d.ts +64 -0
  237. package/dist/utils/ip.js +29 -0
  238. package/dist/utils/ip.js.map +1 -0
  239. package/dist/utils/redact.d.ts +30 -0
  240. package/dist/utils/redact.js +52 -0
  241. package/dist/utils/redact.js.map +1 -0
  242. package/dist/utils/request-id.d.ts +11 -0
  243. package/dist/utils/request-id.js +7 -0
  244. package/dist/utils/request-id.js.map +1 -0
  245. package/dist/utils/timing-safe.d.ts +31 -0
  246. package/dist/utils/timing-safe.js +17 -0
  247. package/dist/utils/timing-safe.js.map +1 -0
  248. package/dist/utils/timing.d.ts +27 -0
  249. package/dist/utils/timing.js +12 -0
  250. package/dist/utils/timing.js.map +1 -0
  251. package/dist/utils/trace-context.d.ts +51 -0
  252. package/dist/utils/trace-context.js +37 -0
  253. package/dist/utils/trace-context.js.map +1 -0
  254. package/package.json +213 -0
@@ -0,0 +1,33 @@
1
+ class TestAdapter {
2
+ promises = [];
3
+ /**
4
+ * Add a promise to the background work queue.
5
+ */
6
+ waitUntil = (promise) => {
7
+ this.promises.push(promise);
8
+ };
9
+ /**
10
+ * Await all pending background work collected via `waitUntil`.
11
+ */
12
+ async waitAll() {
13
+ while (this.promises.length > 0) {
14
+ const batch = [...this.promises];
15
+ this.promises = [];
16
+ await Promise.all(batch);
17
+ }
18
+ }
19
+ /**
20
+ * Reset the collected promises.
21
+ */
22
+ reset() {
23
+ this.promises = [];
24
+ }
25
+ }
26
+ function createTestAdapter() {
27
+ return new TestAdapter();
28
+ }
29
+ export {
30
+ TestAdapter,
31
+ createTestAdapter
32
+ };
33
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/testing.ts"],"sourcesContent":["import type { GatewayAdapter } from \"./types\";\n\n/**\n * A GatewayAdapter implementation for unit testing.\n *\n * Provides a `waitUntil` implementation that collects background promises,\n * allowing tests to `await adapter.waitAll()` before finishing.\n */\nexport class TestAdapter implements GatewayAdapter {\n private promises: Promise<unknown>[] = [];\n\n /**\n * Add a promise to the background work queue.\n */\n waitUntil = (promise: Promise<unknown>): void => {\n this.promises.push(promise);\n };\n\n /**\n * Await all pending background work collected via `waitUntil`.\n */\n async waitAll(): Promise<void> {\n while (this.promises.length > 0) {\n const batch = [...this.promises];\n this.promises = [];\n await Promise.all(batch);\n }\n }\n\n /**\n * Reset the collected promises.\n */\n reset(): void {\n this.promises = [];\n }\n}\n\n/**\n * Create a new {@link TestAdapter}.\n */\nexport function createTestAdapter(): TestAdapter {\n return new TestAdapter();\n}\n"],"mappings":"AAQO,MAAM,YAAsC;AAAA,EACzC,WAA+B,CAAC;AAAA;AAAA;AAAA;AAAA,EAKxC,YAAY,CAAC,YAAoC;AAC/C,SAAK,SAAS,KAAK,OAAO;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAC7B,WAAO,KAAK,SAAS,SAAS,GAAG;AAC/B,YAAM,QAAQ,CAAC,GAAG,KAAK,QAAQ;AAC/B,WAAK,WAAW,CAAC;AACjB,YAAM,QAAQ,IAAI,KAAK;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,WAAW,CAAC;AAAA,EACnB;AACF;AAKO,SAAS,oBAAiC;AAC/C,SAAO,IAAI,YAAY;AACzB;","names":[]}
@@ -0,0 +1,4 @@
1
+ export { G as GatewayAdapter } from '../protocol-2fD3DJrL.js';
2
+ import 'hono';
3
+ import '../policies/sdk/trace.js';
4
+ import '@vivero/stoma-core';
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,11 @@
1
+ export { ScopeConfig } from '../core/scope.js';
2
+ export { GatewayConfig, PipelineConfig, RouteConfig, UpstreamConfig } from '../core/types.js';
3
+ export { mergeConfigs } from './merge.js';
4
+ export { GatewayConfigSchema, PipelineSchema, PolicySchema, RouteSchema, UpstreamSchema, safeValidateConfig, validateConfig } from './schema.js';
5
+ import '../protocol-2fD3DJrL.js';
6
+ import 'hono';
7
+ import '../policies/sdk/trace.js';
8
+ import '@vivero/stoma-core';
9
+ import '../observability/metrics.js';
10
+ import '../observability/tracing.js';
11
+ import 'zod';
@@ -0,0 +1,21 @@
1
+ import { mergeConfigs } from "./merge";
2
+ import {
3
+ GatewayConfigSchema,
4
+ PipelineSchema,
5
+ PolicySchema,
6
+ RouteSchema,
7
+ safeValidateConfig,
8
+ UpstreamSchema,
9
+ validateConfig
10
+ } from "./schema";
11
+ export {
12
+ GatewayConfigSchema,
13
+ PipelineSchema,
14
+ PolicySchema,
15
+ RouteSchema,
16
+ UpstreamSchema,
17
+ mergeConfigs,
18
+ safeValidateConfig,
19
+ validateConfig
20
+ };
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/config/index.ts"],"sourcesContent":["/**\n * Configuration types and optional Zod validation for stoma gateway configs.\n *\n * Import from `@vivero/stoma/config` for gateway configuration types\n * and runtime validation utilities. The Zod schemas are **optional** - they\n * require the `zod` peer dependency. Consumers who only use the TypeScript\n * types never need to install Zod.\n *\n * @example\n * ```ts\n * import type { GatewayConfig } from \"@vivero/stoma/config\";\n * import { validateConfig, safeValidateConfig } from \"@vivero/stoma/config\";\n *\n * // Type-only usage (no zod required)\n * const config: GatewayConfig = { ... };\n *\n * // Runtime validation (requires zod peer dep)\n * const validated = validateConfig(untrustedConfig); // throws on invalid\n * const result = safeValidateConfig(untrustedConfig); // returns { success, data?, error? }\n * ```\n *\n * @module config\n */\n\nexport type {\n /** Configuration for a route scope: prefix, shared policies, child routes, and metadata. */\n ScopeConfig,\n} from \"../core/scope\";\nexport type {\n /** Top-level gateway configuration: routes, global policies, error handling, debug, and adapter. */\n GatewayConfig,\n /** Pipeline definition: ordered policy chain leading to an upstream target. */\n PipelineConfig,\n /** Individual route definition: path, methods, and pipeline (policies + upstream). */\n RouteConfig,\n /** Discriminated union of upstream types: URL proxy, Service Binding, or custom handler. */\n UpstreamConfig,\n} from \"../core/types\";\n\n/** Merge multiple partial gateway configs into a single complete config. */\nexport { mergeConfigs } from \"./merge\";\n\nexport {\n /** Zod schema for the full GatewayConfig object. */\n GatewayConfigSchema,\n /** Zod schema for a PipelineConfig. */\n PipelineSchema,\n /** Zod schema for a single Policy object (name, handler, priority). */\n PolicySchema,\n /** Zod schema for a single RouteConfig. */\n RouteSchema,\n /** Validate a gateway config returning a Zod SafeParseResult instead of throwing (requires zod). */\n safeValidateConfig,\n /** Zod schema for the UpstreamConfig discriminated union. */\n UpstreamSchema,\n /** Validate a gateway config object at runtime, throwing on invalid input (requires zod). */\n validateConfig,\n} from \"./schema\";\n"],"mappings":"AAwCA,SAAS,oBAAoB;AAE7B;AAAA,EAEE;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,OACK;","names":[]}
@@ -0,0 +1,48 @@
1
+ import { GatewayConfig } from '../core/types.js';
2
+ import 'hono';
3
+ import '../protocol-2fD3DJrL.js';
4
+ import '../policies/sdk/trace.js';
5
+ import '@vivero/stoma-core';
6
+ import '../observability/metrics.js';
7
+ import '../observability/tracing.js';
8
+
9
+ /**
10
+ * Configuration merging utilities for splitting gateway configs across files.
11
+ *
12
+ * {@link mergeConfigs} accepts any number of partial gateway configs and
13
+ * merges them left-to-right into a single complete {@link GatewayConfig}.
14
+ * This enables patterns like per-domain route files, shared policy sets,
15
+ * and environment-specific overrides.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { mergeConfigs, createGateway } from "@vivero/stoma";
20
+ *
21
+ * const base = { name: "my-gw", policies: [cors(), rateLimit({ max: 100 })] };
22
+ * const authRoutes = { routes: [{ path: "/auth/*", ... }] };
23
+ * const apiRoutes = { routes: [{ path: "/api/*", ... }] };
24
+ *
25
+ * const gw = createGateway(mergeConfigs(base, authRoutes, apiRoutes));
26
+ * ```
27
+ *
28
+ * @module config/merge
29
+ */
30
+
31
+ /**
32
+ * Merge multiple partial gateway configs into a single complete config.
33
+ *
34
+ * Merge semantics by field:
35
+ * - **routes** - concatenated (all routes from all configs, in order)
36
+ * - **policies** - deduplicated by `name` (later config wins on conflict)
37
+ * - **admin**, **debugHeaders** - shallow-merged when both are objects;
38
+ * last-defined wins when types differ (boolean vs object)
39
+ * - All other scalar fields - last-defined wins (undefined values are skipped)
40
+ *
41
+ * @typeParam TBindings - Worker bindings type, propagated to routes.
42
+ * @param configs - Partial configs to merge (left to right, later wins).
43
+ * @returns A merged GatewayConfig.
44
+ * @throws {GatewayError} If the merged result has zero routes.
45
+ */
46
+ declare function mergeConfigs<TBindings = Record<string, unknown>>(...configs: Array<Partial<GatewayConfig<TBindings>>>): GatewayConfig<TBindings>;
47
+
48
+ export { mergeConfigs };
@@ -0,0 +1,83 @@
1
+ import { GatewayError } from "../core/errors";
2
+ function mergeConfigs(...configs) {
3
+ const mergedRoutes = [];
4
+ const policyMap = /* @__PURE__ */ new Map();
5
+ let name;
6
+ let basePath;
7
+ let debug;
8
+ let requestIdHeader;
9
+ let defaultErrorMessage;
10
+ let defaultPolicyPriority;
11
+ let defaultMethods;
12
+ let onError;
13
+ let adapter;
14
+ let admin;
15
+ let debugHeaders;
16
+ for (const cfg of configs) {
17
+ if (cfg.routes) {
18
+ mergedRoutes.push(...cfg.routes);
19
+ }
20
+ if (cfg.policies) {
21
+ for (const policy of cfg.policies) {
22
+ policyMap.set(policy.name, policy);
23
+ }
24
+ }
25
+ if (cfg.name !== void 0) name = cfg.name;
26
+ if (cfg.basePath !== void 0) basePath = cfg.basePath;
27
+ if (cfg.debug !== void 0) debug = cfg.debug;
28
+ if (cfg.requestIdHeader !== void 0)
29
+ requestIdHeader = cfg.requestIdHeader;
30
+ if (cfg.defaultErrorMessage !== void 0)
31
+ defaultErrorMessage = cfg.defaultErrorMessage;
32
+ if (cfg.defaultPolicyPriority !== void 0)
33
+ defaultPolicyPriority = cfg.defaultPolicyPriority;
34
+ if (cfg.defaultMethods !== void 0) defaultMethods = cfg.defaultMethods;
35
+ if (cfg.onError !== void 0) onError = cfg.onError;
36
+ if (cfg.adapter !== void 0) adapter = cfg.adapter;
37
+ if (cfg.admin !== void 0) {
38
+ admin = shallowMergeUnion(admin, cfg.admin);
39
+ }
40
+ if (cfg.debugHeaders !== void 0) {
41
+ debugHeaders = shallowMergeUnion(
42
+ debugHeaders,
43
+ cfg.debugHeaders
44
+ );
45
+ }
46
+ }
47
+ if (mergedRoutes.length === 0) {
48
+ throw new GatewayError(
49
+ 500,
50
+ "config_error",
51
+ "mergeConfigs: merged config has zero routes"
52
+ );
53
+ }
54
+ const result = {
55
+ routes: mergedRoutes
56
+ };
57
+ const mergedPolicies = Array.from(policyMap.values());
58
+ if (mergedPolicies.length > 0) result.policies = mergedPolicies;
59
+ if (name !== void 0) result.name = name;
60
+ if (basePath !== void 0) result.basePath = basePath;
61
+ if (debug !== void 0) result.debug = debug;
62
+ if (requestIdHeader !== void 0) result.requestIdHeader = requestIdHeader;
63
+ if (defaultErrorMessage !== void 0)
64
+ result.defaultErrorMessage = defaultErrorMessage;
65
+ if (defaultPolicyPriority !== void 0)
66
+ result.defaultPolicyPriority = defaultPolicyPriority;
67
+ if (defaultMethods !== void 0) result.defaultMethods = defaultMethods;
68
+ if (onError !== void 0) result.onError = onError;
69
+ if (adapter !== void 0) result.adapter = adapter;
70
+ if (admin !== void 0) result.admin = admin;
71
+ if (debugHeaders !== void 0) result.debugHeaders = debugHeaders;
72
+ return result;
73
+ }
74
+ function shallowMergeUnion(existing, incoming) {
75
+ if (existing !== void 0 && typeof existing === "object" && typeof incoming === "object") {
76
+ return { ...existing, ...incoming };
77
+ }
78
+ return incoming;
79
+ }
80
+ export {
81
+ mergeConfigs
82
+ };
83
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/config/merge.ts"],"sourcesContent":["/**\n * Configuration merging utilities for splitting gateway configs across files.\n *\n * {@link mergeConfigs} accepts any number of partial gateway configs and\n * merges them left-to-right into a single complete {@link GatewayConfig}.\n * This enables patterns like per-domain route files, shared policy sets,\n * and environment-specific overrides.\n *\n * @example\n * ```ts\n * import { mergeConfigs, createGateway } from \"@vivero/stoma\";\n *\n * const base = { name: \"my-gw\", policies: [cors(), rateLimit({ max: 100 })] };\n * const authRoutes = { routes: [{ path: \"/auth/*\", ... }] };\n * const apiRoutes = { routes: [{ path: \"/api/*\", ... }] };\n *\n * const gw = createGateway(mergeConfigs(base, authRoutes, apiRoutes));\n * ```\n *\n * @module config/merge\n */\n\nimport { GatewayError } from \"../core/errors\";\nimport type {\n AdminConfig,\n DebugHeadersConfig,\n GatewayConfig,\n} from \"../core/types\";\nimport type { Policy } from \"../policies/types\";\n\n/**\n * Merge multiple partial gateway configs into a single complete config.\n *\n * Merge semantics by field:\n * - **routes** - concatenated (all routes from all configs, in order)\n * - **policies** - deduplicated by `name` (later config wins on conflict)\n * - **admin**, **debugHeaders** - shallow-merged when both are objects;\n * last-defined wins when types differ (boolean vs object)\n * - All other scalar fields - last-defined wins (undefined values are skipped)\n *\n * @typeParam TBindings - Worker bindings type, propagated to routes.\n * @param configs - Partial configs to merge (left to right, later wins).\n * @returns A merged GatewayConfig.\n * @throws {GatewayError} If the merged result has zero routes.\n */\nexport function mergeConfigs<TBindings = Record<string, unknown>>(\n ...configs: Array<Partial<GatewayConfig<TBindings>>>\n): GatewayConfig<TBindings> {\n const mergedRoutes: GatewayConfig<TBindings>[\"routes\"] = [];\n const policyMap = new Map<string, Policy>();\n\n // Scalar fields - last-defined wins\n let name: GatewayConfig<TBindings>[\"name\"];\n let basePath: GatewayConfig<TBindings>[\"basePath\"];\n let debug: GatewayConfig<TBindings>[\"debug\"];\n let requestIdHeader: GatewayConfig<TBindings>[\"requestIdHeader\"];\n let defaultErrorMessage: GatewayConfig<TBindings>[\"defaultErrorMessage\"];\n let defaultPolicyPriority: GatewayConfig<TBindings>[\"defaultPolicyPriority\"];\n let defaultMethods: GatewayConfig<TBindings>[\"defaultMethods\"];\n let onError: GatewayConfig<TBindings>[\"onError\"];\n let adapter: GatewayConfig<TBindings>[\"adapter\"];\n\n // Object/boolean union fields - shallow-merge when both objects\n let admin: GatewayConfig<TBindings>[\"admin\"];\n let debugHeaders: GatewayConfig<TBindings>[\"debugHeaders\"];\n\n for (const cfg of configs) {\n // Routes: concatenate\n if (cfg.routes) {\n mergedRoutes.push(...cfg.routes);\n }\n\n // Policies: deduplicate by name (later wins)\n if (cfg.policies) {\n for (const policy of cfg.policies) {\n policyMap.set(policy.name, policy);\n }\n }\n\n // Scalar fields: last-defined wins (skip undefined)\n if (cfg.name !== undefined) name = cfg.name;\n if (cfg.basePath !== undefined) basePath = cfg.basePath;\n if (cfg.debug !== undefined) debug = cfg.debug;\n if (cfg.requestIdHeader !== undefined)\n requestIdHeader = cfg.requestIdHeader;\n if (cfg.defaultErrorMessage !== undefined)\n defaultErrorMessage = cfg.defaultErrorMessage;\n if (cfg.defaultPolicyPriority !== undefined)\n defaultPolicyPriority = cfg.defaultPolicyPriority;\n if (cfg.defaultMethods !== undefined) defaultMethods = cfg.defaultMethods;\n if (cfg.onError !== undefined) onError = cfg.onError;\n if (cfg.adapter !== undefined) adapter = cfg.adapter;\n\n // Admin: shallow-merge objects, last-defined wins for boolean/object mismatch\n if (cfg.admin !== undefined) {\n admin = shallowMergeUnion<AdminConfig>(admin, cfg.admin);\n }\n\n // DebugHeaders: same strategy as admin\n if (cfg.debugHeaders !== undefined) {\n debugHeaders = shallowMergeUnion<DebugHeadersConfig>(\n debugHeaders,\n cfg.debugHeaders\n );\n }\n }\n\n if (mergedRoutes.length === 0) {\n throw new GatewayError(\n 500,\n \"config_error\",\n \"mergeConfigs: merged config has zero routes\"\n );\n }\n\n const result: GatewayConfig<TBindings> = {\n routes: mergedRoutes,\n };\n\n // Only set fields that were defined\n const mergedPolicies = Array.from(policyMap.values());\n if (mergedPolicies.length > 0) result.policies = mergedPolicies;\n if (name !== undefined) result.name = name;\n if (basePath !== undefined) result.basePath = basePath;\n if (debug !== undefined) result.debug = debug;\n if (requestIdHeader !== undefined) result.requestIdHeader = requestIdHeader;\n if (defaultErrorMessage !== undefined)\n result.defaultErrorMessage = defaultErrorMessage;\n if (defaultPolicyPriority !== undefined)\n result.defaultPolicyPriority = defaultPolicyPriority;\n if (defaultMethods !== undefined) result.defaultMethods = defaultMethods;\n if (onError !== undefined) result.onError = onError;\n if (adapter !== undefined) result.adapter = adapter;\n if (admin !== undefined) result.admin = admin;\n if (debugHeaders !== undefined) result.debugHeaders = debugHeaders;\n\n return result;\n}\n\n/**\n * Shallow-merge two values that may be boolean or object.\n *\n * - Both objects → shallow-merge (later wins on key conflicts)\n * - Either is boolean or types differ → last value wins entirely\n */\nfunction shallowMergeUnion<T extends object>(\n existing: boolean | T | undefined,\n incoming: boolean | T\n): boolean | T {\n if (\n existing !== undefined &&\n typeof existing === \"object\" &&\n typeof incoming === \"object\"\n ) {\n return { ...existing, ...incoming };\n }\n return incoming;\n}\n"],"mappings":"AAsBA,SAAS,oBAAoB;AAuBtB,SAAS,gBACX,SACuB;AAC1B,QAAM,eAAmD,CAAC;AAC1D,QAAM,YAAY,oBAAI,IAAoB;AAG1C,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AAGJ,MAAI;AACJ,MAAI;AAEJ,aAAW,OAAO,SAAS;AAEzB,QAAI,IAAI,QAAQ;AACd,mBAAa,KAAK,GAAG,IAAI,MAAM;AAAA,IACjC;AAGA,QAAI,IAAI,UAAU;AAChB,iBAAW,UAAU,IAAI,UAAU;AACjC,kBAAU,IAAI,OAAO,MAAM,MAAM;AAAA,MACnC;AAAA,IACF;AAGA,QAAI,IAAI,SAAS,OAAW,QAAO,IAAI;AACvC,QAAI,IAAI,aAAa,OAAW,YAAW,IAAI;AAC/C,QAAI,IAAI,UAAU,OAAW,SAAQ,IAAI;AACzC,QAAI,IAAI,oBAAoB;AAC1B,wBAAkB,IAAI;AACxB,QAAI,IAAI,wBAAwB;AAC9B,4BAAsB,IAAI;AAC5B,QAAI,IAAI,0BAA0B;AAChC,8BAAwB,IAAI;AAC9B,QAAI,IAAI,mBAAmB,OAAW,kBAAiB,IAAI;AAC3D,QAAI,IAAI,YAAY,OAAW,WAAU,IAAI;AAC7C,QAAI,IAAI,YAAY,OAAW,WAAU,IAAI;AAG7C,QAAI,IAAI,UAAU,QAAW;AAC3B,cAAQ,kBAA+B,OAAO,IAAI,KAAK;AAAA,IACzD;AAGA,QAAI,IAAI,iBAAiB,QAAW;AAClC,qBAAe;AAAA,QACb;AAAA,QACA,IAAI;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAEA,MAAI,aAAa,WAAW,GAAG;AAC7B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAmC;AAAA,IACvC,QAAQ;AAAA,EACV;AAGA,QAAM,iBAAiB,MAAM,KAAK,UAAU,OAAO,CAAC;AACpD,MAAI,eAAe,SAAS,EAAG,QAAO,WAAW;AACjD,MAAI,SAAS,OAAW,QAAO,OAAO;AACtC,MAAI,aAAa,OAAW,QAAO,WAAW;AAC9C,MAAI,UAAU,OAAW,QAAO,QAAQ;AACxC,MAAI,oBAAoB,OAAW,QAAO,kBAAkB;AAC5D,MAAI,wBAAwB;AAC1B,WAAO,sBAAsB;AAC/B,MAAI,0BAA0B;AAC5B,WAAO,wBAAwB;AACjC,MAAI,mBAAmB,OAAW,QAAO,iBAAiB;AAC1D,MAAI,YAAY,OAAW,QAAO,UAAU;AAC5C,MAAI,YAAY,OAAW,QAAO,UAAU;AAC5C,MAAI,UAAU,OAAW,QAAO,QAAQ;AACxC,MAAI,iBAAiB,OAAW,QAAO,eAAe;AAEtD,SAAO;AACT;AAQA,SAAS,kBACP,UACA,UACa;AACb,MACE,aAAa,UACb,OAAO,aAAa,YACpB,OAAO,aAAa,UACpB;AACA,WAAO,EAAE,GAAG,UAAU,GAAG,SAAS;AAAA,EACpC;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,254 @@
1
+ import { z } from 'zod';
2
+ import { GatewayConfig } from '../core/types.js';
3
+ import 'hono';
4
+ import '../protocol-2fD3DJrL.js';
5
+ import '../policies/sdk/trace.js';
6
+ import '@vivero/stoma-core';
7
+ import '../observability/metrics.js';
8
+ import '../observability/tracing.js';
9
+
10
+ /**
11
+ * Zod schemas for gateway configuration validation.
12
+ *
13
+ * These schemas mirror the TypeScript types in `core/types.ts` and can be used
14
+ * to validate untrusted configuration objects at runtime (e.g. config loaded
15
+ * from KV, environment variables, or external sources).
16
+ *
17
+ * **Scope**: Validates the structural shape of `GatewayConfig` (routes, upstream
18
+ * types, methods, admin config). Individual policy configs are **not** validated -
19
+ * each policy validates its own config internally via `resolveConfig()`. This
20
+ * means `validateConfig()` catches structural misconfigurations (missing routes,
21
+ * invalid upstream type) but won't catch typos in policy-specific options.
22
+ *
23
+ * Zod is an **optional** peer dependency. Consumers who only use the static
24
+ * TypeScript types never pay for it.
25
+ *
26
+ * @module config/schema
27
+ */
28
+
29
+ /**
30
+ * Validates the Policy shape (name + handler + priority).
31
+ *
32
+ * This only checks that a policy _looks_ like a Policy - it does not validate
33
+ * the policy's own config options. Policy-specific validation happens inside
34
+ * each policy factory via `resolveConfig()` at construction time.
35
+ */
36
+ declare const PolicySchema: z.ZodObject<{
37
+ name: z.ZodString;
38
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
39
+ priority: z.ZodOptional<z.ZodNumber>;
40
+ }, z.core.$strip>;
41
+ declare const UpstreamSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
42
+ type: z.ZodLiteral<"url">;
43
+ target: z.ZodString;
44
+ rewritePath: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
45
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
46
+ }, z.core.$strip>, z.ZodObject<{
47
+ type: z.ZodLiteral<"service-binding">;
48
+ service: z.ZodString;
49
+ rewritePath: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
50
+ }, z.core.$strip>, z.ZodObject<{
51
+ type: z.ZodLiteral<"handler">;
52
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
53
+ }, z.core.$strip>], "type">;
54
+ declare const PipelineSchema: z.ZodObject<{
55
+ policies: z.ZodOptional<z.ZodArray<z.ZodObject<{
56
+ name: z.ZodString;
57
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
58
+ priority: z.ZodOptional<z.ZodNumber>;
59
+ }, z.core.$strip>>>;
60
+ upstream: z.ZodDiscriminatedUnion<[z.ZodObject<{
61
+ type: z.ZodLiteral<"url">;
62
+ target: z.ZodString;
63
+ rewritePath: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
64
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
65
+ }, z.core.$strip>, z.ZodObject<{
66
+ type: z.ZodLiteral<"service-binding">;
67
+ service: z.ZodString;
68
+ rewritePath: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
69
+ }, z.core.$strip>, z.ZodObject<{
70
+ type: z.ZodLiteral<"handler">;
71
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
72
+ }, z.core.$strip>], "type">;
73
+ }, z.core.$strip>;
74
+ declare const HttpMethodSchema: z.ZodEnum<{
75
+ POST: "POST";
76
+ GET: "GET";
77
+ PUT: "PUT";
78
+ PATCH: "PATCH";
79
+ DELETE: "DELETE";
80
+ HEAD: "HEAD";
81
+ OPTIONS: "OPTIONS";
82
+ }>;
83
+ declare const RouteSchema: z.ZodObject<{
84
+ path: z.ZodString;
85
+ methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
86
+ POST: "POST";
87
+ GET: "GET";
88
+ PUT: "PUT";
89
+ PATCH: "PATCH";
90
+ DELETE: "DELETE";
91
+ HEAD: "HEAD";
92
+ OPTIONS: "OPTIONS";
93
+ }>>>;
94
+ pipeline: z.ZodObject<{
95
+ policies: z.ZodOptional<z.ZodArray<z.ZodObject<{
96
+ name: z.ZodString;
97
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
98
+ priority: z.ZodOptional<z.ZodNumber>;
99
+ }, z.core.$strip>>>;
100
+ upstream: z.ZodDiscriminatedUnion<[z.ZodObject<{
101
+ type: z.ZodLiteral<"url">;
102
+ target: z.ZodString;
103
+ rewritePath: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
104
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
105
+ }, z.core.$strip>, z.ZodObject<{
106
+ type: z.ZodLiteral<"service-binding">;
107
+ service: z.ZodString;
108
+ rewritePath: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
109
+ }, z.core.$strip>, z.ZodObject<{
110
+ type: z.ZodLiteral<"handler">;
111
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
112
+ }, z.core.$strip>], "type">;
113
+ }, z.core.$strip>;
114
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
115
+ }, z.core.$strip>;
116
+ declare const TracingConfigSchema: z.ZodOptional<z.ZodObject<{
117
+ exporter: z.ZodAny;
118
+ serviceName: z.ZodOptional<z.ZodString>;
119
+ serviceVersion: z.ZodOptional<z.ZodString>;
120
+ sampleRate: z.ZodOptional<z.ZodNumber>;
121
+ }, z.core.$strip>>;
122
+ declare const ScopeConfigSchema: z.ZodObject<{
123
+ prefix: z.ZodString;
124
+ policies: z.ZodOptional<z.ZodArray<z.ZodObject<{
125
+ name: z.ZodString;
126
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
127
+ priority: z.ZodOptional<z.ZodNumber>;
128
+ }, z.core.$strip>>>;
129
+ routes: z.ZodArray<z.ZodLazy<z.ZodObject<{
130
+ path: z.ZodString;
131
+ methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
132
+ POST: "POST";
133
+ GET: "GET";
134
+ PUT: "PUT";
135
+ PATCH: "PATCH";
136
+ DELETE: "DELETE";
137
+ HEAD: "HEAD";
138
+ OPTIONS: "OPTIONS";
139
+ }>>>;
140
+ pipeline: z.ZodObject<{
141
+ policies: z.ZodOptional<z.ZodArray<z.ZodObject<{
142
+ name: z.ZodString;
143
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
144
+ priority: z.ZodOptional<z.ZodNumber>;
145
+ }, z.core.$strip>>>;
146
+ upstream: z.ZodDiscriminatedUnion<[z.ZodObject<{
147
+ type: z.ZodLiteral<"url">;
148
+ target: z.ZodString;
149
+ rewritePath: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
150
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
151
+ }, z.core.$strip>, z.ZodObject<{
152
+ type: z.ZodLiteral<"service-binding">;
153
+ service: z.ZodString;
154
+ rewritePath: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
155
+ }, z.core.$strip>, z.ZodObject<{
156
+ type: z.ZodLiteral<"handler">;
157
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
158
+ }, z.core.$strip>], "type">;
159
+ }, z.core.$strip>;
160
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
161
+ }, z.core.$strip>>>;
162
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
163
+ }, z.core.$strip>;
164
+ declare const GatewayConfigSchema: z.ZodObject<{
165
+ name: z.ZodOptional<z.ZodString>;
166
+ basePath: z.ZodOptional<z.ZodString>;
167
+ routes: z.ZodArray<z.ZodObject<{
168
+ path: z.ZodString;
169
+ methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
170
+ POST: "POST";
171
+ GET: "GET";
172
+ PUT: "PUT";
173
+ PATCH: "PATCH";
174
+ DELETE: "DELETE";
175
+ HEAD: "HEAD";
176
+ OPTIONS: "OPTIONS";
177
+ }>>>;
178
+ pipeline: z.ZodObject<{
179
+ policies: z.ZodOptional<z.ZodArray<z.ZodObject<{
180
+ name: z.ZodString;
181
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
182
+ priority: z.ZodOptional<z.ZodNumber>;
183
+ }, z.core.$strip>>>;
184
+ upstream: z.ZodDiscriminatedUnion<[z.ZodObject<{
185
+ type: z.ZodLiteral<"url">;
186
+ target: z.ZodString;
187
+ rewritePath: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
188
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
189
+ }, z.core.$strip>, z.ZodObject<{
190
+ type: z.ZodLiteral<"service-binding">;
191
+ service: z.ZodString;
192
+ rewritePath: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
193
+ }, z.core.$strip>, z.ZodObject<{
194
+ type: z.ZodLiteral<"handler">;
195
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
196
+ }, z.core.$strip>], "type">;
197
+ }, z.core.$strip>;
198
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
199
+ }, z.core.$strip>>;
200
+ policies: z.ZodOptional<z.ZodArray<z.ZodObject<{
201
+ name: z.ZodString;
202
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
203
+ priority: z.ZodOptional<z.ZodNumber>;
204
+ }, z.core.$strip>>>;
205
+ onError: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
206
+ debug: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
207
+ requestIdHeader: z.ZodOptional<z.ZodString>;
208
+ defaultMethods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
209
+ POST: "POST";
210
+ GET: "GET";
211
+ PUT: "PUT";
212
+ PATCH: "PATCH";
213
+ DELETE: "DELETE";
214
+ HEAD: "HEAD";
215
+ OPTIONS: "OPTIONS";
216
+ }>>>;
217
+ defaultErrorMessage: z.ZodOptional<z.ZodString>;
218
+ defaultPolicyPriority: z.ZodOptional<z.ZodNumber>;
219
+ admin: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
220
+ enabled: z.ZodBoolean;
221
+ prefix: z.ZodOptional<z.ZodString>;
222
+ auth: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
223
+ metrics: z.ZodOptional<z.ZodAny>;
224
+ }, z.core.$strip>]>>;
225
+ debugHeaders: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
226
+ requestHeader: z.ZodOptional<z.ZodString>;
227
+ allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
228
+ }, z.core.$strip>]>>;
229
+ tracing: z.ZodOptional<z.ZodObject<{
230
+ exporter: z.ZodAny;
231
+ serviceName: z.ZodOptional<z.ZodString>;
232
+ serviceVersion: z.ZodOptional<z.ZodString>;
233
+ sampleRate: z.ZodOptional<z.ZodNumber>;
234
+ }, z.core.$strip>>;
235
+ }, z.core.$strip>;
236
+
237
+ /**
238
+ * Validate a gateway config object, throwing on failure.
239
+ *
240
+ * @throws {z.ZodError} with readable error messages
241
+ */
242
+ declare function validateConfig(config: unknown): GatewayConfig;
243
+ /**
244
+ * Safely validate a gateway config, returning success/error without throwing.
245
+ */
246
+ declare function safeValidateConfig(config: unknown): {
247
+ success: true;
248
+ data: GatewayConfig;
249
+ } | {
250
+ success: false;
251
+ error: z.ZodError;
252
+ };
253
+
254
+ export { GatewayConfigSchema, HttpMethodSchema, PipelineSchema, PolicySchema, RouteSchema, ScopeConfigSchema, TracingConfigSchema, UpstreamSchema, safeValidateConfig, validateConfig };
@@ -0,0 +1,109 @@
1
+ import { z } from "zod";
2
+ const PolicySchema = z.object({
3
+ name: z.string().min(1, "Policy name is required"),
4
+ handler: z.function(),
5
+ priority: z.number().int().optional()
6
+ });
7
+ const UrlUpstreamSchema = z.object({
8
+ type: z.literal("url"),
9
+ target: z.string().url("Upstream target must be a valid URL"),
10
+ rewritePath: z.function().optional(),
11
+ headers: z.record(z.string(), z.string()).optional()
12
+ });
13
+ const ServiceBindingUpstreamSchema = z.object({
14
+ type: z.literal("service-binding"),
15
+ service: z.string().min(1, "Service binding name is required"),
16
+ rewritePath: z.function().optional()
17
+ });
18
+ const HandlerUpstreamSchema = z.object({
19
+ type: z.literal("handler"),
20
+ handler: z.function()
21
+ });
22
+ const UpstreamSchema = z.discriminatedUnion("type", [
23
+ UrlUpstreamSchema,
24
+ ServiceBindingUpstreamSchema,
25
+ HandlerUpstreamSchema
26
+ ]);
27
+ const PipelineSchema = z.object({
28
+ policies: z.array(PolicySchema).optional(),
29
+ upstream: UpstreamSchema
30
+ });
31
+ const HttpMethodSchema = z.enum([
32
+ "GET",
33
+ "POST",
34
+ "PUT",
35
+ "PATCH",
36
+ "DELETE",
37
+ "HEAD",
38
+ "OPTIONS"
39
+ ]);
40
+ const RouteSchema = z.object({
41
+ path: z.string().startsWith("/", "Route path must start with /"),
42
+ methods: z.array(HttpMethodSchema).optional(),
43
+ pipeline: PipelineSchema,
44
+ metadata: z.record(z.string(), z.unknown()).optional()
45
+ });
46
+ const TracingConfigSchema = z.object({
47
+ exporter: z.any(),
48
+ serviceName: z.string().optional(),
49
+ serviceVersion: z.string().optional(),
50
+ sampleRate: z.number().min(0).max(1).optional()
51
+ }).optional();
52
+ const ScopeConfigSchema = z.object({
53
+ prefix: z.string().min(1, "Scope prefix is required"),
54
+ policies: z.array(PolicySchema).optional(),
55
+ routes: z.array(z.lazy(() => RouteSchema)).min(1, "Scope requires at least one route"),
56
+ metadata: z.record(z.string(), z.unknown()).optional()
57
+ });
58
+ const GatewayConfigSchema = z.object({
59
+ name: z.string().optional(),
60
+ basePath: z.string().optional(),
61
+ routes: z.array(RouteSchema).min(1, "At least one route is required"),
62
+ policies: z.array(PolicySchema).optional(),
63
+ onError: z.function().optional(),
64
+ debug: z.union([z.boolean(), z.string()]).optional(),
65
+ requestIdHeader: z.string().optional(),
66
+ defaultMethods: z.array(HttpMethodSchema).optional(),
67
+ defaultErrorMessage: z.string().optional(),
68
+ defaultPolicyPriority: z.number().int().optional(),
69
+ admin: z.union([
70
+ z.boolean(),
71
+ z.object({
72
+ enabled: z.boolean(),
73
+ prefix: z.string().optional(),
74
+ auth: z.function().optional(),
75
+ metrics: z.any().optional()
76
+ })
77
+ ]).optional(),
78
+ debugHeaders: z.union([
79
+ z.boolean(),
80
+ z.object({
81
+ requestHeader: z.string().optional(),
82
+ allow: z.array(z.string()).optional()
83
+ })
84
+ ]).optional(),
85
+ tracing: TracingConfigSchema
86
+ });
87
+ function validateConfig(config) {
88
+ return GatewayConfigSchema.parse(config);
89
+ }
90
+ function safeValidateConfig(config) {
91
+ const result = GatewayConfigSchema.safeParse(config);
92
+ if (result.success) {
93
+ return { success: true, data: result.data };
94
+ }
95
+ return { success: false, error: result.error };
96
+ }
97
+ export {
98
+ GatewayConfigSchema,
99
+ HttpMethodSchema,
100
+ PipelineSchema,
101
+ PolicySchema,
102
+ RouteSchema,
103
+ ScopeConfigSchema,
104
+ TracingConfigSchema,
105
+ UpstreamSchema,
106
+ safeValidateConfig,
107
+ validateConfig
108
+ };
109
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/config/schema.ts"],"sourcesContent":["/**\n * Zod schemas for gateway configuration validation.\n *\n * These schemas mirror the TypeScript types in `core/types.ts` and can be used\n * to validate untrusted configuration objects at runtime (e.g. config loaded\n * from KV, environment variables, or external sources).\n *\n * **Scope**: Validates the structural shape of `GatewayConfig` (routes, upstream\n * types, methods, admin config). Individual policy configs are **not** validated -\n * each policy validates its own config internally via `resolveConfig()`. This\n * means `validateConfig()` catches structural misconfigurations (missing routes,\n * invalid upstream type) but won't catch typos in policy-specific options.\n *\n * Zod is an **optional** peer dependency. Consumers who only use the static\n * TypeScript types never pay for it.\n *\n * @module config/schema\n */\nimport { z } from \"zod\";\nimport type { GatewayConfig } from \"../core/types\";\n\n// ---------------------------------------------------------------------------\n// Building blocks\n// ---------------------------------------------------------------------------\n\n/**\n * Validates the Policy shape (name + handler + priority).\n *\n * This only checks that a policy _looks_ like a Policy - it does not validate\n * the policy's own config options. Policy-specific validation happens inside\n * each policy factory via `resolveConfig()` at construction time.\n */\nconst PolicySchema = z.object({\n name: z.string().min(1, \"Policy name is required\"),\n handler: z.function(),\n priority: z.number().int().optional(),\n});\n\nconst UrlUpstreamSchema = z.object({\n type: z.literal(\"url\"),\n target: z.string().url(\"Upstream target must be a valid URL\"),\n rewritePath: z.function().optional(),\n headers: z.record(z.string(), z.string()).optional(),\n});\n\nconst ServiceBindingUpstreamSchema = z.object({\n type: z.literal(\"service-binding\"),\n service: z.string().min(1, \"Service binding name is required\"),\n rewritePath: z.function().optional(),\n});\n\nconst HandlerUpstreamSchema = z.object({\n type: z.literal(\"handler\"),\n handler: z.function(),\n});\n\nconst UpstreamSchema = z.discriminatedUnion(\"type\", [\n UrlUpstreamSchema,\n ServiceBindingUpstreamSchema,\n HandlerUpstreamSchema,\n]);\n\nconst PipelineSchema = z.object({\n policies: z.array(PolicySchema).optional(),\n upstream: UpstreamSchema,\n});\n\nconst HttpMethodSchema = z.enum([\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"HEAD\",\n \"OPTIONS\",\n]);\n\nconst RouteSchema = z.object({\n path: z.string().startsWith(\"/\", \"Route path must start with /\"),\n methods: z.array(HttpMethodSchema).optional(),\n pipeline: PipelineSchema,\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst TracingConfigSchema = z\n .object({\n exporter: z.any(),\n serviceName: z.string().optional(),\n serviceVersion: z.string().optional(),\n sampleRate: z.number().min(0).max(1).optional(),\n })\n .optional();\n\nconst ScopeConfigSchema = z.object({\n prefix: z.string().min(1, \"Scope prefix is required\"),\n policies: z.array(PolicySchema).optional(),\n routes: z\n .array(z.lazy(() => RouteSchema))\n .min(1, \"Scope requires at least one route\"),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst GatewayConfigSchema = z.object({\n name: z.string().optional(),\n basePath: z.string().optional(),\n routes: z.array(RouteSchema).min(1, \"At least one route is required\"),\n policies: z.array(PolicySchema).optional(),\n onError: z.function().optional(),\n debug: z.union([z.boolean(), z.string()]).optional(),\n requestIdHeader: z.string().optional(),\n defaultMethods: z.array(HttpMethodSchema).optional(),\n defaultErrorMessage: z.string().optional(),\n defaultPolicyPriority: z.number().int().optional(),\n admin: z\n .union([\n z.boolean(),\n z.object({\n enabled: z.boolean(),\n prefix: z.string().optional(),\n auth: z.function().optional(),\n metrics: z.any().optional(),\n }),\n ])\n .optional(),\n debugHeaders: z\n .union([\n z.boolean(),\n z.object({\n requestHeader: z.string().optional(),\n allow: z.array(z.string()).optional(),\n }),\n ])\n .optional(),\n tracing: TracingConfigSchema,\n});\n\n// ---------------------------------------------------------------------------\n// Exports - schemas\n// ---------------------------------------------------------------------------\n\nexport {\n GatewayConfigSchema,\n RouteSchema,\n PipelineSchema,\n UpstreamSchema,\n PolicySchema,\n HttpMethodSchema,\n TracingConfigSchema,\n ScopeConfigSchema,\n};\n\n// ---------------------------------------------------------------------------\n// Validation helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Validate a gateway config object, throwing on failure.\n *\n * @throws {z.ZodError} with readable error messages\n */\nexport function validateConfig(config: unknown): GatewayConfig {\n return GatewayConfigSchema.parse(config) as GatewayConfig;\n}\n\n/**\n * Safely validate a gateway config, returning success/error without throwing.\n */\nexport function safeValidateConfig(\n config: unknown\n):\n | { success: true; data: GatewayConfig }\n | { success: false; error: z.ZodError } {\n const result = GatewayConfigSchema.safeParse(config);\n if (result.success) {\n return { success: true, data: result.data as GatewayConfig };\n }\n return { success: false, error: result.error };\n}\n"],"mappings":"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","names":[]}