@ricsam/quickjs-fetch 0.0.1 → 0.2.1

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 (59) hide show
  1. package/README.md +109 -43
  2. package/dist/cjs/abort-controller.cjs +212 -0
  3. package/dist/cjs/abort-controller.cjs.map +10 -0
  4. package/dist/cjs/fetch.cjs +199 -0
  5. package/dist/cjs/fetch.cjs.map +10 -0
  6. package/dist/cjs/form-data.cjs +289 -0
  7. package/dist/cjs/form-data.cjs.map +10 -0
  8. package/dist/cjs/handle.cjs +248 -0
  9. package/dist/cjs/handle.cjs.map +10 -0
  10. package/dist/cjs/headers.cjs +196 -0
  11. package/dist/cjs/headers.cjs.map +10 -0
  12. package/dist/cjs/index.cjs +47 -0
  13. package/dist/cjs/index.cjs.map +10 -0
  14. package/dist/cjs/package.json +5 -0
  15. package/dist/cjs/request.cjs +315 -0
  16. package/dist/cjs/request.cjs.map +10 -0
  17. package/dist/cjs/response.cjs +315 -0
  18. package/dist/cjs/response.cjs.map +10 -0
  19. package/dist/cjs/serve.cjs +182 -0
  20. package/dist/cjs/serve.cjs.map +10 -0
  21. package/dist/cjs/setup.cjs +119 -0
  22. package/dist/cjs/setup.cjs.map +10 -0
  23. package/dist/cjs/types.cjs +26 -0
  24. package/dist/cjs/types.cjs.map +9 -0
  25. package/dist/mjs/abort-controller.mjs +181 -0
  26. package/dist/mjs/abort-controller.mjs.map +10 -0
  27. package/dist/mjs/fetch.mjs +168 -0
  28. package/dist/mjs/fetch.mjs.map +10 -0
  29. package/dist/mjs/form-data.mjs +258 -0
  30. package/dist/mjs/form-data.mjs.map +10 -0
  31. package/dist/mjs/handle.mjs +217 -0
  32. package/dist/mjs/handle.mjs.map +10 -0
  33. package/dist/mjs/headers.mjs +165 -0
  34. package/dist/mjs/headers.mjs.map +10 -0
  35. package/dist/mjs/index.mjs +22 -0
  36. package/dist/mjs/index.mjs.map +10 -0
  37. package/dist/mjs/package.json +5 -0
  38. package/dist/mjs/request.mjs +284 -0
  39. package/dist/mjs/request.mjs.map +10 -0
  40. package/dist/mjs/response.mjs +284 -0
  41. package/dist/mjs/response.mjs.map +10 -0
  42. package/dist/mjs/serve.mjs +151 -0
  43. package/dist/mjs/serve.mjs.map +10 -0
  44. package/dist/mjs/setup.mjs +92 -0
  45. package/dist/mjs/setup.mjs.map +10 -0
  46. package/dist/mjs/types.mjs +3 -0
  47. package/dist/mjs/types.mjs.map +9 -0
  48. package/dist/types/globals/abort-controller.d.ts +9 -0
  49. package/dist/types/globals/fetch.d.ts +5 -0
  50. package/dist/types/globals/form-data.d.ts +37 -0
  51. package/dist/types/globals/headers.d.ts +36 -0
  52. package/dist/types/globals/request.d.ts +16 -0
  53. package/dist/types/globals/response.d.ts +25 -0
  54. package/dist/types/globals/serve.d.ts +16 -0
  55. package/dist/types/handle.d.ts +6 -0
  56. package/dist/types/index.d.ts +5 -0
  57. package/dist/types/setup.d.ts +48 -0
  58. package/dist/types/types.d.ts +175 -0
  59. package/package.json +51 -6
@@ -0,0 +1,284 @@
1
+ // @bun
2
+ // packages/fetch/src/globals/response.ts
3
+ import { defineClass } from "@ricsam/quickjs-core";
4
+ import { headersStateToNative, createHeadersLike } from "./headers.ts";
5
+ import { parseMultipartFormData, parseUrlEncodedFormData } from "./form-data.ts";
6
+ function createResponseClass(context, stateMap, createStream) {
7
+ const classHandle = defineClass(context, stateMap, {
8
+ name: "Response",
9
+ construct: (args) => {
10
+ const bodyInit = args[0];
11
+ const init = args[1];
12
+ let body = null;
13
+ const status = init?.status ?? 200;
14
+ const statusText = init?.statusText ?? "";
15
+ let headersState = { headers: new Map };
16
+ if (bodyInit !== null && bodyInit !== undefined) {
17
+ if (typeof bodyInit === "string") {
18
+ body = new TextEncoder().encode(bodyInit);
19
+ } else if (bodyInit instanceof ArrayBuffer) {
20
+ body = new Uint8Array(bodyInit);
21
+ } else if (bodyInit instanceof Uint8Array) {
22
+ body = bodyInit;
23
+ } else if (bodyInit && typeof bodyInit === "object" && "parts" in bodyInit) {
24
+ const parts = bodyInit.parts;
25
+ const totalLength = parts.reduce((sum, p) => sum + p.length, 0);
26
+ body = new Uint8Array(totalLength);
27
+ let offset = 0;
28
+ for (const part of parts) {
29
+ body.set(part, offset);
30
+ offset += part.length;
31
+ }
32
+ }
33
+ }
34
+ if (init?.headers) {
35
+ if (init.headers && typeof init.headers === "object" && "headers" in init.headers && init.headers.headers instanceof Map) {
36
+ headersState = {
37
+ headers: new Map(init.headers.headers)
38
+ };
39
+ } else {
40
+ headersState = { headers: new Map };
41
+ for (const [key, value] of Object.entries(init.headers)) {
42
+ headersState.headers.set(key.toLowerCase(), [String(value)]);
43
+ }
44
+ }
45
+ }
46
+ return {
47
+ status,
48
+ statusText,
49
+ headersState,
50
+ body,
51
+ bodyUsed: false,
52
+ url: "",
53
+ redirected: false,
54
+ type: init?._type ?? "default",
55
+ ok: status >= 200 && status < 300
56
+ };
57
+ },
58
+ properties: {
59
+ body: {
60
+ get() {
61
+ if (!this.body)
62
+ return null;
63
+ if (!createStream) {
64
+ return this.body;
65
+ }
66
+ const bodyData = this.body;
67
+ let offset = 0;
68
+ const chunkSize = 65536;
69
+ return createStream({
70
+ pull(controller) {
71
+ if (offset >= bodyData.length) {
72
+ controller.close();
73
+ return;
74
+ }
75
+ const chunk = bodyData.slice(offset, Math.min(offset + chunkSize, bodyData.length));
76
+ offset += chunk.length;
77
+ controller.enqueue(chunk);
78
+ }
79
+ });
80
+ }
81
+ },
82
+ bodyUsed: {
83
+ get() {
84
+ return this.bodyUsed;
85
+ }
86
+ },
87
+ headers: {
88
+ get() {
89
+ return createHeadersLike(this.headersState);
90
+ }
91
+ },
92
+ ok: {
93
+ get() {
94
+ return this.ok;
95
+ }
96
+ },
97
+ redirected: {
98
+ get() {
99
+ return this.redirected;
100
+ }
101
+ },
102
+ status: {
103
+ get() {
104
+ return this.status;
105
+ }
106
+ },
107
+ statusText: {
108
+ get() {
109
+ return this.statusText;
110
+ }
111
+ },
112
+ type: {
113
+ get() {
114
+ return this.type;
115
+ }
116
+ },
117
+ url: {
118
+ get() {
119
+ return this.url;
120
+ }
121
+ }
122
+ },
123
+ methods: {
124
+ async arrayBuffer() {
125
+ if (this.bodyUsed) {
126
+ throw new TypeError("Body has already been consumed");
127
+ }
128
+ this.bodyUsed = true;
129
+ if (!this.body) {
130
+ return new ArrayBuffer(0);
131
+ }
132
+ return this.body.buffer.slice(this.body.byteOffset, this.body.byteOffset + this.body.byteLength);
133
+ },
134
+ async blob() {
135
+ if (this.bodyUsed) {
136
+ throw new TypeError("Body has already been consumed");
137
+ }
138
+ this.bodyUsed = true;
139
+ const contentType = this.headersState.headers.get("content-type")?.[0] || "";
140
+ return {
141
+ parts: this.body ? [this.body] : [],
142
+ type: contentType,
143
+ size: this.body?.length || 0
144
+ };
145
+ },
146
+ clone() {
147
+ if (this.bodyUsed) {
148
+ throw new TypeError("Body has already been consumed");
149
+ }
150
+ return {
151
+ ...this,
152
+ headersState: {
153
+ headers: new Map(this.headersState.headers)
154
+ },
155
+ body: this.body ? new Uint8Array(this.body) : null,
156
+ bodyUsed: false
157
+ };
158
+ },
159
+ async json() {
160
+ if (this.bodyUsed) {
161
+ throw new TypeError("Body has already been consumed");
162
+ }
163
+ this.bodyUsed = true;
164
+ if (!this.body) {
165
+ return JSON.parse("");
166
+ }
167
+ const text = new TextDecoder().decode(this.body);
168
+ return JSON.parse(text);
169
+ },
170
+ async text() {
171
+ if (this.bodyUsed) {
172
+ throw new TypeError("Body has already been consumed");
173
+ }
174
+ this.bodyUsed = true;
175
+ if (!this.body) {
176
+ return "";
177
+ }
178
+ return new TextDecoder().decode(this.body);
179
+ },
180
+ async formData() {
181
+ if (this.bodyUsed) {
182
+ throw new TypeError("Body has already been consumed");
183
+ }
184
+ this.bodyUsed = true;
185
+ if (!this.body) {
186
+ return { entries: [] };
187
+ }
188
+ const contentType = this.headersState.headers.get("content-type")?.[0] || "";
189
+ if (contentType.includes("multipart/form-data")) {
190
+ return parseMultipartFormData(this.body, contentType);
191
+ } else if (contentType.includes("application/x-www-form-urlencoded")) {
192
+ return parseUrlEncodedFormData(this.body);
193
+ }
194
+ throw new TypeError("Could not parse content as FormData");
195
+ }
196
+ }
197
+ });
198
+ return classHandle;
199
+ }
200
+ function addResponseStaticMethods(context) {
201
+ const staticMethodsCode = `
202
+ Response.error = function() {
203
+ return new Response(null, { status: 0, _type: "error" });
204
+ };
205
+
206
+ Response.json = function(data, init = {}) {
207
+ const body = JSON.stringify(data);
208
+ // Merge content-type with any provided headers
209
+ const headers = Object.assign(
210
+ { "content-type": "application/json" },
211
+ init.headers || {}
212
+ );
213
+ return new Response(body, {
214
+ status: init.status ?? 200,
215
+ statusText: init.statusText ?? "",
216
+ headers: headers
217
+ });
218
+ };
219
+
220
+ Response.redirect = function(url, status = 302) {
221
+ return new Response(null, {
222
+ status: status,
223
+ headers: { "location": String(url) }
224
+ });
225
+ };
226
+ `;
227
+ const result = context.evalCode(staticMethodsCode);
228
+ if (result.error) {
229
+ result.error.dispose();
230
+ } else {
231
+ result.value.dispose();
232
+ }
233
+ }
234
+ function responseStateToNative(state) {
235
+ const body = state.body ?? state.body;
236
+ const status = state.status ?? 200;
237
+ const statusText = state.statusText ?? "";
238
+ let headersState;
239
+ if (state.headersState) {
240
+ headersState = state.headersState;
241
+ } else if (state.headers) {
242
+ const headers = state.headers;
243
+ if (headers.headers instanceof Map) {
244
+ headersState = { headers: headers.headers };
245
+ } else {
246
+ headersState = { headers: new Map };
247
+ }
248
+ } else {
249
+ headersState = { headers: new Map };
250
+ }
251
+ return new Response(body, {
252
+ status,
253
+ statusText,
254
+ headers: headersStateToNative(headersState)
255
+ });
256
+ }
257
+ async function createResponseStateFromNative(response) {
258
+ const body = response.body ? new Uint8Array(await response.arrayBuffer()) : null;
259
+ const headersState = { headers: new Map };
260
+ response.headers.forEach((value, key) => {
261
+ const existing = headersState.headers.get(key.toLowerCase()) || [];
262
+ existing.push(value);
263
+ headersState.headers.set(key.toLowerCase(), existing);
264
+ });
265
+ return {
266
+ status: response.status,
267
+ statusText: response.statusText,
268
+ headersState,
269
+ body,
270
+ bodyUsed: false,
271
+ url: response.url,
272
+ redirected: response.redirected,
273
+ type: response.type,
274
+ ok: response.ok
275
+ };
276
+ }
277
+ export {
278
+ responseStateToNative,
279
+ createResponseStateFromNative,
280
+ createResponseClass,
281
+ addResponseStaticMethods
282
+ };
283
+
284
+ //# debugId=EE3E1617102A436164756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/globals/response.ts"],
4
+ "sourcesContent": [
5
+ "import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport type { StateMap } from \"@ricsam/quickjs-core\";\nimport { defineClass } from \"@ricsam/quickjs-core\";\nimport type { ResponseState, HeadersState, FormDataState } from \"../types.ts\";\nimport { headersStateToNative, createHeadersLike } from \"./headers.ts\";\nimport { parseMultipartFormData, parseUrlEncodedFormData } from \"./form-data.ts\";\n\n/**\n * Type for the stream factory function\n */\ntype StreamFactory = (source: UnderlyingSource) => QuickJSHandle;\n\n/**\n * Create the Response class for QuickJS\n */\nexport function createResponseClass(\n context: QuickJSContext,\n stateMap: StateMap,\n createStream?: StreamFactory\n): QuickJSHandle {\n const classHandle = defineClass<ResponseState>(context, stateMap, {\n name: \"Response\",\n construct: (args) => {\n const bodyInit = args[0];\n const init = args[1] as {\n status?: number;\n statusText?: string;\n headers?: object;\n _type?: string; // Internal: for Response.error() to set type\n } | undefined;\n\n let body: Uint8Array | null = null;\n const status = init?.status ?? 200;\n const statusText = init?.statusText ?? \"\";\n let headersState: HeadersState = { headers: new Map() };\n\n // Parse body\n if (bodyInit !== null && bodyInit !== undefined) {\n if (typeof bodyInit === \"string\") {\n body = new TextEncoder().encode(bodyInit);\n } else if (bodyInit instanceof ArrayBuffer) {\n body = new Uint8Array(bodyInit);\n } else if (bodyInit instanceof Uint8Array) {\n body = bodyInit;\n } else if (\n bodyInit &&\n typeof bodyInit === \"object\" &&\n \"parts\" in bodyInit\n ) {\n // Blob-like\n const parts = (bodyInit as { parts: Uint8Array[] }).parts;\n const totalLength = parts.reduce((sum, p) => sum + p.length, 0);\n body = new Uint8Array(totalLength);\n let offset = 0;\n for (const part of parts) {\n body.set(part, offset);\n offset += part.length;\n }\n }\n }\n\n // Parse headers\n if (init?.headers) {\n if (\n init.headers &&\n typeof init.headers === \"object\" &&\n \"headers\" in init.headers &&\n init.headers.headers instanceof Map\n ) {\n headersState = {\n headers: new Map((init.headers as HeadersState).headers),\n };\n } else {\n headersState = { headers: new Map() };\n for (const [key, value] of Object.entries(init.headers)) {\n headersState.headers.set(key.toLowerCase(), [String(value)]);\n }\n }\n }\n\n return {\n status,\n statusText,\n headersState,\n body,\n bodyUsed: false,\n url: \"\",\n redirected: false,\n type: init?._type ?? \"default\",\n ok: status >= 200 && status < 300,\n };\n },\n properties: {\n body: {\n get(this: ResponseState) {\n if (!this.body) return null;\n if (!createStream) {\n // Fallback: return raw body if no stream factory\n return this.body;\n }\n // Create a ReadableStream from the body data\n const bodyData = this.body;\n let offset = 0;\n const chunkSize = 65536; // 64KB chunks\n return createStream({\n pull(controller) {\n if (offset >= bodyData.length) {\n controller.close();\n return;\n }\n const chunk = bodyData.slice(offset, Math.min(offset + chunkSize, bodyData.length));\n offset += chunk.length;\n controller.enqueue(chunk);\n },\n });\n },\n },\n bodyUsed: {\n get(this: ResponseState) {\n return this.bodyUsed;\n },\n },\n headers: {\n get(this: ResponseState) {\n return createHeadersLike(this.headersState);\n },\n },\n ok: {\n get(this: ResponseState) {\n return this.ok;\n },\n },\n redirected: {\n get(this: ResponseState) {\n return this.redirected;\n },\n },\n status: {\n get(this: ResponseState) {\n return this.status;\n },\n },\n statusText: {\n get(this: ResponseState) {\n return this.statusText;\n },\n },\n type: {\n get(this: ResponseState) {\n return this.type;\n },\n },\n url: {\n get(this: ResponseState) {\n return this.url;\n },\n },\n },\n methods: {\n async arrayBuffer(this: ResponseState): Promise<ArrayBuffer> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n if (!this.body) {\n return new ArrayBuffer(0);\n }\n return this.body.buffer.slice(\n this.body.byteOffset,\n this.body.byteOffset + this.body.byteLength\n ) as ArrayBuffer;\n },\n async blob(this: ResponseState): Promise<object> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n const contentType =\n this.headersState.headers.get(\"content-type\")?.[0] || \"\";\n return {\n parts: this.body ? [this.body] : [],\n type: contentType,\n size: this.body?.length || 0,\n };\n },\n clone(this: ResponseState): ResponseState {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n return {\n ...this,\n headersState: {\n headers: new Map(this.headersState.headers),\n },\n body: this.body ? new Uint8Array(this.body) : null,\n bodyUsed: false,\n };\n },\n async json(this: ResponseState): Promise<unknown> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n if (!this.body) {\n return JSON.parse(\"\");\n }\n const text = new TextDecoder().decode(this.body);\n return JSON.parse(text);\n },\n async text(this: ResponseState): Promise<string> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n if (!this.body) {\n return \"\";\n }\n return new TextDecoder().decode(this.body);\n },\n async formData(this: ResponseState): Promise<FormDataState> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n if (!this.body) {\n return { entries: [] };\n }\n\n const contentType = this.headersState.headers.get(\"content-type\")?.[0] || \"\";\n\n if (contentType.includes(\"multipart/form-data\")) {\n return parseMultipartFormData(this.body, contentType);\n } else if (contentType.includes(\"application/x-www-form-urlencoded\")) {\n return parseUrlEncodedFormData(this.body);\n }\n\n throw new TypeError(\"Could not parse content as FormData\");\n },\n },\n });\n\n return classHandle;\n}\n\n/**\n * Add static methods to Response class after it's been set on global\n * This must be called after Response and Headers are available on global\n */\nexport function addResponseStaticMethods(context: QuickJSContext): void {\n const staticMethodsCode = `\n Response.error = function() {\n return new Response(null, { status: 0, _type: \"error\" });\n };\n\n Response.json = function(data, init = {}) {\n const body = JSON.stringify(data);\n // Merge content-type with any provided headers\n const headers = Object.assign(\n { \"content-type\": \"application/json\" },\n init.headers || {}\n );\n return new Response(body, {\n status: init.status ?? 200,\n statusText: init.statusText ?? \"\",\n headers: headers\n });\n };\n\n Response.redirect = function(url, status = 302) {\n return new Response(null, {\n status: status,\n headers: { \"location\": String(url) }\n });\n };\n `;\n const result = context.evalCode(staticMethodsCode);\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n}\n\n/**\n * Convert ResponseState (or unmarshalled Response object) to native Response\n */\nexport function responseStateToNative(state: ResponseState | Record<string, unknown>): Response {\n const body = (state as ResponseState).body ?? (state as Record<string, unknown>).body;\n const status = (state as ResponseState).status ?? 200;\n const statusText = (state as ResponseState).statusText ?? \"\";\n\n // Handle both headersState (internal) and headers (from getter)\n let headersState: HeadersState;\n if ((state as ResponseState).headersState) {\n headersState = (state as ResponseState).headersState;\n } else if ((state as Record<string, unknown>).headers) {\n // When unmarshalled, headers is the HeadersLike object from getter\n const headers = (state as Record<string, unknown>).headers as { headers?: Map<string, string[]> };\n if (headers.headers instanceof Map) {\n headersState = { headers: headers.headers };\n } else {\n headersState = { headers: new Map() };\n }\n } else {\n headersState = { headers: new Map() };\n }\n\n return new Response(body as BodyInit | null, {\n status,\n statusText,\n headers: headersStateToNative(headersState),\n });\n}\n\n/**\n * Create a ResponseState from a native Response\n */\nexport async function createResponseStateFromNative(\n response: Response\n): Promise<ResponseState> {\n const body = response.body\n ? new Uint8Array(await response.arrayBuffer())\n : null;\n\n const headersState: HeadersState = { headers: new Map() };\n response.headers.forEach((value, key) => {\n const existing = headersState.headers.get(key.toLowerCase()) || [];\n existing.push(value);\n headersState.headers.set(key.toLowerCase(), existing);\n });\n\n return {\n status: response.status,\n statusText: response.statusText,\n headersState,\n body,\n bodyUsed: false,\n url: response.url,\n redirected: response.redirected,\n type: response.type,\n ok: response.ok,\n };\n}\n"
6
+ ],
7
+ "mappings": ";;AAEA;AAEA;AACA;AAUO,SAAS,mBAAmB,CACjC,SACA,UACA,cACe;AAAA,EACf,MAAM,cAAc,YAA2B,SAAS,UAAU;AAAA,IAChE,MAAM;AAAA,IACN,WAAW,CAAC,SAAS;AAAA,MACnB,MAAM,WAAW,KAAK;AAAA,MACtB,MAAM,OAAO,KAAK;AAAA,MAOlB,IAAI,OAA0B;AAAA,MAC9B,MAAM,SAAS,MAAM,UAAU;AAAA,MAC/B,MAAM,aAAa,MAAM,cAAc;AAAA,MACvC,IAAI,eAA6B,EAAE,SAAS,IAAI,IAAM;AAAA,MAGtD,IAAI,aAAa,QAAQ,aAAa,WAAW;AAAA,QAC/C,IAAI,OAAO,aAAa,UAAU;AAAA,UAChC,OAAO,IAAI,YAAY,EAAE,OAAO,QAAQ;AAAA,QAC1C,EAAO,SAAI,oBAAoB,aAAa;AAAA,UAC1C,OAAO,IAAI,WAAW,QAAQ;AAAA,QAChC,EAAO,SAAI,oBAAoB,YAAY;AAAA,UACzC,OAAO;AAAA,QACT,EAAO,SACL,YACA,OAAO,aAAa,YACpB,WAAW,UACX;AAAA,UAEA,MAAM,QAAS,SAAqC;AAAA,UACpD,MAAM,cAAc,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC;AAAA,UAC9D,OAAO,IAAI,WAAW,WAAW;AAAA,UACjC,IAAI,SAAS;AAAA,UACb,WAAW,QAAQ,OAAO;AAAA,YACxB,KAAK,IAAI,MAAM,MAAM;AAAA,YACrB,UAAU,KAAK;AAAA,UACjB;AAAA,QACF;AAAA,MACF;AAAA,MAGA,IAAI,MAAM,SAAS;AAAA,QACjB,IACE,KAAK,WACL,OAAO,KAAK,YAAY,YACxB,aAAa,KAAK,WAClB,KAAK,QAAQ,mBAAmB,KAChC;AAAA,UACA,eAAe;AAAA,YACb,SAAS,IAAI,IAAK,KAAK,QAAyB,OAAO;AAAA,UACzD;AAAA,QACF,EAAO;AAAA,UACL,eAAe,EAAE,SAAS,IAAI,IAAM;AAAA,UACpC,YAAY,KAAK,UAAU,OAAO,QAAQ,KAAK,OAAO,GAAG;AAAA,YACvD,aAAa,QAAQ,IAAI,IAAI,YAAY,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC;AAAA,UAC7D;AAAA;AAAA,MAEJ;AAAA,MAEA,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV,KAAK;AAAA,QACL,YAAY;AAAA,QACZ,MAAM,MAAM,SAAS;AAAA,QACrB,IAAI,UAAU,OAAO,SAAS;AAAA,MAChC;AAAA;AAAA,IAEF,YAAY;AAAA,MACV,MAAM;AAAA,QACJ,GAAG,GAAsB;AAAA,UACvB,IAAI,CAAC,KAAK;AAAA,YAAM,OAAO;AAAA,UACvB,IAAI,CAAC,cAAc;AAAA,YAEjB,OAAO,KAAK;AAAA,UACd;AAAA,UAEA,MAAM,WAAW,KAAK;AAAA,UACtB,IAAI,SAAS;AAAA,UACb,MAAM,YAAY;AAAA,UAClB,OAAO,aAAa;AAAA,YAClB,IAAI,CAAC,YAAY;AAAA,cACf,IAAI,UAAU,SAAS,QAAQ;AAAA,gBAC7B,WAAW,MAAM;AAAA,gBACjB;AAAA,cACF;AAAA,cACA,MAAM,QAAQ,SAAS,MAAM,QAAQ,KAAK,IAAI,SAAS,WAAW,SAAS,MAAM,CAAC;AAAA,cAClF,UAAU,MAAM;AAAA,cAChB,WAAW,QAAQ,KAAK;AAAA;AAAA,UAE5B,CAAC;AAAA;AAAA,MAEL;AAAA,MACA,UAAU;AAAA,QACR,GAAG,GAAsB;AAAA,UACvB,OAAO,KAAK;AAAA;AAAA,MAEhB;AAAA,MACA,SAAS;AAAA,QACP,GAAG,GAAsB;AAAA,UACvB,OAAO,kBAAkB,KAAK,YAAY;AAAA;AAAA,MAE9C;AAAA,MACA,IAAI;AAAA,QACF,GAAG,GAAsB;AAAA,UACvB,OAAO,KAAK;AAAA;AAAA,MAEhB;AAAA,MACA,YAAY;AAAA,QACV,GAAG,GAAsB;AAAA,UACvB,OAAO,KAAK;AAAA;AAAA,MAEhB;AAAA,MACA,QAAQ;AAAA,QACN,GAAG,GAAsB;AAAA,UACvB,OAAO,KAAK;AAAA;AAAA,MAEhB;AAAA,MACA,YAAY;AAAA,QACV,GAAG,GAAsB;AAAA,UACvB,OAAO,KAAK;AAAA;AAAA,MAEhB;AAAA,MACA,MAAM;AAAA,QACJ,GAAG,GAAsB;AAAA,UACvB,OAAO,KAAK;AAAA;AAAA,MAEhB;AAAA,MACA,KAAK;AAAA,QACH,GAAG,GAAsB;AAAA,UACvB,OAAO,KAAK;AAAA;AAAA,MAEhB;AAAA,IACF;AAAA,IACA,SAAS;AAAA,WACD,YAAW,GAA4C;AAAA,QAC3D,IAAI,KAAK,UAAU;AAAA,UACjB,MAAM,IAAI,UAAU,gCAAgC;AAAA,QACtD;AAAA,QACA,KAAK,WAAW;AAAA,QAChB,IAAI,CAAC,KAAK,MAAM;AAAA,UACd,OAAO,IAAI,YAAY,CAAC;AAAA,QAC1B;AAAA,QACA,OAAO,KAAK,KAAK,OAAO,MACtB,KAAK,KAAK,YACV,KAAK,KAAK,aAAa,KAAK,KAAK,UACnC;AAAA;AAAA,WAEI,KAAI,GAAuC;AAAA,QAC/C,IAAI,KAAK,UAAU;AAAA,UACjB,MAAM,IAAI,UAAU,gCAAgC;AAAA,QACtD;AAAA,QACA,KAAK,WAAW;AAAA,QAChB,MAAM,cACJ,KAAK,aAAa,QAAQ,IAAI,cAAc,IAAI,MAAM;AAAA,QACxD,OAAO;AAAA,UACL,OAAO,KAAK,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC;AAAA,UAClC,MAAM;AAAA,UACN,MAAM,KAAK,MAAM,UAAU;AAAA,QAC7B;AAAA;AAAA,MAEF,KAAK,GAAqC;AAAA,QACxC,IAAI,KAAK,UAAU;AAAA,UACjB,MAAM,IAAI,UAAU,gCAAgC;AAAA,QACtD;AAAA,QACA,OAAO;AAAA,aACF;AAAA,UACH,cAAc;AAAA,YACZ,SAAS,IAAI,IAAI,KAAK,aAAa,OAAO;AAAA,UAC5C;AAAA,UACA,MAAM,KAAK,OAAO,IAAI,WAAW,KAAK,IAAI,IAAI;AAAA,UAC9C,UAAU;AAAA,QACZ;AAAA;AAAA,WAEI,KAAI,GAAwC;AAAA,QAChD,IAAI,KAAK,UAAU;AAAA,UACjB,MAAM,IAAI,UAAU,gCAAgC;AAAA,QACtD;AAAA,QACA,KAAK,WAAW;AAAA,QAChB,IAAI,CAAC,KAAK,MAAM;AAAA,UACd,OAAO,KAAK,MAAM,EAAE;AAAA,QACtB;AAAA,QACA,MAAM,OAAO,IAAI,YAAY,EAAE,OAAO,KAAK,IAAI;AAAA,QAC/C,OAAO,KAAK,MAAM,IAAI;AAAA;AAAA,WAElB,KAAI,GAAuC;AAAA,QAC/C,IAAI,KAAK,UAAU;AAAA,UACjB,MAAM,IAAI,UAAU,gCAAgC;AAAA,QACtD;AAAA,QACA,KAAK,WAAW;AAAA,QAChB,IAAI,CAAC,KAAK,MAAM;AAAA,UACd,OAAO;AAAA,QACT;AAAA,QACA,OAAO,IAAI,YAAY,EAAE,OAAO,KAAK,IAAI;AAAA;AAAA,WAErC,SAAQ,GAA8C;AAAA,QAC1D,IAAI,KAAK,UAAU;AAAA,UACjB,MAAM,IAAI,UAAU,gCAAgC;AAAA,QACtD;AAAA,QACA,KAAK,WAAW;AAAA,QAChB,IAAI,CAAC,KAAK,MAAM;AAAA,UACd,OAAO,EAAE,SAAS,CAAC,EAAE;AAAA,QACvB;AAAA,QAEA,MAAM,cAAc,KAAK,aAAa,QAAQ,IAAI,cAAc,IAAI,MAAM;AAAA,QAE1E,IAAI,YAAY,SAAS,qBAAqB,GAAG;AAAA,UAC/C,OAAO,uBAAuB,KAAK,MAAM,WAAW;AAAA,QACtD,EAAO,SAAI,YAAY,SAAS,mCAAmC,GAAG;AAAA,UACpE,OAAO,wBAAwB,KAAK,IAAI;AAAA,QAC1C;AAAA,QAEA,MAAM,IAAI,UAAU,qCAAqC;AAAA;AAAA,IAE7D;AAAA,EACF,CAAC;AAAA,EAED,OAAO;AAAA;AAOF,SAAS,wBAAwB,CAAC,SAA+B;AAAA,EACtE,MAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0B1B,MAAM,SAAS,QAAQ,SAAS,iBAAiB;AAAA,EACjD,IAAI,OAAO,OAAO;AAAA,IAChB,OAAO,MAAM,QAAQ;AAAA,EACvB,EAAO;AAAA,IACL,OAAO,MAAM,QAAQ;AAAA;AAAA;AAOlB,SAAS,qBAAqB,CAAC,OAA0D;AAAA,EAC9F,MAAM,OAAQ,MAAwB,QAAS,MAAkC;AAAA,EACjF,MAAM,SAAU,MAAwB,UAAU;AAAA,EAClD,MAAM,aAAc,MAAwB,cAAc;AAAA,EAG1D,IAAI;AAAA,EACJ,IAAK,MAAwB,cAAc;AAAA,IACzC,eAAgB,MAAwB;AAAA,EAC1C,EAAO,SAAK,MAAkC,SAAS;AAAA,IAErD,MAAM,UAAW,MAAkC;AAAA,IACnD,IAAI,QAAQ,mBAAmB,KAAK;AAAA,MAClC,eAAe,EAAE,SAAS,QAAQ,QAAQ;AAAA,IAC5C,EAAO;AAAA,MACL,eAAe,EAAE,SAAS,IAAI,IAAM;AAAA;AAAA,EAExC,EAAO;AAAA,IACL,eAAe,EAAE,SAAS,IAAI,IAAM;AAAA;AAAA,EAGtC,OAAO,IAAI,SAAS,MAAyB;AAAA,IAC3C;AAAA,IACA;AAAA,IACA,SAAS,qBAAqB,YAAY;AAAA,EAC5C,CAAC;AAAA;AAMH,eAAsB,6BAA6B,CACjD,UACwB;AAAA,EACxB,MAAM,OAAO,SAAS,OAClB,IAAI,WAAW,MAAM,SAAS,YAAY,CAAC,IAC3C;AAAA,EAEJ,MAAM,eAA6B,EAAE,SAAS,IAAI,IAAM;AAAA,EACxD,SAAS,QAAQ,QAAQ,CAAC,OAAO,QAAQ;AAAA,IACvC,MAAM,WAAW,aAAa,QAAQ,IAAI,IAAI,YAAY,CAAC,KAAK,CAAC;AAAA,IACjE,SAAS,KAAK,KAAK;AAAA,IACnB,aAAa,QAAQ,IAAI,IAAI,YAAY,GAAG,QAAQ;AAAA,GACrD;AAAA,EAED,OAAO;AAAA,IACL,QAAQ,SAAS;AAAA,IACjB,YAAY,SAAS;AAAA,IACrB;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,KAAK,SAAS;AAAA,IACd,YAAY,SAAS;AAAA,IACrB,MAAM,SAAS;AAAA,IACf,IAAI,SAAS;AAAA,EACf;AAAA;",
8
+ "debugId": "EE3E1617102A436164756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,151 @@
1
+ // @bun
2
+ // packages/fetch/src/globals/serve.ts
3
+ import { defineClass } from "@ricsam/quickjs-core";
4
+ function createServerWebSocketClass(context, stateMap, onWebSocketCommand) {
5
+ return defineClass(context, stateMap, {
6
+ name: "ServerWebSocket",
7
+ construct: (args) => {
8
+ const connectionId = String(args[0]);
9
+ const data = args[1];
10
+ return {
11
+ data,
12
+ readyState: 1,
13
+ connectionId,
14
+ wsHandle: null
15
+ };
16
+ },
17
+ properties: {
18
+ data: {
19
+ get() {
20
+ return this.data;
21
+ }
22
+ },
23
+ readyState: {
24
+ get() {
25
+ return this.readyState;
26
+ }
27
+ }
28
+ },
29
+ methods: {
30
+ send(message) {
31
+ if (this.readyState !== 1) {
32
+ throw new Error("WebSocket is not open");
33
+ }
34
+ let data;
35
+ if (typeof message === "string") {
36
+ data = message;
37
+ } else if (message instanceof ArrayBuffer) {
38
+ data = message;
39
+ } else if (message instanceof Uint8Array) {
40
+ data = message.buffer.slice(message.byteOffset, message.byteOffset + message.byteLength);
41
+ } else {
42
+ data = String(message);
43
+ }
44
+ onWebSocketCommand({
45
+ type: "message",
46
+ connectionId: this.connectionId,
47
+ data
48
+ });
49
+ },
50
+ close(code, reason) {
51
+ if (this.readyState === 3) {
52
+ return;
53
+ }
54
+ this.readyState = 2;
55
+ onWebSocketCommand({
56
+ type: "close",
57
+ connectionId: this.connectionId,
58
+ code: typeof code === "number" ? code : undefined,
59
+ reason: typeof reason === "string" ? reason : undefined
60
+ });
61
+ }
62
+ }
63
+ });
64
+ }
65
+ function createServerClass(context, stateMap, serveState) {
66
+ return defineClass(context, stateMap, {
67
+ name: "Server",
68
+ construct: () => {
69
+ return { serveState };
70
+ },
71
+ methods: {
72
+ upgrade(_request, options) {
73
+ const data = options && typeof options === "object" && "data" in options ? options.data : undefined;
74
+ this.serveState.pendingUpgrade = {
75
+ requested: true,
76
+ data
77
+ };
78
+ return true;
79
+ }
80
+ }
81
+ });
82
+ }
83
+ function createServeFunction(context, stateMap, serveState) {
84
+ return context.newFunction("serve", (optionsHandle) => {
85
+ if (context.typeof(optionsHandle) !== "object") {
86
+ throw context.newError("serve requires an options object");
87
+ }
88
+ if (serveState.fetchHandler) {
89
+ serveState.fetchHandler.dispose();
90
+ serveState.fetchHandler = null;
91
+ }
92
+ const fetchHandle = context.getProp(optionsHandle, "fetch");
93
+ if (context.typeof(fetchHandle) === "function") {
94
+ serveState.fetchHandler = fetchHandle;
95
+ } else {
96
+ fetchHandle.dispose();
97
+ }
98
+ const websocketHandle = context.getProp(optionsHandle, "websocket");
99
+ if (context.typeof(websocketHandle) === "object") {
100
+ if (serveState.websocketHandlers.open) {
101
+ serveState.websocketHandlers.open.dispose();
102
+ serveState.websocketHandlers.open = undefined;
103
+ }
104
+ if (serveState.websocketHandlers.message) {
105
+ serveState.websocketHandlers.message.dispose();
106
+ serveState.websocketHandlers.message = undefined;
107
+ }
108
+ if (serveState.websocketHandlers.close) {
109
+ serveState.websocketHandlers.close.dispose();
110
+ serveState.websocketHandlers.close = undefined;
111
+ }
112
+ if (serveState.websocketHandlers.error) {
113
+ serveState.websocketHandlers.error.dispose();
114
+ serveState.websocketHandlers.error = undefined;
115
+ }
116
+ const openHandle = context.getProp(websocketHandle, "open");
117
+ if (context.typeof(openHandle) === "function") {
118
+ serveState.websocketHandlers.open = openHandle;
119
+ } else {
120
+ openHandle.dispose();
121
+ }
122
+ const messageHandle = context.getProp(websocketHandle, "message");
123
+ if (context.typeof(messageHandle) === "function") {
124
+ serveState.websocketHandlers.message = messageHandle;
125
+ } else {
126
+ messageHandle.dispose();
127
+ }
128
+ const closeHandle = context.getProp(websocketHandle, "close");
129
+ if (context.typeof(closeHandle) === "function") {
130
+ serveState.websocketHandlers.close = closeHandle;
131
+ } else {
132
+ closeHandle.dispose();
133
+ }
134
+ const errorHandle = context.getProp(websocketHandle, "error");
135
+ if (context.typeof(errorHandle) === "function") {
136
+ serveState.websocketHandlers.error = errorHandle;
137
+ } else {
138
+ errorHandle.dispose();
139
+ }
140
+ }
141
+ websocketHandle.dispose();
142
+ return context.undefined;
143
+ });
144
+ }
145
+ export {
146
+ createServerWebSocketClass,
147
+ createServerClass,
148
+ createServeFunction
149
+ };
150
+
151
+ //# debugId=273172F2CE17BE7964756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/globals/serve.ts"],
4
+ "sourcesContent": [
5
+ "import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport type { StateMap } from \"@ricsam/quickjs-core\";\nimport { defineClass } from \"@ricsam/quickjs-core\";\nimport type { ServeState, ServerWebSocketState, WebSocketCommand } from \"../types.ts\";\n\ninterface ServerState {\n serveState: ServeState;\n}\n\n/**\n * Create the ServerWebSocket class for QuickJS\n */\nexport function createServerWebSocketClass(\n context: QuickJSContext,\n stateMap: StateMap,\n onWebSocketCommand: (cmd: WebSocketCommand) => void\n): QuickJSHandle {\n return defineClass<ServerWebSocketState>(context, stateMap, {\n name: \"ServerWebSocket\",\n construct: (args) => {\n const connectionId = String(args[0]);\n const data = args[1];\n\n return {\n data,\n readyState: 1, // OPEN\n connectionId,\n wsHandle: null,\n };\n },\n properties: {\n data: {\n get(this: ServerWebSocketState) {\n return this.data;\n },\n },\n readyState: {\n get(this: ServerWebSocketState) {\n return this.readyState;\n },\n },\n },\n methods: {\n send(this: ServerWebSocketState, message: unknown) {\n if (this.readyState !== 1) {\n throw new Error(\"WebSocket is not open\");\n }\n\n let data: string | ArrayBuffer;\n if (typeof message === \"string\") {\n data = message;\n } else if (message instanceof ArrayBuffer) {\n data = message;\n } else if (message instanceof Uint8Array) {\n data = message.buffer.slice(\n message.byteOffset,\n message.byteOffset + message.byteLength\n ) as ArrayBuffer;\n } else {\n data = String(message);\n }\n\n onWebSocketCommand({\n type: \"message\",\n connectionId: this.connectionId,\n data,\n });\n },\n close(this: ServerWebSocketState, code?: unknown, reason?: unknown) {\n if (this.readyState === 3) {\n return; // Already closed\n }\n\n this.readyState = 2; // CLOSING\n\n onWebSocketCommand({\n type: \"close\",\n connectionId: this.connectionId,\n code: typeof code === \"number\" ? code : undefined,\n reason: typeof reason === \"string\" ? reason : undefined,\n });\n },\n },\n });\n}\n\n/**\n * Create the Server class for QuickJS\n */\nexport function createServerClass(\n context: QuickJSContext,\n stateMap: StateMap,\n serveState: ServeState\n): QuickJSHandle {\n return defineClass<ServerState>(context, stateMap, {\n name: \"Server\",\n construct: () => {\n return { serveState };\n },\n methods: {\n upgrade(\n this: ServerState,\n _request: unknown,\n options?: unknown\n ): boolean {\n const data =\n options && typeof options === \"object\" && \"data\" in options\n ? (options as { data: unknown }).data\n : undefined;\n\n this.serveState.pendingUpgrade = {\n requested: true,\n data,\n };\n\n return true;\n },\n },\n });\n}\n\n/**\n * Create the serve function for QuickJS\n * Uses context.newFunction instead of defineFunction to properly manage handle lifetimes\n */\nexport function createServeFunction(\n context: QuickJSContext,\n stateMap: StateMap,\n serveState: ServeState\n): QuickJSHandle {\n return context.newFunction(\"serve\", (optionsHandle) => {\n if (context.typeof(optionsHandle) !== \"object\") {\n throw context.newError(\"serve requires an options object\");\n }\n\n // Dispose previous handlers if any\n if (serveState.fetchHandler) {\n serveState.fetchHandler.dispose();\n serveState.fetchHandler = null;\n }\n\n // Get and store fetch handler - don't dispose, keep alive for later calls\n const fetchHandle = context.getProp(optionsHandle, \"fetch\");\n if (context.typeof(fetchHandle) === \"function\") {\n serveState.fetchHandler = fetchHandle;\n } else {\n fetchHandle.dispose();\n }\n\n // Get and store websocket handlers\n const websocketHandle = context.getProp(optionsHandle, \"websocket\");\n if (context.typeof(websocketHandle) === \"object\") {\n // Dispose previous handlers\n if (serveState.websocketHandlers.open) {\n serveState.websocketHandlers.open.dispose();\n serveState.websocketHandlers.open = undefined;\n }\n if (serveState.websocketHandlers.message) {\n serveState.websocketHandlers.message.dispose();\n serveState.websocketHandlers.message = undefined;\n }\n if (serveState.websocketHandlers.close) {\n serveState.websocketHandlers.close.dispose();\n serveState.websocketHandlers.close = undefined;\n }\n if (serveState.websocketHandlers.error) {\n serveState.websocketHandlers.error.dispose();\n serveState.websocketHandlers.error = undefined;\n }\n\n const openHandle = context.getProp(websocketHandle, \"open\");\n if (context.typeof(openHandle) === \"function\") {\n serveState.websocketHandlers.open = openHandle;\n } else {\n openHandle.dispose();\n }\n\n const messageHandle = context.getProp(websocketHandle, \"message\");\n if (context.typeof(messageHandle) === \"function\") {\n serveState.websocketHandlers.message = messageHandle;\n } else {\n messageHandle.dispose();\n }\n\n const closeHandle = context.getProp(websocketHandle, \"close\");\n if (context.typeof(closeHandle) === \"function\") {\n serveState.websocketHandlers.close = closeHandle;\n } else {\n closeHandle.dispose();\n }\n\n const errorHandle = context.getProp(websocketHandle, \"error\");\n if (context.typeof(errorHandle) === \"function\") {\n serveState.websocketHandlers.error = errorHandle;\n } else {\n errorHandle.dispose();\n }\n }\n websocketHandle.dispose();\n\n return context.undefined;\n });\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "273172F2CE17BE7964756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,92 @@
1
+ // @bun
2
+ // packages/fetch/src/setup.ts
3
+ import { setupCore, createReadableStream } from "@ricsam/quickjs-core";
4
+ import { createHeadersClass } from "./globals/headers.ts";
5
+ import { createRequestClass } from "./globals/request.ts";
6
+ import { createResponseClass, addResponseStaticMethods } from "./globals/response.ts";
7
+ import { setupAbortControllerAndSignal } from "./globals/abort-controller.ts";
8
+ import { createFormDataClass } from "./globals/form-data.ts";
9
+ import { createFetchFunction } from "./globals/fetch.ts";
10
+ import {
11
+ createServeFunction,
12
+ createServerClass,
13
+ createServerWebSocketClass
14
+ } from "./globals/serve.ts";
15
+ import { createFetchHandle } from "./handle.ts";
16
+ function setupFetch(context, options = {}) {
17
+ const coreHandle = options.coreHandle ?? setupCore(context, {
18
+ stateMap: options.stateMap
19
+ });
20
+ const stateMap = options.stateMap ?? coreHandle.stateMap;
21
+ const serveState = {
22
+ fetchHandler: null,
23
+ websocketHandlers: {},
24
+ pendingUpgrade: null,
25
+ activeConnections: new Map
26
+ };
27
+ const wsCommandCallbacks = new Set;
28
+ const dispatchWsCommand = (cmd) => {
29
+ for (const cb of wsCommandCallbacks) {
30
+ cb(cmd);
31
+ }
32
+ };
33
+ const streamFactory = (source) => createReadableStream(context, stateMap, source);
34
+ const HeadersClass = createHeadersClass(context, stateMap);
35
+ context.setProp(context.global, "Headers", HeadersClass);
36
+ HeadersClass.dispose();
37
+ const iteratorResult = context.evalCode(`
38
+ Headers.prototype[Symbol.iterator] = function() {
39
+ return this.entries()[Symbol.iterator]();
40
+ };
41
+ `);
42
+ if (iteratorResult.error) {
43
+ iteratorResult.error.dispose();
44
+ } else {
45
+ iteratorResult.value.dispose();
46
+ }
47
+ const RequestClass = createRequestClass(context, stateMap, streamFactory);
48
+ context.setProp(context.global, "Request", RequestClass);
49
+ RequestClass.dispose();
50
+ const ResponseClass = createResponseClass(context, stateMap, streamFactory);
51
+ context.setProp(context.global, "Response", ResponseClass);
52
+ ResponseClass.dispose();
53
+ addResponseStaticMethods(context);
54
+ setupAbortControllerAndSignal(context);
55
+ const FormDataClass = createFormDataClass(context, stateMap);
56
+ context.setProp(context.global, "FormData", FormDataClass);
57
+ FormDataClass.dispose();
58
+ const formDataIteratorResult = context.evalCode(`
59
+ FormData.prototype[Symbol.iterator] = function() {
60
+ return this.entries()[Symbol.iterator]();
61
+ };
62
+ `);
63
+ if (formDataIteratorResult.error) {
64
+ formDataIteratorResult.error.dispose();
65
+ } else {
66
+ formDataIteratorResult.value.dispose();
67
+ }
68
+ const ServerWebSocketClass = createServerWebSocketClass(context, stateMap, dispatchWsCommand);
69
+ context.setProp(context.global, "__ServerWebSocket__", ServerWebSocketClass);
70
+ ServerWebSocketClass.dispose();
71
+ const ServerClass = createServerClass(context, stateMap, serveState);
72
+ context.setProp(context.global, "__Server__", ServerClass);
73
+ ServerClass.dispose();
74
+ const fetchFn = createFetchFunction(context, options.onFetch);
75
+ context.setProp(context.global, "fetch", fetchFn);
76
+ fetchFn.dispose();
77
+ const serveFn = createServeFunction(context, stateMap, serveState);
78
+ context.setProp(context.global, "serve", serveFn);
79
+ serveFn.dispose();
80
+ const fetchHandle = createFetchHandle(context, stateMap, serveState);
81
+ const originalOnWebSocketCommand = fetchHandle.onWebSocketCommand;
82
+ fetchHandle.onWebSocketCommand = (callback) => {
83
+ wsCommandCallbacks.add(callback);
84
+ return () => wsCommandCallbacks.delete(callback);
85
+ };
86
+ return fetchHandle;
87
+ }
88
+ export {
89
+ setupFetch
90
+ };
91
+
92
+ //# debugId=D36BEC990F9352E664756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/setup.ts"],
4
+ "sourcesContent": [
5
+ "import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { setupCore, createStateMap, createReadableStream } from \"@ricsam/quickjs-core\";\nimport type { SetupFetchOptions, FetchHandle, ServeState } from \"./types.ts\";\nimport { createHeadersClass } from \"./globals/headers.ts\";\nimport { createRequestClass } from \"./globals/request.ts\";\nimport { createResponseClass, addResponseStaticMethods } from \"./globals/response.ts\";\nimport { setupAbortControllerAndSignal } from \"./globals/abort-controller.ts\";\nimport { createFormDataClass } from \"./globals/form-data.ts\";\nimport { createFetchFunction } from \"./globals/fetch.ts\";\nimport {\n createServeFunction,\n createServerClass,\n createServerWebSocketClass,\n} from \"./globals/serve.ts\";\nimport { createFetchHandle } from \"./handle.ts\";\n\n/**\n * Setup Fetch API in a QuickJS context\n *\n * Injects the following globals:\n * - fetch\n * - Request\n * - Response\n * - Headers\n * - AbortController\n * - AbortSignal\n * - serve\n * - FormData\n *\n * Also sets up Core APIs (Streams, Blob, File) if not already present.\n *\n * **Private globals (internal use):**\n * - `__Server__` - Server class for serve() handler, instantiated via evalCode\n * - `__ServerWebSocket__` - WebSocket class for connection handling\n * - `__scheduleTimeout__` - Host function to schedule AbortSignal.timeout()\n * - `__checkTimeout__` - Host function to check if timeout elapsed\n *\n * These private globals follow the `__Name__` convention and are required for\n * JavaScript code in QuickJS to create class instances via evalCode.\n * See PATTERNS.md section 5 for details.\n *\n * @example\n * const handle = setupFetch(context, {\n * onFetch: async (request) => {\n * // Proxy to real fetch\n * return fetch(request);\n * }\n * });\n *\n * context.evalCode(`\n * serve({\n * fetch(request, server) {\n * return new Response(\"Hello!\");\n * }\n * });\n * `);\n *\n * const response = await handle.dispatchRequest(\n * new Request(\"http://localhost/\")\n * );\n */\nexport function setupFetch(\n context: QuickJSContext,\n options: SetupFetchOptions = {}\n): FetchHandle {\n // Setup core if not already done\n const coreHandle =\n options.coreHandle ??\n setupCore(context, {\n stateMap: options.stateMap,\n });\n\n const stateMap = options.stateMap ?? coreHandle.stateMap;\n\n // Create serve state\n const serveState: ServeState = {\n fetchHandler: null,\n websocketHandlers: {},\n pendingUpgrade: null,\n activeConnections: new Map(),\n };\n\n // WebSocket command dispatcher\n const wsCommandCallbacks = new Set<\n (cmd: import(\"./types.ts\").WebSocketCommand) => void\n >();\n const dispatchWsCommand = (cmd: import(\"./types.ts\").WebSocketCommand) => {\n for (const cb of wsCommandCallbacks) {\n cb(cmd);\n }\n };\n\n // Create stream factory for Request/Response body\n const streamFactory = (source: UnderlyingSource) =>\n createReadableStream(context, stateMap, source);\n\n // Create Headers class\n const HeadersClass = createHeadersClass(context, stateMap);\n context.setProp(context.global, \"Headers\", HeadersClass);\n HeadersClass.dispose();\n\n // Add Symbol.iterator support for Headers (for...of, Array.from, spread)\n const iteratorResult = context.evalCode(`\n Headers.prototype[Symbol.iterator] = function() {\n return this.entries()[Symbol.iterator]();\n };\n `);\n if (iteratorResult.error) {\n iteratorResult.error.dispose();\n } else {\n iteratorResult.value.dispose();\n }\n\n // Create Request class\n const RequestClass = createRequestClass(context, stateMap, streamFactory);\n context.setProp(context.global, \"Request\", RequestClass);\n RequestClass.dispose();\n\n // Create Response class\n const ResponseClass = createResponseClass(context, stateMap, streamFactory);\n context.setProp(context.global, \"Response\", ResponseClass);\n ResponseClass.dispose();\n\n // Add Response static methods (must be after Response and Headers are on global)\n addResponseStaticMethods(context);\n\n // Create AbortSignal and AbortController classes (pure QuickJS implementation)\n setupAbortControllerAndSignal(context);\n\n // Create FormData class\n const FormDataClass = createFormDataClass(context, stateMap);\n context.setProp(context.global, \"FormData\", FormDataClass);\n FormDataClass.dispose();\n\n // Add Symbol.iterator support for FormData (for...of, Array.from, spread)\n const formDataIteratorResult = context.evalCode(`\n FormData.prototype[Symbol.iterator] = function() {\n return this.entries()[Symbol.iterator]();\n };\n `);\n if (formDataIteratorResult.error) {\n formDataIteratorResult.error.dispose();\n } else {\n formDataIteratorResult.value.dispose();\n }\n\n // Create ServerWebSocket class (internal, for WebSocket handling)\n const ServerWebSocketClass = createServerWebSocketClass(\n context,\n stateMap,\n dispatchWsCommand\n );\n // Set on global with internal name so we can use evalCode to instantiate\n context.setProp(context.global, \"__ServerWebSocket__\", ServerWebSocketClass);\n ServerWebSocketClass.dispose();\n // Note: ServerWebSocketClass handle is now owned by global\n\n // Create Server class (internal, passed to fetch handler)\n const ServerClass = createServerClass(context, stateMap, serveState);\n // Set on global with internal name so we can use evalCode to instantiate\n context.setProp(context.global, \"__Server__\", ServerClass);\n ServerClass.dispose();\n // Note: ServerClass handle is now owned by global\n\n // Create fetch function\n const fetchFn = createFetchFunction(context, options.onFetch);\n context.setProp(context.global, \"fetch\", fetchFn);\n fetchFn.dispose();\n\n // Create serve function\n const serveFn = createServeFunction(context, stateMap, serveState);\n context.setProp(context.global, \"serve\", serveFn);\n serveFn.dispose();\n\n // Create and return the handle\n const fetchHandle = createFetchHandle(\n context,\n stateMap,\n serveState\n );\n\n // Wire up WebSocket command callbacks\n const originalOnWebSocketCommand = fetchHandle.onWebSocketCommand;\n fetchHandle.onWebSocketCommand = (callback) => {\n wsCommandCallbacks.add(callback);\n return () => wsCommandCallbacks.delete(callback);\n };\n\n return fetchHandle;\n}\n"
6
+ ],
7
+ "mappings": ";;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAKA;AA+CO,SAAS,UAAU,CACxB,SACA,UAA6B,CAAC,GACjB;AAAA,EAEb,MAAM,aACJ,QAAQ,cACR,UAAU,SAAS;AAAA,IACjB,UAAU,QAAQ;AAAA,EACpB,CAAC;AAAA,EAEH,MAAM,WAAW,QAAQ,YAAY,WAAW;AAAA,EAGhD,MAAM,aAAyB;AAAA,IAC7B,cAAc;AAAA,IACd,mBAAmB,CAAC;AAAA,IACpB,gBAAgB;AAAA,IAChB,mBAAmB,IAAI;AAAA,EACzB;AAAA,EAGA,MAAM,qBAAqB,IAAI;AAAA,EAG/B,MAAM,oBAAoB,CAAC,QAA+C;AAAA,IACxE,WAAW,MAAM,oBAAoB;AAAA,MACnC,GAAG,GAAG;AAAA,IACR;AAAA;AAAA,EAIF,MAAM,gBAAgB,CAAC,WACrB,qBAAqB,SAAS,UAAU,MAAM;AAAA,EAGhD,MAAM,eAAe,mBAAmB,SAAS,QAAQ;AAAA,EACzD,QAAQ,QAAQ,QAAQ,QAAQ,WAAW,YAAY;AAAA,EACvD,aAAa,QAAQ;AAAA,EAGrB,MAAM,iBAAiB,QAAQ,SAAS;AAAA;AAAA;AAAA;AAAA,GAIvC;AAAA,EACD,IAAI,eAAe,OAAO;AAAA,IACxB,eAAe,MAAM,QAAQ;AAAA,EAC/B,EAAO;AAAA,IACL,eAAe,MAAM,QAAQ;AAAA;AAAA,EAI/B,MAAM,eAAe,mBAAmB,SAAS,UAAU,aAAa;AAAA,EACxE,QAAQ,QAAQ,QAAQ,QAAQ,WAAW,YAAY;AAAA,EACvD,aAAa,QAAQ;AAAA,EAGrB,MAAM,gBAAgB,oBAAoB,SAAS,UAAU,aAAa;AAAA,EAC1E,QAAQ,QAAQ,QAAQ,QAAQ,YAAY,aAAa;AAAA,EACzD,cAAc,QAAQ;AAAA,EAGtB,yBAAyB,OAAO;AAAA,EAGhC,8BAA8B,OAAO;AAAA,EAGrC,MAAM,gBAAgB,oBAAoB,SAAS,QAAQ;AAAA,EAC3D,QAAQ,QAAQ,QAAQ,QAAQ,YAAY,aAAa;AAAA,EACzD,cAAc,QAAQ;AAAA,EAGtB,MAAM,yBAAyB,QAAQ,SAAS;AAAA;AAAA;AAAA;AAAA,GAI/C;AAAA,EACD,IAAI,uBAAuB,OAAO;AAAA,IAChC,uBAAuB,MAAM,QAAQ;AAAA,EACvC,EAAO;AAAA,IACL,uBAAuB,MAAM,QAAQ;AAAA;AAAA,EAIvC,MAAM,uBAAuB,2BAC3B,SACA,UACA,iBACF;AAAA,EAEA,QAAQ,QAAQ,QAAQ,QAAQ,uBAAuB,oBAAoB;AAAA,EAC3E,qBAAqB,QAAQ;AAAA,EAI7B,MAAM,cAAc,kBAAkB,SAAS,UAAU,UAAU;AAAA,EAEnE,QAAQ,QAAQ,QAAQ,QAAQ,cAAc,WAAW;AAAA,EACzD,YAAY,QAAQ;AAAA,EAIpB,MAAM,UAAU,oBAAoB,SAAS,QAAQ,OAAO;AAAA,EAC5D,QAAQ,QAAQ,QAAQ,QAAQ,SAAS,OAAO;AAAA,EAChD,QAAQ,QAAQ;AAAA,EAGhB,MAAM,UAAU,oBAAoB,SAAS,UAAU,UAAU;AAAA,EACjE,QAAQ,QAAQ,QAAQ,QAAQ,SAAS,OAAO;AAAA,EAChD,QAAQ,QAAQ;AAAA,EAGhB,MAAM,cAAc,kBAClB,SACA,UACA,UACF;AAAA,EAGA,MAAM,6BAA6B,YAAY;AAAA,EAC/C,YAAY,qBAAqB,CAAC,aAAa;AAAA,IAC7C,mBAAmB,IAAI,QAAQ;AAAA,IAC/B,OAAO,MAAM,mBAAmB,OAAO,QAAQ;AAAA;AAAA,EAGjD,OAAO;AAAA;",
8
+ "debugId": "D36BEC990F9352E664756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,3 @@
1
+ // @bun
2
+
3
+ //# debugId=9A9583F2B3A7714664756E2164756E21
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "9A9583F2B3A7714664756E2164756E21",
8
+ "names": []
9
+ }
@@ -0,0 +1,9 @@
1
+ import type { QuickJSContext } from "quickjs-emscripten";
2
+ /**
3
+ * Setup AbortController and AbortSignal classes in QuickJS.
4
+ * Uses pure QuickJS implementation so that:
5
+ * - controller.signal returns an actual AbortSignal instance
6
+ * - Event listeners work correctly
7
+ * - All prototype methods are accessible
8
+ */
9
+ export declare function setupAbortControllerAndSignal(context: QuickJSContext): void;
@@ -0,0 +1,5 @@
1
+ import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
2
+ /**
3
+ * Create the fetch function for QuickJS
4
+ */
5
+ export declare function createFetchFunction(context: QuickJSContext, onFetch?: (request: Request) => Promise<Response>): QuickJSHandle;