2026-09-21 11:23:26 -07:00
import type {
InterfaceRevision ,
InterfaceOperation ,
ExecutionContract ,
PackageRevision ,
} from "../capability-model/types.js" ;
import { canonicalJson } from "../capability-model/evolution.js" ;
import { portableValueSchema , tsType } from "../bindings/portable.js" ;
import { type ExecutionPlan , resolvePlannedOperation , hexDigest , ExecutionPlanError } from "./plan.js" ;
/** Public shape only: no slot inventory, implementation binding, or workspace identity. */
export interface PublicOperationContract {
interface : string ;
operation : string ;
scope : "object" | "class" ;
execution : ExecutionContract ;
input : unknown ;
output : unknown ;
}
export function publicOperationContract (
iface : InterfaceRevision ,
operation : InterfaceOperation ,
) : PublicOperationContract {
if ( iface . template || operation . mode !== "call" || ! iface . members . some (( m ) => m . operations . includes ( operation )))
throw new ExecutionPlanError (
"UNSUPPORTED_CLIENT_CONTRACT" ,
iface . revisionId ,
"Expected a closed interface call operation" ,
);
const execution = operation . execution ?? { kind : "remote" };
return {
interface : iface . revisionId ,
operation : operation.id ,
scope : operation.scope ?? "object" ,
execution : execution.kind === "remote" ? execution : { ... execution , targets : [... execution . targets ]. sort () },
input : portableValueSchema ( operation . inputType ),
output : portableValueSchema ( operation . outputType ),
};
}
/** Resolve a public contract to internal dispatch. This does not authorize the receiver. */
export function linkInterfaceOperation ( plan : ExecutionPlan , atom : string , contract : PublicOperationContract ) {
if ( hexDigest ( plan . definition ) !== plan . digest )
throw new ExecutionPlanError ( "INVALID_PLAN" , atom , "Invalid execution plan digest" );
const iface = plan . definition . workspace . interfaceImports . find (( i ) => i . revisionId === contract . interface );
const operation = iface ? . members . flatMap (( m ) => m . operations ). find (( o ) => o . id === contract . operation );
if ( ! iface || ! operation || canonicalJson ( publicOperationContract ( iface , operation )) !== canonicalJson ( contract ))
throw new ExecutionPlanError (
"CLIENT_CONTRACT_MISMATCH" ,
` ${ contract . interface } / ${ contract . operation } ` ,
"Installed interface differs from the imported operation contract" ,
);
return resolvePlannedOperation ( plan , atom , contract . interface , contract . operation );
}
export interface PublicQueryContract {
package : string ;
query : string ;
definitionDigest : string ;
root : { kind : "object" | "workspace-interface" ; interface : string };
placement : "server" | "replica" | "local" ;
sourceQuery? : string ;
input : unknown ;
output : unknown ;
}
export function publicQueryContract ( pkg : PackageRevision , queryId : string ) : PublicQueryContract {
const query = pkg . checkedQueries ? . find (( q ) => q . declaration . id === queryId );
if ( ! query )
throw new ExecutionPlanError ( "UNCHECKED_QUERY" , ` ${ pkg . revisionId } / ${ queryId } ` , "Expected a checked package query" );
const placement = query . declaration . placement ;
return {
package : pkg . revisionId ,
query : queryId ,
definitionDigest : query.definitionDigest ,
root : query.declaration.root ,
placement : placement?.kind ?? "server" ,
...( placement ? . kind === "local" ? { sourceQuery : placement.sourceQueryId } : {}),
input : portableValueSchema ( query . variables ),
output : portableValueSchema ( query . output ),
};
}
export function linkPackageQuery ( plan : ExecutionPlan , contract : PublicQueryContract ) {
if ( hexDigest ( plan . definition ) !== plan . digest )
throw new ExecutionPlanError ( "INVALID_PLAN" , contract . query , "Invalid execution plan digest" );
const pkg = plan . definition . workspace . packageImports . find (( p ) => p . revisionId === contract . package );
if ( ! pkg || canonicalJson ( publicQueryContract ( pkg , contract . query )) !== canonicalJson ( contract ))
throw new ExecutionPlanError (
"CLIENT_QUERY_MISMATCH" ,
` ${ contract . package } / ${ contract . query } ` ,
"Installed query differs from the imported query contract" ,
);
const query = plan . definition . queries . find (
( q ) => q . query . packageRevisionId === contract . package && q . query . checked . declaration . id === contract . query ,
);
if ( ! query ) throw new ExecutionPlanError ( "UNINSTALLED_QUERY" , contract . query , "No installed query binding" );
return query ;
}
/** Generate only the explicitly imported interfaces. No concrete atom inventory is needed. */
export function generateInterfaceClientBindings (
imports : Readonly < Record < string , InterfaceRevision >>,
runtimeModule = "@quixos/camino-replica-core" ,
) : string {
const entries = Object . entries ( imports ). map (([ alias , iface ]) => {
if ( iface . template )
throw new ExecutionPlanError (
"OPEN_CLIENT_CONTRACT" ,
alias ,
"Specialize the interface before generating a client" ,
);
const members = iface . members . map (( member ) => {
const operations = member . operations
. filter (( op ) => op . mode === "call" )
. map (
( op ) =>
2026-09-21 14:03:14 -07:00
` ${ JSON . stringify ( op . displayName ) } : defineInterfaceOperation< ${ tsType ( op . inputType ) } , ${ tsType ( op . outputType ) } , ${ JSON . stringify ( publicOperationContract ( iface , op ). execution ) } >( ${ JSON . stringify ( publicOperationContract ( iface , op )) } )` ,
2026-09-21 11:23:26 -07:00
);
return ` ${ JSON . stringify ( member . displayName ) } : { ${ operations . join ( ",\n" ) } }` ;
});
return ` ${ JSON . stringify ( alias ) } : { ${ members . join ( ",\n" ) } }` ;
});
return `// Generated from imported interface contracts. Regenerate; do not edit. \ nimport { defineInterfaceOperation, type PreparedChange, type ManagedReference } from ${ JSON . stringify ( runtimeModule ) } ; \ nexport const interfaces = { ${ entries . join ( ",\n" ) } } as const; \ n` ;
}
2026-09-21 14:03:14 -07:00
export function generatePackageQueryClientBindings (
pkg : PackageRevision ,
runtimeModule = "@quixos/camino-replica-core" ,
) : string {
const queries = ( pkg . checkedQueries ?? []). map (
( query ) =>
2026-09-21 15:24:05 -07:00
` ${ JSON . stringify ( query . declaration . displayName ) } : definePackageQuery< ${ tsType ( query . variables ) } , ${ tsType ( query . output ) } , ${ JSON . stringify ( publicQueryContract ( pkg , query . declaration . id ). placement ) } >( ${ JSON . stringify ( publicQueryContract ( pkg , query . declaration . id )) } )` ,
2026-09-21 14:03:14 -07:00
);
return `// Generated from checked package query contracts. Regenerate; do not edit. \ nimport { definePackageQuery, type ManagedReference } from ${ JSON . stringify ( runtimeModule ) } ; \ nexport const queries = { ${ queries . join ( ",\n" ) } } as const; \ n` ;
}
2026-09-21 15:24:05 -07:00
/** Receipt-covered bindings expose the package's imports, component subjects and
* declared queries, never the workspace's complete atom/interface inventory. */
export function generateComponentClientBindings ( plan : ExecutionPlan , packageRevision : string ) : string {
const pkg = plan . definition . workspace . packageImports . find (( pkg ) => pkg . revisionId === packageRevision );
2026-09-22 13:16:23 -07:00
const components = plan . definition . packages . find (( pkg ) => pkg . revision === packageRevision ) ? . components ;
2026-09-22 13:51:32 -07:00
const modules = plan . definition . packages . find (( pkg ) => pkg . revision === packageRevision ) ? . modules ?? [];
2026-09-22 13:16:23 -07:00
if ( ! pkg || ! components ? . length )
2026-09-21 15:24:05 -07:00
throw new ExecutionPlanError ( "NO_COMPONENT_CONTRACT" , packageRevision , "Package declares no component entries" );
const byId = new Map ( plan . definition . workspace . interfaceImports . map (( iface ) => [ iface . revisionId as string , iface ]));
const find = ( id : string ) => {
const contract = byId . get ( id );
if ( ! contract || contract . template )
throw new ExecutionPlanError ( "OPEN_CLIENT_CONTRACT" , id , "Component contracts must be closed" );
return contract ;
};
const imports = Object . fromEntries (
Object . entries ( pkg . clientInterfaces ?? {}). map (([ alias , id ]) => [ alias , find ( id )]),
);
2026-09-22 13:16:23 -07:00
const subjects = Object . fromEntries ( components . map (( component ) => [ component . displayName , find ( component . subject )]));
2026-09-21 15:24:05 -07:00
const interfaces = generateInterfaceClientBindings ( imports , "@quixos/camino-react" );
const subjectBindings = generateInterfaceClientBindings ( subjects , "@quixos/camino-react" )
. split ( "\n" )
. filter (( line ) => ! line . startsWith ( "import " ))
. join ( "\n" )
. replace ( "export const interfaces =" , "export const subjects =" );
const queries = generatePackageQueryClientBindings ( pkg , "@quixos/camino-react" )
. split ( "\n" )
. filter (( line ) => ! line . startsWith ( "import " ))
. join ( "\n" );
return (
interfaces +
'\nimport {definePackageQuery,type ComponentType,type ComponentProps} from "@quixos/camino-react";\n' +
subjectBindings +
"\n" +
queries +
2026-09-22 13:51:32 -07:00
"\nexport interface ModuleProps {}\n" +
"export type ExtraModuleProps<K extends string> = K extends keyof ModuleProps ? ModuleProps[K] : {};\n" +
"export interface Modules {\n" +
2026-09-22 13:16:23 -07:00
components
2026-09-22 13:51:32 -07:00
. map (( component ) => {
const module = modules . find (( module ) => module . id === component . id ) ! ;
const key = JSON . stringify ([ module . interfaceRevisionId , module . memberId ]);
return ` ${ JSON . stringify ( component . displayName ) } : ComponentType<ComponentProps< ${ JSON . stringify ( component . subject ) } > & ExtraModuleProps< ${ JSON . stringify ( key ) } >>;` ;
})
2026-09-21 15:24:05 -07:00
. join ( "\n" ) +
"\n}\n"
);
}
export function componentAssetDeclarations () : string {
return (
"// Generated component asset modules. Regenerate; do not edit.\n" +
[ "svg" , "png" , "jpg" , "jpeg" , "gif" , "webp" , "avif" , "woff" , "woff2" , "ttf" , "ico" ]
. map (( extension ) => `declare module "*. ${ extension } " { const value: string; export default value; }` )
. join ( "\n" ) +
'\ndeclare module "*.css";\n'
);
}