sst-http 1.3.2 → 1.3.3-beta.3

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