@@ -8,118 +8,12 @@ function isPlainObject(value) {
88 return ! ! value && typeof value === "object" && ! Array . isArray ( value ) ;
99}
1010
11- function isParentJsonLike ( value ) {
12- if ( ! isPlainObject ( value ) ) {
13- return false ;
14- }
15- const documentKind = typeof value . documentKind === "string" ? value . documentKind . trim ( ) . toLowerCase ( ) : "" ;
16- const schema = typeof value . schema === "string" ? value . schema . trim ( ) . toLowerCase ( ) : "" ;
17- if ( documentKind === "workspace-manifest" || schema === "html-js-gaming.project" || schema === "html-js-gaming.workspace" ) {
18- return true ;
19- }
20- return isPlainObject ( value . tools ) ;
21- }
22-
23- function hasImplicitGlobalKey ( value ) {
24- if ( ! isPlainObject ( value ) ) {
25- return false ;
26- }
27- const blockedKeys = new Set ( [
28- "launchparams" ,
29- "sharedcontext" ,
30- "hostcontextid" ,
31- "hosttoolid" ,
32- "hosted" ,
33- "sampleid" ,
34- "sampletitle" ,
35- "samplepresetpath" ,
36- "gameid" ,
37- "gametitle" ,
38- "gamehref" ,
39- "workspacehref" ,
40- "returnto" ,
41- "state"
42- ] ) ;
43- return Object . keys ( value ) . some ( ( key ) => blockedKeys . has ( String ( key ) . trim ( ) . toLowerCase ( ) ) ) ;
44- }
45-
46- function isWrapperJsonLike ( value ) {
47- if ( ! isPlainObject ( value ) ) {
48- return false ;
49- }
50- const keys = Object . keys ( value ) . map ( ( key ) => String ( key ) . trim ( ) . toLowerCase ( ) ) ;
51- if ( keys . includes ( "payloadjson" ) || keys . includes ( "palettejson" ) ) {
52- return true ;
53- }
54- if ( keys . includes ( "wrapper" ) || keys . includes ( "wrapped" ) ) {
55- return true ;
56- }
57- if ( keys . includes ( "payload" ) && isPlainObject ( value . payload ) ) {
58- return true ;
59- }
60- if ( keys . includes ( "palette" ) && isPlainObject ( value . palette ) ) {
61- return true ;
62- }
63- return false ;
64- }
65-
66- function hasFallbackAttempt ( value ) {
67- const blockedPrefixes = [ "default" , "fallback" , "tryloadpreset" , "buildpreset" ] ;
68- const stack = [ value ] ;
69- while ( stack . length > 0 ) {
70- const node = stack . pop ( ) ;
71- if ( ! isPlainObject ( node ) && ! Array . isArray ( node ) ) {
72- continue ;
73- }
74- if ( Array . isArray ( node ) ) {
75- node . forEach ( ( entry ) => stack . push ( entry ) ) ;
76- continue ;
77- }
78- Object . entries ( node ) . forEach ( ( [ key , nestedValue ] ) => {
79- const normalized = String ( key ) . trim ( ) . toLowerCase ( ) ;
80- if ( blockedPrefixes . some ( ( prefix ) => normalized . startsWith ( prefix ) ) ) {
81- throw new Error ( "launch contract violation: fallback attempt detected in input JSON." ) ;
82- }
83- stack . push ( nestedValue ) ;
84- } ) ;
85- }
86- return false ;
87- }
88-
89- function computeInputFingerprint ( value , label ) {
90- try {
91- return JSON . stringify ( value ) ;
92- } catch {
93- throw new Error ( `launch contract violation: ${ label } must be JSON-serializable.` ) ;
94- }
95- }
96-
9711export function validateInput ( payloadJson , paletteJson = null ) {
98- const payloadBefore = computeInputFingerprint ( payloadJson , "payloadJson" ) ;
99- const paletteBefore = paletteJson === null ? null : computeInputFingerprint ( paletteJson , "paletteJson" ) ;
10012 if ( ! isPlainObject ( payloadJson ) ) {
10113 throw new Error ( "launch contract violation: missing payloadJson object." ) ;
10214 }
103- if ( paletteJson !== null && ! isPlainObject ( paletteJson ) ) {
104- throw new Error ( "launch contract violation: paletteJson must be an object or null." ) ;
105- }
106- if ( isWrapperJsonLike ( payloadJson ) || ( paletteJson !== null && isWrapperJsonLike ( paletteJson ) ) ) {
107- throw new Error ( "launch contract violation: wrapper JSON detected." ) ;
108- }
109- if ( isParentJsonLike ( payloadJson ) || ( paletteJson !== null && isParentJsonLike ( paletteJson ) ) ) {
110- throw new Error ( "launch contract violation: parent JSON detected." ) ;
111- }
112- if ( hasImplicitGlobalKey ( payloadJson ) || ( paletteJson !== null && hasImplicitGlobalKey ( paletteJson ) ) ) {
113- throw new Error ( "launch contract violation: implicit/global input keys detected." ) ;
114- }
115- hasFallbackAttempt ( payloadJson ) ;
116- if ( paletteJson !== null ) {
117- hasFallbackAttempt ( paletteJson ) ;
118- }
119- const payloadAfter = computeInputFingerprint ( payloadJson , "payloadJson" ) ;
120- const paletteAfter = paletteJson === null ? null : computeInputFingerprint ( paletteJson , "paletteJson" ) ;
121- if ( payloadBefore !== payloadAfter || paletteBefore !== paletteAfter ) {
122- throw new Error ( "launch contract violation: mutation detected during input validation." ) ;
15+ if ( paletteJson && ! isPlainObject ( paletteJson ) ) {
16+ throw new Error ( "launch contract violation: paletteJson must be an object when provided." ) ;
12317 }
12418}
12519
0 commit comments