sst-http 1.3.2 → 1.3.3-beta.2
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.
- package/README.md +87 -28
- package/dist/bus/index.cjs +185 -0
- package/dist/bus/index.d.cts +7 -0
- package/dist/bus/index.d.ts +7 -0
- package/dist/bus/index.js +9 -0
- package/dist/{chunk-5MOJ3SW6.js → chunk-5OUNKYO5.js} +1 -1
- package/dist/chunk-LLR3DQ65.js +140 -0
- package/dist/chunk-SENBWWVV.js +499 -0
- package/dist/chunk-YMGEGOSD.js +117 -0
- package/dist/cli.cjs +49 -13
- package/dist/cli.js +49 -13
- package/dist/handler-DaM4Racx.d.cts +4 -0
- package/dist/handler-DaM4Racx.d.ts +4 -0
- package/dist/http/index.cjs +644 -0
- package/dist/http/index.d.cts +51 -0
- package/dist/http/index.d.ts +51 -0
- package/dist/http/index.js +53 -0
- package/dist/index.cjs +204 -14
- package/dist/index.d.cts +6 -45
- package/dist/index.d.ts +6 -45
- package/dist/index.js +33 -517
- package/dist/infra.cjs +273 -126
- package/dist/infra.d.cts +35 -18
- package/dist/infra.d.ts +35 -18
- package/dist/infra.js +268 -121
- package/dist/{types-BF3w-wTx.d.cts → types-w1A7o_rd.d.cts} +5 -1
- package/dist/{types-BF3w-wTx.d.ts → types-w1A7o_rd.d.ts} +5 -1
- package/package.json +16 -5
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeRouterPath
|
|
3
|
+
} from "./chunk-5OUNKYO5.js";
|
|
4
|
+
import {
|
|
5
|
+
getRegisteredEvents,
|
|
6
|
+
getRegisteredRoutes,
|
|
7
|
+
registerFirebaseAuth,
|
|
8
|
+
registerParameter,
|
|
9
|
+
registerRoute,
|
|
10
|
+
resolveHandler
|
|
11
|
+
} from "./chunk-YMGEGOSD.js";
|
|
12
|
+
|
|
13
|
+
// src/http/router.ts
|
|
14
|
+
import { match } from "path-to-regexp";
|
|
15
|
+
var Router = class {
|
|
16
|
+
routes;
|
|
17
|
+
constructor(entries) {
|
|
18
|
+
this.routes = entries.map((entry) => ({
|
|
19
|
+
entry,
|
|
20
|
+
matcher: match(normalizeRouterPath(entry.path), { decode: decodeURIComponent })
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
find(method, pathname) {
|
|
24
|
+
const allowed = /* @__PURE__ */ new Set();
|
|
25
|
+
for (const route of this.routes) {
|
|
26
|
+
const result = route.matcher(pathname);
|
|
27
|
+
if (!result) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
allowed.add(route.entry.method);
|
|
31
|
+
if (route.entry.method === method) {
|
|
32
|
+
return {
|
|
33
|
+
type: "found",
|
|
34
|
+
entry: route.entry,
|
|
35
|
+
params: normalizeParams(result.params)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (allowed.size > 0) {
|
|
40
|
+
return {
|
|
41
|
+
type: "method-not-allowed",
|
|
42
|
+
allowedMethods: [...allowed]
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return void 0;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
function normalizeParams(params) {
|
|
49
|
+
const normalized = {};
|
|
50
|
+
for (const [key, value] of Object.entries(params)) {
|
|
51
|
+
normalized[key] = Array.isArray(value) ? value[value.length - 1] ?? "" : value;
|
|
52
|
+
}
|
|
53
|
+
return normalized;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/http/runtime.ts
|
|
57
|
+
var HTTP_ERROR_MARKER = Symbol.for("sst-http.HttpError");
|
|
58
|
+
var HttpError = class extends Error {
|
|
59
|
+
statusCode;
|
|
60
|
+
headers;
|
|
61
|
+
details;
|
|
62
|
+
constructor(statusCode, message, options) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.name = "HttpError";
|
|
65
|
+
Object.defineProperty(this, HTTP_ERROR_MARKER, { value: true });
|
|
66
|
+
this.statusCode = statusCode;
|
|
67
|
+
this.headers = options?.headers;
|
|
68
|
+
this.details = options?.details;
|
|
69
|
+
if ("cause" in (options ?? {})) {
|
|
70
|
+
this.cause = options?.cause;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
function json(status, data, headers = {}) {
|
|
75
|
+
return {
|
|
76
|
+
statusCode: status,
|
|
77
|
+
body: JSON.stringify(data ?? null),
|
|
78
|
+
headers: {
|
|
79
|
+
"content-type": "application/json; charset=utf-8",
|
|
80
|
+
...headers
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function text(status, body, headers = {}) {
|
|
85
|
+
return {
|
|
86
|
+
statusCode: status,
|
|
87
|
+
body,
|
|
88
|
+
headers: {
|
|
89
|
+
"content-type": "text/plain; charset=utf-8",
|
|
90
|
+
...headers
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function noContent(headers = {}) {
|
|
95
|
+
return {
|
|
96
|
+
statusCode: 204,
|
|
97
|
+
headers,
|
|
98
|
+
body: ""
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function createHandler() {
|
|
102
|
+
const routes = getRegisteredRoutes();
|
|
103
|
+
const router = new Router(routes);
|
|
104
|
+
const eventHandlers = getRegisteredEvents();
|
|
105
|
+
return async (event, lambdaContext) => {
|
|
106
|
+
if (isEventBridgeEvent(event)) {
|
|
107
|
+
await handleEventBridgeEvent(event, eventHandlers, lambdaContext);
|
|
108
|
+
return {
|
|
109
|
+
statusCode: 200,
|
|
110
|
+
body: ""
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const httpEvent = event;
|
|
114
|
+
const method = extractMethod(httpEvent);
|
|
115
|
+
const path = extractPath(httpEvent);
|
|
116
|
+
const preferV2 = isHttpApiEvent(httpEvent);
|
|
117
|
+
if (!method || !path) {
|
|
118
|
+
return formatResponse(text(400, "Invalid request"), preferV2);
|
|
119
|
+
}
|
|
120
|
+
const normalizedMethod = method.toUpperCase();
|
|
121
|
+
if (!isSupportedMethod(normalizedMethod)) {
|
|
122
|
+
return formatResponse(text(405, "Method Not Allowed"), preferV2);
|
|
123
|
+
}
|
|
124
|
+
const match2 = router.find(normalizedMethod, path);
|
|
125
|
+
if (!match2) {
|
|
126
|
+
return formatResponse(text(404, "Not Found"), preferV2);
|
|
127
|
+
}
|
|
128
|
+
if (match2.type === "method-not-allowed") {
|
|
129
|
+
return formatResponse({
|
|
130
|
+
statusCode: 405,
|
|
131
|
+
headers: {
|
|
132
|
+
Allow: match2.allowedMethods.join(", ")
|
|
133
|
+
},
|
|
134
|
+
body: ""
|
|
135
|
+
}, preferV2);
|
|
136
|
+
}
|
|
137
|
+
const { entry, params } = match2;
|
|
138
|
+
const headers = normalizeHeaders(httpEvent.headers ?? {});
|
|
139
|
+
const query = extractQuery(httpEvent);
|
|
140
|
+
let bodyValue = void 0;
|
|
141
|
+
let bodyParsed = false;
|
|
142
|
+
const requiresJson = entry.parameters.some((p) => p.type === "body");
|
|
143
|
+
const ensureBody = () => {
|
|
144
|
+
if (!bodyParsed) {
|
|
145
|
+
bodyValue = parseBody(httpEvent, headers, requiresJson);
|
|
146
|
+
bodyParsed = true;
|
|
147
|
+
}
|
|
148
|
+
return bodyValue;
|
|
149
|
+
};
|
|
150
|
+
const ctxResponse = {
|
|
151
|
+
json,
|
|
152
|
+
text,
|
|
153
|
+
noContent
|
|
154
|
+
};
|
|
155
|
+
const ctx = {
|
|
156
|
+
event: httpEvent,
|
|
157
|
+
lambdaContext,
|
|
158
|
+
params,
|
|
159
|
+
query,
|
|
160
|
+
body: void 0,
|
|
161
|
+
headers,
|
|
162
|
+
auth: extractAuthClaims(httpEvent, entry),
|
|
163
|
+
response: ctxResponse
|
|
164
|
+
};
|
|
165
|
+
const getBody = (schema) => {
|
|
166
|
+
const current = ensureBody();
|
|
167
|
+
if (schema && typeof schema.parse === "function") {
|
|
168
|
+
try {
|
|
169
|
+
bodyValue = schema.parse(current);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error("[ERROR] Body validation failed", error);
|
|
172
|
+
throw new HttpError(400, "Body validation failed", { cause: error });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
ctx.body = bodyValue;
|
|
176
|
+
return bodyValue;
|
|
177
|
+
};
|
|
178
|
+
try {
|
|
179
|
+
ctx.body = ensureBody();
|
|
180
|
+
const args = buildHandlerArguments(entry, ctx, getBody);
|
|
181
|
+
const result = await entry.handler(...args);
|
|
182
|
+
if (result === void 0) {
|
|
183
|
+
return formatResponse(noContent(), preferV2);
|
|
184
|
+
}
|
|
185
|
+
return formatResponse(result, preferV2);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
return handleError(error, preferV2);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function isEventBridgeEvent(event) {
|
|
192
|
+
if (!event || typeof event !== "object") {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
const e = event;
|
|
196
|
+
return typeof e["detail-type"] === "string" || typeof e.detailType === "string";
|
|
197
|
+
}
|
|
198
|
+
async function handleEventBridgeEvent(event, handlers, lambdaContext) {
|
|
199
|
+
const eventType = event["detail-type"] ?? event.detailType;
|
|
200
|
+
if (!eventType) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const matches = handlers.filter((entry) => {
|
|
204
|
+
if (entry.event !== eventType) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
return true;
|
|
208
|
+
});
|
|
209
|
+
if (matches.length === 0) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
await Promise.all(
|
|
213
|
+
matches.map((entry) => Promise.resolve(entry.handler(event.detail, event, lambdaContext)))
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
function buildHandlerArguments(entry, ctx, getBody) {
|
|
217
|
+
const maxIndex = entry.parameters.reduce((max, meta) => Math.max(max, meta.index), -1);
|
|
218
|
+
const length = Math.max(entry.handler.length, maxIndex + 1, 1);
|
|
219
|
+
const args = new Array(length).fill(void 0);
|
|
220
|
+
for (const meta of entry.parameters) {
|
|
221
|
+
switch (meta.type) {
|
|
222
|
+
case "body": {
|
|
223
|
+
args[meta.index] = getBody(meta.schema);
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case "query": {
|
|
227
|
+
args[meta.index] = meta.name ? ctx.query[meta.name] : ctx.query;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case "param": {
|
|
231
|
+
args[meta.index] = meta.name ? ctx.params[meta.name] : ctx.params;
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
case "headers": {
|
|
235
|
+
args[meta.index] = ctx.headers;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
case "header": {
|
|
239
|
+
args[meta.index] = meta.name ? ctx.headers[meta.name.toLowerCase()] : ctx.headers;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
case "req": {
|
|
243
|
+
args[meta.index] = ctx.event;
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
case "res": {
|
|
247
|
+
args[meta.index] = ctx.response;
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
case "auth": {
|
|
251
|
+
args[meta.index] = ctx.auth;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
default: {
|
|
255
|
+
args[meta.index] = ctx;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
for (let i = 0; i < length; i += 1) {
|
|
260
|
+
if (args[i] === void 0) {
|
|
261
|
+
args[i] = ctx;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return args;
|
|
265
|
+
}
|
|
266
|
+
function parseBody(event, headers, forceJson) {
|
|
267
|
+
const raw = extractRawBody(event);
|
|
268
|
+
if (raw === void 0) {
|
|
269
|
+
return void 0;
|
|
270
|
+
}
|
|
271
|
+
const contentType = headers["content-type"];
|
|
272
|
+
const shouldParse = forceJson || isJsonContentType(contentType);
|
|
273
|
+
if (!shouldParse) {
|
|
274
|
+
return raw;
|
|
275
|
+
}
|
|
276
|
+
if (raw.trim() === "") {
|
|
277
|
+
return void 0;
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
return JSON.parse(raw);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
throw new HttpError(400, "Invalid JSON body", { cause: error });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function extractRawBody(event) {
|
|
286
|
+
if (!event.body) {
|
|
287
|
+
return void 0;
|
|
288
|
+
}
|
|
289
|
+
if (event.isBase64Encoded) {
|
|
290
|
+
return Buffer.from(event.body, "base64").toString("utf8");
|
|
291
|
+
}
|
|
292
|
+
return event.body;
|
|
293
|
+
}
|
|
294
|
+
function isJsonContentType(contentType) {
|
|
295
|
+
if (!contentType) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
return contentType.includes("application/json") || contentType.includes("+json");
|
|
299
|
+
}
|
|
300
|
+
function normalizeHeaders(headers) {
|
|
301
|
+
const normalized = {};
|
|
302
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
303
|
+
if (typeof value === "undefined") {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
normalized[key.toLowerCase()] = value;
|
|
307
|
+
}
|
|
308
|
+
return normalized;
|
|
309
|
+
}
|
|
310
|
+
function extractQuery(event) {
|
|
311
|
+
const single = event.queryStringParameters ?? event.queryStringParameters ?? {};
|
|
312
|
+
const multi = event.multiValueQueryStringParameters ?? {};
|
|
313
|
+
const query = {};
|
|
314
|
+
for (const [key, value] of Object.entries(single ?? {})) {
|
|
315
|
+
query[key] = value ?? void 0;
|
|
316
|
+
}
|
|
317
|
+
for (const [key, value] of Object.entries(multi ?? {})) {
|
|
318
|
+
if (!value || value.length === 0) {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
query[key] = value[value.length - 1];
|
|
322
|
+
}
|
|
323
|
+
return query;
|
|
324
|
+
}
|
|
325
|
+
function extractMethod(event) {
|
|
326
|
+
return event.requestContext?.http?.method || event.httpMethod || void 0;
|
|
327
|
+
}
|
|
328
|
+
function extractPath(event) {
|
|
329
|
+
return event.rawPath || event.path || void 0;
|
|
330
|
+
}
|
|
331
|
+
function extractAuthClaims(event, entry) {
|
|
332
|
+
if (!entry.auth || entry.auth.type !== "firebase") {
|
|
333
|
+
return void 0;
|
|
334
|
+
}
|
|
335
|
+
const ctxV2 = event.requestContext;
|
|
336
|
+
const ctxV1 = event.requestContext;
|
|
337
|
+
const claims = ctxV2?.authorizer?.jwt?.claims || ctxV1?.authorizer?.claims;
|
|
338
|
+
return claims ?? void 0;
|
|
339
|
+
}
|
|
340
|
+
function isHttpError(error) {
|
|
341
|
+
if (!error || typeof error !== "object") {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
const marker = error[HTTP_ERROR_MARKER] === true;
|
|
345
|
+
const named = error.name === "HttpError";
|
|
346
|
+
const status = typeof error.statusCode === "number";
|
|
347
|
+
return status && (marker || named);
|
|
348
|
+
}
|
|
349
|
+
function handleError(error, preferV2) {
|
|
350
|
+
if (isHttpError(error)) {
|
|
351
|
+
return formatResponse({
|
|
352
|
+
statusCode: error.statusCode,
|
|
353
|
+
headers: {
|
|
354
|
+
"content-type": "application/json; charset=utf-8",
|
|
355
|
+
...error.headers
|
|
356
|
+
},
|
|
357
|
+
body: error.details ? JSON.stringify({ message: error.message, details: error.details }) : JSON.stringify({ message: error.message })
|
|
358
|
+
}, preferV2);
|
|
359
|
+
}
|
|
360
|
+
console.error("Unhandled error in sst-http handler", error);
|
|
361
|
+
return formatResponse({
|
|
362
|
+
statusCode: 500,
|
|
363
|
+
headers: {
|
|
364
|
+
"content-type": "application/json; charset=utf-8"
|
|
365
|
+
},
|
|
366
|
+
body: JSON.stringify({ message: "Internal Server Error" })
|
|
367
|
+
}, preferV2);
|
|
368
|
+
}
|
|
369
|
+
function formatResponse(result, preferV2) {
|
|
370
|
+
if (typeof result === "string") {
|
|
371
|
+
result = { statusCode: 200, body: result };
|
|
372
|
+
}
|
|
373
|
+
const normalized = {
|
|
374
|
+
statusCode: result.statusCode ?? 200,
|
|
375
|
+
headers: result.headers,
|
|
376
|
+
body: result.body ?? "",
|
|
377
|
+
cookies: "cookies" in result ? result.cookies : void 0,
|
|
378
|
+
isBase64Encoded: result.isBase64Encoded
|
|
379
|
+
};
|
|
380
|
+
if (preferV2) {
|
|
381
|
+
const response2 = {
|
|
382
|
+
statusCode: normalized.statusCode,
|
|
383
|
+
headers: normalized.headers,
|
|
384
|
+
body: normalized.body
|
|
385
|
+
};
|
|
386
|
+
if (normalized.cookies) {
|
|
387
|
+
response2.cookies = normalized.cookies;
|
|
388
|
+
}
|
|
389
|
+
if (typeof normalized.isBase64Encoded === "boolean") {
|
|
390
|
+
response2.isBase64Encoded = normalized.isBase64Encoded;
|
|
391
|
+
}
|
|
392
|
+
return response2;
|
|
393
|
+
}
|
|
394
|
+
const response = {
|
|
395
|
+
statusCode: normalized.statusCode,
|
|
396
|
+
headers: normalized.headers,
|
|
397
|
+
body: normalized.body
|
|
398
|
+
};
|
|
399
|
+
if (typeof normalized.isBase64Encoded === "boolean") {
|
|
400
|
+
response.isBase64Encoded = normalized.isBase64Encoded;
|
|
401
|
+
}
|
|
402
|
+
return response;
|
|
403
|
+
}
|
|
404
|
+
function isHttpApiEvent(event) {
|
|
405
|
+
return event.version === "2.0";
|
|
406
|
+
}
|
|
407
|
+
var SUPPORTED_METHODS = /* @__PURE__ */ new Set([
|
|
408
|
+
"GET",
|
|
409
|
+
"POST",
|
|
410
|
+
"PUT",
|
|
411
|
+
"PATCH",
|
|
412
|
+
"DELETE",
|
|
413
|
+
"HEAD",
|
|
414
|
+
"OPTIONS"
|
|
415
|
+
]);
|
|
416
|
+
function isSupportedMethod(value) {
|
|
417
|
+
return SUPPORTED_METHODS.has(value);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// src/http/decorators.ts
|
|
421
|
+
function createRouteDecorator(method) {
|
|
422
|
+
return (path) => (target, propertyKey, descriptor) => {
|
|
423
|
+
const handler = resolveHandler(target, propertyKey, descriptor);
|
|
424
|
+
registerRoute(handler, method, path);
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function createParameterDecorator(type, options) {
|
|
428
|
+
return (target, propertyKey, parameterIndex) => {
|
|
429
|
+
const handler = resolveHandler(target, propertyKey);
|
|
430
|
+
registerParameter(handler, {
|
|
431
|
+
index: parameterIndex,
|
|
432
|
+
type,
|
|
433
|
+
schema: options?.schema,
|
|
434
|
+
name: options?.name
|
|
435
|
+
});
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
var Get = createRouteDecorator("GET");
|
|
439
|
+
var Post = createRouteDecorator("POST");
|
|
440
|
+
var Put = createRouteDecorator("PUT");
|
|
441
|
+
var Patch = createRouteDecorator("PATCH");
|
|
442
|
+
var Delete = createRouteDecorator("DELETE");
|
|
443
|
+
var Head = createRouteDecorator("HEAD");
|
|
444
|
+
var Options = createRouteDecorator("OPTIONS");
|
|
445
|
+
function FirebaseAuth(options) {
|
|
446
|
+
return (target, propertyKey, descriptor) => {
|
|
447
|
+
const handler = resolveHandler(target, propertyKey, descriptor);
|
|
448
|
+
registerFirebaseAuth(handler, options);
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function Auth() {
|
|
452
|
+
return createParameterDecorator("auth");
|
|
453
|
+
}
|
|
454
|
+
function Body(schema) {
|
|
455
|
+
return createParameterDecorator("body", { schema });
|
|
456
|
+
}
|
|
457
|
+
function Query(name) {
|
|
458
|
+
return createParameterDecorator("query", { name });
|
|
459
|
+
}
|
|
460
|
+
function Param(name) {
|
|
461
|
+
return createParameterDecorator("param", { name });
|
|
462
|
+
}
|
|
463
|
+
function Headers() {
|
|
464
|
+
return createParameterDecorator("headers");
|
|
465
|
+
}
|
|
466
|
+
function Header(name) {
|
|
467
|
+
return createParameterDecorator("header", { name });
|
|
468
|
+
}
|
|
469
|
+
function Req() {
|
|
470
|
+
return createParameterDecorator("req");
|
|
471
|
+
}
|
|
472
|
+
function Res() {
|
|
473
|
+
return createParameterDecorator("res");
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export {
|
|
477
|
+
HttpError,
|
|
478
|
+
json,
|
|
479
|
+
text,
|
|
480
|
+
noContent,
|
|
481
|
+
createHandler,
|
|
482
|
+
handleError,
|
|
483
|
+
Get,
|
|
484
|
+
Post,
|
|
485
|
+
Put,
|
|
486
|
+
Patch,
|
|
487
|
+
Delete,
|
|
488
|
+
Head,
|
|
489
|
+
Options,
|
|
490
|
+
FirebaseAuth,
|
|
491
|
+
Auth,
|
|
492
|
+
Body,
|
|
493
|
+
Query,
|
|
494
|
+
Param,
|
|
495
|
+
Headers,
|
|
496
|
+
Header,
|
|
497
|
+
Req,
|
|
498
|
+
Res
|
|
499
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// src/core/registry.ts
|
|
2
|
+
var routeMeta = /* @__PURE__ */ new Map();
|
|
3
|
+
var parameterMeta = /* @__PURE__ */ new Map();
|
|
4
|
+
var eventMeta = /* @__PURE__ */ new Map();
|
|
5
|
+
var options = {
|
|
6
|
+
inferPathFromName: false
|
|
7
|
+
};
|
|
8
|
+
function configureRoutes(next) {
|
|
9
|
+
options = {
|
|
10
|
+
...options,
|
|
11
|
+
...next
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function registerRoute(target, method, explicitPath) {
|
|
15
|
+
const handler = target;
|
|
16
|
+
const pathInput = explicitPath ?? inferPath(handler);
|
|
17
|
+
const path = pathInput?.startsWith("/") ? pathInput : pathInput ? `/${pathInput}` : void 0;
|
|
18
|
+
if (!path) {
|
|
19
|
+
const name = handler.name || "<anonymous>";
|
|
20
|
+
throw new Error(`Route for "${name}" is missing a path. Provide one or enable name inference.`);
|
|
21
|
+
}
|
|
22
|
+
const current = routeMeta.get(handler) ?? {};
|
|
23
|
+
routeMeta.set(handler, {
|
|
24
|
+
...current,
|
|
25
|
+
method,
|
|
26
|
+
path
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function registerFirebaseAuth(target, cfg) {
|
|
30
|
+
const handler = target;
|
|
31
|
+
const current = routeMeta.get(handler) ?? {};
|
|
32
|
+
routeMeta.set(handler, {
|
|
33
|
+
...current,
|
|
34
|
+
auth: {
|
|
35
|
+
type: "firebase",
|
|
36
|
+
...cfg
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function registerParameter(target, meta) {
|
|
41
|
+
const handler = target;
|
|
42
|
+
const list = parameterMeta.get(handler) ?? [];
|
|
43
|
+
list.push(meta);
|
|
44
|
+
list.sort((a, b) => a.index - b.index);
|
|
45
|
+
parameterMeta.set(handler, list);
|
|
46
|
+
}
|
|
47
|
+
function registerEvent(target, event) {
|
|
48
|
+
const handler = target;
|
|
49
|
+
const list = eventMeta.get(handler) ?? [];
|
|
50
|
+
list.push({ event });
|
|
51
|
+
eventMeta.set(handler, list);
|
|
52
|
+
}
|
|
53
|
+
function getRegisteredRoutes() {
|
|
54
|
+
const routes = [];
|
|
55
|
+
for (const [handler, meta] of routeMeta.entries()) {
|
|
56
|
+
if (!meta.method || !meta.path) {
|
|
57
|
+
const name = handler.name || "<anonymous>";
|
|
58
|
+
throw new Error(`Route for "${name}" is incomplete. Ensure it has an HTTP method decorator.`);
|
|
59
|
+
}
|
|
60
|
+
routes.push({
|
|
61
|
+
handler,
|
|
62
|
+
method: meta.method,
|
|
63
|
+
path: meta.path,
|
|
64
|
+
auth: meta.auth,
|
|
65
|
+
parameters: [...parameterMeta.get(handler) ?? []]
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return routes;
|
|
69
|
+
}
|
|
70
|
+
function getRegisteredEvents() {
|
|
71
|
+
const events = [];
|
|
72
|
+
for (const [handler, list] of eventMeta.entries()) {
|
|
73
|
+
for (const entry of list) {
|
|
74
|
+
events.push({
|
|
75
|
+
handler,
|
|
76
|
+
event: entry.event
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return events;
|
|
81
|
+
}
|
|
82
|
+
function inferPath(handler) {
|
|
83
|
+
if (!options.inferPathFromName) {
|
|
84
|
+
return void 0;
|
|
85
|
+
}
|
|
86
|
+
const name = handler.name;
|
|
87
|
+
if (!name) {
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
const slug = name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[_\s]+/g, "-").toLowerCase();
|
|
91
|
+
return slug ? `/${slug}` : void 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/core/handler.ts
|
|
95
|
+
function resolveHandler(target, propertyKey, descriptor) {
|
|
96
|
+
if (descriptor?.value && typeof descriptor.value === "function") {
|
|
97
|
+
return descriptor.value;
|
|
98
|
+
}
|
|
99
|
+
if (typeof target === "function" && propertyKey === void 0) {
|
|
100
|
+
return target;
|
|
101
|
+
}
|
|
102
|
+
if (target && propertyKey && typeof target[propertyKey] === "function") {
|
|
103
|
+
return target[propertyKey];
|
|
104
|
+
}
|
|
105
|
+
throw new Error("Unable to determine decorated function. Ensure decorators are applied to functions.");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
configureRoutes,
|
|
110
|
+
registerRoute,
|
|
111
|
+
registerFirebaseAuth,
|
|
112
|
+
registerParameter,
|
|
113
|
+
registerEvent,
|
|
114
|
+
getRegisteredRoutes,
|
|
115
|
+
getRegisteredEvents,
|
|
116
|
+
resolveHandler
|
|
117
|
+
};
|