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 +58 -0
- package/dist/Resolver.d.ts +2 -1
- package/dist/Resolver.js +17 -5
- package/dist/index.d.ts +5 -4
- package/dist/index.js +17 -10
- package/package.json +1 -1
- package/src/Resolver.ts +15 -4
- package/src/index.ts +18 -11
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
|
package/dist/Resolver.d.ts
CHANGED
|
@@ -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(
|
|
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 (!
|
|
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<
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
145
|
-
|
|
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:
|
|
163
|
+
return { statusCode: 500, body: "Internal Server Error.", };
|
|
157
164
|
});
|
|
158
165
|
}
|
|
159
166
|
}
|
package/package.json
CHANGED
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(
|
|
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(!
|
|
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<
|
|
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,
|
|
77
|
-
{ apiPath?: string, apiVersion?: string, isCorsEnabled?: boolean,
|
|
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
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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"])
|
|
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
|
-
|
|
201
|
-
|
|
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:
|
|
219
|
+
return { statusCode: 500, body: "Internal Server Error.", }
|
|
213
220
|
})
|
|
214
221
|
}
|
|
215
222
|
|