lambda-pipe 0.1.0
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/LICENSE +21 -0
- package/README.md +761 -0
- package/build/cjs/cli/index.js +1 -0
- package/build/cjs/index.js +1 -0
- package/build/cjs/jobs/index.js +1 -0
- package/build/cjs/logger/index.js +1 -0
- package/build/cjs/runtime/index.js +1 -0
- package/build/cjs/websocket/index.js +1 -0
- package/build/cli/dev.d.ts +10 -0
- package/build/core/cache/index.d.ts +19 -0
- package/build/core/context/createRequestContext.d.ts +2 -0
- package/build/core/context/index.d.ts +2 -0
- package/build/core/context/types.d.ts +52 -0
- package/build/core/context/utils.d.ts +6 -0
- package/build/core/errors/HttpError.d.ts +6 -0
- package/build/core/errors/handleError.d.ts +2 -0
- package/build/core/errors/http.d.ts +13 -0
- package/build/core/errors/index.d.ts +3 -0
- package/build/core/http/helpers.d.ts +6 -0
- package/build/core/http/index.d.ts +3 -0
- package/build/core/http/serialize.d.ts +2 -0
- package/build/core/http/types.d.ts +6 -0
- package/build/core/index.d.ts +8 -0
- package/build/core/lifecycle/hooks.d.ts +4 -0
- package/build/core/lifecycle/index.d.ts +1 -0
- package/build/core/middleware/index.d.ts +2 -0
- package/build/core/middleware/run.d.ts +4 -0
- package/build/core/middleware/types.d.ts +11 -0
- package/build/core/plugins/index.d.ts +1 -0
- package/build/core/plugins/types.d.ts +15 -0
- package/build/core/routing/defineHandler.d.ts +14 -0
- package/build/core/routing/defineRoute.d.ts +20 -0
- package/build/core/routing/execution.d.ts +4 -0
- package/build/core/routing/index.d.ts +3 -0
- package/build/core/routing/types.d.ts +55 -0
- package/build/core/validation/index.d.ts +2 -0
- package/build/core/validation/types.d.ts +8 -0
- package/build/core/validation/validate.d.ts +2 -0
- package/build/esm/cli/index.js +1 -0
- package/build/esm/index.js +1 -0
- package/build/esm/jobs/index.js +1 -0
- package/build/esm/logger/index.js +1 -0
- package/build/esm/runtime/index.js +1 -0
- package/build/esm/websocket/index.js +1 -0
- package/build/index.d.ts +1 -0
- package/build/jobs/defineJob.d.ts +5 -0
- package/build/jobs/index.d.ts +2 -0
- package/build/jobs/sqs.d.ts +7 -0
- package/build/logger/index.d.ts +2 -0
- package/build/logger/logger.d.ts +2 -0
- package/build/logger/types.d.ts +6 -0
- package/build/routes/health.d.ts +2 -0
- package/build/routes/user.d.ts +2 -0
- package/build/runtime/aws/createAWSHandler.d.ts +3 -0
- package/build/runtime/aws/index.d.ts +2 -0
- package/build/runtime/aws/request.d.ts +4 -0
- package/build/runtime/fastify/createFastifyHandler.d.ts +3 -0
- package/build/runtime/fastify/index.d.ts +2 -0
- package/build/runtime/fastify/request.d.ts +4 -0
- package/build/runtime/index.d.ts +2 -0
- package/build/websocket/index.d.ts +16 -0
- package/docs/dev-server.md +278 -0
- package/docs/websocket.md +118 -0
- package/package.json +135 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e=require("node:crypto"),t=require("node:fs"),r=require("node:path"),o=require("node:url"),n=require("@fastify/cookie"),s=require("chokidar"),a=require("fastify");Math.random().toString(36).slice(2);const i=[];function c(e){return e.endsWith(".ts")||e.endsWith(".js")||e.endsWith(".mjs")||e.endsWith(".cjs")}function u(e){if(e)return function(e){try{return JSON.parse(e)}catch{return e}}(e)}function h(t){return{headers:t.headers,body:t.body,queryStringParameters:t.query,pathParameters:t.params,cookies:t.cookies,rawPath:t.url,requestContext:{requestId:e.randomUUID(),http:{method:t.method}}}}async function d(e,n){const s=t.readdirSync(n,{withFileTypes:!0});for(const t of s){const s=r.join(n,t.name);if(t.isDirectory()){await d(e,s);continue}if(!c(t.name))continue;const a=(await import(o.pathToFileURL(s).href+"?v="+Date.now())).default;a?.__isRoute&&(i.push({method:a.method.toUpperCase(),path:a.path,file:s}),e.route({method:a.method.toUpperCase(),url:a.path,handler:async(e,t)=>{try{const r=await a.handler(h(e),{});if(t.code(r.statusCode),r.headers)for(const[e,o]of Object.entries(r.headers))t.header(e,o);return u(r.body)}catch(e){t.code(500).send({error:e.message,stack:e.stack,path:a.path})}}}))}}async function p(e={}){const o=a({logger:e.logger??!0});await o.register(n);const c=(u=e.routesDir,r.resolve(process.cwd(),u||process.env.ROUTES_DIR||"src/routes"));var u;if(!t.existsSync(c))throw Error("Routes directory not found: "+c);return function(e){e.get("/__routes",async()=>({routes:i})),e.get("/__health",async()=>({ok:!0,ts:Date.now()}))}(o),await d(o,c),!1!==e.hmr&&function(e,t){s.watch(t,{ignoreInitial:!0}).on("change",e=>{process.exit(0)})}(0,c),o}exports.bootstrap=async function(e={}){const t=await p(e),r=function(e){return Number(e||process.env.PORT||3e3)}(e.port),o=function(e){return e||process.env.HOST||"0.0.0.0"}(e.host);return await t.listen({port:r,host:o}),t},exports.createDevApp=p;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e=require("node:crypto");class t{constructor(){this.store=new Map}get(e){const t=this.store.get(e);return t?Date.now()>t.expireAt?(this.store.delete(e),null):t.value:null}set(e,t,s=5e3){this.store.set(e,{value:t,expireAt:Date.now()+s})}delete(e){this.store.delete(e)}clear(){this.store.clear()}has(e){return null!==this.get(e)}async getOrSet(e,t,s=5e3){const r=this.get(e);if(null!==r)return r;const o=await t();return this.set(e,o,s),o}startCleanup(e=3e4){setInterval(()=>{const e=Date.now();for(const[t,s]of this.store.entries())e>s.expireAt&&this.store.delete(t)},e)}}const s=new t;function r(e){const t={};for(const[s,r]of Object.entries(e||{}))"string"==typeof r&&(t[s.toLowerCase()]=r);return t}function o(e){if(!e)return null;const t=e.match(/^Bearer\s+(.+)$/i);return t?t[1]:null}function n(e){return e?e.split(";").map(e=>e.trim()).filter(Boolean).reduce((e,t)=>{const s=t.indexOf("=");if(-1===s)return e;const r=t.slice(0,s),o=t.slice(s+1);return e[r]=decodeURIComponent(o),e},{}):{}}function a(e){try{return JSON.parse(e)}catch{return e}}function i(e){return{headers:r(e?.headers),body:"string"==typeof e?.body?a(e.body):e?.body,query:e?.queryStringParameters||{},params:e?.pathParameters||{}}}class u extends Error{constructor(e,t,s){super(t),this.statusCode=e,this.expose=e<500,this.details=s,Object.setPrototypeOf(this,u.prototype)}}function d(e){return e instanceof u?{statusCode:e.statusCode,headers:{"content-type":"application/json"},body:JSON.stringify({message:e.message,...e.expose&&e.details?{details:e.details}:{},..."production"!==process.env.NODE_ENV?{stack:e.stack}:{}})}:{statusCode:500,headers:{"content-type":"application/json"},body:JSON.stringify({message:"Internal Server Error"})}}function c(e,t=200){return{statusCode:t,headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}}function p(e){return"object"==typeof(t=e)&&null!==t&&"statusCode"in t&&"body"in t?e:null==e?{statusCode:204,body:""}:"string"==typeof e?{statusCode:200,headers:{"content-type":"text/plain"},body:e}:{statusCode:200,headers:{"content-type":"application/json"},body:JSON.stringify(e)};var t}async function l(e,t=[],s){await async function r(o){const n=t[o];if(!n)return s();if(!n.onRequest)return r(o+1);let a=!1;await n.onRequest(e,async()=>{if(a)throw Error("next() called multiple times");a=!0,await r(o+1)})}(0)}async function h(e,t){return e(t)}const f=Math.random().toString(36).slice(2);const y=new WeakSet;let x=!0;function m(t){return async(a,u)=>{let c;try{const d=a,m=i(d);t.validate?.body&&(m.body=await h(t.validate.body,m.body)),t.validate?.query&&(m.query=await h(t.validate.query,m.query)),t.validate?.params&&(m.params=await h(t.validate.params,m.params));const g=function({isColdStart:e}){return{isColdStart:e,containerId:f,cache:s,startedAt:Date.now()}}({isColdStart:x}),w=await async function(t,s){const a=r(t?.headers),i=n(a.cookie),u=o(a.authorization);let d,c,p=[];if(u&&s?.authenticate){const e=await s.authenticate(u,t);e&&(d=e.user,p=e.scope||[],c=e.metadata)}return{requestId:t?.requestContext?.requestId??e.randomUUID(),headers:a,cookies:i,authToken:u||void 0,user:d,scope:p,metadata:c,ip:t?.requestContext?.http?.sourceIp,userAgent:a["user-agent"],receivedAt:Date.now(),event:t}}(d,{authenticate:t.authenticate});for(const e of t.plugins||[])y.has(e)||(await(e.setup?.()),y.add(e));const C=Object.assign({},...(t.plugins||[]).map(e=>e.extend?.()||{})),b={statusCode:200,headers:{},cookies:[]},q={info:(e,t)=>{},warn:(e,t)=>{},error:(e,t)=>{},debug:(e,t)=>{}};if(c={event:d,awsContext:u,exec:g,req:w,body:m.body,query:m.query,params:m.params,state:{},log:C.log||q,setHeader:(e,t)=>{b.headers[e]=t},getHeader:e=>b.headers[e],status:e=>{b.statusCode=e},setCookie:(e,t,s={})=>{const r=[`${e}=${encodeURIComponent(t)}`];s.path&&r.push("Path="+s.path),s.httpOnly&&r.push("HttpOnly"),s.secure&&r.push("Secure"),s.maxAge&&r.push("Max-Age="+s.maxAge),b.cookies.push(r.join("; "))},...C},x){for(const e of t.plugins||[])await(e.onColdStart?.());for(const e of t.middleware||[])await(e.onInit?.(c));x=!1}for(const e of t.plugins||[])await(e.onRequest?.());let v;await l(c,t.middleware||[],async()=>{v=await t.handle(c)});for(const e of t.middleware||[])await(e.onResponse?.(c));const S=p(v);for(const e of t.middleware||[])await(e.afterResponse?.(c));return{...S,statusCode:b.statusCode||S.statusCode,headers:{...S.headers||{},...b.headers,...b.cookies.length?{"set-cookie":b.cookies.join(", ")}:{}}}}catch(e){if(c)for(const s of t.middleware||[])await(s.onError?.(e,c));return d(e)}}}exports.ForbiddenError=class extends u{constructor(e="Forbidden"){super(403,e)}},exports.HttpError=u,exports.MemoryCache=t,exports.NotFoundError=class extends u{constructor(e="Not Found"){super(404,e)}},exports.UnauthorizedError=class extends u{constructor(e="Unauthorized"){super(401,e)}},exports.ValidationError=class extends u{constructor(e="Validation Failed"){super(400,e)}},exports.badRequest=function(e="Bad Request"){return c({message:e},400)},exports.defineHandler=m,exports.defineRoute=function(e){for(const t of e.plugins||[])t.onRouteRegister?.({method:e.method,path:e.path});const t=m({middleware:[...(e.plugins||[]).flatMap(e=>e.middleware||[]),...e.middleware||[]],plugins:e.plugins,authenticate:e.authenticate,handle:e.handler});return{__isRoute:!0,method:e.method,path:e.path,handler:t}},exports.extractBearerToken=o,exports.forbidden=function(e="Forbidden"){return c({message:e},403)},exports.globalCache=s,exports.handleError=d,exports.json=c,exports.normalizeHeaders=r,exports.normalizeRequest=i,exports.ok=function(e=""){return{statusCode:200,body:"string"==typeof e?e:JSON.stringify(e)}},exports.parseCookies=n,exports.runMiddleware=l,exports.safeJsonParse=a,exports.serializeResponse=p,exports.unauthorized=function(e="Unauthorized"){return c({message:e},401)},exports.validate=h;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";exports.defineJob=function(e){return e},exports.sqsHandler=function(e){return async t=>{const r=[];for(const s of t.Records)try{const t=JSON.parse(s.body);await e.handler(t)}catch{r.push({itemIdentifier:s.messageId})}return{batchItemFailures:r}}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";exports.loggerPlugin=function(){return{name:"logger",middleware:[{onRequest:async(e,t)=>{const n=performance.now();let r,o;var s;(s=e.event)&&"object"==typeof s&&"requestContext"in s&&(r=e.event.requestContext.http?.method,o=e.event.rawPath),e.req.requestId,e.exec.isColdStart,e.exec.containerId,await t();const a=performance.now()-n;e.req.requestId,a.toFixed(2)},onResponse:async e=>{e.req.requestId}}]}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e=require("node:crypto");function t(e){const t={};for(const[s,a]of Object.entries(e||{}))"string"==typeof a&&(t[s.toLowerCase()]=a);return t}function s(e){try{return JSON.parse(e)}catch{return e}}async function a(s,a){const r=t(s?.headers),o=(n=r.cookie)?n.split(";").map(e=>e.trim()).filter(Boolean).reduce((e,t)=>{const s=t.indexOf("=");if(-1===s)return e;const a=t.slice(0,s),r=t.slice(s+1);return e[a]=decodeURIComponent(r),e},{}):{};var n;const i=function(e){if(!e)return null;const t=e.match(/^Bearer\s+(.+)$/i);return t?t[1]:null}(r.authorization);let u,c,d=[];if(i&&a?.authenticate){const e=await a.authenticate(i,s);e&&(u=e.user,d=e.scope||[],c=e.metadata)}return{requestId:s?.requestContext?.requestId??e.randomUUID(),headers:r,cookies:o,authToken:i||void 0,user:u,scope:d,metadata:c,ip:s?.requestContext?.http?.sourceIp,userAgent:r["user-agent"],receivedAt:Date.now(),event:s}}class r extends Error{constructor(e,t,s){super(t),this.statusCode=e,this.expose=e<500,this.details=s,Object.setPrototypeOf(this,r.prototype)}}async function o(e,t){return e(t)}const n=new class{constructor(){this.store=new Map}get(e){const t=this.store.get(e);return t?Date.now()>t.expireAt?(this.store.delete(e),null):t.value:null}set(e,t,s=5e3){this.store.set(e,{value:t,expireAt:Date.now()+s})}delete(e){this.store.delete(e)}clear(){this.store.clear()}has(e){return null!==this.get(e)}async getOrSet(e,t,s=5e3){const a=this.get(e);if(null!==a)return a;const r=await t();return this.set(e,r,s),r}startCleanup(e=3e4){setInterval(()=>{const e=Date.now();for(const[t,s]of this.store.entries())e>s.expireAt&&this.store.delete(t)},e)}},i=Math.random().toString(36).slice(2);const u=new WeakSet;let c=!0;function d(e){return async(d,l)=>{let p;try{const r=d,h=(y=r,{headers:t(y?.headers),body:"string"==typeof y?.body?s(y.body):y?.body,query:y?.queryStringParameters||{},params:y?.pathParameters||{}});e.validate?.body&&(h.body=await o(e.validate.body,h.body)),e.validate?.query&&(h.query=await o(e.validate.query,h.query)),e.validate?.params&&(h.params=await o(e.validate.params,h.params));const f=function({isColdStart:e}){return{isColdStart:e,containerId:i,cache:n,startedAt:Date.now()}}({isColdStart:c}),m=await a(r,{authenticate:e.authenticate});for(const t of e.plugins||[])u.has(t)||(await(t.setup?.()),u.add(t));const w=Object.assign({},...(e.plugins||[]).map(e=>e.extend?.()||{})),g={statusCode:200,headers:{},cookies:[]},b={info:(e,t)=>{},warn:(e,t)=>{},error:(e,t)=>{},debug:(e,t)=>{}};if(p={event:r,awsContext:l,exec:f,req:m,body:h.body,query:h.query,params:h.params,state:{},log:w.log||b,setHeader:(e,t)=>{g.headers[e]=t},getHeader:e=>g.headers[e],status:e=>{g.statusCode=e},setCookie:(e,t,s={})=>{const a=[`${e}=${encodeURIComponent(t)}`];s.path&&a.push("Path="+s.path),s.httpOnly&&a.push("HttpOnly"),s.secure&&a.push("Secure"),s.maxAge&&a.push("Max-Age="+s.maxAge),g.cookies.push(a.join("; "))},...w},c){for(const t of e.plugins||[])await(t.onColdStart?.());for(const t of e.middleware||[])await(t.onInit?.(p));c=!1}for(const t of e.plugins||[])await(t.onRequest?.());let C;await async function(e,t=[],s){await async function a(r){const o=t[r];if(!o)return s();if(!o.onRequest)return a(r+1);let n=!1;await o.onRequest(e,async()=>{if(n)throw Error("next() called multiple times");n=!0,await a(r+1)})}(0)}(p,e.middleware||[],async()=>{C=await e.handle(p)});for(const t of e.middleware||[])await(t.onResponse?.(p));const q=function(e){return"object"==typeof(t=e)&&null!==t&&"statusCode"in t&&"body"in t?e:null==e?{statusCode:204,body:""}:"string"==typeof e?{statusCode:200,headers:{"content-type":"text/plain"},body:e}:{statusCode:200,headers:{"content-type":"application/json"},body:JSON.stringify(e)};var t}(C);for(const t of e.middleware||[])await(t.afterResponse?.(p));return{...q,statusCode:g.statusCode||q.statusCode,headers:{...q.headers||{},...g.headers,...g.cookies.length?{"set-cookie":g.cookies.join(", ")}:{}}}}catch(t){if(p)for(const s of e.middleware||[])await(s.onError?.(t,p));return function(e){return e instanceof r?{statusCode:e.statusCode,headers:{"content-type":"application/json"},body:JSON.stringify({message:e.message,...e.expose&&e.details?{details:e.details}:{},..."production"!==process.env.NODE_ENV?{stack:e.stack}:{}})}:{statusCode:500,headers:{"content-type":"application/json"},body:JSON.stringify({message:"Internal Server Error"})}}(t)}var y}}function l(e){return{headers:t(e.headers),body:"string"==typeof e.body?s(e.body):e.body,query:e.queryStringParameters||{},params:e.pathParameters||{}}}const p={normalizeRequest:l};function y(e){return{headers:t(e.headers),body:e.body,query:e.query,params:e.params}}const h={normalizeRequest:y};exports.awsAdapter=p,exports.createAWSHandler=function(e){const t=d(e);return async(e,s)=>t(e,s)},exports.createFastifyHandler=function(e){const t=d(e);return async(e,s)=>{const a=await t(e,s);s.status(a.statusCode).headers(a.headers||{}).send(a.body)}},exports.fastifyAdapter=h,exports.normalizeAWSRequest=l,exports.normalizeFastifyRequest=y;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var t=require("@aws-sdk/client-apigatewaymanagementapi");function n(t){return"string"==typeof t?Buffer.from(t):t instanceof Uint8Array?t:Buffer.from(JSON.stringify(t))}exports.wsHandler=function(e){return async o=>{const a=o.requestContext.routeKey,s=o.requestContext.connectionId||"",c=function(n){const e=n.requestContext.domainName,o=n.requestContext.stage;return new t.ApiGatewayManagementApi({endpoint:`https://${e}/${o}`})}(o),i={event:o,connectionId:s,routeKey:a,send:async(t,e)=>{const o=e??s;o&&await c.postToConnection({ConnectionId:o,Data:n(t)})},broadcast:async(t,e)=>{const o=n(e);await Promise.all(t.map(async t=>{try{await c.postToConnection({ConnectionId:t,Data:o})}catch(t){}}))}};return"$connect"===a?await(e.connect?.(i))??{statusCode:200,body:""}:"$disconnect"===a?await(e.disconnect?.(i))??{statusCode:200,body:""}:await(e.message?.(i))??{statusCode:200,body:""}}};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type FastifyInstance } from 'fastify';
|
|
2
|
+
export type CreateDevAppOptions = {
|
|
3
|
+
port?: number;
|
|
4
|
+
host?: string;
|
|
5
|
+
routesDir?: string;
|
|
6
|
+
logger?: boolean;
|
|
7
|
+
hmr?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function createDevApp(options?: CreateDevAppOptions): Promise<FastifyInstance<import("node:http").Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>, import("node:http").IncomingMessage, import("node:http").ServerResponse<import("node:http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>>;
|
|
10
|
+
export declare function bootstrap(options?: CreateDevAppOptions): Promise<FastifyInstance<import("node:http").Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>, import("node:http").IncomingMessage, import("node:http").ServerResponse<import("node:http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface Cache<T = unknown> {
|
|
2
|
+
get(key: string): T | null;
|
|
3
|
+
set(key: string, value: T, ttl?: number): void;
|
|
4
|
+
delete(key: string): void;
|
|
5
|
+
clear(): void;
|
|
6
|
+
has(key: string): boolean;
|
|
7
|
+
getOrSet?(key: string, factory: () => Promise<T>, ttl?: number): Promise<T>;
|
|
8
|
+
}
|
|
9
|
+
export declare class MemoryCache<T = unknown> implements Cache<T> {
|
|
10
|
+
private store;
|
|
11
|
+
get(key: string): T | null;
|
|
12
|
+
set(key: string, value: T, ttl?: number): void;
|
|
13
|
+
delete(key: string): void;
|
|
14
|
+
clear(): void;
|
|
15
|
+
has(key: string): boolean;
|
|
16
|
+
getOrSet(key: string, factory: () => Promise<T>, ttl?: number): Promise<T>;
|
|
17
|
+
startCleanup(interval?: number): void;
|
|
18
|
+
}
|
|
19
|
+
export declare const globalCache: MemoryCache<unknown>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { LambdaEvent } from '../routing';
|
|
2
|
+
export type AuthUser = {
|
|
3
|
+
id: string;
|
|
4
|
+
teamId?: string;
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
};
|
|
7
|
+
export type AuthResult<TUser extends AuthUser = AuthUser, TMetadata extends object = Record<string, unknown>> = {
|
|
8
|
+
user?: TUser;
|
|
9
|
+
scope?: string[];
|
|
10
|
+
metadata?: TMetadata;
|
|
11
|
+
};
|
|
12
|
+
export type RequestHeaders = Record<string, string>;
|
|
13
|
+
export type RequestCookies = Record<string, string>;
|
|
14
|
+
export type RequestContext<TUser extends AuthUser = AuthUser, TMetadata extends object = Record<string, unknown>> = {
|
|
15
|
+
requestId: string;
|
|
16
|
+
headers: RequestHeaders;
|
|
17
|
+
cookies: RequestCookies;
|
|
18
|
+
authToken?: string;
|
|
19
|
+
user?: TUser;
|
|
20
|
+
scope: string[];
|
|
21
|
+
metadata?: TMetadata;
|
|
22
|
+
ip?: string;
|
|
23
|
+
userAgent?: string;
|
|
24
|
+
receivedAt: number;
|
|
25
|
+
event: LambdaEvent;
|
|
26
|
+
};
|
|
27
|
+
export type AuthenticateFunction<TUser extends AuthUser = AuthUser> = (token: string, event: LambdaEvent) => Promise<AuthResult<TUser> | null>;
|
|
28
|
+
export type CreateContextOptions<TUser extends AuthUser = AuthUser> = {
|
|
29
|
+
authenticate?: AuthenticateFunction<TUser>;
|
|
30
|
+
};
|
|
31
|
+
export type NormalizedHeaders = Record<string, string> & {
|
|
32
|
+
authorization?: string;
|
|
33
|
+
'content-type'?: string;
|
|
34
|
+
'user-agent'?: string;
|
|
35
|
+
cookie?: string;
|
|
36
|
+
host?: string;
|
|
37
|
+
origin?: string;
|
|
38
|
+
referer?: string;
|
|
39
|
+
accept?: string;
|
|
40
|
+
'x-forwarded-for'?: string;
|
|
41
|
+
'x-forwarded-proto'?: string;
|
|
42
|
+
'x-request-id'?: string;
|
|
43
|
+
};
|
|
44
|
+
export type NormalizedRequest = {
|
|
45
|
+
headers: Record<string, string>;
|
|
46
|
+
body: unknown;
|
|
47
|
+
query: Record<string, unknown>;
|
|
48
|
+
params: Record<string, unknown>;
|
|
49
|
+
};
|
|
50
|
+
export type RuntimeAdapter = {
|
|
51
|
+
normalizeRequest(input: unknown): NormalizedRequest;
|
|
52
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { NormalizedHeaders, NormalizedRequest } from './types';
|
|
2
|
+
export declare function normalizeHeaders(headers?: Record<string, string | undefined>): NormalizedHeaders;
|
|
3
|
+
export declare function extractBearerToken(authorization?: string): string | null;
|
|
4
|
+
export declare function parseCookies(cookieHeader?: string): Record<string, string>;
|
|
5
|
+
export declare function safeJsonParse(value: string): any;
|
|
6
|
+
export declare function normalizeRequest(input: any): NormalizedRequest;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HttpError } from './HttpError';
|
|
2
|
+
export declare class UnauthorizedError extends HttpError {
|
|
3
|
+
constructor(message?: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class ForbiddenError extends HttpError {
|
|
6
|
+
constructor(message?: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class NotFoundError extends HttpError {
|
|
9
|
+
constructor(message?: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class ValidationError extends HttpError {
|
|
12
|
+
constructor(message?: string);
|
|
13
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { APIGatewayProxyResult } from 'aws-lambda';
|
|
2
|
+
export declare function json(body: any, statusCode?: number): APIGatewayProxyResult;
|
|
3
|
+
export declare function ok(body?: any): APIGatewayProxyResult;
|
|
4
|
+
export declare function badRequest(message?: string): APIGatewayProxyResult;
|
|
5
|
+
export declare function unauthorized(message?: string): APIGatewayProxyResult;
|
|
6
|
+
export declare function forbidden(message?: string): APIGatewayProxyResult;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './hooks';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AuthUser } from '../context';
|
|
2
|
+
import type { AppContext, RouteSchema } from '../routing';
|
|
3
|
+
import type { Middleware, NextFunction } from './types';
|
|
4
|
+
export declare function runMiddleware<TSchema extends RouteSchema = {}, TUser extends AuthUser = AuthUser, TPluginContext extends object = {}>(context: AppContext<TSchema, TUser, TPluginContext>, middleware: Middleware<TSchema, TUser, TPluginContext>[] | undefined, done: NextFunction): Promise<void>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AuthUser } from '../context';
|
|
2
|
+
import type { AppContext, RouteSchema } from '../routing';
|
|
3
|
+
export type NextFunction = () => Promise<void>;
|
|
4
|
+
export type MiddlewareResult = void | Promise<void>;
|
|
5
|
+
export type Middleware<TSchema extends RouteSchema = {}, TUser extends AuthUser = AuthUser, TPluginContext extends object = {}> = {
|
|
6
|
+
onInit?: (context: AppContext<TSchema, TUser, TPluginContext>) => MiddlewareResult;
|
|
7
|
+
onRequest?: (context: AppContext<TSchema, TUser, TPluginContext>, next: NextFunction) => MiddlewareResult;
|
|
8
|
+
onResponse?: (context: AppContext<TSchema, TUser, TPluginContext>) => MiddlewareResult;
|
|
9
|
+
onError?: (error: unknown, context: AppContext<TSchema, TUser, TPluginContext>) => MiddlewareResult;
|
|
10
|
+
afterResponse?: (context: AppContext<TSchema, TUser, TPluginContext>) => MiddlewareResult;
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Middleware } from '../middleware';
|
|
2
|
+
export type Plugin<TContext extends object = {}> = {
|
|
3
|
+
name: string;
|
|
4
|
+
dependsOn?: string[];
|
|
5
|
+
setup?: () => Promise<void> | void;
|
|
6
|
+
destroy?: () => Promise<void> | void;
|
|
7
|
+
onColdStart?: () => Promise<void> | void;
|
|
8
|
+
onRequest?: () => Promise<void> | void;
|
|
9
|
+
onRouteRegister?: (route: {
|
|
10
|
+
method: string;
|
|
11
|
+
path: string;
|
|
12
|
+
}) => Promise<void> | void;
|
|
13
|
+
extend?: () => TContext;
|
|
14
|
+
middleware?: Middleware<any, any, any>[];
|
|
15
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ResponseLike, type ResponseShape } from '../http';
|
|
2
|
+
import { type Middleware } from '../middleware';
|
|
3
|
+
import type { AuthResult, AuthUser } from '../context';
|
|
4
|
+
import type { Plugin } from '../plugins';
|
|
5
|
+
import type { ValidationSchema } from '../validation';
|
|
6
|
+
import type { AppContext, LambdaEvent, RouteSchema } from './types';
|
|
7
|
+
export type HandlerConfig<TSchema extends RouteSchema = {}, TUser extends AuthUser = AuthUser, TPluginContext extends object = {}> = {
|
|
8
|
+
middleware?: Middleware<TSchema, TUser, TPluginContext>[];
|
|
9
|
+
plugins?: Plugin<any>[];
|
|
10
|
+
validate?: ValidationSchema;
|
|
11
|
+
authenticate?: (token: string, event: LambdaEvent) => Promise<AuthResult<TUser> | null>;
|
|
12
|
+
handle: (context: AppContext<TSchema, TUser, TPluginContext>) => Promise<ResponseLike>;
|
|
13
|
+
};
|
|
14
|
+
export declare function defineHandler<TSchema extends RouteSchema = {}, TUser extends AuthUser = AuthUser, TPluginContext extends object = {}>(config: HandlerConfig<TSchema, TUser, TPluginContext>): (rawEvent: unknown, awsContext: unknown) => Promise<ResponseShape>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AuthResult, AuthUser } from '../context';
|
|
2
|
+
import type { ResponseLike } from '../http';
|
|
3
|
+
import type { Middleware } from '../middleware';
|
|
4
|
+
import type { Plugin } from '../plugins';
|
|
5
|
+
import type { ValidationSchema } from '../validation';
|
|
6
|
+
import type { AppContext, LambdaEvent, RouteDefinition, RouteSchema } from './types';
|
|
7
|
+
export type RouteConfig<TSchema extends RouteSchema = {}, TUser extends AuthUser = AuthUser, TPluginContext extends object = {}> = {
|
|
8
|
+
method: string;
|
|
9
|
+
path: string;
|
|
10
|
+
summary?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
tags?: string[];
|
|
13
|
+
deprecated?: boolean;
|
|
14
|
+
validate?: ValidationSchema;
|
|
15
|
+
middleware?: Middleware<TSchema, TUser, TPluginContext>[];
|
|
16
|
+
plugins?: Plugin<any>[];
|
|
17
|
+
authenticate?: (token: string, event: LambdaEvent) => Promise<AuthResult<TUser> | null>;
|
|
18
|
+
handler: (context: AppContext<TSchema, TUser, TPluginContext>) => Promise<ResponseLike>;
|
|
19
|
+
};
|
|
20
|
+
export declare function defineRoute<TSchema extends RouteSchema = {}, TUser extends AuthUser = AuthUser, TPluginContext extends object = {}>(config: RouteConfig<TSchema, TUser, TPluginContext>): RouteDefinition;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Cache } from '../cache';
|
|
2
|
+
import type { AuthUser, RequestContext } from '../context';
|
|
3
|
+
import type { APIGatewayProxyEventV2, EventBridgeEvent, SQSEvent } from 'aws-lambda';
|
|
4
|
+
export type ApiGwV2Event = {
|
|
5
|
+
source: 'apigw-v2';
|
|
6
|
+
} & APIGatewayProxyEventV2;
|
|
7
|
+
export type SqsEvent = {
|
|
8
|
+
source: 'sqs';
|
|
9
|
+
} & SQSEvent;
|
|
10
|
+
export type EventBridgeEvt = {
|
|
11
|
+
source: 'eventbridge';
|
|
12
|
+
} & EventBridgeEvent<string, any>;
|
|
13
|
+
export type LambdaEvent = ApiGwV2Event | SqsEvent | EventBridgeEvt;
|
|
14
|
+
import type { Logger } from '../../logger';
|
|
15
|
+
export type RouteDefinition = {
|
|
16
|
+
__isRoute: true;
|
|
17
|
+
method: string;
|
|
18
|
+
path: string;
|
|
19
|
+
summary?: string;
|
|
20
|
+
tags?: string[];
|
|
21
|
+
schema?: RouteSchema;
|
|
22
|
+
handler: (event: unknown, awsContext: unknown) => Promise<any>;
|
|
23
|
+
};
|
|
24
|
+
export type ExecutionContext = {
|
|
25
|
+
isColdStart: boolean;
|
|
26
|
+
containerId: string;
|
|
27
|
+
cache: Cache;
|
|
28
|
+
startedAt: number;
|
|
29
|
+
};
|
|
30
|
+
export type RouteSchema = {
|
|
31
|
+
body?: unknown;
|
|
32
|
+
query?: unknown;
|
|
33
|
+
params?: unknown;
|
|
34
|
+
headers?: unknown;
|
|
35
|
+
};
|
|
36
|
+
export type AppContext<TSchema extends RouteSchema = {}, TUser extends AuthUser = AuthUser, TPluginContext extends object = {}> = {
|
|
37
|
+
event: unknown;
|
|
38
|
+
awsContext: unknown;
|
|
39
|
+
exec: ExecutionContext;
|
|
40
|
+
req: RequestContext<TUser>;
|
|
41
|
+
body: TSchema['body'];
|
|
42
|
+
query: TSchema['query'];
|
|
43
|
+
params: TSchema['params'];
|
|
44
|
+
log: Logger;
|
|
45
|
+
state: Record<string, unknown>;
|
|
46
|
+
setHeader: (key: string, value: string) => void;
|
|
47
|
+
getHeader: (key: string) => string | undefined;
|
|
48
|
+
status: (code: number) => void;
|
|
49
|
+
setCookie: (name: string, value: string, options?: {
|
|
50
|
+
path?: string;
|
|
51
|
+
httpOnly?: boolean;
|
|
52
|
+
secure?: boolean;
|
|
53
|
+
maxAge?: number;
|
|
54
|
+
}) => void;
|
|
55
|
+
} & TPluginContext;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type Validator<TOutput = any, TInput = unknown> = (input: TInput) => Promise<TOutput> | TOutput;
|
|
2
|
+
export type ValidatorInput = Record<string, unknown>;
|
|
3
|
+
export type ValidationSchema = {
|
|
4
|
+
body?: Validator<any, unknown>;
|
|
5
|
+
query?: Validator<any, unknown>;
|
|
6
|
+
params?: Validator<any, unknown>;
|
|
7
|
+
headers?: Validator<any, unknown>;
|
|
8
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t from"node:crypto";import e from"node:fs";import r from"node:path";import{pathToFileURL as o}from"node:url";import n from"@fastify/cookie";import s from"chokidar";import a from"fastify";Math.random().toString(36).slice(2);const i=[];function c(t){return t.endsWith(".ts")||t.endsWith(".js")||t.endsWith(".mjs")||t.endsWith(".cjs")}function u(t){if(t)return function(t){try{return JSON.parse(t)}catch{return t}}(t)}function h(e){return{headers:e.headers,body:e.body,queryStringParameters:e.query,pathParameters:e.params,cookies:e.cookies,rawPath:e.url,requestContext:{requestId:t.randomUUID(),http:{method:e.method}}}}async function d(t,n){const s=e.readdirSync(n,{withFileTypes:!0});for(const e of s){const s=r.join(n,e.name);if(e.isDirectory()){await d(t,s);continue}if(!c(e.name))continue;const a=(await import(o(s).href+"?v="+Date.now())).default;a?.__isRoute&&(i.push({method:a.method.toUpperCase(),path:a.path,file:s}),t.route({method:a.method.toUpperCase(),url:a.path,handler:async(t,e)=>{try{const r=await a.handler(h(t),{});if(e.code(r.statusCode),r.headers)for(const[t,o]of Object.entries(r.headers))e.header(t,o);return u(r.body)}catch(t){e.code(500).send({error:t.message,stack:t.stack,path:a.path})}}}))}}async function f(t={}){const o=a({logger:t.logger??!0});await o.register(n);const c=(u=t.routesDir,r.resolve(process.cwd(),u||process.env.ROUTES_DIR||"src/routes"));var u;if(!e.existsSync(c))throw Error("Routes directory not found: "+c);return function(t){t.get("/__routes",async()=>({routes:i})),t.get("/__health",async()=>({ok:!0,ts:Date.now()}))}(o),await d(o,c),!1!==t.hmr&&function(t,e){s.watch(e,{ignoreInitial:!0}).on("change",t=>{process.exit(0)})}(0,c),o}async function p(t={}){const e=await f(t),r=function(t){return Number(t||process.env.PORT||3e3)}(t.port),o=function(t){return t||process.env.HOST||"0.0.0.0"}(t.host);return await e.listen({port:r,host:o}),e}export{p as bootstrap,f as createDevApp};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t from"node:crypto";class e{constructor(){this.store=new Map}get(t){const e=this.store.get(t);return e?Date.now()>e.expireAt?(this.store.delete(t),null):e.value:null}set(t,e,s=5e3){this.store.set(t,{value:e,expireAt:Date.now()+s})}delete(t){this.store.delete(t)}clear(){this.store.clear()}has(t){return null!==this.get(t)}async getOrSet(t,e,s=5e3){const n=this.get(t);if(null!==n)return n;const o=await e();return this.set(t,o,s),o}startCleanup(t=3e4){setInterval(()=>{const t=Date.now();for(const[e,s]of this.store.entries())t>s.expireAt&&this.store.delete(e)},t)}}const s=new e;function n(t){const e={};for(const[s,n]of Object.entries(t||{}))"string"==typeof n&&(e[s.toLowerCase()]=n);return e}function o(t){if(!t)return null;const e=t.match(/^Bearer\s+(.+)$/i);return e?e[1]:null}function a(t){return t?t.split(";").map(t=>t.trim()).filter(Boolean).reduce((t,e)=>{const s=e.indexOf("=");if(-1===s)return t;const n=e.slice(0,s),o=e.slice(s+1);return t[n]=decodeURIComponent(o),t},{}):{}}function r(t){try{return JSON.parse(t)}catch{return t}}function i(t){return{headers:n(t?.headers),body:"string"==typeof t?.body?r(t.body):t?.body,query:t?.queryStringParameters||{},params:t?.pathParameters||{}}}class u extends Error{constructor(t,e,s){super(e),this.statusCode=t,this.expose=t<500,this.details=s,Object.setPrototypeOf(this,u.prototype)}}function c(t){return t instanceof u?{statusCode:t.statusCode,headers:{"content-type":"application/json"},body:JSON.stringify({message:t.message,...t.expose&&t.details?{details:t.details}:{},..."production"!==process.env.NODE_ENV?{stack:t.stack}:{}})}:{statusCode:500,headers:{"content-type":"application/json"},body:JSON.stringify({message:"Internal Server Error"})}}class d extends u{constructor(t="Unauthorized"){super(401,t)}}class l extends u{constructor(t="Forbidden"){super(403,t)}}class p extends u{constructor(t="Not Found"){super(404,t)}}class h extends u{constructor(t="Validation Failed"){super(400,t)}}function f(t,e=200){return{statusCode:e,headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}}function y(t=""){return{statusCode:200,body:"string"==typeof t?t:JSON.stringify(t)}}function m(t="Bad Request"){return f({message:t},400)}function g(t="Unauthorized"){return f({message:t},401)}function w(t="Forbidden"){return f({message:t},403)}function C(t){return"object"==typeof(e=t)&&null!==e&&"statusCode"in e&&"body"in e?t:null==t?{statusCode:204,body:""}:"string"==typeof t?{statusCode:200,headers:{"content-type":"text/plain"},body:t}:{statusCode:200,headers:{"content-type":"application/json"},body:JSON.stringify(t)};var e}async function b(t,e=[],s){await async function n(o){const a=e[o];if(!a)return s();if(!a.onRequest)return n(o+1);let r=!1;await a.onRequest(t,async()=>{if(r)throw Error("next() called multiple times");r=!0,await n(o+1)})}(0)}async function x(t,e){return t(e)}const q=Math.random().toString(36).slice(2);const v=new WeakSet;let S=!0;function O(e){return async(r,u)=>{let d;try{const c=r,l=i(c);e.validate?.body&&(l.body=await x(e.validate.body,l.body)),e.validate?.query&&(l.query=await x(e.validate.query,l.query)),e.validate?.params&&(l.params=await x(e.validate.params,l.params));const p=function({isColdStart:t}){return{isColdStart:t,containerId:q,cache:s,startedAt:Date.now()}}({isColdStart:S}),h=await async function(e,s){const r=n(e?.headers),i=a(r.cookie),u=o(r.authorization);let c,d,l=[];if(u&&s?.authenticate){const t=await s.authenticate(u,e);t&&(c=t.user,l=t.scope||[],d=t.metadata)}return{requestId:e?.requestContext?.requestId??t.randomUUID(),headers:r,cookies:i,authToken:u||void 0,user:c,scope:l,metadata:d,ip:e?.requestContext?.http?.sourceIp,userAgent:r["user-agent"],receivedAt:Date.now(),event:e}}(c,{authenticate:e.authenticate});for(const t of e.plugins||[])v.has(t)||(await(t.setup?.()),v.add(t));const f=Object.assign({},...(e.plugins||[]).map(t=>t.extend?.()||{})),y={statusCode:200,headers:{},cookies:[]},m={info:(t,e)=>{},warn:(t,e)=>{},error:(t,e)=>{},debug:(t,e)=>{}};if(d={event:c,awsContext:u,exec:p,req:h,body:l.body,query:l.query,params:l.params,state:{},log:f.log||m,setHeader:(t,e)=>{y.headers[t]=e},getHeader:t=>y.headers[t],status:t=>{y.statusCode=t},setCookie:(t,e,s={})=>{const n=[`${t}=${encodeURIComponent(e)}`];s.path&&n.push("Path="+s.path),s.httpOnly&&n.push("HttpOnly"),s.secure&&n.push("Secure"),s.maxAge&&n.push("Max-Age="+s.maxAge),y.cookies.push(n.join("; "))},...f},S){for(const t of e.plugins||[])await(t.onColdStart?.());for(const t of e.middleware||[])await(t.onInit?.(d));S=!1}for(const t of e.plugins||[])await(t.onRequest?.());let g;await b(d,e.middleware||[],async()=>{g=await e.handle(d)});for(const t of e.middleware||[])await(t.onResponse?.(d));const w=C(g);for(const t of e.middleware||[])await(t.afterResponse?.(d));return{...w,statusCode:y.statusCode||w.statusCode,headers:{...w.headers||{},...y.headers,...y.cookies.length?{"set-cookie":y.cookies.join(", ")}:{}}}}catch(t){if(d)for(const s of e.middleware||[])await(s.onError?.(t,d));return c(t)}}}function k(t){for(const e of t.plugins||[])e.onRouteRegister?.({method:t.method,path:t.path});const e=O({middleware:[...(t.plugins||[]).flatMap(t=>t.middleware||[]),...t.middleware||[]],plugins:t.plugins,authenticate:t.authenticate,handle:t.handler});return{__isRoute:!0,method:t.method,path:t.path,handler:e}}export{l as ForbiddenError,u as HttpError,e as MemoryCache,p as NotFoundError,d as UnauthorizedError,h as ValidationError,m as badRequest,O as defineHandler,k as defineRoute,o as extractBearerToken,w as forbidden,s as globalCache,c as handleError,f as json,n as normalizeHeaders,i as normalizeRequest,y as ok,a as parseCookies,b as runMiddleware,r as safeJsonParse,C as serializeResponse,g as unauthorized,x as validate};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function t(t){return t}function e(t){return async e=>{const r=[];for(const n of e.Records)try{const e=JSON.parse(n.body);await t.handler(e)}catch{r.push({itemIdentifier:n.messageId})}return{batchItemFailures:r}}}export{t as defineJob,e as sqsHandler};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(){return{name:"logger",middleware:[{onRequest:async(e,t)=>{const n=performance.now();let r,o;var a;(a=e.event)&&"object"==typeof a&&"requestContext"in a&&(r=e.event.requestContext.http?.method,o=e.event.rawPath),e.req.requestId,e.exec.isColdStart,e.exec.containerId,await t();const s=performance.now()-n;e.req.requestId,s.toFixed(2)},onResponse:async e=>{e.req.requestId}}]}}export{e as loggerPlugin};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"node:crypto";function t(e){const t={};for(const[s,a]of Object.entries(e||{}))"string"==typeof a&&(t[s.toLowerCase()]=a);return t}function s(e){try{return JSON.parse(e)}catch{return e}}async function a(s,a){const o=t(s?.headers),r=(n=o.cookie)?n.split(";").map(e=>e.trim()).filter(Boolean).reduce((e,t)=>{const s=t.indexOf("=");if(-1===s)return e;const a=t.slice(0,s),o=t.slice(s+1);return e[a]=decodeURIComponent(o),e},{}):{};var n;const i=function(e){if(!e)return null;const t=e.match(/^Bearer\s+(.+)$/i);return t?t[1]:null}(o.authorization);let u,c,d=[];if(i&&a?.authenticate){const e=await a.authenticate(i,s);e&&(u=e.user,d=e.scope||[],c=e.metadata)}return{requestId:s?.requestContext?.requestId??e.randomUUID(),headers:o,cookies:r,authToken:i||void 0,user:u,scope:d,metadata:c,ip:s?.requestContext?.http?.sourceIp,userAgent:o["user-agent"],receivedAt:Date.now(),event:s}}class o extends Error{constructor(e,t,s){super(t),this.statusCode=e,this.expose=e<500,this.details=s,Object.setPrototypeOf(this,o.prototype)}}async function r(e,t){return e(t)}const n=new class{constructor(){this.store=new Map}get(e){const t=this.store.get(e);return t?Date.now()>t.expireAt?(this.store.delete(e),null):t.value:null}set(e,t,s=5e3){this.store.set(e,{value:t,expireAt:Date.now()+s})}delete(e){this.store.delete(e)}clear(){this.store.clear()}has(e){return null!==this.get(e)}async getOrSet(e,t,s=5e3){const a=this.get(e);if(null!==a)return a;const o=await t();return this.set(e,o,s),o}startCleanup(e=3e4){setInterval(()=>{const e=Date.now();for(const[t,s]of this.store.entries())e>s.expireAt&&this.store.delete(t)},e)}},i=Math.random().toString(36).slice(2);const u=new WeakSet;let c=!0;function d(e){return async(d,l)=>{let p;try{const o=d,h=(y=o,{headers:t(y?.headers),body:"string"==typeof y?.body?s(y.body):y?.body,query:y?.queryStringParameters||{},params:y?.pathParameters||{}});e.validate?.body&&(h.body=await r(e.validate.body,h.body)),e.validate?.query&&(h.query=await r(e.validate.query,h.query)),e.validate?.params&&(h.params=await r(e.validate.params,h.params));const f=function({isColdStart:e}){return{isColdStart:e,containerId:i,cache:n,startedAt:Date.now()}}({isColdStart:c}),m=await a(o,{authenticate:e.authenticate});for(const t of e.plugins||[])u.has(t)||(await(t.setup?.()),u.add(t));const w=Object.assign({},...(e.plugins||[]).map(e=>e.extend?.()||{})),g={statusCode:200,headers:{},cookies:[]},b={info:(e,t)=>{},warn:(e,t)=>{},error:(e,t)=>{},debug:(e,t)=>{}};if(p={event:o,awsContext:l,exec:f,req:m,body:h.body,query:h.query,params:h.params,state:{},log:w.log||b,setHeader:(e,t)=>{g.headers[e]=t},getHeader:e=>g.headers[e],status:e=>{g.statusCode=e},setCookie:(e,t,s={})=>{const a=[`${e}=${encodeURIComponent(t)}`];s.path&&a.push("Path="+s.path),s.httpOnly&&a.push("HttpOnly"),s.secure&&a.push("Secure"),s.maxAge&&a.push("Max-Age="+s.maxAge),g.cookies.push(a.join("; "))},...w},c){for(const t of e.plugins||[])await(t.onColdStart?.());for(const t of e.middleware||[])await(t.onInit?.(p));c=!1}for(const t of e.plugins||[])await(t.onRequest?.());let C;await async function(e,t=[],s){await async function a(o){const r=t[o];if(!r)return s();if(!r.onRequest)return a(o+1);let n=!1;await r.onRequest(e,async()=>{if(n)throw Error("next() called multiple times");n=!0,await a(o+1)})}(0)}(p,e.middleware||[],async()=>{C=await e.handle(p)});for(const t of e.middleware||[])await(t.onResponse?.(p));const q=function(e){return"object"==typeof(t=e)&&null!==t&&"statusCode"in t&&"body"in t?e:null==e?{statusCode:204,body:""}:"string"==typeof e?{statusCode:200,headers:{"content-type":"text/plain"},body:e}:{statusCode:200,headers:{"content-type":"application/json"},body:JSON.stringify(e)};var t}(C);for(const t of e.middleware||[])await(t.afterResponse?.(p));return{...q,statusCode:g.statusCode||q.statusCode,headers:{...q.headers||{},...g.headers,...g.cookies.length?{"set-cookie":g.cookies.join(", ")}:{}}}}catch(t){if(p)for(const s of e.middleware||[])await(s.onError?.(t,p));return function(e){return e instanceof o?{statusCode:e.statusCode,headers:{"content-type":"application/json"},body:JSON.stringify({message:e.message,...e.expose&&e.details?{details:e.details}:{},..."production"!==process.env.NODE_ENV?{stack:e.stack}:{}})}:{statusCode:500,headers:{"content-type":"application/json"},body:JSON.stringify({message:"Internal Server Error"})}}(t)}var y}}function l(e){const t=d(e);return async(e,s)=>t(e,s)}function p(e){return{headers:t(e.headers),body:"string"==typeof e.body?s(e.body):e.body,query:e.queryStringParameters||{},params:e.pathParameters||{}}}const y={normalizeRequest:p};function h(e){const t=d(e);return async(e,s)=>{const a=await t(e,s);s.status(a.statusCode).headers(a.headers||{}).send(a.body)}}function f(e){return{headers:t(e.headers),body:e.body,query:e.query,params:e.params}}const m={normalizeRequest:f};export{y as awsAdapter,l as createAWSHandler,h as createFastifyHandler,m as fastifyAdapter,p as normalizeAWSRequest,f as normalizeFastifyRequest};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ApiGatewayManagementApi as t}from"@aws-sdk/client-apigatewaymanagementapi";function n(t){return"string"==typeof t?Buffer.from(t):t instanceof Uint8Array?t:Buffer.from(JSON.stringify(t))}function e(e){return async o=>{const a=o.requestContext.routeKey,s=o.requestContext.connectionId||"",c=function(n){const e=n.requestContext.domainName,o=n.requestContext.stage;return new t({endpoint:`https://${e}/${o}`})}(o),i={event:o,connectionId:s,routeKey:a,send:async(t,e)=>{const o=e??s;o&&await c.postToConnection({ConnectionId:o,Data:n(t)})},broadcast:async(t,e)=>{const o=n(e);await Promise.all(t.map(async t=>{try{await c.postToConnection({ConnectionId:t,Data:o})}catch(t){}}))}};return"$connect"===a?await(e.connect?.(i))??{statusCode:200,body:""}:"$disconnect"===a?await(e.disconnect?.(i))??{statusCode:200,body:""}:await(e.message?.(i))??{statusCode:200,body:""}}}export{e as wsHandler};
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './core';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type HandlerConfig } from '../../core/routing';
|
|
2
|
+
import type { APIGatewayProxyEventV2, Context } from 'aws-lambda';
|
|
3
|
+
export declare function createAWSHandler(config: HandlerConfig<any, any, any>): (event: APIGatewayProxyEventV2, context: Context) => Promise<import("../..").ResponseShape>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type NormalizedRequest, type RuntimeAdapter } from '../../core/context';
|
|
2
|
+
import type { APIGatewayProxyEventV2 } from 'aws-lambda';
|
|
3
|
+
export declare function normalizeAWSRequest(event: APIGatewayProxyEventV2): NormalizedRequest;
|
|
4
|
+
export declare const awsAdapter: RuntimeAdapter;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { APIGatewayProxyResult, APIGatewayProxyWebsocketEventV2 } from 'aws-lambda';
|
|
2
|
+
export type WSResponse = APIGatewayProxyResult;
|
|
3
|
+
export type WSSendPayload = string | Record<string, unknown> | Uint8Array;
|
|
4
|
+
export type WSContext = {
|
|
5
|
+
event: APIGatewayProxyWebsocketEventV2;
|
|
6
|
+
connectionId: string;
|
|
7
|
+
routeKey: string;
|
|
8
|
+
send: (payload: WSSendPayload, connectionId?: string) => Promise<void>;
|
|
9
|
+
broadcast: (connectionIds: string[], payload: WSSendPayload) => Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
export type WSHandler = ((context: WSContext) => Promise<WSResponse>) | ((context: WSContext) => WSResponse);
|
|
12
|
+
export declare function wsHandler(handlers: {
|
|
13
|
+
connect?: WSHandler;
|
|
14
|
+
message?: WSHandler;
|
|
15
|
+
disconnect?: WSHandler;
|
|
16
|
+
}): (event: APIGatewayProxyWebsocketEventV2) => Promise<WSResponse>;
|