@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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# @vivero/stoma
|
|
2
|
+
|
|
3
|
+
## 0.1.0-rc.10
|
|
4
|
+
### Patch Changes
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
- [`c6f0576`](https://github.com/vivero-dev/stoma/commit/c6f05763d6f2ba3e8a3c6845258d57e6f8c1d693) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - fix: resolve `workspace:*` protocols in published packages
|
|
9
|
+
|
|
10
|
+
Replaces `changeset publish` with a custom publish script (`scripts/publish.mjs`) that uses `yarn pack` to resolve `workspace:*` protocols and apply `publishConfig` overrides, then `npm publish <tarball>` for OIDC trusted publishing with provenance. The prepack/postpack workaround scripts have been removed.
|
|
11
|
+
- Updated dependencies [[`c6f0576`](https://github.com/vivero-dev/stoma/commit/c6f05763d6f2ba3e8a3c6845258d57e6f8c1d693)]:
|
|
12
|
+
- @vivero/stoma-core@0.1.0-rc.3
|
|
13
|
+
|
|
14
|
+
## 0.1.0-rc.9
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
- [`c6f0576`](https://github.com/vivero-dev/stoma/commit/c6f05763d6f2ba3e8a3c6845258d57e6f8c1d693) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - fix: publish via `yarn npm publish` to correctly resolve `workspace:*` protocols
|
|
20
|
+
|
|
21
|
+
Replaces `changeset publish` (which used `npm publish <dir>` and never resolved workspace protocols) with a custom publish script that runs `yarn npm publish` from each package directory. Yarn natively handles `workspace:*` resolution and `publishConfig` field overrides, so the prepack/postpack workaround scripts have been removed.
|
|
22
|
+
- Updated dependencies [[`c6f0576`](https://github.com/vivero-dev/stoma/commit/c6f05763d6f2ba3e8a3c6845258d57e6f8c1d693)]:
|
|
23
|
+
- @vivero/stoma-core@0.1.0-rc.2
|
|
24
|
+
|
|
25
|
+
## 0.1.0-rc.8
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
- [`cfa0a04`](https://github.com/vivero-dev/stoma/commit/cfa0a040eda481782cc34131b2e4b99015e74cea) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - Add CLI test coverage and split monorepo/package READMEs
|
|
31
|
+
|
|
32
|
+
### CLI tests
|
|
33
|
+
|
|
34
|
+
Add 26 tests across 3 files covering the playground wrapper, OAuth relay, gateway resolution security boundary, and TypeScript transpilation end-to-end:
|
|
35
|
+
|
|
36
|
+
- **Playground routing invariants**: `/__playground` returns HTML, `/registry` returns exact JSON, non-playground paths always pass through to the gateway
|
|
37
|
+
- **Send proxy contract**: response shape (`status`, `statusText`, `headers`, `body`, `elapsed`), header forwarding, error handling, body stripping for GET
|
|
38
|
+
- **OAuth relay**: interception only fires for navigation requests to callback routes with query params; XSS prevention via `<` escaping
|
|
39
|
+
- **Security boundary**: remote URLs without `--trust-remote` always reject with security warning
|
|
40
|
+
- **TS transpilation e2e**: loads real `.ts` fixture through esbuild, verifies the gateway's fetch handler produces correct responses
|
|
41
|
+
|
|
42
|
+
### README
|
|
43
|
+
|
|
44
|
+
- Root README is now a monorepo landing page with package table, quick start, and dev/release instructions
|
|
45
|
+
- Gateway package (`packages/gateway`) retains the full library README for npm
|
|
46
|
+
|
|
47
|
+
## 0.1.0-rc.7
|
|
48
|
+
### Patch Changes
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
- [`a414008`](https://github.com/vivero-dev/stoma/commit/a41400882fbe51f9f5ac7f623dec52f0e2ca1dd6) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - Fix npm publish pipeline to correctly resolve workspace protocols and apply publishConfig overrides
|
|
53
|
+
|
|
54
|
+
Adds prepack/postpack lifecycle scripts that prepare package.json for `npm publish` by resolving `workspace:*` to real versions and applying `publishConfig` field overrides (main, types, exports, bin). This replaces the previous `@changesets/cli` yarn patch approach and restores compatibility with GitHub Actions OIDC tokens for npm authentication and provenance.
|
|
55
|
+
- Updated dependencies [[`a414008`](https://github.com/vivero-dev/stoma/commit/a41400882fbe51f9f5ac7f623dec52f0e2ca1dd6)]:
|
|
56
|
+
- @vivero/stoma-core@0.1.0-rc.1
|
|
57
|
+
|
|
58
|
+
## 0.1.0-rc.6
|
|
59
|
+
### Patch Changes
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
- [`c14161a`](https://github.com/vivero-dev/stoma/commit/c14161a0846ef1991bb0fa71337435e6366579a7) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - Fix Content-Encoding/Content-Length mismatch on proxied upstream responses; enrich request logs with upstream target and client IP from socket
|
|
64
|
+
|
|
65
|
+
### Fixes
|
|
66
|
+
|
|
67
|
+
- **Stale encoding headers on URL and Service Binding upstreams**: The runtime's `fetch()` transparently decompresses gzip/deflate/br responses, but the gateway was forwarding the original `Content-Encoding` and `Content-Length` headers unchanged. Downstream clients (browsers, proxies) would then attempt to decompress an already-decoded body, resulting in `ERR_CONTENT_DECODING_FAILED` or `ERR_CONTENT_LENGTH_MISMATCH` errors. Both `createUrlUpstream` and `createServiceBindingUpstream` now strip `content-encoding` and `content-length` from upstream responses before returning them.
|
|
68
|
+
- **`upstream` always "unknown" in request logs**: The `request-log` policy hardcoded `upstream: "unknown"` because no upstream handler was setting the value. All three upstream types (`createUrlUpstream`, `createServiceBindingUpstream`, `createHandlerUpstream`) now set `c.set("_upstreamTarget", identifier)` with the full resolved URL (including rewritten path and query string), `"service-binding:<name>"`, or `"handler"` respectively.
|
|
69
|
+
- **`clientIp` always "unknown" when running locally**: `extractClientIp()` only checked HTTP headers (`cf-connecting-ip`, `x-forwarded-for`), which aren't present when running with `@hono/node-server` locally. Added a `fallbackAddress` option to `extractClientIp()` and a `getRemoteAddress()` helper in `request-log` that duck-types `c.env.incoming.socket.remoteAddress` from the Node.js adapter.
|
|
70
|
+
|
|
71
|
+
## 0.1.0-rc.5
|
|
72
|
+
### Patch Changes
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
- [`363341d`](https://github.com/vivero-dev/stoma/commit/363341dbc92a9f58a4b7df1ca0c66db2407cfe43) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - Accept trailing slashes on route paths
|
|
77
|
+
|
|
78
|
+
### Fixes
|
|
79
|
+
|
|
80
|
+
- **Trailing-slash route aliases**: The gateway now registers both `/path` and `/path/` for every non-wildcard route, so requests with a trailing slash no longer 404. This also covers CORS preflight — if a `cors` policy is present, the OPTIONS handler is registered on both variants.
|
|
81
|
+
- **Scope path normalisation**: `scope()` and the internal `joinPaths` helper now strip trailing slashes from prefixes and handle root-path (`"/"`) children correctly, avoiding double-slash joins or unintended `/path/` suffixes.
|
|
82
|
+
|
|
83
|
+
## 0.1.0-rc.5
|
|
84
|
+
### Patch Changes
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
- [`277d1a2`](https://github.com/vivero-dev/stoma/commit/277d1a2d27d98b444f074e4ecf0ae8095ef6f133) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - Fix policy middleware swallowing handler return values, breaking context finalization
|
|
89
|
+
|
|
90
|
+
### Fixes
|
|
91
|
+
|
|
92
|
+
- **Policy pipeline context finalization**: `policiesToMiddleware` now propagates the return value from policy handlers back to Hono's compose chain. Previously, policies that short-circuit by returning a `Response` (rather than setting `c.res` or calling `next()`) would have their return value discarded, leaving `context.finalized` as `false` and causing Hono to throw "Context is not finalized". Both the fast path (no tracing) and slow path (OTel/policy trace active) are fixed.
|
|
93
|
+
- **Auto-inject OPTIONS for preflight**: When a route restricts its methods (e.g. `methods: ["GET"]`) and a policy that handles OPTIONS preflight is present, the gateway now automatically registers an OPTIONS handler for that path so preflight requests don't 404.
|
|
94
|
+
|
|
95
|
+
## 0.1.0-rc.4
|
|
96
|
+
### Patch Changes
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
- [`c4f3901`](https://github.com/vivero-dev/stoma/commit/c4f39017bf8187e5e750d4bae1acb4fe016b78a8) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - ### Fixes
|
|
101
|
+
|
|
102
|
+
- Refactored JWT auth validation: Extracted duplicated validation logic from `handler` and `evaluate.onRequest` into a shared `validateJwt()` function. Returns a discriminated `JWTValidationResult`, so each runtime path maps to its own error model.
|
|
103
|
+
|
|
104
|
+
### Docs
|
|
105
|
+
|
|
106
|
+
- Docs have been updated with new about and sustainability pages.
|
|
107
|
+
|
|
108
|
+
## 0.1.0-rc.3
|
|
109
|
+
### Patch Changes
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
- [`e7ecfb7`](https://github.com/vivero-dev/stoma/commit/e7ecfb763ef8b7a750984690436ce8bf7253f804) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - # Docs updates only:
|
|
114
|
+
|
|
115
|
+
Add Open Graph meta tags and decouple docs deployment from package releases
|
|
116
|
+
|
|
117
|
+
- Add `og:image` and related OG headers to Starlight config
|
|
118
|
+
- Decouple `deploy-docs` CI job from npm publish — docs now deploy on any successful release job
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
- [`c1485eb`](https://github.com/vivero-dev/stoma/commit/c1485eb0e5933c1e45cb0ba05dff75d11569f46d) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - Tree-shakeable builds — 57% smaller bundles for consumers
|
|
122
|
+
|
|
123
|
+
### Build
|
|
124
|
+
|
|
125
|
+
- **Unbundled dist output**: Switched tsup from bundled code-splitting (`splitting: true`) to per-file transpilation (`bundle: false`). The published `dist/` now mirrors the `src/` module structure, allowing consumer bundlers (esbuild, Rollup, webpack) to tree-shake at the module level instead of importing a monolithic chunk.
|
|
126
|
+
- **`sideEffects: false`**: Added to `package.json` so bundlers can safely drop unused modules.
|
|
127
|
+
- **`/*#__PURE__*/` annotations**: Added to all 33 `definePolicy()` call sites across policy files. These tell bundlers the factory calls can be dropped when their return values are unused.
|
|
128
|
+
|
|
129
|
+
### Impact
|
|
130
|
+
|
|
131
|
+
A basic gateway with `requestLog` + `cors` drops from **89 KB / 28 KB gzip** to **38 KB / 15 KB gzip**. Consumers only pay for the policies they actually import.
|
|
132
|
+
|
|
133
|
+
### Docs
|
|
134
|
+
|
|
135
|
+
- Fixed rate-limit error response in how-it-works guide: error code corrected from `rate_limit_exceeded` to `rate_limited`, `retryAfter` moved from JSON body to response header (matching actual implementation).
|
|
136
|
+
- Fixed IP extraction order: `cf-connecting-ip` checked first, then `x-forwarded-for`.
|
|
137
|
+
|
|
138
|
+
## 0.1.0-rc.2
|
|
139
|
+
### Patch Changes
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
- [`75d0447`](https://github.com/vivero-dev/stoma/commit/75d04472e736fafe9528e258514a9fe3e352e511) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - Live demo API, interactive editor links across docs, release workflow fixes
|
|
144
|
+
|
|
145
|
+
### Features
|
|
146
|
+
|
|
147
|
+
- **Demo API gateway** (`docs/src/demo-api/`): Real Stoma gateway deployed alongside the docs site at `/demo-api/*`, serving echo, users, products, status, and delay endpoints. Dogfoods Stoma with rate limiting (60 req/min) and CORS. Same-origin deployment eliminates CORS issues from the editor.
|
|
148
|
+
- **EditorLink on all guide and recipe pages**: Added "Open in Editor" buttons to 8 docs pages — webhook firewall, cache resilience, shadow release, caching (basic + advanced), JWT auth (HMAC + JWKS), route scopes, and the basic gateway partial. Real-world example page converted from inline code to imported example with EditorLink.
|
|
149
|
+
|
|
150
|
+
### Fixes
|
|
151
|
+
|
|
152
|
+
- **EditorLink Unicode encoding**: Replaced `btoa()` with `Buffer.from().toString("base64")` to handle non-Latin1 characters in example code.
|
|
153
|
+
- **Release workflow: npm auth**: Added `NODE_AUTH_TOKEN` env var — `setup-node` with `registry-url` generates an `.npmrc` referencing `NODE_AUTH_TOKEN`, not `NPM_TOKEN`. Without this, `changeset publish` would fail with 401.
|
|
154
|
+
- **Release workflow: docs deploy build**: Added `yarn build` (tsup) step before docs build — the demo API worker imports `@vivero/stoma` which resolves to `dist/`, so stoma must be built first.
|
|
155
|
+
- **Release workflow: docs build command**: Changed from `yarn docs:build` (`astro build` only) to `cd docs && yarn build` (`build:assets && astro build`) so the stoma bundle, editor worker, and service worker are built before Astro runs.
|
|
156
|
+
- **Webhook firewall test**: Rewritten to test policy behavior (auth rejection for missing signature) instead of depending on upstream DNS resolution. Tests no longer make outbound network requests.
|
|
157
|
+
|
|
158
|
+
### Docs
|
|
159
|
+
|
|
160
|
+
- All concept examples updated to use the live demo API (`https://stoma.vivero.dev/demo-api`) as upstream target, so editor demos produce real responses instead of 502 errors.
|
|
161
|
+
- Docs Cloudflare Worker updated with `main` entry point and `ASSETS` binding to serve both the demo API and static assets.
|
|
162
|
+
|
|
163
|
+
## 0.1.0-rc.1
|
|
164
|
+
### Patch Changes
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
- [`155433e`](https://github.com/vivero-dev/stoma/commit/155433e7eb9f0050cc7b81fdc582e1d9045a583e) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - Protocol-agnostic policy evaluation, docs editor improvements, lint fixes
|
|
169
|
+
|
|
170
|
+
- Multi-protocol policy architecture: `PolicyInput`, `PolicyResult`, `PolicyEvaluator` types enabling policies to run outside HTTP (ext_proc, WebSocket)
|
|
171
|
+
- `ipFilter` now exposes `evaluate.onRequest` alongside the existing Hono `handler`
|
|
172
|
+
- Docs editor: Hono types inlined into the stoma type bundle via `--external-inlines hono` (removes hand-written stubs)
|
|
173
|
+
- Docs editor: subpath registration for Monaco IntelliSense (`/sdk`, `/config`, `/adapters/*`)
|
|
174
|
+
- Fixed broken `EditorLink` component (`_href` → `href`)
|
|
175
|
+
- Lint error cleanup
|
|
176
|
+
|
|
177
|
+
## 0.1.0-rc.0
|
|
178
|
+
### Minor Changes
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
- [`bb4d04f`](https://github.com/vivero-dev/stoma/commit/bb4d04ff85c8c133b10c323d92695cf11b944552) Thanks [@JonathanBennett](https://github.com/JonathanBennett)! - Initial release candidate for v0.1.0
|
|
183
|
+
|
|
184
|
+
Declarative API gateway library built on Hono for Cloudflare Workers and edge runtimes. Features:
|
|
185
|
+
|
|
186
|
+
- Gateway construction from declarative TypeScript config
|
|
187
|
+
- 43 policies across auth, traffic, resilience, transform, and observability domains
|
|
188
|
+
- 4-layer policy SDK with `definePolicy()`, priority constants, composable helpers, and test harness
|
|
189
|
+
- Three upstream types: URL proxy, Cloudflare Service Binding, and custom handler
|
|
190
|
+
- Runtime adapters for Cloudflare Workers, Node.js, Deno, and Bun
|
|
191
|
+
- Cloudflare-specific stores (KV, Durable Objects, Cache API)
|
|
192
|
+
- Admin introspection API with Prometheus metrics export
|
|
193
|
+
- W3C trace context propagation
|
|
194
|
+
- Zod-based config validation (optional peer dependency)
|
|
195
|
+
- Zero-dependency debug system with namespace filtering
|
|
196
|
+
- SSRF protection on URL upstreams
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jonathan Bennett
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# Stoma 🌱
|
|
2
|
+
|
|
3
|
+
**Declarative API gateway as a TypeScript library. Runs on Cloudflare Workers, Node.js, Deno, Bun, and more.**
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Why
|
|
12
|
+
|
|
13
|
+
API gateways in the JavaScript ecosystem are stuck between two bad options:
|
|
14
|
+
|
|
15
|
+
1. **Infrastructure you deploy** -- Kong, KrakenD, AWS API Gateway. Powerful, but they are separate services you provision, configure with YAML, and operate independently from your application code. You lose type safety, your gateway config drifts from your codebase, and you pay for another piece of infrastructure.
|
|
16
|
+
|
|
17
|
+
2. **Dead or abandoned libraries** -- Express Gateway was the closest thing to a TS-native gateway. It is unmaintained, locked to Express 4, and has no story for modern runtimes.
|
|
18
|
+
|
|
19
|
+
There is a third option: **a declarative gateway library that lives in your codebase and deploys with your standard workflow.**
|
|
20
|
+
|
|
21
|
+
`@vivero/stoma` fills this gap. It is a TypeScript library you import -- not a Go binary or YAML config you deploy. Define your routes, policies, and upstreams as typed objects. The library compiles them into an HTTP application you can export directly.
|
|
22
|
+
|
|
23
|
+
- Configured with **TypeScript**, not YAML
|
|
24
|
+
- Lives in **your codebase** -- deploy as part of your app or as a standalone service
|
|
25
|
+
- Runs on Cloudflare Workers, Node.js, Deno, Bun, Fastly, Lambda@Edge, Vercel Edge Functions
|
|
26
|
+
- Optionally leverages **platform-specific features** (Cloudflare Service Bindings, KV, Durable Objects) through a pluggable adapter system
|
|
27
|
+
|
|
28
|
+
Think KrakenD's declarative route-to-pipeline model, but as a TypeScript library.
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- **TypeScript-first with full type safety** -- Every config object, policy, and upstream is typed. No YAML. No JSON Schema. Your editor catches mistakes before your users do.
|
|
33
|
+
- **Powered by Hono** -- lightweight, zero dependencies, Web Standards API. One of the fastest routers in the JavaScript ecosystem.
|
|
34
|
+
- **Declarative route / pipeline / policy chain model** -- Define routes, attach ordered policy chains, and point at an upstream. The gateway wires it all together.
|
|
35
|
+
- **Dozens of built-in policies** -- Auth (JWT, API key, OAuth2, RBAC, HTTP signatures), traffic control (rate limiting, IP filtering, caching, threat protection), resilience (circuit breaker, retry, timeout), transforms (CORS, request/response rewriting, validation), and observability (structured logging, metrics, health checks).
|
|
36
|
+
- **Multi-runtime** -- Core depends only on Hono and the Web `Request`/`Response` API. No Node.js built-ins, no platform lock-in.
|
|
37
|
+
- **Pluggable adapter system** -- Runtime-specific capabilities (distributed stores, background tasks, service bindings) are injected through adapters, not hard-coded. Adapters ship for Cloudflare Workers, Node.js, Deno, Bun, and in-memory development.
|
|
38
|
+
- **Pluggable policy system** -- Policies are middleware functions with metadata. Write a custom policy in a few lines, or use `definePolicy()` from the SDK for a structured approach.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
npm install @vivero/stoma hono
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
### Basic gateway (runs on any runtime)
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { createGateway, jwtAuth, rateLimit, requestLog, cors } from "@vivero/stoma";
|
|
52
|
+
|
|
53
|
+
const gateway = createGateway({
|
|
54
|
+
name: "my-api-gateway",
|
|
55
|
+
basePath: "/api",
|
|
56
|
+
policies: [requestLog(), cors()],
|
|
57
|
+
routes: [
|
|
58
|
+
{
|
|
59
|
+
path: "/users/*",
|
|
60
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
61
|
+
pipeline: {
|
|
62
|
+
policies: [
|
|
63
|
+
jwtAuth({ secret: "your-secret" }),
|
|
64
|
+
rateLimit({ max: 100, windowSeconds: 60 }),
|
|
65
|
+
],
|
|
66
|
+
upstream: {
|
|
67
|
+
type: "url",
|
|
68
|
+
target: "https://users-api.internal.example.com",
|
|
69
|
+
rewritePath: (path) => path.replace("/api/users", ""),
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
path: "/health",
|
|
75
|
+
pipeline: {
|
|
76
|
+
upstream: {
|
|
77
|
+
type: "handler",
|
|
78
|
+
handler: () =>
|
|
79
|
+
new Response(JSON.stringify({ status: "ok" }), {
|
|
80
|
+
headers: { "Content-Type": "application/json" },
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export default gateway.app;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This gateway works out of the box on Cloudflare Workers (`export default gateway.app`), Bun (`export default gateway.app`), Deno (`Deno.serve(gateway.app.fetch)`), or Node.js (via `@hono/node-server`).
|
|
92
|
+
|
|
93
|
+
### With Cloudflare-specific features
|
|
94
|
+
|
|
95
|
+
When deploying to Cloudflare Workers, use the Cloudflare adapter to unlock Service Bindings, KV-backed rate limiting, and Durable Objects:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { createGateway, jwtAuth, rateLimit, requestLog } from "@vivero/stoma";
|
|
99
|
+
import { cloudflareAdapter } from "@vivero/stoma/adapters/cloudflare";
|
|
100
|
+
|
|
101
|
+
type Env = {
|
|
102
|
+
JWT_SECRET: string;
|
|
103
|
+
USERS_SERVICE: Fetcher;
|
|
104
|
+
RATE_LIMIT_KV: KVNamespace;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export default {
|
|
108
|
+
fetch(request: Request, env: Env, ctx: ExecutionContext) {
|
|
109
|
+
const gateway = createGateway({
|
|
110
|
+
name: "my-api-gateway",
|
|
111
|
+
basePath: "/api",
|
|
112
|
+
adapter: cloudflareAdapter({
|
|
113
|
+
rateLimitKv: env.RATE_LIMIT_KV,
|
|
114
|
+
executionCtx: ctx,
|
|
115
|
+
env,
|
|
116
|
+
}),
|
|
117
|
+
policies: [requestLog()],
|
|
118
|
+
routes: [
|
|
119
|
+
{
|
|
120
|
+
path: "/users/*",
|
|
121
|
+
pipeline: {
|
|
122
|
+
policies: [
|
|
123
|
+
jwtAuth({ secret: env.JWT_SECRET }),
|
|
124
|
+
rateLimit({ max: 100, windowSeconds: 60 }),
|
|
125
|
+
],
|
|
126
|
+
upstream: {
|
|
127
|
+
type: "service-binding",
|
|
128
|
+
service: "USERS_SERVICE",
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return gateway.app.fetch(request, env, ctx);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Configuration
|
|
141
|
+
|
|
142
|
+
The gateway is configured entirely through TypeScript. The top-level `GatewayConfig` type drives everything:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
interface GatewayConfig {
|
|
146
|
+
name?: string; // Gateway name, used in logs and metrics
|
|
147
|
+
basePath?: string; // Base path prefix for all routes (e.g. "/api")
|
|
148
|
+
routes: RouteConfig[]; // Route definitions
|
|
149
|
+
policies?: Policy[]; // Global policies applied to every route
|
|
150
|
+
adapter?: GatewayAdapter; // Runtime adapter for stores and platform capabilities
|
|
151
|
+
onError?: (error: Error, c: unknown) => Response | Promise<Response>;
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Each route defines a **path**, optional **methods**, and a **pipeline**:
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
interface RouteConfig {
|
|
159
|
+
path: string; // Hono path syntax: "/users/:id", "/files/*"
|
|
160
|
+
methods?: HttpMethod[]; // ["GET", "POST", ...] -- defaults to all
|
|
161
|
+
pipeline: PipelineConfig; // Policy chain + upstream target
|
|
162
|
+
metadata?: Record<string, unknown>;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
A pipeline is an ordered chain of policies leading to an upstream:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
interface PipelineConfig {
|
|
170
|
+
policies?: Policy[]; // Executed in order before the upstream
|
|
171
|
+
upstream: UpstreamConfig; // Where the request goes
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Upstream Types
|
|
176
|
+
|
|
177
|
+
Three upstream types cover the common deployment patterns:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// URL proxy -- forward to any HTTP endpoint (works on all runtimes)
|
|
181
|
+
{ type: "url", target: "https://api.example.com", headers: { "X-Forwarded-By": "gateway" } }
|
|
182
|
+
|
|
183
|
+
// Service Binding -- zero-latency Worker-to-Worker routing (Cloudflare only)
|
|
184
|
+
{ type: "service-binding", service: "AUTH_SERVICE" }
|
|
185
|
+
|
|
186
|
+
// Inline handler -- respond directly without proxying (works on all runtimes)
|
|
187
|
+
{ type: "handler", handler: (c) => new Response("ok") }
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Adapters
|
|
191
|
+
|
|
192
|
+
Adapters provide runtime-specific store implementations and platform capabilities. The gateway core is runtime-agnostic; adapters plug in distributed rate limiting, caching, background tasks, and service binding dispatch.
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import { cloudflareAdapter } from "@vivero/stoma/adapters/cloudflare";
|
|
196
|
+
import { nodeAdapter } from "@vivero/stoma/adapters/node";
|
|
197
|
+
import { denoAdapter } from "@vivero/stoma/adapters/deno";
|
|
198
|
+
import { bunAdapter } from "@vivero/stoma/adapters/bun";
|
|
199
|
+
import { memoryAdapter } from "@vivero/stoma/adapters/memory";
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
| Adapter | Stores | `waitUntil` | Service Bindings | Use case |
|
|
203
|
+
|---------|--------|-------------|------------------|----------|
|
|
204
|
+
| `cloudflareAdapter` | KV, Durable Objects, Cache API | Yes | Yes | Production on Cloudflare Workers |
|
|
205
|
+
| `nodeAdapter` | In-memory defaults | No | No | Node.js servers via `@hono/node-server` |
|
|
206
|
+
| `denoAdapter` | In-memory defaults | No | No | Deno / Deno Deploy |
|
|
207
|
+
| `bunAdapter` | In-memory defaults | No | No | Bun servers |
|
|
208
|
+
| `memoryAdapter` | In-memory (all stores) | No | No | Development, testing, single-instance |
|
|
209
|
+
|
|
210
|
+
No adapter is required. Without one, policies that need stores (rate limiting, caching, circuit breaking) fall back to in-memory defaults automatically.
|
|
211
|
+
|
|
212
|
+
## Policies
|
|
213
|
+
|
|
214
|
+
Policies are the building blocks of gateway pipelines. They are middleware handlers with a name and a priority (lower numbers execute first).
|
|
215
|
+
|
|
216
|
+
### Built-in Policies
|
|
217
|
+
|
|
218
|
+
| Category | Policies |
|
|
219
|
+
|----------|----------|
|
|
220
|
+
| **Auth** | `jwtAuth`, `apiKeyAuth`, `basicAuth`, `oauth2`, `rbac`, `generateJwt`, `jws`, `generateHttpSignature`, `verifyHttpSignature` |
|
|
221
|
+
| **Traffic** | `rateLimit`, `ipFilter`, `geoIpFilter`, `cache`, `sslEnforce`, `requestLimit`, `jsonThreatProtection`, `regexThreatProtection`, `trafficShadow`, `interrupt`, `dynamicRouting`, `httpCallout`, `resourceFilter` |
|
|
222
|
+
| **Resilience** | `timeout`, `retry`, `circuitBreaker`, `latencyInjection` |
|
|
223
|
+
| **Transform** | `cors`, `overrideMethod`, `assignAttributes`, `assignContent`, `requestTransform`, `responseTransform`, `requestValidation`, `jsonValidation` |
|
|
224
|
+
| **Observability** | `requestLog`, `metricsReporter`, `assignMetrics`, `health` |
|
|
225
|
+
| **Utility** | `proxy`, `mock` |
|
|
226
|
+
|
|
227
|
+
Every policy accepts a `skip` function for conditional execution:
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
rateLimit({
|
|
231
|
+
max: 100,
|
|
232
|
+
skip: (c) => c.req.header("X-Internal") === "true",
|
|
233
|
+
})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Writing a Custom Policy
|
|
237
|
+
|
|
238
|
+
A policy is any object conforming to the `Policy` interface:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import type { Policy } from "@vivero/stoma";
|
|
242
|
+
|
|
243
|
+
function requestTimer(): Policy {
|
|
244
|
+
return {
|
|
245
|
+
name: "request-timer",
|
|
246
|
+
priority: 5,
|
|
247
|
+
handler: async (c, next) => {
|
|
248
|
+
const start = performance.now();
|
|
249
|
+
await next();
|
|
250
|
+
c.header("X-Response-Time", `${(performance.now() - start).toFixed(1)}ms`);
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
For a more structured approach, use the SDK:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { definePolicy, Priority } from "@vivero/stoma";
|
|
260
|
+
|
|
261
|
+
const requestTimer = definePolicy({
|
|
262
|
+
name: "request-timer",
|
|
263
|
+
priority: Priority.EARLY_TRANSFORM,
|
|
264
|
+
handler: async ({ c, next }) => {
|
|
265
|
+
const start = performance.now();
|
|
266
|
+
await next();
|
|
267
|
+
c.header("X-Response-Time", `${(performance.now() - start).toFixed(1)}ms`);
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Policy Execution Order
|
|
273
|
+
|
|
274
|
+
Policies execute in **priority order** (lowest number first), then in **declaration order** for policies sharing a priority. Global policies and route-level policies are merged and sorted together.
|
|
275
|
+
|
|
276
|
+
| Priority | Phase | Examples |
|
|
277
|
+
|----------|-------|---------|
|
|
278
|
+
| 0 | Observability | `requestLog`, `assignMetrics` |
|
|
279
|
+
| 1 | Network filter | `ipFilter`, `geoIpFilter`, `metricsReporter` |
|
|
280
|
+
| 5 | Early transform | `cors`, `sslEnforce`, `requestLimit`, `latencyInjection` |
|
|
281
|
+
| 10 | Authentication | `jwtAuth`, `apiKeyAuth`, `basicAuth`, `oauth2`, `rbac` |
|
|
282
|
+
| 20 | Rate limiting | `rateLimit` |
|
|
283
|
+
| 30 | Circuit breaker | `circuitBreaker` |
|
|
284
|
+
| 40 | Caching | `cache` |
|
|
285
|
+
| 50 | Mid-pipeline | `requestTransform`, `assignAttributes`, `generateJwt`, `dynamicRouting` |
|
|
286
|
+
| 85 | Timeout | `timeout` |
|
|
287
|
+
| 90 | Retry | `retry` |
|
|
288
|
+
| 92 | Response | `responseTransform`, `trafficShadow`, `resourceFilter` |
|
|
289
|
+
| 95 | Proxy | `proxy`, `generateHttpSignature` |
|
|
290
|
+
| 100 | Default | Custom policies |
|
|
291
|
+
| 999 | Terminal | `mock` |
|
|
292
|
+
|
|
293
|
+
## Documentation & Resources
|
|
294
|
+
|
|
295
|
+
- [Architecture Design](ARCHITECTURE.md) - Deep dive into the gateway's internal design and decisions.
|
|
296
|
+
- [Full Documentation](https://stoma.vivero.dev) - Comprehensive guides, recipes, and API reference.
|
|
297
|
+
|
|
298
|
+
## Package Exports
|
|
299
|
+
|
|
300
|
+
| Import path | Contents |
|
|
301
|
+
|-------------|----------|
|
|
302
|
+
| `@vivero/stoma` | `createGateway`, all policies, metrics, core types |
|
|
303
|
+
| `@vivero/stoma/policies` | Policies only |
|
|
304
|
+
| `@vivero/stoma/sdk` | `definePolicy`, `Priority`, test harness |
|
|
305
|
+
| `@vivero/stoma/config` | Config types + optional Zod validation (requires `zod` peer dep) |
|
|
306
|
+
| `@vivero/stoma/adapters` | All adapter factories |
|
|
307
|
+
| `@vivero/stoma/adapters/cloudflare` | Cloudflare adapter only |
|
|
308
|
+
| `@vivero/stoma/adapters/node` | Node.js adapter only |
|
|
309
|
+
| `@vivero/stoma/adapters/deno` | Deno adapter only |
|
|
310
|
+
| `@vivero/stoma/adapters/bun` | Bun adapter only |
|
|
311
|
+
| `@vivero/stoma/adapters/memory` | In-memory adapter (dev/test) |
|
|
312
|
+
|
|
313
|
+
## Contributing
|
|
314
|
+
|
|
315
|
+
Contributions are welcome. Please open an issue to discuss proposed changes before submitting a pull request.
|
|
316
|
+
|
|
317
|
+
```sh
|
|
318
|
+
yarn install
|
|
319
|
+
yarn test
|
|
320
|
+
yarn typecheck
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## License
|
|
324
|
+
|
|
325
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { G as GatewayAdapter } from '../protocol-2fD3DJrL.js';
|
|
2
|
+
import 'hono';
|
|
3
|
+
import '../policies/sdk/trace.js';
|
|
4
|
+
import '@vivero/stoma-core';
|
|
5
|
+
|
|
6
|
+
/** Create a GatewayAdapter for Bun. Delegates to memoryAdapter() for in-memory stores. */
|
|
7
|
+
declare function bunAdapter(): GatewayAdapter;
|
|
8
|
+
|
|
9
|
+
export { bunAdapter };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/bun.ts"],"sourcesContent":["/**\n * Bun adapter for stoma.\n *\n * Bun doesn't provide `waitUntil` or service bindings natively.\n * Use `memoryAdapter()` for stores and this adapter as a marker/extension\n * point for Bun-specific capabilities.\n *\n * @example\n * ```ts\n * import { createGateway } from \"@vivero/stoma\";\n * import { bunAdapter } from \"@vivero/stoma/adapters/bun\";\n *\n * const gateway = createGateway({\n * adapter: bunAdapter(),\n * routes: [...]\n * });\n *\n * export default gateway.app;\n * ```\n *\n * @module bun-adapter\n */\nimport { memoryAdapter } from \"./memory\";\nimport type { GatewayAdapter } from \"./types\";\n\n/** Create a GatewayAdapter for Bun. Delegates to memoryAdapter() for in-memory stores. */\nexport function bunAdapter(): GatewayAdapter {\n return memoryAdapter();\n}\n"],"mappings":"AAsBA,SAAS,qBAAqB;AAIvB,SAAS,aAA6B;AAC3C,SAAO,cAAc;AACvB;","names":[]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { C as CacheStore, R as RateLimitStore, G as GatewayAdapter } from '../protocol-2fD3DJrL.js';
|
|
2
|
+
import 'hono';
|
|
3
|
+
import '../policies/sdk/trace.js';
|
|
4
|
+
import '@vivero/stoma-core';
|
|
5
|
+
|
|
6
|
+
/** Rate limit store backed by Cloudflare Workers KV. */
|
|
7
|
+
declare class KVRateLimitStore implements RateLimitStore {
|
|
8
|
+
private kv;
|
|
9
|
+
constructor(kv: KVNamespace);
|
|
10
|
+
increment(key: string, windowSeconds: number): Promise<{
|
|
11
|
+
count: number;
|
|
12
|
+
resetAt: number;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
/** Response cache backed by the Cloudflare Cache API. */
|
|
16
|
+
declare class CacheApiCacheStore implements CacheStore {
|
|
17
|
+
private cache;
|
|
18
|
+
private origin;
|
|
19
|
+
/**
|
|
20
|
+
* @param cache - A `Cache` instance (e.g. `caches.default`). Falls back to `caches.default` when omitted.
|
|
21
|
+
* @param origin - Synthetic origin used to construct cache keys. Default: `"https://edge-gateway.internal"`.
|
|
22
|
+
*/
|
|
23
|
+
constructor(cache?: Cache, origin?: string);
|
|
24
|
+
get(key: string): Promise<Response | null>;
|
|
25
|
+
put(key: string, response: Response, ttlSeconds: number): Promise<void>;
|
|
26
|
+
delete(key: string): Promise<boolean>;
|
|
27
|
+
}
|
|
28
|
+
interface CloudflareAdapterBindings {
|
|
29
|
+
rateLimitKv?: KVNamespace;
|
|
30
|
+
rateLimitDo?: DurableObjectNamespace;
|
|
31
|
+
cache?: Cache;
|
|
32
|
+
/** Synthetic origin used for Cache API cache keys. Default: `"https://edge-gateway.internal"`. */
|
|
33
|
+
cacheOrigin?: string;
|
|
34
|
+
/** Workers `ExecutionContext` - enables `waitUntil` for background work (e.g. traffic shadow). */
|
|
35
|
+
executionCtx?: ExecutionContext;
|
|
36
|
+
/** Workers `env` object - enables `dispatchBinding` for service binding dispatch via the adapter. */
|
|
37
|
+
env?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a GatewayAdapter using Cloudflare-native stores.
|
|
41
|
+
*
|
|
42
|
+
* Rate limiting priority: Durable Objects (strongly consistent) > KV (eventually consistent) > none.
|
|
43
|
+
*
|
|
44
|
+
* Pass `executionCtx` to enable `waitUntil` (for traffic shadow and other background work).
|
|
45
|
+
* Pass `env` to enable `dispatchBinding` (for service binding dispatch via the adapter).
|
|
46
|
+
*/
|
|
47
|
+
declare function cloudflareAdapter(bindings: CloudflareAdapterBindings): GatewayAdapter;
|
|
48
|
+
|
|
49
|
+
export { CacheApiCacheStore, type CloudflareAdapterBindings, KVRateLimitStore, cloudflareAdapter };
|