@ricsam/quickjs-fetch 0.2.0 → 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 (43) hide show
  1. package/dist/cjs/abort-controller.cjs +212 -0
  2. package/dist/cjs/abort-controller.cjs.map +10 -0
  3. package/dist/cjs/fetch.cjs +199 -0
  4. package/dist/cjs/fetch.cjs.map +10 -0
  5. package/dist/cjs/form-data.cjs +289 -0
  6. package/dist/cjs/form-data.cjs.map +10 -0
  7. package/dist/cjs/handle.cjs +248 -0
  8. package/dist/cjs/handle.cjs.map +10 -0
  9. package/dist/cjs/headers.cjs +196 -0
  10. package/dist/cjs/headers.cjs.map +10 -0
  11. package/dist/cjs/package.json +1 -1
  12. package/dist/cjs/request.cjs +315 -0
  13. package/dist/cjs/request.cjs.map +10 -0
  14. package/dist/cjs/response.cjs +315 -0
  15. package/dist/cjs/response.cjs.map +10 -0
  16. package/dist/cjs/serve.cjs +182 -0
  17. package/dist/cjs/serve.cjs.map +10 -0
  18. package/dist/cjs/setup.cjs +119 -0
  19. package/dist/cjs/setup.cjs.map +10 -0
  20. package/dist/cjs/types.cjs +26 -0
  21. package/dist/cjs/types.cjs.map +9 -0
  22. package/dist/mjs/abort-controller.mjs +181 -0
  23. package/dist/mjs/abort-controller.mjs.map +10 -0
  24. package/dist/mjs/fetch.mjs +168 -0
  25. package/dist/mjs/fetch.mjs.map +10 -0
  26. package/dist/mjs/form-data.mjs +258 -0
  27. package/dist/mjs/form-data.mjs.map +10 -0
  28. package/dist/mjs/handle.mjs +217 -0
  29. package/dist/mjs/handle.mjs.map +10 -0
  30. package/dist/mjs/headers.mjs +165 -0
  31. package/dist/mjs/headers.mjs.map +10 -0
  32. package/dist/mjs/package.json +1 -1
  33. package/dist/mjs/request.mjs +284 -0
  34. package/dist/mjs/request.mjs.map +10 -0
  35. package/dist/mjs/response.mjs +284 -0
  36. package/dist/mjs/response.mjs.map +10 -0
  37. package/dist/mjs/serve.mjs +151 -0
  38. package/dist/mjs/serve.mjs.map +10 -0
  39. package/dist/mjs/setup.mjs +92 -0
  40. package/dist/mjs/setup.mjs.map +10 -0
  41. package/dist/mjs/types.mjs +3 -0
  42. package/dist/mjs/types.mjs.map +9 -0
  43. package/package.json +2 -2
@@ -0,0 +1,258 @@
1
+ // @bun
2
+ // packages/fetch/src/globals/form-data.ts
3
+ import { defineClass } from "@ricsam/quickjs-core";
4
+ function isFileValue(value) {
5
+ return value !== null && typeof value === "object" && "data" in value && "filename" in value;
6
+ }
7
+ function createFormDataClass(context, stateMap) {
8
+ return defineClass(context, stateMap, {
9
+ name: "FormData",
10
+ construct: () => {
11
+ return { entries: [] };
12
+ },
13
+ methods: {
14
+ append(name, value, filename) {
15
+ const nameStr = String(name);
16
+ if (value && typeof value === "object" && "parts" in value) {
17
+ const blobLike = value;
18
+ const data = concatenateParts(blobLike.parts);
19
+ const fileValue = {
20
+ data,
21
+ filename: filename !== undefined ? String(filename) : blobLike.name || "blob",
22
+ type: blobLike.type || "application/octet-stream"
23
+ };
24
+ this.entries.push({ name: nameStr, value: fileValue });
25
+ } else if (value && typeof value === "object" && "data" in value && "filename" in value) {
26
+ this.entries.push({
27
+ name: nameStr,
28
+ value: {
29
+ ...value,
30
+ filename: filename !== undefined ? String(filename) : value.filename
31
+ }
32
+ });
33
+ } else {
34
+ this.entries.push({ name: nameStr, value: String(value) });
35
+ }
36
+ },
37
+ delete(name) {
38
+ const nameStr = String(name);
39
+ this.entries = this.entries.filter((e) => e.name !== nameStr);
40
+ },
41
+ get(name) {
42
+ const nameStr = String(name);
43
+ const entry = this.entries.find((e) => e.name === nameStr);
44
+ return entry ? entry.value : null;
45
+ },
46
+ getAll(name) {
47
+ const nameStr = String(name);
48
+ return this.entries.filter((e) => e.name === nameStr).map((e) => e.value);
49
+ },
50
+ has(name) {
51
+ const nameStr = String(name);
52
+ return this.entries.some((e) => e.name === nameStr);
53
+ },
54
+ set(name, value, filename) {
55
+ const nameStr = String(name);
56
+ this.entries = this.entries.filter((e) => e.name !== nameStr);
57
+ if (value && typeof value === "object" && "parts" in value) {
58
+ const blobLike = value;
59
+ const data = concatenateParts(blobLike.parts);
60
+ const fileValue = {
61
+ data,
62
+ filename: filename !== undefined ? String(filename) : blobLike.name || "blob",
63
+ type: blobLike.type || "application/octet-stream"
64
+ };
65
+ this.entries.push({ name: nameStr, value: fileValue });
66
+ } else if (value && typeof value === "object" && "data" in value && "filename" in value) {
67
+ this.entries.push({
68
+ name: nameStr,
69
+ value: {
70
+ ...value,
71
+ filename: filename !== undefined ? String(filename) : value.filename
72
+ }
73
+ });
74
+ } else {
75
+ this.entries.push({ name: nameStr, value: String(value) });
76
+ }
77
+ },
78
+ entries() {
79
+ return this.entries.map((e) => [e.name, e.value]);
80
+ },
81
+ keys() {
82
+ const seen = new Set;
83
+ const result = [];
84
+ for (const entry of this.entries) {
85
+ if (!seen.has(entry.name)) {
86
+ seen.add(entry.name);
87
+ result.push(entry.name);
88
+ }
89
+ }
90
+ return result;
91
+ },
92
+ values() {
93
+ return this.entries.map((e) => e.value);
94
+ },
95
+ forEach(callback) {
96
+ if (typeof callback !== "function") {
97
+ throw new TypeError("callback must be a function");
98
+ }
99
+ for (const entry of this.entries) {
100
+ callback(entry.value, entry.name, this);
101
+ }
102
+ }
103
+ }
104
+ });
105
+ }
106
+ function concatenateParts(parts) {
107
+ const totalLength = parts.reduce((sum, part) => sum + part.length, 0);
108
+ const result = new Uint8Array(totalLength);
109
+ let offset = 0;
110
+ for (const part of parts) {
111
+ result.set(part, offset);
112
+ offset += part.length;
113
+ }
114
+ return result;
115
+ }
116
+ function parseMultipartFormData(body, contentType) {
117
+ const entries = [];
118
+ const boundaryMatch = contentType.match(/boundary=([^;]+)/i);
119
+ if (!boundaryMatch || !boundaryMatch[1]) {
120
+ return { entries };
121
+ }
122
+ const boundary = boundaryMatch[1].replace(/^["']|["']$/g, "");
123
+ const boundaryBytes = new TextEncoder().encode(`--${boundary}`);
124
+ const endBoundaryBytes = new TextEncoder().encode(`--${boundary}--`);
125
+ const decoder = new TextDecoder;
126
+ let pos = 0;
127
+ pos = findSequence(body, boundaryBytes, pos);
128
+ if (pos === -1)
129
+ return { entries };
130
+ pos += boundaryBytes.length;
131
+ while (pos < body.length) {
132
+ if (body[pos] === 13 && body[pos + 1] === 10) {
133
+ pos += 2;
134
+ } else if (body[pos] === 10) {
135
+ pos += 1;
136
+ }
137
+ if (pos + 2 <= body.length && body[pos] === 45 && body[pos + 1] === 45) {
138
+ break;
139
+ }
140
+ const headersEnd = findSequence(body, new Uint8Array([13, 10, 13, 10]), pos);
141
+ if (headersEnd === -1)
142
+ break;
143
+ const headersText = decoder.decode(body.slice(pos, headersEnd));
144
+ const headers = parseHeaders(headersText);
145
+ pos = headersEnd + 4;
146
+ const nextBoundary = findSequence(body, boundaryBytes, pos);
147
+ if (nextBoundary === -1)
148
+ break;
149
+ let contentEnd = nextBoundary;
150
+ if (contentEnd > 0 && body[contentEnd - 1] === 10)
151
+ contentEnd--;
152
+ if (contentEnd > 0 && body[contentEnd - 1] === 13)
153
+ contentEnd--;
154
+ const content = body.slice(pos, contentEnd);
155
+ const disposition = headers["content-disposition"] || "";
156
+ const nameMatch = disposition.match(/name="([^"]+)"/);
157
+ const filenameMatch = disposition.match(/filename="([^"]+)"/);
158
+ if (nameMatch && nameMatch[1]) {
159
+ const name = nameMatch[1];
160
+ if (filenameMatch && filenameMatch[1]) {
161
+ const filename = filenameMatch[1];
162
+ const type = headers["content-type"] || "application/octet-stream";
163
+ entries.push({
164
+ name,
165
+ value: { data: content, filename, type }
166
+ });
167
+ } else {
168
+ entries.push({
169
+ name,
170
+ value: decoder.decode(content)
171
+ });
172
+ }
173
+ }
174
+ pos = nextBoundary + boundaryBytes.length;
175
+ }
176
+ return { entries };
177
+ }
178
+ function parseUrlEncodedFormData(body) {
179
+ const text = new TextDecoder().decode(body);
180
+ const entries = [];
181
+ const params = new URLSearchParams(text);
182
+ for (const [name, value] of params) {
183
+ entries.push({ name, value });
184
+ }
185
+ return { entries };
186
+ }
187
+ function findSequence(haystack, needle, start) {
188
+ outer:
189
+ for (let i = start;i <= haystack.length - needle.length; i++) {
190
+ for (let j = 0;j < needle.length; j++) {
191
+ if (haystack[i + j] !== needle[j])
192
+ continue outer;
193
+ }
194
+ return i;
195
+ }
196
+ return -1;
197
+ }
198
+ function parseHeaders(text) {
199
+ const headers = {};
200
+ const lines = text.split(/\r?\n/);
201
+ for (const line of lines) {
202
+ const colonIndex = line.indexOf(":");
203
+ if (colonIndex > 0) {
204
+ const name = line.slice(0, colonIndex).trim().toLowerCase();
205
+ const value = line.slice(colonIndex + 1).trim();
206
+ headers[name] = value;
207
+ }
208
+ }
209
+ return headers;
210
+ }
211
+ function serializeFormData(state) {
212
+ const boundary = `----FormDataBoundary${Math.random().toString(36).slice(2)}`;
213
+ const encoder = new TextEncoder;
214
+ const parts = [];
215
+ for (const entry of state.entries) {
216
+ const headerLines = [];
217
+ headerLines.push(`--${boundary}`);
218
+ if (isFileValue(entry.value)) {
219
+ headerLines.push(`Content-Disposition: form-data; name="${entry.name}"; filename="${entry.value.filename}"`);
220
+ headerLines.push(`Content-Type: ${entry.value.type}`);
221
+ headerLines.push("");
222
+ parts.push(encoder.encode(headerLines.join(`\r
223
+ `) + `\r
224
+ `));
225
+ parts.push(entry.value.data);
226
+ parts.push(encoder.encode(`\r
227
+ `));
228
+ } else {
229
+ headerLines.push(`Content-Disposition: form-data; name="${entry.name}"`);
230
+ headerLines.push("");
231
+ headerLines.push(entry.value);
232
+ parts.push(encoder.encode(headerLines.join(`\r
233
+ `) + `\r
234
+ `));
235
+ }
236
+ }
237
+ parts.push(encoder.encode(`--${boundary}--\r
238
+ `));
239
+ const totalLength = parts.reduce((sum, part) => sum + part.length, 0);
240
+ const body = new Uint8Array(totalLength);
241
+ let offset = 0;
242
+ for (const part of parts) {
243
+ body.set(part, offset);
244
+ offset += part.length;
245
+ }
246
+ return {
247
+ body,
248
+ contentType: `multipart/form-data; boundary=${boundary}`
249
+ };
250
+ }
251
+ export {
252
+ serializeFormData,
253
+ parseUrlEncodedFormData,
254
+ parseMultipartFormData,
255
+ createFormDataClass
256
+ };
257
+
258
+ //# debugId=84B55DCACAA9340664756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/globals/form-data.ts"],
4
+ "sourcesContent": [
5
+ "import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport type { StateMap } from \"@ricsam/quickjs-core\";\nimport { defineClass } from \"@ricsam/quickjs-core\";\n\n/**\n * Internal state for FormData entries\n * Each entry can have multiple values (same key appended multiple times)\n */\nexport interface FormDataEntry {\n name: string;\n value: string | FormDataFileValue;\n}\n\nexport interface FormDataFileValue {\n data: Uint8Array;\n filename: string;\n type: string;\n}\n\nexport interface FormDataState {\n entries: FormDataEntry[];\n}\n\nfunction isFileValue(value: unknown): value is FormDataFileValue {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"data\" in value &&\n \"filename\" in value\n );\n}\n\n/**\n * Create the FormData class for QuickJS\n */\nexport function createFormDataClass(\n context: QuickJSContext,\n stateMap: StateMap\n): QuickJSHandle {\n return defineClass<FormDataState>(context, stateMap, {\n name: \"FormData\",\n construct: () => {\n return { entries: [] };\n },\n methods: {\n append(this: FormDataState, name: unknown, value: unknown, filename?: unknown) {\n const nameStr = String(name);\n\n if (value && typeof value === \"object\" && \"parts\" in value) {\n // Blob-like value\n const blobLike = value as { parts: Uint8Array[]; type?: string; name?: string };\n const data = concatenateParts(blobLike.parts);\n const fileValue: FormDataFileValue = {\n data,\n filename: filename !== undefined ? String(filename) : (blobLike.name || \"blob\"),\n type: blobLike.type || \"application/octet-stream\",\n };\n this.entries.push({ name: nameStr, value: fileValue });\n } else if (value && typeof value === \"object\" && \"data\" in value && \"filename\" in value) {\n // Already a FormDataFileValue\n this.entries.push({\n name: nameStr,\n value: {\n ...(value as FormDataFileValue),\n filename: filename !== undefined ? String(filename) : (value as FormDataFileValue).filename,\n },\n });\n } else {\n // String value\n this.entries.push({ name: nameStr, value: String(value) });\n }\n },\n delete(this: FormDataState, name: unknown) {\n const nameStr = String(name);\n this.entries = this.entries.filter((e) => e.name !== nameStr);\n },\n get(this: FormDataState, name: unknown): string | FormDataFileValue | null {\n const nameStr = String(name);\n const entry = this.entries.find((e) => e.name === nameStr);\n return entry ? entry.value : null;\n },\n getAll(this: FormDataState, name: unknown): Array<string | FormDataFileValue> {\n const nameStr = String(name);\n return this.entries\n .filter((e) => e.name === nameStr)\n .map((e) => e.value);\n },\n has(this: FormDataState, name: unknown): boolean {\n const nameStr = String(name);\n return this.entries.some((e) => e.name === nameStr);\n },\n set(this: FormDataState, name: unknown, value: unknown, filename?: unknown) {\n const nameStr = String(name);\n // Remove all existing entries with this name\n this.entries = this.entries.filter((e) => e.name !== nameStr);\n\n // Add the new entry (using append logic)\n if (value && typeof value === \"object\" && \"parts\" in value) {\n const blobLike = value as { parts: Uint8Array[]; type?: string; name?: string };\n const data = concatenateParts(blobLike.parts);\n const fileValue: FormDataFileValue = {\n data,\n filename: filename !== undefined ? String(filename) : (blobLike.name || \"blob\"),\n type: blobLike.type || \"application/octet-stream\",\n };\n this.entries.push({ name: nameStr, value: fileValue });\n } else if (value && typeof value === \"object\" && \"data\" in value && \"filename\" in value) {\n this.entries.push({\n name: nameStr,\n value: {\n ...(value as FormDataFileValue),\n filename: filename !== undefined ? String(filename) : (value as FormDataFileValue).filename,\n },\n });\n } else {\n this.entries.push({ name: nameStr, value: String(value) });\n }\n },\n entries(this: FormDataState): Array<[string, string | FormDataFileValue]> {\n return this.entries.map((e) => [e.name, e.value]);\n },\n keys(this: FormDataState): string[] {\n // Return unique keys in order of first appearance\n const seen = new Set<string>();\n const result: string[] = [];\n for (const entry of this.entries) {\n if (!seen.has(entry.name)) {\n seen.add(entry.name);\n result.push(entry.name);\n }\n }\n return result;\n },\n values(this: FormDataState): Array<string | FormDataFileValue> {\n return this.entries.map((e) => e.value);\n },\n forEach(this: FormDataState, callback: unknown) {\n if (typeof callback !== \"function\") {\n throw new TypeError(\"callback must be a function\");\n }\n for (const entry of this.entries) {\n (callback as (value: string | FormDataFileValue, key: string, parent: FormDataState) => void)(\n entry.value,\n entry.name,\n this\n );\n }\n },\n },\n });\n}\n\n/**\n * Concatenate Uint8Arrays into a single Uint8Array\n */\nfunction concatenateParts(parts: Uint8Array[]): Uint8Array {\n const totalLength = parts.reduce((sum, part) => sum + part.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const part of parts) {\n result.set(part, offset);\n offset += part.length;\n }\n return result;\n}\n\n/**\n * Parse multipart/form-data body\n */\nexport function parseMultipartFormData(\n body: Uint8Array,\n contentType: string\n): FormDataState {\n const entries: FormDataEntry[] = [];\n\n // Extract boundary from content-type\n const boundaryMatch = contentType.match(/boundary=([^;]+)/i);\n if (!boundaryMatch || !boundaryMatch[1]) {\n return { entries };\n }\n\n const boundary = boundaryMatch[1].replace(/^[\"']|[\"']$/g, \"\");\n const boundaryBytes = new TextEncoder().encode(`--${boundary}`);\n const endBoundaryBytes = new TextEncoder().encode(`--${boundary}--`);\n\n // Find all parts\n const decoder = new TextDecoder();\n let pos = 0;\n\n // Skip preamble and first boundary\n pos = findSequence(body, boundaryBytes, pos);\n if (pos === -1) return { entries };\n pos += boundaryBytes.length;\n\n while (pos < body.length) {\n // Skip CRLF after boundary\n if (body[pos] === 0x0d && body[pos + 1] === 0x0a) {\n pos += 2;\n } else if (body[pos] === 0x0a) {\n pos += 1;\n }\n\n // Check for end boundary\n if (pos + 2 <= body.length && body[pos] === 0x2d && body[pos + 1] === 0x2d) {\n break; // End of multipart\n }\n\n // Parse headers\n const headersEnd = findSequence(body, new Uint8Array([0x0d, 0x0a, 0x0d, 0x0a]), pos);\n if (headersEnd === -1) break;\n\n const headersText = decoder.decode(body.slice(pos, headersEnd));\n const headers = parseHeaders(headersText);\n pos = headersEnd + 4;\n\n // Find next boundary\n const nextBoundary = findSequence(body, boundaryBytes, pos);\n if (nextBoundary === -1) break;\n\n // Content is between current position and next boundary (minus CRLF)\n let contentEnd = nextBoundary;\n if (contentEnd > 0 && body[contentEnd - 1] === 0x0a) contentEnd--;\n if (contentEnd > 0 && body[contentEnd - 1] === 0x0d) contentEnd--;\n\n const content = body.slice(pos, contentEnd);\n\n // Parse Content-Disposition header\n const disposition = headers[\"content-disposition\"] || \"\";\n const nameMatch = disposition.match(/name=\"([^\"]+)\"/);\n const filenameMatch = disposition.match(/filename=\"([^\"]+)\"/);\n\n if (nameMatch && nameMatch[1]) {\n const name = nameMatch[1];\n if (filenameMatch && filenameMatch[1]) {\n // File entry\n const filename = filenameMatch[1];\n const type = headers[\"content-type\"] || \"application/octet-stream\";\n entries.push({\n name,\n value: { data: content, filename, type },\n });\n } else {\n // String entry\n entries.push({\n name,\n value: decoder.decode(content),\n });\n }\n }\n\n pos = nextBoundary + boundaryBytes.length;\n }\n\n return { entries };\n}\n\n/**\n * Parse URL-encoded form data\n */\nexport function parseUrlEncodedFormData(body: Uint8Array): FormDataState {\n const text = new TextDecoder().decode(body);\n const entries: FormDataEntry[] = [];\n\n const params = new URLSearchParams(text);\n for (const [name, value] of params) {\n entries.push({ name, value });\n }\n\n return { entries };\n}\n\nfunction findSequence(haystack: Uint8Array, needle: Uint8Array, start: number): number {\n outer: for (let i = start; i <= haystack.length - needle.length; i++) {\n for (let j = 0; j < needle.length; j++) {\n if (haystack[i + j] !== needle[j]) continue outer;\n }\n return i;\n }\n return -1;\n}\n\nfunction parseHeaders(text: string): Record<string, string> {\n const headers: Record<string, string> = {};\n const lines = text.split(/\\r?\\n/);\n for (const line of lines) {\n const colonIndex = line.indexOf(\":\");\n if (colonIndex > 0) {\n const name = line.slice(0, colonIndex).trim().toLowerCase();\n const value = line.slice(colonIndex + 1).trim();\n headers[name] = value;\n }\n }\n return headers;\n}\n\n/**\n * Serialize FormData to multipart/form-data format\n */\nexport function serializeFormData(state: FormDataState): { body: Uint8Array; contentType: string } {\n const boundary = `----FormDataBoundary${Math.random().toString(36).slice(2)}`;\n const encoder = new TextEncoder();\n const parts: Uint8Array[] = [];\n\n for (const entry of state.entries) {\n const headerLines: string[] = [];\n headerLines.push(`--${boundary}`);\n\n if (isFileValue(entry.value)) {\n headerLines.push(\n `Content-Disposition: form-data; name=\"${entry.name}\"; filename=\"${entry.value.filename}\"`\n );\n headerLines.push(`Content-Type: ${entry.value.type}`);\n headerLines.push(\"\");\n\n parts.push(encoder.encode(headerLines.join(\"\\r\\n\") + \"\\r\\n\"));\n parts.push(entry.value.data);\n parts.push(encoder.encode(\"\\r\\n\"));\n } else {\n headerLines.push(`Content-Disposition: form-data; name=\"${entry.name}\"`);\n headerLines.push(\"\");\n headerLines.push(entry.value);\n\n parts.push(encoder.encode(headerLines.join(\"\\r\\n\") + \"\\r\\n\"));\n }\n }\n\n parts.push(encoder.encode(`--${boundary}--\\r\\n`));\n\n // Concatenate all parts\n const totalLength = parts.reduce((sum, part) => sum + part.length, 0);\n const 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 return {\n body,\n contentType: `multipart/form-data; boundary=${boundary}`,\n };\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "84B55DCACAA9340664756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,217 @@
1
+ // @bun
2
+ // packages/fetch/src/handle.ts
3
+ import { marshal, getInstanceState, setInstanceState } from "@ricsam/quickjs-core";
4
+ import { createRequestStateFromNative } from "./globals/request.ts";
5
+ import { responseStateToNative } from "./globals/response.ts";
6
+ function createRequestInstance(context, stateMap, requestState) {
7
+ const urlHandle = context.newString(requestState.url);
8
+ context.setProp(context.global, "__requestUrl__", urlHandle);
9
+ urlHandle.dispose();
10
+ const requestResult = context.evalCode(`new Request(__requestUrl__)`);
11
+ context.setProp(context.global, "__requestUrl__", context.undefined);
12
+ if (requestResult.error) {
13
+ const error = context.dump(requestResult.error);
14
+ requestResult.error.dispose();
15
+ throw new Error(`Failed to create Request: ${JSON.stringify(error)}`);
16
+ }
17
+ const requestHandle = requestResult.value;
18
+ setInstanceState(context, requestHandle, requestState);
19
+ return requestHandle;
20
+ }
21
+ function createFetchHandle(context, stateMap, serveState) {
22
+ const wsCommandCallbacks = new Set;
23
+ return {
24
+ stateMap,
25
+ async dispatchRequest(request) {
26
+ if (!serveState.fetchHandler) {
27
+ throw new Error("No serve() handler registered");
28
+ }
29
+ serveState.pendingUpgrade = null;
30
+ const requestState = await createRequestStateFromNative(request);
31
+ const requestHandle = createRequestInstance(context, stateMap, requestState);
32
+ const serverResult = context.evalCode(`new __Server__()`);
33
+ if (serverResult.error) {
34
+ requestHandle.dispose();
35
+ const error = context.dump(serverResult.error);
36
+ serverResult.error.dispose();
37
+ throw new Error(`Failed to create Server: ${error}`);
38
+ }
39
+ const serverHandle = serverResult.value;
40
+ try {
41
+ const result = context.callFunction(serveState.fetchHandler, context.undefined, requestHandle, serverHandle);
42
+ if (result.error) {
43
+ const error = context.dump(result.error);
44
+ result.error.dispose();
45
+ throw new Error(`Fetch handler error: ${JSON.stringify(error)}`);
46
+ }
47
+ let responseHandle = result.value;
48
+ const typeofResult = context.typeof(responseHandle);
49
+ if (typeofResult === "object") {
50
+ const thenHandle = context.getProp(responseHandle, "then");
51
+ const hasThen = context.typeof(thenHandle) === "function";
52
+ thenHandle.dispose();
53
+ if (hasThen) {
54
+ const resolved = await context.resolvePromise(responseHandle);
55
+ responseHandle.dispose();
56
+ context.runtime.executePendingJobs();
57
+ if (resolved.error) {
58
+ const error = context.dump(resolved.error);
59
+ resolved.error.dispose();
60
+ throw new Error(`Fetch handler promise rejected: ${JSON.stringify(error)}`);
61
+ }
62
+ responseHandle = resolved.value;
63
+ }
64
+ }
65
+ const responseState = getInstanceState(context, responseHandle);
66
+ responseHandle.dispose();
67
+ if (!responseState) {
68
+ throw new Error("Failed to get Response state");
69
+ }
70
+ return responseStateToNative(responseState);
71
+ } finally {
72
+ requestHandle.dispose();
73
+ serverHandle.dispose();
74
+ }
75
+ },
76
+ getUpgradeRequest() {
77
+ return serveState.pendingUpgrade;
78
+ },
79
+ dispatchWebSocketOpen(connectionId, data) {
80
+ if (!serveState.websocketHandlers.open) {
81
+ return;
82
+ }
83
+ const connectionIdHandle = context.newString(connectionId);
84
+ const dataHandle = marshal(context, data);
85
+ context.setProp(context.global, "__wsConnectionId__", connectionIdHandle);
86
+ context.setProp(context.global, "__wsData__", dataHandle);
87
+ connectionIdHandle.dispose();
88
+ dataHandle.dispose();
89
+ const wsResult = context.evalCode(`new __ServerWebSocket__(__wsConnectionId__, __wsData__)`);
90
+ context.setProp(context.global, "__wsConnectionId__", context.undefined);
91
+ context.setProp(context.global, "__wsData__", context.undefined);
92
+ if (wsResult.error) {
93
+ wsResult.error.dispose();
94
+ return;
95
+ }
96
+ const wsHandle = wsResult.value;
97
+ serveState.activeConnections.set(connectionId, {
98
+ data,
99
+ readyState: 1,
100
+ connectionId,
101
+ wsHandle
102
+ });
103
+ const result = context.callFunction(serveState.websocketHandlers.open, context.undefined, wsHandle);
104
+ if (result.error) {
105
+ result.error.dispose();
106
+ } else {
107
+ result.value.dispose();
108
+ }
109
+ context.runtime.executePendingJobs();
110
+ },
111
+ dispatchWebSocketMessage(connectionId, message) {
112
+ if (!serveState.websocketHandlers.message) {
113
+ return;
114
+ }
115
+ const connection = serveState.activeConnections.get(connectionId);
116
+ if (!connection || !connection.wsHandle) {
117
+ return;
118
+ }
119
+ const messageHandle = typeof message === "string" ? context.newString(message) : context.newArrayBuffer(message);
120
+ const result = context.callFunction(serveState.websocketHandlers.message, context.undefined, connection.wsHandle, messageHandle);
121
+ messageHandle.dispose();
122
+ if (result.error) {
123
+ result.error.dispose();
124
+ } else {
125
+ result.value.dispose();
126
+ }
127
+ context.runtime.executePendingJobs();
128
+ },
129
+ dispatchWebSocketClose(connectionId, code, reason) {
130
+ const connection = serveState.activeConnections.get(connectionId);
131
+ if (!connection || !connection.wsHandle) {
132
+ serveState.activeConnections.delete(connectionId);
133
+ return;
134
+ }
135
+ if (!serveState.websocketHandlers.close) {
136
+ connection.wsHandle.dispose();
137
+ serveState.activeConnections.delete(connectionId);
138
+ return;
139
+ }
140
+ connection.readyState = 3;
141
+ const codeHandle = context.newNumber(code);
142
+ const reasonHandle = context.newString(reason);
143
+ const result = context.callFunction(serveState.websocketHandlers.close, context.undefined, connection.wsHandle, codeHandle, reasonHandle);
144
+ codeHandle.dispose();
145
+ reasonHandle.dispose();
146
+ if (result.error) {
147
+ result.error.dispose();
148
+ } else {
149
+ result.value.dispose();
150
+ }
151
+ connection.wsHandle.dispose();
152
+ serveState.activeConnections.delete(connectionId);
153
+ context.runtime.executePendingJobs();
154
+ },
155
+ dispatchWebSocketError(connectionId, error) {
156
+ if (!serveState.websocketHandlers.error) {
157
+ return;
158
+ }
159
+ const connection = serveState.activeConnections.get(connectionId);
160
+ if (!connection || !connection.wsHandle) {
161
+ return;
162
+ }
163
+ const errorHandle = marshal(context, {
164
+ name: error.name,
165
+ message: error.message
166
+ });
167
+ const result = context.callFunction(serveState.websocketHandlers.error, context.undefined, connection.wsHandle, errorHandle);
168
+ errorHandle.dispose();
169
+ if (result.error) {
170
+ result.error.dispose();
171
+ } else {
172
+ result.value.dispose();
173
+ }
174
+ context.runtime.executePendingJobs();
175
+ },
176
+ onWebSocketCommand(callback) {
177
+ wsCommandCallbacks.add(callback);
178
+ return () => wsCommandCallbacks.delete(callback);
179
+ },
180
+ hasServeHandler() {
181
+ return serveState.fetchHandler !== null;
182
+ },
183
+ dispose() {
184
+ if (serveState.websocketHandlers.open) {
185
+ serveState.websocketHandlers.open.dispose();
186
+ serveState.websocketHandlers.open = undefined;
187
+ }
188
+ if (serveState.websocketHandlers.message) {
189
+ serveState.websocketHandlers.message.dispose();
190
+ serveState.websocketHandlers.message = undefined;
191
+ }
192
+ if (serveState.websocketHandlers.close) {
193
+ serveState.websocketHandlers.close.dispose();
194
+ serveState.websocketHandlers.close = undefined;
195
+ }
196
+ if (serveState.websocketHandlers.error) {
197
+ serveState.websocketHandlers.error.dispose();
198
+ serveState.websocketHandlers.error = undefined;
199
+ }
200
+ if (serveState.fetchHandler) {
201
+ serveState.fetchHandler.dispose();
202
+ serveState.fetchHandler = null;
203
+ }
204
+ for (const connection of serveState.activeConnections.values()) {
205
+ if (connection.wsHandle) {
206
+ connection.wsHandle.dispose();
207
+ }
208
+ }
209
+ serveState.activeConnections.clear();
210
+ }
211
+ };
212
+ }
213
+ export {
214
+ createFetchHandle
215
+ };
216
+
217
+ //# debugId=DD8288C8B39BDBBE64756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/handle.ts"],
4
+ "sourcesContent": [
5
+ "import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { marshal, getInstanceState, setInstanceState } from \"@ricsam/quickjs-core\";\nimport type {\n StateMap,\n FetchHandle,\n ServeState,\n UpgradeRequest,\n WebSocketCommand,\n ServerWebSocketState,\n RequestState,\n ResponseState,\n} from \"./types.ts\";\nimport { createRequestStateFromNative } from \"./globals/request.ts\";\nimport { responseStateToNative } from \"./globals/response.ts\";\n\n/**\n * Create a proper Request instance in QuickJS with the given state\n */\nfunction createRequestInstance(\n context: QuickJSContext,\n stateMap: StateMap,\n requestState: RequestState\n): QuickJSHandle {\n // Create the Request instance using evalCode (classes must be called with 'new')\n // Pass the URL as the constructor argument - minimal construction\n const urlHandle = context.newString(requestState.url);\n context.setProp(context.global, \"__requestUrl__\", urlHandle);\n urlHandle.dispose();\n\n const requestResult = context.evalCode(`new Request(__requestUrl__)`);\n\n // Clean up temporary global\n context.setProp(context.global, \"__requestUrl__\", context.undefined);\n\n if (requestResult.error) {\n const error = context.dump(requestResult.error);\n requestResult.error.dispose();\n throw new Error(`Failed to create Request: ${JSON.stringify(error)}`);\n }\n\n const requestHandle = requestResult.value;\n\n // Overwrite the internal state with our full RequestState\n setInstanceState(context, requestHandle, requestState);\n\n return requestHandle;\n}\n\n/**\n * Create the FetchHandle implementation\n */\nexport function createFetchHandle(\n context: QuickJSContext,\n stateMap: StateMap,\n serveState: ServeState\n): FetchHandle {\n const wsCommandCallbacks = new Set<(cmd: WebSocketCommand) => void>();\n\n return {\n stateMap,\n\n async dispatchRequest(request: Request): Promise<Response> {\n if (!serveState.fetchHandler) {\n throw new Error(\"No serve() handler registered\");\n }\n\n // Clear any previous upgrade request\n serveState.pendingUpgrade = null;\n\n // Convert native Request to RequestState\n const requestState = await createRequestStateFromNative(request);\n\n // Create proper QuickJS Request instance with internal state\n const requestHandle = createRequestInstance(context, stateMap, requestState);\n\n // Create Server instance using evalCode (classes must be called with 'new')\n const serverResult = context.evalCode(`new __Server__()`);\n if (serverResult.error) {\n requestHandle.dispose();\n const error = context.dump(serverResult.error);\n serverResult.error.dispose();\n throw new Error(`Failed to create Server: ${error}`);\n }\n const serverHandle = serverResult.value;\n\n try {\n // Call the fetch handler\n const result = context.callFunction(\n serveState.fetchHandler,\n context.undefined,\n requestHandle,\n serverHandle\n );\n\n if (result.error) {\n const error = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Fetch handler error: ${JSON.stringify(error)}`);\n }\n\n let responseHandle = result.value;\n\n // Check if result is a Promise\n const typeofResult = context.typeof(responseHandle);\n if (typeofResult === \"object\") {\n // Check for .then method (Promise-like)\n const thenHandle = context.getProp(responseHandle, \"then\");\n const hasThen = context.typeof(thenHandle) === \"function\";\n thenHandle.dispose();\n\n if (hasThen) {\n // Resolve the promise\n const resolved = await context.resolvePromise(responseHandle);\n responseHandle.dispose();\n context.runtime.executePendingJobs();\n\n if (resolved.error) {\n const error = context.dump(resolved.error);\n resolved.error.dispose();\n throw new Error(`Fetch handler promise rejected: ${JSON.stringify(error)}`);\n }\n responseHandle = resolved.value;\n }\n }\n\n // Get the ResponseState directly from the instance\n const responseState = getInstanceState<ResponseState>(context, responseHandle);\n responseHandle.dispose();\n\n if (!responseState) {\n throw new Error(\"Failed to get Response state\");\n }\n\n // Convert to native Response\n return responseStateToNative(responseState);\n } finally {\n requestHandle.dispose();\n serverHandle.dispose();\n }\n },\n\n getUpgradeRequest(): UpgradeRequest | null {\n return serveState.pendingUpgrade;\n },\n\n /**\n * Dispatch WebSocket open event to the QuickJS handler.\n *\n * **IMPORTANT:** An `open` handler MUST be defined in the serve() websocket\n * options for the connection to be tracked. Without an open handler, the\n * connection is not stored and subsequent message/close/error events will\n * be silently ignored.\n *\n * @param connectionId - Unique identifier for this WebSocket connection (host-assigned)\n * @param data - Optional user data from server.upgrade() to associate with the connection\n */\n dispatchWebSocketOpen(connectionId: string, data?: unknown): void {\n if (!serveState.websocketHandlers.open) {\n return;\n }\n\n // Create ServerWebSocket instance using evalCode (classes must be called with 'new')\n // Pass arguments through temporary globals\n const connectionIdHandle = context.newString(connectionId);\n const dataHandle = marshal(context, data);\n context.setProp(context.global, \"__wsConnectionId__\", connectionIdHandle);\n context.setProp(context.global, \"__wsData__\", dataHandle);\n connectionIdHandle.dispose();\n dataHandle.dispose();\n\n const wsResult = context.evalCode(`new __ServerWebSocket__(__wsConnectionId__, __wsData__)`);\n\n // Clean up temporary globals\n context.setProp(context.global, \"__wsConnectionId__\", context.undefined);\n context.setProp(context.global, \"__wsData__\", context.undefined);\n\n if (wsResult.error) {\n wsResult.error.dispose();\n return;\n }\n\n const wsHandle = wsResult.value;\n\n // Store the connection\n serveState.activeConnections.set(connectionId, {\n data,\n readyState: 1,\n connectionId,\n wsHandle,\n });\n\n // Call the open handler\n const result = context.callFunction(\n serveState.websocketHandlers.open,\n context.undefined,\n wsHandle\n );\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Dispatch WebSocket message event to the QuickJS handler.\n *\n * Requires the connection to have been tracked via dispatchWebSocketOpen().\n * If no message handler is defined, or the connection is not tracked, this is a no-op.\n *\n * @param connectionId - The connection ID from dispatchWebSocketOpen()\n * @param message - The message content (string or binary data)\n */\n dispatchWebSocketMessage(\n connectionId: string,\n message: string | ArrayBuffer\n ): void {\n if (!serveState.websocketHandlers.message) {\n return;\n }\n\n const connection = serveState.activeConnections.get(connectionId);\n if (!connection || !connection.wsHandle) {\n return;\n }\n\n const messageHandle =\n typeof message === \"string\"\n ? context.newString(message)\n : context.newArrayBuffer(message);\n\n const result = context.callFunction(\n serveState.websocketHandlers.message,\n context.undefined,\n connection.wsHandle,\n messageHandle\n );\n\n messageHandle.dispose();\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Dispatch WebSocket close event to the QuickJS handler and clean up the connection.\n *\n * Updates the connection ready state to CLOSED (3), calls the close handler if defined,\n * disposes the WebSocket handle, and removes the connection from tracking.\n *\n * @param connectionId - The connection ID from dispatchWebSocketOpen()\n * @param code - The WebSocket close code (e.g., 1000 for normal closure)\n * @param reason - The close reason string\n */\n dispatchWebSocketClose(\n connectionId: string,\n code: number,\n reason: string\n ): void {\n const connection = serveState.activeConnections.get(connectionId);\n if (!connection || !connection.wsHandle) {\n serveState.activeConnections.delete(connectionId);\n return;\n }\n\n if (!serveState.websocketHandlers.close) {\n // No close handler, but still need to cleanup the connection\n connection.wsHandle.dispose();\n serveState.activeConnections.delete(connectionId);\n return;\n }\n\n // Update ready state\n connection.readyState = 3; // CLOSED\n\n const codeHandle = context.newNumber(code);\n const reasonHandle = context.newString(reason);\n\n const result = context.callFunction(\n serveState.websocketHandlers.close,\n context.undefined,\n connection.wsHandle,\n codeHandle,\n reasonHandle\n );\n\n codeHandle.dispose();\n reasonHandle.dispose();\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n // Cleanup\n connection.wsHandle.dispose();\n serveState.activeConnections.delete(connectionId);\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Dispatch WebSocket error event to the QuickJS handler.\n *\n * Requires the connection to have been tracked via dispatchWebSocketOpen().\n * If no error handler is defined, or the connection is not tracked, this is a no-op.\n *\n * @param connectionId - The connection ID from dispatchWebSocketOpen()\n * @param error - The error that occurred\n */\n dispatchWebSocketError(connectionId: string, error: Error): void {\n if (!serveState.websocketHandlers.error) {\n return;\n }\n\n const connection = serveState.activeConnections.get(connectionId);\n if (!connection || !connection.wsHandle) {\n return;\n }\n\n const errorHandle = marshal(context, {\n name: error.name,\n message: error.message,\n });\n\n const result = context.callFunction(\n serveState.websocketHandlers.error,\n context.undefined,\n connection.wsHandle,\n errorHandle\n );\n\n errorHandle.dispose();\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Register a callback for outgoing WebSocket commands from QuickJS.\n *\n * Called when QuickJS code calls ws.send() or ws.close() on a ServerWebSocket.\n * The callback receives command objects that should be forwarded to the actual WebSocket.\n *\n * @param callback - Function to handle outgoing WebSocket commands\n * @returns Unsubscribe function to remove the callback\n */\n onWebSocketCommand(\n callback: (command: WebSocketCommand) => void\n ): () => void {\n wsCommandCallbacks.add(callback);\n return () => wsCommandCallbacks.delete(callback);\n },\n\n hasServeHandler(): boolean {\n return serveState.fetchHandler !== null;\n },\n\n dispose(): void {\n // Dispose WebSocket handler handles\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 // Dispose fetch handler handle\n if (serveState.fetchHandler) {\n serveState.fetchHandler.dispose();\n serveState.fetchHandler = null;\n }\n\n // Dispose active WebSocket connection handles\n for (const connection of serveState.activeConnections.values()) {\n if (connection.wsHandle) {\n connection.wsHandle.dispose();\n }\n }\n serveState.activeConnections.clear();\n\n // Note: __Server__ and __ServerWebSocket__ are on global and will be cleaned up by context.dispose()\n },\n };\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "DD8288C8B39BDBBE64756E2164756E21",
9
+ "names": []
10
+ }