@storylet-studio/play-helpers 0.13.0 → 0.15.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/README.md +3 -2
- package/dist/index.cjs +21 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -48
- package/dist/index.d.ts +57 -48
- package/dist/index.js +22 -8
- package/dist/index.js.map +1 -1
- package/dist/storyletengine.min.js +4 -4
- package/dist/storyletengine.min.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var StoryletEngine=(()=>{var Ne=Object.defineProperty;var Tn=Object.getOwnPropertyDescriptor;var Dn=Object.getOwnPropertyNames;var Nn=Object.prototype.hasOwnProperty;var Vn=(r,e)=>{for(var t in e)Ne(r,t,{get:e[t],enumerable:!0})},Hn=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Dn(e))!Nn.call(r,s)&&s!==t&&Ne(r,s,{get:()=>e[s],enumerable:!(n=Tn(e,s))||n.enumerable});return r};var jn=r=>Hn(Ne({},"__esModule",{value:!0}),r);var Hr={};Vn(Hr,{DURABLE_SCHEMA:()=>fe,Engine:()=>Ce,Flow:()=>ge,StoryletError:()=>w,applyLiveBundle:()=>In,boardFrame:()=>ft,compareStages:()=>_e,createBundleInspector:()=>On,createKernelStateLogger:()=>de,createLiveLink:()=>Ln,createPropertyInspector:()=>Rn,createStateLogger:()=>hn,createWorldContainer:()=>An,describeBundle:()=>Be,deserializeState:()=>Re,diffState:()=>ae,effectiveFields:()=>dn,ensureInspectorStyle:()=>me,extensionDecls:()=>ln,extensionSettings:()=>cn,extensionsOf:()=>pn,fieldDecls:()=>on,fieldValue:()=>st,formatLogEntry:()=>pt,formatPropertySummary:()=>ut,formatScopeLabel:()=>gt,loadState:()=>it,makePrng:()=>H,nextStage:()=>Ge,outcomeFieldDecls:()=>an,saveState:()=>ot,serializeState:()=>Me,shuffleInPlace:()=>_,snapshotState:()=>un,stageIndex:()=>Fe});function ne(r,e){return r!==void 0&&Object.prototype.hasOwnProperty.call(r,e)?r[e]:void 0}function j(r){switch(r[0]){case"b":return{kind:"bool",value:r[1]};case"n":return{kind:"number",value:r[1]};case"s":return{kind:"string",value:r[1]};case"sv":return{kind:"scopedvar",scope:r[1],name:r[2]};case"u":return{kind:"unary",op:r[1],operand:j(r[2])};case"bin":return{kind:"binary",op:r[1],left:j(r[2]),right:j(r[3])};case"call":{let e=r.slice(2).map(j);return{kind:"call",name:r[1],args:e}}case"fd":return{kind:"flagdelta",sign:r[1],name:r[2]}}}var S=class extends Error{constructor(e){super(e),this.name="EvalError"}};function we(r,e,t){let n=new Map(t.scopes.map(o=>[o.token,o.missing??"false"])),s=o=>{switch(o.kind){case"bool":return o.value;case"number":return o.value;case"string":return o.value;case"scopedvar":{let i=ne(e.scopes,o.scope);if(i===void 0)return!1;let a=typeof i.get=="function"?i.get(o.name):ne(i,o.name);if(a===void 0){if(n.get(o.scope)==="throw")throw new S(`@${o.scope}.${o.name} is not declared on the current ${o.scope}.`);return!1}return a}case"call":{if(o.name==="advance"&&!ne(t.functions,o.name)){let a=o.args[0];if(o.args.length!==1||a===void 0)throw new S(`advance() takes exactly 1 argument, got ${o.args.length}`);let d=Ve(a,e);if(d===void 0)throw new S("advance() needs a quality reference (@scope.name of a quality property)");let l=N(s(a),d,"advance");return d[Math.min(l+1,d.length-1)]}let i=ne(t.functions,o.name);if(!i)throw new S(`unknown function '${o.name}'`);return i.eval(o.args,{evaluate:s,ctx:e})}case"flagdelta":throw new S("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(o.op==="not"){let a=s(o.operand);if(typeof a!="boolean")throw new S(`'not' requires a boolean operand, got ${typeof a}`);return!a}let i=s(o.operand);if(typeof i!="number")throw new S(`unary '-' requires a numeric operand, got ${typeof i}`);return-i}case"binary":{if(o.op==="and"){let p=s(o.left);if(typeof p!="boolean")throw new S(`'and' requires boolean operands, left is ${typeof p}`);if(!p)return!1;let g=s(o.right);if(typeof g!="boolean")throw new S(`'and' requires boolean operands, right is ${typeof g}`);return g}if(o.op==="or"){let p=s(o.left);if(typeof p!="boolean")throw new S(`'or' requires boolean operands, left is ${typeof p}`);if(p)return!0;let g=s(o.right);if(typeof g!="boolean")throw new S(`'or' requires boolean operands, right is ${typeof g}`);return g}let i=s(o.left),a=s(o.right),d=Ve(o.left,e),l=Ve(o.right,e),c=d??l;if(c!==void 0){if(d&&l&&!Fn(d,l)&&(o.op===">"||o.op===">="||o.op==="<"||o.op==="<="))throw new S(`'${o.op}' compares two different qualities, whose stage orders are unrelated`);switch(o.op){case">":return N(i,c,">")>N(a,c,">");case">=":return N(i,c,">=")>=N(a,c,">=");case"<":return N(i,c,"<")<N(a,c,"<");case"<=":return N(i,c,"<=")<=N(a,c,"<=");case"+":case"-":case"*":case"/":throw new S(`'${o.op}' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(o.op){case"==":return kt(i,a);case"!=":return!kt(i,a);case">":return F(i,a,">"),i>a;case">=":return F(i,a,">="),i>=a;case"<":return F(i,a,"<"),i<a;case"<=":return F(i,a,"<="),i<=a;case"+":if(typeof i=="number"&&typeof a=="number"||typeof i=="string"&&typeof a=="string")return i+a;throw new S(`'+' requires two numbers or two strings, got ${typeof i} and ${typeof a}`);case"-":return F(i,a,"-"),i-a;case"*":return F(i,a,"*"),i*a;case"/":if(F(i,a,"/"),a===0)throw new S("division by zero");return i/a}}}};return s(r)}function kt(r,e){if(Array.isArray(r)||Array.isArray(e)){if(!Array.isArray(r)||!Array.isArray(e)||r.length!==e.length)return!1;let t=[...r].sort(),n=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==n[s])return!1;return!0}return r===e}function F(r,e,t){if(typeof r!="number"||typeof e!="number")throw new S(`'${t}' requires numeric operands, got ${typeof r} and ${typeof e}`)}function Ve(r,e){if(!(r.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(r.scope,r.name)}function N(r,e,t){if(typeof r!="string")throw new S(`'${t}' on a quality compares stages, got ${typeof r}`);let n=e.indexOf(r);if(n<0)throw new S(`"${r}" is not a stage of this quality (stages: ${e.join(", ")})`);return n}var Fn=(r,e)=>r.length===e.length&&r.every((t,n)=>t===e[n]);function Pt(r){if(Number.isNaN(r)||!Number.isFinite(r))return 0;let e=Math.trunc(r)%4294967296;return e<0?e+4294967296:e}function H(r){let e=Pt(r);return{next(){e=e+1831565813>>>0;let t=Math.imul(e^e>>>15,1|e);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296},state(){return e}}}function _(r,e){for(let t=r.length-1;t>0;t--){let n=Math.floor(e.next()*(t+1));[r[t],r[n]]=[r[n],r[t]]}}var je=r=>Array.isArray(r)?r:r.stages??[],_n=r=>Array.isArray(r)?"flags":r==null?"nothing":typeof r;function Fe(r,e){return typeof e=="string"?je(r).indexOf(e):-1}function He(r,e){if(typeof e!="string")throw new S(`a stage is a name, got ${_n(e)}`);let t=r.indexOf(e);if(t<0)throw new S(`"${e}" is not a stage of this quality (stages: ${r.join(", ")})`);return t}function _e(r,e,t){let n=je(r),s=He(n,e)-He(n,t);return s<0?-1:s>0?1:0}function Ge(r,e){let t=je(r);return t[Math.min(He(t,e)+1,t.length-1)]}function U(r,e,t){switch(r.type){case"boolean":return typeof e=="boolean";case"number":return typeof e=="number";case"string":case"text":case"multiline":return typeof e=="string";case"enum":return typeof e!="string"?!1:e===""&&t?.noneChosen?!0:!Array.isArray(r.values)||r.values.includes(e);case"flags":return Array.isArray(e)&&e.every(n=>typeof n=="string"&&(!Array.isArray(r.values)||r.values.includes(n)));case"quality":return typeof e=="string"&&(!Array.isArray(r.stages)||r.stages.includes(e));default:return!0}}var Gn={name:"check_flags",count:r=>Math.max(1,r.args.length-1)},qn=[Gn];function Ct(r,e,t){let n=t?.countingCalls??qn;return ve(r,t?.want??!0,e,n)}function ve(r,e,t,n){if(r.kind==="binary"&&(r.op==="and"||r.op==="or")){let s=ve(r.left,e,t,n),o=ve(r.right,e,t,n);return r.op==="and"===e?s>0&&o>0?s+o:0:Math.max(s,o)}if(r.kind==="unary"&&r.op==="not")return ve(r.operand,!e,t,n);if(r.kind==="call"){let s=n.find(o=>o.name===r.name);if(s){let o=s.count(r),i=t(r);return e?i?o:0:i?0:1}}return t(r)===e?1:0}var $t=[{token:"patter",engine:"Patterplay",means:"Patter's shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine's shared @story properties"}];var xe=9999,re=r=>r.ctx.host??{},K=(r,e,t,n)=>{let s=t.evaluate(e[n]);if(typeof s!="string"||s==="")throw new S(`${r}() argument ${n+1} must be a non-empty string`);return s},Bt=(r,e,t)=>{if(e.length===0)throw new S(`${r}() requires at least one argument (the flags property)`);let n=t.evaluate(e[0]);if(Array.isArray(n))return n;if(n===!1)return[];throw new S(`${r}() first argument must be a flags property`)},Mt=["story","world","box","deck","hand"],zn=$t.map(r=>r.token).filter(r=>!Mt.includes(r)),qe={scopes:[...Mt.map(r=>({token:r,missing:"throw"})),...zn.map(r=>({token:r,missing:"throw"}))],defaultScope:"story",functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(r,e){if(r.length!==2)throw new S("random(a, b) requires exactly 2 arguments");let t=re(e).nextRandom;if(!t)throw new S("random() called without a PRNG in context");let n=e.evaluate(r[0]),s=e.evaluate(r[1]);if(typeof n!="number"||typeof s!="number")throw new S("random(a, b) arguments must be numbers");if(!Number.isInteger(n)||!Number.isInteger(s))throw new S("random(a, b) arguments must be integers");let o=Math.min(n,s),i=Math.max(n,s);return Math.floor(t()*(i-o+1))+o}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,eval(r,e){let t=Bt("check_flags",r,e);for(let n=1;n<r.length;n++){let s=r[n];if(s.kind!=="flagdelta")throw new S("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,eval(r,e){let t=[...Bt("set_flags",r,e)];for(let n=1;n<r.length;n++){let s=r[n];if(s.kind!=="flagdelta")throw new S("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let o=t.indexOf(s.name);o>=0&&t.splice(o,1)}}return t.sort()}},count_played:{minArgs:1,maxArgs:1,returnType:"number",eval(r,e){let t=K("count_played",r,e,0),n=re(e).countPlayed;if(!n)throw new S("count_played() called without a play log in context");return n(t)}},turns_since_played:{minArgs:1,maxArgs:1,returnType:"number",eval(r,e){let t=K("turns_since_played",r,e,0),n=re(e).turnsSincePlayed;if(!n)throw new S("turns_since_played() called without a play log in context");return n(t)}},count_played_in:{minArgs:2,maxArgs:2,returnType:"number",eval(r,e){let t=K("count_played_in",r,e,0),n=K("count_played_in",r,e,1),s=re(e).countPlayedIn;if(!s)throw new S("count_played_in() called without a play log in context");return s(t,n)}},turns_since_played_in:{minArgs:2,maxArgs:2,returnType:"number",eval(r,e){let t=K("turns_since_played_in",r,e,0),n=K("turns_since_played_in",r,e,1),s=re(e).turnsSincePlayedIn;if(!s)throw new S("turns_since_played_in() called without a play log in context");return s(t,n)}}}};function Wn(r){return r.toLowerCase().replace(/['’]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function f(r){let e=r.gameId?.trim();return e||(r.title?Wn(r.title):"")||r.id}function Rt(r){let e=[];for(let a of r.boxes){let d=f(a);for(let l of a.tagGroups)for(let c of l.tags){let p=f(c);e.push({id:c.id,gameId:p,qualified:`${d}/${p}`})}}let t=new Map;for(let a of e){let d=t.get(a.gameId)??[];d.includes(a.qualified)||d.push(a.qualified),t.set(a.gameId,d)}let n=new Map,s=new Map,o=new Map,i=new Map;for(let a of e){let d=t.get(a.gameId)??[a.qualified],l=d.length>1;n.set(a.id,l?a.qualified:a.gameId),s.has(a.qualified)||s.set(a.qualified,a.id),!l&&!s.has(a.gameId)&&s.set(a.gameId,a.id),l&&o.set(a.gameId,d)}for(let a of r.map?.group.tags??[]){let d=f(a);n.set(a.id,d),s.has(d)||s.set(d,a.id);for(let l of r.boxes)i.set(`${f(l)}/${d}`,d)}return{print:n,accept:s,repeated:o,zoneQualified:i}}function Ot(r,e,t){return`"value.${r}.${t}": "${e}" is a zone of the project map, which belongs to no box; write "value.${e}.${t}"`}function Lt(r,e,t){let n=t.map(o=>`"value.${o}.${e}"`),s=n.length<=1?n[0]??"":`${n.slice(0,-1).join(", ")} or ${n[n.length-1]}`;return`"value.${r}.${e}" names a tag in ${t.length} boxes; write ${s}`}var A="place",Un=/^@(hand|world|story)\.([a-z][a-z0-9_-]*)$/,Se=r=>r.startsWith("@"),It=r=>{let e=Un.exec(r);return e===null?void 0:{scope:e[1],name:e[2]}},Kn="storylets/bundle@1",Jn="storylets/bundle@0",ze=[Jn,Kn];function se(r,e){return e.usesMap===!0&&r.map!==void 0?[...e.tagGroups,r.map.group]:e.tagGroups}function At(r){let e=r.boxes.flatMap(t=>t.tagGroups);return r.map!==void 0?[...e,r.map.group]:e}var oe="storylets/save@2",ie="storylets/save@1",Ee="storylets/savefile@1";var Qn={project:".storyletproj",box:".storyletbox",tags:".storylettags",hands:".storylethands",deck:".storyletdeck",view:".storyletview",map:".storyletmap",notes:".storyletnotes",contract:".storyletcontract",extension:".storyletextension"},Xn=".extension.json";var ps=[...Object.values(Qn),Xn];function ae(r,e){let t=[],n=new Set([...Object.keys(r),...Object.keys(e)]);for(let s of[...n].sort()){let o=r[s],i=e[s];JSON.stringify(o)!==JSON.stringify(i)&&t.push({path:s,from:o,to:i})}return t}var Tt=r=>r===void 0?"<unset>":JSON.stringify(r),Dt=r=>r.pathPrefix??r.bag.pathPrefix;function de(r,e={}){let t=e.sink??(p=>console.log(p)),n=e.label??"",s=p=>{t(`${n}${p.path}: ${Tt(p.from)} -> ${Tt(p.to)}`)},o=()=>{let p={};for(let g of r.mounts()){let u=Dt(g);for(let[m,h]of Object.entries(g.bag.values))p[u+m]=h}return Object.assign(p,r.extra?.()??{}),structuredClone(p)},i=o(),a=[],d=[],l=(p,g)=>g.onAudit(u=>{let m=structuredClone({path:p+u.name,from:u.prev,to:u.next});s(m),a.push(m),i[m.path]=structuredClone(u.next)}),c=()=>{let p=r.mounts();if(!(d.length===p.length&&p.every((u,m)=>d[m].bag===u.bag))){for(let u of d)u.off();d=p.map(u=>({bag:u.bag,off:l(Dt(u),u.bag)}))}};return c(),{snapshot:o,capture(){let p=o(),g=ae(i,p);for(let m of g)s(m);let u=[...a,...g];return a=[],i=p,c(),u},dispose(){for(let p of d)p.off();d=[],a=[]}}}var G=class r{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(n=>n.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let n=this.norm(t.name);this.decls.set(n,t),this.values[n]=structuredClone(t.default??O(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,n){let s=this.norm(e);if(!n?.host&&!this.writable(s))throw new Error(`'${e}' is read-only`);let o={name:s,prev:this.values[s],next:t,silent:n?.silent??!1,reason:n?.reason};this.values[s]=t;for(let i of[...this.auditors])i(o);if(!o.silent)for(let i of[...this.subscribers])i(o);return o}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}onAudit(e){return this.auditors.add(e),()=>this.auditors.delete(e)}rows(){return[...this.decls.entries()].map(([e,t])=>Vt(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new r([],{normalise:this.norm,pathPrefix:this.pathPrefix});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e,t){let n=new Map;for(let[o,i]of Object.entries(e)){let a=this.norm(o);this.values[a]=structuredClone(i),n.set(a,i)}let s=[];if(!t?.refit)return s;for(let[o,i]of this.decls){if(!n.has(o))continue;let a=n.get(o);U(i,a)||(this.values[o]=structuredClone(O(i)),s.push({name:o,value:a}))}return s}};function Vt(r,e,t,n,s=""){let o=n??r.name.toLowerCase();return{name:o,path:s+o,type:r.type,value:e,default:r.default??O(r),...r.values!==void 0?{values:r.values}:{},...r.stages!==void 0?{stages:r.stages}:{},writable:t??r.writable??!0}}var Yn=r=>r.toLowerCase(),We=1,ke=class{scopes=new Map;parked=new Map;parkedRefit=new Set;misfits=[];rev=0;get revision(){return this.rev}defineOwned(e,t,n){let s=typeof n=="string"?{pathPrefix:n}:n??{},o=new G(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,o,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,n){this.assertFree(e,n?.owner),this.scopes.set(e,{kind:"owned",bag:t,...n?.owner!==void 0?{owner:n.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(this.note(e,t.load(s,{refit:this.parkedRefit.has(e)})),this.parked.delete(e),this.parkedRefit.delete(e)),this}remove(e,t){let n=this.scopes.get(e);if(!n)throw new Error(`unknown scope '@${e}'`);return t?.keep&&n.kind==="owned"&&(this.parked.set(e,n.bag.save()),this.parkedRefit.delete(e)),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear(),this.parkedRefit.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&(this.parked.delete(t),this.parkedRefit.delete(t));return this}takeMisfits(e){if(!e){let n=this.misfits;return this.misfits=[],n}let t=this.misfits.filter(n=>e(n.scope));return this.misfits=this.misfits.filter(n=>!e(n.scope)),t}note(e,t){for(let n of t)this.misfits.push({scope:e,name:n.name,value:n.value})}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`'@${e}' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,n=[],s=!0){let o=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,o.owner);let i=o.normalise??Yn,a=new Map;for(let d of n)a.set(i(d.name),d);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:o.writable??!0,norm:i,...o.owner!==void 0?{owner:o.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let n=this.scopes.get(e);if(n)return n.kind==="owned"?n.bag.get(t):n.resolver.get(n.norm(t))}set(e,t,n,s){let o=this.scopes.get(e);if(!o)throw new Error(`unknown scope '@${e}'`);if(o.kind==="owned"){if(!s?.host&&!o.bag.writable(t))throw new Error(`'@${e}.${t}' is read-only`);o.bag.set(t,n,s?.host?{host:!0}:void 0);return}let i=o.norm(t);if(!o.resolver.set)throw new Error(`'@${e}.${t}' is read-only`);if(!s?.host&&!this.foreignWritable(o,i))throw new Error(`'@${e}.${t}' is read-only`);o.resolver.set(i,n)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,n]of this.scopes){let s=n.owner!==void 0?{owner:n.owner}:{};if(n.kind==="owned")for(let o of n.bag.rows())e.push({scope:t,...s,...o});else for(let[o,i]of n.decls)e.push({scope:t,...s,...Vt(i,n.resolver.get(o),this.foreignWritable(n,o),o,`${t}.`)})}return e}toEvalContext(e,t){let n=this.view(t?.aliases),s={};for(let[i,a]of n)s[i]=a.kind==="owned"?a.bag.values:a.resolver;let o=this.qualityLadders(n);return o.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(i,a)=>{let d=n.get(i);return d?o.get(i)?.get(Zn(d)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[n,s]of Object.entries(e??{})){let o=this.scopes.get(s);if(!o)throw new Error(`alias '@${n}' names '${s}', which is not registered`);t.set(n,o)}return t}qualityLadders(e){let t=new Map;for(let[n,s]of e)for(let[o,i]of Nt(s)){if(i.type!=="quality"||i.stages===void 0)continue;let a=t.get(n);a||(a=new Map,t.set(n,a)),a.set(o,i.stages)}return t}toSchema(e){let t=new Map;for(let[n,s]of this.view(e?.aliases)){let o=Nt(s);if(o.length===0)continue;let i=new Map;for(let[a,d]of o)i.set(a,{type:d.type,enumValues:d.values,...d.stages!==void 0?{stages:d.stages}:{}});t.set(n,i)}return{properties:t}}save(){let e={};for(let[t,n]of this.scopes)n.kind==="owned"&&(e[t]=n.bag.save());for(let[t,n]of this.parked)e[t]=structuredClone(n);return e}load(e,t){t?.keepParked||(this.parked.clear(),this.parkedRefit.clear());let n=t?.refit===!0;for(let[s,o]of Object.entries(e)){let i=this.scopes.get(s);i?.kind==="owned"?this.note(s,i.bag.load(o,{refit:n})):i||(this.parked.set(s,structuredClone(o)),n?this.parkedRefit.add(s):this.parkedRefit.delete(s))}}saveFragment(){return{version:We,scopes:this.save()}}loadFragment(e){if(e.version!==We)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${We})`);this.load(e.scopes)}assertFree(e,t){let n=this.scopes.get(e);if(!n)return;let s=n.owner!==void 0?` by ${n.owner}`:"",o=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope '@${e}' is already registered${s}${o}`)}};function Nt(r){return r.kind==="foreign"?[...r.decls.entries()]:r.bag.declarations().map(e=>[r.bag.normalise(e.name),e])}function Zn(r){return r.kind==="foreign"?r.norm:e=>r.bag.normalise(e)}function O(r){if(r.default!==void 0)return r.default;switch(r.type){case"boolean":return!1;case"number":return 0;case"string":case"text":case"multiline":return"";case"enum":return r.values?.[0]??"";case"flags":return[];case"quality":return r.stages?.[0]??"";default:return!1}}var Ht=(r,e,t)=>`${r}${e}${t}`,w=class extends Error{constructor(e){super(e),this.name="StoryletError"}},fe="storylets/durable@1",pe=(r,e)=>r.shared??e,er=(r,e)=>r.redraw==="never"&&(r.durable??e.durable??!1)===!0,Jt=r=>r.sharedCopies??r.copies??1,J="Storylet Engine",Ye=r=>r,nt=r=>r.replace(/%/g,"%25").replace(/\//g,"%2F"),Ue=r=>r.replace(/%2F/g,"/").replace(/%25/g,"%"),Ke=(r,e)=>`storylets/${r}/${nt(e)}`,Ze=r=>`storylets/flow/${nt(r)}/`,et=(r,e,t)=>e==="story"?`${Ze(r)}story`:`${Ze(r)}${e}/${nt(t)}`,ue=()=>({story:{},box:{},deck:{},hand:{},value:{}});function tr(r,e){let t=ue(),n=new Map,s={},o=i=>{let a=Ue(i);if(!e.has(a))return;let d=n.get(a);return d||(d=ue(),n.set(a,d)),d};for(let[i,a]of Object.entries(r)){let d;if(i==="story")t.story=a;else if(d=/^storylets\/(box|deck|hand|value)\/([^/]+)$/.exec(i))t[d[1]][Ue(d[2])]=a;else if(d=/^storylets\/flow\/([^/]+)\/story$/.exec(i)){let l=o(d[1]);l&&(l.story=a)}else if(d=/^storylets\/flow\/([^/]+)\/(box|deck|hand|value)\/([^/]+)$/.exec(i)){let l=o(d[1]);l&&(l[d[2]][Ue(d[3])]=a)}else i.startsWith("storylets/")||(s[i]=a)}return{shared:t,flows:n,rest:s}}function jt(r,e,t){Object.keys(r.story).length>0&&(t[e("story")]=r.story);for(let n of R)for(let[s,o]of Object.entries(r[n]))Object.keys(o).length>0&&(t[e(n,s)]=o)}var Ft=(r,e)=>new G(r,{normalise:Ye,pathPrefix:e}),L=(r,e)=>r!==void 0&&Object.prototype.hasOwnProperty.call(r.values,e)?r.values[e]:void 0,Je=Object.freeze(Object.create(null));function _t(r){return typeof r=="boolean"?r:typeof r=="number"?r!==0:typeof r=="string"?r!=="":r.length>0}var nr={story:!0,box:!1,deck:!1,hand:!1,value:!1},Qt=(r,e)=>e.shared??nr[r],rr=(r,e)=>e.filter(t=>Qt(r,t)),sr=(r,e)=>e.filter(t=>!Qt(r,t)),R=["box","deck","hand","value"],Xt=r=>R.includes(r??""),Pe=()=>({gameId:new Map,id:new Map,repeated:new Map,zoneQualified:new Map}),or=()=>({box:Pe(),deck:Pe(),hand:Pe(),value:Pe()}),Qe=(r,e)=>{let t=f(e);r.gameId.set(e.id,t),r.id.has(t)||r.id.set(t,e.id)},ir=(r,e)=>{let t=Rt(e);for(let[n,s]of t.print)r.gameId.set(n,s);for(let[n,s]of t.accept)r.id.set(n,s);for(let[n,s]of t.repeated)r.repeated.set(n,s);for(let[n,s]of t.zoneQualified)r.zoneQualified.set(n,s)},Gt=(r,e)=>{let t=[],n=new Set,s=o=>{n.has(o.address)||(n.add(o.address),t.push(o))};for(let o of e.story)o.durable===!0&&s({address:`story.${o.name}`,kind:"story",decl:o});for(let o of R)for(let[i,a]of e[o])for(let d of a)d.durable===!0&&s({address:`${V(r,o,i)}.${d.name}`,kind:o,owner:i,decl:d});return t},rt=(r,e)=>e.kind==="story"?r.story:r[e.kind].get(e.owner),Yt=(r,e)=>{let t=[];for(let n of e){let s=L(rt(r,n),n.decl.name);s!==void 0&&t.push([n.address,s])}return Object.fromEntries(Q(t,([n])=>n))};function qt(r,e,t,n,s,o){let i=g=>({...s!==void 0?{flow:s}:{},path:g}),a=new Map(e.map(g=>[g.address,g])),d=new Map,l=n.values??{};for(let[g,u]of Object.entries(l)){let m=a.get(g);if(m===void 0){o.droppedProperties.push(i(g));continue}if(!U(m.decl,u)){o.retypedProperties.push(i(g));continue}d.set(m,u)}for(let g of e)Object.prototype.hasOwnProperty.call(l,g.address)||o.defaultedProperties.push(i(g.address));let c=new Set(t.map(g=>g.card.id)),p=[];for(let g of n.spent??[]){let u=r.cardsByGameId.get(g);u!==void 0&&c.has(u.card.id)?p.push(u.card.id):s!==void 0?o.droppedCooldowns.push({flow:s,card:g}):o.droppedSpent.push(g)}return{values:d,cards:p}}var zt=(r,e)=>{let t=e?.schema;if(t!==fe)throw new w(`unsupported durable schema: ${String(t)}`);let n=r.bundle.content.project;if(e.content?.project!==n)throw new w(`durable state is for project "${String(e.content?.project)}", bundle is "${n}"`)},Zt=(r,e,t)=>{r?.set(e,structuredClone(t),{silent:!0,reason:"host durable",host:!0})},Wt=(r,e)=>e.template!==void 0?r.templatesById.get(e.template)?.properties??[]:e.properties??[],V=(r,e,t)=>`${e}.${r.owners[e].gameId.get(t)??t}`,ar=(r,e,t)=>{let n=r.owners[e].repeated.get(t);if(n!==void 0)return{ambiguous:n};let s=r.owners[e].zoneQualified.get(t);if(s!==void 0)return{zone:s};let o=r.owners[e].id.get(t);if(o!==void 0)return{id:o,legacy:!1};if(r.owners[e].gameId.has(t))return{id:t,legacy:!0}},en=(r,e,t,n)=>{let s=ar(r,e,t);if(s===void 0)throw new w(`no ${e} store "${t}"`);if("ambiguous"in s)throw new w(Lt(t,n,s.ambiguous));if("zone"in s)throw new w(Ot(t,s.zone,n));return s},tn=(r,e,t,n)=>`"${e}.${t}.${n}" names the ${e} by its internal id; write "${V(r,e,t)}.${n}". The internal-id form is refused after the next release.`,nn=(r,e)=>{let t=n=>new Map([...e[n]].map(([s,o])=>[s,Ft(o,`${V(r,n,s)}.`)]));return{story:Ft(e.story,"story."),box:t("box"),deck:t("deck"),hand:t("hand"),value:t("value")}},dr=r=>({story:r.story.values,box:Object.fromEntries([...r.box].map(([e,t])=>[e,t.values])),deck:Object.fromEntries([...r.deck].map(([e,t])=>[e,t.values])),hand:Object.fromEntries([...r.hand].map(([e,t])=>[e,t.values])),value:Object.fromEntries([...r.value].map(([e,t])=>[e,t.values]))}),lr=(r,e)=>{r.story.load(e?.story??{});for(let t of R)for(let[n,s]of Object.entries(e?.[t]??{}))r[t].get(n)?.load(s)},le=()=>({evicted:[],droppedCooldowns:[],droppedSpent:[],droppedProperties:[],defaultedProperties:[],retypedProperties:[]}),tt="",Q=(r,e)=>[...r].map(t=>({item:t,k:e(t)})).sort((t,n)=>t.k<n.k?-1:t.k>n.k?1:0).map(t=>t.item),Xe=r=>`${r.flow??""}${tt}${r.path}`;function Ut(r,e,t,n,s){let o=l=>({...n!==void 0?{flow:n}:{},path:t(l)}),i=new Map((r??[]).map(l=>[l.name,l])),a=e??{},d={};for(let[l,c]of Object.entries(a)){let p=i.get(l);if(p===void 0){s.droppedProperties.push(o(l));continue}if(!U(p,c)){s.retypedProperties.push(o(l));continue}d[l]=c}for(let l of r??[])l.name in a||s.defaultedProperties.push(o(l.name));return d}function Kt(r,e,t,n,s){let o={story:Ut(e.story,t?.story,i=>`story.${i}`,n,s),box:{},deck:{},hand:{},value:{}};for(let i of R){let a=t?.[i]??{},d=[...new Set([...e[i].keys(),...Object.keys(a)])].sort();for(let l of d){let c=V(r,i,l);o[i][l]=Ut(e[i].get(l),a[l],p=>`${c}.${p}`,n,s)}}return o}function ce(r,e,t,n){let s=e.version!==r.version||e.hash!==r.hash,o=Q(n.evicted,p=>[p.flow,p.hand,p.card,p.reason].join(tt)),i=Q(n.droppedCooldowns,p=>`${p.flow}${tt}${p.card}`),a=[...n.droppedSpent].sort(),d=Q(n.droppedProperties,Xe),l=Q(n.defaultedProperties,Xe),c=Q(n.retypedProperties,Xe);return{exact:!s&&o.length===0&&i.length===0&&a.length===0&&d.length===0&&l.length===0&&c.length===0,project:r.project,version:{saved:e.version,bundle:r.version},hash:{saved:e.hash,bundle:r.hash},flows:t,evicted:o,droppedCooldowns:i,droppedSpent:a,droppedProperties:d,defaultedProperties:l,retypedProperties:c}}var cr=r=>{let e=r.schema;if(typeof e!="string"||!ze.includes(e))throw new w(`unsupported bundle schema: ${String(e)} (this runtime reads ${ze.join(" and ")})`);let t=[],n=r.map?.group,s=n!==void 0?f(n):void 0;n!==void 0&&s===A&&t.push(`the project map's tag group is called "${A}", which is reserved for a box's own hands`);let o=new Map;for(let i of r.boxes)for(let a of i.tagGroups)for(let d of a.tags){let l=f(d);o.has(l)||o.set(l,{box:f(i),group:f(a)})}for(let i of n?.tags??[]){let a=f(i),d=o.get(a);d!==void 0&&t.push(`the project map's zone "${a}" has the name of tag "${a}" in box "${d.box}", group "${d.group}", so "value.${a}.<name>" would name two things`)}for(let i of r.boxes){let a=f(i);if(i.usesMap===!0){if(n===void 0){t.push(`box "${a}" uses the project map, but the bundle has no map`);continue}i.tagGroups.find(c=>f(c)===s)!==void 0&&t.push(`box "${a}" uses the project map and declares its own tag group "${s}", the map's name`);continue}if(n===void 0)continue;let d=l=>{t.push(`box "${a}" is not on the project map, but ${l} names the map's tag group "${s}"`)};for(let l of i.decks)for(let c of l.cards)c.tags?.[n.id]!==void 0&&d(`card "${f(c)}"`);for(let l of i.handTemplates)(l.bindings?.[n.id]!==void 0||l.chooses?.includes(n.id)===!0)&&d(`hand template "${f(l)}"`);for(let l of i.hands)(l.chosen?.[n.id]!==void 0||l.rule?.bindings?.[n.id]!==void 0)&&d(`hand "${f(l)}"`)}if(t.length>0)throw new w(`bundle refused: ${t.join("; ")}`)},Ce=class r{internals;seed;onReplacedFlow;flowsById=new Map;engineTraceHandlers=new Set;hostWorld;creationOptions;sharedMounts=[];constructor(e,t={}){cr(e),this.creationOptions=t,this.seed=t.seed??0,this.onReplacedFlow=t.onReplacedFlow,t.world!==void 0&&(this.hostWorld=t.world);let n={bundle:e,...t.log?{logCap:typeof t.log=="object"?t.log.cap??1e3:1e3}:{},cardsById:new Map,cardsByGameId:new Map,boxesByGameId:new Map,boxesById:new Map,handsById:new Map,handsByGameId:new Map,owners:or(),templatesById:new Map,groupsById:new Map,requiredGroups:new Set,nodeCache:new WeakMap,ladders:{world:new Map,story:new Map,box:new Map,deck:new Map,value:new Map,hand:new Map},hasQualities:!1,hasShared:!1,flowDecls:{story:[],box:new Map,deck:new Map,hand:new Map,value:new Map},sharedDecls:{story:[],box:new Map,deck:new Map,hand:new Map,value:new Map},durable:{shared:[],flow:[],sharedCards:[],flowCards:[]},shared:void 0,registry:t.registry??new ke,ownsRegistry:t.registry===void 0,selfWorld:!1,registryView:()=>i(),worldResolver:void 0,worldReadOnly:new Set,emitEngine:(d,l,c)=>{this.internals.logCap!==void 0&&(this.engineLog.push({...l,flow:d,seq:this.engineSeq++,...c!==void 0?{turn:c}:{}}),this.engineLog.length>this.internals.logCap&&this.engineLog.splice(0,this.engineLog.length-this.internals.logCap));for(let p of[...this.engineTraceHandlers])p(d,l)},engineTracing:()=>this.engineTraceHandlers.size>0};this.internals=n;let s=-1,o={scopes:{},qualities:void 0},i=()=>{let d=n.registry;if(d.revision!==s){let l=d.toEvalContext(),c={};for(let[p,g]of Object.entries(l.scopes))p.includes("/")||(c[p]=g);o={scopes:c,qualities:l.qualities},s=d.revision}return o};ir(n.owners.value,e);for(let d of e.boxes){n.boxesById.set(d.id,d),n.boxesByGameId.set(f(d),d),Qe(n.owners.box,d);for(let l of d.tagGroups)n.groupsById.set(l.id,{group:l,box:d}),l.required===!0&&n.requiredGroups.add(l.id);for(let l of d.decks){Qe(n.owners.deck,l),l.shared===!0&&(n.hasShared=!0);for(let c of l.cards){let p={card:c,deck:l,box:d};n.cardsById.set(c.id,p),n.cardsByGameId.set(f(c),p),c.shared===!0&&(n.hasShared=!0),er(c,l)&&(pe(c,l.shared??!1)?n.durable.sharedCards:n.durable.flowCards).push(p)}}for(let l of d.handTemplates)n.templatesById.set(l.id,l);for(let l of d.hands)n.handsById.set(l.id,{hand:l,box:d}),n.handsByGameId.set(f(l),{hand:l,box:d}),Qe(n.owners.hand,l)}e.map!==void 0&&(n.groupsById.set(e.map.group.id,{group:e.map.group}),e.map.group.required===!0&&n.requiredGroups.add(e.map.group.id)),this.initLadders();let a=d=>({story:d("story",e.story.properties),box:new Map(e.boxes.map(l=>[l.id,d("box",l.properties)])),deck:new Map(e.boxes.flatMap(l=>l.decks.map(c=>[c.id,d("deck",c.properties)]))),hand:new Map(e.boxes.flatMap(l=>l.hands.map(c=>[c.id,d("hand",Wt(n,c))]))),value:new Map(At(e).flatMap(l=>l.tags.map(c=>[c.id,d("value",c.properties??[])])))});n.flowDecls=a(sr),n.sharedDecls=a(rr),n.durable.shared=Gt(n,n.sharedDecls),n.durable.flow=Gt(n,n.flowDecls),this.initShared(this.hostWorld)}initShared(e){let t=this.internals,n=t.registry,s=t.bundle.world.properties;t.shared=nn(t,t.sharedDecls),t.worldReadOnly=new Set(t.bundle.world.properties.filter(a=>a.writable===!1).map(a=>a.name));let o=[],i=(a,d)=>{d(),o.push(a),this.sharedMounts.push({key:a,mount:d})};try{let a=t.shared.story;i("story",()=>{n.mountOwned("story",a,{owner:J})});for(let d of R)for(let[l,c]of t.shared[d])c.declarations().length!==0&&i(Ke(d,l),()=>{n.mountOwned(Ke(d,l),c,{owner:J})});if(e!==void 0)i("world",()=>{n.defineForeign("world",e,s,{normalise:Ye,owner:J})});else if(t.ownsRegistry&&!n.has("world")){n.defineOwned("world",s,{normalise:Ye,pathPrefix:"world.",owner:J});let d=n.ownedBag("world");o.push("world"),this.sharedMounts.push({key:"world",mount:()=>{n.mountOwned("world",d,{owner:J})}}),t.selfWorld=!0}}catch(a){for(let d of o)n.remove(d,{keep:!0});throw a}t.worldResolver={get:a=>n.get("world",a),set:(a,d)=>{n.set("world",a,d)}},e!==void 0?t.worldSet=e.set!==void 0?(a,d)=>{n.set("world",a,d,{host:!0})}:void 0:t.worldSet=(a,d,l)=>{n.set("world",a,d,l===!0?{host:!0}:void 0)}}reseedShared(){let{shared:e,sharedDecls:t,registry:n}=this.internals;e.story.reseed(t.story);for(let s of R)for(let[o,i]of e[s])i.reseed(t[s].get(o)??[]);this.internals.selfWorld&&n.reseedOwned("world",this.internals.bundle.world.properties)}initLadders(){let e=this.internals,t=o=>{let i=new Map;for(let a of o??[])a.type==="quality"&&a.stages!==void 0&&i.set(a.name,a.stages);return i},n=e.bundle;e.ladders.world=t(n.world.properties),e.ladders.story=t(n.story.properties);for(let o of n.boxes){e.ladders.box.set(o.id,t(o.properties));for(let i of o.decks)e.ladders.deck.set(i.id,t(i.properties));for(let i of o.tagGroups)for(let a of i.tags)e.ladders.value.set(a.id,t(a.properties));for(let i of o.hands)e.ladders.hand.set(i.id,t(Wt(e,i)))}for(let o of n.map?.group.tags??[])e.ladders.value.set(o.id,t(o.properties));let s=o=>[...o.values()].some(i=>i.size>0);e.hasQualities=e.ladders.world.size>0||e.ladders.story.size>0||s(e.ladders.box)||s(e.ladders.deck)||s(e.ladders.value)||s(e.ladders.hand)}get registry(){return this.internals.registry}openFlow(e,t={}){return this.open(e,t,!1)}open(e,t,n){if(t.durable!==void 0&&t.restore!==void 0)throw new w(`openFlow "${e}": restore and durable cannot be given together; a restore already carries the flow's durable state`);let s;if(t.durable!==void 0){zt(this.internals,t.durable);let d=le(),{flow:l,flowCards:c}=this.internals.durable;s={plan:qt(this.internals,l,c,t.durable,e,d),draft:d,content:t.durable.content}}this.assertExternalScopes();let o=t.restore!==void 0?this.sharedClaims(e):void 0,i=this.flowsById.get(e);if(i){let d=i.heldCardIds().length;d>0&&this.onReplacedFlow?.(e,d),i.markClosed()}n||this.internals.registry.discardParked(Ze(e));let a=new ge(this,this.internals,e,t.seed??this.seed);if(this.flowsById.set(e,a),t.restore!==void 0){let d=le(),l=this.planFlowRestore(e,structuredClone(t.restore),o,d);a.restore(l);let c=this.internals.bundle.content;t.onRestoreReport?.(ce(c,c,[e],d))}return s!==void 0&&(a.writeDurable(s.plan),t.onRestoreReport?.(ce(this.internals.bundle.content,s.content,[e],s.draft))),a}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}closeFlow(e){let t=this.flowsById.get(e);t&&(this.flowsById.delete(e),t.markClosed())}dropFlow(e,t){this.flowsById.get(e)===t&&this.flowsById.delete(e)}reset(){this.dropRun(!1),this.reseedShared(),this.internals.registry.discardParked("storylets/")}dropRun(e){this.engineLog=[];for(let[t,n]of this.flowsById)n.releaseBags(e!==!1&&e.has(t)),n.markClosed();this.flowsById.clear(),this.spent.clear()}spent=new Set;isTaken(e){return this.spent.has(e)}markTaken(e){this.spent.add(e)}engineLog=[];engineSeq=0;log(){return this.engineLog}clearLog(){this.engineLog=[]}sharedClaims(e){let t=new Map;for(let[n,s]of this.flowsById)if(n!==e)for(let o of s.heldCardIds())t.set(o,(t.get(o)??0)+1);return t}getProperty(e){let t=this.resolveShared(e),n=t.kind==="world"?this.internals.worldResolver.get(t.name):t.kind==="scope"?this.internals.registry.get(t.token,t.name):t.bag.get(t.name);if(n===void 0)throw new w(`no property at "${e}"`);return n}setProperty(e,t){let n=this.resolveShared(e);if(n.kind==="world"){if(!this.internals.worldSet)throw new w("@world is read-only here: the host bound no write");this.internals.worldSet(n.name,t,!0);return}if(n.kind==="scope"){this.internals.registry.set(n.token,n.name,t,{host:!0});return}n.bag.set(n.name,t,{silent:!0,reason:"host setProperty",host:!0})}resolveShared(e){let t=e.split("."),n=()=>{throw new w(`"${e}" is per-flow state - read it on a Flow, not the Engine`)};if(t.length===2&&t[0]==="world")return{kind:"world",name:t[1]};if(t.length===2&&t[0]!=="story"&&this.internals.registry.has(t[0]))return{kind:"scope",token:t[0],name:t[1]};if(t.length===2&&t[0]==="story"){let s=t[1];if(L(this.internals.shared.story,s)!==void 0)return{kind:"bag",bag:this.internals.shared.story,name:s};throw this.internals.flowDecls.story.some(o=>o.name===s)&&n(),new w(`no property at "${e}"`)}if(t.length===3&&Xt(t[0])){let s=t[0],[,o,i]=t,a=en(this.internals,s,o,i);a.legacy&&this.diagnose(tn(this.internals,s,o,i));let d=a.id,l=this.internals.shared[s].get(d);if(l!==void 0&&L(l,i)!==void 0)return{kind:"bag",bag:l,name:i};throw this.internals.flowDecls[s].get(d)?.some(c=>c.name===i)&&n(),l===void 0&&!this.internals.flowDecls[s].has(d)?new w(`no ${s} store "${o}"`):new w(`no property at "${e}"`)}throw new w(`bad property path "${e}"`)}diagnose(e){this.internals.emitEngine("",{type:"diagnostic",where:"property address",message:e})}assertExternalScopes(){for(let e of this.internals.bundle.externalScopes??[])if(!this.internals.registry.has(e))throw new w(`this content names @${e}, which no engine on this registry registered: give every engine the game's one registry`)}listProperties(){let e=[];for(let n of this.internals.bundle.world.properties){let s=this.internals.worldResolver.get(n.name);e.push({path:`world.${n.name}`,name:n.name,type:n.type,value:s??O(n),default:O(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:this.internals.worldSet!==void 0&&!this.internals.worldReadOnly.has(n.name)})}let t=(n,s)=>{for(let o of s.rows())e.push(o)};t("story",this.internals.shared.story);for(let n of R)for(let[s,o]of this.internals.shared[n])t(V(this.internals,n,s),o);return e}listBags(){let e=[{prefix:"story",bag:this.internals.shared.story}];for(let t of R)for(let[n,s]of this.internals.shared[t])e.push({prefix:V(this.internals,t,n),bag:s});return e}subscribeTrace(e){return this.engineTraceHandlers.add(e),()=>{this.engineTraceHandlers.delete(e)}}hotSwap(e,t={}){if(e.content.project!==this.internals.bundle.content.project)throw new w(`hotSwap: the bundle is for project "${e.content.project}", this engine runs "${this.internals.bundle.content.project}"`);let n={...this.creationOptions,...t},s=this.saveGame(),o=this.internals.registry;if(this.internals.ownsRegistry){let c=new r(e,{...n,registry:void 0});return{engine:c,report:c.loadGame(s)}}let i={};for(let[c,p]of Object.entries(o.save()))(c==="story"||c.startsWith("storylets/"))&&(i[c]=p);let a=new Set([...this.sharedMounts.map(c=>c.key),...[...this.flowsById.values()].flatMap(c=>c.registeredKeys())]),d=Object.fromEntries(Object.entries(i).filter(([c])=>!a.has(c)));for(let{key:c}of this.sharedMounts)o.has(c)&&o.remove(c);for(let c of this.flowsById.values())c.releaseBags(!1);let l;try{l=new r(e,{...n,registry:o});let c=l.loadGame({...s,registry:i}),p=Object.fromEntries(Object.entries(d).filter(([g])=>!o.has(g)));return Object.keys(p).length>0&&o.load(p,{keepParked:!0}),this.dropRun(!1),{engine:l,report:c}}catch(c){l?.dropRun(!1);for(let{key:p}of l?.sharedMounts??[])o.has(p)&&o.remove(p);o.discardParked("storylets/");for(let{mount:p}of this.sharedMounts)p();for(let p of this.flowsById.values())p.mountBags();throw this.reseedShared(),o.load(i,{keepParked:!0}),c}}saveGame(){return structuredClone({schema:oe,content:this.internals.bundle.content,...this.internals.ownsRegistry?{registry:this.registrySection()}:{},shared:{spent:[...this.spent].sort()},flows:Object.fromEntries([...this.flowsById].map(([e,t])=>[e,t.snapshot(!1)]))})}registrySection(){let e=this.internals.registry.save(),t={},n=s=>{Object.prototype.hasOwnProperty.call(e,s)&&!Object.prototype.hasOwnProperty.call(t,s)&&(t[s]=e[s])};for(let{key:s}of this.sharedMounts)n(s);for(let s of Object.values(Object.fromEntries(this.flowsById)))for(let o of s.registeredKeys())n(o);for(let s of Object.keys(e))n(s);return t}saveFlow(e){let t=this.flowsById.get(e);if(!t)throw new w(`unknown flow "${e}"`);return structuredClone(t.snapshot(!0))}saveDurable(){let{bundle:e,shared:t,durable:n}=this.internals;return structuredClone({schema:fe,content:e.content,values:Yt(t,n.shared),spent:n.sharedCards.filter(s=>this.spent.has(s.card.id)).map(s=>f(s.card)).sort()})}loadDurable(e){let t=this.internals;zt(t,e);let n=le(),s=qt(t,t.durable.shared,t.durable.sharedCards,e,void 0,n);for(let o of t.durable.shared){let i=s.values.has(o)?s.values.get(o):O(o.decl);Zt(rt(t.shared,o),o.decl.name,i)}for(let o of s.cards)this.spent.add(o);return ce(t.bundle.content,e.content,[],n)}previewLoad(e){return this.assertSameProject(e),this.planLoad(e).report}previewFlowRestore(e,t){let n=le();this.planFlowRestore(e,t,this.sharedClaims(e),n);let s=this.internals.bundle.content;return ce(s,s,[e],n)}loadGame(e){this.assertSameProject(e),this.assertExternalScopes();let t=this.planLoad(structuredClone(e)),n=this.internals.registry;t.sections!==void 0?(this.reset(),this.internals.ownsRegistry?n.load(t.sections):n.load(t.sections,{keepParked:!0})):this.dropRun(new Set(t.flows.map(([s])=>s)));for(let s of t.spent)this.spent.add(s);for(let[s,o]of t.flows)this.open(s,{},!0).restore(o);return t.report}assertSameProject(e){if(e.content.project!==this.internals.bundle.content.project)throw new w(`save is for project "${e.content.project}", bundle is "${this.internals.bundle.content.project}"`)}planLoad(e){let t=e.schema;if(t!==oe&&t!==ie)throw new w(`unsupported save schema: ${String(t)}`);let n=le(),s=new Set(Object.keys(e.flows??{})),o;e.schema===ie?o={shared:e.shared?.props??ue(),flows:new Map(Object.entries(e.flows??{}).map(([c,p])=>[c,p.props??ue()])),rest:{}}:e.registry!==void 0&&(o=tr(e.registry,s));let i=o!==void 0?Kt(this.internals,this.internals.sharedDecls,o.shared,void 0,n):void 0,a=[];for(let c of e.shared?.spent??[])this.internals.cardsById.has(c)?a.push(c):n.droppedSpent.push(c);let d=[],l=o!==void 0?{...o.rest}:void 0;l!==void 0&&i!==void 0&&jt(i,(c,p)=>c==="story"?"story":Ke(c,p),l);for(let[c,p]of Object.entries(e.flows??{})){let g=o!==void 0?{...p,props:o.flows.get(c)??ue()}:{...p},u=this.planFlowRestore(c,g,void 0,n);l!==void 0&&u.props!==void 0&&(jt(u.props,(m,h)=>et(c,m,h),l),delete u.props),d.push([c,u])}return{report:ce(this.internals.bundle.content,e.content,d.map(([c])=>c),n),...l!==void 0?{sections:l}:{},spent:a,flows:d}}planFlowRestore(e,t,n,s){let o=this.internals,i=t.props!==void 0?Kt(o,o.flowDecls,t.props,e,s):void 0,a={};for(let[p,g]of Object.entries(t.cooldowns??{}))o.cardsById.has(p)?a[p]=g:s.droppedCooldowns.push({flow:e,card:p});let d=p=>{let g=o.cardsById.get(p);return g?f(g.card):p},l={},c=new Map;for(let[p,g]of Object.entries(t.board??{})){let u=o.handsById.get(p);if(u===void 0){for(let x of g)s.evicted.push({flow:e,hand:p,card:d(x),reason:"hand-vanished"});continue}let m=f(u.hand),h=[];for(let x of g){let v=o.cardsById.get(x);if(v===void 0){s.evicted.push({flow:e,hand:m,card:x,reason:"vanished"});continue}if(n!==void 0&&pe(v.card,v.deck.shared??!1)){if((n.get(x)??0)+(c.get(x)??0)>=Jt(v.card)){s.evicted.push({flow:e,hand:m,card:f(v.card),reason:"claimed-elsewhere"});continue}c.set(x,(c.get(x)??0)+1)}h.push(x)}l[p]=h}return{...i!==void 0?{props:i}:{},turns:t.turns??{},prng:t.prng,cooldowns:a,board:l,playLog:t.playLog??[]}}},ge=class{id;engine;internals;closed=!1;prng;turnCounts=new Map;cooldowns={};boardContents=new Map;playLog=[];playCount=new Map;lastPlayOf=new Map;tagPlayCount=new Map;lastPlayInTag=new Map;stores;registered=[];bagKeys=[];traceHandlers=new Set;logEntries=[];logSeq=0;storyReader;boxReaders=new Map;deckReaders=new Map;constructor(e,t,n,s){this.engine=e,this.internals=t,this.id=n,this.prng=H(s),this.stores=nn(t,t.flowDecls);let o=(a,d)=>{d.declarations().length!==0&&this.bagKeys.push([a,d])};o(et(n,"story"),this.stores.story);for(let a of R)for(let[d,l]of this.stores[a])o(et(n,a,d),l);this.mountBags();for(let a of t.bundle.boxes){this.turnCounts.set(a.id,0);for(let d of a.hands)this.boardContents.set(d.id,[])}let i=(a,d)=>({get:l=>L(a,l)??L(d,l)});this.storyReader=i(this.stores.story,t.shared.story);for(let a of t.bundle.boxes){this.boxReaders.set(a.id,i(this.stores.box.get(a.id),t.shared.box.get(a.id)));for(let d of a.decks)this.deckReaders.set(d.id,i(this.stores.deck.get(d.id),t.shared.deck.get(d.id)))}}get isClosed(){return this.closed}close(){this.closed||(this.engine.dropFlow(this.id,this),this.markClosed())}markClosed(){this.releaseBags(!1),this.traceHandlers.clear(),this.closed=!0}releaseBags(e){for(let t of this.registered)this.internals.registry.remove(t,{keep:e});this.registered.length=0}registeredKeys(){return[...this.registered]}mountBags(){for(let[e,t]of this.bagKeys)this.internals.registry.mountOwned(e,t,{owner:J}),this.registered.push(e)}assertOpen(){if(this.closed)throw new w(`flow "${this.id}" is closed`)}resolveBox(e){let t=this.internals.boxesByGameId.get(e)??this.internals.boxesById.get(e);if(!t)throw new w(`unknown box "${e}"`);return t}turn(e){return this.assertOpen(),this.turnCounts.get(this.resolveBox(e).id)??0}subscribeTrace(e){return this.traceHandlers.add(e),()=>{this.traceHandlers.delete(e)}}get tracing(){return this.traceHandlers.size>0||this.internals.logCap!==void 0||this.internals.engineTracing()}emit(e,t){this.internals.logCap!==void 0&&(this.logEntries.push({...e,seq:this.logSeq++,...t!==void 0?{turn:t}:{}}),this.logEntries.length>this.internals.logCap&&this.logEntries.splice(0,this.logEntries.length-this.internals.logCap));for(let n of[...this.traceHandlers])n(e);this.internals.emitEngine(this.id,e,t)}log(){return this.logEntries}clearLog(){this.logEntries=[]}node(e){let t=this.internals.nodeCache.get(e);return t||(t=j(e.ast),this.internals.nodeCache.set(e,t)),t}groupInBox(e,t){let n=se(this.internals.bundle,e);return n.find(s=>f(s)===t)??n.find(s=>s.id===t)}indexPlay(e){this.playCount.set(e.card,(this.playCount.get(e.card)??0)+1),this.lastPlayOf.set(e.card,e);let t=this.internals.cardsByGameId.get(e.card);if(t)for(let[n,s]of Object.entries(t.card.tags??{}))for(let o of s){let i=Ht(t.box.id,n,o);this.tagPlayCount.set(i,(this.tagPlayCount.get(i)??0)+1),this.lastPlayInTag.set(i,e)}}rebuildPlayIndex(){this.playCount.clear(),this.lastPlayOf.clear(),this.tagPlayCount.clear(),this.lastPlayInTag.clear();for(let e of this.playLog)this.indexPlay(e)}hostsByBox=new Map;host(e){let t=this.hostsByBox.get(e.id);if(t!==void 0)return t;let n=this.makeHost(e);return this.hostsByBox.set(e.id,n),n}makeHost(e){let t=new Map,n=(o,i)=>{let a=`${o}${i}`;if(t.has(a))return t.get(a);let d=this.groupInBox(e,o),l=d?.tags.find(p=>p.gameId===i),c=d&&l?Ht(e.id,d.id,l.id):void 0;return t.set(a,c),c},s=o=>{let i=this.internals.cardsByGameId.get(o.card);return i?(this.turnCounts.get(i.box.id)??0)-o.turn:xe};return{nextRandom:()=>this.prng.next(),countPlayed:o=>this.playCount.get(o)??0,turnsSincePlayed:o=>{let i=this.lastPlayOf.get(o);return i?s(i):xe},countPlayedIn:(o,i)=>{let a=n(o,i);return a===void 0?0:this.tagPlayCount.get(a)??0},turnsSincePlayedIn:(o,i)=>{let a=n(o,i),d=a===void 0?void 0:this.lastPlayInTag.get(a);return d?s(d):xe}}}evalCtx(e,t,n){let s=this.internals.registryView();return{scopes:{...s.scopes,world:this.internals.worldResolver,story:this.storyReader,box:this.boxReaders.get(e.id)??Je,deck:t?this.deckReaders.get(t.id)??Je:Je,hand:n.bag},host:this.host(e),...this.internals.hasQualities||s.qualities!==void 0?{qualities:(o,i)=>o==="world"?this.internals.ladders.world.get(i)??s.qualities?.(o,i):o==="story"?this.internals.ladders.story.get(i):o==="box"?this.internals.ladders.box.get(e.id)?.get(i):o==="deck"&&t?this.internals.ladders.deck.get(t.id)?.get(i):o==="hand"?this.handLadder(n,i):s.qualities?.(o,i)}:{}}}handLadder(e,t){let n=e.sources.get(t);if(n)return n.kind==="value"?this.internals.ladders.value.get(n.id)?.get(t):n.kind==="hand"?this.internals.ladders.hand.get(n.id)?.get(t):void 0}eval(e,t){return we(this.node(e),t,qe)}passes(e,t,n){if(!e)return!0;try{return _t(this.eval(e,t))}catch(s){return this.tracing&&this.emit({type:"diagnostic",where:n??"condition",message:s instanceof Error?s.message:String(s)}),!1}}tagByGameId(e,t){return e.tags.find(n=>n.gameId===t)}askForHand(e,t){let n=new Map,s={},o=a=>{for(let[d,l]of Object.entries(a??{})){if(Se(l)){this.fillHoleFromProperty(e,d,l,n,s);continue}n.set(d,l);let c=this.internals.groupsById.get(d),p=c?.group.tags.find(g=>g.id===l);c&&p&&(s[f(c.group)]=f(p))}},i;if(e.template!==void 0){let a=this.internals.templatesById.get(e.template);if(!a)throw new w(`hand "${f(e)}": unknown template "${e.template}"`);for(let[d,l]of Object.entries(a.bindings??{}))n.set(d,l);o(e.chosen),i=a.condition}else o(e.rule?.bindings),i=e.rule?.condition;return n.set(A,e.id),this.bindStateGroups(t,n,s),{box:t,hand:e,...i!==void 0?{condition:i}:{},boundTags:n,askNames:s}}askForPeek(e,t){let n=new Map,s={};for(let[o,i]of Object.entries(t)){if(o===A){let l=this.internals.handsByGameId.get(i)??this.internals.handsById.get(i);if(!l)throw new w(`peek: unknown hand "${i}" in home criteria`);n.set(A,l.hand.id);continue}let a=this.groupInBox(e,o);if(!a)throw new w(`peek: unknown tag group "${o}" in box "${f(e)}"`);let d=this.tagByGameId(a,i)??a.tags.find(l=>l.id===i);if(!d)throw new w(`peek: unknown tag "${i}" in group "${f(a)}"`);n.set(a.id,d.id),s[f(a)]=f(d)}return this.bindStateGroups(e,n,s),{box:e,boundTags:n,askNames:s}}fillHoleFromProperty(e,t,n,s,o){let i=this.internals.groupsById.get(t),a=i?f(i.group):t,d=`hand ${f(e)}, tag group ${a}`,l=It(n);if(!l){this.emit({type:"diagnostic",where:d,message:`"${n}" is not a @hand, @world or @story property reference`});return}if(!i){this.emit({type:"diagnostic",where:d,message:`"${n}" fills a tag group that is not in this box`});return}let c;if(l.scope==="hand")c=this.valuesOf("hand",e.id)[l.name];else try{c=this.getProperty(`${l.scope}.${l.name}`)}catch{c=void 0}if(c===void 0){this.emit({type:"diagnostic",where:d,message:`"${n}" names a property that is not declared`});return}let p=typeof c=="string"?c:String(c),g=i.group.tags.find(u=>f(u)===p);if(!g){this.emit({type:"diagnostic",where:d,message:`${n} is "${p}", which is not one of the tags of "${a}"`});return}s.set(t,g.id),o[a]=f(g)}bindStateGroups(e,t,n){for(let s of se(this.internals.bundle,e)){if(s.boundBy===void 0||t.has(s.id))continue;let o=/^@(world|story)\.([a-z][a-z0-9_-]*)$/.exec(s.boundBy);if(!o){this.emit({type:"diagnostic",where:`tag group ${f(s)}`,message:`boundBy "${s.boundBy}" is not a @world or @story property reference`});continue}let i;try{i=this.getProperty(`${o[1]}.${o[2]}`)}catch{this.emit({type:"diagnostic",where:`tag group ${f(s)}`,message:`boundBy "${s.boundBy}" names a property that is not declared`});continue}let a=typeof i=="string"?i:String(i),d=s.tags.find(l=>f(l)===a);if(!d){this.emit({type:"diagnostic",where:`tag group ${f(s)}`,message:`${s.boundBy} is "${a}", which is not one of its tags`});continue}t.set(s.id,d.id),n[f(s)]=f(d)}}valuesOf(e,t){return Object.assign(Object.create(null),this.internals.shared[e].get(t)?.values??{},this.stores[e].get(t)?.values??{})}buildHandEnv(e){let t=Object.create(null),n=new Map;for(let[s,o]of e.boundTags)if(s!==A)for(let[i,a]of Object.entries(this.valuesOf("value",o)))t[i]=a,n.set(i,{kind:"value",id:o});if(e.hand)for(let[s,o]of Object.entries(this.valuesOf("hand",e.hand.id)))t[s]=o,n.set(s,{kind:"hand",id:e.hand.id});for(let[s,o]of Object.entries(e.askNames))t[s]=o,n.set(s,{kind:"criteria"});return{bag:t,sources:n,boundTags:e.boundTags}}claims(){let e=new Map;for(let t of this.boardContents.values())for(let n of t)e.set(n,(e.get(n)??0)+1);return e}heldCardIds(){return[...this.boardContents.values()].flat()}copiesOf(e){return e.copies??1}claimVerdict(e,t,n,s){let o=e.id;return(n.get(o)??0)>=this.copiesOf(e)?"claimed":t&&(s.get(o)??0)>=Jt(e)?"claimed-elsewhere":null}tagsMatch(e,t){let n=e.tags?.[A];if(n!==void 0&&n.length>0){let s=t.get(A);if(s===void 0||!n.includes(s))return!1}for(let[s,o]of t){if(s===A)continue;let i=e.tags?.[s];if(i===void 0){if(this.internals.requiredGroups.has(s))return!1;continue}if(!i.includes(o))return!1}return!0}runAsk(e,t,n){let{box:s}=e,o=this.buildHandEnv(e),i=(p,g)=>{n?.push({id:f(p),verdict:g})};if(!this.passes(e.condition,this.evalCtx(s,void 0,o),`hand ${e.hand?f(e.hand):""} condition`))return{ordered:[],handEnv:o};let a=new Map;for(let p of s.decks)a.set(p.id,this.passes(p.condition,this.evalCtx(s,p,o),`deck ${p.gameId} gate`));let d=this.turnCounts.get(s.id)??0,l=[];for(let p of s.decks){let g=this.evalCtx(s,p,o),u=p.shared??!1;for(let m of p.cards){let h=pe(m,u);if(!a.get(p.id)){i(m,"deck-gate");continue}if(h&&this.engine.isTaken(m.id)){i(m,"taken");continue}if((this.cooldowns[m.id]??0)>d){i(m,"cooldown");continue}if(!this.tagsMatch(m,o.boundTags)){i(m,"tags");continue}if(m.condition&&!this.passes(m.condition,g,this.tracing?`card ${m.gameId} condition`:void 0)){i(m,"condition");continue}let x=t(m,h);if(x){i(m,x);continue}let v;if(typeof m.priority=="number")v=m.priority;else try{let y=this.eval(m.priority,g);if(typeof y!="number"||Number.isNaN(y)){i(m,"priority");continue}v=y}catch(y){this.tracing&&this.emit({type:"diagnostic",where:`card ${m.gameId} priority`,message:y instanceof Error?y.message:String(y)}),i(m,"priority");continue}let b=0;if(s.ranking.specificity&&m.condition){let y=this.node(m.condition);b=Ct(y,P=>{try{return _t(we(P,g,qe))}catch{return!1}})}l.push({entry:{card:m,deck:p,box:s},priority:v,spec:b})}}l.sort((p,g)=>g.priority-p.priority||g.spec-p.spec);let c=0;for(;c<l.length;){let p=c+1;for(;p<l.length&&l[p].priority===l[c].priority&&l[p].spec===l[c].spec;)p++;if(p-c>1){let g=l.slice(c,p);_(g,this.prng);for(let u=0;u<g.length;u++)l[c+u]=g[u]}c=p}for(let p of l)n?.push({id:f(p.entry.card),verdict:"dealt",priority:p.priority,specificity:p.spec});return{ordered:l.map(p=>p.entry),handEnv:o}}capTrace(e,t){for(let n of e)n.verdict==="dealt"&&!t.has(n.id)&&(n.verdict="capped")}view(e){let{card:t}=e;return{id:t.id,gameId:f(t),...t.title!==void 0?{title:t.title}:{},...t.purpose!==void 0?{purpose:t.purpose}:{},...t.fields!==void 0?{fields:t.fields}:{},...t.extensions!==void 0?{extensions:t.extensions}:{}}}handCapacity(e){if(e.slots!==void 0)return e.slots;let t=e.template!==void 0?this.internals.templatesById.get(e.template)?.slots:e.rule?.slots;return t===void 0||t==="unbounded"?1/0:t}resolveHand(e){let t=this.internals.handsByGameId.get(e)??this.internals.handsById.get(e);if(!t)throw new w(`unknown hand "${e}"`);return t}peek(e,t={},n){this.assertOpen();let s=this.resolveBox(e),o=this.askForPeek(s,t),i=this.claims(),a=this.internals.hasShared?this.engine.sharedClaims():new Map,d=this.tracing?[]:void 0,l=this.prng;this.prng=H(l.state());let c;try{({ordered:c}=this.runAsk(o,(g,u)=>this.claimVerdict(g,u,i,a),d))}finally{this.prng=l}let p=n===void 0?c:c.slice(0,Math.max(n,0));return d&&(this.capTrace(d,new Set(p.map(g=>f(g.card)))),this.emit({type:"peek",box:f(s),criteria:t,cards:d},this.turnCounts.get(s.id)??0)),{box:f(s),cards:p.map(g=>this.view(g))}}deal(e){this.assertOpen();let{hand:t}=this.resolveHand(e);return this.dealMany([e])[f(t)]??[]}dealMany(e){this.assertOpen();let t=(e??[...this.internals.handsById.keys()].sort()).map(o=>this.resolveHand(o));_(t,this.prng);for(let{hand:o,box:i}of t){let a=this.askForHand(o,i),d=this.buildHandEnv(a),l=this.passes(a.condition,this.evalCtx(i,void 0,d)),c=new Map,p=new Map;for(let v of i.decks){let b=this.evalCtx(i,v,d);p.set(v.id,b),c.set(v.id,this.passes(v.condition,b))}let g=this.tracing,u=this.turnCounts.get(i.id)??0,m=[],h=(v,b)=>{let y=this.internals.cardsById.get(v);return m.push({card:y?f(y.card):v,reason:b}),!1},x=(this.boardContents.get(o.id)??[]).filter(v=>{if(!l)return h(v,"hand-condition");let b=this.internals.cardsById.get(v);return!b||b.box.id!==i.id?h(v,"vanished"):c.get(b.deck.id)?pe(b.card,b.deck.shared??!1)&&this.engine.isTaken(v)?h(v,"taken"):(this.cooldowns[v]??0)>u?h(v,"cooldown"):this.tagsMatch(b.card,d.boundTags)?this.passes(b.card.condition,p.get(b.deck.id),g?`card ${b.card.gameId} condition`:void 0)?!0:h(v,"condition"):h(v,"tags"):h(v,"deck-gate")});if(this.boardContents.set(o.id,x),g)for(let v of m)this.emit({type:"evict",hand:f(o),card:v.card,reason:v.reason},u)}let n=this.claims(),s=this.internals.hasShared?this.engine.sharedClaims():new Map;for(let{hand:o,box:i}of t){let a=this.boardContents.get(o.id)??[],d=this.handCapacity(o)-a.length;if(d<=0)continue;let l=this.askForHand(o,i),c=new Set(a),p=this.tracing?[]:void 0,{ordered:g}=this.runAsk(l,(h,x)=>c.has(h.id)?"claimed":this.claimVerdict(h,x,n,s),p),u=g.slice(0,d),m=u.map(h=>h.card.id);this.boardContents.set(o.id,[...a,...m]);for(let h of m)n.set(h,(n.get(h)??0)+1),s.set(h,(s.get(h)??0)+1);p&&(this.capTrace(p,new Set(u.map(h=>f(h.card)))),this.emit({type:"deal",hand:f(o),cards:p},this.turnCounts.get(i.id)??0))}return Object.fromEntries(t.map(({hand:o})=>[f(o),(this.boardContents.get(o.id)??[]).map(i=>this.view(this.internals.cardsById.get(i)))]))}board(e){this.assertOpen();let t=e!==void 0?this.resolveBox(e).id:void 0;return Object.fromEntries([...this.boardContents.entries()].filter(([n])=>t===void 0||this.internals.handsById.get(n).box.id===t).map(([n,s])=>[f(this.internals.handsById.get(n).hand),s.map(o=>this.view(this.internals.cardsById.get(o)))]))}resolveDealt(e,t){let n=this.internals.cardsById.get(e)??this.internals.cardsByGameId.get(e);if(!n)throw new w(`unknown card "${e}"`);let{hand:s,box:o}=this.resolveHand(t);if(!(this.boardContents.get(s.id)??[]).includes(n.card.id))throw new w(`card "${f(n.card)}" is not dealt to hand "${f(s)}"`);return{entry:n,ask:this.askForHand(s,o)}}outcomes(e,t){this.assertOpen();let{entry:n,ask:s}=this.resolveDealt(e,t),o=this.evalCtx(n.box,n.deck,this.buildHandEnv(s));return n.card.outcomes.map(i=>({id:i.id,gameId:f(i),...i.title!==void 0?{title:i.title}:{},...i.purpose!==void 0?{purpose:i.purpose}:{},...i.fields!==void 0?{fields:i.fields}:{},...i.extensions!==void 0?{extensions:i.extensions}:{},available:this.passes(i.condition,o)}))}play(e,t,n,s={}){this.assertOpen();let{entry:o,ask:i}=this.resolveDealt(e,n),a=t==="";if(a&&o.card.outcomes.length>0)throw new w(`card "${f(o.card)}" has outcomes (${o.card.outcomes.map(y=>f(y)).join(", ")}); name the one played`);let d=a?void 0:o.card.outcomes.find(y=>f(y)===t);if(!a&&!d)throw new w(`card "${f(o.card)}" has no outcome "${t}"`);let l=this.buildHandEnv(i),c=this.evalCtx(o.box,o.deck,l);if(d&&!this.passes(d.condition,c))throw new w(`outcome "${t}" on "${f(o.card)}" is gated shut`);let p=o.box.turn!==void 0?0:this.internals.bundle.settings.playAdvancesTurns,g=(this.turnCounts.get(o.box.id)??0)+(s.advanceTurns??p),m=Object.entries(d?.changes??{}).map(([y,P])=>({target:y,expr:P,plan:this.planWrite(y,o,l)})).map(y=>({...y,value:this.eval(y.expr,c)})),h=[];try{for(let y of m)h.push({target:y.target,plan:y.plan,value:y.value,prev:this.landWrite(y.plan,y.value)})}catch(y){for(let P of[...h].reverse())this.undoWrite(P.plan,P.prev);throw y}if(this.tracing)for(let{target:y,plan:P,value:T,prev:be}of h)this.emit({type:"write",target:y,path:P.path,value:T,...be!==void 0?{prev:be}:{}},g);let x=d?f(d):"",v={card:f(o.card),outcome:x,turn:g};this.playLog.push(v),this.indexPlay(v),o.card.redraw==="never"?pe(o.card,o.deck.shared??!1)?this.engine.markTaken(o.card.id):this.cooldowns[o.card.id]=Number.MAX_SAFE_INTEGER:typeof o.card.redraw=="number"&&(this.cooldowns[o.card.id]=g+o.card.redraw);let b=i.hand.id;this.boardContents.set(b,(this.boardContents.get(b)??[]).filter(y=>y!==o.card.id)),this.turnCounts.set(o.box.id,g),this.tracing&&this.emit({type:"play",card:f(o.card),outcome:x,turn:g},g)}address(e,t){return V(this.internals,e,t)}planBag(e,t,n,s){let o=e==="story"?this.stores.story:t!==void 0?this.stores[e].get(t):void 0,i=e==="story"?this.internals.shared.story:t!==void 0?this.internals.shared[e].get(t):void 0,a=L(o,n)!==void 0?o:L(i,n)!==void 0?i:void 0;if(a===void 0)throw new w(`no property at "${s}"`);if(a.declarations().some(d=>d.name===n&&d.writable===!1))throw new w(`'${n}' is read-only`);return{kind:"bag",bag:a,name:n,path:s}}planWrite(e,t,n){let s=/^@([a-z]+)\.([A-Za-z_][A-Za-z0-9_-]*)$/.exec(e);if(!s)throw new w(`bad change target "${e}"`);let[,o,i]=s;switch(o){case"world":{if(!this.internals.worldSet)throw new w(`@world.${i} cannot be written: the host bound @world read-only`);if(this.internals.worldReadOnly.has(i))throw new w(`'@world.${i}' is read-only (writable: false)`);return{kind:"world",name:i,path:`world.${i}`}}case"story":return this.planBag("story",void 0,i,`story.${i}`);case"box":return this.planBag("box",t.box.id,i,`${this.address("box",t.box.id)}.${i}`);case"deck":return this.planBag("deck",t.deck.id,i,`${this.address("deck",t.deck.id)}.${i}`);case"hand":{let a=n.sources.get(i);if(!a)throw new w(`@hand.${i} is not composed in this ask`);if(a.kind==="criteria")throw new w(`@hand.${i} is a chosen tag / criteria name and cannot be written`);return this.planBag(a.kind,a.id,i,`${this.address(a.kind,a.id)}.${i}`)}default:{if(this.internals.registry.has(o))return{kind:"scope",scope:o,name:i,path:`${o}.${i}`};throw this.internals.bundle.externalScopes?.includes(o)?new w(`@${o}.${i} cannot be written: no engine on this registry registered @${o}`):new w(`bad change target scope "@${o}"`)}}}landWrite(e,t){switch(e.kind){case"world":{let n=this.internals.worldResolver.get(e.name);return this.internals.worldSet(e.name,t),n}case"bag":return e.bag.set(e.name,t).prev;case"scope":{let n=this.internals.registry.get(e.scope,e.name);return this.internals.registry.set(e.scope,e.name,t),n}}}undoWrite(e,t){if(t!==void 0)switch(e.kind){case"world":this.internals.worldSet(e.name,t,!0);return;case"bag":e.bag.set(e.name,t,{silent:!0,reason:"play refused",host:!0});return;case"scope":this.internals.registry.set(e.scope,e.name,t,{host:!0});return}}advanceTurns(e,t=1){this.assertOpen();let n=this.resolveBox(e),s=(this.turnCounts.get(n.id)??0)+t;this.turnCounts.set(n.id,s),this.tracing&&this.emit({type:"turns",box:f(n),turn:s},s)}listBoxes(){return this.assertOpen(),this.internals.bundle.boxes.map(e=>({id:e.id,gameId:f(e),...e.title!==void 0?{title:e.title}:{},turn:this.turnCounts.get(e.id)??0,...e.extensions!==void 0?{extensions:e.extensions}:{}}))}listBags(){this.assertOpen();let e=[{prefix:"story",bag:this.stores.story}];for(let t of R)for(let[n,s]of this.stores[t])e.push({prefix:V(this.internals,t,n),bag:s});return e}listProperties(){this.assertOpen();let e=[];for(let n of this.internals.bundle.world.properties){let s=this.internals.worldResolver.get(n.name);e.push({path:`world.${n.name}`,name:n.name,type:n.type,value:s??O(n),default:O(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:this.internals.worldSet!==void 0&&!this.internals.worldReadOnly.has(n.name)})}let t=(n,s,o)=>{for(let i of[s,o])if(i!==void 0)for(let a of i.rows())e.push(a)};t("story",this.internals.shared.story,this.stores.story);for(let n of R){let s=new Set([...this.internals.shared[n].keys(),...this.stores[n].keys()]);for(let o of s)t(V(this.internals,n,o),this.internals.shared[n].get(o),this.stores[n].get(o))}return e}getProperty(e){this.assertOpen();let t=this.resolvePath(e),n=t.kind==="world"?this.internals.worldResolver.get(t.name):t.kind==="scope"?this.internals.registry.get(t.token,t.name):L(t.own,t.name)??L(t.shared,t.name);if(n===void 0)throw new w(`no property at "${e}"`);return n}setProperty(e,t){this.assertOpen();let n=this.resolvePath(e);if(n.kind==="world"){if(!this.internals.worldSet)throw new w("@world is read-only here: the host bound no write");this.internals.worldSet(n.name,t,!0);return}if(n.kind==="scope"){this.internals.registry.set(n.token,n.name,t,{host:!0});return}let s=L(n.own,n.name)!==void 0?n.own:L(n.shared,n.name)!==void 0?n.shared:void 0;if(s===void 0)throw new w(`no property at "${e}"`);s.set(n.name,t,{silent:!0,reason:"host setProperty",host:!0})}resolvePath(e){let t=e.split(".");if(t.length===2&&t[0]==="world")return{kind:"world",name:t[1]};if(t.length===2&&t[0]!=="story"&&this.internals.registry.has(t[0]))return{kind:"scope",token:t[0],name:t[1]};if(t.length===2&&t[0]==="story")return{kind:"bag",own:this.stores.story,shared:this.internals.shared.story,name:t[1]};if(t.length===3&&Xt(t[0])){let n=t[0],[,s,o]=t,i=en(this.internals,n,s,o);i.legacy&&this.tracing&&this.emit({type:"diagnostic",where:"property address",message:tn(this.internals,n,s,o)});let a=this.stores[n].get(i.id),d=this.internals.shared[n].get(i.id);if(a===void 0&&d===void 0)throw new w(`no ${n} store "${s}"`);return{kind:"bag",...a!==void 0?{own:a}:{},...d!==void 0?{shared:d}:{},name:o}}throw new w(`bad property path "${e}"`)}saveDurable(){this.assertOpen();let{bundle:e,durable:t}=this.internals;return structuredClone({schema:fe,content:e.content,values:Yt(this.stores,t.flow),spent:t.flowCards.filter(n=>this.cooldowns[n.card.id]===Number.MAX_SAFE_INTEGER).map(n=>f(n.card)).sort()})}writeDurable(e){for(let[t,n]of e.values)Zt(rt(this.stores,t),t.decl.name,n);for(let t of e.cards)this.cooldowns[t]=Number.MAX_SAFE_INTEGER}snapshot(e){return{...e?{props:dr(this.stores)}:{},turns:Object.fromEntries(this.turnCounts),prng:this.prng.state(),cooldowns:this.cooldowns,board:Object.fromEntries(this.boardContents),playLog:this.playLog}}restore(e){e.props!==void 0&&lr(this.stores,e.props),this.turnCounts=new Map(this.internals.bundle.boxes.map(t=>[t.id,0]));for(let[t,n]of Object.entries(e.turns??{}))this.turnCounts.has(t)&&this.turnCounts.set(t,n);this.prng=H(e.prng),this.cooldowns=e.cooldowns??{},this.playLog=e.playLog??[],this.rebuildPlayIndex(),this.boardContents=new Map(Object.entries(e.board??{}).filter(([t])=>this.internals.handsById.has(t)).map(([t,n])=>[t,n.filter(s=>this.internals.cardsById.has(s))]));for(let t of this.internals.handsById.keys())this.boardContents.has(t)||this.boardContents.set(t,[])}};var $e=r=>r.map(e=>({name:e.name,type:e.type,default:O(e),...e.values!==void 0?{values:e.values}:{},...e.durable===!0?{durable:!0}:{},...e.purpose!==void 0?{purpose:e.purpose}:{}})),rn=r=>r.decks.reduce((e,t)=>e+t.cards.filter(n=>(n.durable??t.durable)===!0).length,0),pr=(r,e)=>r.template!==void 0?e.handTemplates.find(t=>t.id===r.template)?.properties??[]:r.properties??[],ur=(r,e,t)=>{let n=e.template!==void 0?e.chosen:e.rule?.bindings,s=[];for(let[o,i]of Object.entries(n??{})){if(!Se(i))continue;let a=se(r,t).find(d=>d.id===o);a!==void 0&&s.push({group:f(a),from:i})}return s},gr=(r,e)=>{if(r.slots!==void 0)return r.slots;let t=r.template!==void 0?e.handTemplates.find(n=>n.id===r.template)?.slots:r.rule?.slots;return t===void 0?"unbounded":t};function Be(r){let e=[],t=[],n=[{scope:"world",owner:"",properties:$e(r.world.properties)},{scope:"story",owner:"",properties:$e(r.story.properties)}],s={boxes:0,decks:0,cards:0,hands:0,templates:0,tagGroups:0};for(let i of r.boxes){let a=f(i),d=i.decks.reduce((c,p)=>c+p.cards.length,0);e.push({gameId:a,...i.title!==void 0?{title:i.title}:{},...i.usesMap===!0?{usesMap:!0}:{},ranking:{specificity:i.ranking.specificity},...i.turn!==void 0?{turn:{seconds:i.turn.seconds}}:{},...rn(i)>0?{durableCards:rn(i)}:{},tagGroups:i.tagGroups.map(c=>({gameId:f(c),tags:c.tags.map(p=>f(p))})),counts:{decks:i.decks.length,cards:d,hands:i.hands.length,templates:i.handTemplates.length,tagGroups:i.tagGroups.length}}),s.boxes+=1,s.decks+=i.decks.length,s.cards+=d,s.hands+=i.hands.length,s.templates+=i.handTemplates.length,s.tagGroups+=i.tagGroups.length;for(let c of i.hands){let p=c.template!==void 0?i.handTemplates.find(u=>u.id===c.template):void 0,g=ur(r,c,i);t.push({gameId:f(c),...c.title!==void 0?{title:c.title}:{},box:a,slots:gr(c,i),...p!==void 0?{template:f(p)}:{},...g.length>0?{movable:g}:{}})}let l=(c,p,g,u)=>{g.length!==0&&n.push({scope:c,owner:p,box:a,...u!==void 0?{group:u}:{},properties:$e(g)})};l("box",a,i.properties);for(let c of i.decks)l("deck",f(c),c.properties);for(let c of i.hands)l("hand",f(c),pr(c,i));for(let c of i.tagGroups)for(let p of c.tags)l("tag",f(p),p.properties??[],f(c))}let o=r.map;if(o!==void 0){let i=f(o.group);for(let a of o.group.tags){let d=a.properties??[];d.length>0&&n.push({scope:"tag",owner:f(a),group:i,properties:$e(d)})}s.tagGroups+=1}return{identity:{schema:r.schema,project:r.content.project,version:r.content.version,hash:r.content.hash,metadata:r.metadata},totals:s,boxes:e,hands:t,properties:n,...o!==void 0?{map:{group:f(o.group),tags:o.group.tags.map(i=>f(i)),boxes:r.boxes.filter(i=>i.usesMap===!0).map(i=>f(i)),zones:o.geometry?.zones.length??0,backgrounds:o.geometry?.backgrounds?.length??0,sites:Object.fromEntries(Object.entries(o.geometry?.sites??{}).map(([i,a])=>[i,a.length]))}}:{}}}var sn=(r,e)=>r.boxes.find(t=>t.gameId===e)??r.boxes.find(t=>t.id===e),he=(r,e)=>Object.prototype.hasOwnProperty.call(r,e);function on(r,e){return sn(r,e)?.fields??[]}function an(r,e){return sn(r,e)?.outcomeFields??[]}function st(r,e,t){return e!==void 0&&he(e,t)?e[t]:r.find(n=>n.name===t)?.default}function dn(r,e){let t={};for(let n of r){let s=st(r,e,n.name);s!==void 0&&(t[n.name]=s)}for(let[n,s]of Object.entries(e??{}))he(t,n)||(t[n]=s);return t}function ln(r,e,t){let n=r.extensions!==void 0&&he(r.extensions,e)?r.extensions[e]:void 0;return n!==void 0&&he(n.fields,t)?n.fields[t]??[]:[]}function cn(r,e){return{...(r.extensions!==void 0&&he(r.extensions,e)?r.extensions[e]:void 0)?.settings??{}}}function pn(r,e,t=""){let n=d=>({...d?.extensions??{}}),[s,o]=t.includes("/")?[t.slice(0,t.indexOf("/")),t.slice(t.indexOf("/")+1)]:[t,void 0],i=(d,l)=>d.find(c=>(c.gameId??c.id)===l),a=()=>r.boxes.flatMap(d=>d.decks.flatMap(l=>l.cards));switch(e){case"project":{let d={};for(let[l,c]of Object.entries(r.extensions??{}))c.project!==void 0&&(d[l]=c.project);return d}case"box":return n(i(r.boxes,t));case"deck":return n(i(r.boxes.flatMap(d=>d.decks),t));case"card":return n(i(a(),t));case"outcome":return o===void 0?{}:n(i(i(a(),s)?.outcomes??[],o));case"hand":return n(i(r.boxes.flatMap(d=>d.hands),t));case"handTemplate":return o===void 0?{}:n(i(i(r.boxes,s)?.handTemplates??[],o));case"tagGroup":return o===void 0?r.map!==void 0&&(r.map.group.gameId??r.map.group.id)===s?n(r.map.group):{}:n(i(i(r.boxes,s)?.tagGroups??[],o));case"tag":return n(o===void 0?r.map!==void 0?i(r.map.group.tags,s):void 0:i((i(r.boxes,s)?.tagGroups??[]).flatMap(d=>d.tags),o));default:return{}}}function un(r,e){let t={};for(let{bag:n}of[...r.listBags(),...e.listBags()])for(let s of n.rows())s.value!==void 0&&(t[s.path]=s.value);return Object.assign(t,fn(gn(r,e.id))),t}function gn(r,e){return r.getFlow(e)!==void 0?r.saveFlow(e):void 0}function fn(r){let e={};if(r===void 0)return e;for(let[t,n]of Object.entries(r.turns))e[`turn:${t}`]=n;for(let[t,n]of Object.entries(r.cooldowns))e[`cooldown:${t}`]=n;for(let[t,n]of Object.entries(r.board))e[`board:${t}`]=[...n];return e}function hn(r,e,t={}){let n=e.id,s=()=>r.getFlow(n);return de({mounts:()=>[...r.listBags(),...s()?.listBags()??[]].map(({bag:o})=>({bag:o})),extra:()=>fn(gn(r,n))},t)}function Me(r,e){return JSON.stringify(ot(r,e),null,2)}function ot(r,e){return{schema:Ee,engine:r.saveGame(),...e!==void 0?{world:e}:{}}}function it(r,e,t={}){if(!e||typeof e!="object"||e.schema!==Ee||e.engine?.schema!==oe&&e.engine?.schema!==ie)throw new Error(`not a storylets save (expected schema "${Ee}")`);let n=r.loadGame(e.engine);return t.onReport?.(n),e.world}function Re(r,e,t={}){let n;try{n=JSON.parse(e)}catch{throw new Error("not valid JSON")}return it(r,n,t)}function fr(r,e){r.textContent="";let t=/\u0002([^\u0003]*)\u0003/g,n=0,s;for(;(s=t.exec(e))!==null;){s.index>n&&r.append(e.slice(n,s.index));let o=document.createElement("strong");o.textContent=s[1]??"",r.append(o),n=s.index+s[0].length}n<e.length&&r.append(e.slice(n))}var lt=!1,mn=!1,xn,hr=350,at=6,yn=7,Y=null,Z=0,Oe=null,mr=null;function Sn(){if(Y)return Y;let r=document.createElement("div");return r.className="tooltip",r.setAttribute("role","tooltip"),r.hidden=!0,document.body.appendChild(r),Y=r,r}var yr=r=>r instanceof HTMLElement?r.getBoundingClientRect():r;function br(r){let e=Sn(),t=yr(r),n=e.getBoundingClientRect(),s=t.top-n.height-yn;s<at&&(s=t.top+t.height+yn);let o=t.left+t.width/2-n.width/2;o=Math.max(at,Math.min(o,window.innerWidth-n.width-at)),e.style.left=`${Math.round(o)}px`,e.style.top=`${Math.round(s)}px`}function wr(r){let e=r.dataset.tip;e&&vr(r,e)}function vr(r,e){if(xn?.())return;let t=Sn(),n=(r instanceof HTMLElement?r.closest("dialog[open]"):null)??document.body;t.parentElement!==n&&n.appendChild(t),Oe=r instanceof HTMLElement?r:null,fr(t,e),t.hidden=!1,t.classList.remove("show"),t.offsetWidth,br(r),t.classList.add("show")}function X(){Z&&(window.clearTimeout(Z),Z=0),Oe=null,mr=null,Y&&(Y.hidden=!0,Y.classList.remove("show"))}function bn(r){r!==Oe&&(Z&&window.clearTimeout(Z),Z=window.setTimeout(()=>wr(r),hr))}function xr(){Er()}function Sr(){mn||(mn=!0,setTimeout(()=>{lt||typeof document>"u"||document.querySelector("[data-tip]")&&console.warn("app-shell: something set `data-tip` but no tooltip host is mounted, so those rollovers will not draw. Call initTooltips() once per window.")},0))}var dt=r=>r instanceof Element?r.closest("[data-tip]"):null;function Er(r={}){r.suppressed!==void 0&&(xn=r.suppressed),!lt&&(lt=!0,document.addEventListener("pointerover",e=>{let t=dt(e.target);t&&bn(t)}),document.addEventListener("pointerout",e=>{let t=dt(e.target);if(!t)return;let n=e.relatedTarget;n instanceof Node&&t.contains(n)||X()}),document.addEventListener("focusin",e=>{let t=dt(e.target);t&&bn(t)}),document.addEventListener("focusout",()=>X()),document.addEventListener("pointerdown",()=>X(),!0),document.addEventListener("scroll",()=>{Oe&&X()},!0),document.addEventListener("keydown",e=>{e.key==="Escape"&&X()}),window.addEventListener("blur",()=>X()))}var kr={close:"\u2715",more:"\u22EF",collapsed:"\u25B8",expanded:"\u25BE",dropdown:"\u25BE",back:"\u2039",forward:"\u203A",up:"\u2191",down:"\u2193",tick:"\u2713",locked:"\u2298",readOnly:"\u25CB",checkedOut:"\u270E",modified:"\u25CF",untracked:"+",warning:"\u203C",dot:"\u25CF",play:"\u25B6",restart:"\u27F2",viewCards:"\u25A6",viewTable:"\u2630",viewNode:"\u25C8",grip:"\u283F",add:"+",note:"\u270E",comment:"\u275D"},Pr='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.571" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">',Cr='<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">',E=r=>`${Pr}${r}</svg>`,En=r=>`${Cr}${r}</svg>`,wn=En('<circle cx="12" cy="12" r="6"/>'),$r=En('<circle cx="12" cy="12" r="9"/>'),kn={close:E('<path d="M18 6 6 18"/><path d="m6 6 12 12"/>'),more:E('<circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/>'),collapsed:E('<path d="m9 18 6-6-6-6"/>'),expanded:E('<path d="m6 9 6 6 6-6"/>'),dropdown:E('<path d="m6 9 6 6 6-6"/>'),back:E('<path d="m15 18-6-6 6-6"/>'),forward:E('<path d="m9 18 6-6-6-6"/>'),up:E('<path d="m5 12 7-7 7 7"/><path d="M12 19V5"/>'),down:E('<path d="M12 5v14"/><path d="m19 12-7 7-7-7"/>'),arrowLeft:E('<path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>'),arrowRight:E('<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>'),tick:E('<path d="M20 6 9 17l-5-5"/>'),locked:E('<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>'),readOnly:E('<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 9.9-1"/>'),checkedOut:E('<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/>'),modified:wn,untracked:E('<path d="M5 12h14"/><path d="M12 5v14"/>'),warning:E('<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/>'),dot:wn,play:E('<path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z"/>'),restart:E('<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/>'),viewCards:E('<rect width="7" height="7" x="3" y="3" rx="1"/><rect width="7" height="7" x="14" y="3" rx="1"/><rect width="7" height="7" x="14" y="14" rx="1"/><rect width="7" height="7" x="3" y="14" rx="1"/>'),viewTable:E('<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M21 9H3"/><path d="M21 15H3"/>'),viewNode:E('<path d="m10.586 5.414-5.172 5.172"/><path d="m18.586 13.414-5.172 5.172"/><path d="M6 12h12"/><circle cx="12" cy="20" r="2"/><circle cx="12" cy="4" r="2"/><circle cx="20" cy="12" r="2"/><circle cx="4" cy="12" r="2"/>'),record:$r,speaker:E('<path d="M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z"/><path d="M16 9a5 5 0 0 1 0 6"/><path d="M19.364 18.364a9 9 0 0 0 0-12.728"/>'),grip:E('<circle cx="9" cy="12" r="1"/><circle cx="9" cy="5" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="15" cy="19" r="1"/>'),add:E('<path d="M5 12h14"/><path d="M12 5v14"/>'),copy:E('<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>'),connect:E('<path d="M12 22v-5"/><path d="M15 8V2"/><path d="M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z"/><path d="M9 8V2"/>'),search:E('<path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/>'),pin:E('<path d="M12 17v5"/><path d="M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/>'),settings:E('<path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"/><circle cx="12" cy="12" r="3"/>'),note:E('<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/>'),comment:E('<path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"/>')},Ws={...kn,noteFilled:'<svg viewBox="0 0 16 16" width="13" height="13" fill="currentColor" fill-rule="evenodd" aria-hidden="true"><path d="M3.4 1.4h5.3L12.6 5.3v8.1a1.2 1.2 0 0 1-1.2 1.2H3.4a1.2 1.2 0 0 1-1.2-1.2V2.6A1.2 1.2 0 0 1 3.4 1.4ZM4.9 6.8h6.2v1.1H4.9Zm0 2.1h6.2v1.1H4.9Zm0 2.1h3.7v1.1H4.9Z"/></svg>',noteOutline:'<svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linejoin="round" stroke-linecap="round" aria-hidden="true"><path d="M3.4 2h4.9l3.9 3.9v7.5a1 1 0 0 1-1 1H3.4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Z"/><path d="M8.3 2.2v3.8h3.8"/><path d="M4.9 8h6.2M4.9 10h6.2M4.9 12h3.7" stroke-width="0.95"/></svg>'},Us=Object.keys(kn);var vn=new Map;for(let[r,e]of Object.entries(kr))vn.has(e)||vn.set(e,r);function Pn(r,e,...t){let n=document.createElement(r),s=typeof e=="string"?{className:e}:e??{};s.className&&(n.className=s.className),s.text!==void 0&&s.text!==null&&(n.textContent=s.text),s.title!==void 0&&(n.title=s.title),s.tip!==void 0&&(n.dataset.tip=s.tip,n.setAttribute("aria-label",s.tip),xr()),s.onClick&&n.addEventListener("click",s.onClick);for(let o of t)o!=null&&n.append(o);return Sr(),n}function Cn(r){let e=r.default;if(e!=null)return typeof e=="boolean"?e?"True":"False":Array.isArray(e)?e.length===0?"none":e.join(", "):e===""?"none":String(e);switch(r.type){case"boolean":return"False";case"number":return"0";case"enum":return r.values?.[0]??"none";case"quality":return r.stages?.[0]??"first stage";case"flags":return"none";default:return"empty"}}var $n=r=>`default (${Cn(r)})`;function ct(r){let e=r.doc===void 0?Pn:(a,d)=>{let l=r.doc.createElement(a);return l.className=d.className,d.text!==void 0&&(l.textContent=d.text),d.tip!==void 0&&(l.title=d.tip,l.setAttribute("aria-label",d.tip)),d.onClick&&l.addEventListener("click",d.onClick),a==="button"&&(l.type="button"),l},t=e("span",{className:"seg shell-rungs"}),n=r.decl??{type:"quality",stages:r.stages},s=r.value===void 0,o=typeof n.default=="string"&&r.stages.includes(n.default)?n.default:r.stages[0],i=r.onPick===void 0;r.onClear&&t.append(e("button",{className:`seg-opt shell-rung shell-rung-default${s?" on":""}`,text:$n(n),tip:s?"Not set here: the declared default holds":"Clear the value, back to the default",onClick:()=>{s||r.onClear()}}));for(let a of r.stages){let d=r.value===a,l=s&&a===o,c=e(i?"span":"button",{className:`seg-opt shell-rung${d?" on":""}${l?" fallback":""}`,text:a,tip:d?"The current stage":l?"The stage it falls back to":i?a:r.pickTip?.(a)??`Set to "${a}"`,...i?{}:{onClick:()=>{d||r.onPick(a)}}});t.append(c)}return!s&&!r.stages.includes(r.value)&&t.append(e("span",{className:"seg-opt shell-rung stray on",text:r.value,tip:`"${r.value}" is not one of the stages: pick the one it belongs at now`})),t}var Bn="sl-inspector-style",Br=`
|
|
1
|
+
"use strict";var StoryletEngine=(()=>{var We=Object.defineProperty;var zn=Object.getOwnPropertyDescriptor;var Un=Object.getOwnPropertyNames;var Kn=Object.prototype.hasOwnProperty;var Jn=(n,e)=>{for(var t in e)We(n,t,{get:e[t],enumerable:!0})},Qn=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Un(e))!Kn.call(n,s)&&s!==t&&We(n,s,{get:()=>e[s],enumerable:!(r=zn(e,s))||r.enumerable});return n};var Xn=n=>Qn(We({},"__esModule",{value:!0}),n);var Zr={};Jn(Zr,{DURABLE_SCHEMA:()=>we,Engine:()=>Ie,Flow:()=>be,StoryletError:()=>b,applyLiveBundle:()=>qn,boardFrame:()=>kt,compareStages:()=>Qe,createBundleInspector:()=>_n,createKernelStateLogger:()=>fe,createLiveLink:()=>Gn,createPropertyInspector:()=>Fn,createStateLogger:()=>Ln,createWorldContainer:()=>Wn,describeBundle:()=>De,deserializeState:()=>He,diffState:()=>ge,effectiveFields:()=>kn,ensureInspectorStyle:()=>ve,extensionDecls:()=>Pn,extensionsOf:()=>Cn,fieldDecls:()=>Sn,fieldValue:()=>ft,formatLogEntry:()=>xt,formatPropertySummary:()=>St,formatScopeLabel:()=>Et,loadState:()=>mt,makePrng:()=>T,nextStage:()=>Xe,outcomeFieldDecls:()=>En,saveState:()=>ht,serializeState:()=>Ne,shuffleInPlace:()=>_,snapshotState:()=>$n,stageIndex:()=>Je});function le(n,e){return n!==void 0&&Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0}function j(n){switch(n[0]){case"b":return{kind:"bool",value:n[1]};case"n":return{kind:"number",value:n[1]};case"s":return{kind:"string",value:n[1]};case"sv":return{kind:"scopedvar",scope:n[1],name:n[2]};case"u":return{kind:"unary",op:n[1],operand:j(n[2])};case"bin":return{kind:"binary",op:n[1],left:j(n[2]),right:j(n[3])};case"call":{let e=n.slice(2).map(j);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var v=class extends Error{constructor(e){super(e),this.name="EvalError"}};function Ee(n,e,t){let r=new Map(t.scopes.map(o=>[o.token,o.missing??"false"])),s=o=>{switch(o.kind){case"bool":return o.value;case"number":return o.value;case"string":return o.value;case"scopedvar":{let i=le(e.scopes,o.scope);if(i===void 0)return!1;let a=typeof i.get=="function"?i.get(o.name):le(i,o.name);if(a===void 0){if(r.get(o.scope)==="throw")throw new v(`@${o.scope}.${o.name} is not declared on the current ${o.scope}.`);return!1}return a}case"call":{if(o.name==="advance"&&!le(t.functions,o.name)){let a=o.args[0];if(o.args.length!==1||a===void 0)throw new v(`advance() takes exactly 1 argument, got ${o.args.length}`);let d=ze(a,e);if(d===void 0)throw new v("advance() needs a quality reference (@scope.name of a quality property)");let l=D(s(a),d,"advance");return d[Math.min(l+1,d.length-1)]}let i=le(t.functions,o.name);if(!i)throw new v(`unknown function '${o.name}'`);return i.eval(o.args,{evaluate:s,ctx:e})}case"flagdelta":throw new v("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(o.op==="not"){let a=s(o.operand);if(typeof a!="boolean")throw new v(`'not' requires a boolean operand, got ${typeof a}`);return!a}let i=s(o.operand);if(typeof i!="number")throw new v(`unary '-' requires a numeric operand, got ${typeof i}`);return-i}case"binary":{if(o.op==="and"){let p=s(o.left);if(typeof p!="boolean")throw new v(`'and' requires boolean operands, left is ${typeof p}`);if(!p)return!1;let u=s(o.right);if(typeof u!="boolean")throw new v(`'and' requires boolean operands, right is ${typeof u}`);return u}if(o.op==="or"){let p=s(o.left);if(typeof p!="boolean")throw new v(`'or' requires boolean operands, left is ${typeof p}`);if(p)return!0;let u=s(o.right);if(typeof u!="boolean")throw new v(`'or' requires boolean operands, right is ${typeof u}`);return u}let i=s(o.left),a=s(o.right),d=ze(o.left,e),l=ze(o.right,e),c=d??l;if(c!==void 0){if(d&&l&&!Yn(d,l)&&(o.op===">"||o.op===">="||o.op==="<"||o.op==="<="))throw new v(`'${o.op}' compares two different qualities, whose stage orders are unrelated`);switch(o.op){case">":return D(i,c,">")>D(a,c,">");case">=":return D(i,c,">=")>=D(a,c,">=");case"<":return D(i,c,"<")<D(a,c,"<");case"<=":return D(i,c,"<=")<=D(a,c,"<=");case"+":case"-":case"*":case"/":throw new v(`'${o.op}' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(o.op){case"==":return Mt(i,a);case"!=":return!Mt(i,a);case">":return F(i,a,">"),i>a;case">=":return F(i,a,">="),i>=a;case"<":return F(i,a,"<"),i<a;case"<=":return F(i,a,"<="),i<=a;case"+":if(typeof i=="number"&&typeof a=="number"||typeof i=="string"&&typeof a=="string")return i+a;throw new v(`'+' requires two numbers or two strings, got ${typeof i} and ${typeof a}`);case"-":return F(i,a,"-"),i-a;case"*":return F(i,a,"*"),i*a;case"/":if(F(i,a,"/"),a===0)throw new v("division by zero");return i/a}}}};return s(n)}function Mt(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;let t=[...n].sort(),r=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==r[s])return!1;return!0}return n===e}function F(n,e,t){if(typeof n!="number"||typeof e!="number")throw new v(`'${t}' requires numeric operands, got ${typeof n} and ${typeof e}`)}function ze(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function D(n,e,t){if(typeof n!="string")throw new v(`'${t}' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new v(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var Yn=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function It(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function T(n){let e=It(n);return{next(){e=e+1831565813>>>0;let t=Math.imul(e^e>>>15,1|e);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296},state(){return e}}}function _(n,e){for(let t=n.length-1;t>0;t--){let r=Math.floor(e.next()*(t+1));[n[t],n[r]]=[n[r],n[t]]}}var Ke=n=>Array.isArray(n)?n:n.stages??[],Zn=n=>Array.isArray(n)?"flags":n==null?"nothing":typeof n;function Je(n,e){return typeof e=="string"?Ke(n).indexOf(e):-1}function Ue(n,e){if(typeof e!="string")throw new v(`a stage is a name, got ${Zn(e)}`);let t=n.indexOf(e);if(t<0)throw new v(`"${e}" is not a stage of this quality (stages: ${n.join(", ")})`);return t}function Qe(n,e,t){let r=Ke(n),s=Ue(r,e)-Ue(r,t);return s<0?-1:s>0?1:0}function Xe(n,e){let t=Ke(n);return t[Math.min(Ue(t,e)+1,t.length-1)]}function G(n,e,t){switch(n.type){case"boolean":return typeof e=="boolean";case"number":return typeof e=="number";case"string":case"text":case"multiline":return typeof e=="string";case"enum":return typeof e!="string"?!1:e===""&&t?.noneChosen?!0:!Array.isArray(n.values)||n.values.includes(e);case"flags":return Array.isArray(e)&&e.every(r=>typeof r=="string"&&(!Array.isArray(n.values)||n.values.includes(r)));case"quality":return typeof e=="string"&&(!Array.isArray(n.stages)||n.stages.includes(e));default:return!0}}var er={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},tr=[er];function At(n,e,t){let r=t?.countingCalls??tr;return ke(n,t?.want??!0,e,r)}function ke(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=ke(n.left,e,t,r),o=ke(n.right,e,t,r);return n.op==="and"===e?s>0&&o>0?s+o:0:Math.max(s,o)}if(n.kind==="unary"&&n.op==="not")return ke(n.operand,!e,t,r);if(n.kind==="call"){let s=r.find(o=>o.name===n.name);if(s){let o=s.count(n),i=t(n);return e?i?o:0:i?0:1}}return t(n)===e?1:0}var Dt=[{token:"patter",engine:"Patterplay",means:"Patter's shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine's shared @story properties"}];var Pe=9999,ce=n=>n.ctx.host??{},Y=(n,e,t,r)=>{let s=t.evaluate(e[r]);if(typeof s!="string"||s==="")throw new v(`${n}() argument ${r+1} must be a non-empty string`);return s},Tt=(n,e,t)=>{if(e.length===0)throw new v(`${n}() requires at least one argument (the flags property)`);let r=t.evaluate(e[0]);if(Array.isArray(r))return r;if(r===!1)return[];throw new v(`${n}() first argument must be a flags property`)},Nt=["story","world","box","deck","hand"],nr=Dt.map(n=>n.token).filter(n=>!Nt.includes(n)),Ye={scopes:[...Nt.map(n=>({token:n,missing:"throw"})),...nr.map(n=>({token:n,missing:"throw"}))],defaultScope:"story",functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new v("random(a, b) requires exactly 2 arguments");let t=ce(e).nextRandom;if(!t)throw new v("random() called without a PRNG in context");let r=e.evaluate(n[0]),s=e.evaluate(n[1]);if(typeof r!="number"||typeof s!="number")throw new v("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new v("random(a, b) arguments must be integers");let o=Math.min(r,s),i=Math.max(r,s);return Math.floor(t()*(i-o+1))+o}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,eval(n,e){let t=Tt("check_flags",n,e);for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new v("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,eval(n,e){let t=[...Tt("set_flags",n,e)];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new v("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let o=t.indexOf(s.name);o>=0&&t.splice(o,1)}}return t.sort()}},count_played:{minArgs:1,maxArgs:1,returnType:"number",eval(n,e){let t=Y("count_played",n,e,0),r=ce(e).countPlayed;if(!r)throw new v("count_played() called without a play log in context");return r(t)}},turns_since_played:{minArgs:1,maxArgs:1,returnType:"number",eval(n,e){let t=Y("turns_since_played",n,e,0),r=ce(e).turnsSincePlayed;if(!r)throw new v("turns_since_played() called without a play log in context");return r(t)}},count_played_in:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){let t=Y("count_played_in",n,e,0),r=Y("count_played_in",n,e,1),s=ce(e).countPlayedIn;if(!s)throw new v("count_played_in() called without a play log in context");return s(t,r)}},turns_since_played_in:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){let t=Y("turns_since_played_in",n,e,0),r=Y("turns_since_played_in",n,e,1),s=ce(e).turnsSincePlayedIn;if(!s)throw new v("turns_since_played_in() called without a play log in context");return s(t,r)}}}};function Ht(n){return n.toLowerCase().replace(/['’]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function Z(n){if(n.scopeRegistry!==void 0)return n.scopeRegistry.scopes;let e=n.world?.properties;return e!==void 0&&e.length>0?[{token:"world",declarations:e}]:[]}function rr(n,e){return Z(n).find(t=>t.token===e)}function Vt(n,e){return rr(n,e)?.declarations??[]}function f(n){let e=n.gameId?.trim();return e||(n.title?Ht(n.title):"")||n.id}function jt(n){let e=[];for(let a of n.boxes){let d=f(a);for(let l of a.tagGroups)for(let c of l.tags){let p=f(c);e.push({id:c.id,gameId:p,qualified:`${d}/${p}`})}}let t=new Map;for(let a of e){let d=t.get(a.gameId)??[];d.includes(a.qualified)||d.push(a.qualified),t.set(a.gameId,d)}let r=new Map,s=new Map,o=new Map,i=new Map;for(let a of e){let d=t.get(a.gameId)??[a.qualified],l=d.length>1;r.set(a.id,l?a.qualified:a.gameId),s.has(a.qualified)||s.set(a.qualified,a.id),!l&&!s.has(a.gameId)&&s.set(a.gameId,a.id),l&&o.set(a.gameId,d)}for(let a of n.map?.group.tags??[]){let d=f(a);r.set(a.id,d),s.has(d)||s.set(d,a.id);for(let l of n.boxes)i.set(`${f(l)}/${d}`,d)}return{print:r,accept:s,repeated:o,zoneQualified:i}}function Ft(n,e,t){return`"value.${n}.${t}": "${e}" is a zone of the project map, which belongs to no box; write "value.${e}.${t}"`}function _t(n,e,t){let r=t.map(o=>`"value.${o}.${e}"`),s=r.length<=1?r[0]??"":`${r.slice(0,-1).join(", ")} or ${r[r.length-1]}`;return`"value.${n}.${e}" names a tag in ${t.length} boxes; write ${s}`}var I="place",sr=/^@(hand|world|story)\.([a-z][a-z0-9_-]*)$/,Ce=n=>n.startsWith("@"),Gt=n=>{let e=sr.exec(n);return e===null?void 0:{scope:e[1],name:e[2]}},Ze="storylets/bundle@2",or="storylets/bundle@1",ir="storylets/bundle@0",pe=[ir,or,Ze];function ue(n,e){return e.usesMap===!0&&n.map!==void 0?[...e.tagGroups,n.map.group]:e.tagGroups}function qt(n){let e=n.boxes.flatMap(t=>t.tagGroups);return n.map!==void 0?[...e,n.map.group]:e}var q="storylets/save@3",ee="storylets/save@2",W="storylets/save@1",$e="storylets/savefile@1";var ar={project:".storyletproj",box:".storyletbox",tags:".storylettags",hands:".storylethands",deck:".storyletdeck",view:".storyletview",map:".storyletmap",notes:".storyletnotes",contract:".storyletcontract",extension:".storyletextension"},dr=".extension.json";var Rs=[...Object.values(ar),dr];function ge(n,e){let t=[],r=new Set([...Object.keys(n),...Object.keys(e)]);for(let s of[...r].sort()){let o=n[s],i=e[s];JSON.stringify(o)!==JSON.stringify(i)&&t.push({path:s,from:o,to:i})}return t}var Wt=n=>n===void 0?"<unset>":JSON.stringify(n),zt=n=>n.pathPrefix??n.bag.pathPrefix;function fe(n,e={}){let t=e.sink??(p=>console.log(p)),r=e.label??"",s=p=>{t(`${r}${p.path}: ${Wt(p.from)} -> ${Wt(p.to)}`)},o=()=>{let p={};for(let u of n.mounts()){let g=zt(u);for(let[m,h]of Object.entries(u.bag.values))p[g+m]=h}return Object.assign(p,n.extra?.()??{}),structuredClone(p)},i=o(),a=[],d=[],l=(p,u)=>u.onAudit(g=>{let m=structuredClone({path:p+g.name,from:g.prev,to:g.next});s(m),a.push(m),i[m.path]=structuredClone(g.next)}),c=()=>{let p=n.mounts();if(!(d.length===p.length&&p.every((g,m)=>d[m].bag===g.bag))){for(let g of d)g.off();d=p.map(g=>({bag:g.bag,off:l(zt(g),g.bag)}))}};return c(),{snapshot:o,capture(){let p=o(),u=ge(i,p);for(let m of u)s(m);let g=[...a,...u];return a=[],i=p,c(),g},dispose(){for(let p of d)p.off();d=[],a=[]}}}var z=class n{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let r=this.norm(t.name);this.decls.set(r,t),this.values[r]=structuredClone(t.default??N(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,r){let s=this.norm(e);if(!r?.host&&!this.writable(s))throw new Error(`'${e}' is read-only`);let o={name:s,prev:this.values[s],next:t,silent:r?.silent??!1,reason:r?.reason};this.values[s]=t;for(let i of[...this.auditors])i(o);if(!o.silent)for(let i of[...this.subscribers])i(o);return o}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}onAudit(e){return this.auditors.add(e),()=>this.auditors.delete(e)}rows(){return[...this.decls.entries()].map(([e,t])=>Kt(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm,pathPrefix:this.pathPrefix});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e,t){let r=new Map;for(let[o,i]of Object.entries(e)){let a=this.norm(o);this.values[a]=structuredClone(i),r.set(a,i)}let s=[];if(!t?.refit)return s;for(let[o,i]of this.decls){if(!r.has(o))continue;let a=r.get(o);G(i,a,{noneChosen:i.type==="enum"&&i.default===""})||(this.values[o]=structuredClone(N(i)),s.push({name:o,value:a}))}return s}};function Kt(n,e,t,r,s=""){let o=r??n.name.toLowerCase();return{name:o,path:s+o,type:n.type,value:e,default:n.default??N(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var lr=n=>n.toLowerCase(),et=1,Re=class{scopes=new Map;parked=new Map;parkedRefit=new Set;misfits=[];rev=0;get revision(){return this.rev}defineOwned(e,t,r){let s=typeof r=="string"?{pathPrefix:r}:r??{},o=new z(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,o,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,r){this.assertFree(e,r?.owner),this.scopes.set(e,{kind:"owned",bag:t,...r?.owner!==void 0?{owner:r.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(this.note(e,t.load(s,{refit:this.parkedRefit.has(e)})),this.parked.delete(e),this.parkedRefit.delete(e)),this}remove(e,t){let r=this.scopes.get(e);if(!r)throw new Error(`unknown scope '@${e}'`);return t?.keep&&r.kind==="owned"&&(this.parked.set(e,r.bag.save()),this.parkedRefit.delete(e)),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear(),this.parkedRefit.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&(this.parked.delete(t),this.parkedRefit.delete(t));return this}takeMisfits(e){if(!e){let r=this.misfits;return this.misfits=[],r}let t=this.misfits.filter(r=>e(r.scope));return this.misfits=this.misfits.filter(r=>!e(r.scope)),t}note(e,t){for(let r of t)this.misfits.push({scope:e,name:r.name,value:r.value})}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`'@${e}' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){let o=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,o.owner);let i=o.normalise??lr,a=new Map;for(let d of r)a.set(i(d.name),d);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:o.writable??!0,norm:i,...o.owner!==void 0?{owner:o.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let r=this.scopes.get(e);if(r)return r.kind==="owned"?r.bag.get(t):r.resolver.get(r.norm(t))}set(e,t,r,s){let o=this.scopes.get(e);if(!o)throw new Error(`unknown scope '@${e}'`);if(o.kind==="owned"){if(!s?.host&&!o.bag.writable(t))throw new Error(`'@${e}.${t}' is read-only`);o.bag.set(t,r,s?.host?{host:!0}:void 0);return}let i=o.norm(t);if(!o.resolver.set)throw new Error(`'@${e}.${t}' is read-only`);if(!s?.host&&!this.foreignWritable(o,i))throw new Error(`'@${e}.${t}' is read-only`);o.resolver.set(i,r)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,r]of this.scopes){let s=r.owner!==void 0?{owner:r.owner}:{};if(r.kind==="owned")for(let o of r.bag.rows())e.push({scope:t,...s,...o});else for(let[o,i]of r.decls)e.push({scope:t,...s,...Kt(i,r.resolver.get(o),this.foreignWritable(r,o),o,`${t}.`)})}return e}toEvalContext(e,t){let r=this.view(t?.aliases),s={};for(let[i,a]of r)s[i]=a.kind==="owned"?a.bag.values:a.resolver;let o=this.qualityLadders(r);return o.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(i,a)=>{let d=r.get(i);return d?o.get(i)?.get(cr(d)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[r,s]of Object.entries(e??{})){let o=this.scopes.get(s);if(!o)throw new Error(`alias '@${r}' names '${s}', which is not registered`);t.set(r,o)}return t}qualityLadders(e){let t=new Map;for(let[r,s]of e)for(let[o,i]of Ut(s)){if(i.type!=="quality"||i.stages===void 0)continue;let a=t.get(r);a||(a=new Map,t.set(r,a)),a.set(o,i.stages)}return t}toSchema(e){let t=new Map;for(let[r,s]of this.view(e?.aliases)){let o=Ut(s);if(o.length===0)continue;let i=new Map;for(let[a,d]of o)i.set(a,{type:d.type,enumValues:d.values,...d.stages!==void 0?{stages:d.stages}:{}});t.set(r,i)}return{properties:t}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());for(let[t,r]of this.parked)e[t]=structuredClone(r);return e}load(e,t){t?.keepParked||(this.parked.clear(),this.parkedRefit.clear());let r=t?.refit===!0;for(let[s,o]of Object.entries(e)){let i=this.scopes.get(s);i?.kind==="owned"?this.note(s,i.bag.load(o,{refit:r})):i||(this.parked.set(s,structuredClone(o)),r?this.parkedRefit.add(s):this.parkedRefit.delete(s))}}saveFragment(){return{version:et,scopes:this.save()}}loadFragment(e){if(e.version!==et)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${et})`);this.load(e.scopes)}assertFree(e,t){let r=this.scopes.get(e);if(!r)return;let s=r.owner!==void 0?` by ${r.owner}`:"",o=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope '@${e}' is already registered${s}${o}`)}};function Ut(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function cr(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function N(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":case"text":case"multiline":return"";case"enum":return n.values?.[0]??"";case"flags":return[];case"quality":return n.stages?.[0]??"";default:return!1}}var Jt=(n,e,t)=>`${n}${e}${t}`,b=class extends Error{constructor(e){super(e),this.name="StoryletError"}},pr=new Set(["seed","log","hostScopes","registry","onReplacedFlow","onError","onRefit"]),we="storylets/durable@1",ne=(n,e)=>n.shared??e,ur=(n,e)=>n.redraw==="never"&&(n.durable??e.durable??!1)===!0,dn=n=>n.sharedCopies??ln(n),ln=n=>n.acrossHands==="any"?1/0:n.acrossHands==="one"?1:n.copies??1;function gr(n,e,t){let r=e===void 0?[]:n.filter(i=>e.has(i.entry.card.id)),s=e===void 0?n:n.filter(i=>!e.has(i.entry.card.id)),o=i=>i.entry.card.weight??1;if(s.length>1&&s.some(i=>o(i)!==o(s[0]))){let i=[],a=[...s];for(;a.length>1;){let d=0;for(let p of a)d+=o(p);let l=t.next()*d,c=0;for(;c<a.length-1&&l>=o(a[c]);)l-=o(a[c]),c++;i.push(a.splice(c,1)[0])}return i.push(a[0]),[...r,...i]}return s.length>1&&_(s,t),[...r,...s]}var fr=n=>n.inOneHand??1,te="Storylet Engine",it=n=>n;function Oe(n){if(!n.startsWith("@"))return n;let e=n.slice(1);return e.includes(".")?e:`story.${e}`}var ct=n=>n.replace(/%/g,"%25").replace(/\//g,"%2F"),tt=n=>n.replace(/%2F/g,"/").replace(/%25/g,"%"),nt=(n,e)=>`storylets/${n}/${ct(e)}`,at=n=>`storylets/flow/${ct(n)}/`,dt=(n,e,t)=>e==="story"?`${at(n)}story`:`${at(n)}${e}/${ct(t)}`,ye=()=>({story:{},box:{},deck:{},hand:{},value:{}});function hr(n,e){let t=ye(),r=new Map,s={},o=i=>{let a=tt(i);if(!e.has(a))return;let d=r.get(a);return d||(d=ye(),r.set(a,d)),d};for(let[i,a]of Object.entries(n)){let d;if(i==="story")t.story=a;else if(d=/^storylets\/(box|deck|hand|value)\/([^/]+)$/.exec(i))t[d[1]][tt(d[2])]=a;else if(d=/^storylets\/flow\/([^/]+)\/story$/.exec(i)){let l=o(d[1]);l&&(l.story=a)}else if(d=/^storylets\/flow\/([^/]+)\/(box|deck|hand|value)\/([^/]+)$/.exec(i)){let l=o(d[1]);l&&(l[d[2]][tt(d[3])]=a)}else i.startsWith("storylets/")||(s[i]=a)}return{shared:t,flows:r,rest:s}}function Qt(n,e,t){Object.keys(n.story).length>0&&(t[e("story")]=n.story);for(let r of L)for(let[s,o]of Object.entries(n[r]))Object.keys(o).length>0&&(t[e(r,s)]=o)}var Xt=(n,e)=>new z(n,{normalise:it,pathPrefix:e}),O=(n,e)=>n!==void 0&&Object.prototype.hasOwnProperty.call(n.values,e)?n.values[e]:void 0,rt=Object.freeze(Object.create(null));function Yt(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var mr={story:!0,box:!1,deck:!1,hand:!1,value:!1},cn=(n,e)=>e.shared??mr[n],yr=(n,e)=>e.filter(t=>cn(n,t)),br=(n,e)=>e.filter(t=>!cn(n,t)),L=["box","deck","hand","value"],pt=n=>L.includes(n??""),Be=()=>({gameId:new Map,id:new Map,repeated:new Map,zoneQualified:new Map}),wr=()=>({box:Be(),deck:Be(),hand:Be(),value:Be()}),st=(n,e)=>{let t=f(e);n.gameId.set(e.id,t),n.id.has(t)||n.id.set(t,e.id)},vr=(n,e)=>{let t=jt(e);for(let[r,s]of t.print)n.gameId.set(r,s);for(let[r,s]of t.accept)n.id.set(r,s);for(let[r,s]of t.repeated)n.repeated.set(r,s);for(let[r,s]of t.zoneQualified)n.zoneQualified.set(r,s)},Zt=(n,e)=>{let t=[],r=new Set,s=o=>{r.has(o.address)||(r.add(o.address),t.push(o))};for(let o of e.story)o.durable===!0&&s({address:`story.${o.name}`,kind:"story",decl:o});for(let o of L)for(let[i,a]of e[o])for(let d of a)d.durable===!0&&s({address:`${H(n,o,i)}.${d.name}`,kind:o,owner:i,decl:d});return t},ut=(n,e)=>e.kind==="story"?n.story:n[e.kind].get(e.owner),pn=(n,e)=>{let t=[];for(let r of e){let s=O(ut(n,r),r.decl.name);s!==void 0&&t.push([r.address,s])}return Object.fromEntries(U(t,([r])=>r))};function en(n,e,t,r,s,o){let i=u=>({...s!==void 0?{flow:s}:{},path:u}),a=new Map(e.map(u=>[u.address,u])),d=new Map,l=r.values??{};for(let[u,g]of Object.entries(l)){let m=a.get(u);if(m===void 0){o.droppedProperties.push(i(u));continue}if(!G(m.decl,g)){o.retypedProperties.push(i(u));continue}d.set(m,g)}for(let u of e)Object.prototype.hasOwnProperty.call(l,u.address)||o.defaultedProperties.push(i(u.address));let c=new Set(t.map(u=>u.card.id)),p=[];for(let u of r.spent??[]){let g=n.cardsByGameId.get(u);g!==void 0&&c.has(g.card.id)?p.push(g.card.id):s!==void 0?o.droppedCooldowns.push({flow:s,card:u}):o.droppedSpent.push(u)}return{values:d,cards:p}}var tn=(n,e)=>{let t=e?.schema;if(t!==we)throw new b(`unsupported durable schema: ${String(t)}`);let r=n.bundle.content.project;if(e.content?.project!==r)throw new b(`durable state is for project "${String(e.content?.project)}", bundle is "${r}"`)},un=(n,e,t)=>{n?.set(e,structuredClone(t),{silent:!0,reason:"host durable",host:!0})},gn=(n,e)=>(n.declarations?.find(t=>t.name===e)?.writable??n.writable)===!1,fn=n=>({name:n.name,type:n.type,...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},...n.default!==void 0?{default:n.default}:{},...n.writable!==void 0?{writable:n.writable}:{}}),nn=n=>(n.declarations??[]).map(e=>{let t=fn(e),r=e.writable??n.writable;return r===void 0?t:{...t,writable:r}}),rn=(n,e)=>e.template!==void 0?n.templatesById.get(e.template)?.properties??[]:e.properties??[],H=(n,e,t)=>`${e}.${n.owners[e].gameId.get(t)??t}`,xr=(n,e,t)=>{let r=n.owners[e].repeated.get(t);if(r!==void 0)return{ambiguous:r};let s=n.owners[e].zoneQualified.get(t);if(s!==void 0)return{zone:s};let o=n.owners[e].id.get(t);if(o!==void 0)return{id:o,legacy:!1};if(n.owners[e].gameId.has(t))return{id:t,legacy:!0}},gt=(n,e,t,r)=>{let s=xr(n,e,t);if(s===void 0)throw new b(`no ${e} store "${t}"`);if("ambiguous"in s)throw new b(_t(t,r,s.ambiguous));if("zone"in s)throw new b(Ft(t,s.zone,r));return s},hn=(n,e,t,r)=>`"${e}.${t}.${r}" names the ${e} by its internal id; write "${H(n,e,t)}.${r}". The internal-id form is refused after the next release.`,mn=(n,e)=>{let t=r=>new Map([...e[r]].map(([s,o])=>[s,Xt(o,`${H(n,r,s)}.`)]));return{story:Xt(e.story,"story."),box:t("box"),deck:t("deck"),hand:t("hand"),value:t("value")}},Sr=n=>({story:n.story.values,box:Object.fromEntries([...n.box].map(([e,t])=>[e,t.values])),deck:Object.fromEntries([...n.deck].map(([e,t])=>[e,t.values])),hand:Object.fromEntries([...n.hand].map(([e,t])=>[e,t.values])),value:Object.fromEntries([...n.value].map(([e,t])=>[e,t.values]))}),Er=(n,e)=>{n.story.load(e?.story??{});for(let t of L)for(let[r,s]of Object.entries(e?.[t]??{}))n[t].get(r)?.load(s)},he=()=>({evicted:[],droppedCooldowns:[],droppedSpent:[],droppedProperties:[],defaultedProperties:[],retypedProperties:[]}),Me="",U=(n,e)=>[...n].map(t=>({item:t,k:e(t)})).sort((t,r)=>t.k<r.k?-1:t.k>r.k?1:0).map(t=>t.item),ot=n=>`${n.flow??""}${Me}${n.path}`;function sn(n,e,t,r,s){let o=l=>({...r!==void 0?{flow:r}:{},path:t(l)}),i=new Map((n??[]).map(l=>[l.name,l])),a=e??{},d={};for(let[l,c]of Object.entries(a)){let p=i.get(l);if(p===void 0){s.droppedProperties.push(o(l));continue}if(!G(p,c)){s.retypedProperties.push(o(l));continue}d[l]=c}for(let l of n??[])l.name in a||s.defaultedProperties.push(o(l.name));return d}function on(n,e,t,r,s){let o={story:sn(e.story,t?.story,i=>`story.${i}`,r,s),box:{},deck:{},hand:{},value:{}};for(let i of L){let a=t?.[i]??{},d=[...new Set([...e[i].keys(),...Object.keys(a)])].sort();for(let l of d){let c=H(n,i,l);o[i][l]=sn(e[i].get(l),a[l],p=>`${c}.${p}`,r,s)}}return o}function me(n,e,t,r){let s=e.version!==n.version||e.hash!==n.hash,o=U(r.evicted,p=>[p.flow,p.hand,p.card,p.reason].join(Me)),i=U(r.droppedCooldowns,p=>`${p.flow}${Me}${p.card}`),a=[...r.droppedSpent].sort(),d=U(r.droppedProperties,ot),l=U(r.defaultedProperties,ot),c=U(r.retypedProperties,ot);return{exact:!s&&o.length===0&&i.length===0&&a.length===0&&d.length===0&&l.length===0&&c.length===0,project:n.project,version:{saved:e.version,bundle:n.version},hash:{saved:e.hash,bundle:n.hash},flows:t,evicted:o,droppedCooldowns:i,droppedSpent:a,droppedProperties:d,defaultedProperties:l,retypedProperties:c}}var K=n=>n!==null&&typeof n=="object"&&!Array.isArray(n),yn=n=>n===null?"null":n===void 0?"nothing":Array.isArray(n)?"an array":`a ${typeof n}`,kr=n=>{let e=[];K(n.content)||e.push("no content"),K(n.settings)||e.push("no settings");let t=n.schema===Ze?["story"]:["story","world"];for(let s of t){let o=n[s];(!K(o)||!Array.isArray(o.properties))&&e.push(`no ${s} properties`)}let r=n.scopeRegistry;return r!==void 0&&(!K(r)||!Array.isArray(r.scopes))&&e.push("no host scopes in its scopeRegistry"),Array.isArray(n.boxes)||e.push("no boxes"),e},an=n=>{if(!K(n))throw new b(`not a Storylets save: expected an object, got ${yn(n)}`);let e=n.schema;if(e!==q&&e!==ee&&e!==W)throw new b(`unsupported save schema: ${String(e)} (this runtime reads ${W}, ${ee} and ${q})`);if(!K(n.content))throw new b("not a complete Storylets save: no content")},Pr=n=>{let e=t=>(t??[]).map(r=>JSON.stringify(r)).join(", ");switch(n.type){case"number":return"a number";case"boolean":return"true or false";case"enum":return`one of ${e(n.values)}`;case"flags":return n.values!==void 0&&n.values.length>0?`a list of flags from ${e(n.values)}`:"a list of flags";case"quality":return`one of its stages ${e(n.stages)}`;default:return"text"}},Le=(n,e)=>n.cardsByGameId.get(e)??n.cardsById.get(e),Cr=(n,e)=>n.handsByGameId.get(e)??n.handsById.get(e),$r=(n,e)=>n.boxesByGameId.get(e)??n.boxesById.get(e),lt=(n,e)=>{let t=n.cardsById.get(e);return t!==void 0?f(t.card):e},bn=(n,e,t)=>{let r=e.split("."),s,o=r.length===2?n.hostScopes.get(r[0]):void 0;if(o!==void 0){let i=o.spec.declarations;if(i===void 0)return;let a=r[1];if(s=i.find(d=>d.name===a)??i.find(d=>d.name.toLowerCase()===a.toLowerCase()),s===void 0)throw new b(`no property at "${e}": the content declares no @${o.spec.token}.${a}`)}else if(r.length===2&&r[0]==="story"){let i=r[1];s=n.sharedDecls.story.find(a=>a.name===i)??n.flowDecls.story.find(a=>a.name===i)}else if(r.length===3&&pt(r[0])){let i=r[0],[,a,d]=r,l=gt(n,i,a,d).id;s=n.sharedDecls[i].get(l)?.find(c=>c.name===d)??n.flowDecls[i].get(l)?.find(c=>c.name===d)}if(s!==void 0&&!G(s,t,{noneChosen:s.type==="enum"&&s.default===""}))throw new b(`"${e}" takes ${Pr(s)}; ${JSON.stringify(t)} doesn't fit`)},wn=n=>{let e=[];for(let t of n.hostScopes.values())for(let r of t.spec.declarations??[]){let s=t.resolver.get(r.name);e.push({path:`${t.spec.token}.${r.name}`,name:r.name,type:r.type,value:s??N(r),default:N(r),...r.values!==void 0?{values:r.values}:{},...r.stages!==void 0?{stages:r.stages}:{},writable:t.set!==void 0&&!gn(t.spec,r.name)})}return e},Rr=n=>{if(!K(n))throw new b(`not a Storylets bundle: expected an object, got ${yn(n)}`);let e=n.schema;if(typeof e!="string"||!pe.includes(e))throw new b(`unsupported bundle schema: ${String(e)} (this runtime reads ${pe.slice(0,-1).join(", ")} and ${pe[pe.length-1]})`);let t=kr(n);if(t.length>0)throw new b(`not a complete Storylets bundle: ${t.join("; ")}`);let r=[],s=n.map?.group,o=s!==void 0?f(s):void 0;s!==void 0&&o===I&&r.push(`the project map's tag group is called "${I}", which is reserved for a box's own hands`);let i=new Map;for(let a of n.boxes)for(let d of a.tagGroups)for(let l of d.tags){let c=f(l);i.has(c)||i.set(c,{box:f(a),group:f(d)})}for(let a of s?.tags??[]){let d=f(a),l=i.get(d);l!==void 0&&r.push(`the project map's zone "${d}" has the name of tag "${d}" in box "${l.box}", group "${l.group}", so "value.${d}.<name>" would name two things`)}for(let a of n.boxes){let d=f(a);if(a.usesMap===!0){if(s===void 0){r.push(`box "${d}" uses the project map, but the bundle has no map`);continue}a.tagGroups.find(p=>f(p)===o)!==void 0&&r.push(`box "${d}" uses the project map and declares its own tag group "${o}", the map's name`);continue}if(s===void 0)continue;let l=c=>{r.push(`box "${d}" is not on the project map, but ${c} names the map's tag group "${o}"`)};for(let c of a.decks)for(let p of c.cards)p.tags?.[s.id]!==void 0&&l(`card "${f(p)}"`);for(let c of a.handTemplates)(c.bindings?.[s.id]!==void 0||c.chooses?.includes(s.id)===!0)&&l(`hand template "${f(c)}"`);for(let c of a.hands)(c.chosen?.[s.id]!==void 0||c.rule?.bindings?.[s.id]!==void 0)&&l(`hand "${f(c)}"`)}if(r.length>0)throw new b(`bundle refused: ${r.join("; ")}`)},Ie=class n{internals;seed;onReplacedFlow;flowsById=new Map;engineTraceHandlers=new Set;creationOptions;sharedMounts=[];constructor(e,t={}){Rr(e),this.creationOptions=t,this.seed=t.seed??0,this.onReplacedFlow=t.onReplacedFlow;let r=[];for(let l of Object.keys(t))pr.has(l)||r.push({node:l,message:l==="world"?'the engine option "world" has gone: bind the scope as hostScopes: { world }':`"${l}" isn't an engine option, so it's ignored`});let s={bundle:e,...t.log?{logCap:typeof t.log=="object"?t.log.cap??1e3:1e3}:{},cardsById:new Map,cardsByGameId:new Map,boxesByGameId:new Map,boxesById:new Map,handsById:new Map,handsByGameId:new Map,owners:wr(),templatesById:new Map,groupsById:new Map,requiredGroups:new Set,nodeCache:new WeakMap,ladders:{host:new Map,story:new Map,box:new Map,deck:new Map,value:new Map,hand:new Map},hasQualities:!1,hasShared:!1,flowDecls:{story:[],box:new Map,deck:new Map,hand:new Map,value:new Map},sharedDecls:{story:[],box:new Map,deck:new Map,hand:new Map,value:new Map},durable:{shared:[],flow:[],sharedCards:[],flowCards:[]},shared:void 0,registry:t.registry??new Re,onError:t.onError??(l=>console.warn(l.kind==="setup"?`Storylet Engine: ${l.message}`:`Storylet Engine: the condition on ${l.node} in flow '${l.flow}' failed, counted as false: ${l.message}`)),ownsRegistry:t.registry===void 0,reportRefits:()=>{let l=this.internals.registry.takeMisfits(c=>c==="story"||c.startsWith("storylets/")||this.internals.hostScopes.get(c)?.selfBacked===!0);if(l.length!==0)if(t.onRefit!==void 0)t.onRefit(l);else for(let c of l)console.warn(`Storylet Engine: the save's ${c.scope}.${c.name} was ${JSON.stringify(c.value)}, which no longer fits its declaration; it is back at its default`)},registryView:()=>a(),hostScopes:new Map,hostResolvers:{},emitEngine:(l,c,p)=>{this.internals.logCap!==void 0&&(this.engineLog.push({...c,flow:l,seq:this.engineSeq++,...p!==void 0?{turn:p}:{}}),this.engineLog.length>this.internals.logCap&&this.engineLog.splice(0,this.engineLog.length-this.internals.logCap));for(let u of[...this.engineTraceHandlers])u(l,c)},engineTracing:()=>this.engineTraceHandlers.size>0};this.internals=s;let o=-1,i={scopes:{},qualities:void 0},a=()=>{let l=s.registry;if(l.revision!==o){let c=l.toEvalContext(),p={};for(let[u,g]of Object.entries(c.scopes))u.includes("/")||(p[u]=g);i={scopes:p,qualities:c.qualities},o=l.revision}return i};vr(s.owners.value,e);for(let l of e.boxes){s.boxesById.set(l.id,l),s.boxesByGameId.set(f(l),l),st(s.owners.box,l);for(let c of l.tagGroups)s.groupsById.set(c.id,{group:c,box:l}),c.required===!0&&s.requiredGroups.add(c.id);for(let c of l.decks){st(s.owners.deck,c),c.shared===!0&&(s.hasShared=!0);for(let p of c.cards){let u={card:p,deck:c,box:l};s.cardsById.set(p.id,u),s.cardsByGameId.set(f(p),u),p.shared===!0&&(s.hasShared=!0),ur(p,c)&&(ne(p,c.shared??!1)?s.durable.sharedCards:s.durable.flowCards).push(u)}}for(let c of l.handTemplates)s.templatesById.set(c.id,c);for(let c of l.hands)s.handsById.set(c.id,{hand:c,box:l}),s.handsByGameId.set(f(c),{hand:c,box:l}),st(s.owners.hand,c)}e.map!==void 0&&(s.groupsById.set(e.map.group.id,{group:e.map.group}),e.map.group.required===!0&&s.requiredGroups.add(e.map.group.id)),this.initLadders();let d=l=>({story:l("story",e.story.properties),box:new Map(e.boxes.map(c=>[c.id,l("box",c.properties)])),deck:new Map(e.boxes.flatMap(c=>c.decks.map(p=>[p.id,l("deck",p.properties)]))),hand:new Map(e.boxes.flatMap(c=>c.hands.map(p=>[p.id,l("hand",rn(s,p))]))),value:new Map(qt(e).flatMap(c=>c.tags.map(p=>[p.id,l("value",p.properties??[])])))});s.flowDecls=d(br),s.sharedDecls=d(yr),s.durable.shared=Zt(s,s.sharedDecls),s.durable.flow=Zt(s,s.flowDecls),this.initShared(t.hostScopes??{},r),s.reportRefits();for(let l of r)this.reportSetup(l.node,l.message)}reportSetup(e,t){this.internals.onError({flow:"",kind:"setup",node:e,message:t}),(this.internals.logCap!==void 0||this.internals.engineTracing())&&this.internals.emitEngine("",{type:"diagnostic",where:"setup",message:t})}initShared(e,t){let r=this.internals,s=r.registry,o=Z(r.bundle);r.shared=mn(r,r.sharedDecls);let i=[],a=(d,l)=>{l(),i.push(d),this.sharedMounts.push({key:d,mount:l})};try{let d=r.shared.story;a("story",()=>{s.mountOwned("story",d,{owner:te})});for(let l of L)for(let[c,p]of r.shared[l])p.declarations().length!==0&&a(nt(l,c),()=>{s.mountOwned(nt(l,c),p,{owner:te})});for(let[l,c]of Object.entries(e)){if(c===void 0)continue;let p=o.find(g=>g.token===l);if(p===void 0&&!(r.bundle.externalScopes??[]).includes(l)){let g=o.find(m=>m.token.toLowerCase()===l.toLowerCase());t.push({node:l,message:g?`hostScopes binds "${l}", which this bundle doesn't declare: it declares "${g.token}", and a scope's token is case-sensitive`:`hostScopes binds "${l}", which this bundle doesn't declare`})}let u=p?.declarations?.map(fn);a(l,()=>{s.defineForeign(l,c,u,{normalise:it,writable:p?.writable??!0,owner:te})})}for(let l of r.ownsRegistry?o:[]){let c=l.token;if(e[c]!==void 0||s.has(c))continue;s.defineOwned(c,nn(l),{normalise:it,pathPrefix:`${c}.`,owner:te});let p=s.ownedBag(c);i.push(c),this.sharedMounts.push({key:c,mount:()=>{s.mountOwned(c,p,{owner:te})}})}}catch(d){for(let l of i)s.remove(l,{keep:!0});throw d}r.hostScopes=new Map,r.hostResolvers={};for(let d of o){let l=d.token,c=e[l],p=c!==void 0,u={spec:d,resolver:{get:g=>s.get(l,g),set:(g,m)=>{s.set(l,g,m)}},...p?c.set!==void 0?{set:(g,m)=>{s.set(l,g,m,{host:!0})}}:{}:{set:(g,m,h)=>{s.set(l,g,m,h===!0?{host:!0}:void 0)}},selfBacked:!p&&this.sharedMounts.some(g=>g.key===l)};r.hostScopes.set(l,u),r.hostResolvers[l]=u.resolver}}reseedShared(){let{shared:e,sharedDecls:t,registry:r}=this.internals;e.story.reseed(t.story);for(let s of L)for(let[o,i]of e[s])i.reseed(t[s].get(o)??[]);for(let s of this.internals.hostScopes.values())s.selfBacked&&r.reseedOwned(s.spec.token,nn(s.spec))}initLadders(){let e=this.internals,t=o=>{let i=new Map;for(let a of o??[])a.type==="quality"&&a.stages!==void 0&&i.set(a.name,a.stages);return i},r=e.bundle;for(let o of Z(r))e.ladders.host.set(o.token,t(o.declarations));e.ladders.story=t(r.story.properties);for(let o of r.boxes){e.ladders.box.set(o.id,t(o.properties));for(let i of o.decks)e.ladders.deck.set(i.id,t(i.properties));for(let i of o.tagGroups)for(let a of i.tags)e.ladders.value.set(a.id,t(a.properties));for(let i of o.hands)e.ladders.hand.set(i.id,t(rn(e,i)))}for(let o of r.map?.group.tags??[])e.ladders.value.set(o.id,t(o.properties));let s=o=>[...o.values()].some(i=>i.size>0);e.hasQualities=[...e.ladders.host.values()].some(o=>o.size>0)||e.ladders.story.size>0||s(e.ladders.box)||s(e.ladders.deck)||s(e.ladders.value)||s(e.ladders.hand)}get registry(){return this.internals.registry}openFlow(e,t={}){return this.open(e,t,!1)}open(e,t,r){if(t.durable!==void 0&&t.restore!==void 0)throw new b(`openFlow "${e}": restore and durable cannot be given together; a restore already carries the flow's durable state`);let s;if(t.durable!==void 0){tn(this.internals,t.durable);let d=he(),{flow:l,flowCards:c}=this.internals.durable;s={plan:en(this.internals,l,c,t.durable,e,d),draft:d,content:t.durable.content}}this.assertExternalScopes();let o=t.restore!==void 0?this.sharedClaims(e):void 0,i=this.flowsById.get(e);if(i){let d=i.heldCardIds().length;d>0&&this.onReplacedFlow?.(e,d),i.markClosed()}r||this.internals.registry.discardParked(at(e));let a=new be(this,this.internals,e,t.seed??this.seed);if(this.flowsById.set(e,a),t.restore!==void 0){let d=he(),l=this.planFlowRestore(e,structuredClone(t.restore),o,d);a.restore(l);let c=this.internals.bundle.content;t.onRestoreReport?.(me(c,c,[e],d))}return s!==void 0&&(a.writeDurable(s.plan),t.onRestoreReport?.(me(this.internals.bundle.content,s.content,[e],s.draft))),a}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}closeFlow(e){let t=this.flowsById.get(e);t&&(this.flowsById.delete(e),t.markClosed())}dropFlow(e,t){this.flowsById.get(e)===t&&this.flowsById.delete(e)}reset(){this.dropRun(!1),this.reseedShared(),this.internals.registry.discardParked("storylets/")}dropRun(e){this.engineLog=[];for(let[t,r]of this.flowsById)r.releaseBags(e!==!1&&e.has(t)),r.markClosed();this.flowsById.clear(),this.spent.clear()}spent=new Set;isTaken(e){return this.spent.has(e)}markTaken(e){this.spent.add(e)}engineLog=[];engineSeq=0;log(){return this.engineLog}clearLog(){this.engineLog=[]}sharedClaims(e){let t=new Map;for(let[r,s]of this.flowsById)if(r!==e)for(let o of s.heldCardIds())t.set(o,(t.get(o)??0)+1);return t}getProperty(e){let t=this.resolveShared(Oe(e)),r=t.kind==="host"?t.host.resolver.get(t.name):t.kind==="scope"?this.internals.registry.get(t.token,t.name):t.bag.get(t.name);if(r===void 0)throw new b(`no property at "${e}"`);return r}setProperty(e,t){let r=this.resolveShared(Oe(e));if(bn(this.internals,e,t),r.kind==="host"){if(!r.host.set)throw new b(`@${r.host.spec.token} is read-only here: the host bound no write`);r.host.set(r.name,t,!0);return}if(r.kind==="scope"){this.internals.registry.set(r.token,r.name,t,{host:!0});return}r.bag.set(r.name,t,{silent:!0,reason:"host setProperty",host:!0})}resolveShared(e){let t=e.split("."),r=()=>{throw new b(`"${e}" is per-flow state - read it on a Flow, not the Engine`)},s=t.length===2?this.internals.hostScopes.get(t[0]):void 0;if(s!==void 0)return{kind:"host",host:s,name:t[1]};if(t.length===2&&t[0]!=="story"&&this.internals.registry.has(t[0]))return{kind:"scope",token:t[0],name:t[1]};if(t.length===2&&t[0]==="story"){let o=t[1];if(O(this.internals.shared.story,o)!==void 0)return{kind:"bag",bag:this.internals.shared.story,name:o};throw this.internals.flowDecls.story.some(i=>i.name===o)&&r(),new b(`no property at "${e}"`)}if(t.length===3&&pt(t[0])){let o=t[0],[,i,a]=t,d=gt(this.internals,o,i,a);d.legacy&&this.diagnose(hn(this.internals,o,i,a));let l=d.id,c=this.internals.shared[o].get(l);if(c!==void 0&&O(c,a)!==void 0)return{kind:"bag",bag:c,name:a};throw this.internals.flowDecls[o].get(l)?.some(p=>p.name===a)&&r(),c===void 0&&!this.internals.flowDecls[o].has(l)?new b(`no ${o} store "${i}"`):new b(`no property at "${e}"`)}throw new b(`bad property path "${e}"`)}diagnose(e){this.internals.emitEngine("",{type:"diagnostic",where:"property address",message:e})}assertExternalScopes(){for(let e of this.internals.bundle.externalScopes??[])if(!this.internals.registry.has(e))throw new b(`this content names @${e}, which no engine on this registry registered: give every engine the game's one registry`)}listProperties(){let e=wn(this.internals),t=(r,s)=>{for(let o of s.rows())e.push(o)};t("story",this.internals.shared.story);for(let r of L)for(let[s,o]of this.internals.shared[r])t(H(this.internals,r,s),o);return e}listBags(){let e=[{prefix:"story",bag:this.internals.shared.story}];for(let t of L)for(let[r,s]of this.internals.shared[t])e.push({prefix:H(this.internals,t,r),bag:s});return e}subscribeTrace(e){return this.engineTraceHandlers.add(e),()=>{this.engineTraceHandlers.delete(e)}}hotSwap(e,t={}){if(e.content.project!==this.internals.bundle.content.project)throw new b(`hotSwap: the bundle is for project "${e.content.project}", this engine runs "${this.internals.bundle.content.project}"`);let r={...this.creationOptions,...t},s=this.saveGame(),o=this.internals.registry;if(this.internals.ownsRegistry){let c=new n(e,{...r,registry:void 0});return{engine:c,report:c.loadGame(s)}}let i={};for(let[c,p]of Object.entries(o.save()))(c==="story"||c.startsWith("storylets/"))&&(i[c]=p);let a=new Set([...this.sharedMounts.map(c=>c.key),...[...this.flowsById.values()].flatMap(c=>c.registeredKeys())]),d=Object.fromEntries(Object.entries(i).filter(([c])=>!a.has(c)));for(let{key:c}of this.sharedMounts)o.has(c)&&o.remove(c);for(let c of this.flowsById.values())c.releaseBags(!1);let l;try{l=new n(e,{...r,registry:o});let c=l.loadGame({...s,registry:i}),p=Object.fromEntries(Object.entries(d).filter(([u])=>!o.has(u)));return Object.keys(p).length>0&&o.load(p,{keepParked:!0}),this.dropRun(!1),{engine:l,report:c}}catch(c){l?.dropRun(!1);for(let{key:p}of l?.sharedMounts??[])o.has(p)&&o.remove(p);o.discardParked("storylets/");for(let{mount:p}of this.sharedMounts)p();for(let p of this.flowsById.values())p.mountBags();throw this.reseedShared(),o.load(i,{keepParked:!0}),c}}saveGame(){return structuredClone({schema:q,content:this.internals.bundle.content,...this.internals.ownsRegistry?{registry:this.registrySection()}:{},shared:{spent:[...this.spent].map(e=>lt(this.internals,e)).sort()},flows:Object.fromEntries([...this.flowsById].map(([e,t])=>[e,t.snapshot(!1)]))})}registrySection(){let e=this.internals.registry.save(),t={},r=s=>{Object.prototype.hasOwnProperty.call(e,s)&&!Object.prototype.hasOwnProperty.call(t,s)&&(t[s]=e[s])};for(let{key:s}of this.sharedMounts)r(s);for(let s of Object.values(Object.fromEntries(this.flowsById)))for(let o of s.registeredKeys())r(o);for(let s of Object.keys(e))r(s);return t}saveFlow(e){let t=this.flowsById.get(e);if(!t)throw new b(`unknown flow "${e}"`);return structuredClone(t.snapshot(!0))}saveDurable(){let{bundle:e,shared:t,durable:r}=this.internals;return structuredClone({schema:we,content:e.content,values:pn(t,r.shared),spent:r.sharedCards.filter(s=>this.spent.has(s.card.id)).map(s=>f(s.card)).sort()})}loadDurable(e){let t=this.internals;tn(t,e);let r=he(),s=en(t,t.durable.shared,t.durable.sharedCards,e,void 0,r);for(let o of t.durable.shared){let i=s.values.has(o)?s.values.get(o):N(o.decl);un(ut(t.shared,o),o.decl.name,i)}for(let o of s.cards)this.spent.add(o);return me(t.bundle.content,e.content,[],r)}previewLoad(e){an(e),this.assertSameProject(e);let t=this.planLoad(e);if(t.sections===void 0)return t.report;let{seed:r,hostScopes:s}=this.creationOptions,o=new n(this.internals.bundle,{...r!==void 0?{seed:r}:{},...s!==void 0?{hostScopes:s}:{},onError:()=>{}});try{return o.loadGame(e)}catch{return t.report}}previewFlowRestore(e,t){let r=he();this.planFlowRestore(e,t,this.sharedClaims(e),r);let s=this.internals.bundle.content;return me(s,s,[e],r)}loadGame(e){try{return this.loadGameInto(e)}finally{this.internals.reportRefits()}}loadGameInto(e){an(e),this.assertSameProject(e),this.assertExternalScopes();let t=this.planLoad(structuredClone(e)),r=this.internals.registry;t.sections!==void 0?(this.reset(),this.internals.ownsRegistry?r.load(t.sections):r.load(t.sections,{keepParked:!0})):this.dropRun(new Set(t.flows.map(([o])=>o)));for(let o of t.spent)this.spent.add(o);for(let[o,i]of t.flows)this.open(o,{},!0).restore(i);if(t.sections===void 0)return t.report;let s=t.flows.flatMap(([o])=>(this.flowsById.get(o)?.dropUnqualified()??[]).map(i=>({flow:o,...i})));return s.length===0?t.report:{...t.report,exact:!1,evicted:U([...t.report.evicted,...s],o=>[o.flow,o.hand,o.card,o.reason].join(Me))}}assertSameProject(e){if(e.content.project!==this.internals.bundle.content.project)throw new b(`save is for project "${e.content.project}", bundle is "${this.internals.bundle.content.project}"`)}planLoad(e){let t=e.schema;if(t!==q&&t!==ee&&t!==W)throw new b(`unsupported save schema: ${String(t)}`);let r=he(),s=new Set(Object.keys(e.flows??{})),o;e.schema===W?o={shared:e.shared?.props??ye(),flows:new Map(Object.entries(e.flows??{}).map(([c,p])=>[c,p.props??ye()])),rest:{}}:e.registry!==void 0&&(o=hr(e.registry,s));let i=o!==void 0?on(this.internals,this.internals.sharedDecls,o.shared,void 0,r):void 0,a=[];for(let c of e.shared?.spent??[]){let p=Le(this.internals,c);p!==void 0?a.push(p.card.id):r.droppedSpent.push(c)}let d=[],l=o!==void 0?{...o.rest}:void 0;l!==void 0&&i!==void 0&&Qt(i,(c,p)=>c==="story"?"story":nt(c,p),l);for(let[c,p]of Object.entries(e.flows??{})){let u=o!==void 0?{...p,props:o.flows.get(c)??ye()}:{...p},g=this.planFlowRestore(c,u,void 0,r);l!==void 0&&g.props!==void 0&&(Qt(g.props,(m,h)=>dt(c,m,h),l),delete g.props),d.push([c,g])}return{report:me(this.internals.bundle.content,e.content,d.map(([c])=>c),r),...l!==void 0?{sections:l}:{},spent:a,flows:d}}planFlowRestore(e,t,r,s){let o=this.internals,i=t.props!==void 0?on(o,o.flowDecls,t.props,e,s):void 0,a={};for(let[u,g]of Object.entries(t.cooldowns??{})){let m=Le(o,u);m!==void 0?a[m.card.id]=g:s.droppedCooldowns.push({flow:e,card:u})}let d=u=>{let g=Le(o,u);return g?f(g.card):u},l={},c=new Map;for(let[u,g]of Object.entries(t.board??{})){let m=Cr(o,u);if(m===void 0){for(let x of g)s.evicted.push({flow:e,hand:u,card:d(x),reason:"hand-vanished"});continue}let h=f(m.hand),y=[];for(let x of g){let w=Le(o,x);if(w===void 0){s.evicted.push({flow:e,hand:h,card:x,reason:"vanished"});continue}let S=w.card.id;if(r!==void 0&&ne(w.card,w.deck.shared??!1)){if((r.get(S)??0)+(c.get(S)??0)>=dn(w.card)){s.evicted.push({flow:e,hand:h,card:f(w.card),reason:"claimed-elsewhere"});continue}c.set(S,(c.get(S)??0)+1)}y.push(S)}l[m.hand.id]=y}let p={};for(let[u,g]of Object.entries(t.turns??{})){let m=$r(o,u);m!==void 0&&(p[m.id]=g)}return{...i!==void 0?{props:i}:{},turns:p,prng:t.prng,cooldowns:a,board:l,playLog:t.playLog??[]}}},be=class{id;engine;internals;closed=!1;prng;turnCounts=new Map;cooldowns={};boardContents=new Map;playLog=[];playCount=new Map;lastPlayOf=new Map;tagPlayCount=new Map;lastPlayInTag=new Map;stores;registered=[];bagKeys=[];traceHandlers=new Set;logEntries=[];logSeq=0;storyReader;boxReaders=new Map;deckReaders=new Map;constructor(e,t,r,s){this.engine=e,this.internals=t,this.id=r,this.prng=T(s),this.stores=mn(t,t.flowDecls);let o=(a,d)=>{d.declarations().length!==0&&this.bagKeys.push([a,d])};o(dt(r,"story"),this.stores.story);for(let a of L)for(let[d,l]of this.stores[a])o(dt(r,a,d),l);this.mountBags(),t.reportRefits();for(let a of t.bundle.boxes){this.turnCounts.set(a.id,0);for(let d of a.hands)this.boardContents.set(d.id,[])}let i=(a,d)=>({get:l=>O(a,l)??O(d,l)});this.storyReader=i(this.stores.story,t.shared.story);for(let a of t.bundle.boxes){this.boxReaders.set(a.id,i(this.stores.box.get(a.id),t.shared.box.get(a.id)));for(let d of a.decks)this.deckReaders.set(d.id,i(this.stores.deck.get(d.id),t.shared.deck.get(d.id)))}}get isClosed(){return this.closed}close(){this.closed||(this.engine.dropFlow(this.id,this),this.markClosed())}markClosed(){this.releaseBags(!1),this.traceHandlers.clear(),this.closed=!0}releaseBags(e){for(let t of this.registered)this.internals.registry.remove(t,{keep:e});this.registered.length=0}registeredKeys(){return[...this.registered]}mountBags(){for(let[e,t]of this.bagKeys)this.internals.registry.mountOwned(e,t,{owner:te}),this.registered.push(e)}assertOpen(){if(this.closed)throw new b(`flow "${this.id}" is closed`)}resolveBox(e){let t=this.internals.boxesByGameId.get(e)??this.internals.boxesById.get(e);if(!t)throw new b(`unknown box "${e}"`);return t}turn(e){return this.assertOpen(),this.turnCounts.get(this.resolveBox(e).id)??0}subscribeTrace(e){return this.traceHandlers.add(e),()=>{this.traceHandlers.delete(e)}}get tracing(){return this.traceHandlers.size>0||this.internals.logCap!==void 0||this.internals.engineTracing()}emit(e,t){this.internals.logCap!==void 0&&(this.logEntries.push({...e,seq:this.logSeq++,...t!==void 0?{turn:t}:{}}),this.logEntries.length>this.internals.logCap&&this.logEntries.splice(0,this.logEntries.length-this.internals.logCap));for(let r of[...this.traceHandlers])r(e);this.internals.emitEngine(this.id,e,t)}log(){return this.logEntries}clearLog(){this.logEntries=[]}node(e){let t=this.internals.nodeCache.get(e);return t||(t=j(e.ast),this.internals.nodeCache.set(e,t)),t}groupInBox(e,t){let r=ue(this.internals.bundle,e);return r.find(s=>f(s)===t)??r.find(s=>s.id===t)}indexPlay(e){this.playCount.set(e.card,(this.playCount.get(e.card)??0)+1),this.lastPlayOf.set(e.card,e);let t=this.internals.cardsByGameId.get(e.card);if(t)for(let[r,s]of Object.entries(t.card.tags??{}))for(let o of s){let i=Jt(t.box.id,r,o);this.tagPlayCount.set(i,(this.tagPlayCount.get(i)??0)+1),this.lastPlayInTag.set(i,e)}}rebuildPlayIndex(){this.playCount.clear(),this.lastPlayOf.clear(),this.tagPlayCount.clear(),this.lastPlayInTag.clear();for(let e of this.playLog)this.indexPlay(e)}hostsByBox=new Map;host(e){let t=this.hostsByBox.get(e.id);if(t!==void 0)return t;let r=this.makeHost(e);return this.hostsByBox.set(e.id,r),r}makeHost(e){let t=new Map,r=(o,i)=>{let a=`${o}${i}`;if(t.has(a))return t.get(a);let d=this.groupInBox(e,o),l=d?.tags.find(p=>p.gameId===i),c=d&&l?Jt(e.id,d.id,l.id):void 0;return t.set(a,c),c},s=o=>{let i=this.internals.cardsByGameId.get(o.card);return i?(this.turnCounts.get(i.box.id)??0)-o.turn:Pe};return{nextRandom:()=>this.prng.next(),countPlayed:o=>this.playCount.get(o)??0,turnsSincePlayed:o=>{let i=this.lastPlayOf.get(o);return i?s(i):Pe},countPlayedIn:(o,i)=>{let a=r(o,i);return a===void 0?0:this.tagPlayCount.get(a)??0},turnsSincePlayedIn:(o,i)=>{let a=r(o,i),d=a===void 0?void 0:this.lastPlayInTag.get(a);return d?s(d):Pe}}}evalCtx(e,t,r){let s=this.internals.registryView();return{scopes:{...s.scopes,...this.internals.hostResolvers,story:this.storyReader,box:this.boxReaders.get(e.id)??rt,deck:t?this.deckReaders.get(t.id)??rt:rt,hand:r.bag},host:this.host(e),...this.internals.hasQualities||s.qualities!==void 0?{qualities:(o,i)=>o==="story"?this.internals.ladders.story.get(i):o==="box"?this.internals.ladders.box.get(e.id)?.get(i):o==="deck"&&t?this.internals.ladders.deck.get(t.id)?.get(i):o==="hand"?this.handLadder(r,i):this.internals.ladders.host.get(o)?.get(i)??s.qualities?.(o,i)}:{}}}handLadder(e,t){let r=e.sources.get(t);if(r)return r.kind==="value"?this.internals.ladders.value.get(r.id)?.get(t):r.kind==="hand"?this.internals.ladders.hand.get(r.id)?.get(t):void 0}eval(e,t){return Ee(this.node(e),t,Ye)}passes(e,t,r){if(!e)return!0;try{return Yt(this.eval(e,t))}catch(s){let o=s instanceof Error?s.message:String(s),i=r(),a=e.src;return this.internals.onError({flow:this.id,kind:"condition",node:i,...typeof a=="string"&&a!==""?{source:a}:{},message:o}),this.tracing&&this.emit({type:"diagnostic",where:i.startsWith("deck ")?`${i} gate`:`${i} condition`,message:o}),!1}}tagByGameId(e,t){return e.tags.find(r=>r.gameId===t)}askForHand(e,t){let r=new Map,s={},o=a=>{for(let[d,l]of Object.entries(a??{})){if(Ce(l)){this.fillHoleFromProperty(e,d,l,r,s);continue}r.set(d,l);let c=this.internals.groupsById.get(d),p=c?.group.tags.find(u=>u.id===l);c&&p&&(s[f(c.group)]=f(p))}},i;if(e.template!==void 0){let a=this.internals.templatesById.get(e.template);if(!a)throw new b(`hand "${f(e)}": unknown template "${e.template}"`);for(let[d,l]of Object.entries(a.bindings??{}))r.set(d,l);o(e.chosen),i=a.condition}else o(e.rule?.bindings),i=e.rule?.condition;return r.set(I,e.id),this.bindStateGroups(t,r,s),{box:t,hand:e,...i!==void 0?{condition:i}:{},boundTags:r,askNames:s}}askForPeek(e,t){let r=new Map,s={};for(let[o,i]of Object.entries(t)){if(o===I){let l=this.internals.handsByGameId.get(i)??this.internals.handsById.get(i);if(!l)throw new b(`peek: unknown hand "${i}" in home criteria`);r.set(I,l.hand.id);continue}let a=this.groupInBox(e,o);if(!a)throw new b(`peek: unknown tag group "${o}" in box "${f(e)}"`);let d=this.tagByGameId(a,i)??a.tags.find(l=>l.id===i);if(!d)throw new b(`peek: unknown tag "${i}" in group "${f(a)}"`);r.set(a.id,d.id),s[f(a)]=f(d)}return this.bindStateGroups(e,r,s),{box:e,boundTags:r,askNames:s}}fillHoleFromProperty(e,t,r,s,o){let i=this.internals.groupsById.get(t),a=i?f(i.group):t,d=`hand ${f(e)}, tag group ${a}`,l=Gt(r);if(!l){this.emit({type:"diagnostic",where:d,message:`"${r}" is not a @hand, @world or @story property reference`});return}if(!i){this.emit({type:"diagnostic",where:d,message:`"${r}" fills a tag group that is not in this box`});return}let c;if(l.scope==="hand")c=this.valuesOf("hand",e.id)[l.name];else try{c=this.getProperty(`${l.scope}.${l.name}`)}catch{c=void 0}if(c===void 0){this.emit({type:"diagnostic",where:d,message:`"${r}" names a property that is not declared`});return}let p=typeof c=="string"?c:String(c),u=i.group.tags.find(g=>f(g)===p);if(!u){this.emit({type:"diagnostic",where:d,message:`${r} is "${p}", which is not one of the tags of "${a}"`});return}s.set(t,u.id),o[a]=f(u)}bindStateGroups(e,t,r){for(let s of ue(this.internals.bundle,e)){if(s.boundBy===void 0||t.has(s.id))continue;let o=/^@(world|story)\.([a-z][a-z0-9_-]*)$/.exec(s.boundBy);if(!o){this.emit({type:"diagnostic",where:`tag group ${f(s)}`,message:`boundBy "${s.boundBy}" is not a @world or @story property reference`});continue}let i;try{i=this.getProperty(`${o[1]}.${o[2]}`)}catch{this.emit({type:"diagnostic",where:`tag group ${f(s)}`,message:`boundBy "${s.boundBy}" names a property that is not declared`});continue}let a=typeof i=="string"?i:String(i),d=s.tags.find(l=>f(l)===a);if(!d){this.emit({type:"diagnostic",where:`tag group ${f(s)}`,message:`${s.boundBy} is "${a}", which is not one of its tags`});continue}t.set(s.id,d.id),r[f(s)]=f(d)}}valuesOf(e,t){return Object.assign(Object.create(null),this.internals.shared[e].get(t)?.values??{},this.stores[e].get(t)?.values??{})}buildHandEnv(e){let t=Object.create(null),r=new Map;for(let[s,o]of e.boundTags)if(s!==I)for(let[i,a]of Object.entries(this.valuesOf("value",o)))t[i]=a,r.set(i,{kind:"value",id:o});if(e.hand)for(let[s,o]of Object.entries(this.valuesOf("hand",e.hand.id)))t[s]=o,r.set(s,{kind:"hand",id:e.hand.id});for(let[s,o]of Object.entries(e.askNames))t[s]=o,r.set(s,{kind:"criteria"});return{bag:t,sources:r,boundTags:e.boundTags}}claims(){let e=new Map;for(let t of this.boardContents.values())for(let r of t)e.set(r,(e.get(r)??0)+1);return e}heldCardIds(){return[...this.boardContents.values()].flat()}claimVerdict(e,t,r,s,o=0){let i=e.id;return(r.get(i)??0)-o>=ln(e)?"claimed":t&&(s.get(i)??0)-o>=dn(e)?"claimed-elsewhere":null}tagsMatch(e,t){let r=e.tags?.[I];if(r!==void 0&&r.length>0){let s=t.get(I);if(s===void 0||!r.includes(s))return!1}for(let[s,o]of t){if(s===I)continue;let i=e.tags?.[s];if(i===void 0){if(this.internals.requiredGroups.has(s))return!1;continue}if(!i.includes(o))return!1}return!0}runAsk(e,t,r,s){let{box:o}=e,i=this.buildHandEnv(e),a=(u,g)=>{r?.push({id:f(u),verdict:g})};if(!this.passes(e.condition,this.evalCtx(o,void 0,i),()=>`hand ${e.hand?f(e.hand):""}`))return{ordered:[],handEnv:i};let d=new Map;for(let u of o.decks)d.set(u.id,this.passes(u.condition,this.evalCtx(o,u,i),()=>`deck ${u.gameId}`));let l=this.turnCounts.get(o.id)??0,c=[];for(let u of o.decks){let g=this.evalCtx(o,u,i),m=u.shared??!1;for(let h of u.cards){let y=ne(h,m);if(!d.get(u.id)){a(h,"deck-gate");continue}if(y&&this.engine.isTaken(h.id)){a(h,"taken");continue}if((this.cooldowns[h.id]??0)>l){a(h,"cooldown");continue}if(!this.tagsMatch(h,i.boundTags)){a(h,"tags");continue}if(h.condition&&!this.passes(h.condition,g,()=>`card ${h.gameId}`)){a(h,"condition");continue}let x=t(h,y);if(x){a(h,x);continue}let w;if(typeof h.priority=="number")w=h.priority;else try{let E=this.eval(h.priority,g);if(typeof E!="number"||Number.isNaN(E)){a(h,"priority");continue}w=E}catch(E){this.tracing&&this.emit({type:"diagnostic",where:`card ${h.gameId} priority`,message:E instanceof Error?E.message:String(E)}),a(h,"priority");continue}let S=0;if(o.ranking.specificity&&h.condition){let E=this.node(h.condition);S=At(E,P=>{try{return Yt(Ee(P,g,Ye))}catch{return!1}})}c.push({entry:{card:h,deck:u,box:o},priority:w,spec:S})}}c.sort((u,g)=>g.priority-u.priority||g.spec-u.spec);let p=0;for(;p<c.length;){let u=p+1;for(;u<c.length&&c[u].priority===c[p].priority&&c[u].spec===c[p].spec;)u++;if(u-p>1){let g=gr(c.slice(p,u),s,this.prng);for(let m=0;m<g.length;m++)c[p+m]=g[m]}p=u}for(let u of c)r?.push({id:f(u.entry.card),verdict:"dealt",priority:u.priority,specificity:u.spec});return{ordered:c.map(u=>u.entry),handEnv:i}}capTrace(e,t){for(let r of e)r.verdict==="dealt"&&!t.has(r.id)&&(r.verdict="capped")}view(e){let{card:t}=e;return{id:t.id,gameId:f(t),...t.title!==void 0?{title:t.title}:{},...t.purpose!==void 0?{purpose:t.purpose}:{},...t.fields!==void 0?{fields:structuredClone(t.fields)}:{},...t.extensions!==void 0?{extensions:t.extensions}:{}}}handCapacity(e){if(e.slots!==void 0)return e.slots;let t=e.template!==void 0?this.internals.templatesById.get(e.template)?.slots:e.rule?.slots;return t===void 0||t==="unbounded"?1/0:t}resolveHand(e){let t=this.internals.handsByGameId.get(e)??this.internals.handsById.get(e);if(!t)throw new b(`unknown hand "${e}"`);return t}peek(e,t={},r){this.assertOpen();let s=this.resolveBox(e),o=this.askForPeek(s,t),i=this.claims(),a=this.internals.hasShared?this.engine.sharedClaims():new Map,d=this.tracing?[]:void 0,l=this.prng;this.prng=T(l.state());let c;try{({ordered:c}=this.runAsk(o,(u,g)=>this.claimVerdict(u,g,i,a),d))}finally{this.prng=l}let p=r===void 0?c:c.slice(0,Math.max(r,0));return d&&(this.capTrace(d,new Set(p.map(u=>f(u.card)))),this.emit({type:"peek",box:f(s),criteria:t,cards:d},this.turnCounts.get(s.id)??0)),{box:f(s),cards:p.map(u=>this.view(u))}}deal(e){this.assertOpen();let{hand:t}=this.resolveHand(e);return this.dealMany([e])[f(t)]??[]}evictUnavailable(e,t){let r=this.askForHand(e,t),s=this.buildHandEnv(r),o=this.passes(r.condition,this.evalCtx(t,void 0,s),()=>`hand ${f(e)}`),i=new Map,a=new Map;for(let u of t.decks){let g=this.evalCtx(t,u,s);a.set(u.id,g),i.set(u.id,this.passes(u.condition,g,()=>`deck ${u.gameId}`))}let d=this.turnCounts.get(t.id)??0,l=[],c=(u,g)=>{let m=this.internals.cardsById.get(u);return l.push({card:m?f(m.card):u,reason:g}),!1},p=(this.boardContents.get(e.id)??[]).filter(u=>{if(!o)return c(u,"hand-condition");let g=this.internals.cardsById.get(u);return!g||g.box.id!==t.id?c(u,"vanished"):i.get(g.deck.id)?ne(g.card,g.deck.shared??!1)&&this.engine.isTaken(u)?c(u,"taken"):(this.cooldowns[u]??0)>d?c(u,"cooldown"):this.tagsMatch(g.card,s.boundTags)?this.passes(g.card.condition,a.get(g.deck.id),()=>`card ${g.card.gameId}`)?!0:c(u,"condition"):c(u,"tags"):c(u,"deck-gate")});return this.boardContents.set(e.id,p),l}dropUnqualified(){let e=this.prng;this.prng=T(e.state());try{let t=[];for(let r of[...this.internals.handsById.keys()].sort()){let{hand:s,box:o}=this.internals.handsById.get(r);for(let i of this.evictUnavailable(s,o))t.push({hand:f(s),...i})}return t}finally{this.prng=e}}dealMany(e){this.assertOpen();let t=(e??[...this.internals.handsById.keys()].sort()).map(o=>this.resolveHand(o));_(t,this.prng);for(let{hand:o,box:i}of t){let a=this.evictUnavailable(o,i);if(this.tracing){let d=this.turnCounts.get(i.id)??0;for(let l of a)this.emit({type:"evict",hand:f(o),card:l.card,reason:l.reason},d)}}let r=this.claims(),s=this.internals.hasShared?this.engine.sharedClaims():new Map;for(let{hand:o,box:i}of t){let a=this.boardContents.get(o.id)??[],d=new Map;for(let y of a)d.set(y,(d.get(y)??0)+1);let l=this.askForHand(o,i),c=this.tracing?[]:void 0,{ordered:p}=this.runAsk(l,(y,x)=>this.claimVerdict(y,x,r,s,d.get(y.id)??0),c,d),u=new Map,g=this.handCapacity(o);for(let y of p){if(g<=0)break;let x=Math.min(fr(y.card),g);u.set(y.card.id,x),g-=x}let m=[],h=[];for(let y of a){let x=u.get(y)??0;x>0?(m.push(y),u.set(y,x-1)):h.push(y)}for(let y of p){for(let x=u.get(y.card.id)??0;x>0;x--)m.push(y.card.id);u.delete(y.card.id)}this.boardContents.set(o.id,m);for(let y of a)r.set(y,(r.get(y)??1)-1),s.set(y,(s.get(y)??1)-1);for(let y of m)r.set(y,(r.get(y)??0)+1),s.set(y,(s.get(y)??0)+1);if(c){let y=this.turnCounts.get(i.id)??0;for(let x of h)this.emit({type:"evict",hand:f(o),card:f(this.internals.cardsById.get(x).card),reason:"outranked"},y);this.capTrace(c,new Set(m.map(x=>f(this.internals.cardsById.get(x).card)))),this.emit({type:"deal",hand:f(o),cards:c},y)}}return Object.fromEntries(t.map(({hand:o})=>[f(o),(this.boardContents.get(o.id)??[]).map(i=>this.view(this.internals.cardsById.get(i)))]))}board(e){this.assertOpen();let t=e!==void 0?this.resolveBox(e).id:void 0;return Object.fromEntries([...this.boardContents.entries()].filter(([r])=>t===void 0||this.internals.handsById.get(r).box.id===t).map(([r,s])=>[f(this.internals.handsById.get(r).hand),s.map(o=>this.view(this.internals.cardsById.get(o)))]))}resolveDealt(e,t){let r=this.internals.cardsById.get(e)??this.internals.cardsByGameId.get(e);if(!r)throw new b(`unknown card "${e}"`);let{hand:s,box:o}=this.resolveHand(t);if(!(this.boardContents.get(s.id)??[]).includes(r.card.id))throw new b(`card "${f(r.card)}" is not dealt to hand "${f(s)}"`);return{entry:r,ask:this.askForHand(s,o)}}outcomes(e,t){this.assertOpen();let{entry:r,ask:s}=this.resolveDealt(e,t),o=this.evalCtx(r.box,r.deck,this.buildHandEnv(s));return r.card.outcomes.map(i=>({id:i.id,gameId:f(i),...i.title!==void 0?{title:i.title}:{},...i.purpose!==void 0?{purpose:i.purpose}:{},...i.fields!==void 0?{fields:structuredClone(i.fields)}:{},...i.extensions!==void 0?{extensions:i.extensions}:{},available:this.passes(i.condition,o,()=>`outcome ${f(r.card)}/${f(i)}`)}))}play(e,t,r,s={}){this.assertOpen();let{entry:o,ask:i}=this.resolveDealt(e,r),a=t==="";if(a&&o.card.outcomes.length>0)throw new b(`card "${f(o.card)}" has outcomes (${o.card.outcomes.map(P=>f(P)).join(", ")}); name the one played`);let d=a?void 0:o.card.outcomes.find(P=>f(P)===t);if(!a&&!d)throw new b(`card "${f(o.card)}" has no outcome "${t}"`);let l=this.buildHandEnv(i),c=this.evalCtx(o.box,o.deck,l);if(d&&!this.passes(d.condition,c,()=>`outcome ${f(o.card)}/${t}`))throw new b(`outcome "${t}" on "${f(o.card)}" is gated shut`);let p=o.box.turn!==void 0?0:this.internals.bundle.settings.playAdvancesTurns,u=(this.turnCounts.get(o.box.id)??0)+(s.advanceTurns??p),m=Object.entries(d?.changes??{}).map(([P,V])=>({target:P,expr:V,plan:this.planWrite(P,o,l)})).map(P=>({...P,value:this.eval(P.expr,c)})),h=[];try{for(let P of m)h.push({target:P.target,plan:P.plan,value:P.value,prev:this.landWrite(P.plan,P.value)})}catch(P){for(let V of[...h].reverse())this.undoWrite(V.plan,V.prev);throw P}if(this.tracing)for(let{target:P,plan:V,value:Se,prev:ie}of h)this.emit({type:"write",target:P,path:V.path,value:Se,...ie!==void 0?{prev:ie}:{}},u);let y=d?f(d):"",x={card:f(o.card),outcome:y,turn:u};this.playLog.push(x),this.indexPlay(x),o.card.redraw==="never"?ne(o.card,o.deck.shared??!1)?this.engine.markTaken(o.card.id):this.cooldowns[o.card.id]=Number.MAX_SAFE_INTEGER:typeof o.card.redraw=="number"&&(this.cooldowns[o.card.id]=u+o.card.redraw);let w=i.hand.id,S=this.boardContents.get(w)??[],E=S.indexOf(o.card.id);this.boardContents.set(w,[...S.slice(0,E),...S.slice(E+1)]),this.turnCounts.set(o.box.id,u),this.tracing&&this.emit({type:"play",card:f(o.card),outcome:y,turn:u},u)}discard(e,t){this.assertOpen();let{entry:r,ask:s}=this.resolveDealt(e,t),o=this.turnCounts.get(r.box.id)??0;r.card.redraw==="never"?ne(r.card,r.deck.shared??!1)?this.engine.markTaken(r.card.id):this.cooldowns[r.card.id]=Number.MAX_SAFE_INTEGER:typeof r.card.redraw=="number"&&(this.cooldowns[r.card.id]=o+r.card.redraw);let i=s.hand.id,a=this.boardContents.get(i)??[],d=a.indexOf(r.card.id);this.boardContents.set(i,[...a.slice(0,d),...a.slice(d+1)]),this.tracing&&this.emit({type:"discard",hand:f(s.hand),card:f(r.card),turn:o},o)}address(e,t){return H(this.internals,e,t)}planBag(e,t,r,s){let o=e==="story"?this.stores.story:t!==void 0?this.stores[e].get(t):void 0,i=e==="story"?this.internals.shared.story:t!==void 0?this.internals.shared[e].get(t):void 0,a=O(o,r)!==void 0?o:O(i,r)!==void 0?i:void 0;if(a===void 0)throw new b(`no property at "${s}"`);if(a.declarations().some(d=>d.name===r&&d.writable===!1))throw new b(`'${r}' is read-only`);return{kind:"bag",bag:a,name:r,path:s}}planWrite(e,t,r){let s=/^@([a-z][a-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_-]*)$/.exec(e);if(!s)throw new b(`bad change target "${e}"`);let[,o,i]=s,a=this.internals.hostScopes.get(o);if(a!==void 0){if(!a.set)throw new b(`@${o}.${i} cannot be written: the host bound @${o} read-only`);if(gn(a.spec,i))throw new b(`'@${o}.${i}' is read-only (writable: false)`);return{kind:"host",host:a,name:i,path:`${o}.${i}`}}switch(o){case"story":return this.planBag("story",void 0,i,`story.${i}`);case"box":return this.planBag("box",t.box.id,i,`${this.address("box",t.box.id)}.${i}`);case"deck":return this.planBag("deck",t.deck.id,i,`${this.address("deck",t.deck.id)}.${i}`);case"hand":{let d=r.sources.get(i);if(!d)throw new b(`@hand.${i} is not composed in this ask`);if(d.kind==="criteria")throw new b(`@hand.${i} is a chosen tag / criteria name and cannot be written`);return this.planBag(d.kind,d.id,i,`${this.address(d.kind,d.id)}.${i}`)}default:{if(this.internals.registry.has(o))return{kind:"scope",scope:o,name:i,path:`${o}.${i}`};throw this.internals.bundle.externalScopes?.includes(o)?new b(`@${o}.${i} cannot be written: no engine on this registry registered @${o}`):new b(`bad change target scope "@${o}"`)}}}landWrite(e,t){switch(e.kind){case"host":{let r=e.host.resolver.get(e.name);return e.host.set(e.name,t),r}case"bag":return e.bag.set(e.name,t).prev;case"scope":{let r=this.internals.registry.get(e.scope,e.name);return this.internals.registry.set(e.scope,e.name,t),r}}}undoWrite(e,t){if(t!==void 0)switch(e.kind){case"host":e.host.set(e.name,t,!0);return;case"bag":e.bag.set(e.name,t,{silent:!0,reason:"play refused",host:!0});return;case"scope":this.internals.registry.set(e.scope,e.name,t,{host:!0});return}}advanceTurns(e,t=1){this.assertOpen();let r=this.resolveBox(e),s=(this.turnCounts.get(r.id)??0)+t;this.turnCounts.set(r.id,s),this.tracing&&this.emit({type:"turns",box:f(r),turn:s},s)}listBoxes(){return this.assertOpen(),this.internals.bundle.boxes.map(e=>({id:e.id,gameId:f(e),...e.title!==void 0?{title:e.title}:{},turn:this.turnCounts.get(e.id)??0,...e.extensions!==void 0?{extensions:e.extensions}:{}}))}listBags(){this.assertOpen();let e=[{prefix:"story",bag:this.stores.story}];for(let t of L)for(let[r,s]of this.stores[t])e.push({prefix:H(this.internals,t,r),bag:s});return e}listProperties(){this.assertOpen();let e=wn(this.internals),t=(r,s,o)=>{for(let i of[s,o])if(i!==void 0)for(let a of i.rows())e.push(a)};t("story",this.internals.shared.story,this.stores.story);for(let r of L){let s=new Set([...this.internals.shared[r].keys(),...this.stores[r].keys()]);for(let o of s)t(H(this.internals,r,o),this.internals.shared[r].get(o),this.stores[r].get(o))}return e}getProperty(e){this.assertOpen();let t=this.resolvePath(Oe(e)),r=t.kind==="host"?t.host.resolver.get(t.name):t.kind==="scope"?this.internals.registry.get(t.token,t.name):O(t.own,t.name)??O(t.shared,t.name);if(r===void 0)throw new b(`no property at "${e}"`);return r}setProperty(e,t){this.assertOpen();let r=this.resolvePath(Oe(e));if(bn(this.internals,e,t),r.kind==="host"){if(!r.host.set)throw new b(`@${r.host.spec.token} is read-only here: the host bound no write`);r.host.set(r.name,t,!0);return}if(r.kind==="scope"){this.internals.registry.set(r.token,r.name,t,{host:!0});return}let s=O(r.own,r.name)!==void 0?r.own:O(r.shared,r.name)!==void 0?r.shared:void 0;if(s===void 0)throw new b(`no property at "${e}"`);s.set(r.name,t,{silent:!0,reason:"host setProperty",host:!0})}resolvePath(e){let t=e.split("."),r=t.length===2?this.internals.hostScopes.get(t[0]):void 0;if(r!==void 0)return{kind:"host",host:r,name:t[1]};if(t.length===2&&t[0]!=="story"&&this.internals.registry.has(t[0]))return{kind:"scope",token:t[0],name:t[1]};if(t.length===2&&t[0]==="story")return{kind:"bag",own:this.stores.story,shared:this.internals.shared.story,name:t[1]};if(t.length===3&&pt(t[0])){let s=t[0],[,o,i]=t,a=gt(this.internals,s,o,i);a.legacy&&this.tracing&&this.emit({type:"diagnostic",where:"property address",message:hn(this.internals,s,o,i)});let d=this.stores[s].get(a.id),l=this.internals.shared[s].get(a.id);if(d===void 0&&l===void 0)throw new b(`no ${s} store "${o}"`);return{kind:"bag",...d!==void 0?{own:d}:{},...l!==void 0?{shared:l}:{},name:i}}throw new b(`bad property path "${e}"`)}saveDurable(){this.assertOpen();let{bundle:e,durable:t}=this.internals;return structuredClone({schema:we,content:e.content,values:pn(this.stores,t.flow),spent:t.flowCards.filter(r=>this.cooldowns[r.card.id]===Number.MAX_SAFE_INTEGER).map(r=>f(r.card)).sort()})}writeDurable(e){for(let[t,r]of e.values)un(ut(this.stores,t),t.decl.name,r);for(let t of e.cards)this.cooldowns[t]=Number.MAX_SAFE_INTEGER}snapshot(e){let t=this.internals,r=o=>{let i=t.boxesById.get(o);return i!==void 0?f(i):o},s=o=>{let i=t.handsById.get(o);return i!==void 0?f(i.hand):o};return{...e?{props:Sr(this.stores)}:{},turns:Object.fromEntries([...this.turnCounts].map(([o,i])=>[r(o),i])),prng:this.prng.state(),cooldowns:Object.fromEntries(Object.entries(this.cooldowns).map(([o,i])=>[lt(t,o),i])),board:Object.fromEntries([...this.boardContents].map(([o,i])=>[s(o),i.map(a=>lt(t,a))])),playLog:this.playLog}}restore(e){e.props!==void 0&&Er(this.stores,e.props),this.turnCounts=new Map(this.internals.bundle.boxes.map(t=>[t.id,0]));for(let[t,r]of Object.entries(e.turns??{}))this.turnCounts.has(t)&&this.turnCounts.set(t,r);this.prng=T(e.prng),this.cooldowns=e.cooldowns??{},this.playLog=e.playLog??[],this.rebuildPlayIndex(),this.boardContents=new Map(Object.entries(e.board??{}).filter(([t])=>this.internals.handsById.has(t)).map(([t,r])=>[t,r.filter(s=>this.internals.cardsById.has(s))]));for(let t of this.internals.handsById.keys())this.boardContents.has(t)||this.boardContents.set(t,[])}};var Ae=n=>n.map(e=>({name:e.name,type:e.type,default:N(e),...e.values!==void 0?{values:e.values}:{},...e.durable===!0?{durable:!0}:{},...e.purpose!==void 0?{purpose:e.purpose}:{}})),vn=n=>n.decks.reduce((e,t)=>e+t.cards.filter(r=>(r.durable??t.durable)===!0).length,0),Br=(n,e)=>n.template!==void 0?e.handTemplates.find(t=>t.id===n.template)?.properties??[]:n.properties??[],Lr=(n,e,t)=>{let r=e.template!==void 0?e.chosen:e.rule?.bindings,s=[];for(let[o,i]of Object.entries(r??{})){if(!Ce(i))continue;let a=ue(n,t).find(d=>d.id===o);a!==void 0&&s.push({group:f(a),from:i})}return s},Or=(n,e)=>{if(n.slots!==void 0)return n.slots;let t=n.template!==void 0?e.handTemplates.find(r=>r.id===n.template)?.slots:n.rule?.slots;return t===void 0?"unbounded":t};function De(n){let e=[],t=[],r=Z(n),s=[...r.some(a=>a.token==="world")?[]:[{scope:"world",owner:"",properties:[]}],...r.map(a=>({...a.token==="world"?{scope:"world",owner:""}:{scope:"host",owner:a.token},...a.declarations===void 0?{opaque:!0}:{},properties:Ae(a.declarations??[])})),{scope:"story",owner:"",properties:Ae(n.story.properties)}],o={boxes:0,decks:0,cards:0,hands:0,templates:0,tagGroups:0};for(let a of n.boxes){let d=f(a),l=a.decks.reduce((p,u)=>p+u.cards.length,0);e.push({gameId:d,...a.title!==void 0?{title:a.title}:{},...a.usesMap===!0?{usesMap:!0}:{},ranking:{specificity:a.ranking.specificity},...a.turn!==void 0?{turn:a.turn.seconds!==void 0?{seconds:a.turn.seconds}:{}}:{},...vn(a)>0?{durableCards:vn(a)}:{},tagGroups:a.tagGroups.map(p=>({gameId:f(p),tags:p.tags.map(u=>f(u))})),counts:{decks:a.decks.length,cards:l,hands:a.hands.length,templates:a.handTemplates.length,tagGroups:a.tagGroups.length}}),o.boxes+=1,o.decks+=a.decks.length,o.cards+=l,o.hands+=a.hands.length,o.templates+=a.handTemplates.length,o.tagGroups+=a.tagGroups.length;for(let p of a.hands){let u=p.template!==void 0?a.handTemplates.find(m=>m.id===p.template):void 0,g=Lr(n,p,a);t.push({gameId:f(p),...p.title!==void 0?{title:p.title}:{},box:d,slots:Or(p,a),...u!==void 0?{template:f(u)}:{},...g.length>0?{movable:g}:{}})}let c=(p,u,g,m)=>{g.length!==0&&s.push({scope:p,owner:u,box:d,...m!==void 0?{group:m}:{},properties:Ae(g)})};c("box",d,a.properties);for(let p of a.decks)c("deck",f(p),p.properties);for(let p of a.hands)c("hand",f(p),Br(p,a));for(let p of a.tagGroups)for(let u of p.tags)c("tag",f(u),u.properties??[],f(p))}let i=n.map;if(i!==void 0){let a=f(i.group);for(let d of i.group.tags){let l=d.properties??[];l.length>0&&s.push({scope:"tag",owner:f(d),group:a,properties:Ae(l)})}o.tagGroups+=1}return{identity:{schema:n.schema,project:n.content.project,version:n.content.version,hash:n.content.hash,metadata:n.metadata},totals:o,boxes:e,hands:t,properties:s,...i!==void 0?{map:{group:f(i.group),tags:i.group.tags.map(a=>f(a)),boxes:n.boxes.filter(a=>a.usesMap===!0).map(a=>f(a)),zones:i.geometry?.zones.length??0,backgrounds:i.geometry?.backgrounds?.length??0,sites:Object.fromEntries(Object.entries(i.geometry?.sites??{}).map(([a,d])=>[a,d.length]))}}:{}}}var xn=(n,e)=>n.boxes.find(t=>t.gameId===e)??n.boxes.find(t=>t.id===e),Te=(n,e)=>Object.prototype.hasOwnProperty.call(n,e);function Sn(n,e){return xn(n,e)?.fields??[]}function En(n,e){return xn(n,e)?.outcomeFields??[]}function ft(n,e,t){if(e!==void 0&&Te(e,t))return e[t];let r=n.find(s=>s.name===t)?.default;return r!==null&&typeof r=="object"?structuredClone(r):r}function kn(n,e){let t={};for(let r of n){let s=ft(n,e,r.name);s!==void 0&&(t[r.name]=s)}for(let[r,s]of Object.entries(e??{}))Te(t,r)||(t[r]=s);return t}function Pn(n,e,t){let r=n.extensions!==void 0&&Te(n.extensions,e)?n.extensions[e]:void 0;return r!==void 0&&Te(r.fields,t)?r.fields[t]??[]:[]}function Cn(n,e,t=""){let r=d=>({...d?.extensions??{}}),[s,o]=t.includes("/")?[t.slice(0,t.indexOf("/")),t.slice(t.indexOf("/")+1)]:[t,void 0],i=(d,l)=>d.find(c=>(c.gameId??c.id)===l),a=()=>n.boxes.flatMap(d=>d.decks.flatMap(l=>l.cards));switch(e){case"project":{let d={};for(let[l,c]of Object.entries(n.extensions??{}))c.project!==void 0&&(d[l]=c.project);return d}case"box":return r(i(n.boxes,t));case"deck":return r(i(n.boxes.flatMap(d=>d.decks),t));case"card":return r(i(a(),t));case"outcome":return o===void 0?{}:r(i(i(a(),s)?.outcomes??[],o));case"hand":return r(i(n.boxes.flatMap(d=>d.hands),t));case"handTemplate":return o===void 0?{}:r(i(i(n.boxes,s)?.handTemplates??[],o));case"tagGroup":return o===void 0?n.map!==void 0&&(n.map.group.gameId??n.map.group.id)===s?r(n.map.group):{}:r(i(i(n.boxes,s)?.tagGroups??[],o));case"tag":return r(o===void 0?n.map!==void 0?i(n.map.group.tags,s):void 0:i((i(n.boxes,s)?.tagGroups??[]).flatMap(d=>d.tags),o));default:return{}}}function $n(n,e){let t={};for(let{bag:r}of[...n.listBags(),...e.listBags()])for(let s of r.rows())s.value!==void 0&&(t[s.path]=s.value);return Object.assign(t,Bn(Rn(n,e.id))),t}function Rn(n,e){return n.getFlow(e)!==void 0?n.saveFlow(e):void 0}function Bn(n){let e={};if(n===void 0)return e;for(let[t,r]of Object.entries(n.turns))e[`turn:${t}`]=r;for(let[t,r]of Object.entries(n.cooldowns))e[`cooldown:${t}`]=r;for(let[t,r]of Object.entries(n.board))e[`board:${t}`]=[...r];return e}function Ln(n,e,t={}){let r=e.id,s=()=>n.getFlow(r);return fe({mounts:()=>[...n.listBags(),...s()?.listBags()??[]].map(({bag:o})=>({bag:o})),extra:()=>Bn(Rn(n,r))},t)}function Ne(n,e){return JSON.stringify(ht(n,e),null,2)}function ht(n,e){return{schema:$e,engine:n.saveGame(),...e!==void 0?{world:e}:{}}}function mt(n,e,t={}){if(!e||typeof e!="object"||e.schema!==$e||e.engine?.schema!==q&&e.engine?.schema!==ee&&e.engine?.schema!==W)throw new Error(`not a storylets save (expected schema "${$e}")`);let r=t.world,s=r!==void 0?structuredClone(r.values()):void 0;r!==void 0&&e.world!==void 0&&r.load(e.world);let o;try{o=n.loadGame(e.engine)}catch(i){throw r!==void 0&&s!==void 0&&r.load(s),i}return t.onReport?.(o),e.world}function He(n,e,t={}){let r;try{r=JSON.parse(e)}catch{throw new Error("not valid JSON")}return mt(n,r,t)}function Mr(n,e){n.textContent="";let t=/\u0002([^\u0003]*)\u0003/g,r=0,s;for(;(s=t.exec(e))!==null;){s.index>r&&n.append(e.slice(r,s.index));let o=document.createElement("strong");o.textContent=s[1]??"",n.append(o),r=s.index+s[0].length}r<e.length&&n.append(e.slice(r))}var wt=!1,On=!1,An,Ir=350,yt=6,Mn=7,se=null,oe=0,Ve=null,Ar=null;function Dn(){if(se)return se;let n=document.createElement("div");return n.className="tooltip",n.setAttribute("role","tooltip"),n.hidden=!0,document.body.appendChild(n),se=n,n}var Dr=n=>n instanceof HTMLElement?n.getBoundingClientRect():n;function Tr(n){let e=Dn(),t=Dr(n),r=e.getBoundingClientRect(),s=t.top-r.height-Mn;s<yt&&(s=t.top+t.height+Mn);let o=t.left+t.width/2-r.width/2;o=Math.max(yt,Math.min(o,window.innerWidth-r.width-yt)),e.style.left=`${Math.round(o)}px`,e.style.top=`${Math.round(s)}px`}function Nr(n){let e=n.dataset.tip;e&&Hr(n,e)}function Hr(n,e){if(An?.())return;let t=Dn(),r=(n instanceof HTMLElement?n.closest("dialog[open]"):null)??document.body;t.parentElement!==r&&r.appendChild(t),Ve=n instanceof HTMLElement?n:null,Mr(t,e),t.hidden=!1,t.classList.remove("show"),t.offsetWidth,Tr(n),t.classList.add("show")}function re(){oe&&(window.clearTimeout(oe),oe=0),Ve=null,Ar=null,se&&(se.hidden=!0,se.classList.remove("show"))}function In(n){n!==Ve&&(oe&&window.clearTimeout(oe),oe=window.setTimeout(()=>Nr(n),Ir))}function Vr(){Fr()}function jr(){On||(On=!0,setTimeout(()=>{wt||typeof document>"u"||document.querySelector("[data-tip]")&&console.warn("app-shell: something set `data-tip` but no tooltip host is mounted, so those rollovers will not draw. Call initTooltips() once per window.")},0))}var bt=n=>n instanceof Element?n.closest("[data-tip]"):null;function Fr(n={}){n.suppressed!==void 0&&(An=n.suppressed),!wt&&(wt=!0,document.addEventListener("pointerover",e=>{let t=bt(e.target);t&&In(t)}),document.addEventListener("pointerout",e=>{let t=bt(e.target);if(!t)return;let r=e.relatedTarget;r instanceof Node&&t.contains(r)||re()}),document.addEventListener("focusin",e=>{let t=bt(e.target);t&&In(t)}),document.addEventListener("focusout",()=>re()),document.addEventListener("pointerdown",()=>re(),!0),document.addEventListener("scroll",()=>{Ve&&re()},!0),document.addEventListener("keydown",e=>{e.key==="Escape"&&re()}),window.addEventListener("blur",()=>re()))}function Tn(n,e,...t){let r=document.createElement(n),s=typeof e=="string"?{className:e}:e??{};s.className&&(r.className=s.className),s.text!==void 0&&s.text!==null&&(r.textContent=s.text),s.title!==void 0&&(r.title=s.title),s.tip!==void 0&&(r.dataset.tip=s.tip,r.setAttribute("aria-label",s.tip),Vr()),s.onClick&&r.addEventListener("click",s.onClick);for(let o of t)o!=null&&r.append(o);return jr(),r}function Nn(n){let e=n.default;if(e!=null)return typeof e=="boolean"?e?"True":"False":Array.isArray(e)?e.length===0?"none":e.join(", "):e===""?"none":String(e);switch(n.type){case"boolean":return"False";case"number":return"0";case"enum":return n.values?.[0]??"none";case"quality":return n.stages?.[0]??"first stage";case"flags":return"none";default:return"empty"}}var Hn=n=>`default (${Nn(n)})`;function vt(n){let e=n.doc===void 0?Tn:(a,d)=>{let l=n.doc.createElement(a);return l.className=d.className,d.text!==void 0&&(l.textContent=d.text),d.tip!==void 0&&(l.title=d.tip,l.setAttribute("aria-label",d.tip)),d.onClick&&l.addEventListener("click",d.onClick),a==="button"&&(l.type="button"),l},t=e("span",{className:"seg shell-rungs"}),r=n.decl??{type:"quality",stages:n.stages},s=n.value===void 0,o=typeof r.default=="string"&&n.stages.includes(r.default)?r.default:n.stages[0],i=n.onPick===void 0;n.onClear&&t.append(e("button",{className:`seg-opt shell-rung shell-rung-default${s?" on":""}`,text:Hn(r),tip:s?"Not set here: the declared default holds":"Clear the value, back to the default",onClick:()=>{s||n.onClear()}}));for(let a of n.stages){let d=n.value===a,l=s&&a===o,c=e(i?"span":"button",{className:`seg-opt shell-rung${d?" on":""}${l?" fallback":""}`,text:a,tip:d?"The current stage":l?"The stage it falls back to":i?a:n.pickTip?.(a)??`Set to "${a}"`,...i?{}:{onClick:()=>{d||n.onPick(a)}}});t.append(c)}return!s&&!n.stages.includes(n.value)&&t.append(e("span",{className:"seg-opt shell-rung stray on",text:n.value,tip:`"${n.value}" is not one of the stages: pick the one it belongs at now`})),t}var Vn="sl-inspector-style",_r=`
|
|
2
2
|
.sl-insp { font: 12px system-ui, sans-serif; color: var(--ink, #222); background: var(--surface, #fafafa);
|
|
3
3
|
border: 1px solid var(--line, #ccc); border-radius: 8px; padding: 10px 12px; max-width: 26rem; }
|
|
4
4
|
.sl-insp h3 { margin: 0 0 8px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em;
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
.sl-insp .seg-opt + .seg-opt { border-left: 1px solid var(--line, #ccc); }
|
|
34
34
|
.sl-insp .seg-opt.on { color: var(--ink, #222); font-weight: 600; background: var(--accent-soft, #e6eef0); cursor: default; }
|
|
35
35
|
.sl-insp .shell-rung.stray { color: #9a3b2e; text-decoration: line-through; }
|
|
36
|
-
`;function
|
|
37
|
-
`):
|
|
38
|
-
`))});let
|
|
36
|
+
`;function ve(){if(document.getElementById(Vn))return;let n=document.createElement("style");n.id=Vn,n.textContent=_r,document.head.append(n)}var Gr=(n,e)=>JSON.stringify(n)===JSON.stringify(e),qr=["deal","play","write","evict","turns","diagnostic"],Wr={deal:"Deal",play:"Play",write:"Write",evict:"Evict",turns:"Turns",diagnostic:"Diag"},zr=n=>n.type==="peek"?"deal":n.type==="discard"?"play":n.type,jn=n=>n===void 0?"<unset>":JSON.stringify(n);function xt(n){return Ur(n,"flow"in n&&n.flow?`${n.flow} `:"")}function Ur(n,e){let t=(n.turn!==void 0?`[${n.turn}] `:"[-] ")+e;switch(n.type){case"deal":{let r=n.cards.filter(s=>s.verdict==="dealt").map(s=>s.id);return`${t}deal ${n.hand}: ${r.length>0?r.join(", "):"(none)"} (${n.cards.length} considered)`}case"peek":{let r=Object.entries(n.criteria).map(([o,i])=>`${o}=${i}`).join(", "),s=n.cards.filter(o=>o.verdict==="dealt").map(o=>o.id);return`${t}peek ${n.box}${r?` [${r}]`:""}: ${s.length>0?s.join(", "):"(none)"} (${n.cards.length} considered)`}case"evict":return`${t}evict ${n.card} from ${n.hand} (${n.reason})`;case"play":return`${t}play ${n.card}${n.outcome===""?"":` -> ${n.outcome}`}`;case"write":return`${t}write ${n.path}: ${jn(n.prev)} -> ${jn(n.value)}`;case"turns":return`${t}turns ${n.box} -> ${n.turn}`;case"discard":return`${t}discard ${n.card} from ${n.hand}`;default:return`${t}diagnostic ${n.where}: ${n.message}`}}var Kr=n=>{let e=n.split(".");return e.length===3?`${e[0]} ${e[1]}`:e[0]};function Fn(n,e,t={}){ve();let r=e,s=()=>r,o=document.createElement("div");o.className="sl-insp";let i=document.createElement("div");i.className="sl-head";let a=document.createElement("h3");a.textContent=t.title??"Runtime state",i.append(a);let d=document.createElement("button");d.type="button",d.className="sl-save",d.textContent="Save state",d.addEventListener("click",()=>{let k=new Blob([Ne(n)],{type:"application/json"}),$=URL.createObjectURL(k),C=document.createElement("a");C.href=$,C.download="save.storyletsave",C.click(),URL.revokeObjectURL($)}),i.append(d);let l=document.createElement("input");l.type="file",l.accept=".storyletsave,application/json",l.hidden=!0,l.addEventListener("change",()=>{let k=l.files?.[0];if(!k)return;let $=new FileReader;$.onload=()=>{try{He(n,String($.result)),r=n.getFlow(e.id)??n.openFlow(e.id),je()}catch(C){console.error("storylets inspector: load failed:",C instanceof Error?C.message:C)}l.value=""},$.readAsText(k)});let c=document.createElement("button");c.type="button",c.className="sl-load",c.textContent="Load state",c.addEventListener("click",()=>l.click()),i.append(c,l),o.append(i);let p=document.createElement("input");p.type="text",p.className="sl-filter",p.placeholder="Filter properties",o.append(p);let u=[],g=[],m="";for(let k of s().listProperties()){let $=Kr(k.path);if($!==m){let B=document.createElement("div");B.className="sl-group",B.textContent=$,o.append(B),g.push({el:B,rows:[]}),m=$}let C=Jr(s,k,u);g[g.length-1].rows.push({el:C,text:`${k.name} ${k.path}`.toLowerCase()}),o.append(C)}p.addEventListener("input",()=>{let k=p.value.trim().toLowerCase();for(let $ of g){let C=!1;for(let B of $.rows){let M=k===""||B.text.includes(k);B.el.style.display=M?"":"none",C=C||M}$.el.style.display=C?"":"none"}});let h=document.createElement("div");h.className="sl-section",h.textContent="Turns (per box)";let y=document.createElement("div");y.className="sl-turns";let x=document.createElement("div");x.className="sl-section",x.textContent="Board";let w=document.createElement("div");w.className="sl-board",o.append(h,y,x,w);let S=(k,$,C)=>{let B=document.createElement("label");B.className=C;let M=document.createElement("input");return M.type="checkbox",M.checked=!0,M.addEventListener("change",()=>$(M.checked)),B.append(M,k),B},E=(k,$,C,B,M)=>{let _e=document.createElement("div");_e.className=`sl-section sl-${k}`,_e.textContent=$;let ae=document.createElement("div");ae.className=`sl-logbar sl-${k}`;let de=document.createElement("div");de.className=`sl-log sl-${k}`;let Ge=new Map,$t=()=>C().filter(A=>Ge.get(zr(A))!==!1).map(xt),Rt=!0,Bt="",qe=(A=!1)=>{let X=C(),Lt=`${X.length}:${X.length>0?X[X.length-1].seq:-1}`;if(!A&&Lt===Bt)return;Bt=Lt;let Ot=$t();de.textContent=Ot.length>0?Ot.join(`
|
|
37
|
+
`):M,Rt&&(de.scrollTop=de.scrollHeight)};for(let A of qr)Ge.set(A,!0),ae.append(S(Wr[A],X=>{Ge.set(A,X),qe(!0)},"sl-logkind"));ae.append(S("Autoscroll",A=>{Rt=A},"sl-logscroll"));let J=document.createElement("button");J.type="button",J.className="sl-logcopy",J.textContent="Copy",J.title="Copy the visible (filtered) log to the clipboard",J.addEventListener("click",()=>{navigator.clipboard?.writeText($t().join(`
|
|
38
|
+
`))});let Q=document.createElement("button");return Q.type="button",Q.className="sl-logclear",Q.textContent="Clear",Q.title="Drop the retained log entries (cosmetic - no game state changes)",Q.addEventListener("click",()=>{B(),qe(!0)}),ae.append(J,Q),o.append(_e,ae,de),{render:qe}},P=E("flow","Log",()=>s().log(),()=>s().clearLog(),"(empty - new Engine(bundle, { log: true }) retains the flow log)"),V=E("run","Run log (every flow)",()=>n.log(),()=>n.clearLog(),"(empty - new Engine(bundle, { log: true }) retains the run log)"),Se=(k=!1)=>{V.render(k),P.render(k)},ie=(k,$)=>{let C=document.createElement("div");C.className="sl-line",C.textContent=$,k.append(C)},Pt=()=>{y.textContent="";for(let k of s().listBoxes())ie(y,`${k.title??k.gameId}: turn ${k.turn}`);w.textContent="";for(let[k,$]of Object.entries(s().board())){let C=$.map(B=>B.title??B.gameId);ie(w,`${k}: ${C.length>0?C.join(", "):"(empty)"}`)}},je=()=>{for(let k of u)k.read();Pt(),Se()};Pt(),Se(!0);let Fe,Ct=t.pollMs??250;return Ct>0&&(Fe=setInterval(je,Ct)),(t.container??document.body).append(o),{el:o,refresh:je,destroy(){Fe!==void 0&&clearInterval(Fe),o.remove()}}}function Jr(n,e,t){let r=document.createElement("div");r.className="sl-row";let s=document.createElement("span");s.className="sl-name",s.textContent=e.name,s.title=e.path;let o=()=>{try{return n().getProperty(e.path)}catch{return}},i=()=>{},a=g=>{n().setProperty(e.path,g),i()},d=document.createElement("button");d.type="button",d.className="sl-reset",d.textContent="\u21BA",d.title="Reset to default",d.addEventListener("click",()=>a(e.default));let l,c,p=g=>document.activeElement===g;switch(e.type){case"boolean":{let g=document.createElement("input");g.type="checkbox",g.addEventListener("change",()=>a(g.checked)),l=g,c=()=>{p(g)||(g.checked=o()===!0)};break}case"number":{let g=document.createElement("input");g.type="number",g.addEventListener("change",()=>a(Number(g.value))),l=g,c=()=>{p(g)||(g.value=String(o()??0))};break}case"quality":{let g=document.createElement("span"),m=()=>{let h=o();g.replaceChildren(vt({stages:e.stages??[],value:typeof h=="string"?h:void 0,decl:e,onPick:y=>{a(y),m()},doc:document}))};l=g,c=m;break}case"enum":{let g=document.createElement("select");for(let m of e.values??[]){let h=document.createElement("option");h.value=m,h.textContent=m,g.append(h)}g.addEventListener("change",()=>a(g.value)),l=g,c=()=>{p(g)||(g.value=String(o()??""))};break}case"flags":{let g=document.createElement("input");g.type="text",g.placeholder="comma, separated, flags",g.addEventListener("change",()=>a(g.value.split(",").map(m=>m.trim()).filter(m=>m.length>0))),l=g,c=()=>{p(g)||(g.value=(o()??[]).join(", "))};break}default:{let g=document.createElement("input");g.type="text",g.addEventListener("change",()=>a(g.value)),l=g,c=()=>{p(g)||(g.value=String(o()??""))}}}let u=()=>{c(),d.disabled=Gr(o(),e.default)};return i=u,u(),t.push({row:e,read:u}),r.append(s,l,d),r}var Qr=n=>n===void 0?"<unset>":JSON.stringify(n);function St(n){let e=n.values!==void 0&&n.values.length>0?` [${n.values.join(", ")}]`:"",t=n.durable===!0?" (durable)":"";return`${n.name}: ${n.type} = ${Qr(n.default)}${e}${t}`}function Et(n){if(n.scope==="world"||n.scope==="story")return n.scope;if(n.scope==="host")return n.owner;let e=n.group!==void 0?` (${n.group})`:"";return`${n.scope} ${n.owner}${e}`}var R=(n,e,t="sl-line")=>{let r=document.createElement("div");return r.className=t,r.textContent=e,n.append(r),r},xe=(n,e,t)=>{let r=document.createElement("details");r.className="sl-fold",r.open=t;let s=document.createElement("summary");s.textContent=e,r.append(s);let o=document.createElement("div");return r.append(o),n.append(r),o};function _n(n,e={}){ve();let t=De(n),r=e.open??!0,s=document.createElement("div");s.className="sl-insp";let o=document.createElement("div");o.className="sl-head";let i=document.createElement("h3");i.textContent=e.title??"Bundle",o.append(i),s.append(o);let{identity:a,totals:d}=t,l=document.createElement("div");l.className="sl-ident",s.append(l),R(l,`${a.project} ${a.version}`,"sl-line"),R(l,`schema ${a.schema}`,"sl-line sl-note"),R(l,`hash ${a.hash===""?"(none)":a.hash} - metadata ${a.metadata}`,"sl-line sl-note");let c=xe(s,"Hands (deal)",r);c.className="sl-hands",t.hands.length===0&&R(c,"(no hands - this bundle is peek-only)","sl-line sl-note");for(let h of t.hands){let y=h.template!==void 0?`, template ${h.template}`:"",x=h.movable===void 0?"":`, moves ${h.movable.map(w=>`${w.group} from ${w.from}`).join(" and ")}`;R(c,`${h.gameId}: box ${h.box}, slots ${h.slots}${y}${x}`+(h.title!==void 0?` - ${h.title}`:""))}let p=xe(s,"Tags by box (peek criteria)",r);p.className="sl-tags";for(let h of t.boxes){R(p,`${h.title??h.gameId}`,"sl-group"),h.tagGroups.length===0&&R(p," (no tag groups)","sl-line sl-note");for(let y of h.tagGroups)R(p,` ${y.gameId}: ${y.tags.length>0?y.tags.join(", "):"(no tags)"}`)}let u=xe(s,"Properties (declared)",r);u.className="sl-props";for(let h of t.properties){R(u,Et(h),"sl-group"),h.properties.length===0&&R(u," (none declared)","sl-line sl-note");for(let y of h.properties)R(u,` ${St(y)}`)}let g=t.map;if(g!==void 0){let h=xe(s,"Project map",r);h.className="sl-maps",R(h,`${g.group}: ${g.tags.join(", ")}`),R(h,`boxes on the map: ${g.boxes.length>0?g.boxes.join(", "):"none"}`);let y=Object.entries(g.sites).map(([x,w])=>`${x} ${w}`).join(", ");R(h,`geometry carried (the engine ignores it): zones ${g.zones}, pictures ${g.backgrounds}, hands ${y===""?"0":y}`,"sl-line sl-note")}let m=xe(s,"Counts",r);m.className="sl-counts",R(m,`boxes ${d.boxes} - decks ${d.decks} - cards ${d.cards}`),R(m,`hands ${d.hands} - templates ${d.templates} - tag groups ${d.tagGroups}`);for(let h of t.boxes)R(m,`${h.gameId}: decks ${h.counts.decks}, cards ${h.counts.cards}, hands ${h.counts.hands}, templates ${h.counts.templates}, tag groups ${h.counts.tagGroups}, ranking.specificity ${h.ranking.specificity}`+(h.turn===void 0?"":h.turn.seconds!==void 0?`, turn = ${h.turn.seconds}s`:", turn moved by the game")+(h.durableCards!==void 0?`, durable cards ${h.durableCards}`:""));return(e.container??document.body).append(s),{el:s,description:t,destroy(){s.remove()}}}var Xr="ws://127.0.0.1:4472",Yr=new Set(["deal","play","discard","evict","turns"]);function kt(n){let e=n.id,t={};for(let[s,o]of Object.entries(n.board()))t[s]=o.map(i=>i.gameId);let r={};for(let s of n.listBoxes())r[s.gameId]=s.turn;return{t:"board",flow:e,hands:t,turns:r}}function Gn(n){let e=n.url??Xr,t=n.WebSocket??globalThis.WebSocket,r=[],s=null,o=!1,i=n.build,a=null,d=null,l=new Set;if(!t)return{state:"closed",attach(){},detach(){},setBuild(){},close(){o=!0}};let c=()=>{if(!(!s||s.readyState!==1)){for(let w of r)try{s.send(w)}catch{}r=[]}},p=w=>{o||(r.push(JSON.stringify(w)),r.length>512&&r.splice(0,r.length-512),c())},u=()=>{try{return a?a.flows():[]}catch{return[]}},g=()=>{let w=u(),S={t:"hello",v:2,build:i,flows:w.map(P=>P.id)};n.project!==void 0&&(S.project=n.project);let E=w[0];if(E)try{S.boxes=E.listBoxes().map(P=>P.gameId)}catch{}l=new Set(w.map(P=>P.id));try{s?.send(JSON.stringify(S))}catch{}},m=w=>{try{p(kt(w))}catch{}},h=()=>{let w=u(),S=new Set(w.map(E=>E.id));for(let E of w)l.has(E.id)||(l.add(E.id),p({t:"flowOpen",flow:E.id}),m(E));for(let E of[...l])S.has(E)||(l.delete(E),p({t:"flowClose",flow:E}))},y=(w,S)=>{try{if(h(),p({t:"trace",flow:w,event:S}),Yr.has(S.type)){let E=a?.getFlow(w);E&&m(E)}}catch{}};try{s=new t(e),s.addEventListener("open",()=>{g(),c()}),s.addEventListener("message",w=>{if(!(!n.onBundle||typeof w.data!="string"))try{let S=JSON.parse(w.data);S.t==="bundle"&&typeof S.build=="string"&&typeof S.data=="string"&&n.onBundle({build:S.build,data:S.data})}catch{}}),s.addEventListener("error",()=>{}),s.addEventListener("close",()=>{s=null,o=!0,r=[]})}catch{s=null,o=!0}let x=()=>{d?.(),d=null,a=null,l=new Set};return{get state(){return o?"closed":s!==null&&s.readyState===1?"connected":"connecting"},attach(w){if(!o){x(),a=w;try{d=w.subscribeTrace(y)}catch{a=null;return}l=new Set(u().map(S=>S.id));for(let S of u())m(S)}},detach:x,setBuild(w){if(!(o||w===i)&&(i=w,s&&s.readyState===1)){g();for(let S of u())m(S)}},close(){o=!0,x(),r=[];try{s?.close()}catch{}s=null}}}function qn(n,e,t={}){let r;try{r=JSON.parse(e)}catch{return{ok:!1,error:"pushed bundle is not valid JSON"}}try{let{engine:s,report:o}=n.hotSwap(r,t);return{ok:!0,engine:s,bundle:r,report:o}}catch(s){return{ok:!1,error:s instanceof Error?s.message:String(s)}}}function Wn(n,e="world"){let t=new z(Vt(n,e),{normalise:r=>r});return{resolver:{get:r=>t.get(r),set:(r,s)=>{t.set(r,s,{host:!0})}},bag:t,values:()=>t.values,load:r=>t.load(r)}}return Xn(Zr);})();
|
|
39
39
|
//# sourceMappingURL=storyletengine.min.js.map
|