lambder 2.0.18 → 3.1.1

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 (69) hide show
  1. package/Readme.md +184 -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/LambderDdbCache.d.ts +65 -0
  10. package/dist/LambderDdbCache.js +480 -0
  11. package/dist/LambderHtml.d.ts +33 -0
  12. package/dist/LambderHtml.js +62 -0
  13. package/dist/LambderMSW.d.ts +16 -1
  14. package/dist/LambderMSW.js +5 -9
  15. package/dist/LambderPublicFiles.d.ts +47 -0
  16. package/dist/LambderPublicFiles.js +108 -0
  17. package/dist/LambderResolver.d.ts +30 -31
  18. package/dist/LambderResolver.js +29 -43
  19. package/dist/LambderResponse.d.ts +71 -0
  20. package/dist/LambderResponse.js +196 -0
  21. package/dist/LambderResponseBuilder.d.ts +58 -33
  22. package/dist/LambderResponseBuilder.js +114 -167
  23. package/dist/LambderRouting.d.ts +23 -0
  24. package/dist/LambderRouting.js +67 -0
  25. package/dist/LambderSessionController.d.ts +13 -1
  26. package/dist/LambderSessionController.js +33 -10
  27. package/dist/LambderSessionManager.d.ts +3 -1
  28. package/dist/LambderSessionManager.js +15 -6
  29. package/dist/LambderTemplatingEngine.d.ts +87 -0
  30. package/dist/LambderTemplatingEngine.js +156 -0
  31. package/dist/index.d.ts +16 -2
  32. package/dist/index.js +12 -1
  33. package/dist/node-polyfills.d.ts +4 -2
  34. package/dist/node-polyfills.js +28 -0
  35. package/package.json +8 -5
  36. package/.eslintrc.cjs +0 -26
  37. package/.vscode/settings.json +0 -26
  38. package/deploy +0 -22
  39. package/dist/LambderUtils.d.ts +0 -10
  40. package/dist/LambderUtils.js +0 -70
  41. package/docs/DYNAMODB_SETUP.md +0 -96
  42. package/docs/LAMBDER_MSW.md +0 -409
  43. package/docs/TYPE_SAFE_QUICK_START.md +0 -77
  44. package/examples/msw-testing-example.ts +0 -280
  45. package/examples/secure-session-example.ts +0 -207
  46. package/examples/zod-chained-api-example.ts +0 -63
  47. package/src/Lambder.ts +0 -430
  48. package/src/LambderApiContract.ts +0 -20
  49. package/src/LambderCaller.ts +0 -238
  50. package/src/LambderContext.ts +0 -78
  51. package/src/LambderMSW.ts +0 -180
  52. package/src/LambderResolver.ts +0 -101
  53. package/src/LambderResponseBuilder.ts +0 -332
  54. package/src/LambderSessionController.ts +0 -114
  55. package/src/LambderSessionManager.ts +0 -217
  56. package/src/LambderUtils.ts +0 -75
  57. package/src/index.ts +0 -17
  58. package/src/node-polyfills.ts +0 -27
  59. package/tests/error-handling.test.ts +0 -585
  60. package/tests/file-serving.test.ts +0 -194
  61. package/tests/fixtures/public/index.html +0 -1
  62. package/tests/fixtures/public/main.css +0 -1
  63. package/tests/hooks.test.ts +0 -561
  64. package/tests/output-type-runtime.test.ts +0 -381
  65. package/tests/redirect.test.ts +0 -88
  66. package/tests/routes.test.ts +0 -543
  67. package/tests/session.test.ts +0 -1083
  68. package/tests/use-plugin.test.ts +0 -460
  69. package/tsconfig.json +0 -24
@@ -1,561 +0,0 @@
1
- /**
2
- * Hooks System Tests
3
- *
4
- * Tests for the hooks system including:
5
- * - beforeRender hook execution
6
- * - afterRender hook execution
7
- * - fallback hook execution
8
- * - created hook execution
9
- * - Hook priority ordering
10
- * - Multiple hooks of same type
11
- * - Hook error handling
12
- * - Context modification in hooks
13
- * - Response modification in hooks
14
- */
15
-
16
- import { describe, it, expect, beforeEach, vi } from 'vitest';
17
- import { z } from 'zod';
18
- import Lambder from '../src/Lambder.js';
19
- import type { APIGatewayProxyEvent, Context } from 'aws-lambda';
20
-
21
- const createMockEvent = (path: string, method: string = 'GET', apiName?: string, payload?: any): APIGatewayProxyEvent => ({
22
- body: apiName ? JSON.stringify({ apiName, payload }) : null,
23
- headers: { Host: 'localhost' },
24
- multiValueHeaders: {},
25
- httpMethod: method,
26
- isBase64Encoded: false,
27
- path: apiName ? '/api' : path,
28
- pathParameters: null,
29
- queryStringParameters: null,
30
- multiValueQueryStringParameters: null,
31
- stageVariables: null,
32
- requestContext: {} as any,
33
- resource: '',
34
- });
35
-
36
- const createMockContext = (): Context => ({
37
- callbackWaitsForEmptyEventLoop: false,
38
- functionName: 'test',
39
- functionVersion: '1',
40
- invokedFunctionArn: 'arn',
41
- memoryLimitInMB: '128',
42
- awsRequestId: '123',
43
- logGroupName: 'group',
44
- logStreamName: 'stream',
45
- getRemainingTimeInMillis: () => 1000,
46
- done: () => {},
47
- fail: () => {},
48
- succeed: () => {},
49
- });
50
-
51
- describe('Hooks - beforeRender Hook', () => {
52
- it('should execute beforeRender hook before route handler', async () => {
53
- const executionOrder: string[] = [];
54
-
55
- const lambder = new Lambder({
56
- publicPath: './public',
57
- apiPath: '/api'
58
- });
59
-
60
- await lambder.addHook('beforeRender', async (ctx, res) => {
61
- executionOrder.push('beforeRender');
62
- return ctx;
63
- });
64
-
65
- lambder.addRoute('/test', (ctx, res) => {
66
- executionOrder.push('routeHandler');
67
- return res.html('Test');
68
- });
69
-
70
- const handler = lambder.getHandler();
71
- await handler(createMockEvent('/test'), createMockContext());
72
-
73
- expect(executionOrder).toEqual(['beforeRender', 'routeHandler']);
74
- });
75
-
76
- it('should allow context modification in beforeRender', async () => {
77
- const lambder = new Lambder({
78
- publicPath: './public',
79
- apiPath: '/api'
80
- });
81
-
82
- await lambder.addHook('beforeRender', async (ctx, res) => {
83
- // Add custom property to context
84
- (ctx as any).customData = 'modified';
85
- return ctx;
86
- });
87
-
88
- lambder.addRoute('/test', (ctx, res) => {
89
- return res.json({ data: (ctx as any).customData });
90
- });
91
-
92
- const handler = lambder.getHandler();
93
- const result = await handler(createMockEvent('/test'), createMockContext());
94
-
95
- const body = JSON.parse(result.body || '{}');
96
- expect(body.data).toBe('modified');
97
- });
98
-
99
- it('should execute multiple beforeRender hooks', async () => {
100
- const executionOrder: number[] = [];
101
-
102
- const lambder = new Lambder({
103
- publicPath: './public',
104
- apiPath: '/api'
105
- });
106
-
107
- await lambder.addHook('beforeRender', async (ctx, res) => {
108
- executionOrder.push(1);
109
- return ctx;
110
- });
111
-
112
- await lambder.addHook('beforeRender', async (ctx, res) => {
113
- executionOrder.push(2);
114
- return ctx;
115
- });
116
-
117
- await lambder.addHook('beforeRender', async (ctx, res) => {
118
- executionOrder.push(3);
119
- return ctx;
120
- });
121
-
122
- lambder.addRoute('/test', (ctx, res) => res.html('Test'));
123
-
124
- const handler = lambder.getHandler();
125
- await handler(createMockEvent('/test'), createMockContext());
126
-
127
- expect(executionOrder).toEqual([1, 2, 3]);
128
- });
129
-
130
- it('should stop execution if beforeRender returns Error', async () => {
131
- let routeHandlerCalled = false;
132
-
133
- const lambder = new Lambder({
134
- publicPath: './public',
135
- apiPath: '/api'
136
- })
137
- .setGlobalErrorHandler((err, ctx, res) => {
138
- return res.raw({ statusCode: 403, body: err.message });
139
- });
140
-
141
- await lambder.addHook('beforeRender', async (ctx, res) => {
142
- return new Error('Access Denied');
143
- });
144
-
145
- lambder.addRoute('/test', (ctx, res) => {
146
- routeHandlerCalled = true;
147
- return res.html('Test');
148
- });
149
-
150
- const handler = lambder.getHandler();
151
- const result = await handler(createMockEvent('/test'), createMockContext());
152
-
153
- expect(result.statusCode).toBe(403);
154
- expect(result.body).toBe('Access Denied');
155
- expect(routeHandlerCalled).toBe(false);
156
- });
157
- });
158
-
159
- describe('Hooks - afterRender Hook', () => {
160
- it('should execute afterRender hook after route handler', async () => {
161
- const executionOrder: string[] = [];
162
-
163
- const lambder = new Lambder({
164
- publicPath: './public',
165
- apiPath: '/api'
166
- });
167
-
168
- lambder.addRoute('/test', (ctx, res) => {
169
- executionOrder.push('routeHandler');
170
- return res.html('Test');
171
- });
172
-
173
- await lambder.addHook('afterRender', async (ctx, res, response) => {
174
- executionOrder.push('afterRender');
175
- return response;
176
- });
177
-
178
- const handler = lambder.getHandler();
179
- await handler(createMockEvent('/test'), createMockContext());
180
-
181
- expect(executionOrder).toEqual(['routeHandler', 'afterRender']);
182
- });
183
-
184
- it('should allow response modification in afterRender', async () => {
185
- const lambder = new Lambder({
186
- publicPath: './public',
187
- apiPath: '/api'
188
- });
189
-
190
- lambder.addRoute('/test', (ctx, res) => {
191
- return res.json({ original: true });
192
- });
193
-
194
- await lambder.addHook('afterRender', async (ctx, res, response) => {
195
- // Modify response body
196
- const body = JSON.parse(response.body || '{}');
197
- body.modified = true;
198
- response.body = JSON.stringify(body);
199
- return response;
200
- });
201
-
202
- const handler = lambder.getHandler();
203
- const result = await handler(createMockEvent('/test'), createMockContext());
204
-
205
- const body = JSON.parse(result.body || '{}');
206
- expect(body.original).toBe(true);
207
- expect(body.modified).toBe(true);
208
- });
209
-
210
- it('should execute multiple afterRender hooks', async () => {
211
- const executionOrder: number[] = [];
212
-
213
- const lambder = new Lambder({
214
- publicPath: './public',
215
- apiPath: '/api'
216
- });
217
-
218
- lambder.addRoute('/test', (ctx, res) => res.html('Test'));
219
-
220
- await lambder.addHook('afterRender', async (ctx, res, response) => {
221
- executionOrder.push(1);
222
- return response;
223
- });
224
-
225
- await lambder.addHook('afterRender', async (ctx, res, response) => {
226
- executionOrder.push(2);
227
- return response;
228
- });
229
-
230
- await lambder.addHook('afterRender', async (ctx, res, response) => {
231
- executionOrder.push(3);
232
- return response;
233
- });
234
-
235
- const handler = lambder.getHandler();
236
- await handler(createMockEvent('/test'), createMockContext());
237
-
238
- expect(executionOrder).toEqual([1, 2, 3]);
239
- });
240
-
241
- it('should add custom headers in afterRender', async () => {
242
- const lambder = new Lambder({
243
- publicPath: './public',
244
- apiPath: '/api'
245
- });
246
-
247
- lambder.addRoute('/test', (ctx, res) => res.json({ data: 'test' }));
248
-
249
- await lambder.addHook('afterRender', async (ctx, res, response) => {
250
- response.multiValueHeaders = response.multiValueHeaders || {};
251
- response.multiValueHeaders['X-Custom-Header'] = ['CustomValue'];
252
- return response;
253
- });
254
-
255
- const handler = lambder.getHandler();
256
- const result = await handler(createMockEvent('/test'), createMockContext());
257
-
258
- expect(result.multiValueHeaders?.['X-Custom-Header']).toEqual(['CustomValue']);
259
- });
260
-
261
- it('should stop execution if afterRender returns Error', async () => {
262
- const lambder = new Lambder({
263
- publicPath: './public',
264
- apiPath: '/api'
265
- })
266
- .setGlobalErrorHandler((err, ctx, res) => {
267
- return res.raw({ statusCode: 500, body: `Error: ${err.message}` });
268
- });
269
-
270
- lambder.addRoute('/test', (ctx, res) => res.html('Test'));
271
-
272
- await lambder.addHook('afterRender', async (ctx, res, response) => {
273
- return new Error('Post-processing failed');
274
- });
275
-
276
- const handler = lambder.getHandler();
277
- const result = await handler(createMockEvent('/test'), createMockContext());
278
-
279
- expect(result.statusCode).toBe(500);
280
- expect(result.body).toContain('Post-processing failed');
281
- });
282
- });
283
-
284
- describe('Hooks - fallback Hook', () => {
285
- it('should execute fallback hook when no route matches', async () => {
286
- let fallbackCalled = false;
287
-
288
- const lambder = new Lambder({
289
- publicPath: './public',
290
- apiPath: '/api'
291
- });
292
-
293
- lambder.addRoute('/exists', (ctx, res) => res.html('Exists'));
294
-
295
- await lambder.addHook('fallback', async (ctx, res) => {
296
- fallbackCalled = true;
297
- });
298
-
299
- lambder.setRouteFallbackHandler((ctx, res) => {
300
- return res.status404('Not Found');
301
- });
302
-
303
- const handler = lambder.getHandler();
304
- await handler(createMockEvent('/nonexistent'), createMockContext());
305
-
306
- expect(fallbackCalled).toBe(true);
307
- });
308
-
309
- it('should execute multiple fallback hooks', async () => {
310
- const executionOrder: number[] = [];
311
-
312
- const lambder = new Lambder({
313
- publicPath: './public',
314
- apiPath: '/api'
315
- });
316
-
317
- await lambder.addHook('fallback', async (ctx, res) => {
318
- executionOrder.push(1);
319
- });
320
-
321
- await lambder.addHook('fallback', async (ctx, res) => {
322
- executionOrder.push(2);
323
- });
324
-
325
- lambder.setRouteFallbackHandler((ctx, res) => res.status404('Not Found'));
326
-
327
- const handler = lambder.getHandler();
328
- await handler(createMockEvent('/nonexistent'), createMockContext());
329
-
330
- expect(executionOrder).toEqual([1, 2]);
331
- });
332
-
333
- it('should not execute fallback hook when route matches', async () => {
334
- let fallbackCalled = false;
335
-
336
- const lambder = new Lambder({
337
- publicPath: './public',
338
- apiPath: '/api'
339
- });
340
-
341
- lambder.addRoute('/test', (ctx, res) => res.html('Test'));
342
-
343
- await lambder.addHook('fallback', async (ctx, res) => {
344
- fallbackCalled = true;
345
- });
346
-
347
- const handler = lambder.getHandler();
348
- await handler(createMockEvent('/test'), createMockContext());
349
-
350
- expect(fallbackCalled).toBe(false);
351
- });
352
- });
353
-
354
- describe('Hooks - created Hook', () => {
355
- it('should execute created hook immediately', async () => {
356
- let createdCalled = false;
357
- let lambderInstance: Lambder | null = null;
358
-
359
- const lambder = new Lambder({
360
- publicPath: './public',
361
- apiPath: '/api'
362
- });
363
-
364
- await lambder.addHook('created', async (instance) => {
365
- createdCalled = true;
366
- lambderInstance = instance;
367
- });
368
-
369
- expect(createdCalled).toBe(true);
370
- expect(lambderInstance).toBe(lambder);
371
- });
372
-
373
- it('should allow configuration in created hook', async () => {
374
- const lambder = new Lambder({
375
- publicPath: './public',
376
- apiPath: '/api'
377
- });
378
-
379
- await lambder.addHook('created', async (instance) => {
380
- instance.enableCors(true);
381
- });
382
-
383
- expect(lambder.isCorsEnabled).toBe(true);
384
- });
385
- });
386
-
387
- describe('Hooks - Priority Ordering', () => {
388
- it('should execute beforeRender hooks in priority order', async () => {
389
- const executionOrder: number[] = [];
390
-
391
- const lambder = new Lambder({
392
- publicPath: './public',
393
- apiPath: '/api'
394
- });
395
-
396
- await lambder.addHook('beforeRender', async (ctx, res) => {
397
- executionOrder.push(20);
398
- return ctx;
399
- }, 20);
400
-
401
- await lambder.addHook('beforeRender', async (ctx, res) => {
402
- executionOrder.push(10);
403
- return ctx;
404
- }, 10);
405
-
406
- await lambder.addHook('beforeRender', async (ctx, res) => {
407
- executionOrder.push(30);
408
- return ctx;
409
- }, 30);
410
-
411
- lambder.addRoute('/test', (ctx, res) => res.html('Test'));
412
-
413
- const handler = lambder.getHandler();
414
- await handler(createMockEvent('/test'), createMockContext());
415
-
416
- // Should execute in priority order: 10, 20, 30
417
- expect(executionOrder).toEqual([10, 20, 30]);
418
- });
419
-
420
- it('should execute afterRender hooks in priority order', async () => {
421
- const executionOrder: number[] = [];
422
-
423
- const lambder = new Lambder({
424
- publicPath: './public',
425
- apiPath: '/api'
426
- });
427
-
428
- lambder.addRoute('/test', (ctx, res) => res.html('Test'));
429
-
430
- await lambder.addHook('afterRender', async (ctx, res, response) => {
431
- executionOrder.push(50);
432
- return response;
433
- }, 50);
434
-
435
- await lambder.addHook('afterRender', async (ctx, res, response) => {
436
- executionOrder.push(5);
437
- return response;
438
- }, 5);
439
-
440
- await lambder.addHook('afterRender', async (ctx, res, response) => {
441
- executionOrder.push(25);
442
- return response;
443
- }, 25);
444
-
445
- const handler = lambder.getHandler();
446
- await handler(createMockEvent('/test'), createMockContext());
447
-
448
- expect(executionOrder).toEqual([5, 25, 50]);
449
- });
450
-
451
- it('should use priority 0 as default', async () => {
452
- const executionOrder: string[] = [];
453
-
454
- const lambder = new Lambder({
455
- publicPath: './public',
456
- apiPath: '/api'
457
- });
458
-
459
- await lambder.addHook('beforeRender', async (ctx, res) => {
460
- executionOrder.push('no-priority');
461
- return ctx;
462
- }); // No priority specified, defaults to 0
463
-
464
- await lambder.addHook('beforeRender', async (ctx, res) => {
465
- executionOrder.push('negative');
466
- return ctx;
467
- }, -10);
468
-
469
- await lambder.addHook('beforeRender', async (ctx, res) => {
470
- executionOrder.push('positive');
471
- return ctx;
472
- }, 10);
473
-
474
- lambder.addRoute('/test', (ctx, res) => res.html('Test'));
475
-
476
- const handler = lambder.getHandler();
477
- await handler(createMockEvent('/test'), createMockContext());
478
-
479
- expect(executionOrder).toEqual(['negative', 'no-priority', 'positive']);
480
- });
481
- });
482
-
483
- describe('Hooks - Combined Workflow', () => {
484
- it('should execute hooks in correct order: beforeRender -> handler -> afterRender', async () => {
485
- const executionOrder: string[] = [];
486
-
487
- const lambder = new Lambder({
488
- publicPath: './public',
489
- apiPath: '/api'
490
- });
491
-
492
- await lambder.addHook('beforeRender', async (ctx, res) => {
493
- executionOrder.push('before-1');
494
- return ctx;
495
- });
496
-
497
- await lambder.addHook('beforeRender', async (ctx, res) => {
498
- executionOrder.push('before-2');
499
- return ctx;
500
- });
501
-
502
- lambder.addRoute('/test', (ctx, res) => {
503
- executionOrder.push('handler');
504
- return res.html('Test');
505
- });
506
-
507
- await lambder.addHook('afterRender', async (ctx, res, response) => {
508
- executionOrder.push('after-1');
509
- return response;
510
- });
511
-
512
- await lambder.addHook('afterRender', async (ctx, res, response) => {
513
- executionOrder.push('after-2');
514
- return response;
515
- });
516
-
517
- const handler = lambder.getHandler();
518
- await handler(createMockEvent('/test'), createMockContext());
519
-
520
- expect(executionOrder).toEqual([
521
- 'before-1',
522
- 'before-2',
523
- 'handler',
524
- 'after-1',
525
- 'after-2'
526
- ]);
527
- });
528
-
529
- it('should work with API handlers', async () => {
530
- const executionOrder: string[] = [];
531
-
532
- const lambder = new Lambder({
533
- publicPath: './public',
534
- apiPath: '/api'
535
- });
536
-
537
- await lambder.addHook('beforeRender', async (ctx, res) => {
538
- executionOrder.push('beforeRender');
539
- return ctx;
540
- });
541
-
542
- lambder.addApi('testApi', {
543
- input: z.object({ value: z.string() }),
544
- output: z.object({ result: z.string() })
545
- }, async (ctx, res) => {
546
- executionOrder.push('apiHandler');
547
- return res.api({ result: ctx.apiPayload.value });
548
- });
549
-
550
- await lambder.addHook('afterRender', async (ctx, res, response) => {
551
- executionOrder.push('afterRender');
552
- return response;
553
- });
554
-
555
- const handler = lambder.getHandler();
556
- const event = createMockEvent('/api', 'POST', 'testApi', { value: 'test' });
557
- await handler(event, createMockContext());
558
-
559
- expect(executionOrder).toEqual(['beforeRender', 'apiHandler', 'afterRender']);
560
- });
561
- });