@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.
- package/CHANGELOG.md +196 -0
- package/LICENSE +21 -0
- package/README.md +325 -0
- package/dist/adapters/bun.d.ts +9 -0
- package/dist/adapters/bun.js +8 -0
- package/dist/adapters/bun.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +49 -0
- package/dist/adapters/cloudflare.js +85 -0
- package/dist/adapters/cloudflare.js.map +1 -0
- package/dist/adapters/deno.d.ts +9 -0
- package/dist/adapters/deno.js +8 -0
- package/dist/adapters/deno.js.map +1 -0
- package/dist/adapters/durable-object.d.ts +63 -0
- package/dist/adapters/durable-object.js +46 -0
- package/dist/adapters/durable-object.js.map +1 -0
- package/dist/adapters/index.d.ts +13 -0
- package/dist/adapters/index.js +53 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/memory.d.ts +9 -0
- package/dist/adapters/memory.js +14 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/node.d.ts +9 -0
- package/dist/adapters/node.js +8 -0
- package/dist/adapters/node.js.map +1 -0
- package/dist/adapters/postgres.d.ts +109 -0
- package/dist/adapters/postgres.js +242 -0
- package/dist/adapters/postgres.js.map +1 -0
- package/dist/adapters/redis.d.ts +116 -0
- package/dist/adapters/redis.js +194 -0
- package/dist/adapters/redis.js.map +1 -0
- package/dist/adapters/testing.d.ts +32 -0
- package/dist/adapters/testing.js +33 -0
- package/dist/adapters/testing.js.map +1 -0
- package/dist/adapters/types.d.ts +4 -0
- package/dist/adapters/types.js +1 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/config/index.d.ts +11 -0
- package/dist/config/index.js +21 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/merge.d.ts +48 -0
- package/dist/config/merge.js +83 -0
- package/dist/config/merge.js.map +1 -0
- package/dist/config/schema.d.ts +254 -0
- package/dist/config/schema.js +109 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/core/errors.d.ts +66 -0
- package/dist/core/errors.js +47 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/gateway.d.ts +44 -0
- package/dist/core/gateway.js +400 -0
- package/dist/core/gateway.js.map +1 -0
- package/dist/core/health.d.ts +78 -0
- package/dist/core/health.js +65 -0
- package/dist/core/health.js.map +1 -0
- package/dist/core/pipeline.d.ts +62 -0
- package/dist/core/pipeline.js +214 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/protocol.d.ts +4 -0
- package/dist/core/protocol.js +1 -0
- package/dist/core/protocol.js.map +1 -0
- package/dist/core/scope.d.ts +67 -0
- package/dist/core/scope.js +44 -0
- package/dist/core/scope.js.map +1 -0
- package/dist/core/types.d.ts +252 -0
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +158 -0
- package/dist/index.js.map +1 -0
- package/dist/observability/admin.d.ts +32 -0
- package/dist/observability/admin.js +85 -0
- package/dist/observability/admin.js.map +1 -0
- package/dist/observability/metrics.d.ts +78 -0
- package/dist/observability/metrics.js +107 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/tracing.d.ts +149 -0
- package/dist/observability/tracing.js +191 -0
- package/dist/observability/tracing.js.map +1 -0
- package/dist/policies/auth/api-key-auth.d.ts +64 -0
- package/dist/policies/auth/api-key-auth.js +93 -0
- package/dist/policies/auth/api-key-auth.js.map +1 -0
- package/dist/policies/auth/basic-auth.d.ts +33 -0
- package/dist/policies/auth/basic-auth.js +96 -0
- package/dist/policies/auth/basic-auth.js.map +1 -0
- package/dist/policies/auth/crypto.d.ts +29 -0
- package/dist/policies/auth/crypto.js +100 -0
- package/dist/policies/auth/crypto.js.map +1 -0
- package/dist/policies/auth/generate-http-signature.d.ts +30 -0
- package/dist/policies/auth/generate-http-signature.js +79 -0
- package/dist/policies/auth/generate-http-signature.js.map +1 -0
- package/dist/policies/auth/generate-jwt.d.ts +44 -0
- package/dist/policies/auth/generate-jwt.js +99 -0
- package/dist/policies/auth/generate-jwt.js.map +1 -0
- package/dist/policies/auth/http-signature-base.d.ts +55 -0
- package/dist/policies/auth/http-signature-base.js +140 -0
- package/dist/policies/auth/http-signature-base.js.map +1 -0
- package/dist/policies/auth/jws.d.ts +46 -0
- package/dist/policies/auth/jws.js +317 -0
- package/dist/policies/auth/jws.js.map +1 -0
- package/dist/policies/auth/jwt-auth.d.ts +64 -0
- package/dist/policies/auth/jwt-auth.js +266 -0
- package/dist/policies/auth/jwt-auth.js.map +1 -0
- package/dist/policies/auth/oauth2.d.ts +38 -0
- package/dist/policies/auth/oauth2.js +254 -0
- package/dist/policies/auth/oauth2.js.map +1 -0
- package/dist/policies/auth/rbac.d.ts +30 -0
- package/dist/policies/auth/rbac.js +115 -0
- package/dist/policies/auth/rbac.js.map +1 -0
- package/dist/policies/auth/verify-http-signature.d.ts +30 -0
- package/dist/policies/auth/verify-http-signature.js +147 -0
- package/dist/policies/auth/verify-http-signature.js.map +1 -0
- package/dist/policies/index.d.ts +51 -0
- package/dist/policies/index.js +109 -0
- package/dist/policies/index.js.map +1 -0
- package/dist/policies/mock.d.ts +60 -0
- package/dist/policies/mock.js +29 -0
- package/dist/policies/mock.js.map +1 -0
- package/dist/policies/observability/assign-metrics.d.ts +37 -0
- package/dist/policies/observability/assign-metrics.js +29 -0
- package/dist/policies/observability/assign-metrics.js.map +1 -0
- package/dist/policies/observability/metrics-reporter.d.ts +25 -0
- package/dist/policies/observability/metrics-reporter.js +62 -0
- package/dist/policies/observability/metrics-reporter.js.map +1 -0
- package/dist/policies/observability/request-log.d.ts +135 -0
- package/dist/policies/observability/request-log.js +134 -0
- package/dist/policies/observability/request-log.js.map +1 -0
- package/dist/policies/observability/server-timing.d.ts +35 -0
- package/dist/policies/observability/server-timing.js +89 -0
- package/dist/policies/observability/server-timing.js.map +1 -0
- package/dist/policies/proxy.d.ts +59 -0
- package/dist/policies/proxy.js +47 -0
- package/dist/policies/proxy.js.map +1 -0
- package/dist/policies/resilience/circuit-breaker.d.ts +4 -0
- package/dist/policies/resilience/circuit-breaker.js +280 -0
- package/dist/policies/resilience/circuit-breaker.js.map +1 -0
- package/dist/policies/resilience/latency-injection.d.ts +35 -0
- package/dist/policies/resilience/latency-injection.js +26 -0
- package/dist/policies/resilience/latency-injection.js.map +1 -0
- package/dist/policies/resilience/retry.d.ts +71 -0
- package/dist/policies/resilience/retry.js +79 -0
- package/dist/policies/resilience/retry.js.map +1 -0
- package/dist/policies/resilience/timeout.d.ts +32 -0
- package/dist/policies/resilience/timeout.js +46 -0
- package/dist/policies/resilience/timeout.js.map +1 -0
- package/dist/policies/sdk/define-policy.d.ts +176 -0
- package/dist/policies/sdk/define-policy.js +42 -0
- package/dist/policies/sdk/define-policy.js.map +1 -0
- package/dist/policies/sdk/helpers.d.ts +132 -0
- package/dist/policies/sdk/helpers.js +87 -0
- package/dist/policies/sdk/helpers.js.map +1 -0
- package/dist/policies/sdk/index.d.ts +10 -0
- package/dist/policies/sdk/index.js +35 -0
- package/dist/policies/sdk/index.js.map +1 -0
- package/dist/policies/sdk/priority.d.ts +44 -0
- package/dist/policies/sdk/priority.js +36 -0
- package/dist/policies/sdk/priority.js.map +1 -0
- package/dist/policies/sdk/testing.d.ts +53 -0
- package/dist/policies/sdk/testing.js +41 -0
- package/dist/policies/sdk/testing.js.map +1 -0
- package/dist/policies/sdk/trace.d.ts +73 -0
- package/dist/policies/sdk/trace.js +25 -0
- package/dist/policies/sdk/trace.js.map +1 -0
- package/dist/policies/traffic/cache.d.ts +4 -0
- package/dist/policies/traffic/cache.js +224 -0
- package/dist/policies/traffic/cache.js.map +1 -0
- package/dist/policies/traffic/dynamic-routing.d.ts +54 -0
- package/dist/policies/traffic/dynamic-routing.js +36 -0
- package/dist/policies/traffic/dynamic-routing.js.map +1 -0
- package/dist/policies/traffic/geo-ip-filter.d.ts +37 -0
- package/dist/policies/traffic/geo-ip-filter.js +74 -0
- package/dist/policies/traffic/geo-ip-filter.js.map +1 -0
- package/dist/policies/traffic/http-callout.d.ts +59 -0
- package/dist/policies/traffic/http-callout.js +69 -0
- package/dist/policies/traffic/http-callout.js.map +1 -0
- package/dist/policies/traffic/interrupt.d.ts +46 -0
- package/dist/policies/traffic/interrupt.js +38 -0
- package/dist/policies/traffic/interrupt.js.map +1 -0
- package/dist/policies/traffic/ip-filter.d.ts +47 -0
- package/dist/policies/traffic/ip-filter.js +57 -0
- package/dist/policies/traffic/ip-filter.js.map +1 -0
- package/dist/policies/traffic/json-threat-protection.d.ts +51 -0
- package/dist/policies/traffic/json-threat-protection.js +173 -0
- package/dist/policies/traffic/json-threat-protection.js.map +1 -0
- package/dist/policies/traffic/rate-limit.d.ts +4 -0
- package/dist/policies/traffic/rate-limit.js +145 -0
- package/dist/policies/traffic/rate-limit.js.map +1 -0
- package/dist/policies/traffic/regex-threat-protection.d.ts +54 -0
- package/dist/policies/traffic/regex-threat-protection.js +109 -0
- package/dist/policies/traffic/regex-threat-protection.js.map +1 -0
- package/dist/policies/traffic/request-limit.d.ts +27 -0
- package/dist/policies/traffic/request-limit.js +41 -0
- package/dist/policies/traffic/request-limit.js.map +1 -0
- package/dist/policies/traffic/resource-filter.d.ts +38 -0
- package/dist/policies/traffic/resource-filter.js +184 -0
- package/dist/policies/traffic/resource-filter.js.map +1 -0
- package/dist/policies/traffic/ssl-enforce.d.ts +27 -0
- package/dist/policies/traffic/ssl-enforce.js +38 -0
- package/dist/policies/traffic/ssl-enforce.js.map +1 -0
- package/dist/policies/traffic/traffic-shadow.d.ts +40 -0
- package/dist/policies/traffic/traffic-shadow.js +87 -0
- package/dist/policies/traffic/traffic-shadow.js.map +1 -0
- package/dist/policies/transform/assign-attributes.d.ts +33 -0
- package/dist/policies/transform/assign-attributes.js +38 -0
- package/dist/policies/transform/assign-attributes.js.map +1 -0
- package/dist/policies/transform/assign-content.d.ts +40 -0
- package/dist/policies/transform/assign-content.js +185 -0
- package/dist/policies/transform/assign-content.js.map +1 -0
- package/dist/policies/transform/cors.d.ts +57 -0
- package/dist/policies/transform/cors.js +23 -0
- package/dist/policies/transform/cors.js.map +1 -0
- package/dist/policies/transform/json-validation.d.ts +50 -0
- package/dist/policies/transform/json-validation.js +125 -0
- package/dist/policies/transform/json-validation.js.map +1 -0
- package/dist/policies/transform/override-method.d.ts +33 -0
- package/dist/policies/transform/override-method.js +48 -0
- package/dist/policies/transform/override-method.js.map +1 -0
- package/dist/policies/transform/request-validation.d.ts +59 -0
- package/dist/policies/transform/request-validation.js +121 -0
- package/dist/policies/transform/request-validation.js.map +1 -0
- package/dist/policies/transform/transform.d.ts +75 -0
- package/dist/policies/transform/transform.js +116 -0
- package/dist/policies/transform/transform.js.map +1 -0
- package/dist/policies/types.d.ts +4 -0
- package/dist/policies/types.js +1 -0
- package/dist/policies/types.js.map +1 -0
- package/dist/protocol-2fD3DJrL.d.ts +725 -0
- package/dist/utils/cidr.d.ts +58 -0
- package/dist/utils/cidr.js +107 -0
- package/dist/utils/cidr.js.map +1 -0
- package/dist/utils/debug.d.ts +1 -0
- package/dist/utils/debug.js +13 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/headers.d.ts +68 -0
- package/dist/utils/headers.js +25 -0
- package/dist/utils/headers.js.map +1 -0
- package/dist/utils/ip.d.ts +64 -0
- package/dist/utils/ip.js +29 -0
- package/dist/utils/ip.js.map +1 -0
- package/dist/utils/redact.d.ts +30 -0
- package/dist/utils/redact.js +52 -0
- package/dist/utils/redact.js.map +1 -0
- package/dist/utils/request-id.d.ts +11 -0
- package/dist/utils/request-id.js +7 -0
- package/dist/utils/request-id.js.map +1 -0
- package/dist/utils/timing-safe.d.ts +31 -0
- package/dist/utils/timing-safe.js +17 -0
- package/dist/utils/timing-safe.js.map +1 -0
- package/dist/utils/timing.d.ts +27 -0
- package/dist/utils/timing.js +12 -0
- package/dist/utils/timing.js.map +1 -0
- package/dist/utils/trace-context.d.ts +51 -0
- package/dist/utils/trace-context.js +37 -0
- package/dist/utils/trace-context.js.map +1 -0
- package/package.json +213 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { GatewayError } from "../../core/errors";
|
|
2
|
+
import { definePolicy, Priority } from "../sdk";
|
|
3
|
+
import {
|
|
4
|
+
algorithmToCrypto,
|
|
5
|
+
buildSignatureBase,
|
|
6
|
+
fromBase64,
|
|
7
|
+
importVerifyKey,
|
|
8
|
+
parseSignatureParams
|
|
9
|
+
} from "./http-signature-base";
|
|
10
|
+
const verifyHttpSignature = /* @__PURE__ */ definePolicy({
|
|
11
|
+
name: "verify-http-signature",
|
|
12
|
+
priority: Priority.AUTH,
|
|
13
|
+
defaults: {
|
|
14
|
+
requiredComponents: ["@method"],
|
|
15
|
+
maxAge: 300,
|
|
16
|
+
signatureHeaderName: "Signature",
|
|
17
|
+
signatureInputHeaderName: "Signature-Input",
|
|
18
|
+
label: "sig1"
|
|
19
|
+
},
|
|
20
|
+
handler: async (c, next, { config, debug }) => {
|
|
21
|
+
if (!config.keys || Object.keys(config.keys).length === 0) {
|
|
22
|
+
throw new GatewayError(
|
|
23
|
+
500,
|
|
24
|
+
"config_error",
|
|
25
|
+
"verifyHttpSignature requires at least one key in 'keys'"
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
const label = config.label;
|
|
29
|
+
const signatureInputHeader = c.req.header(
|
|
30
|
+
config.signatureInputHeaderName
|
|
31
|
+
);
|
|
32
|
+
if (!signatureInputHeader) {
|
|
33
|
+
throw new GatewayError(
|
|
34
|
+
401,
|
|
35
|
+
"signature_invalid",
|
|
36
|
+
"Missing Signature-Input header"
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const signatureHeader = c.req.header(config.signatureHeaderName);
|
|
40
|
+
if (!signatureHeader) {
|
|
41
|
+
throw new GatewayError(
|
|
42
|
+
401,
|
|
43
|
+
"signature_invalid",
|
|
44
|
+
"Missing Signature header"
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const inputPrefix = `${label}=`;
|
|
48
|
+
if (!signatureInputHeader.startsWith(inputPrefix)) {
|
|
49
|
+
throw new GatewayError(
|
|
50
|
+
401,
|
|
51
|
+
"signature_invalid",
|
|
52
|
+
`Missing signature label "${label}" in Signature-Input header`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const inputValue = signatureInputHeader.slice(inputPrefix.length);
|
|
56
|
+
const { components, params } = parseSignatureParams(inputValue);
|
|
57
|
+
debug(`verifying label=${label}, components=${components.join(",")}`);
|
|
58
|
+
const sigPrefix = `${label}=:`;
|
|
59
|
+
if (!signatureHeader.startsWith(sigPrefix) || !signatureHeader.endsWith(":")) {
|
|
60
|
+
throw new GatewayError(
|
|
61
|
+
401,
|
|
62
|
+
"signature_invalid",
|
|
63
|
+
`Invalid Signature header format for label "${label}"`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
const signatureB64 = signatureHeader.slice(sigPrefix.length, -1);
|
|
67
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
68
|
+
if (params.created) {
|
|
69
|
+
const created = Number.parseInt(params.created, 10);
|
|
70
|
+
if (created + config.maxAge < now) {
|
|
71
|
+
throw new GatewayError(
|
|
72
|
+
401,
|
|
73
|
+
"signature_invalid",
|
|
74
|
+
"Signature has expired (maxAge exceeded)"
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (params.expires) {
|
|
79
|
+
const expires = Number.parseInt(params.expires, 10);
|
|
80
|
+
if (expires < now) {
|
|
81
|
+
throw new GatewayError(
|
|
82
|
+
401,
|
|
83
|
+
"signature_invalid",
|
|
84
|
+
"Signature has expired (expires parameter)"
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const required of config.requiredComponents) {
|
|
89
|
+
if (!components.includes(required)) {
|
|
90
|
+
throw new GatewayError(
|
|
91
|
+
401,
|
|
92
|
+
"signature_invalid",
|
|
93
|
+
`Required component "${required}" not found in signature`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const keyId = params.keyid;
|
|
98
|
+
if (!keyId) {
|
|
99
|
+
throw new GatewayError(
|
|
100
|
+
401,
|
|
101
|
+
"signature_invalid",
|
|
102
|
+
"Missing keyid in signature parameters"
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const keyEntry = config.keys[keyId];
|
|
106
|
+
if (!keyEntry) {
|
|
107
|
+
throw new GatewayError(
|
|
108
|
+
401,
|
|
109
|
+
"signature_invalid",
|
|
110
|
+
"Unknown key identifier"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
const signatureParamsStr = inputValue;
|
|
114
|
+
const signatureBase = buildSignatureBase(
|
|
115
|
+
components,
|
|
116
|
+
signatureParamsStr,
|
|
117
|
+
c.req.raw
|
|
118
|
+
);
|
|
119
|
+
const key = await importVerifyKey(
|
|
120
|
+
keyEntry.algorithm,
|
|
121
|
+
keyEntry.secret,
|
|
122
|
+
keyEntry.publicKey
|
|
123
|
+
);
|
|
124
|
+
const { signAlg } = algorithmToCrypto(keyEntry.algorithm);
|
|
125
|
+
const encoder = new TextEncoder();
|
|
126
|
+
const signatureBytes = fromBase64(signatureB64);
|
|
127
|
+
const valid = await crypto.subtle.verify(
|
|
128
|
+
signAlg,
|
|
129
|
+
key,
|
|
130
|
+
signatureBytes,
|
|
131
|
+
encoder.encode(signatureBase)
|
|
132
|
+
);
|
|
133
|
+
if (!valid) {
|
|
134
|
+
throw new GatewayError(
|
|
135
|
+
401,
|
|
136
|
+
"signature_invalid",
|
|
137
|
+
"Signature verification failed"
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
debug("signature verified successfully");
|
|
141
|
+
await next();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
export {
|
|
145
|
+
verifyHttpSignature
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=verify-http-signature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/policies/auth/verify-http-signature.ts"],"sourcesContent":["/**\n * Verify HTTP Message Signatures per RFC 9421.\n *\n * Validates inbound requests by parsing `Signature` + `Signature-Input` headers,\n * reconstructing the signature base, and verifying the cryptographic signature.\n *\n * @module verify-http-signature\n */\n\nimport { GatewayError } from \"../../core/errors\";\nimport { definePolicy, Priority } from \"../sdk\";\nimport type { PolicyConfig } from \"../types\";\nimport {\n algorithmToCrypto,\n buildSignatureBase,\n fromBase64,\n importVerifyKey,\n parseSignatureParams,\n} from \"./http-signature-base\";\n\nexport interface HttpSignatureKey {\n /** HMAC secret. */\n secret?: string;\n /** RSA public key as JWK. */\n publicKey?: JsonWebKey;\n /** Algorithm identifier. */\n algorithm: string;\n}\n\nexport interface VerifyHttpSignatureConfig extends PolicyConfig {\n /** Map of keyId to key material. */\n keys: Record<string, HttpSignatureKey>;\n /** Components that MUST be in the signature. Default: [\"@method\"]. */\n requiredComponents?: string[];\n /** Max signature age in seconds. Default: 300 (5 min). */\n maxAge?: number;\n /** Signature header name. Default: \"Signature\". */\n signatureHeaderName?: string;\n /** Signature-Input header name. Default: \"Signature-Input\". */\n signatureInputHeaderName?: string;\n /** Expected signature label. Default: \"sig1\". */\n label?: string;\n}\n\nexport const verifyHttpSignature =\n /*#__PURE__*/ definePolicy<VerifyHttpSignatureConfig>({\n name: \"verify-http-signature\",\n priority: Priority.AUTH,\n defaults: {\n requiredComponents: [\"@method\"],\n maxAge: 300,\n signatureHeaderName: \"Signature\",\n signatureInputHeaderName: \"Signature-Input\",\n label: \"sig1\",\n },\n handler: async (c, next, { config, debug }) => {\n // Validate keys at request time\n if (!config.keys || Object.keys(config.keys).length === 0) {\n throw new GatewayError(\n 500,\n \"config_error\",\n \"verifyHttpSignature requires at least one key in 'keys'\"\n );\n }\n\n const label = config.label!;\n\n // 1. Extract Signature-Input header\n const signatureInputHeader = c.req.header(\n config.signatureInputHeaderName!\n );\n if (!signatureInputHeader) {\n throw new GatewayError(\n 401,\n \"signature_invalid\",\n \"Missing Signature-Input header\"\n );\n }\n\n // 2. Extract Signature header\n const signatureHeader = c.req.header(config.signatureHeaderName!);\n if (!signatureHeader) {\n throw new GatewayError(\n 401,\n \"signature_invalid\",\n \"Missing Signature header\"\n );\n }\n\n // 3. Parse the labelled Signature-Input: label=(...);params\n const inputPrefix = `${label}=`;\n if (!signatureInputHeader.startsWith(inputPrefix)) {\n throw new GatewayError(\n 401,\n \"signature_invalid\",\n `Missing signature label \"${label}\" in Signature-Input header`\n );\n }\n const inputValue = signatureInputHeader.slice(inputPrefix.length);\n const { components, params } = parseSignatureParams(inputValue);\n\n debug(`verifying label=${label}, components=${components.join(\",\")}`);\n\n // 4. Parse the labelled Signature: label=:<base64>:\n const sigPrefix = `${label}=:`;\n if (\n !signatureHeader.startsWith(sigPrefix) ||\n !signatureHeader.endsWith(\":\")\n ) {\n throw new GatewayError(\n 401,\n \"signature_invalid\",\n `Invalid Signature header format for label \"${label}\"`\n );\n }\n const signatureB64 = signatureHeader.slice(sigPrefix.length, -1);\n\n // 5. Validate created + maxAge (signature freshness)\n const now = Math.floor(Date.now() / 1000);\n if (params.created) {\n const created = Number.parseInt(params.created, 10);\n if (created + config.maxAge! < now) {\n throw new GatewayError(\n 401,\n \"signature_invalid\",\n \"Signature has expired (maxAge exceeded)\"\n );\n }\n }\n\n // 6. Validate expires if present\n if (params.expires) {\n const expires = Number.parseInt(params.expires, 10);\n if (expires < now) {\n throw new GatewayError(\n 401,\n \"signature_invalid\",\n \"Signature has expired (expires parameter)\"\n );\n }\n }\n\n // 7. Check required components are present\n for (const required of config.requiredComponents!) {\n if (!components.includes(required)) {\n throw new GatewayError(\n 401,\n \"signature_invalid\",\n `Required component \"${required}\" not found in signature`\n );\n }\n }\n\n // 8. Look up key by keyid parameter\n const keyId = params.keyid;\n if (!keyId) {\n throw new GatewayError(\n 401,\n \"signature_invalid\",\n \"Missing keyid in signature parameters\"\n );\n }\n\n const keyEntry = config.keys[keyId];\n if (!keyEntry) {\n throw new GatewayError(\n 401,\n \"signature_invalid\",\n \"Unknown key identifier\"\n );\n }\n\n // 9. Reconstruct the signature base\n const signatureParamsStr = inputValue;\n const signatureBase = buildSignatureBase(\n components,\n signatureParamsStr,\n c.req.raw\n );\n\n // 10. Verify the signature\n const key = await importVerifyKey(\n keyEntry.algorithm,\n keyEntry.secret,\n keyEntry.publicKey\n );\n const { signAlg } = algorithmToCrypto(keyEntry.algorithm);\n const encoder = new TextEncoder();\n const signatureBytes = fromBase64(signatureB64);\n\n const valid = await crypto.subtle.verify(\n signAlg,\n key,\n signatureBytes,\n encoder.encode(signatureBase)\n );\n\n if (!valid) {\n throw new GatewayError(\n 401,\n \"signature_invalid\",\n \"Signature verification failed\"\n );\n }\n\n debug(\"signature verified successfully\");\n await next();\n },\n });\n"],"mappings":"AASA,SAAS,oBAAoB;AAC7B,SAAS,cAAc,gBAAgB;AAEvC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AA0BA,MAAM,sBACG,6BAAwC;AAAA,EACpD,MAAM;AAAA,EACN,UAAU,SAAS;AAAA,EACnB,UAAU;AAAA,IACR,oBAAoB,CAAC,SAAS;AAAA,IAC9B,QAAQ;AAAA,IACR,qBAAqB;AAAA,IACrB,0BAA0B;AAAA,IAC1B,OAAO;AAAA,EACT;AAAA,EACA,SAAS,OAAO,GAAG,MAAM,EAAE,QAAQ,MAAM,MAAM;AAE7C,QAAI,CAAC,OAAO,QAAQ,OAAO,KAAK,OAAO,IAAI,EAAE,WAAW,GAAG;AACzD,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAAQ,OAAO;AAGrB,UAAM,uBAAuB,EAAE,IAAI;AAAA,MACjC,OAAO;AAAA,IACT;AACA,QAAI,CAAC,sBAAsB;AACzB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,UAAM,kBAAkB,EAAE,IAAI,OAAO,OAAO,mBAAoB;AAChE,QAAI,CAAC,iBAAiB;AACpB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,UAAM,cAAc,GAAG,KAAK;AAC5B,QAAI,CAAC,qBAAqB,WAAW,WAAW,GAAG;AACjD,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA,4BAA4B,KAAK;AAAA,MACnC;AAAA,IACF;AACA,UAAM,aAAa,qBAAqB,MAAM,YAAY,MAAM;AAChE,UAAM,EAAE,YAAY,OAAO,IAAI,qBAAqB,UAAU;AAE9D,UAAM,mBAAmB,KAAK,gBAAgB,WAAW,KAAK,GAAG,CAAC,EAAE;AAGpE,UAAM,YAAY,GAAG,KAAK;AAC1B,QACE,CAAC,gBAAgB,WAAW,SAAS,KACrC,CAAC,gBAAgB,SAAS,GAAG,GAC7B;AACA,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA,8CAA8C,KAAK;AAAA,MACrD;AAAA,IACF;AACA,UAAM,eAAe,gBAAgB,MAAM,UAAU,QAAQ,EAAE;AAG/D,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAI,OAAO,SAAS;AAClB,YAAM,UAAU,OAAO,SAAS,OAAO,SAAS,EAAE;AAClD,UAAI,UAAU,OAAO,SAAU,KAAK;AAClC,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,SAAS;AAClB,YAAM,UAAU,OAAO,SAAS,OAAO,SAAS,EAAE;AAClD,UAAI,UAAU,KAAK;AACjB,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,eAAW,YAAY,OAAO,oBAAqB;AACjD,UAAI,CAAC,WAAW,SAAS,QAAQ,GAAG;AAClC,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA,uBAAuB,QAAQ;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAGA,UAAM,QAAQ,OAAO;AACrB,QAAI,CAAC,OAAO;AACV,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,OAAO,KAAK,KAAK;AAClC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,UAAM,qBAAqB;AAC3B,UAAM,gBAAgB;AAAA,MACpB;AAAA,MACA;AAAA,MACA,EAAE,IAAI;AAAA,IACR;AAGA,UAAM,MAAM,MAAM;AAAA,MAChB,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AACA,UAAM,EAAE,QAAQ,IAAI,kBAAkB,SAAS,SAAS;AACxD,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,iBAAiB,WAAW,YAAY;AAE9C,UAAM,QAAQ,MAAM,OAAO,OAAO;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,OAAO,aAAa;AAAA,IAC9B;AAEA,QAAI,CAAC,OAAO;AACV,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,iCAAiC;AACvC,UAAM,KAAK;AAAA,EACb;AACF,CAAC;","names":[]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export { mock } from './mock.js';
|
|
2
|
+
export { proxy } from './proxy.js';
|
|
3
|
+
export { apiKeyAuth } from './auth/api-key-auth.js';
|
|
4
|
+
export { basicAuth } from './auth/basic-auth.js';
|
|
5
|
+
export { GenerateHttpSignatureConfig, generateHttpSignature } from './auth/generate-http-signature.js';
|
|
6
|
+
export { GenerateJwtConfig, generateJwt } from './auth/generate-jwt.js';
|
|
7
|
+
export { JwsConfig, jws } from './auth/jws.js';
|
|
8
|
+
export { JwtAuthConfig, jwtAuth } from './auth/jwt-auth.js';
|
|
9
|
+
export { OAuth2Config, clearOAuth2Cache, oauth2 } from './auth/oauth2.js';
|
|
10
|
+
export { RbacConfig, rbac } from './auth/rbac.js';
|
|
11
|
+
export { HttpSignatureKey, VerifyHttpSignatureConfig, verifyHttpSignature } from './auth/verify-http-signature.js';
|
|
12
|
+
export { A as AttributeMutation, B as BodyMutation, u as CacheConfig, C as CacheStore, v as CircuitBreakerConfig, a as CircuitBreakerSnapshot, b as CircuitBreakerStore, c as CircuitState, H as HeaderMutation, I as InMemoryCacheStore, d as InMemoryCacheStoreOptions, e as InMemoryCircuitBreakerStore, w as InMemoryRateLimitStore, f as InMemoryRateLimitStoreOptions, M as Mutation, P as Policy, g as PolicyConfig, h as PolicyContext, i as PolicyContinue, j as PolicyEvalContext, k as PolicyEvaluator, l as PolicyImmediateResponse, m as PolicyInput, n as PolicyReject, o as PolicyResult, p as ProcessingPhase, q as ProtocolType, x as RateLimitConfig, R as RateLimitStore, S as StatusMutation, r as cache, s as circuitBreaker, t as rateLimit } from '../protocol-2fD3DJrL.js';
|
|
13
|
+
export { DynamicRoutingConfig, RoutingRule, dynamicRouting } from './traffic/dynamic-routing.js';
|
|
14
|
+
export { GeoIpFilterConfig, geoIpFilter } from './traffic/geo-ip-filter.js';
|
|
15
|
+
export { HttpCalloutConfig, httpCallout } from './traffic/http-callout.js';
|
|
16
|
+
export { InterruptConfig, interrupt } from './traffic/interrupt.js';
|
|
17
|
+
export { IpFilterConfig, ipFilter } from './traffic/ip-filter.js';
|
|
18
|
+
export { JsonThreatProtectionConfig, jsonThreatProtection } from './traffic/json-threat-protection.js';
|
|
19
|
+
export { RegexThreatProtectionConfig, regexThreatProtection } from './traffic/regex-threat-protection.js';
|
|
20
|
+
export { RequestLimitConfig, requestLimit } from './traffic/request-limit.js';
|
|
21
|
+
export { ResourceFilterConfig, resourceFilter } from './traffic/resource-filter.js';
|
|
22
|
+
export { SslEnforceConfig, sslEnforce } from './traffic/ssl-enforce.js';
|
|
23
|
+
export { TrafficShadowConfig, trafficShadow } from './traffic/traffic-shadow.js';
|
|
24
|
+
export { LatencyInjectionConfig, latencyInjection } from './resilience/latency-injection.js';
|
|
25
|
+
export { RetryConfig, retry } from './resilience/retry.js';
|
|
26
|
+
export { TimeoutConfig, timeout } from './resilience/timeout.js';
|
|
27
|
+
export { AssignAttributesConfig, assignAttributes } from './transform/assign-attributes.js';
|
|
28
|
+
export { AssignContentConfig, assignContent } from './transform/assign-content.js';
|
|
29
|
+
export { cors } from './transform/cors.js';
|
|
30
|
+
export { JsonValidationConfig, JsonValidationResult, jsonValidation } from './transform/json-validation.js';
|
|
31
|
+
export { OverrideMethodConfig, overrideMethod } from './transform/override-method.js';
|
|
32
|
+
export { RequestValidationConfig, requestValidation } from './transform/request-validation.js';
|
|
33
|
+
export { RequestTransformConfig, ResponseTransformConfig, requestTransform, responseTransform } from './transform/transform.js';
|
|
34
|
+
export { HealthConfig, health } from '../core/health.js';
|
|
35
|
+
export { AssignMetricsConfig, assignMetrics } from './observability/assign-metrics.js';
|
|
36
|
+
export { MetricsReporterConfig, metricsReporter } from './observability/metrics-reporter.js';
|
|
37
|
+
export { LogEntry, RequestLogConfig, requestLog } from './observability/request-log.js';
|
|
38
|
+
export { ServerTimingConfig, ServerTimingVisibility, serverTiming } from './observability/server-timing.js';
|
|
39
|
+
export { Priority, PriorityLevel } from './sdk/priority.js';
|
|
40
|
+
export { policyDebug, resolveConfig, withSkip } from './sdk/helpers.js';
|
|
41
|
+
export { PolicyDefinition, PolicyEvalHandlerContext, PolicyHandlerContext, definePolicy } from './sdk/define-policy.js';
|
|
42
|
+
export { PolicyTestHarnessOptions, createPolicyTestHarness } from './sdk/testing.js';
|
|
43
|
+
export { PolicyTrace, PolicyTraceDetail, PolicyTraceEntry, TraceReporter, policyTrace } from './sdk/trace.js';
|
|
44
|
+
export { clearJwksCache, clearJwksCache as clearJwsJwksCache } from './auth/crypto.js';
|
|
45
|
+
import 'hono';
|
|
46
|
+
import '@vivero/stoma-core';
|
|
47
|
+
import '../core/types.js';
|
|
48
|
+
import '../observability/metrics.js';
|
|
49
|
+
import '../observability/tracing.js';
|
|
50
|
+
import 'hono/types';
|
|
51
|
+
import '../adapters/testing.js';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { mock } from "./mock";
|
|
2
|
+
import { proxy } from "./proxy";
|
|
3
|
+
import { apiKeyAuth } from "./auth/api-key-auth";
|
|
4
|
+
import { basicAuth } from "./auth/basic-auth";
|
|
5
|
+
import { generateHttpSignature } from "./auth/generate-http-signature";
|
|
6
|
+
import { generateJwt } from "./auth/generate-jwt";
|
|
7
|
+
import { clearJwsJwksCache, jws } from "./auth/jws";
|
|
8
|
+
import { clearJwksCache, jwtAuth } from "./auth/jwt-auth";
|
|
9
|
+
import { clearOAuth2Cache, oauth2 } from "./auth/oauth2";
|
|
10
|
+
import { rbac } from "./auth/rbac";
|
|
11
|
+
import { verifyHttpSignature } from "./auth/verify-http-signature";
|
|
12
|
+
import { cache, InMemoryCacheStore } from "./traffic/cache";
|
|
13
|
+
import { dynamicRouting } from "./traffic/dynamic-routing";
|
|
14
|
+
import { geoIpFilter } from "./traffic/geo-ip-filter";
|
|
15
|
+
import { httpCallout } from "./traffic/http-callout";
|
|
16
|
+
import { interrupt } from "./traffic/interrupt";
|
|
17
|
+
import { ipFilter } from "./traffic/ip-filter";
|
|
18
|
+
import { jsonThreatProtection } from "./traffic/json-threat-protection";
|
|
19
|
+
import { InMemoryRateLimitStore, rateLimit } from "./traffic/rate-limit";
|
|
20
|
+
import { regexThreatProtection } from "./traffic/regex-threat-protection";
|
|
21
|
+
import { requestLimit } from "./traffic/request-limit";
|
|
22
|
+
import { resourceFilter } from "./traffic/resource-filter";
|
|
23
|
+
import { sslEnforce } from "./traffic/ssl-enforce";
|
|
24
|
+
import { trafficShadow } from "./traffic/traffic-shadow";
|
|
25
|
+
import {
|
|
26
|
+
circuitBreaker,
|
|
27
|
+
InMemoryCircuitBreakerStore
|
|
28
|
+
} from "./resilience/circuit-breaker";
|
|
29
|
+
import { latencyInjection } from "./resilience/latency-injection";
|
|
30
|
+
import { retry } from "./resilience/retry";
|
|
31
|
+
import { timeout } from "./resilience/timeout";
|
|
32
|
+
import { assignAttributes } from "./transform/assign-attributes";
|
|
33
|
+
import { assignContent } from "./transform/assign-content";
|
|
34
|
+
import { cors } from "./transform/cors";
|
|
35
|
+
import { jsonValidation } from "./transform/json-validation";
|
|
36
|
+
import { overrideMethod } from "./transform/override-method";
|
|
37
|
+
import { requestValidation } from "./transform/request-validation";
|
|
38
|
+
import { requestTransform, responseTransform } from "./transform/transform";
|
|
39
|
+
import { health } from "../core/health";
|
|
40
|
+
import { assignMetrics } from "./observability/assign-metrics";
|
|
41
|
+
import { metricsReporter } from "./observability/metrics-reporter";
|
|
42
|
+
import { requestLog } from "./observability/request-log";
|
|
43
|
+
import { serverTiming } from "./observability/server-timing";
|
|
44
|
+
import {
|
|
45
|
+
createPolicyTestHarness,
|
|
46
|
+
definePolicy,
|
|
47
|
+
Priority,
|
|
48
|
+
policyDebug,
|
|
49
|
+
policyTrace,
|
|
50
|
+
resolveConfig,
|
|
51
|
+
withSkip
|
|
52
|
+
} from "./sdk";
|
|
53
|
+
export {
|
|
54
|
+
InMemoryCacheStore,
|
|
55
|
+
InMemoryCircuitBreakerStore,
|
|
56
|
+
InMemoryRateLimitStore,
|
|
57
|
+
Priority,
|
|
58
|
+
apiKeyAuth,
|
|
59
|
+
assignAttributes,
|
|
60
|
+
assignContent,
|
|
61
|
+
assignMetrics,
|
|
62
|
+
basicAuth,
|
|
63
|
+
cache,
|
|
64
|
+
circuitBreaker,
|
|
65
|
+
clearJwksCache,
|
|
66
|
+
clearJwsJwksCache,
|
|
67
|
+
clearOAuth2Cache,
|
|
68
|
+
cors,
|
|
69
|
+
createPolicyTestHarness,
|
|
70
|
+
definePolicy,
|
|
71
|
+
dynamicRouting,
|
|
72
|
+
generateHttpSignature,
|
|
73
|
+
generateJwt,
|
|
74
|
+
geoIpFilter,
|
|
75
|
+
health,
|
|
76
|
+
httpCallout,
|
|
77
|
+
interrupt,
|
|
78
|
+
ipFilter,
|
|
79
|
+
jsonThreatProtection,
|
|
80
|
+
jsonValidation,
|
|
81
|
+
jws,
|
|
82
|
+
jwtAuth,
|
|
83
|
+
latencyInjection,
|
|
84
|
+
metricsReporter,
|
|
85
|
+
mock,
|
|
86
|
+
oauth2,
|
|
87
|
+
overrideMethod,
|
|
88
|
+
policyDebug,
|
|
89
|
+
policyTrace,
|
|
90
|
+
proxy,
|
|
91
|
+
rateLimit,
|
|
92
|
+
rbac,
|
|
93
|
+
regexThreatProtection,
|
|
94
|
+
requestLimit,
|
|
95
|
+
requestLog,
|
|
96
|
+
requestTransform,
|
|
97
|
+
requestValidation,
|
|
98
|
+
resolveConfig,
|
|
99
|
+
resourceFilter,
|
|
100
|
+
responseTransform,
|
|
101
|
+
retry,
|
|
102
|
+
serverTiming,
|
|
103
|
+
sslEnforce,
|
|
104
|
+
timeout,
|
|
105
|
+
trafficShadow,
|
|
106
|
+
verifyHttpSignature,
|
|
107
|
+
withSkip
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/policies/index.ts"],"sourcesContent":["/**\n * Policy barrel - re-exports all built-in policies, their config types, and the policy SDK.\n *\n * Import from `@vivero/stoma/policies` for direct access to all policies\n * and their configuration types, or from `@vivero/stoma` which re-exports\n * the most common symbols.\n *\n * @module policies\n */\n\n// ── Root-level policies ─────────────────────────────────────────────────\n\n/** Return a static mock response, bypassing the upstream entirely (priority 999). */\nexport { mock } from \"./mock\";\n/** Per-route header manipulation, timeout control, and Host header preservation (priority 95). */\nexport { proxy } from \"./proxy\";\n\n// ── Auth ────────────────────────────────────────────────────────────────\n\n/** Validate API keys from headers or query parameters (priority 10). */\nexport { apiKeyAuth } from \"./auth/api-key-auth\";\n/** Validate HTTP Basic credentials with WWW-Authenticate challenge (priority 10). */\nexport { basicAuth } from \"./auth/basic-auth\";\n/** Generate RFC 9421 HTTP Message Signatures on outbound requests (priority 95). */\nexport { generateHttpSignature } from \"./auth/generate-http-signature\";\n/** Mint JWTs (HMAC or RSA) and attach to outgoing requests (priority 50). */\nexport { generateJwt } from \"./auth/generate-jwt\";\n/** Verify JWS compact serialization signatures - embedded or detached payloads (priority 10). */\nexport { clearJwsJwksCache, jws } from \"./auth/jws\";\n/** Validate JWT bearer tokens via HMAC secret or JWKS endpoint (priority 10). */\nexport { clearJwksCache, jwtAuth } from \"./auth/jwt-auth\";\n/** Validate OAuth2 tokens via RFC 7662 introspection or local validation (priority 10). */\nexport { clearOAuth2Cache, oauth2 } from \"./auth/oauth2\";\n/** Role-based access control using claims forwarded as request headers (priority 10). */\nexport { rbac } from \"./auth/rbac\";\n\n/** Verify RFC 9421 HTTP Message Signatures on inbound requests (priority 10). */\nexport { verifyHttpSignature } from \"./auth/verify-http-signature\";\n\n// ── Traffic ─────────────────────────────────────────────────────────────\n\n/** Response caching with pluggable storage and TTL (priority 40). */\nexport { cache, InMemoryCacheStore } from \"./traffic/cache\";\n/** Evaluate ordered routing rules and expose first match on context (priority 50). */\nexport { dynamicRouting } from \"./traffic/dynamic-routing\";\n\n/** Block or allow requests by geographic country code (priority 1). */\nexport { geoIpFilter } from \"./traffic/geo-ip-filter\";\n/** Make an external HTTP call mid-pipeline for authorization or enrichment (priority 50). */\nexport { httpCallout } from \"./traffic/http-callout\";\n/** Conditionally short-circuit the pipeline and return a static response (priority 100). */\nexport { interrupt } from \"./traffic/interrupt\";\n/** Block or allow requests by client IP address or CIDR range (priority 1). */\nexport { ipFilter } from \"./traffic/ip-filter\";\n\n/** Enforce structural limits on JSON bodies - depth, key count, string length, array size (priority 5). */\nexport { jsonThreatProtection } from \"./traffic/json-threat-protection\";\n/** Sliding-window rate limiting with pluggable counter storage (priority 20). */\nexport { InMemoryRateLimitStore, rateLimit } from \"./traffic/rate-limit\";\n/** Block requests matching regex patterns in path, query, headers, or body (priority 5). */\nexport { regexThreatProtection } from \"./traffic/regex-threat-protection\";\n/** Reject requests whose Content-Length exceeds a byte limit (priority 5). */\nexport { requestLimit } from \"./traffic/request-limit\";\n/** Strip or allow fields from JSON responses using dot-notation paths (priority 92). */\nexport { resourceFilter } from \"./traffic/resource-filter\";\n/** Enforce HTTPS with optional redirect (301) and HSTS header injection (priority 5). */\nexport { sslEnforce } from \"./traffic/ssl-enforce\";\n/** Mirror traffic to a secondary upstream without affecting the primary response (priority 92). */\nexport { trafficShadow } from \"./traffic/traffic-shadow\";\n\n// ── Resilience ──────────────────────────────────────────────────────────\n\n/** Three-state circuit breaker (closed/open/half-open) with pluggable state storage (priority 30). */\nexport {\n circuitBreaker,\n InMemoryCircuitBreakerStore,\n} from \"./resilience/circuit-breaker\";\n/** Inject artificial latency for chaos/resilience testing (priority 5). */\nexport { latencyInjection } from \"./resilience/latency-injection\";\n/** Retry failed upstream calls with exponential or fixed backoff and jitter (priority 90). */\nexport { retry } from \"./resilience/retry\";\n/** Enforce a response time budget - races downstream against a timer (priority 85). */\nexport { timeout } from \"./resilience/timeout\";\n\n// ── Transform ───────────────────────────────────────────────────────────\n\n/** Set key-value attributes on the Hono request context (priority 50). */\nexport { assignAttributes } from \"./transform/assign-attributes\";\n/** Inject or override fields in JSON request/response bodies (priority 50). */\nexport { assignContent } from \"./transform/assign-content\";\n/** Add CORS headers to responses via Hono's built-in CORS middleware (priority 5). */\nexport { cors } from \"./transform/cors\";\n/** Pluggable JSON body validation - wrap Zod, AJV, or any validator (priority 10). */\nexport { jsonValidation } from \"./transform/json-validation\";\n/** Override the HTTP method of POST requests via a header (priority 5). */\nexport { overrideMethod } from \"./transform/override-method\";\n\n/** Pluggable request body validation via user-provided sync/async function (priority 10). */\nexport { requestValidation } from \"./transform/request-validation\";\n/** Modify request headers (set/remove/rename, priority 50) and response headers (priority 92). */\nexport { requestTransform, responseTransform } from \"./transform/transform\";\n\n// ── Observability ───────────────────────────────────────────────────────\n\n/** Create a health check route with optional upstream probing (returns a RouteConfig). */\nexport { health } from \"../core/health\";\n/** Attach metric tags/dimensions to request context for metricsReporter consumption (priority 0). */\nexport { assignMetrics } from \"./observability/assign-metrics\";\n/** Record request counts, latencies, and errors to a pluggable MetricsCollector (priority 1). */\nexport { metricsReporter } from \"./observability/metrics-reporter\";\n/** Structured JSON request logging with trace context, body capture, and redaction (priority 0). */\nexport { requestLog } from \"./observability/request-log\";\n/** Emit W3C Server-Timing and X-Response-Time response headers with per-policy breakdown (priority 1). */\nexport { serverTiming } from \"./observability/server-timing\";\n\n// ── SDK - shared primitives for built-in and custom policies ────────────\n\nexport type {\n /** Declarative policy definition passed to definePolicy. */\n PolicyDefinition,\n /** Context injected into definePolicy evaluate handlers (protocol-agnostic, with typed config). */\n PolicyEvalHandlerContext,\n /** Context injected into definePolicy handlers. */\n PolicyHandlerContext,\n /** Options for createPolicyTestHarness. */\n PolicyTestHarnessOptions,\n /** Full trace payload. */\n PolicyTrace,\n /** Policy-reported trace detail. */\n PolicyTraceDetail,\n /** Combined trace entry (baseline + detail). */\n PolicyTraceEntry,\n /** Union of all named priority level values. */\n PriorityLevel,\n /** Trace reporter function type. */\n TraceReporter,\n} from \"./sdk\";\nexport {\n /** Create a minimal test harness for a policy. */\n createPolicyTestHarness,\n /** Create a policy factory from a declarative definition. */\n definePolicy,\n /** Named priority constants for policy ordering. */\n Priority,\n /** Get a debug logger pre-namespaced to `stoma:policy:{name}`. */\n policyDebug,\n /** Get a trace reporter for a specific policy. */\n policyTrace,\n /** Shallow-merge default config values with user-provided config. */\n resolveConfig,\n /** Wrap a handler with `PolicyConfig.skip` conditional bypass logic. */\n withSkip,\n} from \"./sdk\";\n\n// ── Types ───────────────────────────────────────────────────────────────\n\n/** Configuration for the health route factory. */\nexport type { HealthConfig } from \"../core/health\";\n/** Configuration for the generateHttpSignature policy. */\nexport type { GenerateHttpSignatureConfig } from \"./auth/generate-http-signature\";\n/** Configuration for the generateJwt policy. */\nexport type { GenerateJwtConfig } from \"./auth/generate-jwt\";\n/** Configuration for the jws policy. */\nexport type { JwsConfig } from \"./auth/jws\";\n/** Configuration for the jwtAuth policy. */\nexport type { JwtAuthConfig } from \"./auth/jwt-auth\";\n/** Configuration for the oauth2 policy. */\nexport type { OAuth2Config } from \"./auth/oauth2\";\n/** Configuration for the rbac policy. */\nexport type { RbacConfig } from \"./auth/rbac\";\n/** Configuration for the verifyHttpSignature policy. */\nexport type {\n /** Key material (HMAC secret or RSA public key) for HTTP signature verification. */\n HttpSignatureKey,\n VerifyHttpSignatureConfig,\n} from \"./auth/verify-http-signature\";\n/** Configuration for the assignMetrics policy. */\nexport type { AssignMetricsConfig } from \"./observability/assign-metrics\";\n/** Configuration for the metricsReporter policy. */\nexport type { MetricsReporterConfig } from \"./observability/metrics-reporter\";\n/** Structured log entry emitted by the requestLog policy. */\n/** Configuration for the requestLog policy. */\nexport type { LogEntry, RequestLogConfig } from \"./observability/request-log\";\n/** Configuration for the serverTiming policy. */\nexport type {\n ServerTimingConfig,\n /** Visibility mode for the serverTiming policy. */\n ServerTimingVisibility,\n} from \"./observability/server-timing\";\nexport type {\n /** Configuration for the circuitBreaker policy. */\n CircuitBreakerConfig,\n /** Point-in-time snapshot of a circuit's state and counters. */\n CircuitBreakerSnapshot,\n /** Pluggable storage backend for circuit breaker state. */\n CircuitBreakerStore,\n /** The three circuit breaker states: `\"closed\"`, `\"open\"`, `\"half-open\"`. */\n CircuitState,\n} from \"./resilience/circuit-breaker\";\n/** Configuration for the latencyInjection policy. */\nexport type { LatencyInjectionConfig } from \"./resilience/latency-injection\";\n/** Configuration for the retry policy. */\nexport type { RetryConfig } from \"./resilience/retry\";\n/** Configuration for the timeout policy. */\nexport type { TimeoutConfig } from \"./resilience/timeout\";\n/** Configuration for the cache policy. */\nexport type {\n CacheConfig,\n /** Pluggable cache storage backend. */\n CacheStore,\n /** Options for the in-memory cache store. */\n InMemoryCacheStoreOptions,\n} from \"./traffic/cache\";\n/** Configuration for the dynamicRouting policy. */\nexport type {\n DynamicRoutingConfig,\n /** A single routing rule evaluated by the dynamicRouting policy. */\n RoutingRule,\n} from \"./traffic/dynamic-routing\";\n/** Configuration for the geoIpFilter policy. */\nexport type { GeoIpFilterConfig } from \"./traffic/geo-ip-filter\";\n/** Configuration for the httpCallout policy. */\nexport type { HttpCalloutConfig } from \"./traffic/http-callout\";\n\n/** Configuration for the interrupt policy. */\nexport type { InterruptConfig } from \"./traffic/interrupt\";\n/** Configuration for the ipFilter policy. */\nexport type { IpFilterConfig } from \"./traffic/ip-filter\";\n/** Configuration for the jsonThreatProtection policy. */\nexport type { JsonThreatProtectionConfig } from \"./traffic/json-threat-protection\";\n/** Pluggable storage backend for rate limit counters. */\n/** Configuration for the rateLimit policy. */\nexport type {\n /** Options for the in-memory rate limit store. */\n InMemoryRateLimitStoreOptions,\n RateLimitConfig,\n RateLimitStore,\n} from \"./traffic/rate-limit\";\n/** Configuration for the regexThreatProtection policy. */\nexport type { RegexThreatProtectionConfig } from \"./traffic/regex-threat-protection\";\n/** Configuration for the requestLimit policy. */\nexport type { RequestLimitConfig } from \"./traffic/request-limit\";\n/** Configuration for the resourceFilter policy. */\nexport type { ResourceFilterConfig } from \"./traffic/resource-filter\";\n/** Configuration for the sslEnforce policy. */\nexport type { SslEnforceConfig } from \"./traffic/ssl-enforce\";\n/** Configuration for the trafficShadow policy. */\nexport type { TrafficShadowConfig } from \"./traffic/traffic-shadow\";\n/** Configuration for the assignAttributes policy. */\nexport type { AssignAttributesConfig } from \"./transform/assign-attributes\";\n/** Configuration for the assignContent policy. */\nexport type { AssignContentConfig } from \"./transform/assign-content\";\n/** Configuration for the jsonValidation policy. */\nexport type {\n JsonValidationConfig,\n /** Result shape returned by jsonValidation's user-provided validator. */\n JsonValidationResult,\n} from \"./transform/json-validation\";\n/** Configuration for the overrideMethod policy. */\nexport type { OverrideMethodConfig } from \"./transform/override-method\";\n/** Configuration for the requestValidation policy. */\nexport type { RequestValidationConfig } from \"./transform/request-validation\";\n/** Configuration for requestTransform and responseTransform policies. */\nexport type {\n RequestTransformConfig,\n ResponseTransformConfig,\n} from \"./transform/transform\";\n/** A composable policy: name, priority, Hono middleware handler, and optional protocol-agnostic evaluator. */\nexport type {\n Policy,\n /** Base configuration for all policies, including the `skip` bypass predicate. */\n PolicyConfig,\n /** Per-request gateway context: request ID, trace ID, span ID, timing, and debug factory. */\n PolicyContext,\n} from \"./types\";\n\n// ── Protocol - multi-runtime types ──────────────────────────────────────\n\nexport type {\n AttributeMutation,\n BodyMutation,\n HeaderMutation,\n Mutation,\n PolicyContinue,\n PolicyEvalContext,\n PolicyEvaluator,\n PolicyImmediateResponse,\n PolicyInput,\n PolicyReject,\n PolicyResult,\n ProcessingPhase,\n ProtocolType,\n StatusMutation,\n} from \"../core/protocol\";\n"],"mappings":"AAaA,SAAS,YAAY;AAErB,SAAS,aAAa;AAKtB,SAAS,kBAAkB;AAE3B,SAAS,iBAAiB;AAE1B,SAAS,6BAA6B;AAEtC,SAAS,mBAAmB;AAE5B,SAAS,mBAAmB,WAAW;AAEvC,SAAS,gBAAgB,eAAe;AAExC,SAAS,kBAAkB,cAAc;AAEzC,SAAS,YAAY;AAGrB,SAAS,2BAA2B;AAKpC,SAAS,OAAO,0BAA0B;AAE1C,SAAS,sBAAsB;AAG/B,SAAS,mBAAmB;AAE5B,SAAS,mBAAmB;AAE5B,SAAS,iBAAiB;AAE1B,SAAS,gBAAgB;AAGzB,SAAS,4BAA4B;AAErC,SAAS,wBAAwB,iBAAiB;AAElD,SAAS,6BAA6B;AAEtC,SAAS,oBAAoB;AAE7B,SAAS,sBAAsB;AAE/B,SAAS,kBAAkB;AAE3B,SAAS,qBAAqB;AAK9B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,SAAS,wBAAwB;AAEjC,SAAS,aAAa;AAEtB,SAAS,eAAe;AAKxB,SAAS,wBAAwB;AAEjC,SAAS,qBAAqB;AAE9B,SAAS,YAAY;AAErB,SAAS,sBAAsB;AAE/B,SAAS,sBAAsB;AAG/B,SAAS,yBAAyB;AAElC,SAAS,kBAAkB,yBAAyB;AAKpD,SAAS,cAAc;AAEvB,SAAS,qBAAqB;AAE9B,SAAS,uBAAuB;AAEhC,SAAS,kBAAkB;AAE3B,SAAS,oBAAoB;AAwB7B;AAAA,EAEE;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,OACK;","names":[]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { g as PolicyConfig, P as Policy } from '../protocol-2fD3DJrL.js';
|
|
2
|
+
import 'hono';
|
|
3
|
+
import './sdk/trace.js';
|
|
4
|
+
import '@vivero/stoma-core';
|
|
5
|
+
|
|
6
|
+
interface MockConfig extends PolicyConfig {
|
|
7
|
+
/** HTTP status code to return. Default: 200. */
|
|
8
|
+
status?: number;
|
|
9
|
+
/** Response body. Can be a string or object (will be JSON-serialized). */
|
|
10
|
+
body?: string | Record<string, unknown>;
|
|
11
|
+
/** Response headers. */
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
/** Artificial delay in milliseconds. Default: 0. */
|
|
14
|
+
delayMs?: number;
|
|
15
|
+
/** When `true`, suppress the production usage warning. Default: `false`. */
|
|
16
|
+
allowInProduction?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Return a static mock response, bypassing the upstream entirely.
|
|
20
|
+
*
|
|
21
|
+
* Useful for development stubs, testing, and placeholder routes. Runs at
|
|
22
|
+
* priority 999 (always last) and short-circuits - `next()` is never called,
|
|
23
|
+
* so no upstream request is made. Object bodies are automatically
|
|
24
|
+
* JSON-serialized with `content-type: application/json`.
|
|
25
|
+
*
|
|
26
|
+
* @param config - Status code, response body, headers, and artificial delay. All fields optional.
|
|
27
|
+
* @returns A {@link Policy} at priority 999 (replaces the upstream).
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* import { createGateway } from "@vivero/stoma";
|
|
32
|
+
* import { mock } from "@vivero/stoma/policies";
|
|
33
|
+
*
|
|
34
|
+
* createGateway({
|
|
35
|
+
* routes: [{
|
|
36
|
+
* path: "/api/stub",
|
|
37
|
+
* pipeline: {
|
|
38
|
+
* policies: [
|
|
39
|
+
* // Return a JSON stub with 200ms simulated latency
|
|
40
|
+
* mock({
|
|
41
|
+
* body: { message: "Hello from stub" },
|
|
42
|
+
* delayMs: 200,
|
|
43
|
+
* }),
|
|
44
|
+
* ],
|
|
45
|
+
* upstream: { type: "handler", handler: () => new Response() }, // never reached
|
|
46
|
+
* },
|
|
47
|
+
* }],
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* // Simulate a 503 maintenance page
|
|
51
|
+
* mock({
|
|
52
|
+
* status: 503,
|
|
53
|
+
* body: "Service temporarily unavailable",
|
|
54
|
+
* headers: { "retry-after": "300" },
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare const mock: (config?: MockConfig | undefined) => Policy;
|
|
59
|
+
|
|
60
|
+
export { type MockConfig, mock };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { definePolicy, Priority } from "./sdk";
|
|
2
|
+
const mock = /* @__PURE__ */ definePolicy({
|
|
3
|
+
name: "mock",
|
|
4
|
+
priority: Priority.MOCK,
|
|
5
|
+
httpOnly: true,
|
|
6
|
+
defaults: { status: 200, delayMs: 0 },
|
|
7
|
+
validate: (config) => {
|
|
8
|
+
if (!config.allowInProduction) {
|
|
9
|
+
console.warn(
|
|
10
|
+
"[stoma] mock policy active - intended for development/testing only"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
handler: async (c, _next, { config }) => {
|
|
15
|
+
if (config.delayMs > 0) {
|
|
16
|
+
await new Promise((resolve) => setTimeout(resolve, config.delayMs));
|
|
17
|
+
}
|
|
18
|
+
const body = config.body === void 0 ? null : typeof config.body === "string" ? config.body : JSON.stringify(config.body);
|
|
19
|
+
const headers = new Headers(config.headers);
|
|
20
|
+
if (typeof config.body === "object" && !headers.has("content-type")) {
|
|
21
|
+
headers.set("content-type", "application/json");
|
|
22
|
+
}
|
|
23
|
+
c.res = new Response(body, { status: config.status, headers });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
export {
|
|
27
|
+
mock
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=mock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/policies/mock.ts"],"sourcesContent":["/**\n * Mock policy - return static responses for development and testing.\n *\n * @module mock\n */\n\nimport { definePolicy, Priority } from \"./sdk\";\nimport type { PolicyConfig } from \"./types\";\n\nexport interface MockConfig extends PolicyConfig {\n /** HTTP status code to return. Default: 200. */\n status?: number;\n /** Response body. Can be a string or object (will be JSON-serialized). */\n body?: string | Record<string, unknown>;\n /** Response headers. */\n headers?: Record<string, string>;\n /** Artificial delay in milliseconds. Default: 0. */\n delayMs?: number;\n /** When `true`, suppress the production usage warning. Default: `false`. */\n allowInProduction?: boolean;\n}\n\n/**\n * Return a static mock response, bypassing the upstream entirely.\n *\n * Useful for development stubs, testing, and placeholder routes. Runs at\n * priority 999 (always last) and short-circuits - `next()` is never called,\n * so no upstream request is made. Object bodies are automatically\n * JSON-serialized with `content-type: application/json`.\n *\n * @param config - Status code, response body, headers, and artificial delay. All fields optional.\n * @returns A {@link Policy} at priority 999 (replaces the upstream).\n *\n * @example\n * ```ts\n * import { createGateway } from \"@vivero/stoma\";\n * import { mock } from \"@vivero/stoma/policies\";\n *\n * createGateway({\n * routes: [{\n * path: \"/api/stub\",\n * pipeline: {\n * policies: [\n * // Return a JSON stub with 200ms simulated latency\n * mock({\n * body: { message: \"Hello from stub\" },\n * delayMs: 200,\n * }),\n * ],\n * upstream: { type: \"handler\", handler: () => new Response() }, // never reached\n * },\n * }],\n * });\n *\n * // Simulate a 503 maintenance page\n * mock({\n * status: 503,\n * body: \"Service temporarily unavailable\",\n * headers: { \"retry-after\": \"300\" },\n * });\n * ```\n */\nexport const mock = /*#__PURE__*/ definePolicy<MockConfig>({\n name: \"mock\",\n priority: Priority.MOCK,\n httpOnly: true,\n defaults: { status: 200, delayMs: 0 },\n validate: (config) => {\n if (!config.allowInProduction) {\n console.warn(\n \"[stoma] mock policy active - intended for development/testing only\"\n );\n }\n },\n handler: async (c, _next, { config }) => {\n if (config.delayMs! > 0) {\n await new Promise((resolve) => setTimeout(resolve, config.delayMs!));\n }\n\n const body =\n config.body === undefined\n ? null\n : typeof config.body === \"string\"\n ? config.body\n : JSON.stringify(config.body);\n\n const headers = new Headers(config.headers);\n if (typeof config.body === \"object\" && !headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n\n // Short-circuit: set the response directly, skipping upstream.\n // Middleware that ran before mock (context injector, auth, etc.)\n // already ran their pre-next() code. The context injector adds\n // x-request-id via c.res.headers after next(), which won't fire\n // for mock - but that's acceptable for mock/dev scenarios.\n c.res = new Response(body, { status: config.status!, headers });\n },\n});\n"],"mappings":"AAMA,SAAS,cAAc,gBAAgB;AAwDhC,MAAM,OAAqB,6BAAyB;AAAA,EACzD,MAAM;AAAA,EACN,UAAU,SAAS;AAAA,EACnB,UAAU;AAAA,EACV,UAAU,EAAE,QAAQ,KAAK,SAAS,EAAE;AAAA,EACpC,UAAU,CAAC,WAAW;AACpB,QAAI,CAAC,OAAO,mBAAmB;AAC7B,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAS,OAAO,GAAG,OAAO,EAAE,OAAO,MAAM;AACvC,QAAI,OAAO,UAAW,GAAG;AACvB,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,OAAO,OAAQ,CAAC;AAAA,IACrE;AAEA,UAAM,OACJ,OAAO,SAAS,SACZ,OACA,OAAO,OAAO,SAAS,WACrB,OAAO,OACP,KAAK,UAAU,OAAO,IAAI;AAElC,UAAM,UAAU,IAAI,QAAQ,OAAO,OAAO;AAC1C,QAAI,OAAO,OAAO,SAAS,YAAY,CAAC,QAAQ,IAAI,cAAc,GAAG;AACnE,cAAQ,IAAI,gBAAgB,kBAAkB;AAAA,IAChD;AAOA,MAAE,MAAM,IAAI,SAAS,MAAM,EAAE,QAAQ,OAAO,QAAS,QAAQ,CAAC;AAAA,EAChE;AACF,CAAC;","names":[]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { g as PolicyConfig, P as Policy } from '../../protocol-2fD3DJrL.js';
|
|
2
|
+
import { Context } from 'hono';
|
|
3
|
+
import '../sdk/trace.js';
|
|
4
|
+
import '@vivero/stoma-core';
|
|
5
|
+
|
|
6
|
+
interface AssignMetricsConfig extends PolicyConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Metric tags to attach to the request.
|
|
9
|
+
* Values can be static strings or functions that receive the context.
|
|
10
|
+
*/
|
|
11
|
+
tags: Record<string, string | ((c: Context) => string | Promise<string>)>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Attach metric tags to the request context for downstream consumers.
|
|
15
|
+
*
|
|
16
|
+
* Tags are resolved (static or dynamic) and stored as a plain object at
|
|
17
|
+
* `c.get("_metricsTags")`. The {@link metricsReporter} policy (or any custom
|
|
18
|
+
* observer) can read these tags to enrich collected metrics.
|
|
19
|
+
*
|
|
20
|
+
* @param config - Must include `tags` - a record of tag names to values or resolver functions.
|
|
21
|
+
* @returns A {@link Policy} at priority 0 (OBSERVABILITY).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { assignMetrics } from "@vivero/stoma";
|
|
26
|
+
*
|
|
27
|
+
* assignMetrics({
|
|
28
|
+
* tags: {
|
|
29
|
+
* service: "users-api",
|
|
30
|
+
* region: (c) => c.req.header("cf-ipcountry") ?? "unknown",
|
|
31
|
+
* },
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
declare const assignMetrics: (config: AssignMetricsConfig) => Policy;
|
|
36
|
+
|
|
37
|
+
export { type AssignMetricsConfig, assignMetrics };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { definePolicy, Priority, safeCall } from "../sdk";
|
|
2
|
+
const assignMetrics = /* @__PURE__ */ definePolicy({
|
|
3
|
+
name: "assign-metrics",
|
|
4
|
+
priority: Priority.OBSERVABILITY,
|
|
5
|
+
httpOnly: true,
|
|
6
|
+
handler: async (c, next, { config, debug }) => {
|
|
7
|
+
const resolvedTags = {};
|
|
8
|
+
for (const [key, value] of Object.entries(config.tags)) {
|
|
9
|
+
if (typeof value === "function") {
|
|
10
|
+
resolvedTags[key] = await safeCall(
|
|
11
|
+
() => Promise.resolve(value(c)),
|
|
12
|
+
"unknown",
|
|
13
|
+
debug,
|
|
14
|
+
`tag resolver(${key})`
|
|
15
|
+
);
|
|
16
|
+
debug("tag %s = %s (dynamic)", key, resolvedTags[key]);
|
|
17
|
+
} else {
|
|
18
|
+
resolvedTags[key] = value;
|
|
19
|
+
debug("tag %s = %s (static)", key, value);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
c.set("_metricsTags", resolvedTags);
|
|
23
|
+
await next();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
export {
|
|
27
|
+
assignMetrics
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=assign-metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/policies/observability/assign-metrics.ts"],"sourcesContent":["/**\n * Assign metric tags/dimensions to the request context.\n *\n * Resolved tags are stored at `c.get(\"_metricsTags\")` for consumption by\n * {@link metricsReporter} or custom downstream observers.\n *\n * @module assign-metrics\n */\nimport type { Context } from \"hono\";\nimport { definePolicy, Priority, safeCall } from \"../sdk\";\nimport type { PolicyConfig } from \"../types\";\n\nexport interface AssignMetricsConfig extends PolicyConfig {\n /**\n * Metric tags to attach to the request.\n * Values can be static strings or functions that receive the context.\n */\n tags: Record<string, string | ((c: Context) => string | Promise<string>)>;\n}\n\n/**\n * Attach metric tags to the request context for downstream consumers.\n *\n * Tags are resolved (static or dynamic) and stored as a plain object at\n * `c.get(\"_metricsTags\")`. The {@link metricsReporter} policy (or any custom\n * observer) can read these tags to enrich collected metrics.\n *\n * @param config - Must include `tags` - a record of tag names to values or resolver functions.\n * @returns A {@link Policy} at priority 0 (OBSERVABILITY).\n *\n * @example\n * ```ts\n * import { assignMetrics } from \"@vivero/stoma\";\n *\n * assignMetrics({\n * tags: {\n * service: \"users-api\",\n * region: (c) => c.req.header(\"cf-ipcountry\") ?? \"unknown\",\n * },\n * });\n * ```\n */\nexport const assignMetrics = /*#__PURE__*/ definePolicy<AssignMetricsConfig>({\n name: \"assign-metrics\",\n priority: Priority.OBSERVABILITY,\n httpOnly: true,\n handler: async (c, next, { config, debug }) => {\n const resolvedTags: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(config.tags)) {\n if (typeof value === \"function\") {\n // Tag resolver failure must never prevent the request from proceeding\n resolvedTags[key] = await safeCall(\n () => Promise.resolve(value(c)),\n \"unknown\",\n debug,\n `tag resolver(${key})`\n );\n debug(\"tag %s = %s (dynamic)\", key, resolvedTags[key]);\n } else {\n resolvedTags[key] = value;\n debug(\"tag %s = %s (static)\", key, value);\n }\n }\n\n c.set(\"_metricsTags\", resolvedTags);\n await next();\n },\n});\n"],"mappings":"AASA,SAAS,cAAc,UAAU,gBAAgB;AAiC1C,MAAM,gBAA8B,6BAAkC;AAAA,EAC3E,MAAM;AAAA,EACN,UAAU,SAAS;AAAA,EACnB,UAAU;AAAA,EACV,SAAS,OAAO,GAAG,MAAM,EAAE,QAAQ,MAAM,MAAM;AAC7C,UAAM,eAAuC,CAAC;AAE9C,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,IAAI,GAAG;AACtD,UAAI,OAAO,UAAU,YAAY;AAE/B,qBAAa,GAAG,IAAI,MAAM;AAAA,UACxB,MAAM,QAAQ,QAAQ,MAAM,CAAC,CAAC;AAAA,UAC9B;AAAA,UACA;AAAA,UACA,gBAAgB,GAAG;AAAA,QACrB;AACA,cAAM,yBAAyB,KAAK,aAAa,GAAG,CAAC;AAAA,MACvD,OAAO;AACL,qBAAa,GAAG,IAAI;AACpB,cAAM,wBAAwB,KAAK,KAAK;AAAA,MAC1C;AAAA,IACF;AAEA,MAAE,IAAI,gBAAgB,YAAY;AAClC,UAAM,KAAK;AAAA,EACb;AACF,CAAC;","names":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { g as PolicyConfig, P as Policy } from '../../protocol-2fD3DJrL.js';
|
|
2
|
+
import { MetricsCollector } from '../../observability/metrics.js';
|
|
3
|
+
import 'hono';
|
|
4
|
+
import '../sdk/trace.js';
|
|
5
|
+
import '@vivero/stoma-core';
|
|
6
|
+
|
|
7
|
+
interface MetricsReporterConfig extends PolicyConfig {
|
|
8
|
+
/** The metrics collector to record to. */
|
|
9
|
+
collector: MetricsCollector;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Record standard gateway metrics for every request.
|
|
13
|
+
*
|
|
14
|
+
* Metrics recorded:
|
|
15
|
+
* - `gateway_requests_total` (counter) - total requests, tagged by method/path/status/gateway
|
|
16
|
+
* - `gateway_request_duration_ms` (histogram) - end-to-end request duration
|
|
17
|
+
* - `gateway_request_errors_total` (counter) - requests with status >= 400
|
|
18
|
+
* - `gateway_policy_duration_ms` (histogram) - per-policy timing when available
|
|
19
|
+
*
|
|
20
|
+
* @param config - Must include a {@link MetricsCollector} instance.
|
|
21
|
+
* @returns A {@link Policy} at priority 1.
|
|
22
|
+
*/
|
|
23
|
+
declare const metricsReporter: (config: MetricsReporterConfig) => Policy;
|
|
24
|
+
|
|
25
|
+
export { type MetricsReporterConfig, metricsReporter };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { definePolicy, Priority, safeCall } from "../sdk";
|
|
2
|
+
const metricsReporter = /* @__PURE__ */ definePolicy({
|
|
3
|
+
name: "metrics-reporter",
|
|
4
|
+
priority: Priority.METRICS,
|
|
5
|
+
httpOnly: true,
|
|
6
|
+
handler: async (c, next, { config, debug, gateway }) => {
|
|
7
|
+
const startTime = Date.now();
|
|
8
|
+
await next();
|
|
9
|
+
await safeCall(
|
|
10
|
+
async () => {
|
|
11
|
+
const dynamicTagsRaw = c.get("_metricsTags");
|
|
12
|
+
const dynamicTags = {};
|
|
13
|
+
if (dynamicTagsRaw) {
|
|
14
|
+
for (const [key, value] of Object.entries(dynamicTagsRaw)) {
|
|
15
|
+
if (typeof value === "string") {
|
|
16
|
+
dynamicTags[key] = value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const url = new URL(c.req.url);
|
|
21
|
+
const tags = {
|
|
22
|
+
...dynamicTags,
|
|
23
|
+
method: c.req.method,
|
|
24
|
+
path: gateway?.routePath ?? url.pathname,
|
|
25
|
+
status: String(c.res.status),
|
|
26
|
+
gateway: gateway?.gatewayName ?? "unknown"
|
|
27
|
+
};
|
|
28
|
+
config.collector.increment("gateway_requests_total", 1, tags);
|
|
29
|
+
const duration = Date.now() - startTime;
|
|
30
|
+
config.collector.histogram(
|
|
31
|
+
"gateway_request_duration_ms",
|
|
32
|
+
duration,
|
|
33
|
+
tags
|
|
34
|
+
);
|
|
35
|
+
if (c.res.status >= 400) {
|
|
36
|
+
config.collector.increment("gateway_request_errors_total", 1, tags);
|
|
37
|
+
}
|
|
38
|
+
const timings = c.get("_policyTimings");
|
|
39
|
+
if (timings) {
|
|
40
|
+
for (const t of timings) {
|
|
41
|
+
config.collector.histogram(
|
|
42
|
+
"gateway_policy_duration_ms",
|
|
43
|
+
t.durationMs,
|
|
44
|
+
{
|
|
45
|
+
...dynamicTags,
|
|
46
|
+
policy: t.name,
|
|
47
|
+
gateway: gateway?.gatewayName ?? "unknown"
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
void 0,
|
|
54
|
+
debug,
|
|
55
|
+
"collector"
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
export {
|
|
60
|
+
metricsReporter
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=metrics-reporter.js.map
|