1 import Base 2 import ./schema_fragments.bend as Fragments 3 import ./relation_commands.bend as RelationWorld 4 import ./relation_providers.bend as RelationOps 5 import ./ordinary_app_relation.bend as Relation 6 import ./relation_failure_reader.bend as RelationDecl 7 import ./relation_types.bend as RelationTypes 8 import ./ordinary_app_observation_data.bend as DTO 9 import ./machine.bend as Machine 10 import ./ordinary_machine_declaration.bend as MachineDecl 11 import ./ordinary_machine_operations.bend as MachineOps 12 import ./inspector.bend as Inspect 13 import ./capabilities.bend as Cap 14 import ./ordinary_query_declaration.bend as Query 15 import ./resource_field_binding.bend as Binding 16 import ./resource_field.bend as Lens 17 import ./ordinary_resource_field_system.bend as Field 18 import ./ordinary_schema.bend as Schema 19 import ./owner_product.bend as Product 20 import ./world.bend as W 21 import ./transaction.bend as T 22 # Same canonical field binding drives the grant and detached owning observation. 23 # App declaration for a selected resource field or a declared machine/relation. 24 # Combines an operational grant with its owner-threading Data observation and 25 # renderer. Schema and resource path are fixed by the typed constructors below; 26 # there is no live resource owner in this declaration. Ordinary resource-field 27 # operations and explicit recoverable operations are distinct APIs; see 28 # ordinary-recovery-resource-field for returned recovery owners. 29 type Declaration<-S:Data,-C:Type,-R:Type,-E:Data,-Ops:Type -> Type,-V:Data> is Type: 30 Declaration{ordinary:Field.Declaration,E>,Ops(T.Tx,E>)>,observe:W.World -> W.World & V,render:V -> String} 31 # Operation-family alias for reading one declared field as a Data view. 32 # The resource identity remains in the grant type, so equal payload types do not 33 # make distinct schema resource declarations interchangeable. 34 def read_ops(~S:Data,~P:Type,~identity:Schema.Resource,~V:Data,H:Type) -> Type: 35 Field.Read 36 # Operation-family alias for reading and replacing one declared field P. 37 # Replacement consumes P; the enclosing resource and affine sibling owners stay 38 # inside the opaque callback owner H. 39 def write_ops(~S:Data,~P:Type,~identity:Schema.Resource,~V:Data,H:Type) -> Type: 40 Field.Write 41 # Declare selected-field read access through the canonical binding. 42 # lens, identity and binding must describe the same field of resource owner R; 43 # view threads the selected P back with Data V. The observer uses that same lens 44 # and view, returning the World and siblings rather than exposing live aliases. 45 # Construct bindings with resource-field-binding; do not supply a metadata-only 46 # name as authority. No field operation runs while building this declaration. 47 def read(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~Rest:Type,~V:Data,~lens:Product.Lens,~identity:Schema.Resource,~binding:Binding.Binding,~view:P -> P & V,~render:V -> String) -> Declaration: 48 Declaration{Field.read(~S,~C,~R,~E,~P,~Rest,~V,~lens,~identity,~binding,~view),W.observe_resource(~S,~C,~R,~E,~V,~Lens.project(~R,~P,~Rest,~V,~lens,~view)),render} 49 # Declare selected-field read/replacement access through the canonical binding. 50 # The setter journals the displaced field and threads siblings through the lens; 51 # a failed transaction restores its recorded field owner. With tracing enabled, 52 # the declared view/render functions supply before/after resource observations. 53 # view returns P with its observation; render receives only detached V and 54 # returns String. This is not general rollback for projector side effects or IO. For explicit displaced/rejected owner 55 # recovery, use ordinary-recovery-resource-field rather than this declaration. 56 def write(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~Rest:Type,~V:Data,~lens:Product.Lens,~identity:Schema.Resource,~binding:Binding.Binding,~view:P -> P & V,~render:V -> String) -> Declaration: 57 Declaration{Field.Declaration{Field.Write{Lens.write_observed(~S,~C,~R,~E,~P,~Rest,~V,~lens,~view,~render,~Schema.resource_key(~S,~P,identity))},[Query.Clause{Schema.resource_key(~S,~P,identity),Query.Write{}}]},W.observe_resource(~S,~C,~R,~E,~V,~Lens.project(~R,~P,~Rest,~V,~lens,~view)),render} 58 # Advanced App projection: consume this declaration and obtain its operational 59 # field grant and access clauses. This does not register a system or run a write. 60 def ordinary(~S:Data,~C:Type,~R:Type,~E:Data,~Ops:Type -> Type,~V:Data,declaration:Declaration) -> Field.Declaration,E>,Ops(T.Tx,E>)>: 61 match declaration: 62 case Declaration{ordinary,_,_}:ordinary 63 # Advanced App projection: consume this declaration to obtain its World-returning 64 # Data observer. Observing uses the declared projection; no live owner is copied. 65 def observer(~S:Data,~C:Type,~R:Type,~E:Data,~Ops:Type -> Type,~V:Data,declaration:Declaration) -> W.World -> W.World & V: 66 match declaration: 67 case Declaration{_,observe,_}:observe 68 # Obtain the declared V -> String renderer, consuming the declaration. 69 # The renderer receives only a Data view, not the selected affine field. 70 def renderer(~S:Data,~C:Type,~R:Type,~E:Data,~Ops:Type -> Type,~V:Data,declaration:Declaration) -> V -> String: 71 match declaration: 72 case Declaration{_,_,render}:render 73 74 def names(~S:Data,clauses:List<&2,Query.Clause>) -> String: 75 match clauses: 76 case []:"" 77 case Query.Clause{key,_} <> []:key 78 case Query.Clause{key,_} <> rest:key ++ "," ++ names(~S,rest) 79 # Return clause keys in declaration order, comma-separated, consuming the 80 # carrier. The returned display string is not a schema identity or capability. 81 def name(~S:Data,~C:Type,~R:Type,~E:Data,~Ops:Type -> Type,~V:Data,declaration:Declaration) -> String: 82 names(~S,Field.clauses(~S,~T.Tx,E>,~Ops(T.Tx,E>),ordinary(~S,~C,~R,~E,~Ops,~V,declaration))) 83 # App-facing alias of renderer for the same declaration and Data view. 84 # Consumes the carrier; it performs no read, write or registration itself. 85 def presenter(~S:Data,~C:Type,~R:Type,~E:Data,~Ops:Type -> Type,~V:Data,declaration:Declaration) -> V -> String: 86 renderer(~S,~C,~R,~E,~Ops,~V,declaration) 87 88 # The same declared machine supplies its grant, access name and current observer. 89 # Operation-family alias for reading a declared machine's CurrentView and 90 # submitting QueueInput. The write input is a queued transition request, not a 91 # replacement for the complete resource owner or immediate transition execution. 92 def machine_ops(~S:Data,~K:Data,~V:Data,H:Type) -> Type: 93 Cap.ValueWrite,Machine.CurrentView> 94 def machine_presented(~S:Data,~K:Data,~V:Data,~C:Type,~R:Type,~E:Data,result:Inspect.Frame & Machine.CurrentView) -> W.World & Machine.CurrentView: 95 match result: 96 case (Inspect.Frame{world,_},view):(world,view) 97 # Advanced observation route for the declared machine's current value. 98 # Returns (World, CurrentView), including Unavailable when the slot has no value; 99 # uses a fresh inspector cursor and does not run queued transitions. 100 def machine_observe(~S:Data,~K:Data,~V:Data,~C:Type,~R:Type,~E:Data,~take:R -> R & Machine.Slot,~machine:MachineDecl.Declaration,world:W.World) -> W.World & Machine.CurrentView: 101 machine_presented(~S,~K,~V,~C,~R,~E,MachineDecl.get(~Inspect.Frame,~S,~K,~V,~MachineDecl.inspect(~S,~K,~V,~C,~R,~E,~take,~machine),Inspect.Frame{world,None{}})) 102 def machine_render(~S:Data,~K:Data,~V:Data,~render:V -> String,view:Machine.CurrentView) -> String: 103 match view: 104 case Machine.Unavailable{}:"Unavailable" 105 case Machine.Current{value}:"Current:" ++ render(value) 106 # Build a resource-style App declaration for one declared state machine. 107 # The same machine supplies the access key, current-value observer and queued 108 # write operation. take/put select its Slot and same compares values. The grant 109 # submits QueueInput through ordinary-machine-operations; constructing or reading 110 # this declaration does not apply a queued transition. See 111 # ordinary-machine-declaration and ordinary-machine-operations for that boundary. 112 def machine_write(~S:Data,~K:Data,~V:Data,~C:Type,~R:Type,~E:Data,~take:R -> R & Machine.Slot,~put:R -> Machine.Slot -> R,~same:V -> V -> Bool,~machine:MachineDecl.Declaration,~render:V -> String) -> Declaration>: 113 Declaration{Field.Declaration{MachineOps.write(~S,~K,~V,~C,~R,~E,~take,~put,~same,~machine),[Query.Clause{MachineDecl.name(~S,~K,~V,~R,~take,machine),Query.Write{}}]},machine_observe(~S,~K,~V,~C,~R,~E,~take,~machine),machine_render(~S,~K,~V,~render)} 114 115 # Operation-family alias for a declared schema relation's writable operations. 116 # The callback still threads its opaque owner; this is not a raw relation store. 117 def relation_ops(~S:Data,H:Type) -> Type:RelationOps.Writable 118 def relation_name(~S:Data,declaration:RelationDecl.Declaration) -> String: 119 match declaration: 120 case RelationDecl.Declaration{RelationTypes.Descriptor{_,name,_,_}}:name 121 def relation_viewed(~S:Data,~C:Type,~R:Type,~E:Data,result:Relation.World(~S,~C,~R,~E) & DTO.Observation>) -> Relation.World(~S,~C,~R,~E) & Relation.View: 122 match result: 123 case (world,DTO.Observation{view,_}):(world,view) 124 # Advanced observation route returning the relation World with the declared 125 # relation's descriptor and projected rows. It uses the ordinary relation 126 # observer; it does not consume an application failure-reader cursor. 127 def relation_observe(~S:Data,~C:Type,~R:Type,~E:Data,~relation:RelationDecl.Declaration,world:Relation.World(~S,~C,~R,~E)) -> Relation.World(~S,~C,~R,~E) & Relation.View: 128 relation_viewed(~S,~C,~R,~E,Relation.observe(~S,~C,~R,~E,~relation,~Relation.builtin_row(~S),world)) 129 def relation_render(~S:Data,view:Relation.View) -> String: 130 match view: 131 case Relation.View{descriptor,rows}:Relation.descriptor_text(~S,descriptor) ++ ":" ++ List.show(&2,Relation.Row(~S),Relation.builtin_row(~S),rows) 132 # Build an App resource-style declaration for a canonical relation descriptor. 133 # The World uses the relation Store wrapper and relation Notice event wrapper; 134 # its write grant, access clause and observer come from that same descriptor. 135 # This constructor does not execute relation commands or apply a barrier. 136 # See ordinary-app-relation and relation-providers for execution and observation. 137 def relation_write(~S:Data,~C:Type,~R:Type,~E:Data,~relation:RelationDecl.Declaration) -> Declaration,R,RelationWorld.Notice,relation_ops(~S),Relation.View>: 138 Declaration{Field.Declaration{RelationOps.provide_write(~S,~C,~R,~E,~Relation.descriptor(~S,relation)),[Query.Clause{relation_name(~S,relation),Query.Write{}}]},relation_observe(~S,~C,~R,~E,~relation),relation_render(~S)} 139 # Schema-assembly projection: consume a relation declaration and emit entries 140 # for its forward name followed by its inverse name. Entries describe schema 141 # metadata; they do not create an edge, register a system or grant write access. 142 def relation_entries(~S:Data,relation:RelationDecl.Declaration) -> List<&2,Fragments.Entry>: 143 match relation: 144 case RelationDecl.Declaration{RelationTypes.Descriptor{_,+name,+inverseName,_}}:[Fragments.Entry{Fragments.Relation{},name,name},Fragments.Entry{Fragments.Relation{},inverseName,inverseName}]