@easel-sh/cli 0.1.0-alpha.12 → 0.1.0-alpha.14
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.
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* - Fetch Web Standard: export default { fetch(request) { return new Response(...); } };
|
|
4
4
|
* - HTTP method exports: export function GET(request) { return new Response(...); }
|
|
5
5
|
* - Node-style: export default (req, res) => { ... } with Vercel-like helpers (query, cookies, body, status, send, json, redirect).
|
|
6
|
+
* Uses Lambda response streaming (awslambda.streamifyResponse) so streaming Fetch responses are piped to the client.
|
|
6
7
|
*/
|
|
7
8
|
export declare function generateHandlerWrapper(handlerFile: string): string;
|
|
8
9
|
//# sourceMappingURL=handler-wrapper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler-wrapper.d.ts","sourceRoot":"","sources":["../src/handler-wrapper.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"handler-wrapper.d.ts","sourceRoot":"","sources":["../src/handler-wrapper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA+RlE"}
|
package/dist/handler-wrapper.js
CHANGED
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
* - Fetch Web Standard: export default { fetch(request) { return new Response(...); } };
|
|
4
4
|
* - HTTP method exports: export function GET(request) { return new Response(...); }
|
|
5
5
|
* - Node-style: export default (req, res) => { ... } with Vercel-like helpers (query, cookies, body, status, send, json, redirect).
|
|
6
|
+
* Uses Lambda response streaming (awslambda.streamifyResponse) so streaming Fetch responses are piped to the client.
|
|
6
7
|
*/
|
|
7
8
|
export function generateHandlerWrapper(handlerFile) {
|
|
8
9
|
const handlerPath = JSON.stringify("./" + handlerFile.replace(/\\/g, "/"));
|
|
9
10
|
return `/* eslint-disable @typescript-eslint/no-require-imports */
|
|
10
11
|
|
|
11
12
|
const { Readable } = require("stream");
|
|
13
|
+
const { pipeline } = require("stream/promises");
|
|
12
14
|
const { EventEmitter } = require("events");
|
|
13
15
|
|
|
14
16
|
const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
|
|
@@ -22,292 +24,273 @@ function getModule() {
|
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
function normalizeStatus(status) {
|
|
25
|
-
// Valid HTTP status is 100-599. Response.status can be 0 for error/opaque (Fetch API).
|
|
26
27
|
if (typeof status === "number" && status >= 100 && status < 600) return status;
|
|
27
|
-
if (status === 0) return 502;
|
|
28
|
+
if (status === 0) return 502;
|
|
28
29
|
return 200;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
response.headers.forEach((value, key)
|
|
34
|
-
|
|
32
|
+
function getResponseHeaders(response) {
|
|
33
|
+
const out = {};
|
|
34
|
+
response.headers.forEach(function (value, key) {
|
|
35
|
+
out[key.toLowerCase()] = value;
|
|
35
36
|
});
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
statusCode: normalizeStatus(response.status),
|
|
39
|
-
headers: responseHeaders,
|
|
40
|
-
body: body,
|
|
41
|
-
};
|
|
37
|
+
return out;
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
|
|
40
|
+
function writeStreamFirstLine(responseStream, statusCode, headers) {
|
|
41
|
+
const line = JSON.stringify({ statusCode: statusCode, headers: headers }) + "\\n";
|
|
42
|
+
responseStream.write(line);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function writeErrorToStream(responseStream, err) {
|
|
46
|
+
writeStreamFirstLine(responseStream, 502, { "content-type": "application/json" });
|
|
47
|
+
responseStream.write(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
|
|
48
|
+
responseStream.end();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function writeFetchResponseToStream(responseStream, response) {
|
|
52
|
+
const statusCode = normalizeStatus(response.status);
|
|
53
|
+
const headers = getResponseHeaders(response);
|
|
54
|
+
writeStreamFirstLine(responseStream, statusCode, headers);
|
|
55
|
+
|
|
56
|
+
if (response.body != null && typeof response.body.getReader === "function") {
|
|
57
|
+
const nodeReadable = Readable.fromWeb(response.body);
|
|
58
|
+
var streamStartMs = Date.now();
|
|
59
|
+
console.log("[handler] stream body pipeline starting at " + streamStartMs + " ms");
|
|
60
|
+
try {
|
|
61
|
+
await pipeline(nodeReadable, responseStream);
|
|
62
|
+
var streamEndMs = Date.now();
|
|
63
|
+
console.log("[handler] stream body pipeline finished at " + streamEndMs + " ms (duration " + (streamEndMs - streamStartMs) + " ms)");
|
|
64
|
+
} catch (pipeErr) {
|
|
65
|
+
console.error("[handler] stream body pipeline error at " + Date.now() + " ms:", pipeErr instanceof Error ? pipeErr.message : String(pipeErr));
|
|
66
|
+
throw pipeErr;
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
const body = await response.text();
|
|
70
|
+
responseStream.write(body);
|
|
71
|
+
responseStream.end();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function runStreamingHandler(event, responseStream, context) {
|
|
76
|
+
try {
|
|
77
|
+
const mod = await getModule();
|
|
78
|
+
const handler = mod.default !== undefined ? mod.default : mod;
|
|
79
|
+
|
|
80
|
+
const isV2 = event.requestContext && event.requestContext.http;
|
|
81
|
+
const method = isV2 ? event.requestContext.http.method : event.httpMethod || "GET";
|
|
82
|
+
|
|
83
|
+
const normalizedHeaders = {};
|
|
84
|
+
if (event.headers) {
|
|
85
|
+
Object.entries(event.headers).forEach(function (entry) {
|
|
86
|
+
const key = entry[0];
|
|
87
|
+
const value = entry[1];
|
|
88
|
+
normalizedHeaders[key.toLowerCase()] = value;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const queryParams = { ...(event.queryStringParameters || {}) };
|
|
93
|
+
const originalPath = normalizedHeaders["x-router-original-path"];
|
|
94
|
+
const pathFromEvent = isV2 ? event.rawPath || event.path || "/" : event.path || "/";
|
|
95
|
+
const path = (originalPath && originalPath.trim())
|
|
96
|
+
? (originalPath.trim().startsWith("/") ? originalPath.trim() : "/" + originalPath.trim())
|
|
97
|
+
: pathFromEvent;
|
|
98
|
+
const host = event.headers && (event.headers.host || event.headers["host"]) || "localhost";
|
|
99
|
+
const url = new URL(path, "http://" + host);
|
|
100
|
+
Object.entries(queryParams).forEach(function (entry) {
|
|
101
|
+
url.searchParams.set(entry[0], entry[1]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
let bodyBuffer = null;
|
|
105
|
+
if (event.body) {
|
|
106
|
+
bodyBuffer = event.isBase64Encoded
|
|
107
|
+
? Buffer.from(event.body, "base64")
|
|
108
|
+
: Buffer.from(event.body, "utf8");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const requestUrl = "http://" + host + url.pathname + url.search;
|
|
112
|
+
const webRequest = new Request(requestUrl, {
|
|
113
|
+
method: method,
|
|
114
|
+
headers: normalizedHeaders,
|
|
115
|
+
body: bodyBuffer && bodyBuffer.length > 0 ? bodyBuffer : undefined,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
console.log("[handler] reconstructed request:", JSON.stringify({
|
|
119
|
+
method: method,
|
|
120
|
+
url: requestUrl,
|
|
121
|
+
path: url.pathname,
|
|
122
|
+
query: queryParams,
|
|
123
|
+
headers: normalizedHeaders,
|
|
124
|
+
bodyLength: bodyBuffer ? bodyBuffer.length : 0,
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
if (HTTP_METHODS.includes(method) && typeof mod[method] === "function") {
|
|
128
|
+
const response = await Promise.resolve(mod[method](webRequest));
|
|
129
|
+
await writeFetchResponseToStream(responseStream, response);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (handler && typeof handler.fetch === "function") {
|
|
134
|
+
const response = await handler.fetch(webRequest, context);
|
|
135
|
+
await writeFetchResponseToStream(responseStream, response);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (typeof handler !== "function") {
|
|
140
|
+
throw new TypeError(
|
|
141
|
+
"Handler must export a function (req, res), an object with fetch(request), or named GET/POST/etc. Got: " + typeof handler
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
var result = await runNodeStyleHandler(handler, method, url, normalizedHeaders, queryParams, bodyBuffer);
|
|
146
|
+
writeStreamFirstLine(responseStream, result.statusCode || 200, result.headers);
|
|
147
|
+
responseStream.write(result.body || "");
|
|
148
|
+
responseStream.end();
|
|
149
|
+
} catch (err) {
|
|
150
|
+
writeErrorToStream(responseStream, err);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function responseToLambda(response) {
|
|
155
|
+
const responseHeaders = getResponseHeaders(response);
|
|
156
|
+
return response.text().then(function (body) {
|
|
157
|
+
return {
|
|
158
|
+
statusCode: normalizeStatus(response.status),
|
|
159
|
+
headers: responseHeaders,
|
|
160
|
+
body: body,
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function runBufferedHandler(event, context) {
|
|
45
166
|
const mod = await getModule();
|
|
46
167
|
const handler = mod.default !== undefined ? mod.default : mod;
|
|
47
|
-
|
|
48
168
|
const isV2 = event.requestContext && event.requestContext.http;
|
|
49
169
|
const method = isV2 ? event.requestContext.http.method : event.httpMethod || "GET";
|
|
50
|
-
|
|
51
170
|
const normalizedHeaders = {};
|
|
52
171
|
if (event.headers) {
|
|
53
|
-
Object.entries(event.headers).forEach((
|
|
54
|
-
normalizedHeaders[
|
|
172
|
+
Object.entries(event.headers).forEach(function (entry) {
|
|
173
|
+
normalizedHeaders[entry[0].toLowerCase()] = entry[1];
|
|
55
174
|
});
|
|
56
175
|
}
|
|
57
|
-
|
|
58
176
|
const queryParams = { ...(event.queryStringParameters || {}) };
|
|
59
|
-
// Prefer original client path from router when present so Lambda sees the same URL the client requested (event.rawPath may be wrong if event was built by API Gateway or an older gateway)
|
|
60
177
|
const originalPath = normalizedHeaders["x-router-original-path"];
|
|
61
178
|
const pathFromEvent = isV2 ? event.rawPath || event.path || "/" : event.path || "/";
|
|
62
179
|
const path = (originalPath && originalPath.trim())
|
|
63
180
|
? (originalPath.trim().startsWith("/") ? originalPath.trim() : "/" + originalPath.trim())
|
|
64
181
|
: pathFromEvent;
|
|
65
|
-
const host = event.headers
|
|
182
|
+
const host = event.headers && (event.headers.host || event.headers["host"]) || "localhost";
|
|
66
183
|
const url = new URL(path, "http://" + host);
|
|
67
|
-
Object.entries(queryParams).forEach(([
|
|
68
|
-
url.searchParams.set(key, value);
|
|
69
|
-
});
|
|
70
|
-
|
|
184
|
+
Object.entries(queryParams).forEach(function (entry) { url.searchParams.set(entry[0], entry[1]); });
|
|
71
185
|
let bodyBuffer = null;
|
|
72
186
|
if (event.body) {
|
|
73
|
-
bodyBuffer = event.isBase64Encoded
|
|
74
|
-
? Buffer.from(event.body, "base64")
|
|
75
|
-
: Buffer.from(event.body, "utf8");
|
|
187
|
+
bodyBuffer = event.isBase64Encoded ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "utf8");
|
|
76
188
|
}
|
|
77
|
-
|
|
78
189
|
const requestUrl = "http://" + host + url.pathname + url.search;
|
|
79
190
|
const webRequest = new Request(requestUrl, {
|
|
80
191
|
method: method,
|
|
81
192
|
headers: normalizedHeaders,
|
|
82
193
|
body: bodyBuffer && bodyBuffer.length > 0 ? bodyBuffer : undefined,
|
|
83
194
|
});
|
|
84
|
-
|
|
85
|
-
const requestDetails = {
|
|
86
|
-
method: method,
|
|
87
|
-
url: requestUrl,
|
|
88
|
-
path: url.pathname,
|
|
89
|
-
query: queryParams,
|
|
90
|
-
headers: normalizedHeaders,
|
|
91
|
-
bodyLength: bodyBuffer ? bodyBuffer.length : 0,
|
|
92
|
-
};
|
|
93
|
-
console.log("[handler] reconstructed request:", JSON.stringify(requestDetails));
|
|
94
|
-
|
|
95
|
-
// Vercel: named method export (e.g. export function GET(request) { ... })
|
|
96
195
|
if (HTTP_METHODS.includes(method) && typeof mod[method] === "function") {
|
|
97
|
-
|
|
196
|
+
var response = await Promise.resolve(mod[method](webRequest));
|
|
98
197
|
return responseToLambda(response);
|
|
99
198
|
}
|
|
100
|
-
|
|
101
|
-
// Vercel/Nitro: default export with fetch(request)
|
|
102
199
|
if (handler && typeof handler.fetch === "function") {
|
|
103
|
-
|
|
200
|
+
var response = await handler.fetch(webRequest, context);
|
|
104
201
|
return responseToLambda(response);
|
|
105
202
|
}
|
|
106
|
-
|
|
107
|
-
// Node-style: default export as function (req, res), with Vercel-like helpers
|
|
108
203
|
if (typeof handler !== "function") {
|
|
109
|
-
throw new TypeError(
|
|
110
|
-
"Handler must export a function (req, res), an object with fetch(request), or named GET/POST/etc. Got: " + typeof handler
|
|
111
|
-
);
|
|
204
|
+
throw new TypeError("Handler must export a function (req, res), an object with fetch(request), or named GET/POST/etc. Got: " + typeof handler);
|
|
112
205
|
}
|
|
206
|
+
return runNodeStyleHandler(handler, method, url, normalizedHeaders, queryParams, bodyBuffer);
|
|
207
|
+
}
|
|
113
208
|
|
|
114
|
-
|
|
115
|
-
|
|
209
|
+
function runNodeStyleHandler(handler, method, url, normalizedHeaders, queryParams, bodyBuffer) {
|
|
210
|
+
return new Promise(function (resolve, reject) {
|
|
211
|
+
var _bodyParsed, cookiesObj;
|
|
116
212
|
function getParsedBody() {
|
|
117
213
|
if (_bodyParsed !== undefined) return _bodyParsed;
|
|
118
|
-
if (!bodyBuffer || bodyBuffer.length === 0)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
const ct = (normalizedHeaders["content-type"] || "").toLowerCase();
|
|
123
|
-
const text = bodyBuffer.toString("utf8");
|
|
214
|
+
if (!bodyBuffer || bodyBuffer.length === 0) return undefined;
|
|
215
|
+
var ct = (normalizedHeaders["content-type"] || "").toLowerCase();
|
|
216
|
+
var text = bodyBuffer.toString("utf8");
|
|
124
217
|
try {
|
|
125
|
-
if (ct.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
else if (ct.includes("text/plain")) _bodyParsed = text;
|
|
130
|
-
else if (ct.includes("application/octet-stream")) _bodyParsed = bodyBuffer;
|
|
131
|
-
else _bodyParsed = undefined;
|
|
218
|
+
if (ct.indexOf("application/json") !== -1) return _bodyParsed = JSON.parse(text);
|
|
219
|
+
if (ct.indexOf("application/x-www-form-urlencoded") !== -1) return _bodyParsed = Object.fromEntries(new URLSearchParams(text));
|
|
220
|
+
if (ct.indexOf("text/plain") !== -1) return _bodyParsed = text;
|
|
221
|
+
if (ct.indexOf("application/octet-stream") !== -1) return _bodyParsed = bodyBuffer;
|
|
132
222
|
} catch (e) {
|
|
133
|
-
if (ct.
|
|
134
|
-
_bodyParsed = undefined;
|
|
223
|
+
if (ct.indexOf("application/json") !== -1) throw e;
|
|
135
224
|
}
|
|
136
|
-
return _bodyParsed;
|
|
225
|
+
return _bodyParsed = undefined;
|
|
137
226
|
}
|
|
138
|
-
|
|
139
|
-
let cookiesObj = undefined;
|
|
140
227
|
function getCookies() {
|
|
141
228
|
if (cookiesObj !== undefined) return cookiesObj;
|
|
142
229
|
cookiesObj = {};
|
|
143
|
-
|
|
230
|
+
var cookieHeader = normalizedHeaders["cookie"];
|
|
144
231
|
if (cookieHeader) {
|
|
145
232
|
cookieHeader.split(";").forEach(function (part) {
|
|
146
|
-
|
|
147
|
-
if (eq > 0)
|
|
148
|
-
const key = part.slice(0, eq).trim();
|
|
149
|
-
const val = part.slice(eq + 1).trim();
|
|
150
|
-
cookiesObj[key] = val;
|
|
151
|
-
}
|
|
233
|
+
var eq = part.indexOf("=");
|
|
234
|
+
if (eq > 0) cookiesObj[part.slice(0, eq).trim()] = part.slice(eq + 1).trim();
|
|
152
235
|
});
|
|
153
236
|
}
|
|
154
237
|
return cookiesObj;
|
|
155
238
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
read() {
|
|
160
|
-
if (bodyBuffer) this.push(bodyBuffer);
|
|
161
|
-
this.push(null);
|
|
162
|
-
},
|
|
163
|
-
}),
|
|
164
|
-
{
|
|
165
|
-
method: method,
|
|
166
|
-
url: url.pathname + url.search,
|
|
167
|
-
headers: normalizedHeaders,
|
|
168
|
-
query: queryParams,
|
|
169
|
-
}
|
|
239
|
+
var req = Object.assign(
|
|
240
|
+
new Readable({ read: function () { if (bodyBuffer) this.push(bodyBuffer); this.push(null); } }),
|
|
241
|
+
{ method: method, url: url.pathname + url.search, headers: normalizedHeaders, query: queryParams }
|
|
170
242
|
);
|
|
171
|
-
Object.defineProperty(req, "body", {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
enumerable: true,
|
|
175
|
-
configurable: true,
|
|
176
|
-
});
|
|
177
|
-
Object.defineProperty(req, "cookies", {
|
|
178
|
-
get: getCookies,
|
|
179
|
-
set: function (val) { cookiesObj = val; },
|
|
180
|
-
enumerable: true,
|
|
181
|
-
configurable: true,
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
let statusCode = 200;
|
|
185
|
-
let statusMessage = "";
|
|
186
|
-
const headers = {};
|
|
187
|
-
let body = "";
|
|
188
|
-
let headersSent = false;
|
|
189
|
-
let resolved = false;
|
|
190
|
-
let _destroyed = false;
|
|
191
|
-
let _errored = null;
|
|
192
|
-
|
|
243
|
+
Object.defineProperty(req, "body", { get: getParsedBody, set: function (v) { _bodyParsed = v; }, enumerable: true, configurable: true });
|
|
244
|
+
Object.defineProperty(req, "cookies", { get: getCookies, set: function (v) { cookiesObj = v; }, enumerable: true, configurable: true });
|
|
245
|
+
var statusCode = 200, headers = {}, body = "", headersSent = false, resolved = false;
|
|
193
246
|
function finishRes() {
|
|
194
247
|
if (resolved) return;
|
|
195
248
|
resolved = true;
|
|
196
|
-
|
|
197
|
-
Object.entries(headers).forEach(([
|
|
198
|
-
out[k] = Array.isArray(v) ? v.join(", ") : v;
|
|
199
|
-
});
|
|
249
|
+
var out = {};
|
|
250
|
+
Object.entries(headers).forEach(function (e) { out[e[0]] = Array.isArray(e[1]) ? e[1].join(", ") : e[1]; });
|
|
200
251
|
resolve({ statusCode: statusCode || 200, headers: out, body: body });
|
|
201
252
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
getHeader: (key) => headers[key.toLowerCase()],
|
|
208
|
-
removeHeader: (key) => { delete headers[key.toLowerCase()]; },
|
|
209
|
-
appendHeader: (key, value) => {
|
|
253
|
+
var res = Object.assign(new EventEmitter(), {
|
|
254
|
+
setHeader: function (k, v) { if (!headersSent) headers[k.toLowerCase()] = v; },
|
|
255
|
+
getHeader: function (k) { return headers[k.toLowerCase()]; },
|
|
256
|
+
removeHeader: function (k) { delete headers[k.toLowerCase()]; },
|
|
257
|
+
appendHeader: function (k, v) {
|
|
210
258
|
if (!headersSent) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
headers[lowerKey] = existing
|
|
214
|
-
? (Array.isArray(existing) ? existing.concat(value) : [existing, value])
|
|
215
|
-
: value;
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
hasHeader: (key) => key.toLowerCase() in headers,
|
|
219
|
-
getHeaderNames: () => Object.keys(headers),
|
|
220
|
-
writeHead: (code, responseHeaders) => {
|
|
221
|
-
if (!headersSent) {
|
|
222
|
-
statusCode = code;
|
|
223
|
-
if (responseHeaders) {
|
|
224
|
-
Object.entries(responseHeaders).forEach(([key, value]) => {
|
|
225
|
-
headers[key.toLowerCase()] = value;
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
headersSent = true;
|
|
229
|
-
}
|
|
230
|
-
return res;
|
|
231
|
-
},
|
|
232
|
-
flushHeaders: () => {
|
|
233
|
-
if (!headersSent) headersSent = true;
|
|
234
|
-
return res;
|
|
235
|
-
},
|
|
236
|
-
write: (chunk) => {
|
|
237
|
-
if (!headersSent) headersSent = true;
|
|
238
|
-
body += chunk.toString();
|
|
239
|
-
return true;
|
|
240
|
-
},
|
|
241
|
-
end: (chunk) => {
|
|
242
|
-
if (chunk) body += chunk.toString();
|
|
243
|
-
if (!resolved) {
|
|
244
|
-
finishRes();
|
|
245
|
-
res.emit("finish");
|
|
246
|
-
res.emit("close");
|
|
259
|
+
var l = k.toLowerCase();
|
|
260
|
+
headers[l] = headers[l] ? (Array.isArray(headers[l]) ? headers[l].concat(v) : [headers[l], v]) : v;
|
|
247
261
|
}
|
|
248
262
|
},
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
res.emit("close");
|
|
254
|
-
reject(_errored ?? new Error("Response destroyed"));
|
|
263
|
+
hasHeader: function (k) { return k.toLowerCase() in headers; },
|
|
264
|
+
getHeaderNames: function () { return Object.keys(headers); },
|
|
265
|
+
writeHead: function (code, h) {
|
|
266
|
+
if (!headersSent) { statusCode = code; if (h) Object.entries(h).forEach(function (e) { headers[e[0].toLowerCase()] = e[1]; }); headersSent = true; }
|
|
255
267
|
return res;
|
|
256
268
|
},
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
},
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
body = val == null ? "" : String(val);
|
|
268
|
-
}
|
|
269
|
-
finishRes();
|
|
270
|
-
return res;
|
|
271
|
-
},
|
|
272
|
-
json: (obj) => {
|
|
273
|
-
headers["content-type"] = "application/json";
|
|
274
|
-
body = JSON.stringify(obj);
|
|
275
|
-
finishRes();
|
|
276
|
-
return res;
|
|
269
|
+
flushHeaders: function () { headersSent = true; return res; },
|
|
270
|
+
write: function (chunk) { headersSent = true; body += chunk.toString(); return true; },
|
|
271
|
+
end: function (chunk) { if (chunk) body += chunk.toString(); if (!resolved) { finishRes(); res.emit("finish"); res.emit("close"); } },
|
|
272
|
+
destroy: function (err) { if (!resolved) { res.emit("close"); reject(err || new Error("Response destroyed")); } return res; },
|
|
273
|
+
flush: function () {},
|
|
274
|
+
status: function (c) { statusCode = c; return res; },
|
|
275
|
+
send: function (val) {
|
|
276
|
+
if (typeof val === "object" && val !== null && !Buffer.isBuffer(val)) { if (!headers["content-type"]) headers["content-type"] = "application/json"; body = JSON.stringify(val); }
|
|
277
|
+
else body = val == null ? "" : String(val);
|
|
278
|
+
finishRes(); return res;
|
|
277
279
|
},
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const loc = typeof a === "number" ? b : a;
|
|
281
|
-
statusCode = code;
|
|
282
|
-
headers["location"] = loc;
|
|
283
|
-
finishRes();
|
|
284
|
-
return res;
|
|
285
|
-
},
|
|
286
|
-
get writableFinished() { return resolved; },
|
|
287
|
-
get errored() { return _errored; },
|
|
288
|
-
get destroyed() { return _destroyed; },
|
|
289
|
-
get statusMessage() { return statusMessage; },
|
|
290
|
-
set statusMessage(value) { statusMessage = value; },
|
|
280
|
+
json: function (obj) { headers["content-type"] = "application/json"; body = JSON.stringify(obj); finishRes(); return res; },
|
|
281
|
+
redirect: function (a, b) { statusCode = typeof a === "number" ? a : 307; headers["location"] = typeof a === "number" ? b : a; finishRes(); return res; },
|
|
291
282
|
headersSent: false,
|
|
292
283
|
});
|
|
293
|
-
|
|
294
|
-
Object.defineProperty(res, "statusCode", {
|
|
295
|
-
get: () => statusCode,
|
|
296
|
-
set: (value) => { statusCode = value; },
|
|
297
|
-
enumerable: true,
|
|
298
|
-
configurable: true,
|
|
299
|
-
});
|
|
300
|
-
|
|
284
|
+
Object.defineProperty(res, "statusCode", { get: function () { return statusCode; }, set: function (v) { statusCode = v; }, enumerable: true, configurable: true });
|
|
301
285
|
req.on("error", reject);
|
|
302
286
|
res.on("error", reject);
|
|
303
|
-
|
|
304
|
-
try {
|
|
305
|
-
handler(req, res);
|
|
306
|
-
} catch (error) {
|
|
307
|
-
reject(error);
|
|
308
|
-
}
|
|
287
|
+
try { handler(req, res); } catch (e) { reject(e); }
|
|
309
288
|
});
|
|
310
|
-
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
module.exports.handler = typeof awslambda !== "undefined" && awslambda.streamifyResponse
|
|
292
|
+
? awslambda.streamifyResponse(runStreamingHandler)
|
|
293
|
+
: runBufferedHandler;
|
|
311
294
|
`;
|
|
312
295
|
}
|
|
313
296
|
//# sourceMappingURL=handler-wrapper.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler-wrapper.js","sourceRoot":"","sources":["../src/handler-wrapper.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"handler-wrapper.js","sourceRoot":"","sources":["../src/handler-wrapper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,WAAmB;IACxD,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3E,OAAO;;;;;;;;;;;iCAWwB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiR3C,CAAC;AACF,CAAC"}
|
|
@@ -49,5 +49,24 @@ describe("handler-wrapper", () => {
|
|
|
49
49
|
expect(result.statusCode).toBe(200);
|
|
50
50
|
expect(result.body).toBe("ok");
|
|
51
51
|
});
|
|
52
|
+
it("buffered path: Fetch GET returning Response with text body returns status and body", async () => {
|
|
53
|
+
const wrapperCode = generateHandlerWrapper("index.mjs");
|
|
54
|
+
fs.writeFileSync(path.join(tmpDir, "handler.js"), wrapperCode, "utf8");
|
|
55
|
+
fs.writeFileSync(path.join(tmpDir, "index.mjs"), `export function GET() {
|
|
56
|
+
return new Response("hello world", { status: 200, headers: { "content-type": "text/plain" } });
|
|
57
|
+
}`, "utf8");
|
|
58
|
+
const handler = require(path.join(tmpDir, "handler.js")).handler;
|
|
59
|
+
const mockEvent = {
|
|
60
|
+
requestContext: { http: { method: "GET" } },
|
|
61
|
+
headers: { host: "localhost" },
|
|
62
|
+
rawPath: "/",
|
|
63
|
+
};
|
|
64
|
+
const result = await handler(mockEvent, {});
|
|
65
|
+
expect(result).toBeDefined();
|
|
66
|
+
expect(result.statusCode).toBe(200);
|
|
67
|
+
expect(result.body).toBe("hello world");
|
|
68
|
+
expect(result.headers).toBeDefined();
|
|
69
|
+
expect(result.headers["content-type"]).toBe("text/plain");
|
|
70
|
+
});
|
|
52
71
|
});
|
|
53
72
|
//# sourceMappingURL=handler-wrapper.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler-wrapper.test.js","sourceRoot":"","sources":["../src/handler-wrapper.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"handler-wrapper.test.js","sourceRoot":"","sources":["../src/handler-wrapper.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,IAAI,MAAc,CAAC;IACnB,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE/C,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,uBAAuB,CAAC,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;QACrF,MAAM,WAAW,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC;QACxD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;QACvE,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,EAC9B;;;QAGE,EACF,MAAM,CACP,CAAC;QAEF,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QACjE,MAAM,SAAS,GAAG;YAChB,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;YAC3C,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;YAC9B,OAAO,EAAE,GAAG;SACb,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAE5C,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,WAAW,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC;QACxD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;QACvE,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,EAC9B;;;QAGE,EACF,MAAM,CACP,CAAC;QAEF,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QACjE,MAAM,SAAS,GAAG;YAChB,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;YAC3C,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;YAC9B,OAAO,EAAE,GAAG;SACb,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAE5C,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oFAAoF,EAAE,KAAK,IAAI,EAAE;QAClG,MAAM,WAAW,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC;QACxD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;QACvE,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,EAC9B;;QAEE,EACF,MAAM,CACP,CAAC;QAEF,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QACjE,MAAM,SAAS,GAAG;YAChB,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;YAC3C,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;YAC9B,OAAO,EAAE,GAAG;SACb,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAE5C,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|