@@ -60,6 +60,26 @@ function resolveRecordOutput(record: StaticRpcRecord): any {
6060 return record . output
6161}
6262
63+ // Placeholder args (`[null]`/`[undefined]`) from framework setup hooks carry no
64+ // addressing info and must be treated as a no-arg call.
65+ function hasMeaningfulArgs ( args : any [ ] ) : boolean {
66+ return args . some ( arg => arg !== null && arg !== undefined )
67+ }
68+
69+ // `collectStaticRpcDump`/`StaticRpcDumpFile` are public, so consumers may persist
70+ // the whole `{ serialization, fnName, data }` envelope instead of just `data`.
71+ function unwrapEnvelope ( raw : unknown ) : unknown {
72+ if (
73+ raw !== null
74+ && typeof raw === 'object'
75+ && 'serialization' in raw
76+ && 'data' in raw
77+ ) {
78+ return ( raw as { data : unknown } ) . data
79+ }
80+ return raw
81+ }
82+
6383export function createStaticRpcCaller (
6484 manifest : StaticRpcManifest ,
6585 fetchJson : ( path : string ) => Promise < any > ,
@@ -68,16 +88,22 @@ export function createStaticRpcCaller(
6888 const queryRecordCache = new Map < string , Promise < StaticRpcRecord > > ( )
6989
7090 function reviveIfStructuredClone ( value : unknown , serialization : StaticRpcSerialization | undefined ) : any {
71- if ( serialization === 'structured-clone' )
72- return structuredCloneDeserialize ( value as any )
91+ // structured-clone-es always encodes to a records array; a non-array here
92+ // means the payload was not SC-encoded, so pass it through untouched.
93+ if ( serialization === 'structured-clone' && Array . isArray ( value ) )
94+ return structuredCloneDeserialize ( value )
7395 return value
7496 }
7597
98+ function decode ( raw : unknown , serialization : StaticRpcSerialization | undefined ) : any {
99+ return reviveIfStructuredClone ( unwrapEnvelope ( raw ) , serialization )
100+ }
101+
76102 async function loadStatic ( entry : StaticRpcManifestStaticEntry ) : Promise < any > {
77103 if ( ! staticCache . has ( entry . path ) ) {
78104 staticCache . set (
79105 entry . path ,
80- fetchJson ( entry . path ) . then ( raw => reviveIfStructuredClone ( raw , entry . serialization ) ) ,
106+ fetchJson ( entry . path ) . then ( raw => decode ( raw , entry . serialization ) ) ,
81107 )
82108 }
83109 const data = await staticCache . get ( entry . path ) !
@@ -94,7 +120,7 @@ export function createStaticRpcCaller(
94120 if ( ! queryRecordCache . has ( path ) ) {
95121 queryRecordCache . set (
96122 path ,
97- fetchJson ( path ) . then ( raw => reviveIfStructuredClone ( raw , serialization ) ) ,
123+ fetchJson ( path ) . then ( raw => decode ( raw , serialization ) ) ,
98124 )
99125 }
100126 return await queryRecordCache . get ( path ) !
@@ -107,7 +133,7 @@ export function createStaticRpcCaller(
107133
108134 const entry = manifest [ functionName ]
109135 if ( isStaticEntry ( entry ) ) {
110- if ( args . length > 0 ) {
136+ if ( hasMeaningfulArgs ( args ) ) {
111137 throw new Error (
112138 `[devframe-rpc] No dump match for "${ functionName } " with args: ${ JSON . stringify ( args ) } ` ,
113139 )
@@ -134,7 +160,7 @@ export function createStaticRpcCaller(
134160 )
135161 }
136162
137- if ( args . length === 0 ) {
163+ if ( ! hasMeaningfulArgs ( args ) ) {
138164 return entry
139165 }
140166
0 commit comments