@@ -3,6 +3,8 @@ export type ManifestType = {
33 name ?: string ;
44 methods ?: MethodType [ ] ;
55 classes ?: ClassType [ ] ;
6+ prototypes ?: MethodType [ ] ;
7+ enums ?: EnumType [ ] ;
68}
79
810export type BaseType = {
@@ -29,8 +31,8 @@ export type MethodType = BaseType & {
2931export type ParamType = BaseType & {
3032 type ?: string ;
3133 ref ?: boolean ;
32- prototype ?: MethodType ;
33- enum ?: EnumType ;
34+ prototype ?: MethodType | string ;
35+ enum ?: EnumType | string ;
3436} ;
3537
3638export type AliasType = BaseType & {
@@ -56,3 +58,79 @@ export type MethodMap = Record<string, MethodType>;
5658export type EnumMap = Record < string , EnumType > ;
5759export type ClassMap = Record < string , ClassType > ;
5860
61+ /**
62+ * Links every by-name prototype/enum reference in a manifest to its definition,
63+ * and hoists inline definitions into the shared tables, so that afterwards every
64+ * `paramTypes`/`retType` entry carries a full object.
65+ *
66+ * Mirrors Manifest::Resolve in plugify core. Unlike the loader this is forgiving:
67+ * a reference that names nothing is left as it was rather than rejected, since a
68+ * viewer showing most of a page beats one showing an error.
69+ */
70+ export function resolveManifest ( manifest : ManifestType ) : ManifestType {
71+ const prototypes : MethodMap = { } ;
72+ const enums : EnumMap = { } ;
73+
74+ for ( const prototype of manifest . prototypes ?? [ ] ) {
75+ if ( prototype . name ) prototypes [ prototype . name ] = prototype ;
76+ }
77+ for ( const enumerator of manifest . enums ?? [ ] ) {
78+ if ( enumerator . name ) enums [ enumerator . name ] = enumerator ;
79+ }
80+
81+ // Pass one: collect the definitions written inline.
82+ const collect = ( param : ParamType , seen : Set < MethodType > ) => {
83+ const prototype = param . prototype ;
84+ if ( prototype && typeof prototype !== 'string' && prototype . name ) {
85+ prototypes [ prototype . name ] ??= prototype ;
86+ if ( ! seen . has ( prototype ) ) {
87+ seen . add ( prototype ) ;
88+ for ( const nested of prototype . paramTypes ?? [ ] ) collect ( nested , seen ) ;
89+ if ( prototype . retType ) collect ( prototype . retType , seen ) ;
90+ }
91+ }
92+ const enumerator = param . enum ;
93+ if ( enumerator && typeof enumerator !== 'string' && enumerator . name && enumerator . values ?. length ) {
94+ enums [ enumerator . name ] ??= enumerator ;
95+ }
96+ } ;
97+
98+ const seen = new Set < MethodType > ( ) ;
99+ for ( const method of manifest . methods ?? [ ] ) {
100+ for ( const param of method . paramTypes ?? [ ] ) collect ( param , seen ) ;
101+ if ( method . retType ) collect ( method . retType , seen ) ;
102+ }
103+ for ( const prototype of manifest . prototypes ?? [ ] ) {
104+ for ( const param of prototype . paramTypes ?? [ ] ) collect ( param , seen ) ;
105+ if ( prototype . retType ) collect ( prototype . retType , seen ) ;
106+ }
107+
108+ // Pass two: swap each reference for the definition it names. Every definition
109+ // is known by now, so references may point forwards.
110+ const link = ( param : ParamType ) => {
111+ if ( typeof param . prototype === 'string' ) {
112+ param . prototype = prototypes [ param . prototype ] ?? param . prototype ;
113+ }
114+ if ( typeof param . enum === 'string' ) {
115+ param . enum = enums [ param . enum ] ?? param . enum ;
116+ }
117+ } ;
118+
119+ const linkOwner = ( owner : MethodType ) => {
120+ for ( const param of owner . paramTypes ?? [ ] ) link ( param ) ;
121+ if ( owner . retType ) link ( owner . retType ) ;
122+ } ;
123+
124+ for ( const method of manifest . methods ?? [ ] ) linkOwner ( method ) ;
125+ for ( const prototype of Object . values ( prototypes ) ) linkOwner ( prototype ) ;
126+
127+ manifest . prototypes = Object . keys ( prototypes ) . sort ( ) . map ( name => prototypes [ name ] ) ;
128+ manifest . enums = Object . keys ( enums ) . sort ( ) . map ( name => enums [ name ] ) ;
129+ return manifest ;
130+ }
131+
132+ /** Narrows a resolved prototype/enum field to its definition. */
133+ export function definitionOf < T extends BaseType > ( value : T | string | undefined ) : T | undefined {
134+ return typeof value === 'string' ? undefined : value ;
135+ }
136+
0 commit comments