@beignet/core 0.0.2 → 0.0.4
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 +173 -0
- package/README.md +821 -30
- package/dist/application/index.d.ts +28 -2
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +140 -12
- package/dist/application/index.js.map +1 -1
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +136 -48
- package/dist/client/client.js.map +1 -1
- package/dist/client/error-messages.d.ts +14 -0
- package/dist/client/error-messages.d.ts.map +1 -0
- package/dist/client/error-messages.js +23 -0
- package/dist/client/error-messages.js.map +1 -0
- package/dist/client/index.d.ts +8 -4
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +6 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/types.d.ts +35 -5
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client-only.d.ts +8 -0
- package/dist/client-only.d.ts.map +1 -0
- package/dist/client-only.js +8 -0
- package/dist/client-only.js.map +1 -0
- package/dist/config/index.d.ts +5 -5
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +2 -2
- package/dist/config/index.js.map +1 -1
- package/dist/contracts/catalog-errors.d.ts +27 -0
- package/dist/contracts/catalog-errors.d.ts.map +1 -0
- package/dist/contracts/catalog-errors.js +69 -0
- package/dist/contracts/catalog-errors.js.map +1 -0
- package/dist/contracts/contract-builder.d.ts +15 -12
- package/dist/contracts/contract-builder.d.ts.map +1 -1
- package/dist/contracts/contract-builder.js +15 -41
- package/dist/contracts/contract-builder.js.map +1 -1
- package/dist/contracts/contract-group.d.ts +11 -8
- package/dist/contracts/contract-group.d.ts.map +1 -1
- package/dist/contracts/contract-group.js +13 -40
- package/dist/contracts/contract-group.js.map +1 -1
- package/dist/contracts/contract-like.d.ts +1 -1
- package/dist/contracts/contract-like.d.ts.map +1 -1
- package/dist/contracts/index.d.ts +13 -9
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +9 -5
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/openapi-meta.d.ts +48 -0
- package/dist/contracts/openapi-meta.d.ts.map +1 -1
- package/dist/contracts/openapi-meta.js +3 -0
- package/dist/contracts/openapi-meta.js.map +1 -1
- package/dist/contracts/path-template.d.ts +1 -1
- package/dist/contracts/path-template.js +2 -2
- package/dist/contracts/path-template.js.map +1 -1
- package/dist/contracts/schema-shape.d.ts +37 -0
- package/dist/contracts/schema-shape.d.ts.map +1 -0
- package/dist/contracts/schema-shape.js +61 -0
- package/dist/contracts/schema-shape.js.map +1 -0
- package/dist/contracts/success-status.d.ts +32 -0
- package/dist/contracts/success-status.d.ts.map +1 -0
- package/dist/contracts/success-status.js +18 -0
- package/dist/contracts/success-status.js.map +1 -0
- package/dist/contracts/types.d.ts +25 -5
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/contracts/types.js.map +1 -1
- package/dist/contracts/utils.d.ts +1 -1
- package/dist/contracts/utils.d.ts.map +1 -1
- package/dist/contracts/utils.js +1 -1
- package/dist/contracts/utils.js.map +1 -1
- package/dist/domain/events.d.ts +1 -1
- package/dist/domain/events.d.ts.map +1 -1
- package/dist/domain/events.js +1 -1
- package/dist/domain/events.js.map +1 -1
- package/dist/domain/index.d.ts +3 -3
- package/dist/domain/index.d.ts.map +1 -1
- package/dist/domain/index.js +3 -3
- package/dist/domain/index.js.map +1 -1
- package/dist/errors/catalog.d.ts +9 -1
- package/dist/errors/catalog.d.ts.map +1 -1
- package/dist/errors/catalog.js +7 -1
- package/dist/errors/catalog.js.map +1 -1
- package/dist/errors/http.d.ts +10 -0
- package/dist/errors/http.d.ts.map +1 -1
- package/dist/errors/http.js +11 -1
- package/dist/errors/http.js.map +1 -1
- package/dist/errors/index.d.ts +4 -4
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +4 -4
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/response.d.ts +4 -1
- package/dist/errors/response.d.ts.map +1 -1
- package/dist/errors/response.js.map +1 -1
- package/dist/events/index.d.ts +10 -12
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +10 -10
- package/dist/events/index.js.map +1 -1
- package/dist/idempotency/index.d.ts +5 -3
- package/dist/idempotency/index.d.ts.map +1 -1
- package/dist/idempotency/index.js.map +1 -1
- package/dist/jobs/index.d.ts +148 -16
- package/dist/jobs/index.d.ts.map +1 -1
- package/dist/jobs/index.js +174 -14
- package/dist/jobs/index.js.map +1 -1
- package/dist/notifications/index.d.ts +14 -16
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +14 -14
- package/dist/notifications/index.js.map +1 -1
- package/dist/openapi/index.d.ts +8 -3
- package/dist/openapi/index.d.ts.map +1 -1
- package/dist/openapi/index.js +41 -29
- package/dist/openapi/index.js.map +1 -1
- package/dist/openapi/schema-introspector.d.ts +37 -0
- package/dist/openapi/schema-introspector.d.ts.map +1 -1
- package/dist/openapi/schema-introspector.js +23 -17
- package/dist/openapi/schema-introspector.js.map +1 -1
- package/dist/outbox/index.d.ts +18 -4
- package/dist/outbox/index.d.ts.map +1 -1
- package/dist/outbox/index.js +104 -4
- package/dist/outbox/index.js.map +1 -1
- package/dist/ports/audit.d.ts +56 -10
- package/dist/ports/audit.d.ts.map +1 -1
- package/dist/ports/audit.js +71 -3
- package/dist/ports/audit.js.map +1 -1
- package/dist/ports/auth.d.ts +92 -0
- package/dist/ports/auth.d.ts.map +1 -1
- package/dist/ports/auth.js +92 -0
- package/dist/ports/auth.js.map +1 -1
- package/dist/ports/events.d.ts +2 -2
- package/dist/ports/events.d.ts.map +1 -1
- package/dist/ports/index.d.ts +62 -33
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js +28 -34
- package/dist/ports/index.js.map +1 -1
- package/dist/ports/policy.d.ts +32 -3
- package/dist/ports/policy.d.ts.map +1 -1
- package/dist/ports/policy.js +13 -2
- package/dist/ports/policy.js.map +1 -1
- package/dist/ports/testing.d.ts +1030 -2
- package/dist/ports/testing.d.ts.map +1 -1
- package/dist/ports/testing.js +1031 -1
- package/dist/ports/testing.js.map +1 -1
- package/dist/ports/unbound.d.ts +21 -0
- package/dist/ports/unbound.d.ts.map +1 -0
- package/dist/ports/unbound.js +57 -0
- package/dist/ports/unbound.js.map +1 -0
- package/dist/ports/unit-of-work.d.ts +1 -1
- package/dist/ports/unit-of-work.d.ts.map +1 -1
- package/dist/ports/unit-of-work.js +1 -1
- package/dist/ports/unit-of-work.js.map +1 -1
- package/dist/providers/index.d.ts +3 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -2
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/instrumentation.d.ts +46 -5
- package/dist/providers/instrumentation.d.ts.map +1 -1
- package/dist/providers/instrumentation.js +25 -6
- package/dist/providers/instrumentation.js.map +1 -1
- package/dist/providers/metadata.d.ts +39 -0
- package/dist/providers/metadata.d.ts.map +1 -0
- package/dist/providers/metadata.js +169 -0
- package/dist/providers/metadata.js.map +1 -0
- package/dist/providers/provider.d.ts +114 -9
- package/dist/providers/provider.d.ts.map +1 -1
- package/dist/providers/provider.js +3 -20
- package/dist/providers/provider.js.map +1 -1
- package/dist/schedules/index.d.ts +94 -13
- package/dist/schedules/index.d.ts.map +1 -1
- package/dist/schedules/index.js +66 -12
- package/dist/schedules/index.js.map +1 -1
- package/dist/server/audit-context.d.ts +29 -0
- package/dist/server/audit-context.d.ts.map +1 -0
- package/dist/server/audit-context.js +44 -0
- package/dist/server/audit-context.js.map +1 -0
- package/dist/server/context.d.ts +141 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +39 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/contract-like.d.ts +1 -1
- package/dist/server/contract-like.d.ts.map +1 -1
- package/dist/server/contract-like.js +1 -1
- package/dist/server/contract-like.js.map +1 -1
- package/dist/server/health.d.ts +2 -2
- package/dist/server/health.d.ts.map +1 -1
- package/dist/server/hooks/auth.d.ts +89 -65
- package/dist/server/hooks/auth.d.ts.map +1 -1
- package/dist/server/hooks/auth.js +84 -55
- package/dist/server/hooks/auth.js.map +1 -1
- package/dist/server/hooks/cors.d.ts +1 -1
- package/dist/server/hooks/cors.d.ts.map +1 -1
- package/dist/server/hooks/errors.d.ts +2 -2
- package/dist/server/hooks/errors.d.ts.map +1 -1
- package/dist/server/hooks/errors.js +2 -2
- package/dist/server/hooks/errors.js.map +1 -1
- package/dist/server/hooks/idempotency.d.ts +78 -0
- package/dist/server/hooks/idempotency.d.ts.map +1 -0
- package/dist/server/hooks/idempotency.js +154 -0
- package/dist/server/hooks/idempotency.js.map +1 -0
- package/dist/server/hooks/index.d.ts +8 -7
- package/dist/server/hooks/index.d.ts.map +1 -1
- package/dist/server/hooks/index.js +6 -5
- package/dist/server/hooks/index.js.map +1 -1
- package/dist/server/hooks/logging.d.ts +2 -2
- package/dist/server/hooks/logging.d.ts.map +1 -1
- package/dist/server/hooks/logging.js +1 -1
- package/dist/server/hooks/logging.js.map +1 -1
- package/dist/server/hooks/rate-limit.d.ts +25 -7
- package/dist/server/hooks/rate-limit.d.ts.map +1 -1
- package/dist/server/hooks/rate-limit.js +47 -12
- package/dist/server/hooks/rate-limit.js.map +1 -1
- package/dist/server/hooks.d.ts +1 -1
- package/dist/server/hooks.d.ts.map +1 -1
- package/dist/server/hooks.js +1 -1
- package/dist/server/hooks.js.map +1 -1
- package/dist/server/http.d.ts +84 -6
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/index.d.ts +36 -12
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +24 -8
- package/dist/server/index.js.map +1 -1
- package/dist/server/instrumentation.d.ts +108 -0
- package/dist/server/instrumentation.d.ts.map +1 -0
- package/dist/server/instrumentation.js +297 -0
- package/dist/server/instrumentation.js.map +1 -0
- package/dist/server/openapi.d.ts +3 -3
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/openapi.js +1 -1
- package/dist/server/openapi.js.map +1 -1
- package/dist/server/providers/index.d.ts +3 -3
- package/dist/server/providers/index.d.ts.map +1 -1
- package/dist/server/providers/index.js +3 -3
- package/dist/server/providers/index.js.map +1 -1
- package/dist/server/providers/loadProviderConfig.d.ts +2 -2
- package/dist/server/providers/loadProviderConfig.d.ts.map +1 -1
- package/dist/server/providers/loadProviderConfig.js +2 -2
- package/dist/server/providers/loadProviderConfig.js.map +1 -1
- package/dist/server/request-context.d.ts +67 -0
- package/dist/server/request-context.d.ts.map +1 -0
- package/dist/server/request-context.js +79 -0
- package/dist/server/request-context.js.map +1 -0
- package/dist/server/server-context.d.ts +38 -0
- package/dist/server/server-context.d.ts.map +1 -0
- package/dist/server/server-context.js +38 -0
- package/dist/server/server-context.js.map +1 -0
- package/dist/server/server.d.ts +148 -35
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +482 -145
- package/dist/server/server.js.map +1 -1
- package/dist/server/types.d.ts +2 -2
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +2 -2
- package/dist/server/types.js.map +1 -1
- package/dist/server/use-case-route.d.ts +263 -0
- package/dist/server/use-case-route.d.ts.map +1 -0
- package/dist/server/use-case-route.js +77 -0
- package/dist/server/use-case-route.js.map +1 -0
- package/dist/server-only.d.ts +8 -0
- package/dist/server-only.d.ts.map +1 -0
- package/dist/server-only.js +8 -0
- package/dist/server-only.js.map +1 -0
- package/dist/tasks/index.d.ts +139 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +98 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/testing/index.d.ts +611 -5
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +434 -4
- package/dist/testing/index.js.map +1 -1
- package/dist/tracing/index.d.ts +89 -0
- package/dist/tracing/index.d.ts.map +1 -0
- package/dist/tracing/index.js +101 -0
- package/dist/tracing/index.js.map +1 -0
- package/dist/uploads/client.d.ts +278 -0
- package/dist/uploads/client.d.ts.map +1 -0
- package/dist/uploads/client.js +428 -0
- package/dist/uploads/client.js.map +1 -0
- package/dist/uploads/index.d.ts +361 -0
- package/dist/uploads/index.d.ts.map +1 -0
- package/dist/uploads/index.js +543 -0
- package/dist/uploads/index.js.map +1 -0
- package/package.json +34 -3
- package/src/application/index.ts +193 -10
- package/src/client/client.ts +148 -150
- package/src/client/error-messages.ts +35 -0
- package/src/client/index.ts +12 -4
- package/src/client/types.ts +44 -5
- package/src/client-only.ts +7 -0
- package/src/config/index.ts +6 -6
- package/src/contracts/catalog-errors.ts +115 -0
- package/src/contracts/contract-builder.ts +39 -76
- package/src/contracts/contract-group.ts +33 -68
- package/src/contracts/contract-like.ts +1 -1
- package/src/contracts/index.ts +24 -11
- package/src/contracts/openapi-meta.ts +55 -0
- package/src/contracts/path-template.ts +2 -2
- package/src/contracts/schema-shape.ts +75 -0
- package/src/contracts/success-status.ts +68 -0
- package/src/contracts/types.ts +32 -5
- package/src/contracts/utils.ts +5 -2
- package/src/domain/events.ts +6 -2
- package/src/domain/index.ts +3 -3
- package/src/errors/catalog.ts +9 -1
- package/src/errors/http.ts +11 -1
- package/src/errors/index.ts +4 -4
- package/src/errors/response.ts +4 -1
- package/src/events/index.ts +12 -26
- package/src/idempotency/index.ts +5 -3
- package/src/jobs/index.ts +340 -29
- package/src/notifications/index.ts +17 -27
- package/src/openapi/index.ts +73 -38
- package/src/openapi/schema-introspector.ts +68 -17
- package/src/outbox/index.ts +151 -6
- package/src/ports/audit.ts +120 -11
- package/src/ports/auth.ts +132 -0
- package/src/ports/events.ts +2 -2
- package/src/ports/index.ts +104 -35
- package/src/ports/policy.ts +50 -3
- package/src/ports/testing.ts +2220 -33
- package/src/ports/unbound.ts +64 -0
- package/src/ports/unit-of-work.ts +6 -2
- package/src/providers/index.ts +16 -3
- package/src/providers/instrumentation.ts +93 -8
- package/src/providers/metadata.ts +234 -0
- package/src/providers/provider.ts +168 -9
- package/src/schedules/index.ts +173 -23
- package/src/server/audit-context.ts +45 -0
- package/src/server/context.ts +224 -0
- package/src/server/contract-like.ts +1 -1
- package/src/server/health.ts +2 -2
- package/src/server/hooks/auth.ts +175 -158
- package/src/server/hooks/cors.ts +1 -1
- package/src/server/hooks/errors.ts +7 -4
- package/src/server/hooks/idempotency.ts +263 -0
- package/src/server/hooks/index.ts +15 -12
- package/src/server/hooks/logging.ts +3 -3
- package/src/server/hooks/rate-limit.ts +85 -17
- package/src/server/hooks.ts +1 -1
- package/src/server/http.ts +112 -6
- package/src/server/index.ts +63 -12
- package/src/server/instrumentation.ts +470 -0
- package/src/server/openapi.ts +4 -4
- package/src/server/providers/index.ts +6 -3
- package/src/server/providers/loadProviderConfig.ts +4 -4
- package/src/server/request-context.ts +116 -0
- package/src/server/server-context.ts +44 -0
- package/src/server/server.ts +1045 -229
- package/src/server/types.ts +2 -2
- package/src/server/use-case-route.ts +430 -0
- package/src/server-only.ts +7 -0
- package/src/tasks/index.ts +275 -0
- package/src/testing/index.ts +1153 -6
- package/src/tracing/index.ts +176 -0
- package/src/uploads/client.ts +861 -0
- package/src/uploads/index.ts +1071 -0
- package/dist/ports/mailer.d.ts +0 -6
- package/dist/ports/mailer.d.ts.map +0 -1
- package/dist/ports/mailer.js +0 -2
- package/dist/ports/mailer.js.map +0 -1
- package/dist/ports/schedules.d.ts +0 -9
- package/dist/ports/schedules.d.ts.map +0 -1
- package/dist/ports/schedules.js +0 -2
- package/dist/ports/schedules.js.map +0 -1
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Idempotency hooks for @beignet/core/server
|
|
3
|
+
*/
|
|
4
|
+
import { AppError, httpErrors } from "../../errors/index.js";
|
|
5
|
+
import { createIdempotencyFingerprint, IdempotencyConflictError, IdempotencyInProgressError, } from "../../idempotency/index.js";
|
|
6
|
+
/**
|
|
7
|
+
* Header set on replayed responses.
|
|
8
|
+
*/
|
|
9
|
+
const IDEMPOTENCY_REPLAYED_HEADER = "idempotency-replayed";
|
|
10
|
+
function defaultIdempotencyScope(ctx, meta) {
|
|
11
|
+
const mode = meta.scope ?? "global";
|
|
12
|
+
switch (mode) {
|
|
13
|
+
case "global":
|
|
14
|
+
return "global";
|
|
15
|
+
case "actor":
|
|
16
|
+
return { actorId: ctx.actor?.id };
|
|
17
|
+
case "tenant":
|
|
18
|
+
return { tenantId: ctx.tenant?.id };
|
|
19
|
+
case "actor-tenant":
|
|
20
|
+
return { actorId: ctx.actor?.id, tenantId: ctx.tenant?.id };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function isReplayableHttpResponse(value) {
|
|
24
|
+
if (typeof value !== "object" || value === null)
|
|
25
|
+
return false;
|
|
26
|
+
const candidate = value;
|
|
27
|
+
if (typeof candidate.status !== "number")
|
|
28
|
+
return false;
|
|
29
|
+
if (candidate.headers !== undefined &&
|
|
30
|
+
(typeof candidate.headers !== "object" ||
|
|
31
|
+
candidate.headers === null ||
|
|
32
|
+
Array.isArray(candidate.headers))) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create metadata-driven idempotency hooks.
|
|
39
|
+
*
|
|
40
|
+
* The hook reads `contract.metadata.idempotency` and enforces it with
|
|
41
|
+
* `ctx.ports.idempotency`. In `beforeHandle` it reserves the client key after
|
|
42
|
+
* request parsing, replays completed matching responses with an
|
|
43
|
+
* `idempotency-replayed: true` header, and rejects in-progress or conflicting
|
|
44
|
+
* keys with the framework `IdempotencyInProgress`/`IdempotencyConflict` catalog
|
|
45
|
+
* errors. In `beforeSend` it stores 2xx framework-neutral responses for replay
|
|
46
|
+
* and releases the reservation for errors, non-2xx responses, and native
|
|
47
|
+
* `Response` results, which are not replayable.
|
|
48
|
+
*
|
|
49
|
+
* Use `runIdempotently(...)` from `@beignet/core/idempotency` for non-HTTP
|
|
50
|
+
* workflows such as jobs, listeners, webhooks, and schedules.
|
|
51
|
+
*
|
|
52
|
+
* @param options - Optional namespace, scope, and fingerprint-input builders.
|
|
53
|
+
* @returns A server hook backed by `ctx.ports.idempotency`.
|
|
54
|
+
*/
|
|
55
|
+
export function createIdempotencyHooks(options = {}) {
|
|
56
|
+
const pending = new WeakMap();
|
|
57
|
+
return {
|
|
58
|
+
name: "idempotency",
|
|
59
|
+
beforeHandle: async ({ ctx, contract, req, path, query, body }) => {
|
|
60
|
+
const meta = contract.metadata?.idempotency;
|
|
61
|
+
if (!meta) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
const header = (meta.header ?? "idempotency-key").toLowerCase();
|
|
65
|
+
const key = req.headers.get(header);
|
|
66
|
+
if (!key) {
|
|
67
|
+
if (meta.required) {
|
|
68
|
+
throw new AppError(httpErrors.BadRequest, {
|
|
69
|
+
contract: contract.name,
|
|
70
|
+
header,
|
|
71
|
+
}, `Missing required idempotency key header "${header}"`);
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
const namespace = options.namespace?.({ contract: { name: contract.name } }) ??
|
|
76
|
+
`http.${contract.name}`;
|
|
77
|
+
const scope = options.scope?.({ ctx, req, meta }) ??
|
|
78
|
+
defaultIdempotencyScope(ctx, meta);
|
|
79
|
+
const fingerprint = await createIdempotencyFingerprint(options.fingerprintInput?.({ path, query, body }) ?? {
|
|
80
|
+
path,
|
|
81
|
+
query,
|
|
82
|
+
body,
|
|
83
|
+
});
|
|
84
|
+
const reservation = await ctx.ports.idempotency.reserve({
|
|
85
|
+
namespace,
|
|
86
|
+
key,
|
|
87
|
+
scope,
|
|
88
|
+
fingerprint,
|
|
89
|
+
ttlSec: meta.ttlSec,
|
|
90
|
+
});
|
|
91
|
+
switch (reservation.status) {
|
|
92
|
+
case "replay": {
|
|
93
|
+
if (!isReplayableHttpResponse(reservation.result)) {
|
|
94
|
+
throw new AppError(httpErrors.InternalServerError, { namespace, key }, `Stored idempotency result for "${namespace}" key "${key}" is not a replayable HTTP response`);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
status: reservation.result.status,
|
|
98
|
+
headers: {
|
|
99
|
+
...(reservation.result.headers ?? {}),
|
|
100
|
+
[IDEMPOTENCY_REPLAYED_HEADER]: "true",
|
|
101
|
+
},
|
|
102
|
+
body: reservation.result.body,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
case "inProgress": {
|
|
106
|
+
throw new IdempotencyInProgressError(reservation);
|
|
107
|
+
}
|
|
108
|
+
case "conflict": {
|
|
109
|
+
throw new IdempotencyConflictError(reservation);
|
|
110
|
+
}
|
|
111
|
+
case "reserved": {
|
|
112
|
+
pending.set(req, {
|
|
113
|
+
port: ctx.ports.idempotency,
|
|
114
|
+
namespace,
|
|
115
|
+
key,
|
|
116
|
+
scope,
|
|
117
|
+
fingerprint,
|
|
118
|
+
});
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
beforeSend: async ({ req, response, error, native }) => {
|
|
124
|
+
const reservation = pending.get(req);
|
|
125
|
+
if (!reservation) {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
pending.delete(req);
|
|
129
|
+
const { port, namespace, key, scope, fingerprint } = reservation;
|
|
130
|
+
if (!native &&
|
|
131
|
+
!error &&
|
|
132
|
+
response.status >= 200 &&
|
|
133
|
+
response.status < 300) {
|
|
134
|
+
await port.complete({
|
|
135
|
+
namespace,
|
|
136
|
+
key,
|
|
137
|
+
scope,
|
|
138
|
+
fingerprint,
|
|
139
|
+
result: {
|
|
140
|
+
status: response.status,
|
|
141
|
+
headers: response.headers,
|
|
142
|
+
body: response.body,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
// Errors, non-2xx responses, and native `Response` results release the
|
|
148
|
+
// reservation. Streams are not replayable.
|
|
149
|
+
await port.fail({ namespace, key, scope, fingerprint, error });
|
|
150
|
+
return undefined;
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=idempotency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../../../src/server/hooks/idempotency.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Hook utilities for @beignet/core/server
|
|
3
3
|
*/
|
|
4
|
-
import type { AnyPorts } from "../../ports";
|
|
5
|
-
import type { ServerHook } from "../http";
|
|
6
|
-
export { type AuthHookArgs, type
|
|
7
|
-
export { applyCorsHeaders, type CorsConfig, createCorsHooks, } from "./cors";
|
|
8
|
-
export { defaultMapErrorToResponse, type ErrorMappingConfig, type ErrorMappingResult, } from "./errors";
|
|
9
|
-
export {
|
|
10
|
-
export { type
|
|
4
|
+
import type { AnyPorts } from "../../ports/index.js";
|
|
5
|
+
import type { ServerHook } from "../http.js";
|
|
6
|
+
export { type AuthHookArgs, type AuthHooksOptions, type AuthRouteHooks, createAuthHooks, } from "./auth.js";
|
|
7
|
+
export { applyCorsHeaders, type CorsConfig, createCorsHooks, } from "./cors.js";
|
|
8
|
+
export { defaultMapErrorToResponse, type ErrorMappingConfig, type ErrorMappingResult, } from "./errors.js";
|
|
9
|
+
export { type CtxWithIdempotency, createIdempotencyHooks, type IdempotencyHooksOptions, type IdempotencyPorts, } from "./idempotency.js";
|
|
10
|
+
export { createLoggingHooks, type Logger, type LoggingConfig, } from "./logging.js";
|
|
11
|
+
export { type CtxWithRateLimit, createRateLimitHooks, type RateLimitIpSource, type RateLimitOptions, } from "./rate-limit.js";
|
|
11
12
|
/**
|
|
12
13
|
* Flatten hook arrays into a single hook list.
|
|
13
14
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,eAAe,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,gBAAgB,EAChB,KAAK,UAAU,EACf,eAAe,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,kBAAkB,EACvB,sBAAsB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,KAAK,MAAM,EACX,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,gBAAgB,EACrB,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,KAAK,SAAS,QAAQ,GAAG,QAAQ,EACjE,GAAG,KAAK,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE,GACvE,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAE1B"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Hook utilities for @beignet/core/server
|
|
3
3
|
*/
|
|
4
|
-
export { createAuthHooks, } from "./auth";
|
|
5
|
-
export { applyCorsHeaders, createCorsHooks, } from "./cors";
|
|
6
|
-
export { defaultMapErrorToResponse, } from "./errors";
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
4
|
+
export { createAuthHooks, } from "./auth.js";
|
|
5
|
+
export { applyCorsHeaders, createCorsHooks, } from "./cors.js";
|
|
6
|
+
export { defaultMapErrorToResponse, } from "./errors.js";
|
|
7
|
+
export { createIdempotencyHooks, } from "./idempotency.js";
|
|
8
|
+
export { createLoggingHooks, } from "./logging.js";
|
|
9
|
+
export { createRateLimitHooks, } from "./rate-limit.js";
|
|
9
10
|
/**
|
|
10
11
|
* Flatten hook arrays into a single hook list.
|
|
11
12
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAIL,eAAe,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,gBAAgB,EAEhB,eAAe,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,yBAAyB,GAG1B,MAAM,aAAa,CAAC;AACrB,OAAO,EAEL,sBAAsB,GAGvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,GAGnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAEL,oBAAoB,GAGrB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,GAAG,KAAqE;IAExE,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Logging hook utilities for @beignet/core/server
|
|
3
3
|
*/
|
|
4
|
-
import type { HttpContractConfig } from "../../contracts";
|
|
5
|
-
import type { HttpRequestLike, ServerHook } from "../types";
|
|
4
|
+
import type { HttpContractConfig } from "../../contracts/index.js";
|
|
5
|
+
import type { HttpRequestLike, ServerHook } from "../types.js";
|
|
6
6
|
/**
|
|
7
7
|
* Minimal logger shape accepted by `createLoggingHooks(...)`.
|
|
8
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../../src/server/hooks/logging.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../../src/server/hooks/logging.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG/D;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB;;OAEG;IACH,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnC;;OAEG;IACH,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACpC;;OAEG;IACH,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACpC;;OAEG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,GAAG;IAChC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE;QACtB,GAAG,CAAC,EAAE,GAAG,CAAC;QACV,GAAG,EAAE,eAAe,CAAC;QACrB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;KAC/B,KAAK,IAAI,CAAC;IACX;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE;QACpB,GAAG,CAAC,EAAE,GAAG,CAAC;QACV,GAAG,EAAE,eAAe,CAAC;QACrB,GAAG,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;SAAE,CAAC;QACzD,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;QAC9B,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,KAAK,IAAI,CAAC;CACZ;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EACpC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,GACzB,UAAU,CAAC,GAAG,CAAC,CAqFjB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../../../src/server/hooks/logging.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,uBAAuB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../../../src/server/hooks/logging.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Rate limit hooks for @beignet/core/server
|
|
3
3
|
*/
|
|
4
|
-
import type { RateLimitScope } from "../../contracts";
|
|
5
|
-
import type { ActivityActor, RateLimitPort } from "../../ports";
|
|
6
|
-
import type { HttpRequestLike, ServerHook } from "../types";
|
|
4
|
+
import type { RateLimitScope } from "../../contracts/index.js";
|
|
5
|
+
import type { ActivityActor, RateLimitPort } from "../../ports/index.js";
|
|
6
|
+
import type { HttpRequestLike, ServerHook } from "../types.js";
|
|
7
7
|
/**
|
|
8
8
|
* Ports required by rate-limit hooks.
|
|
9
9
|
*/
|
|
@@ -18,6 +18,19 @@ export type CtxWithRateLimit = {
|
|
|
18
18
|
actor?: ActivityActor;
|
|
19
19
|
};
|
|
20
20
|
type EarlyRateLimitScope = Exclude<RateLimitScope, "user">;
|
|
21
|
+
/**
|
|
22
|
+
* Strategy for resolving the client IP used by `ip`-scoped limits.
|
|
23
|
+
*
|
|
24
|
+
* - `"x-forwarded-for-last"` (default): the last `x-forwarded-for` entry.
|
|
25
|
+
* This is the address appended by the platform's trusted reverse proxy and
|
|
26
|
+
* cannot be chosen by the client.
|
|
27
|
+
* - `"x-forwarded-for-first"`: the first `x-forwarded-for` entry. This value
|
|
28
|
+
* is client-controlled, so only use it when a trusted edge normalizes the
|
|
29
|
+
* header before it reaches the app.
|
|
30
|
+
* - A function receives the raw request and returns the client IP, for
|
|
31
|
+
* platform-specific headers such as `cf-connecting-ip`.
|
|
32
|
+
*/
|
|
33
|
+
export type RateLimitIpSource = "x-forwarded-for-last" | "x-forwarded-for-first" | ((req: HttpRequestLike) => string | undefined);
|
|
21
34
|
/**
|
|
22
35
|
* Options for `createRateLimitHooks(...)`.
|
|
23
36
|
*/
|
|
@@ -42,9 +55,12 @@ export interface RateLimitOptions<Ctx> {
|
|
|
42
55
|
scope: EarlyRateLimitScope;
|
|
43
56
|
}) => string;
|
|
44
57
|
/**
|
|
45
|
-
* Resolve
|
|
58
|
+
* Resolve the client IP for `ip`-scoped limits.
|
|
59
|
+
*
|
|
60
|
+
* Defaults to `"x-forwarded-for-last"`, the entry appended by the
|
|
61
|
+
* platform's trusted proxy. Pass a function for platform-specific headers.
|
|
46
62
|
*/
|
|
47
|
-
|
|
63
|
+
ipSource?: RateLimitIpSource;
|
|
48
64
|
}
|
|
49
65
|
/**
|
|
50
66
|
* Create metadata-driven rate-limit hooks.
|
|
@@ -52,9 +68,11 @@ export interface RateLimitOptions<Ctx> {
|
|
|
52
68
|
* The hook reads `contract.metadata.rateLimit`. Global and IP-scoped limits run
|
|
53
69
|
* in `onRequest` before context creation; user-scoped limits run in
|
|
54
70
|
* `beforeHandle` after `ctx.actor` is available. Exceeded limits throw the
|
|
55
|
-
* framework `TooManyRequests` app error
|
|
71
|
+
* framework `TooManyRequests` app error with `scope`, `retryAfterSeconds`, and
|
|
72
|
+
* `resetAt` details. The bucket key is never sent to clients; denials emit a
|
|
73
|
+
* `rateLimit.denied` instrumentation event that carries the key for operators.
|
|
56
74
|
*
|
|
57
|
-
* @param options - Optional key builders and client-IP
|
|
75
|
+
* @param options - Optional key builders and client-IP source.
|
|
58
76
|
* @returns A server hook backed by `ctx.ports.rateLimit`.
|
|
59
77
|
*/
|
|
60
78
|
export declare function createRateLimitHooks<Ctx extends CtxWithRateLimit>(options?: RateLimitOptions<Ctx>): ServerHook<Ctx, RateLimitPorts>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/server/hooks/rate-limit.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/server/hooks/rate-limit.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAKzE,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,aAAa,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC;AAEF,KAAK,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;AAE3D;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,iBAAiB,GACzB,sBAAsB,GACtB,uBAAuB,GACvB,CAAC,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAAC,GAAG;IACnC;;;;OAIG;IACH,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE;QACX,GAAG,EAAE,GAAG,CAAC;QACT,GAAG,EAAE,eAAe,CAAC;QACrB,KAAK,EAAE,cAAc,CAAC;KACvB,KAAK,MAAM,CAAC;IACb;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAChB,GAAG,EAAE,eAAe,CAAC;QACrB,KAAK,EAAE,mBAAmB,CAAC;KAC5B,KAAK,MAAM,CAAC;IACb;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B;AA4HD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,SAAS,gBAAgB,EAC/D,OAAO,GAAE,gBAAgB,CAAC,GAAG,CAAM,GAClC,UAAU,CAAC,GAAG,EAAE,cAAc,CAAC,CAuDjC"}
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Rate limit hooks for @beignet/core/server
|
|
3
3
|
*/
|
|
4
|
-
import { AppError, httpErrors } from "../../errors";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { AppError, httpErrors } from "../../errors/index.js";
|
|
5
|
+
import { createProviderInstrumentation, } from "../../providers/index.js";
|
|
6
|
+
function parseForwardedFor(req) {
|
|
7
|
+
const header = req.headers.get("x-forwarded-for") ?? "";
|
|
8
|
+
return header
|
|
9
|
+
.split(",")
|
|
10
|
+
.map((entry) => entry.trim())
|
|
11
|
+
.filter(Boolean);
|
|
12
|
+
}
|
|
13
|
+
function resolveClientIp(req, ipSource) {
|
|
14
|
+
if (typeof ipSource === "function") {
|
|
15
|
+
return ipSource(req) || undefined;
|
|
16
|
+
}
|
|
17
|
+
const entries = parseForwardedFor(req);
|
|
18
|
+
if (entries.length === 0) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
return ipSource === "x-forwarded-for-first"
|
|
22
|
+
? entries[0]
|
|
23
|
+
: entries[entries.length - 1];
|
|
8
24
|
}
|
|
9
25
|
function emitUserKey(userId) {
|
|
10
26
|
return `user:${userId}`;
|
|
@@ -30,8 +46,8 @@ function defaultEarlyRateLimitKey(args, getClientIp) {
|
|
|
30
46
|
}
|
|
31
47
|
return "global";
|
|
32
48
|
}
|
|
33
|
-
async function enforceRateLimit(
|
|
34
|
-
const result = await rateLimit.hit({
|
|
49
|
+
async function enforceRateLimit(ports, args) {
|
|
50
|
+
const result = await ports.rateLimit.hit({
|
|
35
51
|
key: args.key,
|
|
36
52
|
limit: args.limit,
|
|
37
53
|
windowSec: args.windowSec,
|
|
@@ -39,8 +55,24 @@ async function enforceRateLimit(rateLimit, args) {
|
|
|
39
55
|
if (result.allowed) {
|
|
40
56
|
return;
|
|
41
57
|
}
|
|
58
|
+
// App ports may carry an `instrumentation` or `devtools` sink alongside the
|
|
59
|
+
// typed rate-limit port; the helper resolves them at runtime.
|
|
60
|
+
const instrumentation = createProviderInstrumentation(ports, {
|
|
61
|
+
providerName: "rate-limit",
|
|
62
|
+
watcher: "rateLimit",
|
|
63
|
+
});
|
|
64
|
+
instrumentation.custom({
|
|
65
|
+
name: "rateLimit.denied",
|
|
66
|
+
label: "Rate limit denied",
|
|
67
|
+
summary: `Rate limit denied for ${args.key}`,
|
|
68
|
+
details: {
|
|
69
|
+
key: args.key,
|
|
70
|
+
scope: args.scope,
|
|
71
|
+
limit: args.limit,
|
|
72
|
+
windowSec: args.windowSec,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
42
75
|
throw new AppError(httpErrors.TooManyRequests, {
|
|
43
|
-
key: args.key,
|
|
44
76
|
scope: args.scope,
|
|
45
77
|
retryAfterSeconds: result.retryAfterSeconds,
|
|
46
78
|
resetAt: result.resetAt?.toISOString() ?? null,
|
|
@@ -52,13 +84,16 @@ async function enforceRateLimit(rateLimit, args) {
|
|
|
52
84
|
* The hook reads `contract.metadata.rateLimit`. Global and IP-scoped limits run
|
|
53
85
|
* in `onRequest` before context creation; user-scoped limits run in
|
|
54
86
|
* `beforeHandle` after `ctx.actor` is available. Exceeded limits throw the
|
|
55
|
-
* framework `TooManyRequests` app error
|
|
87
|
+
* framework `TooManyRequests` app error with `scope`, `retryAfterSeconds`, and
|
|
88
|
+
* `resetAt` details. The bucket key is never sent to clients; denials emit a
|
|
89
|
+
* `rateLimit.denied` instrumentation event that carries the key for operators.
|
|
56
90
|
*
|
|
57
|
-
* @param options - Optional key builders and client-IP
|
|
91
|
+
* @param options - Optional key builders and client-IP source.
|
|
58
92
|
* @returns A server hook backed by `ctx.ports.rateLimit`.
|
|
59
93
|
*/
|
|
60
94
|
export function createRateLimitHooks(options = {}) {
|
|
61
|
-
const
|
|
95
|
+
const ipSource = options.ipSource ?? "x-forwarded-for-last";
|
|
96
|
+
const getClientIp = (req) => resolveClientIp(req, ipSource);
|
|
62
97
|
return {
|
|
63
98
|
name: "rate-limit",
|
|
64
99
|
onRequest: async ({ contract, ports, req }) => {
|
|
@@ -72,7 +107,7 @@ export function createRateLimitHooks(options = {}) {
|
|
|
72
107
|
}
|
|
73
108
|
const key = options.earlyKey?.({ req, scope }) ??
|
|
74
109
|
defaultEarlyRateLimitKey({ req, scope }, getClientIp);
|
|
75
|
-
await enforceRateLimit(ports
|
|
110
|
+
await enforceRateLimit(ports, {
|
|
76
111
|
key,
|
|
77
112
|
limit: rlMeta.max,
|
|
78
113
|
windowSec: rlMeta.windowSec,
|
|
@@ -91,7 +126,7 @@ export function createRateLimitHooks(options = {}) {
|
|
|
91
126
|
}
|
|
92
127
|
const key = options.key?.({ ctx, req, scope }) ??
|
|
93
128
|
defaultRateLimitKey({ ctx, req, scope }, getClientIp);
|
|
94
|
-
await enforceRateLimit(ctx.ports
|
|
129
|
+
await enforceRateLimit(ctx.ports, {
|
|
95
130
|
key,
|
|
96
131
|
limit: rlMeta.max,
|
|
97
132
|
windowSec: rlMeta.windowSec,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../../src/server/hooks/rate-limit.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../../src/server/hooks/rate-limit.ts"],"names":[],"mappings":"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"}
|
package/dist/server/hooks.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./hooks/index";
|
|
1
|
+
export * from "./hooks/index.js";
|
|
2
2
|
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/server/hooks.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/server/hooks.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
package/dist/server/hooks.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./hooks/index";
|
|
1
|
+
export * from "./hooks/index.js";
|
|
2
2
|
//# sourceMappingURL=hooks.js.map
|
package/dist/server/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/server/hooks.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/server/hooks.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
package/dist/server/http.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { HttpContractConfig, InferHeaderSchemaOutput, InferOutput, StandardSchema } from "../contracts";
|
|
2
|
-
import type { AnyPorts } from "../ports";
|
|
1
|
+
import type { HttpContractConfig, InferHeaderSchemaOutput, InferOutput, StandardSchema } from "../contracts/index.js";
|
|
2
|
+
import type { AnyPorts } from "../ports/index.js";
|
|
3
3
|
/**
|
|
4
4
|
* Framework-neutral request shape consumed by Beignet server adapters.
|
|
5
5
|
*
|
|
@@ -75,6 +75,41 @@ export interface HttpResponseLike {
|
|
|
75
75
|
* `Response` only when the current adapter can pass it through unchanged.
|
|
76
76
|
*/
|
|
77
77
|
export type HttpResponse = HttpResponseLike | Response;
|
|
78
|
+
/**
|
|
79
|
+
* Framework-neutral Beignet API handler consumed by HTTP adapters.
|
|
80
|
+
*/
|
|
81
|
+
export type HttpAdapterApiHandler = (req: HttpRequestLike) => Promise<HttpResponse>;
|
|
82
|
+
/**
|
|
83
|
+
* Native handler shape produced by an HTTP adapter.
|
|
84
|
+
*/
|
|
85
|
+
export type HttpAdapterHandler<NativeRequest, NativeResponse> = (req: NativeRequest) => Promise<NativeResponse>;
|
|
86
|
+
/**
|
|
87
|
+
* Contract implemented by packages that adapt Beignet's framework-neutral
|
|
88
|
+
* server runtime to a platform HTTP API.
|
|
89
|
+
*
|
|
90
|
+
* Core owns request parsing, hooks, route matching, validation, error mapping,
|
|
91
|
+
* response ownership, and provider lifecycle. Adapters own only the conversion
|
|
92
|
+
* between the platform request/response types and Beignet's `HttpRequestLike`
|
|
93
|
+
* / `HttpResponse` boundary.
|
|
94
|
+
*/
|
|
95
|
+
export interface HttpAdapter<NativeRequest, NativeResponse> {
|
|
96
|
+
/**
|
|
97
|
+
* Human-readable adapter name for diagnostics and documentation.
|
|
98
|
+
*/
|
|
99
|
+
name: string;
|
|
100
|
+
/**
|
|
101
|
+
* Convert a platform request into Beignet's framework-neutral request shape.
|
|
102
|
+
*/
|
|
103
|
+
toRequestLike(req: NativeRequest): HttpRequestLike;
|
|
104
|
+
/**
|
|
105
|
+
* Convert a Beignet response into the platform response type.
|
|
106
|
+
*/
|
|
107
|
+
toNativeResponse(res: HttpResponse): NativeResponse | Promise<NativeResponse>;
|
|
108
|
+
/**
|
|
109
|
+
* Wrap a Beignet API handler in the platform's native handler shape.
|
|
110
|
+
*/
|
|
111
|
+
createHandler(handler: HttpAdapterApiHandler): HttpAdapterHandler<NativeRequest, NativeResponse>;
|
|
112
|
+
}
|
|
78
113
|
type InferSchemaOrFallback<T extends StandardSchema | null, Fallback> = T extends StandardSchema ? InferOutput<T> : Fallback;
|
|
79
114
|
/**
|
|
80
115
|
* Infer the handler path parameter type for a contract.
|
|
@@ -101,7 +136,7 @@ export interface HandlerArgs<Ctx, C extends HttpContractConfig> {
|
|
|
101
136
|
*/
|
|
102
137
|
req: HttpRequestLike;
|
|
103
138
|
/**
|
|
104
|
-
* Application context
|
|
139
|
+
* Application context assembled by the server context blueprint.
|
|
105
140
|
*/
|
|
106
141
|
ctx: Ctx;
|
|
107
142
|
/**
|
|
@@ -133,6 +168,40 @@ export type Handler<Ctx, C extends HttpContractConfig> = (args: HandlerArgs<Ctx,
|
|
|
133
168
|
* Value or promise of that value.
|
|
134
169
|
*/
|
|
135
170
|
export type MaybePromise<T> = T | Promise<T>;
|
|
171
|
+
/**
|
|
172
|
+
* Arguments passed to a route-scoped hook after request parsing and context
|
|
173
|
+
* creation.
|
|
174
|
+
*/
|
|
175
|
+
export type RouteHookArgs<Ctx, C extends HttpContractConfig = HttpContractConfig> = HandlerArgs<Ctx, C>;
|
|
176
|
+
/**
|
|
177
|
+
* Hook that runs only for the route or route group where it is attached.
|
|
178
|
+
*
|
|
179
|
+
* Route hooks are for scoped policy and context enrichment such as
|
|
180
|
+
* authentication, tenant resolution, feature gates, and idempotency. They add
|
|
181
|
+
* fields to the handler context instead of replacing the app context.
|
|
182
|
+
*
|
|
183
|
+
* Hook additions must not include `gate`: the server re-attaches the gate
|
|
184
|
+
* declared by the context blueprint after every hook, so identity changes are
|
|
185
|
+
* picked up automatically.
|
|
186
|
+
*/
|
|
187
|
+
export interface RouteHook<Ctx, AddedCtx extends object & {
|
|
188
|
+
gate?: never;
|
|
189
|
+
} = Record<string, never>> {
|
|
190
|
+
/**
|
|
191
|
+
* Optional name used in diagnostics and devtools.
|
|
192
|
+
*/
|
|
193
|
+
name?: string;
|
|
194
|
+
/**
|
|
195
|
+
* Resolve additional context for this route or throw to stop handling.
|
|
196
|
+
*/
|
|
197
|
+
resolve: (args: RouteHookArgs<Ctx>) => MaybePromise<AddedCtx | undefined>;
|
|
198
|
+
}
|
|
199
|
+
type AddedCtxFromHook<Hook> = Hook extends RouteHook<infer _Ctx, infer AddedCtx> ? AddedCtx : unknown;
|
|
200
|
+
type UnionToIntersection<Union> = (Union extends unknown ? (value: Union) => void : never) extends (value: infer Intersection) => void ? Intersection : never;
|
|
201
|
+
/**
|
|
202
|
+
* Intersection of the context fields added by a route hook list.
|
|
203
|
+
*/
|
|
204
|
+
export type AddedCtxFromHooks<Hooks extends readonly unknown[]> = Hooks extends readonly [] ? unknown : UnionToIntersection<AddedCtxFromHook<Hooks[number]>>;
|
|
136
205
|
/**
|
|
137
206
|
* Hook that runs after a route is matched but before request parsing and
|
|
138
207
|
* context creation.
|
|
@@ -168,10 +237,11 @@ export type BeforeHandleHook<Ctx, C extends HttpContractConfig = HttpContractCon
|
|
|
168
237
|
body: InferBody<C>;
|
|
169
238
|
}) => MaybePromise<BeforeHandleResult<Ctx>>;
|
|
170
239
|
/**
|
|
171
|
-
* Hook that runs before
|
|
240
|
+
* Hook that runs before the response is returned.
|
|
172
241
|
*
|
|
173
242
|
* Return a response to replace or decorate the outgoing response. Hooks run in
|
|
174
|
-
* declaration order.
|
|
243
|
+
* declaration order. For native web `Response` results the hook receives a
|
|
244
|
+
* headers-only view and only header changes are applied.
|
|
175
245
|
*/
|
|
176
246
|
export type BeforeSendHook<Ctx, C extends HttpContractConfig = HttpContractConfig> = (args: {
|
|
177
247
|
req: HttpRequestLike;
|
|
@@ -183,6 +253,13 @@ export type BeforeSendHook<Ctx, C extends HttpContractConfig = HttpContractConfi
|
|
|
183
253
|
body?: InferBody<C>;
|
|
184
254
|
response: HttpResponseLike;
|
|
185
255
|
error?: unknown;
|
|
256
|
+
/**
|
|
257
|
+
* True when the route returned a native web Response. The response argument
|
|
258
|
+
* is a headers-only view ({ status, headers }); the body is not readable and
|
|
259
|
+
* returned body/status changes are ignored. Header changes are merged onto
|
|
260
|
+
* the native Response.
|
|
261
|
+
*/
|
|
262
|
+
native?: boolean;
|
|
186
263
|
}) => MaybePromise<HttpResponseLike | undefined>;
|
|
187
264
|
/**
|
|
188
265
|
* Hook that runs after the response has been prepared.
|
|
@@ -253,7 +330,8 @@ export interface ServerHook<Ctx, Ports extends AnyPorts = AnyPorts> {
|
|
|
253
330
|
*/
|
|
254
331
|
beforeHandle?: BeforeHandleHook<Ctx>;
|
|
255
332
|
/**
|
|
256
|
-
* Runs before
|
|
333
|
+
* Runs before the response is returned. Native web `Response` results get a
|
|
334
|
+
* headers-only view with `native: true`.
|
|
257
335
|
*/
|
|
258
336
|
beforeSend?: BeforeSendHook<Ctx>;
|
|
259
337
|
/**
|