@@ -2,8 +2,12 @@ import {
22 SELF_MODULE ,
33 type Bridge ,
44 type NodeRef ,
5+ type Wire ,
56} from "@stackables/bridge-core" ;
67
8+ const isPull = ( w : Wire ) : boolean => w . sources [ 0 ] ?. expr . type === "ref" ;
9+ const wRef = ( w : Wire ) : NodeRef => ( w . sources [ 0 ] . expr as { ref : NodeRef } ) . ref ;
10+
711export class BridgeCompilerIncompatibleError extends Error {
812 constructor (
913 public readonly operation : string ,
@@ -56,20 +60,22 @@ export function assertBridgeCompilerCompatible(
5660) : void {
5761 const op = `${ bridge . type } .${ bridge . field } ` ;
5862
63+ const wires : Wire [ ] = bridge . wires ;
64+
5965 // Pipe-handle trunk keys — block-scoped aliases inside array maps
6066 // reference these; the compiler handles them correctly.
6167 const pipeTrunkKeys = new Set ( ( bridge . pipeHandles ?? [ ] ) . map ( ( ph ) => ph . key ) ) ;
6268
63- for ( const w of bridge . wires ) {
69+ for ( const w of wires ) {
6470 // User-level alias (Shadow) wires: compiler has TDZ ordering bugs.
6571 // Block-scoped aliases inside array maps wire FROM a pipe-handle tool
6672 // instance (key is in pipeTrunkKeys) and are handled correctly.
6773 if ( w . to . module === "__local" && w . to . type === "Shadow" ) {
68- if ( ! ( "from" in w ) ) continue ;
74+ if ( ! isPull ( w ) ) continue ;
6975 const fromKey =
70- w . from . instance != null
71- ? `${ w . from . module } :${ w . from . type } :${ w . from . field } :${ w . from . instance } `
72- : `${ w . from . module } :${ w . from . type } :${ w . from . field } ` ;
76+ wRef ( w ) . instance != null
77+ ? `${ wRef ( w ) . module } :${ wRef ( w ) . type } :${ wRef ( w ) . field } :${ wRef ( w ) . instance } `
78+ : `${ wRef ( w ) . module } :${ wRef ( w ) . type } :${ wRef ( w ) . field } ` ;
7379 if ( ! pipeTrunkKeys . has ( fromKey ) ) {
7480 throw new BridgeCompilerIncompatibleError (
7581 op ,
@@ -79,16 +85,12 @@ export function assertBridgeCompilerCompatible(
7985 continue ;
8086 }
8187
82- if ( ! ( "from" in w ) ) continue ;
88+ if ( ! isPull ( w ) ) continue ;
8389
8490 // Catch fallback on pipe wires (expression results) — the catch must
8591 // propagate to the upstream tool, not the internal operator; codegen
8692 // does not handle this yet.
87- if (
88- "pipe" in w &&
89- w . pipe &&
90- ( "catchFallback" in w || "catchFallbackRef" in w || "catchControl" in w )
91- ) {
93+ if ( w . pipe && w . catch ) {
9294 throw new BridgeCompilerIncompatibleError (
9395 op ,
9496 "Catch fallback on expression (pipe) wires is not yet supported by the compiler." ,
@@ -97,13 +99,11 @@ export function assertBridgeCompilerCompatible(
9799
98100 // Catch fallback that references a pipe handle — the compiler eagerly
99101 // calls all tools in the catch branch even when the main wire succeeds.
100- if ( "catchFallbackRef " in w && w . catchFallbackRef ) {
101- const ref = w . catchFallbackRef as NodeRef ;
102+ if ( w . catch && "ref " in w . catch ) {
103+ const ref = w . catch . ref ;
102104 if ( ref . instance != null ) {
103105 const refKey = `${ ref . module } :${ ref . type } :${ ref . field } :${ ref . instance } ` ;
104- if (
105- bridge . pipeHandles ?. some ( ( ph ) => ph . key === refKey )
106- ) {
106+ if ( bridge . pipeHandles ?. some ( ( ph ) => ph . key === refKey ) ) {
107107 throw new BridgeCompilerIncompatibleError (
108108 op ,
109109 "Catch fallback referencing a pipe expression is not yet supported by the compiler." ,
@@ -115,16 +115,12 @@ export function assertBridgeCompilerCompatible(
115115 // Catch fallback on wires whose source tool has tool-backed input
116116 // dependencies — the compiler only catch-guards the direct source
117117 // tool, not its transitive dependency chain.
118- if (
119- ( "catchFallback" in w || "catchFallbackRef" in w || "catchControl" in w ) &&
120- "from" in w &&
121- isToolRef ( w . from , bridge )
122- ) {
123- const sourceTrunk = `${ w . from . module } :${ w . from . type } :${ w . from . field } ` ;
124- for ( const iw of bridge . wires ) {
125- if ( ! ( "from" in iw ) ) continue ;
118+ if ( w . catch && isToolRef ( wRef ( w ) , bridge ) ) {
119+ const sourceTrunk = `${ wRef ( w ) . module } :${ wRef ( w ) . type } :${ wRef ( w ) . field } ` ;
120+ for ( const iw of wires ) {
121+ if ( ! isPull ( iw ) ) continue ;
126122 const iwDest = `${ iw . to . module } :${ iw . to . type } :${ iw . to . field } ` ;
127- if ( iwDest === sourceTrunk && isToolRef ( iw . from , bridge ) ) {
123+ if ( iwDest === sourceTrunk && isToolRef ( wRef ( iw ) , bridge ) ) {
128124 throw new BridgeCompilerIncompatibleError (
129125 op ,
130126 "Catch fallback on wires with tool chain dependencies is not yet supported by the compiler." ,
@@ -136,30 +132,28 @@ export function assertBridgeCompilerCompatible(
136132 // Fallback chains (|| / ??) with tool-backed refs — compiler eagerly
137133 // calls all tools via Promise.all, so short-circuit semantics are lost
138134 // and tool side effects fire unconditionally.
139- if ( w . fallbacks ) {
140- for ( const fb of w . fallbacks ) {
141- if ( fb . ref && isToolRef ( fb . ref , bridge ) ) {
142- throw new BridgeCompilerIncompatibleError (
143- op ,
144- "Fallback chains (|| / ??) with tool-backed sources are not yet supported by the compiler." ,
145- ) ;
146- }
135+ for ( const src of w . sources . slice ( 1 ) ) {
136+ if ( src . expr . type === "ref" && isToolRef ( src . expr . ref , bridge ) ) {
137+ throw new BridgeCompilerIncompatibleError (
138+ op ,
139+ "Fallback chains (|| / ??) with tool-backed sources are not yet supported by the compiler." ,
140+ ) ;
147141 }
148142 }
149143 }
150144
151145 // Same-cost overdefinition sourced only from tools can diverge from runtime
152146 // tracing/error behavior in current AOT codegen; compile must downgrade.
153147 const toolOnlyOverdefs = new Map < string , number > ( ) ;
154- for ( const w of bridge . wires ) {
148+ for ( const w of wires ) {
155149 if (
156150 w . to . module !== SELF_MODULE ||
157151 w . to . type !== bridge . type ||
158152 w . to . field !== bridge . field
159153 ) {
160154 continue ;
161155 }
162- if ( ! ( "from" in w ) || ! isToolRef ( w . from , bridge ) ) {
156+ if ( ! isPull ( w ) || ! isToolRef ( wRef ( w ) , bridge ) ) {
163157 continue ;
164158 }
165159
@@ -196,8 +190,8 @@ export function assertBridgeCompilerCompatible(
196190 ) ;
197191 }
198192
199- for ( const w of bridge . wires ) {
200- if ( ! ( "from" in w ) || w . to . path . length === 0 ) continue ;
193+ for ( const w of wires ) {
194+ if ( ! isPull ( w ) || w . to . path . length === 0 ) continue ;
201195 // Build the full key for this wire target
202196 const fullKey =
203197 w . to . instance != null
0 commit comments