lambder 2.0.18 → 3.0.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.
Files changed (67) hide show
  1. package/Readme.md +162 -41
  2. package/dist/Lambder.d.ts +154 -46
  3. package/dist/Lambder.js +312 -166
  4. package/dist/LambderCaller.js +6 -3
  5. package/dist/LambderContext.d.ts +20 -9
  6. package/dist/LambderContext.js +57 -17
  7. package/dist/LambderCors.d.ts +12 -0
  8. package/dist/LambderCors.js +30 -0
  9. package/dist/LambderHtml.d.ts +33 -0
  10. package/dist/LambderHtml.js +62 -0
  11. package/dist/LambderMSW.d.ts +16 -1
  12. package/dist/LambderMSW.js +5 -9
  13. package/dist/LambderPublicFiles.d.ts +47 -0
  14. package/dist/LambderPublicFiles.js +108 -0
  15. package/dist/LambderResolver.d.ts +30 -31
  16. package/dist/LambderResolver.js +29 -43
  17. package/dist/LambderResponse.d.ts +71 -0
  18. package/dist/LambderResponse.js +196 -0
  19. package/dist/LambderResponseBuilder.d.ts +58 -33
  20. package/dist/LambderResponseBuilder.js +114 -167
  21. package/dist/LambderRouting.d.ts +23 -0
  22. package/dist/LambderRouting.js +67 -0
  23. package/dist/LambderSessionController.d.ts +13 -1
  24. package/dist/LambderSessionController.js +33 -10
  25. package/dist/LambderSessionManager.d.ts +3 -1
  26. package/dist/LambderSessionManager.js +15 -6
  27. package/dist/LambderTemplatingEngine.d.ts +87 -0
  28. package/dist/LambderTemplatingEngine.js +156 -0
  29. package/dist/index.d.ts +14 -2
  30. package/dist/index.js +10 -1
  31. package/dist/node-polyfills.d.ts +4 -2
  32. package/dist/node-polyfills.js +28 -0
  33. package/package.json +7 -5
  34. package/.eslintrc.cjs +0 -26
  35. package/.vscode/settings.json +0 -26
  36. package/deploy +0 -22
  37. package/dist/LambderUtils.d.ts +0 -10
  38. package/dist/LambderUtils.js +0 -70
  39. package/docs/DYNAMODB_SETUP.md +0 -96
  40. package/docs/LAMBDER_MSW.md +0 -409
  41. package/docs/TYPE_SAFE_QUICK_START.md +0 -77
  42. package/examples/msw-testing-example.ts +0 -280
  43. package/examples/secure-session-example.ts +0 -207
  44. package/examples/zod-chained-api-example.ts +0 -63
  45. package/src/Lambder.ts +0 -430
  46. package/src/LambderApiContract.ts +0 -20
  47. package/src/LambderCaller.ts +0 -238
  48. package/src/LambderContext.ts +0 -78
  49. package/src/LambderMSW.ts +0 -180
  50. package/src/LambderResolver.ts +0 -101
  51. package/src/LambderResponseBuilder.ts +0 -332
  52. package/src/LambderSessionController.ts +0 -114
  53. package/src/LambderSessionManager.ts +0 -217
  54. package/src/LambderUtils.ts +0 -75
  55. package/src/index.ts +0 -17
  56. package/src/node-polyfills.ts +0 -27
  57. package/tests/error-handling.test.ts +0 -585
  58. package/tests/file-serving.test.ts +0 -194
  59. package/tests/fixtures/public/index.html +0 -1
  60. package/tests/fixtures/public/main.css +0 -1
  61. package/tests/hooks.test.ts +0 -561
  62. package/tests/output-type-runtime.test.ts +0 -381
  63. package/tests/redirect.test.ts +0 -88
  64. package/tests/routes.test.ts +0 -543
  65. package/tests/session.test.ts +0 -1083
  66. package/tests/use-plugin.test.ts +0 -460
  67. package/tsconfig.json +0 -24
@@ -1,78 +0,0 @@
1
- import cookieParser from "cookie";
2
- import type { APIGatewayProxyEvent, APIGatewayProxyEventHeaders, Context } from "aws-lambda";
3
- import type { LambderSessionContext } from "./LambderSessionManager.js";
4
-
5
- export type LambderRenderContext<TApiPayload = any> = {
6
- host: string;
7
- path: string;
8
- pathParams: Record<string, any> | null;
9
- method: string;
10
- get: Record<string, any>;
11
- post: Record<string, any>;
12
- cookie: Record<string, any>;
13
- session: null;
14
- apiName: string;
15
- apiPayload: TApiPayload;
16
- headers: APIGatewayProxyEventHeaders;
17
- event: APIGatewayProxyEvent;
18
- lambdaContext: Context;
19
- _otherInternal: {
20
- isApiCall: boolean,
21
- requestVersion: string|null;
22
- setHeaderFnAccumulator: { key:string, value:string|string[] }[];
23
- addHeaderFnAccumulator: { key:string, value:string }[];
24
- logToApiResponseAccumulator: any[];
25
- };
26
- };
27
-
28
- export type LambderSessionRenderContext<
29
- TApiPayload = any, SessionData = any
30
- > = Omit<LambderRenderContext<TApiPayload>, 'session'> & { session: LambderSessionContext<SessionData> };
31
-
32
- export const createContext = (
33
- event: APIGatewayProxyEvent,
34
- lambdaContext: Context,
35
- apiPath: string,
36
- ):LambderRenderContext<any> => {
37
- const host = event.headers.Host || event.headers.host || "";
38
- const path = event.path;
39
- const pathParams = null;
40
- const get: Record<string, any> = event.queryStringParameters || {};
41
- const method = event.httpMethod;
42
- const cookie = cookieParser.parse(event.headers.Cookie || event.headers.cookie || "");
43
- const headers = event.headers;
44
-
45
- // Decode body for the post
46
- let post: Record<string, any> = {};
47
- try {
48
- const decodedBody = event.isBase64Encoded ? ( event.body ? Buffer.from(event.body,"base64").toString() : "{}" ) : ( event.body || "{}" );
49
- try { post = JSON.parse(decodedBody) || {}; }
50
- catch(e){
51
- const params = new URLSearchParams(decodedBody);
52
- post = {};
53
- for(const [key, value] of params.entries()){
54
- post[key] = value;
55
- }
56
- }
57
- }catch(e){}
58
- // Parse api variables
59
-
60
- const isApiCall = method === "POST" && apiPath && path === apiPath && post.apiName;
61
- const apiName:string = isApiCall ? post.apiName : null;
62
- const apiPayload:string = isApiCall ? post.payload : null;
63
- const requestVersion:string = isApiCall ? post.version : null;
64
-
65
- return {
66
- host, path, pathParams, method,
67
- get, post, cookie, event,
68
- session: null,
69
- apiName, apiPayload,
70
- headers, lambdaContext,
71
- _otherInternal: {
72
- isApiCall, requestVersion,
73
- setHeaderFnAccumulator: [],
74
- addHeaderFnAccumulator: [],
75
- logToApiResponseAccumulator: [],
76
- }
77
- };
78
- }
package/src/LambderMSW.ts DELETED
@@ -1,180 +0,0 @@
1
- import type { ApiContractShape } from './LambderApiContract';
2
- import type { LambderApiResponse } from './LambderResponseBuilder';
3
-
4
- // MSW types - these will be resolved at runtime when msw is installed
5
- type RequestHandler = any;
6
-
7
- type MockApiOptions = {
8
- versionExpired?: boolean;
9
- sessionExpired?: boolean;
10
- notAuthorized?: boolean;
11
- message?: any;
12
- errorMessage?: any;
13
- logList?: any[];
14
- delay?: number; // Add artificial delay to simulate network latency
15
- };
16
-
17
- // Extended response type that includes apiVersion
18
- type MockApiResponse<T> = {
19
- apiVersion?: string;
20
- } & LambderApiResponse<T>;
21
-
22
- export default class LambderMSW<TContract extends ApiContractShape = any> {
23
- private apiPath: string;
24
- private apiVersion?: string;
25
- private http: any;
26
- private HttpResponse: any;
27
-
28
- constructor({
29
- apiPath,
30
- apiVersion,
31
- }: {
32
- apiPath: string;
33
- apiVersion?: string;
34
- }) {
35
- this.apiPath = apiPath;
36
- this.apiVersion = apiVersion;
37
-
38
- // Dynamically import MSW - it needs to be installed by the user
39
- try {
40
- const msw = require('msw');
41
- this.http = msw.http;
42
- this.HttpResponse = msw.HttpResponse;
43
- } catch (err) {
44
- throw new Error('MSW (Mock Service Worker) is required. Install it with: npm install msw --save-dev');
45
- }
46
- }
47
-
48
- /**
49
- * Mock an API endpoint with MSW
50
- * @param apiName - The name of the API to mock
51
- * @param handler - Function that returns the mock payload
52
- * @param options - Additional response options (session expired, version expired, etc.)
53
- *
54
- * Note: TypeScript will check that the return type is assignable to the output type,
55
- * but due to structural typing, extra properties are allowed. Enable strict checks
56
- * in your tsconfig.json with "noUncheckedIndexedAccess" and "exactOptionalPropertyTypes"
57
- * for better type safety.
58
- */
59
- mockApi<TApiName extends keyof TContract>(
60
- apiName: TApiName,
61
- handler: (payload?: TContract[TApiName]['input']) => Promise<TContract[TApiName]['output']> | TContract[TApiName]['output'],
62
- options?: MockApiOptions
63
- ): RequestHandler {
64
- return this.http.post(this.apiPath, async ({ request }: any) => {
65
- let body: any;
66
-
67
- try {
68
- const clonedRequest = request.clone();
69
- body = await clonedRequest.json();
70
- } catch (parseErr) {
71
- // If JSON parsing fails, return undefined to let other handlers try
72
- console.warn("LambderMSW: Failed to parse request body as JSON");
73
- return;
74
- }
75
-
76
- // Check if body is valid and has apiName
77
- if (!body || typeof body.apiName !== 'string') { return; }
78
- if (body.apiName !== apiName) { return; }
79
-
80
- try {
81
- // Add artificial delay if specified
82
- if (options?.delay) {
83
- await new Promise(resolve => setTimeout(resolve, options.delay));
84
- }
85
-
86
- // Call the handler with the payload from the request
87
- const payload = await handler(body.payload as TContract[TApiName]['input']);
88
-
89
- const response: MockApiResponse<TContract[TApiName]['output']> = {
90
- apiVersion: this.apiVersion,
91
- payload,
92
- ...(options?.versionExpired ? { versionExpired: options.versionExpired } : {}),
93
- ...(options?.sessionExpired ? { sessionExpired: options.sessionExpired } : {}),
94
- ...(options?.notAuthorized ? { notAuthorized: options.notAuthorized } : {}),
95
- ...(options?.message ? { message: options.message } : {}),
96
- ...(options?.errorMessage ? { errorMessage: options.errorMessage } : {}),
97
- ...(options?.logList?.length ? { logList: options.logList } : {}),
98
- };
99
-
100
- return this.HttpResponse.json(response);
101
- } catch (err: any) {
102
- // Only handle errors that occur during handler execution for matched APIs
103
- console.error("Error in LambderMSW handler for", apiName, ":", err);
104
-
105
- const errorResponse: MockApiResponse<null> = {
106
- apiVersion: this.apiVersion,
107
- payload: null,
108
- errorMessage: err.message || "Unknown error",
109
- };
110
-
111
- return this.HttpResponse.json(errorResponse, { status: 500 });
112
- }
113
- });
114
- }
115
-
116
- /**
117
- * Mock an API endpoint that returns a session expired error
118
- */
119
- mockSessionExpired<TApiName extends keyof TContract>(
120
- apiName: TApiName
121
- ): RequestHandler {
122
- return this.mockApi(
123
- apiName,
124
- async () => null as any,
125
- { sessionExpired: true }
126
- );
127
- }
128
-
129
- /**
130
- * Mock an API endpoint that returns a version expired error
131
- */
132
- mockVersionExpired<TApiName extends keyof TContract>(
133
- apiName: TApiName
134
- ): RequestHandler {
135
- return this.mockApi(
136
- apiName,
137
- async () => null as any,
138
- { versionExpired: true }
139
- );
140
- }
141
-
142
- /**
143
- * Mock an API endpoint that returns a not authorized error
144
- */
145
- mockNotAuthorized<TApiName extends keyof TContract>(
146
- apiName: TApiName
147
- ): RequestHandler {
148
- return this.mockApi(
149
- apiName,
150
- async () => null as any,
151
- { notAuthorized: true }
152
- );
153
- }
154
-
155
- /**
156
- * Mock an API endpoint that returns an error message
157
- */
158
- mockError<TApiName extends keyof TContract>(
159
- apiName: TApiName,
160
- errorMessage: string
161
- ): RequestHandler {
162
- return this.mockApi(
163
- apiName,
164
- async () => null as any,
165
- { errorMessage }
166
- );
167
- }
168
-
169
- /**
170
- * Mock an API endpoint with a custom message
171
- */
172
- mockWithMessage<TApiName extends keyof TContract>(
173
- apiName: TApiName,
174
- handler: (payload?: TContract[TApiName]['input']) => Promise<TContract[TApiName]['output']> | TContract[TApiName]['output'],
175
- message: any
176
- ): RequestHandler {
177
- return this.mockApi(apiName, handler, { message });
178
- }
179
- }
180
-
@@ -1,101 +0,0 @@
1
- import type { LambderRenderContext } from "./LambderContext.js";
2
- import LambderResponseBuilder, { LambderResolverResponse } from "./LambderResponseBuilder.js";
3
- import LambderUtils from "./LambderUtils.js";
4
-
5
- type MethodType<T, M extends keyof T> = T[M] extends (...args: any[]) => any ? T[M] : never;
6
-
7
- interface DieResolverMethods<TOutput> {
8
- raw: MethodType<LambderResponseBuilder, 'raw'>;
9
- json: MethodType<LambderResponseBuilder, 'json'>;
10
- xml: MethodType<LambderResponseBuilder, 'xml'>;
11
- html: MethodType<LambderResponseBuilder, 'html'>;
12
- redirect: MethodType<LambderResponseBuilder, 'redirect'>;
13
- status404: MethodType<LambderResponseBuilder, 'status404'>;
14
- cors: MethodType<LambderResponseBuilder, 'cors'>;
15
- fileBase64: MethodType<LambderResponseBuilder, 'fileBase64'>;
16
- file: MethodType<LambderResponseBuilder, 'file'>;
17
- ejsFile: MethodType<LambderResponseBuilder, 'ejsFile'>;
18
- ejsTemplate: MethodType<LambderResponseBuilder, 'ejsTemplate'>;
19
- api: (
20
- payload: TOutput | null,
21
- config?: Parameters<LambderResponseBuilder['api']>[1],
22
- headers?: Parameters<LambderResponseBuilder['api']>[2]
23
- ) => LambderResolverResponse;
24
- }
25
-
26
- export default class LambderResolver<TOutput = any> extends LambderResponseBuilder<TOutput> {
27
- public resolve: (response: LambderResolverResponse) => void;
28
- public reject: (err: Error) => void;
29
- public die: DieResolverMethods<TOutput>;
30
-
31
- constructor(
32
- { isCorsEnabled, publicPath, apiVersion, lambderUtils, ctx, resolve, reject }:
33
- {
34
- isCorsEnabled: boolean,
35
- publicPath: string,
36
- apiVersion?: string|null,
37
- lambderUtils: LambderUtils,
38
- ctx: LambderRenderContext<any>,
39
- resolve: (response: LambderResolverResponse) => void,
40
- reject: (err: Error) => void,
41
- }
42
- ){
43
- super({ isCorsEnabled, publicPath, apiVersion, lambderUtils, ctx, });
44
- this.resolve = resolve;
45
- this.reject = reject;
46
-
47
- this.die = {
48
- raw: this.autoResolve(this.raw),
49
- json: this.autoResolve(this.json),
50
- xml: this.autoResolve(this.xml),
51
- html: this.autoResolve(this.html),
52
- redirect: this.autoResolve(this.redirect),
53
- status404: this.autoResolve(this.status404),
54
- cors: this.autoResolve(this.cors),
55
- fileBase64: this.autoResolve(this.fileBase64),
56
- file: this.autoResolvePromise(this.file),
57
- ejsFile: this.autoResolvePromise(this.ejsFile),
58
- ejsTemplate: this.autoResolvePromise(this.ejsTemplate),
59
- api: this.autoResolve(this.api),
60
- };
61
- }
62
-
63
- // Override api method with proper typing
64
- api(
65
- payload: TOutput | null,
66
- config?: Parameters<LambderResponseBuilder['api']>[1],
67
- headers?: Parameters<LambderResponseBuilder['api']>[2]
68
- ): LambderResolverResponse {
69
- return super.api(payload, config, headers);
70
- }
71
-
72
-
73
- private autoResolve<
74
- T extends (...args: any[]) => LambderResolverResponse
75
- >(method: T): (...funcArgs: Parameters<T>) => LambderResolverResponse {
76
- return (...args: Parameters<T>): LambderResolverResponse => {
77
- const result: LambderResolverResponse = method.apply(this, args);
78
- this.resolve(result);
79
- return result;
80
- };
81
- }
82
-
83
- private autoResolvePromise<
84
- T extends (...args: any[]) => Promise<LambderResolverResponse>
85
- >(method: T): (...funcArgs: Parameters<T>) => Promise<LambderResolverResponse> {
86
- return (...args: Parameters<T>): Promise<LambderResolverResponse> => {
87
- return new Promise<LambderResolverResponse>((resolve, reject) => {
88
- method.apply(this, args)
89
- .then(result => {
90
- this.resolve(result);
91
- resolve(result);
92
- })
93
- .catch(err => {
94
- this.reject(err);
95
- reject(err);
96
- });
97
- });
98
- };
99
- }
100
-
101
- };
@@ -1,332 +0,0 @@
1
- import mimeTypeResolver from "mime-types";
2
- import LambderUtils from "./LambderUtils.js";
3
- import { LambderRenderContext } from "./LambderContext.js";
4
- import { getFS, getPath } from "./node-polyfills.js";
5
-
6
- const convertToMultiHeader = (
7
- headers: Record<string, string|string[]> | undefined
8
- ):Record<string, string[]> =>
9
- Object.fromEntries(Object.entries(headers||{}).map(([k,v])=>[ k, Array.isArray(v) ? v : [v] ]));
10
-
11
- const CORS_HEADERS = convertToMultiHeader({
12
- "Access-Control-Allow-Credentials": "true",
13
- "Access-Control-Allow-Headers" : "Origin, X-Requested-With, Content-Type, Accept",
14
- "Access-Control-Allow-Origin": "*",
15
- "Access-Control-Allow-Methods": "OPTIONS,POST",
16
- });
17
-
18
- export type HttpStatusCode =
19
- | 200 | 201 | 202 | 204
20
- | 300 | 301 | 302 | 303 | 304 | 307 | 308
21
- | 400 | 401 | 403 | 404 | 405 | 409 | 410 | 413 | 415 | 422 | 429
22
- | 500 | 501 | 502 | 503 | 504;
23
-
24
- export type LambderResolverResponse = {
25
- statusCode: HttpStatusCode,
26
- multiValueHeaders?: Record<string, string[]>,
27
- body: string | null,
28
- isBase64Encoded?: boolean,
29
- };
30
-
31
- export type LambderApiResponseConfig = {
32
- versionExpired?: boolean;
33
- sessionExpired?: boolean;
34
- notAuthorized?: boolean;
35
- message?: any;
36
- errorMessage?: any;
37
- logList?: any[];
38
- }
39
-
40
-
41
- export type LambderApiResponse<T> = LambderApiResponseConfig & {
42
- payload?: T | null;
43
- }
44
-
45
- export default class LambderResponseBuilder<TResponse = any> {
46
- private isCorsEnabled: boolean;
47
- private publicPath: string;
48
- private apiVersion: string|null;
49
- private lambderUtils: LambderUtils;
50
- private ctx?: LambderRenderContext<any>;
51
-
52
- constructor(
53
- { isCorsEnabled, publicPath, apiVersion, lambderUtils, ctx }:
54
- {
55
- isCorsEnabled: boolean,
56
- publicPath: string,
57
- apiVersion?: string|null,
58
- lambderUtils: LambderUtils,
59
- ctx?: LambderRenderContext<any>,
60
- }
61
- ){
62
- this.isCorsEnabled = isCorsEnabled;
63
- this.publicPath = publicPath;
64
- this.apiVersion = apiVersion ?? null;
65
- this.lambderUtils = lambderUtils;
66
- this.ctx = ctx;
67
- };
68
-
69
- private async readPublicFileSync(filePath: string){
70
- const fs = await getFS();
71
- const path = await getPath();
72
-
73
- if (!fs || !path) {
74
- throw new Error("File system operations are not available in browser environment");
75
- }
76
-
77
- const publicPath = path.resolve(this.publicPath);
78
- const normalizedFilePath = filePath.startsWith('/') ? filePath.slice(1) : filePath;
79
- const absolutePath = path.resolve(publicPath, normalizedFilePath);
80
- if(!absolutePath.startsWith(publicPath)){ return "forbidden-public-path"; }
81
- return await fs.promises.readFile(absolutePath);
82
- };
83
-
84
- private async checkPublicFileExist(filePath: string){
85
- const fs = await getFS();
86
- const path = await getPath();
87
-
88
- if (!fs || !path) { return false; }
89
-
90
- const publicPath = path.resolve(this.publicPath);
91
- const normalizedFilePath = filePath.startsWith('/') ? filePath.slice(1) : filePath;
92
- const absolutePath = path.resolve(publicPath, normalizedFilePath);
93
- if(!absolutePath.startsWith(publicPath)){ return false; }
94
- try {
95
- const stat = await fs.promises.stat(absolutePath);
96
- return stat.isFile();
97
- } catch {
98
- return false;
99
- }
100
- };
101
-
102
- addHeader(key: string, value: string){
103
- if(!this.ctx) throw new Error(".addHeader function is not available within this hook");
104
- else{
105
- this.ctx._otherInternal.addHeaderFnAccumulator.push({ key, value });
106
- }
107
- };
108
-
109
- setHeader(key: string, value: string|string[]){
110
- if(!this.ctx) throw new Error(".setHeader function is not available within this hook");
111
- else{
112
- this.ctx._otherInternal.addHeaderFnAccumulator = this.ctx._otherInternal.addHeaderFnAccumulator
113
- .filter((header: { key: string, value: string }) => header.key !== key);
114
- this.ctx._otherInternal.setHeaderFnAccumulator.push({ key, value });
115
- }
116
- };
117
-
118
- logToApiResponse(input:any){
119
- if(!this.ctx) throw new Error(".logToResponse function is not available within this hook");
120
- else{
121
- this.ctx._otherInternal.logToApiResponseAccumulator.push(input);
122
- }
123
- };
124
-
125
-
126
- raw(param: LambderResolverResponse){
127
- return param;
128
- };
129
-
130
- json(
131
- data: Record<string, any>,
132
- headers?: Record<string, string|string[]>
133
- ):LambderResolverResponse{
134
- return this.raw({
135
- statusCode: 200,
136
- multiValueHeaders: {
137
- "Content-Type": ["application/json; charset=utf-8"],
138
- ...(this.isCorsEnabled ? CORS_HEADERS : {}),
139
- ...convertToMultiHeader(headers)
140
- },
141
- body: JSON.stringify(data),
142
- });
143
- }
144
-
145
- xml(data: string):LambderResolverResponse{
146
- return this.raw({
147
- statusCode: 200,
148
- isBase64Encoded: true,
149
- multiValueHeaders: { "Content-Type": ["application/xml; charset=utf-8"]},
150
- body: Buffer.from(data).toString("base64"),
151
- });
152
- };
153
-
154
- html(
155
- data: string,
156
- headers?: Record<string, string|string[]>,
157
- ):LambderResolverResponse{
158
- return this.raw({
159
- statusCode: 200,
160
- isBase64Encoded: true,
161
- multiValueHeaders: {"Content-Type": ["text/html; charset=utf-8"], ...convertToMultiHeader(headers)},
162
- body: Buffer.from(data).toString("base64"),
163
- });
164
- };
165
-
166
- redirect(
167
- url: string,
168
- statusCode: HttpStatusCode = 302,
169
- headers?: Record<string, string|string[]>,
170
- ):LambderResolverResponse{
171
- return this.raw({
172
- statusCode: statusCode,
173
- multiValueHeaders: { "Location" : [url], ...convertToMultiHeader(headers) },
174
- body:null
175
- });
176
- };
177
-
178
- status404(
179
- data: string,
180
- headers?: Record<string, string|string[]>,
181
- ):LambderResolverResponse{
182
- return this.raw({
183
- statusCode: 404,
184
- isBase64Encoded: true,
185
- multiValueHeaders: {"Content-Type": ["text/html; charset=utf-8"], ...convertToMultiHeader(headers)},
186
- body: Buffer.from(data).toString("base64"),
187
- });
188
- };
189
-
190
- versionExpired(
191
- headers?: Record<string, string|string[]>,
192
- ):LambderResolverResponse{
193
- return this.api(null, { versionExpired: true }, headers);
194
- };
195
-
196
- cors():LambderResolverResponse{
197
- return this.raw({
198
- statusCode: 200,
199
- multiValueHeaders: this.isCorsEnabled ? CORS_HEADERS: {},
200
- body: JSON.stringify(""),
201
- });
202
- };
203
-
204
- fileBase64 (
205
- fileBase64: string,
206
- mimeType: string,
207
- headers?: Record<string, string|string[]>,
208
- ):LambderResolverResponse{
209
- return this.raw({
210
- statusCode: 200,
211
- isBase64Encoded: true,
212
- multiValueHeaders: { "Content-Type": [mimeType || "application/octet-stream"], ...convertToMultiHeader(headers) },
213
- body: fileBase64,
214
- });
215
- };
216
-
217
- async file(
218
- filePath: string,
219
- headers?: Record<string, string|string[]>,
220
- fallbackFilePath?: string,
221
- ):Promise<LambderResolverResponse>{
222
- const doesFileExist = await this.checkPublicFileExist(filePath);
223
- if(!doesFileExist){
224
- if(fallbackFilePath){
225
- const doesFallbackExist = await this.checkPublicFileExist(fallbackFilePath);
226
- if(doesFallbackExist){
227
- return await this.file(fallbackFilePath, headers);
228
- }
229
- }
230
- return this.json({ error: "File not found: " + filePath })
231
- }
232
- const mimeType = mimeTypeResolver.lookup(filePath);
233
- const body = await this.readPublicFileSync(filePath);
234
- if (body === "forbidden-public-path") {
235
- throw new Error("Forbidden public path: " + filePath);
236
- }
237
- const bodyBuffer: Buffer = Buffer.isBuffer(body) ? body : Buffer.from(body);
238
- const bodyBase64 = bodyBuffer.toString("base64");
239
- console.log("bodyBase64.length",bodyBase64.length);
240
- return this.fileBase64(bodyBase64, mimeType || "application/octet-stream", headers);
241
- };
242
-
243
- async ejsTemplate(
244
- template: string,
245
- pageData: Record<string, any>,
246
- headers?: Record<string, string|string[]>,
247
- ):Promise<LambderResolverResponse>{
248
- const renderedResult = await this.lambderUtils.renderEjs(template, pageData);
249
-
250
- return this.raw({
251
- statusCode: 200,
252
- isBase64Encoded: true,
253
- multiValueHeaders: {"Content-Type": ["text/html; charset=utf-8"], ...convertToMultiHeader(headers)},
254
- body: Buffer.from(renderedResult).toString("base64"),
255
- });
256
- };
257
-
258
- async ejsFile(
259
- filePath: string,
260
- pageData: Record<string, any>,
261
- headers?: Record<string, string|string[]>,
262
- ):Promise<LambderResolverResponse>{
263
- const renderedResult = await this.lambderUtils.renderEjsFile(filePath, pageData);
264
-
265
- return this.raw({
266
- statusCode: 200,
267
- isBase64Encoded: true,
268
- multiValueHeaders: {"Content-Type": ["text/html; charset=utf-8"], ...convertToMultiHeader(headers)},
269
- body: Buffer.from(renderedResult).toString("base64"),
270
- });
271
- };
272
-
273
- api(
274
- payload: TResponse | null,
275
- {
276
- versionExpired, sessionExpired, notAuthorized,
277
- message, errorMessage, logList,
278
- }: LambderApiResponseConfig = {
279
- versionExpired: undefined, sessionExpired: undefined, notAuthorized: undefined,
280
- message: null, errorMessage: null, logList: undefined
281
- },
282
- headers?: Record<string, string|string[]>,
283
- ): LambderResolverResponse {
284
- const finalLogList = logList || this.ctx?._otherInternal?.logToApiResponseAccumulator;
285
- return this.json({
286
- apiVersion: this.apiVersion,
287
- payload,
288
- ...(versionExpired ? {versionExpired} : {}),
289
- ...(sessionExpired ? {sessionExpired} : {}),
290
- ...(notAuthorized ? {notAuthorized} : {}),
291
- ...(message ? {message} : {}),
292
- ...(errorMessage ? {errorMessage} : {}),
293
- ...(finalLogList?.length ? {logList: finalLogList} : {}),
294
- }, headers);
295
- };
296
-
297
- apiBinary<T=any>(
298
- payload: T | null,
299
- {
300
- versionExpired, sessionExpired, notAuthorized,
301
- message, errorMessage, logList,
302
- }: LambderApiResponseConfig = {
303
- versionExpired: undefined, sessionExpired: undefined, notAuthorized: undefined,
304
- message: null, errorMessage: null, logList: undefined
305
- },
306
- headers?: Record<string, string|string[]>,
307
- ): LambderResolverResponse {
308
- const finalLogList = logList || this.ctx?._otherInternal?.logToApiResponseAccumulator;
309
- const result = {
310
- apiVersion: this.apiVersion,
311
- payload,
312
- ...(versionExpired ? {versionExpired} : {}),
313
- ...(sessionExpired ? {sessionExpired} : {}),
314
- ...(notAuthorized ? {notAuthorized} : {}),
315
- ...(message ? {message} : {}),
316
- ...(errorMessage ? {errorMessage} : {}),
317
- ...(finalLogList?.length ? {logList: finalLogList} : {}),
318
- };
319
-
320
- return this.raw({
321
- statusCode: 200,
322
- isBase64Encoded: true,
323
- multiValueHeaders: {
324
- "Content-Type": ["application/lambder-json-stream"],
325
- "Content-Encoding": ["gzip"],
326
- ...convertToMultiHeader(headers)
327
- },
328
- body: Buffer.from(JSON.stringify(result)).toString("base64"),
329
- });
330
- };
331
-
332
- };