@opetope/react 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +268 -17
- package/README.md +107 -67
- package/README.ru.md +53 -66
- package/dist/command-hook-controller.d.ts +4 -4
- package/dist/command-hook.d.ts +3 -3
- package/dist/command.d.ts +8 -8
- package/dist/contribution-frame-CCKnOxZR.js +2 -0
- package/dist/contribution-frame-CCKnOxZR.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/model-binding.d.ts +4 -4
- package/dist/model-hook.d.ts +2 -2
- package/dist/resource-command.d.ts +11 -4
- package/dist/scenario-slot.d.ts +6 -3
- package/dist/scenario-types.d.ts +2 -2
- package/dist/scenario.d.ts +1 -2
- package/dist/{commands-hook.d.ts → selection-hooks.d.ts} +4 -11
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/package.json +5 -5
- package/dist/contribution-frame-DkNmjChZ.js +0 -2
- package/dist/contribution-frame-DkNmjChZ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useState as O,useInsertionEffect as E,useLayoutEffect as w,useMemo as l,useSyncExternalStore as y,useRef as S,useCallback as F,useEffect as B,createElement as P,Fragment as J}from"react";import{CallError as Q,declarationId as V}from"@opetope/core";import{compatibleAbortReason as X,isCancellation as Y,reportDetachedError as N,isCallTarget as Z,isReadable as _,createAggregateError as ee,createContributionTarget as te}from"@opetope/core/internal";import{C as d}from"./contribution-isolation-Bzfbt4iM.js";import{u as C,r as ne,i as se,p as ie,c as oe,a as I,M as re,b as ae}from"./contribution-frame-DkNmjChZ.js";import{d as dt}from"./contribution-frame-DkNmjChZ.js";function A(t,e){var n;if(Y(e))return{reason:e,status:"cancelled"};try{const s=(n=t==null?void 0:t.onFailure)==null?void 0:n.call(t,e);s!==void 0&&Promise.resolve(s).catch(N)}catch(s){N(s)}return{error:e,status:"failed"}}function ce(t){const e=t==null?void 0:t.onSuccess,n=t==null?void 0:t.signal;return e===void 0?n===void 0?void 0:{signal:n}:n===void 0?{onSuccess:e}:{onSuccess:e,signal:n}}function ue(t,e){let n;const s=new Promise(i=>{n=i});return{input:t,options:e===void 0?void 0:{...e},promise:s,resolve:n}}function de(t,e){const n=t.resolve;t.input=void 0,t.options=void 0,t.resolve=void 0,n==null||n(e)}class h{invoker;run=((e,n)=>{if(this.notify===void 0)return Promise.resolve({reason:new d("inactive","Command consumer is not mounted."),status:"cancelled"});const s=this.cancelledInput(n);if(s!==void 0)return Promise.resolve(s);const i=ue(e,n);return this.start(i),i.promise});inFlight=0;notify;constructor(e){this.invoker=e}attach(e){return this.notify=e,()=>{this.notify===e&&(this.notify=void 0)}}synchronize(){var e;(e=this.notify)==null||e.call(this,this.inFlight>0)}aborted(e){return{reason:new Q("cancelled","Command input was cancelled.",X(e)),status:"cancelled"}}cancelledInput(e){var n;return((n=e==null?void 0:e.signal)==null?void 0:n.aborted)===!0?this.aborted(e.signal):void 0}finish(e,n){var s;e.resolve!==void 0&&(this.inFlight-=1,de(e,n),(s=this.notify)==null||s.call(this,this.inFlight>0,n))}invoke(e){let n;try{n=this.invoker.run(e.input,ce(e.options)),e.input=void 0}catch(s){this.finish(e,A(e.options,s));return}n.then(s=>this.finish(e,{status:"ok",value:s}),s=>this.finish(e,A(e.options,s)))}start(e){var n;this.inFlight+=1,(n=this.notify)==null||n.call(this,!0),this.invoke(e)}}const fe={inFlight:!1,outcome:void 0};function le(t){return t===void 0||t.status==="cancelled"?void 0:t}function he(t,e,n,s){return s===void 0&&t.slot===e&&t.inFlight===n}function be(t,e,n,s){const i=le(s);if(he(t,e,n,i))return t;const o=t.slot===e?t.outcome:void 0;return{inFlight:n,outcome:i??o,slot:e}}function pe(t){const[e]=O(()=>new WeakMap);let n=e.get(t);return n===void 0&&(n=new h(t),e.set(t,n)),n}function b(t){const[e,n]=O(()=>({inFlight:!1,outcome:void 0,slot:t}));E(()=>t.attach((r,a)=>n(c=>be(c,t,r,a))),[t]),w(()=>t.synchronize(),[t]);const{inFlight:s,outcome:i}=e.slot===t?e:fe,o=t.run;return l(()=>({inFlight:s,outcome:i,run:o}),[s,i,o])}function ge(t){const n=C().commandRecords.get(t);if(n===void 0)throw new d("missing",`Command ${t.id} is not bound in this contribution.`);return b(pe(n.invoker))}const me=()=>{},K=()=>me,we=()=>0,ye=Object.freeze({}),Se=new Map;function p(t){return"kind"in t}function g(t){const e=new h(t);return{detach:void 0,hook:{inFlight:!1,outcome:void 0,run:e.run},invoker:t,slot:e}}function ve(t,e){return t.inFlight===e.inFlight&&Object.is(t.outcome,e.outcome)}function ke(t,e,n){const s=t.hook;if(n===void 0&&s.inFlight===e)return!1;const i=n===void 0||n.status==="cancelled"?void 0:n,o={inFlight:e,outcome:i??s.outcome,run:s.run};return ve(s,o)?!1:(t.hook=o,!0)}function Oe(t,e){return t!==void 0&&!p(t)&&t.invoker===e?t:g(e)}function H(t,e){return{invalidate:t.invalidate.hook,pagination:t.paginated?{loadNext:t.loadNext.hook,state:e.pagination}:void 0,refresh:t.refresh.hook,retry:t.retry.hook,state:e.state}}function Ee(t,e){const n={invalidate:g(t.invalidate),kind:"resource",loadNext:g(t.loadNext),paginated:e.pagination!==void 0,refresh:g(t.refresh),retry:g(t.retry)};return{...n,hook:H(n,e)}}function Ce(t){var n;const{hook:e}=t;return e.refresh===t.refresh.hook&&e.retry===t.retry.hook&&e.invalidate===t.invalidate.hook&&(!t.paginated||((n=e.pagination)==null?void 0:n.loadNext)===t.loadNext.hook)}function Re(t,e){var n;return Object.is(t.hook.state,e.state)&&Object.is((n=t.hook.pagination)==null?void 0:n.state,e.pagination)}function ze(t,e){return Ce(t)&&Re(t,e)}function Me(t,e,n){return t===void 0||!p(t)||t.refresh.invoker!==e.refresh?Ee(e,n):(ze(t,n)||(t.hook=H(t,n)),t)}function je(t){return p(t)?[t.refresh,t.retry,t.invalidate,t.loadNext]:[t]}function $(t){var e;for(const n of je(t))(e=n.detach)==null||e.call(n),n.detach=void 0}function xe(t){return t.hook}class Fe{entries=new Map;invokers=new WeakMap;listeners=new Set;revision=0;close(){for(const e of this.entries.values())$(e);this.entries.clear()}commit(e){for(const[n,s]of this.entries)e.get(n)!==s&&$(s);this.entries=e;for(const n of e.values())this.attachEntry(n)}attachEntry(e){if(p(e)){this.attachCommand(e.refresh),this.attachCommand(e.retry),this.attachCommand(e.invalidate),this.attachCommand(e.loadNext);return}this.attachCommand(e)}attachCommand(e){e.detach??(e.detach=e.slot.attach((n,s)=>{if(ke(e,n,s)){this.revision+=1;for(const i of this.listeners)i()}}))}getSnapshot=()=>this.revision;prepare(e,n,s){const i=new Map;for(const o of Reflect.ownKeys(e)){if(!Object.prototype.propertyIsEnumerable.call(e,o))continue;const r=e[o],a=r===void 0?void 0:s.commandRecords.get(r);if(a===void 0)throw new d("missing",`Command at ${String(o)} is not bound in this contribution.`);i.set(o,Oe(this.entries.get(o),a.invoker))}for(const[o,r]of n){const a=ne(r.resource,this.invokers);i.set(o,Me(this.entries.get(o),a,r))}return i}subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)})}function Pe(t,e){const n=S(void 0);return e.size>0||Reflect.ownKeys(t).some(i=>Object.prototype.propertyIsEnumerable.call(t,i))?n.current??(n.current=new Fe):void 0}function L(t,e){const n=C(),s=Pe(t,e),i=(s==null?void 0:s.getSnapshot)??we;y((s==null?void 0:s.subscribe)??K,i,i);const o=s==null?void 0:s.prepare(t,e,n);return E(()=>()=>s==null?void 0:s.close(),[s]),E(()=>{o!==void 0&&(s==null||s.commit(o))}),w(()=>{if(o!==void 0)for(const r of o.values())p(r)?(r.refresh.slot.synchronize(),r.retry.slot.synchronize(),r.invalidate.slot.synchronize(),r.loadNext.slot.synchronize()):r.slot.synchronize()}),o===void 0?ye:Object.fromEntries([...o].map(([r,a])=>[r,xe(a)]))}function Ne(t){return L(t,Se)}const T=new Map,U=new Set;function Ie(t,e){if(!_(t))throw new d("binding-invalid","Model selection read requires a Readable.");if(e.has(t))return e.get(t);const n=t.getSnapshot();return e.set(t,n),n}function W(t,e){if(e.has(t))return e.get(t);const n=t.getSnapshot();return e.set(t,n),n}function Ae(t,e,n,s){if(!se(e))return;const i=e,o=i.pagination;n.set(t,{pagination:o===void 0?void 0:W(o.state,s),resource:i,state:W(i.state,s)})}function Ke(t){return t.size===0?T:t}function He(t,e){const s=t({read:(r,a)=>{const c=Ie(r,e);return a===void 0?c:a(c)}});if(typeof s!="object"||s===null||Array.isArray(s))throw new d("binding-invalid","Model selection requires a record.");const i=[],o=new Map;for(const r of Reflect.ownKeys(s)){if(!Object.prototype.propertyIsEnumerable.call(s,r))continue;const a=s[r];i.push([r,a]),Ae(r,a,o,e)}return{resources:Ke(o),values:Object.fromEntries(i)}}function $e(t,e){if(t.size!==e.size)return!1;for(const[n,s]of e){const i=t.get(n);if((i==null?void 0:i.resource)!==s.resource||!Object.is(i.state,s.state)||!Object.is(i.pagination,s.pagination))return!1}return!0}function Le(t,e,n,s){const i=Reflect.ownKeys(e),o=Reflect.ownKeys(t.values);if(i.length!==o.length||n.size!==t.sources.size)return!1;for(const r of n)if(!t.sources.has(r))return!1;return $e(t.resources,s)?i.every((r,a)=>o[a]===r&&Object.is(t.values[r],e[r])):!1}const v={commands:{},leases:U,resources:T,sources:new Set,values:{}};function Te(t){return Object.fromEntries(Reflect.ownKeys(t).filter(e=>Z(t[e])).map(e=>[e,t[e]]))}function Ue(t){if(t.size===0)return U;const e=new Set;for(const n of t.values())e.add(n.resource);return e}function We(t){for(const[e,n]of t)if(!Object.is(e.getSnapshot(),n))return!1;return!0}function Ge(){return{model:void 0,observed:void 0,select:void 0,snapshot:v}}function qe(t,e,n){return n.model!==t&&(n.model=t,n.observed=void 0,n.select=void 0,n.snapshot=v),()=>{const s=e.current;if(s===void 0)return v;const i=n.observed;if(i!==void 0&&n.select===s&&We(i))return n.snapshot;const o=new Map,{resources:r,values:a}=He(f=>s(t,f),o),c=new Set(o.keys());return(i===void 0||!Le(n.snapshot,a,c,r))&&(n.snapshot={commands:Te(a),leases:Ue(r),resources:r,sources:c,values:a}),n.observed=o,n.select=s,n.snapshot}}const R=new Set;function G(t,e=[]){for(const n of t){const s=n.dispose;n.dispose=void 0;try{s==null||s()}catch(i){e.push(i)}}if(e.length===1)throw e[0];if(e.length>1)throw ee(e,"Model selection sources failed to release.")}class De{enabled=!1;leases=new Map;listeners=new Set;retained=R;sources=new Set;subscriptions=new Map;close(){this.enabled=!1,this.sources=new Set,this.retained=R,this.releaseAll()}commit(e,n=R){this.sources=e,this.retained=n,this.enabled=!0;try{this.synchronize()}catch(s){this.releaseAll([s])}}subscribe=e=>{const n={notify:e};this.listeners.add(n);try{this.synchronize()}catch(s){this.listeners.delete(n),this.releaseAll([s])}return()=>{this.listeners.delete(n),this.listeners.size===0&&this.releaseAll()}};add(e){const n={dispose:void 0};this.subscriptions.set(e,n);const s=e.subscribe(this.notify);if(typeof s!="function")throw new TypeError("Model selection source must return a disposer.");this.subscriptions.get(e)===n?n.dispose=s:s()}notify=()=>{for(const e of[...this.listeners])this.listeners.has(e)&&e.notify()};releaseAll(e){const n=[...this.subscriptions.values(),...this.leases.values()];this.subscriptions.clear(),this.leases.clear(),G(n,e)}removeUnselected(){const e=[];for(const[n,s]of this.subscriptions)this.sources.has(n)||(this.subscriptions.delete(n),e.push(s));for(const[n,s]of this.leases)this.retained.has(n)||(this.leases.delete(n),e.push(s));G(e)}retain(e){const n={dispose:void 0};this.leases.set(e,n);const s=e.acquire();if(typeof s!="function")throw new TypeError("Model selection resource must answer a release disposer.");this.leases.get(e)===n?n.dispose=s:s()}ready(){return this.enabled&&this.listeners.size>0}retainSelected(){for(const e of this.retained){if(!this.ready())return;this.retained.has(e)&&!this.leases.has(e)&&this.retain(e)}}subscribeSources(){for(const e of this.sources){if(!this.ready())return;this.sources.has(e)&&!this.subscriptions.has(e)&&this.add(e)}}synchronize(){this.removeUnselected(),this.subscribeSources(),this.retainSelected()}}const Be=()=>v;function Je(t){const e=S(void 0);return t?e.current??(e.current=new De):void 0}function Qe(t,e){return Object.fromEntries(Reflect.ownKeys(t.values).map(n=>[n,Object.prototype.hasOwnProperty.call(e,n)?e[n]:t.values[n]]))}function Ve(t,e){const n=C();if(!n.models.has(t))throw new d("missing",`Model ${t.id} is not granted to this contribution.`);const s=n.models.get(t),i=Je(e!==void 0),o=S(void 0);o.current=e;const r=e!==void 0,a=S(void 0),c=a.current??(a.current=Ge()),f=l(()=>r?qe(s,o,c):Be,[c,s,r]),u=y((i==null?void 0:i.subscribe)??K,f,f),m=L(u.commands,u.resources);return w(()=>()=>i==null?void 0:i.close(),[i]),w(()=>i==null?void 0:i.commit(u.sources,u.leases),[u,i]),e===void 0?s:Qe(u,m)}function Xe(t){const e=Object.freeze([...t]);return n=>{if(n.requires!==void 0)throw new d("duplicate","Component already declares the models it requires.");return Object.defineProperty(n,"requires",{enumerable:!1,value:e}),n}}function Ye(t){const[e]=O(()=>new WeakMap),n=e.get(t);if(n!==void 0)return n;const s=oe(t),i={invalidate:new h(s.invalidate),loadNext:new h(s.loadNext),refresh:new h(s.refresh),retry:new h(s.retry)};return e.set(t,i),i}const Ze=Object.freeze({kind:"unavailable",reason:"resource-not-ready"}),_e=Object.freeze({getSnapshot:()=>Ze,subscribe:()=>()=>{}});function et(t){const e=t,n=ie(e),s=F(k=>t.state.subscribe(k),[t]),i=l(()=>()=>t.state.getSnapshot(),[t]),o=y(s,i,i),r=(n==null?void 0:n.state)??_e,a=F(k=>r.subscribe(k),[r]),c=l(()=>()=>r.getSnapshot(),[r]),f=y(a,c,c);B(()=>t.acquire(),[t]);const u=Ye(e),m=b(u.refresh),z=b(u.retry),M=b(u.invalidate),j=b(u.loadNext),x=l(()=>n===void 0?void 0:{loadNext:j,state:f},[n,j,f]);return l(()=>({invalidate:M,pagination:x,refresh:m,retry:z,state:o}),[M,x,m,z,o])}const q=Object.freeze({});function D(t){return te(t,"slot")}function tt(t){const e=V(t),n=new Map,s=i=>{const o=n.get(i);if(o!==void 0)return o;const r=D(`${e}/${i}`);return n.set(i,r),r};return Object.defineProperty(s,"id",{enumerable:!0,value:e}),Object.freeze(s)}function nt(t){const e=I(t.target.entries),n="props"in t?t.props??q:q;return e.length===0?null:e.map(s=>P(J,{key:s.id},P(re,{authority:ae(s),contribution:s.value,contributionId:s.id,slotProps:n,targetId:t.target.id})))}export{d as ContributionError,nt as Slot,D as defineSlot,tt as defineSwitchSlot,Xe as requiresModels,ge as useCommand,Ne as useCommands,Ve as useModel,I as useReadable,et as useResource,dt as useSelector};
|
|
1
|
+
import{useState as O,useInsertionEffect as E,useLayoutEffect as w,useMemo as l,useSyncExternalStore as y,useRef as S,useCallback as F,useEffect as D,createElement as P,Fragment as B}from"react";import{CommandError as J,declarationId as Q}from"@opetope/core";import{compatibleAbortReason as V,isCancellation as X,reportDetachedError as N,isCallTarget as Y,isReadable as Z,createAggregateError as _,createContributionTarget as ee}from"@opetope/core/internal";import{C as d}from"./contribution-isolation-Bzfbt4iM.js";import{u as C,r as te,i as ne,p as se,c as ie,a as I,M as oe,b as re}from"./contribution-frame-CCKnOxZR.js";import{d as at}from"./contribution-frame-CCKnOxZR.js";function A(t,e){var n;if(X(e))return{reason:e,kind:"cancelled"};try{const s=(n=t==null?void 0:t.onFailure)==null?void 0:n.call(t,e);s!==void 0&&Promise.resolve(s).catch(N)}catch(s){N(s)}return{error:e,kind:"failed"}}function ce(t){const e=t==null?void 0:t.onSuccess,n=t==null?void 0:t.signal;return e===void 0?n===void 0?void 0:{signal:n}:n===void 0?{onSuccess:e}:{onSuccess:e,signal:n}}function ae(t,e){let n;const s=new Promise(i=>{n=i});return{input:t,options:e===void 0?void 0:{...e},promise:s,resolve:n}}function ue(t,e){const n=t.resolve;t.input=void 0,t.options=void 0,t.resolve=void 0,n==null||n(e)}class h{invoker;run=((e,n)=>{if(this.notify===void 0)return Promise.resolve({reason:new d("inactive","Command consumer is not mounted."),kind:"cancelled"});const s=this.cancelledInput(n);if(s!==void 0)return Promise.resolve(s);const i=ae(e,n);return this.start(i),i.promise});inFlight=0;notify;constructor(e){this.invoker=e}attach(e){return this.notify=e,()=>{this.notify===e&&(this.notify=void 0)}}synchronize(){var e;(e=this.notify)==null||e.call(this,this.inFlight>0)}aborted(e){return{reason:new J("cancelled","Command input was cancelled.",V(e)),kind:"cancelled"}}cancelledInput(e){var n;return((n=e==null?void 0:e.signal)==null?void 0:n.aborted)===!0?this.aborted(e.signal):void 0}finish(e,n){var s;e.resolve!==void 0&&(this.inFlight-=1,ue(e,n),(s=this.notify)==null||s.call(this,this.inFlight>0,n))}invoke(e){let n;try{n=this.invoker.run(e.input,ce(e.options)),e.input=void 0}catch(s){this.finish(e,A(e.options,s));return}n.then(s=>this.finish(e,{kind:"ok",value:s}),s=>this.finish(e,A(e.options,s)))}start(e){var n;this.inFlight+=1,(n=this.notify)==null||n.call(this,!0),this.invoke(e)}}const de={inFlight:!1,outcome:void 0};function fe(t){return t===void 0||t.kind==="cancelled"?void 0:t}function le(t,e,n,s){return s===void 0&&t.slot===e&&t.inFlight===n}function he(t,e,n,s){const i=fe(s);if(le(t,e,n,i))return t;const o=t.slot===e?t.outcome:void 0;return{inFlight:n,outcome:i??o,slot:e}}function be(t){const[e]=O(()=>new WeakMap);let n=e.get(t);return n===void 0&&(n=new h(t),e.set(t,n)),n}function b(t){const[e,n]=O(()=>({inFlight:!1,outcome:void 0,slot:t}));E(()=>t.attach((r,c)=>n(a=>he(a,t,r,c))),[t]),w(()=>t.synchronize(),[t]);const{inFlight:s,outcome:i}=e.slot===t?e:de,o=t.run;return l(()=>({inFlight:s,outcome:i,run:o}),[s,i,o])}function pe(t){const n=C().commandRecords.get(t);if(n===void 0)throw new d("missing",`Command ${t.id} is not bound in this contribution.`);return b(be(n.invoker))}const ge=()=>{},K=()=>ge,me=()=>0,we=Object.freeze({});function p(t){return"kind"in t}function g(t){const e=new h(t);return{detach:void 0,hook:{inFlight:!1,outcome:void 0,run:e.run},invoker:t,slot:e}}function ye(t,e){return t.inFlight===e.inFlight&&Object.is(t.outcome,e.outcome)}function Se(t,e,n){const s=t.hook;if(n===void 0&&s.inFlight===e)return!1;const i=n===void 0||n.kind==="cancelled"?void 0:n,o={inFlight:e,outcome:i??s.outcome,run:s.run};return ye(s,o)?!1:(t.hook=o,!0)}function ke(t,e){return t!==void 0&&!p(t)&&t.invoker===e?t:g(e)}function H(t,e){return{pagination:t.paginated?{loadNext:t.loadNext.hook,state:e.pagination}:void 0,refresh:t.refresh.hook,reset:t.reset.hook,retry:t.retry.hook,state:e.state}}function ve(t,e){const n={kind:"resource",loadNext:g(t.loadNext),paginated:e.pagination!==void 0,refresh:g(t.refresh),reset:g(t.reset),retry:g(t.retry)};return{...n,hook:H(n,e)}}function Oe(t){var n;const{hook:e}=t;return e.refresh===t.refresh.hook&&e.reset===t.reset.hook&&e.retry===t.retry.hook&&(!t.paginated||((n=e.pagination)==null?void 0:n.loadNext)===t.loadNext.hook)}function Ee(t,e){var n;return Object.is(t.hook.state,e.state)&&Object.is((n=t.hook.pagination)==null?void 0:n.state,e.pagination)}function Ce(t,e){return Oe(t)&&Ee(t,e)}function Re(t,e,n){return t===void 0||!p(t)||t.refresh.invoker!==e.refresh?ve(e,n):(Ce(t,n)||(t.hook=H(t,n)),t)}function ze(t){return p(t)?[t.refresh,t.reset,t.retry,t.loadNext]:[t]}function $(t){var e;for(const n of ze(t))(e=n.detach)==null||e.call(n),n.detach=void 0}function Me(t){return t.hook}class je{entries=new Map;invokers=new WeakMap;listeners=new Set;revision=0;close(){for(const e of this.entries.values())$(e);this.entries.clear()}commit(e){for(const[n,s]of this.entries)e.get(n)!==s&&$(s);this.entries=e;for(const n of e.values())this.attachEntry(n)}attachEntry(e){if(p(e)){this.attachCommand(e.refresh),this.attachCommand(e.retry),this.attachCommand(e.reset),this.attachCommand(e.loadNext);return}this.attachCommand(e)}attachCommand(e){e.detach??(e.detach=e.slot.attach((n,s)=>{if(Se(e,n,s)){this.revision+=1;for(const i of this.listeners)i()}}))}getSnapshot=()=>this.revision;prepare(e,n,s){const i=new Map;for(const o of Reflect.ownKeys(e)){if(!Object.prototype.propertyIsEnumerable.call(e,o))continue;const r=e[o],c=r===void 0?void 0:s.commandRecords.get(r);if(c===void 0)throw new d("missing",`Command at ${String(o)} is not bound in this contribution.`);i.set(o,ke(this.entries.get(o),c.invoker))}for(const[o,r]of n){const c=te(r.resource,this.invokers);i.set(o,Re(this.entries.get(o),c,r))}return i}subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)})}function xe(t,e){const n=S(void 0);return e.size>0||Reflect.ownKeys(t).some(i=>Object.prototype.propertyIsEnumerable.call(t,i))?n.current??(n.current=new je):void 0}function Fe(t,e){const n=C(),s=xe(t,e),i=(s==null?void 0:s.getSnapshot)??me;y((s==null?void 0:s.subscribe)??K,i,i);const o=s==null?void 0:s.prepare(t,e,n);return E(()=>()=>s==null?void 0:s.close(),[s]),E(()=>{o!==void 0&&(s==null||s.commit(o))}),w(()=>{if(o!==void 0)for(const r of o.values())p(r)?(r.refresh.slot.synchronize(),r.retry.slot.synchronize(),r.reset.slot.synchronize(),r.loadNext.slot.synchronize()):r.slot.synchronize()}),o===void 0?we:Object.fromEntries([...o].map(([r,c])=>[r,Me(c)]))}const L=new Map,T=new Set;function Pe(t,e){if(!Z(t))throw new d("binding-invalid","Model selection read requires a Readable.");if(e.has(t))return e.get(t);const n=t.getSnapshot();return e.set(t,n),n}function U(t,e){if(e.has(t))return e.get(t);const n=t.getSnapshot();return e.set(t,n),n}function Ne(t,e,n,s){if(!ne(e))return;const i=e,o=i.pagination;n.set(t,{pagination:o===void 0?void 0:U(o.state,s),resource:i,state:U(i.state,s)})}function Ie(t){return t.size===0?L:t}function Ae(t,e){const s=t({read:(r,c)=>{const a=Pe(r,e);return c===void 0?a:c(a)}});if(typeof s!="object"||s===null||Array.isArray(s))throw new d("binding-invalid","Model selection requires a record.");const i=[],o=new Map;for(const r of Reflect.ownKeys(s)){if(!Object.prototype.propertyIsEnumerable.call(s,r))continue;const c=s[r];i.push([r,c]),Ne(r,c,o,e)}return{resources:Ie(o),values:Object.fromEntries(i)}}function Ke(t,e){if(t.size!==e.size)return!1;for(const[n,s]of e){const i=t.get(n);if((i==null?void 0:i.resource)!==s.resource||!Object.is(i.state,s.state)||!Object.is(i.pagination,s.pagination))return!1}return!0}function He(t,e,n,s){const i=Reflect.ownKeys(e),o=Reflect.ownKeys(t.values);if(i.length!==o.length||n.size!==t.sources.size)return!1;for(const r of n)if(!t.sources.has(r))return!1;return Ke(t.resources,s)?i.every((r,c)=>o[c]===r&&Object.is(t.values[r],e[r])):!1}const k={commands:{},leases:T,resources:L,sources:new Set,values:{}};function $e(t){return Object.fromEntries(Reflect.ownKeys(t).filter(e=>Y(t[e])).map(e=>[e,t[e]]))}function Le(t){if(t.size===0)return T;const e=new Set;for(const n of t.values())e.add(n.resource);return e}function Te(t){for(const[e,n]of t)if(!Object.is(e.getSnapshot(),n))return!1;return!0}function Ue(){return{model:void 0,observed:void 0,select:void 0,snapshot:k}}function We(t,e,n){return n.model!==t&&(n.model=t,n.observed=void 0,n.select=void 0,n.snapshot=k),()=>{const s=e.current;if(s===void 0)return k;const i=n.observed;if(i!==void 0&&n.select===s&&Te(i))return n.snapshot;const o=new Map,{resources:r,values:c}=Ae(f=>s(t,f),o),a=new Set(o.keys());return(i===void 0||!He(n.snapshot,c,a,r))&&(n.snapshot={commands:$e(c),leases:Le(r),resources:r,sources:a,values:c}),n.observed=o,n.select=s,n.snapshot}}const R=new Set;function W(t,e=[]){for(const n of t){const s=n.dispose;n.dispose=void 0;try{s==null||s()}catch(i){e.push(i)}}if(e.length===1)throw e[0];if(e.length>1)throw _(e,"Model selection sources failed to release.")}class Ge{enabled=!1;leases=new Map;listeners=new Set;retained=R;sources=new Set;subscriptions=new Map;close(){this.enabled=!1,this.sources=new Set,this.retained=R,this.releaseAll()}commit(e,n=R){this.sources=e,this.retained=n,this.enabled=!0;try{this.synchronize()}catch(s){this.releaseAll([s])}}subscribe=e=>{const n={notify:e};this.listeners.add(n);try{this.synchronize()}catch(s){this.listeners.delete(n),this.releaseAll([s])}return()=>{this.listeners.delete(n),this.listeners.size===0&&this.releaseAll()}};add(e){const n={dispose:void 0};this.subscriptions.set(e,n);const s=e.subscribe(this.notify);if(typeof s!="function")throw new TypeError("Model selection source must return a disposer.");this.subscriptions.get(e)===n?n.dispose=s:s()}notify=()=>{for(const e of[...this.listeners])this.listeners.has(e)&&e.notify()};releaseAll(e){const n=[...this.subscriptions.values(),...this.leases.values()];this.subscriptions.clear(),this.leases.clear(),W(n,e)}removeUnselected(){const e=[];for(const[n,s]of this.subscriptions)this.sources.has(n)||(this.subscriptions.delete(n),e.push(s));for(const[n,s]of this.leases)this.retained.has(n)||(this.leases.delete(n),e.push(s));W(e)}retain(e){const n={dispose:void 0};this.leases.set(e,n);const s=e.acquire();if(typeof s!="function")throw new TypeError("Model selection resource must answer a release disposer.");this.leases.get(e)===n?n.dispose=s:s()}ready(){return this.enabled&&this.listeners.size>0}retainSelected(){for(const e of this.retained){if(!this.ready())return;this.retained.has(e)&&!this.leases.has(e)&&this.retain(e)}}subscribeSources(){for(const e of this.sources){if(!this.ready())return;this.sources.has(e)&&!this.subscriptions.has(e)&&this.add(e)}}synchronize(){this.removeUnselected(),this.subscribeSources(),this.retainSelected()}}const qe=()=>k;function De(t){const e=S(void 0);return t?e.current??(e.current=new Ge):void 0}function Be(t,e){return Object.fromEntries(Reflect.ownKeys(t.values).map(n=>[n,Object.prototype.hasOwnProperty.call(e,n)?e[n]:t.values[n]]))}function Je(t,e){const n=C();if(!n.models.has(t))throw new d("missing",`Model ${t.id} is not granted to this contribution.`);const s=n.models.get(t),i=De(e!==void 0),o=S(void 0);o.current=e;const r=e!==void 0,c=S(void 0),a=c.current??(c.current=Ue()),f=l(()=>r?We(s,o,a):qe,[a,s,r]),u=y((i==null?void 0:i.subscribe)??K,f,f),m=Fe(u.commands,u.resources);return w(()=>()=>i==null?void 0:i.close(),[i]),w(()=>i==null?void 0:i.commit(u.sources,u.leases),[u,i]),e===void 0?s:Be(u,m)}function Qe(t){const e=Object.freeze([...t]);return n=>{if(n.requires!==void 0)throw new d("duplicate","Component already declares the models it requires.");return Object.defineProperty(n,"requires",{enumerable:!1,value:e}),n}}function Ve(t){const[e]=O(()=>new WeakMap),n=e.get(t);if(n!==void 0)return n;const s=ie(t),i={loadNext:new h(s.loadNext),refresh:new h(s.refresh),reset:new h(s.reset),retry:new h(s.retry)};return e.set(t,i),i}const Xe=Object.freeze({kind:"unavailable",reason:"resource-not-ready"}),Ye=Object.freeze({getSnapshot:()=>Xe,subscribe:()=>()=>{}});function Ze(t){const e=t,n=se(e),s=F(v=>t.state.subscribe(v),[t]),i=l(()=>()=>t.state.getSnapshot(),[t]),o=y(s,i,i),r=(n==null?void 0:n.state)??Ye,c=F(v=>r.subscribe(v),[r]),a=l(()=>()=>r.getSnapshot(),[r]),f=y(c,a,a);D(()=>t.acquire(),[t]);const u=Ve(e),m=b(u.refresh),z=b(u.retry),M=b(u.reset),j=b(u.loadNext),x=l(()=>n===void 0?void 0:{loadNext:j,state:f},[n,j,f]);return l(()=>({pagination:x,refresh:m,reset:M,retry:z,state:o}),[x,m,M,z,o])}const G=Object.freeze({});function q(t){return ee(t,"slot")}function _e(t){const e=Q(t),n=new Map,s=i=>{const o=n.get(i);if(o!==void 0)return o;const r=q(`${e}/${i}`);return n.set(i,r),r};return Object.defineProperty(s,"id",{enumerable:!0,value:e}),Object.freeze(s)}function et(t){const e=I(t.target.entries),n="props"in t?t.props??G:G;return e.length===0?null:e.map(s=>P(B,{key:s.id},P(oe,{authority:re(s),contribution:s.value,contributionId:s.id,slotProps:n,targetId:t.target.id})))}export{d as ContributionError,et as Slot,q as defineSlot,_e as defineSwitchSlot,Qe as requiresModels,pe as useCommand,Je as useModel,I as useReadable,Ze as useResource,at as useSelector};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/command-hook-controller.ts","../src/command-hook.ts","../src/idle-subscription.ts","../src/commands-hook.ts","../src/model-selection-snapshot.ts","../src/model-selection-store.ts","../src/model-hook.ts","../src/requires-models.ts","../src/resource-hook.ts","../src/slot.tsx"],"sourcesContent":["import { CallError } from '@opetope/core';\nimport { compatibleAbortReason, isCancellation, reportDetachedError } from '@opetope/core/internal';\nimport type { CallInputArgs, CallRunOptions } from '@opetope/core/internal';\n\nimport type { CommandOutcome } from './command';\nimport { ContributionError } from './errors';\n\ntype CommandRunOptions<Output> = Readonly<{\n onFailure?: (error: unknown) => PromiseLike<void> | void;\n onSuccess?: (value: Output) => PromiseLike<void> | void;\n signal?: AbortSignal;\n}>;\n\ntype CommandRun<Input, Output> = {\n (input: Input, options?: CommandRunOptions<Output>): Promise<CommandOutcome<Output>>;\n (...args: [...CallInputArgs<Input>, options?: CommandRunOptions<Output>]): Promise<CommandOutcome<Output>>;\n};\n\ntype CommandInvoker = Readonly<{\n run: (input: never, options?: CallRunOptions<unknown>) => Promise<unknown>;\n}>;\n\ntype CommandNotification<Output> = (inFlight: boolean, outcome?: CommandOutcome<Output>) => void;\n\ntype CommandRequest<Input, Output> = {\n input: Input | undefined;\n options: CommandRunOptions<Output> | undefined;\n readonly promise: Promise<CommandOutcome<Output>>;\n resolve: ((outcome: CommandOutcome<Output>) => void) | undefined;\n};\n\nfunction failureOutcome<Output>(options: CommandRunOptions<Output> | undefined, error: unknown): CommandOutcome<never> {\n if (isCancellation(error)) return { reason: error, status: 'cancelled' };\n\n try {\n const notification = options?.onFailure?.(error);\n\n if (notification !== undefined) void Promise.resolve(notification).catch(reportDetachedError);\n } catch (callbackError) {\n reportDetachedError(callbackError);\n }\n\n return { error, status: 'failed' };\n}\n\nfunction runOptions<Output>(options: CommandRunOptions<Output> | undefined): CallRunOptions<Output> | undefined {\n const onSuccess = options?.onSuccess;\n const signal = options?.signal;\n\n if (onSuccess === undefined) return signal === undefined ? undefined : { signal };\n\n return signal === undefined ? { onSuccess } : { onSuccess, signal };\n}\n\nfunction requestOf<Input, Output>(\n input: Input,\n options: CommandRunOptions<Output> | undefined,\n): CommandRequest<Input, Output> {\n let resolve!: (outcome: CommandOutcome<Output>) => void;\n const promise = new Promise<CommandOutcome<Output>>(settle => {\n resolve = settle;\n });\n\n return { input, options: options === undefined ? undefined : { ...options }, promise, resolve };\n}\n\nfunction settle<Input, Output>(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n const resolve = request.resolve;\n request.input = undefined;\n request.options = undefined;\n request.resolve = undefined;\n resolve?.(outcome);\n}\n\n/**\n * D203: the consumer keeps a status, not a schedule. Every `run` reaches the call, and the policy the call was\n * created with — `queue`, `latest`, `parallel`, `once`, `dedupe` — decides what happens to it. What stays\n * local is what only this consumer knows: whether it is mounted, its own input signal, and the outcome it shows.\n */\nclass CommandHookController<Input, Output> {\n readonly run: CommandRun<Input, Output> = ((input: Input, options?: CommandRunOptions<Output>) => {\n if (this.notify === undefined) {\n return Promise.resolve({\n reason: new ContributionError('inactive', 'Command consumer is not mounted.'),\n status: 'cancelled' as const,\n });\n }\n\n const aborted = this.cancelledInput(options);\n\n if (aborted !== undefined) return Promise.resolve(aborted);\n\n const request = requestOf(input, options);\n this.start(request);\n\n return request.promise;\n }) as CommandRun<Input, Output>;\n private inFlight = 0;\n private notify: CommandNotification<Output> | undefined;\n\n constructor(private readonly invoker: CommandInvoker) {}\n\n attach(notify: CommandNotification<Output>): () => void {\n this.notify = notify;\n\n return () => {\n if (this.notify !== notify) return;\n\n this.notify = undefined;\n };\n }\n\n synchronize(): void {\n this.notify?.(this.inFlight > 0);\n }\n\n private aborted(signal: AbortSignal): CommandOutcome<never> {\n return {\n reason: new CallError('cancelled', 'Command input was cancelled.', compatibleAbortReason(signal)),\n status: 'cancelled',\n };\n }\n\n private cancelledInput(options: CommandRunOptions<Output> | undefined): CommandOutcome<never> | undefined {\n return options?.signal?.aborted === true ? this.aborted(options.signal) : undefined;\n }\n\n private finish(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n if (request.resolve === undefined) return;\n\n this.inFlight -= 1;\n settle(request, outcome);\n this.notify?.(this.inFlight > 0, outcome);\n }\n\n private invoke(request: CommandRequest<Input, Output>): void {\n let invocation: Promise<Output>;\n\n try {\n invocation = this.invoker.run(request.input as never, runOptions(request.options) as never) as Promise<Output>;\n request.input = undefined;\n } catch (error) {\n this.finish(request, failureOutcome(request.options, error));\n\n return;\n }\n\n void invocation.then(\n value => this.finish(request, { status: 'ok', value }),\n (error: unknown) => this.finish(request, failureOutcome(request.options, error)),\n );\n }\n\n private start(request: CommandRequest<Input, Output>): void {\n this.inFlight += 1;\n // Reserve the flight before publishing: a reentrant run must already see this consumer as busy.\n this.notify?.(true);\n this.invoke(request);\n }\n}\n\nexport { CommandHookController };\nexport type { CommandInvoker, CommandRun };\n","import { useInsertionEffect, useLayoutEffect, useMemo, useState } from 'react';\n\nimport type { Call } from '@opetope/core';\n\nimport type { CommandOutcome } from './command';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker, CommandRun } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { useFrame } from './mount-context';\n\n/** D292: a cancellation is not an answer of the product, so it never settles into the outcome this hook shows. */\ntype SettledOutcome<Output> = Exclude<CommandOutcome<Output>, { readonly status: 'cancelled' }>;\n\ninterface CommandHookStatus<Output> {\n readonly inFlight: boolean;\n readonly outcome: SettledOutcome<Output> | undefined;\n}\n\nconst emptyStatus: CommandHookStatus<never> = { inFlight: false, outcome: undefined };\n\ntype CommandHook<Input, Output> = CommandHookStatus<Output> & {\n readonly run: CommandRun<Input, Output>;\n};\n\ntype CommandHookState<Input, Output> = CommandHookStatus<Output> & {\n readonly slot: CommandHookController<Input, Output>;\n};\n\n/** D292: a cancellation moves nothing, so it is the same non-event as a notification without an outcome at all. */\nfunction settledOutcome<Output>(outcome: CommandOutcome<Output> | undefined): SettledOutcome<Output> | undefined {\n return outcome === undefined || outcome.status === 'cancelled' ? undefined : outcome;\n}\n\nfunction unchangedState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n settled: SettledOutcome<Output> | undefined,\n): boolean {\n return settled === undefined && current.slot === slot && current.inFlight === inFlight;\n}\n\nfunction nextState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n outcome?: CommandOutcome<Output>,\n): CommandHookState<Input, Output> {\n const settled = settledOutcome(outcome);\n\n if (unchangedState(current, slot, inFlight, settled)) return current;\n\n // A new run clears nothing: the previous answer stays readable while its retry is in flight.\n const previous = current.slot === slot ? current.outcome : undefined;\n\n return { inFlight, outcome: settled ?? previous, slot };\n}\n\nfunction useCommandSlot<Input, Output>(invoker: CommandInvoker): CommandHookController<Input, Output> {\n // Slots are inert until commit. An interrupted render cannot fence the committed source or admit its own work.\n const [slots] = useState(() => new WeakMap<object, CommandHookController<Input, Output>>());\n let slot = slots.get(invoker);\n\n if (slot === undefined) {\n slot = new CommandHookController(invoker);\n slots.set(invoker, slot);\n }\n\n return slot;\n}\n\n/**\n * The status half of a consumer, apart from what it is a consumer of. D321 gives a resource the same two verbs a\n * command has, so the same three React hooks answer for them: the state of this consumer, the attachment that moves\n * it, and the synchronisation of a slot that was already busy when this mount arrived.\n */\nfunction useCommandStatus<Input, Output>(\n currentSlot: CommandHookController<Input, Output>,\n): CommandHook<Input, Output> {\n const [hookState, setHookState] = useState<CommandHookState<Input, Output>>(() => ({\n inFlight: false,\n outcome: undefined,\n slot: currentSlot,\n }));\n useInsertionEffect(\n () =>\n currentSlot.attach((inFlight, outcome) =>\n setHookState(current => nextState(current, currentSlot, inFlight, outcome)),\n ),\n [currentSlot],\n );\n useLayoutEffect(() => currentSlot.synchronize(), [currentSlot]);\n const { inFlight, outcome } = hookState.slot === currentSlot ? hookState : emptyStatus;\n const run = currentSlot.run;\n\n return useMemo(() => ({ inFlight, outcome, run }), [inFlight, outcome, run]);\n}\n\nfunction useCommand<Input, Output>(command: Call<Input, Output>): CommandHook<Input, Output> {\n const frame = useFrame();\n const record = frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command ${command.id} is not bound in this contribution.`);\n }\n\n return useCommandStatus(useCommandSlot<Input, Output>(record.invoker));\n}\n\nexport { useCommand, useCommandStatus };\nexport type { CommandHook };\n","const idleRelease = (): void => undefined;\nconst subscribeIdle = (): (() => void) => idleRelease;\nconst idleRevision = (): number => 0;\n\nexport { idleRevision, subscribeIdle };\n","import { useInsertionEffect, useLayoutEffect, useRef, useSyncExternalStore } from 'react';\n\nimport type { Call } from '@opetope/core';\n\nimport type { CommandOutcome } from './command';\nimport type { CommandHook } from './command-hook';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { idleRevision, subscribeIdle } from './idle-subscription';\nimport type { SelectedResource } from './model-selection-snapshot';\nimport { useFrame } from './mount-context';\nimport type { Frame } from './mount-frame';\nimport { resourceInvokers } from './resource-command';\nimport type { ResourceInvokers } from './resource-command';\n\ntype CommandSelection = Readonly<Record<string, Call<never, unknown>>>;\ntype ResourceSelection = ReadonlyMap<PropertyKey, SelectedResource>;\ntype HookOf<Command> = Command extends Call<infer Input, infer Output> ? CommandHook<Input, Output> : never;\ntype CommandsHook<Commands> = {\n readonly [Key in keyof Commands]: HookOf<Commands[Key]>;\n};\n/** D359: what one selected Resource answers — its three verbs, its state, and the pagination capability if it has one. */\ntype SelectedResourceHook = Readonly<{\n invalidate: CommandHook<never, unknown>;\n pagination: Readonly<{ loadNext: CommandHook<never, unknown>; state: unknown }> | undefined;\n refresh: CommandHook<never, unknown>;\n retry: CommandHook<never, unknown>;\n state: unknown;\n}>;\ntype SelectionHooks = Readonly<Record<PropertyKey, CommandHook<never, unknown> | SelectedResourceHook>>;\n\nconst emptyHooks: SelectionHooks = Object.freeze({});\nconst noResources: ResourceSelection = new Map();\n\ntype CommandEntry = {\n detach: (() => void) | undefined;\n hook: CommandHook<never, unknown>;\n readonly invoker: CommandInvoker;\n readonly slot: CommandHookController<never, unknown>;\n};\n\n/** D359: one key of a selection, four consumers. The record is the value; `entries` still holds one entry per key. */\ntype ResourceEntry = {\n /** D359: the value this key hands the render, kept on the record so a still frame is the same object again. */\n hook: SelectedResourceHook;\n readonly invalidate: CommandEntry;\n readonly kind: 'resource';\n readonly loadNext: CommandEntry;\n /** Whether the declaration carried the pagination capability at all. */\n readonly paginated: boolean;\n readonly refresh: CommandEntry;\n readonly retry: CommandEntry;\n};\n\ntype SelectionEntry = CommandEntry | ResourceEntry;\n\nfunction isResourceEntry(entry: SelectionEntry): entry is ResourceEntry {\n return 'kind' in entry;\n}\n\nfunction createEntry(invoker: CommandInvoker): CommandEntry {\n const slot = new CommandHookController<never, unknown>(invoker);\n\n return { detach: undefined, hook: { inFlight: false, outcome: undefined, run: slot.run }, invoker, slot };\n}\n\nfunction unchangedHook(previous: CommandHook<never, unknown>, next: CommandHook<never, unknown>): boolean {\n return previous.inFlight === next.inFlight && Object.is(previous.outcome, next.outcome);\n}\n\nfunction updateEntry(entry: CommandEntry, inFlight: boolean, outcome?: CommandOutcome<unknown>): boolean {\n const previous = entry.hook;\n\n if (outcome === undefined && previous.inFlight === inFlight) return false;\n\n // D292: only a settled non-cancelled outcome replaces the one this key shows; a retry keeps the previous answer.\n const settled = outcome === undefined || outcome.status === 'cancelled' ? undefined : outcome;\n const next = { inFlight, outcome: settled ?? previous.outcome, run: previous.run };\n\n if (unchangedHook(previous, next)) return false;\n\n entry.hook = next;\n\n return true;\n}\n\nfunction selectEntry(previous: SelectionEntry | undefined, invoker: CommandInvoker): CommandEntry {\n return previous !== undefined && !isResourceEntry(previous) && previous.invoker === invoker\n ? previous\n : createEntry(invoker);\n}\n\nfunction resourceHook(entry: Omit<ResourceEntry, 'hook'>, selected: SelectedResource): SelectedResourceHook {\n return {\n invalidate: entry.invalidate.hook,\n pagination: entry.paginated ? { loadNext: entry.loadNext.hook, state: selected.pagination } : undefined,\n refresh: entry.refresh.hook,\n retry: entry.retry.hook,\n state: selected.state,\n };\n}\n\nfunction createResourceEntry(invokers: ResourceInvokers, selected: SelectedResource): ResourceEntry {\n const parts = {\n invalidate: createEntry(invokers.invalidate),\n kind: 'resource' as const,\n loadNext: createEntry(invokers.loadNext),\n paginated: selected.pagination !== undefined,\n refresh: createEntry(invokers.refresh),\n retry: createEntry(invokers.retry),\n };\n\n return { ...parts, hook: resourceHook(parts, selected) };\n}\n\n/** The four commands of a Resource: the three verbs every one has, and `loadNext` only where one was declared. */\nfunction unchangedResourceCommands(entry: ResourceEntry): boolean {\n const { hook } = entry;\n\n return (\n hook.refresh === entry.refresh.hook &&\n hook.retry === entry.retry.hook &&\n hook.invalidate === entry.invalidate.hook &&\n (!entry.paginated || hook.pagination?.loadNext === entry.loadNext.hook)\n );\n}\n\n/** What this selection read: the state of the Resource, and the state of its capability where it has one. */\nfunction unchangedResourceState(entry: ResourceEntry, selected: SelectedResource): boolean {\n return Object.is(entry.hook.state, selected.state) && Object.is(entry.hook.pagination?.state, selected.pagination);\n}\n\nfunction unchangedResourceHook(entry: ResourceEntry, selected: SelectedResource): boolean {\n return unchangedResourceCommands(entry) && unchangedResourceState(entry, selected);\n}\n\n/**\n * S6/D359: the same Resource that did not move answers the same object, so a child under `memo` is not re-rendered by\n * the mere act of reading it. Every part is a render input, so this is a memo and not a piece of state.\n */\nfunction selectResourceEntry(\n previous: SelectionEntry | undefined,\n invokers: ResourceInvokers,\n selected: SelectedResource,\n): ResourceEntry {\n if (previous === undefined || !isResourceEntry(previous) || previous.refresh.invoker !== invokers.refresh) {\n return createResourceEntry(invokers, selected);\n }\n\n if (!unchangedResourceHook(previous, selected)) previous.hook = resourceHook(previous, selected);\n\n return previous;\n}\n\nfunction commandEntries(entry: SelectionEntry): readonly CommandEntry[] {\n return isResourceEntry(entry) ? [entry.refresh, entry.retry, entry.invalidate, entry.loadNext] : [entry];\n}\n\nfunction detachEntry(entry: SelectionEntry): void {\n for (const command of commandEntries(entry)) {\n command.detach?.();\n command.detach = undefined;\n }\n}\n\nfunction hookOf(entry: SelectionEntry): CommandHook<never, unknown> | SelectedResourceHook {\n return entry.hook;\n}\n\n/** Selection is inert during render; only a committed selection can admit work or discard an old queue. */\nclass CommandGroup {\n private entries = new Map<PropertyKey, SelectionEntry>();\n /** One set of invokers per Resource identity, so `run` keeps its reference across renders (D359). */\n private readonly invokers = new WeakMap<object, ResourceInvokers>();\n private readonly listeners = new Set<() => void>();\n private revision = 0;\n\n close(): void {\n for (const entry of this.entries.values()) detachEntry(entry);\n\n this.entries.clear();\n }\n\n commit(selected: Map<PropertyKey, SelectionEntry>): void {\n for (const [key, entry] of this.entries) {\n if (selected.get(key) !== entry) detachEntry(entry);\n }\n\n this.entries = selected;\n\n for (const entry of selected.values()) this.attachEntry(entry);\n }\n\n /** S7: the attach runs on every commit, so the verbs are named rather than gathered into an array. */\n private attachEntry(entry: SelectionEntry): void {\n if (isResourceEntry(entry)) {\n this.attachCommand(entry.refresh);\n this.attachCommand(entry.retry);\n this.attachCommand(entry.invalidate);\n this.attachCommand(entry.loadNext);\n\n return;\n }\n\n this.attachCommand(entry);\n }\n\n private attachCommand(command: CommandEntry): void {\n command.detach ??= command.slot.attach((inFlight, outcome) => {\n if (!updateEntry(command, inFlight, outcome)) return;\n\n this.revision += 1;\n\n for (const listener of this.listeners) listener();\n });\n }\n\n getSnapshot = (): number => this.revision;\n\n /**\n * D321: two outcomes no longer, but two kinds. A command that is not bound is still a `ContributionError`; a\n * resource never appears in `frame.commandRecords`, so it is prepared from its own identity instead.\n */\n prepare(commands: CommandSelection, resources: ResourceSelection, frame: Frame): Map<PropertyKey, SelectionEntry> {\n const selected = new Map<PropertyKey, SelectionEntry>();\n\n for (const key of Reflect.ownKeys(commands)) {\n if (!Object.prototype.propertyIsEnumerable.call(commands, key)) continue;\n\n const command = (commands as Readonly<Record<PropertyKey, Call<never, unknown>>>)[key];\n const record = command === undefined ? undefined : frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command at ${String(key)} is not bound in this contribution.`);\n }\n\n selected.set(key, selectEntry(this.entries.get(key), record.invoker));\n }\n\n for (const [key, resource] of resources) {\n const invokers = resourceInvokers(resource.resource, this.invokers);\n\n selected.set(key, selectResourceEntry(this.entries.get(key), invokers, resource));\n }\n\n return selected;\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n };\n}\n\nfunction useCommandGroup(commands: CommandSelection, resources: ResourceSelection): CommandGroup | undefined {\n const groupRef = useRef<CommandGroup | undefined>(undefined);\n const populated =\n resources.size > 0 ||\n Reflect.ownKeys(commands).some(key => Object.prototype.propertyIsEnumerable.call(commands, key));\n\n return populated ? (groupRef.current ??= new CommandGroup()) : undefined;\n}\n\n/** The consumers of one selection: a command per key, and the two verbs of a resource per key (D321). */\nfunction useSelectionHooks(commands: CommandSelection, resources: ResourceSelection): SelectionHooks {\n const frame = useFrame();\n const group = useCommandGroup(commands, resources);\n const getSnapshot = group?.getSnapshot ?? idleRevision;\n useSyncExternalStore(group?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const selected = group?.prepare(commands, resources, frame);\n useInsertionEffect(() => () => group?.close(), [group]);\n useInsertionEffect(() => {\n if (selected !== undefined) group?.commit(selected);\n });\n useLayoutEffect(() => {\n if (selected === undefined) return;\n\n // S7: the same branch as the attach — this runs after every commit, and the arrays were pure allocation.\n for (const entry of selected.values()) {\n if (isResourceEntry(entry)) {\n entry.refresh.slot.synchronize();\n entry.retry.slot.synchronize();\n entry.invalidate.slot.synchronize();\n entry.loadNext.slot.synchronize();\n } else entry.slot.synchronize();\n }\n });\n\n return selected === undefined\n ? emptyHooks\n : Object.fromEntries([...selected].map(([key, entry]) => [key, hookOf(entry)]));\n}\n\nfunction useCommands<Commands extends { readonly [Key in keyof Commands]: Call<never, unknown> }>(\n commands: Commands,\n): CommandsHook<Commands> {\n return useSelectionHooks(commands, noResources) as CommandsHook<Commands>;\n}\n\nexport { useCommands, useSelectionHooks };\nexport type { SelectionHooks };\n","import type { Readable } from '@opetope/core';\nimport { isCallTarget, isReadable } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\nimport type { AnyCommand } from './model-binding';\nimport { isResource } from './resource-command';\nimport type { AnyResource } from './resource-command';\n\ninterface SelectionRead {\n <Value>(source: Readable<Value>): Value;\n <Value, Selected>(source: Readable<Value>, select: (value: Value) => Selected): Selected;\n}\n\ntype SelectionContext = Readonly<{ read: SelectionRead }>;\ntype NonSelectionRecord =\n | readonly unknown[]\n | ((...arguments_: never[]) => unknown)\n | (abstract new (...arguments_: never[]) => unknown)\n | Date\n | PromiseLike<unknown>\n | ReadonlyMap<unknown, unknown>\n | ReadonlySet<unknown>\n | RegExp\n | WeakMap<object, unknown>\n | WeakSet<object>;\n/** A named interface needs no open index signature; container and callable results are not field selections. */\ntype SelectionShape<Selected extends object> = Selected extends NonSelectionRecord ? never : Selected;\ntype SelectionRecord = Readonly<Record<PropertyKey, unknown>>;\n/**\n * D359: the Resource a field selected, and what this selection read from it — its state, and the state of its\n * pagination capability when it has one. Both are read passively: a selection takes no lease by reading.\n */\ntype SelectedResource = Readonly<{ pagination: unknown; resource: AnyResource; state: unknown }>;\ntype SelectionSnapshot = Readonly<{\n commands: Readonly<Record<PropertyKey, AnyCommand>>;\n /** The set the mount leases; two keys naming one resource are one lease. */\n leases: ReadonlySet<AnyResource>;\n resources: ReadonlyMap<PropertyKey, SelectedResource>;\n sources: ReadonlySet<Readable<unknown>>;\n values: SelectionRecord;\n}>;\n\nconst noResources: ReadonlyMap<PropertyKey, SelectedResource> = new Map();\n\n/**\n * D363: what this mount read last and the snapshot it answered with. It belongs to the mount and not to the reader:\n * React may discard the `useMemo` that builds the reader, and a memory kept there would be empty exactly when it is\n * needed. D364: it no longer remembers which sources ended — a source whose owner retired stops instead of refusing,\n * so it answers its last value on every read and there is nothing to remember or to step around.\n */\ntype SelectionMemory = {\n model: unknown;\n observed: Map<Readable<unknown>, unknown> | undefined;\n select: unknown;\n snapshot: SelectionSnapshot;\n};\n\nconst noLeases: ReadonlySet<AnyResource> = new Set();\n\nfunction readSource<Value>(source: Readable<Value>, sources: Map<Readable<unknown>, unknown>): Value {\n if (!isReadable(source)) {\n throw new ContributionError('binding-invalid', 'Model selection read requires a Readable.');\n }\n\n if (sources.has(source)) return sources.get(source) as Value;\n\n // D364: a refusal here is the source's own failure and belongs to the render, because no lifetime ends in one.\n const value: unknown = source.getSnapshot();\n sources.set(source, value);\n\n return value as Value;\n}\n\nfunction readPassively(source: Readable<unknown>, sources: Map<Readable<unknown>, unknown>): unknown {\n if (sources.has(source)) return sources.get(source);\n\n const value: unknown = source.getSnapshot();\n sources.set(source, value);\n\n return value;\n}\n\n/** D359: the predicate that answers the question the type of the field asked, and the read that leases nothing. */\nfunction classifyResource(\n key: PropertyKey,\n value: unknown,\n resources: Map<PropertyKey, SelectedResource>,\n sources: Map<Readable<unknown>, unknown>,\n): void {\n if (!isResource(value)) return;\n\n const resource = value as AnyResource;\n const capability = (resource as { readonly pagination?: { readonly state: Readable<unknown> } }).pagination;\n\n resources.set(key, {\n pagination: capability === undefined ? undefined : readPassively(capability.state, sources),\n resource,\n state: readPassively(resource.state, sources),\n });\n}\n\nfunction selectedResources(resources: Map<PropertyKey, SelectedResource>): ReadonlyMap<PropertyKey, SelectedResource> {\n return resources.size === 0 ? noResources : resources;\n}\n\ntype CollectedSelection = Readonly<{\n resources: ReadonlyMap<PropertyKey, SelectedResource>;\n values: SelectionRecord;\n}>;\n\n/**\n * D359: recognition happens in the one pass that already walks the selection. `Resource` is a structural contract\n * (D199) and the type of a selected field promises `ResourceHook` for anything that satisfies it, so the predicate\n * this uses answers the same question the type asked. Reading the state here also puts it among `sources`, so its\n * publication invalidates the fast path of this reader exactly as a read source does.\n */\nfunction collectSelection(\n select: (context: SelectionContext) => object,\n sources: Map<Readable<unknown>, unknown>,\n): CollectedSelection {\n const read = <Value, Selected>(source: Readable<Value>, project?: (value: Value) => Selected) => {\n const value = readSource(source, sources);\n\n return project === undefined ? value : project(value);\n };\n const selected = select({ read });\n\n if (typeof selected !== 'object' || selected === null || Array.isArray(selected)) {\n throw new ContributionError('binding-invalid', 'Model selection requires a record.');\n }\n\n const entries: (readonly [PropertyKey, unknown])[] = [];\n const resources = new Map<PropertyKey, SelectedResource>();\n\n for (const key of Reflect.ownKeys(selected)) {\n if (!Object.prototype.propertyIsEnumerable.call(selected, key)) continue;\n\n const value = (selected as SelectionRecord)[key];\n\n entries.push([key, value]);\n classifyResource(key, value, resources, sources);\n }\n\n // `__proto__` is a key like any other here, and only a defined property keeps it one (D214).\n return { resources: selectedResources(resources), values: Object.fromEntries(entries) };\n}\n\n/**\n * D359: `values` keeps the Resource itself — a stable reference — so a selection that holds one is compared by\n * `Object.is` exactly as before. What moves is the state behind it, and that is compared here: without this the\n * selection would be «equal», the previous state would be kept, and a published Resource would reach no render.\n */\nfunction equalResources(\n previous: ReadonlyMap<PropertyKey, SelectedResource>,\n next: ReadonlyMap<PropertyKey, SelectedResource>,\n): boolean {\n if (previous.size !== next.size) return false;\n\n for (const [key, selected] of next) {\n const before = previous.get(key);\n\n if (\n before?.resource !== selected.resource ||\n !Object.is(before.state, selected.state) ||\n !Object.is(before.pagination, selected.pagination)\n ) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction equalSelection(\n previous: SelectionSnapshot,\n values: SelectionRecord,\n sources: ReadonlySet<Readable<unknown>>,\n resources: ReadonlyMap<PropertyKey, SelectedResource>,\n) {\n const keys = Reflect.ownKeys(values);\n const previousKeys = Reflect.ownKeys(previous.values);\n\n if (keys.length !== previousKeys.length || sources.size !== previous.sources.size) return false;\n\n for (const source of sources) if (!previous.sources.has(source)) return false;\n\n if (!equalResources(previous.resources, resources)) return false;\n\n return keys.every((key, index) => previousKeys[index] === key && Object.is(previous.values[key], values[key]));\n}\n\ntype ModelSelector<Model> = (model: Model, context: SelectionContext) => object;\ntype SelectionReaderRef<Model> = { readonly current: ModelSelector<Model> | undefined };\n\nconst emptySelectionSnapshot: SelectionSnapshot = {\n commands: {},\n leases: noLeases,\n resources: noResources,\n sources: new Set(),\n values: {},\n};\n\nfunction selectionCommands(values: SelectionRecord): Readonly<Record<PropertyKey, AnyCommand>> {\n return Object.fromEntries(\n Reflect.ownKeys(values)\n .filter(key => isCallTarget(values[key]))\n .map(key => [key, values[key]]),\n ) as Readonly<Record<PropertyKey, AnyCommand>>;\n}\n\nfunction selectionLeases(resources: ReadonlyMap<PropertyKey, SelectedResource>): ReadonlySet<AnyResource> {\n if (resources.size === 0) return noLeases;\n\n const leased = new Set<AnyResource>();\n\n for (const selected of resources.values()) leased.add(selected.resource);\n\n return leased;\n}\n\n/**\n * The fast path re-reads what it observed. D364: a source that stopped answers the same value it answered last, so\n * it compares equal and costs one read — there is no terminal source to record and step around any more.\n */\nfunction observedUnchanged(observed: Map<Readable<unknown>, unknown>): boolean {\n for (const [source, value] of observed) {\n if (!Object.is(source.getSnapshot(), value)) return false;\n }\n\n return true;\n}\n\n/** D363: the memory of one mount, empty until its first read and reset when the reader is built for another model. */\nfunction createSelectionMemory(): SelectionMemory {\n return { model: undefined, observed: undefined, select: undefined, snapshot: emptySelectionSnapshot };\n}\n\n/**\n * D214: one reader per model, not per render. The author's selector arrives through a ref, so an inline arrow does not\n * rebuild the reader; the fast path — nothing observed has moved — belongs to the selector that computed the snapshot,\n * so a new selector is always called again, its result compared field by field, and an equal selection keeps the\n * previous snapshot. A rejected or abandoned render still changes nothing: only a committed selection subscribes.\n *\n * D363: what the reader remembers lives in the mount's memory, so a reader React rebuilt — because it discarded the\n * memo — still knows what this selection read last.\n */\nfunction createSelectionSnapshot<Model>(\n model: Model,\n selectRef: SelectionReaderRef<Model>,\n memory: SelectionMemory,\n): () => SelectionSnapshot {\n if (memory.model !== model) {\n memory.model = model;\n memory.observed = undefined;\n memory.select = undefined;\n memory.snapshot = emptySelectionSnapshot;\n }\n\n return () => {\n const select = selectRef.current;\n\n if (select === undefined) return emptySelectionSnapshot;\n\n const observed = memory.observed;\n\n if (observed !== undefined && memory.select === select && observedUnchanged(observed)) {\n return memory.snapshot;\n }\n\n const nextObserved = new Map<Readable<unknown>, unknown>();\n const { resources, values } = collectSelection(context => select(model, context), nextObserved);\n const sources = new Set(nextObserved.keys());\n\n if (observed === undefined || !equalSelection(memory.snapshot, values, sources, resources)) {\n memory.snapshot = {\n commands: selectionCommands(values),\n leases: selectionLeases(resources),\n resources,\n sources,\n values,\n };\n }\n\n memory.observed = nextObserved;\n memory.select = select;\n\n return memory.snapshot;\n };\n}\n\nexport { createSelectionMemory, createSelectionSnapshot, emptySelectionSnapshot };\nexport type { ModelSelector, SelectedResource, SelectionContext, SelectionMemory, SelectionShape, SelectionSnapshot };\n","import type { Readable } from '@opetope/core';\nimport { createAggregateError } from '@opetope/core/internal';\n\nimport type { AnyResource } from './resource-command';\n\ntype Subscription = { dispose: (() => void) | undefined };\ntype Listener = Readonly<{ notify: () => void }>;\n\nconst noLeases: ReadonlySet<AnyResource> = new Set();\n\nfunction releaseSubscriptions(subscriptions: Iterable<Subscription>, failures: unknown[] = []): void {\n for (const subscription of subscriptions) {\n const dispose = subscription.dispose;\n subscription.dispose = undefined;\n\n try {\n dispose?.();\n } catch (error) {\n failures.push(error);\n }\n }\n\n if (failures.length === 1) throw failures[0];\n\n if (failures.length > 1) throw createAggregateError(failures, 'Model selection sources failed to release.');\n}\n\n/** A subscription set, not a scheduler: source notifications use React's snapshot consistency check. */\nclass ModelSelectionStore {\n private enabled = false;\n /** D359: one lease per Resource identity, diffed like a subscription, so an unchanged selection takes none. */\n private readonly leases = new Map<AnyResource, Subscription>();\n private readonly listeners = new Set<Listener>();\n private retained: ReadonlySet<AnyResource> = noLeases;\n private sources: ReadonlySet<Readable<unknown>> = new Set();\n private readonly subscriptions = new Map<Readable<unknown>, Subscription>();\n\n close(): void {\n this.enabled = false;\n this.sources = new Set();\n this.retained = noLeases;\n this.releaseAll();\n }\n\n commit(sources: ReadonlySet<Readable<unknown>>, retained: ReadonlySet<AnyResource> = noLeases): void {\n this.sources = sources;\n this.retained = retained;\n this.enabled = true;\n\n try {\n this.synchronize();\n } catch (error) {\n this.releaseAll([error]);\n }\n }\n\n subscribe = (notify: () => void): (() => void) => {\n const listener = { notify };\n this.listeners.add(listener);\n\n try {\n this.synchronize();\n } catch (error) {\n this.listeners.delete(listener);\n this.releaseAll([error]);\n }\n\n return () => {\n this.listeners.delete(listener);\n\n if (this.listeners.size === 0) this.releaseAll();\n };\n };\n\n private add(source: Readable<unknown>): void {\n const subscription: Subscription = { dispose: undefined };\n // Reserve before foreign code: a synchronous notification may replace or close this selection.\n this.subscriptions.set(source, subscription);\n const dispose = source.subscribe(this.notify);\n\n if (typeof dispose !== 'function') throw new TypeError('Model selection source must return a disposer.');\n\n if (this.subscriptions.get(source) === subscription) subscription.dispose = dispose;\n else dispose();\n }\n\n private notify = (): void => {\n for (const listener of [...this.listeners]) {\n if (this.listeners.has(listener)) listener.notify();\n }\n };\n\n private releaseAll(failures?: unknown[]): void {\n const held = [...this.subscriptions.values(), ...this.leases.values()];\n this.subscriptions.clear();\n this.leases.clear();\n releaseSubscriptions(held, failures);\n }\n\n private removeUnselected(): void {\n const removed: Subscription[] = [];\n\n for (const [source, subscription] of this.subscriptions) {\n if (this.sources.has(source)) continue;\n\n this.subscriptions.delete(source);\n removed.push(subscription);\n }\n\n for (const [resource, lease] of this.leases) {\n if (this.retained.has(resource)) continue;\n\n this.leases.delete(resource);\n removed.push(lease);\n }\n\n releaseSubscriptions(removed);\n }\n\n /**\n * D321: `retain()` is reentrant — it reaches `activate()`, the target subscription, the first read and the\n * publication that follows, all synchronously — and a listener woken there may rebuild or close this selection.\n * The record is reserved first, the lease is taken second, and it is kept only if the record is still this one.\n */\n private retain(resource: AnyResource): void {\n const lease: Subscription = { dispose: undefined };\n this.leases.set(resource, lease);\n const release = resource.acquire();\n\n if (typeof release !== 'function') throw new TypeError('Model selection resource must answer a release disposer.');\n\n if (this.leases.get(resource) === lease) lease.dispose = release;\n else release();\n }\n\n /** Foreign code inside a subscription or a lease may replace this selection, so both loops ask again. */\n private ready(): boolean {\n return this.enabled && this.listeners.size > 0;\n }\n\n private retainSelected(): void {\n for (const resource of this.retained) {\n if (!this.ready()) return;\n\n if (this.retained.has(resource) && !this.leases.has(resource)) this.retain(resource);\n }\n }\n\n private subscribeSources(): void {\n for (const source of this.sources) {\n if (!this.ready()) return;\n\n if (this.sources.has(source) && !this.subscriptions.has(source)) this.add(source);\n }\n }\n\n private synchronize(): void {\n this.removeUnselected();\n this.subscribeSources();\n this.retainSelected();\n }\n}\n\nexport { ModelSelectionStore };\n","import { useLayoutEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\nimport type { Call, ModelOf, PaginatedResource, Resource } from '@opetope/core';\n\nimport type { CommandHook } from './command-hook';\nimport { useSelectionHooks } from './commands-hook';\nimport type { SelectionHooks } from './commands-hook';\nimport { ContributionError } from './errors';\nimport { subscribeIdle } from './idle-subscription';\nimport type { AnyModel } from './model-binding';\nimport { createSelectionMemory, createSelectionSnapshot, emptySelectionSnapshot } from './model-selection-snapshot';\nimport type {\n ModelSelector,\n SelectionContext,\n SelectionMemory,\n SelectionShape,\n SelectionSnapshot,\n} from './model-selection-snapshot';\nimport { ModelSelectionStore } from './model-selection-store';\nimport { useFrame } from './mount-context';\nimport type { PaginatedResourceHook, ResourceHook, ResourceKey } from './resource-command';\n\n/**\n * D359: a field whose value is a Resource answers the hook `useResource` answers, and the mount leases it exactly as\n * `useResource` does. `read(resource.state)` is unchanged — it hands out the state and leases nothing — so naming one\n * Resource both ways registers it twice: the subscription and the lease are independent.\n */\ntype SelectedValue<Value> =\n Value extends Call<infer Input, infer Output>\n ? CommandHook<Input, Output>\n : Value extends PaginatedResource<infer Data, infer Key extends ResourceKey, infer Cursor extends ResourceKey>\n ? PaginatedResourceHook<Data, Key, Cursor>\n : Value extends Resource<infer Data, infer Key extends ResourceKey>\n ? ResourceHook<Data, Key>\n : Value;\ntype ModelSelection<Selected> = { readonly [Key in keyof Selected]: SelectedValue<Selected[Key]> };\n\nconst readEmptySnapshot = () => emptySelectionSnapshot;\n\nfunction useSelectionStore(selected: boolean): ModelSelectionStore | undefined {\n const storeRef = useRef<ModelSelectionStore | undefined>(undefined);\n\n return selected ? (storeRef.current ??= new ModelSelectionStore()) : undefined;\n}\n\n/**\n * Two branches of one key: the consumer this selection prepared — a bound command, or the verbs, the state and the\n * capability of a selected Resource — and otherwise the value the selector wrote. S6/D359: the Resource branch is\n * assembled on the record and not here, so a key whose state did not move hands back the very same object.\n */\nfunction mergeSelection(snapshot: SelectionSnapshot, hooks: SelectionHooks): unknown {\n return Object.fromEntries(\n Reflect.ownKeys(snapshot.values).map(key => [\n key,\n Object.prototype.hasOwnProperty.call(hooks, key) ? hooks[key] : snapshot.values[key],\n ]),\n );\n}\n\n/** D205: resolve only a granted model, explicitly read data, and reuse the ordinary per-key command consumers. */\nfunction useModel<Declaration extends AnyModel>(declaration: Declaration): ModelOf<Declaration>;\nfunction useModel<Declaration extends AnyModel, const Selected extends object>(\n declaration: Declaration,\n select: (model: ModelOf<Declaration>, context: SelectionContext) => Selected & SelectionShape<Selected>,\n): ModelSelection<Selected>;\nfunction useModel<Declaration extends AnyModel>(\n declaration: Declaration,\n select?: (model: ModelOf<Declaration>, context: SelectionContext) => object,\n): unknown {\n const frame = useFrame();\n\n if (!frame.models.has(declaration)) {\n throw new ContributionError('missing', `Model ${declaration.id} is not granted to this contribution.`);\n }\n\n const model = frame.models.get(declaration) as ModelOf<Declaration>;\n const store = useSelectionStore(select !== undefined);\n // D214: the selector of this render, read by a reader that belongs to the model. Writing the latest callback into\n // a ref is what `useSyncExternalStoreWithSelector` does: the reader only reads it, and never writes to it.\n const selectRef = useRef<ModelSelector<ModelOf<Declaration>> | undefined>(undefined);\n selectRef.current = select;\n const selected = select !== undefined;\n // D363: the memory of this selection belongs to the mount, so a reader React rebuilt keeps what it read last.\n const memoryRef = useRef<SelectionMemory | undefined>(undefined);\n const memory = (memoryRef.current ??= createSelectionMemory());\n const getSnapshot = useMemo(\n () => (selected ? createSelectionSnapshot(model, selectRef, memory) : readEmptySnapshot),\n [memory, model, selected],\n );\n const snapshot = useSyncExternalStore(store?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const hooks = useSelectionHooks(snapshot.commands, snapshot.resources);\n useLayoutEffect(() => () => store?.close(), [store]);\n // D359: a lease is taken in commit and never in a render, and the store diffs the set by identity.\n useLayoutEffect(() => store?.commit(snapshot.sources, snapshot.leases), [snapshot, store]);\n\n if (select === undefined) return model;\n\n return mergeSelection(snapshot, hooks);\n}\n\nexport { useModel };\n","import type { FunctionComponent } from 'react';\n\nimport type { ModelIdentity } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\n\ntype AnyModel = ModelIdentity;\n\n/**\n * The marker a component uses to state which UI models of its contribution it reads. `slot` checks by type that the\n * contribution grants every model of this list — it may grant more, because the mount serves its whole instance.\n *\n * The runtime authority is the mount frame, not this marker: `useModel` resolves against what the mount was given\n * and refuses anything else. The lint rule `opetope/require-declared-models` checks visible contribution sites and\n * each reader of a per-mount model within the same module. Imported implementations and dynamic declaration lists\n * remain unknown to this syntactic check (D85, D158, D250).\n */\ntype ComponentRequiringModels<Props, Models extends readonly AnyModel[]> = FunctionComponent<Props> & {\n readonly requires: Models;\n};\n\nfunction requiresModels<const Models extends readonly AnyModel[]>(\n models: Models,\n): <Props>(component: FunctionComponent<Props>) => ComponentRequiringModels<Props, Models> {\n const declared = Object.freeze([...models]) as unknown as Models;\n\n return <Props>(component: FunctionComponent<Props>): ComponentRequiringModels<Props, Models> => {\n const existing = (component as { readonly requires?: readonly AnyModel[] }).requires;\n\n if (existing !== undefined) {\n throw new ContributionError('duplicate', 'Component already declares the models it requires.');\n }\n\n Object.defineProperty(component, 'requires', { enumerable: false, value: declared });\n\n return component as ComponentRequiringModels<Props, Models>;\n };\n}\n\nexport { requiresModels };\n","import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react';\n\nimport type { PaginatedResource, PaginationState, Readable, Resource } from '@opetope/core';\nimport type { ResourceKey } from '@opetope/core/internal';\n\nimport { useCommandStatus } from './command-hook';\nimport { CommandHookController } from './command-hook-controller';\nimport { createResourceInvokers, paginationOf } from './resource-command';\nimport type { AnyResource, PaginatedResourceHook, ResourceHook, ResourcePaginationHook } from './resource-command';\n\ntype ResourceSlots = Readonly<{\n invalidate: CommandHookController<void, unknown>;\n loadNext: CommandHookController<void, unknown>;\n refresh: CommandHookController<void, unknown>;\n retry: CommandHookController<void, unknown>;\n}>;\n\n/**\n * D359: one set of controllers per Resource identity, exactly as `useCommandSlot` keeps one per invoker. The slots are\n * what the set of invokers is kept alive by, so one map answers for all four. Slots are inert until commit, so an\n * interrupted render neither admits work nor resets the status of the committed one.\n */\nfunction useResourceSlots(resource: AnyResource): ResourceSlots {\n const [cache] = useState(() => new WeakMap<object, ResourceSlots>());\n const existing = cache.get(resource);\n\n if (existing !== undefined) return existing;\n\n const invokers = createResourceInvokers(resource);\n const slots: ResourceSlots = {\n invalidate: new CommandHookController<void, unknown>(invokers.invalidate),\n loadNext: new CommandHookController<void, unknown>(invokers.loadNext),\n refresh: new CommandHookController<void, unknown>(invokers.refresh),\n retry: new CommandHookController<void, unknown>(invokers.retry),\n };\n cache.set(resource, slots);\n\n return slots;\n}\n\nconst unavailablePagination: PaginationState<ResourceKey> = Object.freeze({\n kind: 'unavailable',\n reason: 'resource-not-ready',\n});\nconst noPaginationSource: Readable<PaginationState<ResourceKey>> = Object.freeze({\n getSnapshot: () => unavailablePagination,\n subscribe: () => () => undefined,\n});\n\n/**\n * D359: the state of a Resource, the lease that keeps it open for as long as this component is mounted, and its verbs\n * as commands. The number of React hooks here does not depend on the Resource: a declaration without `pagination`\n * subscribes to a constant source and keeps a `loadNext` that answers `skipped`.\n */\nfunction useResource<Data, Key extends ResourceKey, Cursor extends ResourceKey>(\n resource: PaginatedResource<Data, Key, Cursor>,\n): PaginatedResourceHook<Data, Key, Cursor>;\nfunction useResource<Data, Key extends ResourceKey>(resource: Resource<Data, Key>): ResourceHook<Data, Key>;\nfunction useResource<Data, Key extends ResourceKey>(resource: Resource<Data, Key>): ResourceHook<Data, Key> {\n const handle = resource as unknown as AnyResource;\n const capability = paginationOf(handle);\n const subscribe = useCallback((listener: () => void) => resource.state.subscribe(listener), [resource]);\n /*\n * D364: `resource.state` is a total `Readable` for the life of the object this hook holds — a retired one answers\n * `idle('retired')` — so the read needs no memory of its own. What it can still refuse is a producer failure, a\n * broken `nextCursor` among them (D356), and that belongs to the render (G13).\n */\n const getState = useMemo(() => () => resource.state.getSnapshot(), [resource]);\n const state = useSyncExternalStore(subscribe, getState, getState);\n const paginationSource: Readable<PaginationState<ResourceKey>> =\n (capability?.state as Readable<PaginationState<ResourceKey>> | undefined) ?? noPaginationSource;\n const subscribePagination = useCallback(\n (listener: () => void) => paginationSource.subscribe(listener),\n [paginationSource],\n );\n const getPaginationState = useMemo(() => () => paginationSource.getSnapshot(), [paginationSource]);\n const paginationState = useSyncExternalStore(subscribePagination, getPaginationState, getPaginationState);\n\n // D349: the lease is taken in a commit effect, so an interrupted render opens nothing.\n useEffect(() => resource.acquire(), [resource]);\n\n const slots = useResourceSlots(handle);\n const refresh = useCommandStatus(slots.refresh);\n const retry = useCommandStatus(slots.retry);\n const invalidate = useCommandStatus(slots.invalidate);\n const loadNext = useCommandStatus(slots.loadNext);\n const pagination = useMemo(\n () =>\n capability === undefined\n ? undefined\n : ({ loadNext, state: paginationState } as unknown as ResourcePaginationHook<ResourceKey>),\n [capability, loadNext, paginationState],\n );\n\n return useMemo(\n () => ({ invalidate, pagination, refresh, retry, state }) as ResourceHook<Data, Key>,\n [invalidate, pagination, refresh, retry, state],\n );\n}\n\nexport { useResource };\n","import type { ComponentType, ReactNode } from 'react';\nimport { createElement, Fragment } from 'react';\n\nimport type { DeclarationId } from '@opetope/core';\nimport { declarationId } from '@opetope/core';\nimport type { ContributionTarget } from '@opetope/core/internal';\nimport { createContributionTarget } from '@opetope/core/internal';\nimport type { ContributionModel } from '@opetope/runtime/internal';\n\nimport { contributionAuthority, ContributionMount } from './contribution-frame';\nimport { useReadable } from './readable-hooks';\n\ntype SlotProperties = object | undefined;\ntype EmptySlotProperties = Readonly<Record<string, never>>;\ntype SlotComponentProperties<Props extends SlotProperties> = [Props] extends [undefined]\n ? EmptySlotProperties\n : Extract<Props, object>;\n\n/**\n * What a contribution publishes: the component, the optional props adapter and the optional UI models the mount\n * creates. Without an adapter the component takes the slot props; with one it takes exactly the adapter result (D85).\n */\ntype SlotContribution<Props extends SlotProperties = undefined> = {\n readonly Component: ComponentType<never>;\n readonly models?: readonly ContributionModel[];\n readonly props?: (slotProps: SlotComponentProperties<Props>) => object;\n};\n\ntype SlotTarget<Props extends SlotProperties = undefined> = ContributionTarget<SlotContribution<Props>>;\n\ntype SwitchSlotTarget<Route extends string, Props extends SlotProperties = undefined> = ((\n route: Route,\n) => SlotTarget<Props>) & {\n readonly id: DeclarationId;\n};\n\ntype SlotRenderProps<Props extends SlotProperties> = [Props] extends [undefined]\n ? {\n readonly props?: never;\n readonly target: SlotTarget<Props>;\n }\n : {\n readonly props: SlotComponentProperties<Props>;\n readonly target: SlotTarget<Props>;\n };\n\nconst emptySlotProperties: EmptySlotProperties = Object.freeze({});\n\n/** D280: a slot is a name, so it takes its id as the first argument. */\nfunction defineSlot<Props extends SlotProperties = undefined>(id: string): SlotTarget<Props> {\n return createContributionTarget<SlotContribution<Props>>(id, 'slot');\n}\n\nfunction defineSwitchSlot<Route extends string, Props extends SlotProperties = undefined>(\n id: string,\n): SwitchSlotTarget<Route, Props> {\n const declared = declarationId(id);\n const targets = new Map<Route, SlotTarget<Props>>();\n\n const switchSlot = (route: Route): SlotTarget<Props> => {\n const existing = targets.get(route);\n\n if (existing !== undefined) return existing;\n\n const target = defineSlot<Props>(`${declared}/${route}`);\n targets.set(route, target);\n\n return target;\n };\n\n Object.defineProperty(switchSlot, 'id', { enumerable: true, value: declared });\n\n return Object.freeze(switchSlot) as SwitchSlotTarget<Route, Props>;\n}\n\n/**\n * Every contribution renders inside its own mount: the models its generation owns plus the UI models it declared,\n * created on mount and closed on unmount (D70, D85).\n */\nfunction Slot<Props extends SlotProperties>(options: SlotRenderProps<Props>): ReactNode {\n const entries = useReadable(options.target.entries);\n const slotProps: object = 'props' in options ? (options.props ?? emptySlotProperties) : emptySlotProperties;\n\n if (entries.length === 0) return null;\n\n return entries.map(entry =>\n createElement(\n Fragment,\n { key: entry.id },\n createElement(ContributionMount, {\n authority: contributionAuthority(entry),\n contribution: entry.value,\n contributionId: entry.id,\n slotProps,\n targetId: options.target.id,\n }),\n ),\n );\n}\n\nexport { defineSlot, defineSwitchSlot, Slot };\nexport type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget, SwitchSlotTarget };\n"],"names":["failureOutcome","options","error","isCancellation","notification","_a","reportDetachedError","callbackError","runOptions","onSuccess","signal","requestOf","input","resolve","promise","settle","request","outcome","CommandHookController","ContributionError","aborted","invoker","notify","CallError","compatibleAbortReason","invocation","value","emptyStatus","settledOutcome","unchangedState","current","slot","inFlight","settled","nextState","previous","useCommandSlot","slots","useState","useCommandStatus","currentSlot","hookState","setHookState","useInsertionEffect","useLayoutEffect","run","useMemo","useCommand","command","record","useFrame","idleRelease","subscribeIdle","idleRevision","emptyHooks","noResources","isResourceEntry","entry","createEntry","unchangedHook","next","updateEntry","selectEntry","resourceHook","selected","createResourceEntry","invokers","parts","unchangedResourceCommands","hook","unchangedResourceState","unchangedResourceHook","selectResourceEntry","commandEntries","detachEntry","hookOf","CommandGroup","key","listener","commands","resources","frame","resource","resourceInvokers","useCommandGroup","groupRef","useRef","useSelectionHooks","group","getSnapshot","useSyncExternalStore","useCommands","noLeases","readSource","source","sources","isReadable","readPassively","classifyResource","isResource","capability","selectedResources","collectSelection","select","project","entries","equalResources","before","equalSelection","values","keys","previousKeys","index","emptySelectionSnapshot","selectionCommands","isCallTarget","selectionLeases","leased","observedUnchanged","observed","createSelectionMemory","createSelectionSnapshot","model","selectRef","memory","nextObserved","context","releaseSubscriptions","subscriptions","failures","subscription","dispose","createAggregateError","ModelSelectionStore","retained","held","removed","lease","release","readEmptySnapshot","useSelectionStore","storeRef","mergeSelection","snapshot","hooks","useModel","declaration","store","memoryRef","requiresModels","models","declared","component","useResourceSlots","cache","existing","createResourceInvokers","unavailablePagination","noPaginationSource","useResource","handle","paginationOf","subscribe","useCallback","getState","state","paginationSource","subscribePagination","getPaginationState","paginationState","useEffect","refresh","retry","invalidate","loadNext","pagination","emptySlotProperties","defineSlot","id","createContributionTarget","defineSwitchSlot","declarationId","targets","switchSlot","route","target","Slot","useReadable","slotProps","createElement","Fragment","ContributionMount","contributionAuthority"],"mappings":"kqBA+BA,SAASA,EAAuBC,EAAgDC,EAAc,OAC5F,GAAIC,EAAeD,CAAK,EAAG,MAAO,CAAE,OAAQA,EAAO,OAAQ,WAAW,EAEtE,GAAI,CACF,MAAME,GAAeC,EAAAJ,GAAA,YAAAA,EAAS,YAAT,YAAAI,EAAA,KAAAJ,EAAqBC,GAEtCE,IAAiB,QAAgB,QAAQ,QAAQA,CAAY,EAAE,MAAME,CAAmB,CAC9F,OAASC,EAAe,CACtBD,EAAoBC,CAAa,CACnC,CAEA,MAAO,CAAE,MAAAL,EAAO,OAAQ,QAAQ,CAClC,CAEA,SAASM,GAAmBP,EAA8C,CACxE,MAAMQ,EAAYR,GAAA,YAAAA,EAAS,UACrBS,EAAST,GAAA,YAAAA,EAAS,OAExB,OAAIQ,IAAc,OAAkBC,IAAW,OAAY,OAAY,CAAE,OAAAA,CAAM,EAExEA,IAAW,OAAY,CAAE,UAAAD,CAAS,EAAK,CAAE,UAAAA,EAAW,OAAAC,CAAM,CACnE,CAEA,SAASC,GACPC,EACAX,EAA8C,CAE9C,IAAIY,EACJ,MAAMC,EAAU,IAAI,QAAgCC,GAAS,CAC3DF,EAAUE,CACZ,CAAC,EAED,MAAO,CAAE,MAAAH,EAAO,QAASX,IAAY,OAAY,OAAY,CAAE,GAAGA,CAAO,EAAI,QAAAa,EAAS,QAAAD,CAAO,CAC/F,CAEA,SAASE,GAAsBC,EAAwCC,EAA+B,CACpG,MAAMJ,EAAUG,EAAQ,QACxBA,EAAQ,MAAQ,OAChBA,EAAQ,QAAU,OAClBA,EAAQ,QAAU,OAClBH,GAAA,MAAAA,EAAUI,EACZ,CAOA,MAAMC,CAAqB,CAqBI,QApBpB,KAAkC,CAACN,EAAcX,IAAuC,CAC/F,GAAI,KAAK,SAAW,OAClB,OAAO,QAAQ,QAAQ,CACrB,OAAQ,IAAIkB,EAAkB,WAAY,kCAAkC,EAC5E,OAAQ,WACT,CAAA,EAGH,MAAMC,EAAU,KAAK,eAAenB,CAAO,EAE3C,GAAImB,IAAY,OAAW,OAAO,QAAQ,QAAQA,CAAO,EAEzD,MAAMJ,EAAUL,GAAUC,EAAOX,CAAO,EACxC,YAAK,MAAMe,CAAO,EAEXA,EAAQ,OACjB,GACQ,SAAW,EACX,OAER,YAA6BK,EAAuB,CAAvB,KAAA,QAAAA,CAA0B,CAEvD,OAAOC,EAAmC,CACxC,YAAK,OAASA,EAEP,IAAK,CACN,KAAK,SAAWA,IAEpB,KAAK,OAAS,OAChB,CACF,CAEA,aAAW,QACTjB,EAAA,KAAK,SAAL,MAAAA,EAAA,UAAc,KAAK,SAAW,EAChC,CAEQ,QAAQK,EAAmB,CACjC,MAAO,CACL,OAAQ,IAAIa,EAAU,YAAa,+BAAgCC,EAAsBd,CAAM,CAAC,EAChG,OAAQ,YAEZ,CAEQ,eAAeT,EAA8C,OACnE,QAAOI,EAAAJ,GAAA,YAAAA,EAAS,SAAT,YAAAI,EAAiB,WAAY,GAAO,KAAK,QAAQJ,EAAQ,MAAM,EAAI,MAC5E,CAEQ,OAAOe,EAAwCC,EAA+B,OAChFD,EAAQ,UAAY,SAExB,KAAK,UAAY,EACjBD,GAAOC,EAASC,CAAO,GACvBZ,EAAA,KAAK,SAAL,MAAAA,EAAA,UAAc,KAAK,SAAW,EAAGY,GACnC,CAEQ,OAAOD,EAAsC,CACnD,IAAIS,EAEJ,GAAI,CACFA,EAAa,KAAK,QAAQ,IAAIT,EAAQ,MAAgBR,GAAWQ,EAAQ,OAAO,CAAU,EAC1FA,EAAQ,MAAQ,MAClB,OAASd,EAAO,CACd,KAAK,OAAOc,EAAShB,EAAegB,EAAQ,QAASd,CAAK,CAAC,EAE3D,MACF,CAEKuB,EAAW,KACdC,GAAS,KAAK,OAAOV,EAAS,CAAE,OAAQ,KAAM,MAAAU,CAAK,CAAE,EACpDxB,GAAmB,KAAK,OAAOc,EAAShB,EAAegB,EAAQ,QAASd,CAAK,CAAC,CAAC,CAEpF,CAEQ,MAAMc,EAAsC,OAClD,KAAK,UAAY,GAEjBX,EAAA,KAAK,SAAL,MAAAA,EAAA,UAAc,IACd,KAAK,OAAOW,CAAO,CACrB,CACD,CC7ID,MAAMW,GAAwC,CAAE,SAAU,GAAO,QAAS,MAAS,EAWnF,SAASC,GAAuBX,EAA2C,CACzE,OAAOA,IAAY,QAAaA,EAAQ,SAAW,YAAc,OAAYA,CAC/E,CAEA,SAASY,GACPC,EACAC,EACAC,EACAC,EAA2C,CAE3C,OAAOA,IAAY,QAAaH,EAAQ,OAASC,GAAQD,EAAQ,WAAaE,CAChF,CAEA,SAASE,GACPJ,EACAC,EACAC,EACAf,EAAgC,CAEhC,MAAMgB,EAAUL,GAAeX,CAAO,EAEtC,GAAIY,GAAeC,EAASC,EAAMC,EAAUC,CAAO,EAAG,OAAOH,EAG7D,MAAMK,EAAWL,EAAQ,OAASC,EAAOD,EAAQ,QAAU,OAE3D,MAAO,CAAE,SAAAE,EAAU,QAASC,GAAWE,EAAU,KAAAJ,CAAI,CACvD,CAEA,SAASK,GAA8Bf,EAAuB,CAE5D,KAAM,CAACgB,CAAK,EAAIC,EAAS,IAAM,IAAI,OAAuD,EAC1F,IAAIP,EAAOM,EAAM,IAAIhB,CAAO,EAE5B,OAAIU,IAAS,SACXA,EAAO,IAAIb,EAAsBG,CAAO,EACxCgB,EAAM,IAAIhB,EAASU,CAAI,GAGlBA,CACT,CAOA,SAASQ,EACPC,EAAiD,CAEjD,KAAM,CAACC,EAAWC,CAAY,EAAIJ,EAA0C,KAAO,CACjF,SAAU,GACV,QAAS,OACT,KAAME,CACP,EAAC,EACFG,EACE,IACEH,EAAY,OAAO,CAACR,EAAUf,IAC5ByB,EAAaZ,GAAWI,GAAUJ,EAASU,EAAaR,EAAUf,CAAO,CAAC,CAAC,EAE/E,CAACuB,CAAW,CAAC,EAEfI,EAAgB,IAAMJ,EAAY,YAAW,EAAI,CAACA,CAAW,CAAC,EAC9D,KAAM,CAAE,SAAAR,EAAU,QAAAf,CAAO,EAAKwB,EAAU,OAASD,EAAcC,EAAYd,GACrEkB,EAAML,EAAY,IAExB,OAAOM,EAAQ,KAAO,CAAE,SAAAd,EAAU,QAAAf,EAAS,IAAA4B,IAAQ,CAACb,EAAUf,EAAS4B,CAAG,CAAC,CAC7E,CAEA,SAASE,GAA0BC,EAA4B,CAE7D,MAAMC,EADQC,EAAQ,EACD,eAAe,IAAIF,CAAO,EAE/C,GAAIC,IAAW,OACb,MAAM,IAAI9B,EAAkB,UAAW,WAAW6B,EAAQ,EAAE,qCAAqC,EAGnG,OAAOT,EAAiBH,GAA8Ba,EAAO,OAAO,CAAC,CACvE,CC3GA,MAAME,GAAc,IAAA,GACdC,EAAgB,IAAoBD,GACpCE,GAAe,IAAc,EC8B7BC,GAA6B,OAAO,OAAO,EAAE,EAC7CC,GAAiC,IAAI,IAwB3C,SAASC,EAAgBC,EAAqB,CAC5C,MAAO,SAAUA,CACnB,CAEA,SAASC,EAAYrC,EAAuB,CAC1C,MAAMU,EAAO,IAAIb,EAAsCG,CAAO,EAE9D,MAAO,CAAE,OAAQ,OAAW,KAAM,CAAE,SAAU,GAAO,QAAS,OAAW,IAAKU,EAAK,GAAG,EAAI,QAAAV,EAAS,KAAAU,CAAI,CACzG,CAEA,SAAS4B,GAAcxB,EAAuCyB,EAAiC,CAC7F,OAAOzB,EAAS,WAAayB,EAAK,UAAY,OAAO,GAAGzB,EAAS,QAASyB,EAAK,OAAO,CACxF,CAEA,SAASC,GAAYJ,EAAqBzB,EAAmBf,EAAiC,CAC5F,MAAMkB,EAAWsB,EAAM,KAEvB,GAAIxC,IAAY,QAAakB,EAAS,WAAaH,EAAU,MAAO,GAGpE,MAAMC,EAAUhB,IAAY,QAAaA,EAAQ,SAAW,YAAc,OAAYA,EAChF2C,EAAO,CAAE,SAAA5B,EAAU,QAASC,GAAWE,EAAS,QAAS,IAAKA,EAAS,GAAG,EAEhF,OAAIwB,GAAcxB,EAAUyB,CAAI,EAAU,IAE1CH,EAAM,KAAOG,EAEN,GACT,CAEA,SAASE,GAAY3B,EAAsCd,EAAuB,CAChF,OAAOc,IAAa,QAAa,CAACqB,EAAgBrB,CAAQ,GAAKA,EAAS,UAAYd,EAChFc,EACAuB,EAAYrC,CAAO,CACzB,CAEA,SAAS0C,EAAaN,EAAoCO,EAA0B,CAClF,MAAO,CACL,WAAYP,EAAM,WAAW,KAC7B,WAAYA,EAAM,UAAY,CAAE,SAAUA,EAAM,SAAS,KAAM,MAAOO,EAAS,UAAU,EAAK,OAC9F,QAASP,EAAM,QAAQ,KACvB,MAAOA,EAAM,MAAM,KACnB,MAAOO,EAAS,MAEpB,CAEA,SAASC,GAAoBC,EAA4BF,EAA0B,CACjF,MAAMG,EAAQ,CACZ,WAAYT,EAAYQ,EAAS,UAAU,EAC3C,KAAM,WACN,SAAUR,EAAYQ,EAAS,QAAQ,EACvC,UAAWF,EAAS,aAAe,OACnC,QAASN,EAAYQ,EAAS,OAAO,EACrC,MAAOR,EAAYQ,EAAS,KAAK,GAGnC,MAAO,CAAE,GAAGC,EAAO,KAAMJ,EAAaI,EAAOH,CAAQ,CAAC,CACxD,CAGA,SAASI,GAA0BX,EAAoB,OACrD,KAAM,CAAE,KAAAY,CAAI,EAAKZ,EAEjB,OACEY,EAAK,UAAYZ,EAAM,QAAQ,MAC/BY,EAAK,QAAUZ,EAAM,MAAM,MAC3BY,EAAK,aAAeZ,EAAM,WAAW,OACpC,CAACA,EAAM,aAAapD,EAAAgE,EAAK,aAAL,YAAAhE,EAAiB,YAAaoD,EAAM,SAAS,KAEtE,CAGA,SAASa,GAAuBb,EAAsBO,EAA0B,OAC9E,OAAO,OAAO,GAAGP,EAAM,KAAK,MAAOO,EAAS,KAAK,GAAK,OAAO,IAAG3D,EAAAoD,EAAM,KAAK,aAAX,YAAApD,EAAuB,MAAO2D,EAAS,UAAU,CACnH,CAEA,SAASO,GAAsBd,EAAsBO,EAA0B,CAC7E,OAAOI,GAA0BX,CAAK,GAAKa,GAAuBb,EAAOO,CAAQ,CACnF,CAMA,SAASQ,GACPrC,EACA+B,EACAF,EAA0B,CAE1B,OAAI7B,IAAa,QAAa,CAACqB,EAAgBrB,CAAQ,GAAKA,EAAS,QAAQ,UAAY+B,EAAS,QACzFD,GAAoBC,EAAUF,CAAQ,GAG1CO,GAAsBpC,EAAU6B,CAAQ,IAAG7B,EAAS,KAAO4B,EAAa5B,EAAU6B,CAAQ,GAExF7B,EACT,CAEA,SAASsC,GAAehB,EAAqB,CAC3C,OAAOD,EAAgBC,CAAK,EAAI,CAACA,EAAM,QAASA,EAAM,MAAOA,EAAM,WAAYA,EAAM,QAAQ,EAAI,CAACA,CAAK,CACzG,CAEA,SAASiB,EAAYjB,EAAqB,OACxC,UAAWT,KAAWyB,GAAehB,CAAK,GACxCpD,EAAA2C,EAAQ,SAAR,MAAA3C,EAAA,KAAA2C,GACAA,EAAQ,OAAS,MAErB,CAEA,SAAS2B,GAAOlB,EAAqB,CACnC,OAAOA,EAAM,IACf,CAGA,MAAMmB,EAAY,CACR,QAAU,IAAI,IAEL,SAAW,IAAI,QACf,UAAY,IAAI,IACzB,SAAW,EAEnB,OAAK,CACH,UAAWnB,KAAS,KAAK,QAAQ,OAAM,EAAIiB,EAAYjB,CAAK,EAE5D,KAAK,QAAQ,MAAK,CACpB,CAEA,OAAOO,EAA0C,CAC/C,SAAW,CAACa,EAAKpB,CAAK,IAAK,KAAK,QAC1BO,EAAS,IAAIa,CAAG,IAAMpB,GAAOiB,EAAYjB,CAAK,EAGpD,KAAK,QAAUO,EAEf,UAAWP,KAASO,EAAS,OAAM,EAAI,KAAK,YAAYP,CAAK,CAC/D,CAGQ,YAAYA,EAAqB,CACvC,GAAID,EAAgBC,CAAK,EAAG,CAC1B,KAAK,cAAcA,EAAM,OAAO,EAChC,KAAK,cAAcA,EAAM,KAAK,EAC9B,KAAK,cAAcA,EAAM,UAAU,EACnC,KAAK,cAAcA,EAAM,QAAQ,EAEjC,MACF,CAEA,KAAK,cAAcA,CAAK,CAC1B,CAEQ,cAAcT,EAAqB,CACzCA,EAAQ,SAARA,EAAQ,OAAWA,EAAQ,KAAK,OAAO,CAAChB,EAAUf,IAAW,CAC3D,GAAK4C,GAAYb,EAAShB,EAAUf,CAAO,EAE3C,MAAK,UAAY,EAEjB,UAAW6D,KAAY,KAAK,UAAWA,EAAQ,EACjD,CAAC,EACH,CAEA,YAAc,IAAc,KAAK,SAMjC,QAAQC,EAA4BC,EAA8BC,EAAY,CAC5E,MAAMjB,EAAW,IAAI,IAErB,UAAWa,KAAO,QAAQ,QAAQE,CAAQ,EAAG,CAC3C,GAAI,CAAC,OAAO,UAAU,qBAAqB,KAAKA,EAAUF,CAAG,EAAG,SAEhE,MAAM7B,EAAW+B,EAAiEF,CAAG,EAC/E5B,EAASD,IAAY,OAAY,OAAYiC,EAAM,eAAe,IAAIjC,CAAO,EAEnF,GAAIC,IAAW,OACb,MAAM,IAAI9B,EAAkB,UAAW,cAAc,OAAO0D,CAAG,CAAC,qCAAqC,EAGvGb,EAAS,IAAIa,EAAKf,GAAY,KAAK,QAAQ,IAAIe,CAAG,EAAG5B,EAAO,OAAO,CAAC,CACtE,CAEA,SAAW,CAAC4B,EAAKK,CAAQ,IAAKF,EAAW,CACvC,MAAMd,EAAWiB,GAAiBD,EAAS,SAAU,KAAK,QAAQ,EAElElB,EAAS,IAAIa,EAAKL,GAAoB,KAAK,QAAQ,IAAIK,CAAG,EAAGX,EAAUgB,CAAQ,CAAC,CAClF,CAEA,OAAOlB,CACT,CAEA,UAAac,IACX,KAAK,UAAU,IAAIA,CAAQ,EAEpB,IAAK,CACV,KAAK,UAAU,OAAOA,CAAQ,CAChC,EAEH,CAED,SAASM,GAAgBL,EAA4BC,EAA4B,CAC/E,MAAMK,EAAWC,EAAiC,MAAS,EAK3D,OAHEN,EAAU,KAAO,GACjB,QAAQ,QAAQD,CAAQ,EAAE,KAAKF,GAAO,OAAO,UAAU,qBAAqB,KAAKE,EAAUF,CAAG,CAAC,EAE7EQ,EAAS,UAATA,EAAS,QAAY,IAAIT,IAAkB,MACjE,CAGA,SAASW,EAAkBR,EAA4BC,EAA4B,CACjF,MAAMC,EAAQ/B,EAAQ,EAChBsC,EAAQJ,GAAgBL,EAAUC,CAAS,EAC3CS,GAAcD,GAAA,YAAAA,EAAO,cAAenC,GAC1CqC,GAAqBF,GAAA,YAAAA,EAAO,YAAapC,EAAeqC,EAAaA,CAAW,EAChF,MAAMzB,EAAWwB,GAAA,YAAAA,EAAO,QAAQT,EAAUC,EAAWC,GACrD,OAAAtC,EAAmB,IAAM,IAAM6C,GAAA,YAAAA,EAAO,QAAS,CAACA,CAAK,CAAC,EACtD7C,EAAmB,IAAK,CAClBqB,IAAa,SAAWwB,GAAA,MAAAA,EAAO,OAAOxB,GAC5C,CAAC,EACDpB,EAAgB,IAAK,CACnB,GAAIoB,IAAa,OAGjB,UAAWP,KAASO,EAAS,SACvBR,EAAgBC,CAAK,GACvBA,EAAM,QAAQ,KAAK,YAAW,EAC9BA,EAAM,MAAM,KAAK,YAAW,EAC5BA,EAAM,WAAW,KAAK,YAAW,EACjCA,EAAM,SAAS,KAAK,YAAW,GAC1BA,EAAM,KAAK,YAAW,CAEjC,CAAC,EAEMO,IAAa,OAChBV,GACA,OAAO,YAAY,CAAC,GAAGU,CAAQ,EAAE,IAAI,CAAC,CAACa,EAAKpB,CAAK,IAAM,CAACoB,EAAKF,GAAOlB,CAAK,CAAC,CAAC,CAAC,CAClF,CAEA,SAASkC,GACPZ,EAAkB,CAElB,OAAOQ,EAAkBR,EAAUxB,EAAW,CAChD,CCnQA,MAAMA,EAA0D,IAAI,IAe9DqC,EAAqC,IAAI,IAE/C,SAASC,GAAkBC,EAAyBC,EAAwC,CAC1F,GAAI,CAACC,EAAWF,CAAM,EACpB,MAAM,IAAI3E,EAAkB,kBAAmB,2CAA2C,EAG5F,GAAI4E,EAAQ,IAAID,CAAM,EAAG,OAAOC,EAAQ,IAAID,CAAM,EAGlD,MAAMpE,EAAiBoE,EAAO,YAAW,EACzC,OAAAC,EAAQ,IAAID,EAAQpE,CAAK,EAElBA,CACT,CAEA,SAASuE,EAAcH,EAA2BC,EAAwC,CACxF,GAAIA,EAAQ,IAAID,CAAM,EAAG,OAAOC,EAAQ,IAAID,CAAM,EAElD,MAAMpE,EAAiBoE,EAAO,YAAW,EACzC,OAAAC,EAAQ,IAAID,EAAQpE,CAAK,EAElBA,CACT,CAGA,SAASwE,GACPrB,EACAnD,EACAsD,EACAe,EAAwC,CAExC,GAAI,CAACI,GAAWzE,CAAK,EAAG,OAExB,MAAMwD,EAAWxD,EACX0E,EAAclB,EAA6E,WAEjGF,EAAU,IAAIH,EAAK,CACjB,WAAYuB,IAAe,OAAY,OAAYH,EAAcG,EAAW,MAAOL,CAAO,EAC1F,SAAAb,EACA,MAAOe,EAAcf,EAAS,MAAOa,CAAO,CAC7C,CAAA,CACH,CAEA,SAASM,GAAkBrB,EAA6C,CACtE,OAAOA,EAAU,OAAS,EAAIzB,EAAcyB,CAC9C,CAaA,SAASsB,GACPC,EACAR,EAAwC,CAOxC,MAAM/B,EAAWuC,EAAO,CAAE,KALb,CAAkBT,EAAyBU,IAAwC,CAC9F,MAAM9E,EAAQmE,GAAWC,EAAQC,CAAO,EAExC,OAAOS,IAAY,OAAY9E,EAAQ8E,EAAQ9E,CAAK,CACtD,EACgC,EAEhC,GAAI,OAAOsC,GAAa,UAAYA,IAAa,MAAQ,MAAM,QAAQA,CAAQ,EAC7E,MAAM,IAAI7C,EAAkB,kBAAmB,oCAAoC,EAGrF,MAAMsF,EAA+C,CAAA,EAC/CzB,EAAY,IAAI,IAEtB,UAAWH,KAAO,QAAQ,QAAQb,CAAQ,EAAG,CAC3C,GAAI,CAAC,OAAO,UAAU,qBAAqB,KAAKA,EAAUa,CAAG,EAAG,SAEhE,MAAMnD,EAASsC,EAA6Ba,CAAG,EAE/C4B,EAAQ,KAAK,CAAC5B,EAAKnD,CAAK,CAAC,EACzBwE,GAAiBrB,EAAKnD,EAAOsD,EAAWe,CAAO,CACjD,CAGA,MAAO,CAAE,UAAWM,GAAkBrB,CAAS,EAAG,OAAQ,OAAO,YAAYyB,CAAO,CAAC,CACvF,CAOA,SAASC,GACPvE,EACAyB,EAAgD,CAEhD,GAAIzB,EAAS,OAASyB,EAAK,KAAM,MAAO,GAExC,SAAW,CAACiB,EAAKb,CAAQ,IAAKJ,EAAM,CAClC,MAAM+C,EAASxE,EAAS,IAAI0C,CAAG,EAE/B,IACE8B,GAAA,YAAAA,EAAQ,YAAa3C,EAAS,UAC9B,CAAC,OAAO,GAAG2C,EAAO,MAAO3C,EAAS,KAAK,GACvC,CAAC,OAAO,GAAG2C,EAAO,WAAY3C,EAAS,UAAU,EAEjD,MAAO,EAEX,CAEA,MAAO,EACT,CAEA,SAAS4C,GACPzE,EACA0E,EACAd,EACAf,EAAqD,CAErD,MAAM8B,EAAO,QAAQ,QAAQD,CAAM,EAC7BE,EAAe,QAAQ,QAAQ5E,EAAS,MAAM,EAEpD,GAAI2E,EAAK,SAAWC,EAAa,QAAUhB,EAAQ,OAAS5D,EAAS,QAAQ,KAAM,MAAO,GAE1F,UAAW2D,KAAUC,EAAS,GAAI,CAAC5D,EAAS,QAAQ,IAAI2D,CAAM,EAAG,MAAO,GAExE,OAAKY,GAAevE,EAAS,UAAW6C,CAAS,EAE1C8B,EAAK,MAAM,CAACjC,EAAKmC,IAAUD,EAAaC,CAAK,IAAMnC,GAAO,OAAO,GAAG1C,EAAS,OAAO0C,CAAG,EAAGgC,EAAOhC,CAAG,CAAC,CAAC,EAFlD,EAG7D,CAKA,MAAMoC,EAA4C,CAChD,SAAU,CAAA,EACV,OAAQrB,EACR,UAAWrC,EACX,QAAS,IAAI,IACb,OAAQ,CAAA,GAGV,SAAS2D,GAAkBL,EAAuB,CAChD,OAAO,OAAO,YACZ,QAAQ,QAAQA,CAAM,EACnB,OAAOhC,GAAOsC,EAAaN,EAAOhC,CAAG,CAAC,CAAC,EACvC,IAAIA,GAAO,CAACA,EAAKgC,EAAOhC,CAAG,CAAC,CAAC,CAAC,CAErC,CAEA,SAASuC,GAAgBpC,EAAqD,CAC5E,GAAIA,EAAU,OAAS,EAAG,OAAOY,EAEjC,MAAMyB,EAAS,IAAI,IAEnB,UAAWrD,KAAYgB,EAAU,OAAM,EAAIqC,EAAO,IAAIrD,EAAS,QAAQ,EAEvE,OAAOqD,CACT,CAMA,SAASC,GAAkBC,EAAyC,CAClE,SAAW,CAACzB,EAAQpE,CAAK,IAAK6F,EAC5B,GAAI,CAAC,OAAO,GAAGzB,EAAO,YAAW,EAAIpE,CAAK,EAAG,MAAO,GAGtD,MAAO,EACT,CAGA,SAAS8F,IAAqB,CAC5B,MAAO,CAAE,MAAO,OAAW,SAAU,OAAW,OAAQ,OAAW,SAAUP,CAAsB,CACrG,CAWA,SAASQ,GACPC,EACAC,EACAC,EAAuB,CAEvB,OAAIA,EAAO,QAAUF,IACnBE,EAAO,MAAQF,EACfE,EAAO,SAAW,OAClBA,EAAO,OAAS,OAChBA,EAAO,SAAWX,GAGb,IAAK,CACV,MAAMV,EAASoB,EAAU,QAEzB,GAAIpB,IAAW,OAAW,OAAOU,EAEjC,MAAMM,EAAWK,EAAO,SAExB,GAAIL,IAAa,QAAaK,EAAO,SAAWrB,GAAUe,GAAkBC,CAAQ,EAClF,OAAOK,EAAO,SAGhB,MAAMC,EAAe,IAAI,IACnB,CAAE,UAAA7C,EAAW,OAAA6B,CAAM,EAAKP,GAAiBwB,GAAWvB,EAAOmB,EAAOI,CAAO,EAAGD,CAAY,EACxF9B,EAAU,IAAI,IAAI8B,EAAa,KAAI,CAAE,EAE3C,OAAIN,IAAa,QAAa,CAACX,GAAegB,EAAO,SAAUf,EAAQd,EAASf,CAAS,KACvF4C,EAAO,SAAW,CAChB,SAAUV,GAAkBL,CAAM,EAClC,OAAQO,GAAgBpC,CAAS,EACjC,UAAAA,EACA,QAAAe,EACA,OAAAc,IAIJe,EAAO,SAAWC,EAClBD,EAAO,OAASrB,EAETqB,EAAO,QAChB,CACF,CCxRA,MAAMhC,EAAqC,IAAI,IAE/C,SAASmC,EAAqBC,EAAuCC,EAAsB,GAAE,CAC3F,UAAWC,KAAgBF,EAAe,CACxC,MAAMG,EAAUD,EAAa,QAC7BA,EAAa,QAAU,OAEvB,GAAI,CACFC,GAAA,MAAAA,GACF,OAASjI,EAAO,CACd+H,EAAS,KAAK/H,CAAK,CACrB,CACF,CAEA,GAAI+H,EAAS,SAAW,EAAG,MAAMA,EAAS,CAAC,EAE3C,GAAIA,EAAS,OAAS,EAAG,MAAMG,GAAqBH,EAAU,4CAA4C,CAC5G,CAGA,MAAMI,EAAmB,CACf,QAAU,GAED,OAAS,IAAI,IACb,UAAY,IAAI,IACzB,SAAqCzC,EACrC,QAA0C,IAAI,IACrC,cAAgB,IAAI,IAErC,OAAK,CACH,KAAK,QAAU,GACf,KAAK,QAAU,IAAI,IACnB,KAAK,SAAWA,EAChB,KAAK,WAAU,CACjB,CAEA,OAAOG,EAAyCuC,EAAqC1C,EAAQ,CAC3F,KAAK,QAAUG,EACf,KAAK,SAAWuC,EAChB,KAAK,QAAU,GAEf,GAAI,CACF,KAAK,YAAW,CAClB,OAASpI,EAAO,CACd,KAAK,WAAW,CAACA,CAAK,CAAC,CACzB,CACF,CAEA,UAAaoB,GAAoC,CAC/C,MAAMwD,EAAW,CAAE,OAAAxD,CAAM,EACzB,KAAK,UAAU,IAAIwD,CAAQ,EAE3B,GAAI,CACF,KAAK,YAAW,CAClB,OAAS5E,EAAO,CACd,KAAK,UAAU,OAAO4E,CAAQ,EAC9B,KAAK,WAAW,CAAC5E,CAAK,CAAC,CACzB,CAEA,MAAO,IAAK,CACV,KAAK,UAAU,OAAO4E,CAAQ,EAE1B,KAAK,UAAU,OAAS,GAAG,KAAK,WAAU,CAChD,CACF,EAEQ,IAAIgB,EAAyB,CACnC,MAAMoC,EAA6B,CAAE,QAAS,MAAS,EAEvD,KAAK,cAAc,IAAIpC,EAAQoC,CAAY,EAC3C,MAAMC,EAAUrC,EAAO,UAAU,KAAK,MAAM,EAE5C,GAAI,OAAOqC,GAAY,WAAY,MAAM,IAAI,UAAU,gDAAgD,EAEnG,KAAK,cAAc,IAAIrC,CAAM,IAAMoC,EAAcA,EAAa,QAAUC,EACvEA,EAAO,CACd,CAEQ,OAAS,IAAW,CAC1B,UAAWrD,IAAY,CAAC,GAAG,KAAK,SAAS,EACnC,KAAK,UAAU,IAAIA,CAAQ,GAAGA,EAAS,OAAM,CAErD,EAEQ,WAAWmD,EAAoB,CACrC,MAAMM,EAAO,CAAC,GAAG,KAAK,cAAc,OAAM,EAAI,GAAG,KAAK,OAAO,QAAQ,EACrE,KAAK,cAAc,MAAK,EACxB,KAAK,OAAO,MAAK,EACjBR,EAAqBQ,EAAMN,CAAQ,CACrC,CAEQ,kBAAgB,CACtB,MAAMO,EAA0B,CAAA,EAEhC,SAAW,CAAC1C,EAAQoC,CAAY,IAAK,KAAK,cACpC,KAAK,QAAQ,IAAIpC,CAAM,IAE3B,KAAK,cAAc,OAAOA,CAAM,EAChC0C,EAAQ,KAAKN,CAAY,GAG3B,SAAW,CAAChD,EAAUuD,CAAK,IAAK,KAAK,OAC/B,KAAK,SAAS,IAAIvD,CAAQ,IAE9B,KAAK,OAAO,OAAOA,CAAQ,EAC3BsD,EAAQ,KAAKC,CAAK,GAGpBV,EAAqBS,CAAO,CAC9B,CAOQ,OAAOtD,EAAqB,CAClC,MAAMuD,EAAsB,CAAE,QAAS,MAAS,EAChD,KAAK,OAAO,IAAIvD,EAAUuD,CAAK,EAC/B,MAAMC,EAAUxD,EAAS,QAAO,EAEhC,GAAI,OAAOwD,GAAY,WAAY,MAAM,IAAI,UAAU,0DAA0D,EAE7G,KAAK,OAAO,IAAIxD,CAAQ,IAAMuD,EAAOA,EAAM,QAAUC,EACpDA,EAAO,CACd,CAGQ,OAAK,CACX,OAAO,KAAK,SAAW,KAAK,UAAU,KAAO,CAC/C,CAEQ,gBAAc,CACpB,UAAWxD,KAAY,KAAK,SAAU,CACpC,GAAI,CAAC,KAAK,MAAK,EAAI,OAEf,KAAK,SAAS,IAAIA,CAAQ,GAAK,CAAC,KAAK,OAAO,IAAIA,CAAQ,GAAG,KAAK,OAAOA,CAAQ,CACrF,CACF,CAEQ,kBAAgB,CACtB,UAAWY,KAAU,KAAK,QAAS,CACjC,GAAI,CAAC,KAAK,MAAK,EAAI,OAEf,KAAK,QAAQ,IAAIA,CAAM,GAAK,CAAC,KAAK,cAAc,IAAIA,CAAM,GAAG,KAAK,IAAIA,CAAM,CAClF,CACF,CAEQ,aAAW,CACjB,KAAK,iBAAgB,EACrB,KAAK,iBAAgB,EACrB,KAAK,eAAc,CACrB,CACD,CC5HD,MAAM6C,GAAoB,IAAM1B,EAEhC,SAAS2B,GAAkB5E,EAAiB,CAC1C,MAAM6E,EAAWvD,EAAwC,MAAS,EAElE,OAAOtB,EAAY6E,EAAS,UAATA,EAAS,QAAY,IAAIR,IAAyB,MACvE,CAOA,SAASS,GAAeC,EAA6BC,EAAqB,CACxE,OAAO,OAAO,YACZ,QAAQ,QAAQD,EAAS,MAAM,EAAE,IAAIlE,GAAO,CAC1CA,EACA,OAAO,UAAU,eAAe,KAAKmE,EAAOnE,CAAG,EAAImE,EAAMnE,CAAG,EAAIkE,EAAS,OAAOlE,CAAG,CACpF,CAAA,CAAC,CAEN,CAQA,SAASoE,GACPC,EACA3C,EAA2E,CAE3E,MAAMtB,EAAQ/B,EAAQ,EAEtB,GAAI,CAAC+B,EAAM,OAAO,IAAIiE,CAAW,EAC/B,MAAM,IAAI/H,EAAkB,UAAW,SAAS+H,EAAY,EAAE,uCAAuC,EAGvG,MAAMxB,EAAQzC,EAAM,OAAO,IAAIiE,CAAW,EACpCC,EAAQP,GAAkBrC,IAAW,MAAS,EAG9CoB,EAAYrC,EAAwD,MAAS,EACnFqC,EAAU,QAAUpB,EACpB,MAAMvC,EAAWuC,IAAW,OAEtB6C,EAAY9D,EAAoC,MAAS,EACzDsC,EAAUwB,EAAU,UAAVA,EAAU,QAAY5B,GAAqB,GACrD/B,EAAc3C,EAClB,IAAOkB,EAAWyD,GAAwBC,EAAOC,EAAWC,CAAM,EAAIe,GACtE,CAACf,EAAQF,EAAO1D,CAAQ,CAAC,EAErB+E,EAAWrD,GAAqByD,GAAA,YAAAA,EAAO,YAAa/F,EAAeqC,EAAaA,CAAW,EAC3FuD,EAAQzD,EAAkBwD,EAAS,SAAUA,EAAS,SAAS,EAKrE,OAJAnG,EAAgB,IAAM,IAAMuG,GAAA,YAAAA,EAAO,QAAS,CAACA,CAAK,CAAC,EAEnDvG,EAAgB,IAAMuG,GAAA,YAAAA,EAAO,OAAOJ,EAAS,QAASA,EAAS,QAAS,CAACA,EAAUI,CAAK,CAAC,EAErF5C,IAAW,OAAkBmB,EAE1BoB,GAAeC,EAAUC,CAAK,CACvC,CC7EA,SAASK,GACPC,EAAc,CAEd,MAAMC,EAAW,OAAO,OAAO,CAAC,GAAGD,CAAM,CAAC,EAE1C,OAAeE,GAAgF,CAG7F,GAFkBA,EAA0D,WAE3D,OACf,MAAM,IAAIrI,EAAkB,YAAa,oDAAoD,EAG/F,cAAO,eAAeqI,EAAW,WAAY,CAAE,WAAY,GAAO,MAAOD,EAAU,EAE5EC,CACT,CACF,CCfA,SAASC,GAAiBvE,EAAqB,CAC7C,KAAM,CAACwE,CAAK,EAAIpH,EAAS,IAAM,IAAI,OAAgC,EAC7DqH,EAAWD,EAAM,IAAIxE,CAAQ,EAEnC,GAAIyE,IAAa,OAAW,OAAOA,EAEnC,MAAMzF,EAAW0F,GAAuB1E,CAAQ,EAC1C7C,EAAuB,CAC3B,WAAY,IAAInB,EAAqCgD,EAAS,UAAU,EACxE,SAAU,IAAIhD,EAAqCgD,EAAS,QAAQ,EACpE,QAAS,IAAIhD,EAAqCgD,EAAS,OAAO,EAClE,MAAO,IAAIhD,EAAqCgD,EAAS,KAAK,GAEhE,OAAAwF,EAAM,IAAIxE,EAAU7C,CAAK,EAElBA,CACT,CAEA,MAAMwH,GAAsD,OAAO,OAAO,CACxE,KAAM,cACN,OAAQ,oBACT,CAAA,EACKC,GAA6D,OAAO,OAAO,CAC/E,YAAa,IAAMD,GACnB,UAAW,IAAM,IAAA,EAClB,CAAA,EAWD,SAASE,GAA2C7E,EAA6B,CAC/E,MAAM8E,EAAS9E,EACTkB,EAAa6D,GAAaD,CAAM,EAChCE,EAAYC,EAAarF,GAAyBI,EAAS,MAAM,UAAUJ,CAAQ,EAAG,CAACI,CAAQ,CAAC,EAMhGkF,EAAWtH,EAAQ,IAAM,IAAMoC,EAAS,MAAM,YAAW,EAAI,CAACA,CAAQ,CAAC,EACvEmF,EAAQ3E,EAAqBwE,EAAWE,EAAUA,CAAQ,EAC1DE,GACHlE,GAAA,YAAAA,EAAY,QAAgE0D,GACzES,EAAsBJ,EACzBrF,GAAyBwF,EAAiB,UAAUxF,CAAQ,EAC7D,CAACwF,CAAgB,CAAC,EAEdE,EAAqB1H,EAAQ,IAAM,IAAMwH,EAAiB,YAAW,EAAI,CAACA,CAAgB,CAAC,EAC3FG,EAAkB/E,EAAqB6E,EAAqBC,EAAoBA,CAAkB,EAGxGE,EAAU,IAAMxF,EAAS,QAAO,EAAI,CAACA,CAAQ,CAAC,EAE9C,MAAM7C,EAAQoH,GAAiBO,CAAM,EAC/BW,EAAUpI,EAAiBF,EAAM,OAAO,EACxCuI,EAAQrI,EAAiBF,EAAM,KAAK,EACpCwI,EAAatI,EAAiBF,EAAM,UAAU,EAC9CyI,EAAWvI,EAAiBF,EAAM,QAAQ,EAC1C0I,EAAajI,EACjB,IACEsD,IAAe,OACX,OACC,CAAE,SAAA0E,EAAU,MAAOL,CAAe,EACzC,CAACrE,EAAY0E,EAAUL,CAAe,CAAC,EAGzC,OAAO3H,EACL,KAAO,CAAE,WAAA+H,EAAY,WAAAE,EAAY,QAAAJ,EAAS,MAAAC,EAAO,MAAAP,CAAK,GACtD,CAACQ,EAAYE,EAAYJ,EAASC,EAAOP,CAAK,CAAC,CAEnD,CCpDA,MAAMW,EAA2C,OAAO,OAAO,EAAE,EAGjE,SAASC,EAAqDC,EAAU,CACtE,OAAOC,GAAkDD,EAAI,MAAM,CACrE,CAEA,SAASE,GACPF,EAAU,CAEV,MAAM3B,EAAW8B,EAAcH,CAAE,EAC3BI,EAAU,IAAI,IAEdC,EAAcC,GAAmC,CACrD,MAAM7B,EAAW2B,EAAQ,IAAIE,CAAK,EAElC,GAAI7B,IAAa,OAAW,OAAOA,EAEnC,MAAM8B,EAASR,EAAkB,GAAG1B,CAAQ,IAAIiC,CAAK,EAAE,EACvD,OAAAF,EAAQ,IAAIE,EAAOC,CAAM,EAElBA,CACT,EAEA,cAAO,eAAeF,EAAY,KAAM,CAAE,WAAY,GAAM,MAAOhC,EAAU,EAEtE,OAAO,OAAOgC,CAAU,CACjC,CAMA,SAASG,GAAmCzL,EAA+B,CACzE,MAAMwG,EAAUkF,EAAY1L,EAAQ,OAAO,OAAO,EAC5C2L,EAAoB,UAAW3L,EAAWA,EAAQ,OAAS+K,EAAuBA,EAExF,OAAIvE,EAAQ,SAAW,EAAU,KAE1BA,EAAQ,IAAIhD,GACjBoI,EACEC,EACA,CAAE,IAAKrI,EAAM,IACboI,EAAcE,GAAmB,CAC/B,UAAWC,GAAsBvI,CAAK,EACtC,aAAcA,EAAM,MACpB,eAAgBA,EAAM,GACtB,UAAAmI,EACA,SAAU3L,EAAQ,OAAO,GAC1B,CAAC,CACH,CAEL"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/command-hook-controller.ts","../src/command-hook.ts","../src/idle-subscription.ts","../src/selection-hooks.ts","../src/model-selection-snapshot.ts","../src/model-selection-store.ts","../src/model-hook.ts","../src/requires-models.ts","../src/resource-hook.ts","../src/slot.tsx"],"sourcesContent":["import { CommandError } from '@opetope/core';\nimport { compatibleAbortReason, isCancellation, reportDetachedError } from '@opetope/core/internal';\nimport type { CommandInputArgs, CallRunOptions } from '@opetope/core/internal';\n\nimport type { CommandOutcome } from './command';\nimport { ContributionError } from './errors';\n\ntype CommandRunOptions<Output> = Readonly<{\n onFailure?: (error: unknown) => PromiseLike<void> | void;\n onSuccess?: (value: Output) => PromiseLike<void> | void;\n signal?: AbortSignal;\n}>;\n\ntype CommandRun<Input, Output> = {\n (input: Input, options?: CommandRunOptions<Output>): Promise<CommandOutcome<Output>>;\n (...args: [...CommandInputArgs<Input>, options?: CommandRunOptions<Output>]): Promise<CommandOutcome<Output>>;\n};\n\ntype CommandInvoker = Readonly<{\n run: (input: never, options?: CallRunOptions<unknown>) => Promise<unknown>;\n}>;\n\ntype CommandNotification<Output> = (inFlight: boolean, outcome?: CommandOutcome<Output>) => void;\n\ntype CommandRequest<Input, Output> = {\n input: Input | undefined;\n options: CommandRunOptions<Output> | undefined;\n readonly promise: Promise<CommandOutcome<Output>>;\n resolve: ((outcome: CommandOutcome<Output>) => void) | undefined;\n};\n\nfunction failureOutcome<Output>(options: CommandRunOptions<Output> | undefined, error: unknown): CommandOutcome<never> {\n if (isCancellation(error)) return { reason: error, kind: 'cancelled' };\n\n try {\n const notification = options?.onFailure?.(error);\n\n if (notification !== undefined) void Promise.resolve(notification).catch(reportDetachedError);\n } catch (callbackError) {\n reportDetachedError(callbackError);\n }\n\n return { error, kind: 'failed' };\n}\n\nfunction runOptions<Output>(options: CommandRunOptions<Output> | undefined): CallRunOptions<Output> | undefined {\n const onSuccess = options?.onSuccess;\n const signal = options?.signal;\n\n if (onSuccess === undefined) return signal === undefined ? undefined : { signal };\n\n return signal === undefined ? { onSuccess } : { onSuccess, signal };\n}\n\nfunction requestOf<Input, Output>(\n input: Input,\n options: CommandRunOptions<Output> | undefined,\n): CommandRequest<Input, Output> {\n let resolve!: (outcome: CommandOutcome<Output>) => void;\n const promise = new Promise<CommandOutcome<Output>>(settle => {\n resolve = settle;\n });\n\n return { input, options: options === undefined ? undefined : { ...options }, promise, resolve };\n}\n\nfunction settle<Input, Output>(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n const resolve = request.resolve;\n request.input = undefined;\n request.options = undefined;\n request.resolve = undefined;\n resolve?.(outcome);\n}\n\n/**\n * D203: the consumer keeps a status, not a schedule. Every `run` reaches the command, and what the command was\n * declared with — `concurrency`, `memoize`, `dedupe` — decides what happens to it. What stays\n * local is what only this consumer knows: whether it is mounted, its own input signal, and the outcome it shows.\n */\nclass CommandHookController<Input, Output> {\n readonly run: CommandRun<Input, Output> = ((input: Input, options?: CommandRunOptions<Output>) => {\n if (this.notify === undefined) {\n return Promise.resolve({\n reason: new ContributionError('inactive', 'Command consumer is not mounted.'),\n kind: 'cancelled' as const,\n });\n }\n\n const aborted = this.cancelledInput(options);\n\n if (aborted !== undefined) return Promise.resolve(aborted);\n\n const request = requestOf(input, options);\n this.start(request);\n\n return request.promise;\n }) as CommandRun<Input, Output>;\n private inFlight = 0;\n private notify: CommandNotification<Output> | undefined;\n\n constructor(private readonly invoker: CommandInvoker) {}\n\n attach(notify: CommandNotification<Output>): () => void {\n this.notify = notify;\n\n return () => {\n if (this.notify !== notify) return;\n\n this.notify = undefined;\n };\n }\n\n synchronize(): void {\n this.notify?.(this.inFlight > 0);\n }\n\n private aborted(signal: AbortSignal): CommandOutcome<never> {\n return {\n reason: new CommandError('cancelled', 'Command input was cancelled.', compatibleAbortReason(signal)),\n kind: 'cancelled',\n };\n }\n\n private cancelledInput(options: CommandRunOptions<Output> | undefined): CommandOutcome<never> | undefined {\n return options?.signal?.aborted === true ? this.aborted(options.signal) : undefined;\n }\n\n private finish(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n if (request.resolve === undefined) return;\n\n this.inFlight -= 1;\n settle(request, outcome);\n this.notify?.(this.inFlight > 0, outcome);\n }\n\n private invoke(request: CommandRequest<Input, Output>): void {\n let invocation: Promise<Output>;\n\n try {\n invocation = this.invoker.run(request.input as never, runOptions(request.options) as never) as Promise<Output>;\n request.input = undefined;\n } catch (error) {\n this.finish(request, failureOutcome(request.options, error));\n\n return;\n }\n\n void invocation.then(\n value => this.finish(request, { kind: 'ok', value }),\n (error: unknown) => this.finish(request, failureOutcome(request.options, error)),\n );\n }\n\n private start(request: CommandRequest<Input, Output>): void {\n this.inFlight += 1;\n // Reserve the flight before publishing: a reentrant run must already see this consumer as busy.\n this.notify?.(true);\n this.invoke(request);\n }\n}\n\nexport { CommandHookController };\nexport type { CommandInvoker, CommandRun };\n","import { useInsertionEffect, useLayoutEffect, useMemo, useState } from 'react';\n\nimport type { Command } from '@opetope/core';\n\nimport type { CommandOutcome } from './command';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker, CommandRun } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { useFrame } from './mount-context';\n\n/** D292: a cancellation is not an answer of the product, so it never settles into the outcome this hook shows. */\ntype SettledOutcome<Output> = Exclude<CommandOutcome<Output>, { readonly kind: 'cancelled' }>;\n\ninterface CommandHookStatus<Output> {\n readonly inFlight: boolean;\n readonly outcome: SettledOutcome<Output> | undefined;\n}\n\nconst emptyStatus: CommandHookStatus<never> = { inFlight: false, outcome: undefined };\n\ntype CommandHook<Input, Output> = CommandHookStatus<Output> & {\n readonly run: CommandRun<Input, Output>;\n};\n\ntype CommandHookState<Input, Output> = CommandHookStatus<Output> & {\n readonly slot: CommandHookController<Input, Output>;\n};\n\n/** D292: a cancellation moves nothing, so it is the same non-event as a notification without an outcome at all. */\nfunction settledOutcome<Output>(outcome: CommandOutcome<Output> | undefined): SettledOutcome<Output> | undefined {\n return outcome === undefined || outcome.kind === 'cancelled' ? undefined : outcome;\n}\n\nfunction unchangedState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n settled: SettledOutcome<Output> | undefined,\n): boolean {\n return settled === undefined && current.slot === slot && current.inFlight === inFlight;\n}\n\nfunction nextState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n outcome?: CommandOutcome<Output>,\n): CommandHookState<Input, Output> {\n const settled = settledOutcome(outcome);\n\n if (unchangedState(current, slot, inFlight, settled)) return current;\n\n // A new run clears nothing: the previous answer stays readable while its retry is in flight.\n const previous = current.slot === slot ? current.outcome : undefined;\n\n return { inFlight, outcome: settled ?? previous, slot };\n}\n\nfunction useCommandSlot<Input, Output>(invoker: CommandInvoker): CommandHookController<Input, Output> {\n // Slots are inert until commit. An interrupted render cannot fence the committed source or admit its own work.\n const [slots] = useState(() => new WeakMap<object, CommandHookController<Input, Output>>());\n let slot = slots.get(invoker);\n\n if (slot === undefined) {\n slot = new CommandHookController(invoker);\n slots.set(invoker, slot);\n }\n\n return slot;\n}\n\n/**\n * The status half of a consumer, apart from what it is a consumer of. D321 gives a resource the same two verbs a\n * command has, so the same three React hooks answer for them: the state of this consumer, the attachment that moves\n * it, and the synchronisation of a slot that was already busy when this mount arrived.\n */\nfunction useCommandStatus<Input, Output>(\n currentSlot: CommandHookController<Input, Output>,\n): CommandHook<Input, Output> {\n const [hookState, setHookState] = useState<CommandHookState<Input, Output>>(() => ({\n inFlight: false,\n outcome: undefined,\n slot: currentSlot,\n }));\n useInsertionEffect(\n () =>\n currentSlot.attach((inFlight, outcome) =>\n setHookState(current => nextState(current, currentSlot, inFlight, outcome)),\n ),\n [currentSlot],\n );\n useLayoutEffect(() => currentSlot.synchronize(), [currentSlot]);\n const { inFlight, outcome } = hookState.slot === currentSlot ? hookState : emptyStatus;\n const run = currentSlot.run;\n\n return useMemo(() => ({ inFlight, outcome, run }), [inFlight, outcome, run]);\n}\n\nfunction useCommand<Input, Output>(command: Command<Input, Output>): CommandHook<Input, Output> {\n const frame = useFrame();\n const record = frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command ${command.id} is not bound in this contribution.`);\n }\n\n return useCommandStatus(useCommandSlot<Input, Output>(record.invoker));\n}\n\nexport { useCommand, useCommandStatus };\nexport type { CommandHook };\n","const idleRelease = (): void => undefined;\nconst subscribeIdle = (): (() => void) => idleRelease;\nconst idleRevision = (): number => 0;\n\nexport { idleRevision, subscribeIdle };\n","import { useInsertionEffect, useLayoutEffect, useRef, useSyncExternalStore } from 'react';\n\nimport type { Command } from '@opetope/core';\n\nimport type { CommandOutcome } from './command';\nimport type { CommandHook } from './command-hook';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { idleRevision, subscribeIdle } from './idle-subscription';\nimport type { SelectedResource } from './model-selection-snapshot';\nimport { useFrame } from './mount-context';\nimport type { Frame } from './mount-frame';\nimport { resourceInvokers } from './resource-command';\nimport type { ResourceInvokers } from './resource-command';\n\ntype CommandSelection = Readonly<Record<string, Command<never, unknown>>>;\ntype ResourceSelection = ReadonlyMap<PropertyKey, SelectedResource>;\n/** D359: what one selected Resource answers — its three verbs, its state, and the pagination capability if it has one. */\ntype SelectedResourceHook = Readonly<{\n pagination: Readonly<{ loadNext: CommandHook<never, unknown>; state: unknown }> | undefined;\n refresh: CommandHook<never, unknown>;\n reset: CommandHook<never, unknown>;\n retry: CommandHook<never, unknown>;\n state: unknown;\n}>;\ntype SelectionHooks = Readonly<Record<PropertyKey, CommandHook<never, unknown> | SelectedResourceHook>>;\n\nconst emptyHooks: SelectionHooks = Object.freeze({});\n\ntype CommandEntry = {\n detach: (() => void) | undefined;\n hook: CommandHook<never, unknown>;\n readonly invoker: CommandInvoker;\n readonly slot: CommandHookController<never, unknown>;\n};\n\n/** D359: one key of a selection, four consumers. The record is the value; `entries` still holds one entry per key. */\ntype ResourceEntry = {\n /** D359: the value this key hands the render, kept on the record so a still frame is the same object again. */\n hook: SelectedResourceHook;\n readonly kind: 'resource';\n readonly loadNext: CommandEntry;\n /** Whether the declaration carried the pagination capability at all. */\n readonly paginated: boolean;\n readonly refresh: CommandEntry;\n readonly reset: CommandEntry;\n readonly retry: CommandEntry;\n};\n\ntype SelectionEntry = CommandEntry | ResourceEntry;\n\nfunction isResourceEntry(entry: SelectionEntry): entry is ResourceEntry {\n return 'kind' in entry;\n}\n\nfunction createEntry(invoker: CommandInvoker): CommandEntry {\n const slot = new CommandHookController<never, unknown>(invoker);\n\n return { detach: undefined, hook: { inFlight: false, outcome: undefined, run: slot.run }, invoker, slot };\n}\n\nfunction unchangedHook(previous: CommandHook<never, unknown>, next: CommandHook<never, unknown>): boolean {\n return previous.inFlight === next.inFlight && Object.is(previous.outcome, next.outcome);\n}\n\nfunction updateEntry(entry: CommandEntry, inFlight: boolean, outcome?: CommandOutcome<unknown>): boolean {\n const previous = entry.hook;\n\n if (outcome === undefined && previous.inFlight === inFlight) return false;\n\n // D292: only a settled non-cancelled outcome replaces the one this key shows; a retry keeps the previous answer.\n const settled = outcome === undefined || outcome.kind === 'cancelled' ? undefined : outcome;\n const next = { inFlight, outcome: settled ?? previous.outcome, run: previous.run };\n\n if (unchangedHook(previous, next)) return false;\n\n entry.hook = next;\n\n return true;\n}\n\nfunction selectEntry(previous: SelectionEntry | undefined, invoker: CommandInvoker): CommandEntry {\n return previous !== undefined && !isResourceEntry(previous) && previous.invoker === invoker\n ? previous\n : createEntry(invoker);\n}\n\nfunction resourceHook(entry: Omit<ResourceEntry, 'hook'>, selected: SelectedResource): SelectedResourceHook {\n return {\n pagination: entry.paginated ? { loadNext: entry.loadNext.hook, state: selected.pagination } : undefined,\n refresh: entry.refresh.hook,\n reset: entry.reset.hook,\n retry: entry.retry.hook,\n state: selected.state,\n };\n}\n\nfunction createResourceEntry(invokers: ResourceInvokers, selected: SelectedResource): ResourceEntry {\n const parts = {\n kind: 'resource' as const,\n loadNext: createEntry(invokers.loadNext),\n paginated: selected.pagination !== undefined,\n refresh: createEntry(invokers.refresh),\n reset: createEntry(invokers.reset),\n retry: createEntry(invokers.retry),\n };\n\n return { ...parts, hook: resourceHook(parts, selected) };\n}\n\n/** The four commands of a Resource: the three verbs every one has, and `loadNext` only where one was declared. */\nfunction unchangedResourceCommands(entry: ResourceEntry): boolean {\n const { hook } = entry;\n\n return (\n hook.refresh === entry.refresh.hook &&\n hook.reset === entry.reset.hook &&\n hook.retry === entry.retry.hook &&\n (!entry.paginated || hook.pagination?.loadNext === entry.loadNext.hook)\n );\n}\n\n/** What this selection read: the state of the Resource, and the state of its capability where it has one. */\nfunction unchangedResourceState(entry: ResourceEntry, selected: SelectedResource): boolean {\n return Object.is(entry.hook.state, selected.state) && Object.is(entry.hook.pagination?.state, selected.pagination);\n}\n\nfunction unchangedResourceHook(entry: ResourceEntry, selected: SelectedResource): boolean {\n return unchangedResourceCommands(entry) && unchangedResourceState(entry, selected);\n}\n\n/**\n * S6/D359: the same Resource that did not move answers the same object, so a child under `memo` is not re-rendered by\n * the mere act of reading it. Every part is a render input, so this is a memo and not a piece of state.\n */\nfunction selectResourceEntry(\n previous: SelectionEntry | undefined,\n invokers: ResourceInvokers,\n selected: SelectedResource,\n): ResourceEntry {\n if (previous === undefined || !isResourceEntry(previous) || previous.refresh.invoker !== invokers.refresh) {\n return createResourceEntry(invokers, selected);\n }\n\n if (!unchangedResourceHook(previous, selected)) previous.hook = resourceHook(previous, selected);\n\n return previous;\n}\n\nfunction commandEntries(entry: SelectionEntry): readonly CommandEntry[] {\n return isResourceEntry(entry) ? [entry.refresh, entry.reset, entry.retry, entry.loadNext] : [entry];\n}\n\nfunction detachEntry(entry: SelectionEntry): void {\n for (const command of commandEntries(entry)) {\n command.detach?.();\n command.detach = undefined;\n }\n}\n\nfunction hookOf(entry: SelectionEntry): CommandHook<never, unknown> | SelectedResourceHook {\n return entry.hook;\n}\n\n/** Selection is inert during render; only a committed selection can admit work or discard an old queue. */\nclass CommandGroup {\n private entries = new Map<PropertyKey, SelectionEntry>();\n /** One set of invokers per Resource identity, so `run` keeps its reference across renders (D359). */\n private readonly invokers = new WeakMap<object, ResourceInvokers>();\n private readonly listeners = new Set<() => void>();\n private revision = 0;\n\n close(): void {\n for (const entry of this.entries.values()) detachEntry(entry);\n\n this.entries.clear();\n }\n\n commit(selected: Map<PropertyKey, SelectionEntry>): void {\n for (const [key, entry] of this.entries) {\n if (selected.get(key) !== entry) detachEntry(entry);\n }\n\n this.entries = selected;\n\n for (const entry of selected.values()) this.attachEntry(entry);\n }\n\n /** S7: the attach runs on every commit, so the verbs are named rather than gathered into an array. */\n private attachEntry(entry: SelectionEntry): void {\n if (isResourceEntry(entry)) {\n this.attachCommand(entry.refresh);\n this.attachCommand(entry.retry);\n this.attachCommand(entry.reset);\n this.attachCommand(entry.loadNext);\n\n return;\n }\n\n this.attachCommand(entry);\n }\n\n private attachCommand(command: CommandEntry): void {\n command.detach ??= command.slot.attach((inFlight, outcome) => {\n if (!updateEntry(command, inFlight, outcome)) return;\n\n this.revision += 1;\n\n for (const listener of this.listeners) listener();\n });\n }\n\n getSnapshot = (): number => this.revision;\n\n /**\n * D321: two outcomes no longer, but two kinds. A command that is not bound is still a `ContributionError`; a\n * resource never appears in `frame.commandRecords`, so it is prepared from its own identity instead.\n */\n prepare(commands: CommandSelection, resources: ResourceSelection, frame: Frame): Map<PropertyKey, SelectionEntry> {\n const selected = new Map<PropertyKey, SelectionEntry>();\n\n for (const key of Reflect.ownKeys(commands)) {\n if (!Object.prototype.propertyIsEnumerable.call(commands, key)) continue;\n\n const command = (commands as Readonly<Record<PropertyKey, Command<never, unknown>>>)[key];\n const record = command === undefined ? undefined : frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command at ${String(key)} is not bound in this contribution.`);\n }\n\n selected.set(key, selectEntry(this.entries.get(key), record.invoker));\n }\n\n for (const [key, resource] of resources) {\n const invokers = resourceInvokers(resource.resource, this.invokers);\n\n selected.set(key, selectResourceEntry(this.entries.get(key), invokers, resource));\n }\n\n return selected;\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n };\n}\n\nfunction useCommandGroup(commands: CommandSelection, resources: ResourceSelection): CommandGroup | undefined {\n const groupRef = useRef<CommandGroup | undefined>(undefined);\n const populated =\n resources.size > 0 ||\n Reflect.ownKeys(commands).some(key => Object.prototype.propertyIsEnumerable.call(commands, key));\n\n return populated ? (groupRef.current ??= new CommandGroup()) : undefined;\n}\n\n/** The consumers of one selection: a command per key, and the two verbs of a resource per key (D321). */\nfunction useSelectionHooks(commands: CommandSelection, resources: ResourceSelection): SelectionHooks {\n const frame = useFrame();\n const group = useCommandGroup(commands, resources);\n const getSnapshot = group?.getSnapshot ?? idleRevision;\n useSyncExternalStore(group?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const selected = group?.prepare(commands, resources, frame);\n useInsertionEffect(() => () => group?.close(), [group]);\n useInsertionEffect(() => {\n if (selected !== undefined) group?.commit(selected);\n });\n useLayoutEffect(() => {\n if (selected === undefined) return;\n\n // S7: the same branch as the attach — this runs after every commit, and the arrays were pure allocation.\n for (const entry of selected.values()) {\n if (isResourceEntry(entry)) {\n entry.refresh.slot.synchronize();\n entry.retry.slot.synchronize();\n entry.reset.slot.synchronize();\n entry.loadNext.slot.synchronize();\n } else entry.slot.synchronize();\n }\n });\n\n return selected === undefined\n ? emptyHooks\n : Object.fromEntries([...selected].map(([key, entry]) => [key, hookOf(entry)]));\n}\n\nexport { useSelectionHooks };\nexport type { SelectionHooks };\n","import type { Readable } from '@opetope/core';\nimport { isCallTarget, isReadable } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\nimport type { AnyCommand } from './model-binding';\nimport { isResource } from './resource-command';\nimport type { AnyResource } from './resource-command';\n\ninterface SelectionRead {\n <Value>(source: Readable<Value>): Value;\n <Value, Selected>(source: Readable<Value>, select: (value: Value) => Selected): Selected;\n}\n\ntype SelectionContext = Readonly<{ read: SelectionRead }>;\ntype NonSelectionRecord =\n | readonly unknown[]\n | ((...arguments_: never[]) => unknown)\n | (abstract new (...arguments_: never[]) => unknown)\n | Date\n | PromiseLike<unknown>\n | ReadonlyMap<unknown, unknown>\n | ReadonlySet<unknown>\n | RegExp\n | WeakMap<object, unknown>\n | WeakSet<object>;\n/** A named interface needs no open index signature; container and callable results are not field selections. */\ntype SelectionShape<Selected extends object> = Selected extends NonSelectionRecord ? never : Selected;\ntype SelectionRecord = Readonly<Record<PropertyKey, unknown>>;\n/**\n * D359: the Resource a field selected, and what this selection read from it — its state, and the state of its\n * pagination capability when it has one. Both are read passively: a selection takes no lease by reading.\n */\ntype SelectedResource = Readonly<{ pagination: unknown; resource: AnyResource; state: unknown }>;\ntype SelectionSnapshot = Readonly<{\n commands: Readonly<Record<PropertyKey, AnyCommand>>;\n /** The set the mount leases; two keys naming one resource are one lease. */\n leases: ReadonlySet<AnyResource>;\n resources: ReadonlyMap<PropertyKey, SelectedResource>;\n sources: ReadonlySet<Readable<unknown>>;\n values: SelectionRecord;\n}>;\n\nconst noResources: ReadonlyMap<PropertyKey, SelectedResource> = new Map();\n\n/**\n * D363: what this mount read last and the snapshot it answered with. It belongs to the mount and not to the reader:\n * React may discard the `useMemo` that builds the reader, and a memory kept there would be empty exactly when it is\n * needed. D364: it no longer remembers which sources ended — a source whose owner retired stops instead of refusing,\n * so it answers its last value on every read and there is nothing to remember or to step around.\n */\ntype SelectionMemory = {\n model: unknown;\n observed: Map<Readable<unknown>, unknown> | undefined;\n select: unknown;\n snapshot: SelectionSnapshot;\n};\n\nconst noLeases: ReadonlySet<AnyResource> = new Set();\n\nfunction readSource<Value>(source: Readable<Value>, sources: Map<Readable<unknown>, unknown>): Value {\n if (!isReadable(source)) {\n throw new ContributionError('binding-invalid', 'Model selection read requires a Readable.');\n }\n\n if (sources.has(source)) return sources.get(source) as Value;\n\n // D364: a refusal here is the source's own failure and belongs to the render, because no lifetime ends in one.\n const value: unknown = source.getSnapshot();\n sources.set(source, value);\n\n return value as Value;\n}\n\nfunction readPassively(source: Readable<unknown>, sources: Map<Readable<unknown>, unknown>): unknown {\n if (sources.has(source)) return sources.get(source);\n\n const value: unknown = source.getSnapshot();\n sources.set(source, value);\n\n return value;\n}\n\n/** D359: the predicate that answers the question the type of the field asked, and the read that leases nothing. */\nfunction classifyResource(\n key: PropertyKey,\n value: unknown,\n resources: Map<PropertyKey, SelectedResource>,\n sources: Map<Readable<unknown>, unknown>,\n): void {\n if (!isResource(value)) return;\n\n const resource = value as AnyResource;\n const capability = (resource as { readonly pagination?: { readonly state: Readable<unknown> } }).pagination;\n\n resources.set(key, {\n pagination: capability === undefined ? undefined : readPassively(capability.state, sources),\n resource,\n state: readPassively(resource.state, sources),\n });\n}\n\nfunction selectedResources(resources: Map<PropertyKey, SelectedResource>): ReadonlyMap<PropertyKey, SelectedResource> {\n return resources.size === 0 ? noResources : resources;\n}\n\ntype CollectedSelection = Readonly<{\n resources: ReadonlyMap<PropertyKey, SelectedResource>;\n values: SelectionRecord;\n}>;\n\n/**\n * D359: recognition happens in the one pass that already walks the selection. `Resource` is a structural contract\n * (D199) and the type of a selected field promises `ResourceHook` for anything that satisfies it, so the predicate\n * this uses answers the same question the type asked. Reading the state here also puts it among `sources`, so its\n * publication invalidates the fast path of this reader exactly as a read source does.\n */\nfunction collectSelection(\n select: (context: SelectionContext) => object,\n sources: Map<Readable<unknown>, unknown>,\n): CollectedSelection {\n const read = <Value, Selected>(source: Readable<Value>, project?: (value: Value) => Selected) => {\n const value = readSource(source, sources);\n\n return project === undefined ? value : project(value);\n };\n const selected = select({ read });\n\n if (typeof selected !== 'object' || selected === null || Array.isArray(selected)) {\n throw new ContributionError('binding-invalid', 'Model selection requires a record.');\n }\n\n const entries: (readonly [PropertyKey, unknown])[] = [];\n const resources = new Map<PropertyKey, SelectedResource>();\n\n for (const key of Reflect.ownKeys(selected)) {\n if (!Object.prototype.propertyIsEnumerable.call(selected, key)) continue;\n\n const value = (selected as SelectionRecord)[key];\n\n entries.push([key, value]);\n classifyResource(key, value, resources, sources);\n }\n\n // `__proto__` is a key like any other here, and only a defined property keeps it one (D214).\n return { resources: selectedResources(resources), values: Object.fromEntries(entries) };\n}\n\n/**\n * D359: `values` keeps the Resource itself — a stable reference — so a selection that holds one is compared by\n * `Object.is` exactly as before. What moves is the state behind it, and that is compared here: without this the\n * selection would be «equal», the previous state would be kept, and a published Resource would reach no render.\n */\nfunction equalResources(\n previous: ReadonlyMap<PropertyKey, SelectedResource>,\n next: ReadonlyMap<PropertyKey, SelectedResource>,\n): boolean {\n if (previous.size !== next.size) return false;\n\n for (const [key, selected] of next) {\n const before = previous.get(key);\n\n if (\n before?.resource !== selected.resource ||\n !Object.is(before.state, selected.state) ||\n !Object.is(before.pagination, selected.pagination)\n ) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction equalSelection(\n previous: SelectionSnapshot,\n values: SelectionRecord,\n sources: ReadonlySet<Readable<unknown>>,\n resources: ReadonlyMap<PropertyKey, SelectedResource>,\n) {\n const keys = Reflect.ownKeys(values);\n const previousKeys = Reflect.ownKeys(previous.values);\n\n if (keys.length !== previousKeys.length || sources.size !== previous.sources.size) return false;\n\n for (const source of sources) if (!previous.sources.has(source)) return false;\n\n if (!equalResources(previous.resources, resources)) return false;\n\n return keys.every((key, index) => previousKeys[index] === key && Object.is(previous.values[key], values[key]));\n}\n\ntype ModelSelector<Model> = (model: Model, context: SelectionContext) => object;\ntype SelectionReaderRef<Model> = { readonly current: ModelSelector<Model> | undefined };\n\nconst emptySelectionSnapshot: SelectionSnapshot = {\n commands: {},\n leases: noLeases,\n resources: noResources,\n sources: new Set(),\n values: {},\n};\n\nfunction selectionCommands(values: SelectionRecord): Readonly<Record<PropertyKey, AnyCommand>> {\n return Object.fromEntries(\n Reflect.ownKeys(values)\n .filter(key => isCallTarget(values[key]))\n .map(key => [key, values[key]]),\n ) as Readonly<Record<PropertyKey, AnyCommand>>;\n}\n\nfunction selectionLeases(resources: ReadonlyMap<PropertyKey, SelectedResource>): ReadonlySet<AnyResource> {\n if (resources.size === 0) return noLeases;\n\n const leased = new Set<AnyResource>();\n\n for (const selected of resources.values()) leased.add(selected.resource);\n\n return leased;\n}\n\n/**\n * The fast path re-reads what it observed. D364: a source that stopped answers the same value it answered last, so\n * it compares equal and costs one read — there is no terminal source to record and step around any more.\n */\nfunction observedUnchanged(observed: Map<Readable<unknown>, unknown>): boolean {\n for (const [source, value] of observed) {\n if (!Object.is(source.getSnapshot(), value)) return false;\n }\n\n return true;\n}\n\n/** D363: the memory of one mount, empty until its first read and reset when the reader is built for another model. */\nfunction createSelectionMemory(): SelectionMemory {\n return { model: undefined, observed: undefined, select: undefined, snapshot: emptySelectionSnapshot };\n}\n\n/**\n * D214: one reader per model, not per render. The author's selector arrives through a ref, so an inline arrow does not\n * rebuild the reader; the fast path — nothing observed has moved — belongs to the selector that computed the snapshot,\n * so a new selector is always called again, its result compared field by field, and an equal selection keeps the\n * previous snapshot. A rejected or abandoned render still changes nothing: only a committed selection subscribes.\n *\n * D363: what the reader remembers lives in the mount's memory, so a reader React rebuilt — because it discarded the\n * memo — still knows what this selection read last.\n */\nfunction createSelectionSnapshot<Model>(\n model: Model,\n selectRef: SelectionReaderRef<Model>,\n memory: SelectionMemory,\n): () => SelectionSnapshot {\n if (memory.model !== model) {\n memory.model = model;\n memory.observed = undefined;\n memory.select = undefined;\n memory.snapshot = emptySelectionSnapshot;\n }\n\n return () => {\n const select = selectRef.current;\n\n if (select === undefined) return emptySelectionSnapshot;\n\n const observed = memory.observed;\n\n if (observed !== undefined && memory.select === select && observedUnchanged(observed)) {\n return memory.snapshot;\n }\n\n const nextObserved = new Map<Readable<unknown>, unknown>();\n const { resources, values } = collectSelection(context => select(model, context), nextObserved);\n const sources = new Set(nextObserved.keys());\n\n if (observed === undefined || !equalSelection(memory.snapshot, values, sources, resources)) {\n memory.snapshot = {\n commands: selectionCommands(values),\n leases: selectionLeases(resources),\n resources,\n sources,\n values,\n };\n }\n\n memory.observed = nextObserved;\n memory.select = select;\n\n return memory.snapshot;\n };\n}\n\nexport { createSelectionMemory, createSelectionSnapshot, emptySelectionSnapshot };\nexport type { ModelSelector, SelectedResource, SelectionContext, SelectionMemory, SelectionShape, SelectionSnapshot };\n","import type { Readable } from '@opetope/core';\nimport { createAggregateError } from '@opetope/core/internal';\n\nimport type { AnyResource } from './resource-command';\n\ntype Subscription = { dispose: (() => void) | undefined };\ntype Listener = Readonly<{ notify: () => void }>;\n\nconst noLeases: ReadonlySet<AnyResource> = new Set();\n\nfunction releaseSubscriptions(subscriptions: Iterable<Subscription>, failures: unknown[] = []): void {\n for (const subscription of subscriptions) {\n const dispose = subscription.dispose;\n subscription.dispose = undefined;\n\n try {\n dispose?.();\n } catch (error) {\n failures.push(error);\n }\n }\n\n if (failures.length === 1) throw failures[0];\n\n if (failures.length > 1) throw createAggregateError(failures, 'Model selection sources failed to release.');\n}\n\n/** A subscription set, not a scheduler: source notifications use React's snapshot consistency check. */\nclass ModelSelectionStore {\n private enabled = false;\n /** D359: one lease per Resource identity, diffed like a subscription, so an unchanged selection takes none. */\n private readonly leases = new Map<AnyResource, Subscription>();\n private readonly listeners = new Set<Listener>();\n private retained: ReadonlySet<AnyResource> = noLeases;\n private sources: ReadonlySet<Readable<unknown>> = new Set();\n private readonly subscriptions = new Map<Readable<unknown>, Subscription>();\n\n close(): void {\n this.enabled = false;\n this.sources = new Set();\n this.retained = noLeases;\n this.releaseAll();\n }\n\n commit(sources: ReadonlySet<Readable<unknown>>, retained: ReadonlySet<AnyResource> = noLeases): void {\n this.sources = sources;\n this.retained = retained;\n this.enabled = true;\n\n try {\n this.synchronize();\n } catch (error) {\n this.releaseAll([error]);\n }\n }\n\n subscribe = (notify: () => void): (() => void) => {\n const listener = { notify };\n this.listeners.add(listener);\n\n try {\n this.synchronize();\n } catch (error) {\n this.listeners.delete(listener);\n this.releaseAll([error]);\n }\n\n return () => {\n this.listeners.delete(listener);\n\n if (this.listeners.size === 0) this.releaseAll();\n };\n };\n\n private add(source: Readable<unknown>): void {\n const subscription: Subscription = { dispose: undefined };\n // Reserve before foreign code: a synchronous notification may replace or close this selection.\n this.subscriptions.set(source, subscription);\n const dispose = source.subscribe(this.notify);\n\n if (typeof dispose !== 'function') throw new TypeError('Model selection source must return a disposer.');\n\n if (this.subscriptions.get(source) === subscription) subscription.dispose = dispose;\n else dispose();\n }\n\n private notify = (): void => {\n for (const listener of [...this.listeners]) {\n if (this.listeners.has(listener)) listener.notify();\n }\n };\n\n private releaseAll(failures?: unknown[]): void {\n const held = [...this.subscriptions.values(), ...this.leases.values()];\n this.subscriptions.clear();\n this.leases.clear();\n releaseSubscriptions(held, failures);\n }\n\n private removeUnselected(): void {\n const removed: Subscription[] = [];\n\n for (const [source, subscription] of this.subscriptions) {\n if (this.sources.has(source)) continue;\n\n this.subscriptions.delete(source);\n removed.push(subscription);\n }\n\n for (const [resource, lease] of this.leases) {\n if (this.retained.has(resource)) continue;\n\n this.leases.delete(resource);\n removed.push(lease);\n }\n\n releaseSubscriptions(removed);\n }\n\n /**\n * D321: `retain()` is reentrant — it reaches `activate()`, the target subscription, the first read and the\n * publication that follows, all synchronously — and a listener woken there may rebuild or close this selection.\n * The record is reserved first, the lease is taken second, and it is kept only if the record is still this one.\n */\n private retain(resource: AnyResource): void {\n const lease: Subscription = { dispose: undefined };\n this.leases.set(resource, lease);\n const release = resource.acquire();\n\n if (typeof release !== 'function') throw new TypeError('Model selection resource must answer a release disposer.');\n\n if (this.leases.get(resource) === lease) lease.dispose = release;\n else release();\n }\n\n /** Foreign code inside a subscription or a lease may replace this selection, so both loops ask again. */\n private ready(): boolean {\n return this.enabled && this.listeners.size > 0;\n }\n\n private retainSelected(): void {\n for (const resource of this.retained) {\n if (!this.ready()) return;\n\n if (this.retained.has(resource) && !this.leases.has(resource)) this.retain(resource);\n }\n }\n\n private subscribeSources(): void {\n for (const source of this.sources) {\n if (!this.ready()) return;\n\n if (this.sources.has(source) && !this.subscriptions.has(source)) this.add(source);\n }\n }\n\n private synchronize(): void {\n this.removeUnselected();\n this.subscribeSources();\n this.retainSelected();\n }\n}\n\nexport { ModelSelectionStore };\n","import { useLayoutEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\nimport type { Command, ModelOf, PaginatedResource, Resource } from '@opetope/core';\n\nimport type { CommandHook } from './command-hook';\nimport { useSelectionHooks } from './selection-hooks';\nimport type { SelectionHooks } from './selection-hooks';\nimport { ContributionError } from './errors';\nimport { subscribeIdle } from './idle-subscription';\nimport type { AnyModel } from './model-binding';\nimport { createSelectionMemory, createSelectionSnapshot, emptySelectionSnapshot } from './model-selection-snapshot';\nimport type {\n ModelSelector,\n SelectionContext,\n SelectionMemory,\n SelectionShape,\n SelectionSnapshot,\n} from './model-selection-snapshot';\nimport { ModelSelectionStore } from './model-selection-store';\nimport { useFrame } from './mount-context';\nimport type { PaginatedResourceHook, ResourceHook, ResourceKey } from './resource-command';\n\n/**\n * D359: a field whose value is a Resource answers the hook `useResource` answers, and the mount leases it exactly as\n * `useResource` does. `read(resource.state)` is unchanged — it hands out the state and leases nothing — so naming one\n * Resource both ways registers it twice: the subscription and the lease are independent.\n */\ntype SelectedValue<Value> =\n Value extends Command<infer Input, infer Output>\n ? CommandHook<Input, Output>\n : Value extends PaginatedResource<infer Data, infer Key extends ResourceKey, infer Cursor extends ResourceKey>\n ? PaginatedResourceHook<Data, Key, Cursor>\n : Value extends Resource<infer Data, infer Key extends ResourceKey>\n ? ResourceHook<Data, Key>\n : Value;\ntype ModelSelection<Selected> = { readonly [Key in keyof Selected]: SelectedValue<Selected[Key]> };\n\nconst readEmptySnapshot = () => emptySelectionSnapshot;\n\nfunction useSelectionStore(selected: boolean): ModelSelectionStore | undefined {\n const storeRef = useRef<ModelSelectionStore | undefined>(undefined);\n\n return selected ? (storeRef.current ??= new ModelSelectionStore()) : undefined;\n}\n\n/**\n * Two branches of one key: the consumer this selection prepared — a bound command, or the verbs, the state and the\n * capability of a selected Resource — and otherwise the value the selector wrote. S6/D359: the Resource branch is\n * assembled on the record and not here, so a key whose state did not move hands back the very same object.\n */\nfunction mergeSelection(snapshot: SelectionSnapshot, hooks: SelectionHooks): unknown {\n return Object.fromEntries(\n Reflect.ownKeys(snapshot.values).map(key => [\n key,\n Object.prototype.hasOwnProperty.call(hooks, key) ? hooks[key] : snapshot.values[key],\n ]),\n );\n}\n\n/** D205: resolve only a granted model, explicitly read data, and reuse the ordinary per-key command consumers. */\nfunction useModel<Declaration extends AnyModel>(declaration: Declaration): ModelOf<Declaration>;\nfunction useModel<Declaration extends AnyModel, const Selected extends object>(\n declaration: Declaration,\n select: (model: ModelOf<Declaration>, context: SelectionContext) => Selected & SelectionShape<Selected>,\n): ModelSelection<Selected>;\nfunction useModel<Declaration extends AnyModel>(\n declaration: Declaration,\n select?: (model: ModelOf<Declaration>, context: SelectionContext) => object,\n): unknown {\n const frame = useFrame();\n\n if (!frame.models.has(declaration)) {\n throw new ContributionError('missing', `Model ${declaration.id} is not granted to this contribution.`);\n }\n\n const model = frame.models.get(declaration) as ModelOf<Declaration>;\n const store = useSelectionStore(select !== undefined);\n // D214: the selector of this render, read by a reader that belongs to the model. Writing the latest callback into\n // a ref is what `useSyncExternalStoreWithSelector` does: the reader only reads it, and never writes to it.\n const selectRef = useRef<ModelSelector<ModelOf<Declaration>> | undefined>(undefined);\n selectRef.current = select;\n const selected = select !== undefined;\n // D363: the memory of this selection belongs to the mount, so a reader React rebuilt keeps what it read last.\n const memoryRef = useRef<SelectionMemory | undefined>(undefined);\n const memory = (memoryRef.current ??= createSelectionMemory());\n const getSnapshot = useMemo(\n () => (selected ? createSelectionSnapshot(model, selectRef, memory) : readEmptySnapshot),\n [memory, model, selected],\n );\n const snapshot = useSyncExternalStore(store?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const hooks = useSelectionHooks(snapshot.commands, snapshot.resources);\n useLayoutEffect(() => () => store?.close(), [store]);\n // D359: a lease is taken in commit and never in a render, and the store diffs the set by identity.\n useLayoutEffect(() => store?.commit(snapshot.sources, snapshot.leases), [snapshot, store]);\n\n if (select === undefined) return model;\n\n return mergeSelection(snapshot, hooks);\n}\n\nexport { useModel };\n","import type { FunctionComponent } from 'react';\n\nimport type { ModelIdentity } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\n\ntype AnyModel = ModelIdentity;\n\n/**\n * The marker a component uses to state which UI models of its contribution it reads. `slot` checks by type that the\n * contribution grants every model of this list — it may grant more, because the mount serves its whole instance.\n *\n * The runtime authority is the mount frame, not this marker: `useModel` resolves against what the mount was given\n * and refuses anything else. The lint rule `opetope/require-declared-models` checks visible contribution sites and\n * each reader of a per-mount model within the same module. Imported implementations and dynamic declaration lists\n * remain unknown to this syntactic check (D85, D158, D250).\n */\ntype ComponentRequiringModels<Props, Models extends readonly AnyModel[]> = FunctionComponent<Props> & {\n readonly requires: Models;\n};\n\nfunction requiresModels<const Models extends readonly AnyModel[]>(\n models: Models,\n): <Props>(component: FunctionComponent<Props>) => ComponentRequiringModels<Props, Models> {\n const declared = Object.freeze([...models]) as unknown as Models;\n\n return <Props>(component: FunctionComponent<Props>): ComponentRequiringModels<Props, Models> => {\n const existing = (component as { readonly requires?: readonly AnyModel[] }).requires;\n\n if (existing !== undefined) {\n throw new ContributionError('duplicate', 'Component already declares the models it requires.');\n }\n\n Object.defineProperty(component, 'requires', { enumerable: false, value: declared });\n\n return component as ComponentRequiringModels<Props, Models>;\n };\n}\n\nexport { requiresModels };\n","import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react';\n\nimport type { PaginatedResource, PaginationState, Readable, Resource } from '@opetope/core';\nimport type { ResourceKey } from '@opetope/core/internal';\n\nimport { useCommandStatus } from './command-hook';\nimport { CommandHookController } from './command-hook-controller';\nimport { createResourceInvokers, paginationOf } from './resource-command';\nimport type { AnyResource, PaginatedResourceHook, ResourceHook, ResourcePaginationHook } from './resource-command';\n\ntype ResourceSlots = Readonly<{\n loadNext: CommandHookController<void, unknown>;\n refresh: CommandHookController<void, unknown>;\n reset: CommandHookController<void, unknown>;\n retry: CommandHookController<void, unknown>;\n}>;\n\n/**\n * D359: one set of controllers per Resource identity, exactly as `useCommandSlot` keeps one per invoker. The slots are\n * what the set of invokers is kept alive by, so one map answers for all four. Slots are inert until commit, so an\n * interrupted render neither admits work nor resets the status of the committed one.\n */\nfunction useResourceSlots(resource: AnyResource): ResourceSlots {\n const [cache] = useState(() => new WeakMap<object, ResourceSlots>());\n const existing = cache.get(resource);\n\n if (existing !== undefined) return existing;\n\n const invokers = createResourceInvokers(resource);\n const slots: ResourceSlots = {\n loadNext: new CommandHookController<void, unknown>(invokers.loadNext),\n refresh: new CommandHookController<void, unknown>(invokers.refresh),\n reset: new CommandHookController<void, unknown>(invokers.reset),\n retry: new CommandHookController<void, unknown>(invokers.retry),\n };\n cache.set(resource, slots);\n\n return slots;\n}\n\nconst unavailablePagination: PaginationState<ResourceKey> = Object.freeze({\n kind: 'unavailable',\n reason: 'resource-not-ready',\n});\nconst noPaginationSource: Readable<PaginationState<ResourceKey>> = Object.freeze({\n getSnapshot: () => unavailablePagination,\n subscribe: () => () => undefined,\n});\n\n/**\n * D359: the state of a Resource, the lease that keeps it open for as long as this component is mounted, and its verbs\n * as commands. The number of React hooks here does not depend on the Resource: a declaration without `pagination`\n * subscribes to a constant source and keeps a `loadNext` that answers `skipped`.\n */\nfunction useResource<Data, Key extends ResourceKey, Cursor extends ResourceKey>(\n resource: PaginatedResource<Data, Key, Cursor>,\n): PaginatedResourceHook<Data, Key, Cursor>;\nfunction useResource<Data, Key extends ResourceKey>(resource: Resource<Data, Key>): ResourceHook<Data, Key>;\nfunction useResource<Data, Key extends ResourceKey>(resource: Resource<Data, Key>): ResourceHook<Data, Key> {\n const handle = resource as unknown as AnyResource;\n const capability = paginationOf(handle);\n const subscribe = useCallback((listener: () => void) => resource.state.subscribe(listener), [resource]);\n /*\n * D364: `resource.state` is a total `Readable` for the life of the object this hook holds — a retired one answers\n * `idle('retired')` — so the read needs no memory of its own. What it can still refuse is a producer failure, a\n * broken `nextCursor` among them (D356), and that belongs to the render (G13).\n */\n const getState = useMemo(() => () => resource.state.getSnapshot(), [resource]);\n const state = useSyncExternalStore(subscribe, getState, getState);\n const paginationSource: Readable<PaginationState<ResourceKey>> =\n (capability?.state as Readable<PaginationState<ResourceKey>> | undefined) ?? noPaginationSource;\n const subscribePagination = useCallback(\n (listener: () => void) => paginationSource.subscribe(listener),\n [paginationSource],\n );\n const getPaginationState = useMemo(() => () => paginationSource.getSnapshot(), [paginationSource]);\n const paginationState = useSyncExternalStore(subscribePagination, getPaginationState, getPaginationState);\n\n // D349: the lease is taken in a commit effect, so an interrupted render opens nothing.\n useEffect(() => resource.acquire(), [resource]);\n\n const slots = useResourceSlots(handle);\n const refresh = useCommandStatus(slots.refresh);\n const retry = useCommandStatus(slots.retry);\n const reset = useCommandStatus(slots.reset);\n const loadNext = useCommandStatus(slots.loadNext);\n const pagination = useMemo(\n () =>\n capability === undefined\n ? undefined\n : ({ loadNext, state: paginationState } as unknown as ResourcePaginationHook<ResourceKey>),\n [capability, loadNext, paginationState],\n );\n\n return useMemo(\n () => ({ pagination, refresh, reset, retry, state }) as ResourceHook<Data, Key>,\n [pagination, refresh, reset, retry, state],\n );\n}\n\nexport { useResource };\n","import type { ComponentType, ReactNode } from 'react';\nimport { createElement, Fragment } from 'react';\n\nimport type { DeclarationId } from '@opetope/core';\nimport { declarationId } from '@opetope/core';\nimport type { ContributionTarget } from '@opetope/core/internal';\nimport { createContributionTarget } from '@opetope/core/internal';\nimport type { ContributionModel } from '@opetope/runtime/internal';\n\nimport { contributionAuthority, ContributionMount } from './contribution-frame';\nimport { useReadable } from './readable-hooks';\n\ntype SlotProperties = object | undefined;\ntype EmptySlotProperties = Readonly<Record<string, never>>;\ntype SlotComponentProperties<Props extends SlotProperties> = [Props] extends [undefined]\n ? EmptySlotProperties\n : Extract<Props, object>;\n\n/**\n * What a contribution publishes: the component, the optional props adapter and the optional UI models the mount\n * creates. Without an adapter the component takes the slot props; with one it takes exactly the adapter result (D85).\n */\ntype SlotContribution<Props extends SlotProperties = undefined> = {\n readonly Component: ComponentType<never>;\n readonly models?: readonly ContributionModel[];\n readonly props?: (slotProps: SlotComponentProperties<Props>) => object;\n};\n\ntype SlotTarget<Props extends SlotProperties = undefined> = ContributionTarget<SlotContribution<Props>>;\n\ntype SwitchSlotTarget<Route extends string, Props extends SlotProperties = undefined> = ((\n route: Route,\n) => SlotTarget<Props>) & {\n readonly id: DeclarationId;\n};\n\ntype SlotRenderProps<Props extends SlotProperties> = [Props] extends [undefined]\n ? {\n readonly props?: never;\n readonly target: SlotTarget<Props>;\n }\n : {\n readonly props: SlotComponentProperties<Props>;\n readonly target: SlotTarget<Props>;\n };\n\nconst emptySlotProperties: EmptySlotProperties = Object.freeze({});\n\n/** D280: a slot is a name, so it takes its id as the first argument. */\nfunction defineSlot<Props extends SlotProperties = undefined>(id: string): SlotTarget<Props> {\n return createContributionTarget<SlotContribution<Props>>(id, 'slot');\n}\n\nfunction defineSwitchSlot<Route extends string, Props extends SlotProperties = undefined>(\n id: string,\n): SwitchSlotTarget<Route, Props> {\n const declared = declarationId(id);\n const targets = new Map<Route, SlotTarget<Props>>();\n\n const switchSlot = (route: Route): SlotTarget<Props> => {\n const existing = targets.get(route);\n\n if (existing !== undefined) return existing;\n\n const target = defineSlot<Props>(`${declared}/${route}`);\n targets.set(route, target);\n\n return target;\n };\n\n Object.defineProperty(switchSlot, 'id', { enumerable: true, value: declared });\n\n return Object.freeze(switchSlot) as SwitchSlotTarget<Route, Props>;\n}\n\n/**\n * Every contribution renders inside its own mount: the models its generation owns plus the UI models it declared,\n * created on mount and closed on unmount (D70, D85).\n */\nfunction Slot<Props extends SlotProperties>(options: SlotRenderProps<Props>): ReactNode {\n const entries = useReadable(options.target.entries);\n const slotProps: object = 'props' in options ? (options.props ?? emptySlotProperties) : emptySlotProperties;\n\n if (entries.length === 0) return null;\n\n return entries.map(entry =>\n createElement(\n Fragment,\n { key: entry.id },\n createElement(ContributionMount, {\n authority: contributionAuthority(entry),\n contribution: entry.value,\n contributionId: entry.id,\n slotProps,\n targetId: options.target.id,\n }),\n ),\n );\n}\n\nexport { defineSlot, defineSwitchSlot, Slot };\nexport type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget, SwitchSlotTarget };\n"],"names":["failureOutcome","options","error","isCancellation","notification","_a","reportDetachedError","callbackError","runOptions","onSuccess","signal","requestOf","input","resolve","promise","settle","request","outcome","CommandHookController","ContributionError","aborted","invoker","notify","CommandError","compatibleAbortReason","invocation","value","emptyStatus","settledOutcome","unchangedState","current","slot","inFlight","settled","nextState","previous","useCommandSlot","slots","useState","useCommandStatus","currentSlot","hookState","setHookState","useInsertionEffect","useLayoutEffect","run","useMemo","useCommand","command","record","useFrame","idleRelease","subscribeIdle","idleRevision","emptyHooks","isResourceEntry","entry","createEntry","unchangedHook","next","updateEntry","selectEntry","resourceHook","selected","createResourceEntry","invokers","parts","unchangedResourceCommands","hook","unchangedResourceState","unchangedResourceHook","selectResourceEntry","commandEntries","detachEntry","hookOf","CommandGroup","key","listener","commands","resources","frame","resource","resourceInvokers","useCommandGroup","groupRef","useRef","useSelectionHooks","group","getSnapshot","useSyncExternalStore","noResources","noLeases","readSource","source","sources","isReadable","readPassively","classifyResource","isResource","capability","selectedResources","collectSelection","select","project","entries","equalResources","before","equalSelection","values","keys","previousKeys","index","emptySelectionSnapshot","selectionCommands","isCallTarget","selectionLeases","leased","observedUnchanged","observed","createSelectionMemory","createSelectionSnapshot","model","selectRef","memory","nextObserved","context","releaseSubscriptions","subscriptions","failures","subscription","dispose","createAggregateError","ModelSelectionStore","retained","held","removed","lease","release","readEmptySnapshot","useSelectionStore","storeRef","mergeSelection","snapshot","hooks","useModel","declaration","store","memoryRef","requiresModels","models","declared","component","useResourceSlots","cache","existing","createResourceInvokers","unavailablePagination","noPaginationSource","useResource","handle","paginationOf","subscribe","useCallback","getState","state","paginationSource","subscribePagination","getPaginationState","paginationState","useEffect","refresh","retry","reset","loadNext","pagination","emptySlotProperties","defineSlot","id","createContributionTarget","defineSwitchSlot","declarationId","targets","switchSlot","route","target","Slot","useReadable","slotProps","createElement","Fragment","ContributionMount","contributionAuthority"],"mappings":"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"}
|
package/dist/model-binding.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { Command, ModelOf } from '@opetope/core';
|
|
2
|
+
import type { ExactKeys, ModelIdentity } from '@opetope/core/internal';
|
|
3
3
|
declare const modelBindingBrand: unique symbol;
|
|
4
4
|
declare const modelBindingIdentityBrand: unique symbol;
|
|
5
5
|
type AnyModel = ModelIdentity;
|
|
6
|
-
type AnyCommand =
|
|
6
|
+
type AnyCommand = Command<never, unknown>;
|
|
7
7
|
interface ModelBindingIdentity {
|
|
8
8
|
readonly [modelBindingIdentityBrand]: true;
|
|
9
9
|
}
|
|
@@ -15,7 +15,7 @@ type ModelBindingMetadata = {
|
|
|
15
15
|
readonly declaration: AnyModel;
|
|
16
16
|
readonly model: object;
|
|
17
17
|
};
|
|
18
|
-
declare function bindModel<Declaration extends AnyModel, const
|
|
18
|
+
declare function bindModel<Declaration extends AnyModel, const Keys extends PropertyKey = never>(declaration: Declaration, model: ExactKeys<Keys, ModelOf<Declaration>> & ModelOf<Declaration>): ModelBinding<Declaration>;
|
|
19
19
|
declare function getModelBindingMetadata(binding: ModelBindingIdentity): ModelBindingMetadata;
|
|
20
20
|
export { bindModel, getModelBindingMetadata };
|
|
21
21
|
export type { AnyCommand, AnyModel, ModelBindingIdentity };
|
package/dist/model-hook.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Command, ModelOf, PaginatedResource, Resource } from '@opetope/core';
|
|
2
2
|
import type { CommandHook } from './command-hook.js';
|
|
3
3
|
import type { AnyModel } from './model-binding.js';
|
|
4
4
|
import type { SelectionContext, SelectionShape } from './model-selection-snapshot.js';
|
|
@@ -8,7 +8,7 @@ import type { PaginatedResourceHook, ResourceHook, ResourceKey } from './resourc
|
|
|
8
8
|
* `useResource` does. `read(resource.state)` is unchanged — it hands out the state and leases nothing — so naming one
|
|
9
9
|
* Resource both ways registers it twice: the subscription and the lease are independent.
|
|
10
10
|
*/
|
|
11
|
-
type SelectedValue<Value> = Value extends
|
|
11
|
+
type SelectedValue<Value> = Value extends Command<infer Input, infer Output> ? CommandHook<Input, Output> : Value extends PaginatedResource<infer Data, infer Key extends ResourceKey, infer Cursor extends ResourceKey> ? PaginatedResourceHook<Data, Key, Cursor> : Value extends Resource<infer Data, infer Key extends ResourceKey> ? ResourceHook<Data, Key> : Value;
|
|
12
12
|
type ModelSelection<Selected> = {
|
|
13
13
|
readonly [Key in keyof Selected]: SelectedValue<Selected[Key]>;
|
|
14
14
|
};
|
|
@@ -8,7 +8,14 @@ import type { PaginationOutcome, PaginationState, Resource, ResourceOperationOut
|
|
|
8
8
|
import type { ResourceKey } from '@opetope/core/internal';
|
|
9
9
|
import type { CommandHook } from './command-hook.js';
|
|
10
10
|
import type { CommandInvoker } from './command-hook-controller.js';
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* D439: the widest Resource, and the name says so. `Resource<never, ResourceKey>` said the opposite in both
|
|
13
|
+
* arguments — `never` is the narrowest `Data` a reader can be handed and a named `Key` *requires* `state.key`, so
|
|
14
|
+
* hardly any member of the family fitted it. Nothing was refused, because every value reaches this name through a
|
|
15
|
+
* cast, which is exactly why the name could drift. `ResourceKeyField<never>` requires no field, so
|
|
16
|
+
* `Resource<unknown, never>` is the one form every Resource satisfies — keyed or not, paginated or not.
|
|
17
|
+
*/
|
|
18
|
+
type AnyResource = Resource<unknown, never>;
|
|
12
19
|
/**
|
|
13
20
|
* D349, D359: a Resource is a structural contract a host may implement with an ordinary object, so the question the
|
|
14
21
|
* type of a selected field asked is answered by the runtime that owns it — the registry of the materializations it
|
|
@@ -25,14 +32,14 @@ type ResourcePaginationHook<Cursor extends ResourceKey> = Readonly<{
|
|
|
25
32
|
/**
|
|
26
33
|
* D359: what `useResource` answers, and what a field of a `useModel` selection whose value is a Resource answers.
|
|
27
34
|
* The verbs never reject (a Resource answers an outcome), so the ordinary path of `outcome` is
|
|
28
|
-
* `{
|
|
35
|
+
* `{ kind: 'ok', value }`; `failed` stays in the type because the controller turns a synchronous throw of an
|
|
29
36
|
* invoker into it, and the wrapper below carries its own test that it never throws synchronously.
|
|
30
37
|
*/
|
|
31
38
|
type ResourceHook<Data, Key extends ResourceKey = never> = Readonly<{
|
|
32
|
-
invalidate: CommandHook<void, ResourceOperationOutcome>;
|
|
33
39
|
/** Present when the declaration carried `pagination`; `undefined` for every other Resource. */
|
|
34
40
|
pagination: ResourcePaginationHook<ResourceKey> | undefined;
|
|
35
41
|
refresh: CommandHook<void, ResourceOperationOutcome>;
|
|
42
|
+
reset: CommandHook<void, ResourceOperationOutcome>;
|
|
36
43
|
retry: CommandHook<void, ResourceOperationOutcome>;
|
|
37
44
|
state: ResourceState<Data, Key>;
|
|
38
45
|
}>;
|
|
@@ -40,9 +47,9 @@ type PaginatedResourceHook<Data, Key extends ResourceKey, Cursor extends Resourc
|
|
|
40
47
|
pagination: ResourcePaginationHook<Cursor>;
|
|
41
48
|
}>;
|
|
42
49
|
type ResourceInvokers = Readonly<{
|
|
43
|
-
invalidate: CommandInvoker;
|
|
44
50
|
loadNext: CommandInvoker;
|
|
45
51
|
refresh: CommandInvoker;
|
|
52
|
+
reset: CommandInvoker;
|
|
46
53
|
retry: CommandInvoker;
|
|
47
54
|
}>;
|
|
48
55
|
declare function paginationOf(resource: AnyResource): ResourcePaginationHook<ResourceKey> | undefined;
|
package/dist/scenario-slot.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FC } from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Command } from '@opetope/core';
|
|
3
3
|
import type { ModelFixture } from './contribution-frame.js';
|
|
4
4
|
import type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget } from './slot.js';
|
|
5
5
|
/**
|
|
@@ -22,10 +22,13 @@ interface SlotTestHarness<Props extends SlotProperties> {
|
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* A command a fixture publishes: the test writes the body, the harness gives it the identity a model requires. The
|
|
25
|
-
*
|
|
25
|
+
* Command itself is the core fixture (D300); what this word adds, and what keeps it in the React package, is the
|
|
26
26
|
* contribution binding a mount reads (D285).
|
|
27
27
|
*/
|
|
28
|
-
declare function command<Input, Output>(run: (
|
|
28
|
+
declare function command<Input = void, Output = void>(run: (context: {
|
|
29
|
+
readonly input: Input;
|
|
30
|
+
readonly signal: AbortSignal;
|
|
31
|
+
}) => Output | PromiseLike<Output>): Command<Input, Output>;
|
|
29
32
|
/**
|
|
30
33
|
* Mounts the contributions of one slot the way a host does, with optional model fixtures for a component test. D19 and D85: the same
|
|
31
34
|
* ContributionMount is shared by component tests and the application scenario harness (D206).
|
package/dist/scenario-types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FC } from 'react';
|
|
2
|
-
import type { Application } from '@opetope/runtime';
|
|
3
|
-
import type {
|
|
2
|
+
import type { Application, ApplicationConditionBinding } from '@opetope/runtime';
|
|
3
|
+
import type { OpenApplicationOptions, RuntimeGraphSnapshot } from '@opetope/runtime/internal';
|
|
4
4
|
import type { SlotComponentProperties, SlotProperties, SlotTarget } from './slot.js';
|
|
5
5
|
type ScenarioFeatures = Application extends Application<infer Features> ? Features : never;
|
|
6
6
|
interface ScenarioHostMount {
|
package/dist/scenario.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { Application } from '@opetope/runtime';
|
|
2
|
-
import type { ApplicationConditionBinding } from '@opetope/runtime/internal';
|
|
1
|
+
import type { Application, ApplicationConditionBinding } from '@opetope/runtime';
|
|
3
2
|
import type { Scenario, ScenarioFeatures, ScenarioHostMount, ScenarioOptions } from './scenario-types.js';
|
|
4
3
|
/** D206: one real application, its existing inspection session and renderer-owned Slot ingress. */
|
|
5
4
|
declare function createScenario<const Features extends ScenarioFeatures, Mounted extends ScenarioHostMount, const Bindings extends readonly ApplicationConditionBinding[] = readonly []>(application: Application<Features>, options: ScenarioOptions<NoInfer<Features>, Mounted, Bindings>): Scenario<Mounted>;
|