1
+ /**
2
+ * This contains:
3
+ * - Static type checks to verify the Spec's types are compatible with the SDK's types
4
+ * (mutually assignable, w/ slight affordances to get rid of ZodObject.passthrough() index signatures, etc)
5
+ * - Runtime checks to verify all Spec types have a static check
6
+ * (a few don't have SDK types, see TODOs in this file)
7
+ */
1
8
import * as SDKTypes from "./types.js" ;
2
9
import * as SpecTypes from "./spec.types.js" ;
3
10
4
11
/* eslint-disable @typescript-eslint/no-unused-vars */
5
12
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
6
13
/* eslint-disable @typescript-eslint/no-require-imports */
7
14
8
- // Deep version that recursively removes index signatures (caused by ZodObject.passthrough()) and turns unknowns into `object | undefined`
9
- // TODO: make string index mapping tighter
10
- // TODO: split into multiple transformations if needed
15
+ // Removes index signatures added by ZodObject.passthrough().
11
16
type RemovePassthrough < T > = T extends object
12
17
? T extends Array < infer U >
13
18
? Array < RemovePassthrough < U > >
14
19
: T extends Function
15
20
? T
16
21
: { [ K in keyof T as string extends K ? never : K ] : RemovePassthrough < T [ K ] > }
17
- : T ;
22
+ : T ;
23
+
24
+ type IsUnknown < T > = [ unknown ] extends [ T ] ? [ T ] extends [ unknown ] ? true : false : false ;
25
+
26
+ // Turns {x?: unknown} into {x: unknown} but keeps {_meta?: unknown} unchanged (and leaves other optional properties unchanged, e.g. {x?: string}).
27
+ // This works around an apparent quirk of ZodObject.unknown() (makes fields optional)
28
+ type MakeUnknownsNotOptional < T > =
29
+ IsUnknown < T > extends true
30
+ ? unknown
31
+ : ( T extends object
32
+ ? ( T extends Array < infer U >
33
+ ? Array < MakeUnknownsNotOptional < U > >
34
+ : ( T extends Function
35
+ ? T
36
+ : Pick < T , never > & {
37
+ // Start with empty object to avoid duplicates
38
+ // Make unknown properties required (except _meta)
39
+ [ K in keyof T as '_meta' extends K ? never : IsUnknown < T [ K ] > extends true ? K : never ] -?: unknown ;
40
+ } &
41
+ Pick < T , {
42
+ // Pick all _meta and non-unknown properties with original modifiers
43
+ [ K in keyof T ] : '_meta' extends K ? K : IsUnknown < T [ K ] > extends true ? never : K
44
+ } [ keyof T ] > & {
45
+ // Recurse on the picked properties
46
+ [ K in keyof Pick < T , { [ K in keyof T ] : '_meta' extends K ? K : IsUnknown < T [ K ] > extends true ? never : K } [ keyof T ] > ] : MakeUnknownsNotOptional < T [ K ] >
47
+ } ) )
48
+ : T ) ;
18
49
19
50
function checkCancelledNotification (
20
51
sdk : SDKTypes . CancelledNotification ,
@@ -36,7 +67,7 @@ function checkImplementation(
36
67
) {
37
68
sdk = spec ;
38
69
spec = sdk ;
39
- }
70
+ }
40
71
function checkProgressNotification (
41
72
sdk : SDKTypes . ProgressNotification ,
42
73
spec : SpecTypes . ProgressNotification
@@ -564,70 +595,70 @@ function checkJSONRPCMessage(
564
595
spec = sdk ;
565
596
}
566
597
function checkCreateMessageRequest (
567
- sdk : RemovePassthrough < SDKTypes . CreateMessageRequest > , // TODO(quirk): some {} typ>e
598
+ sdk : RemovePassthrough < SDKTypes . CreateMessageRequest > ,
568
599
spec : SpecTypes . CreateMessageRequest
569
600
) {
570
601
sdk = spec ;
571
602
spec = sdk ;
572
603
}
573
604
function checkInitializeRequest (
574
- sdk : RemovePassthrough < SDKTypes . InitializeRequest > , // TODO(quirk): some {} type
605
+ sdk : RemovePassthrough < SDKTypes . InitializeRequest > ,
575
606
spec : SpecTypes . InitializeRequest
576
607
) {
577
608
sdk = spec ;
578
609
spec = sdk ;
579
610
}
580
611
function checkInitializeResult (
581
- sdk : RemovePassthrough < SDKTypes . InitializeResult > , // TODO(quirk): some {} type
612
+ sdk : RemovePassthrough < SDKTypes . InitializeResult > ,
582
613
spec : SpecTypes . InitializeResult
583
614
) {
584
615
sdk = spec ;
585
616
spec = sdk ;
586
617
}
587
618
function checkClientCapabilities (
588
- sdk : RemovePassthrough < SDKTypes . ClientCapabilities > , // TODO(quirk): {}
619
+ sdk : RemovePassthrough < SDKTypes . ClientCapabilities > ,
589
620
spec : SpecTypes . ClientCapabilities
590
621
) {
591
622
sdk = spec ;
592
623
spec = sdk ;
593
624
}
594
625
function checkServerCapabilities (
595
- sdk : RemovePassthrough < SDKTypes . ServerCapabilities > , // TODO(quirk): {}
626
+ sdk : RemovePassthrough < SDKTypes . ServerCapabilities > ,
596
627
spec : SpecTypes . ServerCapabilities
597
628
) {
598
629
sdk = spec ;
599
630
spec = sdk ;
600
631
}
601
632
function checkClientRequest (
602
- sdk : RemovePassthrough < SDKTypes . ClientRequest > , // TODO(quirk): capabilities.logging is {}
633
+ sdk : RemovePassthrough < SDKTypes . ClientRequest > ,
603
634
spec : SpecTypes . ClientRequest
604
635
) {
605
636
sdk = spec ;
606
637
spec = sdk ;
607
638
}
608
639
function checkServerRequest (
609
- sdk : RemovePassthrough < SDKTypes . ServerRequest > , // TODO(quirk): some {} typ
640
+ sdk : RemovePassthrough < SDKTypes . ServerRequest > ,
610
641
spec : SpecTypes . ServerRequest
611
642
) {
612
643
sdk = spec ;
613
644
spec = sdk ;
614
645
}
615
646
function checkLoggingMessageNotification (
616
- sdk : SDKTypes . LoggingMessageNotification ,
647
+ sdk : MakeUnknownsNotOptional < SDKTypes . LoggingMessageNotification > ,
617
648
spec : SpecTypes . LoggingMessageNotification
618
649
) {
619
650
sdk = spec ;
620
- // spec = sdk; // TODO(bug): data is optional
651
+ spec = sdk ;
621
652
}
622
653
function checkServerNotification (
623
- sdk : SDKTypes . ServerNotification ,
654
+ sdk : MakeUnknownsNotOptional < SDKTypes . ServerNotification > ,
624
655
spec : SpecTypes . ServerNotification
625
656
) {
626
657
sdk = spec ;
627
- // spec = sdk; // TODO(bug): data is optional
658
+ spec = sdk ;
628
659
}
629
660
630
- // TODO(bug): missing type in SDK
661
+ // TODO(bug): missing type in SDK. This dead code is checked by the test suite below.
631
662
// function checkModelHint(
632
663
// RemovePassthrough< sdk: SDKTypes.ModelHint>,
633
664
// spec: SpecTypes.ModelHint
@@ -636,7 +667,7 @@ function checkServerNotification(
636
667
// spec = sdk;
637
668
// }
638
669
639
- // TODO(bug): missing type in SDK
670
+ // TODO(bug): missing type in SDK. This dead code is checked by the test suite below.
640
671
// function checkModelPreferences(
641
672
// RemovePassthrough< sdk: SDKTypes.ModelPreferences>,
642
673
// spec: SpecTypes.ModelPreferences
@@ -645,7 +676,7 @@ function checkServerNotification(
645
676
// spec = sdk;
646
677
// }
647
678
648
- // TODO(bug): missing type in SDK
679
+ // TODO(bug): missing type in SDK. This dead code is checked by the test suite below.
649
680
// function checkAnnotations(
650
681
// RemovePassthrough< sdk: SDKTypes.Annotations>,
651
682
// spec: SpecTypes.Annotations
@@ -661,7 +692,7 @@ describe('Spec Types', () => {
661
692
const specTypesContent = require ( 'fs' ) . readFileSync ( SPEC_TYPES_FILE , 'utf-8' ) ;
662
693
const typeNames = [ ...specTypesContent . matchAll ( / e x p o r t \s + i n t e r f a c e \s + ( \w + ) \b / g) ] . map ( m => m [ 1 ] ) ;
663
694
const testContent = require ( 'fs' ) . readFileSync ( THIS_SOURCE_FILE , 'utf-8' ) ;
664
-
695
+
665
696
it ( 'should define some expected types' , ( ) => {
666
697
expect ( typeNames ) . toContain ( 'JSONRPCNotification' ) ;
667
698
expect ( typeNames ) . toContain ( 'ElicitResult' ) ;
0 commit comments