lambder 1.0.39 → 1.0.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Lambder.d.ts +11 -9
- package/dist/Lambder.js +56 -48
- package/dist/Resolver.d.ts +12 -12
- package/dist/Resolver.js +2 -3
- package/dist/{ResolverLight.d.ts → ResponseBuilder.d.ts} +1 -1
- package/dist/{ResolverLight.js → ResponseBuilder.js} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/Lambder.ts +68 -60
- package/src/Resolver.ts +12 -13
- package/src/{ResolverLight.ts → ResponseBuilder.ts} +2 -36
- package/src/index.ts +1 -1
package/dist/Lambder.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { APIGatewayProxyEvent, APIGatewayProxyEventHeaders, Context } from "aws-lambda";
|
|
2
2
|
import Resolver from "./Resolver.js";
|
|
3
|
-
import
|
|
3
|
+
import ResponseBuilder, { ResolverResponse } from "./ResponseBuilder.js";
|
|
4
4
|
type DDBSession = {
|
|
5
5
|
userId: string;
|
|
6
6
|
userIdHash: string;
|
|
@@ -29,16 +29,16 @@ type RenderContext = {
|
|
|
29
29
|
session: DDBSession;
|
|
30
30
|
lambdaContext: Context;
|
|
31
31
|
};
|
|
32
|
-
type ConditionFunction = (
|
|
33
|
-
type ActionFunction = (
|
|
32
|
+
type ConditionFunction = (ctx: RenderContext) => boolean | Promise<boolean>;
|
|
33
|
+
type ActionFunction = (ctx: RenderContext, resolver: Resolver) => ResolverResponse | Promise<ResolverResponse>;
|
|
34
34
|
type HookCreatedFunction = (lambderInstance: Lambder) => Promise<void>;
|
|
35
|
-
type HookBeforeRenderFunction = (
|
|
36
|
-
type HookAfterRenderFunction = (
|
|
37
|
-
type HookFallbackFunction = (
|
|
35
|
+
type HookBeforeRenderFunction = (ctx: RenderContext, resolver: Resolver) => RenderContext | Promise<RenderContext>;
|
|
36
|
+
type HookAfterRenderFunction = (ctx: RenderContext, resolver: Resolver, renderResult: ResolverResponse) => ResolverResponse | Promise<ResolverResponse>;
|
|
37
|
+
type HookFallbackFunction = (ctx: RenderContext, resolver: Resolver) => ResolverResponse | Promise<ResolverResponse>;
|
|
38
38
|
type HookCompletedFunction = (renderResult: ResolverResponse) => ResolverResponse | Promise<ResolverResponse>;
|
|
39
|
-
type GlobalErrorHandlerFunction = (err: Error,
|
|
40
|
-
type RouteFallbackHandlerFunction = (
|
|
41
|
-
type ApiFallbackHandlerFunction = (
|
|
39
|
+
type GlobalErrorHandlerFunction = (err: Error, ctx: RenderContext | null, response: ResponseBuilder) => ResolverResponse | Promise<ResolverResponse>;
|
|
40
|
+
type RouteFallbackHandlerFunction = (ctx: RenderContext, resolver: Resolver) => ResolverResponse;
|
|
41
|
+
type ApiFallbackHandlerFunction = (ctx: RenderContext, resolver: Resolver) => ResolverResponse;
|
|
42
42
|
export declare const createContext: (event: APIGatewayProxyEvent, lambdaContext: Context) => RenderContext;
|
|
43
43
|
export default class Lambder {
|
|
44
44
|
private apiPath;
|
|
@@ -71,6 +71,8 @@ export default class Lambder {
|
|
|
71
71
|
addHook(hookEvent: 'afterRender', hookFn: HookAfterRenderFunction, priority?: number): Promise<void>;
|
|
72
72
|
addHook(hookEvent: 'fallback', hookFn: HookFallbackFunction, priority?: number): Promise<void>;
|
|
73
73
|
addHook(hookEvent: 'completed', hookFn: HookCompletedFunction, priority?: number): Promise<void>;
|
|
74
|
+
getResponseBuilder(): ResponseBuilder;
|
|
75
|
+
private getResolver;
|
|
74
76
|
render(event: APIGatewayProxyEvent, lambdaContext: Context): Promise<ResolverResponse>;
|
|
75
77
|
}
|
|
76
78
|
export {};
|
package/dist/Lambder.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import querystring from "querystring";
|
|
2
2
|
import cookieParser from "cookie";
|
|
3
3
|
import Resolver from "./Resolver.js";
|
|
4
|
-
import
|
|
4
|
+
import ResponseBuilder from "./ResponseBuilder.js";
|
|
5
5
|
export const createContext = (event, lambdaContext) => {
|
|
6
6
|
const host = event.headers.Host || event.headers.host || "";
|
|
7
7
|
const url = event.path;
|
|
@@ -71,20 +71,20 @@ export default class Lambder {
|
|
|
71
71
|
return false;
|
|
72
72
|
}
|
|
73
73
|
;
|
|
74
|
-
async handleNoMatchedAction(
|
|
74
|
+
async handleNoMatchedAction(ctx, resolver) {
|
|
75
75
|
for (const hook of this._hookList["fallback"]) {
|
|
76
|
-
await hook.hookFn(
|
|
76
|
+
await hook.hookFn(ctx, resolver);
|
|
77
77
|
}
|
|
78
|
-
if (
|
|
78
|
+
if (ctx.url === this.apiPath) {
|
|
79
79
|
if (this.apiFallbackHandler) {
|
|
80
|
-
resolver.resolve(await this.apiFallbackHandler(
|
|
80
|
+
resolver.resolve(await this.apiFallbackHandler(ctx, resolver));
|
|
81
81
|
}
|
|
82
82
|
else {
|
|
83
83
|
resolver.resolve({ statusCode: 204, body: "API handler not set.", });
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
else if (this.routeFallbackHandler) {
|
|
87
|
-
resolver.resolve(await this.routeFallbackHandler(
|
|
87
|
+
resolver.resolve(await this.routeFallbackHandler(ctx, resolver));
|
|
88
88
|
}
|
|
89
89
|
else {
|
|
90
90
|
resolver.resolve({ statusCode: 204, body: "Handler not set.", });
|
|
@@ -95,45 +95,45 @@ export default class Lambder {
|
|
|
95
95
|
}
|
|
96
96
|
addRoute(condition, actionFn) {
|
|
97
97
|
this._actionList.push({
|
|
98
|
-
conditionFn: async (
|
|
99
|
-
((typeof condition === "string" &&
|
|
100
|
-
(typeof condition === "function" && (await condition(
|
|
101
|
-
(condition?.constructor == RegExp && condition.test(
|
|
102
|
-
actionFn: async (
|
|
98
|
+
conditionFn: async (ctx) => (ctx.method === "GET" &&
|
|
99
|
+
((typeof condition === "string" && ctx.url === condition) ||
|
|
100
|
+
(typeof condition === "function" && (await condition(ctx))) ||
|
|
101
|
+
(condition?.constructor == RegExp && condition.test(ctx.url)))),
|
|
102
|
+
actionFn: async (ctx, resolver) => await actionFn(ctx, resolver)
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
;
|
|
106
106
|
addSessionRoute(condition, actionFn) {
|
|
107
107
|
this._actionList.push({
|
|
108
|
-
conditionFn: async (
|
|
109
|
-
((typeof condition === "string" &&
|
|
110
|
-
(typeof condition === "function" && (await condition(
|
|
111
|
-
(condition?.constructor == RegExp && condition.test(
|
|
112
|
-
actionFn: async (
|
|
108
|
+
conditionFn: async (ctx) => (ctx.method === "GET" && !!ctx.session &&
|
|
109
|
+
((typeof condition === "string" && ctx.url === condition) ||
|
|
110
|
+
(typeof condition === "function" && (await condition(ctx))) ||
|
|
111
|
+
(condition?.constructor == RegExp && condition.test(ctx.url)))),
|
|
112
|
+
actionFn: async (ctx, resolver) => await actionFn(ctx, resolver)
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
115
|
;
|
|
116
116
|
addApi(condition, actionFn) {
|
|
117
117
|
this._actionList.push({
|
|
118
|
-
conditionFn: async (
|
|
119
|
-
((typeof condition === "string" &&
|
|
120
|
-
(typeof condition === "function" && (await condition(
|
|
121
|
-
(condition?.constructor == RegExp && condition.test(
|
|
122
|
-
actionFn: async (
|
|
118
|
+
conditionFn: async (ctx) => (ctx.method === "POST" && ctx.url === this.apiPath &&
|
|
119
|
+
((typeof condition === "string" && ctx.post?.api === condition) ||
|
|
120
|
+
(typeof condition === "function" && (await condition(ctx))) ||
|
|
121
|
+
(condition?.constructor == RegExp && condition.test(ctx.post?.api)))),
|
|
122
|
+
actionFn: async (ctx, resolver) => await actionFn(ctx, resolver)
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
125
|
;
|
|
126
126
|
addSessionApi(condition, actionFn) {
|
|
127
127
|
this._actionList.push({
|
|
128
|
-
conditionFn: async (
|
|
129
|
-
((typeof condition === "string" &&
|
|
130
|
-
(typeof condition === "function" && (await condition(
|
|
131
|
-
(condition?.constructor == RegExp && condition.test(
|
|
132
|
-
actionFn: async (
|
|
133
|
-
const isSessionValid = this.validateSession(
|
|
128
|
+
conditionFn: async (ctx) => (ctx.method === "POST" && ctx.url === this.apiPath && !!ctx.session &&
|
|
129
|
+
((typeof condition === "string" && ctx.post?.api === condition) ||
|
|
130
|
+
(typeof condition === "function" && (await condition(ctx))) ||
|
|
131
|
+
(condition?.constructor == RegExp && condition.test(ctx.post?.api)))),
|
|
132
|
+
actionFn: async (ctx, resolver) => {
|
|
133
|
+
const isSessionValid = this.validateSession(ctx.session);
|
|
134
134
|
if (!isSessionValid)
|
|
135
135
|
throw new Error("Session not found");
|
|
136
|
-
return await actionFn(
|
|
136
|
+
return await actionFn(ctx, resolver);
|
|
137
137
|
}
|
|
138
138
|
});
|
|
139
139
|
}
|
|
@@ -147,38 +147,50 @@ export default class Lambder {
|
|
|
147
147
|
this._hookList[hookEvent].sort((a, b) => a.priority - b.priority);
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
+
getResponseBuilder() {
|
|
151
|
+
return new ResponseBuilder({
|
|
152
|
+
isCorsEnabled: this.isCorsEnabled,
|
|
153
|
+
publicPath: this.publicPath,
|
|
154
|
+
apiVersion: this.apiVersion,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
;
|
|
158
|
+
getResolver(resolve, reject) {
|
|
159
|
+
return new Resolver({
|
|
160
|
+
isCorsEnabled: this.isCorsEnabled,
|
|
161
|
+
publicPath: this.publicPath,
|
|
162
|
+
apiVersion: this.apiVersion,
|
|
163
|
+
resolve, reject
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
;
|
|
150
167
|
async render(event, lambdaContext) {
|
|
151
168
|
let eventRenderContext = null;
|
|
152
169
|
try {
|
|
153
|
-
let
|
|
154
|
-
eventRenderContext =
|
|
170
|
+
let ctx = createContext(event, lambdaContext);
|
|
171
|
+
eventRenderContext = ctx;
|
|
155
172
|
return await new Promise(async (resolve, reject) => {
|
|
156
|
-
const resolver =
|
|
157
|
-
|
|
158
|
-
publicPath: this.publicPath,
|
|
159
|
-
apiVersion: this.apiVersion,
|
|
160
|
-
resolve, reject
|
|
161
|
-
});
|
|
162
|
-
if (renderContext.method === "OPTIONS")
|
|
173
|
+
const resolver = this.getResolver(resolve, reject);
|
|
174
|
+
if (ctx.method === "OPTIONS")
|
|
163
175
|
return resolver.cors();
|
|
164
176
|
const firstMatchedAction = await (async () => {
|
|
165
177
|
for (const action of this._actionList) {
|
|
166
|
-
if (await action.conditionFn(
|
|
178
|
+
if (await action.conditionFn(ctx))
|
|
167
179
|
return action;
|
|
168
180
|
}
|
|
169
181
|
})();
|
|
170
182
|
if (firstMatchedAction) {
|
|
171
183
|
for (const hook of this._hookList["beforeRender"]) {
|
|
172
|
-
|
|
184
|
+
ctx = await hook.hookFn(ctx, resolver);
|
|
173
185
|
}
|
|
174
|
-
let renderResult = await firstMatchedAction.actionFn(
|
|
186
|
+
let renderResult = await firstMatchedAction.actionFn(ctx, resolver);
|
|
175
187
|
for (const hook of this._hookList["afterRender"]) {
|
|
176
|
-
renderResult = await hook.hookFn(
|
|
188
|
+
renderResult = await hook.hookFn(ctx, resolver, renderResult);
|
|
177
189
|
}
|
|
178
190
|
resolve(renderResult);
|
|
179
191
|
}
|
|
180
192
|
else {
|
|
181
|
-
return this.handleNoMatchedAction(
|
|
193
|
+
return this.handleNoMatchedAction(ctx, resolver);
|
|
182
194
|
}
|
|
183
195
|
}).then(async (renderResult) => {
|
|
184
196
|
for (const hook of this._hookList["completed"]) {
|
|
@@ -188,14 +200,10 @@ export default class Lambder {
|
|
|
188
200
|
});
|
|
189
201
|
}
|
|
190
202
|
catch (err) {
|
|
191
|
-
const
|
|
192
|
-
isCorsEnabled: this.isCorsEnabled,
|
|
193
|
-
publicPath: this.publicPath,
|
|
194
|
-
apiVersion: this.apiVersion,
|
|
195
|
-
});
|
|
203
|
+
const responseBuilder = this.getResponseBuilder();
|
|
196
204
|
const wrappedError = err instanceof Error ? err : new Error("Error: " + String(err));
|
|
197
205
|
if (this.globalErrorHandler) {
|
|
198
|
-
return this.globalErrorHandler(wrappedError, eventRenderContext,
|
|
206
|
+
return this.globalErrorHandler(wrappedError, eventRenderContext, responseBuilder);
|
|
199
207
|
}
|
|
200
208
|
return { statusCode: 500, body: "Internal Server Error.", };
|
|
201
209
|
}
|
package/dist/Resolver.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ResponseBuilder, { ResolverResponse } from "./ResponseBuilder.js";
|
|
2
2
|
type MethodType<T, M extends keyof T> = T[M] extends (...args: any[]) => any ? T[M] : never;
|
|
3
3
|
interface DieResolverMethods {
|
|
4
|
-
raw: MethodType<
|
|
5
|
-
json: MethodType<
|
|
6
|
-
xml: MethodType<
|
|
7
|
-
html: MethodType<
|
|
8
|
-
status301: MethodType<
|
|
9
|
-
status404: MethodType<
|
|
10
|
-
cors: MethodType<
|
|
11
|
-
fileBase64: MethodType<
|
|
12
|
-
file: MethodType<
|
|
13
|
-
api: MethodType<
|
|
4
|
+
raw: MethodType<ResponseBuilder, 'raw'>;
|
|
5
|
+
json: MethodType<ResponseBuilder, 'json'>;
|
|
6
|
+
xml: MethodType<ResponseBuilder, 'xml'>;
|
|
7
|
+
html: MethodType<ResponseBuilder, 'html'>;
|
|
8
|
+
status301: MethodType<ResponseBuilder, 'status301'>;
|
|
9
|
+
status404: MethodType<ResponseBuilder, 'status404'>;
|
|
10
|
+
cors: MethodType<ResponseBuilder, 'cors'>;
|
|
11
|
+
fileBase64: MethodType<ResponseBuilder, 'fileBase64'>;
|
|
12
|
+
file: MethodType<ResponseBuilder, 'file'>;
|
|
13
|
+
api: MethodType<ResponseBuilder, 'api'>;
|
|
14
14
|
}
|
|
15
|
-
export default class Resolver extends
|
|
15
|
+
export default class Resolver extends ResponseBuilder {
|
|
16
16
|
resolve: (renderResult: ResolverResponse) => void;
|
|
17
17
|
reject: (err: Error) => void;
|
|
18
18
|
die: DieResolverMethods;
|
package/dist/Resolver.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
export default class Resolver extends
|
|
1
|
+
import ResponseBuilder from "./ResponseBuilder.js";
|
|
2
|
+
export default class Resolver extends ResponseBuilder {
|
|
3
3
|
resolve;
|
|
4
4
|
reject;
|
|
5
5
|
die;
|
|
@@ -7,7 +7,6 @@ export default class Resolver extends ResolverLight {
|
|
|
7
7
|
super({ isCorsEnabled, publicPath, apiVersion });
|
|
8
8
|
this.resolve = resolve;
|
|
9
9
|
this.reject = reject;
|
|
10
|
-
// Bind ResolverLight methods to `this` for `die` and automatically resolve
|
|
11
10
|
this.die = {
|
|
12
11
|
raw: this.autoResolve(this.raw),
|
|
13
12
|
json: this.autoResolve(this.json),
|
|
@@ -8,7 +8,7 @@ const CORS_HEADERS = convertToMultiHeader({
|
|
|
8
8
|
"Access-Control-Allow-Origin": "*",
|
|
9
9
|
"Access-Control-Allow-Methods": "OPTIONS,POST",
|
|
10
10
|
});
|
|
11
|
-
export default class
|
|
11
|
+
export default class ResponseBuilder {
|
|
12
12
|
isCorsEnabled;
|
|
13
13
|
publicPath;
|
|
14
14
|
apiVersion;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Lambder from './Lambder.js';
|
|
2
2
|
export default Lambder;
|
|
3
3
|
export { default as LambderCaller } from "./LambderCaller.js";
|
|
4
|
-
export { default as
|
|
4
|
+
export { default as ResponseBuilder } from "./ResponseBuilder.js";
|
|
5
5
|
export { default as Resolver } from "./Resolver.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Lambder from './Lambder.js';
|
|
2
2
|
export default Lambder;
|
|
3
3
|
export { default as LambderCaller } from "./LambderCaller.js";
|
|
4
|
-
export { default as
|
|
4
|
+
export { default as ResponseBuilder } from "./ResponseBuilder.js";
|
|
5
5
|
export { default as Resolver } from "./Resolver.js";
|
package/package.json
CHANGED
package/src/Lambder.ts
CHANGED
|
@@ -3,7 +3,7 @@ import cookieParser from "cookie";
|
|
|
3
3
|
|
|
4
4
|
import type { APIGatewayProxyEvent, APIGatewayProxyEventHeaders, Context } from "aws-lambda";
|
|
5
5
|
import Resolver from "./Resolver.js";
|
|
6
|
-
import
|
|
6
|
+
import ResponseBuilder, { ResolverResponse } from "./ResponseBuilder.js";
|
|
7
7
|
|
|
8
8
|
type DDBSession = {
|
|
9
9
|
userId: string,
|
|
@@ -27,19 +27,19 @@ type RenderContext = {
|
|
|
27
27
|
lambdaContext: Context;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
type ConditionFunction = (
|
|
31
|
-
type ActionFunction = (
|
|
30
|
+
type ConditionFunction = (ctx: RenderContext) => boolean|Promise<boolean>;
|
|
31
|
+
type ActionFunction = (ctx: RenderContext, resolver: Resolver) => ResolverResponse|Promise<ResolverResponse>;
|
|
32
32
|
type ActionType = { conditionFn: ConditionFunction, actionFn: ActionFunction };
|
|
33
33
|
|
|
34
34
|
type HookCreatedFunction = (lambderInstance: Lambder) => Promise<void>;
|
|
35
|
-
type HookBeforeRenderFunction = (
|
|
36
|
-
type HookAfterRenderFunction = (
|
|
37
|
-
type HookFallbackFunction = (
|
|
35
|
+
type HookBeforeRenderFunction = (ctx: RenderContext, resolver: Resolver) => RenderContext|Promise<RenderContext>;
|
|
36
|
+
type HookAfterRenderFunction = (ctx: RenderContext, resolver: Resolver, renderResult: ResolverResponse) => ResolverResponse|Promise<ResolverResponse>;
|
|
37
|
+
type HookFallbackFunction = (ctx: RenderContext, resolver: Resolver) => ResolverResponse|Promise<ResolverResponse>;
|
|
38
38
|
type HookCompletedFunction = (renderResult: ResolverResponse) => ResolverResponse|Promise<ResolverResponse>;
|
|
39
39
|
|
|
40
|
-
type GlobalErrorHandlerFunction = (err: Error,
|
|
41
|
-
type RouteFallbackHandlerFunction = (
|
|
42
|
-
type ApiFallbackHandlerFunction = (
|
|
40
|
+
type GlobalErrorHandlerFunction = (err: Error, ctx: RenderContext|null, response: ResponseBuilder) => ResolverResponse|Promise<ResolverResponse>;
|
|
41
|
+
type RouteFallbackHandlerFunction = (ctx:RenderContext, resolver: Resolver) => ResolverResponse;
|
|
42
|
+
type ApiFallbackHandlerFunction = (ctx:RenderContext, resolver: Resolver) => ResolverResponse;
|
|
43
43
|
|
|
44
44
|
type HookEventType = "created"|"beforeRender"|"afterRender"|"fallback"|"completed";
|
|
45
45
|
type HookListType = {
|
|
@@ -123,18 +123,18 @@ export default class Lambder {
|
|
|
123
123
|
return false;
|
|
124
124
|
};
|
|
125
125
|
|
|
126
|
-
private async handleNoMatchedAction(
|
|
126
|
+
private async handleNoMatchedAction(ctx: RenderContext, resolver: Resolver){
|
|
127
127
|
for(const hook of this._hookList["fallback"]){
|
|
128
|
-
await hook.hookFn(
|
|
128
|
+
await hook.hookFn(ctx, resolver);
|
|
129
129
|
}
|
|
130
|
-
if(
|
|
130
|
+
if(ctx.url === this.apiPath){
|
|
131
131
|
if(this.apiFallbackHandler){
|
|
132
|
-
resolver.resolve(await this.apiFallbackHandler(
|
|
132
|
+
resolver.resolve(await this.apiFallbackHandler(ctx, resolver));
|
|
133
133
|
}else{
|
|
134
134
|
resolver.resolve({ statusCode: 204, body: "API handler not set.", })
|
|
135
135
|
}
|
|
136
136
|
}else if(this.routeFallbackHandler){
|
|
137
|
-
resolver.resolve(await this.routeFallbackHandler(
|
|
137
|
+
resolver.resolve(await this.routeFallbackHandler(ctx, resolver));
|
|
138
138
|
}else{
|
|
139
139
|
resolver.resolve({ statusCode: 204, body: "Handler not set.", })
|
|
140
140
|
}
|
|
@@ -145,60 +145,60 @@ export default class Lambder {
|
|
|
145
145
|
}
|
|
146
146
|
addRoute(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
147
147
|
this._actionList.push({
|
|
148
|
-
conditionFn: async (
|
|
149
|
-
|
|
148
|
+
conditionFn: async (ctx:RenderContext) => (
|
|
149
|
+
ctx.method === "GET" &&
|
|
150
150
|
(
|
|
151
|
-
(typeof condition === "string" &&
|
|
152
|
-
(typeof condition === "function" && (await condition(
|
|
153
|
-
(condition?.constructor == RegExp && condition.test(
|
|
151
|
+
(typeof condition === "string" && ctx.url === condition) ||
|
|
152
|
+
(typeof condition === "function" && (await condition(ctx))) ||
|
|
153
|
+
(condition?.constructor == RegExp && condition.test(ctx.url))
|
|
154
154
|
)
|
|
155
155
|
),
|
|
156
|
-
actionFn: async (
|
|
156
|
+
actionFn: async (ctx:RenderContext, resolver: Resolver) => await actionFn(ctx, resolver)
|
|
157
157
|
});
|
|
158
158
|
};
|
|
159
159
|
|
|
160
160
|
addSessionRoute(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
161
161
|
this._actionList.push({
|
|
162
|
-
conditionFn: async (
|
|
163
|
-
|
|
162
|
+
conditionFn: async (ctx:RenderContext) => (
|
|
163
|
+
ctx.method === "GET" && !!ctx.session &&
|
|
164
164
|
(
|
|
165
|
-
(typeof condition === "string" &&
|
|
166
|
-
(typeof condition === "function" && (await condition(
|
|
167
|
-
(condition?.constructor == RegExp && condition.test(
|
|
165
|
+
(typeof condition === "string" && ctx.url === condition) ||
|
|
166
|
+
(typeof condition === "function" && (await condition(ctx))) ||
|
|
167
|
+
(condition?.constructor == RegExp && condition.test(ctx.url))
|
|
168
168
|
)
|
|
169
169
|
),
|
|
170
|
-
actionFn: async (
|
|
170
|
+
actionFn: async (ctx:RenderContext, resolver: Resolver) => await actionFn(ctx, resolver)
|
|
171
171
|
});
|
|
172
172
|
};
|
|
173
173
|
|
|
174
174
|
addApi(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
175
175
|
this._actionList.push({
|
|
176
|
-
conditionFn: async (
|
|
177
|
-
|
|
176
|
+
conditionFn: async (ctx:RenderContext) => (
|
|
177
|
+
ctx.method === "POST" && ctx.url === this.apiPath &&
|
|
178
178
|
(
|
|
179
|
-
(typeof condition === "string" &&
|
|
180
|
-
(typeof condition === "function" && (await condition(
|
|
181
|
-
(condition?.constructor == RegExp && condition.test(
|
|
179
|
+
(typeof condition === "string" && ctx.post?.api === condition) ||
|
|
180
|
+
(typeof condition === "function" && (await condition(ctx))) ||
|
|
181
|
+
(condition?.constructor == RegExp && condition.test(ctx.post?.api))
|
|
182
182
|
)
|
|
183
183
|
),
|
|
184
|
-
actionFn: async (
|
|
184
|
+
actionFn: async (ctx:RenderContext, resolver: Resolver) => await actionFn(ctx, resolver)
|
|
185
185
|
});
|
|
186
186
|
};
|
|
187
187
|
|
|
188
188
|
addSessionApi(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
189
189
|
this._actionList.push({
|
|
190
|
-
conditionFn: async (
|
|
191
|
-
|
|
190
|
+
conditionFn: async (ctx:RenderContext) => (
|
|
191
|
+
ctx.method === "POST" && ctx.url === this.apiPath && !!ctx.session &&
|
|
192
192
|
(
|
|
193
|
-
(typeof condition === "string" &&
|
|
194
|
-
(typeof condition === "function" && (await condition(
|
|
195
|
-
(condition?.constructor == RegExp && condition.test(
|
|
193
|
+
(typeof condition === "string" && ctx.post?.api === condition) ||
|
|
194
|
+
(typeof condition === "function" && (await condition(ctx))) ||
|
|
195
|
+
(condition?.constructor == RegExp && condition.test(ctx.post?.api))
|
|
196
196
|
)
|
|
197
197
|
),
|
|
198
|
-
actionFn: async (
|
|
199
|
-
const isSessionValid = this.validateSession(
|
|
198
|
+
actionFn: async (ctx:RenderContext, resolver: Resolver) => {
|
|
199
|
+
const isSessionValid = this.validateSession(ctx.session);
|
|
200
200
|
if(!isSessionValid) throw new Error("Session not found");
|
|
201
|
-
return await actionFn(
|
|
201
|
+
return await actionFn(ctx, resolver);
|
|
202
202
|
}
|
|
203
203
|
});
|
|
204
204
|
};
|
|
@@ -222,45 +222,57 @@ export default class Lambder {
|
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
public getResponseBuilder(){
|
|
226
|
+
return new ResponseBuilder({
|
|
227
|
+
isCorsEnabled: this.isCorsEnabled,
|
|
228
|
+
publicPath: this.publicPath,
|
|
229
|
+
apiVersion: this.apiVersion,
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
private getResolver(resolve: (renderResult: ResolverResponse) => void, reject: (err: Error) => void){
|
|
234
|
+
return new Resolver({
|
|
235
|
+
isCorsEnabled: this.isCorsEnabled,
|
|
236
|
+
publicPath: this.publicPath,
|
|
237
|
+
apiVersion: this.apiVersion,
|
|
238
|
+
resolve, reject
|
|
239
|
+
});
|
|
240
|
+
};
|
|
225
241
|
|
|
226
242
|
async render(
|
|
227
243
|
event: APIGatewayProxyEvent,
|
|
228
244
|
lambdaContext: Context
|
|
229
245
|
): Promise<ResolverResponse>{
|
|
230
246
|
let eventRenderContext:RenderContext|null = null;
|
|
247
|
+
|
|
231
248
|
try {
|
|
232
|
-
let
|
|
233
|
-
eventRenderContext =
|
|
249
|
+
let ctx = createContext(event, lambdaContext);
|
|
250
|
+
eventRenderContext = ctx;
|
|
234
251
|
|
|
235
252
|
return await new Promise(async (
|
|
236
253
|
resolve:(renderResult: ResolverResponse)=>void,
|
|
237
254
|
reject:(err: Error)=>void
|
|
238
255
|
)=> {
|
|
239
|
-
const resolver =
|
|
240
|
-
|
|
241
|
-
publicPath: this.publicPath,
|
|
242
|
-
apiVersion: this.apiVersion,
|
|
243
|
-
resolve, reject
|
|
244
|
-
});
|
|
245
|
-
if(renderContext.method === "OPTIONS") return resolver.cors();
|
|
256
|
+
const resolver = this.getResolver(resolve, reject);
|
|
257
|
+
if(ctx.method === "OPTIONS") return resolver.cors();
|
|
246
258
|
|
|
247
259
|
const firstMatchedAction = await (async () => {
|
|
248
260
|
for(const action of this._actionList){
|
|
249
|
-
if(await action.conditionFn(
|
|
261
|
+
if(await action.conditionFn(ctx)) return action;
|
|
250
262
|
}
|
|
251
263
|
})();
|
|
252
264
|
|
|
253
265
|
if(firstMatchedAction){
|
|
254
266
|
for(const hook of this._hookList["beforeRender"]){
|
|
255
|
-
|
|
267
|
+
ctx = await hook.hookFn(ctx, resolver);
|
|
256
268
|
}
|
|
257
|
-
let renderResult = await firstMatchedAction.actionFn(
|
|
269
|
+
let renderResult = await firstMatchedAction.actionFn(ctx, resolver);
|
|
258
270
|
for(const hook of this._hookList["afterRender"]){
|
|
259
|
-
renderResult = await hook.hookFn(
|
|
271
|
+
renderResult = await hook.hookFn(ctx, resolver, renderResult);
|
|
260
272
|
}
|
|
261
273
|
resolve(renderResult);
|
|
262
274
|
}else{
|
|
263
|
-
return this.handleNoMatchedAction(
|
|
275
|
+
return this.handleNoMatchedAction(ctx, resolver);
|
|
264
276
|
}
|
|
265
277
|
}).then(async (renderResult: ResolverResponse):Promise<ResolverResponse> => {
|
|
266
278
|
for(const hook of this._hookList["completed"]){
|
|
@@ -269,14 +281,10 @@ export default class Lambder {
|
|
|
269
281
|
return renderResult;
|
|
270
282
|
});
|
|
271
283
|
}catch(err){
|
|
272
|
-
const
|
|
273
|
-
isCorsEnabled: this.isCorsEnabled,
|
|
274
|
-
publicPath: this.publicPath,
|
|
275
|
-
apiVersion: this.apiVersion,
|
|
276
|
-
})
|
|
284
|
+
const responseBuilder = this.getResponseBuilder();
|
|
277
285
|
const wrappedError = err instanceof Error ? err : new Error("Error: " + String(err));
|
|
278
286
|
if(this.globalErrorHandler){
|
|
279
|
-
return this.globalErrorHandler(wrappedError, eventRenderContext,
|
|
287
|
+
return this.globalErrorHandler(wrappedError, eventRenderContext, responseBuilder);
|
|
280
288
|
}
|
|
281
289
|
return { statusCode: 500, body: "Internal Server Error.", }
|
|
282
290
|
|
package/src/Resolver.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ResponseBuilder, { ResolverResponse } from "./ResponseBuilder.js";
|
|
2
2
|
|
|
3
3
|
type MethodType<T, M extends keyof T> = T[M] extends (...args: any[]) => any ? T[M] : never;
|
|
4
4
|
|
|
5
5
|
interface DieResolverMethods {
|
|
6
|
-
raw: MethodType<
|
|
7
|
-
json: MethodType<
|
|
8
|
-
xml: MethodType<
|
|
9
|
-
html: MethodType<
|
|
10
|
-
status301: MethodType<
|
|
11
|
-
status404: MethodType<
|
|
12
|
-
cors: MethodType<
|
|
13
|
-
fileBase64: MethodType<
|
|
14
|
-
file: MethodType<
|
|
15
|
-
api: MethodType<
|
|
6
|
+
raw: MethodType<ResponseBuilder, 'raw'>;
|
|
7
|
+
json: MethodType<ResponseBuilder, 'json'>;
|
|
8
|
+
xml: MethodType<ResponseBuilder, 'xml'>;
|
|
9
|
+
html: MethodType<ResponseBuilder, 'html'>;
|
|
10
|
+
status301: MethodType<ResponseBuilder, 'status301'>;
|
|
11
|
+
status404: MethodType<ResponseBuilder, 'status404'>;
|
|
12
|
+
cors: MethodType<ResponseBuilder, 'cors'>;
|
|
13
|
+
fileBase64: MethodType<ResponseBuilder, 'fileBase64'>;
|
|
14
|
+
file: MethodType<ResponseBuilder, 'file'>;
|
|
15
|
+
api: MethodType<ResponseBuilder, 'api'>;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export default class Resolver extends
|
|
18
|
+
export default class Resolver extends ResponseBuilder {
|
|
19
19
|
public resolve: (renderResult: ResolverResponse) => void;
|
|
20
20
|
public reject: (err: Error) => void;
|
|
21
21
|
public die: DieResolverMethods;
|
|
@@ -34,7 +34,6 @@ export default class Resolver extends ResolverLight {
|
|
|
34
34
|
this.resolve = resolve;
|
|
35
35
|
this.reject = reject;
|
|
36
36
|
|
|
37
|
-
// Bind ResolverLight methods to `this` for `die` and automatically resolve
|
|
38
37
|
this.die = {
|
|
39
38
|
raw: this.autoResolve(this.raw),
|
|
40
39
|
json: this.autoResolve(this.json),
|
|
@@ -23,7 +23,7 @@ export type ResolverResponse = {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
export default class
|
|
26
|
+
export default class ResponseBuilder {
|
|
27
27
|
private isCorsEnabled: boolean;
|
|
28
28
|
private publicPath: string;
|
|
29
29
|
private apiVersion: string|null;
|
|
@@ -187,38 +187,4 @@ export default class ResolverLight {
|
|
|
187
187
|
}, headers);
|
|
188
188
|
};
|
|
189
189
|
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
interface IResolverMethods {
|
|
195
|
-
raw(param: {
|
|
196
|
-
statusCode: number,
|
|
197
|
-
isBase64Encoded?: boolean,
|
|
198
|
-
body: null | string,
|
|
199
|
-
multiValueHeaders: { [key: string]: string[] },
|
|
200
|
-
}): ResolverResponse;
|
|
201
|
-
json(data: { [key: string]: any }, headers?: { [key: string]: string | string[] }): ResolverResponse;
|
|
202
|
-
xml(data: string): ResolverResponse;
|
|
203
|
-
html(data: string, headers?: { [key: string]: string | string[] }): ResolverResponse;
|
|
204
|
-
status301(url: string, headers?: { [key: string]: string | string[] }): ResolverResponse;
|
|
205
|
-
status404(data: string, headers?: { [key: string]: string | string[] }): ResolverResponse;
|
|
206
|
-
cors(): ResolverResponse;
|
|
207
|
-
fileBase64(fileBase64: string, mimeType: string, headers?: { [key: string]: string | string[] }): ResolverResponse;
|
|
208
|
-
file(filePath: string, headers?: { [key: string]: string | string[] }): ResolverResponse;
|
|
209
|
-
api({
|
|
210
|
-
payload,
|
|
211
|
-
versionExpired, sessionExpired, notAuthorized,
|
|
212
|
-
message, errorMessage,
|
|
213
|
-
...other
|
|
214
|
-
}: {
|
|
215
|
-
payload?: any;
|
|
216
|
-
versionExpired?: boolean;
|
|
217
|
-
sessionExpired?: boolean;
|
|
218
|
-
notAuthorized?: boolean;
|
|
219
|
-
message?: any;
|
|
220
|
-
errorMessage?: any;
|
|
221
|
-
},
|
|
222
|
-
headers?: { [key:string]: string|string[] }
|
|
223
|
-
): ResolverResponse;
|
|
224
|
-
}
|
|
190
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -2,5 +2,5 @@ import Lambder from './Lambder.js';
|
|
|
2
2
|
|
|
3
3
|
export default Lambder;
|
|
4
4
|
export { default as LambderCaller } from "./LambderCaller.js";
|
|
5
|
-
export { default as
|
|
5
|
+
export { default as ResponseBuilder } from "./ResponseBuilder.js";
|
|
6
6
|
export { default as Resolver } from "./Resolver.js";
|