sst-http 1.3.0 → 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/dist/index.js CHANGED
@@ -1,520 +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
- throw new HttpError(400, "Body validation failed", { cause: error });
229
- }
230
- }
231
- ctx.body = bodyValue;
232
- return bodyValue;
233
- };
234
- try {
235
- ctx.body = ensureBody();
236
- const args = buildHandlerArguments(entry, ctx, getBody);
237
- const result = await entry.handler(...args);
238
- if (result === void 0) {
239
- return formatResponse(noContent(), preferV2);
240
- }
241
- return formatResponse(result, preferV2);
242
- } catch (error) {
243
- return handleError(error, preferV2);
244
- }
245
- };
246
- }
247
- function buildHandlerArguments(entry, ctx, getBody) {
248
- const maxIndex = entry.parameters.reduce((max, meta) => Math.max(max, meta.index), -1);
249
- const length = Math.max(entry.handler.length, maxIndex + 1, 1);
250
- const args = new Array(length).fill(void 0);
251
- for (const meta of entry.parameters) {
252
- switch (meta.type) {
253
- case "body": {
254
- args[meta.index] = getBody(meta.schema);
255
- break;
256
- }
257
- case "query": {
258
- args[meta.index] = meta.name ? ctx.query[meta.name] : ctx.query;
259
- break;
260
- }
261
- case "param": {
262
- args[meta.index] = meta.name ? ctx.params[meta.name] : ctx.params;
263
- break;
264
- }
265
- case "headers": {
266
- args[meta.index] = ctx.headers;
267
- break;
268
- }
269
- case "header": {
270
- args[meta.index] = meta.name ? ctx.headers[meta.name.toLowerCase()] : ctx.headers;
271
- break;
272
- }
273
- case "req": {
274
- args[meta.index] = ctx.event;
275
- break;
276
- }
277
- case "res": {
278
- args[meta.index] = ctx.response;
279
- break;
280
- }
281
- case "auth": {
282
- args[meta.index] = ctx.auth;
283
- break;
284
- }
285
- default: {
286
- args[meta.index] = ctx;
287
- }
288
- }
289
- }
290
- for (let i = 0; i < length; i += 1) {
291
- if (args[i] === void 0) {
292
- args[i] = ctx;
293
- }
294
- }
295
- return args;
296
- }
297
- function parseBody(event, headers, forceJson) {
298
- const raw = extractRawBody(event);
299
- if (raw === void 0) {
300
- return void 0;
301
- }
302
- const contentType = headers["content-type"];
303
- const shouldParse = forceJson || isJsonContentType(contentType);
304
- if (!shouldParse) {
305
- return raw;
306
- }
307
- if (raw.trim() === "") {
308
- return void 0;
309
- }
310
- try {
311
- return JSON.parse(raw);
312
- } catch (error) {
313
- throw new HttpError(400, "Invalid JSON body", { cause: error });
314
- }
315
- }
316
- function extractRawBody(event) {
317
- if (!event.body) {
318
- return void 0;
319
- }
320
- if (event.isBase64Encoded) {
321
- return Buffer.from(event.body, "base64").toString("utf8");
322
- }
323
- return event.body;
324
- }
325
- function isJsonContentType(contentType) {
326
- if (!contentType) {
327
- return false;
328
- }
329
- return contentType.includes("application/json") || contentType.includes("+json");
330
- }
331
- function normalizeHeaders(headers) {
332
- const normalized = {};
333
- for (const [key, value] of Object.entries(headers)) {
334
- if (typeof value === "undefined") {
335
- continue;
336
- }
337
- normalized[key.toLowerCase()] = value;
338
- }
339
- return normalized;
340
- }
341
- function extractQuery(event) {
342
- const single = event.queryStringParameters ?? event.queryStringParameters ?? {};
343
- const multi = event.multiValueQueryStringParameters ?? {};
344
- const query = {};
345
- for (const [key, value] of Object.entries(single ?? {})) {
346
- query[key] = value ?? void 0;
347
- }
348
- for (const [key, value] of Object.entries(multi ?? {})) {
349
- if (!value || value.length === 0) {
350
- continue;
351
- }
352
- query[key] = value[value.length - 1];
353
- }
354
- return query;
355
- }
356
- function extractMethod(event) {
357
- return event.requestContext?.http?.method || event.httpMethod || void 0;
358
- }
359
- function extractPath(event) {
360
- return event.rawPath || event.path || void 0;
361
- }
362
- function extractAuthClaims(event, entry) {
363
- if (!entry.auth || entry.auth.type !== "firebase") {
364
- return void 0;
365
- }
366
- const ctxV2 = event.requestContext;
367
- const ctxV1 = event.requestContext;
368
- const claims = ctxV2?.authorizer?.jwt?.claims || ctxV1?.authorizer?.claims;
369
- return claims ?? void 0;
370
- }
371
- function isHttpError(error) {
372
- if (!error || typeof error !== "object") {
373
- return false;
374
- }
375
- const marker = error[HTTP_ERROR_MARKER] === true;
376
- const named = error.name === "HttpError";
377
- const status = typeof error.statusCode === "number";
378
- return status && (marker || named);
379
- }
380
- function handleError(error, preferV2) {
381
- if (isHttpError(error)) {
382
- return formatResponse({
383
- statusCode: error.statusCode,
384
- headers: {
385
- "content-type": "application/json; charset=utf-8",
386
- ...error.headers
387
- },
388
- body: error.details ? JSON.stringify({ message: error.message, details: error.details }) : JSON.stringify({ message: error.message })
389
- }, preferV2);
390
- }
391
- console.error("Unhandled error in sst-http handler", error);
392
- return formatResponse({
393
- statusCode: 500,
394
- headers: {
395
- "content-type": "application/json; charset=utf-8"
396
- },
397
- body: JSON.stringify({ message: "Internal Server Error" })
398
- }, preferV2);
399
- }
400
- function formatResponse(result, preferV2) {
401
- if (typeof result === "string") {
402
- result = { statusCode: 200, body: result };
403
- }
404
- const normalized = {
405
- statusCode: result.statusCode ?? 200,
406
- headers: result.headers,
407
- body: result.body ?? "",
408
- cookies: "cookies" in result ? result.cookies : void 0,
409
- isBase64Encoded: result.isBase64Encoded
410
- };
411
- if (preferV2) {
412
- const response2 = {
413
- statusCode: normalized.statusCode,
414
- headers: normalized.headers,
415
- body: normalized.body
416
- };
417
- if (normalized.cookies) {
418
- response2.cookies = normalized.cookies;
419
- }
420
- if (typeof normalized.isBase64Encoded === "boolean") {
421
- response2.isBase64Encoded = normalized.isBase64Encoded;
422
- }
423
- return response2;
424
- }
425
- const response = {
426
- statusCode: normalized.statusCode,
427
- headers: normalized.headers,
428
- body: normalized.body
429
- };
430
- if (typeof normalized.isBase64Encoded === "boolean") {
431
- response.isBase64Encoded = normalized.isBase64Encoded;
432
- }
433
- return response;
434
- }
435
- function isHttpApiEvent(event) {
436
- return event.version === "2.0";
437
- }
438
- var SUPPORTED_METHODS = /* @__PURE__ */ new Set([
439
- "GET",
440
- "POST",
441
- "PUT",
442
- "PATCH",
443
- "DELETE",
444
- "HEAD",
445
- "OPTIONS"
446
- ]);
447
- function isSupportedMethod(value) {
448
- return SUPPORTED_METHODS.has(value);
449
- }
450
-
451
- // src/decorators.ts
452
- function resolveHandler(target, propertyKey, descriptor) {
453
- if (descriptor?.value && typeof descriptor.value === "function") {
454
- return descriptor.value;
455
- }
456
- if (typeof target === "function" && propertyKey === void 0) {
457
- return target;
458
- }
459
- if (target && propertyKey && typeof target[propertyKey] === "function") {
460
- return target[propertyKey];
461
- }
462
- throw new Error("Unable to determine decorated function. Ensure decorators are applied to functions.");
463
- }
464
- function createRouteDecorator(method) {
465
- return (path) => (target, propertyKey, descriptor) => {
466
- const handler = resolveHandler(target, propertyKey, descriptor);
467
- registerRoute(handler, method, path);
468
- };
469
- }
470
- function createParameterDecorator(type, options2) {
471
- return (target, propertyKey, parameterIndex) => {
472
- const handler = resolveHandler(target, propertyKey);
473
- registerParameter(handler, {
474
- index: parameterIndex,
475
- type,
476
- schema: options2?.schema,
477
- name: options2?.name
478
- });
479
- };
480
- }
481
- var Get = createRouteDecorator("GET");
482
- var Post = createRouteDecorator("POST");
483
- var Put = createRouteDecorator("PUT");
484
- var Patch = createRouteDecorator("PATCH");
485
- var Delete = createRouteDecorator("DELETE");
486
- var Head = createRouteDecorator("HEAD");
487
- var Options = createRouteDecorator("OPTIONS");
488
- function FirebaseAuth(options2) {
489
- return (target, propertyKey, descriptor) => {
490
- const handler = resolveHandler(target, propertyKey, descriptor);
491
- registerFirebaseAuth(handler, options2);
492
- };
493
- }
494
- function Auth() {
495
- return createParameterDecorator("auth");
496
- }
497
- function Body(schema) {
498
- return createParameterDecorator("body", { schema });
499
- }
500
- function Query(name) {
501
- return createParameterDecorator("query", { name });
502
- }
503
- function Param(name) {
504
- return createParameterDecorator("param", { name });
505
- }
506
- function Headers() {
507
- return createParameterDecorator("headers");
508
- }
509
- function Header(name) {
510
- return createParameterDecorator("header", { name });
511
- }
512
- function Req() {
513
- return createParameterDecorator("req");
514
- }
515
- function Res() {
516
- return createParameterDecorator("res");
517
- }
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";
518
33
  export {
519
34
  Auth,
520
35
  Body,
@@ -525,6 +40,7 @@ export {
525
40
  Header,
526
41
  Headers,
527
42
  HttpError,
43
+ On,
528
44
  Options,
529
45
  Param,
530
46
  Patch,
@@ -538,5 +54,6 @@ export {
538
54
  handleError,
539
55
  json,
540
56
  noContent,
57
+ publish,
541
58
  text
542
59
  };