@easel-sh/cli 0.1.0-alpha.12 → 0.1.0-alpha.13
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,CAsRlE"}
|
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,264 @@ 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
|
+
await pipeline(nodeReadable, responseStream);
|
|
59
|
+
} else {
|
|
60
|
+
const body = await response.text();
|
|
61
|
+
responseStream.write(body);
|
|
62
|
+
responseStream.end();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function runStreamingHandler(event, responseStream, context) {
|
|
67
|
+
try {
|
|
68
|
+
const mod = await getModule();
|
|
69
|
+
const handler = mod.default !== undefined ? mod.default : mod;
|
|
70
|
+
|
|
71
|
+
const isV2 = event.requestContext && event.requestContext.http;
|
|
72
|
+
const method = isV2 ? event.requestContext.http.method : event.httpMethod || "GET";
|
|
73
|
+
|
|
74
|
+
const normalizedHeaders = {};
|
|
75
|
+
if (event.headers) {
|
|
76
|
+
Object.entries(event.headers).forEach(function (entry) {
|
|
77
|
+
const key = entry[0];
|
|
78
|
+
const value = entry[1];
|
|
79
|
+
normalizedHeaders[key.toLowerCase()] = value;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const queryParams = { ...(event.queryStringParameters || {}) };
|
|
84
|
+
const originalPath = normalizedHeaders["x-router-original-path"];
|
|
85
|
+
const pathFromEvent = isV2 ? event.rawPath || event.path || "/" : event.path || "/";
|
|
86
|
+
const path = (originalPath && originalPath.trim())
|
|
87
|
+
? (originalPath.trim().startsWith("/") ? originalPath.trim() : "/" + originalPath.trim())
|
|
88
|
+
: pathFromEvent;
|
|
89
|
+
const host = event.headers && (event.headers.host || event.headers["host"]) || "localhost";
|
|
90
|
+
const url = new URL(path, "http://" + host);
|
|
91
|
+
Object.entries(queryParams).forEach(function (entry) {
|
|
92
|
+
url.searchParams.set(entry[0], entry[1]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
let bodyBuffer = null;
|
|
96
|
+
if (event.body) {
|
|
97
|
+
bodyBuffer = event.isBase64Encoded
|
|
98
|
+
? Buffer.from(event.body, "base64")
|
|
99
|
+
: Buffer.from(event.body, "utf8");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const requestUrl = "http://" + host + url.pathname + url.search;
|
|
103
|
+
const webRequest = new Request(requestUrl, {
|
|
104
|
+
method: method,
|
|
105
|
+
headers: normalizedHeaders,
|
|
106
|
+
body: bodyBuffer && bodyBuffer.length > 0 ? bodyBuffer : undefined,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
console.log("[handler] reconstructed request:", JSON.stringify({
|
|
110
|
+
method: method,
|
|
111
|
+
url: requestUrl,
|
|
112
|
+
path: url.pathname,
|
|
113
|
+
query: queryParams,
|
|
114
|
+
headers: normalizedHeaders,
|
|
115
|
+
bodyLength: bodyBuffer ? bodyBuffer.length : 0,
|
|
116
|
+
}));
|
|
117
|
+
|
|
118
|
+
if (HTTP_METHODS.includes(method) && typeof mod[method] === "function") {
|
|
119
|
+
const response = await Promise.resolve(mod[method](webRequest));
|
|
120
|
+
await writeFetchResponseToStream(responseStream, response);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (handler && typeof handler.fetch === "function") {
|
|
125
|
+
const response = await handler.fetch(webRequest, context);
|
|
126
|
+
await writeFetchResponseToStream(responseStream, response);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (typeof handler !== "function") {
|
|
131
|
+
throw new TypeError(
|
|
132
|
+
"Handler must export a function (req, res), an object with fetch(request), or named GET/POST/etc. Got: " + typeof handler
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
var result = await runNodeStyleHandler(handler, method, url, normalizedHeaders, queryParams, bodyBuffer);
|
|
137
|
+
writeStreamFirstLine(responseStream, result.statusCode || 200, result.headers);
|
|
138
|
+
responseStream.write(result.body || "");
|
|
139
|
+
responseStream.end();
|
|
140
|
+
} catch (err) {
|
|
141
|
+
writeErrorToStream(responseStream, err);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function responseToLambda(response) {
|
|
146
|
+
const responseHeaders = getResponseHeaders(response);
|
|
147
|
+
return response.text().then(function (body) {
|
|
148
|
+
return {
|
|
149
|
+
statusCode: normalizeStatus(response.status),
|
|
150
|
+
headers: responseHeaders,
|
|
151
|
+
body: body,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function runBufferedHandler(event, context) {
|
|
45
157
|
const mod = await getModule();
|
|
46
158
|
const handler = mod.default !== undefined ? mod.default : mod;
|
|
47
|
-
|
|
48
159
|
const isV2 = event.requestContext && event.requestContext.http;
|
|
49
160
|
const method = isV2 ? event.requestContext.http.method : event.httpMethod || "GET";
|
|
50
|
-
|
|
51
161
|
const normalizedHeaders = {};
|
|
52
162
|
if (event.headers) {
|
|
53
|
-
Object.entries(event.headers).forEach((
|
|
54
|
-
normalizedHeaders[
|
|
163
|
+
Object.entries(event.headers).forEach(function (entry) {
|
|
164
|
+
normalizedHeaders[entry[0].toLowerCase()] = entry[1];
|
|
55
165
|
});
|
|
56
166
|
}
|
|
57
|
-
|
|
58
167
|
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
168
|
const originalPath = normalizedHeaders["x-router-original-path"];
|
|
61
169
|
const pathFromEvent = isV2 ? event.rawPath || event.path || "/" : event.path || "/";
|
|
62
170
|
const path = (originalPath && originalPath.trim())
|
|
63
171
|
? (originalPath.trim().startsWith("/") ? originalPath.trim() : "/" + originalPath.trim())
|
|
64
172
|
: pathFromEvent;
|
|
65
|
-
const host = event.headers
|
|
173
|
+
const host = event.headers && (event.headers.host || event.headers["host"]) || "localhost";
|
|
66
174
|
const url = new URL(path, "http://" + host);
|
|
67
|
-
Object.entries(queryParams).forEach(([
|
|
68
|
-
url.searchParams.set(key, value);
|
|
69
|
-
});
|
|
70
|
-
|
|
175
|
+
Object.entries(queryParams).forEach(function (entry) { url.searchParams.set(entry[0], entry[1]); });
|
|
71
176
|
let bodyBuffer = null;
|
|
72
177
|
if (event.body) {
|
|
73
|
-
bodyBuffer = event.isBase64Encoded
|
|
74
|
-
? Buffer.from(event.body, "base64")
|
|
75
|
-
: Buffer.from(event.body, "utf8");
|
|
178
|
+
bodyBuffer = event.isBase64Encoded ? Buffer.from(event.body, "base64") : Buffer.from(event.body, "utf8");
|
|
76
179
|
}
|
|
77
|
-
|
|
78
180
|
const requestUrl = "http://" + host + url.pathname + url.search;
|
|
79
181
|
const webRequest = new Request(requestUrl, {
|
|
80
182
|
method: method,
|
|
81
183
|
headers: normalizedHeaders,
|
|
82
184
|
body: bodyBuffer && bodyBuffer.length > 0 ? bodyBuffer : undefined,
|
|
83
185
|
});
|
|
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
186
|
if (HTTP_METHODS.includes(method) && typeof mod[method] === "function") {
|
|
97
|
-
|
|
187
|
+
var response = await Promise.resolve(mod[method](webRequest));
|
|
98
188
|
return responseToLambda(response);
|
|
99
189
|
}
|
|
100
|
-
|
|
101
|
-
// Vercel/Nitro: default export with fetch(request)
|
|
102
190
|
if (handler && typeof handler.fetch === "function") {
|
|
103
|
-
|
|
191
|
+
var response = await handler.fetch(webRequest, context);
|
|
104
192
|
return responseToLambda(response);
|
|
105
193
|
}
|
|
106
|
-
|
|
107
|
-
// Node-style: default export as function (req, res), with Vercel-like helpers
|
|
108
194
|
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
|
-
);
|
|
195
|
+
throw new TypeError("Handler must export a function (req, res), an object with fetch(request), or named GET/POST/etc. Got: " + typeof handler);
|
|
112
196
|
}
|
|
197
|
+
return runNodeStyleHandler(handler, method, url, normalizedHeaders, queryParams, bodyBuffer);
|
|
198
|
+
}
|
|
113
199
|
|
|
114
|
-
|
|
115
|
-
|
|
200
|
+
function runNodeStyleHandler(handler, method, url, normalizedHeaders, queryParams, bodyBuffer) {
|
|
201
|
+
return new Promise(function (resolve, reject) {
|
|
202
|
+
var _bodyParsed, cookiesObj;
|
|
116
203
|
function getParsedBody() {
|
|
117
204
|
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");
|
|
205
|
+
if (!bodyBuffer || bodyBuffer.length === 0) return undefined;
|
|
206
|
+
var ct = (normalizedHeaders["content-type"] || "").toLowerCase();
|
|
207
|
+
var text = bodyBuffer.toString("utf8");
|
|
124
208
|
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;
|
|
209
|
+
if (ct.indexOf("application/json") !== -1) return _bodyParsed = JSON.parse(text);
|
|
210
|
+
if (ct.indexOf("application/x-www-form-urlencoded") !== -1) return _bodyParsed = Object.fromEntries(new URLSearchParams(text));
|
|
211
|
+
if (ct.indexOf("text/plain") !== -1) return _bodyParsed = text;
|
|
212
|
+
if (ct.indexOf("application/octet-stream") !== -1) return _bodyParsed = bodyBuffer;
|
|
132
213
|
} catch (e) {
|
|
133
|
-
if (ct.
|
|
134
|
-
_bodyParsed = undefined;
|
|
214
|
+
if (ct.indexOf("application/json") !== -1) throw e;
|
|
135
215
|
}
|
|
136
|
-
return _bodyParsed;
|
|
216
|
+
return _bodyParsed = undefined;
|
|
137
217
|
}
|
|
138
|
-
|
|
139
|
-
let cookiesObj = undefined;
|
|
140
218
|
function getCookies() {
|
|
141
219
|
if (cookiesObj !== undefined) return cookiesObj;
|
|
142
220
|
cookiesObj = {};
|
|
143
|
-
|
|
221
|
+
var cookieHeader = normalizedHeaders["cookie"];
|
|
144
222
|
if (cookieHeader) {
|
|
145
223
|
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
|
-
}
|
|
224
|
+
var eq = part.indexOf("=");
|
|
225
|
+
if (eq > 0) cookiesObj[part.slice(0, eq).trim()] = part.slice(eq + 1).trim();
|
|
152
226
|
});
|
|
153
227
|
}
|
|
154
228
|
return cookiesObj;
|
|
155
229
|
}
|
|
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
|
-
}
|
|
230
|
+
var req = Object.assign(
|
|
231
|
+
new Readable({ read: function () { if (bodyBuffer) this.push(bodyBuffer); this.push(null); } }),
|
|
232
|
+
{ method: method, url: url.pathname + url.search, headers: normalizedHeaders, query: queryParams }
|
|
170
233
|
);
|
|
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
|
-
|
|
234
|
+
Object.defineProperty(req, "body", { get: getParsedBody, set: function (v) { _bodyParsed = v; }, enumerable: true, configurable: true });
|
|
235
|
+
Object.defineProperty(req, "cookies", { get: getCookies, set: function (v) { cookiesObj = v; }, enumerable: true, configurable: true });
|
|
236
|
+
var statusCode = 200, headers = {}, body = "", headersSent = false, resolved = false;
|
|
193
237
|
function finishRes() {
|
|
194
238
|
if (resolved) return;
|
|
195
239
|
resolved = true;
|
|
196
|
-
|
|
197
|
-
Object.entries(headers).forEach(([
|
|
198
|
-
out[k] = Array.isArray(v) ? v.join(", ") : v;
|
|
199
|
-
});
|
|
240
|
+
var out = {};
|
|
241
|
+
Object.entries(headers).forEach(function (e) { out[e[0]] = Array.isArray(e[1]) ? e[1].join(", ") : e[1]; });
|
|
200
242
|
resolve({ statusCode: statusCode || 200, headers: out, body: body });
|
|
201
243
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
getHeader: (key) => headers[key.toLowerCase()],
|
|
208
|
-
removeHeader: (key) => { delete headers[key.toLowerCase()]; },
|
|
209
|
-
appendHeader: (key, value) => {
|
|
244
|
+
var res = Object.assign(new EventEmitter(), {
|
|
245
|
+
setHeader: function (k, v) { if (!headersSent) headers[k.toLowerCase()] = v; },
|
|
246
|
+
getHeader: function (k) { return headers[k.toLowerCase()]; },
|
|
247
|
+
removeHeader: function (k) { delete headers[k.toLowerCase()]; },
|
|
248
|
+
appendHeader: function (k, v) {
|
|
210
249
|
if (!headersSent) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
headers[lowerKey] = existing
|
|
214
|
-
? (Array.isArray(existing) ? existing.concat(value) : [existing, value])
|
|
215
|
-
: value;
|
|
250
|
+
var l = k.toLowerCase();
|
|
251
|
+
headers[l] = headers[l] ? (Array.isArray(headers[l]) ? headers[l].concat(v) : [headers[l], v]) : v;
|
|
216
252
|
}
|
|
217
253
|
},
|
|
218
|
-
hasHeader: (
|
|
219
|
-
getHeaderNames: ()
|
|
220
|
-
writeHead: (code,
|
|
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;
|
|
254
|
+
hasHeader: function (k) { return k.toLowerCase() in headers; },
|
|
255
|
+
getHeaderNames: function () { return Object.keys(headers); },
|
|
256
|
+
writeHead: function (code, h) {
|
|
257
|
+
if (!headersSent) { statusCode = code; if (h) Object.entries(h).forEach(function (e) { headers[e[0].toLowerCase()] = e[1]; }); headersSent = true; }
|
|
234
258
|
return res;
|
|
235
259
|
},
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
260
|
+
flushHeaders: function () { headersSent = true; return res; },
|
|
261
|
+
write: function (chunk) { headersSent = true; body += chunk.toString(); return true; },
|
|
262
|
+
end: function (chunk) { if (chunk) body += chunk.toString(); if (!resolved) { finishRes(); res.emit("finish"); res.emit("close"); } },
|
|
263
|
+
destroy: function (err) { if (!resolved) { res.emit("close"); reject(err || new Error("Response destroyed")); } return res; },
|
|
264
|
+
flush: function () {},
|
|
265
|
+
status: function (c) { statusCode = c; return res; },
|
|
266
|
+
send: function (val) {
|
|
267
|
+
if (typeof val === "object" && val !== null && !Buffer.isBuffer(val)) { if (!headers["content-type"]) headers["content-type"] = "application/json"; body = JSON.stringify(val); }
|
|
268
|
+
else body = val == null ? "" : String(val);
|
|
269
|
+
finishRes(); return res;
|
|
240
270
|
},
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (!resolved) {
|
|
244
|
-
finishRes();
|
|
245
|
-
res.emit("finish");
|
|
246
|
-
res.emit("close");
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
destroy: (err) => {
|
|
250
|
-
if (resolved) return res;
|
|
251
|
-
_destroyed = true;
|
|
252
|
-
_errored = err ?? null;
|
|
253
|
-
res.emit("close");
|
|
254
|
-
reject(_errored ?? new Error("Response destroyed"));
|
|
255
|
-
return res;
|
|
256
|
-
},
|
|
257
|
-
flush: () => { /* no-op for compression middleware */ },
|
|
258
|
-
status: (code) => {
|
|
259
|
-
statusCode = code;
|
|
260
|
-
return res;
|
|
261
|
-
},
|
|
262
|
-
send: (val) => {
|
|
263
|
-
if (typeof val === "object" && val !== null && !Buffer.isBuffer(val)) {
|
|
264
|
-
if (!headers["content-type"]) headers["content-type"] = "application/json";
|
|
265
|
-
body = JSON.stringify(val);
|
|
266
|
-
} else {
|
|
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;
|
|
277
|
-
},
|
|
278
|
-
redirect: (a, b) => {
|
|
279
|
-
const code = typeof a === "number" ? a : 307;
|
|
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; },
|
|
271
|
+
json: function (obj) { headers["content-type"] = "application/json"; body = JSON.stringify(obj); finishRes(); return res; },
|
|
272
|
+
redirect: function (a, b) { statusCode = typeof a === "number" ? a : 307; headers["location"] = typeof a === "number" ? b : a; finishRes(); return res; },
|
|
291
273
|
headersSent: false,
|
|
292
274
|
});
|
|
293
|
-
|
|
294
|
-
Object.defineProperty(res, "statusCode", {
|
|
295
|
-
get: () => statusCode,
|
|
296
|
-
set: (value) => { statusCode = value; },
|
|
297
|
-
enumerable: true,
|
|
298
|
-
configurable: true,
|
|
299
|
-
});
|
|
300
|
-
|
|
275
|
+
Object.defineProperty(res, "statusCode", { get: function () { return statusCode; }, set: function (v) { statusCode = v; }, enumerable: true, configurable: true });
|
|
301
276
|
req.on("error", reject);
|
|
302
277
|
res.on("error", reject);
|
|
303
|
-
|
|
304
|
-
try {
|
|
305
|
-
handler(req, res);
|
|
306
|
-
} catch (error) {
|
|
307
|
-
reject(error);
|
|
308
|
-
}
|
|
278
|
+
try { handler(req, res); } catch (e) { reject(e); }
|
|
309
279
|
});
|
|
310
|
-
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
module.exports.handler = typeof awslambda !== "undefined" && awslambda.streamifyResponse
|
|
283
|
+
? awslambda.streamifyResponse(runStreamingHandler)
|
|
284
|
+
: runBufferedHandler;
|
|
311
285
|
`;
|
|
312
286
|
}
|
|
313
287
|
//# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwQ3C,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":"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"}
|