lambder 1.0.127 → 1.0.129

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.
@@ -197,6 +197,318 @@ describe('Complex Types', () => {
197
197
  });
198
198
  });
199
199
 
200
+ // ============================================================================
201
+ // Test 6: Output Type Enforcement
202
+ // ============================================================================
203
+
204
+ describe('Output Type Enforcement', () => {
205
+ it('should enforce output types in resolver.api()', () => {
206
+ const lambder = new Lambder<TestApiContract>({
207
+ publicPath: './public',
208
+ apiPath: '/api'
209
+ });
210
+
211
+ // ✅ CORRECT: Return correct output type
212
+ lambder.addApi('getUser', async (ctx, resolver) => {
213
+ const userId = ctx.apiPayload.userId;
214
+ return resolver.api({ id: userId, name: 'John Doe' });
215
+ });
216
+
217
+ // ✅ CORRECT: Return null is allowed
218
+ lambder.addApi('getUser', async (ctx, resolver) => {
219
+ return resolver.api(null);
220
+ });
221
+
222
+ // ✅ CORRECT: Boolean output
223
+ lambder.addApi('deleteUser', async (ctx, resolver) => {
224
+ return resolver.api(true);
225
+ });
226
+
227
+ // ✅ CORRECT: Array output
228
+ lambder.addApi('listUsers', async (ctx, resolver) => {
229
+ return resolver.api([
230
+ { id: '1', name: 'User 1' },
231
+ { id: '2', name: 'User 2' }
232
+ ]);
233
+ });
234
+
235
+ // ✅ CORRECT: Empty array
236
+ lambder.addApi('listUsers', async (ctx, resolver) => {
237
+ return resolver.api([]);
238
+ });
239
+
240
+ expect(lambder).toBeDefined();
241
+
242
+ // ❌ These would cause TypeScript errors (commented out):
243
+
244
+ // Wrong output type:
245
+ // lambder.addApi('getUser', async (ctx, resolver) => {
246
+ // return resolver.api("wrong"); // Error: string not assignable to { id: string, name: string }
247
+ // });
248
+
249
+ // Missing required property:
250
+ // lambder.addApi('getUser', async (ctx, resolver) => {
251
+ // return resolver.api({ id: '123' }); // Error: missing 'name' property
252
+ // });
253
+
254
+ // Wrong property type:
255
+ // lambder.addApi('getUser', async (ctx, resolver) => {
256
+ // return resolver.api({ id: 123, name: 'Test' }); // Error: id should be string
257
+ // });
258
+
259
+ // Wrong boolean output:
260
+ // lambder.addApi('deleteUser', async (ctx, resolver) => {
261
+ // return resolver.api("true"); // Error: string not assignable to boolean
262
+ // });
263
+
264
+ // Wrong array element type:
265
+ // lambder.addApi('listUsers', async (ctx, resolver) => {
266
+ // return resolver.api([{ id: '1' }]); // Error: missing 'name' property
267
+ // });
268
+ });
269
+
270
+ it('should enforce output types in resolver.die.api()', () => {
271
+ const lambder = new Lambder<TestApiContract>({
272
+ publicPath: './public',
273
+ apiPath: '/api'
274
+ });
275
+
276
+ // ✅ CORRECT: die.api also enforces types
277
+ lambder.addApi('getUser', async (ctx, resolver) => {
278
+ return resolver.die.api({ id: ctx.apiPayload.userId, name: 'Test' });
279
+ });
280
+
281
+ // ✅ CORRECT: die.api with null
282
+ lambder.addApi('deleteUser', async (ctx, resolver) => {
283
+ return resolver.die.api(true);
284
+ });
285
+
286
+ expect(lambder).toBeDefined();
287
+
288
+ // ❌ These would cause TypeScript errors (commented out):
289
+ // lambder.addApi('getUser', async (ctx, resolver) => {
290
+ // return resolver.die.api("wrong"); // Error: wrong type
291
+ // });
292
+ });
293
+
294
+ it('should enforce output types in addSessionApi()', () => {
295
+ const lambder = new Lambder<TestApiContract>({
296
+ publicPath: './public',
297
+ apiPath: '/api'
298
+ });
299
+
300
+ // ✅ CORRECT: Session APIs also enforce output types
301
+ lambder.addSessionApi('createUser', async (ctx, resolver) => {
302
+ const { name, email } = ctx.apiPayload;
303
+ return resolver.api({ id: '1', name, email });
304
+ });
305
+
306
+ // ✅ CORRECT: Session API with null
307
+ lambder.addSessionApi('deleteUser', async (ctx, resolver) => {
308
+ return resolver.api(true);
309
+ });
310
+
311
+ expect(lambder).toBeDefined();
312
+
313
+ // ❌ These would cause TypeScript errors (commented out):
314
+ // lambder.addSessionApi('createUser', async (ctx, resolver) => {
315
+ // return resolver.api({ id: '1' }); // Error: missing name and email
316
+ // });
317
+ });
318
+ });
319
+
320
+ // ============================================================================
321
+ // Test 7: Complex Output Types
322
+ // ============================================================================
323
+
324
+ type ComplexOutputContract = ApiContract<{
325
+ // Primitive outputs
326
+ getCount: { input: void, output: number },
327
+ getMessage: { input: void, output: string },
328
+ isActive: { input: void, output: boolean },
329
+
330
+ // Nested objects
331
+ getStats: {
332
+ input: void,
333
+ output: {
334
+ users: { total: number, active: number },
335
+ products: { total: number, inStock: number }
336
+ }
337
+ },
338
+
339
+ // Union types
340
+ findUser: {
341
+ input: { email: string },
342
+ output: { id: string, name: string } | null
343
+ },
344
+
345
+ // Array of complex objects
346
+ getOrders: {
347
+ input: { userId: string },
348
+ output: Array<{
349
+ id: string,
350
+ items: Array<{ name: string, price: number }>,
351
+ total: number
352
+ }>
353
+ }
354
+ }>;
355
+
356
+ describe('Complex Output Types', () => {
357
+ it('should enforce primitive output types', () => {
358
+ const lambder = new Lambder<ComplexOutputContract>({
359
+ publicPath: './public',
360
+ apiPath: '/api'
361
+ });
362
+
363
+ // ✅ CORRECT: Primitive outputs
364
+ lambder.addApi('getCount', async (ctx, resolver) => {
365
+ return resolver.api(42);
366
+ });
367
+
368
+ lambder.addApi('getMessage', async (ctx, resolver) => {
369
+ return resolver.api("Hello World");
370
+ });
371
+
372
+ lambder.addApi('isActive', async (ctx, resolver) => {
373
+ return resolver.api(true);
374
+ });
375
+
376
+ expect(lambder).toBeDefined();
377
+
378
+ // ❌ These would cause TypeScript errors (commented out):
379
+ // lambder.addApi('getCount', async (ctx, resolver) => {
380
+ // return resolver.api("42"); // Error: string not assignable to number
381
+ // });
382
+ });
383
+
384
+ it('should enforce nested object types', () => {
385
+ const lambder = new Lambder<ComplexOutputContract>({
386
+ publicPath: './public',
387
+ apiPath: '/api'
388
+ });
389
+
390
+ // ✅ CORRECT: Nested structure
391
+ lambder.addApi('getStats', async (ctx, resolver) => {
392
+ return resolver.api({
393
+ users: { total: 100, active: 75 },
394
+ products: { total: 50, inStock: 40 }
395
+ });
396
+ });
397
+
398
+ expect(lambder).toBeDefined();
399
+
400
+ // ❌ These would cause TypeScript errors (commented out):
401
+ // lambder.addApi('getStats', async (ctx, resolver) => {
402
+ // return resolver.api({
403
+ // users: { total: 100 } // Error: missing 'active'
404
+ // });
405
+ // });
406
+ });
407
+
408
+ it('should enforce union types correctly', () => {
409
+ const lambder = new Lambder<ComplexOutputContract>({
410
+ publicPath: './public',
411
+ apiPath: '/api'
412
+ });
413
+
414
+ // ✅ CORRECT: Return object
415
+ lambder.addApi('findUser', async (ctx, resolver) => {
416
+ return resolver.api({ id: '123', name: 'John' });
417
+ });
418
+
419
+ // ✅ CORRECT: Return null (part of union)
420
+ lambder.addApi('findUser', async (ctx, resolver) => {
421
+ return resolver.api(null);
422
+ });
423
+
424
+ expect(lambder).toBeDefined();
425
+
426
+ // ❌ These would cause TypeScript errors (commented out):
427
+ // lambder.addApi('findUser', async (ctx, resolver) => {
428
+ // return resolver.api(undefined); // Error: undefined not in union
429
+ // });
430
+ });
431
+
432
+ it('should enforce complex nested array types', () => {
433
+ const lambder = new Lambder<ComplexOutputContract>({
434
+ publicPath: './public',
435
+ apiPath: '/api'
436
+ });
437
+
438
+ // ✅ CORRECT: Complex nested array
439
+ lambder.addApi('getOrders', async (ctx, resolver) => {
440
+ return resolver.api([
441
+ {
442
+ id: '1',
443
+ items: [
444
+ { name: 'Item 1', price: 10 },
445
+ { name: 'Item 2', price: 20 }
446
+ ],
447
+ total: 30
448
+ }
449
+ ]);
450
+ });
451
+
452
+ expect(lambder).toBeDefined();
453
+
454
+ // ❌ These would cause TypeScript errors (commented out):
455
+ // lambder.addApi('getOrders', async (ctx, resolver) => {
456
+ // return resolver.api([
457
+ // {
458
+ // id: '1',
459
+ // items: [{ name: 'Item 1' }], // Error: missing 'price'
460
+ // total: 30
461
+ // }
462
+ // ]);
463
+ // });
464
+ });
465
+ });
466
+
467
+ // ============================================================================
468
+ // Test 8: Type Inference
469
+ // ============================================================================
470
+
471
+ describe('Type Inference', () => {
472
+ it('should infer types correctly from contract', () => {
473
+ const lambder = new Lambder<TestApiContract>({
474
+ publicPath: './public',
475
+ apiPath: '/api'
476
+ });
477
+
478
+ // Test that TypeScript correctly infers the types
479
+ lambder.addApi('getUser', async (ctx, resolver) => {
480
+ // ctx.apiPayload type should be inferred as { userId: string }
481
+ const userId: string = ctx.apiPayload.userId;
482
+
483
+ // Variable with explicit type matching contract
484
+ const user: { id: string, name: string } = {
485
+ id: userId,
486
+ name: 'Test User'
487
+ };
488
+
489
+ // Should accept the correctly typed variable
490
+ return resolver.api(user);
491
+ });
492
+
493
+ lambder.addApi('createUser', async (ctx, resolver) => {
494
+ // Input type inference
495
+ const name: string = ctx.apiPayload.name;
496
+ const email: string = ctx.apiPayload.email;
497
+
498
+ // Output type inference
499
+ const result: { id: string, name: string, email: string } = {
500
+ id: '123',
501
+ name,
502
+ email
503
+ };
504
+
505
+ return resolver.api(result);
506
+ });
507
+
508
+ expect(lambder).toBeDefined();
509
+ });
510
+ });
511
+
200
512
  // ============================================================================
201
513
  // If this file compiles without errors, the type system is working! ✅
202
514
  // ============================================================================