@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.
Files changed (263) hide show
  1. package/README.md +1596 -6
  2. package/dist/accepts.d.ts +6 -0
  3. package/dist/accepts.d.ts.map +1 -0
  4. package/dist/body-limit.d.ts +22 -0
  5. package/dist/body-limit.d.ts.map +1 -0
  6. package/dist/build/index.d.ts +42 -0
  7. package/dist/build/index.d.ts.map +1 -0
  8. package/dist/cli.d.ts +3 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/client/error.d.ts +14 -0
  11. package/dist/client/error.d.ts.map +1 -0
  12. package/dist/client/http.d.ts +88 -0
  13. package/dist/client/http.d.ts.map +1 -0
  14. package/dist/client/index.d.ts +22 -0
  15. package/dist/client/index.d.ts.map +1 -0
  16. package/dist/client/sdk.d.ts +34 -0
  17. package/dist/client/sdk.d.ts.map +1 -0
  18. package/dist/client/sse.d.ts +10 -0
  19. package/dist/client/sse.d.ts.map +1 -0
  20. package/dist/client/types.d.ts +124 -0
  21. package/dist/client/types.d.ts.map +1 -0
  22. package/dist/client/ws.d.ts +15 -0
  23. package/dist/client/ws.d.ts.map +1 -0
  24. package/dist/client-mcp/server.d.ts +10 -0
  25. package/dist/client-mcp/server.d.ts.map +1 -0
  26. package/dist/client-mcp/types.d.ts +16 -0
  27. package/dist/client-mcp/types.d.ts.map +1 -0
  28. package/dist/cloudflare/realtime.d.ts +30 -0
  29. package/dist/cloudflare/realtime.d.ts.map +1 -0
  30. package/dist/codegen-go-cli.d.ts +22 -0
  31. package/dist/codegen-go-cli.d.ts.map +1 -0
  32. package/dist/codegen-go.d.ts +29 -0
  33. package/dist/codegen-go.d.ts.map +1 -0
  34. package/dist/codegen-hash.d.ts +3 -0
  35. package/dist/codegen-hash.d.ts.map +1 -0
  36. package/dist/codegen-ir.d.ts +123 -0
  37. package/dist/codegen-ir.d.ts.map +1 -0
  38. package/dist/codegen-loaders.d.ts +12 -0
  39. package/dist/codegen-loaders.d.ts.map +1 -0
  40. package/dist/codegen-mcp.d.ts +13 -0
  41. package/dist/codegen-mcp.d.ts.map +1 -0
  42. package/dist/codegen-python.d.ts +21 -0
  43. package/dist/codegen-python.d.ts.map +1 -0
  44. package/dist/codegen-rust.d.ts +18 -0
  45. package/dist/codegen-rust.d.ts.map +1 -0
  46. package/dist/codegen-sanitize.d.ts +3 -0
  47. package/dist/codegen-sanitize.d.ts.map +1 -0
  48. package/dist/codegen-schema-naming.d.ts +22 -0
  49. package/dist/codegen-schema-naming.d.ts.map +1 -0
  50. package/dist/codegen.d.ts +178 -0
  51. package/dist/codegen.d.ts.map +1 -0
  52. package/dist/context.d.ts +56 -0
  53. package/dist/context.d.ts.map +1 -0
  54. package/dist/cookie-sign.d.ts +3 -0
  55. package/dist/cookie-sign.d.ts.map +1 -0
  56. package/dist/cookie.d.ts +12 -0
  57. package/dist/cookie.d.ts.map +1 -0
  58. package/dist/cors.d.ts +13 -0
  59. package/dist/cors.d.ts.map +1 -0
  60. package/dist/crypto.d.ts +2 -0
  61. package/dist/crypto.d.ts.map +1 -0
  62. package/dist/csrf.d.ts +9 -0
  63. package/dist/csrf.d.ts.map +1 -0
  64. package/dist/curl-logger.d.ts +33 -0
  65. package/dist/curl-logger.d.ts.map +1 -0
  66. package/dist/detect-runtime.d.ts +16 -0
  67. package/dist/detect-runtime.d.ts.map +1 -0
  68. package/dist/error.d.ts +21 -0
  69. package/dist/error.d.ts.map +1 -0
  70. package/dist/errors.d.ts +40 -0
  71. package/dist/errors.d.ts.map +1 -0
  72. package/dist/etag.d.ts +9 -0
  73. package/dist/etag.d.ts.map +1 -0
  74. package/dist/feature-load.d.ts +3 -0
  75. package/dist/feature-load.d.ts.map +1 -0
  76. package/dist/go-type-emitter.d.ts +33 -0
  77. package/dist/go-type-emitter.d.ts.map +1 -0
  78. package/dist/honey-response.d.ts +82 -0
  79. package/dist/honey-response.d.ts.map +1 -0
  80. package/dist/i18n-slot.d.ts +7 -0
  81. package/dist/i18n-slot.d.ts.map +1 -0
  82. package/dist/i18n.d.ts +26 -0
  83. package/dist/i18n.d.ts.map +1 -0
  84. package/dist/index.d.ts +470 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/init.d.ts +7 -0
  87. package/dist/init.d.ts.map +1 -0
  88. package/dist/input.d.ts +6 -0
  89. package/dist/input.d.ts.map +1 -0
  90. package/dist/ip-restrict.d.ts +12 -0
  91. package/dist/ip-restrict.d.ts.map +1 -0
  92. package/dist/logger.d.ts +45 -0
  93. package/dist/logger.d.ts.map +1 -0
  94. package/dist/meta-spec.d.ts +81 -0
  95. package/dist/meta-spec.d.ts.map +1 -0
  96. package/dist/middleware.d.ts +60 -0
  97. package/dist/middleware.d.ts.map +1 -0
  98. package/dist/node-request.d.ts +59 -0
  99. package/dist/node-request.d.ts.map +1 -0
  100. package/dist/node.d.ts +13 -0
  101. package/dist/node.d.ts.map +1 -0
  102. package/dist/openapi/register.d.ts +3 -0
  103. package/dist/openapi/register.d.ts.map +1 -0
  104. package/dist/openapi/scalar.d.ts +12 -0
  105. package/dist/openapi/scalar.d.ts.map +1 -0
  106. package/dist/openapi/spec-factory.d.ts +31 -0
  107. package/dist/openapi/spec-factory.d.ts.map +1 -0
  108. package/dist/openapi/spec.d.ts +19 -0
  109. package/dist/openapi/spec.d.ts.map +1 -0
  110. package/dist/openapi/swagger.d.ts +12 -0
  111. package/dist/openapi/swagger.d.ts.map +1 -0
  112. package/dist/plugin.d.ts +167 -0
  113. package/dist/plugin.d.ts.map +1 -0
  114. package/dist/powered-by.d.ts +7 -0
  115. package/dist/powered-by.d.ts.map +1 -0
  116. package/dist/pretty-json.d.ts +10 -0
  117. package/dist/pretty-json.d.ts.map +1 -0
  118. package/dist/proxy.d.ts +51 -0
  119. package/dist/proxy.d.ts.map +1 -0
  120. package/dist/python-type-emitter.d.ts +11 -0
  121. package/dist/python-type-emitter.d.ts.map +1 -0
  122. package/dist/realtime/buffer.d.ts +18 -0
  123. package/dist/realtime/buffer.d.ts.map +1 -0
  124. package/dist/realtime/bus.d.ts +11 -0
  125. package/dist/realtime/bus.d.ts.map +1 -0
  126. package/dist/realtime/longpoll.d.ts +23 -0
  127. package/dist/realtime/longpoll.d.ts.map +1 -0
  128. package/dist/realtime/protocol.d.ts +50 -0
  129. package/dist/realtime/protocol.d.ts.map +1 -0
  130. package/dist/realtime/route.d.ts +42 -0
  131. package/dist/realtime/route.d.ts.map +1 -0
  132. package/dist/realtime/runtime.d.ts +74 -0
  133. package/dist/realtime/runtime.d.ts.map +1 -0
  134. package/dist/request-id.d.ts +12 -0
  135. package/dist/request-id.d.ts.map +1 -0
  136. package/dist/request-to-curl.d.ts +9 -0
  137. package/dist/request-to-curl.d.ts.map +1 -0
  138. package/dist/response.d.ts +57 -0
  139. package/dist/response.d.ts.map +1 -0
  140. package/dist/rust-type-emitter.d.ts +32 -0
  141. package/dist/rust-type-emitter.d.ts.map +1 -0
  142. package/dist/secure-headers.d.ts +16 -0
  143. package/dist/secure-headers.d.ts.map +1 -0
  144. package/dist/serve-register.d.ts +2 -0
  145. package/dist/serve-register.d.ts.map +1 -0
  146. package/dist/serve-slot.d.ts +6 -0
  147. package/dist/serve-slot.d.ts.map +1 -0
  148. package/dist/serve.d.ts +21 -0
  149. package/dist/serve.d.ts.map +1 -0
  150. package/dist/server-timing.d.ts +9 -0
  151. package/dist/server-timing.d.ts.map +1 -0
  152. package/dist/static.d.ts +12 -0
  153. package/dist/static.d.ts.map +1 -0
  154. package/dist/telemetry/otel.d.ts +59 -0
  155. package/dist/telemetry/otel.d.ts.map +1 -0
  156. package/dist/testing.d.ts +24 -0
  157. package/dist/testing.d.ts.map +1 -0
  158. package/dist/timeout.d.ts +7 -0
  159. package/dist/timeout.d.ts.map +1 -0
  160. package/dist/tree.d.ts +90 -0
  161. package/dist/tree.d.ts.map +1 -0
  162. package/dist/ts-type-emitter.d.ts +4 -0
  163. package/dist/ts-type-emitter.d.ts.map +1 -0
  164. package/dist/type-emitter.d.ts +8 -0
  165. package/dist/type-emitter.d.ts.map +1 -0
  166. package/dist/type-extractor.d.ts +39 -0
  167. package/dist/type-extractor.d.ts.map +1 -0
  168. package/dist/types.d.ts +451 -0
  169. package/dist/types.d.ts.map +1 -0
  170. package/dist/validation.d.ts +18 -0
  171. package/dist/validation.d.ts.map +1 -0
  172. package/dist/ws/bun.d.ts +28 -0
  173. package/dist/ws/bun.d.ts.map +1 -0
  174. package/dist/ws/cloudflare.d.ts +55 -0
  175. package/dist/ws/cloudflare.d.ts.map +1 -0
  176. package/dist/ws/deno.d.ts +13 -0
  177. package/dist/ws/deno.d.ts.map +1 -0
  178. package/dist/ws/node.d.ts +10 -0
  179. package/dist/ws/node.d.ts.map +1 -0
  180. package/dist/yaml.d.ts +4 -0
  181. package/dist/yaml.d.ts.map +1 -0
  182. package/docs/api-ref/go.md +82 -82
  183. package/docs/api-ref/python.md +82 -82
  184. package/docs/api-ref/rust.md +82 -82
  185. package/docs/api-ref/typescript.md +82 -82
  186. package/docs/invalidate-spec.md +13 -13
  187. package/docs/meta-spec.md +513 -0
  188. package/docs/packaging.md +85 -0
  189. package/docs/sdk.md +87 -73
  190. package/package.json +195 -51
  191. package/src/body-limit.ts +2 -1
  192. package/src/build/index.ts +2 -9
  193. package/src/cli.ts +2 -3
  194. package/src/client/http.ts +204 -223
  195. package/src/client/index.ts +6 -14
  196. package/src/client/sdk.ts +24 -17
  197. package/src/client/types.ts +4 -9
  198. package/src/client/ws.ts +3 -5
  199. package/src/client-mcp/server.ts +1 -4
  200. package/src/cloudflare/realtime.ts +8 -10
  201. package/src/codegen-go-cli.ts +74 -67
  202. package/src/codegen-go.ts +71 -51
  203. package/src/codegen-ir.ts +16 -27
  204. package/src/codegen-loaders.ts +5 -8
  205. package/src/codegen-mcp.ts +9 -27
  206. package/src/codegen-python.ts +47 -27
  207. package/src/codegen-rust.ts +110 -66
  208. package/src/codegen-sanitize.ts +1 -7
  209. package/src/codegen-schema-naming.ts +8 -2
  210. package/src/codegen.ts +299 -196
  211. package/src/context.ts +91 -95
  212. package/src/cookie-sign.ts +5 -13
  213. package/src/cookie.ts +1 -2
  214. package/src/cors.ts +4 -6
  215. package/src/crypto.ts +1 -4
  216. package/src/csrf.ts +3 -3
  217. package/src/curl-logger.ts +14 -32
  218. package/src/detect-runtime.ts +1 -3
  219. package/src/errors.ts +1 -3
  220. package/src/etag.ts +3 -4
  221. package/src/go-type-emitter.ts +45 -40
  222. package/src/honey-response.ts +1 -3
  223. package/src/i18n.ts +3 -12
  224. package/src/index.ts +1168 -1412
  225. package/src/ip-restrict.ts +2 -1
  226. package/src/logger.ts +13 -13
  227. package/src/meta-spec.ts +0 -0
  228. package/src/middleware.ts +111 -79
  229. package/src/node-request.ts +2 -1
  230. package/src/node.ts +9 -10
  231. package/src/openapi/register.ts +1 -2
  232. package/src/openapi/scalar.ts +9 -9
  233. package/src/openapi/spec-factory.ts +2 -2
  234. package/src/openapi/spec.ts +26 -19
  235. package/src/openapi/swagger.ts +9 -9
  236. package/src/plugin.ts +226 -256
  237. package/src/powered-by.ts +2 -1
  238. package/src/pretty-json.ts +2 -1
  239. package/src/proxy.ts +1 -3
  240. package/src/python-type-emitter.ts +37 -12
  241. package/src/realtime/route.ts +1 -1
  242. package/src/realtime/runtime.ts +9 -12
  243. package/src/request-id.ts +3 -4
  244. package/src/request-to-curl.ts +1 -4
  245. package/src/response.ts +150 -199
  246. package/src/rust-type-emitter.ts +66 -25
  247. package/src/secure-headers.ts +2 -1
  248. package/src/serve.ts +3 -12
  249. package/src/server-timing.ts +2 -1
  250. package/src/static.ts +3 -4
  251. package/src/telemetry/otel.ts +1 -6
  252. package/src/testing.ts +2 -11
  253. package/src/timeout.ts +2 -1
  254. package/src/tree.ts +12 -27
  255. package/src/ts-type-emitter.ts +71 -15
  256. package/src/type-emitter.ts +226 -237
  257. package/src/type-extractor.ts +260 -334
  258. package/src/types.ts +168 -68
  259. package/src/validation.ts +7 -30
  260. package/src/ws/bun.ts +2 -9
  261. package/src/ws/cloudflare.ts +13 -16
  262. package/src/ws/deno.ts +31 -40
  263. package/src/ws/node.ts +4 -19
package/src/response.ts CHANGED
@@ -1,114 +1,103 @@
1
- import type { CookieOptions } from "./cookie.ts";
2
- import { serializeCookie } from "./cookie.ts";
3
- import type { HoneyError } from "./error.ts";
4
- import { createHoneyResponse } from "./honey-response.ts";
5
- import type { StatusKey } from "./types.ts";
6
- import { statusKeyToCode } from "./types.ts";
1
+ import type { CookieOptions } from "./cookie.ts"
2
+ import { serializeCookie } from "./cookie.ts"
3
+ import type { HoneyError } from "./error.ts"
4
+ import { createHoneyResponse } from "./honey-response.ts"
5
+ import type { StatusKey } from "./types.ts"
6
+ import { statusKeyToCode } from "./types.ts"
7
7
 
8
- export type { CookieOptions } from "./cookie.ts";
9
- export { serializeCookie } from "./cookie.ts";
8
+ export type { CookieOptions } from "./cookie.ts"
9
+ export { serializeCookie } from "./cookie.ts"
10
10
 
11
11
  export type ResponseOptions = {
12
- cookies?: Record<string, CookieOptions>;
13
- headers?: Record<string, string>;
14
- status?: number;
15
- };
12
+ cookies?: Record<string, CookieOptions>
13
+ headers?: Record<string, string>
14
+ status?: number
15
+ }
16
16
 
17
17
  export type SSEEvent = {
18
- data: object | string;
19
- event: string;
20
- id?: string;
21
- retry?: number;
22
- };
18
+ data: object | string
19
+ event: string
20
+ id?: string
21
+ retry?: number
22
+ }
23
23
 
24
24
  export type SSEOptions = ResponseOptions & {
25
- defaultRetry?: number;
26
- keepalive?: number;
27
- lastEventId?: string;
28
- };
25
+ defaultRetry?: number
26
+ keepalive?: number
27
+ lastEventId?: string
28
+ }
29
29
 
30
30
  export type SSEStream = {
31
- close(): void;
32
- lastEventId: string | undefined;
33
- send(event: SSEEvent): Promise<void>;
34
- };
31
+ close(): void
32
+ lastEventId: string | undefined
33
+ send(event: SSEEvent): Promise<void>
34
+ }
35
35
 
36
- export type ErrorFormatter = (
37
- error: HoneyError,
38
- defaultShape: Record<string, unknown>,
39
- ) => Record<string, unknown>;
36
+ export type ErrorFormatter = (error: HoneyError, defaultShape: Record<string, unknown>) => Record<string, unknown>
40
37
 
41
38
  function applyResponseOptions(headers: Headers, opts?: ResponseOptions): void {
42
39
  if (opts?.headers) {
43
40
  for (const [k, v] of Object.entries(opts.headers)) {
44
- headers.set(k, v);
41
+ headers.set(k, v)
45
42
  }
46
43
  }
47
44
  if (opts?.cookies) {
48
45
  for (const [name, cookieOpts] of Object.entries(opts.cookies)) {
49
- headers.append("set-cookie", serializeCookie(name, cookieOpts));
46
+ headers.append("set-cookie", serializeCookie(name, cookieOpts))
50
47
  }
51
48
  }
52
49
  }
53
50
 
54
- function applyPlainOptions(
55
- headers: Record<string, string | string[]>,
56
- opts?: ResponseOptions,
57
- ): void {
51
+ function applyPlainOptions(headers: Record<string, string | string[]>, opts?: ResponseOptions): void {
58
52
  if (opts?.headers) {
59
53
  for (const [k, v] of Object.entries(opts.headers)) {
60
- headers[k.toLowerCase()] = v;
54
+ headers[k.toLowerCase()] = v
61
55
  }
62
56
  }
63
57
  if (opts?.cookies) {
64
- const cookies: string[] = [];
58
+ const cookies: string[] = []
65
59
  for (const [name, cookieOpts] of Object.entries(opts.cookies)) {
66
- cookies.push(serializeCookie(name, cookieOpts));
60
+ cookies.push(serializeCookie(name, cookieOpts))
67
61
  }
68
- headers["set-cookie"] = cookies;
62
+ headers["set-cookie"] = cookies
69
63
  }
70
64
  }
71
65
 
72
66
  /* phantom brands — exist only at type level, never assigned at runtime */
73
- declare const CONTENT_TYPE: unique symbol;
74
- declare const STATUS_KEY: unique symbol;
67
+ declare const CONTENT_TYPE: unique symbol
68
+ declare const STATUS_KEY: unique symbol
75
69
 
76
70
  /** Branded Response — phantom content-type + status-key at type level, native Response at runtime */
77
- export type TypedResponse<
78
- CT extends string = string,
79
- SK extends string = string,
80
- > = Response & {
81
- readonly [CONTENT_TYPE]: CT;
82
- readonly [STATUS_KEY]: SK;
83
- };
71
+ export type TypedResponse<CT extends string = string, SK extends string = string> = Response & {
72
+ readonly [CONTENT_TYPE]: CT
73
+ readonly [STATUS_KEY]: SK
74
+ }
84
75
 
85
76
  /** Cast a native Response to TypedResponse — zero runtime cost */
86
- function typed<CT extends string, SK extends string>(
87
- response: Response,
88
- ): TypedResponse<CT, SK> {
89
- return response as TypedResponse<CT, SK>;
77
+ function typed<CT extends string, SK extends string>(response: Response): TypedResponse<CT, SK> {
78
+ return response as TypedResponse<CT, SK>
90
79
  }
91
80
 
92
81
  /** Node adapter reads this to `res.end(payload)` without draining the Fetch body. */
93
- const RAW_BODY = Symbol.for("honey.rawBody");
82
+ const RAW_BODY = Symbol.for("honey.rawBody")
94
83
 
95
84
  function withRawBody(response: Response, body: string | Uint8Array): Response {
96
- Object.defineProperty(response, RAW_BODY, { value: body });
97
- return response;
85
+ Object.defineProperty(response, RAW_BODY, { value: body })
86
+ return response
98
87
  }
99
88
 
100
89
  /* pre-allocated header objects — Bun optimizes plain objects better than Headers instances */
101
- const JSON_HEADERS = { "content-type": "application/json" };
102
- const TEXT_HEADERS = { "content-type": "text/plain; charset=utf-8" };
103
- const HTML_HEADERS = { "content-type": "text/html; charset=utf-8" };
104
- const CSV_HEADERS = { "content-type": "text/csv; charset=utf-8" };
105
- const BINARY_HEADERS = { "content-type": "application/octet-stream" };
90
+ const JSON_HEADERS = { "content-type": "application/json" }
91
+ const TEXT_HEADERS = { "content-type": "text/plain; charset=utf-8" }
92
+ const HTML_HEADERS = { "content-type": "text/html; charset=utf-8" }
93
+ const CSV_HEADERS = { "content-type": "text/csv; charset=utf-8" }
94
+ const BINARY_HEADERS = { "content-type": "application/octet-stream" }
106
95
 
107
96
  export class HoneyRes {
108
- protected readonly _nodeOut: boolean;
97
+ protected readonly _nodeOut: boolean
109
98
 
110
99
  constructor(nodeOut = false) {
111
- this._nodeOut = nodeOut;
100
+ this._nodeOut = nodeOut
112
101
  }
113
102
 
114
103
  private _known<CT extends string, SK extends string>(
@@ -119,18 +108,18 @@ export class HoneyRes {
119
108
  ): TypedResponse<CT, SK> {
120
109
  if (this._nodeOut) {
121
110
  if (!opts?.headers && !opts?.cookies) {
122
- return typed(createHoneyResponse({ headers, raw, status }));
111
+ return typed(createHoneyResponse({ headers, raw, status }))
123
112
  }
124
- const plain: Record<string, string | string[]> = { ...headers };
125
- applyPlainOptions(plain, opts);
126
- return typed(createHoneyResponse({ headers: plain, raw, status }));
113
+ const plain: Record<string, string | string[]> = { ...headers }
114
+ applyPlainOptions(plain, opts)
115
+ return typed(createHoneyResponse({ headers: plain, raw, status }))
127
116
  }
128
117
  if (!opts?.headers && !opts?.cookies) {
129
- return typed(withRawBody(new Response(raw as BodyInit, { headers, status }), raw));
118
+ return typed(withRawBody(new Response(raw as BodyInit, { headers, status }), raw))
130
119
  }
131
- const native = new Headers(headers);
132
- applyResponseOptions(native, opts);
133
- return typed(withRawBody(new Response(raw as BodyInit, { headers: native, status }), raw));
120
+ const native = new Headers(headers)
121
+ applyResponseOptions(native, opts)
122
+ return typed(withRawBody(new Response(raw as BodyInit, { headers: native, status }), raw))
134
123
  }
135
124
 
136
125
  binary<SK extends StatusKey>(
@@ -138,24 +127,16 @@ export class HoneyRes {
138
127
  body: ArrayBuffer | Uint8Array<ArrayBuffer>,
139
128
  opts?: ResponseOptions,
140
129
  ): TypedResponse<"application/octet-stream", SK> {
141
- const raw = body instanceof Uint8Array ? body : new Uint8Array(body);
142
- return this._known(statusKeyToCode[statusKey], BINARY_HEADERS, raw, opts);
130
+ const raw = body instanceof Uint8Array ? body : new Uint8Array(body)
131
+ return this._known(statusKeyToCode[statusKey], BINARY_HEADERS, raw, opts)
143
132
  }
144
133
 
145
- csv<SK extends StatusKey>(
146
- statusKey: SK,
147
- body: string,
148
- opts?: ResponseOptions,
149
- ): TypedResponse<"text/csv", SK> {
150
- return this._known(statusKeyToCode[statusKey], CSV_HEADERS, body, opts);
134
+ csv<SK extends StatusKey>(statusKey: SK, body: string, opts?: ResponseOptions): TypedResponse<"text/csv", SK> {
135
+ return this._known(statusKeyToCode[statusKey], CSV_HEADERS, body, opts)
151
136
  }
152
137
 
153
- html<SK extends StatusKey>(
154
- statusKey: SK,
155
- body: string,
156
- opts?: ResponseOptions,
157
- ): TypedResponse<"text/html", SK> {
158
- return this._known(statusKeyToCode[statusKey], HTML_HEADERS, body, opts);
138
+ html<SK extends StatusKey>(statusKey: SK, body: string, opts?: ResponseOptions): TypedResponse<"text/html", SK> {
139
+ return this._known(statusKeyToCode[statusKey], HTML_HEADERS, body, opts)
159
140
  }
160
141
 
161
142
  json<SK extends StatusKey>(
@@ -163,220 +144,190 @@ export class HoneyRes {
163
144
  data: unknown,
164
145
  opts?: ResponseOptions,
165
146
  ): TypedResponse<"application/json", SK> {
166
- return this._known(statusKeyToCode[statusKey], JSON_HEADERS, JSON.stringify(data), opts);
147
+ return this._known(statusKeyToCode[statusKey], JSON_HEADERS, JSON.stringify(data), opts)
167
148
  }
168
149
 
169
150
  noContent(opts?: ResponseOptions): TypedResponse<"none", "no_content"> {
170
151
  if (this._nodeOut) {
171
- const headers: Record<string, string | string[]> = {};
172
- applyPlainOptions(headers, opts);
173
- return typed(createHoneyResponse({ headers, raw: null, status: 204 }));
152
+ const headers: Record<string, string | string[]> = {}
153
+ applyPlainOptions(headers, opts)
154
+ return typed(createHoneyResponse({ headers, raw: null, status: 204 }))
174
155
  }
175
- const headers = new Headers();
176
- applyResponseOptions(headers, opts);
177
- return typed(new Response(null, { headers, status: 204 }));
156
+ const headers = new Headers()
157
+ applyResponseOptions(headers, opts)
158
+ return typed(new Response(null, { headers, status: 204 }))
178
159
  }
179
160
 
180
161
  raw(response: Response): TypedResponse {
181
- return typed(response);
162
+ return typed(response)
182
163
  }
183
164
 
184
- redirect(
185
- url: string,
186
- opts?: ResponseOptions,
187
- ): TypedResponse<"none", "found"> {
165
+ redirect(url: string, opts?: ResponseOptions): TypedResponse<"none", "found"> {
188
166
  if (this._nodeOut) {
189
- const headers: Record<string, string | string[]> = { location: url };
190
- applyPlainOptions(headers, opts);
191
- return typed(createHoneyResponse({ headers, raw: null, status: opts?.status ?? 302 }));
167
+ const headers: Record<string, string | string[]> = { location: url }
168
+ applyPlainOptions(headers, opts)
169
+ return typed(createHoneyResponse({ headers, raw: null, status: opts?.status ?? 302 }))
192
170
  }
193
- const headers = new Headers({ location: url });
194
- applyResponseOptions(headers, opts);
195
- return typed(new Response(null, { headers, status: opts?.status ?? 302 }));
171
+ const headers = new Headers({ location: url })
172
+ applyResponseOptions(headers, opts)
173
+ return typed(new Response(null, { headers, status: opts?.status ?? 302 }))
196
174
  }
197
175
 
198
- sse(
199
- callback: (stream: SSEStream) => Promise<void>,
200
- opts?: SSEOptions,
201
- ): TypedResponse<"text/event-stream", "ok"> {
202
- const { readable, writable } = new TransformStream();
203
- const writer = writable.getWriter();
204
- const encoder = new TextEncoder();
205
- let keepaliveTimer: ReturnType<typeof setInterval> | undefined;
176
+ sse(callback: (stream: SSEStream) => Promise<void>, opts?: SSEOptions): TypedResponse<"text/event-stream", "ok"> {
177
+ const { readable, writable } = new TransformStream()
178
+ const writer = writable.getWriter()
179
+ const encoder = new TextEncoder()
180
+ let keepaliveTimer: ReturnType<typeof setInterval> | undefined
206
181
 
207
- let closed = false;
182
+ let closed = false
208
183
  const sseStream: SSEStream = {
209
184
  close() {
210
- if (closed) return;
211
- closed = true;
212
- if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer);
213
- writer.close();
185
+ if (closed) return
186
+ closed = true
187
+ if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer)
188
+ writer.close()
214
189
  },
215
190
  lastEventId: opts?.lastEventId,
216
191
  send(event) {
217
192
  if (/[\r\n]/.test(event.event)) {
218
- throw new Error("SSE event name must not contain newlines");
193
+ throw new Error("SSE event name must not contain newlines")
219
194
  }
220
195
  if (event.id && /[\r\n]/.test(event.id)) {
221
- throw new Error("SSE id must not contain newlines");
196
+ throw new Error("SSE id must not contain newlines")
222
197
  }
223
- const dataStr =
224
- typeof event.data === "string"
225
- ? event.data
226
- : JSON.stringify(event.data);
198
+ const dataStr = typeof event.data === "string" ? event.data : JSON.stringify(event.data)
227
199
  const dataLines = dataStr
228
200
  .split(/\r\n|\r|\n/)
229
201
  .map((line) => `data: ${line}`)
230
- .join("\n");
231
- let msg = `event: ${event.event}\n${dataLines}\n`;
232
- if (event.id) msg += `id: ${event.id}\n`;
202
+ .join("\n")
203
+ let msg = `event: ${event.event}\n${dataLines}\n`
204
+ if (event.id) msg += `id: ${event.id}\n`
233
205
  /** SSE retry must be a non-negative integer per spec — skip if invalid */
234
- if (
235
- event.retry !== undefined &&
236
- Number.isFinite(event.retry) &&
237
- event.retry >= 0
238
- ) {
239
- msg += `retry: ${Math.floor(event.retry)}\n`;
206
+ if (event.retry !== undefined && Number.isFinite(event.retry) && event.retry >= 0) {
207
+ msg += `retry: ${Math.floor(event.retry)}\n`
240
208
  }
241
- msg += "\n";
242
- return writer.write(encoder.encode(msg));
209
+ msg += "\n"
210
+ return writer.write(encoder.encode(msg))
243
211
  },
244
- };
212
+ }
245
213
 
246
214
  if (opts?.defaultRetry !== undefined) {
247
- writer.write(encoder.encode(`retry: ${opts.defaultRetry}\n\n`));
215
+ writer.write(encoder.encode(`retry: ${opts.defaultRetry}\n\n`))
248
216
  }
249
217
 
250
218
  if (opts?.keepalive !== undefined && opts.keepalive > 0) {
251
219
  keepaliveTimer = setInterval(() => {
252
220
  writer.write(encoder.encode(": heartbeat\n\n")).catch(() => {
253
- if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer);
254
- });
255
- }, opts.keepalive);
221
+ if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer)
222
+ })
223
+ }, opts.keepalive)
256
224
  }
257
225
 
258
226
  void callback(sseStream).then(
259
227
  async () => {
260
- if (closed) return;
261
- closed = true;
262
- if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer);
228
+ if (closed) return
229
+ closed = true
230
+ if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer)
263
231
  try {
264
- await writer.close();
232
+ await writer.close()
265
233
  } catch {}
266
234
  },
267
235
  async () => {
268
- if (closed) return;
269
- closed = true;
270
- if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer);
236
+ if (closed) return
237
+ closed = true
238
+ if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer)
271
239
  try {
272
- await writer.close();
240
+ await writer.close()
273
241
  } catch {}
274
242
  },
275
- );
243
+ )
276
244
 
277
245
  const headers = new Headers({
278
246
  "cache-control": "no-cache",
279
247
  connection: "keep-alive",
280
248
  "content-type": "text/event-stream",
281
- });
282
- applyResponseOptions(headers, opts);
283
- return typed(new Response(readable, { headers, status: 200 }));
249
+ })
250
+ applyResponseOptions(headers, opts)
251
+ return typed(new Response(readable, { headers, status: 200 }))
284
252
  }
285
253
 
286
254
  generate(
287
- generator:
288
- | AsyncGenerator<string, void, unknown>
289
- | Generator<string, void, unknown>,
255
+ generator: AsyncGenerator<string, void, unknown> | Generator<string, void, unknown>,
290
256
  opts?: { contentType?: string; status?: number },
291
257
  ): TypedResponse<string, "ok"> {
292
- const encoder = new TextEncoder();
293
- const gen = generator;
258
+ const encoder = new TextEncoder()
259
+ const gen = generator
294
260
  const readable = new ReadableStream({
295
261
  async pull(controller) {
296
262
  try {
297
- const { done, value } = await gen.next();
263
+ const { done, value } = await gen.next()
298
264
  if (done) {
299
- controller.close();
300
- return;
265
+ controller.close()
266
+ return
301
267
  }
302
- controller.enqueue(encoder.encode(value));
268
+ controller.enqueue(encoder.encode(value))
303
269
  } catch {
304
- controller.close();
270
+ controller.close()
305
271
  }
306
272
  },
307
- });
273
+ })
308
274
  const headers = new Headers({
309
275
  "content-type": opts?.contentType ?? "application/octet-stream",
310
- });
311
- return typed(
312
- new Response(readable, { headers, status: opts?.status ?? 200 }),
313
- );
276
+ })
277
+ return typed(new Response(readable, { headers, status: opts?.status ?? 200 }))
314
278
  }
315
279
 
316
280
  stream(
317
281
  callback: (writable: WritableStream) => Promise<void>,
318
282
  opts?: ResponseOptions,
319
283
  ): TypedResponse<"application/octet-stream", "ok"> {
320
- const { readable, writable } = new TransformStream();
284
+ const { readable, writable } = new TransformStream()
321
285
  void callback(writable).then(
322
286
  async () => {
323
287
  try {
324
- await writable.close();
288
+ await writable.close()
325
289
  } catch {}
326
290
  },
327
291
  async () => {
328
292
  try {
329
- await writable.close();
293
+ await writable.close()
330
294
  } catch {}
331
295
  },
332
- );
333
- const headers = new Headers();
334
- applyResponseOptions(headers, opts);
335
- return typed(
336
- new Response(readable, { headers, status: opts?.status ?? 200 }),
337
- );
296
+ )
297
+ const headers = new Headers()
298
+ applyResponseOptions(headers, opts)
299
+ return typed(new Response(readable, { headers, status: opts?.status ?? 200 }))
338
300
  }
339
301
 
340
- text<SK extends StatusKey>(
341
- statusKey: SK,
342
- body: string,
343
- opts?: ResponseOptions,
344
- ): TypedResponse<"text/plain", SK> {
345
- return this._known(statusKeyToCode[statusKey], TEXT_HEADERS, body, opts);
302
+ text<SK extends StatusKey>(statusKey: SK, body: string, opts?: ResponseOptions): TypedResponse<"text/plain", SK> {
303
+ return this._known(statusKeyToCode[statusKey], TEXT_HEADERS, body, opts)
346
304
  }
347
305
 
348
- xml<SK extends StatusKey>(
349
- statusKey: SK,
350
- body: string,
351
- opts?: ResponseOptions,
352
- ): TypedResponse<"application/xml", SK> {
353
- return this._known(statusKeyToCode[statusKey], { "content-type": "application/xml" }, body, opts);
306
+ xml<SK extends StatusKey>(statusKey: SK, body: string, opts?: ResponseOptions): TypedResponse<"application/xml", SK> {
307
+ return this._known(statusKeyToCode[statusKey], { "content-type": "application/xml" }, body, opts)
354
308
  }
355
309
  }
356
310
 
357
- export type CustomErrorFormatter = (
358
- error: HoneyError,
359
- data: Record<string, unknown>,
360
- ) => Record<string, unknown>;
311
+ export type CustomErrorFormatter = (error: HoneyError, data: Record<string, unknown>) => Record<string, unknown>
361
312
 
362
313
  export function createErrorResponse(
363
314
  error: HoneyError,
364
315
  defaultFormatter: ErrorFormatter,
365
316
  customFormatter?: CustomErrorFormatter | null,
366
317
  ): Response {
367
- let body: Record<string, unknown>;
318
+ let body: Record<string, unknown>
368
319
 
369
320
  if (error.data !== undefined) {
370
321
  /* custom schema error — apply customErrorFormatter if set, else use data as-is */
371
- const rawData = error.data as Record<string, unknown>;
322
+ const rawData = error.data as Record<string, unknown>
372
323
  if (customFormatter) {
373
324
  try {
374
- body = customFormatter(error, rawData);
325
+ body = customFormatter(error, rawData)
375
326
  } catch {
376
- body = rawData;
327
+ body = rawData
377
328
  }
378
329
  } else {
379
- body = rawData;
330
+ body = rawData
380
331
  }
381
332
  } else {
382
333
  /* standard error — build default shape, apply defaultErrorFormatter */
@@ -388,12 +339,12 @@ export function createErrorResponse(
388
339
  status_key: error.statusKey,
389
340
  success: false,
390
341
  ...(error.vars ? { vars: error.vars } : {}),
391
- };
342
+ }
392
343
  try {
393
- body = defaultFormatter(error, defaultShape);
344
+ body = defaultFormatter(error, defaultShape)
394
345
  } catch {
395
346
  /* formatter crashed — fall back to default shape */
396
- body = defaultShape;
347
+ body = defaultShape
397
348
  }
398
349
  }
399
350
 
@@ -403,12 +354,12 @@ export function createErrorResponse(
403
354
  headers[k] = v
404
355
  }
405
356
  }
406
- const payload = JSON.stringify(body);
357
+ const payload = JSON.stringify(body)
407
358
  return withRawBody(
408
359
  new Response(payload, {
409
360
  headers,
410
361
  status: error.status,
411
362
  }),
412
363
  payload,
413
- );
364
+ )
414
365
  }
@@ -5,14 +5,58 @@ import type { IRSchema } from "./codegen-ir.ts"
5
5
  import { goPascal } from "./go-type-emitter.ts"
6
6
 
7
7
  export const RUST_KEYWORDS = new Set([
8
- "as", "break", "const", "continue", "crate", "else", "enum", "extern",
9
- "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod",
10
- "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct",
11
- "super", "trait", "true", "type", "unsafe", "use", "where", "while",
12
- "async", "await", "dyn",
8
+ "as",
9
+ "break",
10
+ "const",
11
+ "continue",
12
+ "crate",
13
+ "else",
14
+ "enum",
15
+ "extern",
16
+ "false",
17
+ "fn",
18
+ "for",
19
+ "if",
20
+ "impl",
21
+ "in",
22
+ "let",
23
+ "loop",
24
+ "match",
25
+ "mod",
26
+ "move",
27
+ "mut",
28
+ "pub",
29
+ "ref",
30
+ "return",
31
+ "self",
32
+ "Self",
33
+ "static",
34
+ "struct",
35
+ "super",
36
+ "trait",
37
+ "true",
38
+ "type",
39
+ "unsafe",
40
+ "use",
41
+ "where",
42
+ "while",
43
+ "async",
44
+ "await",
45
+ "dyn",
13
46
  /* reserved / future */
14
- "abstract", "become", "box", "do", "final", "macro", "override", "priv",
15
- "typeof", "unsized", "virtual", "yield", "try",
47
+ "abstract",
48
+ "become",
49
+ "box",
50
+ "do",
51
+ "final",
52
+ "macro",
53
+ "override",
54
+ "priv",
55
+ "typeof",
56
+ "unsized",
57
+ "virtual",
58
+ "yield",
59
+ "try",
16
60
  ])
17
61
 
18
62
  export function rustPascal(name: string): string {
@@ -97,11 +141,16 @@ function hoistEnumName(parentName: string, fieldName: string): string {
97
141
 
98
142
  function primitiveFor(t: string | undefined): string | null {
99
143
  switch (t) {
100
- case "string": return "String"
101
- case "integer": return "i64"
102
- case "number": return "f64"
103
- case "boolean": return "bool"
104
- default: return null
144
+ case "string":
145
+ return "String"
146
+ case "integer":
147
+ return "i64"
148
+ case "number":
149
+ return "f64"
150
+ case "boolean":
151
+ return "bool"
152
+ default:
153
+ return null
105
154
  }
106
155
  }
107
156
 
@@ -231,10 +280,9 @@ export function irRenderTopLevelRust(
231
280
  if (ir.kind === "const") {
232
281
  const rawType = raw?.type as string | undefined
233
282
  const base = rawType ? (primitiveFor(rawType) ?? "String") : constBaseType(ir.value)
234
- return [
235
- `#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]`,
236
- `pub struct ${typeName}(pub ${base});`,
237
- ].join("\n")
283
+ return [`#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]`, `pub struct ${typeName}(pub ${base});`].join(
284
+ "\n",
285
+ )
238
286
  }
239
287
 
240
288
  if (ir.kind === "allOf") {
@@ -396,17 +444,10 @@ export function irRenderTopLevelRust(
396
444
  * Render a schema as a Rust type expression for a struct field position.
397
445
  * Mutates `ctx.decls` when hoisting enums or anonymous structs.
398
446
  */
399
- export function renderUseRust(
400
- schema: Record<string, unknown> | undefined,
401
- ctx: RustRenderUseCtx,
402
- ): string {
447
+ export function renderUseRust(schema: Record<string, unknown> | undefined, ctx: RustRenderUseCtx): string {
403
448
  return irRenderUseRust(schemaToIR(schema), ctx, ctx.depth ?? 0)
404
449
  }
405
450
 
406
- export function renderTopLevelRust(
407
- name: string,
408
- schema: Record<string, unknown>,
409
- decls: Map<string, string>,
410
- ): string {
451
+ export function renderTopLevelRust(name: string, schema: Record<string, unknown>, decls: Map<string, string>): string {
411
452
  return irRenderTopLevelRust(name, schemaToIR(schema), decls, schema)
412
453
  }