@lovrozagar/honey 0.1.0 → 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 +1596 -6
- 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 +195 -51
- package/src/body-limit.ts +2 -1
- package/src/build/index.ts +2 -9
- package/src/cli.ts +2 -3
- 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 +1168 -1412
- 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-request.ts +2 -1
- package/src/node.ts +9 -10
- 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/context.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { isNodeOutbound } from "./honey-response.ts"
|
|
2
|
-
import type { SSEOptions, SSEStream, TypedResponse } from "./response.ts"
|
|
3
|
-
import { HoneyRes } from "./response.ts"
|
|
4
|
-
import type { PendingTap } from "./types.ts"
|
|
5
|
-
import { EMPTY_OBJ } from "./types.ts"
|
|
6
|
-
import { parseCookies } from "./validation.ts"
|
|
1
|
+
import { isNodeOutbound } from "./honey-response.ts"
|
|
2
|
+
import type { SSEOptions, SSEStream, TypedResponse } from "./response.ts"
|
|
3
|
+
import { HoneyRes } from "./response.ts"
|
|
4
|
+
import type { PendingTap } from "./types.ts"
|
|
5
|
+
import { EMPTY_OBJ } from "./types.ts"
|
|
6
|
+
import { parseCookies } from "./validation.ts"
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Per-request res with lazy lastEventId — only reads the header when SSE is actually called.
|
|
10
10
|
* All non-SSE methods (json/text/html/etc) are inherited from HoneyRes with zero overhead.
|
|
11
11
|
*/
|
|
12
12
|
class ContextRes extends HoneyRes {
|
|
13
|
-
private _req: Request
|
|
14
|
-
private _lastEventId: string | undefined | false = false
|
|
13
|
+
private _req: Request
|
|
14
|
+
private _lastEventId: string | undefined | false = false
|
|
15
15
|
|
|
16
16
|
constructor(req: Request) {
|
|
17
|
-
super(isNodeOutbound(req))
|
|
18
|
-
this._req = req
|
|
17
|
+
super(isNodeOutbound(req))
|
|
18
|
+
this._req = req
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
override sse(
|
|
@@ -23,35 +23,35 @@ class ContextRes extends HoneyRes {
|
|
|
23
23
|
opts?: SSEOptions,
|
|
24
24
|
): TypedResponse<"text/event-stream", "ok"> {
|
|
25
25
|
if (this._lastEventId === false) {
|
|
26
|
-
this._lastEventId = this._req.headers.get("last-event-id") ?? undefined
|
|
26
|
+
this._lastEventId = this._req.headers.get("last-event-id") ?? undefined
|
|
27
27
|
}
|
|
28
28
|
return super.sse(callback, {
|
|
29
29
|
...opts,
|
|
30
30
|
lastEventId: opts?.lastEventId ?? this._lastEventId,
|
|
31
|
-
})
|
|
31
|
+
})
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
function parseSearch(ctx: HoneyContext): void {
|
|
35
|
-
const url = ctx._lzUrlFn ? ctx._lzUrlFn() : new URL(ctx.req.url)
|
|
36
|
-
const first: Record<string, string> = {}
|
|
37
|
-
const all: Record<string, string[]> = {}
|
|
35
|
+
const url = ctx._lzUrlFn ? ctx._lzUrlFn() : new URL(ctx.req.url)
|
|
36
|
+
const first: Record<string, string> = {}
|
|
37
|
+
const all: Record<string, string[]> = {}
|
|
38
38
|
for (const [key, value] of url.searchParams) {
|
|
39
39
|
if (first[key] === undefined) {
|
|
40
|
-
first[key] = value
|
|
41
|
-
all[key] = [value]
|
|
40
|
+
first[key] = value
|
|
41
|
+
all[key] = [value]
|
|
42
42
|
} else {
|
|
43
|
-
const values = all[key]
|
|
43
|
+
const values = all[key]
|
|
44
44
|
if (values) {
|
|
45
|
-
values.push(value)
|
|
45
|
+
values.push(value)
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
ctx._lzSearch = first
|
|
50
|
-
ctx._lzSearchAll = all
|
|
49
|
+
ctx._lzSearch = first
|
|
50
|
+
ctx._lzSearchAll = all
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
function bgSwallow(p: Promise<unknown>) {
|
|
54
|
-
p.catch(noop)
|
|
54
|
+
p.catch(noop)
|
|
55
55
|
}
|
|
56
56
|
function noop() {}
|
|
57
57
|
|
|
@@ -64,98 +64,94 @@ function noop() {}
|
|
|
64
64
|
* works structurally for ApplyOutput type narrowing.
|
|
65
65
|
*/
|
|
66
66
|
export class HoneyContext<TEnv = Record<string, unknown>> {
|
|
67
|
-
readonly background: (p: Promise<unknown>) => void
|
|
68
|
-
readonly env: TEnv
|
|
69
|
-
readonly params: Record<string, string
|
|
70
|
-
readonly req: Request
|
|
71
|
-
readonly res: HoneyRes
|
|
67
|
+
readonly background: (p: Promise<unknown>) => void
|
|
68
|
+
readonly env: TEnv
|
|
69
|
+
readonly params: Record<string, string>
|
|
70
|
+
readonly req: Request
|
|
71
|
+
readonly res: HoneyRes
|
|
72
72
|
|
|
73
73
|
/* set after construction — declare keeps them off the private brand */
|
|
74
|
-
declare readonly cookies: Record<string, string
|
|
75
|
-
declare readonly errors: Record<string, (...args: never[]) => unknown
|
|
76
|
-
declare readonly executionCtx:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
declare readonly
|
|
80
|
-
declare readonly
|
|
81
|
-
declare readonly
|
|
82
|
-
declare readonly
|
|
83
|
-
declare readonly
|
|
84
|
-
declare readonly search: Record<string, string>;
|
|
85
|
-
declare readonly searchAll: Record<string, string[]>;
|
|
74
|
+
declare readonly cookies: Record<string, string>
|
|
75
|
+
declare readonly errors: Record<string, (...args: never[]) => unknown>
|
|
76
|
+
declare readonly executionCtx: { waitUntil?: (p: Promise<unknown>) => void } | undefined
|
|
77
|
+
declare readonly headers: Record<string, string>
|
|
78
|
+
declare readonly meta: Record<string, unknown>
|
|
79
|
+
declare readonly path: string
|
|
80
|
+
declare readonly realtime: { publish(topic: string, data: unknown): void }
|
|
81
|
+
declare readonly routePattern: string
|
|
82
|
+
declare readonly search: Record<string, string>
|
|
83
|
+
declare readonly searchAll: Record<string, string[]>
|
|
86
84
|
|
|
87
85
|
/* backing state for lazy getters — stored directly on instance to avoid extra object allocation */
|
|
88
|
-
/** @internal */ _lzCookies: Record<string, string> | null
|
|
89
|
-
/** @internal */ _lzHeaders: Record<string, string> | null
|
|
90
|
-
/** @internal */ _lzSearch: Record<string, string> | null
|
|
91
|
-
/** @internal */ _lzSearchAll: Record<string, string[]> | null
|
|
92
|
-
/** @internal */ _lzUrlFn: (() => URL) | null
|
|
86
|
+
/** @internal */ _lzCookies: Record<string, string> | null
|
|
87
|
+
/** @internal */ _lzHeaders: Record<string, string> | null
|
|
88
|
+
/** @internal */ _lzSearch: Record<string, string> | null
|
|
89
|
+
/** @internal */ _lzSearchAll: Record<string, string[]> | null
|
|
90
|
+
/** @internal */ _lzUrlFn: (() => URL) | null
|
|
93
91
|
|
|
94
92
|
/* error propagation — lets handler errors flow back through middleware chain */
|
|
95
|
-
/** @internal */ _errorToResponse:
|
|
96
|
-
|
|
97
|
-
| null;
|
|
98
|
-
/** @internal */ _isErrorResponse: boolean;
|
|
93
|
+
/** @internal */ _errorToResponse: ((thrown: unknown) => Promise<Response>) | null
|
|
94
|
+
/** @internal */ _isErrorResponse: boolean
|
|
99
95
|
|
|
100
96
|
/* tap side-effects — queued by c.tap(), drained after handler */
|
|
101
|
-
/** @internal */ _pendingTaps: PendingTap[] | null
|
|
97
|
+
/** @internal */ _pendingTaps: PendingTap[] | null
|
|
102
98
|
|
|
103
99
|
constructor(opts: {
|
|
104
|
-
env: TEnv
|
|
105
|
-
executionCtx?: { waitUntil?: (p: Promise<unknown>) => void }
|
|
106
|
-
meta?: Record<string, unknown
|
|
107
|
-
params: Record<string, string
|
|
108
|
-
path?: string
|
|
109
|
-
req: Request
|
|
110
|
-
routePattern?: string
|
|
111
|
-
urlFn?: () => URL
|
|
100
|
+
env: TEnv
|
|
101
|
+
executionCtx?: { waitUntil?: (p: Promise<unknown>) => void }
|
|
102
|
+
meta?: Record<string, unknown>
|
|
103
|
+
params: Record<string, string>
|
|
104
|
+
path?: string
|
|
105
|
+
req: Request
|
|
106
|
+
routePattern?: string
|
|
107
|
+
urlFn?: () => URL
|
|
112
108
|
}) {
|
|
113
|
-
this.req = opts.req
|
|
114
|
-
this.env = opts.env
|
|
115
|
-
this.params = opts.params
|
|
116
|
-
this.res = new ContextRes(opts.req)
|
|
109
|
+
this.req = opts.req
|
|
110
|
+
this.env = opts.env
|
|
111
|
+
this.params = opts.params
|
|
112
|
+
this.res = new ContextRes(opts.req)
|
|
117
113
|
|
|
118
114
|
/* direct assignment — no defineProperty */
|
|
119
|
-
(this as Record<string, unknown>)["executionCtx"] = opts.executionCtx
|
|
120
|
-
(this as Record<string, unknown>)["path"] = opts.path ?? ""
|
|
121
|
-
(this as Record<string, unknown>)["routePattern"] = opts.routePattern ?? ""
|
|
122
|
-
(this as Record<string, unknown>)["meta"] = opts.meta ?? EMPTY_OBJ
|
|
115
|
+
;(this as Record<string, unknown>)["executionCtx"] = opts.executionCtx
|
|
116
|
+
;(this as Record<string, unknown>)["path"] = opts.path ?? ""
|
|
117
|
+
;(this as Record<string, unknown>)["routePattern"] = opts.routePattern ?? ""
|
|
118
|
+
;(this as Record<string, unknown>)["meta"] = opts.meta ?? EMPTY_OBJ
|
|
123
119
|
|
|
124
120
|
/* invoke through executionCtx so `this` binds to the owning object — workerd's native ExecutionContext.waitUntil throws "Illegal invocation" when called detached */
|
|
125
|
-
const executionCtx = opts.executionCtx
|
|
126
|
-
const waitUntil = executionCtx?.waitUntil
|
|
121
|
+
const executionCtx = opts.executionCtx
|
|
122
|
+
const waitUntil = executionCtx?.waitUntil
|
|
127
123
|
this.background = waitUntil
|
|
128
124
|
? (p: Promise<unknown>) => {
|
|
129
|
-
waitUntil.call(executionCtx, p)
|
|
125
|
+
waitUntil.call(executionCtx, p)
|
|
130
126
|
}
|
|
131
|
-
: bgSwallow
|
|
127
|
+
: bgSwallow
|
|
132
128
|
|
|
133
129
|
/* lazy getter backing — flat on instance, no extra object */
|
|
134
|
-
this._lzCookies = null
|
|
135
|
-
this._lzHeaders = null
|
|
136
|
-
this._lzSearch = null
|
|
137
|
-
this._lzSearchAll = null
|
|
138
|
-
this._lzUrlFn = opts.urlFn ?? null
|
|
130
|
+
this._lzCookies = null
|
|
131
|
+
this._lzHeaders = null
|
|
132
|
+
this._lzSearch = null
|
|
133
|
+
this._lzSearchAll = null
|
|
134
|
+
this._lzUrlFn = opts.urlFn ?? null
|
|
139
135
|
|
|
140
136
|
/* error propagation */
|
|
141
|
-
this._errorToResponse = null
|
|
142
|
-
this._isErrorResponse = false
|
|
137
|
+
this._errorToResponse = null
|
|
138
|
+
this._isErrorResponse = false
|
|
143
139
|
|
|
144
140
|
/* taps */
|
|
145
|
-
this._pendingTaps = null
|
|
141
|
+
this._pendingTaps = null
|
|
146
142
|
}
|
|
147
143
|
|
|
148
144
|
/** @internal */
|
|
149
145
|
_setErrors(errors: Record<string, (...args: never[]) => unknown>): void {
|
|
150
|
-
(this as { errors: typeof errors }).errors = errors
|
|
146
|
+
;(this as { errors: typeof errors }).errors = errors
|
|
151
147
|
}
|
|
152
148
|
|
|
153
149
|
/** Queue a side-effect to fire after handler returns successfully */
|
|
154
150
|
tap(key: string, payload: unknown): void {
|
|
155
151
|
if (this._pendingTaps === null) {
|
|
156
|
-
this._pendingTaps = [{ key, payload }]
|
|
152
|
+
this._pendingTaps = [{ key, payload }]
|
|
157
153
|
} else {
|
|
158
|
-
this._pendingTaps.push({ key, payload })
|
|
154
|
+
this._pendingTaps.push({ key, payload })
|
|
159
155
|
}
|
|
160
156
|
}
|
|
161
157
|
|
|
@@ -167,44 +163,44 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
|
|
|
167
163
|
enumerable: true,
|
|
168
164
|
get(this: HoneyContext): Record<string, string> {
|
|
169
165
|
if (this._lzCookies === null) {
|
|
170
|
-
this._lzCookies = parseCookies(this.req.headers.get("cookie") ?? "")
|
|
166
|
+
this._lzCookies = parseCookies(this.req.headers.get("cookie") ?? "")
|
|
171
167
|
}
|
|
172
|
-
return this._lzCookies
|
|
168
|
+
return this._lzCookies
|
|
173
169
|
},
|
|
174
|
-
})
|
|
170
|
+
})
|
|
175
171
|
Object.defineProperty(HoneyContext.prototype, "headers", {
|
|
176
172
|
configurable: true,
|
|
177
173
|
enumerable: true,
|
|
178
174
|
get(this: HoneyContext): Record<string, string> {
|
|
179
175
|
if (this._lzHeaders === null) {
|
|
180
|
-
const h: Record<string, string> = {}
|
|
176
|
+
const h: Record<string, string> = {}
|
|
181
177
|
this.req.headers.forEach((value, key) => {
|
|
182
|
-
h[key] = value
|
|
183
|
-
})
|
|
184
|
-
this._lzHeaders = h
|
|
178
|
+
h[key] = value
|
|
179
|
+
})
|
|
180
|
+
this._lzHeaders = h
|
|
185
181
|
}
|
|
186
|
-
return this._lzHeaders
|
|
182
|
+
return this._lzHeaders
|
|
187
183
|
},
|
|
188
|
-
})
|
|
184
|
+
})
|
|
189
185
|
Object.defineProperty(HoneyContext.prototype, "search", {
|
|
190
186
|
configurable: true,
|
|
191
187
|
enumerable: true,
|
|
192
188
|
get(this: HoneyContext): Record<string, string> {
|
|
193
189
|
if (this._lzSearch === null) {
|
|
194
|
-
parseSearch(this)
|
|
190
|
+
parseSearch(this)
|
|
195
191
|
}
|
|
196
|
-
return this._lzSearch as Record<string, string
|
|
192
|
+
return this._lzSearch as Record<string, string>
|
|
197
193
|
},
|
|
198
|
-
})
|
|
194
|
+
})
|
|
199
195
|
Object.defineProperty(HoneyContext.prototype, "searchAll", {
|
|
200
196
|
configurable: true,
|
|
201
197
|
enumerable: true,
|
|
202
198
|
get(this: HoneyContext): Record<string, string[]> {
|
|
203
199
|
if (this._lzSearchAll === null) {
|
|
204
|
-
parseSearch(this)
|
|
200
|
+
parseSearch(this)
|
|
205
201
|
}
|
|
206
|
-
return this._lzSearchAll as Record<string, string[]
|
|
202
|
+
return this._lzSearchAll as Record<string, string[]>
|
|
207
203
|
},
|
|
208
|
-
})
|
|
204
|
+
})
|
|
209
205
|
}
|
|
210
206
|
}
|
package/src/cookie-sign.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
const encoder = new TextEncoder()
|
|
2
2
|
|
|
3
3
|
function importKey(secret: string): Promise<CryptoKey> {
|
|
4
|
-
return crypto.subtle.importKey(
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
false,
|
|
9
|
-
["sign", "verify"],
|
|
10
|
-
)
|
|
4
|
+
return crypto.subtle.importKey("raw", encoder.encode(secret), { hash: "SHA-256", name: "HMAC" }, false, [
|
|
5
|
+
"sign",
|
|
6
|
+
"verify",
|
|
7
|
+
])
|
|
11
8
|
}
|
|
12
9
|
|
|
13
10
|
function toBase64Url(buffer: ArrayBuffer): string {
|
|
@@ -45,12 +42,7 @@ export async function verify(signed: string, secrets: string[]): Promise<string
|
|
|
45
42
|
|
|
46
43
|
for (const secret of secrets) {
|
|
47
44
|
const key = await importKey(secret)
|
|
48
|
-
const valid = await crypto.subtle.verify(
|
|
49
|
-
"HMAC",
|
|
50
|
-
key,
|
|
51
|
-
sig.buffer as ArrayBuffer,
|
|
52
|
-
encoder.encode(value),
|
|
53
|
-
)
|
|
45
|
+
const valid = await crypto.subtle.verify("HMAC", key, sig.buffer as ArrayBuffer, encoder.encode(value))
|
|
54
46
|
if (valid) return value
|
|
55
47
|
}
|
|
56
48
|
|
package/src/cookie.ts
CHANGED
|
@@ -83,7 +83,6 @@ export function serializeCookie(name: string, opts: CookieOptions): string {
|
|
|
83
83
|
}
|
|
84
84
|
if (opts.httpOnly) cookie += "; HttpOnly"
|
|
85
85
|
if (secure) cookie += "; Secure"
|
|
86
|
-
if (opts.sameSite)
|
|
87
|
-
cookie += `; SameSite=${opts.sameSite.charAt(0).toUpperCase()}${opts.sameSite.slice(1)}`
|
|
86
|
+
if (opts.sameSite) cookie += `; SameSite=${opts.sameSite.charAt(0).toUpperCase()}${opts.sameSite.slice(1)}`
|
|
88
87
|
return cookie
|
|
89
88
|
}
|
package/src/cors.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { namedMiddleware } from "./middleware.ts"
|
|
1
2
|
import { replaceResponse } from "./honey-response.ts"
|
|
2
3
|
import type { MiddlewareFn } from "./middleware.ts"
|
|
3
4
|
|
|
@@ -25,8 +26,7 @@ export function cors(options?: CORSOptions): MiddlewareFn<{ req: Request }, {}>
|
|
|
25
26
|
const opts = options ?? {}
|
|
26
27
|
|
|
27
28
|
/* credentials + wildcard is a CORS spec violation — auto-narrow to echo origin */
|
|
28
|
-
const credentialWildcard =
|
|
29
|
-
opts.credentials === true && (opts.origin === undefined || opts.origin === "*")
|
|
29
|
+
const credentialWildcard = opts.credentials === true && (opts.origin === undefined || opts.origin === "*")
|
|
30
30
|
|
|
31
31
|
const mw: MiddlewareFn<{ req: Request }, {}> = async (ctx, next) => {
|
|
32
32
|
const req = ctx.req
|
|
@@ -37,9 +37,7 @@ export function cors(options?: CORSOptions): MiddlewareFn<{ req: Request }, {}>
|
|
|
37
37
|
return next()
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const allowedOrigin = credentialWildcard
|
|
41
|
-
? requestOrigin
|
|
42
|
-
: matchOrigin(requestOrigin, opts.origin)
|
|
40
|
+
const allowedOrigin = credentialWildcard ? requestOrigin : matchOrigin(requestOrigin, opts.origin)
|
|
43
41
|
|
|
44
42
|
/* origin not allowed */
|
|
45
43
|
if (allowedOrigin === null) {
|
|
@@ -92,5 +90,5 @@ export function cors(options?: CORSOptions): MiddlewareFn<{ req: Request }, {}>
|
|
|
92
90
|
})
|
|
93
91
|
}
|
|
94
92
|
|
|
95
|
-
return mw
|
|
93
|
+
return namedMiddleware("cors", mw)
|
|
96
94
|
}
|
package/src/crypto.ts
CHANGED
|
@@ -4,10 +4,7 @@ let cachedKey: CryptoKey | null = null
|
|
|
4
4
|
|
|
5
5
|
async function getKey(): Promise<CryptoKey> {
|
|
6
6
|
if (cachedKey === null) {
|
|
7
|
-
cachedKey = await crypto.subtle.generateKey({ hash: "SHA-256", name: "HMAC" }, false, [
|
|
8
|
-
"sign",
|
|
9
|
-
"verify",
|
|
10
|
-
])
|
|
7
|
+
cachedKey = await crypto.subtle.generateKey({ hash: "SHA-256", name: "HMAC" }, false, ["sign", "verify"])
|
|
11
8
|
}
|
|
12
9
|
return cachedKey
|
|
13
10
|
}
|
package/src/csrf.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { namedMiddleware } from "./middleware.ts"
|
|
1
2
|
import { HoneyError } from "./error.ts"
|
|
2
3
|
import type { MiddlewareFn } from "./middleware.ts"
|
|
3
4
|
import { EK, SK } from "./types.ts"
|
|
@@ -7,8 +8,7 @@ type CSRFOptions = {
|
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
const safeMethodRe = /^(GET|HEAD|OPTIONS)$/
|
|
10
|
-
const formContentTypeRe =
|
|
11
|
-
/^\b(application\/x-www-form-urlencoded|multipart\/form-data|text\/plain)\b/i
|
|
11
|
+
const formContentTypeRe = /^\b(application\/x-www-form-urlencoded|multipart\/form-data|text\/plain)\b/i
|
|
12
12
|
|
|
13
13
|
function matchOrigin(origin: string, config: CSRFOptions["origin"]): boolean {
|
|
14
14
|
if (config === undefined) return false
|
|
@@ -50,5 +50,5 @@ export function csrf(options?: CSRFOptions): MiddlewareFn<{ req: Request }, {}>
|
|
|
50
50
|
})
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
return mw
|
|
53
|
+
return namedMiddleware("csrf", mw)
|
|
54
54
|
}
|
package/src/curl-logger.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
+
import { namedMiddleware } from "./middleware.ts"
|
|
1
2
|
import type { MiddlewareFn } from "./middleware.ts"
|
|
2
3
|
import type { LoggerInstance } from "./logger.ts"
|
|
3
4
|
|
|
4
|
-
type BodyOmittedReason =
|
|
5
|
-
| "content-type"
|
|
6
|
-
| "disabled"
|
|
7
|
-
| "missing"
|
|
8
|
-
| "read-error"
|
|
9
|
-
| "too-large"
|
|
5
|
+
type BodyOmittedReason = "content-type" | "disabled" | "missing" | "read-error" | "too-large"
|
|
10
6
|
|
|
11
7
|
type CurlLogData = {
|
|
12
8
|
bodyIncluded: boolean
|
|
@@ -33,12 +29,7 @@ type CurlLoggerOptions = {
|
|
|
33
29
|
skip?: (data: CurlLogData) => boolean
|
|
34
30
|
}
|
|
35
31
|
|
|
36
|
-
const DEFAULT_BODY_CONTENT_TYPES = [
|
|
37
|
-
"application/json",
|
|
38
|
-
"application/x-www-form-urlencoded",
|
|
39
|
-
"application/xml",
|
|
40
|
-
"text/",
|
|
41
|
-
]
|
|
32
|
+
const DEFAULT_BODY_CONTENT_TYPES = ["application/json", "application/x-www-form-urlencoded", "application/xml", "text/"]
|
|
42
33
|
|
|
43
34
|
const DEFAULT_MAX_BODY_BYTES = 16_384
|
|
44
35
|
|
|
@@ -70,7 +61,10 @@ function redactUrl(url: URL, redactQueryParam?: (name: string, value: string) =>
|
|
|
70
61
|
return redacted.toString()
|
|
71
62
|
}
|
|
72
63
|
|
|
73
|
-
async function readBodyWithinLimit(
|
|
64
|
+
async function readBodyWithinLimit(
|
|
65
|
+
request: Request,
|
|
66
|
+
maxBytes: number,
|
|
67
|
+
): Promise<{
|
|
74
68
|
body: string | null
|
|
75
69
|
omittedReason: BodyOmittedReason | null
|
|
76
70
|
}> {
|
|
@@ -131,9 +125,7 @@ async function buildCurlLogData(
|
|
|
131
125
|
const parts: string[] = [`curl -X ${request.method}`]
|
|
132
126
|
|
|
133
127
|
for (const [name, value] of request.headers.entries()) {
|
|
134
|
-
const redactedValue = options?.redactHeader !== undefined
|
|
135
|
-
? options.redactHeader(name, value)
|
|
136
|
-
: value
|
|
128
|
+
const redactedValue = options?.redactHeader !== undefined ? options.redactHeader(name, value) : value
|
|
137
129
|
if (redactedValue === null) continue
|
|
138
130
|
parts.push(`-H '${shellEscape(name)}: ${shellEscape(redactedValue)}'`)
|
|
139
131
|
}
|
|
@@ -149,10 +141,7 @@ async function buildCurlLogData(
|
|
|
149
141
|
if (shouldIncludeBody(contentType, allowContentTypes) === false) {
|
|
150
142
|
bodyOmittedReason = request.body === null ? "missing" : "content-type"
|
|
151
143
|
} else {
|
|
152
|
-
const result = await readBodyWithinLimit(
|
|
153
|
-
request,
|
|
154
|
-
bodyOptions.maxBytes ?? DEFAULT_MAX_BODY_BYTES,
|
|
155
|
-
)
|
|
144
|
+
const result = await readBodyWithinLimit(request, bodyOptions.maxBytes ?? DEFAULT_MAX_BODY_BYTES)
|
|
156
145
|
|
|
157
146
|
if (result.body === null) {
|
|
158
147
|
bodyOmittedReason = result.omittedReason
|
|
@@ -174,17 +163,15 @@ async function buildCurlLogData(
|
|
|
174
163
|
}
|
|
175
164
|
}
|
|
176
165
|
|
|
177
|
-
function curlLogger(
|
|
178
|
-
options?: CurlLoggerOptions,
|
|
179
|
-
): MiddlewareFn<{ path: string, req: Request }, {}> {
|
|
166
|
+
function curlLogger(options?: CurlLoggerOptions): MiddlewareFn<{ path: string; req: Request }, {}> {
|
|
180
167
|
const log = options?.log ?? defaultLog
|
|
181
168
|
const skip = options?.skip
|
|
182
169
|
|
|
183
|
-
return async (ctx, next) => {
|
|
170
|
+
return namedMiddleware("curlLogger", async (ctx, next) => {
|
|
184
171
|
const start = performance.now()
|
|
185
172
|
const method = ctx.req.method
|
|
186
173
|
const path = ctx.path
|
|
187
|
-
const rid = (ctx as Record<string, unknown>)["requestId"] as string | null ?? null
|
|
174
|
+
const rid = ((ctx as Record<string, unknown>)["requestId"] as string | null) ?? null
|
|
188
175
|
const curlDataPromise = buildCurlLogData(ctx.req, options)
|
|
189
176
|
|
|
190
177
|
const response = await next()
|
|
@@ -223,13 +210,8 @@ function curlLogger(
|
|
|
223
210
|
}
|
|
224
211
|
|
|
225
212
|
return response
|
|
226
|
-
}
|
|
213
|
+
})
|
|
227
214
|
}
|
|
228
215
|
|
|
229
216
|
export { buildCurlLogData, curlLogger }
|
|
230
|
-
export type {
|
|
231
|
-
BodyOmittedReason,
|
|
232
|
-
CurlLogData,
|
|
233
|
-
CurlLoggerBodyOptions,
|
|
234
|
-
CurlLoggerOptions,
|
|
235
|
-
}
|
|
217
|
+
export type { BodyOmittedReason, CurlLogData, CurlLoggerBodyOptions, CurlLoggerOptions }
|
package/src/detect-runtime.ts
CHANGED
|
@@ -13,7 +13,5 @@ export function detectRuntime(g: DetectGlobal = globalThis): ServeRuntime {
|
|
|
13
13
|
const ua = g.navigator?.userAgent ?? ""
|
|
14
14
|
if (ua.includes("Cloudflare-Workers") || ua.includes("workerd")) return "cloudflare"
|
|
15
15
|
if (g.process?.versions?.node) return "node"
|
|
16
|
-
throw new Error(
|
|
17
|
-
'Honey.serve() could not detect a runtime. Pass runtime: "bun" | "node" | "deno".',
|
|
18
|
-
)
|
|
16
|
+
throw new Error('Honey.serve() could not detect a runtime. Pass runtime: "bun" | "node" | "deno".')
|
|
19
17
|
}
|
package/src/errors.ts
CHANGED
|
@@ -43,9 +43,7 @@ type ErrorFactoryResult<TMap extends Record<string, ErrorDef>, TTranslations ext
|
|
|
43
43
|
export function defineErrors<
|
|
44
44
|
TMap extends Record<string, ErrorDef>,
|
|
45
45
|
TTranslations extends Record<string, string> = Record<string, string>,
|
|
46
|
-
>(
|
|
47
|
-
map: TMap,
|
|
48
|
-
): ErrorFactoryResult<TMap, TTranslations> {
|
|
46
|
+
>(map: TMap): ErrorFactoryResult<TMap, TTranslations> {
|
|
49
47
|
const result = Object.create(null) as Record<string, unknown>
|
|
50
48
|
const meta = Object.create(null) as Record<string, ErrorMetaEntry>
|
|
51
49
|
|
package/src/etag.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { namedMiddleware } from "./middleware.ts"
|
|
1
2
|
import { replaceResponse } from "./honey-response.ts"
|
|
2
3
|
import type { MiddlewareFn } from "./middleware.ts"
|
|
3
4
|
|
|
@@ -35,9 +36,7 @@ export function etag(options?: ETagOptions): MiddlewareFn<{ req: Request }, {}>
|
|
|
35
36
|
/* skip streaming responses — can't hash without buffering entire stream */
|
|
36
37
|
if (
|
|
37
38
|
response.headers.get("transfer-encoding") === "chunked" ||
|
|
38
|
-
(response.body !== null &&
|
|
39
|
-
!response.headers.has("content-length") &&
|
|
40
|
-
!response.headers.has("content-type"))
|
|
39
|
+
(response.body !== null && !response.headers.has("content-length") && !response.headers.has("content-type"))
|
|
41
40
|
) {
|
|
42
41
|
return response
|
|
43
42
|
}
|
|
@@ -70,5 +69,5 @@ export function etag(options?: ETagOptions): MiddlewareFn<{ req: Request }, {}>
|
|
|
70
69
|
})
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
return mw
|
|
72
|
+
return namedMiddleware("etag", mw)
|
|
74
73
|
}
|