@storylet-studio/play-helpers 0.17.0 → 1.0.1
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var StoryletEngine=(()=>{var ze=Object.defineProperty;var Yn=Object.getOwnPropertyDescriptor;var Xn=Object.getOwnPropertyNames;var Zn=Object.prototype.hasOwnProperty;var er=(n,e)=>{for(var t in e)ze(n,t,{get:e[t],enumerable:!0})},tr=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Xn(e))!Zn.call(n,s)&&s!==t&&ze(n,s,{get:()=>e[s],enumerable:!(r=Yn(e,s))||r.enumerable});return n};var nr=n=>tr(ze({},"__esModule",{value:!0}),n);var os={};er(os,{DURABLE_SCHEMA:()=>Ee,Engine:()=>Te,Flow:()=>Se,STORYLET_ENGINE_VERSION:()=>En,StoryletError:()=>b,applyLiveBundle:()=>Jn,boardFrame:()=>Ct,compareStages:()=>Ye,createBundleInspector:()=>Un,createKernelStateLogger:()=>be,createLiveLink:()=>Kn,createPropertyInspector:()=>zn,createStateLogger:()=>Dn,createWorldContainer:()=>Qn,describeBundle:()=>He,deserializeState:()=>Fe,diffState:()=>ye,effectiveFields:()=>Bn,ensureInspectorStyle:()=>ke,extensionDecls:()=>Ln,extensionsOf:()=>On,fieldDecls:()=>$n,fieldValue:()=>mt,formatLogEntry:()=>Et,formatPropertySummary:()=>kt,formatScopeLabel:()=>Pt,loadState:()=>bt,makePrng:()=>F,nextStage:()=>Xe,outcomeFieldDecls:()=>Rn,saveState:()=>yt,serializeState:()=>je,shuffleInPlace:()=>U,snapshotState:()=>Mn,stageIndex:()=>Qe,versionInRange:()=>kn});function ge(n,e){return n!==void 0&&Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0}function W(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:W(n[2])};case"bin":return{kind:"binary",op:n[1],left:W(n[2]),right:W(n[3])};case"call":{let e=n.slice(2).map(W);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 Ce(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=ge(e.scopes,o.scope);if(i===void 0)return!1;let a=typeof i.get=="function"?i.get(o.name):ge(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"&&!ge(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=Ue(a,e);if(d===void 0)throw new v("advance() needs a quality reference (@scope.name of a quality property)");let l=j(s(a),d,"advance");return d[Math.min(l+1,d.length-1)]}let i=ge(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=Ue(o.left,e),l=Ue(o.right,e),c=d??l;if(c!==void 0){if(d&&l&&!rr(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 j(i,c,">")>j(a,c,">");case">=":return j(i,c,">=")>=j(a,c,">=");case"<":return j(i,c,"<")<j(a,c,"<");case"<=":return j(i,c,"<=")<=j(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 It(i,a);case"!=":return!It(i,a);case">":return z(i,a,">"),i>a;case">=":return z(i,a,">="),i>=a;case"<":return z(i,a,"<"),i<a;case"<=":return z(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 z(i,a,"-"),i-a;case"*":return z(i,a,"*"),i*a;case"/":if(z(i,a,"/"),a===0)throw new v("division by zero");return i/a}}}};return s(n)}function It(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 z(n,e,t){if(typeof n!="number"||typeof e!="number")throw new v(`'${t}' requires numeric operands, got ${typeof n} and ${typeof e}`)}function Ue(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function j(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 rr=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function At(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function F(n){let e=At(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 U(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 Je=n=>Array.isArray(n)?n:n.stages??[],sr=n=>Array.isArray(n)?"flags":n==null?"nothing":typeof n;function Qe(n,e){return typeof e=="string"?Je(n).indexOf(e):-1}function Ke(n,e){if(typeof e!="string")throw new v(`a stage is a name, got ${sr(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 Ye(n,e,t){let r=Je(n),s=Ke(r,e)-Ke(r,t);return s<0?-1:s>0?1:0}function Xe(n,e){let t=Je(n);return t[Math.min(Ke(t,e)+1,t.length-1)]}function K(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 or={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},ir=[or];function Dt(n,e,t){let r=t?.countingCalls??ir;return $e(n,t?.want??!0,e,r)}function $e(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=$e(n.left,e,t,r),o=$e(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 $e(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 Tt=[{token:"patter",engine:"Patterplay",means:"Patter's shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine's shared @story properties"}];var Re=9999,fe=n=>n.ctx.host??{},re=(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},Nt=(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`)},Ht=["story","world","box","deck","hand"],ar=Tt.map(n=>n.token).filter(n=>!Ht.includes(n)),Ze={scopes:[...Ht.map(n=>({token:n,missing:"throw"})),...ar.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=fe(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=Nt("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=[...Nt("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=re("count_played",n,e,0),r=fe(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=re("turns_since_played",n,e,0),r=fe(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=re("count_played_in",n,e,0),r=re("count_played_in",n,e,1),s=fe(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=re("turns_since_played_in",n,e,0),r=re("turns_since_played_in",n,e,1),s=fe(e).turnsSincePlayedIn;if(!s)throw new v("turns_since_played_in() called without a play log in context");return s(t,r)}}}};function Vt(n){return n.toLowerCase().replace(/['’]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function se(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 dr(n,e){return se(n).find(t=>t.token===e)}function jt(n,e){return dr(n,e)?.declarations??[]}function f(n){let e=n.gameId?.trim();return e||(n.title?Vt(n.title):"")||n.id}function Ft(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 _t(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 Gt(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 N="place",lr=/^@(hand|world|story)\.([a-z][a-z0-9_-]*)$/,Be=n=>n.startsWith("@"),qt=n=>{let e=lr.exec(n);return e===null?void 0:{scope:e[1],name:e[2]}},et="storylets/bundle@2",cr="storylets/bundle@1",pr="storylets/bundle@0",he=[pr,cr,et];function me(n,e){return e.usesMap===!0&&n.map!==void 0?[...e.tagGroups,n.map.group]:e.tagGroups}function Wt(n){let e=n.boxes.flatMap(t=>t.tagGroups);return n.map!==void 0?[...e,n.map.group]:e}var J="storylets/save@3",oe="storylets/save@2",Q="storylets/save@1",Le="storylets/savefile@1";var ur={project:".storyletproj",box:".storyletbox",tags:".storylettags",hands:".storylethands",deck:".storyletdeck",view:".storyletview",map:".storyletmap",notes:".storyletnotes",contract:".storyletcontract",extension:".storyletextension"},gr=".extension.json";var As=[...Object.values(ur),gr];function ye(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 zt=n=>n===void 0?"<unset>":JSON.stringify(n),Ut=n=>n.pathPrefix??n.bag.pathPrefix;function be(n,e={}){let t=e.sink??(p=>console.log(p)),r=e.label??"",s=p=>{t(`${r}${p.path}: ${zt(p.from)} -> ${zt(p.to)}`)},o=()=>{let p={};for(let u of n.mounts()){let g=Ut(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(Ut(g),g.bag)}))}};return c(),{snapshot:o,capture(){let p=o(),u=ye(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 Y=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??_(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])=>Jt(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);K(i,a,{noneChosen:i.type==="enum"&&i.default===""})||(this.values[o]=structuredClone(_(i)),s.push({name:o,value:a}))}return s}};function Jt(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.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var fr=n=>n.toLowerCase(),tt=1,Oe=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 Y(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??fr,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,...Jt(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(hr(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 Kt(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=Kt(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:tt,scopes:this.save()}}loadFragment(e){if(e.version!==tt)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${tt})`);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 Kt(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function hr(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function _(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 Qt=(n,e,t)=>`${n}${e}${t}`,b=class extends Error{constructor(e){super(e),this.name="StoryletError"}},mr=new Set(["seed","log","hostScopes","registry","onReplacedFlow","onError","onRefit"]),Ee="storylets/durable@1",ae=(n,e)=>n.shared??e,yr=(n,e)=>n.redraw==="never"&&(n.durable??e.durable??!1)===!0,ln=n=>n.sharedCopies??cn(n),cn=n=>n.acrossHands==="any"?1/0:n.acrossHands==="one"?1:n.copies??1;function br(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&&U(s,t),[...r,...s]}var wr=n=>n.inOneHand??1,ie="Storylet Engine",at=n=>n;function Ae(n){if(!n.startsWith("@"))return n;let e=n.slice(1);return e.includes(".")?e:`story.${e}`}var pt=n=>n.replace(/%/g,"%25").replace(/\//g,"%2F"),nt=n=>n.replace(/%2F/g,"/").replace(/%25/g,"%"),rt=(n,e)=>`storylets/${n}/${pt(e)}`,dt=n=>`storylets/flow/${pt(n)}/`,lt=(n,e,t)=>e==="story"?`${dt(n)}story`:`${dt(n)}${e}/${pt(t)}`,xe=()=>({story:{},box:{},deck:{},hand:{},value:{}});function vr(n,e){let t=xe(),r=new Map,s={},o=i=>{let a=nt(i);if(!e.has(a))return;let d=r.get(a);return d||(d=xe(),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]][nt(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]][nt(d[3])]=a)}else i.startsWith("storylets/")||(s[i]=a)}return{shared:t,flows:r,rest:s}}function Yt(n,e,t){Object.keys(n.story).length>0&&(t[e("story")]=n.story);for(let r of I)for(let[s,o]of Object.entries(n[r]))Object.keys(o).length>0&&(t[e(r,s)]=o)}var Xt=(n,e)=>new Y(n,{normalise:at,pathPrefix:e}),D=(n,e)=>n!==void 0&&Object.prototype.hasOwnProperty.call(n.values,e)?n.values[e]:void 0,st=Object.freeze(Object.create(null));function Zt(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var xr={story:!0,box:!1,deck:!1,hand:!1,value:!1},pn=(n,e)=>e.shared??xr[n],Sr=(n,e)=>e.filter(t=>pn(n,t)),Er=(n,e)=>e.filter(t=>!pn(n,t)),I=["box","deck","hand","value"],ut=n=>I.includes(n??""),Me=()=>({gameId:new Map,id:new Map,repeated:new Map,zoneQualified:new Map}),kr=()=>({box:Me(),deck:Me(),hand:Me(),value:Me()}),ot=(n,e)=>{let t=f(e);n.gameId.set(e.id,t),n.id.has(t)||n.id.set(t,e.id)},Pr=(n,e)=>{let t=Ft(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)},en=(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 I)for(let[i,a]of e[o])for(let d of a)d.durable===!0&&s({address:`${G(n,o,i)}.${d.name}`,kind:o,owner:i,decl:d});return t},gt=(n,e)=>e.kind==="story"?n.story:n[e.kind].get(e.owner),un=(n,e)=>{let t=[];for(let r of e){let s=D(gt(n,r),r.decl.name);s!==void 0&&t.push([r.address,s])}return Object.fromEntries(X(t,([r])=>r))};function tn(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(!K(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 nn=(n,e)=>{let t=e?.schema;if(t!==Ee)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}"`)},gn=(n,e,t)=>{n?.set(e,structuredClone(t),{silent:!0,reason:"host durable",host:!0})},fn=(n,e)=>(n.declarations?.find(t=>t.name===e)?.writable??n.writable)===!1,hn=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}:{}}),rn=n=>(n.declarations??[]).map(e=>{let t=hn(e),r=e.writable??n.writable;return r===void 0?t:{...t,writable:r}}),sn=(n,e)=>e.template!==void 0?n.templatesById.get(e.template)?.properties??[]:e.properties??[],G=(n,e,t)=>`${e}.${n.owners[e].gameId.get(t)??t}`,Cr=(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}},ft=(n,e,t,r)=>{let s=Cr(n,e,t);if(s===void 0)throw new b(`no ${e} store "${t}"`);if("ambiguous"in s)throw new b(Gt(t,r,s.ambiguous));if("zone"in s)throw new b(_t(t,s.zone,r));return s},mn=(n,e,t,r)=>`"${e}.${t}.${r}" names the ${e} by its internal id; write "${G(n,e,t)}.${r}". The internal-id form is refused after the next release.`,yn=(n,e)=>{let t=r=>new Map([...e[r]].map(([s,o])=>[s,Xt(o,`${G(n,r,s)}.`)]));return{story:Xt(e.story,"story."),box:t("box"),deck:t("deck"),hand:t("hand"),value:t("value")}},$r=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]))}),Rr=(n,e)=>{n.story.load(e?.story??{});for(let t of I)for(let[r,s]of Object.entries(e?.[t]??{}))n[t].get(r)?.load(s)},we=()=>({evicted:[],droppedCooldowns:[],droppedSpent:[],droppedProperties:[],defaultedProperties:[],retypedProperties:[]}),De="",X=(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),it=n=>`${n.flow??""}${De}${n.path}`;function on(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(!K(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 an(n,e,t,r,s){let o={story:on(e.story,t?.story,i=>`story.${i}`,r,s),box:{},deck:{},hand:{},value:{}};for(let i of I){let a=t?.[i]??{},d=[...new Set([...e[i].keys(),...Object.keys(a)])].sort();for(let l of d){let c=G(n,i,l);o[i][l]=on(e[i].get(l),a[l],p=>`${c}.${p}`,r,s)}}return o}function ve(n,e,t,r){let s=e.version!==n.version||e.hash!==n.hash,o=X(r.evicted,p=>[p.flow,p.hand,p.card,p.reason].join(De)),i=X(r.droppedCooldowns,p=>`${p.flow}${De}${p.card}`),a=[...r.droppedSpent].sort(),d=X(r.droppedProperties,it),l=X(r.defaultedProperties,it),c=X(r.retypedProperties,it);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 Z=n=>n!==null&&typeof n=="object"&&!Array.isArray(n),bn=n=>n===null?"null":n===void 0?"nothing":Array.isArray(n)?"an array":`a ${typeof n}`,Br=n=>{let e=[];Z(n.content)||e.push("no content"),Z(n.settings)||e.push("no settings");let t=n.schema===et?["story"]:["story","world"];for(let s of t){let o=n[s];(!Z(o)||!Array.isArray(o.properties))&&e.push(`no ${s} properties`)}let r=n.scopeRegistry;return r!==void 0&&(!Z(r)||!Array.isArray(r.scopes))&&e.push("no host scopes in its scopeRegistry"),Array.isArray(n.boxes)||e.push("no boxes"),e},dn=n=>{if(!Z(n))throw new b(`not a Storylets save: expected an object, got ${bn(n)}`);let e=n.schema;if(e!==J&&e!==oe&&e!==Q)throw new b(`unsupported save schema: ${String(e)} (this runtime reads ${Q}, ${oe} and ${J})`);if(!Z(n.content))throw new b("not a complete Storylets save: no content")},Lr=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"}},Ie=(n,e)=>n.cardsByGameId.get(e)??n.cardsById.get(e),Or=(n,e)=>n.handsByGameId.get(e)??n.handsById.get(e),Mr=(n,e)=>n.boxesByGameId.get(e)??n.boxesById.get(e),ct=(n,e)=>{let t=n.cardsById.get(e);return t!==void 0?f(t.card):e},wn=(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&&ut(r[0])){let i=r[0],[,a,d]=r,l=ft(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&&!K(s,t,{noneChosen:s.type==="enum"&&s.default===""}))throw new b(`"${e}" takes ${Lr(s)}; ${JSON.stringify(t)} doesn't fit`)},vn=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??_(r),default:_(r),...r.values!==void 0?{values:r.values}:{},...r.stages!==void 0?{stages:r.stages}:{},writable:t.set!==void 0&&!fn(t.spec,r.name)})}return e},Ir=n=>{if(!Z(n))throw new b(`not a Storylets bundle: expected an object, got ${bn(n)}`);let e=n.schema;if(typeof e!="string"||!he.includes(e))throw new b(`unsupported bundle schema: ${String(e)} (this runtime reads ${he.slice(0,-1).join(", ")} and ${he[he.length-1]})`);let t=Br(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===N&&r.push(`the project map's tag group is called "${N}", 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("; ")}`)},Te=class n{internals;seed;onReplacedFlow;flowsById=new Map;engineTraceHandlers=new Set;creationOptions;sharedMounts=[];constructor(e,t={}){Ir(e),this.creationOptions=t,this.seed=t.seed??0,this.onReplacedFlow=t.onReplacedFlow;let r=[];for(let l of Object.keys(t))mr.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:kr(),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 Oe,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};Pr(s.owners.value,e);for(let l of e.boxes){s.boxesById.set(l.id,l),s.boxesByGameId.set(f(l),l),ot(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){ot(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),yr(p,c)&&(ae(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}),ot(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",sn(s,p))]))),value:new Map(Wt(e).flatMap(c=>c.tags.map(p=>[p.id,l("value",p.properties??[])])))});s.flowDecls=d(Er),s.sharedDecls=d(Sr),s.durable.shared=en(s,s.sharedDecls),s.durable.flow=en(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=se(r.bundle);r.shared=yn(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:ie})});for(let l of I)for(let[c,p]of r.shared[l])p.declarations().length!==0&&a(rt(l,c),()=>{s.mountOwned(rt(l,c),p,{owner:ie})});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(hn);a(l,()=>{s.defineForeign(l,c,u,{normalise:at,writable:p?.writable??!0,owner:ie})})}for(let l of r.ownsRegistry?o:[]){let c=l.token;if(e[c]!==void 0||s.has(c))continue;s.defineOwned(c,rn(l),{normalise:at,pathPrefix:`${c}.`,owner:ie});let p=s.ownedBag(c);i.push(c),this.sharedMounts.push({key:c,mount:()=>{s.mountOwned(c,p,{owner:ie})}})}}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 I)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,rn(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 se(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(sn(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){nn(this.internals,t.durable);let d=we(),{flow:l,flowCards:c}=this.internals.durable;s={plan:tn(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(dt(e));let a=new Se(this,this.internals,e,t.seed??this.seed);if(this.flowsById.set(e,a),t.restore!==void 0){let d=we(),l=this.planFlowRestore(e,structuredClone(t.restore),o,d);a.restore(l);let c=this.internals.bundle.content;t.onRestoreReport?.(ve(c,c,[e],d))}return s!==void 0&&(a.writeDurable(s.plan),t.onRestoreReport?.(ve(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(Ae(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(Ae(e));if(wn(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(D(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&&ut(t[0])){let o=t[0],[,i,a]=t,d=ft(this.internals,o,i,a);d.legacy&&this.diagnose(mn(this.internals,o,i,a));let l=d.id,c=this.internals.shared[o].get(l);if(c!==void 0&&D(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=vn(this.internals),t=(r,s)=>{for(let o of s.rows())e.push(o)};t("story",this.internals.shared.story);for(let r of I)for(let[s,o]of this.internals.shared[r])t(G(this.internals,r,s),o);return e}listBags(){let e=[{prefix:"story",bag:this.internals.shared.story}];for(let t of I)for(let[r,s]of this.internals.shared[t])e.push({prefix:G(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:J,content:this.internals.bundle.content,...this.internals.ownsRegistry?{registry:this.registrySection()}:{},shared:{spent:[...this.spent].map(e=>ct(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:Ee,content:e.content,values:un(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;nn(t,e);let r=we(),s=tn(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):_(o.decl);gn(gt(t.shared,o),o.decl.name,i)}for(let o of s.cards)this.spent.add(o);return ve(t.bundle.content,e.content,[],r)}previewLoad(e){dn(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=we();this.planFlowRestore(e,t,this.sharedClaims(e),r);let s=this.internals.bundle.content;return ve(s,s,[e],r)}loadGame(e){try{return this.loadGameInto(e)}finally{this.internals.reportRefits()}}loadGameInto(e){dn(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:X([...t.report.evicted,...s],o=>[o.flow,o.hand,o.card,o.reason].join(De))}}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!==J&&t!==oe&&t!==Q)throw new b(`unsupported save schema: ${String(t)}`);let r=we(),s=new Set(Object.keys(e.flows??{})),o;e.schema===Q?o={shared:e.shared?.props??xe(),flows:new Map(Object.entries(e.flows??{}).map(([c,p])=>[c,p.props??xe()])),rest:{}}:e.registry!==void 0&&(o=vr(e.registry,s));let i=o!==void 0?an(this.internals,this.internals.sharedDecls,o.shared,void 0,r):void 0,a=[];for(let c of e.shared?.spent??[]){let p=Ie(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&&Yt(i,(c,p)=>c==="story"?"story":rt(c,p),l);for(let[c,p]of Object.entries(e.flows??{})){let u=o!==void 0?{...p,props:o.flows.get(c)??xe()}:{...p},g=this.planFlowRestore(c,u,void 0,r);l!==void 0&&g.props!==void 0&&(Yt(g.props,(m,h)=>lt(c,m,h),l),delete g.props),d.push([c,g])}return{report:ve(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?an(o,o.flowDecls,t.props,e,s):void 0,a={};for(let[u,g]of Object.entries(t.cooldowns??{})){let m=Ie(o,u);m!==void 0?a[m.card.id]=g:s.droppedCooldowns.push({flow:e,card:u})}let d=u=>{let g=Ie(o,u);return g?f(g.card):u},l={},c=new Map;for(let[u,g]of Object.entries(t.board??{})){let m=Or(o,u);if(m===void 0){for(let w of g)s.evicted.push({flow:e,hand:u,card:d(w),reason:"hand-vanished"});continue}let h=f(m.hand),y=[];for(let w of g){let E=Ie(o,w);if(E===void 0){s.evicted.push({flow:e,hand:h,card:w,reason:"vanished"});continue}let R=E.card.id;if(r!==void 0&&ae(E.card,E.deck.shared??!1)){if((r.get(R)??0)+(c.get(R)??0)>=ln(E.card)){s.evicted.push({flow:e,hand:h,card:f(E.card),reason:"claimed-elsewhere"});continue}c.set(R,(c.get(R)??0)+1)}y.push(R)}l[m.hand.id]=y}let p={};for(let[u,g]of Object.entries(t.turns??{})){let m=Mr(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??[]}}},Se=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=F(s),this.stores=yn(t,t.flowDecls);let o=(a,d)=>{d.declarations().length!==0&&this.bagKeys.push([a,d])};o(lt(r,"story"),this.stores.story);for(let a of I)for(let[d,l]of this.stores[a])o(lt(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=>D(a,l)??D(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:ie}),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=W(e.ast),this.internals.nodeCache.set(e,t)),t}groupInBox(e,t){let r=me(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=Qt(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?Qt(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:Re};return{nextRandom:()=>this.prng.next(),countPlayed:o=>this.playCount.get(o)??0,turnsSincePlayed:o=>{let i=this.lastPlayOf.get(o);return i?s(i):Re},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):Re}}}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)??st,deck:t?this.deckReaders.get(t.id)??st:st,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 Ce(this.node(e),t,Ze)}passes(e,t,r){if(!e)return!0;try{return Zt(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(Be(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(N,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===N){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(N,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=qt(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 me(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!==N)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>=cn(e)?"claimed":t&&(s.get(i)??0)-o>=ln(e)?"claimed-elsewhere":null}tagsMatch(e,t){let r=e.tags?.[N];if(r!==void 0&&r.length>0){let s=t.get(N);if(s===void 0||!r.includes(s))return!1}for(let[s,o]of t){if(s===N)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=ae(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 w=t(h,y);if(w){a(h,w);continue}let E;if(typeof h.priority=="number")E=h.priority;else try{let L=this.eval(h.priority,g);if(typeof L!="number"||Number.isNaN(L)){a(h,"priority");continue}E=L}catch(L){this.tracing&&this.emit({type:"diagnostic",where:`card ${h.gameId} priority`,message:L instanceof Error?L.message:String(L)}),a(h,"priority");continue}let R=0;if(o.ranking.specificity&&h.condition){let L=this.node(h.condition);R=Dt(L,C=>{try{return Zt(Ce(C,g,Ze))}catch{return!1}})}c.push({entry:{card:h,deck:u,box:o},priority:E,spec:R})}}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=br(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=F(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)?ae(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=F(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));U(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,w)=>this.claimVerdict(y,w,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 w=Math.min(wr(y.card),g);u.set(y.card.id,w),g-=w}let m=[],h=[];for(let y of a){let w=u.get(y)??0;w>0?(m.push(y),u.set(y,w-1)):h.push(y)}for(let y of p){for(let w=u.get(y.card.id)??0;w>0;w--)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 w of h)this.emit({type:"evict",hand:f(o),card:f(this.internals.cardsById.get(w).card),reason:"outranked"},y);this.capTrace(c,new Set(m.map(w=>f(this.internals.cardsById.get(w).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(C=>f(C)).join(", ")}); name the one played`);let d=a?void 0:o.card.outcomes.find(C=>f(C)===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(([C,A])=>({target:C,expr:A,plan:this.planWrite(C,o,l)})).map(C=>({...C,value:this.eval(C.expr,c)})),h=[];try{for(let C of m)h.push({target:C.target,plan:C.plan,value:C.value,prev:this.landWrite(C.plan,C.value)})}catch(C){for(let A of[...h].reverse())this.undoWrite(A.plan,A.prev);throw C}if(this.tracing)for(let{target:C,plan:A,value:q,prev:x}of h)this.emit({type:"write",target:C,path:A.path,value:q,...x!==void 0?{prev:x}:{}},u);let y=d?f(d):"",w={card:f(o.card),outcome:y,turn:u};this.playLog.push(w),this.indexPlay(w),o.card.redraw==="never"?ae(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 E=i.hand.id,R=this.boardContents.get(E)??[],L=R.indexOf(o.card.id);this.boardContents.set(E,[...R.slice(0,L),...R.slice(L+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"?ae(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 G(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=D(o,r)!==void 0?o:D(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(fn(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 I)for(let[r,s]of this.stores[t])e.push({prefix:G(this.internals,t,r),bag:s});return e}listProperties(){this.assertOpen();let e=vn(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 I){let s=new Set([...this.internals.shared[r].keys(),...this.stores[r].keys()]);for(let o of s)t(G(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(Ae(e)),r=t.kind==="host"?t.host.resolver.get(t.name):t.kind==="scope"?this.internals.registry.get(t.token,t.name):D(t.own,t.name)??D(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(Ae(e));if(wn(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=D(r.own,r.name)!==void 0?r.own:D(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&&ut(t[0])){let s=t[0],[,o,i]=t,a=ft(this.internals,s,o,i);a.legacy&&this.tracing&&this.emit({type:"diagnostic",where:"property address",message:mn(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:Ee,content:e.content,values:un(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)gn(gt(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:$r(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])=>[ct(t,o),i])),board:Object.fromEntries([...this.boardContents].map(([o,i])=>[s(o),i.map(a=>ct(t,a))])),playLog:this.playLog}}restore(e){e.props!==void 0&&Rr(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=F(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 En="0.17.0",Ar=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;function ht(n){let e=typeof n=="string"?Ar.exec(n):null;return e?{core:[e[1],e[2],e[3]],pre:e[4]?e[4].split("."):[]}:null}function xn(n,e){return n=n.replace(/^0+(?=\d)/,""),e=e.replace(/^0+(?=\d)/,""),n.length!==e.length?n.length<e.length?-1:1:n===e?0:n<e?-1:1}function Sn(n,e){for(let t=0;t<3;t++){let r=xn(n.core[t],e.core[t]);if(r)return r}if(!n.pre.length||!e.pre.length)return e.pre.length-n.pre.length;for(let t=0;t<Math.min(n.pre.length,e.pre.length);t++){let r=n.pre[t],s=e.pre[t];if(r===s)continue;let o=/^\d+$/.test(r),i=/^\d+$/.test(s);if(o&&i){let a=xn(r,s);if(a)return a;continue}return o!==i?o?-1:1:r<s?-1:1}return n.pre.length-e.pre.length}function kn(n,e,t){let r=ht(n),s=ht(e),o=ht(t);return!r||!s||!o?!1:Sn(r,s)>=0&&Sn(r,o)<0}var Ne=n=>n.map(e=>({name:e.name,type:e.type,default:_(e),...e.values!==void 0?{values:e.values}:{},...e.durable===!0?{durable:!0}:{},...e.purpose!==void 0?{purpose:e.purpose}:{}})),Pn=n=>n.decks.reduce((e,t)=>e+t.cards.filter(r=>(r.durable??t.durable)===!0).length,0),Dr=(n,e)=>n.template!==void 0?e.handTemplates.find(t=>t.id===n.template)?.properties??[]:n.properties??[],Tr=(n,e,t)=>{let r=e.template!==void 0?e.chosen:e.rule?.bindings,s=[];for(let[o,i]of Object.entries(r??{})){if(!Be(i))continue;let a=me(n,t).find(d=>d.id===o);a!==void 0&&s.push({group:f(a),from:i})}return s},Nr=(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 He(n){let e=[],t=[],r=se(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:Ne(a.declarations??[])})),{scope:"story",owner:"",properties:Ne(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}:{}}:{},...Pn(a)>0?{durableCards:Pn(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=Tr(n,p,a);t.push({gameId:f(p),...p.title!==void 0?{title:p.title}:{},box:d,slots:Nr(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:Ne(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),Dr(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:Ne(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 Cn=(n,e)=>n.boxes.find(t=>t.gameId===e)??n.boxes.find(t=>t.id===e),Ve=(n,e)=>Object.prototype.hasOwnProperty.call(n,e);function $n(n,e){return Cn(n,e)?.fields??[]}function Rn(n,e){return Cn(n,e)?.outcomeFields??[]}function mt(n,e,t){if(e!==void 0&&Ve(e,t))return e[t];let r=n.find(s=>s.name===t)?.default;return r!==null&&typeof r=="object"?structuredClone(r):r}function Bn(n,e){let t={};for(let r of n){let s=mt(n,e,r.name);s!==void 0&&(t[r.name]=s)}for(let[r,s]of Object.entries(e??{}))Ve(t,r)||(t[r]=s);return t}function Ln(n,e,t){let r=n.extensions!==void 0&&Ve(n.extensions,e)?n.extensions[e]:void 0;return r!==void 0&&Ve(r.fields,t)?r.fields[t]??[]:[]}function On(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 Mn(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,An(In(n,e.id))),t}function In(n,e){return n.getFlow(e)!==void 0?n.saveFlow(e):void 0}function An(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 Dn(n,e,t={}){let r=e.id,s=()=>n.getFlow(r);return be({mounts:()=>[...n.listBags(),...s()?.listBags()??[]].map(({bag:o})=>({bag:o})),extra:()=>An(In(n,r))},t)}function je(n,e){return JSON.stringify(yt(n,e),null,2)}function yt(n,e){return{schema:Le,engine:n.saveGame(),...e!==void 0?{world:e}:{}}}function bt(n,e,t={}){if(!e||typeof e!="object"||e.schema!==Le||e.engine?.schema!==J&&e.engine?.schema!==oe&&e.engine?.schema!==Q)throw new Error(`not a storylets save (expected schema "${Le}")`);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 Fe(n,e,t={}){let r;try{r=JSON.parse(e)}catch{throw new Error("not valid JSON")}return bt(n,r,t)}function Hr(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 xt=!1,Tn=!1,Vn,Vr=350,wt=6,Nn=7,le=null,ce=0,_e=null,jr=null;function jn(){if(le)return le;let n=document.createElement("div");return n.className="tooltip",n.setAttribute("role","tooltip"),n.hidden=!0,document.body.appendChild(n),le=n,n}var Fr=n=>n instanceof HTMLElement?n.getBoundingClientRect():n;function _r(n){let e=jn(),t=Fr(n),r=e.getBoundingClientRect(),s=t.top-r.height-Nn;s<wt&&(s=t.top+t.height+Nn);let o=t.left+t.width/2-r.width/2;o=Math.max(wt,Math.min(o,window.innerWidth-r.width-wt)),e.style.left=`${Math.round(o)}px`,e.style.top=`${Math.round(s)}px`}function Gr(n){let e=n.dataset.tip;e&&qr(n,e)}function qr(n,e){if(Vn?.())return;let t=jn(),r=(n instanceof HTMLElement?n.closest("dialog[open]"):null)??document.body;t.parentElement!==r&&r.appendChild(t),_e=n instanceof HTMLElement?n:null,Hr(t,e),t.hidden=!1,t.classList.remove("show"),t.offsetWidth,_r(n),t.classList.add("show")}function de(){ce&&(window.clearTimeout(ce),ce=0),_e=null,jr=null,le&&(le.hidden=!0,le.classList.remove("show"))}function Hn(n){n!==_e&&(ce&&window.clearTimeout(ce),ce=window.setTimeout(()=>Gr(n),Vr))}function Wr(){Ur()}function zr(){Tn||(Tn=!0,setTimeout(()=>{xt||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 vt=n=>n instanceof Element?n.closest("[data-tip]"):null;function Ur(n={}){n.suppressed!==void 0&&(Vn=n.suppressed),!xt&&(xt=!0,document.addEventListener("pointerover",e=>{let t=vt(e.target);t&&Hn(t)}),document.addEventListener("pointerout",e=>{let t=vt(e.target);if(!t)return;let r=e.relatedTarget;r instanceof Node&&t.contains(r)||de()}),document.addEventListener("focusin",e=>{let t=vt(e.target);t&&Hn(t)}),document.addEventListener("focusout",()=>de()),document.addEventListener("pointerdown",()=>de(),!0),document.addEventListener("scroll",()=>{_e&&de()},!0),document.addEventListener("keydown",e=>{e.key==="Escape"&&de()}),window.addEventListener("blur",()=>de()))}function Fn(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),Wr()),s.onClick&&r.addEventListener("click",s.onClick);for(let o of t)o!=null&&r.append(o);return zr(),r}function _n(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 Gn=n=>`default (${_n(n)})`;function St(n){let e=n.doc===void 0?Fn:(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:Gn(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 qn="sl-inspector-style",Kr=`
|
|
1
|
+
"use strict";var StoryletEngine=(()=>{var ze=Object.defineProperty;var Yn=Object.getOwnPropertyDescriptor;var Xn=Object.getOwnPropertyNames;var Zn=Object.prototype.hasOwnProperty;var er=(n,e)=>{for(var t in e)ze(n,t,{get:e[t],enumerable:!0})},tr=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Xn(e))!Zn.call(n,s)&&s!==t&&ze(n,s,{get:()=>e[s],enumerable:!(r=Yn(e,s))||r.enumerable});return n};var nr=n=>tr(ze({},"__esModule",{value:!0}),n);var is={};er(is,{DURABLE_SCHEMA:()=>Ee,Engine:()=>Te,Flow:()=>Se,STORYLET_ENGINE_VERSION:()=>En,StoryletError:()=>b,applyLiveBundle:()=>Jn,boardFrame:()=>Ct,compareStages:()=>Ye,createBundleInspector:()=>Un,createKernelStateLogger:()=>be,createLiveLink:()=>Kn,createPropertyInspector:()=>zn,createStateLogger:()=>Dn,createWorldContainer:()=>Qn,describeBundle:()=>He,deserializeState:()=>Fe,diffState:()=>ye,effectiveFields:()=>Bn,ensureInspectorStyle:()=>ke,extensionDecls:()=>Ln,extensionsOf:()=>On,fieldDecls:()=>$n,fieldValue:()=>mt,formatLogEntry:()=>Et,formatPropertySummary:()=>kt,formatScopeLabel:()=>Pt,loadState:()=>bt,makePrng:()=>F,nextStage:()=>Xe,outcomeFieldDecls:()=>Rn,saveState:()=>yt,serializeState:()=>je,shuffleInPlace:()=>U,snapshotState:()=>Mn,stageIndex:()=>Qe,versionInRange:()=>kn});function ge(n,e){return n!==void 0&&Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0}function z(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:z(n[2])};case"bin":return{kind:"binary",op:n[1],left:z(n[2]),right:z(n[3])};case"call":{let e=n.slice(2).map(z);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 Ce(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=ge(e.scopes,o.scope);if(i===void 0)return!1;let a=typeof i.get=="function"?i.get(o.name):ge(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"&&!ge(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=Ue(a,e);if(d===void 0)throw new v("advance() needs a quality reference (@scope.name of a quality property)");let l=j(s(a),d,"advance");return d[Math.min(l+1,d.length-1)]}let i=ge(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=Ue(o.left,e),l=Ue(o.right,e),c=d??l;if(c!==void 0){if(d&&l&&!sr(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 j(i,c,">")>j(a,c,">");case">=":return j(i,c,">=")>=j(a,c,">=");case"<":return j(i,c,"<")<j(a,c,"<");case"<=":return j(i,c,"<=")<=j(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 It(i,a);case"!=":return!It(i,a);case">":return W(i,a,">"),i>a;case">=":return W(i,a,">="),i>=a;case"<":return W(i,a,"<"),i<a;case"<=":return W(i,a,"<="),i<=a;case"+":if(typeof i=="string"||typeof a=="string")throw new v(rr);return W(i,a,"+"),i+a;case"-":return W(i,a,"-"),i-a;case"*":return W(i,a,"*"),i*a;case"/":if(W(i,a,"/"),a===0)throw new v("division by zero");return i/a}}}};return s(n)}function It(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}var rr="'+' cannot join text: put the value into the text itself, so it can be translated";function W(n,e,t){if(typeof n!="number"||typeof e!="number")throw new v(`'${t}' requires numeric operands, got ${typeof n} and ${typeof e}`)}function Ue(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function j(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 sr=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function At(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function F(n){let e=At(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 U(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 Je=n=>Array.isArray(n)?n:n.stages??[],or=n=>Array.isArray(n)?"flags":n==null?"nothing":typeof n;function Qe(n,e){return typeof e=="string"?Je(n).indexOf(e):-1}function Ke(n,e){if(typeof e!="string")throw new v(`a stage is a name, got ${or(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 Ye(n,e,t){let r=Je(n),s=Ke(r,e)-Ke(r,t);return s<0?-1:s>0?1:0}function Xe(n,e){let t=Je(n);return t[Math.min(Ke(t,e)+1,t.length-1)]}function K(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 ir={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},ar=[ir];function Dt(n,e,t){let r=t?.countingCalls??ar;return $e(n,t?.want??!0,e,r)}function $e(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=$e(n.left,e,t,r),o=$e(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 $e(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 Tt=[{token:"patter",engine:"Patterplay",means:"Patter's shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine's shared @story properties"}];var Re=9999,fe=n=>n.ctx.host??{},re=(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},Nt=(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`)},Ht=["story","world","box","deck","hand"],dr=Tt.map(n=>n.token).filter(n=>!Ht.includes(n)),Ze={scopes:[...Ht.map(n=>({token:n,missing:"throw"})),...dr.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=fe(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=Nt("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=[...Nt("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=re("count_played",n,e,0),r=fe(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=re("turns_since_played",n,e,0),r=fe(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=re("count_played_in",n,e,0),r=re("count_played_in",n,e,1),s=fe(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=re("turns_since_played_in",n,e,0),r=re("turns_since_played_in",n,e,1),s=fe(e).turnsSincePlayedIn;if(!s)throw new v("turns_since_played_in() called without a play log in context");return s(t,r)}}}};function Vt(n){return n.toLowerCase().replace(/['’]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function se(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 lr(n,e){return se(n).find(t=>t.token===e)}function jt(n,e){return lr(n,e)?.declarations??[]}function f(n){let e=n.gameId?.trim();return e||(n.title?Vt(n.title):"")||n.id}function Ft(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 _t(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 Gt(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 N="place",cr=/^@(hand|world|story)\.([a-z][a-z0-9_-]*)$/,Be=n=>n.startsWith("@"),qt=n=>{let e=cr.exec(n);return e===null?void 0:{scope:e[1],name:e[2]}},et="storylets/bundle@2",pr="storylets/bundle@1",ur="storylets/bundle@0",he=[ur,pr,et];function me(n,e){return e.usesMap===!0&&n.map!==void 0?[...e.tagGroups,n.map.group]:e.tagGroups}function Wt(n){let e=n.boxes.flatMap(t=>t.tagGroups);return n.map!==void 0?[...e,n.map.group]:e}var J="storylets/save@3",oe="storylets/save@2",Q="storylets/save@1",Le="storylets/savefile@1";var gr={project:".storyletproj",box:".storyletbox",tags:".storylettags",hands:".storylethands",deck:".storyletdeck",view:".storyletview",map:".storyletmap",notes:".storyletnotes",contract:".storyletcontract",extension:".storyletextension"},fr=".extension.json";var Ds=[...Object.values(gr),fr];function ye(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 zt=n=>n===void 0?"<unset>":JSON.stringify(n),Ut=n=>n.pathPrefix??n.bag.pathPrefix;function be(n,e={}){let t=e.sink??(p=>console.log(p)),r=e.label??"",s=p=>{t(`${r}${p.path}: ${zt(p.from)} -> ${zt(p.to)}`)},o=()=>{let p={};for(let u of n.mounts()){let g=Ut(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(Ut(g),g.bag)}))}};return c(),{snapshot:o,capture(){let p=o(),u=ye(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 Y=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??_(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])=>Jt(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);K(i,a,{noneChosen:i.type==="enum"&&i.default===""})||(this.values[o]=structuredClone(_(i)),s.push({name:o,value:a}))}return s}};function Jt(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.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var hr=n=>n.toLowerCase(),tt=1,Oe=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 Y(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??hr,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,...Jt(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(mr(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 Kt(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=Kt(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:tt,scopes:this.save()}}loadFragment(e){if(e.version!==tt)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${tt})`);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 Kt(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function mr(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function _(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 Qt=(n,e,t)=>`${n}${e}${t}`,b=class extends Error{constructor(e){super(e),this.name="StoryletError"}},yr=new Set(["seed","log","hostScopes","registry","onReplacedFlow","onError","onRefit"]),Ee="storylets/durable@1",ae=(n,e)=>n.shared??e,br=(n,e)=>n.redraw==="never"&&(n.durable??e.durable??!1)===!0,ln=n=>n.sharedCopies??cn(n),cn=n=>n.acrossHands==="any"?1/0:n.acrossHands==="one"?1:n.copies??1;function wr(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&&U(s,t),[...r,...s]}var vr=n=>n.inOneHand??1,ie="Storylet Engine",at=n=>n;function Ae(n){if(!n.startsWith("@"))return n;let e=n.slice(1);return e.includes(".")?e:`story.${e}`}var pt=n=>n.replace(/%/g,"%25").replace(/\//g,"%2F"),nt=n=>n.replace(/%2F/g,"/").replace(/%25/g,"%"),rt=(n,e)=>`storylets/${n}/${pt(e)}`,dt=n=>`storylets/flow/${pt(n)}/`,lt=(n,e,t)=>e==="story"?`${dt(n)}story`:`${dt(n)}${e}/${pt(t)}`,xe=()=>({story:{},box:{},deck:{},hand:{},value:{}});function xr(n,e){let t=xe(),r=new Map,s={},o=i=>{let a=nt(i);if(!e.has(a))return;let d=r.get(a);return d||(d=xe(),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]][nt(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]][nt(d[3])]=a)}else i.startsWith("storylets/")||(s[i]=a)}return{shared:t,flows:r,rest:s}}function Yt(n,e,t){Object.keys(n.story).length>0&&(t[e("story")]=n.story);for(let r of I)for(let[s,o]of Object.entries(n[r]))Object.keys(o).length>0&&(t[e(r,s)]=o)}var Xt=(n,e)=>new Y(n,{normalise:at,pathPrefix:e}),D=(n,e)=>n!==void 0&&Object.prototype.hasOwnProperty.call(n.values,e)?n.values[e]:void 0,st=Object.freeze(Object.create(null));function Zt(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var Sr={story:!0,box:!1,deck:!1,hand:!1,value:!1},pn=(n,e)=>e.shared??Sr[n],Er=(n,e)=>e.filter(t=>pn(n,t)),kr=(n,e)=>e.filter(t=>!pn(n,t)),I=["box","deck","hand","value"],ut=n=>I.includes(n??""),Me=()=>({gameId:new Map,id:new Map,repeated:new Map,zoneQualified:new Map}),Pr=()=>({box:Me(),deck:Me(),hand:Me(),value:Me()}),ot=(n,e)=>{let t=f(e);n.gameId.set(e.id,t),n.id.has(t)||n.id.set(t,e.id)},Cr=(n,e)=>{let t=Ft(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)},en=(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 I)for(let[i,a]of e[o])for(let d of a)d.durable===!0&&s({address:`${G(n,o,i)}.${d.name}`,kind:o,owner:i,decl:d});return t},gt=(n,e)=>e.kind==="story"?n.story:n[e.kind].get(e.owner),un=(n,e)=>{let t=[];for(let r of e){let s=D(gt(n,r),r.decl.name);s!==void 0&&t.push([r.address,s])}return Object.fromEntries(X(t,([r])=>r))};function tn(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(!K(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 nn=(n,e)=>{let t=e?.schema;if(t!==Ee)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}"`)},gn=(n,e,t)=>{n?.set(e,structuredClone(t),{silent:!0,reason:"host durable",host:!0})},fn=(n,e)=>(n.declarations?.find(t=>t.name===e)?.writable??n.writable)===!1,hn=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}:{}}),rn=n=>(n.declarations??[]).map(e=>{let t=hn(e),r=e.writable??n.writable;return r===void 0?t:{...t,writable:r}}),sn=(n,e)=>e.template!==void 0?n.templatesById.get(e.template)?.properties??[]:e.properties??[],G=(n,e,t)=>`${e}.${n.owners[e].gameId.get(t)??t}`,$r=(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}},ft=(n,e,t,r)=>{let s=$r(n,e,t);if(s===void 0)throw new b(`no ${e} store "${t}"`);if("ambiguous"in s)throw new b(Gt(t,r,s.ambiguous));if("zone"in s)throw new b(_t(t,s.zone,r));return s},mn=(n,e,t,r)=>`"${e}.${t}.${r}" names the ${e} by its internal id; write "${G(n,e,t)}.${r}". The internal-id form is refused after the next release.`,yn=(n,e)=>{let t=r=>new Map([...e[r]].map(([s,o])=>[s,Xt(o,`${G(n,r,s)}.`)]));return{story:Xt(e.story,"story."),box:t("box"),deck:t("deck"),hand:t("hand"),value:t("value")}},Rr=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]))}),Br=(n,e)=>{n.story.load(e?.story??{});for(let t of I)for(let[r,s]of Object.entries(e?.[t]??{}))n[t].get(r)?.load(s)},we=()=>({evicted:[],droppedCooldowns:[],droppedSpent:[],droppedProperties:[],defaultedProperties:[],retypedProperties:[]}),De="",X=(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),it=n=>`${n.flow??""}${De}${n.path}`;function on(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(!K(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 an(n,e,t,r,s){let o={story:on(e.story,t?.story,i=>`story.${i}`,r,s),box:{},deck:{},hand:{},value:{}};for(let i of I){let a=t?.[i]??{},d=[...new Set([...e[i].keys(),...Object.keys(a)])].sort();for(let l of d){let c=G(n,i,l);o[i][l]=on(e[i].get(l),a[l],p=>`${c}.${p}`,r,s)}}return o}function ve(n,e,t,r){let s=e.version!==n.version||e.hash!==n.hash,o=X(r.evicted,p=>[p.flow,p.hand,p.card,p.reason].join(De)),i=X(r.droppedCooldowns,p=>`${p.flow}${De}${p.card}`),a=[...r.droppedSpent].sort(),d=X(r.droppedProperties,it),l=X(r.defaultedProperties,it),c=X(r.retypedProperties,it);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 Z=n=>n!==null&&typeof n=="object"&&!Array.isArray(n),bn=n=>n===null?"null":n===void 0?"nothing":Array.isArray(n)?"an array":`a ${typeof n}`,Lr=n=>{let e=[];Z(n.content)||e.push("no content"),Z(n.settings)||e.push("no settings");let t=n.schema===et?["story"]:["story","world"];for(let s of t){let o=n[s];(!Z(o)||!Array.isArray(o.properties))&&e.push(`no ${s} properties`)}let r=n.scopeRegistry;return r!==void 0&&(!Z(r)||!Array.isArray(r.scopes))&&e.push("no host scopes in its scopeRegistry"),Array.isArray(n.boxes)||e.push("no boxes"),e},dn=n=>{if(!Z(n))throw new b(`not a Storylets save: expected an object, got ${bn(n)}`);let e=n.schema;if(e!==J&&e!==oe&&e!==Q)throw new b(`unsupported save schema: ${String(e)} (this runtime reads ${Q}, ${oe} and ${J})`);if(!Z(n.content))throw new b("not a complete Storylets save: no content")},Or=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"}},Ie=(n,e)=>n.cardsByGameId.get(e)??n.cardsById.get(e),Mr=(n,e)=>n.handsByGameId.get(e)??n.handsById.get(e),Ir=(n,e)=>n.boxesByGameId.get(e)??n.boxesById.get(e),ct=(n,e)=>{let t=n.cardsById.get(e);return t!==void 0?f(t.card):e},wn=(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&&ut(r[0])){let i=r[0],[,a,d]=r,l=ft(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&&!K(s,t,{noneChosen:s.type==="enum"&&s.default===""}))throw new b(`"${e}" takes ${Or(s)}; ${JSON.stringify(t)} doesn't fit`)},vn=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??_(r),default:_(r),...r.values!==void 0?{values:r.values}:{},...r.stages!==void 0?{stages:r.stages}:{},writable:t.set!==void 0&&!fn(t.spec,r.name)})}return e},Ar=n=>{if(!Z(n))throw new b(`not a Storylets bundle: expected an object, got ${bn(n)}`);let e=n.schema;if(typeof e!="string"||!he.includes(e))throw new b(`unsupported bundle schema: ${String(e)} (this runtime reads ${he.slice(0,-1).join(", ")} and ${he[he.length-1]})`);let t=Lr(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===N&&r.push(`the project map's tag group is called "${N}", 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("; ")}`)},Te=class n{internals;seed;onReplacedFlow;flowsById=new Map;engineTraceHandlers=new Set;creationOptions;sharedMounts=[];constructor(e,t={}){Ar(e),this.creationOptions=t,this.seed=t.seed??0,this.onReplacedFlow=t.onReplacedFlow;let r=[];for(let l of Object.keys(t))yr.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:Pr(),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 Oe,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};Cr(s.owners.value,e);for(let l of e.boxes){s.boxesById.set(l.id,l),s.boxesByGameId.set(f(l),l),ot(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){ot(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),br(p,c)&&(ae(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}),ot(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",sn(s,p))]))),value:new Map(Wt(e).flatMap(c=>c.tags.map(p=>[p.id,l("value",p.properties??[])])))});s.flowDecls=d(kr),s.sharedDecls=d(Er),s.durable.shared=en(s,s.sharedDecls),s.durable.flow=en(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=se(r.bundle);r.shared=yn(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:ie})});for(let l of I)for(let[c,p]of r.shared[l])p.declarations().length!==0&&a(rt(l,c),()=>{s.mountOwned(rt(l,c),p,{owner:ie})});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(hn);a(l,()=>{s.defineForeign(l,c,u,{normalise:at,writable:p?.writable??!0,owner:ie})})}for(let l of r.ownsRegistry?o:[]){let c=l.token;if(e[c]!==void 0||s.has(c))continue;s.defineOwned(c,rn(l),{normalise:at,pathPrefix:`${c}.`,owner:ie});let p=s.ownedBag(c);i.push(c),this.sharedMounts.push({key:c,mount:()=>{s.mountOwned(c,p,{owner:ie})}})}}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 I)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,rn(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 se(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(sn(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){nn(this.internals,t.durable);let d=we(),{flow:l,flowCards:c}=this.internals.durable;s={plan:tn(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(dt(e));let a=new Se(this,this.internals,e,t.seed??this.seed);if(this.flowsById.set(e,a),t.restore!==void 0){let d=we(),l=this.planFlowRestore(e,structuredClone(t.restore),o,d);a.restore(l);let c=this.internals.bundle.content;t.onRestoreReport?.(ve(c,c,[e],d))}return s!==void 0&&(a.writeDurable(s.plan),t.onRestoreReport?.(ve(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(Ae(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(Ae(e));if(wn(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(D(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&&ut(t[0])){let o=t[0],[,i,a]=t,d=ft(this.internals,o,i,a);d.legacy&&this.diagnose(mn(this.internals,o,i,a));let l=d.id,c=this.internals.shared[o].get(l);if(c!==void 0&&D(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=vn(this.internals),t=(r,s)=>{for(let o of s.rows())e.push(o)};t("story",this.internals.shared.story);for(let r of I)for(let[s,o]of this.internals.shared[r])t(G(this.internals,r,s),o);return e}listBags(){let e=[{prefix:"story",bag:this.internals.shared.story}];for(let t of I)for(let[r,s]of this.internals.shared[t])e.push({prefix:G(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:J,content:this.internals.bundle.content,...this.internals.ownsRegistry?{registry:this.registrySection()}:{},shared:{spent:[...this.spent].map(e=>ct(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:Ee,content:e.content,values:un(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;nn(t,e);let r=we(),s=tn(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):_(o.decl);gn(gt(t.shared,o),o.decl.name,i)}for(let o of s.cards)this.spent.add(o);return ve(t.bundle.content,e.content,[],r)}previewLoad(e){dn(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=we();this.planFlowRestore(e,t,this.sharedClaims(e),r);let s=this.internals.bundle.content;return ve(s,s,[e],r)}loadGame(e){try{return this.loadGameInto(e)}finally{this.internals.reportRefits()}}loadGameInto(e){dn(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:X([...t.report.evicted,...s],o=>[o.flow,o.hand,o.card,o.reason].join(De))}}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!==J&&t!==oe&&t!==Q)throw new b(`unsupported save schema: ${String(t)}`);let r=we(),s=new Set(Object.keys(e.flows??{})),o;e.schema===Q?o={shared:e.shared?.props??xe(),flows:new Map(Object.entries(e.flows??{}).map(([c,p])=>[c,p.props??xe()])),rest:{}}:e.registry!==void 0&&(o=xr(e.registry,s));let i=o!==void 0?an(this.internals,this.internals.sharedDecls,o.shared,void 0,r):void 0,a=[];for(let c of e.shared?.spent??[]){let p=Ie(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&&Yt(i,(c,p)=>c==="story"?"story":rt(c,p),l);for(let[c,p]of Object.entries(e.flows??{})){let u=o!==void 0?{...p,props:o.flows.get(c)??xe()}:{...p},g=this.planFlowRestore(c,u,void 0,r);l!==void 0&&g.props!==void 0&&(Yt(g.props,(m,h)=>lt(c,m,h),l),delete g.props),d.push([c,g])}return{report:ve(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?an(o,o.flowDecls,t.props,e,s):void 0,a={};for(let[u,g]of Object.entries(t.cooldowns??{})){let m=Ie(o,u);m!==void 0?a[m.card.id]=g:s.droppedCooldowns.push({flow:e,card:u})}let d=u=>{let g=Ie(o,u);return g?f(g.card):u},l={},c=new Map;for(let[u,g]of Object.entries(t.board??{})){let m=Mr(o,u);if(m===void 0){for(let w of g)s.evicted.push({flow:e,hand:u,card:d(w),reason:"hand-vanished"});continue}let h=f(m.hand),y=[];for(let w of g){let E=Ie(o,w);if(E===void 0){s.evicted.push({flow:e,hand:h,card:w,reason:"vanished"});continue}let R=E.card.id;if(r!==void 0&&ae(E.card,E.deck.shared??!1)){if((r.get(R)??0)+(c.get(R)??0)>=ln(E.card)){s.evicted.push({flow:e,hand:h,card:f(E.card),reason:"claimed-elsewhere"});continue}c.set(R,(c.get(R)??0)+1)}y.push(R)}l[m.hand.id]=y}let p={};for(let[u,g]of Object.entries(t.turns??{})){let m=Ir(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??[]}}},Se=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=F(s),this.stores=yn(t,t.flowDecls);let o=(a,d)=>{d.declarations().length!==0&&this.bagKeys.push([a,d])};o(lt(r,"story"),this.stores.story);for(let a of I)for(let[d,l]of this.stores[a])o(lt(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=>D(a,l)??D(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:ie}),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=z(e.ast),this.internals.nodeCache.set(e,t)),t}groupInBox(e,t){let r=me(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=Qt(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?Qt(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:Re};return{nextRandom:()=>this.prng.next(),countPlayed:o=>this.playCount.get(o)??0,turnsSincePlayed:o=>{let i=this.lastPlayOf.get(o);return i?s(i):Re},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):Re}}}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)??st,deck:t?this.deckReaders.get(t.id)??st:st,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 Ce(this.node(e),t,Ze)}passes(e,t,r){if(!e)return!0;try{return Zt(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(Be(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(N,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===N){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(N,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=qt(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 me(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!==N)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>=cn(e)?"claimed":t&&(s.get(i)??0)-o>=ln(e)?"claimed-elsewhere":null}tagsMatch(e,t){let r=e.tags?.[N];if(r!==void 0&&r.length>0){let s=t.get(N);if(s===void 0||!r.includes(s))return!1}for(let[s,o]of t){if(s===N)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=ae(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 w=t(h,y);if(w){a(h,w);continue}let E;if(typeof h.priority=="number")E=h.priority;else try{let L=this.eval(h.priority,g);if(typeof L!="number"||Number.isNaN(L)){a(h,"priority");continue}E=L}catch(L){this.tracing&&this.emit({type:"diagnostic",where:`card ${h.gameId} priority`,message:L instanceof Error?L.message:String(L)}),a(h,"priority");continue}let R=0;if(o.ranking.specificity&&h.condition){let L=this.node(h.condition);R=Dt(L,C=>{try{return Zt(Ce(C,g,Ze))}catch{return!1}})}c.push({entry:{card:h,deck:u,box:o},priority:E,spec:R})}}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=wr(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=F(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)?ae(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=F(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));U(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,w)=>this.claimVerdict(y,w,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 w=Math.min(vr(y.card),g);u.set(y.card.id,w),g-=w}let m=[],h=[];for(let y of a){let w=u.get(y)??0;w>0?(m.push(y),u.set(y,w-1)):h.push(y)}for(let y of p){for(let w=u.get(y.card.id)??0;w>0;w--)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 w of h)this.emit({type:"evict",hand:f(o),card:f(this.internals.cardsById.get(w).card),reason:"outranked"},y);this.capTrace(c,new Set(m.map(w=>f(this.internals.cardsById.get(w).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(C=>f(C)).join(", ")}); name the one played`);let d=a?void 0:o.card.outcomes.find(C=>f(C)===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(([C,A])=>({target:C,expr:A,plan:this.planWrite(C,o,l)})).map(C=>({...C,value:this.eval(C.expr,c)})),h=[];try{for(let C of m)h.push({target:C.target,plan:C.plan,value:C.value,prev:this.landWrite(C.plan,C.value)})}catch(C){for(let A of[...h].reverse())this.undoWrite(A.plan,A.prev);throw C}if(this.tracing)for(let{target:C,plan:A,value:q,prev:x}of h)this.emit({type:"write",target:C,path:A.path,value:q,...x!==void 0?{prev:x}:{}},u);let y=d?f(d):"",w={card:f(o.card),outcome:y,turn:u};this.playLog.push(w),this.indexPlay(w),o.card.redraw==="never"?ae(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 E=i.hand.id,R=this.boardContents.get(E)??[],L=R.indexOf(o.card.id);this.boardContents.set(E,[...R.slice(0,L),...R.slice(L+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"?ae(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 G(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=D(o,r)!==void 0?o:D(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(fn(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 I)for(let[r,s]of this.stores[t])e.push({prefix:G(this.internals,t,r),bag:s});return e}listProperties(){this.assertOpen();let e=vn(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 I){let s=new Set([...this.internals.shared[r].keys(),...this.stores[r].keys()]);for(let o of s)t(G(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(Ae(e)),r=t.kind==="host"?t.host.resolver.get(t.name):t.kind==="scope"?this.internals.registry.get(t.token,t.name):D(t.own,t.name)??D(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(Ae(e));if(wn(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=D(r.own,r.name)!==void 0?r.own:D(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&&ut(t[0])){let s=t[0],[,o,i]=t,a=ft(this.internals,s,o,i);a.legacy&&this.tracing&&this.emit({type:"diagnostic",where:"property address",message:mn(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:Ee,content:e.content,values:un(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)gn(gt(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:Rr(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])=>[ct(t,o),i])),board:Object.fromEntries([...this.boardContents].map(([o,i])=>[s(o),i.map(a=>ct(t,a))])),playLog:this.playLog}}restore(e){e.props!==void 0&&Br(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=F(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 En="1.0.1",Dr=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;function ht(n){let e=typeof n=="string"?Dr.exec(n):null;return e?{core:[e[1],e[2],e[3]],pre:e[4]?e[4].split("."):[]}:null}function xn(n,e){return n=n.replace(/^0+(?=\d)/,""),e=e.replace(/^0+(?=\d)/,""),n.length!==e.length?n.length<e.length?-1:1:n===e?0:n<e?-1:1}function Sn(n,e){for(let t=0;t<3;t++){let r=xn(n.core[t],e.core[t]);if(r)return r}if(!n.pre.length||!e.pre.length)return e.pre.length-n.pre.length;for(let t=0;t<Math.min(n.pre.length,e.pre.length);t++){let r=n.pre[t],s=e.pre[t];if(r===s)continue;let o=/^\d+$/.test(r),i=/^\d+$/.test(s);if(o&&i){let a=xn(r,s);if(a)return a;continue}return o!==i?o?-1:1:r<s?-1:1}return n.pre.length-e.pre.length}function kn(n,e,t){let r=ht(n),s=ht(e),o=ht(t);return!r||!s||!o?!1:Sn(r,s)>=0&&Sn(r,o)<0}var Ne=n=>n.map(e=>({name:e.name,type:e.type,default:_(e),...e.values!==void 0?{values:e.values}:{},...e.durable===!0?{durable:!0}:{},...e.purpose!==void 0?{purpose:e.purpose}:{}})),Pn=n=>n.decks.reduce((e,t)=>e+t.cards.filter(r=>(r.durable??t.durable)===!0).length,0),Tr=(n,e)=>n.template!==void 0?e.handTemplates.find(t=>t.id===n.template)?.properties??[]:n.properties??[],Nr=(n,e,t)=>{let r=e.template!==void 0?e.chosen:e.rule?.bindings,s=[];for(let[o,i]of Object.entries(r??{})){if(!Be(i))continue;let a=me(n,t).find(d=>d.id===o);a!==void 0&&s.push({group:f(a),from:i})}return s},Hr=(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 He(n){let e=[],t=[],r=se(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:Ne(a.declarations??[])})),{scope:"story",owner:"",properties:Ne(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}:{}}:{},...Pn(a)>0?{durableCards:Pn(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=Nr(n,p,a);t.push({gameId:f(p),...p.title!==void 0?{title:p.title}:{},box:d,slots:Hr(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:Ne(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),Tr(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:Ne(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 Cn=(n,e)=>n.boxes.find(t=>t.gameId===e)??n.boxes.find(t=>t.id===e),Ve=(n,e)=>Object.prototype.hasOwnProperty.call(n,e);function $n(n,e){return Cn(n,e)?.fields??[]}function Rn(n,e){return Cn(n,e)?.outcomeFields??[]}function mt(n,e,t){if(e!==void 0&&Ve(e,t))return e[t];let r=n.find(s=>s.name===t)?.default;return r!==null&&typeof r=="object"?structuredClone(r):r}function Bn(n,e){let t={};for(let r of n){let s=mt(n,e,r.name);s!==void 0&&(t[r.name]=s)}for(let[r,s]of Object.entries(e??{}))Ve(t,r)||(t[r]=s);return t}function Ln(n,e,t){let r=n.extensions!==void 0&&Ve(n.extensions,e)?n.extensions[e]:void 0;return r!==void 0&&Ve(r.fields,t)?r.fields[t]??[]:[]}function On(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 Mn(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,An(In(n,e.id))),t}function In(n,e){return n.getFlow(e)!==void 0?n.saveFlow(e):void 0}function An(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 Dn(n,e,t={}){let r=e.id,s=()=>n.getFlow(r);return be({mounts:()=>[...n.listBags(),...s()?.listBags()??[]].map(({bag:o})=>({bag:o})),extra:()=>An(In(n,r))},t)}function je(n,e){return JSON.stringify(yt(n,e),null,2)}function yt(n,e){return{schema:Le,engine:n.saveGame(),...e!==void 0?{world:e}:{}}}function bt(n,e,t={}){if(!e||typeof e!="object"||e.schema!==Le||e.engine?.schema!==J&&e.engine?.schema!==oe&&e.engine?.schema!==Q)throw new Error(`not a storylets save (expected schema "${Le}")`);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 Fe(n,e,t={}){let r;try{r=JSON.parse(e)}catch{throw new Error("not valid JSON")}return bt(n,r,t)}function Vr(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 xt=!1,Tn=!1,Vn,jr=350,wt=6,Nn=7,le=null,ce=0,_e=null,Fr=null;function jn(){if(le)return le;let n=document.createElement("div");return n.className="tooltip",n.setAttribute("role","tooltip"),n.hidden=!0,document.body.appendChild(n),le=n,n}var _r=n=>n instanceof HTMLElement?n.getBoundingClientRect():n;function Gr(n){let e=jn(),t=_r(n),r=e.getBoundingClientRect(),s=t.top-r.height-Nn;s<wt&&(s=t.top+t.height+Nn);let o=t.left+t.width/2-r.width/2;o=Math.max(wt,Math.min(o,window.innerWidth-r.width-wt)),e.style.left=`${Math.round(o)}px`,e.style.top=`${Math.round(s)}px`}function qr(n){let e=n.dataset.tip;e&&Wr(n,e)}function Wr(n,e){if(Vn?.())return;let t=jn(),r=(n instanceof HTMLElement?n.closest("dialog[open]"):null)??document.body;t.parentElement!==r&&r.appendChild(t),_e=n instanceof HTMLElement?n:null,Vr(t,e),t.hidden=!1,t.classList.remove("show"),t.offsetWidth,Gr(n),t.classList.add("show")}function de(){ce&&(window.clearTimeout(ce),ce=0),_e=null,Fr=null,le&&(le.hidden=!0,le.classList.remove("show"))}function Hn(n){n!==_e&&(ce&&window.clearTimeout(ce),ce=window.setTimeout(()=>qr(n),jr))}function zr(){Kr()}function Ur(){Tn||(Tn=!0,setTimeout(()=>{xt||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 vt=n=>n instanceof Element?n.closest("[data-tip]"):null;function Kr(n={}){n.suppressed!==void 0&&(Vn=n.suppressed),!xt&&(xt=!0,document.addEventListener("pointerover",e=>{let t=vt(e.target);t&&Hn(t)}),document.addEventListener("pointerout",e=>{let t=vt(e.target);if(!t)return;let r=e.relatedTarget;r instanceof Node&&t.contains(r)||de()}),document.addEventListener("focusin",e=>{let t=vt(e.target);t&&Hn(t)}),document.addEventListener("focusout",()=>de()),document.addEventListener("pointerdown",()=>de(),!0),document.addEventListener("scroll",()=>{_e&&de()},!0),document.addEventListener("keydown",e=>{e.key==="Escape"&&de()}),window.addEventListener("blur",()=>de()))}function Fn(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),zr()),s.onClick&&r.addEventListener("click",s.onClick);for(let o of t)o!=null&&r.append(o);return Ur(),r}function _n(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 Gn=n=>`default (${_n(n)})`;function St(n){let e=n.doc===void 0?Fn:(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:Gn(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 qn="sl-inspector-style",Jr=`
|
|
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 ke(){if(document.getElementById(qn))return;let n=document.createElement("style");n.id=qn,n.textContent=
|
|
37
|
-
`):T,Bt&&(ue.scrollTop=ue.scrollHeight)};for(let V of
|
|
38
|
-
`))});let te=document.createElement("button");return te.type="button",te.className="sl-logclear",te.textContent="Clear",te.title="Drop the retained log entries (cosmetic - no game state changes)",te.addEventListener("click",()=>{M(),We(!0)}),pe.append(ee,te),o.append(Ge,pe,ue),{render:We}},C=L("flow","Log",()=>s().log(),()=>s().clearLog(),"(empty - new Engine(bundle, { log: true }) retains the flow log)"),A=L("run","Run log (every flow)",()=>n.log(),()=>n.clearLog(),"(empty - new Engine(bundle, { log: true }) retains the run log)"),q=(S=!1)=>{A.render(S),C.render(S)},x=(S,B)=>{let P=document.createElement("div");P.className="sl-line",P.textContent=B,S.append(P)},$=()=>{y.textContent="";for(let S of s().listBoxes())x(y,`${S.title??S.gameId}: turn ${S.turn}`);E.textContent="";for(let[S,B]of Object.entries(s().board())){let P=B.map(M=>M.title??M.gameId);x(E,`${S}: ${P.length>0?P.join(", "):"(empty)"}`)}},k=()=>{for(let S of u)S.read();$(),q()};$(),q(!0);let H,$t=t.pollMs??250;return $t>0&&(H=setInterval(k,$t)),(t.container??document.body).append(o),{el:o,refresh:k,destroy(){H!==void 0&&clearInterval(H),o.remove()}}}function
|
|
36
|
+
`;function ke(){if(document.getElementById(qn))return;let n=document.createElement("style");n.id=qn,n.textContent=Jr,document.head.append(n)}var Qr=(n,e)=>JSON.stringify(n)===JSON.stringify(e),Yr=["deal","play","write","evict","turns","diagnostic"],Xr={deal:"Deal",play:"Play",write:"Write",evict:"Evict",turns:"Turns",diagnostic:"Diag"},Zr=n=>n.type==="peek"?"deal":n.type==="discard"?"play":n.type,Wn=n=>n===void 0?"<unset>":JSON.stringify(n);function Et(n){return es(n,"flow"in n&&n.flow?`${n.flow} `:"")}function es(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}: ${Wn(n.prev)} -> ${Wn(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 ts=n=>{let e=n.split(".");return e.length===3?`${e[0]} ${e[1]}`:e[0]};function zn(n,e,t={}){ke();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 S=new Blob([je(n)],{type:"application/json"}),B=URL.createObjectURL(S),P=document.createElement("a");P.href=B,P.download="save.storyletsave",P.click(),URL.revokeObjectURL(B)}),i.append(d);let l=document.createElement("input");l.type="file",l.accept=".storyletsave,application/json",l.hidden=!0,l.addEventListener("change",()=>{let S=l.files?.[0];if(!S)return;let B=new FileReader;B.onload=()=>{try{Fe(n,String(B.result)),r=n.getFlow(e.id)??n.openFlow(e.id),k()}catch(P){console.error("storylets inspector: load failed:",P instanceof Error?P.message:P)}l.value=""},B.readAsText(S)});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 S of s().listProperties()){let B=ts(S.path);if(B!==m){let M=document.createElement("div");M.className="sl-group",M.textContent=B,o.append(M),g.push({el:M,rows:[]}),m=B}let P=ns(s,S,u);g[g.length-1].rows.push({el:P,text:`${S.name} ${S.path}`.toLowerCase()}),o.append(P)}p.addEventListener("input",()=>{let S=p.value.trim().toLowerCase();for(let B of g){let P=!1;for(let M of B.rows){let T=S===""||M.text.includes(S);M.el.style.display=T?"":"none",P=P||T}B.el.style.display=P?"":"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 w=document.createElement("div");w.className="sl-section",w.textContent="Board";let E=document.createElement("div");E.className="sl-board",o.append(h,y,w,E);let R=(S,B,P)=>{let M=document.createElement("label");M.className=P;let T=document.createElement("input");return T.type="checkbox",T.checked=!0,T.addEventListener("change",()=>B(T.checked)),M.append(T,S),M},L=(S,B,P,M,T)=>{let Ge=document.createElement("div");Ge.className=`sl-section sl-${S}`,Ge.textContent=B;let pe=document.createElement("div");pe.className=`sl-logbar sl-${S}`;let ue=document.createElement("div");ue.className=`sl-log sl-${S}`;let qe=new Map,Rt=()=>P().filter(V=>qe.get(Zr(V))!==!1).map(Et),Bt=!0,Lt="",We=(V=!1)=>{let ne=P(),Ot=`${ne.length}:${ne.length>0?ne[ne.length-1].seq:-1}`;if(!V&&Ot===Lt)return;Lt=Ot;let Mt=Rt();ue.textContent=Mt.length>0?Mt.join(`
|
|
37
|
+
`):T,Bt&&(ue.scrollTop=ue.scrollHeight)};for(let V of Yr)qe.set(V,!0),pe.append(R(Xr[V],ne=>{qe.set(V,ne),We(!0)},"sl-logkind"));pe.append(R("Autoscroll",V=>{Bt=V},"sl-logscroll"));let ee=document.createElement("button");ee.type="button",ee.className="sl-logcopy",ee.textContent="Copy",ee.title="Copy the visible (filtered) log to the clipboard",ee.addEventListener("click",()=>{navigator.clipboard?.writeText(Rt().join(`
|
|
38
|
+
`))});let te=document.createElement("button");return te.type="button",te.className="sl-logclear",te.textContent="Clear",te.title="Drop the retained log entries (cosmetic - no game state changes)",te.addEventListener("click",()=>{M(),We(!0)}),pe.append(ee,te),o.append(Ge,pe,ue),{render:We}},C=L("flow","Log",()=>s().log(),()=>s().clearLog(),"(empty - new Engine(bundle, { log: true }) retains the flow log)"),A=L("run","Run log (every flow)",()=>n.log(),()=>n.clearLog(),"(empty - new Engine(bundle, { log: true }) retains the run log)"),q=(S=!1)=>{A.render(S),C.render(S)},x=(S,B)=>{let P=document.createElement("div");P.className="sl-line",P.textContent=B,S.append(P)},$=()=>{y.textContent="";for(let S of s().listBoxes())x(y,`${S.title??S.gameId}: turn ${S.turn}`);E.textContent="";for(let[S,B]of Object.entries(s().board())){let P=B.map(M=>M.title??M.gameId);x(E,`${S}: ${P.length>0?P.join(", "):"(empty)"}`)}},k=()=>{for(let S of u)S.read();$(),q()};$(),q(!0);let H,$t=t.pollMs??250;return $t>0&&(H=setInterval(k,$t)),(t.container??document.body).append(o),{el:o,refresh:k,destroy(){H!==void 0&&clearInterval(H),o.remove()}}}function ns(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(St({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=Qr(o(),e.default)};return i=u,u(),t.push({row:e,read:u}),r.append(s,l,d),r}var rs=n=>n===void 0?"<unset>":JSON.stringify(n);function kt(n){let e=n.values!==void 0&&n.values.length>0?` [${n.values.join(", ")}]`:"",t=n.durable===!0?" (durable)":"";return`${n.name}: ${n.type} = ${rs(n.default)}${e}${t}`}function Pt(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 O=(n,e,t="sl-line")=>{let r=document.createElement("div");return r.className=t,r.textContent=e,n.append(r),r},Pe=(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 Un(n,e={}){ke();let t=He(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),O(l,`${a.project} ${a.version}`,"sl-line"),O(l,`schema ${a.schema}`,"sl-line sl-note"),O(l,`hash ${a.hash===""?"(none)":a.hash} - metadata ${a.metadata}`,"sl-line sl-note");let c=Pe(s,"Hands (deal)",r);c.className="sl-hands",t.hands.length===0&&O(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}`:"",w=h.movable===void 0?"":`, moves ${h.movable.map(E=>`${E.group} from ${E.from}`).join(" and ")}`;O(c,`${h.gameId}: box ${h.box}, slots ${h.slots}${y}${w}`+(h.title!==void 0?` - ${h.title}`:""))}let p=Pe(s,"Tags by box (peek criteria)",r);p.className="sl-tags";for(let h of t.boxes){O(p,`${h.title??h.gameId}`,"sl-group"),h.tagGroups.length===0&&O(p," (no tag groups)","sl-line sl-note");for(let y of h.tagGroups)O(p,` ${y.gameId}: ${y.tags.length>0?y.tags.join(", "):"(no tags)"}`)}let u=Pe(s,"Properties (declared)",r);u.className="sl-props";for(let h of t.properties){O(u,Pt(h),"sl-group"),h.properties.length===0&&O(u," (none declared)","sl-line sl-note");for(let y of h.properties)O(u,` ${kt(y)}`)}let g=t.map;if(g!==void 0){let h=Pe(s,"Project map",r);h.className="sl-maps",O(h,`${g.group}: ${g.tags.join(", ")}`),O(h,`boxes on the map: ${g.boxes.length>0?g.boxes.join(", "):"none"}`);let y=Object.entries(g.sites).map(([w,E])=>`${w} ${E}`).join(", ");O(h,`geometry carried (the engine ignores it): zones ${g.zones}, pictures ${g.backgrounds}, hands ${y===""?"0":y}`,"sl-line sl-note")}let m=Pe(s,"Counts",r);m.className="sl-counts",O(m,`boxes ${d.boxes} - decks ${d.decks} - cards ${d.cards}`),O(m,`hands ${d.hands} - templates ${d.templates} - tag groups ${d.tagGroups}`);for(let h of t.boxes)O(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 ss="ws://127.0.0.1:4472";var os=new Set(["deal","play","discard","evict","turns"]);function Ct(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 Kn(n){let e=n.url??ss,t=n.WebSocket??globalThis.WebSocket,r=[],s=null,o=!1,i=n.build,a=null,d=null,l=new Set,c=!1,p=!1,u=!1,g=1e3,m=null;if(!t)return{state:"closed",attach(){},detach(){},setBuild(){},close(){o=!0}};let h=()=>{if(!(!s||s.readyState!==1)){for(let x of r)try{s.send(x)}catch{}r=[]}},y=x=>{o||!s||s.readyState>1||c&&s.readyState!==1||(r.push(JSON.stringify(x)),r.length>512&&r.splice(0,r.length-512),h())},w=()=>{try{return a?a.flows():[]}catch{return[]}},E=()=>{let x=w(),$={t:"hello",v:2,build:i,flows:x.map(H=>H.id)};n.project!==void 0&&($.project=n.project);let k=x[0];if(k)try{$.boxes=k.listBoxes().map(H=>H.gameId)}catch{}l=new Set(x.map(H=>H.id));try{s?.send(JSON.stringify($)),g=1e3}catch{}},R=x=>{try{y(Ct(x))}catch{}},L=()=>{let x=w(),$=new Set(x.map(k=>k.id));for(let k of x)l.has(k.id)||(l.add(k.id),y({t:"flowOpen",flow:k.id}),R(k));for(let k of[...l])$.has(k)||(l.delete(k),y({t:"flowClose",flow:k}))},C=(x,$)=>{try{if(L(),y({t:"trace",flow:x,event:$}),os.has($.type)){let k=a?.getFlow(x);k&&R(k)}}catch{}},A=()=>{if(m=null,o)return;let x;try{x=new t(e)}catch{s=null,o=!0;return}s=x,x.addEventListener("open",()=>{if(s===x){if(p=!0,u&&(u=!1,console.info(`[storylets] Live Link is back (${e}).`)),E(),c)for(let $ of w())R($);h()}}),x.addEventListener("message",$=>{if(!(!n.onBundle||typeof $.data!="string"))try{let k=JSON.parse($.data);k.t==="bundle"&&typeof k.build=="string"&&typeof k.data=="string"&&n.onBundle({build:k.build,data:k.data})}catch{}}),x.addEventListener("error",()=>{}),x.addEventListener("close",$=>{if(s===x&&(s=null,r=[],c=!0,!o)){if($?.code===4e3){o=!0,console.info(`[storylets] Live Link: another game took the editor's link (${e}).`);return}p&&!u&&(u=!0,console.info(`[storylets] Live Link lost (${e}); retrying.`)),m=setTimeout(A,g),g=Math.min(g*2,1e4)}})};A();let q=()=>{d?.(),d=null,a=null,l=new Set};return{get state(){return o?"closed":s!==null&&s.readyState===1?"connected":"connecting"},attach(x){if(!o){q(),a=x;try{d=x.subscribeTrace(C)}catch{a=null;return}l=new Set(w().map($=>$.id));for(let $ of w())R($)}},detach:q,setBuild(x){if(!(o||x===i)&&(i=x,s&&s.readyState===1)){E();for(let $ of w())R($)}},close(){o=!0,q(),r=[],m&&(clearTimeout(m),m=null);try{s?.close()}catch{}s=null}}}function Jn(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 Qn(n,e="world"){let t=new Y(jt(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 nr(is);})();
|
|
39
39
|
//# sourceMappingURL=storyletengine.min.js.map
|