lambder 1.0.2 → 1.0.4

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 ADDED
@@ -0,0 +1,58 @@
1
+ # Lambder
2
+
3
+ Lambder provides a simplified way to handle AWS Lambda functions with HTTP triggers, offering a clear structure for managing API endpoints, static file serving, hooks, and error handling.
4
+
5
+ ## Features
6
+
7
+ - Easy setup of API paths with conditions and actions.
8
+ - Support for hooks at different stages of the request lifecycle.
9
+ - Integrated cookie and query string parsing.
10
+ - Customizable error handling and fallback mechanisms.
11
+ - CORS support and easy configuration for public paths.
12
+ - Response helpers for JSON, HTML, XML, files, and more.
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install lambder
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import Lambder from 'lambder';
24
+ import { APIGatewayProxyEvent, Context } from 'aws-lambda';
25
+
26
+ const app = new Lambder({
27
+ apiPath: '/api',
28
+ publicPath: '/public',
29
+ isCorsEnabled: true,
30
+ });
31
+
32
+ // Define a simple API endpoint
33
+ app.addApi('/hello', async (ctx) => {
34
+ return ctx.resolver.json({ message: 'Hello, World!' });
35
+ });
36
+
37
+ // Handle 404 Not Found
38
+ app.setGlobalErrorHandler((err: Error) => {
39
+ return { statusCode: 404, body: 'Not Found' };
40
+ });
41
+
42
+ // Lambda handler
43
+ exports.handler = async (event: APIGatewayProxyEvent, context: Context) => {
44
+ return app.render(event, context);
45
+ };
46
+ ```
47
+
48
+ ## Documentation
49
+
50
+ For more details on how to use Lambder, including the full API reference, visit [Lambder Documentation](#).
51
+
52
+ ## Contributing
53
+
54
+ Contributions are welcome! Please open an issue or submit a pull request for any improvements or bug fixes.
55
+
56
+ ## License
57
+
58
+ MIT License
@@ -61,6 +61,7 @@ export default class Resolver {
61
61
  reject: (err: Error) => void;
62
62
  });
63
63
  private readFileSync;
64
+ private checkFileExist;
64
65
  raw(param: ResolverResponse): ResolverResponse;
65
66
  json(data: {
66
67
  [key: string]: any;
@@ -83,7 +84,7 @@ export default class Resolver {
83
84
  }): ResolverResponse;
84
85
  file(filePath: string, headers?: {
85
86
  [key: string]: string | string[];
86
- }): ResolverResponse;
87
+ }, fallbackFilePath?: string): ResolverResponse;
87
88
  api({ payload, error, ...other }: {
88
89
  payload?: any;
89
90
  error?: any;
package/dist/Resolver.js CHANGED
@@ -34,13 +34,21 @@ export default class Resolver {
34
34
  });
35
35
  }
36
36
  readFileSync(filePath) {
37
- const __dirname = pathLibrary.resolve(pathLibrary.dirname(""));
38
- const absolutePath = pathLibrary.resolve(filePath);
39
37
  const publicPath = pathLibrary.resolve(this.publicPath);
38
+ const absolutePath = pathLibrary.resolve(publicPath, filePath);
40
39
  if (!absolutePath.includes(publicPath)) {
41
40
  return "forbiddenpath";
42
41
  }
43
- return fs.readFileSync(filePath);
42
+ return fs.readFileSync(absolutePath);
43
+ }
44
+ ;
45
+ checkFileExist(filePath) {
46
+ const absolutePath = pathLibrary.resolve(filePath);
47
+ const publicPath = pathLibrary.resolve(this.publicPath);
48
+ if (!absolutePath.includes(publicPath)) {
49
+ return false;
50
+ }
51
+ return fs.existsSync(absolutePath);
44
52
  }
45
53
  ;
46
54
  raw(param) {
@@ -110,9 +118,13 @@ export default class Resolver {
110
118
  });
111
119
  }
112
120
  ;
113
- file(filePath, headers) {
114
- if (!fs.existsSync(filePath))
121
+ file(filePath, headers, fallbackFilePath) {
122
+ if (!this.checkFileExist(filePath)) {
123
+ if (fallbackFilePath && this.checkFileExist(fallbackFilePath)) {
124
+ return this.file(fallbackFilePath, headers);
125
+ }
115
126
  return this.json({ error: "File not found: " + filePath });
127
+ }
116
128
  const mimeType = mimeTypeResolver.lookup(filePath);
117
129
  const body = this.readFileSync(filePath);
118
130
  const bodyBase64 = Buffer.from(body).toString("base64");
package/dist/index.d.ts CHANGED
@@ -22,7 +22,7 @@ type RenderContext = RawResolvedContext & {
22
22
  type ConditionFunction = (renderContext: RenderContext) => boolean | Promise<boolean>;
23
23
  type ActionFunction = (renderContext: RenderContext) => ResolverResponse | Promise<ResolverResponse>;
24
24
  type HookCreatedFunction = (lambderInstance: Lambder) => Promise<void>;
25
- type HookBeforeRenderFunction = (renderContext: RenderContext) => Promise<void>;
25
+ type HookBeforeRenderFunction = (renderContext: RenderContext) => RenderContext | Promise<RenderContext>;
26
26
  type HookAfterRenderFunction = (renderContext: RenderContext, renderResult: ResolverResponse) => ResolverResponse | Promise<ResolverResponse>;
27
27
  type HookFallbackFunction = (renderContext: RenderContext) => ResolverResponse | Promise<ResolverResponse>;
28
28
  type HookCompletedFunction = (renderResult: ResolverResponse) => ResolverResponse | Promise<ResolverResponse>;
@@ -35,15 +35,16 @@ export default class Lambder {
35
35
  private _actionList;
36
36
  private _hookList;
37
37
  private globalErrorHandler;
38
- constructor({ apiPath, apiVersion, isCorsEnabled, publicPath }: {
38
+ private fallbackHandler;
39
+ constructor({ publicPath, apiPath, apiVersion, isCorsEnabled, }: {
40
+ publicPath: string;
39
41
  apiPath?: string;
40
42
  apiVersion?: string;
41
43
  isCorsEnabled?: boolean;
42
- publicPath: string;
43
44
  });
44
45
  private apiHandler;
45
46
  private pathHandler;
46
- private fallbackHandler;
47
+ setFallbackHandler(fallbackHandler: (renderContext: RenderContext) => ResolverResponse): void;
47
48
  setGlobalErrorHandler(globalErrorHandler: (err: Error) => ResolverResponse): void;
48
49
  addApi(condition: string | ConditionFunction | RegExp, actionFn: ActionFunction): void;
49
50
  addPath(condition: string | ConditionFunction | RegExp, actionFn: ActionFunction): void;
package/dist/index.js CHANGED
@@ -32,11 +32,12 @@ export default class Lambder {
32
32
  _actionList;
33
33
  _hookList;
34
34
  globalErrorHandler = null;
35
- constructor({ apiPath, apiVersion, isCorsEnabled, publicPath }) {
35
+ fallbackHandler = null;
36
+ constructor({ publicPath, apiPath, apiVersion, isCorsEnabled, }) {
37
+ this.publicPath = publicPath || "/incorrect-path-not-found";
36
38
  this.apiPath = apiPath ?? "/api";
37
39
  this.apiVersion = apiVersion ?? null;
38
40
  this.isCorsEnabled = isCorsEnabled ?? false;
39
- this.publicPath = publicPath || "/incorrect-path-not-found";
40
41
  this._actionList = [];
41
42
  this._hookList = {
42
43
  "beforeRender": [],
@@ -53,8 +54,8 @@ export default class Lambder {
53
54
  // Do context stuff;
54
55
  return await actionFn(renderContext);
55
56
  }
56
- async fallbackHandler(renderContext) {
57
- return renderContext.resolver.file("index.html");
57
+ setFallbackHandler(fallbackHandler) {
58
+ this.fallbackHandler = fallbackHandler;
58
59
  }
59
60
  setGlobalErrorHandler(globalErrorHandler) {
60
61
  this.globalErrorHandler = globalErrorHandler;
@@ -118,7 +119,7 @@ export default class Lambder {
118
119
  resolve, reject,
119
120
  });
120
121
  const eventContext = resolveEvent(event, resolve, reject);
121
- const renderContext = { ...eventContext, runContext, resolver };
122
+ let renderContext = { ...eventContext, runContext, resolver };
122
123
  if (renderContext.method === "OPTIONS")
123
124
  return resolver.cors();
124
125
  let firstMatchedAction = null;
@@ -130,8 +131,9 @@ export default class Lambder {
130
131
  }
131
132
  }
132
133
  if (firstMatchedAction) {
133
- for (const hook of this._hookList["beforeRender"])
134
- await hook.hookFn(renderContext);
134
+ for (const hook of this._hookList["beforeRender"]) {
135
+ renderContext = await hook.hookFn(renderContext);
136
+ }
135
137
  let renderResult = await firstMatchedAction.actionFn(renderContext);
136
138
  for (const hook of this._hookList["afterRender"]) {
137
139
  renderResult = await hook.hookFn(renderContext, renderResult);
@@ -141,8 +143,13 @@ export default class Lambder {
141
143
  else {
142
144
  for (const hook of this._hookList["fallback"])
143
145
  await hook.hookFn(renderContext);
144
- const renderResult = await this.fallbackHandler(renderContext);
145
- resolve(renderResult);
146
+ if (this.fallbackHandler) {
147
+ const renderResult = await this.fallbackHandler(renderContext);
148
+ resolve(renderResult);
149
+ }
150
+ else {
151
+ resolve({ statusCode: 204, body: "Fallback handler not set.", });
152
+ }
146
153
  }
147
154
  }).then(async (renderResult) => {
148
155
  for (const hook of this._hookList["completed"]) {
@@ -153,7 +160,7 @@ export default class Lambder {
153
160
  if (this.globalErrorHandler) {
154
161
  return this.globalErrorHandler(err);
155
162
  }
156
- return { statusCode: 501, body: "Unknown Server Error", };
163
+ return { statusCode: 500, body: "Internal Server Error.", };
157
164
  });
158
165
  }
159
166
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lambder",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/Resolver.ts CHANGED
@@ -80,11 +80,16 @@ export default class Resolver {
80
80
  }
81
81
 
82
82
  private readFileSync(filePath: string){
83
- const __dirname = pathLibrary.resolve(pathLibrary.dirname(""));
84
- const absolutePath = pathLibrary.resolve(filePath);
85
83
  const publicPath = pathLibrary.resolve(this.publicPath);
84
+ const absolutePath = pathLibrary.resolve(publicPath, filePath);
86
85
  if(!absolutePath.includes(publicPath)){ return "forbiddenpath"; }
87
- return fs.readFileSync(filePath);
86
+ return fs.readFileSync(absolutePath);
87
+ };
88
+ private checkFileExist(filePath: string){
89
+ const absolutePath = pathLibrary.resolve(filePath);
90
+ const publicPath = pathLibrary.resolve(this.publicPath);
91
+ if(!absolutePath.includes(publicPath)){ return false; }
92
+ return fs.existsSync(absolutePath);
88
93
  };
89
94
 
90
95
  raw(param: ResolverResponse){
@@ -174,8 +179,14 @@ export default class Resolver {
174
179
  file(
175
180
  filePath: string,
176
181
  headers?: { [key:string]: string|string[] },
182
+ fallbackFilePath?: string,
177
183
  ):ResolverResponse{
178
- if(!fs.existsSync(filePath)) return this.json({ error: "File not found: " + filePath });
184
+ if(!this.checkFileExist(filePath)){
185
+ if(fallbackFilePath && this.checkFileExist(fallbackFilePath)){
186
+ return this.file(fallbackFilePath, headers);
187
+ }
188
+ return this.json({ error: "File not found: " + filePath })
189
+ }
179
190
  const mimeType = mimeTypeResolver.lookup(filePath);
180
191
  const body = this.readFileSync(filePath);
181
192
  const bodyBase64 = Buffer.from(body).toString("base64");
package/src/index.ts CHANGED
@@ -24,7 +24,7 @@ type ActionFunction = (renderContext: RenderContext) => ResolverResponse|Promise
24
24
  type ActionType = { conditionFn: ConditionFunction, actionFn: ActionFunction };
25
25
 
26
26
  type HookCreatedFunction = (lambderInstance: Lambder) => Promise<void>;
27
- type HookBeforeRenderFunction = (renderContext: RenderContext) => Promise<void>;
27
+ type HookBeforeRenderFunction = (renderContext: RenderContext) => RenderContext|Promise<RenderContext>;
28
28
  type HookAfterRenderFunction = (renderContext: RenderContext, renderResult: ResolverResponse) => ResolverResponse|Promise<ResolverResponse>;
29
29
  type HookFallbackFunction = (renderContext: RenderContext) => ResolverResponse|Promise<ResolverResponse>;
30
30
  type HookCompletedFunction = (renderResult: ResolverResponse) => ResolverResponse|Promise<ResolverResponse>;
@@ -71,15 +71,16 @@ export default class Lambder {
71
71
  "completed": { priority: number, hookFn: HookCompletedFunction }[]
72
72
  };
73
73
  private globalErrorHandler: Function|null = null;
74
+ private fallbackHandler: Function|null = null;
74
75
 
75
76
  constructor(
76
- { apiPath, apiVersion, isCorsEnabled, publicPath }:
77
- { apiPath?: string, apiVersion?: string, isCorsEnabled?: boolean, publicPath: string }
77
+ { publicPath, apiPath, apiVersion, isCorsEnabled, }:
78
+ { publicPath: string, apiPath?: string, apiVersion?: string, isCorsEnabled?: boolean, }
78
79
  ){
80
+ this.publicPath = publicPath || "/incorrect-path-not-found";
79
81
  this.apiPath = apiPath ?? "/api";
80
82
  this.apiVersion = apiVersion ?? null;
81
83
  this.isCorsEnabled = isCorsEnabled ?? false;
82
- this.publicPath = publicPath || "/incorrect-path-not-found";
83
84
 
84
85
  this._actionList = [];
85
86
  this._hookList = {
@@ -98,8 +99,8 @@ export default class Lambder {
98
99
  // Do context stuff;
99
100
  return await actionFn(renderContext);
100
101
  }
101
- private async fallbackHandler(renderContext:RenderContext){
102
- return renderContext.resolver.file("index.html");
102
+ setFallbackHandler(fallbackHandler: (renderContext:RenderContext)=>ResolverResponse){
103
+ this.fallbackHandler = fallbackHandler;
103
104
  }
104
105
  setGlobalErrorHandler(globalErrorHandler: (err: Error)=>ResolverResponse){
105
106
  this.globalErrorHandler = globalErrorHandler;
@@ -175,7 +176,7 @@ export default class Lambder {
175
176
  resolve, reject,
176
177
  });
177
178
  const eventContext:RawResolvedContext = resolveEvent(event, resolve, reject);
178
- const renderContext:RenderContext = { ...eventContext, runContext, resolver };
179
+ let renderContext:RenderContext = { ...eventContext, runContext, resolver };
179
180
 
180
181
  if(renderContext.method === "OPTIONS") return resolver.cors();
181
182
 
@@ -189,7 +190,9 @@ export default class Lambder {
189
190
  }
190
191
 
191
192
  if(firstMatchedAction){
192
- for(const hook of this._hookList["beforeRender"]) await hook.hookFn(renderContext);
193
+ for(const hook of this._hookList["beforeRender"]){
194
+ renderContext = await hook.hookFn(renderContext);
195
+ }
193
196
  let renderResult = await firstMatchedAction.actionFn(renderContext);
194
197
  for(const hook of this._hookList["afterRender"]){
195
198
  renderResult = await hook.hookFn(renderContext, renderResult);
@@ -197,8 +200,12 @@ export default class Lambder {
197
200
  resolve(renderResult);
198
201
  }else{
199
202
  for(const hook of this._hookList["fallback"]) await hook.hookFn(renderContext);
200
- const renderResult = await this.fallbackHandler(renderContext);
201
- resolve(renderResult);
203
+ if(this.fallbackHandler){
204
+ const renderResult = await this.fallbackHandler(renderContext);
205
+ resolve(renderResult);
206
+ }else{
207
+ resolve({ statusCode: 204, body: "Fallback handler not set.", })
208
+ }
202
209
  }
203
210
  }).then(async (renderResult: ResolverResponse):Promise<ResolverResponse> => {
204
211
  for(const hook of this._hookList["completed"]){
@@ -209,7 +216,7 @@ export default class Lambder {
209
216
  if(this.globalErrorHandler){
210
217
  return this.globalErrorHandler(err);
211
218
  }
212
- return { statusCode: 501, body: "Unknown Server Error", }
219
+ return { statusCode: 500, body: "Internal Server Error.", }
213
220
  })
214
221
  }
215
222