lambder 1.0.147 → 2.0.2
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 +355 -410
- package/dist/Lambder.d.ts +47 -21
- package/dist/Lambder.js +79 -24
- package/dist/LambderApiContract.d.ts +10 -42
- package/dist/LambderApiContract.js +2 -22
- package/dist/LambderCaller.js +2 -2
- package/dist/LambderMSW.js +0 -4
- package/dist/LambderResolver.d.ts +16 -17
- package/dist/LambderResponseBuilder.d.ts +2 -3
- package/dist/LambderResponseBuilder.js +1 -3
- package/dist/LambderUtils.js +1 -3
- package/dist/index.d.ts +1 -1
- package/docs/LAMBDER_MSW.md +6 -6
- package/docs/TYPE_SAFE_QUICK_START.md +54 -177
- package/examples/msw-testing-example.ts +36 -33
- package/examples/secure-session-example.ts +50 -34
- package/examples/zod-chained-api-example.ts +63 -0
- package/package.json +3 -2
- package/src/Lambder.ts +124 -83
- package/src/LambderApiContract.ts +7 -50
- package/src/LambderCaller.ts +2 -2
- package/src/LambderMSW.ts +0 -7
- package/src/LambderResolver.ts +21 -24
- package/src/LambderResponseBuilder.ts +4 -7
- package/src/LambderUtils.ts +1 -3
- package/src/index.ts +0 -3
- package/tests/UNTESTED_FEATURES.md +263 -0
- package/tests/error-handling.test.ts +585 -0
- package/tests/hooks.test.ts +561 -0
- package/tests/output-type-runtime.test.ts +80 -64
- package/tests/routes.test.ts +542 -0
- package/tests/session.test.ts +38 -24
- package/tests/type-safety.test.ts +147 -97
- package/tests/use-plugin.test.ts +437 -0
- package/OUTPUT_TYPE_ENFORCEMENT_SUMMARY.md +0 -90
- package/examples/output-type-enforcement-example.ts +0 -218
- package/examples/simplified-typed-api-example.ts +0 -365
- package/examples/test-output-type-enforcement.ts +0 -101
- package/test-type-enforcement.ts +0 -111
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import { describe, it, expect } from 'vitest';
|
|
9
9
|
import { APIGatewayProxyEvent, Context } from 'aws-lambda';
|
|
10
|
+
import { z } from 'zod';
|
|
10
11
|
import Lambder from '../src/Lambder.js';
|
|
11
|
-
import type { ApiContract } from '../src/index.js';
|
|
12
12
|
|
|
13
13
|
// Mock AWS Lambda event and context
|
|
14
14
|
const createMockEvent = (apiName: string, payload: any): APIGatewayProxyEvent => ({
|
|
@@ -41,28 +41,20 @@ const createMockContext = (): Context => ({
|
|
|
41
41
|
succeed: () => {},
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
// Test contract
|
|
45
|
-
type TestContract = ApiContract<{
|
|
46
|
-
echo: { input: { message: string }, output: { echo: string } },
|
|
47
|
-
add: { input: { a: number, b: number }, output: number },
|
|
48
|
-
getUser: { input: { userId: string }, output: { id: string, name: string, age: number } },
|
|
49
|
-
listUsers: { input: void, output: Array<{ id: string, name: string }> },
|
|
50
|
-
findUser: { input: { email: string }, output: { id: string, name: string } | null },
|
|
51
|
-
deleteUser: { input: { userId: string }, output: boolean },
|
|
52
|
-
}>;
|
|
53
|
-
|
|
54
44
|
// ============================================================================
|
|
55
45
|
// Runtime Tests
|
|
56
46
|
// ============================================================================
|
|
57
47
|
|
|
58
48
|
describe('Output Type Enforcement - Runtime', () => {
|
|
59
49
|
it('should return correct primitive types', async () => {
|
|
60
|
-
const lambder = new Lambder
|
|
50
|
+
const lambder = new Lambder({
|
|
61
51
|
publicPath: './public',
|
|
62
52
|
apiPath: '/api',
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
|
|
53
|
+
})
|
|
54
|
+
.addApi('add', {
|
|
55
|
+
input: z.object({ a: z.number(), b: z.number() }),
|
|
56
|
+
output: z.number()
|
|
57
|
+
}, async (ctx, resolver) => {
|
|
66
58
|
const { a, b } = ctx.apiPayload;
|
|
67
59
|
const sum = a + b;
|
|
68
60
|
return resolver.api(sum);
|
|
@@ -78,12 +70,14 @@ describe('Output Type Enforcement - Runtime', () => {
|
|
|
78
70
|
});
|
|
79
71
|
|
|
80
72
|
it('should return correct object types', async () => {
|
|
81
|
-
const lambder = new Lambder
|
|
73
|
+
const lambder = new Lambder({
|
|
82
74
|
publicPath: './public',
|
|
83
75
|
apiPath: '/api',
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
})
|
|
77
|
+
.addApi('getUser', {
|
|
78
|
+
input: z.object({ userId: z.string() }),
|
|
79
|
+
output: z.object({ id: z.string(), name: z.string(), age: z.number() })
|
|
80
|
+
}, async (ctx, resolver) => {
|
|
87
81
|
const userId = ctx.apiPayload.userId;
|
|
88
82
|
return resolver.api({
|
|
89
83
|
id: userId,
|
|
@@ -98,6 +92,7 @@ describe('Output Type Enforcement - Runtime', () => {
|
|
|
98
92
|
|
|
99
93
|
expect(response.statusCode).toBe(200);
|
|
100
94
|
const body = JSON.parse(response.body || '{}');
|
|
95
|
+
|
|
101
96
|
expect(body.payload).toEqual({
|
|
102
97
|
id: '123',
|
|
103
98
|
name: 'John Doe',
|
|
@@ -106,12 +101,14 @@ describe('Output Type Enforcement - Runtime', () => {
|
|
|
106
101
|
});
|
|
107
102
|
|
|
108
103
|
it('should return correct array types', async () => {
|
|
109
|
-
const lambder = new Lambder
|
|
104
|
+
const lambder = new Lambder({
|
|
110
105
|
publicPath: './public',
|
|
111
106
|
apiPath: '/api',
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
})
|
|
108
|
+
.addApi('listUsers', {
|
|
109
|
+
input: z.void(),
|
|
110
|
+
output: z.array(z.object({ id: z.string(), name: z.string() }))
|
|
111
|
+
}, async (ctx, resolver) => {
|
|
115
112
|
return resolver.api([
|
|
116
113
|
{ id: '1', name: 'Alice' },
|
|
117
114
|
{ id: '2', name: 'Bob' }
|
|
@@ -131,12 +128,14 @@ describe('Output Type Enforcement - Runtime', () => {
|
|
|
131
128
|
});
|
|
132
129
|
|
|
133
130
|
it('should handle null returns correctly', async () => {
|
|
134
|
-
const lambder = new Lambder
|
|
131
|
+
const lambder = new Lambder({
|
|
135
132
|
publicPath: './public',
|
|
136
133
|
apiPath: '/api',
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
})
|
|
135
|
+
.addApi('findUser', {
|
|
136
|
+
input: z.object({ email: z.string() }),
|
|
137
|
+
output: z.object({ id: z.string(), name: z.string() }).nullable()
|
|
138
|
+
}, async (ctx, resolver) => {
|
|
140
139
|
const email = ctx.apiPayload.email;
|
|
141
140
|
if (email === 'notfound@example.com') {
|
|
142
141
|
return resolver.api(null);
|
|
@@ -164,12 +163,14 @@ describe('Output Type Enforcement - Runtime', () => {
|
|
|
164
163
|
});
|
|
165
164
|
|
|
166
165
|
it('should return boolean types correctly', async () => {
|
|
167
|
-
const lambder = new Lambder
|
|
166
|
+
const lambder = new Lambder({
|
|
168
167
|
publicPath: './public',
|
|
169
168
|
apiPath: '/api',
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
})
|
|
170
|
+
.addApi('deleteUser', {
|
|
171
|
+
input: z.object({ userId: z.string() }),
|
|
172
|
+
output: z.boolean()
|
|
173
|
+
}, async (ctx, resolver) => {
|
|
173
174
|
const userId = ctx.apiPayload.userId;
|
|
174
175
|
// Mock deletion
|
|
175
176
|
const success = userId !== '';
|
|
@@ -186,12 +187,14 @@ describe('Output Type Enforcement - Runtime', () => {
|
|
|
186
187
|
});
|
|
187
188
|
|
|
188
189
|
it('should work with die.api()', async () => {
|
|
189
|
-
const lambder = new Lambder
|
|
190
|
+
const lambder = new Lambder({
|
|
190
191
|
publicPath: './public',
|
|
191
192
|
apiPath: '/api',
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
})
|
|
194
|
+
.addApi('echo', {
|
|
195
|
+
input: z.object({ message: z.string() }),
|
|
196
|
+
output: z.object({ echo: z.string() })
|
|
197
|
+
}, async (ctx, resolver) => {
|
|
195
198
|
return resolver.die.api({ echo: ctx.apiPayload.message });
|
|
196
199
|
});
|
|
197
200
|
|
|
@@ -205,13 +208,14 @@ describe('Output Type Enforcement - Runtime', () => {
|
|
|
205
208
|
});
|
|
206
209
|
|
|
207
210
|
it('should work with session APIs', async () => {
|
|
208
|
-
const lambder = new Lambder
|
|
211
|
+
const lambder = new Lambder({
|
|
209
212
|
publicPath: './public',
|
|
210
213
|
apiPath: '/api',
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
214
|
+
})
|
|
215
|
+
.addSessionApi('getUser', {
|
|
216
|
+
input: z.object({ userId: z.string() }),
|
|
217
|
+
output: z.object({ id: z.string(), name: z.string(), age: z.number() })
|
|
218
|
+
}, async (ctx, resolver) => {
|
|
215
219
|
return resolver.api({
|
|
216
220
|
id: ctx.apiPayload.userId,
|
|
217
221
|
name: 'Session User',
|
|
@@ -237,12 +241,14 @@ describe('Output Type Enforcement - Runtime', () => {
|
|
|
237
241
|
|
|
238
242
|
describe('Input Type Enforcement - Runtime', () => {
|
|
239
243
|
it('should receive correctly typed input', async () => {
|
|
240
|
-
const lambder = new Lambder
|
|
244
|
+
const lambder = new Lambder({
|
|
241
245
|
publicPath: './public',
|
|
242
246
|
apiPath: '/api',
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
|
|
247
|
+
})
|
|
248
|
+
.addApi('echo', {
|
|
249
|
+
input: z.object({ message: z.string() }),
|
|
250
|
+
output: z.object({ echo: z.string() })
|
|
251
|
+
}, async (ctx, resolver) => {
|
|
246
252
|
// Verify input is correctly typed at runtime
|
|
247
253
|
const message: string = ctx.apiPayload.message;
|
|
248
254
|
expect(typeof message).toBe('string');
|
|
@@ -255,12 +261,14 @@ describe('Input Type Enforcement - Runtime', () => {
|
|
|
255
261
|
});
|
|
256
262
|
|
|
257
263
|
it('should handle void input correctly', async () => {
|
|
258
|
-
const lambder = new Lambder
|
|
264
|
+
const lambder = new Lambder({
|
|
259
265
|
publicPath: './public',
|
|
260
266
|
apiPath: '/api',
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
|
|
267
|
+
})
|
|
268
|
+
.addApi('listUsers', {
|
|
269
|
+
input: z.void(),
|
|
270
|
+
output: z.array(z.any())
|
|
271
|
+
}, async (ctx, resolver) => {
|
|
264
272
|
// apiPayload should be undefined for void input
|
|
265
273
|
expect(ctx.apiPayload).toBeUndefined();
|
|
266
274
|
return resolver.api([]);
|
|
@@ -272,12 +280,14 @@ describe('Input Type Enforcement - Runtime', () => {
|
|
|
272
280
|
});
|
|
273
281
|
|
|
274
282
|
it('should handle complex input objects', async () => {
|
|
275
|
-
const lambder = new Lambder
|
|
283
|
+
const lambder = new Lambder({
|
|
276
284
|
publicPath: './public',
|
|
277
285
|
apiPath: '/api',
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
|
|
286
|
+
})
|
|
287
|
+
.addApi('add', {
|
|
288
|
+
input: z.object({ a: z.number(), b: z.number() }),
|
|
289
|
+
output: z.number()
|
|
290
|
+
}, async (ctx, resolver) => {
|
|
281
291
|
const { a, b } = ctx.apiPayload;
|
|
282
292
|
expect(typeof a).toBe('number');
|
|
283
293
|
expect(typeof b).toBe('number');
|
|
@@ -299,12 +309,14 @@ describe('Input Type Enforcement - Runtime', () => {
|
|
|
299
309
|
|
|
300
310
|
describe('Edge Cases', () => {
|
|
301
311
|
it('should handle empty arrays', async () => {
|
|
302
|
-
const lambder = new Lambder
|
|
312
|
+
const lambder = new Lambder({
|
|
303
313
|
publicPath: './public',
|
|
304
314
|
apiPath: '/api',
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
|
|
315
|
+
})
|
|
316
|
+
.addApi('listUsers', {
|
|
317
|
+
input: z.void(),
|
|
318
|
+
output: z.array(z.any())
|
|
319
|
+
}, async (ctx, resolver) => {
|
|
308
320
|
return resolver.api([]);
|
|
309
321
|
});
|
|
310
322
|
|
|
@@ -318,12 +330,14 @@ describe('Edge Cases', () => {
|
|
|
318
330
|
});
|
|
319
331
|
|
|
320
332
|
it('should handle zero as a valid number', async () => {
|
|
321
|
-
const lambder = new Lambder
|
|
333
|
+
const lambder = new Lambder({
|
|
322
334
|
publicPath: './public',
|
|
323
335
|
apiPath: '/api',
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
|
|
336
|
+
})
|
|
337
|
+
.addApi('add', {
|
|
338
|
+
input: z.object({ a: z.number(), b: z.number() }),
|
|
339
|
+
output: z.number()
|
|
340
|
+
}, async (ctx, resolver) => {
|
|
327
341
|
return resolver.api(0);
|
|
328
342
|
});
|
|
329
343
|
|
|
@@ -337,12 +351,14 @@ describe('Edge Cases', () => {
|
|
|
337
351
|
});
|
|
338
352
|
|
|
339
353
|
it('should handle empty strings in objects', async () => {
|
|
340
|
-
const lambder = new Lambder
|
|
354
|
+
const lambder = new Lambder({
|
|
341
355
|
publicPath: './public',
|
|
342
356
|
apiPath: '/api',
|
|
343
|
-
})
|
|
344
|
-
|
|
345
|
-
|
|
357
|
+
})
|
|
358
|
+
.addApi('getUser', {
|
|
359
|
+
input: z.object({ userId: z.string() }),
|
|
360
|
+
output: z.object({ id: z.string(), name: z.string(), age: z.number() })
|
|
361
|
+
}, async (ctx, resolver) => {
|
|
346
362
|
return resolver.api({
|
|
347
363
|
id: '',
|
|
348
364
|
name: '',
|