@@ -313,37 +313,107 @@ def _runtime_result(result: SettlementResult[Any]) -> dict[str, Any]:
313313 }
314314
315315
316- def _runtime_result_metadata (
316+ def _required_settlement_string (
317+ payload : Mapping [str , Any ],
318+ key : str ,
319+ * ,
320+ shape : str ,
321+ ) -> str :
322+ value = payload .get (key )
323+ if not isinstance (value , str ) or not value .strip ():
324+ raise RuntimeError (f"TypeScript settlement result { shape } shape mismatch" )
325+ return value
326+
327+
328+ _SettlementEnum = TypeVar ("_SettlementEnum" , bound = StrEnum )
329+
330+
331+ def _required_settlement_enum (
332+ enum_type : type [_SettlementEnum ],
333+ payload : Mapping [str , Any ],
334+ key : str ,
335+ * ,
336+ shape : str ,
337+ ) -> _SettlementEnum :
338+ try :
339+ return enum_type (_required_settlement_string (payload , key , shape = shape ))
340+ except ValueError :
341+ raise RuntimeError (
342+ f"TypeScript settlement result { shape } shape mismatch"
343+ ) from None
344+
345+
346+ def decode_settlement_receipt_payload (payload : Any ) -> SettlementReceipt :
347+ if not isinstance (payload , Mapping ):
348+ raise RuntimeError ("TypeScript settlement result receipts shape mismatch" )
349+ source_ref = payload .get ("source_ref" )
350+ if source_ref is not None and (
351+ not isinstance (source_ref , str ) or not source_ref .strip ()
352+ ):
353+ raise RuntimeError ("TypeScript settlement result receipts shape mismatch" )
354+ return SettlementReceipt (
355+ step_kind = _required_settlement_enum (
356+ SettlementStepKind ,
357+ payload ,
358+ "step_kind" ,
359+ shape = "receipts" ,
360+ ),
361+ status = _required_settlement_string (payload , "status" , shape = "receipts" ),
362+ effect_id = _required_settlement_string (
363+ payload ,
364+ "effect_id" ,
365+ shape = "receipts" ,
366+ ),
367+ source_ref = source_ref ,
368+ )
369+
370+
371+ def decode_settlement_result_payload (
317372 payload : Any ,
318- ) -> tuple [tuple [SettlementReceipt , ...], SettlementFailure | None ]:
373+ ) -> tuple [Any , tuple [SettlementReceipt , ...], SettlementFailure | None ]:
374+ """Decode the shared TS settlement envelope at the Python boundary."""
375+
319376 if not isinstance (payload , Mapping ):
320377 raise RuntimeError ("TypeScript settlement result shape mismatch" )
378+ if "value" not in payload or "failure" not in payload :
379+ raise RuntimeError ("TypeScript settlement result shape mismatch" )
321380 receipts_value = payload .get ("receipts" )
322- receipts = (
323- tuple (
324- SettlementReceipt (
325- step_kind = SettlementStepKind (str (receipt ["step_kind" ])),
326- status = str (receipt ["status" ]),
327- effect_id = str (receipt ["effect_id" ]),
328- source_ref = str (receipt .get ("source_ref" ) or "" ) or None ,
329- )
330- for receipt in receipts_value
331- if isinstance (receipt , Mapping )
332- )
333- if isinstance (receipts_value , list )
334- else ()
381+ if not isinstance (receipts_value , list ):
382+ raise RuntimeError ("TypeScript settlement result receipts shape mismatch" )
383+ receipts = tuple (
384+ decode_settlement_receipt_payload (receipt ) for receipt in receipts_value
335385 )
336386 failure_value = payload .get ("failure" )
337387 failure = None
338- if isinstance (failure_value , Mapping ):
388+ if failure_value is not None :
389+ if not isinstance (failure_value , Mapping ):
390+ raise RuntimeError ("TypeScript settlement result failure shape mismatch" )
391+ if payload .get ("value" ) is not None :
392+ raise RuntimeError ("TypeScript settlement result shape mismatch" )
339393 details = failure_value .get ("details" )
394+ if details is not None and not isinstance (details , Mapping ):
395+ raise RuntimeError ("TypeScript settlement result failure shape mismatch" )
340396 failure = SettlementFailure (
341- kind = SettlementFailureKind (str (failure_value ["kind" ])),
342- step_kind = SettlementStepKind (str (failure_value ["step_kind" ])),
343- reason = str (failure_value ["reason" ]),
344- details = dict (details ) if isinstance (details , Mapping ) else None ,
397+ kind = _required_settlement_enum (
398+ SettlementFailureKind ,
399+ failure_value ,
400+ "kind" ,
401+ shape = "failure" ,
402+ ),
403+ step_kind = _required_settlement_enum (
404+ SettlementStepKind ,
405+ failure_value ,
406+ "step_kind" ,
407+ shape = "failure" ,
408+ ),
409+ reason = _required_settlement_string (
410+ failure_value ,
411+ "reason" ,
412+ shape = "failure" ,
413+ ),
414+ details = dict (details ) if details is not None else None ,
345415 )
346- return receipts , failure
416+ return payload . get ( "value" ), receipts , failure
347417
348418
349419T = TypeVar ("T" )
@@ -412,7 +482,7 @@ def bind(self, step: Callable[[T], SettlementResult[U]]) -> SettlementResult[U]:
412482 "next" : _runtime_result (next_result ),
413483 },
414484 )
415- receipts , failure = _runtime_result_metadata (reduced )
485+ _ , receipts , failure = decode_settlement_result_payload (reduced )
416486 return SettlementResult (
417487 value = next_result .value ,
418488 receipts = receipts ,
0 commit comments