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.
- package/Readme.md +162 -41
- package/dist/Lambder.d.ts +154 -46
- package/dist/Lambder.js +312 -166
- package/dist/LambderCaller.js +6 -3
- package/dist/LambderContext.d.ts +20 -9
- package/dist/LambderContext.js +57 -17
- package/dist/LambderCors.d.ts +12 -0
- package/dist/LambderCors.js +30 -0
- package/dist/LambderHtml.d.ts +33 -0
- package/dist/LambderHtml.js +62 -0
- package/dist/LambderMSW.d.ts +16 -1
- package/dist/LambderMSW.js +5 -9
- package/dist/LambderPublicFiles.d.ts +47 -0
- package/dist/LambderPublicFiles.js +108 -0
- package/dist/LambderResolver.d.ts +30 -31
- package/dist/LambderResolver.js +29 -43
- package/dist/LambderResponse.d.ts +71 -0
- package/dist/LambderResponse.js +196 -0
- package/dist/LambderResponseBuilder.d.ts +58 -33
- package/dist/LambderResponseBuilder.js +114 -167
- package/dist/LambderRouting.d.ts +23 -0
- package/dist/LambderRouting.js +67 -0
- package/dist/LambderSessionController.d.ts +13 -1
- package/dist/LambderSessionController.js +33 -10
- package/dist/LambderSessionManager.d.ts +3 -1
- package/dist/LambderSessionManager.js +15 -6
- package/dist/LambderTemplatingEngine.d.ts +87 -0
- package/dist/LambderTemplatingEngine.js +156 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.js +10 -1
- package/dist/node-polyfills.d.ts +4 -2
- package/dist/node-polyfills.js +28 -0
- package/package.json +7 -5
- package/.eslintrc.cjs +0 -26
- package/.vscode/settings.json +0 -26
- package/deploy +0 -22
- package/dist/LambderUtils.d.ts +0 -10
- package/dist/LambderUtils.js +0 -70
- package/docs/DYNAMODB_SETUP.md +0 -96
- package/docs/LAMBDER_MSW.md +0 -409
- package/docs/TYPE_SAFE_QUICK_START.md +0 -77
- package/examples/msw-testing-example.ts +0 -280
- package/examples/secure-session-example.ts +0 -207
- package/examples/zod-chained-api-example.ts +0 -63
- package/src/Lambder.ts +0 -430
- package/src/LambderApiContract.ts +0 -20
- package/src/LambderCaller.ts +0 -238
- package/src/LambderContext.ts +0 -78
- package/src/LambderMSW.ts +0 -180
- package/src/LambderResolver.ts +0 -101
- package/src/LambderResponseBuilder.ts +0 -332
- package/src/LambderSessionController.ts +0 -114
- package/src/LambderSessionManager.ts +0 -217
- package/src/LambderUtils.ts +0 -75
- package/src/index.ts +0 -17
- package/src/node-polyfills.ts +0 -27
- package/tests/error-handling.test.ts +0 -585
- package/tests/file-serving.test.ts +0 -194
- package/tests/fixtures/public/index.html +0 -1
- package/tests/fixtures/public/main.css +0 -1
- package/tests/hooks.test.ts +0 -561
- package/tests/output-type-runtime.test.ts +0 -381
- package/tests/redirect.test.ts +0 -88
- package/tests/routes.test.ts +0 -543
- package/tests/session.test.ts +0 -1083
- package/tests/use-plugin.test.ts +0 -460
- package/tsconfig.json +0 -24
package/tests/use-plugin.test.ts
DELETED
|
@@ -1,460 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin System (.use) Tests
|
|
3
|
-
*
|
|
4
|
-
* This file tests the plugin system that allows modular API composition
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect, expectTypeOf } from 'vitest';
|
|
8
|
-
import { z } from 'zod';
|
|
9
|
-
import Lambder from '../src/Lambder.js';
|
|
10
|
-
import LambderCaller from '../src/LambderCaller.js';
|
|
11
|
-
import { APIGatewayProxyEvent, Context } from 'aws-lambda';
|
|
12
|
-
|
|
13
|
-
// Mock AWS Lambda event and context helpers
|
|
14
|
-
const createMockEvent = (apiName: string, payload: any): APIGatewayProxyEvent => ({
|
|
15
|
-
body: JSON.stringify({ apiName, payload }),
|
|
16
|
-
headers: { Host: 'localhost' },
|
|
17
|
-
multiValueHeaders: {},
|
|
18
|
-
httpMethod: 'POST',
|
|
19
|
-
isBase64Encoded: false,
|
|
20
|
-
path: '/api',
|
|
21
|
-
pathParameters: null,
|
|
22
|
-
queryStringParameters: null,
|
|
23
|
-
multiValueQueryStringParameters: null,
|
|
24
|
-
stageVariables: null,
|
|
25
|
-
requestContext: {} as any,
|
|
26
|
-
resource: '',
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const createMockContext = (): Context => ({
|
|
30
|
-
callbackWaitsForEmptyEventLoop: false,
|
|
31
|
-
functionName: 'test',
|
|
32
|
-
functionVersion: '1',
|
|
33
|
-
invokedFunctionArn: 'arn',
|
|
34
|
-
memoryLimitInMB: '128',
|
|
35
|
-
awsRequestId: '123',
|
|
36
|
-
logGroupName: 'group',
|
|
37
|
-
logStreamName: 'stream',
|
|
38
|
-
getRemainingTimeInMillis: () => 1000,
|
|
39
|
-
done: () => {},
|
|
40
|
-
fail: () => {},
|
|
41
|
-
succeed: () => {},
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// ============================================================================
|
|
45
|
-
// Test 1: Basic Plugin Usage
|
|
46
|
-
// ============================================================================
|
|
47
|
-
|
|
48
|
-
describe('Plugin System - Basic Usage', () => {
|
|
49
|
-
it('should allow adding APIs via plugin', async () => {
|
|
50
|
-
// Define a simple plugin
|
|
51
|
-
const userPlugin = <T>(lambder: Lambder<T>) => {
|
|
52
|
-
return lambder
|
|
53
|
-
.addApi('getUser', {
|
|
54
|
-
input: z.object({ userId: z.string() }),
|
|
55
|
-
output: z.object({ id: z.string(), name: z.string() })
|
|
56
|
-
}, async (ctx, res) => {
|
|
57
|
-
return res.api({ id: ctx.apiPayload.userId, name: 'John Doe' });
|
|
58
|
-
});
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// Use the plugin
|
|
62
|
-
const lambder = new Lambder({
|
|
63
|
-
publicPath: './public',
|
|
64
|
-
apiPath: '/api'
|
|
65
|
-
}).use(userPlugin);
|
|
66
|
-
|
|
67
|
-
// Test runtime execution
|
|
68
|
-
const handler = lambder.getHandler();
|
|
69
|
-
const event = createMockEvent('getUser', { userId: '123' });
|
|
70
|
-
const result = await handler(event, createMockContext());
|
|
71
|
-
|
|
72
|
-
expect(result.statusCode).toBe(200);
|
|
73
|
-
const body = JSON.parse(result.body || '{}');
|
|
74
|
-
expect(body.payload).toEqual({ id: '123', name: 'John Doe' });
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should preserve type contract after using plugin', () => {
|
|
78
|
-
const userPlugin = <T>(lambder: Lambder<T>) => {
|
|
79
|
-
return lambder
|
|
80
|
-
.addApi('getUser', {
|
|
81
|
-
input: z.object({ userId: z.string() }),
|
|
82
|
-
output: z.object({ id: z.string(), name: z.string() })
|
|
83
|
-
}, async (ctx, res) => {
|
|
84
|
-
return res.api({ id: ctx.apiPayload.userId, name: 'Test' });
|
|
85
|
-
});
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const lambder = new Lambder({
|
|
89
|
-
publicPath: './public',
|
|
90
|
-
apiPath: '/api'
|
|
91
|
-
}).use(userPlugin);
|
|
92
|
-
|
|
93
|
-
type AppContract = typeof lambder.ApiContract;
|
|
94
|
-
|
|
95
|
-
const caller = new LambderCaller<AppContract>({
|
|
96
|
-
apiPath: '/api',
|
|
97
|
-
isCorsEnabled: false
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// Type check - if this compiles, types are correct
|
|
101
|
-
type GetUserInput = Parameters<typeof caller.api<'getUser'>>[1];
|
|
102
|
-
|
|
103
|
-
expect(caller).toBeDefined();
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// ============================================================================
|
|
108
|
-
// Test 2: Multiple Plugins
|
|
109
|
-
// ============================================================================
|
|
110
|
-
|
|
111
|
-
describe('Plugin System - Multiple Plugins', () => {
|
|
112
|
-
it('should allow chaining multiple plugins', async () => {
|
|
113
|
-
const userPlugin = <T>(lambder: Lambder<T>) => {
|
|
114
|
-
return lambder
|
|
115
|
-
.addApi('getUser', {
|
|
116
|
-
input: z.object({ userId: z.string() }),
|
|
117
|
-
output: z.object({ id: z.string(), name: z.string() })
|
|
118
|
-
}, async (ctx, res) => {
|
|
119
|
-
return res.api({ id: ctx.apiPayload.userId, name: 'John' });
|
|
120
|
-
});
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const productPlugin = <T>(lambder: Lambder<T>) => {
|
|
124
|
-
return lambder
|
|
125
|
-
.addApi('getProduct', {
|
|
126
|
-
input: z.object({ productId: z.string() }),
|
|
127
|
-
output: z.object({ id: z.string(), title: z.string(), price: z.number() })
|
|
128
|
-
}, async (ctx, res) => {
|
|
129
|
-
return res.api({
|
|
130
|
-
id: ctx.apiPayload.productId,
|
|
131
|
-
title: 'Test Product',
|
|
132
|
-
price: 99.99
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const orderPlugin = <T>(lambder: Lambder<T>) => {
|
|
138
|
-
return lambder
|
|
139
|
-
.addApi('createOrder', {
|
|
140
|
-
input: z.object({ userId: z.string(), productId: z.string() }),
|
|
141
|
-
output: z.object({ orderId: z.string(), status: z.string() })
|
|
142
|
-
}, async (ctx, res) => {
|
|
143
|
-
return res.api({
|
|
144
|
-
orderId: 'order-123',
|
|
145
|
-
status: 'pending'
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
// Chain multiple plugins
|
|
151
|
-
const lambder = new Lambder({
|
|
152
|
-
publicPath: './public',
|
|
153
|
-
apiPath: '/api'
|
|
154
|
-
})
|
|
155
|
-
.use(userPlugin)
|
|
156
|
-
.use(productPlugin)
|
|
157
|
-
.use(orderPlugin);
|
|
158
|
-
|
|
159
|
-
// Test each API works
|
|
160
|
-
const handler = lambder.getHandler();
|
|
161
|
-
|
|
162
|
-
// Test user API
|
|
163
|
-
const userEvent = createMockEvent('getUser', { userId: '123' });
|
|
164
|
-
const userResult = await handler(userEvent, createMockContext());
|
|
165
|
-
expect(userResult.statusCode).toBe(200);
|
|
166
|
-
expect(JSON.parse(userResult.body || '{}').payload.name).toBe('John');
|
|
167
|
-
|
|
168
|
-
// Test product API
|
|
169
|
-
const productEvent = createMockEvent('getProduct', { productId: 'prod-456' });
|
|
170
|
-
const productResult = await handler(productEvent, createMockContext());
|
|
171
|
-
expect(productResult.statusCode).toBe(200);
|
|
172
|
-
expect(JSON.parse(productResult.body || '{}').payload.title).toBe('Test Product');
|
|
173
|
-
|
|
174
|
-
// Test order API
|
|
175
|
-
const orderEvent = createMockEvent('createOrder', { userId: '123', productId: 'prod-456' });
|
|
176
|
-
const orderResult = await handler(orderEvent, createMockContext());
|
|
177
|
-
expect(orderResult.statusCode).toBe(200);
|
|
178
|
-
expect(JSON.parse(orderResult.body || '{}').payload.orderId).toBe('order-123');
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('should accumulate types from multiple plugins', () => {
|
|
182
|
-
const userPlugin = <T>(lambder: Lambder<T>) => {
|
|
183
|
-
return lambder
|
|
184
|
-
.addApi('getUser', {
|
|
185
|
-
input: z.object({ userId: z.string() }),
|
|
186
|
-
output: z.object({ id: z.string(), name: z.string() })
|
|
187
|
-
}, async (ctx, res) => {
|
|
188
|
-
return res.api({ id: ctx.apiPayload.userId, name: 'Test' });
|
|
189
|
-
});
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const productPlugin = <T>(lambder: Lambder<T>) => {
|
|
193
|
-
return lambder
|
|
194
|
-
.addApi('getProduct', {
|
|
195
|
-
input: z.object({ productId: z.string() }),
|
|
196
|
-
output: z.object({ id: z.string(), title: z.string() })
|
|
197
|
-
}, async (ctx, res) => {
|
|
198
|
-
return res.api({ id: ctx.apiPayload.productId, title: 'Test' });
|
|
199
|
-
});
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const lambder = new Lambder({
|
|
203
|
-
publicPath: './public',
|
|
204
|
-
apiPath: '/api'
|
|
205
|
-
})
|
|
206
|
-
.use(userPlugin)
|
|
207
|
-
.use(productPlugin);
|
|
208
|
-
|
|
209
|
-
type AppContract = typeof lambder.ApiContract;
|
|
210
|
-
|
|
211
|
-
const caller = new LambderCaller<AppContract>({
|
|
212
|
-
apiPath: '/api',
|
|
213
|
-
isCorsEnabled: false
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// Type check - both APIs should be available
|
|
217
|
-
type GetUserInput = Parameters<typeof caller.api<'getUser'>>[1];
|
|
218
|
-
type GetProductInput = Parameters<typeof caller.api<'getProduct'>>[1];
|
|
219
|
-
|
|
220
|
-
expect(caller).toBeDefined();
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
// ============================================================================
|
|
225
|
-
// Test 3: Plugin with Additional APIs
|
|
226
|
-
// ============================================================================
|
|
227
|
-
|
|
228
|
-
describe('Plugin System - Mixed Usage', () => {
|
|
229
|
-
it('should allow mixing direct API addition and plugins', async () => {
|
|
230
|
-
const userPlugin = <T>(lambder: Lambder<T>) => {
|
|
231
|
-
return lambder
|
|
232
|
-
.addApi('getUser', {
|
|
233
|
-
input: z.object({ userId: z.string() }),
|
|
234
|
-
output: z.object({ id: z.string(), name: z.string() })
|
|
235
|
-
}, async (ctx, res) => {
|
|
236
|
-
return res.api({ id: ctx.apiPayload.userId, name: 'John' });
|
|
237
|
-
});
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
const lambder = new Lambder({
|
|
241
|
-
publicPath: './public',
|
|
242
|
-
apiPath: '/api'
|
|
243
|
-
})
|
|
244
|
-
// Direct API
|
|
245
|
-
.addApi('healthCheck', {
|
|
246
|
-
input: z.void(),
|
|
247
|
-
output: z.object({ status: z.string() })
|
|
248
|
-
}, async (ctx, res) => {
|
|
249
|
-
return res.api({ status: 'ok' });
|
|
250
|
-
})
|
|
251
|
-
// Plugin
|
|
252
|
-
.use(userPlugin)
|
|
253
|
-
// Another direct API
|
|
254
|
-
.addApi('getVersion', {
|
|
255
|
-
input: z.void(),
|
|
256
|
-
output: z.object({ version: z.string() })
|
|
257
|
-
}, async (ctx, res) => {
|
|
258
|
-
return res.api({ version: '2.0' });
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
const handler = lambder.getHandler();
|
|
262
|
-
|
|
263
|
-
// Test direct API before plugin
|
|
264
|
-
const healthEvent = createMockEvent('healthCheck', undefined);
|
|
265
|
-
const healthResult = await handler(healthEvent, createMockContext());
|
|
266
|
-
expect(JSON.parse(healthResult.body || '{}').payload.status).toBe('ok');
|
|
267
|
-
|
|
268
|
-
// Test plugin API
|
|
269
|
-
const userEvent = createMockEvent('getUser', { userId: '123' });
|
|
270
|
-
const userResult = await handler(userEvent, createMockContext());
|
|
271
|
-
expect(JSON.parse(userResult.body || '{}').payload.name).toBe('John');
|
|
272
|
-
|
|
273
|
-
// Test direct API after plugin
|
|
274
|
-
const versionEvent = createMockEvent('getVersion', undefined);
|
|
275
|
-
const versionResult = await handler(versionEvent, createMockContext());
|
|
276
|
-
expect(JSON.parse(versionResult.body || '{}').payload.version).toBe('2.0');
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
// ============================================================================
|
|
281
|
-
// Test 4: Plugin with Routes
|
|
282
|
-
// ============================================================================
|
|
283
|
-
|
|
284
|
-
describe('Plugin System - Routes', () => {
|
|
285
|
-
it('should allow plugins to add routes', async () => {
|
|
286
|
-
const healthPlugin = <T>(lambder: Lambder<T>) => {
|
|
287
|
-
// Now addRoute is chainable!
|
|
288
|
-
return lambder
|
|
289
|
-
.addRoute('/health', (ctx, res) => {
|
|
290
|
-
return res.json({ status: 'healthy' });
|
|
291
|
-
})
|
|
292
|
-
.addRoute('/version', (ctx, res) => {
|
|
293
|
-
return res.json({ version: '2.0' });
|
|
294
|
-
});
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
const lambder = new Lambder({
|
|
298
|
-
publicPath: './public',
|
|
299
|
-
apiPath: '/api'
|
|
300
|
-
}).use(healthPlugin);
|
|
301
|
-
|
|
302
|
-
const handler = lambder.getHandler();
|
|
303
|
-
|
|
304
|
-
// Create GET request event
|
|
305
|
-
const healthEvent: APIGatewayProxyEvent = {
|
|
306
|
-
...createMockEvent('', {}),
|
|
307
|
-
httpMethod: 'GET',
|
|
308
|
-
path: '/health'
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
const result = await handler(healthEvent, createMockContext());
|
|
312
|
-
expect(result.statusCode).toBe(200);
|
|
313
|
-
expect(JSON.parse(result.body || '{}').status).toBe('healthy');
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
// ============================================================================
|
|
318
|
-
// Test 5: Complex Plugin Composition
|
|
319
|
-
// ============================================================================
|
|
320
|
-
|
|
321
|
-
describe('Plugin System - Complex Composition', () => {
|
|
322
|
-
it('should support nested plugins (plugin that uses another plugin)', async () => {
|
|
323
|
-
const basePlugin = <T>(lambder: Lambder<T>) => {
|
|
324
|
-
return lambder
|
|
325
|
-
.addApi('base', {
|
|
326
|
-
input: z.void(),
|
|
327
|
-
output: z.object({ value: z.string() })
|
|
328
|
-
}, async (ctx, res) => {
|
|
329
|
-
return res.api({ value: 'base' });
|
|
330
|
-
});
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
const extendedPlugin = <T>(lambder: Lambder<T>) => {
|
|
334
|
-
return lambder
|
|
335
|
-
.use(basePlugin)
|
|
336
|
-
.addApi('extended', {
|
|
337
|
-
input: z.void(),
|
|
338
|
-
output: z.object({ value: z.string() })
|
|
339
|
-
}, async (ctx, res) => {
|
|
340
|
-
return res.api({ value: 'extended' });
|
|
341
|
-
});
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
const lambder = new Lambder({
|
|
345
|
-
publicPath: './public',
|
|
346
|
-
apiPath: '/api'
|
|
347
|
-
}).use(extendedPlugin);
|
|
348
|
-
|
|
349
|
-
const handler = lambder.getHandler();
|
|
350
|
-
|
|
351
|
-
// Both base and extended APIs should work
|
|
352
|
-
const baseEvent = createMockEvent('base', undefined);
|
|
353
|
-
const baseResult = await handler(baseEvent, createMockContext());
|
|
354
|
-
expect(JSON.parse(baseResult.body || '{}').payload.value).toBe('base');
|
|
355
|
-
|
|
356
|
-
const extendedEvent = createMockEvent('extended', undefined);
|
|
357
|
-
const extendedResult = await handler(extendedEvent, createMockContext());
|
|
358
|
-
expect(JSON.parse(extendedResult.body || '{}').payload.value).toBe('extended');
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
it('should allow plugins to be reusable across different lambder instances', async () => {
|
|
362
|
-
const sharedPlugin = <T>(lambder: Lambder<T>) => {
|
|
363
|
-
return lambder
|
|
364
|
-
.addApi('shared', {
|
|
365
|
-
input: z.object({ id: z.string() }),
|
|
366
|
-
output: z.object({ id: z.string(), source: z.string() })
|
|
367
|
-
}, async (ctx, res) => {
|
|
368
|
-
return res.api({ id: ctx.apiPayload.id, source: 'shared-plugin' });
|
|
369
|
-
});
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
// Use same plugin in two different instances
|
|
373
|
-
const lambder1 = new Lambder({
|
|
374
|
-
publicPath: './public',
|
|
375
|
-
apiPath: '/api'
|
|
376
|
-
}).use(sharedPlugin);
|
|
377
|
-
|
|
378
|
-
const lambder2 = new Lambder({
|
|
379
|
-
publicPath: './public',
|
|
380
|
-
apiPath: '/api'
|
|
381
|
-
}).use(sharedPlugin);
|
|
382
|
-
|
|
383
|
-
// Both should work independently
|
|
384
|
-
const event = createMockEvent('shared', { id: 'test-123' });
|
|
385
|
-
|
|
386
|
-
const result1 = await lambder1.getHandler()(event, createMockContext());
|
|
387
|
-
const result2 = await lambder2.getHandler()(event, createMockContext());
|
|
388
|
-
|
|
389
|
-
expect(JSON.parse(result1.body || '{}').payload.source).toBe('shared-plugin');
|
|
390
|
-
expect(JSON.parse(result2.body || '{}').payload.source).toBe('shared-plugin');
|
|
391
|
-
});
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
// ============================================================================
|
|
395
|
-
// Test 6: Plugin Type Safety Edge Cases
|
|
396
|
-
// ============================================================================
|
|
397
|
-
|
|
398
|
-
describe('Plugin System - Type Safety', () => {
|
|
399
|
-
it('should maintain type safety through plugin chain', () => {
|
|
400
|
-
const plugin1 = <T>(lambder: Lambder<T>) => {
|
|
401
|
-
return lambder
|
|
402
|
-
.addApi('api1', {
|
|
403
|
-
input: z.object({ value: z.string() }),
|
|
404
|
-
output: z.object({ result: z.string() })
|
|
405
|
-
}, async (ctx, res) => {
|
|
406
|
-
return res.api({ result: ctx.apiPayload.value });
|
|
407
|
-
});
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
const plugin2 = <T>(lambder: Lambder<T>) => {
|
|
411
|
-
return lambder
|
|
412
|
-
.addApi('api2', {
|
|
413
|
-
input: z.object({ count: z.number() }),
|
|
414
|
-
output: z.object({ total: z.number() })
|
|
415
|
-
}, async (ctx, res) => {
|
|
416
|
-
return res.api({ total: ctx.apiPayload.count * 2 });
|
|
417
|
-
});
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
const lambder = new Lambder({
|
|
421
|
-
publicPath: './public',
|
|
422
|
-
apiPath: '/api'
|
|
423
|
-
})
|
|
424
|
-
.use(plugin1)
|
|
425
|
-
.use(plugin2);
|
|
426
|
-
|
|
427
|
-
type Contract = typeof lambder.ApiContract;
|
|
428
|
-
|
|
429
|
-
// Type assertions - both api1 and api2 should be in the contract
|
|
430
|
-
// We test this at runtime by creating a caller
|
|
431
|
-
const caller = new LambderCaller<Contract>({
|
|
432
|
-
apiPath: '/api',
|
|
433
|
-
isCorsEnabled: false
|
|
434
|
-
});
|
|
435
|
-
expect(caller).toBeDefined();
|
|
436
|
-
});
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
// ============================================================================
|
|
440
|
-
// Test 7: Non-Generic Plugin Type Accumulation
|
|
441
|
-
// ============================================================================
|
|
442
|
-
|
|
443
|
-
describe('Plugin System - Non-Generic Plugins', () => {
|
|
444
|
-
it('should accumulate types correctly when using non-generic plugins', () => {
|
|
445
|
-
const plugin1 = (l: Lambder) => l.addApi('api1', { input: z.void(), output: z.void() }, async (ctx, res) => res.raw({ statusCode: 200, body: '' }));
|
|
446
|
-
const plugin2 = (l: Lambder) => l.addApi('api2', { input: z.void(), output: z.void() }, async (ctx, res) => res.raw({ statusCode: 200, body: '' }));
|
|
447
|
-
|
|
448
|
-
const lambder = new Lambder({ publicPath: '', apiPath: '' })
|
|
449
|
-
.addApi('initialApi', { input: z.void(), output: z.void() }, async (ctx, res) => res.raw({ statusCode: 200, body: '' }))
|
|
450
|
-
.use(plugin1)
|
|
451
|
-
.use(plugin2);
|
|
452
|
-
|
|
453
|
-
type Contract = typeof lambder.ApiContract;
|
|
454
|
-
|
|
455
|
-
// Check if both api1 and api2 exist in Contract
|
|
456
|
-
expectTypeOf<Contract>().toHaveProperty('initialApi');
|
|
457
|
-
expectTypeOf<Contract>().toHaveProperty('api1');
|
|
458
|
-
expectTypeOf<Contract>().toHaveProperty('api2');
|
|
459
|
-
});
|
|
460
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ESNext", // Compile to a modern ECMAScript version that supports modules
|
|
4
|
-
"module": "ESNext", // Use ESNext as module code generation to support ESM
|
|
5
|
-
"declaration": true, // Generate corresponding '.d.ts' file.
|
|
6
|
-
"outDir": "./dist", // Redirect output structure to the 'dist' directory
|
|
7
|
-
"strict": true, // Enable all strict type-checking options
|
|
8
|
-
"esModuleInterop": true, // Enables compatibility with Babel imports
|
|
9
|
-
"skipLibCheck": true, // Skip type checking of all declaration files (*.d.ts)
|
|
10
|
-
"forceConsistentCasingInFileNames": true,
|
|
11
|
-
"moduleResolution": "node", // Module resolution strategy to use
|
|
12
|
-
"resolveJsonModule": true, // Allow importing of '.json' files
|
|
13
|
-
"isolatedModules": true, // Ensure each file can be safely transpiled without relying on other imports
|
|
14
|
-
"baseUrl": ".", // Base directory to resolve non-relative module names
|
|
15
|
-
"paths": { // Specify paths for imports
|
|
16
|
-
"*": ["node_modules/*"]
|
|
17
|
-
},
|
|
18
|
-
"lib": ["ESNext", "DOM"], // Specify library files to be included in the compilation
|
|
19
|
-
"types": ["node"] // Type definitions to be included
|
|
20
|
-
},
|
|
21
|
-
"include": ["src/**/*"], // Include all files in the src directory
|
|
22
|
-
"exclude": ["node_modules", "dist"] // Exclude 'node_modules' directory and 'dist' directory
|
|
23
|
-
}
|
|
24
|
-
|