@lovrozagar/honey 0.1.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 +51 -0
- package/docs/api-ref/go.md +399 -0
- package/docs/api-ref/python.md +399 -0
- package/docs/api-ref/rust.md +399 -0
- package/docs/api-ref/typescript.md +399 -0
- package/docs/invalidate-spec.md +303 -0
- package/docs/sdk.md +722 -0
- package/package.json +120 -0
- package/src/accepts.ts +78 -0
- package/src/body-limit.ts +100 -0
- package/src/build/index.ts +174 -0
- package/src/cli-go/auth.go +15 -0
- package/src/cli-go/config.go +165 -0
- package/src/cli-go/errors.go +57 -0
- package/src/cli-go/multipart.go +55 -0
- package/src/cli-go/output.go +109 -0
- package/src/cli-go/root.go +30 -0
- package/src/cli-go/stream.go +88 -0
- package/src/cli-go/version.go +5 -0
- package/src/cli.ts +204 -0
- package/src/client/error.ts +27 -0
- package/src/client/http.ts +502 -0
- package/src/client/index.ts +127 -0
- package/src/client/sdk.ts +265 -0
- package/src/client/sse.ts +102 -0
- package/src/client/types.ts +211 -0
- package/src/client/ws.ts +115 -0
- package/src/client-go/errors.go +207 -0
- package/src/client-go/invalidation.go +373 -0
- package/src/client-go/realtime.go +317 -0
- package/src/client-go/result.go +20 -0
- package/src/client-go/runtime.go +413 -0
- package/src/client-go/sse.go +149 -0
- package/src/client-go/transport.go +452 -0
- package/src/client-go/ws.go +104 -0
- package/src/client-mcp/README.md +10 -0
- package/src/client-mcp/server.ts +94 -0
- package/src/client-mcp/types.ts +19 -0
- package/src/client-python/_errors.py +115 -0
- package/src/client-python/_invalidation.py +578 -0
- package/src/client-python/_realtime.py +156 -0
- package/src/client-python/_runtime.py +469 -0
- package/src/client-python/_sse.py +99 -0
- package/src/client-python/_transport.py +335 -0
- package/src/client-python/_ws.py +131 -0
- package/src/client-python/py.typed +0 -0
- package/src/client-rust/errors.rs +200 -0
- package/src/client-rust/invalidation.rs +441 -0
- package/src/client-rust/invalidation_sync.rs +306 -0
- package/src/client-rust/realtime.rs +695 -0
- package/src/client-rust/result.rs +22 -0
- package/src/client-rust/runtime.rs +547 -0
- package/src/client-rust/runtime_sync.rs +452 -0
- package/src/client-rust/sse.rs +167 -0
- package/src/client-rust/ws.rs +143 -0
- package/src/cloudflare/realtime.ts +236 -0
- package/src/codegen-go-cli.ts +1180 -0
- package/src/codegen-go.ts +1010 -0
- package/src/codegen-hash.ts +8 -0
- package/src/codegen-ir.ts +517 -0
- package/src/codegen-loaders.ts +53 -0
- package/src/codegen-mcp.ts +364 -0
- package/src/codegen-python.ts +1028 -0
- package/src/codegen-rust.ts +1652 -0
- package/src/codegen-sanitize.ts +67 -0
- package/src/codegen-schema-naming.ts +146 -0
- package/src/codegen.ts +5015 -0
- package/src/context.ts +210 -0
- package/src/cookie-sign.ts +58 -0
- package/src/cookie.ts +89 -0
- package/src/cors.ts +96 -0
- package/src/crypto.ts +19 -0
- package/src/csrf.ts +54 -0
- package/src/curl-logger.ts +235 -0
- package/src/detect-runtime.ts +19 -0
- package/src/error.ts +52 -0
- package/src/errors.ts +87 -0
- package/src/etag.ts +74 -0
- package/src/feature-load.ts +23 -0
- package/src/go-type-emitter.ts +404 -0
- package/src/honey-response.ts +354 -0
- package/src/i18n-slot.ts +20 -0
- package/src/i18n.ts +234 -0
- package/src/index.ts +3843 -0
- package/src/init.ts +149 -0
- package/src/input.ts +8 -0
- package/src/ip-restrict.ts +192 -0
- package/src/logger.ts +149 -0
- package/src/middleware.ts +199 -0
- package/src/node-request.ts +240 -0
- package/src/node.ts +212 -0
- package/src/openapi/register.ts +19 -0
- package/src/openapi/scalar.ts +33 -0
- package/src/openapi/spec-factory.ts +46 -0
- package/src/openapi/spec.ts +31 -0
- package/src/openapi/swagger.ts +34 -0
- package/src/plugin.ts +771 -0
- package/src/powered-by.ts +17 -0
- package/src/pretty-json.ts +39 -0
- package/src/proxy.ts +155 -0
- package/src/python-type-emitter.ts +144 -0
- package/src/realtime/buffer.ts +101 -0
- package/src/realtime/bus.ts +93 -0
- package/src/realtime/longpoll.ts +148 -0
- package/src/realtime/protocol.ts +128 -0
- package/src/realtime/route.ts +104 -0
- package/src/realtime/runtime.ts +430 -0
- package/src/request-id.ts +23 -0
- package/src/request-to-curl.ts +34 -0
- package/src/response.ts +414 -0
- package/src/rust-type-emitter.ts +412 -0
- package/src/secure-headers.ts +69 -0
- package/src/serve-register.ts +8 -0
- package/src/serve-slot.ts +20 -0
- package/src/serve.ts +141 -0
- package/src/server-timing.ts +66 -0
- package/src/static.ts +60 -0
- package/src/telemetry/otel.ts +139 -0
- package/src/testing.ts +114 -0
- package/src/timeout.ts +36 -0
- package/src/tree.ts +464 -0
- package/src/ts-type-emitter.ts +106 -0
- package/src/type-emitter.ts +524 -0
- package/src/type-extractor.ts +749 -0
- package/src/types.ts +638 -0
- package/src/validation.ts +300 -0
- package/src/ws/bun.ts +89 -0
- package/src/ws/cloudflare.ts +133 -0
- package/src/ws/deno.ts +109 -0
- package/src/ws/node.ts +161 -0
- package/src/yaml.ts +80 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { MiddlewareFn } from "./middleware.ts"
|
|
2
|
+
|
|
3
|
+
export type Timing = {
|
|
4
|
+
end(name: string): void
|
|
5
|
+
start(name: string, description?: string): void
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
type TimingEntry = {
|
|
9
|
+
description?: string
|
|
10
|
+
end?: number
|
|
11
|
+
start: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Strip characters that would break Server-Timing header syntax */
|
|
15
|
+
const unsafeNameRe = /[;,="]/g
|
|
16
|
+
function sanitizeName(name: string): string {
|
|
17
|
+
return name.replace(unsafeNameRe, "")
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function escapeDescription(desc: string): string {
|
|
21
|
+
return desc.replace(/\\/g, "\\\\").replace(/"/g, '\\"')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function serverTiming(): MiddlewareFn<{}, { timing: Timing }> {
|
|
25
|
+
const mw: MiddlewareFn<{}, { timing: Timing }> = async (_ctx, next) => {
|
|
26
|
+
const requestStart = performance.now()
|
|
27
|
+
const entries = new Map<string, TimingEntry>()
|
|
28
|
+
|
|
29
|
+
const timing: Timing = {
|
|
30
|
+
end(name: string) {
|
|
31
|
+
const entry = entries.get(name)
|
|
32
|
+
if (entry) {
|
|
33
|
+
entry.end = performance.now()
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
start(name: string, description?: string) {
|
|
37
|
+
entries.set(name, { description, start: performance.now() })
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const response = await next({ timing })
|
|
42
|
+
|
|
43
|
+
/* auto-close any unclosed timings */
|
|
44
|
+
const now = performance.now()
|
|
45
|
+
const parts: string[] = []
|
|
46
|
+
for (const [name, entry] of entries) {
|
|
47
|
+
const dur = ((entry.end ?? now) - entry.start).toFixed(2)
|
|
48
|
+
let part = sanitizeName(name)
|
|
49
|
+
if (entry.description) {
|
|
50
|
+
part += `;desc="${escapeDescription(entry.description)}"`
|
|
51
|
+
}
|
|
52
|
+
part += `;dur=${dur}`
|
|
53
|
+
parts.push(part)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* always include total request duration */
|
|
57
|
+
const totalDur = (performance.now() - requestStart).toFixed(2)
|
|
58
|
+
parts.push(`total;dur=${totalDur}`)
|
|
59
|
+
|
|
60
|
+
response.headers.set("server-timing", parts.join(", "))
|
|
61
|
+
|
|
62
|
+
return response
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return mw
|
|
66
|
+
}
|
package/src/static.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { MiddlewareFn } from "./middleware.ts"
|
|
2
|
+
|
|
3
|
+
type StaticConfig<TCtx> = {
|
|
4
|
+
resolve: (ctx: TCtx, filePath: string) => Response | null | Promise<Response | null>
|
|
5
|
+
prefix?: string
|
|
6
|
+
headers?: Record<string, string> | ((filePath: string) => Record<string, string>)
|
|
7
|
+
rewritePath?: (filePath: string, ctx: TCtx) => string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const TRAVERSAL = /(?:^|\/)\.\.\//
|
|
11
|
+
|
|
12
|
+
export function staticFiles<TCtx extends { req: Request }>(
|
|
13
|
+
config: StaticConfig<TCtx>,
|
|
14
|
+
): MiddlewareFn<TCtx, {}> {
|
|
15
|
+
const prefix = config.prefix ?? "/"
|
|
16
|
+
|
|
17
|
+
const mw: MiddlewareFn<TCtx, {}> = async (ctx, next) => {
|
|
18
|
+
const method = ctx.req.method
|
|
19
|
+
if (method !== "GET" && method !== "HEAD") return next()
|
|
20
|
+
|
|
21
|
+
/* fast path extraction — no new URL() */
|
|
22
|
+
const raw = ctx.req.url
|
|
23
|
+
const protoEnd = raw.indexOf("//")
|
|
24
|
+
const pathStart = protoEnd === -1 ? 0 : raw.indexOf("/", protoEnd + 2)
|
|
25
|
+
const qIdx = pathStart === -1 ? -1 : raw.indexOf("?", pathStart)
|
|
26
|
+
let path: string
|
|
27
|
+
if (pathStart === -1) {
|
|
28
|
+
path = "/"
|
|
29
|
+
} else if (qIdx === -1) {
|
|
30
|
+
path = raw.substring(pathStart)
|
|
31
|
+
} else {
|
|
32
|
+
path = raw.substring(pathStart, qIdx)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!path.startsWith(prefix)) return next()
|
|
36
|
+
|
|
37
|
+
const stripped = prefix === "/" ? path : path.substring(prefix.length)
|
|
38
|
+
const filePath = stripped === "" ? "/" : stripped
|
|
39
|
+
|
|
40
|
+
/* path traversal guard — reject encoded and literal .. segments */
|
|
41
|
+
const decoded = decodeURIComponent(filePath)
|
|
42
|
+
if (TRAVERSAL.test(decoded)) return next()
|
|
43
|
+
|
|
44
|
+
const resolved = config.rewritePath ? config.rewritePath(filePath, ctx) : filePath
|
|
45
|
+
const response = await config.resolve(ctx, resolved)
|
|
46
|
+
if (response === null) return next()
|
|
47
|
+
|
|
48
|
+
/* apply extra headers */
|
|
49
|
+
if (config.headers) {
|
|
50
|
+
const extra = typeof config.headers === "function" ? config.headers(filePath) : config.headers
|
|
51
|
+
for (const key in extra) {
|
|
52
|
+
response.headers.set(key, extra[key])
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return response
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return mw
|
|
60
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { HoneyError } from "../error.ts"
|
|
2
|
+
|
|
3
|
+
type Span = {
|
|
4
|
+
addEvent(name: string, attributes?: Record<string, unknown>): void
|
|
5
|
+
end(): void
|
|
6
|
+
setAttribute(key: string, value: unknown): void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type Tracer = {
|
|
10
|
+
startSpan(name: string): Span
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type TelemetryAdapter = {
|
|
14
|
+
onError?(ctx: { duration: number; error: HoneyError; method: string; path: string }): void
|
|
15
|
+
onHandler?(ctx: { duration: number; method: string; path: string; status: number }): void
|
|
16
|
+
onMethodNotAllowed?(ctx: { allowed: string[]; method: string; path: string; req: Request }): void
|
|
17
|
+
onMiddleware?(ctx: { duration: number; error?: unknown; name: string }): void
|
|
18
|
+
onNotFound?(ctx: { method: string; path: string; req: Request }): void
|
|
19
|
+
onRequest?(ctx: { env: unknown; req: Request }): void
|
|
20
|
+
onResponse?(ctx: { duration: number; req: Request; status: number }): void
|
|
21
|
+
onRoute?(ctx: {
|
|
22
|
+
method: string
|
|
23
|
+
params: Record<string, string>
|
|
24
|
+
path: string
|
|
25
|
+
req: Request
|
|
26
|
+
}): void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function otelAdapter(options: { tracer: Tracer }): TelemetryAdapter {
|
|
30
|
+
const spans = new WeakMap<Request, Span>()
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
onError(ctx) {
|
|
34
|
+
try {
|
|
35
|
+
/*
|
|
36
|
+
* onError doesn't receive req — error details are best captured
|
|
37
|
+
* via a standalone span. Per-request root span is in onResponse.
|
|
38
|
+
*/
|
|
39
|
+
const span = options.tracer.startSpan("http.error")
|
|
40
|
+
span.setAttribute("error.key", ctx.error.errorKey)
|
|
41
|
+
span.setAttribute("error.status", ctx.error.status)
|
|
42
|
+
span.setAttribute("http.duration_ms", ctx.duration)
|
|
43
|
+
span.end()
|
|
44
|
+
} catch {
|
|
45
|
+
/* swallow */
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
onHandler(ctx) {
|
|
50
|
+
try {
|
|
51
|
+
const span = options.tracer.startSpan("http.handler")
|
|
52
|
+
span.setAttribute("http.method", ctx.method)
|
|
53
|
+
span.setAttribute("http.route", ctx.path)
|
|
54
|
+
span.setAttribute("http.status_code", ctx.status)
|
|
55
|
+
span.setAttribute("http.duration_ms", ctx.duration)
|
|
56
|
+
span.end()
|
|
57
|
+
} catch {
|
|
58
|
+
/* swallow */
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
onMethodNotAllowed(ctx) {
|
|
63
|
+
try {
|
|
64
|
+
const span = spans.get(ctx.req)
|
|
65
|
+
if (span) {
|
|
66
|
+
span.addEvent("method_not_allowed", {
|
|
67
|
+
"http.allowed_methods": ctx.allowed.join(", "),
|
|
68
|
+
"http.method": ctx.method,
|
|
69
|
+
"http.route": ctx.path,
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
/* swallow */
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
onMiddleware(ctx) {
|
|
78
|
+
try {
|
|
79
|
+
const span = options.tracer.startSpan(`middleware.${ctx.name}`)
|
|
80
|
+
span.setAttribute("middleware.name", ctx.name)
|
|
81
|
+
span.setAttribute("middleware.duration_ms", ctx.duration)
|
|
82
|
+
if (ctx.error) span.addEvent("error")
|
|
83
|
+
span.end()
|
|
84
|
+
} catch {
|
|
85
|
+
/* swallow */
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
onNotFound(ctx) {
|
|
90
|
+
try {
|
|
91
|
+
const span = spans.get(ctx.req)
|
|
92
|
+
if (span) {
|
|
93
|
+
span.addEvent("not_found", {
|
|
94
|
+
"http.method": ctx.method,
|
|
95
|
+
"http.path": ctx.path,
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
/* swallow */
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
onRequest(ctx) {
|
|
104
|
+
try {
|
|
105
|
+
const span = options.tracer.startSpan("http.request")
|
|
106
|
+
span.setAttribute("http.url", ctx.req.url)
|
|
107
|
+
spans.set(ctx.req, span)
|
|
108
|
+
} catch {
|
|
109
|
+
/* swallow */
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
onResponse(ctx) {
|
|
114
|
+
try {
|
|
115
|
+
const span = spans.get(ctx.req)
|
|
116
|
+
if (span) {
|
|
117
|
+
span.setAttribute("http.status_code", ctx.status)
|
|
118
|
+
span.setAttribute("http.duration_ms", ctx.duration)
|
|
119
|
+
span.end()
|
|
120
|
+
spans.delete(ctx.req)
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
/* swallow */
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
onRoute(ctx) {
|
|
128
|
+
try {
|
|
129
|
+
const span = spans.get(ctx.req)
|
|
130
|
+
if (span) {
|
|
131
|
+
span.setAttribute("http.method", ctx.method)
|
|
132
|
+
span.setAttribute("http.route", ctx.path)
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
/* swallow */
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
}
|
package/src/testing.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Honey } from "./index.ts"
|
|
2
|
+
|
|
3
|
+
type TestRequestOptions = {
|
|
4
|
+
form?: Record<string, string>
|
|
5
|
+
headers?: Record<string, string>
|
|
6
|
+
json?: unknown
|
|
7
|
+
search?: Record<string, string>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type TestClient = {
|
|
11
|
+
delete(path: string, opts?: TestRequestOptions): Promise<Response>
|
|
12
|
+
get(path: string, opts?: TestRequestOptions): Promise<Response>
|
|
13
|
+
head(path: string, opts?: TestRequestOptions): Promise<Response>
|
|
14
|
+
options(path: string, opts?: TestRequestOptions): Promise<Response>
|
|
15
|
+
patch(path: string, opts?: TestRequestOptions): Promise<Response>
|
|
16
|
+
post(path: string, opts?: TestRequestOptions): Promise<Response>
|
|
17
|
+
put(path: string, opts?: TestRequestOptions): Promise<Response>
|
|
18
|
+
request(method: string, path: string, opts?: TestRequestOptions): Promise<Response>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildRequest(
|
|
22
|
+
method: string,
|
|
23
|
+
path: string,
|
|
24
|
+
baseUrl: string,
|
|
25
|
+
opts?: TestRequestOptions,
|
|
26
|
+
): Request {
|
|
27
|
+
const url = new URL(path, baseUrl)
|
|
28
|
+
|
|
29
|
+
if (opts?.search) {
|
|
30
|
+
for (const [k, v] of Object.entries(opts.search)) {
|
|
31
|
+
url.searchParams.set(k, v)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const headers = new Headers(opts?.headers)
|
|
36
|
+
let body: BodyInit | null = null
|
|
37
|
+
|
|
38
|
+
if (opts?.json !== undefined) {
|
|
39
|
+
headers.set("content-type", "application/json")
|
|
40
|
+
body = JSON.stringify(opts.json)
|
|
41
|
+
} else if (opts?.form) {
|
|
42
|
+
const formData = new FormData()
|
|
43
|
+
for (const [k, v] of Object.entries(opts.form)) {
|
|
44
|
+
formData.append(k, v)
|
|
45
|
+
}
|
|
46
|
+
body = formData
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return new Request(url.toString(), { body, headers, method })
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type TestClientOptions<TEnv> = {
|
|
53
|
+
cookies?: boolean
|
|
54
|
+
env: TEnv
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function parseCookieName(setCookie: string): { name: string; value: string } | null {
|
|
58
|
+
const eq = setCookie.indexOf("=")
|
|
59
|
+
if (eq === -1) return null
|
|
60
|
+
const name = setCookie.slice(0, eq).trim()
|
|
61
|
+
const semi = setCookie.indexOf(";", eq)
|
|
62
|
+
const value = semi === -1 ? setCookie.slice(eq + 1) : setCookie.slice(eq + 1, semi)
|
|
63
|
+
return { name, value }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function testClient<TEnv>(
|
|
67
|
+
app: Honey<TEnv, unknown, unknown, unknown, unknown, string, string>,
|
|
68
|
+
options: TestClientOptions<TEnv>,
|
|
69
|
+
): TestClient {
|
|
70
|
+
const baseUrl = "http://localhost"
|
|
71
|
+
const jar = new Map<string, string>()
|
|
72
|
+
|
|
73
|
+
async function doRequest(
|
|
74
|
+
method: string,
|
|
75
|
+
path: string,
|
|
76
|
+
opts?: TestRequestOptions,
|
|
77
|
+
): Promise<Response> {
|
|
78
|
+
const mergedHeaders = { ...opts?.headers }
|
|
79
|
+
|
|
80
|
+
if (options.cookies && jar.size > 0) {
|
|
81
|
+
const existing = mergedHeaders.cookie ?? ""
|
|
82
|
+
const jarStr = Array.from(jar.entries())
|
|
83
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
84
|
+
.join("; ")
|
|
85
|
+
mergedHeaders.cookie = existing ? `${existing}; ${jarStr}` : jarStr
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const req = buildRequest(method, path, baseUrl, { ...opts, headers: mergedHeaders })
|
|
89
|
+
const res = await app.fetch(req, options.env)
|
|
90
|
+
|
|
91
|
+
if (options.cookies) {
|
|
92
|
+
const setCookies = res.headers.getSetCookie()
|
|
93
|
+
for (const sc of setCookies) {
|
|
94
|
+
const parsed = parseCookieName(sc)
|
|
95
|
+
if (parsed) {
|
|
96
|
+
jar.set(parsed.name, parsed.value)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return res
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
delete: (path, opts) => doRequest("DELETE", path, opts),
|
|
106
|
+
get: (path, opts) => doRequest("GET", path, opts),
|
|
107
|
+
head: (path, opts) => doRequest("HEAD", path, opts),
|
|
108
|
+
options: (path, opts) => doRequest("OPTIONS", path, opts),
|
|
109
|
+
patch: (path, opts) => doRequest("PATCH", path, opts),
|
|
110
|
+
post: (path, opts) => doRequest("POST", path, opts),
|
|
111
|
+
put: (path, opts) => doRequest("PUT", path, opts),
|
|
112
|
+
request: (method, path, opts) => doRequest(method, path, opts),
|
|
113
|
+
}
|
|
114
|
+
}
|
package/src/timeout.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { HoneyError } from "./error.ts"
|
|
2
|
+
import type { MiddlewareFn } from "./middleware.ts"
|
|
3
|
+
import { EK, SK } from "./types.ts"
|
|
4
|
+
|
|
5
|
+
type TimeoutOptions = {
|
|
6
|
+
duration: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function timeout(opts: TimeoutOptions): MiddlewareFn<{}, {}> {
|
|
10
|
+
const { duration } = opts
|
|
11
|
+
|
|
12
|
+
const mw: MiddlewareFn<{}, {}> = (_ctx, next) => {
|
|
13
|
+
return new Promise<Response>((resolve, reject) => {
|
|
14
|
+
const timer = setTimeout(() => {
|
|
15
|
+
reject(
|
|
16
|
+
new HoneyError({
|
|
17
|
+
errorKey: EK.request_timeout,
|
|
18
|
+
status: SK.gateway_timeout,
|
|
19
|
+
}),
|
|
20
|
+
)
|
|
21
|
+
}, duration)
|
|
22
|
+
|
|
23
|
+
Promise.resolve(next())
|
|
24
|
+
.then((res) => {
|
|
25
|
+
clearTimeout(timer)
|
|
26
|
+
resolve(res)
|
|
27
|
+
})
|
|
28
|
+
.catch((err) => {
|
|
29
|
+
clearTimeout(timer)
|
|
30
|
+
reject(err)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return mw
|
|
36
|
+
}
|