@lovrozagar/honey 0.1.1 → 0.2.0
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/README.md +645 -542
- package/dist/accepts.d.ts +6 -0
- package/dist/accepts.d.ts.map +1 -0
- package/dist/body-limit.d.ts +22 -0
- package/dist/body-limit.d.ts.map +1 -0
- package/dist/build/index.d.ts +42 -0
- package/dist/build/index.d.ts.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/client/error.d.ts +14 -0
- package/dist/client/error.d.ts.map +1 -0
- package/dist/client/http.d.ts +88 -0
- package/dist/client/http.d.ts.map +1 -0
- package/dist/client/index.d.ts +22 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/sdk.d.ts +34 -0
- package/dist/client/sdk.d.ts.map +1 -0
- package/dist/client/sse.d.ts +10 -0
- package/dist/client/sse.d.ts.map +1 -0
- package/dist/client/types.d.ts +124 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/ws.d.ts +15 -0
- package/dist/client/ws.d.ts.map +1 -0
- package/dist/client-mcp/server.d.ts +10 -0
- package/dist/client-mcp/server.d.ts.map +1 -0
- package/dist/client-mcp/types.d.ts +16 -0
- package/dist/client-mcp/types.d.ts.map +1 -0
- package/dist/cloudflare/realtime.d.ts +30 -0
- package/dist/cloudflare/realtime.d.ts.map +1 -0
- package/dist/codegen-go-cli.d.ts +22 -0
- package/dist/codegen-go-cli.d.ts.map +1 -0
- package/dist/codegen-go.d.ts +29 -0
- package/dist/codegen-go.d.ts.map +1 -0
- package/dist/codegen-hash.d.ts +3 -0
- package/dist/codegen-hash.d.ts.map +1 -0
- package/dist/codegen-ir.d.ts +123 -0
- package/dist/codegen-ir.d.ts.map +1 -0
- package/dist/codegen-loaders.d.ts +12 -0
- package/dist/codegen-loaders.d.ts.map +1 -0
- package/dist/codegen-mcp.d.ts +13 -0
- package/dist/codegen-mcp.d.ts.map +1 -0
- package/dist/codegen-python.d.ts +21 -0
- package/dist/codegen-python.d.ts.map +1 -0
- package/dist/codegen-rust.d.ts +18 -0
- package/dist/codegen-rust.d.ts.map +1 -0
- package/dist/codegen-sanitize.d.ts +3 -0
- package/dist/codegen-sanitize.d.ts.map +1 -0
- package/dist/codegen-schema-naming.d.ts +22 -0
- package/dist/codegen-schema-naming.d.ts.map +1 -0
- package/dist/codegen.d.ts +178 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/context.d.ts +56 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/cookie-sign.d.ts +3 -0
- package/dist/cookie-sign.d.ts.map +1 -0
- package/dist/cookie.d.ts +12 -0
- package/dist/cookie.d.ts.map +1 -0
- package/dist/cors.d.ts +13 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/crypto.d.ts +2 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/csrf.d.ts +9 -0
- package/dist/csrf.d.ts.map +1 -0
- package/dist/curl-logger.d.ts +33 -0
- package/dist/curl-logger.d.ts.map +1 -0
- package/dist/detect-runtime.d.ts +16 -0
- package/dist/detect-runtime.d.ts.map +1 -0
- package/dist/error.d.ts +21 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/errors.d.ts +40 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/etag.d.ts +9 -0
- package/dist/etag.d.ts.map +1 -0
- package/dist/feature-load.d.ts +3 -0
- package/dist/feature-load.d.ts.map +1 -0
- package/dist/go-type-emitter.d.ts +33 -0
- package/dist/go-type-emitter.d.ts.map +1 -0
- package/dist/honey-response.d.ts +82 -0
- package/dist/honey-response.d.ts.map +1 -0
- package/dist/i18n-slot.d.ts +7 -0
- package/dist/i18n-slot.d.ts.map +1 -0
- package/dist/i18n.d.ts +26 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/index.d.ts +470 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/init.d.ts +7 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/input.d.ts +6 -0
- package/dist/input.d.ts.map +1 -0
- package/dist/ip-restrict.d.ts +12 -0
- package/dist/ip-restrict.d.ts.map +1 -0
- package/dist/logger.d.ts +45 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/meta-spec.d.ts +81 -0
- package/dist/meta-spec.d.ts.map +1 -0
- package/dist/middleware.d.ts +60 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/node-request.d.ts +59 -0
- package/dist/node-request.d.ts.map +1 -0
- package/dist/node.d.ts +13 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/openapi/register.d.ts +3 -0
- package/dist/openapi/register.d.ts.map +1 -0
- package/dist/openapi/scalar.d.ts +12 -0
- package/dist/openapi/scalar.d.ts.map +1 -0
- package/dist/openapi/spec-factory.d.ts +31 -0
- package/dist/openapi/spec-factory.d.ts.map +1 -0
- package/dist/openapi/spec.d.ts +19 -0
- package/dist/openapi/spec.d.ts.map +1 -0
- package/dist/openapi/swagger.d.ts +12 -0
- package/dist/openapi/swagger.d.ts.map +1 -0
- package/dist/plugin.d.ts +167 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/powered-by.d.ts +7 -0
- package/dist/powered-by.d.ts.map +1 -0
- package/dist/pretty-json.d.ts +10 -0
- package/dist/pretty-json.d.ts.map +1 -0
- package/dist/proxy.d.ts +51 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/python-type-emitter.d.ts +11 -0
- package/dist/python-type-emitter.d.ts.map +1 -0
- package/dist/realtime/buffer.d.ts +18 -0
- package/dist/realtime/buffer.d.ts.map +1 -0
- package/dist/realtime/bus.d.ts +11 -0
- package/dist/realtime/bus.d.ts.map +1 -0
- package/dist/realtime/longpoll.d.ts +23 -0
- package/dist/realtime/longpoll.d.ts.map +1 -0
- package/dist/realtime/protocol.d.ts +50 -0
- package/dist/realtime/protocol.d.ts.map +1 -0
- package/dist/realtime/route.d.ts +42 -0
- package/dist/realtime/route.d.ts.map +1 -0
- package/dist/realtime/runtime.d.ts +74 -0
- package/dist/realtime/runtime.d.ts.map +1 -0
- package/dist/request-id.d.ts +12 -0
- package/dist/request-id.d.ts.map +1 -0
- package/dist/request-to-curl.d.ts +9 -0
- package/dist/request-to-curl.d.ts.map +1 -0
- package/dist/response.d.ts +57 -0
- package/dist/response.d.ts.map +1 -0
- package/dist/rust-type-emitter.d.ts +32 -0
- package/dist/rust-type-emitter.d.ts.map +1 -0
- package/dist/secure-headers.d.ts +16 -0
- package/dist/secure-headers.d.ts.map +1 -0
- package/dist/serve-register.d.ts +2 -0
- package/dist/serve-register.d.ts.map +1 -0
- package/dist/serve-slot.d.ts +6 -0
- package/dist/serve-slot.d.ts.map +1 -0
- package/dist/serve.d.ts +21 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/server-timing.d.ts +9 -0
- package/dist/server-timing.d.ts.map +1 -0
- package/dist/static.d.ts +12 -0
- package/dist/static.d.ts.map +1 -0
- package/dist/telemetry/otel.d.ts +59 -0
- package/dist/telemetry/otel.d.ts.map +1 -0
- package/dist/testing.d.ts +24 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/timeout.d.ts +7 -0
- package/dist/timeout.d.ts.map +1 -0
- package/dist/tree.d.ts +90 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/ts-type-emitter.d.ts +4 -0
- package/dist/ts-type-emitter.d.ts.map +1 -0
- package/dist/type-emitter.d.ts +8 -0
- package/dist/type-emitter.d.ts.map +1 -0
- package/dist/type-extractor.d.ts +39 -0
- package/dist/type-extractor.d.ts.map +1 -0
- package/dist/types.d.ts +451 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/validation.d.ts +18 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/ws/bun.d.ts +28 -0
- package/dist/ws/bun.d.ts.map +1 -0
- package/dist/ws/cloudflare.d.ts +55 -0
- package/dist/ws/cloudflare.d.ts.map +1 -0
- package/dist/ws/deno.d.ts +13 -0
- package/dist/ws/deno.d.ts.map +1 -0
- package/dist/ws/node.d.ts +10 -0
- package/dist/ws/node.d.ts.map +1 -0
- package/dist/yaml.d.ts +4 -0
- package/dist/yaml.d.ts.map +1 -0
- package/docs/api-ref/go.md +82 -82
- package/docs/api-ref/python.md +82 -82
- package/docs/api-ref/rust.md +82 -82
- package/docs/api-ref/typescript.md +82 -82
- package/docs/invalidate-spec.md +13 -13
- package/docs/meta-spec.md +513 -0
- package/docs/packaging.md +85 -0
- package/docs/sdk.md +87 -73
- package/package.json +188 -50
- package/src/body-limit.ts +2 -1
- package/src/build/index.ts +2 -9
- package/src/cli.ts +1 -2
- package/src/client/http.ts +204 -223
- package/src/client/index.ts +6 -14
- package/src/client/sdk.ts +24 -17
- package/src/client/types.ts +4 -9
- package/src/client/ws.ts +3 -5
- package/src/client-mcp/server.ts +1 -4
- package/src/cloudflare/realtime.ts +8 -10
- package/src/codegen-go-cli.ts +74 -67
- package/src/codegen-go.ts +71 -51
- package/src/codegen-ir.ts +16 -27
- package/src/codegen-loaders.ts +5 -8
- package/src/codegen-mcp.ts +9 -27
- package/src/codegen-python.ts +47 -27
- package/src/codegen-rust.ts +110 -66
- package/src/codegen-sanitize.ts +1 -7
- package/src/codegen-schema-naming.ts +8 -2
- package/src/codegen.ts +299 -196
- package/src/context.ts +91 -95
- package/src/cookie-sign.ts +5 -13
- package/src/cookie.ts +1 -2
- package/src/cors.ts +4 -6
- package/src/crypto.ts +1 -4
- package/src/csrf.ts +3 -3
- package/src/curl-logger.ts +14 -32
- package/src/detect-runtime.ts +1 -3
- package/src/errors.ts +1 -3
- package/src/etag.ts +3 -4
- package/src/go-type-emitter.ts +45 -40
- package/src/honey-response.ts +1 -3
- package/src/i18n.ts +3 -12
- package/src/index.ts +1122 -1409
- package/src/ip-restrict.ts +2 -1
- package/src/logger.ts +13 -13
- package/src/meta-spec.ts +0 -0
- package/src/middleware.ts +111 -79
- package/src/node.ts +2 -8
- package/src/openapi/register.ts +1 -2
- package/src/openapi/scalar.ts +9 -9
- package/src/openapi/spec-factory.ts +2 -2
- package/src/openapi/spec.ts +26 -19
- package/src/openapi/swagger.ts +9 -9
- package/src/plugin.ts +226 -256
- package/src/powered-by.ts +2 -1
- package/src/pretty-json.ts +2 -1
- package/src/proxy.ts +1 -3
- package/src/python-type-emitter.ts +37 -12
- package/src/realtime/route.ts +1 -1
- package/src/realtime/runtime.ts +9 -12
- package/src/request-id.ts +3 -4
- package/src/request-to-curl.ts +1 -4
- package/src/response.ts +150 -199
- package/src/rust-type-emitter.ts +66 -25
- package/src/secure-headers.ts +2 -1
- package/src/serve.ts +3 -12
- package/src/server-timing.ts +2 -1
- package/src/static.ts +3 -4
- package/src/telemetry/otel.ts +1 -6
- package/src/testing.ts +2 -11
- package/src/timeout.ts +2 -1
- package/src/tree.ts +12 -27
- package/src/ts-type-emitter.ts +71 -15
- package/src/type-emitter.ts +226 -237
- package/src/type-extractor.ts +260 -334
- package/src/types.ts +168 -68
- package/src/validation.ts +7 -30
- package/src/ws/bun.ts +2 -9
- package/src/ws/cloudflare.ts +13 -16
- package/src/ws/deno.ts +31 -40
- package/src/ws/node.ts +4 -19
package/src/index.ts
CHANGED
|
@@ -1,62 +1,47 @@
|
|
|
1
|
-
import { HoneyContext } from "./context.ts"
|
|
2
|
-
import { HoneyError } from "./error.ts"
|
|
3
|
-
import { ERROR_META } from "./errors.ts"
|
|
4
|
-
import type { MiddlewareFn, RuntimeMiddleware } from "./middleware.ts"
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
1
|
+
import { HoneyContext } from "./context.ts"
|
|
2
|
+
import { HoneyError } from "./error.ts"
|
|
3
|
+
import { ERROR_META } from "./errors.ts"
|
|
4
|
+
import type { MiddlewareFn, RuntimeMiddleware } from "./middleware.ts"
|
|
5
|
+
import { collectMiddlewareMeta } from "./middleware.ts"
|
|
6
|
+
import { compileChain, executeChain } from "./middleware.ts"
|
|
7
|
+
import type { ProxyConfig } from "./proxy.ts"
|
|
8
|
+
import { createProxyHandler } from "./proxy.ts"
|
|
9
|
+
import type { CustomErrorFormatter, ErrorFormatter, ResponseOptions, TypedResponse } from "./response.ts"
|
|
10
|
+
import { createErrorResponse, type HoneyRes } from "./response.ts"
|
|
11
|
+
import type { OutputValidator, RouteHandler, RouteTree, TreeNode, WSRouteHandler } from "./tree.ts"
|
|
12
|
+
import { createNode, insertRoute, insertWsRoute, matchRoute, matchWsRoute, mergeInto } from "./tree.ts"
|
|
13
|
+
import { createBus } from "./realtime/bus.ts"
|
|
14
|
+
import type { RealtimeBus } from "./realtime/bus.ts"
|
|
15
|
+
import { createConnContext } from "./realtime/route.ts"
|
|
16
|
+
import type { RealtimeRouteOpts } from "./realtime/route.ts"
|
|
8
17
|
import type {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
ResponseOptions,
|
|
12
|
-
TypedResponse,
|
|
13
|
-
} from "./response.ts";
|
|
14
|
-
import { createErrorResponse, type HoneyRes } from "./response.ts";
|
|
15
|
-
import type {
|
|
16
|
-
OutputValidator,
|
|
17
|
-
RouteHandler,
|
|
18
|
-
RouteTree,
|
|
19
|
-
TreeNode,
|
|
20
|
-
WSRouteHandler,
|
|
21
|
-
} from "./tree.ts";
|
|
22
|
-
import {
|
|
23
|
-
createNode,
|
|
24
|
-
insertRoute,
|
|
25
|
-
insertWsRoute,
|
|
26
|
-
matchRoute,
|
|
27
|
-
matchWsRoute,
|
|
28
|
-
mergeInto,
|
|
29
|
-
} from "./tree.ts";
|
|
30
|
-
import { createBus } from "./realtime/bus.ts";
|
|
31
|
-
import type { RealtimeBus } from "./realtime/bus.ts";
|
|
32
|
-
import { createConnContext } from "./realtime/route.ts";
|
|
33
|
-
import type { RealtimeRouteOpts } from "./realtime/route.ts";
|
|
34
|
-
import type {
|
|
35
|
-
ComputeErrorsByStatus,
|
|
36
|
-
DefaultMeta,
|
|
18
|
+
ComputeErrorsByStatus,
|
|
19
|
+
DefaultMeta,
|
|
37
20
|
ExtractSchemas,
|
|
38
21
|
HttpMethod,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
22
|
+
InferInputMap,
|
|
23
|
+
InferOutput,
|
|
24
|
+
HoneyMetaSpec,
|
|
25
|
+
InputSchemasDef,
|
|
26
|
+
MergePath,
|
|
27
|
+
MergeRoute,
|
|
28
|
+
MetaSpecConfig,
|
|
29
|
+
OutputSchemaDef,
|
|
30
|
+
ParamsFromPath,
|
|
31
|
+
StandardSchemaLike,
|
|
32
|
+
StatusKey,
|
|
48
33
|
TapContext,
|
|
49
|
-
} from "./types.ts"
|
|
50
|
-
import { codeToStatusKey, EK, EMPTY_OBJ, SK } from "./types.ts"
|
|
51
|
-
import { validateInput, validateOutput } from "./validation.ts"
|
|
52
|
-
import type { WSAdapter, WSContext, WSHandler } from "./ws/cloudflare.ts"
|
|
53
|
-
import { loadHoneyFeature } from "./feature-load.ts"
|
|
54
|
-
import { getI18nRuntime } from "./i18n-slot.ts"
|
|
55
|
-
import { getOpenApiRuntime } from "./openapi/spec-factory.ts"
|
|
56
|
-
import { getServeRuntime } from "./serve-slot.ts"
|
|
57
|
-
import type { HoneyServeOptions, ServeHandle } from "./serve.ts"
|
|
58
|
-
|
|
59
|
-
export { HoneyContext } from "./context.ts"
|
|
34
|
+
} from "./types.ts"
|
|
35
|
+
import { codeToStatusKey, EK, EMPTY_OBJ, SK } from "./types.ts"
|
|
36
|
+
import { validateInput, validateOutput } from "./validation.ts"
|
|
37
|
+
import type { WSAdapter, WSContext, WSHandler } from "./ws/cloudflare.ts"
|
|
38
|
+
import { loadHoneyFeature } from "./feature-load.ts"
|
|
39
|
+
import { getI18nRuntime } from "./i18n-slot.ts"
|
|
40
|
+
import { getOpenApiRuntime } from "./openapi/spec-factory.ts"
|
|
41
|
+
import { getServeRuntime } from "./serve-slot.ts"
|
|
42
|
+
import type { HoneyServeOptions, ServeHandle } from "./serve.ts"
|
|
43
|
+
|
|
44
|
+
export { HoneyContext } from "./context.ts"
|
|
60
45
|
/** HoneyContext without internal backing fields — use this for consumer-facing types */
|
|
61
46
|
export type HoneyCtx<TEnv = Record<string, unknown>> = Omit<
|
|
62
47
|
import("./context.ts").HoneyContext<TEnv>,
|
|
@@ -68,14 +53,14 @@ export type HoneyCtx<TEnv = Record<string, unknown>> = Omit<
|
|
|
68
53
|
| "_lzSearchAll"
|
|
69
54
|
| "_lzUrlFn"
|
|
70
55
|
| "_setErrors"
|
|
71
|
-
|
|
72
|
-
export { HoneyError } from "./error.ts"
|
|
73
|
-
export { defineErrors, ERROR_META } from "./errors.ts"
|
|
74
|
-
export type { ErrorMetaEntry } from "./errors.ts"
|
|
75
|
-
export type { MiddlewareFn } from "./middleware.ts"
|
|
76
|
-
export { createMiddleware, defineMiddleware } from "./middleware.ts"
|
|
77
|
-
export type { ProxyConfig } from "./proxy.ts"
|
|
78
|
-
export type { PendingTap, TapContext, TapHandler } from "./types.ts"
|
|
56
|
+
>
|
|
57
|
+
export { HoneyError } from "./error.ts"
|
|
58
|
+
export { defineErrors, ERROR_META } from "./errors.ts"
|
|
59
|
+
export type { ErrorMetaEntry } from "./errors.ts"
|
|
60
|
+
export type { MiddlewareFn } from "./middleware.ts"
|
|
61
|
+
export { createMiddleware, defineMiddleware } from "./middleware.ts"
|
|
62
|
+
export type { ProxyConfig } from "./proxy.ts"
|
|
63
|
+
export type { PendingTap, TapContext, TapHandler } from "./types.ts"
|
|
79
64
|
export type {
|
|
80
65
|
CookieOptions,
|
|
81
66
|
CustomErrorFormatter,
|
|
@@ -85,9 +70,9 @@ export type {
|
|
|
85
70
|
SSEOptions,
|
|
86
71
|
SSEStream,
|
|
87
72
|
TypedResponse,
|
|
88
|
-
} from "./response.ts"
|
|
89
|
-
export { createErrorResponse, HoneyRes } from "./response.ts"
|
|
90
|
-
export { mergeTree } from "./tree.ts"
|
|
73
|
+
} from "./response.ts"
|
|
74
|
+
export { createErrorResponse, HoneyRes } from "./response.ts"
|
|
75
|
+
export { mergeTree } from "./tree.ts"
|
|
91
76
|
export type {
|
|
92
77
|
DefaultMeta,
|
|
93
78
|
HoneyCodegen,
|
|
@@ -110,6 +95,18 @@ export type {
|
|
|
110
95
|
InferRoutes,
|
|
111
96
|
InputSchemasDef,
|
|
112
97
|
MergePath,
|
|
98
|
+
HoneyMetaSpec,
|
|
99
|
+
MetaSpecConfig,
|
|
100
|
+
MetaSpecContext,
|
|
101
|
+
MetaSpecEntry,
|
|
102
|
+
MetaSpecExpand,
|
|
103
|
+
MetaSpecProfile,
|
|
104
|
+
MetaSpecSchemaEntry,
|
|
105
|
+
MetaSpecSchemaSearch,
|
|
106
|
+
MetaSpecSchemaSource,
|
|
107
|
+
MetaSpecSingle,
|
|
108
|
+
MetaSpecStrictness,
|
|
109
|
+
MetaSpecTarget,
|
|
113
110
|
OpenApiMeta,
|
|
114
111
|
OutputSchemaDef,
|
|
115
112
|
Overwrite,
|
|
@@ -118,114 +115,92 @@ export type {
|
|
|
118
115
|
StandardSchemaLike,
|
|
119
116
|
StatusKey,
|
|
120
117
|
SuccessStatusKey,
|
|
121
|
-
} from "./types.ts"
|
|
122
|
-
export type { WSAdapter, WSContext, WSHandler, WSPreUpgrade } from "./ws/cloudflare.ts"
|
|
123
|
-
export type { ConnContext, RealtimeRouteOpts } from "./realtime/route.ts"
|
|
124
|
-
export type { RealtimeBus } from "./realtime/bus.ts"
|
|
125
|
-
export type { HoneyServeOptions, ServeHandle } from "./serve.ts"
|
|
126
|
-
export type { ServeRuntime } from "./detect-runtime.ts"
|
|
127
|
-
export { detectRuntime } from "./detect-runtime.ts"
|
|
118
|
+
} from "./types.ts"
|
|
119
|
+
export type { WSAdapter, WSContext, WSHandler, WSPreUpgrade } from "./ws/cloudflare.ts"
|
|
120
|
+
export type { ConnContext, RealtimeRouteOpts } from "./realtime/route.ts"
|
|
121
|
+
export type { RealtimeBus } from "./realtime/bus.ts"
|
|
122
|
+
export type { HoneyServeOptions, ServeHandle } from "./serve.ts"
|
|
123
|
+
export type { ServeRuntime } from "./detect-runtime.ts"
|
|
124
|
+
export { detectRuntime } from "./detect-runtime.ts"
|
|
128
125
|
|
|
129
126
|
/** Type predicate for incoming wire-protocol msg frames from the realtime client. */
|
|
130
127
|
function isMsgFrame(value: unknown): value is { data: unknown; t: "msg" } {
|
|
131
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return false
|
|
132
|
-
if (!("t" in value) || !("data" in value)) return false
|
|
133
|
-
return value.t === "msg"
|
|
128
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false
|
|
129
|
+
if (!("t" in value) || !("data" in value)) return false
|
|
130
|
+
return value.t === "msg"
|
|
134
131
|
}
|
|
135
132
|
|
|
136
133
|
type TelemetryAdapter = {
|
|
137
|
-
onError?(ctx: {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}): void
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
path: string;
|
|
147
|
-
status: number;
|
|
148
|
-
}): void;
|
|
149
|
-
onMethodNotAllowed?(ctx: {
|
|
150
|
-
allowed: string[];
|
|
151
|
-
method: string;
|
|
152
|
-
path: string;
|
|
153
|
-
req: Request;
|
|
154
|
-
}): void;
|
|
155
|
-
onMiddleware?(ctx: { duration: number; error?: unknown; name: string }): void;
|
|
156
|
-
onNotFound?(ctx: { method: string; path: string; req: Request }): void;
|
|
157
|
-
onRequest?(ctx: { env: unknown; req: Request }): void;
|
|
158
|
-
onResponse?(ctx: { duration: number; req: Request; status: number }): void;
|
|
159
|
-
onRoute?(ctx: {
|
|
160
|
-
method: string;
|
|
161
|
-
params: Record<string, string>;
|
|
162
|
-
path: string;
|
|
163
|
-
req: Request;
|
|
164
|
-
}): void;
|
|
165
|
-
};
|
|
134
|
+
onError?(ctx: { duration: number; error: HoneyError; method: string; path: string }): void
|
|
135
|
+
onHandler?(ctx: { duration: number; method: string; path: string; status: number }): void
|
|
136
|
+
onMethodNotAllowed?(ctx: { allowed: string[]; method: string; path: string; req: Request }): void
|
|
137
|
+
onMiddleware?(ctx: { duration: number; error?: unknown; name: string }): void
|
|
138
|
+
onNotFound?(ctx: { method: string; path: string; req: Request }): void
|
|
139
|
+
onRequest?(ctx: { env: unknown; req: Request }): void
|
|
140
|
+
onResponse?(ctx: { duration: number; req: Request; status: number }): void
|
|
141
|
+
onRoute?(ctx: { method: string; params: Record<string, string>; path: string; req: Request }): void
|
|
142
|
+
}
|
|
166
143
|
|
|
167
144
|
type ErrorI18nConfig<TEnv> = {
|
|
168
|
-
errors?: Record<string, Record<string, string
|
|
169
|
-
fieldNames?: Record<string, Record<string, string
|
|
145
|
+
errors?: Record<string, Record<string, string>>
|
|
146
|
+
fieldNames?: Record<string, Record<string, string>>
|
|
170
147
|
resolveLocale: (ctx: {
|
|
171
|
-
cookies: Record<string, string
|
|
172
|
-
env: TEnv
|
|
173
|
-
headers: Record<string, string
|
|
174
|
-
params: Record<string, string
|
|
175
|
-
req: Request
|
|
176
|
-
search: Record<string, string
|
|
177
|
-
}) => string | Promise<string
|
|
178
|
-
}
|
|
148
|
+
cookies: Record<string, string>
|
|
149
|
+
env: TEnv
|
|
150
|
+
headers: Record<string, string>
|
|
151
|
+
params: Record<string, string>
|
|
152
|
+
req: Request
|
|
153
|
+
search: Record<string, string>
|
|
154
|
+
}) => string | Promise<string>
|
|
155
|
+
}
|
|
179
156
|
|
|
180
157
|
type Logger = {
|
|
181
|
-
warn?(msg: string, ...args: unknown[]): void
|
|
182
|
-
}
|
|
158
|
+
warn?(msg: string, ...args: unknown[]): void
|
|
159
|
+
}
|
|
183
160
|
|
|
184
161
|
function mergePath(base: string, path: string): string {
|
|
185
|
-
if (base === "/") return path
|
|
186
|
-
if (path === "/") return base
|
|
187
|
-
return `${base}${path}
|
|
162
|
+
if (base === "/") return path
|
|
163
|
+
if (path === "/") return base
|
|
164
|
+
return `${base}${path}`
|
|
188
165
|
}
|
|
189
166
|
|
|
190
167
|
/** Normalize a scope prefix: ensure leading slash, strip trailing slash (except "/"). */
|
|
191
168
|
function normalizeScopePath(raw: string): string {
|
|
192
|
-
let p = raw
|
|
193
|
-
if (p.length === 0) return "/"
|
|
194
|
-
if (p.charCodeAt(0) !== 47) p = `/${p}
|
|
195
|
-
if (p.length > 1 && p.endsWith("/")) p = p.slice(0, -1)
|
|
196
|
-
return p
|
|
169
|
+
let p = raw
|
|
170
|
+
if (p.length === 0) return "/"
|
|
171
|
+
if (p.charCodeAt(0) !== 47) p = `/${p}`
|
|
172
|
+
if (p.length > 1 && p.endsWith("/")) p = p.slice(0, -1)
|
|
173
|
+
return p
|
|
197
174
|
}
|
|
198
175
|
|
|
199
176
|
/** Check whether fullPath falls under scope prefix — exact match or next char is '/'. */
|
|
200
177
|
function scopeMatches(prefix: string, fullPath: string): boolean {
|
|
201
|
-
if (prefix === "/") return true
|
|
202
|
-
if (fullPath === prefix) return true
|
|
203
|
-
if (fullPath.length <= prefix.length) return false
|
|
204
|
-
if (fullPath.charCodeAt(prefix.length) !== 47) return false
|
|
205
|
-
return fullPath.startsWith(prefix)
|
|
178
|
+
if (prefix === "/") return true
|
|
179
|
+
if (fullPath === prefix) return true
|
|
180
|
+
if (fullPath.length <= prefix.length) return false
|
|
181
|
+
if (fullPath.charCodeAt(prefix.length) !== 47) return false
|
|
182
|
+
return fullPath.startsWith(prefix)
|
|
206
183
|
}
|
|
207
184
|
|
|
208
185
|
type HoneyGraph = {
|
|
209
186
|
handlerMap: Record<string, RouteHandler> | null
|
|
210
187
|
hasRouteTree: boolean
|
|
188
|
+
/** Codegen-time meta → OpenAPI policy. Never read on the request path */
|
|
189
|
+
metaSpec: MetaSpecConfig | null
|
|
211
190
|
realtimeBus: RealtimeBus | null
|
|
212
191
|
root: TreeNode
|
|
213
192
|
}
|
|
214
193
|
|
|
215
|
-
function walkTreeHandlers(
|
|
216
|
-
root: TreeNode,
|
|
217
|
-
cb: (h: RouteHandler) => void,
|
|
218
|
-
wsCb?: (h: WSRouteHandler) => void,
|
|
219
|
-
): void {
|
|
194
|
+
function walkTreeHandlers(root: TreeNode, cb: (h: RouteHandler) => void, wsCb?: (h: WSRouteHandler) => void): void {
|
|
220
195
|
if (root.m) {
|
|
221
|
-
for (const h of Object.values(root.m) as RouteHandler[]) cb(h)
|
|
196
|
+
for (const h of Object.values(root.m) as RouteHandler[]) cb(h)
|
|
222
197
|
}
|
|
223
198
|
if (root.w) {
|
|
224
|
-
for (const h of Object.values(root.w.m) as RouteHandler[]) cb(h)
|
|
199
|
+
for (const h of Object.values(root.w.m) as RouteHandler[]) cb(h)
|
|
225
200
|
}
|
|
226
|
-
if (root.ws && wsCb) wsCb(root.ws)
|
|
227
|
-
for (const child of Object.values(root.s)) walkTreeHandlers(child, cb, wsCb)
|
|
228
|
-
if (root.d) walkTreeHandlers(root.d.c, cb, wsCb)
|
|
201
|
+
if (root.ws && wsCb) wsCb(root.ws)
|
|
202
|
+
for (const child of Object.values(root.s)) walkTreeHandlers(child, cb, wsCb)
|
|
203
|
+
if (root.d) walkTreeHandlers(root.d.c, cb, wsCb)
|
|
229
204
|
}
|
|
230
205
|
|
|
231
206
|
const EMPTY_PARAMS = EMPTY_OBJ as Record<string, string>
|
|
@@ -233,49 +208,49 @@ const EMPTY_MW: RuntimeMiddleware[] = []
|
|
|
233
208
|
|
|
234
209
|
function requestIsWsUpgrade(request: Request): boolean {
|
|
235
210
|
try {
|
|
236
|
-
return request.headers.get("upgrade")?.toLowerCase() === "websocket"
|
|
211
|
+
return request.headers.get("upgrade")?.toLowerCase() === "websocket"
|
|
237
212
|
} catch {
|
|
238
213
|
/* Deno closes the Request after upgradeWebSocket */
|
|
239
|
-
return false
|
|
214
|
+
return false
|
|
240
215
|
}
|
|
241
216
|
}
|
|
242
217
|
|
|
243
218
|
/** Find first '?' or '#' in url starting from pos */
|
|
244
219
|
function findSearchOrHash(url: string, pos: number): number {
|
|
245
220
|
for (let i = pos; i < url.length; i++) {
|
|
246
|
-
const c = url.charCodeAt(i)
|
|
247
|
-
if (c === 63 || c === 35) return i
|
|
221
|
+
const c = url.charCodeAt(i)
|
|
222
|
+
if (c === 63 || c === 35) return i
|
|
248
223
|
}
|
|
249
|
-
return -1
|
|
224
|
+
return -1
|
|
250
225
|
}
|
|
251
226
|
|
|
252
227
|
function safeFire(fn: (() => unknown) | undefined, logger?: Logger): void {
|
|
253
|
-
if (fn === undefined) return
|
|
228
|
+
if (fn === undefined) return
|
|
254
229
|
try {
|
|
255
|
-
const result = fn()
|
|
230
|
+
const result = fn()
|
|
256
231
|
if (result && typeof result === "object" && "catch" in result) {
|
|
257
|
-
(result as Promise<unknown>).catch((e: unknown) => {
|
|
258
|
-
logger?.warn?.("telemetry callback failed", e)
|
|
259
|
-
})
|
|
232
|
+
;(result as Promise<unknown>).catch((e: unknown) => {
|
|
233
|
+
logger?.warn?.("telemetry callback failed", e)
|
|
234
|
+
})
|
|
260
235
|
}
|
|
261
236
|
} catch (e) {
|
|
262
|
-
logger?.warn?.("telemetry callback failed", e)
|
|
237
|
+
logger?.warn?.("telemetry callback failed", e)
|
|
263
238
|
}
|
|
264
239
|
}
|
|
265
240
|
|
|
266
241
|
/** Internal context shared across extracted fetch sub-methods */
|
|
267
242
|
type FetchCtx<TEnv> = {
|
|
268
|
-
env: TEnv
|
|
269
|
-
executionCtx: { waitUntil?: (p: Promise<unknown>) => void } | undefined
|
|
270
|
-
log: Logger | undefined
|
|
271
|
-
request: Request
|
|
272
|
-
startTime: number
|
|
273
|
-
url: () => URL
|
|
243
|
+
env: TEnv
|
|
244
|
+
executionCtx: { waitUntil?: (p: Promise<unknown>) => void } | undefined
|
|
245
|
+
log: Logger | undefined
|
|
246
|
+
request: Request
|
|
247
|
+
startTime: number
|
|
248
|
+
url: () => URL
|
|
274
249
|
/** Already known from fetch() before Deno.upgradeWebSocket consumes the request. */
|
|
275
|
-
wsUpgrade?: boolean
|
|
250
|
+
wsUpgrade?: boolean
|
|
276
251
|
/** Headers copied before Deno.upgradeWebSocket closes the Request. */
|
|
277
|
-
headerSnap?: Headers
|
|
278
|
-
}
|
|
252
|
+
headerSnap?: Headers
|
|
253
|
+
}
|
|
279
254
|
|
|
280
255
|
function requestWithHeaders(req: Request, headers: Headers): Request {
|
|
281
256
|
return new Proxy(req, {
|
|
@@ -297,25 +272,13 @@ function ctxRequest(fc: FetchCtx<unknown>): Request {
|
|
|
297
272
|
}
|
|
298
273
|
}
|
|
299
274
|
|
|
300
|
-
function defaultErrorFormatter(
|
|
301
|
-
|
|
302
|
-
defaultShape: Record<string, unknown>,
|
|
303
|
-
): Record<string, unknown> {
|
|
304
|
-
return defaultShape;
|
|
275
|
+
function defaultErrorFormatter(_error: HoneyError, defaultShape: Record<string, unknown>): Record<string, unknown> {
|
|
276
|
+
return defaultShape
|
|
305
277
|
}
|
|
306
278
|
|
|
307
|
-
type ErrorFormatterFn = ErrorFormatter
|
|
279
|
+
type ErrorFormatterFn = ErrorFormatter
|
|
308
280
|
|
|
309
|
-
const STATIC_CTX_RESERVED = new Set([
|
|
310
|
-
"background",
|
|
311
|
-
"cookies",
|
|
312
|
-
"env",
|
|
313
|
-
"headers",
|
|
314
|
-
"params",
|
|
315
|
-
"req",
|
|
316
|
-
"res",
|
|
317
|
-
"search",
|
|
318
|
-
]);
|
|
281
|
+
const STATIC_CTX_RESERVED = new Set(["background", "cookies", "env", "headers", "params", "req", "res", "search"])
|
|
319
282
|
|
|
320
283
|
/* errorKeys the framework throws on its own behalf — input/output validation, content negotiation,
|
|
321
284
|
* routing. Always passes the boundary check; users never declare these via .errors(). */
|
|
@@ -331,7 +294,7 @@ const FRAMEWORK_EKS = new Set<(typeof EK)[keyof typeof EK]>([
|
|
|
331
294
|
EK.request_timeout,
|
|
332
295
|
EK.gateway_timeout,
|
|
333
296
|
EK.bad_gateway,
|
|
334
|
-
])
|
|
297
|
+
])
|
|
335
298
|
|
|
336
299
|
export class Honey<
|
|
337
300
|
TEnv,
|
|
@@ -344,171 +307,176 @@ export class Honey<
|
|
|
344
307
|
TTaps extends Record<string, unknown> = {},
|
|
345
308
|
TScopedMw extends readonly ScopedMwEntry[] = [],
|
|
346
309
|
> {
|
|
347
|
-
declare readonly $basePath: TBasePath
|
|
348
|
-
declare readonly $ctx: TCtx
|
|
349
|
-
declare readonly $env: TEnv
|
|
350
|
-
declare readonly $errorFactory: TErrorFactory
|
|
351
|
-
declare readonly $meta: TMeta
|
|
352
|
-
declare readonly $routes: TRoutes
|
|
353
|
-
declare readonly $taps: TTaps
|
|
354
|
-
private _basePath: string
|
|
355
|
-
private _defaultBoundaryKey: string | null
|
|
356
|
-
private _defaultErrorKeys: Set<string
|
|
357
|
-
private _errorFactory: unknown
|
|
358
|
-
private _errorSchema: StandardSchemaLike | null
|
|
359
|
-
private _customErrorFormatter: CustomErrorFormatter | null
|
|
360
|
-
private _customErrorSchema: StandardSchemaLike | null
|
|
361
|
-
private _chainMeta: Record<string, unknown> | null
|
|
362
|
-
private _chainMiddlewares: RuntimeMiddleware[]
|
|
363
|
-
private _scopedMiddlewares: ScopedEntry[]
|
|
364
|
-
private _contextValues: Record<string, unknown> | null
|
|
365
|
-
private _errorFormatter: ErrorFormatterFn
|
|
366
|
-
private _errorI18n: ErrorI18nConfig<TEnv> | null
|
|
367
|
-
private _globalMiddlewares: RuntimeMiddleware[]
|
|
368
|
-
private _graph: HoneyGraph
|
|
369
|
-
private _hasWsRoutes: boolean
|
|
370
|
-
private _logger: Logger | null
|
|
371
|
-
private _outputValidation: "always" | "dev" | "off"
|
|
372
|
-
private _staticRoutes: { map: Record<string, RouteHandler> | null }
|
|
373
|
-
private _stripPrefix: string | null
|
|
374
|
-
private _trailingSlash: "enforce" | "ignore" | "strip"
|
|
375
|
-
private _wsAdapter: WSAdapter | null
|
|
376
|
-
private _openApiCache: { epoch: number; value: Promise<unknown> } | null
|
|
377
|
-
private _openApiYamlCache: { epoch: number; value: Promise<string> } | null
|
|
378
|
-
private _manifestCache: { epoch: number; value: Promise<unknown> } | null
|
|
310
|
+
declare readonly $basePath: TBasePath
|
|
311
|
+
declare readonly $ctx: TCtx
|
|
312
|
+
declare readonly $env: TEnv
|
|
313
|
+
declare readonly $errorFactory: TErrorFactory
|
|
314
|
+
declare readonly $meta: TMeta
|
|
315
|
+
declare readonly $routes: TRoutes
|
|
316
|
+
declare readonly $taps: TTaps
|
|
317
|
+
private _basePath: string
|
|
318
|
+
private _defaultBoundaryKey: string | null
|
|
319
|
+
private _defaultErrorKeys: Set<string>
|
|
320
|
+
private _errorFactory: unknown
|
|
321
|
+
private _errorSchema: StandardSchemaLike | null
|
|
322
|
+
private _customErrorFormatter: CustomErrorFormatter | null
|
|
323
|
+
private _customErrorSchema: StandardSchemaLike | null
|
|
324
|
+
private _chainMeta: Record<string, unknown> | null
|
|
325
|
+
private _chainMiddlewares: RuntimeMiddleware[]
|
|
326
|
+
private _scopedMiddlewares: ScopedEntry[]
|
|
327
|
+
private _contextValues: Record<string, unknown> | null
|
|
328
|
+
private _errorFormatter: ErrorFormatterFn
|
|
329
|
+
private _errorI18n: ErrorI18nConfig<TEnv> | null
|
|
330
|
+
private _globalMiddlewares: RuntimeMiddleware[]
|
|
331
|
+
private _graph: HoneyGraph
|
|
332
|
+
private _hasWsRoutes: boolean
|
|
333
|
+
private _logger: Logger | null
|
|
334
|
+
private _outputValidation: "always" | "dev" | "off"
|
|
335
|
+
private _staticRoutes: { map: Record<string, RouteHandler> | null }
|
|
336
|
+
private _stripPrefix: string | null
|
|
337
|
+
private _trailingSlash: "enforce" | "ignore" | "strip"
|
|
338
|
+
private _wsAdapter: WSAdapter | null
|
|
339
|
+
private _openApiCache: { epoch: number; value: Promise<unknown> } | null
|
|
340
|
+
private _openApiYamlCache: { epoch: number; value: Promise<string> } | null
|
|
341
|
+
private _manifestCache: { epoch: number; value: Promise<unknown> } | null
|
|
379
342
|
private _onError:
|
|
380
343
|
| ((
|
|
381
344
|
error: unknown,
|
|
382
345
|
ctx: {
|
|
383
|
-
env: TEnv
|
|
384
|
-
jsonFromError: (err: HoneyError) => Response
|
|
385
|
-
req: Request
|
|
346
|
+
env: TEnv
|
|
347
|
+
jsonFromError: (err: HoneyError) => Response
|
|
348
|
+
req: Request
|
|
386
349
|
},
|
|
387
350
|
) => HoneyError | Response | Promise<HoneyError | Response | undefined | void> | undefined | void)
|
|
388
|
-
| null
|
|
351
|
+
| null
|
|
389
352
|
private _onMethodNotAllowed:
|
|
390
353
|
| ((ctx: {
|
|
391
|
-
allowed: string[]
|
|
392
|
-
env: TEnv
|
|
393
|
-
jsonFromError: (err: HoneyError) => Response
|
|
394
|
-
req: Request
|
|
354
|
+
allowed: string[]
|
|
355
|
+
env: TEnv
|
|
356
|
+
jsonFromError: (err: HoneyError) => Response
|
|
357
|
+
req: Request
|
|
395
358
|
}) => Response | Promise<Response>)
|
|
396
|
-
| null
|
|
359
|
+
| null
|
|
397
360
|
private _onNotFound:
|
|
398
|
-
| ((ctx: {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
| null;
|
|
404
|
-
private _taps: Map<
|
|
361
|
+
| ((ctx: { env: TEnv; jsonFromError: (err: HoneyError) => Response; req: Request }) => Response | Promise<Response>)
|
|
362
|
+
| null
|
|
363
|
+
private _taps: Map<string, (ctx: TapContext<TEnv>, payload: unknown) => void | Promise<void>> | null
|
|
364
|
+
private _telemetry: TelemetryAdapter | null
|
|
365
|
+
private _realtimeRoutes: Map<
|
|
405
366
|
string,
|
|
406
|
-
|
|
407
|
-
>
|
|
408
|
-
private _telemetry: TelemetryAdapter | null;
|
|
409
|
-
private _realtimeRoutes: Map<string, { handler: RealtimeRouteOpts["handler"]; middlewares?: RealtimeRouteOpts["use"]; reconnectBuffer?: number }>;
|
|
367
|
+
{ handler: RealtimeRouteOpts["handler"]; middlewares?: RealtimeRouteOpts["use"]; reconnectBuffer?: number }
|
|
368
|
+
>
|
|
410
369
|
|
|
411
370
|
constructor(opts?: {
|
|
412
|
-
chainMiddlewares?: RuntimeMiddleware[]
|
|
413
|
-
defaultErrorKeys?: Set<string
|
|
414
|
-
globalMiddlewares?: RuntimeMiddleware[]
|
|
415
|
-
graph?: HoneyGraph
|
|
416
|
-
handlerMap?: Record<string, RouteHandler> | null
|
|
417
|
-
root?: TreeNode
|
|
418
|
-
scopedMiddlewares?: ScopedEntry[]
|
|
371
|
+
chainMiddlewares?: RuntimeMiddleware[]
|
|
372
|
+
defaultErrorKeys?: Set<string>
|
|
373
|
+
globalMiddlewares?: RuntimeMiddleware[]
|
|
374
|
+
graph?: HoneyGraph
|
|
375
|
+
handlerMap?: Record<string, RouteHandler> | null
|
|
376
|
+
root?: TreeNode
|
|
377
|
+
scopedMiddlewares?: ScopedEntry[]
|
|
419
378
|
}) {
|
|
420
|
-
this._basePath = "/"
|
|
379
|
+
this._basePath = "/"
|
|
421
380
|
this._graph = opts?.graph ?? {
|
|
422
381
|
handlerMap: opts?.handlerMap ?? null,
|
|
423
382
|
hasRouteTree: false,
|
|
383
|
+
metaSpec: null,
|
|
424
384
|
realtimeBus: null,
|
|
425
385
|
root: opts?.root ?? createNode(),
|
|
426
|
-
}
|
|
427
|
-
this._globalMiddlewares = opts?.globalMiddlewares ?? []
|
|
428
|
-
this._scopedMiddlewares = opts?.scopedMiddlewares ?? []
|
|
429
|
-
this._chainMeta = null
|
|
430
|
-
this._chainMiddlewares = opts?.chainMiddlewares ?? []
|
|
431
|
-
this._contextValues = null
|
|
432
|
-
this._defaultBoundaryKey = null
|
|
433
|
-
this._defaultErrorKeys = opts?.defaultErrorKeys ?? new Set()
|
|
434
|
-
this._errorFactory = null
|
|
435
|
-
this._errorSchema = null
|
|
436
|
-
this._customErrorFormatter = null
|
|
437
|
-
this._customErrorSchema = null
|
|
438
|
-
this._errorFormatter = defaultErrorFormatter
|
|
439
|
-
this._errorI18n = null
|
|
440
|
-
this._hasWsRoutes = false
|
|
441
|
-
this._logger = null
|
|
442
|
-
this._outputValidation = "off"
|
|
443
|
-
this._staticRoutes = { map: null }
|
|
444
|
-
this._stripPrefix = null
|
|
445
|
-
this._trailingSlash = "ignore"
|
|
446
|
-
this._wsAdapter = null
|
|
447
|
-
this._openApiCache = null
|
|
448
|
-
this._openApiYamlCache = null
|
|
449
|
-
this._manifestCache = null
|
|
450
|
-
this._onError = null
|
|
451
|
-
this._onNotFound = null
|
|
452
|
-
this._onMethodNotAllowed = null
|
|
453
|
-
this._taps = null
|
|
454
|
-
this._telemetry = null
|
|
455
|
-
this._realtimeRoutes = new Map()
|
|
386
|
+
}
|
|
387
|
+
this._globalMiddlewares = opts?.globalMiddlewares ?? []
|
|
388
|
+
this._scopedMiddlewares = opts?.scopedMiddlewares ?? []
|
|
389
|
+
this._chainMeta = null
|
|
390
|
+
this._chainMiddlewares = opts?.chainMiddlewares ?? []
|
|
391
|
+
this._contextValues = null
|
|
392
|
+
this._defaultBoundaryKey = null
|
|
393
|
+
this._defaultErrorKeys = opts?.defaultErrorKeys ?? new Set()
|
|
394
|
+
this._errorFactory = null
|
|
395
|
+
this._errorSchema = null
|
|
396
|
+
this._customErrorFormatter = null
|
|
397
|
+
this._customErrorSchema = null
|
|
398
|
+
this._errorFormatter = defaultErrorFormatter
|
|
399
|
+
this._errorI18n = null
|
|
400
|
+
this._hasWsRoutes = false
|
|
401
|
+
this._logger = null
|
|
402
|
+
this._outputValidation = "off"
|
|
403
|
+
this._staticRoutes = { map: null }
|
|
404
|
+
this._stripPrefix = null
|
|
405
|
+
this._trailingSlash = "ignore"
|
|
406
|
+
this._wsAdapter = null
|
|
407
|
+
this._openApiCache = null
|
|
408
|
+
this._openApiYamlCache = null
|
|
409
|
+
this._manifestCache = null
|
|
410
|
+
this._onError = null
|
|
411
|
+
this._onNotFound = null
|
|
412
|
+
this._onMethodNotAllowed = null
|
|
413
|
+
this._taps = null
|
|
414
|
+
this._telemetry = null
|
|
415
|
+
this._realtimeRoutes = new Map()
|
|
456
416
|
}
|
|
457
417
|
|
|
458
418
|
private get _handlerMap(): Record<string, RouteHandler> | null {
|
|
459
|
-
return this._graph.handlerMap
|
|
419
|
+
return this._graph.handlerMap
|
|
460
420
|
}
|
|
461
421
|
private set _handlerMap(value: Record<string, RouteHandler> | null) {
|
|
462
|
-
this._graph.handlerMap = value
|
|
422
|
+
this._graph.handlerMap = value
|
|
463
423
|
}
|
|
464
424
|
|
|
465
425
|
private get _hasRouteTree(): boolean {
|
|
466
|
-
return this._graph.hasRouteTree
|
|
426
|
+
return this._graph.hasRouteTree
|
|
467
427
|
}
|
|
468
428
|
private set _hasRouteTree(value: boolean) {
|
|
469
|
-
this._graph.hasRouteTree = value
|
|
429
|
+
this._graph.hasRouteTree = value
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** @internal — read by codegen */
|
|
433
|
+
private get _metaSpec(): MetaSpecConfig | null {
|
|
434
|
+
return this._graph.metaSpec
|
|
435
|
+
}
|
|
436
|
+
private set _metaSpec(value: MetaSpecConfig | null) {
|
|
437
|
+
this._graph.metaSpec = value
|
|
470
438
|
}
|
|
471
439
|
|
|
472
440
|
private get _root(): TreeNode {
|
|
473
|
-
return this._graph.root
|
|
441
|
+
return this._graph.root
|
|
474
442
|
}
|
|
475
443
|
private set _root(value: TreeNode) {
|
|
476
|
-
this._graph.root = value
|
|
444
|
+
this._graph.root = value
|
|
477
445
|
}
|
|
478
446
|
|
|
479
447
|
/** @internal — used by codegen */
|
|
480
448
|
private get _tree(): TreeNode {
|
|
481
|
-
return this._root
|
|
449
|
+
return this._root
|
|
482
450
|
}
|
|
483
451
|
|
|
484
452
|
private get _realtimeBus(): RealtimeBus | null {
|
|
485
|
-
return this._graph.realtimeBus
|
|
453
|
+
return this._graph.realtimeBus
|
|
486
454
|
}
|
|
487
455
|
private set _realtimeBus(value: RealtimeBus | null) {
|
|
488
|
-
this._graph.realtimeBus = value
|
|
456
|
+
this._graph.realtimeBus = value
|
|
489
457
|
}
|
|
490
458
|
|
|
491
459
|
/** @internal — mark that this app has WS routes */
|
|
492
460
|
_markWsRoutes(): void {
|
|
493
|
-
this._hasWsRoutes = true
|
|
461
|
+
this._hasWsRoutes = true
|
|
494
462
|
}
|
|
495
463
|
|
|
496
464
|
/** @internal — register a static route for O(1) lookup */
|
|
497
465
|
_registerStatic(key: string, handler: RouteHandler): void {
|
|
498
466
|
if (this._staticRoutes.map === null) {
|
|
499
|
-
this._staticRoutes.map = Object.create(null) as Record<string, RouteHandler
|
|
467
|
+
this._staticRoutes.map = Object.create(null) as Record<string, RouteHandler>
|
|
500
468
|
}
|
|
501
|
-
this._staticRoutes.map[key] = handler
|
|
469
|
+
this._staticRoutes.map[key] = handler
|
|
502
470
|
}
|
|
503
471
|
|
|
504
472
|
/** @internal — used by RouteBuilder for pre-filtered error factory */
|
|
505
473
|
get _factory(): unknown {
|
|
506
|
-
return this._errorFactory
|
|
474
|
+
return this._errorFactory
|
|
507
475
|
}
|
|
508
476
|
|
|
509
477
|
/** @internal — used by runtime error boundary */
|
|
510
478
|
get _boundaryKey(): string | null {
|
|
511
|
-
return this._defaultBoundaryKey
|
|
479
|
+
return this._defaultBoundaryKey
|
|
512
480
|
}
|
|
513
481
|
|
|
514
482
|
/** Convert unknown thrown value to error Response — used in WS, 404, 405 catch blocks */
|
|
@@ -520,78 +488,106 @@ export class Honey<
|
|
|
520
488
|
cause: thrown,
|
|
521
489
|
errorKey: EK.internal_server_error,
|
|
522
490
|
status: SK.internal_server_error,
|
|
523
|
-
})
|
|
524
|
-
return createErrorResponse(error, this._errorFormatter, this._customErrorFormatter)
|
|
491
|
+
})
|
|
492
|
+
return createErrorResponse(error, this._errorFormatter, this._customErrorFormatter)
|
|
525
493
|
}
|
|
526
494
|
|
|
527
495
|
private _createBoundaryError(errorKey: string, cause: unknown): HoneyError {
|
|
528
496
|
const factory = this._errorFactory as Record<
|
|
529
497
|
string,
|
|
530
498
|
((opts?: { cause?: unknown }) => HoneyError) | undefined
|
|
531
|
-
> | null
|
|
532
|
-
const factoryFn = factory?.[errorKey]
|
|
499
|
+
> | null
|
|
500
|
+
const factoryFn = factory?.[errorKey]
|
|
533
501
|
if (factoryFn) {
|
|
534
502
|
/* check if this is a custom schema error via ERROR_META — boundary must use standard errors only */
|
|
535
|
-
const meta = (factory as Record<symbol, Record<string, { schema: unknown }>>)?.[ERROR_META]
|
|
503
|
+
const meta = (factory as Record<symbol, Record<string, { schema: unknown }>>)?.[ERROR_META]
|
|
536
504
|
if (meta?.[errorKey]?.schema) {
|
|
537
505
|
/* custom schema error — cannot be used as boundary, fall through to manual construction */
|
|
538
506
|
} else {
|
|
539
|
-
return factoryFn({ cause })
|
|
507
|
+
return factoryFn({ cause })
|
|
540
508
|
}
|
|
541
509
|
}
|
|
542
510
|
return new HoneyError({
|
|
543
511
|
cause,
|
|
544
512
|
errorKey,
|
|
545
513
|
status: SK.internal_server_error,
|
|
546
|
-
})
|
|
514
|
+
})
|
|
547
515
|
}
|
|
548
516
|
|
|
549
517
|
private _createError(errorKey: string, statusKey: StatusKey): HoneyError {
|
|
550
|
-
const factory = this._errorFactory as Record<
|
|
551
|
-
|
|
552
|
-
(() => HoneyError) | undefined
|
|
553
|
-
> | null;
|
|
554
|
-
const factoryFn = factory?.[errorKey];
|
|
518
|
+
const factory = this._errorFactory as Record<string, (() => HoneyError) | undefined> | null
|
|
519
|
+
const factoryFn = factory?.[errorKey]
|
|
555
520
|
if (factoryFn) {
|
|
556
|
-
return factoryFn()
|
|
521
|
+
return factoryFn()
|
|
557
522
|
}
|
|
558
|
-
return new HoneyError({ errorKey, status: statusKey })
|
|
523
|
+
return new HoneyError({ errorKey, status: statusKey })
|
|
559
524
|
}
|
|
560
525
|
|
|
561
526
|
/** Apply error keys from a single scoped entry to every matching handler currently in the tree */
|
|
562
527
|
private _applyScopedEntryErrors(entry: ScopedEntry): void {
|
|
563
|
-
const errors = entry.errors
|
|
564
|
-
if (!errors || errors.length === 0) return
|
|
528
|
+
const errors = entry.errors
|
|
529
|
+
if (!errors || errors.length === 0) return
|
|
565
530
|
walkTreeHandlers(
|
|
566
531
|
this._root,
|
|
567
532
|
(h) => {
|
|
568
533
|
if (scopeMatches(entry.prefix, h.rp)) {
|
|
569
|
-
for (const k of errors) h.ek.add(k)
|
|
534
|
+
for (const k of errors) h.ek.add(k)
|
|
570
535
|
}
|
|
571
536
|
},
|
|
572
537
|
(wh) => {
|
|
573
538
|
if (scopeMatches(entry.prefix, wh.rp)) {
|
|
574
|
-
for (const k of errors) wh.ek.add(k)
|
|
539
|
+
for (const k of errors) wh.ek.add(k)
|
|
575
540
|
}
|
|
576
541
|
},
|
|
577
|
-
)
|
|
542
|
+
)
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Meta contributed by every middleware that will run for `routePath`, in runtime order:
|
|
547
|
+
* global, chain, then scoped entries matching the path. Route-level `.use()` is folded in
|
|
548
|
+
* later, by the builder. Resolved here — at registration — because the precompiled route
|
|
549
|
+
* tree bakes `mt` as a literal, so a per-request derivation would diverge from it.
|
|
550
|
+
*/
|
|
551
|
+
private _contributedMetaFor(routePath: string): Record<string, unknown> | null {
|
|
552
|
+
const scoped: RuntimeMiddleware[] = []
|
|
553
|
+
for (const entry of this._scopedMiddlewares) {
|
|
554
|
+
if (scopeMatches(entry.prefix, routePath)) scoped.push(entry.mw)
|
|
555
|
+
}
|
|
556
|
+
return collectMiddlewareMeta([this._globalMiddlewares, this._chainMiddlewares, scoped])
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Back-fill meta from one scoped entry onto handlers already in the tree. Mirrors
|
|
561
|
+
* `_applyScopedEntryErrors` — `.use("/prefix", mw)` may be registered after the routes it
|
|
562
|
+
* covers, and a tag missing where enforcement happens is the worst failure direction.
|
|
563
|
+
* `mt` is frozen at registration, so this replaces the object rather than mutating it.
|
|
564
|
+
*/
|
|
565
|
+
private _applyScopedEntryMeta(entry: ScopedEntry): void {
|
|
566
|
+
const meta = (entry.mw as { meta?: Record<string, unknown> }).meta
|
|
567
|
+
if (!meta) return
|
|
568
|
+
const apply = (h: { mt: Record<string, unknown> | null; rp: string }): void => {
|
|
569
|
+
if (!scopeMatches(entry.prefix, h.rp)) return
|
|
570
|
+
/* contributed meta never overwrites what the route or chain stated explicitly */
|
|
571
|
+
h.mt = Object.freeze(h.mt ? { ...meta, ...h.mt } : { ...meta })
|
|
572
|
+
}
|
|
573
|
+
walkTreeHandlers(this._root, apply, apply)
|
|
578
574
|
}
|
|
579
575
|
|
|
580
576
|
/** Apply error keys from every scoped entry on this chain to every matching handler in the tree */
|
|
581
577
|
private _applyAllScopedErrors(): void {
|
|
582
578
|
for (const entry of this._scopedMiddlewares) {
|
|
583
|
-
this._applyScopedEntryErrors(entry)
|
|
579
|
+
this._applyScopedEntryErrors(entry)
|
|
584
580
|
}
|
|
585
581
|
}
|
|
586
582
|
|
|
587
583
|
/** Return scoped middleware functions that match the given route path */
|
|
588
584
|
private _filterScopedForPath(routePath: string): RuntimeMiddleware[] {
|
|
589
|
-
if (this._scopedMiddlewares.length === 0) return EMPTY_MW
|
|
590
|
-
const out: RuntimeMiddleware[] = []
|
|
585
|
+
if (this._scopedMiddlewares.length === 0) return EMPTY_MW
|
|
586
|
+
const out: RuntimeMiddleware[] = []
|
|
591
587
|
for (const s of this._scopedMiddlewares) {
|
|
592
|
-
if (scopeMatches(s.prefix, routePath)) out.push(s.mw)
|
|
588
|
+
if (scopeMatches(s.prefix, routePath)) out.push(s.mw)
|
|
593
589
|
}
|
|
594
|
-
return out
|
|
590
|
+
return out
|
|
595
591
|
}
|
|
596
592
|
|
|
597
593
|
/** Mutates `honeyError.message` in-place with the i18n-resolved template for its errorKey.
|
|
@@ -603,7 +599,7 @@ export class Honey<
|
|
|
603
599
|
request: Request,
|
|
604
600
|
log?: Logger,
|
|
605
601
|
): Promise<void> {
|
|
606
|
-
if (!this._errorI18n) return
|
|
602
|
+
if (!this._errorI18n) return
|
|
607
603
|
try {
|
|
608
604
|
const locale = await this._errorI18n.resolveLocale({
|
|
609
605
|
cookies: ctx.cookies,
|
|
@@ -612,39 +608,36 @@ export class Honey<
|
|
|
612
608
|
params: ctx.params,
|
|
613
609
|
req: request,
|
|
614
610
|
search: ctx.search,
|
|
615
|
-
})
|
|
616
|
-
const translations = this._errorI18n.errors?.[locale]
|
|
611
|
+
})
|
|
612
|
+
const translations = this._errorI18n.errors?.[locale]
|
|
617
613
|
if (translations) {
|
|
618
|
-
const template = translations[honeyError.errorKey]
|
|
614
|
+
const template = translations[honeyError.errorKey]
|
|
619
615
|
if (template) {
|
|
620
616
|
await loadHoneyFeature("i18n")
|
|
621
|
-
honeyError.message = getI18nRuntime().interpolate(
|
|
622
|
-
template,
|
|
623
|
-
honeyError.vars ?? {},
|
|
624
|
-
);
|
|
617
|
+
honeyError.message = getI18nRuntime().interpolate(template, honeyError.vars ?? {})
|
|
625
618
|
}
|
|
626
619
|
}
|
|
627
620
|
|
|
628
|
-
const fieldTranslations = this._errorI18n.fieldNames?.[locale]
|
|
621
|
+
const fieldTranslations = this._errorI18n.fieldNames?.[locale]
|
|
629
622
|
if (fieldTranslations && Object.keys(honeyError.fields).length > 0) {
|
|
630
623
|
for (const fieldErrors of Object.values(honeyError.fields)) {
|
|
631
624
|
for (const fe of fieldErrors) {
|
|
632
|
-
let candidate = fe.path
|
|
625
|
+
let candidate = fe.path
|
|
633
626
|
while (candidate) {
|
|
634
|
-
const translated = fieldTranslations[candidate]
|
|
627
|
+
const translated = fieldTranslations[candidate]
|
|
635
628
|
if (translated) {
|
|
636
|
-
fe.path = translated
|
|
637
|
-
break
|
|
629
|
+
fe.path = translated
|
|
630
|
+
break
|
|
638
631
|
}
|
|
639
|
-
const dotIdx = candidate.indexOf(".")
|
|
640
|
-
if (dotIdx === -1) break
|
|
641
|
-
candidate = candidate.slice(dotIdx + 1)
|
|
632
|
+
const dotIdx = candidate.indexOf(".")
|
|
633
|
+
if (dotIdx === -1) break
|
|
634
|
+
candidate = candidate.slice(dotIdx + 1)
|
|
642
635
|
}
|
|
643
636
|
}
|
|
644
637
|
}
|
|
645
638
|
}
|
|
646
639
|
} catch (e) {
|
|
647
|
-
log?.warn?.("i18n resolution failed", e)
|
|
640
|
+
log?.warn?.("i18n resolution failed", e)
|
|
648
641
|
}
|
|
649
642
|
}
|
|
650
643
|
|
|
@@ -661,51 +654,51 @@ export class Honey<
|
|
|
661
654
|
path: string,
|
|
662
655
|
ctx: HoneyContext<TEnv>,
|
|
663
656
|
): Promise<Response> {
|
|
664
|
-
const { env, log, request, startTime } = fc
|
|
665
|
-
let honeyError: HoneyError
|
|
666
|
-
const boundaryKey = handler.bek ?? this._defaultBoundaryKey
|
|
657
|
+
const { env, log, request, startTime } = fc
|
|
658
|
+
let honeyError: HoneyError
|
|
659
|
+
const boundaryKey = handler.bek ?? this._defaultBoundaryKey
|
|
667
660
|
|
|
668
661
|
if (thrown instanceof HoneyError) {
|
|
669
662
|
/* framework-managed errorKeys (input/output validation, content negotiation, etc.) are always allowed
|
|
670
663
|
* regardless of handler.ek — users never declare them, the framework owns them. */
|
|
671
|
-
const isFrameworkEk = FRAMEWORK_EKS.has(thrown.errorKey as (typeof EK)[keyof typeof EK])
|
|
664
|
+
const isFrameworkEk = FRAMEWORK_EKS.has(thrown.errorKey as (typeof EK)[keyof typeof EK])
|
|
672
665
|
if (!isFrameworkEk && handler.ek.size > 0 && !handler.ek.has(thrown.errorKey)) {
|
|
673
666
|
if (boundaryKey) {
|
|
674
|
-
honeyError = this._createBoundaryError(boundaryKey, thrown)
|
|
667
|
+
honeyError = this._createBoundaryError(boundaryKey, thrown)
|
|
675
668
|
} else {
|
|
676
669
|
honeyError = new HoneyError({
|
|
677
670
|
cause: thrown,
|
|
678
671
|
errorKey: EK.internal_server_error,
|
|
679
672
|
status: SK.internal_server_error,
|
|
680
|
-
})
|
|
673
|
+
})
|
|
681
674
|
}
|
|
682
675
|
} else {
|
|
683
|
-
honeyError = thrown
|
|
676
|
+
honeyError = thrown
|
|
684
677
|
}
|
|
685
678
|
} else {
|
|
686
679
|
if (boundaryKey) {
|
|
687
|
-
honeyError = this._createBoundaryError(boundaryKey, thrown)
|
|
680
|
+
honeyError = this._createBoundaryError(boundaryKey, thrown)
|
|
688
681
|
} else {
|
|
689
682
|
honeyError = new HoneyError({
|
|
690
683
|
cause: thrown,
|
|
691
684
|
errorKey: EK.internal_server_error,
|
|
692
685
|
status: SK.internal_server_error,
|
|
693
|
-
})
|
|
686
|
+
})
|
|
694
687
|
}
|
|
695
688
|
}
|
|
696
689
|
|
|
697
|
-
await this._resolveI18n(honeyError, ctx, env, request, log)
|
|
690
|
+
await this._resolveI18n(honeyError, ctx, env, request, log)
|
|
698
691
|
|
|
699
692
|
/* onError handler */
|
|
700
693
|
if (this._onError) {
|
|
701
694
|
try {
|
|
702
|
-
const customResult = await this._onError(thrown, this._makeErrorCtx(fc))
|
|
695
|
+
const customResult = await this._onError(thrown, this._makeErrorCtx(fc))
|
|
703
696
|
if (customResult instanceof HoneyError) {
|
|
704
697
|
/* user-mapped boundary error — re-run i18n against new errorKey,
|
|
705
698
|
* then fall through to default response path so telemetry +
|
|
706
699
|
* jsonFromError run exactly once. */
|
|
707
|
-
honeyError = customResult
|
|
708
|
-
await this._resolveI18n(honeyError, ctx, env, request, log)
|
|
700
|
+
honeyError = customResult
|
|
701
|
+
await this._resolveI18n(honeyError, ctx, env, request, log)
|
|
709
702
|
} else if (customResult) {
|
|
710
703
|
safeFire(
|
|
711
704
|
() =>
|
|
@@ -716,7 +709,7 @@ export class Honey<
|
|
|
716
709
|
path,
|
|
717
710
|
}),
|
|
718
711
|
log,
|
|
719
|
-
)
|
|
712
|
+
)
|
|
720
713
|
safeFire(
|
|
721
714
|
() =>
|
|
722
715
|
this._telemetry?.onResponse?.({
|
|
@@ -725,9 +718,9 @@ export class Honey<
|
|
|
725
718
|
status: customResult.status,
|
|
726
719
|
}),
|
|
727
720
|
log,
|
|
728
|
-
)
|
|
729
|
-
ctx._isErrorResponse = true
|
|
730
|
-
return customResult
|
|
721
|
+
)
|
|
722
|
+
ctx._isErrorResponse = true
|
|
723
|
+
return customResult
|
|
731
724
|
}
|
|
732
725
|
/* customResult === undefined | void → fall through to default path */
|
|
733
726
|
} catch {
|
|
@@ -745,8 +738,8 @@ export class Honey<
|
|
|
745
738
|
path,
|
|
746
739
|
}),
|
|
747
740
|
log,
|
|
748
|
-
)
|
|
749
|
-
const res = this._makeErrorCtx(fc).jsonFromError(honeyError)
|
|
741
|
+
)
|
|
742
|
+
const res = this._makeErrorCtx(fc).jsonFromError(honeyError)
|
|
750
743
|
safeFire(
|
|
751
744
|
() =>
|
|
752
745
|
this._telemetry?.onResponse?.({
|
|
@@ -755,24 +748,14 @@ export class Honey<
|
|
|
755
748
|
status: res.status,
|
|
756
749
|
}),
|
|
757
750
|
log,
|
|
758
|
-
)
|
|
759
|
-
ctx._isErrorResponse = true
|
|
760
|
-
return res
|
|
751
|
+
)
|
|
752
|
+
ctx._isErrorResponse = true
|
|
753
|
+
return res
|
|
761
754
|
}
|
|
762
755
|
|
|
763
756
|
basePath<P extends string>(
|
|
764
757
|
prefix: P,
|
|
765
|
-
): Honey<
|
|
766
|
-
TEnv,
|
|
767
|
-
TCtx,
|
|
768
|
-
TRoutes,
|
|
769
|
-
TMeta,
|
|
770
|
-
TErrorFactory,
|
|
771
|
-
TDefaultErrors,
|
|
772
|
-
MergePath<TBasePath, P>,
|
|
773
|
-
TTaps,
|
|
774
|
-
TScopedMw
|
|
775
|
-
> {
|
|
758
|
+
): Honey<TEnv, TCtx, TRoutes, TMeta, TErrorFactory, TDefaultErrors, MergePath<TBasePath, P>, TTaps, TScopedMw> {
|
|
776
759
|
const next = new Honey<
|
|
777
760
|
TEnv,
|
|
778
761
|
TCtx,
|
|
@@ -789,32 +772,32 @@ export class Honey<
|
|
|
789
772
|
globalMiddlewares: this._globalMiddlewares,
|
|
790
773
|
graph: this._graph,
|
|
791
774
|
scopedMiddlewares: this._scopedMiddlewares,
|
|
792
|
-
})
|
|
793
|
-
next._basePath = mergePath(this._basePath, prefix)
|
|
794
|
-
next._defaultBoundaryKey = this._defaultBoundaryKey
|
|
795
|
-
next._errorFactory = this._errorFactory
|
|
796
|
-
next._errorSchema = this._errorSchema
|
|
797
|
-
next._customErrorFormatter = this._customErrorFormatter
|
|
798
|
-
next._customErrorSchema = this._customErrorSchema
|
|
799
|
-
next._errorFormatter = this._errorFormatter
|
|
800
|
-
next._errorI18n = this._errorI18n
|
|
801
|
-
next._logger = this._logger
|
|
802
|
-
next._outputValidation = this._outputValidation
|
|
803
|
-
next._stripPrefix = this._stripPrefix
|
|
804
|
-
next._trailingSlash = this._trailingSlash
|
|
805
|
-
next._wsAdapter = this._wsAdapter
|
|
806
|
-
next._onError = this._onError
|
|
807
|
-
next._onNotFound = this._onNotFound
|
|
808
|
-
next._onMethodNotAllowed = this._onMethodNotAllowed
|
|
809
|
-
next._chainMeta = this._chainMeta
|
|
810
|
-
next._contextValues = this._contextValues
|
|
811
|
-
next._taps = this._taps
|
|
812
|
-
next._telemetry = this._telemetry
|
|
813
|
-
next._realtimeBus = this._realtimeBus
|
|
814
|
-
next._realtimeRoutes = this._realtimeRoutes
|
|
815
|
-
next._staticRoutes = this._staticRoutes
|
|
816
|
-
next._hasRouteTree = this._hasRouteTree
|
|
817
|
-
next._hasWsRoutes = this._hasWsRoutes
|
|
775
|
+
})
|
|
776
|
+
next._basePath = mergePath(this._basePath, prefix)
|
|
777
|
+
next._defaultBoundaryKey = this._defaultBoundaryKey
|
|
778
|
+
next._errorFactory = this._errorFactory
|
|
779
|
+
next._errorSchema = this._errorSchema
|
|
780
|
+
next._customErrorFormatter = this._customErrorFormatter
|
|
781
|
+
next._customErrorSchema = this._customErrorSchema
|
|
782
|
+
next._errorFormatter = this._errorFormatter
|
|
783
|
+
next._errorI18n = this._errorI18n
|
|
784
|
+
next._logger = this._logger
|
|
785
|
+
next._outputValidation = this._outputValidation
|
|
786
|
+
next._stripPrefix = this._stripPrefix
|
|
787
|
+
next._trailingSlash = this._trailingSlash
|
|
788
|
+
next._wsAdapter = this._wsAdapter
|
|
789
|
+
next._onError = this._onError
|
|
790
|
+
next._onNotFound = this._onNotFound
|
|
791
|
+
next._onMethodNotAllowed = this._onMethodNotAllowed
|
|
792
|
+
next._chainMeta = this._chainMeta
|
|
793
|
+
next._contextValues = this._contextValues
|
|
794
|
+
next._taps = this._taps
|
|
795
|
+
next._telemetry = this._telemetry
|
|
796
|
+
next._realtimeBus = this._realtimeBus
|
|
797
|
+
next._realtimeRoutes = this._realtimeRoutes
|
|
798
|
+
next._staticRoutes = this._staticRoutes
|
|
799
|
+
next._hasRouteTree = this._hasRouteTree
|
|
800
|
+
next._hasWsRoutes = this._hasWsRoutes
|
|
818
801
|
return next as unknown as Honey<
|
|
819
802
|
TEnv,
|
|
820
803
|
TCtx,
|
|
@@ -825,22 +808,12 @@ export class Honey<
|
|
|
825
808
|
MergePath<TBasePath, P>,
|
|
826
809
|
TTaps,
|
|
827
810
|
TScopedMw
|
|
828
|
-
|
|
811
|
+
>
|
|
829
812
|
}
|
|
830
813
|
|
|
831
814
|
context<TAdds extends Record<string, unknown>>(
|
|
832
815
|
values: TAdds,
|
|
833
|
-
): Honey<
|
|
834
|
-
TEnv,
|
|
835
|
-
TCtx & Readonly<TAdds>,
|
|
836
|
-
TRoutes,
|
|
837
|
-
TMeta,
|
|
838
|
-
TErrorFactory,
|
|
839
|
-
TDefaultErrors,
|
|
840
|
-
TBasePath,
|
|
841
|
-
TTaps,
|
|
842
|
-
TScopedMw
|
|
843
|
-
> {
|
|
816
|
+
): Honey<TEnv, TCtx & Readonly<TAdds>, TRoutes, TMeta, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw> {
|
|
844
817
|
for (const key in values) {
|
|
845
818
|
if (STATIC_CTX_RESERVED.has(key)) {
|
|
846
819
|
throw new Error(`context() cannot set reserved key "${key}"`)
|
|
@@ -862,65 +835,73 @@ export class Honey<
|
|
|
862
835
|
globalMiddlewares: this._globalMiddlewares,
|
|
863
836
|
graph: this._graph,
|
|
864
837
|
scopedMiddlewares: this._scopedMiddlewares,
|
|
865
|
-
})
|
|
866
|
-
next._basePath = this._basePath
|
|
867
|
-
next._chainMeta = this._chainMeta
|
|
868
|
-
next._contextValues = this._contextValues
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
next.
|
|
872
|
-
next.
|
|
873
|
-
next.
|
|
874
|
-
next.
|
|
875
|
-
next.
|
|
876
|
-
next.
|
|
877
|
-
next.
|
|
878
|
-
next.
|
|
879
|
-
next.
|
|
880
|
-
next.
|
|
881
|
-
next.
|
|
882
|
-
next.
|
|
883
|
-
next.
|
|
884
|
-
next.
|
|
885
|
-
next.
|
|
886
|
-
next.
|
|
887
|
-
next.
|
|
888
|
-
next.
|
|
889
|
-
next.
|
|
890
|
-
next.
|
|
891
|
-
next
|
|
892
|
-
|
|
893
|
-
|
|
838
|
+
})
|
|
839
|
+
next._basePath = this._basePath
|
|
840
|
+
next._chainMeta = this._chainMeta
|
|
841
|
+
next._contextValues = this._contextValues ? { ...this._contextValues, ...values } : { ...values }
|
|
842
|
+
next._defaultBoundaryKey = this._defaultBoundaryKey
|
|
843
|
+
next._errorFactory = this._errorFactory
|
|
844
|
+
next._errorSchema = this._errorSchema
|
|
845
|
+
next._customErrorFormatter = this._customErrorFormatter
|
|
846
|
+
next._customErrorSchema = this._customErrorSchema
|
|
847
|
+
next._errorFormatter = this._errorFormatter
|
|
848
|
+
next._errorI18n = this._errorI18n
|
|
849
|
+
next._logger = this._logger
|
|
850
|
+
next._outputValidation = this._outputValidation
|
|
851
|
+
next._stripPrefix = this._stripPrefix
|
|
852
|
+
next._trailingSlash = this._trailingSlash
|
|
853
|
+
next._wsAdapter = this._wsAdapter
|
|
854
|
+
next._onError = this._onError
|
|
855
|
+
next._onNotFound = this._onNotFound
|
|
856
|
+
next._onMethodNotAllowed = this._onMethodNotAllowed
|
|
857
|
+
next._taps = this._taps
|
|
858
|
+
next._telemetry = this._telemetry
|
|
859
|
+
next._realtimeBus = this._realtimeBus
|
|
860
|
+
next._realtimeRoutes = this._realtimeRoutes
|
|
861
|
+
next._staticRoutes = this._staticRoutes
|
|
862
|
+
next._hasRouteTree = this._hasRouteTree
|
|
863
|
+
next._hasWsRoutes = this._hasWsRoutes
|
|
864
|
+
return next as unknown as Honey<
|
|
865
|
+
TEnv,
|
|
866
|
+
TCtx & Readonly<TAdds>,
|
|
867
|
+
TRoutes,
|
|
868
|
+
TMeta,
|
|
869
|
+
TErrorFactory,
|
|
870
|
+
TDefaultErrors,
|
|
871
|
+
TBasePath,
|
|
872
|
+
TTaps,
|
|
873
|
+
TScopedMw
|
|
874
|
+
>
|
|
894
875
|
}
|
|
895
876
|
|
|
896
877
|
logger(logger: Logger): this {
|
|
897
|
-
this._logger = logger
|
|
898
|
-
return this
|
|
878
|
+
this._logger = logger
|
|
879
|
+
return this
|
|
899
880
|
}
|
|
900
881
|
|
|
901
882
|
outputValidation(mode: "always" | "dev" | "off"): this {
|
|
902
|
-
this._outputValidation = mode
|
|
903
|
-
return this
|
|
883
|
+
this._outputValidation = mode
|
|
884
|
+
return this
|
|
904
885
|
}
|
|
905
886
|
|
|
906
887
|
trailingSlash(mode: "enforce" | "ignore" | "strip"): this {
|
|
907
|
-
this._trailingSlash = mode
|
|
908
|
-
return this
|
|
888
|
+
this._trailingSlash = mode
|
|
889
|
+
return this
|
|
909
890
|
}
|
|
910
891
|
|
|
911
892
|
/** Strip a URL path prefix before route matching — boundary-safe (won't strip partial segments), paths without the prefix pass through unchanged */
|
|
912
893
|
stripPrefix(prefix: string): this {
|
|
913
|
-
let normalized = prefix.replace(/\/+$/, "")
|
|
894
|
+
let normalized = prefix.replace(/\/+$/, "")
|
|
914
895
|
if (normalized.length > 0 && normalized.charCodeAt(0) !== 47) {
|
|
915
|
-
normalized = `/${normalized}
|
|
896
|
+
normalized = `/${normalized}`
|
|
916
897
|
}
|
|
917
|
-
this._stripPrefix = normalized === "" || normalized === "/" ? null : normalized
|
|
918
|
-
return this
|
|
898
|
+
this._stripPrefix = normalized === "" || normalized === "/" ? null : normalized
|
|
899
|
+
return this
|
|
919
900
|
}
|
|
920
901
|
|
|
921
902
|
wsAdapter(adapter: WSAdapter): this {
|
|
922
|
-
this._wsAdapter = adapter
|
|
923
|
-
return this
|
|
903
|
+
this._wsAdapter = adapter
|
|
904
|
+
return this
|
|
924
905
|
}
|
|
925
906
|
|
|
926
907
|
openapi(options: {
|
|
@@ -929,6 +910,8 @@ export class Honey<
|
|
|
929
910
|
docsPath?: string
|
|
930
911
|
filterRoutes?: (route: { meta: unknown; method: string; path: string }) => boolean
|
|
931
912
|
path?: string
|
|
913
|
+
/** Named metaSpec profile selecting which emitted keys this document carries */
|
|
914
|
+
profile?: string
|
|
932
915
|
securitySchemes?: Record<string, unknown>
|
|
933
916
|
title: string
|
|
934
917
|
version: string
|
|
@@ -950,6 +933,7 @@ export class Honey<
|
|
|
950
933
|
title: options.title,
|
|
951
934
|
version: options.version,
|
|
952
935
|
},
|
|
936
|
+
profile: options.profile,
|
|
953
937
|
securitySchemes: options.securitySchemes,
|
|
954
938
|
}),
|
|
955
939
|
)
|
|
@@ -976,12 +960,8 @@ export class Honey<
|
|
|
976
960
|
return pending
|
|
977
961
|
}
|
|
978
962
|
this._mountInternalGet(`${stem}.json`, async (ctx) => ctx.res.json("ok", await loadJson()))
|
|
979
|
-
this._mountInternalGet(`${stem}.yml`, async (ctx) =>
|
|
980
|
-
|
|
981
|
-
)
|
|
982
|
-
this._mountInternalGet(`${stem}.yaml`, async (ctx) =>
|
|
983
|
-
ctx.res.text("ok", await loadYaml(), yamlHeaders),
|
|
984
|
-
)
|
|
963
|
+
this._mountInternalGet(`${stem}.yml`, async (ctx) => ctx.res.text("ok", await loadYaml(), yamlHeaders))
|
|
964
|
+
this._mountInternalGet(`${stem}.yaml`, async (ctx) => ctx.res.text("ok", await loadYaml(), yamlHeaders))
|
|
985
965
|
if (options.docs) {
|
|
986
966
|
const specUrl = mergePath(this._basePath, `${stem}.json`)
|
|
987
967
|
const docs = options.docs
|
|
@@ -999,9 +979,7 @@ export class Honey<
|
|
|
999
979
|
}
|
|
1000
980
|
}
|
|
1001
981
|
if (!mounted) {
|
|
1002
|
-
throw new Error(
|
|
1003
|
-
`Honey.openapi({ docs }) cannot mount at ${preferred}: already registered. Pass docsPath.`,
|
|
1004
|
-
)
|
|
982
|
+
throw new Error(`Honey.openapi({ docs }) cannot mount at ${preferred}: already registered. Pass docsPath.`)
|
|
1005
983
|
}
|
|
1006
984
|
}
|
|
1007
985
|
return this
|
|
@@ -1042,10 +1020,7 @@ export class Honey<
|
|
|
1042
1020
|
}
|
|
1043
1021
|
|
|
1044
1022
|
/** Mount an internal GET. Returns false when a user route already owns the path. */
|
|
1045
|
-
private _mountInternalGet(
|
|
1046
|
-
path: string,
|
|
1047
|
-
fn: (ctx: { res: HoneyRes }) => Response | Promise<Response>,
|
|
1048
|
-
): boolean {
|
|
1023
|
+
private _mountInternalGet(path: string, fn: (ctx: { res: HoneyRes }) => Response | Promise<Response>): boolean {
|
|
1049
1024
|
const fullPath = mergePath(this._basePath, path)
|
|
1050
1025
|
const existing = this._lookupGet(fullPath)
|
|
1051
1026
|
if (existing) return existing._skip === true
|
|
@@ -1072,114 +1047,111 @@ export class Honey<
|
|
|
1072
1047
|
defaultErrorFormatter<TSchema extends StandardSchemaLike>(
|
|
1073
1048
|
schema: TSchema,
|
|
1074
1049
|
fn: (error: HoneyError) => InferOutput<TSchema>,
|
|
1075
|
-
): this
|
|
1076
|
-
defaultErrorFormatter(fn: ErrorFormatterFn): this
|
|
1050
|
+
): this
|
|
1051
|
+
defaultErrorFormatter(fn: ErrorFormatterFn): this
|
|
1077
1052
|
defaultErrorFormatter(
|
|
1078
1053
|
schemaOrFn: ErrorFormatterFn | StandardSchemaLike,
|
|
1079
1054
|
maybeFn?: (error: HoneyError) => unknown,
|
|
1080
1055
|
): this {
|
|
1081
1056
|
if (typeof schemaOrFn === "function") {
|
|
1082
|
-
this._errorSchema = null
|
|
1083
|
-
this._errorFormatter = schemaOrFn
|
|
1057
|
+
this._errorSchema = null
|
|
1058
|
+
this._errorFormatter = schemaOrFn
|
|
1084
1059
|
} else {
|
|
1085
|
-
this._errorSchema = schemaOrFn
|
|
1086
|
-
const mapper = maybeFn as (error: HoneyError) => Record<string, unknown
|
|
1087
|
-
this._errorFormatter = (error) => mapper(error)
|
|
1060
|
+
this._errorSchema = schemaOrFn
|
|
1061
|
+
const mapper = maybeFn as (error: HoneyError) => Record<string, unknown>
|
|
1062
|
+
this._errorFormatter = (error) => mapper(error)
|
|
1088
1063
|
}
|
|
1089
|
-
return this
|
|
1064
|
+
return this
|
|
1090
1065
|
}
|
|
1091
1066
|
|
|
1092
1067
|
customErrorFormatter<TSchema extends StandardSchemaLike>(
|
|
1093
1068
|
schema: TSchema,
|
|
1094
1069
|
fn: (error: HoneyError, data: Record<string, unknown>) => InferOutput<TSchema>,
|
|
1095
|
-
): this
|
|
1096
|
-
customErrorFormatter(fn: CustomErrorFormatter): this
|
|
1070
|
+
): this
|
|
1071
|
+
customErrorFormatter(fn: CustomErrorFormatter): this
|
|
1097
1072
|
customErrorFormatter(
|
|
1098
1073
|
schemaOrFn: CustomErrorFormatter | StandardSchemaLike,
|
|
1099
1074
|
maybeFn?: (error: HoneyError, data: Record<string, unknown>) => unknown,
|
|
1100
1075
|
): this {
|
|
1101
1076
|
if (typeof schemaOrFn === "function") {
|
|
1102
|
-
this._customErrorSchema = null
|
|
1103
|
-
this._customErrorFormatter = schemaOrFn
|
|
1077
|
+
this._customErrorSchema = null
|
|
1078
|
+
this._customErrorFormatter = schemaOrFn
|
|
1104
1079
|
} else {
|
|
1105
|
-
this._customErrorSchema = schemaOrFn
|
|
1106
|
-
const mapper = maybeFn as (error: HoneyError, data: Record<string, unknown>) => Record<string, unknown
|
|
1107
|
-
this._customErrorFormatter = (error, data) => mapper(error, data)
|
|
1080
|
+
this._customErrorSchema = schemaOrFn
|
|
1081
|
+
const mapper = maybeFn as (error: HoneyError, data: Record<string, unknown>) => Record<string, unknown>
|
|
1082
|
+
this._customErrorFormatter = (error, data) => mapper(error, data)
|
|
1108
1083
|
}
|
|
1109
|
-
return this
|
|
1084
|
+
return this
|
|
1110
1085
|
}
|
|
1111
1086
|
|
|
1112
1087
|
errorI18n(config: ErrorI18nConfig<TEnv>): this {
|
|
1113
|
-
this._errorI18n = config
|
|
1114
|
-
return this
|
|
1088
|
+
this._errorI18n = config
|
|
1089
|
+
return this
|
|
1115
1090
|
}
|
|
1116
1091
|
|
|
1117
1092
|
onError(
|
|
1118
1093
|
handler: (
|
|
1119
1094
|
error: unknown,
|
|
1120
1095
|
ctx: {
|
|
1121
|
-
env: TEnv
|
|
1122
|
-
jsonFromError: (err: HoneyError) => Response
|
|
1123
|
-
req: Request
|
|
1096
|
+
env: TEnv
|
|
1097
|
+
jsonFromError: (err: HoneyError) => Response
|
|
1098
|
+
req: Request
|
|
1124
1099
|
},
|
|
1125
1100
|
) => HoneyError | Response | Promise<HoneyError | Response | undefined | void> | undefined | void,
|
|
1126
1101
|
): this {
|
|
1127
|
-
this._onError = handler
|
|
1128
|
-
return this
|
|
1102
|
+
this._onError = handler
|
|
1103
|
+
return this
|
|
1129
1104
|
}
|
|
1130
1105
|
|
|
1131
1106
|
onMethodNotAllowed(
|
|
1132
1107
|
handler: (ctx: {
|
|
1133
|
-
allowed: string[]
|
|
1134
|
-
env: TEnv
|
|
1135
|
-
jsonFromError: (err: HoneyError) => Response
|
|
1136
|
-
req: Request
|
|
1108
|
+
allowed: string[]
|
|
1109
|
+
env: TEnv
|
|
1110
|
+
jsonFromError: (err: HoneyError) => Response
|
|
1111
|
+
req: Request
|
|
1137
1112
|
}) => Response | Promise<Response>,
|
|
1138
1113
|
): this {
|
|
1139
|
-
this._onMethodNotAllowed = handler
|
|
1140
|
-
return this
|
|
1114
|
+
this._onMethodNotAllowed = handler
|
|
1115
|
+
return this
|
|
1141
1116
|
}
|
|
1142
1117
|
|
|
1143
1118
|
onNotFound(
|
|
1144
1119
|
handler: (ctx: {
|
|
1145
|
-
env: TEnv
|
|
1146
|
-
jsonFromError: (err: HoneyError) => Response
|
|
1147
|
-
req: Request
|
|
1120
|
+
env: TEnv
|
|
1121
|
+
jsonFromError: (err: HoneyError) => Response
|
|
1122
|
+
req: Request
|
|
1148
1123
|
}) => Response | Promise<Response>,
|
|
1149
1124
|
): this {
|
|
1150
|
-
this._onNotFound = handler
|
|
1151
|
-
return this
|
|
1125
|
+
this._onNotFound = handler
|
|
1126
|
+
return this
|
|
1152
1127
|
}
|
|
1153
1128
|
|
|
1154
1129
|
/** Register a tap handler keyed by name — fires after successful handler response */
|
|
1155
1130
|
tap<K extends string>(
|
|
1156
1131
|
key: K,
|
|
1157
|
-
handler: (
|
|
1158
|
-
ctx: TapContext<TEnv>,
|
|
1159
|
-
payload: K extends keyof TTaps ? TTaps[K] : unknown,
|
|
1160
|
-
) => void | Promise<void>,
|
|
1132
|
+
handler: (ctx: TapContext<TEnv>, payload: K extends keyof TTaps ? TTaps[K] : unknown) => void | Promise<void>,
|
|
1161
1133
|
): this {
|
|
1162
1134
|
if (this._taps === null) {
|
|
1163
|
-
this._taps = new Map()
|
|
1135
|
+
this._taps = new Map()
|
|
1164
1136
|
}
|
|
1165
|
-
this._taps.set(key, handler as (ctx: TapContext<TEnv>, payload: unknown) => void | Promise<void>)
|
|
1166
|
-
return this
|
|
1137
|
+
this._taps.set(key, handler as (ctx: TapContext<TEnv>, payload: unknown) => void | Promise<void>)
|
|
1138
|
+
return this
|
|
1167
1139
|
}
|
|
1168
1140
|
|
|
1169
1141
|
telemetry(adapter: TelemetryAdapter): this {
|
|
1170
|
-
this._telemetry = adapter
|
|
1171
|
-
return this
|
|
1142
|
+
this._telemetry = adapter
|
|
1143
|
+
return this
|
|
1172
1144
|
}
|
|
1173
1145
|
|
|
1174
1146
|
routeTree(tree: RouteTree): this {
|
|
1175
|
-
this._root = tree.root
|
|
1176
|
-
this._handlerMap = tree.handlers ?? null
|
|
1177
|
-
this._hasRouteTree = true
|
|
1178
|
-
return this
|
|
1147
|
+
this._root = tree.root
|
|
1148
|
+
this._handlerMap = tree.handlers ?? null
|
|
1149
|
+
this._hasRouteTree = true
|
|
1150
|
+
return this
|
|
1179
1151
|
}
|
|
1180
1152
|
|
|
1181
1153
|
toRouteTree(): RouteTree {
|
|
1182
|
-
return { meta: {}, root: this._root }
|
|
1154
|
+
return { meta: {}, root: this._root }
|
|
1183
1155
|
}
|
|
1184
1156
|
|
|
1185
1157
|
errorFactory<TFactory extends Record<string, (...args: never[]) => unknown>>(
|
|
@@ -1195,28 +1167,14 @@ export class Honey<
|
|
|
1195
1167
|
TBasePath,
|
|
1196
1168
|
TTaps,
|
|
1197
1169
|
TScopedMw
|
|
1198
|
-
|
|
1199
|
-
next._errorFactory = factory
|
|
1200
|
-
return next
|
|
1170
|
+
>
|
|
1171
|
+
next._errorFactory = factory
|
|
1172
|
+
return next
|
|
1201
1173
|
}
|
|
1202
1174
|
|
|
1203
|
-
defaultErrors<
|
|
1204
|
-
TKeys extends [TErrorFactory] extends [never]
|
|
1205
|
-
? never
|
|
1206
|
-
: keyof TErrorFactory & string,
|
|
1207
|
-
>(
|
|
1175
|
+
defaultErrors<TKeys extends ([TErrorFactory] extends [never] ? never : keyof TErrorFactory & string)>(
|
|
1208
1176
|
...keys: TKeys[]
|
|
1209
|
-
): Honey<
|
|
1210
|
-
TEnv,
|
|
1211
|
-
TCtx,
|
|
1212
|
-
TRoutes,
|
|
1213
|
-
TMeta,
|
|
1214
|
-
TErrorFactory,
|
|
1215
|
-
TDefaultErrors | TKeys,
|
|
1216
|
-
TBasePath,
|
|
1217
|
-
TTaps,
|
|
1218
|
-
TScopedMw
|
|
1219
|
-
> {
|
|
1177
|
+
): Honey<TEnv, TCtx, TRoutes, TMeta, TErrorFactory, TDefaultErrors | TKeys, TBasePath, TTaps, TScopedMw> {
|
|
1220
1178
|
const next = this as unknown as Honey<
|
|
1221
1179
|
TEnv,
|
|
1222
1180
|
TCtx,
|
|
@@ -1227,28 +1185,16 @@ export class Honey<
|
|
|
1227
1185
|
TBasePath,
|
|
1228
1186
|
TTaps,
|
|
1229
1187
|
TScopedMw
|
|
1230
|
-
|
|
1188
|
+
>
|
|
1231
1189
|
for (const k of keys) {
|
|
1232
|
-
next._defaultErrorKeys.add(k)
|
|
1190
|
+
next._defaultErrorKeys.add(k)
|
|
1233
1191
|
}
|
|
1234
|
-
return next
|
|
1192
|
+
return next
|
|
1235
1193
|
}
|
|
1236
1194
|
|
|
1237
|
-
defaultBoundary<
|
|
1238
|
-
TKey
|
|
1239
|
-
|
|
1240
|
-
: keyof TErrorFactory & string,
|
|
1241
|
-
>(key: TKey): Honey<
|
|
1242
|
-
TEnv,
|
|
1243
|
-
TCtx,
|
|
1244
|
-
TRoutes,
|
|
1245
|
-
TMeta,
|
|
1246
|
-
TErrorFactory,
|
|
1247
|
-
TDefaultErrors | TKey,
|
|
1248
|
-
TBasePath,
|
|
1249
|
-
TTaps,
|
|
1250
|
-
TScopedMw
|
|
1251
|
-
> {
|
|
1195
|
+
defaultBoundary<TKey extends ([TErrorFactory] extends [never] ? never : keyof TErrorFactory & string)>(
|
|
1196
|
+
key: TKey,
|
|
1197
|
+
): Honey<TEnv, TCtx, TRoutes, TMeta, TErrorFactory, TDefaultErrors | TKey, TBasePath, TTaps, TScopedMw> {
|
|
1252
1198
|
const next = this as unknown as Honey<
|
|
1253
1199
|
TEnv,
|
|
1254
1200
|
TCtx,
|
|
@@ -1259,10 +1205,10 @@ export class Honey<
|
|
|
1259
1205
|
TBasePath,
|
|
1260
1206
|
TTaps,
|
|
1261
1207
|
TScopedMw
|
|
1262
|
-
|
|
1263
|
-
next._defaultBoundaryKey = key
|
|
1264
|
-
next._defaultErrorKeys.add(key)
|
|
1265
|
-
return next
|
|
1208
|
+
>
|
|
1209
|
+
next._defaultBoundaryKey = key
|
|
1210
|
+
next._defaultErrorKeys.add(key)
|
|
1211
|
+
return next
|
|
1266
1212
|
}
|
|
1267
1213
|
|
|
1268
1214
|
/** Phantom overload — constrains what route-level .meta() accepts */
|
|
@@ -1276,102 +1222,84 @@ export class Honey<
|
|
|
1276
1222
|
TBasePath,
|
|
1277
1223
|
TTaps,
|
|
1278
1224
|
TScopedMw
|
|
1279
|
-
|
|
1225
|
+
>
|
|
1280
1226
|
/** Typed chain-level default meta — constrains route meta and sets runtime defaults in one call */
|
|
1281
|
-
meta<
|
|
1282
|
-
TNewMeta extends Record<string, unknown>,
|
|
1283
|
-
TValues extends Partial<DefaultMeta> & Partial<TNewMeta>,
|
|
1284
|
-
>(
|
|
1227
|
+
meta<TNewMeta extends Record<string, unknown>, TValues extends Partial<DefaultMeta> & Partial<TNewMeta>>(
|
|
1285
1228
|
values: TValues,
|
|
1286
|
-
): Honey<
|
|
1287
|
-
TEnv,
|
|
1288
|
-
TCtx,
|
|
1289
|
-
TRoutes,
|
|
1290
|
-
TNewMeta,
|
|
1291
|
-
TErrorFactory,
|
|
1292
|
-
TDefaultErrors,
|
|
1293
|
-
TBasePath,
|
|
1294
|
-
TTaps,
|
|
1295
|
-
TScopedMw
|
|
1296
|
-
>;
|
|
1229
|
+
): Honey<TEnv, TCtx, TRoutes, TNewMeta, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw>
|
|
1297
1230
|
/** Chain-level default meta — merged into every route registered on this chain */
|
|
1298
|
-
meta<
|
|
1299
|
-
TValues extends Partial<DefaultMeta> &
|
|
1300
|
-
([TMeta] extends [never] ? {} : Partial<TMeta>),
|
|
1301
|
-
>(
|
|
1231
|
+
meta<TValues extends Partial<DefaultMeta> & ([TMeta] extends [never] ? {} : Partial<TMeta>)>(
|
|
1302
1232
|
values: TValues,
|
|
1303
|
-
): Honey<
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
TMeta,
|
|
1308
|
-
TErrorFactory,
|
|
1309
|
-
TDefaultErrors,
|
|
1310
|
-
TBasePath,
|
|
1311
|
-
TTaps,
|
|
1312
|
-
TScopedMw
|
|
1313
|
-
>;
|
|
1314
|
-
meta(values?: Record<string, unknown>): Honey<
|
|
1315
|
-
TEnv,
|
|
1316
|
-
TCtx,
|
|
1317
|
-
TRoutes,
|
|
1318
|
-
unknown,
|
|
1319
|
-
TErrorFactory,
|
|
1320
|
-
TDefaultErrors,
|
|
1321
|
-
TBasePath,
|
|
1322
|
-
TTaps,
|
|
1323
|
-
TScopedMw
|
|
1324
|
-
> {
|
|
1233
|
+
): Honey<TEnv, TCtx, TRoutes, TMeta, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw>
|
|
1234
|
+
meta(
|
|
1235
|
+
values?: Record<string, unknown>,
|
|
1236
|
+
): Honey<TEnv, TCtx, TRoutes, unknown, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw> {
|
|
1325
1237
|
if (values === undefined) {
|
|
1326
1238
|
/* phantom overload — type-only, no runtime effect */
|
|
1327
|
-
return this as Honey<TEnv, TCtx, TRoutes, unknown, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw
|
|
1239
|
+
return this as Honey<TEnv, TCtx, TRoutes, unknown, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw>
|
|
1328
1240
|
}
|
|
1329
1241
|
/* chain-level meta — copy-on-write */
|
|
1330
|
-
const next = new Honey<
|
|
1331
|
-
TEnv,
|
|
1332
|
-
TCtx,
|
|
1333
|
-
TRoutes,
|
|
1334
|
-
unknown,
|
|
1335
|
-
TErrorFactory,
|
|
1336
|
-
TDefaultErrors,
|
|
1337
|
-
TBasePath,
|
|
1338
|
-
TTaps,
|
|
1339
|
-
TScopedMw
|
|
1340
|
-
>({
|
|
1242
|
+
const next = new Honey<TEnv, TCtx, TRoutes, unknown, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw>({
|
|
1341
1243
|
chainMiddlewares: this._chainMiddlewares,
|
|
1342
1244
|
defaultErrorKeys: this._defaultErrorKeys,
|
|
1343
1245
|
globalMiddlewares: this._globalMiddlewares,
|
|
1344
1246
|
graph: this._graph,
|
|
1345
1247
|
scopedMiddlewares: this._scopedMiddlewares,
|
|
1346
|
-
})
|
|
1347
|
-
next._basePath = this._basePath
|
|
1348
|
-
next._chainMeta = this._chainMeta
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
next.
|
|
1352
|
-
next.
|
|
1353
|
-
next.
|
|
1354
|
-
next.
|
|
1355
|
-
next.
|
|
1356
|
-
next.
|
|
1357
|
-
next.
|
|
1358
|
-
next.
|
|
1359
|
-
next.
|
|
1360
|
-
next.
|
|
1361
|
-
next.
|
|
1362
|
-
next.
|
|
1363
|
-
next.
|
|
1364
|
-
next.
|
|
1365
|
-
next.
|
|
1366
|
-
next.
|
|
1367
|
-
next.
|
|
1368
|
-
next.
|
|
1369
|
-
next.
|
|
1370
|
-
next.
|
|
1371
|
-
next.
|
|
1372
|
-
next
|
|
1373
|
-
|
|
1374
|
-
|
|
1248
|
+
})
|
|
1249
|
+
next._basePath = this._basePath
|
|
1250
|
+
next._chainMeta = this._chainMeta ? { ...this._chainMeta, ...values } : { ...values }
|
|
1251
|
+
next._contextValues = this._contextValues
|
|
1252
|
+
next._defaultBoundaryKey = this._defaultBoundaryKey
|
|
1253
|
+
next._errorFactory = this._errorFactory
|
|
1254
|
+
next._errorSchema = this._errorSchema
|
|
1255
|
+
next._customErrorFormatter = this._customErrorFormatter
|
|
1256
|
+
next._customErrorSchema = this._customErrorSchema
|
|
1257
|
+
next._errorFormatter = this._errorFormatter
|
|
1258
|
+
next._errorI18n = this._errorI18n
|
|
1259
|
+
next._logger = this._logger
|
|
1260
|
+
next._outputValidation = this._outputValidation
|
|
1261
|
+
next._stripPrefix = this._stripPrefix
|
|
1262
|
+
next._trailingSlash = this._trailingSlash
|
|
1263
|
+
next._wsAdapter = this._wsAdapter
|
|
1264
|
+
next._onError = this._onError
|
|
1265
|
+
next._onNotFound = this._onNotFound
|
|
1266
|
+
next._onMethodNotAllowed = this._onMethodNotAllowed
|
|
1267
|
+
next._taps = this._taps
|
|
1268
|
+
next._telemetry = this._telemetry
|
|
1269
|
+
next._realtimeBus = this._realtimeBus
|
|
1270
|
+
next._realtimeRoutes = this._realtimeRoutes
|
|
1271
|
+
next._staticRoutes = this._staticRoutes
|
|
1272
|
+
next._hasRouteTree = this._hasRouteTree
|
|
1273
|
+
next._hasWsRoutes = this._hasWsRoutes
|
|
1274
|
+
return next
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
* Declare what flows from route meta and route schemas into the OpenAPI document.
|
|
1279
|
+
* Codegen-time only — never consulted on the request path. See docs/meta-spec.md.
|
|
1280
|
+
*/
|
|
1281
|
+
metaSpec<TSpec extends HoneyMetaSpec<TMeta>>(spec: TSpec): this {
|
|
1282
|
+
if (this._metaSpec !== null) {
|
|
1283
|
+
throw new Error("metaSpec() was already declared on this app — declare the whole policy in one call")
|
|
1284
|
+
}
|
|
1285
|
+
this._metaSpec = spec as MetaSpecConfig
|
|
1286
|
+
return this
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
/** Mounted sub-apps fill policy gaps; the parent's entries win on conflict. */
|
|
1290
|
+
private _absorbMetaSpec(sub: MetaSpecConfig | null): void {
|
|
1291
|
+
if (!sub) return
|
|
1292
|
+
const own = this._metaSpec
|
|
1293
|
+
if (!own) {
|
|
1294
|
+
this._metaSpec = sub
|
|
1295
|
+
return
|
|
1296
|
+
}
|
|
1297
|
+
this._metaSpec = {
|
|
1298
|
+
meta: { ...sub.meta, ...own.meta },
|
|
1299
|
+
profiles: { ...sub.profiles, ...own.profiles },
|
|
1300
|
+
schema: { ...sub.schema, ...own.schema },
|
|
1301
|
+
strict: own.strict ?? sub.strict,
|
|
1302
|
+
}
|
|
1375
1303
|
}
|
|
1376
1304
|
|
|
1377
1305
|
/** Declare tap payload types — auto-extends meta with Partial<T> for meta-driven taps */
|
|
@@ -1396,12 +1324,12 @@ export class Honey<
|
|
|
1396
1324
|
TBasePath,
|
|
1397
1325
|
TNewTaps,
|
|
1398
1326
|
TScopedMw
|
|
1399
|
-
|
|
1327
|
+
>
|
|
1400
1328
|
}
|
|
1401
1329
|
|
|
1402
1330
|
use<TAdds>(
|
|
1403
1331
|
mw: MiddlewareFn<TCtx, TAdds>,
|
|
1404
|
-
): Honey<TEnv, TCtx & TAdds, TRoutes, TMeta, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw
|
|
1332
|
+
): Honey<TEnv, TCtx & TAdds, TRoutes, TMeta, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw>
|
|
1405
1333
|
|
|
1406
1334
|
use<const TPath extends string, TAdds>(
|
|
1407
1335
|
path: TPath,
|
|
@@ -1416,153 +1344,132 @@ export class Honey<
|
|
|
1416
1344
|
TBasePath,
|
|
1417
1345
|
TTaps,
|
|
1418
1346
|
readonly [...TScopedMw, { readonly path: MergePath<TBasePath, TPath>; readonly adds: TAdds }]
|
|
1419
|
-
|
|
1347
|
+
>
|
|
1420
1348
|
|
|
1421
1349
|
/* oxlint-disable-next-line typescript/no-explicit-any -- overload impl requires erased types */
|
|
1422
1350
|
use(pathOrMw: string | MiddlewareFn<any, any>, maybeMw?: MiddlewareFn<any, any>): any {
|
|
1423
1351
|
if (typeof pathOrMw !== "string") {
|
|
1424
|
-
const mw = pathOrMw as RuntimeMiddleware
|
|
1352
|
+
const mw = pathOrMw as RuntimeMiddleware
|
|
1425
1353
|
const newChain = new Honey<TEnv>({
|
|
1426
1354
|
chainMiddlewares: [...this._chainMiddlewares, mw],
|
|
1427
1355
|
defaultErrorKeys: this._defaultErrorKeys,
|
|
1428
1356
|
globalMiddlewares: this._globalMiddlewares,
|
|
1429
1357
|
graph: this._graph,
|
|
1430
1358
|
scopedMiddlewares: this._scopedMiddlewares,
|
|
1431
|
-
})
|
|
1432
|
-
newChain._basePath = this._basePath
|
|
1433
|
-
newChain._chainMeta = this._chainMeta
|
|
1434
|
-
newChain._contextValues = this._contextValues
|
|
1435
|
-
newChain._defaultBoundaryKey = this._defaultBoundaryKey
|
|
1436
|
-
newChain._errorFactory = this._errorFactory
|
|
1437
|
-
newChain._errorSchema = this._errorSchema
|
|
1438
|
-
newChain._customErrorFormatter = this._customErrorFormatter
|
|
1439
|
-
newChain._customErrorSchema = this._customErrorSchema
|
|
1440
|
-
newChain._errorFormatter = this._errorFormatter
|
|
1441
|
-
newChain._errorI18n = this._errorI18n
|
|
1442
|
-
newChain._logger = this._logger
|
|
1443
|
-
newChain._outputValidation = this._outputValidation
|
|
1444
|
-
newChain._stripPrefix = this._stripPrefix
|
|
1445
|
-
newChain._trailingSlash = this._trailingSlash
|
|
1446
|
-
newChain._wsAdapter = this._wsAdapter
|
|
1447
|
-
newChain._onError = this._onError
|
|
1448
|
-
newChain._onNotFound = this._onNotFound
|
|
1449
|
-
newChain._onMethodNotAllowed = this._onMethodNotAllowed
|
|
1450
|
-
newChain._taps = this._taps
|
|
1451
|
-
newChain._telemetry = this._telemetry
|
|
1452
|
-
newChain._realtimeBus = this._realtimeBus
|
|
1453
|
-
newChain._realtimeRoutes = this._realtimeRoutes
|
|
1454
|
-
newChain._staticRoutes = this._staticRoutes
|
|
1455
|
-
newChain._hasRouteTree = this._hasRouteTree
|
|
1456
|
-
newChain._hasWsRoutes = this._hasWsRoutes
|
|
1457
|
-
return newChain
|
|
1359
|
+
})
|
|
1360
|
+
newChain._basePath = this._basePath
|
|
1361
|
+
newChain._chainMeta = this._chainMeta
|
|
1362
|
+
newChain._contextValues = this._contextValues
|
|
1363
|
+
newChain._defaultBoundaryKey = this._defaultBoundaryKey
|
|
1364
|
+
newChain._errorFactory = this._errorFactory
|
|
1365
|
+
newChain._errorSchema = this._errorSchema
|
|
1366
|
+
newChain._customErrorFormatter = this._customErrorFormatter
|
|
1367
|
+
newChain._customErrorSchema = this._customErrorSchema
|
|
1368
|
+
newChain._errorFormatter = this._errorFormatter
|
|
1369
|
+
newChain._errorI18n = this._errorI18n
|
|
1370
|
+
newChain._logger = this._logger
|
|
1371
|
+
newChain._outputValidation = this._outputValidation
|
|
1372
|
+
newChain._stripPrefix = this._stripPrefix
|
|
1373
|
+
newChain._trailingSlash = this._trailingSlash
|
|
1374
|
+
newChain._wsAdapter = this._wsAdapter
|
|
1375
|
+
newChain._onError = this._onError
|
|
1376
|
+
newChain._onNotFound = this._onNotFound
|
|
1377
|
+
newChain._onMethodNotAllowed = this._onMethodNotAllowed
|
|
1378
|
+
newChain._taps = this._taps
|
|
1379
|
+
newChain._telemetry = this._telemetry
|
|
1380
|
+
newChain._realtimeBus = this._realtimeBus
|
|
1381
|
+
newChain._realtimeRoutes = this._realtimeRoutes
|
|
1382
|
+
newChain._staticRoutes = this._staticRoutes
|
|
1383
|
+
newChain._hasRouteTree = this._hasRouteTree
|
|
1384
|
+
newChain._hasWsRoutes = this._hasWsRoutes
|
|
1385
|
+
return newChain
|
|
1458
1386
|
}
|
|
1459
1387
|
|
|
1460
1388
|
/* scoped path */
|
|
1461
|
-
const normalizedPrefix = normalizeScopePath(pathOrMw)
|
|
1462
|
-
const fullPrefix = mergePath(this._basePath, normalizedPrefix)
|
|
1463
|
-
const mw = maybeMw as RuntimeMiddleware
|
|
1464
|
-
const mwWithErrors = maybeMw as MiddlewareFn<unknown, unknown
|
|
1389
|
+
const normalizedPrefix = normalizeScopePath(pathOrMw)
|
|
1390
|
+
const fullPrefix = mergePath(this._basePath, normalizedPrefix)
|
|
1391
|
+
const mw = maybeMw as RuntimeMiddleware
|
|
1392
|
+
const mwWithErrors = maybeMw as MiddlewareFn<unknown, unknown>
|
|
1465
1393
|
const entry: ScopedEntry = {
|
|
1466
1394
|
errors: mwWithErrors.errors ? [...mwWithErrors.errors] : undefined,
|
|
1467
1395
|
mw,
|
|
1468
1396
|
prefix: fullPrefix,
|
|
1469
|
-
}
|
|
1470
|
-
this._scopedMiddlewares.push(entry)
|
|
1397
|
+
}
|
|
1398
|
+
this._scopedMiddlewares.push(entry)
|
|
1471
1399
|
const newChain = new Honey<TEnv>({
|
|
1472
1400
|
chainMiddlewares: this._chainMiddlewares,
|
|
1473
1401
|
defaultErrorKeys: this._defaultErrorKeys,
|
|
1474
1402
|
globalMiddlewares: this._globalMiddlewares,
|
|
1475
1403
|
graph: this._graph,
|
|
1476
1404
|
scopedMiddlewares: this._scopedMiddlewares,
|
|
1477
|
-
})
|
|
1478
|
-
newChain._basePath = this._basePath
|
|
1479
|
-
newChain._chainMeta = this._chainMeta
|
|
1480
|
-
newChain._contextValues = this._contextValues
|
|
1481
|
-
newChain._defaultBoundaryKey = this._defaultBoundaryKey
|
|
1482
|
-
newChain._errorFactory = this._errorFactory
|
|
1483
|
-
newChain._errorSchema = this._errorSchema
|
|
1484
|
-
newChain._customErrorFormatter = this._customErrorFormatter
|
|
1485
|
-
newChain._customErrorSchema = this._customErrorSchema
|
|
1486
|
-
newChain._errorFormatter = this._errorFormatter
|
|
1487
|
-
newChain._errorI18n = this._errorI18n
|
|
1488
|
-
newChain._logger = this._logger
|
|
1489
|
-
newChain._outputValidation = this._outputValidation
|
|
1490
|
-
newChain._stripPrefix = this._stripPrefix
|
|
1491
|
-
newChain._trailingSlash = this._trailingSlash
|
|
1492
|
-
newChain._wsAdapter = this._wsAdapter
|
|
1493
|
-
newChain._onError = this._onError
|
|
1494
|
-
newChain._onNotFound = this._onNotFound
|
|
1495
|
-
newChain._onMethodNotAllowed = this._onMethodNotAllowed
|
|
1496
|
-
newChain._taps = this._taps
|
|
1497
|
-
newChain._telemetry = this._telemetry
|
|
1498
|
-
newChain._realtimeBus = this._realtimeBus
|
|
1499
|
-
newChain._realtimeRoutes = this._realtimeRoutes
|
|
1500
|
-
newChain._staticRoutes = this._staticRoutes
|
|
1501
|
-
newChain._hasRouteTree = this._hasRouteTree
|
|
1502
|
-
newChain._hasWsRoutes = this._hasWsRoutes
|
|
1503
|
-
newChain._applyScopedEntryErrors(entry)
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
TSubMeta,
|
|
1510
|
-
|
|
1511
|
-
TSubDefaultErrors extends string,
|
|
1512
|
-
TSubBasePath extends string,
|
|
1513
|
-
>(
|
|
1514
|
-
sub: Honey<
|
|
1515
|
-
TEnv,
|
|
1516
|
-
TCtx,
|
|
1517
|
-
TSubRoutes,
|
|
1518
|
-
TSubMeta,
|
|
1519
|
-
TSubErrorFactory,
|
|
1520
|
-
TSubDefaultErrors,
|
|
1521
|
-
TSubBasePath
|
|
1522
|
-
>,
|
|
1523
|
-
): Honey<
|
|
1524
|
-
TEnv,
|
|
1525
|
-
TCtx,
|
|
1526
|
-
TRoutes & TSubRoutes,
|
|
1527
|
-
TMeta,
|
|
1528
|
-
TErrorFactory,
|
|
1529
|
-
TDefaultErrors,
|
|
1530
|
-
TBasePath,
|
|
1531
|
-
TTaps,
|
|
1532
|
-
TScopedMw
|
|
1533
|
-
> {
|
|
1405
|
+
})
|
|
1406
|
+
newChain._basePath = this._basePath
|
|
1407
|
+
newChain._chainMeta = this._chainMeta
|
|
1408
|
+
newChain._contextValues = this._contextValues
|
|
1409
|
+
newChain._defaultBoundaryKey = this._defaultBoundaryKey
|
|
1410
|
+
newChain._errorFactory = this._errorFactory
|
|
1411
|
+
newChain._errorSchema = this._errorSchema
|
|
1412
|
+
newChain._customErrorFormatter = this._customErrorFormatter
|
|
1413
|
+
newChain._customErrorSchema = this._customErrorSchema
|
|
1414
|
+
newChain._errorFormatter = this._errorFormatter
|
|
1415
|
+
newChain._errorI18n = this._errorI18n
|
|
1416
|
+
newChain._logger = this._logger
|
|
1417
|
+
newChain._outputValidation = this._outputValidation
|
|
1418
|
+
newChain._stripPrefix = this._stripPrefix
|
|
1419
|
+
newChain._trailingSlash = this._trailingSlash
|
|
1420
|
+
newChain._wsAdapter = this._wsAdapter
|
|
1421
|
+
newChain._onError = this._onError
|
|
1422
|
+
newChain._onNotFound = this._onNotFound
|
|
1423
|
+
newChain._onMethodNotAllowed = this._onMethodNotAllowed
|
|
1424
|
+
newChain._taps = this._taps
|
|
1425
|
+
newChain._telemetry = this._telemetry
|
|
1426
|
+
newChain._realtimeBus = this._realtimeBus
|
|
1427
|
+
newChain._realtimeRoutes = this._realtimeRoutes
|
|
1428
|
+
newChain._staticRoutes = this._staticRoutes
|
|
1429
|
+
newChain._hasRouteTree = this._hasRouteTree
|
|
1430
|
+
newChain._hasWsRoutes = this._hasWsRoutes
|
|
1431
|
+
newChain._applyScopedEntryErrors(entry)
|
|
1432
|
+
newChain._applyScopedEntryMeta(entry)
|
|
1433
|
+
return newChain
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
route<TSubRoutes, TSubMeta, TSubErrorFactory, TSubDefaultErrors extends string, TSubBasePath extends string>(
|
|
1437
|
+
sub: Honey<TEnv, TCtx, TSubRoutes, TSubMeta, TSubErrorFactory, TSubDefaultErrors, TSubBasePath>,
|
|
1438
|
+
): Honey<TEnv, TCtx, TRoutes & TSubRoutes, TMeta, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw> {
|
|
1534
1439
|
/* skip self-merge: .handler() already inserted into shared _root */
|
|
1535
1440
|
if (sub._tree !== this._root) {
|
|
1536
|
-
mergeInto(this._root, sub._tree)
|
|
1537
|
-
|
|
1538
|
-
if (sub.
|
|
1441
|
+
mergeInto(this._root, sub._tree)
|
|
1442
|
+
this._absorbMetaSpec(sub._metaSpec)
|
|
1443
|
+
if (sub._hasWsRoutes) this._hasWsRoutes = true
|
|
1444
|
+
if (sub._hasRouteTree) this._hasRouteTree = true
|
|
1539
1445
|
/* carry sub's scoped mw entries into parent's runtime list (parent scopes run first) */
|
|
1540
1446
|
for (const entry of sub._scopedMiddlewares) {
|
|
1541
|
-
this._scopedMiddlewares.push(entry)
|
|
1447
|
+
this._scopedMiddlewares.push(entry)
|
|
1542
1448
|
}
|
|
1543
1449
|
if (sub._staticRoutes.map !== null) {
|
|
1544
1450
|
if (this._staticRoutes.map === null) {
|
|
1545
|
-
this._staticRoutes.map = Object.create(null) as Record<string, RouteHandler
|
|
1451
|
+
this._staticRoutes.map = Object.create(null) as Record<string, RouteHandler>
|
|
1546
1452
|
}
|
|
1547
|
-
Object.assign(this._staticRoutes.map, sub._staticRoutes.map)
|
|
1453
|
+
Object.assign(this._staticRoutes.map, sub._staticRoutes.map)
|
|
1548
1454
|
}
|
|
1549
1455
|
for (const [path, cfg] of sub._realtimeRoutes) {
|
|
1550
1456
|
if (this._realtimeRoutes.has(path)) {
|
|
1551
|
-
throw new Error(`Duplicate realtime route: ${path}`)
|
|
1457
|
+
throw new Error(`Duplicate realtime route: ${path}`)
|
|
1552
1458
|
}
|
|
1553
|
-
this._realtimeRoutes.set(path, cfg)
|
|
1459
|
+
this._realtimeRoutes.set(path, cfg)
|
|
1554
1460
|
}
|
|
1555
1461
|
if (!this._realtimeBus && sub._realtimeBus) {
|
|
1556
|
-
this._realtimeBus = sub._realtimeBus
|
|
1462
|
+
this._realtimeBus = sub._realtimeBus
|
|
1557
1463
|
}
|
|
1558
1464
|
if (sub._taps !== null) {
|
|
1559
|
-
if (this._taps === null) this._taps = new Map()
|
|
1465
|
+
if (this._taps === null) this._taps = new Map()
|
|
1560
1466
|
for (const [key, fn] of sub._taps) {
|
|
1561
|
-
if (!this._taps.has(key)) this._taps.set(key, fn)
|
|
1467
|
+
if (!this._taps.has(key)) this._taps.set(key, fn)
|
|
1562
1468
|
}
|
|
1563
1469
|
}
|
|
1564
|
-
/* walk tree and apply all scoped error keys to matching handlers */
|
|
1565
|
-
this._applyAllScopedErrors()
|
|
1470
|
+
/* walk tree and apply all scoped error keys + contributed meta to matching handlers */
|
|
1471
|
+
this._applyAllScopedErrors()
|
|
1472
|
+
for (const entry of this._scopedMiddlewares) this._applyScopedEntryMeta(entry)
|
|
1566
1473
|
}
|
|
1567
1474
|
return this as unknown as Honey<
|
|
1568
1475
|
TEnv,
|
|
@@ -1574,13 +1481,10 @@ export class Honey<
|
|
|
1574
1481
|
TBasePath,
|
|
1575
1482
|
TTaps,
|
|
1576
1483
|
TScopedMw
|
|
1577
|
-
|
|
1484
|
+
>
|
|
1578
1485
|
}
|
|
1579
1486
|
|
|
1580
|
-
private _registerRoute<
|
|
1581
|
-
TPath extends string,
|
|
1582
|
-
TMethod extends HttpMethod | "ALL",
|
|
1583
|
-
>(
|
|
1487
|
+
private _registerRoute<TPath extends string, TMethod extends HttpMethod | "ALL">(
|
|
1584
1488
|
method: TMethod,
|
|
1585
1489
|
path: TPath,
|
|
1586
1490
|
extraMethods?: (HttpMethod | "ALL")[],
|
|
@@ -1603,11 +1507,11 @@ export class Honey<
|
|
|
1603
1507
|
TTaps,
|
|
1604
1508
|
TScopedMw
|
|
1605
1509
|
> {
|
|
1606
|
-
const fullPath = mergePath(this._basePath, path)
|
|
1607
|
-
const errorKeys = new Set(this._defaultErrorKeys)
|
|
1510
|
+
const fullPath = mergePath(this._basePath, path)
|
|
1511
|
+
const errorKeys = new Set(this._defaultErrorKeys)
|
|
1608
1512
|
for (const entry of this._scopedMiddlewares) {
|
|
1609
1513
|
if (entry.errors && scopeMatches(entry.prefix, fullPath)) {
|
|
1610
|
-
for (const k of entry.errors) errorKeys.add(k)
|
|
1514
|
+
for (const k of entry.errors) errorKeys.add(k)
|
|
1611
1515
|
}
|
|
1612
1516
|
}
|
|
1613
1517
|
return new RouteBuilder<
|
|
@@ -1637,6 +1541,7 @@ export class Honey<
|
|
|
1637
1541
|
meta: this._chainMeta ? { ...this._chainMeta } : null,
|
|
1638
1542
|
method,
|
|
1639
1543
|
middlewares: [],
|
|
1544
|
+
mwMeta: this._contributedMetaFor(fullPath),
|
|
1640
1545
|
outputSchemas: null,
|
|
1641
1546
|
parent: this,
|
|
1642
1547
|
parentMiddlewares: this._chainMiddlewares,
|
|
@@ -1660,15 +1565,12 @@ export class Honey<
|
|
|
1660
1565
|
TBasePath,
|
|
1661
1566
|
TTaps,
|
|
1662
1567
|
TScopedMw
|
|
1663
|
-
|
|
1568
|
+
>
|
|
1664
1569
|
}
|
|
1665
1570
|
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
>(
|
|
1670
|
-
methods: TMethods,
|
|
1671
|
-
path: TPath,
|
|
1571
|
+
on<const TPath extends string, const TMethods extends readonly [HttpMethod | "ALL", ...(HttpMethod | "ALL")[]]>(
|
|
1572
|
+
methods: TMethods,
|
|
1573
|
+
path: TPath,
|
|
1672
1574
|
): BuilderChain<
|
|
1673
1575
|
TEnv,
|
|
1674
1576
|
TCtx & ApplyScoped<TScopedMw, MergePath<TBasePath, TPath>>,
|
|
@@ -1688,37 +1590,37 @@ export class Honey<
|
|
|
1688
1590
|
TTaps,
|
|
1689
1591
|
TScopedMw
|
|
1690
1592
|
> {
|
|
1691
|
-
const [first, ...rest] = methods
|
|
1593
|
+
const [first, ...rest] = methods
|
|
1692
1594
|
return this._registerRoute<TPath, TMethods[number]>(
|
|
1693
1595
|
first,
|
|
1694
1596
|
path,
|
|
1695
1597
|
rest.length > 0 ? (rest as (HttpMethod | "ALL")[]) : undefined,
|
|
1696
|
-
)
|
|
1598
|
+
)
|
|
1697
1599
|
}
|
|
1698
1600
|
|
|
1699
1601
|
all<const TPath extends string>(path: TPath) {
|
|
1700
|
-
return this._registerRoute<TPath, "ALL">("ALL", path)
|
|
1602
|
+
return this._registerRoute<TPath, "ALL">("ALL", path)
|
|
1701
1603
|
}
|
|
1702
1604
|
delete<const TPath extends string>(path: TPath) {
|
|
1703
|
-
return this._registerRoute<TPath, "DELETE">("DELETE", path)
|
|
1605
|
+
return this._registerRoute<TPath, "DELETE">("DELETE", path)
|
|
1704
1606
|
}
|
|
1705
1607
|
get<const TPath extends string>(path: TPath) {
|
|
1706
|
-
return this._registerRoute<TPath, "GET">("GET", path)
|
|
1608
|
+
return this._registerRoute<TPath, "GET">("GET", path)
|
|
1707
1609
|
}
|
|
1708
1610
|
head<const TPath extends string>(path: TPath) {
|
|
1709
|
-
return this._registerRoute<TPath, "HEAD">("HEAD", path)
|
|
1611
|
+
return this._registerRoute<TPath, "HEAD">("HEAD", path)
|
|
1710
1612
|
}
|
|
1711
1613
|
options<const TPath extends string>(path: TPath) {
|
|
1712
|
-
return this._registerRoute<TPath, "OPTIONS">("OPTIONS", path)
|
|
1614
|
+
return this._registerRoute<TPath, "OPTIONS">("OPTIONS", path)
|
|
1713
1615
|
}
|
|
1714
1616
|
patch<const TPath extends string>(path: TPath) {
|
|
1715
|
-
return this._registerRoute<TPath, "PATCH">("PATCH", path)
|
|
1617
|
+
return this._registerRoute<TPath, "PATCH">("PATCH", path)
|
|
1716
1618
|
}
|
|
1717
1619
|
post<const TPath extends string>(path: TPath) {
|
|
1718
|
-
return this._registerRoute<TPath, "POST">("POST", path)
|
|
1620
|
+
return this._registerRoute<TPath, "POST">("POST", path)
|
|
1719
1621
|
}
|
|
1720
1622
|
put<const TPath extends string>(path: TPath) {
|
|
1721
|
-
return this._registerRoute<TPath, "PUT">("PUT", path)
|
|
1623
|
+
return this._registerRoute<TPath, "PUT">("PUT", path)
|
|
1722
1624
|
}
|
|
1723
1625
|
|
|
1724
1626
|
ws<const TPath extends string>(
|
|
@@ -1735,51 +1637,42 @@ export class Honey<
|
|
|
1735
1637
|
TCtx & ApplyScoped<TScopedMw, MergePath<TBasePath, TPath>>,
|
|
1736
1638
|
{},
|
|
1737
1639
|
never,
|
|
1738
|
-
Honey<
|
|
1739
|
-
TEnv,
|
|
1740
|
-
TCtx,
|
|
1741
|
-
TRoutes,
|
|
1742
|
-
TMeta,
|
|
1743
|
-
TErrorFactory,
|
|
1744
|
-
TDefaultErrors,
|
|
1745
|
-
TBasePath,
|
|
1746
|
-
TTaps,
|
|
1747
|
-
TScopedMw
|
|
1748
|
-
>
|
|
1640
|
+
Honey<TEnv, TCtx, TRoutes, TMeta, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw>
|
|
1749
1641
|
>({
|
|
1750
1642
|
boundaryErrorKey: this._defaultBoundaryKey,
|
|
1751
1643
|
errorKeys: new Set(),
|
|
1752
1644
|
inputSchemas: null,
|
|
1753
1645
|
meta: null,
|
|
1754
1646
|
middlewares: [],
|
|
1647
|
+
mwMeta: this._contributedMetaFor(mergePath(this._basePath, path)),
|
|
1755
1648
|
parent: this,
|
|
1756
1649
|
parentMiddlewares: this._chainMiddlewares,
|
|
1757
1650
|
path: mergePath(this._basePath, path),
|
|
1758
1651
|
root: this._root,
|
|
1759
|
-
})
|
|
1652
|
+
})
|
|
1760
1653
|
}
|
|
1761
1654
|
|
|
1762
1655
|
realtime(path: string, opts: RealtimeRouteOpts): this {
|
|
1763
|
-
const fullPath = mergePath(this._basePath, path) || "/"
|
|
1656
|
+
const fullPath = mergePath(this._basePath, path) || "/"
|
|
1764
1657
|
|
|
1765
1658
|
if (!this._realtimeBus) {
|
|
1766
|
-
this._realtimeBus = createBus()
|
|
1659
|
+
this._realtimeBus = createBus()
|
|
1767
1660
|
}
|
|
1768
1661
|
|
|
1769
1662
|
if (this._realtimeRoutes.has(fullPath)) {
|
|
1770
|
-
throw new Error(`Duplicate realtime route: ${fullPath}`)
|
|
1663
|
+
throw new Error(`Duplicate realtime route: ${fullPath}`)
|
|
1771
1664
|
}
|
|
1772
1665
|
|
|
1773
1666
|
this._realtimeRoutes.set(fullPath, {
|
|
1774
1667
|
handler: opts.handler,
|
|
1775
1668
|
middlewares: opts.use,
|
|
1776
1669
|
reconnectBuffer: opts.reconnectBuffer,
|
|
1777
|
-
})
|
|
1670
|
+
})
|
|
1778
1671
|
|
|
1779
|
-
const mw: RuntimeMiddleware[] = []
|
|
1672
|
+
const mw: RuntimeMiddleware[] = []
|
|
1780
1673
|
if (opts.use) {
|
|
1781
1674
|
for (const fn of opts.use) {
|
|
1782
|
-
mw.push(fn as RuntimeMiddleware)
|
|
1675
|
+
mw.push(fn as RuntimeMiddleware)
|
|
1783
1676
|
}
|
|
1784
1677
|
}
|
|
1785
1678
|
|
|
@@ -1791,10 +1684,10 @@ export class Honey<
|
|
|
1791
1684
|
mt: null,
|
|
1792
1685
|
mw: [...this._chainMiddlewares, ...mw],
|
|
1793
1686
|
rp: fullPath,
|
|
1794
|
-
})
|
|
1687
|
+
})
|
|
1795
1688
|
|
|
1796
|
-
this._hasWsRoutes = true
|
|
1797
|
-
return this
|
|
1689
|
+
this._hasWsRoutes = true
|
|
1690
|
+
return this
|
|
1798
1691
|
}
|
|
1799
1692
|
|
|
1800
1693
|
/**
|
|
@@ -1808,7 +1701,7 @@ export class Honey<
|
|
|
1808
1701
|
executionCtx?: { waitUntil?: (p: Promise<unknown>) => void },
|
|
1809
1702
|
): Response | Promise<Response> {
|
|
1810
1703
|
if (this._wsAdapter === null && !this._hasWsRoutes) {
|
|
1811
|
-
return this._doFetch(request, env, executionCtx)
|
|
1704
|
+
return this._doFetch(request, env, executionCtx)
|
|
1812
1705
|
}
|
|
1813
1706
|
const isWsUpgrade = request.headers.get("upgrade")?.toLowerCase() === "websocket"
|
|
1814
1707
|
const headerSnap = isWsUpgrade ? new Headers(request.headers) : undefined
|
|
@@ -1886,31 +1779,30 @@ export class Honey<
|
|
|
1886
1779
|
knownWsUpgrade = false,
|
|
1887
1780
|
headerSnap?: Headers,
|
|
1888
1781
|
): Response | Promise<Response> {
|
|
1889
|
-
const startTime = performance.now()
|
|
1782
|
+
const startTime = performance.now()
|
|
1890
1783
|
|
|
1891
1784
|
/* fast path extraction — avoids expensive new URL() allocation */
|
|
1892
|
-
const rawUrl = request.url
|
|
1893
|
-
const protoEnd = rawUrl.indexOf("//")
|
|
1894
|
-
const pathStart = protoEnd === -1 ? 0 : rawUrl.indexOf("/", protoEnd + 2)
|
|
1895
|
-
const searchOrHash =
|
|
1896
|
-
|
|
1897
|
-
let path: string;
|
|
1785
|
+
const rawUrl = request.url
|
|
1786
|
+
const protoEnd = rawUrl.indexOf("//")
|
|
1787
|
+
const pathStart = protoEnd === -1 ? 0 : rawUrl.indexOf("/", protoEnd + 2)
|
|
1788
|
+
const searchOrHash = pathStart === -1 ? -1 : findSearchOrHash(rawUrl, pathStart)
|
|
1789
|
+
let path: string
|
|
1898
1790
|
if (pathStart === -1) {
|
|
1899
|
-
path = "/"
|
|
1791
|
+
path = "/"
|
|
1900
1792
|
} else if (searchOrHash === -1) {
|
|
1901
|
-
path = rawUrl.substring(pathStart)
|
|
1793
|
+
path = rawUrl.substring(pathStart)
|
|
1902
1794
|
} else {
|
|
1903
|
-
path = rawUrl.substring(pathStart, searchOrHash)
|
|
1795
|
+
path = rawUrl.substring(pathStart, searchOrHash)
|
|
1904
1796
|
}
|
|
1905
1797
|
|
|
1906
1798
|
/* lazily create URL only when actually needed (search params, redirects) */
|
|
1907
|
-
let _url: URL | undefined
|
|
1799
|
+
let _url: URL | undefined
|
|
1908
1800
|
const getUrl = (): URL => {
|
|
1909
|
-
if (_url === undefined) _url = new URL(rawUrl)
|
|
1910
|
-
return _url
|
|
1911
|
-
}
|
|
1801
|
+
if (_url === undefined) _url = new URL(rawUrl)
|
|
1802
|
+
return _url
|
|
1803
|
+
}
|
|
1912
1804
|
|
|
1913
|
-
const log = this._logger ?? undefined
|
|
1805
|
+
const log = this._logger ?? undefined
|
|
1914
1806
|
const fc: FetchCtx<TEnv> = {
|
|
1915
1807
|
env,
|
|
1916
1808
|
executionCtx,
|
|
@@ -1920,109 +1812,94 @@ export class Honey<
|
|
|
1920
1812
|
url: getUrl,
|
|
1921
1813
|
wsUpgrade: knownWsUpgrade,
|
|
1922
1814
|
headerSnap,
|
|
1923
|
-
}
|
|
1815
|
+
}
|
|
1924
1816
|
|
|
1925
1817
|
if (this._telemetry !== null) {
|
|
1926
|
-
safeFire(() => this._telemetry?.onRequest?.({ env, req: request }), log)
|
|
1818
|
+
safeFire(() => this._telemetry?.onRequest?.({ env, req: request }), log)
|
|
1927
1819
|
}
|
|
1928
1820
|
|
|
1929
1821
|
/* trailing slash handling */
|
|
1930
1822
|
if (path.length > 1) {
|
|
1931
1823
|
if (this._trailingSlash === "strip" && path.endsWith("/")) {
|
|
1932
|
-
const redirectUrl = getUrl()
|
|
1933
|
-
redirectUrl.pathname = path.slice(0, -1)
|
|
1824
|
+
const redirectUrl = getUrl()
|
|
1825
|
+
redirectUrl.pathname = path.slice(0, -1)
|
|
1934
1826
|
return new Response(null, {
|
|
1935
1827
|
headers: { location: redirectUrl.toString() },
|
|
1936
1828
|
status: 308,
|
|
1937
|
-
})
|
|
1829
|
+
})
|
|
1938
1830
|
}
|
|
1939
1831
|
if (this._trailingSlash === "enforce" && !path.endsWith("/")) {
|
|
1940
|
-
const redirectUrl = getUrl()
|
|
1941
|
-
redirectUrl.pathname = `${path}
|
|
1832
|
+
const redirectUrl = getUrl()
|
|
1833
|
+
redirectUrl.pathname = `${path}/`
|
|
1942
1834
|
return new Response(null, {
|
|
1943
1835
|
headers: { location: redirectUrl.toString() },
|
|
1944
1836
|
status: 308,
|
|
1945
|
-
})
|
|
1837
|
+
})
|
|
1946
1838
|
}
|
|
1947
1839
|
}
|
|
1948
1840
|
|
|
1949
1841
|
/* prefix stripping — must run AFTER trailing slash so redirects preserve the full prefixed URL */
|
|
1950
1842
|
if (this._stripPrefix !== null) {
|
|
1951
1843
|
if (path === this._stripPrefix) {
|
|
1952
|
-
path = "/"
|
|
1953
|
-
} else if (
|
|
1954
|
-
path.
|
|
1955
|
-
path.charCodeAt(this._stripPrefix.length) === 47
|
|
1956
|
-
) {
|
|
1957
|
-
path = path.slice(this._stripPrefix.length);
|
|
1844
|
+
path = "/"
|
|
1845
|
+
} else if (path.startsWith(this._stripPrefix) && path.charCodeAt(this._stripPrefix.length) === 47) {
|
|
1846
|
+
path = path.slice(this._stripPrefix.length)
|
|
1958
1847
|
}
|
|
1959
1848
|
}
|
|
1960
1849
|
|
|
1961
1850
|
/* WebSocket route check — do not re-read headers after Deno.upgradeWebSocket */
|
|
1962
|
-
const isWsUpgrade = knownWsUpgrade || requestIsWsUpgrade(request)
|
|
1851
|
+
const isWsUpgrade = knownWsUpgrade || requestIsWsUpgrade(request)
|
|
1963
1852
|
if (isWsUpgrade) {
|
|
1964
|
-
const wsMatch = matchWsRoute(this._root, path)
|
|
1853
|
+
const wsMatch = matchWsRoute(this._root, path)
|
|
1965
1854
|
if (wsMatch !== null) {
|
|
1966
|
-
return this._handleWs(fc, wsMatch)
|
|
1855
|
+
return this._handleWs(fc, wsMatch)
|
|
1967
1856
|
}
|
|
1968
1857
|
} else if (this._realtimeRoutes.size > 0) {
|
|
1969
1858
|
/* Non-upgrade request hitting a realtime-only path → 426 Upgrade Required */
|
|
1970
|
-
const wsMatch = matchWsRoute(this._root, path)
|
|
1859
|
+
const wsMatch = matchWsRoute(this._root, path)
|
|
1971
1860
|
if (wsMatch !== null && this._realtimeRoutes.has(wsMatch.handler.rp)) {
|
|
1972
|
-
return new Response(null, { headers: { upgrade: "websocket" }, status: 426 })
|
|
1861
|
+
return new Response(null, { headers: { upgrade: "websocket" }, status: 426 })
|
|
1973
1862
|
}
|
|
1974
1863
|
}
|
|
1975
1864
|
|
|
1976
|
-
const method = request.method.toUpperCase() as HttpMethod
|
|
1865
|
+
const method = request.method.toUpperCase() as HttpMethod
|
|
1977
1866
|
|
|
1978
1867
|
/* fn:null fallthrough — tree match sets meta, catch-all dispatches */
|
|
1979
|
-
let fnNullMeta: Record<string, unknown> | null = null
|
|
1980
|
-
let fnNullParams: Record<string, string> | null = null
|
|
1981
|
-
let fnNullHit = false
|
|
1868
|
+
let fnNullMeta: Record<string, unknown> | null = null
|
|
1869
|
+
let fnNullParams: Record<string, string> | null = null
|
|
1870
|
+
let fnNullHit = false
|
|
1982
1871
|
|
|
1983
1872
|
/* Tier 2: O(1) static route lookup — checks both precompiled and runtime maps */
|
|
1984
|
-
const smap = this._staticRoutes.map ?? this._handlerMap
|
|
1873
|
+
const smap = this._staticRoutes.map ?? this._handlerMap
|
|
1985
1874
|
if (smap !== null) {
|
|
1986
|
-
const key = `${method} ${path}
|
|
1987
|
-
const staticHandler = smap[key]
|
|
1875
|
+
const key = `${method} ${path}`
|
|
1876
|
+
const staticHandler = smap[key]
|
|
1988
1877
|
if (staticHandler) {
|
|
1989
1878
|
if (staticHandler.fn === null) {
|
|
1990
|
-
fnNullMeta = staticHandler.mt
|
|
1991
|
-
fnNullParams = EMPTY_PARAMS
|
|
1992
|
-
fnNullHit = true
|
|
1879
|
+
fnNullMeta = staticHandler.mt
|
|
1880
|
+
fnNullParams = EMPTY_PARAMS
|
|
1881
|
+
fnNullHit = true
|
|
1993
1882
|
} else {
|
|
1994
|
-
return this._handleMatched(
|
|
1995
|
-
fc,
|
|
1996
|
-
method,
|
|
1997
|
-
path,
|
|
1998
|
-
staticHandler,
|
|
1999
|
-
EMPTY_PARAMS,
|
|
2000
|
-
);
|
|
1883
|
+
return this._handleMatched(fc, method, path, staticHandler, EMPTY_PARAMS)
|
|
2001
1884
|
}
|
|
2002
1885
|
}
|
|
2003
1886
|
/* HEAD falls back to GET */
|
|
2004
1887
|
if (!fnNullHit && method === "HEAD") {
|
|
2005
|
-
const getHandler = smap[`GET ${path}`]
|
|
1888
|
+
const getHandler = smap[`GET ${path}`]
|
|
2006
1889
|
if (getHandler) {
|
|
2007
1890
|
if (getHandler.fn === null) {
|
|
2008
|
-
fnNullMeta = getHandler.mt
|
|
2009
|
-
fnNullParams = EMPTY_PARAMS
|
|
2010
|
-
fnNullHit = true
|
|
1891
|
+
fnNullMeta = getHandler.mt
|
|
1892
|
+
fnNullParams = EMPTY_PARAMS
|
|
1893
|
+
fnNullHit = true
|
|
2011
1894
|
} else {
|
|
2012
|
-
return this._handleMatched(
|
|
2013
|
-
fc,
|
|
2014
|
-
method,
|
|
2015
|
-
path,
|
|
2016
|
-
getHandler,
|
|
2017
|
-
EMPTY_PARAMS,
|
|
2018
|
-
);
|
|
1895
|
+
return this._handleMatched(fc, method, path, getHandler, EMPTY_PARAMS)
|
|
2019
1896
|
}
|
|
2020
1897
|
}
|
|
2021
1898
|
}
|
|
2022
1899
|
}
|
|
2023
1900
|
|
|
2024
1901
|
if (!fnNullHit) {
|
|
2025
|
-
const result = matchRoute(this._root, method, path)
|
|
1902
|
+
const result = matchRoute(this._root, method, path)
|
|
2026
1903
|
|
|
2027
1904
|
if (result?.matched) {
|
|
2028
1905
|
/*
|
|
@@ -2034,41 +1911,34 @@ export class Honey<
|
|
|
2034
1911
|
this._hasRouteTree &&
|
|
2035
1912
|
this._root.w !== null &&
|
|
2036
1913
|
result.handler.fn !== null &&
|
|
2037
|
-
(this._root.w.m[method] === result.handler ||
|
|
2038
|
-
this._root.w.m["ALL"] === result.handler);
|
|
1914
|
+
(this._root.w.m[method] === result.handler || this._root.w.m["ALL"] === result.handler)
|
|
2039
1915
|
|
|
2040
1916
|
if (isWildcardCatchAll) {
|
|
2041
1917
|
/* path not in routeTree — 404 */
|
|
2042
|
-
return this._handle404(fc, method, path)
|
|
1918
|
+
return this._handle404(fc, method, path)
|
|
2043
1919
|
}
|
|
2044
1920
|
|
|
2045
1921
|
if (result.handler.fn === null) {
|
|
2046
|
-
fnNullMeta = result.handler.mt
|
|
2047
|
-
fnNullParams = result.params
|
|
2048
|
-
fnNullHit = true
|
|
1922
|
+
fnNullMeta = result.handler.mt
|
|
1923
|
+
fnNullParams = result.params
|
|
1924
|
+
fnNullHit = true
|
|
2049
1925
|
} else {
|
|
2050
|
-
return this._handleMatched(
|
|
2051
|
-
fc,
|
|
2052
|
-
method,
|
|
2053
|
-
path,
|
|
2054
|
-
result.handler,
|
|
2055
|
-
result.params,
|
|
2056
|
-
);
|
|
1926
|
+
return this._handleMatched(fc, method, path, result.handler, result.params)
|
|
2057
1927
|
}
|
|
2058
1928
|
}
|
|
2059
1929
|
|
|
2060
1930
|
if (!fnNullHit) {
|
|
2061
1931
|
if (result === null) {
|
|
2062
1932
|
if (!isWsUpgrade) {
|
|
2063
|
-
const wsMatch = matchWsRoute(this._root, path)
|
|
1933
|
+
const wsMatch = matchWsRoute(this._root, path)
|
|
2064
1934
|
if (wsMatch !== null) {
|
|
2065
1935
|
return new Response("Upgrade Required", {
|
|
2066
1936
|
headers: { connection: "Upgrade", upgrade: "websocket" },
|
|
2067
1937
|
status: 426,
|
|
2068
|
-
})
|
|
1938
|
+
})
|
|
2069
1939
|
}
|
|
2070
1940
|
}
|
|
2071
|
-
return this._handle404(fc, method, path)
|
|
1941
|
+
return this._handle404(fc, method, path)
|
|
2072
1942
|
}
|
|
2073
1943
|
|
|
2074
1944
|
if (!result.matched) {
|
|
@@ -2077,47 +1947,33 @@ export class Honey<
|
|
|
2077
1947
|
fc.request.headers.has("access-control-request-method") &&
|
|
2078
1948
|
result.allowed.length > 0
|
|
2079
1949
|
) {
|
|
2080
|
-
const fallback = this._preflightFallbackMethod(result.allowed)
|
|
2081
|
-
const retry = matchRoute(this._root, fallback, path)
|
|
1950
|
+
const fallback = this._preflightFallbackMethod(result.allowed)
|
|
1951
|
+
const retry = matchRoute(this._root, fallback, path)
|
|
2082
1952
|
if (retry?.matched && retry.handler.fn !== null) {
|
|
2083
|
-
return this._handleCorsPreflight(
|
|
2084
|
-
fc,
|
|
2085
|
-
path,
|
|
2086
|
-
retry.handler,
|
|
2087
|
-
retry.params,
|
|
2088
|
-
result.allowed,
|
|
2089
|
-
);
|
|
1953
|
+
return this._handleCorsPreflight(fc, path, retry.handler, retry.params, result.allowed)
|
|
2090
1954
|
}
|
|
2091
1955
|
}
|
|
2092
|
-
return this._handle405(fc, method, path, result.allowed)
|
|
1956
|
+
return this._handle405(fc, method, path, result.allowed)
|
|
2093
1957
|
}
|
|
2094
1958
|
}
|
|
2095
1959
|
}
|
|
2096
1960
|
|
|
2097
1961
|
/* fn:null hit — find catch-all/wildcard to dispatch with stashed meta */
|
|
2098
|
-
const wildcardResult = matchRoute(this._root, method, "/*")
|
|
1962
|
+
const wildcardResult = matchRoute(this._root, method, "/*")
|
|
2099
1963
|
if (wildcardResult?.matched && wildcardResult.handler.fn !== null) {
|
|
2100
|
-
return this._handleMatched(
|
|
2101
|
-
fc,
|
|
2102
|
-
method,
|
|
2103
|
-
path,
|
|
2104
|
-
wildcardResult.handler,
|
|
2105
|
-
fnNullParams ?? EMPTY_PARAMS,
|
|
2106
|
-
fnNullMeta,
|
|
2107
|
-
);
|
|
1964
|
+
return this._handleMatched(fc, method, path, wildcardResult.handler, fnNullParams ?? EMPTY_PARAMS, fnNullMeta)
|
|
2108
1965
|
}
|
|
2109
1966
|
|
|
2110
|
-
return this._handle404(fc, method, path)
|
|
1967
|
+
return this._handle404(fc, method, path)
|
|
2111
1968
|
}
|
|
2112
1969
|
|
|
2113
1970
|
private _makeErrorCtx(fc: FetchCtx<TEnv>, allowed?: string[]) {
|
|
2114
1971
|
return {
|
|
2115
1972
|
env: fc.env,
|
|
2116
|
-
jsonFromError: (err: HoneyError) =>
|
|
2117
|
-
createErrorResponse(err, this._errorFormatter, this._customErrorFormatter),
|
|
1973
|
+
jsonFromError: (err: HoneyError) => createErrorResponse(err, this._errorFormatter, this._customErrorFormatter),
|
|
2118
1974
|
req: fc.request,
|
|
2119
1975
|
...(allowed ? { allowed } : {}),
|
|
2120
|
-
}
|
|
1976
|
+
}
|
|
2121
1977
|
}
|
|
2122
1978
|
|
|
2123
1979
|
private async _handleWs(
|
|
@@ -2125,27 +1981,27 @@ export class Honey<
|
|
|
2125
1981
|
wsMatch: { handler: WSRouteHandler; params: Record<string, string> },
|
|
2126
1982
|
): Promise<Response> {
|
|
2127
1983
|
/* Dispatch to realtime handler if this path is registered as a realtime route */
|
|
2128
|
-
const realtimeConfig = this._realtimeRoutes.get(wsMatch.handler.rp)
|
|
1984
|
+
const realtimeConfig = this._realtimeRoutes.get(wsMatch.handler.rp)
|
|
2129
1985
|
if (realtimeConfig) {
|
|
2130
|
-
return this._handleRealtime(fc, wsMatch, realtimeConfig)
|
|
1986
|
+
return this._handleRealtime(fc, wsMatch, realtimeConfig)
|
|
2131
1987
|
}
|
|
2132
1988
|
|
|
2133
|
-
const isUpgrade = fc.wsUpgrade === true || requestIsWsUpgrade(fc.request)
|
|
1989
|
+
const isUpgrade = fc.wsUpgrade === true || requestIsWsUpgrade(fc.request)
|
|
2134
1990
|
if (!isUpgrade) {
|
|
2135
1991
|
return new Response(null, {
|
|
2136
1992
|
headers: { upgrade: "websocket" },
|
|
2137
1993
|
status: 426,
|
|
2138
|
-
})
|
|
1994
|
+
})
|
|
2139
1995
|
}
|
|
2140
1996
|
|
|
2141
|
-
const wsAdapter = this._wsAdapter
|
|
1997
|
+
const wsAdapter = this._wsAdapter
|
|
2142
1998
|
if (!wsAdapter) {
|
|
2143
|
-
fc.log?.warn?.("WebSocket adapter not configured — call .wsAdapter()")
|
|
1999
|
+
fc.log?.warn?.("WebSocket adapter not configured — call .wsAdapter()")
|
|
2144
2000
|
return createErrorResponse(
|
|
2145
2001
|
this._createError(EK.internal_server_error, SK.internal_server_error),
|
|
2146
2002
|
this._errorFormatter,
|
|
2147
2003
|
this._customErrorFormatter,
|
|
2148
|
-
)
|
|
2004
|
+
)
|
|
2149
2005
|
}
|
|
2150
2006
|
|
|
2151
2007
|
const wsCtx = new HoneyContext({
|
|
@@ -2154,43 +2010,39 @@ export class Honey<
|
|
|
2154
2010
|
params: wsMatch.params,
|
|
2155
2011
|
req: ctxRequest(fc),
|
|
2156
2012
|
urlFn: fc.url,
|
|
2157
|
-
})
|
|
2158
|
-
if (this._contextValues) Object.assign(wsCtx, this._contextValues)
|
|
2013
|
+
})
|
|
2014
|
+
if (this._contextValues) Object.assign(wsCtx, this._contextValues)
|
|
2159
2015
|
|
|
2160
2016
|
/*
|
|
2161
2017
|
* WS ordering: [global → scoped → chain+handler-route-specific]
|
|
2162
2018
|
* WS bakes chain mw into handler.mw at registration, so scoped runs before chain.
|
|
2163
2019
|
* This is an unavoidable inconsistency vs HTTP (where chain runs before scoped).
|
|
2164
2020
|
*/
|
|
2165
|
-
const scopedForPath = this._filterScopedForPath(wsMatch.handler.rp)
|
|
2166
|
-
const allWsMw: RuntimeMiddleware[] = [
|
|
2167
|
-
...this._globalMiddlewares,
|
|
2168
|
-
...scopedForPath,
|
|
2169
|
-
...wsMatch.handler.mw,
|
|
2170
|
-
];
|
|
2021
|
+
const scopedForPath = this._filterScopedForPath(wsMatch.handler.rp)
|
|
2022
|
+
const allWsMw: RuntimeMiddleware[] = [...this._globalMiddlewares, ...scopedForPath, ...wsMatch.handler.mw]
|
|
2171
2023
|
|
|
2172
2024
|
try {
|
|
2173
2025
|
return await executeChain(allWsMw, wsCtx, async (finalCtx) => {
|
|
2174
|
-
const userHandler = wsMatch.handler.fn
|
|
2175
|
-
let messageQueue: Promise<void> = Promise.resolve()
|
|
2026
|
+
const userHandler = wsMatch.handler.fn
|
|
2027
|
+
let messageQueue: Promise<void> = Promise.resolve()
|
|
2176
2028
|
|
|
2177
|
-
const onOpenFn = userHandler.onOpen
|
|
2178
|
-
const onMsgFn = userHandler.onMessage
|
|
2179
|
-
const onCloseFn = userHandler.onClose
|
|
2180
|
-
const onErrorFn = userHandler.onError
|
|
2181
|
-
const onReconnectFn = userHandler.onReconnect
|
|
2182
|
-
const reconnectToken = fc.url().searchParams.get("reconnect_token")
|
|
2029
|
+
const onOpenFn = userHandler.onOpen
|
|
2030
|
+
const onMsgFn = userHandler.onMessage
|
|
2031
|
+
const onCloseFn = userHandler.onClose
|
|
2032
|
+
const onErrorFn = userHandler.onError
|
|
2033
|
+
const onReconnectFn = userHandler.onReconnect
|
|
2034
|
+
const reconnectToken = fc.url().searchParams.get("reconnect_token")
|
|
2183
2035
|
|
|
2184
|
-
const wrappedHandler: WSHandler<unknown> = {}
|
|
2036
|
+
const wrappedHandler: WSHandler<unknown> = {}
|
|
2185
2037
|
|
|
2186
2038
|
if (reconnectToken && onReconnectFn) {
|
|
2187
2039
|
wrappedHandler.onOpen = (_ctx, ws) => {
|
|
2188
|
-
onReconnectFn(finalCtx, ws, reconnectToken)
|
|
2189
|
-
}
|
|
2040
|
+
onReconnectFn(finalCtx, ws, reconnectToken)
|
|
2041
|
+
}
|
|
2190
2042
|
} else if (onOpenFn) {
|
|
2191
2043
|
wrappedHandler.onOpen = (_ctx, ws) => {
|
|
2192
|
-
onOpenFn(finalCtx, ws)
|
|
2193
|
-
}
|
|
2044
|
+
onOpenFn(finalCtx, ws)
|
|
2045
|
+
}
|
|
2194
2046
|
}
|
|
2195
2047
|
|
|
2196
2048
|
if (onMsgFn) {
|
|
@@ -2198,32 +2050,28 @@ export class Honey<
|
|
|
2198
2050
|
messageQueue = messageQueue
|
|
2199
2051
|
.then(() => onMsgFn(finalCtx, ws, data))
|
|
2200
2052
|
.catch((err: unknown) => {
|
|
2201
|
-
onErrorFn?.(finalCtx, ws, err)
|
|
2202
|
-
})
|
|
2203
|
-
}
|
|
2053
|
+
onErrorFn?.(finalCtx, ws, err)
|
|
2054
|
+
})
|
|
2055
|
+
}
|
|
2204
2056
|
}
|
|
2205
2057
|
|
|
2206
2058
|
if (onCloseFn) {
|
|
2207
2059
|
wrappedHandler.onClose = (_ctx, ws, code, reason) => {
|
|
2208
|
-
onCloseFn(finalCtx, ws, code, reason)
|
|
2209
|
-
}
|
|
2060
|
+
onCloseFn(finalCtx, ws, code, reason)
|
|
2061
|
+
}
|
|
2210
2062
|
}
|
|
2211
2063
|
|
|
2212
2064
|
if (onErrorFn) {
|
|
2213
2065
|
wrappedHandler.onError = (_ctx, ws, error) => {
|
|
2214
|
-
onErrorFn(finalCtx, ws, error)
|
|
2215
|
-
}
|
|
2066
|
+
onErrorFn(finalCtx, ws, error)
|
|
2067
|
+
}
|
|
2216
2068
|
}
|
|
2217
2069
|
|
|
2218
|
-
const upgradeResult = await wsAdapter.upgrade(
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
wrappedHandler,
|
|
2222
|
-
);
|
|
2223
|
-
return upgradeResult.response;
|
|
2224
|
-
});
|
|
2070
|
+
const upgradeResult = await wsAdapter.upgrade(fc.request, fc.env, wrappedHandler)
|
|
2071
|
+
return upgradeResult.response
|
|
2072
|
+
})
|
|
2225
2073
|
} catch (thrown) {
|
|
2226
|
-
return this._toErrorResponse(thrown)
|
|
2074
|
+
return this._toErrorResponse(thrown)
|
|
2227
2075
|
}
|
|
2228
2076
|
}
|
|
2229
2077
|
|
|
@@ -2232,29 +2080,29 @@ export class Honey<
|
|
|
2232
2080
|
wsMatch: { handler: WSRouteHandler; params: Record<string, string> },
|
|
2233
2081
|
config: { handler: RealtimeRouteOpts["handler"]; middlewares?: RealtimeRouteOpts["use"]; reconnectBuffer?: number },
|
|
2234
2082
|
): Promise<Response> {
|
|
2235
|
-
const isUpgrade = fc.wsUpgrade === true || requestIsWsUpgrade(fc.request)
|
|
2083
|
+
const isUpgrade = fc.wsUpgrade === true || requestIsWsUpgrade(fc.request)
|
|
2236
2084
|
if (!isUpgrade) {
|
|
2237
2085
|
return new Response(null, {
|
|
2238
2086
|
headers: { upgrade: "websocket" },
|
|
2239
2087
|
status: 426,
|
|
2240
|
-
})
|
|
2088
|
+
})
|
|
2241
2089
|
}
|
|
2242
2090
|
|
|
2243
|
-
const wsAdapter = this._wsAdapter
|
|
2091
|
+
const wsAdapter = this._wsAdapter
|
|
2244
2092
|
if (!wsAdapter) {
|
|
2245
|
-
fc.log?.warn?.("WebSocket adapter not configured — call .wsAdapter()")
|
|
2093
|
+
fc.log?.warn?.("WebSocket adapter not configured — call .wsAdapter()")
|
|
2246
2094
|
return createErrorResponse(
|
|
2247
2095
|
this._createError(EK.internal_server_error, SK.internal_server_error),
|
|
2248
2096
|
this._errorFormatter,
|
|
2249
2097
|
this._customErrorFormatter,
|
|
2250
|
-
)
|
|
2098
|
+
)
|
|
2251
2099
|
}
|
|
2252
2100
|
|
|
2253
2101
|
/* Lazily create bus if not yet initialized (happens when realtime() was called on a child chain) */
|
|
2254
2102
|
if (!this._realtimeBus) {
|
|
2255
|
-
this._realtimeBus = createBus()
|
|
2103
|
+
this._realtimeBus = createBus()
|
|
2256
2104
|
}
|
|
2257
|
-
const bus = this._realtimeBus
|
|
2105
|
+
const bus = this._realtimeBus
|
|
2258
2106
|
|
|
2259
2107
|
const ctx = new HoneyContext({
|
|
2260
2108
|
env: fc.env,
|
|
@@ -2262,25 +2110,21 @@ export class Honey<
|
|
|
2262
2110
|
params: wsMatch.params,
|
|
2263
2111
|
req: ctxRequest(fc),
|
|
2264
2112
|
urlFn: fc.url,
|
|
2265
|
-
})
|
|
2266
|
-
if (this._contextValues) Object.assign(ctx, this._contextValues)
|
|
2113
|
+
})
|
|
2114
|
+
if (this._contextValues) Object.assign(ctx, this._contextValues)
|
|
2267
2115
|
Object.assign(ctx, {
|
|
2268
2116
|
realtime: { publish: (topic: string, data: unknown) => bus.publish(topic, data) },
|
|
2269
|
-
})
|
|
2117
|
+
})
|
|
2270
2118
|
|
|
2271
|
-
const scopedForPath = this._filterScopedForPath(wsMatch.handler.rp)
|
|
2272
|
-
const allMw: RuntimeMiddleware[] = [
|
|
2273
|
-
...this._globalMiddlewares,
|
|
2274
|
-
...scopedForPath,
|
|
2275
|
-
...wsMatch.handler.mw,
|
|
2276
|
-
];
|
|
2119
|
+
const scopedForPath = this._filterScopedForPath(wsMatch.handler.rp)
|
|
2120
|
+
const allMw: RuntimeMiddleware[] = [...this._globalMiddlewares, ...scopedForPath, ...wsMatch.handler.mw]
|
|
2277
2121
|
|
|
2278
2122
|
try {
|
|
2279
2123
|
return await executeChain(allMw, ctx, async (finalCtx) => {
|
|
2280
|
-
const connId = crypto.randomUUID()
|
|
2124
|
+
const connId = crypto.randomUUID()
|
|
2281
2125
|
|
|
2282
|
-
let socket: WSContext<unknown> | null = null
|
|
2283
|
-
let conn: ReturnType<typeof createConnContext> | null = null
|
|
2126
|
+
let socket: WSContext<unknown> | null = null
|
|
2127
|
+
let conn: ReturnType<typeof createConnContext> | null = null
|
|
2284
2128
|
|
|
2285
2129
|
/*
|
|
2286
2130
|
* initConn creates the ConnContext and calls the user handler.
|
|
@@ -2288,88 +2132,82 @@ export class Honey<
|
|
|
2288
2132
|
* or inline after upgrade (for Node/CF where socket is immediate).
|
|
2289
2133
|
*/
|
|
2290
2134
|
const initConn = (ws: WSContext<unknown>) => {
|
|
2291
|
-
socket = ws
|
|
2135
|
+
socket = ws
|
|
2292
2136
|
conn = createConnContext({
|
|
2293
2137
|
bus,
|
|
2294
2138
|
closeFn: (reason) => {
|
|
2295
|
-
if (socket) socket.close(1000, reason)
|
|
2139
|
+
if (socket) socket.close(1000, reason)
|
|
2296
2140
|
},
|
|
2297
2141
|
id: connId,
|
|
2298
2142
|
sendFn: (payload) => {
|
|
2299
|
-
if (socket)
|
|
2143
|
+
if (socket)
|
|
2144
|
+
socket.send(typeof payload === "object" && payload !== null ? JSON.stringify(payload) : String(payload))
|
|
2300
2145
|
},
|
|
2301
2146
|
transport: "ws",
|
|
2302
2147
|
userId: null,
|
|
2303
|
-
})
|
|
2148
|
+
})
|
|
2304
2149
|
|
|
2305
2150
|
bus.onMessage(connId, (data) => {
|
|
2306
2151
|
if (socket) {
|
|
2307
|
-
socket.send(typeof data === "object" && data !== null ? JSON.stringify(data) : String(data))
|
|
2152
|
+
socket.send(typeof data === "object" && data !== null ? JSON.stringify(data) : String(data))
|
|
2308
2153
|
}
|
|
2309
|
-
})
|
|
2154
|
+
})
|
|
2310
2155
|
|
|
2311
|
-
config.handler(finalCtx, conn)
|
|
2312
|
-
}
|
|
2156
|
+
config.handler(finalCtx, conn)
|
|
2157
|
+
}
|
|
2313
2158
|
|
|
2314
2159
|
const wrappedHandler: WSHandler<unknown> = {
|
|
2315
2160
|
onClose: (_ctx, _ws, _code, reason) => {
|
|
2316
|
-
if (!conn) return
|
|
2317
|
-
const handlers = conn._handlers
|
|
2161
|
+
if (!conn) return
|
|
2162
|
+
const handlers = conn._handlers
|
|
2318
2163
|
if (handlers.close) {
|
|
2319
|
-
handlers.close(reason || "normal")
|
|
2164
|
+
handlers.close(reason || "normal")
|
|
2320
2165
|
}
|
|
2321
|
-
bus.unsubscribeAll(connId)
|
|
2322
|
-
bus.removeHandler(connId)
|
|
2166
|
+
bus.unsubscribeAll(connId)
|
|
2167
|
+
bus.removeHandler(connId)
|
|
2323
2168
|
},
|
|
2324
2169
|
onMessage: (_ctx, _ws, data) => {
|
|
2325
|
-
if (!conn) return
|
|
2326
|
-
const handlers = conn._handlers
|
|
2170
|
+
if (!conn) return
|
|
2171
|
+
const handlers = conn._handlers
|
|
2327
2172
|
if (handlers.message && typeof data === "string") {
|
|
2328
2173
|
try {
|
|
2329
|
-
const parsed: unknown = JSON.parse(data)
|
|
2174
|
+
const parsed: unknown = JSON.parse(data)
|
|
2330
2175
|
if (isMsgFrame(parsed)) {
|
|
2331
|
-
handlers.message(parsed.data)
|
|
2176
|
+
handlers.message(parsed.data)
|
|
2332
2177
|
}
|
|
2333
|
-
} catch {
|
|
2178
|
+
} catch {
|
|
2179
|
+
/* ignore malformed frames */
|
|
2180
|
+
}
|
|
2334
2181
|
}
|
|
2335
2182
|
},
|
|
2336
2183
|
onOpen: (_ctx, ws) => {
|
|
2337
|
-
if (!socket) initConn(ws)
|
|
2184
|
+
if (!socket) initConn(ws)
|
|
2338
2185
|
},
|
|
2339
|
-
}
|
|
2186
|
+
}
|
|
2340
2187
|
|
|
2341
|
-
const upgradeResult = await wsAdapter.upgrade(fc.request, fc.env, wrappedHandler)
|
|
2188
|
+
const upgradeResult = await wsAdapter.upgrade(fc.request, fc.env, wrappedHandler)
|
|
2342
2189
|
|
|
2343
2190
|
/* Node/CF adapters return the socket from upgrade(); Bun returns undefined (socket comes via onOpen).
|
|
2344
2191
|
* Deno pre-upgrade may still be CONNECTING — wait for onOpen so the first send is not dropped. */
|
|
2345
2192
|
if (upgradeResult.socket && !socket && upgradeResult.socket.readyState === 1) {
|
|
2346
|
-
initConn(upgradeResult.socket)
|
|
2193
|
+
initConn(upgradeResult.socket)
|
|
2347
2194
|
}
|
|
2348
2195
|
|
|
2349
|
-
return upgradeResult.response
|
|
2350
|
-
})
|
|
2196
|
+
return upgradeResult.response
|
|
2197
|
+
})
|
|
2351
2198
|
} catch (thrown) {
|
|
2352
|
-
return this._toErrorResponse(thrown)
|
|
2199
|
+
return this._toErrorResponse(thrown)
|
|
2353
2200
|
}
|
|
2354
2201
|
}
|
|
2355
2202
|
|
|
2356
|
-
private async _handle404(
|
|
2357
|
-
|
|
2358
|
-
method: string,
|
|
2359
|
-
path: string,
|
|
2360
|
-
): Promise<Response> {
|
|
2361
|
-
safeFire(
|
|
2362
|
-
() => this._telemetry?.onNotFound?.({ method, path, req: fc.request }),
|
|
2363
|
-
fc.log,
|
|
2364
|
-
);
|
|
2203
|
+
private async _handle404(fc: FetchCtx<TEnv>, method: string, path: string): Promise<Response> {
|
|
2204
|
+
safeFire(() => this._telemetry?.onNotFound?.({ method, path, req: fc.request }), fc.log)
|
|
2365
2205
|
const make404 = () => {
|
|
2366
2206
|
if (this._onNotFound) {
|
|
2367
|
-
return this._onNotFound(this._makeErrorCtx(fc))
|
|
2207
|
+
return this._onNotFound(this._makeErrorCtx(fc))
|
|
2368
2208
|
}
|
|
2369
|
-
return this._makeErrorCtx(fc).jsonFromError(
|
|
2370
|
-
|
|
2371
|
-
);
|
|
2372
|
-
};
|
|
2209
|
+
return this._makeErrorCtx(fc).jsonFromError(this._createError(EK.not_found, SK.not_found))
|
|
2210
|
+
}
|
|
2373
2211
|
try {
|
|
2374
2212
|
const ctx404 = new HoneyContext({
|
|
2375
2213
|
env: fc.env,
|
|
@@ -2377,12 +2215,12 @@ export class Honey<
|
|
|
2377
2215
|
params: {},
|
|
2378
2216
|
req: fc.request,
|
|
2379
2217
|
urlFn: fc.url,
|
|
2380
|
-
})
|
|
2381
|
-
if (this._contextValues) Object.assign(ctx404, this._contextValues)
|
|
2218
|
+
})
|
|
2219
|
+
if (this._contextValues) Object.assign(ctx404, this._contextValues)
|
|
2382
2220
|
const res =
|
|
2383
2221
|
this._chainMiddlewares.length > 0
|
|
2384
2222
|
? await executeChain(this._chainMiddlewares, ctx404, make404)
|
|
2385
|
-
: await make404()
|
|
2223
|
+
: await make404()
|
|
2386
2224
|
safeFire(
|
|
2387
2225
|
() =>
|
|
2388
2226
|
this._telemetry?.onResponse?.({
|
|
@@ -2391,18 +2229,18 @@ export class Honey<
|
|
|
2391
2229
|
status: res.status,
|
|
2392
2230
|
}),
|
|
2393
2231
|
fc.log,
|
|
2394
|
-
)
|
|
2395
|
-
return res
|
|
2232
|
+
)
|
|
2233
|
+
return res
|
|
2396
2234
|
} catch (thrown) {
|
|
2397
|
-
return this._toErrorResponse(thrown)
|
|
2235
|
+
return this._toErrorResponse(thrown)
|
|
2398
2236
|
}
|
|
2399
2237
|
}
|
|
2400
2238
|
|
|
2401
2239
|
private _preflightFallbackMethod(allowed: string[]): HttpMethod {
|
|
2402
|
-
if (allowed.includes("GET")) return "GET"
|
|
2403
|
-
if (allowed.includes("HEAD")) return "HEAD"
|
|
2404
|
-
if (allowed.includes("POST")) return "POST"
|
|
2405
|
-
return allowed[0] as HttpMethod
|
|
2240
|
+
if (allowed.includes("GET")) return "GET"
|
|
2241
|
+
if (allowed.includes("HEAD")) return "HEAD"
|
|
2242
|
+
if (allowed.includes("POST")) return "POST"
|
|
2243
|
+
return allowed[0] as HttpMethod
|
|
2406
2244
|
}
|
|
2407
2245
|
|
|
2408
2246
|
/** Run the existing method's middleware for a CORS preflight. Do not invoke the route handler. */
|
|
@@ -2422,25 +2260,18 @@ export class Honey<
|
|
|
2422
2260
|
req: fc.request,
|
|
2423
2261
|
routePattern: handler.rp,
|
|
2424
2262
|
urlFn: fc.url,
|
|
2425
|
-
})
|
|
2426
|
-
if (this._contextValues) Object.assign(ctx, this._contextValues)
|
|
2263
|
+
})
|
|
2264
|
+
if (this._contextValues) Object.assign(ctx, this._contextValues)
|
|
2427
2265
|
try {
|
|
2428
|
-
return await executeChain(
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
() => this._handle405(fc, "OPTIONS", path, allowed),
|
|
2432
|
-
);
|
|
2266
|
+
return await executeChain([...this._globalMiddlewares, ...handler.mw], ctx, () =>
|
|
2267
|
+
this._handle405(fc, "OPTIONS", path, allowed),
|
|
2268
|
+
)
|
|
2433
2269
|
} catch (thrown) {
|
|
2434
|
-
return this._toErrorResponse(thrown)
|
|
2270
|
+
return this._toErrorResponse(thrown)
|
|
2435
2271
|
}
|
|
2436
2272
|
}
|
|
2437
2273
|
|
|
2438
|
-
private async _handle405(
|
|
2439
|
-
fc: FetchCtx<TEnv>,
|
|
2440
|
-
method: string,
|
|
2441
|
-
path: string,
|
|
2442
|
-
allowed: string[],
|
|
2443
|
-
): Promise<Response> {
|
|
2274
|
+
private async _handle405(fc: FetchCtx<TEnv>, method: string, path: string, allowed: string[]): Promise<Response> {
|
|
2444
2275
|
safeFire(
|
|
2445
2276
|
() =>
|
|
2446
2277
|
this._telemetry?.onMethodNotAllowed?.({
|
|
@@ -2450,36 +2281,33 @@ export class Honey<
|
|
|
2450
2281
|
req: fc.request,
|
|
2451
2282
|
}),
|
|
2452
2283
|
fc.log,
|
|
2453
|
-
)
|
|
2284
|
+
)
|
|
2454
2285
|
const make405 = async () => {
|
|
2455
2286
|
if (this._onMethodNotAllowed) {
|
|
2456
2287
|
const res = await this._onMethodNotAllowed(
|
|
2457
2288
|
this._makeErrorCtx(fc, allowed) as {
|
|
2458
|
-
allowed: string[]
|
|
2459
|
-
env: TEnv
|
|
2460
|
-
jsonFromError: (err: HoneyError) => Response
|
|
2461
|
-
req: Request
|
|
2289
|
+
allowed: string[]
|
|
2290
|
+
env: TEnv
|
|
2291
|
+
jsonFromError: (err: HoneyError) => Response
|
|
2292
|
+
req: Request
|
|
2462
2293
|
},
|
|
2463
|
-
)
|
|
2464
|
-
const responseHeaders = new Headers(res.headers)
|
|
2465
|
-
responseHeaders.set("allow", allowed.join(", "))
|
|
2294
|
+
)
|
|
2295
|
+
const responseHeaders = new Headers(res.headers)
|
|
2296
|
+
responseHeaders.set("allow", allowed.join(", "))
|
|
2466
2297
|
return new Response(res.body, {
|
|
2467
2298
|
headers: responseHeaders,
|
|
2468
2299
|
status: res.status,
|
|
2469
|
-
})
|
|
2300
|
+
})
|
|
2470
2301
|
}
|
|
2471
|
-
const err = this._createError(
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
)
|
|
2475
|
-
const res = this._makeErrorCtx(fc).jsonFromError(err);
|
|
2476
|
-
const responseHeaders = new Headers(res.headers);
|
|
2477
|
-
responseHeaders.set("allow", allowed.join(", "));
|
|
2302
|
+
const err = this._createError(EK.method_not_allowed, SK.method_not_allowed)
|
|
2303
|
+
const res = this._makeErrorCtx(fc).jsonFromError(err)
|
|
2304
|
+
const responseHeaders = new Headers(res.headers)
|
|
2305
|
+
responseHeaders.set("allow", allowed.join(", "))
|
|
2478
2306
|
return new Response(res.body, {
|
|
2479
2307
|
headers: responseHeaders,
|
|
2480
2308
|
status: res.status,
|
|
2481
|
-
})
|
|
2482
|
-
}
|
|
2309
|
+
})
|
|
2310
|
+
}
|
|
2483
2311
|
try {
|
|
2484
2312
|
const ctx405 = new HoneyContext({
|
|
2485
2313
|
env: fc.env,
|
|
@@ -2487,12 +2315,12 @@ export class Honey<
|
|
|
2487
2315
|
params: {},
|
|
2488
2316
|
req: fc.request,
|
|
2489
2317
|
urlFn: fc.url,
|
|
2490
|
-
})
|
|
2491
|
-
if (this._contextValues) Object.assign(ctx405, this._contextValues)
|
|
2318
|
+
})
|
|
2319
|
+
if (this._contextValues) Object.assign(ctx405, this._contextValues)
|
|
2492
2320
|
const finalRes =
|
|
2493
2321
|
this._chainMiddlewares.length > 0
|
|
2494
2322
|
? await executeChain(this._chainMiddlewares, ctx405, make405)
|
|
2495
|
-
: await make405()
|
|
2323
|
+
: await make405()
|
|
2496
2324
|
safeFire(
|
|
2497
2325
|
() =>
|
|
2498
2326
|
this._telemetry?.onResponse?.({
|
|
@@ -2501,10 +2329,10 @@ export class Honey<
|
|
|
2501
2329
|
status: finalRes.status,
|
|
2502
2330
|
}),
|
|
2503
2331
|
fc.log,
|
|
2504
|
-
)
|
|
2505
|
-
return finalRes
|
|
2332
|
+
)
|
|
2333
|
+
return finalRes
|
|
2506
2334
|
} catch (thrown) {
|
|
2507
|
-
return this._toErrorResponse(thrown)
|
|
2335
|
+
return this._toErrorResponse(thrown)
|
|
2508
2336
|
}
|
|
2509
2337
|
}
|
|
2510
2338
|
|
|
@@ -2516,38 +2344,29 @@ export class Honey<
|
|
|
2516
2344
|
params: Record<string, string>,
|
|
2517
2345
|
stashedMeta?: Record<string, unknown> | null,
|
|
2518
2346
|
): Response | Promise<Response> {
|
|
2519
|
-
const { env, executionCtx, log, request } = fc
|
|
2347
|
+
const { env, executionCtx, log, request } = fc
|
|
2520
2348
|
|
|
2521
2349
|
/* resolve error factory — pre-computed ef preferred, else build/use global */
|
|
2522
|
-
let errors: Record<string, (...args: never[]) => unknown> | undefined
|
|
2350
|
+
let errors: Record<string, (...args: never[]) => unknown> | undefined
|
|
2523
2351
|
if (handler.ef !== null) {
|
|
2524
|
-
errors = handler.ef
|
|
2352
|
+
errors = handler.ef
|
|
2525
2353
|
} else if (this._errorFactory !== null) {
|
|
2526
2354
|
if (handler.ek.size > 0) {
|
|
2527
|
-
const factory = this._errorFactory as Record<
|
|
2528
|
-
|
|
2529
|
-
(...args: never[]) => unknown
|
|
2530
|
-
>;
|
|
2531
|
-
const subset = Object.create(null) as Record<string, unknown>;
|
|
2355
|
+
const factory = this._errorFactory as Record<string, (...args: never[]) => unknown>
|
|
2356
|
+
const subset = Object.create(null) as Record<string, unknown>
|
|
2532
2357
|
for (const key of handler.ek) {
|
|
2533
2358
|
if (key in factory) {
|
|
2534
|
-
subset[key] = factory[key]
|
|
2359
|
+
subset[key] = factory[key]
|
|
2535
2360
|
}
|
|
2536
2361
|
}
|
|
2537
|
-
errors = Object.freeze(subset) as Record<
|
|
2538
|
-
string,
|
|
2539
|
-
(...args: never[]) => unknown
|
|
2540
|
-
>;
|
|
2362
|
+
errors = Object.freeze(subset) as Record<string, (...args: never[]) => unknown>
|
|
2541
2363
|
} else {
|
|
2542
|
-
errors = this._errorFactory as Record<
|
|
2543
|
-
string,
|
|
2544
|
-
(...args: never[]) => unknown
|
|
2545
|
-
>;
|
|
2364
|
+
errors = this._errorFactory as Record<string, (...args: never[]) => unknown>
|
|
2546
2365
|
}
|
|
2547
2366
|
}
|
|
2548
2367
|
|
|
2549
2368
|
/* meta: stashed meta from fn:null tree match takes priority over handler meta */
|
|
2550
|
-
const resolvedMeta = stashedMeta ?? handler.mt
|
|
2369
|
+
const resolvedMeta = stashedMeta ?? handler.mt
|
|
2551
2370
|
|
|
2552
2371
|
const ctx = new HoneyContext({
|
|
2553
2372
|
env,
|
|
@@ -2558,21 +2377,21 @@ export class Honey<
|
|
|
2558
2377
|
req: request,
|
|
2559
2378
|
routePattern: handler.rp,
|
|
2560
2379
|
urlFn: fc.url,
|
|
2561
|
-
})
|
|
2562
|
-
if (this._contextValues) Object.assign(ctx, this._contextValues)
|
|
2380
|
+
})
|
|
2381
|
+
if (this._contextValues) Object.assign(ctx, this._contextValues)
|
|
2563
2382
|
if (this._realtimeBus) {
|
|
2564
|
-
const rtBus = this._realtimeBus
|
|
2383
|
+
const rtBus = this._realtimeBus
|
|
2565
2384
|
Object.assign(ctx, {
|
|
2566
2385
|
realtime: { publish: (topic: string, data: unknown) => rtBus.publish(topic, data) },
|
|
2567
|
-
})
|
|
2386
|
+
})
|
|
2568
2387
|
}
|
|
2569
2388
|
if (errors) {
|
|
2570
|
-
ctx._setErrors(errors)
|
|
2389
|
+
ctx._setErrors(errors)
|
|
2571
2390
|
}
|
|
2572
2391
|
|
|
2573
2392
|
if (this._telemetry !== null) {
|
|
2574
2393
|
try {
|
|
2575
|
-
this._telemetry.onRoute?.({ method, params, path, req: request })
|
|
2394
|
+
this._telemetry.onRoute?.({ method, params, path, req: request })
|
|
2576
2395
|
} catch {
|
|
2577
2396
|
/* telemetry must not crash request */
|
|
2578
2397
|
}
|
|
@@ -2585,69 +2404,65 @@ export class Honey<
|
|
|
2585
2404
|
* scoped middleware is in play (scoped mw cannot be baked into the compiled cache
|
|
2586
2405
|
* because each route may match a different subset).
|
|
2587
2406
|
*/
|
|
2588
|
-
const hasTelemetryMw = this._telemetry?.onMiddleware !== undefined
|
|
2589
|
-
const hasInputValidation = handler.iv !== null
|
|
2407
|
+
const hasTelemetryMw = this._telemetry?.onMiddleware !== undefined
|
|
2408
|
+
const hasInputValidation = handler.iv !== null
|
|
2590
2409
|
|
|
2591
2410
|
/*
|
|
2592
2411
|
* Error resolver — stored on ctx so the cached handler wrapper can read it.
|
|
2593
2412
|
* Converts handler errors into error Responses inside the middleware chain,
|
|
2594
2413
|
* allowing all post-next() middleware code (headers, logging, timing) to run.
|
|
2595
2414
|
*/
|
|
2596
|
-
ctx._errorToResponse = (thrown: unknown) =>
|
|
2597
|
-
this._resolveErrorResponse(thrown, handler, fc, method, path, ctx);
|
|
2415
|
+
ctx._errorToResponse = (thrown: unknown) => this._resolveErrorResponse(thrown, handler, fc, method, path, ctx)
|
|
2598
2416
|
|
|
2599
2417
|
const onError = (thrown: unknown): Response | Promise<Response> => {
|
|
2600
|
-
if (ctx._errorToResponse) return ctx._errorToResponse(thrown)
|
|
2601
|
-
return this._toErrorResponse(thrown)
|
|
2602
|
-
}
|
|
2418
|
+
if (ctx._errorToResponse) return ctx._errorToResponse(thrown)
|
|
2419
|
+
return this._toErrorResponse(thrown)
|
|
2420
|
+
}
|
|
2603
2421
|
|
|
2604
2422
|
const after = (response: Response): Response | Promise<Response> => {
|
|
2605
2423
|
try {
|
|
2606
|
-
const done = this._afterMatched(fc, method, path, handler, ctx, response)
|
|
2607
|
-
if (done instanceof Promise) return done.catch(onError)
|
|
2608
|
-
return done
|
|
2424
|
+
const done = this._afterMatched(fc, method, path, handler, ctx, response)
|
|
2425
|
+
if (done instanceof Promise) return done.catch(onError)
|
|
2426
|
+
return done
|
|
2609
2427
|
} catch (thrown) {
|
|
2610
|
-
return onError(thrown)
|
|
2428
|
+
return onError(thrown)
|
|
2611
2429
|
}
|
|
2612
|
-
}
|
|
2430
|
+
}
|
|
2613
2431
|
|
|
2614
2432
|
try {
|
|
2615
2433
|
if (!hasTelemetryMw && !hasInputValidation && this._scopedMiddlewares.length === 0) {
|
|
2616
2434
|
if (!handler._compiled) {
|
|
2617
|
-
const chainMw = this._chainMiddlewares
|
|
2618
|
-
const handlerHasChain =
|
|
2619
|
-
chainMw.length > 0 && chainMw.every((mw, i) => handler.mw[i] === mw);
|
|
2435
|
+
const chainMw = this._chainMiddlewares
|
|
2436
|
+
const handlerHasChain = chainMw.length > 0 && chainMw.every((mw, i) => handler.mw[i] === mw)
|
|
2620
2437
|
const allMw = handlerHasChain
|
|
2621
2438
|
? [...this._globalMiddlewares, ...handler.mw]
|
|
2622
|
-
: [...this._globalMiddlewares, ...chainMw, ...handler.mw]
|
|
2439
|
+
: [...this._globalMiddlewares, ...chainMw, ...handler.mw]
|
|
2623
2440
|
handler._compiled = compileChain(allMw, (c) => {
|
|
2624
2441
|
try {
|
|
2625
|
-
const result = handler.fn(c)
|
|
2442
|
+
const result = handler.fn(c)
|
|
2626
2443
|
if (result instanceof Promise) {
|
|
2627
2444
|
return result.catch((thrown: unknown) => {
|
|
2628
|
-
const hCtx = c as HoneyContext<TEnv
|
|
2629
|
-
if (hCtx._errorToResponse)
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
});
|
|
2445
|
+
const hCtx = c as HoneyContext<TEnv>
|
|
2446
|
+
if (hCtx._errorToResponse) return hCtx._errorToResponse(thrown)
|
|
2447
|
+
throw thrown
|
|
2448
|
+
})
|
|
2633
2449
|
}
|
|
2634
|
-
return result
|
|
2450
|
+
return result
|
|
2635
2451
|
} catch (thrown) {
|
|
2636
|
-
const hCtx = c as HoneyContext<TEnv
|
|
2637
|
-
if (hCtx._errorToResponse) return hCtx._errorToResponse(thrown)
|
|
2638
|
-
throw thrown
|
|
2452
|
+
const hCtx = c as HoneyContext<TEnv>
|
|
2453
|
+
if (hCtx._errorToResponse) return hCtx._errorToResponse(thrown)
|
|
2454
|
+
throw thrown
|
|
2639
2455
|
}
|
|
2640
|
-
})
|
|
2456
|
+
})
|
|
2641
2457
|
}
|
|
2642
|
-
const result = handler._compiled(ctx)
|
|
2643
|
-
if (result instanceof Promise) return result.then(after, onError)
|
|
2644
|
-
return after(result)
|
|
2458
|
+
const result = handler._compiled(ctx)
|
|
2459
|
+
if (result instanceof Promise) return result.then(after, onError)
|
|
2460
|
+
return after(result)
|
|
2645
2461
|
}
|
|
2646
2462
|
|
|
2647
|
-
const scopedForPath = this._filterScopedForPath(handler.rp)
|
|
2648
|
-
const chainMw = this._chainMiddlewares
|
|
2649
|
-
const handlerHasChain =
|
|
2650
|
-
chainMw.length > 0 && chainMw.every((mw, i) => handler.mw[i] === mw);
|
|
2463
|
+
const scopedForPath = this._filterScopedForPath(handler.rp)
|
|
2464
|
+
const chainMw = this._chainMiddlewares
|
|
2465
|
+
const handlerHasChain = chainMw.length > 0 && chainMw.every((mw, i) => handler.mw[i] === mw)
|
|
2651
2466
|
/*
|
|
2652
2467
|
* Ordering: [global → chain → scoped → handler-route-specific]
|
|
2653
2468
|
* When handlerHasChain, handler.mw = [chain..., routeSpecific...].
|
|
@@ -2660,22 +2475,19 @@ export class Honey<
|
|
|
2660
2475
|
...scopedForPath,
|
|
2661
2476
|
...handler.mw.slice(chainMw.length),
|
|
2662
2477
|
]
|
|
2663
|
-
: [...this._globalMiddlewares, ...chainMw, ...scopedForPath, ...handler.mw]
|
|
2478
|
+
: [...this._globalMiddlewares, ...chainMw, ...scopedForPath, ...handler.mw]
|
|
2664
2479
|
|
|
2665
2480
|
if (hasTelemetryMw) {
|
|
2666
|
-
const onMw = this._telemetry?.onMiddleware
|
|
2481
|
+
const onMw = this._telemetry?.onMiddleware
|
|
2667
2482
|
if (onMw) {
|
|
2668
2483
|
allMiddlewares = allMiddlewares.map((mw) => {
|
|
2669
|
-
const name = mw.name || "anonymous"
|
|
2484
|
+
const name = mw.name || "anonymous"
|
|
2670
2485
|
const wrapped: RuntimeMiddleware = async (wCtx, wNext) => {
|
|
2671
|
-
const mwStart = performance.now()
|
|
2486
|
+
const mwStart = performance.now()
|
|
2672
2487
|
try {
|
|
2673
|
-
const res = await mw(wCtx, wNext)
|
|
2674
|
-
safeFire(
|
|
2675
|
-
|
|
2676
|
-
log,
|
|
2677
|
-
);
|
|
2678
|
-
return res;
|
|
2488
|
+
const res = await mw(wCtx, wNext)
|
|
2489
|
+
safeFire(() => onMw({ duration: performance.now() - mwStart, name }), log)
|
|
2490
|
+
return res
|
|
2679
2491
|
} catch (error) {
|
|
2680
2492
|
safeFire(
|
|
2681
2493
|
() =>
|
|
@@ -2685,50 +2497,46 @@ export class Honey<
|
|
|
2685
2497
|
name,
|
|
2686
2498
|
}),
|
|
2687
2499
|
log,
|
|
2688
|
-
)
|
|
2689
|
-
throw error
|
|
2500
|
+
)
|
|
2501
|
+
throw error
|
|
2690
2502
|
}
|
|
2691
|
-
}
|
|
2692
|
-
return wrapped
|
|
2693
|
-
})
|
|
2503
|
+
}
|
|
2504
|
+
return wrapped
|
|
2505
|
+
})
|
|
2694
2506
|
}
|
|
2695
2507
|
}
|
|
2696
2508
|
|
|
2697
2509
|
if (hasInputValidation) {
|
|
2698
|
-
const schemas = handler.iv
|
|
2510
|
+
const schemas = handler.iv
|
|
2699
2511
|
if (schemas) {
|
|
2700
2512
|
const inputMw: RuntimeMiddleware = async (inputCtx, inputNext) => {
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
);
|
|
2706
|
-
return inputNext({ input: validated });
|
|
2707
|
-
};
|
|
2708
|
-
allMiddlewares.push(inputMw);
|
|
2513
|
+
const validated = await validateInput(schemas, inputCtx["req"] as Request, params)
|
|
2514
|
+
return inputNext({ input: validated })
|
|
2515
|
+
}
|
|
2516
|
+
allMiddlewares.push(inputMw)
|
|
2709
2517
|
}
|
|
2710
2518
|
}
|
|
2711
2519
|
|
|
2712
2520
|
return executeChain(allMiddlewares, ctx, (finalCtx) => {
|
|
2713
2521
|
try {
|
|
2714
|
-
const result = handler.fn(finalCtx)
|
|
2522
|
+
const result = handler.fn(finalCtx)
|
|
2715
2523
|
if (result instanceof Promise) {
|
|
2716
2524
|
return result.catch((thrown: unknown) => {
|
|
2717
|
-
const hCtx = finalCtx as HoneyContext<TEnv
|
|
2718
|
-
if (hCtx._errorToResponse) return hCtx._errorToResponse(thrown)
|
|
2719
|
-
throw thrown
|
|
2720
|
-
})
|
|
2525
|
+
const hCtx = finalCtx as HoneyContext<TEnv>
|
|
2526
|
+
if (hCtx._errorToResponse) return hCtx._errorToResponse(thrown)
|
|
2527
|
+
throw thrown
|
|
2528
|
+
})
|
|
2721
2529
|
}
|
|
2722
|
-
return result
|
|
2530
|
+
return result
|
|
2723
2531
|
} catch (thrown) {
|
|
2724
|
-
const hCtx = finalCtx as HoneyContext<TEnv
|
|
2725
|
-
if (hCtx._errorToResponse) return hCtx._errorToResponse(thrown)
|
|
2726
|
-
throw thrown
|
|
2532
|
+
const hCtx = finalCtx as HoneyContext<TEnv>
|
|
2533
|
+
if (hCtx._errorToResponse) return hCtx._errorToResponse(thrown)
|
|
2534
|
+
throw thrown
|
|
2727
2535
|
}
|
|
2728
|
-
}).then(after, onError)
|
|
2536
|
+
}).then(after, onError)
|
|
2729
2537
|
} catch (thrown) {
|
|
2730
2538
|
/* safety net — middleware-level errors (input validation, middleware crash) */
|
|
2731
|
-
return onError(thrown)
|
|
2539
|
+
return onError(thrown)
|
|
2732
2540
|
}
|
|
2733
2541
|
}
|
|
2734
2542
|
|
|
@@ -2743,17 +2551,11 @@ export class Honey<
|
|
|
2743
2551
|
const validateOut =
|
|
2744
2552
|
this._outputValidation === "always" ||
|
|
2745
2553
|
(this._outputValidation === "dev" &&
|
|
2746
|
-
(globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
handler.os &&
|
|
2750
|
-
validateOut &&
|
|
2751
|
-
response.body !== null &&
|
|
2752
|
-
!ctx._isErrorResponse
|
|
2753
|
-
) {
|
|
2754
|
-
return this._validateThenFinish(fc, method, path, handler, ctx, response);
|
|
2554
|
+
(globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env?.NODE_ENV !== "production")
|
|
2555
|
+
if (handler.os && validateOut && response.body !== null && !ctx._isErrorResponse) {
|
|
2556
|
+
return this._validateThenFinish(fc, method, path, handler, ctx, response)
|
|
2755
2557
|
}
|
|
2756
|
-
return this._finishMatched(fc, method, path, handler, ctx, response)
|
|
2558
|
+
return this._finishMatched(fc, method, path, handler, ctx, response)
|
|
2757
2559
|
}
|
|
2758
2560
|
|
|
2759
2561
|
private async _validateThenFinish(
|
|
@@ -2764,31 +2566,31 @@ export class Honey<
|
|
|
2764
2566
|
ctx: HoneyContext<TEnv>,
|
|
2765
2567
|
response: Response,
|
|
2766
2568
|
): Promise<Response> {
|
|
2767
|
-
const ct = response.headers.get("content-type")
|
|
2569
|
+
const ct = response.headers.get("content-type")
|
|
2768
2570
|
|
|
2769
2571
|
/* content-type mismatch check */
|
|
2770
2572
|
if (ct && handler.os) {
|
|
2771
|
-
const declaredTypes = Object.keys(handler.os)
|
|
2772
|
-
const matches = declaredTypes.some((t) => ct.startsWith(t))
|
|
2573
|
+
const declaredTypes = Object.keys(handler.os)
|
|
2574
|
+
const matches = declaredTypes.some((t) => ct.startsWith(t))
|
|
2773
2575
|
if (!matches) {
|
|
2774
2576
|
throw new HoneyError({
|
|
2775
2577
|
errorKey: EK.output_content_type_mismatch,
|
|
2776
2578
|
status: SK.internal_server_error,
|
|
2777
|
-
})
|
|
2579
|
+
})
|
|
2778
2580
|
}
|
|
2779
2581
|
}
|
|
2780
2582
|
|
|
2781
2583
|
/* JSON schema validation — read original, return clone (Bun clone() drains original) */
|
|
2782
2584
|
if (ct?.startsWith("application/json") && handler.ov) {
|
|
2783
|
-
const sk = codeToStatusKey[response.status]
|
|
2585
|
+
const sk = codeToStatusKey[response.status]
|
|
2784
2586
|
if (sk) {
|
|
2785
|
-
const forReturn = response.clone()
|
|
2786
|
-
const data: unknown = await response.json()
|
|
2787
|
-
await handler.ov(sk, data)
|
|
2788
|
-
response = forReturn
|
|
2587
|
+
const forReturn = response.clone()
|
|
2588
|
+
const data: unknown = await response.json()
|
|
2589
|
+
await handler.ov(sk, data)
|
|
2590
|
+
response = forReturn
|
|
2789
2591
|
}
|
|
2790
2592
|
}
|
|
2791
|
-
return this._finishMatched(fc, method, path, handler, ctx, response)
|
|
2593
|
+
return this._finishMatched(fc, method, path, handler, ctx, response)
|
|
2792
2594
|
}
|
|
2793
2595
|
|
|
2794
2596
|
private _finishMatched(
|
|
@@ -2801,19 +2603,19 @@ export class Honey<
|
|
|
2801
2603
|
): Response {
|
|
2802
2604
|
/* taps — fire after successful handler, non-blocking */
|
|
2803
2605
|
if (this._taps !== null && !ctx._isErrorResponse) {
|
|
2804
|
-
const taps = this._taps
|
|
2805
|
-
const log = fc.log
|
|
2606
|
+
const taps = this._taps
|
|
2607
|
+
const log = fc.log
|
|
2806
2608
|
|
|
2807
2609
|
/* meta-driven taps — fire for each registered key found in route meta */
|
|
2808
2610
|
if (handler.mt !== null) {
|
|
2809
2611
|
for (const [key, tapFn] of taps) {
|
|
2810
|
-
const metaValue = handler.mt[key]
|
|
2612
|
+
const metaValue = handler.mt[key]
|
|
2811
2613
|
if (metaValue !== undefined) {
|
|
2812
2614
|
ctx.background(
|
|
2813
2615
|
Promise.resolve()
|
|
2814
2616
|
.then(() => tapFn(ctx, metaValue))
|
|
2815
2617
|
.catch((e) => log?.warn?.("tap failed", key, e)),
|
|
2816
|
-
)
|
|
2618
|
+
)
|
|
2817
2619
|
}
|
|
2818
2620
|
}
|
|
2819
2621
|
}
|
|
@@ -2821,33 +2623,33 @@ export class Honey<
|
|
|
2821
2623
|
/* dynamic taps — fire for each c.tap() call */
|
|
2822
2624
|
if (ctx._pendingTaps !== null) {
|
|
2823
2625
|
for (const pending of ctx._pendingTaps) {
|
|
2824
|
-
const tapFn = taps.get(pending.key)
|
|
2626
|
+
const tapFn = taps.get(pending.key)
|
|
2825
2627
|
if (tapFn !== undefined) {
|
|
2826
2628
|
ctx.background(
|
|
2827
2629
|
Promise.resolve()
|
|
2828
2630
|
.then(() => tapFn(ctx, pending.payload))
|
|
2829
2631
|
.catch((e) => log?.warn?.("tap failed", pending.key, e)),
|
|
2830
|
-
)
|
|
2632
|
+
)
|
|
2831
2633
|
}
|
|
2832
2634
|
}
|
|
2833
|
-
ctx._pendingTaps = null
|
|
2635
|
+
ctx._pendingTaps = null
|
|
2834
2636
|
}
|
|
2835
2637
|
}
|
|
2836
2638
|
|
|
2837
2639
|
if (this._telemetry !== null) {
|
|
2838
2640
|
try {
|
|
2839
|
-
const duration = performance.now() - fc.startTime
|
|
2641
|
+
const duration = performance.now() - fc.startTime
|
|
2840
2642
|
this._telemetry.onHandler?.({
|
|
2841
2643
|
duration,
|
|
2842
2644
|
method,
|
|
2843
2645
|
path,
|
|
2844
2646
|
status: response.status,
|
|
2845
|
-
})
|
|
2647
|
+
})
|
|
2846
2648
|
this._telemetry.onResponse?.({
|
|
2847
2649
|
duration,
|
|
2848
2650
|
req: fc.request,
|
|
2849
2651
|
status: response.status,
|
|
2850
|
-
})
|
|
2652
|
+
})
|
|
2851
2653
|
} catch {
|
|
2852
2654
|
/* telemetry must never crash the response path */
|
|
2853
2655
|
}
|
|
@@ -2857,9 +2659,9 @@ export class Honey<
|
|
|
2857
2659
|
return new Response(null, {
|
|
2858
2660
|
headers: response.headers,
|
|
2859
2661
|
status: response.status,
|
|
2860
|
-
})
|
|
2662
|
+
})
|
|
2861
2663
|
}
|
|
2862
|
-
return response
|
|
2664
|
+
return response
|
|
2863
2665
|
}
|
|
2864
2666
|
}
|
|
2865
2667
|
|
|
@@ -2875,19 +2677,9 @@ export class Honey<
|
|
|
2875
2677
|
* Narrowed methods return TypedResponse<CT, K> for compile-time CT+SK safety.
|
|
2876
2678
|
*/
|
|
2877
2679
|
/** Resolve body type: schema → InferOutput, plain type → use as-is */
|
|
2878
|
-
type ResolveBody<T, TBody> = TBody extends "infer"
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
: T
|
|
2882
|
-
: TBody;
|
|
2883
|
-
|
|
2884
|
-
type NarrowMethod<
|
|
2885
|
-
TRes,
|
|
2886
|
-
TSchemas,
|
|
2887
|
-
Method extends string,
|
|
2888
|
-
CT extends string,
|
|
2889
|
-
TBody,
|
|
2890
|
-
> = [TSchemas] extends [never]
|
|
2680
|
+
type ResolveBody<T, TBody> = TBody extends "infer" ? (T extends StandardSchemaLike ? InferOutput<T> : T) : TBody
|
|
2681
|
+
|
|
2682
|
+
type NarrowMethod<TRes, TSchemas, Method extends string, CT extends string, TBody> = [TSchemas] extends [never]
|
|
2891
2683
|
? Omit<TRes, Method>
|
|
2892
2684
|
: TSchemas extends Record<string, unknown>
|
|
2893
2685
|
? Omit<TRes, Method> & {
|
|
@@ -2895,22 +2687,18 @@ type NarrowMethod<
|
|
|
2895
2687
|
statusKey: K,
|
|
2896
2688
|
body: ResolveBody<TSchemas[K], TBody>,
|
|
2897
2689
|
opts?: ResponseOptions,
|
|
2898
|
-
) => TypedResponse<CT, K
|
|
2690
|
+
) => TypedResponse<CT, K>
|
|
2899
2691
|
}
|
|
2900
|
-
: Omit<TRes, Method
|
|
2692
|
+
: Omit<TRes, Method>
|
|
2901
2693
|
|
|
2902
2694
|
/**
|
|
2903
2695
|
* SSE uses a callback signature, not statusKey+body like other methods.
|
|
2904
2696
|
* Gate presence on text/event-stream declaration, preserve original signature.
|
|
2905
2697
|
*/
|
|
2906
|
-
type NarrowSSE<TOutput> = [
|
|
2907
|
-
ExtractSchemas<TOutput, "text/event-stream">,
|
|
2908
|
-
] extends [never]
|
|
2909
|
-
? {}
|
|
2910
|
-
: { sse: HoneyRes["sse"] };
|
|
2698
|
+
type NarrowSSE<TOutput> = [ExtractSchemas<TOutput, "text/event-stream">] extends [never] ? {} : { sse: HoneyRes["sse"] }
|
|
2911
2699
|
|
|
2912
2700
|
/** Universal methods — always available, not gated by output declaration */
|
|
2913
|
-
type UniversalRes = Pick<HoneyRes, "noContent" | "raw" | "redirect" | "stream"
|
|
2701
|
+
type UniversalRes = Pick<HoneyRes, "noContent" | "raw" | "redirect" | "stream">
|
|
2914
2702
|
|
|
2915
2703
|
/**
|
|
2916
2704
|
* Constrain ctx.res when output schemas are declared.
|
|
@@ -2923,41 +2711,11 @@ type ApplyOutput<TCtx, TOutput> = [keyof TOutput] extends [never]
|
|
|
2923
2711
|
? TCtx
|
|
2924
2712
|
: Omit<TCtx, "res"> & {
|
|
2925
2713
|
readonly res: UniversalRes &
|
|
2926
|
-
NarrowMethod<
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
"infer"
|
|
2932
|
-
> &
|
|
2933
|
-
NarrowMethod<
|
|
2934
|
-
{},
|
|
2935
|
-
ExtractSchemas<TOutput, "text/plain">,
|
|
2936
|
-
"text",
|
|
2937
|
-
"text/plain",
|
|
2938
|
-
string
|
|
2939
|
-
> &
|
|
2940
|
-
NarrowMethod<
|
|
2941
|
-
{},
|
|
2942
|
-
ExtractSchemas<TOutput, "text/html">,
|
|
2943
|
-
"html",
|
|
2944
|
-
"text/html",
|
|
2945
|
-
string
|
|
2946
|
-
> &
|
|
2947
|
-
NarrowMethod<
|
|
2948
|
-
{},
|
|
2949
|
-
ExtractSchemas<TOutput, "application/xml">,
|
|
2950
|
-
"xml",
|
|
2951
|
-
"application/xml",
|
|
2952
|
-
string
|
|
2953
|
-
> &
|
|
2954
|
-
NarrowMethod<
|
|
2955
|
-
{},
|
|
2956
|
-
ExtractSchemas<TOutput, "text/csv">,
|
|
2957
|
-
"csv",
|
|
2958
|
-
"text/csv",
|
|
2959
|
-
string
|
|
2960
|
-
> &
|
|
2714
|
+
NarrowMethod<{}, ExtractSchemas<TOutput, "application/json">, "json", "application/json", "infer"> &
|
|
2715
|
+
NarrowMethod<{}, ExtractSchemas<TOutput, "text/plain">, "text", "text/plain", string> &
|
|
2716
|
+
NarrowMethod<{}, ExtractSchemas<TOutput, "text/html">, "html", "text/html", string> &
|
|
2717
|
+
NarrowMethod<{}, ExtractSchemas<TOutput, "application/xml">, "xml", "application/xml", string> &
|
|
2718
|
+
NarrowMethod<{}, ExtractSchemas<TOutput, "text/csv">, "csv", "text/csv", string> &
|
|
2961
2719
|
NarrowMethod<
|
|
2962
2720
|
{},
|
|
2963
2721
|
ExtractSchemas<TOutput, "application/octet-stream">,
|
|
@@ -2965,11 +2723,11 @@ type ApplyOutput<TCtx, TOutput> = [keyof TOutput] extends [never]
|
|
|
2965
2723
|
"application/octet-stream",
|
|
2966
2724
|
ArrayBuffer | Uint8Array<ArrayBuffer>
|
|
2967
2725
|
> &
|
|
2968
|
-
NarrowSSE<TOutput
|
|
2969
|
-
}
|
|
2726
|
+
NarrowSSE<TOutput>
|
|
2727
|
+
}
|
|
2970
2728
|
|
|
2971
2729
|
/** Public alias for codegen — applies output schema constraints to a context type */
|
|
2972
|
-
export type WithOutput<TCtx, TOutput> = ApplyOutput<TCtx, TOutput
|
|
2730
|
+
export type WithOutput<TCtx, TOutput> = ApplyOutput<TCtx, TOutput>
|
|
2973
2731
|
|
|
2974
2732
|
/** @internal — tuple entry describing a scoped middleware at the type level */
|
|
2975
2733
|
type ScopedMwEntry = { readonly path: string; readonly adds: unknown }
|
|
@@ -2982,10 +2740,7 @@ type ScopedMwEntry = { readonly path: string; readonly adds: unknown }
|
|
|
2982
2740
|
* the user passes a widened variable), skip the entry via `string extends Head["path"] ? {}`.
|
|
2983
2741
|
* This prevents the always-true `'/anywhere' extends string` from polluting every route.
|
|
2984
2742
|
*/
|
|
2985
|
-
type ApplyScoped<
|
|
2986
|
-
TScopedMw extends readonly ScopedMwEntry[],
|
|
2987
|
-
TFullPath extends string,
|
|
2988
|
-
> = TScopedMw extends readonly [
|
|
2743
|
+
type ApplyScoped<TScopedMw extends readonly ScopedMwEntry[], TFullPath extends string> = TScopedMw extends readonly [
|
|
2989
2744
|
infer Head extends ScopedMwEntry,
|
|
2990
2745
|
...infer Rest extends readonly ScopedMwEntry[],
|
|
2991
2746
|
]
|
|
@@ -2996,7 +2751,7 @@ type ApplyScoped<
|
|
|
2996
2751
|
: TFullPath extends `${Head["path"] & string}/${string}`
|
|
2997
2752
|
? Head["adds"]
|
|
2998
2753
|
: {}) &
|
|
2999
|
-
|
|
2754
|
+
ApplyScoped<Rest, TFullPath>
|
|
3000
2755
|
: {}
|
|
3001
2756
|
|
|
3002
2757
|
/** @internal — runtime entry for a scoped middleware */
|
|
@@ -3013,13 +2768,12 @@ type ApplyParams<TCtx, TParams> = [keyof TParams] extends [string]
|
|
|
3013
2768
|
? string extends keyof TParams
|
|
3014
2769
|
? TCtx
|
|
3015
2770
|
: TCtx & { readonly params: TParams }
|
|
3016
|
-
: TCtx
|
|
2771
|
+
: TCtx
|
|
3017
2772
|
|
|
3018
2773
|
/** Typed tap method — constrained to registered tap keys when TTaps is non-empty */
|
|
3019
|
-
type TypedTap<TTaps extends Record<string, unknown>> =
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
: { tap<K extends string & keyof TTaps>(key: K, payload: TTaps[K]): void };
|
|
2774
|
+
type TypedTap<TTaps extends Record<string, unknown>> = [keyof TTaps] extends [never]
|
|
2775
|
+
? { tap(key: string, payload: unknown): void }
|
|
2776
|
+
: { tap<K extends string & keyof TTaps>(key: K, payload: TTaps[K]): void }
|
|
3023
2777
|
|
|
3024
2778
|
/** Build the full handler context: base ctx + params + input + meta + errors + taps + output-constrained methods */
|
|
3025
2779
|
type HandlerCtx<
|
|
@@ -3033,48 +2787,56 @@ type HandlerCtx<
|
|
|
3033
2787
|
TPath extends string = string,
|
|
3034
2788
|
TTaps extends Record<string, unknown> = {},
|
|
3035
2789
|
> = ApplyOutput<
|
|
3036
|
-
ApplyParams<
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
> & {
|
|
3040
|
-
readonly meta: Readonly<Omit<TAccMeta, "openApi">>;
|
|
3041
|
-
readonly routePattern: TPath;
|
|
2790
|
+
ApplyParams<[keyof TInput] extends [never] ? TCtx : TCtx & { input: TInput }, TParams> & {
|
|
2791
|
+
readonly meta: Readonly<Omit<TAccMeta, "openApi">>
|
|
2792
|
+
readonly routePattern: TPath
|
|
3042
2793
|
} & ([TErrorFactory] extends [never]
|
|
3043
2794
|
? {}
|
|
3044
2795
|
: [TErrorKeys] extends [never]
|
|
3045
2796
|
? { readonly errors: TErrorFactory }
|
|
3046
2797
|
: {
|
|
3047
|
-
readonly errors: Pick<
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
>;
|
|
3051
|
-
})
|
|
3052
|
-
& TypedTap<TTaps>,
|
|
2798
|
+
readonly errors: Pick<TErrorFactory, TErrorKeys & keyof TErrorFactory>
|
|
2799
|
+
}) &
|
|
2800
|
+
TypedTap<TTaps>,
|
|
3053
2801
|
TOutput
|
|
3054
|
-
|
|
2802
|
+
>
|
|
3055
2803
|
|
|
3056
2804
|
/** @internal — exposes private Honey members for RouteBuilder/WSRouteBuilder access */
|
|
3057
2805
|
type HoneyInternal = {
|
|
3058
|
-
_factory: unknown
|
|
3059
|
-
_markWsRoutes(): void
|
|
3060
|
-
_registerStatic(key: string, handler: RouteHandler): void
|
|
3061
|
-
}
|
|
2806
|
+
_factory: unknown
|
|
2807
|
+
_markWsRoutes(): void
|
|
2808
|
+
_registerStatic(key: string, handler: RouteHandler): void
|
|
2809
|
+
}
|
|
3062
2810
|
|
|
3063
2811
|
type RouteBuilderState<TParent> = {
|
|
3064
|
-
boundaryErrorKey: string | null
|
|
3065
|
-
errorKeys: Set<string
|
|
3066
|
-
extraMethods: (HttpMethod | "ALL")[] | null
|
|
3067
|
-
handlerMap: Record<string, RouteHandler> | null
|
|
3068
|
-
inputSchemas: InputSchemasDef | null
|
|
3069
|
-
meta: Record<string, unknown> | null
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
2812
|
+
boundaryErrorKey: string | null
|
|
2813
|
+
errorKeys: Set<string>
|
|
2814
|
+
extraMethods: (HttpMethod | "ALL")[] | null
|
|
2815
|
+
handlerMap: Record<string, RouteHandler> | null
|
|
2816
|
+
inputSchemas: InputSchemasDef | null
|
|
2817
|
+
meta: Record<string, unknown> | null
|
|
2818
|
+
/** meta contributed by middleware — kept apart so explicit .meta() always outranks it */
|
|
2819
|
+
mwMeta: Record<string, unknown> | null
|
|
2820
|
+
method: HttpMethod | "ALL"
|
|
2821
|
+
middlewares: RuntimeMiddleware[]
|
|
2822
|
+
outputSchemas: OutputSchemaDef | null
|
|
2823
|
+
parent: TParent
|
|
2824
|
+
parentMiddlewares: RuntimeMiddleware[]
|
|
2825
|
+
path: string
|
|
2826
|
+
root: TreeNode
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2829
|
+
/**
|
|
2830
|
+
* Route meta = middleware-contributed meta, overlaid by everything stated explicitly
|
|
2831
|
+
* (chain `.meta()`, then route `.meta()`). Explicit always wins.
|
|
2832
|
+
*/
|
|
2833
|
+
function mergeContributedMeta(
|
|
2834
|
+
contributed: Record<string, unknown> | null | undefined,
|
|
2835
|
+
explicit: Record<string, unknown> | null,
|
|
2836
|
+
): Record<string, unknown> | null {
|
|
2837
|
+
if (!contributed) return explicit ? Object.freeze(explicit) : null
|
|
2838
|
+
return Object.freeze(explicit ? { ...contributed, ...explicit } : { ...contributed })
|
|
2839
|
+
}
|
|
3078
2840
|
|
|
3079
2841
|
/** Honey.TMeta defaults to never; never & X is never, so start acc meta at {}. */
|
|
3080
2842
|
type InitAccMeta<TMeta> = [TMeta] extends [never] ? {} : TMeta
|
|
@@ -3136,9 +2898,9 @@ type HandlerReturn<
|
|
|
3136
2898
|
TBasePath,
|
|
3137
2899
|
TTaps,
|
|
3138
2900
|
TScopedMw
|
|
3139
|
-
|
|
2901
|
+
>
|
|
3140
2902
|
|
|
3141
|
-
type OneShotKey = "boundary" | "errors" | "input" | "meta" | "output"
|
|
2903
|
+
type OneShotKey = "boundary" | "errors" | "input" | "meta" | "output"
|
|
3142
2904
|
|
|
3143
2905
|
type BuilderChain<
|
|
3144
2906
|
TEnv,
|
|
@@ -3160,12 +2922,26 @@ type BuilderChain<
|
|
|
3160
2922
|
TScopedMw extends readonly ScopedMwEntry[] = [],
|
|
3161
2923
|
> = Omit<
|
|
3162
2924
|
RouteBuilder<
|
|
3163
|
-
TEnv,
|
|
3164
|
-
|
|
3165
|
-
|
|
2925
|
+
TEnv,
|
|
2926
|
+
TCtx,
|
|
2927
|
+
TInput,
|
|
2928
|
+
TErrorKeys,
|
|
2929
|
+
TOutput,
|
|
2930
|
+
TPath,
|
|
2931
|
+
TMethod,
|
|
2932
|
+
TRoutes,
|
|
2933
|
+
TUsed,
|
|
2934
|
+
TBaseCtx,
|
|
2935
|
+
TMeta,
|
|
2936
|
+
TAccMeta,
|
|
2937
|
+
TErrorFactory,
|
|
2938
|
+
TDefaultErrors,
|
|
2939
|
+
TBasePath,
|
|
2940
|
+
TTaps,
|
|
2941
|
+
TScopedMw
|
|
3166
2942
|
>,
|
|
3167
2943
|
TUsed & OneShotKey
|
|
3168
|
-
|
|
2944
|
+
>
|
|
3169
2945
|
|
|
3170
2946
|
class RouteBuilder<
|
|
3171
2947
|
TEnv,
|
|
@@ -3187,35 +2963,15 @@ class RouteBuilder<
|
|
|
3187
2963
|
TScopedMw extends readonly ScopedMwEntry[] = [],
|
|
3188
2964
|
> {
|
|
3189
2965
|
private _s: RouteBuilderState<
|
|
3190
|
-
Honey<
|
|
3191
|
-
|
|
3192
|
-
TBaseCtx,
|
|
3193
|
-
TRoutes,
|
|
3194
|
-
TMeta,
|
|
3195
|
-
TErrorFactory,
|
|
3196
|
-
TDefaultErrors,
|
|
3197
|
-
TBasePath,
|
|
3198
|
-
TTaps,
|
|
3199
|
-
TScopedMw
|
|
3200
|
-
>
|
|
3201
|
-
>;
|
|
2966
|
+
Honey<TEnv, TBaseCtx, TRoutes, TMeta, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw>
|
|
2967
|
+
>
|
|
3202
2968
|
|
|
3203
2969
|
constructor(
|
|
3204
2970
|
state: RouteBuilderState<
|
|
3205
|
-
Honey<
|
|
3206
|
-
TEnv,
|
|
3207
|
-
TBaseCtx,
|
|
3208
|
-
TRoutes,
|
|
3209
|
-
TMeta,
|
|
3210
|
-
TErrorFactory,
|
|
3211
|
-
TDefaultErrors,
|
|
3212
|
-
TBasePath,
|
|
3213
|
-
TTaps,
|
|
3214
|
-
TScopedMw
|
|
3215
|
-
>
|
|
2971
|
+
Honey<TEnv, TBaseCtx, TRoutes, TMeta, TErrorFactory, TDefaultErrors, TBasePath, TTaps, TScopedMw>
|
|
3216
2972
|
>,
|
|
3217
2973
|
) {
|
|
3218
|
-
this._s = state
|
|
2974
|
+
this._s = state
|
|
3219
2975
|
}
|
|
3220
2976
|
|
|
3221
2977
|
errors<
|
|
@@ -3242,11 +2998,9 @@ class RouteBuilder<
|
|
|
3242
2998
|
TBasePath,
|
|
3243
2999
|
TTaps,
|
|
3244
3000
|
TScopedMw
|
|
3245
|
-
|
|
3001
|
+
>
|
|
3246
3002
|
errors<
|
|
3247
|
-
TKeys extends [TErrorFactory] extends [never]
|
|
3248
|
-
? never
|
|
3249
|
-
: Exclude<keyof TErrorFactory & string, TDefaultErrors>,
|
|
3003
|
+
TKeys extends ([TErrorFactory] extends [never] ? never : Exclude<keyof TErrorFactory & string, TDefaultErrors>),
|
|
3250
3004
|
>(
|
|
3251
3005
|
...keys: TKeys[]
|
|
3252
3006
|
): BuilderChain<
|
|
@@ -3267,7 +3021,7 @@ class RouteBuilder<
|
|
|
3267
3021
|
TBasePath,
|
|
3268
3022
|
TTaps,
|
|
3269
3023
|
TScopedMw
|
|
3270
|
-
|
|
3024
|
+
>
|
|
3271
3025
|
errors(
|
|
3272
3026
|
...args: unknown[]
|
|
3273
3027
|
): BuilderChain<
|
|
@@ -3289,12 +3043,9 @@ class RouteBuilder<
|
|
|
3289
3043
|
TTaps,
|
|
3290
3044
|
TScopedMw
|
|
3291
3045
|
> {
|
|
3292
|
-
const keys =
|
|
3293
|
-
typeof args[0] === "object" && args[0] !== null
|
|
3294
|
-
? (args.slice(1) as string[])
|
|
3295
|
-
: (args as string[]);
|
|
3046
|
+
const keys = typeof args[0] === "object" && args[0] !== null ? (args.slice(1) as string[]) : (args as string[])
|
|
3296
3047
|
for (const k of keys) {
|
|
3297
|
-
this._s.errorKeys.add(k)
|
|
3048
|
+
this._s.errorKeys.add(k)
|
|
3298
3049
|
}
|
|
3299
3050
|
return new RouteBuilder<
|
|
3300
3051
|
TEnv,
|
|
@@ -3317,13 +3068,11 @@ class RouteBuilder<
|
|
|
3317
3068
|
>({
|
|
3318
3069
|
...this._s,
|
|
3319
3070
|
errorKeys: this._s.errorKeys,
|
|
3320
|
-
})
|
|
3071
|
+
})
|
|
3321
3072
|
}
|
|
3322
3073
|
|
|
3323
3074
|
boundary<
|
|
3324
|
-
TKey extends [TErrorFactory] extends [never]
|
|
3325
|
-
? never
|
|
3326
|
-
: Exclude<keyof TErrorFactory & string, TDefaultErrors>,
|
|
3075
|
+
TKey extends ([TErrorFactory] extends [never] ? never : Exclude<keyof TErrorFactory & string, TDefaultErrors>),
|
|
3327
3076
|
>(
|
|
3328
3077
|
key: TKey,
|
|
3329
3078
|
): BuilderChain<
|
|
@@ -3345,8 +3094,8 @@ class RouteBuilder<
|
|
|
3345
3094
|
TTaps,
|
|
3346
3095
|
TScopedMw
|
|
3347
3096
|
> {
|
|
3348
|
-
this._s.boundaryErrorKey = key
|
|
3349
|
-
this._s.errorKeys.add(key)
|
|
3097
|
+
this._s.boundaryErrorKey = key
|
|
3098
|
+
this._s.errorKeys.add(key)
|
|
3350
3099
|
return new RouteBuilder<
|
|
3351
3100
|
TEnv,
|
|
3352
3101
|
TCtx,
|
|
@@ -3369,7 +3118,7 @@ class RouteBuilder<
|
|
|
3369
3118
|
...this._s,
|
|
3370
3119
|
boundaryErrorKey: key,
|
|
3371
3120
|
errorKeys: this._s.errorKeys,
|
|
3372
|
-
})
|
|
3121
|
+
})
|
|
3373
3122
|
}
|
|
3374
3123
|
|
|
3375
3124
|
handler(
|
|
@@ -3404,17 +3153,17 @@ class RouteBuilder<
|
|
|
3404
3153
|
TTaps,
|
|
3405
3154
|
TScopedMw
|
|
3406
3155
|
> {
|
|
3407
|
-
let ov: OutputValidator | null = null
|
|
3408
|
-
const outputSchemas = this._s.outputSchemas
|
|
3156
|
+
let ov: OutputValidator | null = null
|
|
3157
|
+
const outputSchemas = this._s.outputSchemas
|
|
3409
3158
|
if (outputSchemas) {
|
|
3410
|
-
const jsonSchemas = outputSchemas["application/json"]
|
|
3159
|
+
const jsonSchemas = outputSchemas["application/json"]
|
|
3411
3160
|
if (jsonSchemas) {
|
|
3412
3161
|
ov = async (statusKey: string, data: unknown) => {
|
|
3413
|
-
const schema = jsonSchemas[statusKey as keyof typeof jsonSchemas]
|
|
3162
|
+
const schema = jsonSchemas[statusKey as keyof typeof jsonSchemas]
|
|
3414
3163
|
if (schema) {
|
|
3415
|
-
await validateOutput(schema, statusKey, data)
|
|
3164
|
+
await validateOutput(schema, statusKey, data)
|
|
3416
3165
|
}
|
|
3417
|
-
}
|
|
3166
|
+
}
|
|
3418
3167
|
}
|
|
3419
3168
|
}
|
|
3420
3169
|
|
|
@@ -3430,39 +3179,36 @@ class RouteBuilder<
|
|
|
3430
3179
|
ek: this._s.errorKeys,
|
|
3431
3180
|
fn: fn as (ctx: unknown) => Response | Promise<Response>,
|
|
3432
3181
|
iv: this._s.inputSchemas,
|
|
3433
|
-
mt: this._s.
|
|
3182
|
+
mt: mergeContributedMeta(this._s.mwMeta, this._s.meta),
|
|
3434
3183
|
mw: [...this._s.parentMiddlewares, ...this._s.middlewares],
|
|
3435
3184
|
os: this._s.outputSchemas,
|
|
3436
3185
|
ov,
|
|
3437
3186
|
rp: this._s.path,
|
|
3438
|
-
}
|
|
3187
|
+
}
|
|
3439
3188
|
|
|
3440
3189
|
/* patch mode: if handler map has this route, patch existing handler instead of insertRoute */
|
|
3441
|
-
const handlerMap = this._s.handlerMap
|
|
3190
|
+
const handlerMap = this._s.handlerMap
|
|
3442
3191
|
if (handlerMap) {
|
|
3443
|
-
const key = `${this._s.method} ${this._s.path}
|
|
3444
|
-
const existing = handlerMap[key]
|
|
3192
|
+
const key = `${this._s.method} ${this._s.path}`
|
|
3193
|
+
const existing = handlerMap[key]
|
|
3445
3194
|
if (existing) {
|
|
3446
|
-
existing.fn = routeHandler.fn
|
|
3447
|
-
existing.mw = routeHandler.mw
|
|
3448
|
-
existing.iv = routeHandler.iv
|
|
3449
|
-
existing.os = routeHandler.os
|
|
3450
|
-
existing.ov = routeHandler.ov
|
|
3195
|
+
existing.fn = routeHandler.fn
|
|
3196
|
+
existing.mw = routeHandler.mw
|
|
3197
|
+
existing.iv = routeHandler.iv
|
|
3198
|
+
existing.os = routeHandler.os
|
|
3199
|
+
existing.ov = routeHandler.ov
|
|
3451
3200
|
/* pre-compute filtered error factory */
|
|
3452
|
-
const factory = (this._s.parent as unknown as HoneyInternal)._factory
|
|
3201
|
+
const factory = (this._s.parent as unknown as HoneyInternal)._factory
|
|
3453
3202
|
if (factory !== null && existing.ek.size > 0) {
|
|
3454
|
-
const ef = Object.create(null) as Record<
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
const factoryFn = fac[k]
|
|
3461
|
-
if (factoryFn !== undefined) {
|
|
3462
|
-
ef[k] = factoryFn
|
|
3463
|
-
}
|
|
3203
|
+
const ef = Object.create(null) as Record<string, (...args: never[]) => unknown>
|
|
3204
|
+
const fac = factory as Record<string, (...args: never[]) => unknown>
|
|
3205
|
+
for (const k of existing.ek) {
|
|
3206
|
+
const factoryFn = fac[k]
|
|
3207
|
+
if (factoryFn !== undefined) {
|
|
3208
|
+
ef[k] = factoryFn
|
|
3464
3209
|
}
|
|
3465
|
-
|
|
3210
|
+
}
|
|
3211
|
+
existing.ef = Object.freeze(ef)
|
|
3466
3212
|
}
|
|
3467
3213
|
return this._s.parent as HandlerReturn<
|
|
3468
3214
|
TEnv,
|
|
@@ -3481,28 +3227,25 @@ class RouteBuilder<
|
|
|
3481
3227
|
TBasePath,
|
|
3482
3228
|
TTaps,
|
|
3483
3229
|
TScopedMw
|
|
3484
|
-
|
|
3230
|
+
>
|
|
3485
3231
|
}
|
|
3486
3232
|
}
|
|
3487
3233
|
|
|
3488
|
-
insertRoute(this._s.root, this._s.method, this._s.path, routeHandler)
|
|
3234
|
+
insertRoute(this._s.root, this._s.method, this._s.path, routeHandler)
|
|
3489
3235
|
|
|
3490
3236
|
/* .on() extra methods — insert same handler for each additional method */
|
|
3491
3237
|
if (this._s.extraMethods) {
|
|
3492
3238
|
for (const m of this._s.extraMethods) {
|
|
3493
|
-
insertRoute(this._s.root, m, this._s.path, routeHandler)
|
|
3239
|
+
insertRoute(this._s.root, m, this._s.path, routeHandler)
|
|
3494
3240
|
if (!this._s.path.includes(":") && !this._s.path.includes("*")) {
|
|
3495
|
-
(this._s.parent as unknown as HoneyInternal)._registerStatic(`${m} ${this._s.path}`, routeHandler)
|
|
3241
|
+
;(this._s.parent as unknown as HoneyInternal)._registerStatic(`${m} ${this._s.path}`, routeHandler)
|
|
3496
3242
|
}
|
|
3497
3243
|
}
|
|
3498
3244
|
}
|
|
3499
3245
|
|
|
3500
3246
|
/* populate static route map for O(1) lookup on non-parameterized routes */
|
|
3501
3247
|
if (!this._s.path.includes(":") && !this._s.path.includes("*")) {
|
|
3502
|
-
(this._s.parent as unknown as HoneyInternal)._registerStatic(
|
|
3503
|
-
`${this._s.method} ${this._s.path}`,
|
|
3504
|
-
routeHandler,
|
|
3505
|
-
);
|
|
3248
|
+
;(this._s.parent as unknown as HoneyInternal)._registerStatic(`${this._s.method} ${this._s.path}`, routeHandler)
|
|
3506
3249
|
}
|
|
3507
3250
|
return this._s.parent as HandlerReturn<
|
|
3508
3251
|
TEnv,
|
|
@@ -3521,7 +3264,7 @@ class RouteBuilder<
|
|
|
3521
3264
|
TBasePath,
|
|
3522
3265
|
TTaps,
|
|
3523
3266
|
TScopedMw
|
|
3524
|
-
|
|
3267
|
+
>
|
|
3525
3268
|
}
|
|
3526
3269
|
|
|
3527
3270
|
proxy(
|
|
@@ -3556,7 +3299,7 @@ class RouteBuilder<
|
|
|
3556
3299
|
TTaps,
|
|
3557
3300
|
TScopedMw
|
|
3558
3301
|
> {
|
|
3559
|
-
const proxyHandler = createProxyHandler(config)
|
|
3302
|
+
const proxyHandler = createProxyHandler(config)
|
|
3560
3303
|
return this.handler(
|
|
3561
3304
|
proxyHandler as (
|
|
3562
3305
|
ctx: HandlerCtx<
|
|
@@ -3571,7 +3314,7 @@ class RouteBuilder<
|
|
|
3571
3314
|
TTaps
|
|
3572
3315
|
>,
|
|
3573
3316
|
) => Promise<TypedResponse>,
|
|
3574
|
-
)
|
|
3317
|
+
)
|
|
3575
3318
|
}
|
|
3576
3319
|
|
|
3577
3320
|
input<TSchemas extends InputSchemasDef>(
|
|
@@ -3616,13 +3359,10 @@ class RouteBuilder<
|
|
|
3616
3359
|
>({
|
|
3617
3360
|
...this._s,
|
|
3618
3361
|
inputSchemas: schemas,
|
|
3619
|
-
})
|
|
3362
|
+
})
|
|
3620
3363
|
}
|
|
3621
3364
|
|
|
3622
|
-
meta<
|
|
3623
|
-
TRouteMeta extends Partial<DefaultMeta> &
|
|
3624
|
-
([TMeta] extends [never] ? {} : TMeta),
|
|
3625
|
-
>(
|
|
3365
|
+
meta<TRouteMeta extends Partial<DefaultMeta> & ([TMeta] extends [never] ? {} : TMeta)>(
|
|
3626
3366
|
meta: TRouteMeta,
|
|
3627
3367
|
): BuilderChain<
|
|
3628
3368
|
TEnv,
|
|
@@ -3671,7 +3411,7 @@ class RouteBuilder<
|
|
|
3671
3411
|
}
|
|
3672
3412
|
return merged
|
|
3673
3413
|
})(),
|
|
3674
|
-
})
|
|
3414
|
+
})
|
|
3675
3415
|
}
|
|
3676
3416
|
|
|
3677
3417
|
output<TOutputSchemas extends OutputSchemaDef>(
|
|
@@ -3716,7 +3456,7 @@ class RouteBuilder<
|
|
|
3716
3456
|
>({
|
|
3717
3457
|
...this._s,
|
|
3718
3458
|
outputSchemas: _schemas,
|
|
3719
|
-
})
|
|
3459
|
+
})
|
|
3720
3460
|
}
|
|
3721
3461
|
|
|
3722
3462
|
use<TAdds>(
|
|
@@ -3742,7 +3482,7 @@ class RouteBuilder<
|
|
|
3742
3482
|
> {
|
|
3743
3483
|
if (mw.errors) {
|
|
3744
3484
|
for (const k of mw.errors) {
|
|
3745
|
-
this._s.errorKeys.add(k)
|
|
3485
|
+
this._s.errorKeys.add(k)
|
|
3746
3486
|
}
|
|
3747
3487
|
}
|
|
3748
3488
|
return new RouteBuilder<
|
|
@@ -3766,21 +3506,23 @@ class RouteBuilder<
|
|
|
3766
3506
|
>({
|
|
3767
3507
|
...this._s,
|
|
3768
3508
|
middlewares: [...this._s.middlewares, mw as RuntimeMiddleware],
|
|
3769
|
-
|
|
3509
|
+
mwMeta: mw.meta ? { ...this._s.mwMeta, ...mw.meta } : this._s.mwMeta,
|
|
3510
|
+
})
|
|
3770
3511
|
}
|
|
3771
3512
|
}
|
|
3772
3513
|
|
|
3773
3514
|
type WSRouteBuilderState<TParent> = {
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3515
|
+
mwMeta?: Record<string, unknown> | null
|
|
3516
|
+
boundaryErrorKey: string | null
|
|
3517
|
+
errorKeys: Set<string>
|
|
3518
|
+
inputSchemas: InputSchemasDef | null
|
|
3519
|
+
meta: Record<string, unknown> | null
|
|
3520
|
+
middlewares: RuntimeMiddleware[]
|
|
3521
|
+
parent: TParent
|
|
3522
|
+
parentMiddlewares: RuntimeMiddleware[]
|
|
3523
|
+
path: string
|
|
3524
|
+
root: TreeNode
|
|
3525
|
+
}
|
|
3784
3526
|
|
|
3785
3527
|
class WSRouteBuilder<
|
|
3786
3528
|
TEnv,
|
|
@@ -3789,36 +3531,24 @@ class WSRouteBuilder<
|
|
|
3789
3531
|
_TErrorKeys extends string = never,
|
|
3790
3532
|
TParent extends { _markWsRoutes(): void } = { _markWsRoutes(): void },
|
|
3791
3533
|
> {
|
|
3792
|
-
private _s: WSRouteBuilderState<TParent
|
|
3534
|
+
private _s: WSRouteBuilderState<TParent>
|
|
3793
3535
|
|
|
3794
3536
|
constructor(state: WSRouteBuilderState<TParent>) {
|
|
3795
|
-
this._s = state
|
|
3537
|
+
this._s = state
|
|
3796
3538
|
}
|
|
3797
3539
|
|
|
3798
3540
|
errors<TFactory extends Record<string, (...args: never[]) => unknown>>(
|
|
3799
3541
|
factory: TFactory,
|
|
3800
3542
|
...keys: Array<keyof TFactory & string>
|
|
3801
|
-
): WSRouteBuilder<
|
|
3802
|
-
|
|
3803
|
-
TCtx,
|
|
3804
|
-
TInput,
|
|
3805
|
-
_TErrorKeys | (keyof TFactory & string),
|
|
3806
|
-
TParent
|
|
3807
|
-
> {
|
|
3808
|
-
void factory;
|
|
3543
|
+
): WSRouteBuilder<TEnv, TCtx, TInput, _TErrorKeys | (keyof TFactory & string), TParent> {
|
|
3544
|
+
void factory
|
|
3809
3545
|
for (const k of keys) {
|
|
3810
|
-
this._s.errorKeys.add(k)
|
|
3546
|
+
this._s.errorKeys.add(k)
|
|
3811
3547
|
}
|
|
3812
|
-
return new WSRouteBuilder<
|
|
3813
|
-
TEnv,
|
|
3814
|
-
TCtx,
|
|
3815
|
-
TInput,
|
|
3816
|
-
_TErrorKeys | (keyof TFactory & string),
|
|
3817
|
-
TParent
|
|
3818
|
-
>({
|
|
3548
|
+
return new WSRouteBuilder<TEnv, TCtx, TInput, _TErrorKeys | (keyof TFactory & string), TParent>({
|
|
3819
3549
|
...this._s,
|
|
3820
3550
|
errorKeys: this._s.errorKeys,
|
|
3821
|
-
})
|
|
3551
|
+
})
|
|
3822
3552
|
}
|
|
3823
3553
|
|
|
3824
3554
|
handler(wsHandler: WSHandler<TCtx>): TParent {
|
|
@@ -3827,60 +3557,43 @@ class WSRouteBuilder<
|
|
|
3827
3557
|
ek: this._s.errorKeys,
|
|
3828
3558
|
fn: wsHandler as WSHandler<unknown>,
|
|
3829
3559
|
iv: this._s.inputSchemas,
|
|
3830
|
-
mt: this._s.
|
|
3560
|
+
mt: mergeContributedMeta(this._s.mwMeta, this._s.meta),
|
|
3831
3561
|
mw: [...this._s.parentMiddlewares, ...this._s.middlewares],
|
|
3832
3562
|
rp: this._s.path,
|
|
3833
|
-
}
|
|
3834
|
-
insertWsRoute(this._s.root, this._s.path, routeHandler)
|
|
3835
|
-
;(this._s.parent as unknown as HoneyInternal)._markWsRoutes()
|
|
3836
|
-
return this._s.parent
|
|
3563
|
+
}
|
|
3564
|
+
insertWsRoute(this._s.root, this._s.path, routeHandler)
|
|
3565
|
+
;(this._s.parent as unknown as HoneyInternal)._markWsRoutes()
|
|
3566
|
+
return this._s.parent
|
|
3837
3567
|
}
|
|
3838
3568
|
|
|
3839
|
-
input<
|
|
3840
|
-
TSchemas extends Pick<InputSchemasDef, "cookies" | "headers" | "search">,
|
|
3841
|
-
>(
|
|
3569
|
+
input<TSchemas extends Pick<InputSchemasDef, "cookies" | "headers" | "search">>(
|
|
3842
3570
|
schemas: TSchemas,
|
|
3843
|
-
): WSRouteBuilder<
|
|
3844
|
-
TEnv,
|
|
3845
|
-
TCtx,
|
|
3846
|
-
TInput & InferInputMap<TSchemas>,
|
|
3847
|
-
_TErrorKeys,
|
|
3848
|
-
TParent
|
|
3849
|
-
> {
|
|
3850
|
-
return new WSRouteBuilder<
|
|
3851
|
-
TEnv,
|
|
3852
|
-
TCtx,
|
|
3853
|
-
TInput & InferInputMap<TSchemas>,
|
|
3854
|
-
_TErrorKeys,
|
|
3855
|
-
TParent
|
|
3856
|
-
>({
|
|
3571
|
+
): WSRouteBuilder<TEnv, TCtx, TInput & InferInputMap<TSchemas>, _TErrorKeys, TParent> {
|
|
3572
|
+
return new WSRouteBuilder<TEnv, TCtx, TInput & InferInputMap<TSchemas>, _TErrorKeys, TParent>({
|
|
3857
3573
|
...this._s,
|
|
3858
3574
|
inputSchemas: schemas,
|
|
3859
|
-
})
|
|
3575
|
+
})
|
|
3860
3576
|
}
|
|
3861
3577
|
|
|
3862
3578
|
meta(meta: Record<string, unknown>): this {
|
|
3863
|
-
this._s.meta = meta
|
|
3864
|
-
return this
|
|
3579
|
+
this._s.meta = meta
|
|
3580
|
+
return this
|
|
3865
3581
|
}
|
|
3866
3582
|
|
|
3867
|
-
use<TAdds>(
|
|
3868
|
-
mw: MiddlewareFn<TCtx, TAdds>,
|
|
3869
|
-
): WSRouteBuilder<TEnv, TCtx & TAdds, TInput, _TErrorKeys, TParent> {
|
|
3583
|
+
use<TAdds>(mw: MiddlewareFn<TCtx, TAdds>): WSRouteBuilder<TEnv, TCtx & TAdds, TInput, _TErrorKeys, TParent> {
|
|
3870
3584
|
if (mw.errors) {
|
|
3871
3585
|
for (const k of mw.errors) {
|
|
3872
|
-
this._s.errorKeys.add(k)
|
|
3586
|
+
this._s.errorKeys.add(k)
|
|
3873
3587
|
}
|
|
3874
3588
|
}
|
|
3875
|
-
return new WSRouteBuilder<TEnv, TCtx & TAdds, TInput, _TErrorKeys, TParent>(
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
);
|
|
3589
|
+
return new WSRouteBuilder<TEnv, TCtx & TAdds, TInput, _TErrorKeys, TParent>({
|
|
3590
|
+
...this._s,
|
|
3591
|
+
middlewares: [...this._s.middlewares, mw as RuntimeMiddleware],
|
|
3592
|
+
mwMeta: mw.meta ? { ...this._s.mwMeta, ...mw.meta } : this._s.mwMeta,
|
|
3593
|
+
})
|
|
3881
3594
|
}
|
|
3882
3595
|
}
|
|
3883
3596
|
|
|
3884
3597
|
export function honey<TEnv>(): Honey<TEnv> {
|
|
3885
|
-
return new Honey<TEnv>()
|
|
3598
|
+
return new Honey<TEnv>()
|
|
3886
3599
|
}
|