1 import Base 2 import ./owner_product.bend as Product 3 import ./capabilities.bend as Cap 4 import ./owned_event_journal.bend as Frame 5 # Canonical event declarations compose heterogeneous payload grants with one 6 # ordered journal. Builder remains erased until the full recovery tree is known. 7 # Raw constructors/builders are trusted engine administration, not gameplay. 8 # Advanced erased builder for an owned-event publication grant family. 9 # Specializes the journal recovery path only after the complete heterogeneous 10 # recovery product is known. Gameplay receives abstract emission grants rather 11 # than constructing runtime journal paths or retaining raw payload aliases. 12 def Builder(~State:Type,~Recovery:Type,~Ops:Type -> Type) -> Type: 13 @-All:Type -> @-Rest:Type -> @-path:Product.Lens -> Unit -> Ops(Frame.Stage) 14 # Detached metadata listing owned event-domain names and whether it is opaque. 15 # Names describe provisioned publication uses; they do not grant access or prove 16 # that a hand-authored builder targets the named log. Canonical constructors 17 # supply this metadata together with their typed operational bindings. 18 type Description is Data: 19 Description{events:List<&2,String>,opaque:Bool} 20 # Combine metadata left first, preserving duplicate names and propagating 21 # opacity from either side. This does not combine live logs or publish payloads. 22 def pair_description(left:Description,right:Description) -> Description: 23 match left right: 24 case Description{left,lo} Description{right,ro}:Description{List.append(&2,String,left,right),Bool.or(lo,ro)} 25 # Report whether the description is nonopaque. This is the automatic App 26 # metadata-admission test, not validation of arbitrary builders or runtime log 27 # existence. Use canonical declarations rather than asserting trusted metadata. 28 def complete(value:Description) -> Bool: 29 match value: 30 case Description{_,opaque}:Bool.not(opaque) 31 # Static publisher declaration carrying a recovery constructor and access names; 32 # State, Recovery, Ops and builder keep its operational route typed. Canonical 33 # ordinary-owned-event-family-write.declaration supplies the matching log/recovery 34 # paths. Payloads may be arbitrary affine Type values; this is not a Data-event 35 # stream reader and does not duplicate payloads for multiple readers. 36 # Legacy Declaration has opaque metadata. Described adds operational-domain 37 # metadata; automatic App publisher registration requires a complete description. 38 # Classic publisher registration remains available for the legacy form. Raw 39 # constructors/builders are trusted administration, not a gameplay permission API. 40 # See docs/parity/affine-events.md for ownership and qualified publication bounds. 41 type Declaration<-S:Data,-State:Type,-Recovery:Type,-Ops:Type -> Type,-builder:Builder(~State,~Recovery,~Ops)> is Type: 42 Declaration{empty:Unit -> Recovery,access:List<&2,String>} 43 Described{empty:Unit -> Recovery,access:List<&2,String>,description:Description} 44 # Consume a declaration and obtain its fresh recovery-owner constructor. 45 # It initializes the finish-time recovery accumulator; it does not copy payloads, 46 # publish an event, retire a log entry or execute cleanup. 47 def empty(~S:Data,~State:Type,~Recovery:Type,~Ops:Type -> Type,~builder:Builder(~State,~Recovery,~Ops),value:Declaration) -> Unit -> Recovery: 48 match value: 49 case Declaration{empty,_}:empty 50 case Described{empty,_,_}:empty 51 # Consume a declaration and return its Registry access strings in stored order. 52 # These are metadata, not a reader cursor, payload owner or substitute for the 53 # builder's schema-bound publication grant. 54 def access(~S:Data,~State:Type,~Recovery:Type,~Ops:Type -> Type,~builder:Builder(~State,~Recovery,~Ops),value:Declaration) -> List<&2,String>: 55 match value: 56 case Declaration{_,access}:access 57 case Described{_,access,_}:access 58 # Consume a declaration and obtain its detached event-use description. 59 # Legacy Declaration yields an opaque description; Described yields the supplied 60 # one. This operation does not read logs, advance readers or publish events. 61 def description(~S:Data,~State:Type,~Recovery:Type,~Ops:Type -> Type,~builder:Builder(~State,~Recovery,~Ops),value:Declaration) -> Description: 62 match value: 63 case Declaration{_,_}:Description{[],True{}} 64 case Described{_,_,description}:description 65 # Operation-family alias for left/right emission grants on the same opaque 66 # journal stage. Its order matches the recovery product produced by both. 67 def PairOps(~L:Type -> Type,~R:Type -> Type,H:Type) -> Type:Cap.Bundle 68 def pair_empty(~L:Type,~R:Type,~left:Unit -> L,~right:Unit -> R,unit:Unit) -> Product.Owner:Product.Owner{left(Unit{}),right(unit)} 69 def pair_grant(~S:Data,~State:Type,~L:Type,~R:Type,~LOps:Type -> Type,~ROps:Type -> Type,~left:Builder(~State,~L,~LOps),~right:Builder(~State,~R,~ROps),~All:Type,~Rest:Type,~path:Product.Lens,Rest>,unit:Unit) -> Cap.Bundle),ROps(Frame.Stage)>: 70 Cap.Bundle{left(All,Rest & R,Product.compose(~All,~Product.Owner,~Rest,~L,~R,~path,~Product.lens(~L,~R,~Product.Left{})),Unit{}),right(All,Rest & L,Product.compose(~All,~Product.Owner,~Rest,~R,~L,~path,~Product.lens(~L,~R,~Product.Right{})),unit)} 71 # Compose owned-event declarations for the same schema and staged State. 72 # The body receives Cap.Bundle{left, right}; recovery is the corresponding 73 # Product.Owner. Access/domain lists remain left 74 # first, with duplicates; opaque metadata on either side remains opaque. 75 # One shared staged journal preserves actual emission order across families, 76 # not declaration grouping. The publisher commits payload owners only on success; 77 # on abort it publishes none and returns staged owners through the typed recovery 78 # product, preserving each family's order. The declaration itself executes none 79 # of these actions. No owner restored by another required ECS rollback may also 80 # be returned as recovered event data, and no automatic IO finalizer is implied. 81 # Use ordinary-owned-event-family-write.declaration for each canonical leaf and 82 # ordinary-owned-event-family-publisher for execution. Reader projection, 83 # retention and retirement use their separate owned-event APIs. 84 def both(~S:Data,~State:Type,~L:Type,~R:Type,~LOps:Type -> Type,~ROps:Type -> Type,~leftBuilder:Builder(~State,~L,~LOps),~rightBuilder:Builder(~State,~R,~ROps),~left:Declaration,~right:Declaration) -> Declaration,PairOps(~LOps,~ROps),pair_grant(~S,~State,~L,~R,~LOps,~ROps,~leftBuilder,~rightBuilder)>: 85 Described{pair_empty(~L,~R,~empty(~S,~State,~L,~LOps,~leftBuilder,left),~empty(~S,~State,~R,~ROps,~rightBuilder,right)),List.append(&2,String,access(~S,~State,~L,~LOps,~leftBuilder,left),access(~S,~State,~R,~ROps,~rightBuilder,right)),pair_description(description(~S,~State,~L,~LOps,~leftBuilder,left),description(~S,~State,~R,~ROps,~rightBuilder,right))} 86 def identity_take(~R:Type,value:R) -> Unit & R:(Unit{},value) 87 def identity_put(~R:Type,unit:Unit,value:R) -> R:value 88 # Advanced builder lens selecting the complete recovery owner with Unit rest. 89 # It threads the owner rather than copying it; it does not publish or abort events. 90 def identity(~R:Type) -> Product.Lens:Product.Lens{identity_take(~R),identity_put(~R)} 91 # Advanced publisher projection: instantiate builder at the full recovery owner 92 # and return its emission operations. The runner supplies the opaque journal 93 # stage; gameplay does not construct raw paths, expose stored owners or decide 94 # publication by invoking this projection outside the publisher protocol. 95 def root_grant(~S:Data,~State:Type,~Recovery:Type,~Ops:Type -> Type,~builder:Builder(~State,~Recovery,~Ops)) -> Ops(Frame.Stage):builder(Recovery,Unit,identity(~Recovery),Unit{})