lambda-pipe 1.0.1 → 1.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/README.md CHANGED
@@ -487,15 +487,17 @@ import {
487
487
  // src/functions/user.ts
488
488
 
489
489
  import {
490
- lambdaHandler,
490
+ createAWSHandler,
491
491
  } from 'lambda-pipe/runtime'
492
492
 
493
493
  import route from '../routes/user'
494
494
 
495
495
  export const handler =
496
- lambdaHandler(route)
496
+ createAWSHandler(route)
497
497
  ```
498
498
 
499
+ > Production-ready AWS Lambda handler for API Gateway and Lambda Function URLs.
500
+
499
501
  ---
500
502
 
501
503
  # SQS Job Example
@@ -767,6 +769,124 @@ See: [DOC](https://github.com/delpikye-v/lambda-pipe/blob/main/docs/prod-server.
767
769
 
768
770
  ---
769
771
 
772
+ # Fastify Runtime Example
773
+
774
+ Use LambdaPipe routes directly inside a Fastify server.
775
+
776
+ ## Server Setup
777
+
778
+ ```ts
779
+ // src/server.ts
780
+
781
+ import Fastify from 'fastify'
782
+
783
+ import {
784
+ createFastifyHandler,
785
+ } from 'lambda-pipe/runtime'
786
+
787
+ import userRoute from './routes/user'
788
+ import healthRoute from './routes/health'
789
+
790
+ const app = Fastify({
791
+ logger: true,
792
+ })
793
+
794
+ // ============================================
795
+ // register routes
796
+ // ============================================
797
+
798
+ app.route({
799
+ method: userRoute.method,
800
+
801
+ url: userRoute.path,
802
+
803
+ handler:
804
+ createFastifyHandler(userRoute),
805
+ })
806
+
807
+ app.route({
808
+ method: healthRoute.method,
809
+
810
+ url: healthRoute.path,
811
+
812
+ handler:
813
+ createFastifyHandler(healthRoute),
814
+ })
815
+
816
+ // ============================================
817
+ // start server
818
+ // ============================================
819
+
820
+ await app.listen({
821
+ port: 3000,
822
+
823
+ host: '0.0.0.0',
824
+ })
825
+
826
+ console.log(
827
+ '🚀 http://localhost:3000',
828
+ )
829
+ ```
830
+
831
+ ## Route Example
832
+
833
+ ```ts
834
+ // src/routes/user.ts
835
+
836
+ import {
837
+ defineRoute,
838
+ ok,
839
+ } from 'lambda-pipe'
840
+
841
+ export default defineRoute({
842
+ method: 'GET',
843
+
844
+ path: '/user/:id',
845
+
846
+ handler: async (context) => {
847
+ return ok({
848
+ id:
849
+ context.pathParameters.id,
850
+
851
+ runtime: 'fastify',
852
+ })
853
+ },
854
+ })
855
+ ```
856
+
857
+ ## Health Route
858
+
859
+ ```ts
860
+ // src/routes/health.ts
861
+
862
+ import {
863
+ defineRoute,
864
+ ok,
865
+ } from 'lambda-pipe'
866
+
867
+ export default defineRoute({
868
+ method: 'GET',
869
+
870
+ path: '/health',
871
+
872
+ handler: async () => {
873
+ return ok({
874
+ ok: true,
875
+ })
876
+ },
877
+ })
878
+ ```
879
+
880
+ ## Run Server
881
+
882
+ ```bash
883
+ tsx src/server.ts
884
+
885
+ # curl http://localhost:3000/user/123
886
+ ```
887
+
888
+ ---
889
+
770
890
  # Philosophy
771
891
 
772
892
  ```text
@@ -1 +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;
1
+ "use strict";var e=require("node:crypto");function t(e){if(e)try{return JSON.parse(e)}catch{return e}}function r(e){return{statusCode:e.statusCode||200,headers:e.headers||{},body:"string"==typeof e.body?e.body:JSON.stringify(e.body??{})}}function s(e){const t={};for(const[r,s]of Object.entries(e||{}))"string"==typeof s&&(t[r.toLowerCase()]=s);return t}function a(e){try{return JSON.parse(e)}catch{return e}}function o(e){return{headers:s(e.headers),body:"string"==typeof e.body?a(e.body):e.body,query:e.queryStringParameters||{},params:e.pathParameters||{}}}const n={normalizeRequest:o};async function i(t,r){const a=s(t?.headers),o=(n=a.cookie)?n.split(";").map(e=>e.trim()).filter(Boolean).reduce((e,t)=>{const r=t.indexOf("=");if(-1===r)return e;const s=t.slice(0,r),a=t.slice(r+1);return e[s]=decodeURIComponent(a),e},{}):{};var n;const i=function(e){if(!e)return null;const t=e.match(/^Bearer\s+(.+)$/i);return t?t[1]:null}(a.authorization);let u,c,d=[];if(i&&r?.authenticate){const e=await r.authenticate(i,t);e&&(u=e.user,d=e.scope||[],c=e.metadata)}return{requestId:t?.requestContext?.requestId??e.randomUUID(),headers:a,cookies:o,authToken:i||void 0,user:u,scope:d,metadata:c,ip:t?.requestContext?.http?.sourceIp,userAgent:a["user-agent"],receivedAt:Date.now(),event:t}}class u extends Error{constructor(e,t,r){super(t),this.statusCode=e,this.expose=e<500,this.details=r,Object.setPrototypeOf(this,u.prototype)}}async function c(e,t){return e(t)}const d=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,r=5e3){this.store.set(e,{value:t,expireAt:Date.now()+r})}delete(e){this.store.delete(e)}clear(){this.store.clear()}has(e){return null!==this.get(e)}async getOrSet(e,t,r=5e3){const s=this.get(e);if(null!==s)return s;const a=await t();return this.set(e,a,r),a}startCleanup(e=3e4){setInterval(()=>{const e=Date.now();for(const[t,r]of this.store.entries())e>r.expireAt&&this.store.delete(t)},e)}},l=Math.random().toString(36).slice(2);const p=new WeakSet;let y=!0;function h(e){return async(t,r)=>{let o;try{const u=t,h=(n=u,{headers:s(n?.headers),body:"string"==typeof n?.body?a(n.body):n?.body,query:n?.queryStringParameters||{},params:n?.pathParameters||{}});e.validate?.body&&(h.body=await c(e.validate.body,h.body)),e.validate?.query&&(h.query=await c(e.validate.query,h.query)),e.validate?.params&&(h.params=await c(e.validate.params,h.params));const f=function({isColdStart:e}){return{isColdStart:e,containerId:l,cache:d,startedAt:Date.now()}}({isColdStart:y}),m=await i(u,{authenticate:e.authenticate});for(const t of e.plugins||[])p.has(t)||(await(t.setup?.()),p.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(o={event:u,awsContext:r,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,r={})=>{const s=[`${e}=${encodeURIComponent(t)}`];r.path&&s.push("Path="+r.path),r.httpOnly&&s.push("HttpOnly"),r.secure&&s.push("Secure"),r.maxAge&&s.push("Max-Age="+r.maxAge),g.cookies.push(s.join("; "))},...w},y){for(const t of e.plugins||[])await(t.onColdStart?.());for(const t of e.middleware||[])await(t.onInit?.(o));y=!1}for(const t of e.plugins||[])await(t.onRequest?.());let C;await async function(e,t=[],r){await async function s(a){const o=t[a];if(!o)return r();if(!o.onRequest)return s(a+1);let n=!1;await o.onRequest(e,async()=>{if(n)throw Error("next() called multiple times");n=!0,await s(a+1)})}(0)}(o,e.middleware||[],async()=>{C=await e.handle(o)});for(const t of e.middleware||[])await(t.onResponse?.(o));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?.(o));return{...q,statusCode:g.statusCode||q.statusCode,headers:{...q.headers||{},...g.headers,...g.cookies.length?{"set-cookie":g.cookies.join(", ")}:{}}}}catch(t){if(o)for(const r of e.middleware||[])await(r.onError?.(t,o));return function(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"})}}(t)}var n}}function f(e){return{headers:s(e.headers),body:e.body,query:e.query,params:e.params}}const m={normalizeRequest:f};exports.awsAdapter=n,exports.createAWSHandler=function(e){return async(s,a)=>{const o=function(e,r){return{headers:e.headers||{},body:t(e.body),queryStringParameters:e.queryStringParameters||{},pathParameters:e.pathParameters||{},cookies:e.cookies||[],rawPath:e.rawPath,method:e.requestContext.http.method,requestId:r.awsRequestId,event:e,awsContext:r}}(s,a);try{await async function(e=[],t){for(const r of e)await r(t)}(e.middlewares,o);return r(await e.handler(o,null))}catch(e){const t=await async function(e,t,r=[]){for(const s of r)s.onError&&await s.onError(e,t);return t.reply.code(e.statusCode||500),{error:e.message||"Internal Server Error",stack:"development"===process.env.NODE_ENV?e.stack:void 0}}(e,{...o,reply:{code:()=>{}}},[]);return r({statusCode:500,body:t})}}},exports.createFastifyHandler=function(e){const t=h(e);return async(e,r)=>{try{const s=await t(e,r);if(r.status(s.statusCode||200),s.headers)for(const[e,t]of Object.entries(s.headers))r.header(e,t);return r.send(s.body)}catch(t){return e.log.error(t),r.status(500).send({error:t?.message||"Internal Server Error"})}}},exports.fastifyAdapter=m,exports.normalizeAWSRequest=o,exports.normalizeFastifyRequest=f;
@@ -0,0 +1,12 @@
1
+ import type { RuntimeMiddleware, RuntimePlugin } from './shared';
2
+ export type CreateDevAppOptions = {
3
+ port?: number;
4
+ host?: string;
5
+ routesDir?: string;
6
+ logger?: boolean;
7
+ hmr?: boolean;
8
+ plugins?: RuntimePlugin[];
9
+ globalMiddlewares?: RuntimeMiddleware[];
10
+ };
11
+ export declare function createDevApp(options?: CreateDevAppOptions): Promise<import("fastify").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>>;
12
+ export declare function bootstrap(options?: CreateDevAppOptions): Promise<import("fastify").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>>;
@@ -1,13 +1,2 @@
1
- import type { RuntimeMiddleware, RuntimePlugin } from './shared';
1
+ export * from './dev-runtime';
2
2
  export type { RouteDefinition, RouteLoader, RuntimeContext, RuntimeMiddleware, RuntimePlugin, RuntimeResponse, } from './shared';
3
- export type CreateDevAppOptions = {
4
- port?: number;
5
- host?: string;
6
- routesDir?: string;
7
- logger?: boolean;
8
- hmr?: boolean;
9
- plugins?: RuntimePlugin[];
10
- globalMiddlewares?: RuntimeMiddleware[];
11
- };
12
- export declare function createDevApp(options?: CreateDevAppOptions): Promise<import("fastify").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>>;
13
- export declare function bootstrap(options?: CreateDevAppOptions): Promise<import("fastify").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,13 @@
1
+ import type { RouteLoader, RuntimeMiddleware, RuntimePlugin } from './shared';
2
+ export type CreateRuntimeAppOptions = {
3
+ routes: RouteLoader[];
4
+ logger?: boolean;
5
+ plugins?: RuntimePlugin[];
6
+ globalMiddlewares?: RuntimeMiddleware[];
7
+ };
8
+ export type BootstrapOptions = CreateRuntimeAppOptions & {
9
+ port?: number;
10
+ host?: string;
11
+ };
12
+ export declare function createRuntimeApp(options: CreateRuntimeAppOptions): Promise<import("fastify").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>>;
13
+ export declare function bootstrap(options: BootstrapOptions): Promise<import("fastify").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>>;
@@ -1,14 +1,2 @@
1
- import type { RouteLoader, RuntimeMiddleware, RuntimePlugin } from './shared';
1
+ export * from './server-runtime';
2
2
  export type { RouteDefinition, RouteLoader, RuntimeContext, RuntimeMiddleware, RuntimePlugin, RuntimeResponse, } from './shared';
3
- export type CreateRuntimeAppOptions = {
4
- routes: RouteLoader[];
5
- logger?: boolean;
6
- plugins?: RuntimePlugin[];
7
- globalMiddlewares?: RuntimeMiddleware[];
8
- };
9
- export type BootstrapOptions = CreateRuntimeAppOptions & {
10
- port?: number;
11
- host?: string;
12
- };
13
- export declare function createRuntimeApp(options: CreateRuntimeAppOptions): Promise<import("fastify").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>>;
14
- export declare function bootstrap(options: BootstrapOptions): Promise<import("fastify").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>>;
@@ -1 +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};
1
+ import e from"node:crypto";function t(e){if(e)try{return JSON.parse(e)}catch{return e}}function r(e){return{statusCode:e.statusCode||200,headers:e.headers||{},body:"string"==typeof e.body?e.body:JSON.stringify(e.body??{})}}function s(e){return async(s,a)=>{const o=function(e,r){return{headers:e.headers||{},body:t(e.body),queryStringParameters:e.queryStringParameters||{},pathParameters:e.pathParameters||{},cookies:e.cookies||[],rawPath:e.rawPath,method:e.requestContext.http.method,requestId:r.awsRequestId,event:e,awsContext:r}}(s,a);try{await async function(e=[],t){for(const r of e)await r(t)}(e.middlewares,o);return r(await e.handler(o,null))}catch(e){const t=await async function(e,t,r=[]){for(const s of r)s.onError&&await s.onError(e,t);return t.reply.code(e.statusCode||500),{error:e.message||"Internal Server Error",stack:"development"===process.env.NODE_ENV?e.stack:void 0}}(e,{...o,reply:{code:()=>{}}},[]);return r({statusCode:500,body:t})}}}function a(e){const t={};for(const[r,s]of Object.entries(e||{}))"string"==typeof s&&(t[r.toLowerCase()]=s);return t}function o(e){try{return JSON.parse(e)}catch{return e}}function n(e){return{headers:a(e.headers),body:"string"==typeof e.body?o(e.body):e.body,query:e.queryStringParameters||{},params:e.pathParameters||{}}}const i={normalizeRequest:n};async function u(t,r){const s=a(t?.headers),o=(n=s.cookie)?n.split(";").map(e=>e.trim()).filter(Boolean).reduce((e,t)=>{const r=t.indexOf("=");if(-1===r)return e;const s=t.slice(0,r),a=t.slice(r+1);return e[s]=decodeURIComponent(a),e},{}):{};var n;const i=function(e){if(!e)return null;const t=e.match(/^Bearer\s+(.+)$/i);return t?t[1]:null}(s.authorization);let u,c,d=[];if(i&&r?.authenticate){const e=await r.authenticate(i,t);e&&(u=e.user,d=e.scope||[],c=e.metadata)}return{requestId:t?.requestContext?.requestId??e.randomUUID(),headers:s,cookies:o,authToken:i||void 0,user:u,scope:d,metadata:c,ip:t?.requestContext?.http?.sourceIp,userAgent:s["user-agent"],receivedAt:Date.now(),event:t}}class c extends Error{constructor(e,t,r){super(t),this.statusCode=e,this.expose=e<500,this.details=r,Object.setPrototypeOf(this,c.prototype)}}async function d(e,t){return e(t)}const l=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,r=5e3){this.store.set(e,{value:t,expireAt:Date.now()+r})}delete(e){this.store.delete(e)}clear(){this.store.clear()}has(e){return null!==this.get(e)}async getOrSet(e,t,r=5e3){const s=this.get(e);if(null!==s)return s;const a=await t();return this.set(e,a,r),a}startCleanup(e=3e4){setInterval(()=>{const e=Date.now();for(const[t,r]of this.store.entries())e>r.expireAt&&this.store.delete(t)},e)}},y=Math.random().toString(36).slice(2);const p=new WeakSet;let h=!0;function f(e){return async(t,r)=>{let s;try{const i=t,c=(n=i,{headers:a(n?.headers),body:"string"==typeof n?.body?o(n.body):n?.body,query:n?.queryStringParameters||{},params:n?.pathParameters||{}});e.validate?.body&&(c.body=await d(e.validate.body,c.body)),e.validate?.query&&(c.query=await d(e.validate.query,c.query)),e.validate?.params&&(c.params=await d(e.validate.params,c.params));const f=function({isColdStart:e}){return{isColdStart:e,containerId:y,cache:l,startedAt:Date.now()}}({isColdStart:h}),m=await u(i,{authenticate:e.authenticate});for(const t of e.plugins||[])p.has(t)||(await(t.setup?.()),p.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(s={event:i,awsContext:r,exec:f,req:m,body:c.body,query:c.query,params:c.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,r={})=>{const s=[`${e}=${encodeURIComponent(t)}`];r.path&&s.push("Path="+r.path),r.httpOnly&&s.push("HttpOnly"),r.secure&&s.push("Secure"),r.maxAge&&s.push("Max-Age="+r.maxAge),g.cookies.push(s.join("; "))},...w},h){for(const t of e.plugins||[])await(t.onColdStart?.());for(const t of e.middleware||[])await(t.onInit?.(s));h=!1}for(const t of e.plugins||[])await(t.onRequest?.());let C;await async function(e,t=[],r){await async function s(a){const o=t[a];if(!o)return r();if(!o.onRequest)return s(a+1);let n=!1;await o.onRequest(e,async()=>{if(n)throw Error("next() called multiple times");n=!0,await s(a+1)})}(0)}(s,e.middleware||[],async()=>{C=await e.handle(s)});for(const t of e.middleware||[])await(t.onResponse?.(s));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?.(s));return{...q,statusCode:g.statusCode||q.statusCode,headers:{...q.headers||{},...g.headers,...g.cookies.length?{"set-cookie":g.cookies.join(", ")}:{}}}}catch(t){if(s)for(const r of e.middleware||[])await(r.onError?.(t,s));return function(e){return e instanceof c?{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 n}}function m(e){const t=f(e);return async(e,r)=>{try{const s=await t(e,r);if(r.status(s.statusCode||200),s.headers)for(const[e,t]of Object.entries(s.headers))r.header(e,t);return r.send(s.body)}catch(t){return e.log.error(t),r.status(500).send({error:t?.message||"Internal Server Error"})}}}function w(e){return{headers:a(e.headers),body:e.body,query:e.query,params:e.params}}const g={normalizeRequest:w};export{i as awsAdapter,s as createAWSHandler,m as createFastifyHandler,g as fastifyAdapter,n as normalizeAWSRequest,w as normalizeFastifyRequest};
@@ -1,3 +1,15 @@
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>;
1
+ import type { RouteDefinition } from '../../cli/shared';
2
+ import type { APIGatewayProxyEventV2, APIGatewayProxyStructuredResultV2, Context } from 'aws-lambda';
3
+ export type AWSLambdaContext = {
4
+ headers: Record<string, any>;
5
+ body: unknown;
6
+ queryStringParameters: Record<string, any>;
7
+ pathParameters: Record<string, any>;
8
+ cookies: string[];
9
+ rawPath: string;
10
+ method: string;
11
+ requestId: string;
12
+ event: APIGatewayProxyEventV2;
13
+ awsContext: Context;
14
+ };
15
+ export declare function createAWSHandler(route: RouteDefinition): (event: APIGatewayProxyEventV2, awsContext: Context) => Promise<APIGatewayProxyStructuredResultV2>;
@@ -1,3 +1,3 @@
1
1
  import { type HandlerConfig } from '../../core/routing';
2
2
  import type { FastifyReply, FastifyRequest } from 'fastify';
3
- export declare function createFastifyHandler(config: HandlerConfig<any, any, any>): (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
3
+ export declare function createFastifyHandler(config: HandlerConfig<any, any, any>): (req: FastifyRequest, reply: FastifyReply) => Promise<never>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lambda-pipe",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Type-safe plugin & middleware runtime for AWS Lambda and serverless APIs.",
5
5
  "license": "MIT",
6
6
  "author": "Delpi.Kye",