lambder 1.0.17 → 1.0.19
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/.vscode/settings.json +1 -1
- package/dist/LambderCaller.d.ts +39 -0
- package/dist/LambderCaller.js +93 -0
- package/dist/Resolver.d.ts +6 -2
- package/dist/Resolver.js +6 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +36 -16
- package/package.json +3 -1
- package/src/LambderCaller.ts +121 -0
- package/src/Resolver.ts +22 -6
- package/src/index.ts +37 -22
package/.vscode/settings.json
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
type VoidFunction = () => void | Promise<void>;
|
|
2
|
+
type StartEndEventHandler = (activeFetchList: {
|
|
3
|
+
apiName: string;
|
|
4
|
+
done: boolean;
|
|
5
|
+
}[]) => void | Promise<void>;
|
|
6
|
+
type ErrorHandler = (err: Error) => void | Promise<void>;
|
|
7
|
+
type MessageHandler = (message: any) => void | Promise<void>;
|
|
8
|
+
export default class LambderCaller {
|
|
9
|
+
isCorsEnabled: boolean;
|
|
10
|
+
apiPath: string;
|
|
11
|
+
apiVersion?: string;
|
|
12
|
+
fetchTrackerList: {
|
|
13
|
+
apiName: string;
|
|
14
|
+
done: boolean;
|
|
15
|
+
}[];
|
|
16
|
+
versionExpiredHandler?: VoidFunction;
|
|
17
|
+
sessionExpiredHandler?: VoidFunction;
|
|
18
|
+
messageHandler?: MessageHandler;
|
|
19
|
+
errorMessageHandler?: MessageHandler;
|
|
20
|
+
notAuthorizedHandler?: VoidFunction;
|
|
21
|
+
errorHandler?: ErrorHandler;
|
|
22
|
+
fetchStartedHandler?: StartEndEventHandler;
|
|
23
|
+
fetchEndedHandler?: StartEndEventHandler;
|
|
24
|
+
constructor({ isCorsEnabled, apiPath, apiVersion, versionExpiredHandler, sessionExpiredHandler, messageHandler, errorMessageHandler, notAuthorizedHandler, errorHandler, fetchStartedHandler, fetchEndedHandler, }: {
|
|
25
|
+
isCorsEnabled: boolean;
|
|
26
|
+
apiPath: string;
|
|
27
|
+
apiVersion?: string;
|
|
28
|
+
versionExpiredHandler?: VoidFunction;
|
|
29
|
+
sessionExpiredHandler?: VoidFunction;
|
|
30
|
+
messageHandler?: MessageHandler;
|
|
31
|
+
errorMessageHandler?: MessageHandler;
|
|
32
|
+
notAuthorizedHandler?: VoidFunction;
|
|
33
|
+
errorHandler?: ErrorHandler;
|
|
34
|
+
fetchStartedHandler?: StartEndEventHandler;
|
|
35
|
+
fetchEndedHandler?: StartEndEventHandler;
|
|
36
|
+
});
|
|
37
|
+
private api;
|
|
38
|
+
}
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import Cookies from 'js-cookie';
|
|
2
|
+
export default class LambderCaller {
|
|
3
|
+
isCorsEnabled;
|
|
4
|
+
apiPath;
|
|
5
|
+
apiVersion;
|
|
6
|
+
fetchTrackerList = [];
|
|
7
|
+
versionExpiredHandler;
|
|
8
|
+
sessionExpiredHandler;
|
|
9
|
+
messageHandler;
|
|
10
|
+
errorMessageHandler;
|
|
11
|
+
notAuthorizedHandler;
|
|
12
|
+
errorHandler;
|
|
13
|
+
fetchStartedHandler;
|
|
14
|
+
fetchEndedHandler;
|
|
15
|
+
constructor({ isCorsEnabled, apiPath, apiVersion, versionExpiredHandler, sessionExpiredHandler, messageHandler, errorMessageHandler, notAuthorizedHandler, errorHandler, fetchStartedHandler, fetchEndedHandler, }) {
|
|
16
|
+
this.isCorsEnabled = isCorsEnabled;
|
|
17
|
+
this.apiPath = apiPath;
|
|
18
|
+
this.apiVersion = apiVersion;
|
|
19
|
+
this.versionExpiredHandler = versionExpiredHandler;
|
|
20
|
+
this.sessionExpiredHandler = sessionExpiredHandler;
|
|
21
|
+
this.messageHandler = messageHandler;
|
|
22
|
+
this.errorMessageHandler = errorMessageHandler;
|
|
23
|
+
this.notAuthorizedHandler = notAuthorizedHandler;
|
|
24
|
+
this.errorHandler = errorHandler;
|
|
25
|
+
this.fetchStartedHandler = fetchStartedHandler;
|
|
26
|
+
this.fetchEndedHandler = fetchEndedHandler;
|
|
27
|
+
}
|
|
28
|
+
;
|
|
29
|
+
async api(apiName, payload, headers) {
|
|
30
|
+
const fetchTracker = { apiName, done: false };
|
|
31
|
+
try {
|
|
32
|
+
this.fetchTrackerList.push(fetchTracker);
|
|
33
|
+
if (this.fetchStartedHandler)
|
|
34
|
+
await this.fetchStartedHandler(this.fetchTrackerList.filter(v => !v.done));
|
|
35
|
+
const version = this.apiVersion;
|
|
36
|
+
const token = Cookies.get("LMBRTOKEN") || "";
|
|
37
|
+
const siteHost = window.location.hostname;
|
|
38
|
+
let data = await fetch(this.apiPath, {
|
|
39
|
+
method: 'POST', mode: 'same-origin', cache: 'no-cache',
|
|
40
|
+
credentials: 'same-origin', redirect: 'follow', referrerPolicy: 'origin',
|
|
41
|
+
headers: { 'Content-Type': 'application/json', ...(headers || {}) },
|
|
42
|
+
body: JSON.stringify({ api: apiName, version, token, siteHost, payload, }),
|
|
43
|
+
}).then(res => {
|
|
44
|
+
if (res.status >= 500)
|
|
45
|
+
throw new Error("Request failed: " + res.status + " - " + res.statusText);
|
|
46
|
+
return res.json();
|
|
47
|
+
});
|
|
48
|
+
if (data && data.versionExpired) {
|
|
49
|
+
if (this.versionExpiredHandler) {
|
|
50
|
+
await this.versionExpiredHandler();
|
|
51
|
+
}
|
|
52
|
+
else if (this.errorHandler) {
|
|
53
|
+
await this.errorHandler(new Error("Version Expired; Please refresh;"));
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (data && data.sessionExpired) {
|
|
58
|
+
Cookies.set('LMBRAUTHID', '', { expires: -1 });
|
|
59
|
+
Cookies.set('LMBRTOKEN', '', { expires: -1 });
|
|
60
|
+
if (this.sessionExpiredHandler) {
|
|
61
|
+
await this.sessionExpiredHandler();
|
|
62
|
+
}
|
|
63
|
+
else if (this.errorHandler) {
|
|
64
|
+
await this.errorHandler(new Error("Version Expired; Please refresh;"));
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
if (data && data.notAuthorized) {
|
|
69
|
+
if (this.notAuthorizedHandler) {
|
|
70
|
+
await this.notAuthorizedHandler();
|
|
71
|
+
}
|
|
72
|
+
else if (this.errorHandler) {
|
|
73
|
+
await this.errorHandler(new Error("Version Expired; Please refresh;"));
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
if (data && data.message && this.messageHandler) {
|
|
78
|
+
await this.messageHandler(data.message);
|
|
79
|
+
}
|
|
80
|
+
if (data && data.errorMessage && this.errorMessageHandler) {
|
|
81
|
+
await this.errorMessageHandler(data.errorMessage);
|
|
82
|
+
}
|
|
83
|
+
return data.payload ?? null;
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
fetchTracker.done = true;
|
|
87
|
+
if (this.errorHandler) {
|
|
88
|
+
this.errorHandler(err instanceof Error ? err : new Error("Error: " + String(err)));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
;
|
|
93
|
+
}
|
package/dist/Resolver.d.ts
CHANGED
|
@@ -85,9 +85,13 @@ export default class Resolver {
|
|
|
85
85
|
file(filePath: string, headers?: {
|
|
86
86
|
[key: string]: string | string[];
|
|
87
87
|
}, fallbackFilePath?: string): ResolverResponse;
|
|
88
|
-
api({ payload,
|
|
88
|
+
api({ payload, versionExpired, sessionExpired, notAuthorized, message, errorMessage, ...other }: {
|
|
89
89
|
payload?: any;
|
|
90
|
-
|
|
90
|
+
versionExpired?: boolean;
|
|
91
|
+
sessionExpired?: boolean;
|
|
92
|
+
notAuthorized?: boolean;
|
|
93
|
+
message?: any;
|
|
94
|
+
errorMessage?: any;
|
|
91
95
|
}, headers?: {
|
|
92
96
|
[key: string]: string | string[];
|
|
93
97
|
}): ResolverResponse;
|
package/dist/Resolver.js
CHANGED
|
@@ -136,15 +136,17 @@ export default class Resolver {
|
|
|
136
136
|
return this.fileBase64(bodyBase64, mimeType || "", headers);
|
|
137
137
|
}
|
|
138
138
|
;
|
|
139
|
-
api({ payload,
|
|
140
|
-
if (error)
|
|
141
|
-
console.log("ERROR: ", error);
|
|
139
|
+
api({ payload, versionExpired, sessionExpired, notAuthorized, message = null, errorMessage = null, ...other }, headers) {
|
|
142
140
|
if (Object.keys(other).length)
|
|
143
141
|
console.log("ERROR: Unexpected key in result: " + Object.keys(other).join(", "));
|
|
144
142
|
return this.json({
|
|
145
143
|
apiVersion: this.apiVersion,
|
|
146
144
|
payload,
|
|
147
|
-
...(
|
|
145
|
+
...(versionExpired ? { versionExpired } : {}),
|
|
146
|
+
...(sessionExpired ? { sessionExpired } : {}),
|
|
147
|
+
...(notAuthorized ? { notAuthorized } : {}),
|
|
148
|
+
...(message ? { message } : {}),
|
|
149
|
+
...(errorMessage ? { errorMessage } : {}),
|
|
148
150
|
}, headers);
|
|
149
151
|
}
|
|
150
152
|
;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { APIGatewayProxyEvent, APIGatewayProxyEventHeaders, Context } from "aws-lambda";
|
|
2
2
|
import Resolver, { ResolverResponse } from "./Resolver.js";
|
|
3
|
+
export { default as LambderCaller } from "./LambderCaller.js";
|
|
3
4
|
type DDBSession = {
|
|
4
5
|
userId: string;
|
|
5
6
|
userIdHash: string;
|
|
@@ -44,17 +45,20 @@ export default class Lambder {
|
|
|
44
45
|
private _actionList;
|
|
45
46
|
private _hookList;
|
|
46
47
|
private globalErrorHandler;
|
|
47
|
-
private
|
|
48
|
+
private routeFallbackHandler;
|
|
49
|
+
private apiFallbackHandler;
|
|
48
50
|
constructor({ publicPath, apiPath, apiVersion, isCorsEnabled, }: {
|
|
49
51
|
publicPath: string;
|
|
50
52
|
apiPath?: string;
|
|
51
53
|
apiVersion?: string;
|
|
52
54
|
isCorsEnabled?: boolean;
|
|
53
55
|
});
|
|
54
|
-
|
|
56
|
+
setRouteFallbackHandler(routeFallbackHandler: (renderContext: RenderContext, resolver: Resolver) => ResolverResponse): void;
|
|
57
|
+
setApiFallbackHandler(apiFallbackHandler: (renderContext: RenderContext, resolver: Resolver) => ResolverResponse): void;
|
|
55
58
|
setGlobalErrorHandler(globalErrorHandler: (err: Error) => ResolverResponse): void;
|
|
56
|
-
addModule(moduleFn: Function): Promise<void>;
|
|
57
59
|
private validateSession;
|
|
60
|
+
private handleNoMatchedAction;
|
|
61
|
+
addModule(moduleFn: Function): Promise<void>;
|
|
58
62
|
addRoute(condition: string | ConditionFunction | RegExp, actionFn: ActionFunction): void;
|
|
59
63
|
addSessionRoute(condition: string | ConditionFunction | RegExp, actionFn: ActionFunction): void;
|
|
60
64
|
addApi(condition: string | ConditionFunction | RegExp, actionFn: ActionFunction): void;
|
|
@@ -66,4 +70,3 @@ export default class Lambder {
|
|
|
66
70
|
addHook(hookEvent: 'completed', hookFn: HookCompletedFunction, priority?: number): Promise<void>;
|
|
67
71
|
render(event: APIGatewayProxyEvent, lambdaContext: Context): Promise<ResolverResponse>;
|
|
68
72
|
}
|
|
69
|
-
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import querystring from "querystring";
|
|
2
2
|
import cookieParser from "cookie";
|
|
3
3
|
import Resolver from "./Resolver.js";
|
|
4
|
+
export { default as LambderCaller } from "./LambderCaller.js";
|
|
4
5
|
export const createContext = (event, lambdaContext, resolve, reject) => {
|
|
5
6
|
const host = event.headers.Host || event.headers.host || "";
|
|
6
7
|
const url = event.path;
|
|
@@ -33,7 +34,8 @@ export default class Lambder {
|
|
|
33
34
|
_actionList;
|
|
34
35
|
_hookList;
|
|
35
36
|
globalErrorHandler = null;
|
|
36
|
-
|
|
37
|
+
routeFallbackHandler = null;
|
|
38
|
+
apiFallbackHandler = null;
|
|
37
39
|
constructor({ publicPath, apiPath, apiVersion, isCorsEnabled, }) {
|
|
38
40
|
this.publicPath = publicPath || "/incorrect-path-not-found";
|
|
39
41
|
this.apiPath = apiPath ?? "/api";
|
|
@@ -47,15 +49,15 @@ export default class Lambder {
|
|
|
47
49
|
"completed": []
|
|
48
50
|
};
|
|
49
51
|
}
|
|
50
|
-
|
|
51
|
-
this.
|
|
52
|
+
setRouteFallbackHandler(routeFallbackHandler) {
|
|
53
|
+
this.routeFallbackHandler = routeFallbackHandler;
|
|
54
|
+
}
|
|
55
|
+
setApiFallbackHandler(apiFallbackHandler) {
|
|
56
|
+
this.apiFallbackHandler = apiFallbackHandler;
|
|
52
57
|
}
|
|
53
58
|
setGlobalErrorHandler(globalErrorHandler) {
|
|
54
59
|
this.globalErrorHandler = globalErrorHandler;
|
|
55
60
|
}
|
|
56
|
-
async addModule(moduleFn) {
|
|
57
|
-
await moduleFn(this);
|
|
58
|
-
}
|
|
59
61
|
async validateSession(session) {
|
|
60
62
|
if (!session)
|
|
61
63
|
return false;
|
|
@@ -68,6 +70,29 @@ export default class Lambder {
|
|
|
68
70
|
// Check DDB;
|
|
69
71
|
return false;
|
|
70
72
|
}
|
|
73
|
+
;
|
|
74
|
+
async handleNoMatchedAction(renderContext, resolver) {
|
|
75
|
+
for (const hook of this._hookList["fallback"]) {
|
|
76
|
+
await hook.hookFn(renderContext, resolver);
|
|
77
|
+
}
|
|
78
|
+
if (renderContext.url === this.apiPath) {
|
|
79
|
+
if (this.apiFallbackHandler) {
|
|
80
|
+
resolver.resolve(await this.apiFallbackHandler(renderContext, resolver));
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
resolver.resolve({ statusCode: 204, body: "API handler not set.", });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (this.routeFallbackHandler) {
|
|
87
|
+
resolver.resolve(await this.routeFallbackHandler(renderContext, resolver));
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
resolver.resolve({ statusCode: 204, body: "Handler not set.", });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async addModule(moduleFn) {
|
|
94
|
+
await moduleFn(this);
|
|
95
|
+
}
|
|
71
96
|
addRoute(condition, actionFn) {
|
|
72
97
|
this._actionList.push({
|
|
73
98
|
conditionFn: async (renderContext) => (renderContext.method === "GET" &&
|
|
@@ -77,6 +102,7 @@ export default class Lambder {
|
|
|
77
102
|
actionFn: async (renderContext, resolver) => await actionFn(renderContext, resolver)
|
|
78
103
|
});
|
|
79
104
|
}
|
|
105
|
+
;
|
|
80
106
|
addSessionRoute(condition, actionFn) {
|
|
81
107
|
this._actionList.push({
|
|
82
108
|
conditionFn: async (renderContext) => (renderContext.method === "GET" &&
|
|
@@ -86,6 +112,7 @@ export default class Lambder {
|
|
|
86
112
|
actionFn: async (renderContext, resolver) => await actionFn(renderContext, resolver)
|
|
87
113
|
});
|
|
88
114
|
}
|
|
115
|
+
;
|
|
89
116
|
addApi(condition, actionFn) {
|
|
90
117
|
this._actionList.push({
|
|
91
118
|
conditionFn: async (renderContext) => (renderContext.method === "POST" && renderContext.url === this.apiPath &&
|
|
@@ -95,6 +122,7 @@ export default class Lambder {
|
|
|
95
122
|
actionFn: async (renderContext, resolver) => await actionFn(renderContext, resolver)
|
|
96
123
|
});
|
|
97
124
|
}
|
|
125
|
+
;
|
|
98
126
|
addSessionApi(condition, actionFn) {
|
|
99
127
|
this._actionList.push({
|
|
100
128
|
conditionFn: async (renderContext) => (renderContext.method === "POST" && renderContext.url === this.apiPath &&
|
|
@@ -109,6 +137,7 @@ export default class Lambder {
|
|
|
109
137
|
}
|
|
110
138
|
});
|
|
111
139
|
}
|
|
140
|
+
;
|
|
112
141
|
async addHook(hookEvent, hookFn, priority = 0) {
|
|
113
142
|
if (hookEvent === "created") {
|
|
114
143
|
await hookFn(this);
|
|
@@ -146,16 +175,7 @@ export default class Lambder {
|
|
|
146
175
|
resolve(renderResult);
|
|
147
176
|
}
|
|
148
177
|
else {
|
|
149
|
-
|
|
150
|
-
await hook.hookFn(renderContext, resolver);
|
|
151
|
-
}
|
|
152
|
-
if (this.fallbackHandler) {
|
|
153
|
-
const renderResult = await this.fallbackHandler(renderContext, resolver);
|
|
154
|
-
resolve(renderResult);
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
resolve({ statusCode: 204, body: "Fallback handler not set.", });
|
|
158
|
-
}
|
|
178
|
+
return this.handleNoMatchedAction(renderContext, resolver);
|
|
159
179
|
}
|
|
160
180
|
}).then(async (renderResult) => {
|
|
161
181
|
for (const hook of this._hookList["completed"]) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lambder",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -14,12 +14,14 @@
|
|
|
14
14
|
"license": "ISC",
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"cookie": "^0.6.0",
|
|
17
|
+
"js-cookie": "^3.0.5",
|
|
17
18
|
"mime-types": "^2.1.35",
|
|
18
19
|
"querystring": "^0.2.1"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|
|
21
22
|
"@types/aws-lambda": "^8.10.136",
|
|
22
23
|
"@types/cookie": "^0.6.0",
|
|
24
|
+
"@types/js-cookie": "^3.0.6",
|
|
23
25
|
"@types/mime-types": "^2.1.4",
|
|
24
26
|
"@types/node": "^20.11.27",
|
|
25
27
|
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import Cookies from 'js-cookie';
|
|
2
|
+
|
|
3
|
+
type VoidFunction = ()=>void|Promise<void>;
|
|
4
|
+
type StartEndEventHandler = (activeFetchList:{ apiName: string, done: boolean}[])=>void|Promise<void>;
|
|
5
|
+
type ErrorHandler = (err: Error) => void|Promise<void>;
|
|
6
|
+
type MessageHandler = (message:any) => void|Promise<void>;
|
|
7
|
+
|
|
8
|
+
export default class LambderCaller {
|
|
9
|
+
isCorsEnabled: boolean;
|
|
10
|
+
apiPath: string;
|
|
11
|
+
apiVersion?: string;
|
|
12
|
+
|
|
13
|
+
fetchTrackerList: { apiName: string, done: boolean}[] = [];
|
|
14
|
+
versionExpiredHandler?: VoidFunction;
|
|
15
|
+
sessionExpiredHandler?: VoidFunction;
|
|
16
|
+
|
|
17
|
+
messageHandler?: MessageHandler;
|
|
18
|
+
errorMessageHandler?: MessageHandler;
|
|
19
|
+
notAuthorizedHandler?: VoidFunction;
|
|
20
|
+
errorHandler?: ErrorHandler;
|
|
21
|
+
|
|
22
|
+
fetchStartedHandler?: StartEndEventHandler;
|
|
23
|
+
fetchEndedHandler?: StartEndEventHandler;
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
{
|
|
27
|
+
isCorsEnabled, apiPath, apiVersion,
|
|
28
|
+
versionExpiredHandler, sessionExpiredHandler,
|
|
29
|
+
messageHandler, errorMessageHandler,
|
|
30
|
+
notAuthorizedHandler, errorHandler,
|
|
31
|
+
fetchStartedHandler, fetchEndedHandler,
|
|
32
|
+
}:
|
|
33
|
+
{
|
|
34
|
+
isCorsEnabled: boolean,
|
|
35
|
+
apiPath: string,
|
|
36
|
+
apiVersion?: string,
|
|
37
|
+
versionExpiredHandler?: VoidFunction,
|
|
38
|
+
sessionExpiredHandler?: VoidFunction,
|
|
39
|
+
messageHandler?: MessageHandler,
|
|
40
|
+
errorMessageHandler?: MessageHandler,
|
|
41
|
+
notAuthorizedHandler?: VoidFunction,
|
|
42
|
+
errorHandler?: ErrorHandler,
|
|
43
|
+
fetchStartedHandler?: StartEndEventHandler,
|
|
44
|
+
fetchEndedHandler?: StartEndEventHandler,
|
|
45
|
+
}
|
|
46
|
+
){
|
|
47
|
+
this.isCorsEnabled = isCorsEnabled;
|
|
48
|
+
this.apiPath = apiPath;
|
|
49
|
+
this.apiVersion = apiVersion;
|
|
50
|
+
|
|
51
|
+
this.versionExpiredHandler = versionExpiredHandler;
|
|
52
|
+
this.sessionExpiredHandler = sessionExpiredHandler;
|
|
53
|
+
|
|
54
|
+
this.messageHandler = messageHandler;
|
|
55
|
+
this.errorMessageHandler = errorMessageHandler;
|
|
56
|
+
this.notAuthorizedHandler = notAuthorizedHandler;
|
|
57
|
+
this.errorHandler = errorHandler;
|
|
58
|
+
|
|
59
|
+
this.fetchStartedHandler = fetchStartedHandler;
|
|
60
|
+
this.fetchEndedHandler = fetchEndedHandler;
|
|
61
|
+
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
private async api(apiName: string, payload: any, headers?: { [key:string]: any }){
|
|
65
|
+
const fetchTracker = { apiName, done: false };
|
|
66
|
+
try {
|
|
67
|
+
this.fetchTrackerList.push(fetchTracker);
|
|
68
|
+
if(this.fetchStartedHandler) await this.fetchStartedHandler(this.fetchTrackerList.filter(v=>!v.done));
|
|
69
|
+
const version = this.apiVersion;
|
|
70
|
+
const token = Cookies.get("LMBRTOKEN") || "";
|
|
71
|
+
const siteHost = window.location.hostname;
|
|
72
|
+
let data = await fetch(this.apiPath, {
|
|
73
|
+
method: 'POST', mode: 'same-origin', cache: 'no-cache',
|
|
74
|
+
credentials: 'same-origin', redirect: 'follow', referrerPolicy: 'origin',
|
|
75
|
+
headers: { 'Content-Type': 'application/json', ...(headers || {}) },
|
|
76
|
+
body: JSON.stringify({ api: apiName, version, token, siteHost, payload, }),
|
|
77
|
+
}).then(res=>{
|
|
78
|
+
if(res.status >= 500) throw new Error("Request failed: " + res.status + " - " + res.statusText);
|
|
79
|
+
return res.json();
|
|
80
|
+
});
|
|
81
|
+
if(data && data.versionExpired){
|
|
82
|
+
if(this.versionExpiredHandler){
|
|
83
|
+
await this.versionExpiredHandler();
|
|
84
|
+
}else if(this.errorHandler){
|
|
85
|
+
await this.errorHandler(new Error("Version Expired; Please refresh;"));
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if(data && data.sessionExpired){
|
|
90
|
+
Cookies.set('LMBRAUTHID', '', { expires: -1 });
|
|
91
|
+
Cookies.set('LMBRTOKEN', '', { expires: -1 });
|
|
92
|
+
if(this.sessionExpiredHandler){
|
|
93
|
+
await this.sessionExpiredHandler();
|
|
94
|
+
}else if(this.errorHandler){
|
|
95
|
+
await this.errorHandler(new Error("Version Expired; Please refresh;"));
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
if(data && data.notAuthorized){
|
|
100
|
+
if(this.notAuthorizedHandler){
|
|
101
|
+
await this.notAuthorizedHandler();
|
|
102
|
+
}else if(this.errorHandler){
|
|
103
|
+
await this.errorHandler(new Error("Version Expired; Please refresh;"));
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
if(data && data.message && this.messageHandler){
|
|
108
|
+
await this.messageHandler(data.message);
|
|
109
|
+
}
|
|
110
|
+
if(data && data.errorMessage && this.errorMessageHandler){
|
|
111
|
+
await this.errorMessageHandler(data.errorMessage);
|
|
112
|
+
}
|
|
113
|
+
return data.payload ?? null;
|
|
114
|
+
}catch(err){
|
|
115
|
+
fetchTracker.done = true
|
|
116
|
+
if(this.errorHandler){
|
|
117
|
+
this.errorHandler(err instanceof Error ? err : new Error("Error: " + String(err)));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
package/src/Resolver.ts
CHANGED
|
@@ -198,16 +198,32 @@ export default class Resolver {
|
|
|
198
198
|
return this.fileBase64(bodyBase64, mimeType || "", headers);
|
|
199
199
|
};
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
{
|
|
201
|
+
api(
|
|
202
|
+
{
|
|
203
|
+
payload,
|
|
204
|
+
versionExpired, sessionExpired, notAuthorized,
|
|
205
|
+
message = null, errorMessage = null,
|
|
206
|
+
...other
|
|
207
|
+
}: {
|
|
208
|
+
payload?: any;
|
|
209
|
+
versionExpired?: boolean;
|
|
210
|
+
sessionExpired?: boolean;
|
|
211
|
+
notAuthorized?: boolean;
|
|
212
|
+
message?: any;
|
|
213
|
+
errorMessage?: any;
|
|
214
|
+
},
|
|
203
215
|
headers?: { [key:string]: string|string[] },
|
|
204
|
-
):ResolverResponse{
|
|
205
|
-
if(error) console.log("ERROR: ", error);
|
|
216
|
+
): ResolverResponse {
|
|
206
217
|
if(Object.keys(other).length) console.log("ERROR: Unexpected key in result: " + Object.keys(other).join(", "));
|
|
207
218
|
return this.json({
|
|
208
219
|
apiVersion: this.apiVersion,
|
|
209
|
-
payload,
|
|
210
|
-
...(
|
|
220
|
+
payload,
|
|
221
|
+
...(versionExpired ? {versionExpired} : {}),
|
|
222
|
+
...(sessionExpired ? {sessionExpired} : {}),
|
|
223
|
+
...(notAuthorized ? {notAuthorized} : {}),
|
|
224
|
+
...(message ? {message} : {}),
|
|
225
|
+
...(errorMessage ? {errorMessage} : {}),
|
|
211
226
|
}, headers);
|
|
212
227
|
};
|
|
228
|
+
|
|
213
229
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ import cookieParser from "cookie";
|
|
|
4
4
|
import type { APIGatewayProxyEvent, APIGatewayProxyEventHeaders, Context } from "aws-lambda";
|
|
5
5
|
import Resolver, { ResolverResponse } from "./Resolver.js";
|
|
6
6
|
|
|
7
|
+
export { default as LambderCaller } from "./LambderCaller.js";
|
|
8
|
+
|
|
7
9
|
type DDBSession = {
|
|
8
10
|
userId: string,
|
|
9
11
|
userIdHash: string;
|
|
@@ -80,7 +82,8 @@ export default class Lambder {
|
|
|
80
82
|
"completed": { priority: number, hookFn: HookCompletedFunction }[]
|
|
81
83
|
};
|
|
82
84
|
private globalErrorHandler: Function|null = null;
|
|
83
|
-
private
|
|
85
|
+
private routeFallbackHandler: Function|null = null;
|
|
86
|
+
private apiFallbackHandler: Function|null = null;
|
|
84
87
|
|
|
85
88
|
constructor(
|
|
86
89
|
{ publicPath, apiPath, apiVersion, isCorsEnabled, }:
|
|
@@ -100,17 +103,16 @@ export default class Lambder {
|
|
|
100
103
|
};
|
|
101
104
|
}
|
|
102
105
|
|
|
103
|
-
|
|
104
|
-
this.
|
|
106
|
+
setRouteFallbackHandler(routeFallbackHandler: (renderContext:RenderContext, resolver: Resolver)=>ResolverResponse){
|
|
107
|
+
this.routeFallbackHandler = routeFallbackHandler;
|
|
108
|
+
}
|
|
109
|
+
setApiFallbackHandler(apiFallbackHandler: (renderContext:RenderContext, resolver: Resolver)=>ResolverResponse){
|
|
110
|
+
this.apiFallbackHandler = apiFallbackHandler;
|
|
105
111
|
}
|
|
106
112
|
setGlobalErrorHandler(globalErrorHandler: (err: Error)=>ResolverResponse){
|
|
107
113
|
this.globalErrorHandler = globalErrorHandler;
|
|
108
114
|
}
|
|
109
115
|
|
|
110
|
-
async addModule(moduleFn: Function): Promise<void>{
|
|
111
|
-
await moduleFn(this);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
116
|
private async validateSession (session: DDBSession): Promise<boolean>{
|
|
115
117
|
if(!session) return false;
|
|
116
118
|
if(!session.userId || !session.userIdHash || !session.sessionHash || !session.csrfToken) return false;
|
|
@@ -118,8 +120,28 @@ export default class Lambder {
|
|
|
118
120
|
if(session.expiresTimeStamp > Date.now()) return false;
|
|
119
121
|
// Check DDB;
|
|
120
122
|
return false;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
private async handleNoMatchedAction(renderContext: RenderContext, resolver: Resolver){
|
|
126
|
+
for(const hook of this._hookList["fallback"]){
|
|
127
|
+
await hook.hookFn(renderContext, resolver);
|
|
128
|
+
}
|
|
129
|
+
if(renderContext.url === this.apiPath){
|
|
130
|
+
if(this.apiFallbackHandler){
|
|
131
|
+
resolver.resolve(await this.apiFallbackHandler(renderContext, resolver));
|
|
132
|
+
}else{
|
|
133
|
+
resolver.resolve({ statusCode: 204, body: "API handler not set.", })
|
|
134
|
+
}
|
|
135
|
+
}else if(this.routeFallbackHandler){
|
|
136
|
+
resolver.resolve(await this.routeFallbackHandler(renderContext, resolver));
|
|
137
|
+
}else{
|
|
138
|
+
resolver.resolve({ statusCode: 204, body: "Handler not set.", })
|
|
139
|
+
}
|
|
121
140
|
}
|
|
122
141
|
|
|
142
|
+
async addModule(moduleFn: Function): Promise<void>{
|
|
143
|
+
await moduleFn(this);
|
|
144
|
+
}
|
|
123
145
|
addRoute(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
124
146
|
this._actionList.push({
|
|
125
147
|
conditionFn: async (renderContext:RenderContext) => (
|
|
@@ -132,7 +154,7 @@ export default class Lambder {
|
|
|
132
154
|
),
|
|
133
155
|
actionFn: async (renderContext:RenderContext, resolver: Resolver) => await actionFn(renderContext, resolver)
|
|
134
156
|
});
|
|
135
|
-
}
|
|
157
|
+
};
|
|
136
158
|
|
|
137
159
|
addSessionRoute(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
138
160
|
this._actionList.push({
|
|
@@ -146,7 +168,7 @@ export default class Lambder {
|
|
|
146
168
|
),
|
|
147
169
|
actionFn: async (renderContext:RenderContext, resolver: Resolver) => await actionFn(renderContext, resolver)
|
|
148
170
|
});
|
|
149
|
-
}
|
|
171
|
+
};
|
|
150
172
|
|
|
151
173
|
addApi(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
152
174
|
this._actionList.push({
|
|
@@ -160,7 +182,7 @@ export default class Lambder {
|
|
|
160
182
|
),
|
|
161
183
|
actionFn: async (renderContext:RenderContext, resolver: Resolver) => await actionFn(renderContext, resolver)
|
|
162
184
|
});
|
|
163
|
-
}
|
|
185
|
+
};
|
|
164
186
|
|
|
165
187
|
addSessionApi(condition: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
166
188
|
this._actionList.push({
|
|
@@ -178,7 +200,7 @@ export default class Lambder {
|
|
|
178
200
|
return await actionFn(renderContext, resolver);
|
|
179
201
|
}
|
|
180
202
|
});
|
|
181
|
-
}
|
|
203
|
+
};
|
|
182
204
|
|
|
183
205
|
|
|
184
206
|
async addHook(hookEvent: 'created', hookFn: HookCreatedFunction, priority?: number): Promise<void>;
|
|
@@ -199,6 +221,7 @@ export default class Lambder {
|
|
|
199
221
|
}
|
|
200
222
|
}
|
|
201
223
|
|
|
224
|
+
|
|
202
225
|
async render(
|
|
203
226
|
event: APIGatewayProxyEvent,
|
|
204
227
|
lambdaContext: Context
|
|
@@ -217,8 +240,8 @@ export default class Lambder {
|
|
|
217
240
|
if(renderContext.method === "OPTIONS") return resolver.cors();
|
|
218
241
|
|
|
219
242
|
const firstMatchedAction = await (async () => {
|
|
220
|
-
for
|
|
221
|
-
if
|
|
243
|
+
for(const action of this._actionList){
|
|
244
|
+
if(await action.conditionFn(renderContext)) return action;
|
|
222
245
|
}
|
|
223
246
|
})();
|
|
224
247
|
|
|
@@ -232,15 +255,7 @@ export default class Lambder {
|
|
|
232
255
|
}
|
|
233
256
|
resolve(renderResult);
|
|
234
257
|
}else{
|
|
235
|
-
|
|
236
|
-
await hook.hookFn(renderContext, resolver);
|
|
237
|
-
}
|
|
238
|
-
if(this.fallbackHandler){
|
|
239
|
-
const renderResult = await this.fallbackHandler(renderContext, resolver);
|
|
240
|
-
resolve(renderResult);
|
|
241
|
-
}else{
|
|
242
|
-
resolve({ statusCode: 204, body: "Fallback handler not set.", })
|
|
243
|
-
}
|
|
258
|
+
return this.handleNoMatchedAction(renderContext, resolver);
|
|
244
259
|
}
|
|
245
260
|
}).then(async (renderResult: ResolverResponse):Promise<ResolverResponse> => {
|
|
246
261
|
for(const hook of this._hookList["completed"]){
|