lambder 1.0.130 → 1.0.132

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 CHANGED
@@ -5,11 +5,9 @@ Lambder is a highly opinionated dynamic serverless framework designed to facilit
5
5
  ## Features
6
6
 
7
7
  - **Simple API & Route Declaration**: Define your APIs and routes using concise and expressive syntax.
8
- - **Type-Safe API Contracts**: Full TypeScript type safety across frontend and backend with zero runtime overhead.
9
8
  - **Session Management**: Built-in session management to secure and personalize user experiences.
10
9
  - **Flexible Hooks System**: Employ hooks to execute code at different stages of the request lifecycle, enabling fine-grained control over the application flow.
11
10
  - **Error Handling**: Comprehensive error handling capabilities, including global error handlers and route-specific fallbacks.
12
- - **MSW Mocking Support**: Optional Mock Service Worker integration for easy API mocking in tests and development.
13
11
  - **Seamless Integration**: Designed to work effortlessly with AWS Lambda and API Gateway, providing a straightforward path to deploy serverless applications.
14
12
 
15
13
  ## Installation
@@ -546,53 +544,6 @@ const user = await caller.api('getUserById', { userId: '123' });
546
544
 
547
545
  📖 **[Read the Quick Start Guide](docs/TYPE_SAFE_QUICK_START.md)** for more details and examples!
548
546
 
549
- ## Testing & Mocking with MSW
550
-
551
- Lambder provides optional integration with [Mock Service Worker (MSW)](https://mswjs.io/) for easy API mocking in tests and development.
552
-
553
- ### Installation
554
-
555
- ```bash
556
- npm install --save-dev msw
557
- ```
558
-
559
- ### Quick Example
560
-
561
- ```typescript
562
- import { mockLambderApi } from 'lambder';
563
- import { setupServer } from 'msw/node';
564
- import type { MyApiContract } from './apiContract';
565
-
566
- // Create type-safe mock handlers
567
- const handlers = [
568
- mockLambderApi<MyApiContract, 'getUserById'>(
569
- 'getUserById',
570
- (input) => ({
571
- id: input.userId,
572
- name: 'Test User',
573
- email: 'test@example.com'
574
- })
575
- )
576
- ];
577
-
578
- // Set up MSW server
579
- const server = setupServer(...handlers);
580
-
581
- beforeAll(() => server.listen());
582
- afterEach(() => server.resetHandlers());
583
- afterAll(() => server.close());
584
- ```
585
-
586
- ### Benefits
587
-
588
- ✅ **Type-Safe Mocking** - Full TypeScript support for mock responses
589
- ✅ **Easy Setup** - Simple functions that match your API contract
590
- ✅ **Test Scenarios** - Mock errors, session expired, not authorized, etc.
591
- ✅ **Development Mode** - Use mocks while developing frontend features
592
- ✅ **Storybook Ready** - Perfect for component stories
593
-
594
- 📖 **[Read the MSW Mocking Guide](docs/MSW_MOCKING.md)** for detailed setup and examples!
595
-
596
547
  ## Contributing
597
548
 
598
549
  Contributions are welcome! Especially for documentation. If you have an idea for an improvement or have found a bug, please open an issue or submit a pull request.
@@ -1,104 +1,49 @@
1
- import type { ApiContractShape } from './LambderApiContract.js';
2
- export type HttpHandler = (info: any) => Promise<Response | undefined> | Response | undefined;
3
- /**
4
- * Creates a mock handler for a Lambder API endpoint
5
- *
6
- * @example
7
- * ```typescript
8
- * mockLambderApi('public.getInitialPageData', () => ({
9
- * userLocationData: { ... },
10
- * sessionUser: null
11
- * }))
12
- * ```
13
- *
14
- * @example with dynamic response based on input
15
- * ```typescript
16
- * mockLambderApi('user.updateProfile', (input) => ({
17
- * success: true,
18
- * userId: input.userId
19
- * }))
20
- * ```
21
- */
22
- export declare function mockLambderApi<TContract extends ApiContractShape, TApiName extends keyof TContract & string>(apiName: TApiName, responseFactory: (input: TContract[TApiName]['input']) => TContract[TApiName]['output'] | Promise<TContract[TApiName]['output']>, options?: {
23
- /** Custom API path, defaults to '/api' */
24
- apiPath?: string;
25
- /** Delay in ms before responding (for testing loading states) */
1
+ import type { ApiContractShape } from './LambderApiContract';
2
+ type RequestHandler = any;
3
+ type MockApiOptions = {
4
+ versionExpired?: boolean;
5
+ sessionExpired?: boolean;
6
+ notAuthorized?: boolean;
7
+ message?: any;
8
+ errorMessage?: any;
9
+ logList?: any[];
26
10
  delay?: number;
27
- /** Custom apiVersion to include in response */
28
- apiVersion?: string | null;
29
- }): HttpHandler;
30
- /**
31
- * Creates a mock handler that returns an error for a Lambder API endpoint
32
- *
33
- * @example
34
- * ```typescript
35
- * mockLambderApiError('user.deleteAccount', 'Account deletion failed')
36
- * ```
37
- */
38
- export declare function mockLambderApiError<TContract extends ApiContractShape, TApiName extends keyof TContract & string>(apiName: TApiName, errorMessage: string, options?: {
39
- /** Custom API path, defaults to '/api' */
40
- apiPath?: string;
41
- /** Delay in ms before responding */
42
- delay?: number;
43
- /** Custom apiVersion to include in response */
44
- apiVersion?: string | null;
45
- }): HttpHandler;
46
- /**
47
- * Creates a mock handler that simulates a session expired error
48
- *
49
- * @example
50
- * ```typescript
51
- * mockLambderSessionExpired('user.updateProfile')
52
- * ```
53
- */
54
- export declare function mockLambderSessionExpired<TContract extends ApiContractShape, TApiName extends keyof TContract & string>(apiName: TApiName, options?: {
55
- /** Custom API path, defaults to '/api' */
56
- apiPath?: string;
57
- /** Custom apiVersion to include in response */
58
- apiVersion?: string | null;
59
- }): HttpHandler;
60
- /**
61
- * Creates a mock handler that simulates a "not authorized" error
62
- *
63
- * @example
64
- * ```typescript
65
- * mockLambderNotAuthorized('admin.deleteUser')
66
- * ```
67
- */
68
- export declare function mockLambderNotAuthorized<TContract extends ApiContractShape, TApiName extends keyof TContract & string>(apiName: TApiName, options?: {
69
- /** Custom API path, defaults to '/api' */
70
- apiPath?: string;
71
- /** Custom apiVersion to include in response */
72
- apiVersion?: string | null;
73
- }): HttpHandler;
74
- /**
75
- * Creates a mock handler that simulates a version expired error
76
- *
77
- * @example
78
- * ```typescript
79
- * mockLambderVersionExpired('user.updateProfile')
80
- * ```
81
- */
82
- export declare function mockLambderVersionExpired<TContract extends ApiContractShape, TApiName extends keyof TContract & string>(apiName: TApiName, options?: {
83
- /** Custom API path, defaults to '/api' */
84
- apiPath?: string;
85
- /** Custom apiVersion to include in response */
86
- apiVersion?: string | null;
87
- }): HttpHandler;
88
- /**
89
- * Helper function to create the MSW http.post handler with proper path matching
90
- * This is useful when you need to set up multiple handlers with the same path
91
- *
92
- * @example
93
- * ```typescript
94
- * import { setupServer } from 'msw/node';
95
- *
96
- * const server = setupServer(
97
- * createLambderApiHandler('/api', [
98
- * mockLambderApi('user.getProfile', () => ({ name: 'Test User' })),
99
- * mockLambderApi('user.updateProfile', (input) => ({ success: true }))
100
- * ])
101
- * );
102
- * ```
103
- */
104
- export declare function createLambderApiHandler(apiPath: string, handlers: HttpHandler[]): HttpHandler;
11
+ };
12
+ export default class LambderMSW<TContract extends ApiContractShape = any> {
13
+ private apiPath;
14
+ private apiVersion?;
15
+ private http;
16
+ private HttpResponse;
17
+ constructor({ apiPath, apiVersion, }: {
18
+ apiPath: string;
19
+ apiVersion?: string;
20
+ });
21
+ /**
22
+ * Mock an API endpoint with MSW
23
+ * @param apiName - The name of the API to mock
24
+ * @param handler - Function that returns the mock payload
25
+ * @param options - Additional response options (session expired, version expired, etc.)
26
+ */
27
+ mockApi<TApiName extends keyof TContract & string, TOutput = TApiName extends keyof TContract ? TContract[TApiName]['output'] : any, TInput = TApiName extends keyof TContract ? TContract[TApiName]['input'] : any>(apiName: TApiName, handler: (payload?: TInput) => Promise<TOutput> | TOutput, options?: MockApiOptions): RequestHandler;
28
+ /**
29
+ * Mock an API endpoint that returns a session expired error
30
+ */
31
+ mockSessionExpired<TApiName extends keyof TContract & string>(apiName: TApiName): RequestHandler;
32
+ /**
33
+ * Mock an API endpoint that returns a version expired error
34
+ */
35
+ mockVersionExpired<TApiName extends keyof TContract & string>(apiName: TApiName): RequestHandler;
36
+ /**
37
+ * Mock an API endpoint that returns a not authorized error
38
+ */
39
+ mockNotAuthorized<TApiName extends keyof TContract & string>(apiName: TApiName): RequestHandler;
40
+ /**
41
+ * Mock an API endpoint that returns an error message
42
+ */
43
+ mockError<TApiName extends keyof TContract & string>(apiName: TApiName, errorMessage: string): RequestHandler;
44
+ /**
45
+ * Mock an API endpoint with a custom message
46
+ */
47
+ mockWithMessage<TApiName extends keyof TContract & string, TOutput = TApiName extends keyof TContract ? TContract[TApiName]['output'] : any, TInput = TApiName extends keyof TContract ? TContract[TApiName]['input'] : any>(apiName: TApiName, handler: (payload?: TInput) => Promise<TOutput> | TOutput, message: any): RequestHandler;
48
+ }
49
+ export {};
@@ -1,203 +1,95 @@
1
- // Dynamic imports to avoid requiring MSW as a hard dependency
2
- let http;
3
- let HttpResponse;
4
- /**
5
- * Initialize MSW dependencies dynamically
6
- * This allows the module to be imported even if MSW is not installed
7
- */
8
- async function ensureMSW() {
9
- if (!http || !HttpResponse) {
1
+ export default class LambderMSW {
2
+ apiPath;
3
+ apiVersion;
4
+ http;
5
+ HttpResponse;
6
+ constructor({ apiPath, apiVersion, }) {
7
+ this.apiPath = apiPath;
8
+ this.apiVersion = apiVersion;
9
+ // Dynamically import MSW - it needs to be installed by the user
10
10
  try {
11
- // @ts-ignore - MSW is an optional peer dependency
12
- const msw = await import('msw');
13
- http = msw.http;
14
- HttpResponse = msw.HttpResponse;
11
+ const msw = require('msw');
12
+ this.http = msw.http;
13
+ this.HttpResponse = msw.HttpResponse;
15
14
  }
16
15
  catch (err) {
17
- throw new Error('MSW is required to use Lambder mocking utilities. ' +
18
- 'Install it with: npm install --save-dev msw');
16
+ throw new Error('MSW (Mock Service Worker) is required. Install it with: npm install msw --save-dev');
19
17
  }
20
18
  }
21
- }
22
- /**
23
- * Creates a mock handler for a Lambder API endpoint
24
- *
25
- * @example
26
- * ```typescript
27
- * mockLambderApi('public.getInitialPageData', () => ({
28
- * userLocationData: { ... },
29
- * sessionUser: null
30
- * }))
31
- * ```
32
- *
33
- * @example with dynamic response based on input
34
- * ```typescript
35
- * mockLambderApi('user.updateProfile', (input) => ({
36
- * success: true,
37
- * userId: input.userId
38
- * }))
39
- * ```
40
- */
41
- export function mockLambderApi(apiName, responseFactory, options) {
42
- const apiPath = options?.apiPath ?? '/api';
43
- const delay = options?.delay ?? 0;
44
- const apiVersion = options?.apiVersion ?? null;
45
- // Return a handler that will be initialized when MSW is available
46
- return (async ({ request }) => {
47
- await ensureMSW();
48
- const body = (await request.json());
49
- // Only handle this specific API
50
- if (body.apiName !== apiName) {
51
- return;
52
- }
53
- console.log(`[MSW Lambder] Mocking ${apiName}`, body.payload);
54
- // Apply delay if specified
55
- if (delay > 0) {
56
- await new Promise((resolve) => setTimeout(resolve, delay));
57
- }
58
- // Generate the response
59
- const payload = await responseFactory(body.payload);
60
- // Return in Lambder API response format
61
- return HttpResponse.json({
62
- ...(apiVersion !== null ? { apiVersion } : {}),
63
- payload,
64
- });
65
- });
66
- }
67
- /**
68
- * Creates a mock handler that returns an error for a Lambder API endpoint
69
- *
70
- * @example
71
- * ```typescript
72
- * mockLambderApiError('user.deleteAccount', 'Account deletion failed')
73
- * ```
74
- */
75
- export function mockLambderApiError(apiName, errorMessage, options) {
76
- const apiPath = options?.apiPath ?? '/api';
77
- const delay = options?.delay ?? 0;
78
- const apiVersion = options?.apiVersion ?? null;
79
- return (async ({ request }) => {
80
- await ensureMSW();
81
- const body = (await request.json());
82
- if (body.apiName !== apiName) {
83
- return;
84
- }
85
- console.log(`[MSW Lambder] Mocking error for ${apiName}:`, errorMessage);
86
- if (delay > 0) {
87
- await new Promise((resolve) => setTimeout(resolve, delay));
88
- }
89
- return HttpResponse.json({
90
- ...(apiVersion !== null ? { apiVersion } : {}),
91
- payload: null,
92
- errorMessage,
93
- });
94
- });
95
- }
96
- /**
97
- * Creates a mock handler that simulates a session expired error
98
- *
99
- * @example
100
- * ```typescript
101
- * mockLambderSessionExpired('user.updateProfile')
102
- * ```
103
- */
104
- export function mockLambderSessionExpired(apiName, options) {
105
- const apiPath = options?.apiPath ?? '/api';
106
- const apiVersion = options?.apiVersion ?? null;
107
- return (async ({ request }) => {
108
- await ensureMSW();
109
- const body = (await request.json());
110
- if (body.apiName !== apiName) {
111
- return;
112
- }
113
- console.log(`[MSW Lambder] Mocking session expired for ${apiName}`);
114
- return HttpResponse.json({
115
- ...(apiVersion !== null ? { apiVersion } : {}),
116
- payload: null,
117
- sessionExpired: true,
118
- errorMessage: 'Session expired',
119
- });
120
- });
121
- }
122
- /**
123
- * Creates a mock handler that simulates a "not authorized" error
124
- *
125
- * @example
126
- * ```typescript
127
- * mockLambderNotAuthorized('admin.deleteUser')
128
- * ```
129
- */
130
- export function mockLambderNotAuthorized(apiName, options) {
131
- const apiPath = options?.apiPath ?? '/api';
132
- const apiVersion = options?.apiVersion ?? null;
133
- return (async ({ request }) => {
134
- await ensureMSW();
135
- const body = (await request.json());
136
- if (body.apiName !== apiName) {
137
- return;
138
- }
139
- console.log(`[MSW Lambder] Mocking not authorized for ${apiName}`);
140
- return HttpResponse.json({
141
- ...(apiVersion !== null ? { apiVersion } : {}),
142
- payload: null,
143
- notAuthorized: true,
144
- errorMessage: 'Not authorized',
145
- });
146
- });
147
- }
148
- /**
149
- * Creates a mock handler that simulates a version expired error
150
- *
151
- * @example
152
- * ```typescript
153
- * mockLambderVersionExpired('user.updateProfile')
154
- * ```
155
- */
156
- export function mockLambderVersionExpired(apiName, options) {
157
- const apiPath = options?.apiPath ?? '/api';
158
- const apiVersion = options?.apiVersion ?? null;
159
- return (async ({ request }) => {
160
- await ensureMSW();
161
- const body = (await request.json());
162
- if (body.apiName !== apiName) {
163
- return;
164
- }
165
- console.log(`[MSW Lambder] Mocking version expired for ${apiName}`);
166
- return HttpResponse.json({
167
- ...(apiVersion !== null ? { apiVersion } : {}),
168
- payload: null,
169
- versionExpired: true,
170
- errorMessage: 'Version expired',
171
- });
172
- });
173
- }
174
- /**
175
- * Helper function to create the MSW http.post handler with proper path matching
176
- * This is useful when you need to set up multiple handlers with the same path
177
- *
178
- * @example
179
- * ```typescript
180
- * import { setupServer } from 'msw/node';
181
- *
182
- * const server = setupServer(
183
- * createLambderApiHandler('/api', [
184
- * mockLambderApi('user.getProfile', () => ({ name: 'Test User' })),
185
- * mockLambderApi('user.updateProfile', (input) => ({ success: true }))
186
- * ])
187
- * );
188
- * ```
189
- */
190
- export function createLambderApiHandler(apiPath, handlers) {
191
- return (async (info) => {
192
- await ensureMSW();
193
- // Call each handler in sequence until one handles the request
194
- for (const handler of handlers) {
195
- const result = await handler(info);
196
- if (result) {
197
- return result;
19
+ /**
20
+ * Mock an API endpoint with MSW
21
+ * @param apiName - The name of the API to mock
22
+ * @param handler - Function that returns the mock payload
23
+ * @param options - Additional response options (session expired, version expired, etc.)
24
+ */
25
+ mockApi(apiName, handler, options) {
26
+ return this.http.post(this.apiPath, async ({ request }) => {
27
+ try {
28
+ const body = await request.json();
29
+ console.log("LambderMSW called for:", body?.apiName, "matching against:", apiName);
30
+ // Check if this is the API we're mocking
31
+ if (body?.apiName === apiName) {
32
+ // Add artificial delay if specified
33
+ if (options?.delay) {
34
+ await new Promise(resolve => setTimeout(resolve, options.delay));
35
+ }
36
+ // Call the handler with the payload from the request
37
+ const payload = await handler(body?.payload);
38
+ console.log("Matched! Returning payload for:", apiName);
39
+ const response = {
40
+ apiVersion: this.apiVersion,
41
+ payload,
42
+ ...(options?.versionExpired ? { versionExpired: options.versionExpired } : {}),
43
+ ...(options?.sessionExpired ? { sessionExpired: options.sessionExpired } : {}),
44
+ ...(options?.notAuthorized ? { notAuthorized: options.notAuthorized } : {}),
45
+ ...(options?.message ? { message: options.message } : {}),
46
+ ...(options?.errorMessage ? { errorMessage: options.errorMessage } : {}),
47
+ ...(options?.logList?.length ? { logList: options.logList } : {}),
48
+ };
49
+ return this.HttpResponse.json(response);
50
+ }
51
+ // If this handler doesn't match, return undefined to let MSW try other handlers
52
+ return;
198
53
  }
199
- }
200
- // No handler matched
201
- return;
202
- });
54
+ catch (err) {
55
+ console.error("Error in LambderMSW:", err);
56
+ const errorResponse = {
57
+ apiVersion: this.apiVersion,
58
+ payload: null,
59
+ errorMessage: err.message || "Unknown error",
60
+ };
61
+ return this.HttpResponse.json(errorResponse, { status: 500 });
62
+ }
63
+ });
64
+ }
65
+ /**
66
+ * Mock an API endpoint that returns a session expired error
67
+ */
68
+ mockSessionExpired(apiName) {
69
+ return this.mockApi(apiName, async () => null, { sessionExpired: true });
70
+ }
71
+ /**
72
+ * Mock an API endpoint that returns a version expired error
73
+ */
74
+ mockVersionExpired(apiName) {
75
+ return this.mockApi(apiName, async () => null, { versionExpired: true });
76
+ }
77
+ /**
78
+ * Mock an API endpoint that returns a not authorized error
79
+ */
80
+ mockNotAuthorized(apiName) {
81
+ return this.mockApi(apiName, async () => null, { notAuthorized: true });
82
+ }
83
+ /**
84
+ * Mock an API endpoint that returns an error message
85
+ */
86
+ mockError(apiName, errorMessage) {
87
+ return this.mockApi(apiName, async () => null, { errorMessage });
88
+ }
89
+ /**
90
+ * Mock an API endpoint with a custom message
91
+ */
92
+ mockWithMessage(apiName, handler, message) {
93
+ return this.mockApi(apiName, handler, { message });
94
+ }
203
95
  }
package/dist/index.d.ts CHANGED
@@ -4,5 +4,5 @@ export { default as LambderCaller } from "./LambderCaller.js";
4
4
  export { default as LambderResponseBuilder } from "./LambderResponseBuilder.js";
5
5
  export { default as LambderResolver } from "./LambderResolver.js";
6
6
  export { default as LambderSessionManager } from "./LambderSessionManager.js";
7
+ export { default as LambderMSW } from "./LambderMSW.js";
7
8
  export { type ApiContractShape, type ApiContract, type ApiInput, type ApiOutput, } from "./LambderApiContract.js";
8
- export { mockLambderApi, mockLambderApiError, mockLambderSessionExpired, mockLambderNotAuthorized, mockLambderVersionExpired, createLambderApiHandler, type HttpHandler, } from "./LambderMSW.js";
package/dist/index.js CHANGED
@@ -4,5 +4,4 @@ export { default as LambderCaller } from "./LambderCaller.js";
4
4
  export { default as LambderResponseBuilder } from "./LambderResponseBuilder.js";
5
5
  export { default as LambderResolver } from "./LambderResolver.js";
6
6
  export { default as LambderSessionManager } from "./LambderSessionManager.js";
7
- // MSW mocking utilities (optional - requires msw to be installed)
8
- export { mockLambderApi, mockLambderApiError, mockLambderSessionExpired, mockLambderNotAuthorized, mockLambderVersionExpired, createLambderApiHandler, } from "./LambderMSW.js";
7
+ export { default as LambderMSW } from "./LambderMSW.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lambder",
3
- "version": "1.0.130",
3
+ "version": "1.0.132",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",