@ziprack/sdk 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/broadcast/index.d.ts +8 -1
  2. package/dist/core/mock/MockRack.d.ts +6 -0
  3. package/dist/core/types.d.ts +4 -0
  4. package/dist/docs/README.md +52 -0
  5. package/dist/docs/activity.md +140 -0
  6. package/dist/docs/ai.md +159 -0
  7. package/dist/docs/applet-packages.md +118 -0
  8. package/dist/docs/attempts.md +184 -0
  9. package/dist/docs/auth.md +106 -0
  10. package/dist/docs/broadcast.md +226 -0
  11. package/dist/docs/conformance.md +102 -0
  12. package/dist/docs/database.md +176 -0
  13. package/dist/docs/extending.md +256 -0
  14. package/dist/docs/installation.md +185 -0
  15. package/dist/docs/introduction.md +124 -0
  16. package/dist/docs/manifest.md +209 -0
  17. package/dist/docs/media.md +85 -0
  18. package/dist/docs/protocol.md +121 -0
  19. package/dist/docs/publishing.md +127 -0
  20. package/dist/docs/reporting.md +146 -0
  21. package/dist/docs/sdk.md +258 -0
  22. package/dist/docs/security.md +127 -0
  23. package/dist/docs/storage.md +131 -0
  24. package/dist/docs/studio.md +425 -0
  25. package/dist/docs/versioning.md +101 -0
  26. package/dist/docs/ziprack-1-authoring.md +182 -0
  27. package/dist/index.js +11 -2
  28. package/dist/index.js.map +1 -1
  29. package/dist/mcp/bin.d.ts +2 -0
  30. package/dist/mcp/bin.js +3 -0
  31. package/dist/mcp/docs.d.ts +31 -0
  32. package/dist/mcp/docs.js +107 -0
  33. package/dist/mcp/index.d.ts +4 -0
  34. package/dist/mcp/index.js +4 -0
  35. package/dist/mcp/rpc.d.ts +33 -0
  36. package/dist/mcp/rpc.js +86 -0
  37. package/dist/mcp/server.d.ts +23 -0
  38. package/dist/mcp/server.js +55 -0
  39. package/dist/mcp/tools.d.ts +20 -0
  40. package/dist/mcp/tools.js +106 -0
  41. package/dist/{store-DSV5q7C7.js → store-BXGon4iJ.js} +15 -5
  42. package/dist/store-BXGon4iJ.js.map +1 -0
  43. package/dist/testing.js +1 -1
  44. package/dist/ziprack-sdk.iife.js +1 -1
  45. package/dist/ziprack-sdk.iife.js.map +1 -1
  46. package/package.json +10 -4
  47. package/dist/store-DSV5q7C7.js.map +0 -1
package/dist/testing.js CHANGED
@@ -1,4 +1,4 @@
1
- import { b as s, e, M as a } from "./store-DSV5q7C7.js";
1
+ import { b as s, e, M as a } from "./store-BXGon4iJ.js";
2
2
  class i {
3
3
  constructor(t = null, r = {}) {
4
4
  this.rack = new s(t, new e(), { participantId: 1, participantName: "Test User", ...r });
@@ -1,2 +1,2 @@
1
- var ZipRackSdk=(function(u){"use strict";class R{constructor(t){this.link=t}activities(){return this.link.launch()?.activity?.activities??[]}activity(t){return this.activities().find(e=>e.id===t)??null}statement(t){return this.link.send("activity:statement",{...t,occurred_at:t.occurred_at??new Date().toISOString()})}answer(t,e,s={}){return this.statement({verb:"answered",activity:{id:t,type:s.type??this.activity(t)?.type},result:{response:e},context:s.context})}progress(t,e){return this.statement({verb:"progressed",activity:{id:t,type:"step"},context:e})}}class E{constructor(t,e){this.link=t,this.promptId=e}async send(t,e){let s="";const r=await this.link.send("ai:chat",{prompt:this.promptId,message:t},{timeoutMs:6e4,onPartial:i=>{const a=typeof i.delta=="string"?i.delta:"";s+=a,e?.(a,s)}});return r.ok?{ok:!0,data:{message_id:String(r.data?.message_id??""),text:r.data?.text??s,usage:r.data?.usage??{input_tokens:0,output_tokens:0}}}:r}reset(){return this.link.send("ai:chat.reset",{prompt:this.promptId})}}class I{constructor(t){this.link=t}prompts(){return this.link.launch()?.ai?.prompts??[]}ask(t,e={}){return this.link.send("ai:ask",{prompt:t,vars:e},{timeoutMs:6e4})}chat(t){return new E(this.link,t)}}class L{constructor(t){this.link=t,this.local={}}current(){const t=this.link.launch()?.attempt;return t?{...t,...this.local}:null}get status(){return this.current()?.status??null}get id(){return this.current()?.id??null}state(){return this.current()?.state??null}segment(){return this.current()?.segment??null}setState(t){return this.local.state=t,this.link.send("attempt:state",{state:t})}setSegment(t){return this.local.segment=t,this.link.send("attempt:segment",{segment:t})}async complete(t){const e=await this.link.send("attempt:complete",{summary:t??null});return e.ok&&(this.local.status="completed"),e}}class O{constructor(t){this.link=t}user(){return this.link.launch()?.auth?.user??null}attribute(t,e=null){const s=this.user();return!s||!(t in s)?e:s[t]}}const j="$room";class T{constructor(t,e){this.link=t,this.name=e,this.listeners=new Map,this.unsubscribe=null}attach(){this.unsubscribe??=this.link.onEvent(t=>{t.channel===this.name&&(this.listeners.get(t.event)?.forEach(e=>e(t.payload,t.from)),this.listeners.get("*")?.forEach(e=>e({event:t.event,payload:t.payload},t.from)))})}listen(t,e){const s=this.listeners.get(t)??new Set;return s.add(e),this.listeners.set(t,s),()=>{s.delete(e)}}publish(t,e={}){return this.link.send("broadcast:publish",{channel:this.name,event:t,payload:e})}async leave(){return this.unsubscribe?.(),this.unsubscribe=null,this.listeners.clear(),this.link.send("broadcast:leave",{channel:this.name})}}class q{constructor(t){this.link=t,this.channels=new Map,this.roomListeners=new Map,this.localRole=null,this.localMembers=null,this.localState=null,this.detach=null,this.state={get:async e=>{const s=await this.link.send("broadcast:state.get",e?{keys:e}:{});return s.ok&&s.data&&(this.localState={...this.localState??{},...s.data.state}),s},set:(e,s)=>(this.localState={...this.localState??this.launch()?.state??{},[e]:s},this.link.send("broadcast:state.set",{key:e,value:s})),snapshot:()=>this.localState??this.launch()?.state??{}}}get room(){return this.launch()?.room??null}get role(){return this.localRole??this.launch()?.role??null}get isFacilitator(){return this.role==="facilitator"}members(){return this.localMembers??this.launch()?.members??[]}async join(t="main"){this.ensureRoomListener();let e=this.channels.get(t);return e||(e=new T(this.link,t),this.channels.set(t,e)),e.attach(),await this.link.send("broadcast:join",{channel:t}),e}async presence(){const t=await this.link.send("broadcast:presence",{});return t.ok&&t.data&&(this.localMembers=t.data.members),t}claim(){return this.link.send("broadcast:claim",{})}transfer(t){return this.link.send("broadcast:transfer",{to:t})}on(t,e){this.ensureRoomListener();const s=this.roomListeners.get(t)??new Set;return s.add(e),this.roomListeners.set(t,s),()=>{s.delete(e)}}launch(){return this.link.launch()?.broadcast??null}ensureRoomListener(){this.detach??=this.link.onEvent(t=>{if(t.channel!==j)return;const e=t.payload;t.event==="presence"&&Array.isArray(e.members)&&(this.localMembers=e.members),t.event==="state"&&typeof e.key=="string"&&(this.localState={...this.localState??this.launch()?.state??{},[e.key]:e.value}),t.event==="role"&&e.participant_id===this.link.launch()?.actor.id&&(this.localRole=e.role),this.roomListeners.get(t.event)?.forEach(s=>s(e))})}}function w(n){return{ok:!1,error:"undeclared_capability",message:`The manifest does not declare the [${n}] capability.`}}class C{constructor(t,e){this.link=t,this.name=e}create(t){return this.send("db:create",{data:t})}get(t){return this.send("db:get",{id:t})}update(t,e,s={}){return this.send("db:update",{id:t,data:e,merge:s.merge??!0})}delete(t){return this.send("db:delete",{id:t})}list(t={}){return this.send("db:list",t)}count(t={}){return this.send("db:count",t)}send(t,e){return this.link.launch()&&!this.link.declares("db")?Promise.resolve(w("db")):this.link.send(t,{collection:this.name,...e})}}class U{constructor(t){this.link=t}collection(t){return new C(this.link,t)}collections(){return this.link.launch()?.db?.collections??[]}}class P{constructor(t){this.link=t}get(t){return this.link.launch()?.media?.[t]??null}embedUrl(t){return this.get(t)?.embedUrl??null}all(){return this.link.launch()?.media??{}}}class N{constructor(t){this.link=t}uploads(){return this.link.launch()?.storage?.uploads??[]}put(t,e,s={}){const r=e instanceof File?e.name:"blob";return this.link.send("storage:put",{upload:t,file:e,name:r,mime:e.type,bytes:e.size,meta:s},{timeoutMs:12e4})}url(t){return this.link.send("storage:url",{id:t})}list(t){return this.link.send("storage:list",t?{upload:t}:{})}delete(t){return this.link.send("storage:delete",{id:t})}}const y="ziprack",S=1;let _=0;function x(){const n=typeof globalThis.crypto<"u"?globalThis.crypto:void 0;return n&&typeof n.randomUUID=="function"?n.randomUUID():(_+=1,`req-${Date.now().toString(36)}-${_.toString(36)}`)}function M(n,t,e){const s={ns:y,v:S,type:n,payload:t};return e!==void 0&&(s.requestId=e),s}function z(n){if(typeof n!="object"||n===null)return!1;const t=n;return t.ns===y&&t.v===S&&typeof t.type=="string"}function p(n){const t=n.indexOf(":");return t===-1?n:n.slice(0,t)}class H{constructor(t){this.options=t,this.kind="host",this.pending=new Map,this.eventListeners=new Set,this.launchResolvers=[],this.launchData=null,this.listener=e=>this.onMessage(e),this.target=t.target??window.parent,this.isHost=t.isHost??(e=>e===window.parent),this.listenOn=t.listenOn??window,this.listenOn.addEventListener("message",this.listener)}announceReady(){this.fire("rack:ready",{})}waitForLaunch(t){return this.launchData?Promise.resolve(this.launchData):new Promise(e=>{const s=setTimeout(()=>{this.launchResolvers=this.launchResolvers.filter(i=>i!==r),e(null)},t),r=i=>{clearTimeout(s),e(i)};this.launchResolvers.push(r)})}send(t,e,s={}){const r=x(),i=s.timeoutMs??this.options.ackTimeoutMs;return new Promise(a=>{const o={resolve:a,timer:setTimeout(()=>this.expire(r),i),timeoutMs:i,onPartial:s.onPartial};this.pending.set(r,o),this.post(M(t,e,r))})}fire(t,e={}){this.post(M(t,e))}onEvent(t){return this.eventListeners.add(t),()=>{this.eventListeners.delete(t)}}dispose(){this.listenOn.removeEventListener("message",this.listener);for(const[t,e]of this.pending)clearTimeout(e.timer),e.resolve({ok:!1,error:"disposed"}),this.pending.delete(t)}expire(t){const e=this.pending.get(t);e&&(this.pending.delete(t),e.resolve({ok:!1,error:"timeout"}))}post(t){this.target.postMessage(t,"*")}onMessage(t){if(!this.isHost(t.source)||!z(t.data))return;const e=t.data;if(e.type==="rack:launch"){this.launchData=e.payload;const s=this.launchResolvers;this.launchResolvers=[],s.forEach(r=>r(this.launchData));return}if(e.type==="rack:event"){const s=e.payload;this.eventListeners.forEach(r=>r(s));return}e.type==="rack:ack"&&this.onAck(e.payload)}onAck(t){const e=this.pending.get(t.requestId);if(!e)return;if(!t.done&&t.data?.stream===!0){clearTimeout(e.timer),e.timer=setTimeout(()=>this.expire(t.requestId),e.timeoutMs);return}if(t.partial&&!t.done){const{requestId:b,ok:v,partial:m,...h}=t;e.onPartial?.(h),clearTimeout(e.timer),e.timer=setTimeout(()=>this.expire(t.requestId),e.timeoutMs);return}clearTimeout(e.timer),this.pending.delete(t.requestId);const{requestId:s,ok:r,data:i,error:a,message:o,details:c,partial:l,done:D,...d}=t;e.resolve({ok:r,data:i??(Object.keys(d).length?d:void 0),error:a,message:o,details:c})}}class F{constructor(t,e,s={}){this.manifest=t,this.store=e,this.options=s,this.statements=[],this.events=[],this.aiHandlers=new Map,this.chatHistory=new Map,this.blobs=new Map,this.eventListeners=new Set,this.roomMembers=new Map,this.roomState={},this.role="participant",this.joinedAt=Date.now()+Math.random(),this.channel=null,this.heartbeat=null,this.participantId=s.participantId??Math.floor(Math.random()*1e6)+1,this.participantName=s.participantName??"Dev User"}get slug(){return this.manifest?.slug??"applet"}key(...t){return`ziprack-mock:${this.slug}:${t.join(":")}`}declares(t){return this.manifest?(this.manifest.requires?.capabilities??[]).some(e=>e.split("@")[0]===t):!0}onEvent(t){return this.eventListeners.add(t),()=>{this.eventListeners.delete(t)}}pushEvent(t){this.events.push(t),this.eventListeners.forEach(e=>e(t))}launchData(t){const e=this.manifest,s={schema:"ziprack/1",actor:{id:this.participantId,name:this.participantName},applet:{slug:this.slug,name:`${e?.name??t??"Applet"} (mock)`,version:e?.version??"dev"},capabilities:e?.requires?.capabilities??[],media:Object.fromEntries((e?.media??[]).map(r=>[r.id,{provider:r.provider,embedUrl:r.provider==="vimeo"?`https://player.vimeo.com/video/${r.key}?dnt=1`:`https://www.youtube-nocookie.com/embed/${r.key}?rel=0`,meta:null}]))};if(this.declares("auth")){const r={user:{id:this.participantId,display_name:this.participantName}};for(const i of e?.requires?.profile??[])r.user[i]=r.user[i]??`mock-${i}`;s.auth=r}return this.declares("attempt")&&(s.attempt=this.attempt()),this.declares("activity")&&(s.activity={activities:[...e?.activities??[]].sort((r,i)=>(r.sequence??0)-(i.sequence??0))}),this.declares("db")&&(s.db={collections:(e?.collections??[]).map(r=>({name:r.name,scope:r.scope}))}),this.declares("storage")&&(s.storage={uploads:(e?.uploads??[]).map(r=>({name:r.name,scope:r.scope,mime:r.mime??[],max_bytes:r.max_bytes??10485760}))}),this.declares("ai")&&(s.ai={prompts:(e?.prompts??[]).map(r=>({id:r.id,mode:r.mode}))}),this.declares("broadcast")&&(this.joinRoom(),s.broadcast={room:{id:1,code:"MOCK01",name:`${this.slug} room`,status:"open",mode:e?.broadcast?.mock_mode??"first_in"},role:this.role,members:this.members(),state:this.stateSnapshot(),channels:e?.broadcast?.channels??["main"]}),s}mockAi(t,e){this.aiHandlers.set(t,e)}dispose(){this.heartbeat&&clearInterval(this.heartbeat),this.channel&&(this.send({t:"bye",from:this.member()}),this.channel.close())}async handle(t,e){if(this.options.log?.(t,e),t==="rack:eject")return{ok:!0};const s=p(t);if((this.options.strict??this.manifest!==null)&&!this.declares(s))return{ok:!1,error:"undeclared_capability",message:`The manifest does not declare the [${s}] capability.`};switch(t){case"attempt:state":return this.attemptState(e);case"attempt:segment":return this.attemptSegment(e);case"attempt:complete":return this.attemptComplete(e);case"activity:statement":return this.statement(e);case"db:create":return this.dbCreate(e);case"db:get":return this.dbGet(e);case"db:update":return this.dbUpdate(e);case"db:delete":return this.dbDelete(e);case"db:list":return this.dbList(e);case"db:count":return this.dbCount(e);case"storage:put":return this.storagePut(e);case"storage:url":return this.storageUrl(e);case"storage:list":return this.storageList(e);case"storage:delete":return this.storageDelete(e);case"ai:ask":return this.aiAsk(e);case"ai:chat":return this.aiChat(e);case"ai:chat.reset":return this.chatHistory.delete(String(e.prompt)),{ok:!0};case"broadcast:join":return{ok:!0,data:{members:this.members(),state:this.stateSnapshot()}};case"broadcast:leave":return{ok:!0};case"broadcast:publish":return this.publish(e);case"broadcast:state.get":return{ok:!0,data:{state:this.stateSnapshot(Array.isArray(e.keys)?e.keys:void 0)}};case"broadcast:state.set":return this.stateSet(e);case"broadcast:presence":return{ok:!0,data:{members:this.members()}};case"broadcast:claim":return this.claim();case"broadcast:transfer":return this.transfer(e);default:return{ok:!1,error:"unknown_type",message:`Unknown message type [${t}].`}}}attempt(){return this.store.get(this.key("attempt"))??{id:1,status:"in_progress",state:null,segment:null}}saveAttempt(t){this.store.set(this.key("attempt"),t)}attemptState(t){const e=this.attempt();if(e.status==="completed")return{ok:!1,error:"completed",message:"This attempt is already completed."};const s=JSON.stringify(t.state??{}).length;return s>65536?{ok:!1,error:"validation",message:`State is ${s} bytes; the limit is 65536.`}:(this.saveAttempt({...e,state:t.state??null}),{ok:!0})}attemptSegment(t){const e=this.attempt();return e.status==="completed"?{ok:!1,error:"completed"}:(this.saveAttempt({...e,segment:t.segment??null}),{ok:!0})}attemptComplete(t){const e=this.attempt(),s=new Date().toISOString();return e.status!=="completed"&&(this.saveAttempt({...e,status:"completed"}),this.declares("activity")&&this.statement({verb:"completed",activity:{id:"attempt",type:"attempt"},result:t.summary??null,occurred_at:s})),{ok:!0,data:{status:"completed",completed_at:s}}}resetAttempt(){this.store.remove(this.key("attempt"))}statement(t){const e=String(t.verb??"");if(!/^[a-z][a-z0-9_.:-]*$/.test(e)||e.length>100)return{ok:!1,error:"validation",message:`Invalid verb [${e}].`};if(!t.activity?.id)return{ok:!1,error:"validation",message:"activity.id is required."};const r=`mock-${Date.now().toString(36)}-${this.statements.length+1}`;return this.statements.push({id:r,...t}),{ok:!0,data:{id:r}}}collectionKey(t){return this.key("db",t)}records(t){return this.store.get(this.collectionKey(t))??[]}saveRecords(t,e){this.store.set(this.collectionKey(t),e)}collectionDeclared(t){return this.manifest&&!(this.manifest.collections??[]).some(s=>s.name===t)?{ok:!1,error:"not_found",message:`Collection [${t}] is not declared in the manifest.`}:(this.manifest?.collections??[]).find(s=>s.name===t)?.scope==="attempt"&&this.attempt().status==="completed"?{ok:!1,error:"completed",message:"Attempt-scoped records are frozen after completion."}:null}dbCreate(t){const e=String(t.collection),s=this.collectionDeclared(e);if(s)return s;const r=new Date().toISOString(),i={id:`rec-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,7)}`,data:t.data??{},owner_id:this.participantId,created_at:r,updated_at:r};return this.saveRecords(e,[...this.records(e),i]),{ok:!0,data:{record:i}}}dbGet(t){const e=this.records(String(t.collection)).find(s=>s.id===t.id);return e?{ok:!0,data:{record:e}}:{ok:!1,error:"not_found"}}dbUpdate(t){const e=String(t.collection),s=this.collectionDeclared(e);if(s&&s.error==="completed")return s;const r=this.records(e),i=r.findIndex(c=>c.id===t.id);if(i===-1)return{ok:!1,error:"not_found"};const a=t.merge!==!1,o=t.data??{};return r[i]={...r[i],data:a?{...r[i].data,...o}:o,updated_at:new Date().toISOString()},this.saveRecords(e,r),{ok:!0,data:{record:r[i]}}}dbDelete(t){const e=String(t.collection),s=this.records(e);return s.some(r=>r.id===t.id)?(this.saveRecords(e,s.filter(r=>r.id!==t.id)),{ok:!0,data:{id:t.id}}):{ok:!1,error:"not_found"}}filter(t,e){return e?t.filter(s=>e.every(([r,i,a])=>{const o=r.split(".").reduce((c,l)=>c?.[l],s.data);switch(i){case"=":return o===a;case"!=":return o!==a;case"<":return o<a;case"<=":return o<=a;case">":return o>a;case">=":return o>=a;case"in":return Array.isArray(a)&&a.includes(o);default:return!1}})):t}dbList(t){const e=String(t.collection);if(this.manifest&&!(this.manifest.collections??[]).some(c=>c.name===e))return{ok:!1,error:"not_found",message:`Collection [${e}] is not declared in the manifest.`};let s=this.filter(this.records(e),t.where);if(typeof t.orderBy=="string"){const c=t.orderBy,l=t.direction==="desc"?-1:1;s=[...s].sort((D,d)=>{const b=c.split(".").reduce((m,h)=>m?.[h],D.data),v=c.split(".").reduce((m,h)=>m?.[h],d.data);return b<v?-l:b>v?l:0})}const r=Math.min(Number(t.limit??50),100),i=t.cursor?Number(t.cursor):0,a=s.slice(i,i+r),o=i+r<s.length?String(i+r):null;return{ok:!0,data:{records:a,nextCursor:o}}}dbCount(t){return{ok:!0,data:{count:this.filter(this.records(String(t.collection)),t.where).length}}}files(){return this.store.get(this.key("files"))??[]}storagePut(t){const e=String(t.upload),s=(this.manifest?.uploads??[]).find(c=>c.name===e);if(this.manifest&&!s)return{ok:!1,error:"not_found",message:`Upload [${e}] is not declared in the manifest.`};const r=String(t.mime??"");if(s?.mime&&s.mime.length>0&&!s.mime.some(c=>c===r||c.endsWith("/*")&&r.startsWith(c.slice(0,-1))))return{ok:!1,error:"validation",message:`MIME type [${r}] is not allowed for upload [${e}].`};const i=Number(t.bytes??0);if(s?.max_bytes&&i>s.max_bytes)return{ok:!1,error:"quota_exceeded",message:`File is ${i} bytes; the limit is ${s.max_bytes}.`};const a={id:`file-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,7)}`,upload:e,name:String(t.name??"blob"),mime:r,bytes:i,status:"clean",created_at:new Date().toISOString()},o=t.file;return typeof URL<"u"&&typeof URL.createObjectURL=="function"&&o instanceof Blob&&this.blobs.set(a.id,URL.createObjectURL(o)),this.store.set(this.key("files"),[...this.files(),a]),{ok:!0,data:{file:a}}}storageUrl(t){const e=this.files().find(s=>s.id===t.id);return e?{ok:!0,data:{url:this.blobs.get(e.id)??`mock://files/${e.id}`,expires_at:new Date(Date.now()+6e5).toISOString()}}:{ok:!1,error:"not_found"}}storageList(t){return{ok:!0,data:{files:this.files().filter(s=>!t.upload||s.upload===t.upload)}}}storageDelete(t){return this.store.set(this.key("files"),this.files().filter(e=>e.id!==t.id)),{ok:!0,data:{id:t.id}}}prompt(t){return(this.manifest?.prompts??[]).find(e=>e.id===t)??(this.manifest?null:{id:t,mode:"analysis"})}async aiAsk(t){const e=this.prompt(String(t.prompt));if(!e)return{ok:!1,error:"not_found",message:`Prompt [${t.prompt}] is not declared in the manifest.`};const s=this.aiHandlers.get(e.id),r=t.vars??{};return{ok:!0,data:{output:s?await s(r,"analysis"):f(e.output_schema,r),usage:{input_tokens:42,output_tokens:17}}}}async aiChat(t){const e=this.prompt(String(t.prompt));if(!e)return{ok:!1,error:"not_found",message:`Prompt [${t.prompt}] is not declared in the manifest.`};const s=String(t.message??""),r=this.chatHistory.get(e.id)??[];r.push({role:"user",content:s});const i=this.aiHandlers.get(e.id),a=i?String(await i({history:r},"chat",s)):`[mock ${e.id}] You said: ${s}`;r.push({role:"assistant",content:a}),this.chatHistory.set(e.id,r);const o=a.split(" "),c=`msg-${Date.now().toString(36)}`;return{stream:(async function*(){for(let l=0;l<o.length;l++)yield{delta:(l===0?"":" ")+o[l]}})(),final:()=>({message_id:c,text:a,usage:{input_tokens:s.length,output_tokens:a.length}})}}member(){return{id:this.participantId,name:this.participantName,role:this.role,present:!0,joinedAt:this.joinedAt}}members(){const t=Date.now(),e=[...this.roomMembers.values()].filter(i=>t-i.lastSeen<15e3).map(({lastSeen:i,joinedAt:a,...o})=>o),{joinedAt:s,...r}=this.member();return[r,...e].sort((i,a)=>i.id-a.id)}stateSnapshot(t){const e={};for(const[s,r]of Object.entries(this.roomState))(!t||t.includes(s))&&(e[s]=r.value);return e}send(t){this.channel?.postMessage({...t,sender:this.participantId})}joinRoom(){if(this.channel)return;const t=this.manifest?.broadcast?.mock_mode??"first_in";this.role=this.options.role??(t==="first_in"?"facilitator":"participant"),!(typeof BroadcastChannel>"u")&&(this.channel=new BroadcastChannel(this.key("room")),this.channel.onmessage=e=>this.onRoomMessage(e.data),this.send({t:"hello",from:this.member()}),this.heartbeat=setInterval(()=>this.send({t:"here",from:this.member()}),5e3))}reconcileFacilitator(t){t.role==="facilitator"&&this.role==="facilitator"&&(t.joinedAt??0)<this.joinedAt&&(this.role="participant",this.pushEvent({channel:"$room",event:"role",payload:{role:"participant",participant_id:this.participantId}}))}onRoomMessage(t){const e=t.from;if(t.sender!==this.participantId)switch(t.t){case"hello":e&&(this.roomMembers.set(e.id,{...e,lastSeen:Date.now()}),this.reconcileFacilitator(e),this.send({t:"here",from:this.member()}),this.send({t:"state-sync",state:this.roomState}),this.pushEvent({channel:"$room",event:"presence",payload:{members:this.members()}}));break;case"here":if(e){const s=this.roomMembers.has(e.id);this.roomMembers.set(e.id,{...e,lastSeen:Date.now()}),this.reconcileFacilitator(e),s||this.pushEvent({channel:"$room",event:"presence",payload:{members:this.members()}})}break;case"bye":e&&(this.roomMembers.delete(e.id),this.pushEvent({channel:"$room",event:"presence",payload:{members:this.members()}}));break;case"event":this.pushEvent({channel:String(t.channel),event:String(t.event),payload:t.payload,from:{participant_id:Number(t.sender),role:e?.role??"participant"}});break;case"state":{const s=String(t.key),r=Number(t.version);(this.roomState[s]?.version??0)<=r&&(this.roomState[s]={value:t.value,version:r},this.pushEvent({channel:"$room",event:"state",payload:{key:s,value:t.value,version:r}}));break}case"state-sync":for(const[s,r]of Object.entries(t.state))(this.roomState[s]?.version??0)<r.version&&(this.roomState[s]=r);break;case"role":e&&(this.roomMembers.set(e.id,{...e,lastSeen:Date.now()}),e.role==="facilitator"&&this.role==="facilitator"&&(this.role="participant",this.pushEvent({channel:"$room",event:"role",payload:{role:"participant",participant_id:this.participantId}})),this.pushEvent({channel:"$room",event:"role",payload:{role:e.role,participant_id:e.id}}),this.pushEvent({channel:"$room",event:"presence",payload:{members:this.members()}}));break}}setRole(t){this.role=t,this.send({t:"role",from:this.member()}),this.pushEvent({channel:"$room",event:"role",payload:{role:t,participant_id:this.participantId}})}publish(t){const e=String(t.event);if((this.manifest?.broadcast?.facilitator_events??[]).includes(e)&&this.role!=="facilitator")return{ok:!1,error:"forbidden",message:`Event [${e}] may only be sent by the facilitator.`};const r={participant_id:this.participantId,role:this.role};return this.send({t:"event",channel:t.channel,event:e,payload:t.payload,from:this.member()}),this.pushEvent({channel:String(t.channel??"main"),event:e,payload:t.payload,from:r}),{ok:!0}}stateSet(t){const e=String(t.key),s=(this.roomState[e]?.version??0)+1;return this.roomState[e]={value:t.value,version:s},this.send({t:"state",key:e,value:t.value,version:s}),this.pushEvent({channel:"$room",event:"state",payload:{key:e,value:t.value,version:s}}),{ok:!0,data:{version:s}}}claim(){return this.setRole("facilitator"),{ok:!0,data:{role:"facilitator"}}}transfer(t){if(this.role!=="facilitator")return{ok:!1,error:"forbidden",message:"Only the facilitator can transfer the role."};const e=this.roomMembers.get(Number(t.to));return e?(this.role="participant",e.role="facilitator",this.send({t:"role",from:{...e}}),this.pushEvent({channel:"$room",event:"role",payload:{role:"participant",participant_id:this.participantId}}),this.pushEvent({channel:"$room",event:"role",payload:{role:"facilitator",participant_id:e.id}}),{ok:!0}):{ok:!1,error:"not_found"}}}function f(n,t){if(!n)return{summary:`Mock analysis of ${JSON.stringify(t).slice(0,60)}`};const e=n.type;if(e==="object"){const s={};for(const[r,i]of Object.entries(n.properties??{}))s[r]=f(i,t);return s}return e==="array"?[f(n.items,t)]:e==="number"||e==="integer"?typeof n.minimum=="number"?n.minimum:3:e==="boolean"?!0:Array.isArray(n.enum)?n.enum[0]:"mock"}class k{constructor(t){this.rack=t,this.kind="mock"}async send(t,e,s={}){const r=await this.rack.handle(t,e??{});if("stream"in r){for await(const i of r.stream)s.onPartial?.(i);return{ok:!0,data:r.final()}}return r}fire(t,e={}){this.rack.handle(t,e??{})}onEvent(t){return this.rack.onEvent(t)}dispose(){this.rack.dispose()}}class B{get(t){try{const e=window.localStorage.getItem(t);return e===null?null:JSON.parse(e)}catch{return null}}set(t,e){try{window.localStorage.setItem(t,JSON.stringify(e))}catch{}}remove(t){try{window.localStorage.removeItem(t)}catch{}}keys(t){try{const e=[];for(let s=0;s<window.localStorage.length;s++){const r=window.localStorage.key(s);r&&r.startsWith(t)&&e.push(r)}return e}catch{return[]}}}const K=3e3,J=5e3;class A{constructor(){this.transport=null,this.initPromise=null,this.launchData=null,this.queue=[],this.launchListeners=new Set,this.eventListeners=new Set,this.pendingAiMocks=[];const t={send:(e,s,r)=>this.send(e,s,r),fire:(e,s)=>this.fire(e,s),launch:()=>this.launchData,declares:e=>this.declares(e),onEvent:e=>this.on("event",e),mode:()=>this.mode};this.auth=new O(t),this.attempt=new L(t),this.activity=new R(t),this.db=new U(t),this.storage=new N(t),this.ai=new I(t),this.broadcast=new q(t),this.media=new P(t),this.mock={ai:(e,s)=>{const r=this.mockRack();r?r.mockAi(e,s):this.pendingAiMocks.push([e,s])},resetAttempt:()=>this.mockRack()?.resetAttempt(),rack:()=>this.mockRack()}}init(t={}){return this.initPromise??=this.performInit(t),this.initPromise}get launched(){return this.launchData!==null}get mode(){return this.transport?.kind??"pending"}getLaunchData(){return this.launchData}declares(t){return(this.launchData?.capabilities??[]).some(e=>e.split("@")[0]===t)}on(t,e){if(t==="launch"){const r=e;return this.launchListeners.add(r),this.launchData&&r(this.launchData),()=>{this.launchListeners.delete(r)}}const s=e;return this.eventListeners.add(s),()=>{this.eventListeners.delete(s)}}eject(){this.fire("rack:eject",{}),this.transport?.kind==="mock"&&console.info("[ziprack mock] eject: the Rack would navigate away now.")}send(t,e,s){return this.launchData&&!t.startsWith("rack:")&&!this.declares(p(t))?Promise.resolve(w(p(t))):this.transport?this.transport.send(t,e,s):new Promise(r=>{this.queue.push({type:t,payload:e,options:s,resolve:r})})}fire(t,e={}){if(this.transport){this.transport.fire(t,e);return}this.send(t,e)}dispose(){this.transport?.dispose(),this.transport=null,this.initPromise=null,this.launchData=null}async performInit(t){const e=t.ackTimeoutMs??J,s=t.launchTimeoutMs??K;if(t.transport){const o=t.launch??(t.transport instanceof k?t.transport.rack.launchData():null);if(!o)throw new Error("init({ transport }) needs `launch` data unless the transport is a MockTransport.");return this.activate(t.transport,o)}if(!(typeof window<"u"&&window.parent!==window))return this.activateMock(t,"not embedded in a Rack");const i=new H({ackTimeoutMs:e});i.announceReady();const a=await i.waitForLaunch(s);return a?this.activate(i,a):(i.dispose(),this.activateMock(t,`no rack:launch within ${s}ms`))}async activateMock(t,e){console.info(`[ziprack] running in mock mode (${e})`);const s=await W(t.manifestUrl??"./manifest.json",t.slug),r=new F(s,new B,{log:(i,a)=>console.info(`[ziprack mock] ${i}`,a)});for(const[i,a]of this.pendingAiMocks)r.mockAi(i,a);return this.pendingAiMocks=[],this.activate(new k(r),r.launchData(t.slug))}activate(t,e){return this.transport=t,this.launchData=e,t.onEvent(s=>this.eventListeners.forEach(r=>r(s))),this.launchListeners.forEach(s=>s(e)),this.flushQueue(),e}flushQueue(){if(this.transport)for(;this.queue.length>0;){const e=this.queue.shift();this.send(e.type,e.payload,e.options).then(e.resolve)}}mockRack(){return this.transport instanceof k?this.transport.rack:null}}async function W(n,t){if(typeof fetch!="function")return null;try{const e=await fetch(n,{cache:"no-store"});if(!e.ok)throw new Error(String(e.status));const s=await e.json();return t&&s.slug!==t&&console.warn(`[ziprack mock] manifest slug [${s.slug}] does not match init({ slug: '${t}' }).`),s}catch(e){return console.warn(`[ziprack mock] could not load ${n}; mock mode will accept every capability.`,e),null}}const g=new A;function $(){return new A}return window.Rack=g,window.ZipRack={Rack:g,createRack:$},u.Rack=g,u.createRack=$,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"}),u})({});
1
+ var ZipRackSdk=(function(u){"use strict";class R{constructor(t){this.link=t}activities(){return this.link.launch()?.activity?.activities??[]}activity(t){return this.activities().find(e=>e.id===t)??null}statement(t){return this.link.send("activity:statement",{...t,occurred_at:t.occurred_at??new Date().toISOString()})}answer(t,e,s={}){return this.statement({verb:"answered",activity:{id:t,type:s.type??this.activity(t)?.type},result:{response:e},context:s.context})}progress(t,e){return this.statement({verb:"progressed",activity:{id:t,type:"step"},context:e})}}class E{constructor(t,e){this.link=t,this.promptId=e}async send(t,e){let s="";const r=await this.link.send("ai:chat",{prompt:this.promptId,message:t},{timeoutMs:6e4,onPartial:i=>{const a=typeof i.delta=="string"?i.delta:"";s+=a,e?.(a,s)}});return r.ok?{ok:!0,data:{message_id:String(r.data?.message_id??""),text:r.data?.text??s,usage:r.data?.usage??{input_tokens:0,output_tokens:0}}}:r}reset(){return this.link.send("ai:chat.reset",{prompt:this.promptId})}}class I{constructor(t){this.link=t}prompts(){return this.link.launch()?.ai?.prompts??[]}ask(t,e={}){return this.link.send("ai:ask",{prompt:t,vars:e},{timeoutMs:6e4})}chat(t){return new E(this.link,t)}}class L{constructor(t){this.link=t,this.local={}}current(){const t=this.link.launch()?.attempt;return t?{...t,...this.local}:null}get status(){return this.current()?.status??null}get id(){return this.current()?.id??null}state(){return this.current()?.state??null}segment(){return this.current()?.segment??null}setState(t){return this.local.state=t,this.link.send("attempt:state",{state:t})}setSegment(t){return this.local.segment=t,this.link.send("attempt:segment",{segment:t})}async complete(t){const e=await this.link.send("attempt:complete",{summary:t??null});return e.ok&&(this.local.status="completed"),e}}class O{constructor(t){this.link=t}user(){return this.link.launch()?.auth?.user??null}attribute(t,e=null){const s=this.user();return!s||!(t in s)?e:s[t]}}const j="$room";class T{constructor(t,e){this.link=t,this.name=e,this.listeners=new Map,this.unsubscribe=null}attach(){this.unsubscribe??=this.link.onEvent(t=>{t.channel===this.name&&(this.listeners.get(t.event)?.forEach(e=>e(t.payload,t.from)),this.listeners.get("*")?.forEach(e=>e({event:t.event,payload:t.payload},t.from)))})}listen(t,e){const s=this.listeners.get(t)??new Set;return s.add(e),this.listeners.set(t,s),()=>{s.delete(e)}}publish(t,e={}){return this.link.send("broadcast:publish",{channel:this.name,event:t,payload:e})}async leave(){return this.unsubscribe?.(),this.unsubscribe=null,this.listeners.clear(),this.link.send("broadcast:leave",{channel:this.name})}}class C{constructor(t){this.link=t,this.channels=new Map,this.roomListeners=new Map,this.localRole=null,this.localMembers=null,this.localState=null,this.detach=null,this.state={get:async e=>{const s=await this.link.send("broadcast:state.get",e?{keys:e}:{});return s.ok&&s.data&&(this.localState={...this.localState??{},...s.data.state}),s},set:(e,s)=>(this.localState={...this.localState??this.launch()?.state??{},[e]:s},this.link.send("broadcast:state.set",{key:e,value:s})),snapshot:()=>this.localState??this.launch()?.state??{}}}get room(){return this.launch()?.room??null}get role(){return this.localRole??this.launch()?.role??null}get isFacilitator(){return this.role==="facilitator"}members(){return this.localMembers??this.launch()?.members??[]}async join(t="main"){this.ensureRoomListener();let e=this.channels.get(t);return e||(e=new T(this.link,t),this.channels.set(t,e)),e.attach(),await this.link.send("broadcast:join",{channel:t}),e}async presence(){const t=await this.link.send("broadcast:presence",{});return t.ok&&t.data&&(this.localMembers=t.data.members),t}invite(){return this.link.send("broadcast:invite",{})}claim(){return this.link.send("broadcast:claim",{})}transfer(t){return this.link.send("broadcast:transfer",{to:t})}on(t,e){this.ensureRoomListener();const s=this.roomListeners.get(t)??new Set;return s.add(e),this.roomListeners.set(t,s),()=>{s.delete(e)}}launch(){return this.link.launch()?.broadcast??null}ensureRoomListener(){this.detach??=this.link.onEvent(t=>{if(t.channel!==j)return;const e=t.payload;t.event==="presence"&&Array.isArray(e.members)&&(this.localMembers=e.members),t.event==="state"&&typeof e.key=="string"&&(this.localState={...this.localState??this.launch()?.state??{},[e.key]:e.value}),t.event==="role"&&e.participant_id===this.link.launch()?.actor.id&&(this.localRole=e.role),this.roomListeners.get(t.event)?.forEach(s=>s(e))})}}function w(n){return{ok:!1,error:"undeclared_capability",message:`The manifest does not declare the [${n}] capability.`}}class q{constructor(t,e){this.link=t,this.name=e}create(t){return this.send("db:create",{data:t})}get(t){return this.send("db:get",{id:t})}update(t,e,s={}){return this.send("db:update",{id:t,data:e,merge:s.merge??!0})}delete(t){return this.send("db:delete",{id:t})}list(t={}){return this.send("db:list",t)}count(t={}){return this.send("db:count",t)}send(t,e){return this.link.launch()&&!this.link.declares("db")?Promise.resolve(w("db")):this.link.send(t,{collection:this.name,...e})}}class U{constructor(t){this.link=t}collection(t){return new q(this.link,t)}collections(){return this.link.launch()?.db?.collections??[]}}class P{constructor(t){this.link=t}get(t){return this.link.launch()?.media?.[t]??null}embedUrl(t){return this.get(t)?.embedUrl??null}all(){return this.link.launch()?.media??{}}}class N{constructor(t){this.link=t}uploads(){return this.link.launch()?.storage?.uploads??[]}put(t,e,s={}){const r=e instanceof File?e.name:"blob";return this.link.send("storage:put",{upload:t,file:e,name:r,mime:e.type,bytes:e.size,meta:s},{timeoutMs:12e4})}url(t){return this.link.send("storage:url",{id:t})}list(t){return this.link.send("storage:list",t?{upload:t}:{})}delete(t){return this.link.send("storage:delete",{id:t})}}const y="ziprack",S=1;let _=0;function x(){const n=typeof globalThis.crypto<"u"?globalThis.crypto:void 0;return n&&typeof n.randomUUID=="function"?n.randomUUID():(_+=1,`req-${Date.now().toString(36)}-${_.toString(36)}`)}function M(n,t,e){const s={ns:y,v:S,type:n,payload:t};return e!==void 0&&(s.requestId=e),s}function z(n){if(typeof n!="object"||n===null)return!1;const t=n;return t.ns===y&&t.v===S&&typeof t.type=="string"}function p(n){const t=n.indexOf(":");return t===-1?n:n.slice(0,t)}class H{constructor(t){this.options=t,this.kind="host",this.pending=new Map,this.eventListeners=new Set,this.launchResolvers=[],this.launchData=null,this.listener=e=>this.onMessage(e),this.target=t.target??window.parent,this.isHost=t.isHost??(e=>e===window.parent),this.listenOn=t.listenOn??window,this.listenOn.addEventListener("message",this.listener)}announceReady(){this.fire("rack:ready",{})}waitForLaunch(t){return this.launchData?Promise.resolve(this.launchData):new Promise(e=>{const s=setTimeout(()=>{this.launchResolvers=this.launchResolvers.filter(i=>i!==r),e(null)},t),r=i=>{clearTimeout(s),e(i)};this.launchResolvers.push(r)})}send(t,e,s={}){const r=x(),i=s.timeoutMs??this.options.ackTimeoutMs;return new Promise(a=>{const o={resolve:a,timer:setTimeout(()=>this.expire(r),i),timeoutMs:i,onPartial:s.onPartial};this.pending.set(r,o),this.post(M(t,e,r))})}fire(t,e={}){this.post(M(t,e))}onEvent(t){return this.eventListeners.add(t),()=>{this.eventListeners.delete(t)}}dispose(){this.listenOn.removeEventListener("message",this.listener);for(const[t,e]of this.pending)clearTimeout(e.timer),e.resolve({ok:!1,error:"disposed"}),this.pending.delete(t)}expire(t){const e=this.pending.get(t);e&&(this.pending.delete(t),e.resolve({ok:!1,error:"timeout"}))}post(t){this.target.postMessage(t,"*")}onMessage(t){if(!this.isHost(t.source)||!z(t.data))return;const e=t.data;if(e.type==="rack:launch"){this.launchData=e.payload;const s=this.launchResolvers;this.launchResolvers=[],s.forEach(r=>r(this.launchData));return}if(e.type==="rack:event"){const s=e.payload;this.eventListeners.forEach(r=>r(s));return}e.type==="rack:ack"&&this.onAck(e.payload)}onAck(t){const e=this.pending.get(t.requestId);if(!e)return;if(!t.done&&t.data?.stream===!0){clearTimeout(e.timer),e.timer=setTimeout(()=>this.expire(t.requestId),e.timeoutMs);return}if(t.partial&&!t.done){const{requestId:b,ok:v,partial:m,...h}=t;e.onPartial?.(h),clearTimeout(e.timer),e.timer=setTimeout(()=>this.expire(t.requestId),e.timeoutMs);return}clearTimeout(e.timer),this.pending.delete(t.requestId);const{requestId:s,ok:r,data:i,error:a,message:o,details:c,partial:l,done:D,...d}=t;e.resolve({ok:r,data:i??(Object.keys(d).length?d:void 0),error:a,message:o,details:c})}}class F{constructor(t,e,s={}){this.manifest=t,this.store=e,this.options=s,this.statements=[],this.events=[],this.aiHandlers=new Map,this.chatHistory=new Map,this.blobs=new Map,this.eventListeners=new Set,this.roomMembers=new Map,this.roomState={},this.role="participant",this.joinedAt=Date.now()+Math.random(),this.channel=null,this.heartbeat=null,this.participantId=s.participantId??Math.floor(Math.random()*1e6)+1,this.participantName=s.participantName??"Dev User"}get slug(){return this.manifest?.slug??"applet"}key(...t){return`ziprack-mock:${this.slug}:${t.join(":")}`}declares(t){return this.manifest?(this.manifest.requires?.capabilities??[]).some(e=>e.split("@")[0]===t):!0}onEvent(t){return this.eventListeners.add(t),()=>{this.eventListeners.delete(t)}}pushEvent(t){this.events.push(t),this.eventListeners.forEach(e=>e(t))}launchData(t){const e=this.manifest,s={schema:"ziprack/1",actor:{id:this.participantId,name:this.participantName},applet:{slug:this.slug,name:`${e?.name??t??"Applet"} (mock)`,version:e?.version??"dev"},capabilities:e?.requires?.capabilities??[],media:Object.fromEntries((e?.media??[]).map(r=>[r.id,{provider:r.provider,embedUrl:r.provider==="vimeo"?`https://player.vimeo.com/video/${r.key}?dnt=1`:`https://www.youtube-nocookie.com/embed/${r.key}?rel=0`,meta:null}]))};if(this.declares("auth")){const r={user:{id:this.participantId,display_name:this.participantName}};for(const i of e?.requires?.profile??[])r.user[i]=r.user[i]??`mock-${i}`;s.auth=r}return this.declares("attempt")&&(s.attempt=this.attempt()),this.declares("activity")&&(s.activity={activities:[...e?.activities??[]].sort((r,i)=>(r.sequence??0)-(i.sequence??0))}),this.declares("db")&&(s.db={collections:(e?.collections??[]).map(r=>({name:r.name,scope:r.scope}))}),this.declares("storage")&&(s.storage={uploads:(e?.uploads??[]).map(r=>({name:r.name,scope:r.scope,mime:r.mime??[],max_bytes:r.max_bytes??10485760}))}),this.declares("ai")&&(s.ai={prompts:(e?.prompts??[]).map(r=>({id:r.id,mode:r.mode}))}),this.declares("broadcast")&&(this.joinRoom(),s.broadcast={room:{id:1,code:"MOCK01",name:`${this.slug} room`,status:"open",mode:e?.broadcast?.mock_mode??"first_in"},role:this.role,members:this.members(),state:this.stateSnapshot(),channels:e?.broadcast?.channels??["main"]}),s}mockAi(t,e){this.aiHandlers.set(t,e)}dispose(){this.heartbeat&&clearInterval(this.heartbeat),this.channel&&(this.send({t:"bye",from:this.member()}),this.channel.close())}async handle(t,e){if(this.options.log?.(t,e),t==="rack:eject")return{ok:!0};const s=p(t);if((this.options.strict??this.manifest!==null)&&!this.declares(s))return{ok:!1,error:"undeclared_capability",message:`The manifest does not declare the [${s}] capability.`};switch(t){case"attempt:state":return this.attemptState(e);case"attempt:segment":return this.attemptSegment(e);case"attempt:complete":return this.attemptComplete(e);case"activity:statement":return this.statement(e);case"db:create":return this.dbCreate(e);case"db:get":return this.dbGet(e);case"db:update":return this.dbUpdate(e);case"db:delete":return this.dbDelete(e);case"db:list":return this.dbList(e);case"db:count":return this.dbCount(e);case"storage:put":return this.storagePut(e);case"storage:url":return this.storageUrl(e);case"storage:list":return this.storageList(e);case"storage:delete":return this.storageDelete(e);case"ai:ask":return this.aiAsk(e);case"ai:chat":return this.aiChat(e);case"ai:chat.reset":return this.chatHistory.delete(String(e.prompt)),{ok:!0};case"broadcast:join":return{ok:!0,data:{members:this.members(),state:this.stateSnapshot()}};case"broadcast:leave":return{ok:!0};case"broadcast:publish":return this.publish(e);case"broadcast:state.get":return{ok:!0,data:{state:this.stateSnapshot(Array.isArray(e.keys)?e.keys:void 0)}};case"broadcast:state.set":return this.stateSet(e);case"broadcast:presence":return{ok:!0,data:{members:this.members()}};case"broadcast:invite":return this.invite();case"broadcast:claim":return this.claim();case"broadcast:transfer":return this.transfer(e);default:return{ok:!1,error:"unknown_type",message:`Unknown message type [${t}].`}}}attempt(){return this.store.get(this.key("attempt"))??{id:1,status:"in_progress",state:null,segment:null}}saveAttempt(t){this.store.set(this.key("attempt"),t)}attemptState(t){const e=this.attempt();if(e.status==="completed")return{ok:!1,error:"completed",message:"This attempt is already completed."};const s=JSON.stringify(t.state??{}).length;return s>65536?{ok:!1,error:"validation",message:`State is ${s} bytes; the limit is 65536.`}:(this.saveAttempt({...e,state:t.state??null}),{ok:!0})}attemptSegment(t){const e=this.attempt();return e.status==="completed"?{ok:!1,error:"completed"}:(this.saveAttempt({...e,segment:t.segment??null}),{ok:!0})}attemptComplete(t){const e=this.attempt(),s=new Date().toISOString();return e.status!=="completed"&&(this.saveAttempt({...e,status:"completed"}),this.declares("activity")&&this.statement({verb:"completed",activity:{id:"attempt",type:"attempt"},result:t.summary??null,occurred_at:s})),{ok:!0,data:{status:"completed",completed_at:s}}}resetAttempt(){this.store.remove(this.key("attempt"))}statement(t){const e=String(t.verb??"");if(!/^[a-z][a-z0-9_.:-]*$/.test(e)||e.length>100)return{ok:!1,error:"validation",message:`Invalid verb [${e}].`};if(!t.activity?.id)return{ok:!1,error:"validation",message:"activity.id is required."};const r=`mock-${Date.now().toString(36)}-${this.statements.length+1}`;return this.statements.push({id:r,...t}),{ok:!0,data:{id:r}}}collectionKey(t){return this.key("db",t)}records(t){return this.store.get(this.collectionKey(t))??[]}saveRecords(t,e){this.store.set(this.collectionKey(t),e)}collectionDeclared(t){return this.manifest&&!(this.manifest.collections??[]).some(s=>s.name===t)?{ok:!1,error:"not_found",message:`Collection [${t}] is not declared in the manifest.`}:(this.manifest?.collections??[]).find(s=>s.name===t)?.scope==="attempt"&&this.attempt().status==="completed"?{ok:!1,error:"completed",message:"Attempt-scoped records are frozen after completion."}:null}dbCreate(t){const e=String(t.collection),s=this.collectionDeclared(e);if(s)return s;const r=new Date().toISOString(),i={id:`rec-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,7)}`,data:t.data??{},owner_id:this.participantId,created_at:r,updated_at:r};return this.saveRecords(e,[...this.records(e),i]),{ok:!0,data:{record:i}}}dbGet(t){const e=this.records(String(t.collection)).find(s=>s.id===t.id);return e?{ok:!0,data:{record:e}}:{ok:!1,error:"not_found"}}dbUpdate(t){const e=String(t.collection),s=this.collectionDeclared(e);if(s&&s.error==="completed")return s;const r=this.records(e),i=r.findIndex(c=>c.id===t.id);if(i===-1)return{ok:!1,error:"not_found"};const a=t.merge!==!1,o=t.data??{};return r[i]={...r[i],data:a?{...r[i].data,...o}:o,updated_at:new Date().toISOString()},this.saveRecords(e,r),{ok:!0,data:{record:r[i]}}}dbDelete(t){const e=String(t.collection),s=this.records(e);return s.some(r=>r.id===t.id)?(this.saveRecords(e,s.filter(r=>r.id!==t.id)),{ok:!0,data:{id:t.id}}):{ok:!1,error:"not_found"}}filter(t,e){return e?t.filter(s=>e.every(([r,i,a])=>{const o=r.split(".").reduce((c,l)=>c?.[l],s.data);switch(i){case"=":return o===a;case"!=":return o!==a;case"<":return o<a;case"<=":return o<=a;case">":return o>a;case">=":return o>=a;case"in":return Array.isArray(a)&&a.includes(o);default:return!1}})):t}dbList(t){const e=String(t.collection);if(this.manifest&&!(this.manifest.collections??[]).some(c=>c.name===e))return{ok:!1,error:"not_found",message:`Collection [${e}] is not declared in the manifest.`};let s=this.filter(this.records(e),t.where);if(typeof t.orderBy=="string"){const c=t.orderBy,l=t.direction==="desc"?-1:1;s=[...s].sort((D,d)=>{const b=c.split(".").reduce((m,h)=>m?.[h],D.data),v=c.split(".").reduce((m,h)=>m?.[h],d.data);return b<v?-l:b>v?l:0})}const r=Math.min(Number(t.limit??50),100),i=t.cursor?Number(t.cursor):0,a=s.slice(i,i+r),o=i+r<s.length?String(i+r):null;return{ok:!0,data:{records:a,nextCursor:o}}}dbCount(t){return{ok:!0,data:{count:this.filter(this.records(String(t.collection)),t.where).length}}}files(){return this.store.get(this.key("files"))??[]}storagePut(t){const e=String(t.upload),s=(this.manifest?.uploads??[]).find(c=>c.name===e);if(this.manifest&&!s)return{ok:!1,error:"not_found",message:`Upload [${e}] is not declared in the manifest.`};const r=String(t.mime??"");if(s?.mime&&s.mime.length>0&&!s.mime.some(c=>c===r||c.endsWith("/*")&&r.startsWith(c.slice(0,-1))))return{ok:!1,error:"validation",message:`MIME type [${r}] is not allowed for upload [${e}].`};const i=Number(t.bytes??0);if(s?.max_bytes&&i>s.max_bytes)return{ok:!1,error:"quota_exceeded",message:`File is ${i} bytes; the limit is ${s.max_bytes}.`};const a={id:`file-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,7)}`,upload:e,name:String(t.name??"blob"),mime:r,bytes:i,status:"clean",created_at:new Date().toISOString()},o=t.file;return typeof URL<"u"&&typeof URL.createObjectURL=="function"&&o instanceof Blob&&this.blobs.set(a.id,URL.createObjectURL(o)),this.store.set(this.key("files"),[...this.files(),a]),{ok:!0,data:{file:a}}}storageUrl(t){const e=this.files().find(s=>s.id===t.id);return e?{ok:!0,data:{url:this.blobs.get(e.id)??`mock://files/${e.id}`,expires_at:new Date(Date.now()+6e5).toISOString()}}:{ok:!1,error:"not_found"}}storageList(t){return{ok:!0,data:{files:this.files().filter(s=>!t.upload||s.upload===t.upload)}}}storageDelete(t){return this.store.set(this.key("files"),this.files().filter(e=>e.id!==t.id)),{ok:!0,data:{id:t.id}}}prompt(t){return(this.manifest?.prompts??[]).find(e=>e.id===t)??(this.manifest?null:{id:t,mode:"analysis"})}async aiAsk(t){const e=this.prompt(String(t.prompt));if(!e)return{ok:!1,error:"not_found",message:`Prompt [${t.prompt}] is not declared in the manifest.`};const s=this.aiHandlers.get(e.id),r=t.vars??{};return{ok:!0,data:{output:s?await s(r,"analysis"):f(e.output_schema,r),usage:{input_tokens:42,output_tokens:17}}}}async aiChat(t){const e=this.prompt(String(t.prompt));if(!e)return{ok:!1,error:"not_found",message:`Prompt [${t.prompt}] is not declared in the manifest.`};const s=String(t.message??""),r=this.chatHistory.get(e.id)??[];r.push({role:"user",content:s});const i=this.aiHandlers.get(e.id),a=i?String(await i({history:r},"chat",s)):`[mock ${e.id}] You said: ${s}`;r.push({role:"assistant",content:a}),this.chatHistory.set(e.id,r);const o=a.split(" "),c=`msg-${Date.now().toString(36)}`;return{stream:(async function*(){for(let l=0;l<o.length;l++)yield{delta:(l===0?"":" ")+o[l]}})(),final:()=>({message_id:c,text:a,usage:{input_tokens:s.length,output_tokens:a.length}})}}member(){return{id:this.participantId,name:this.participantName,role:this.role,present:!0,joinedAt:this.joinedAt}}members(){const t=Date.now(),e=[...this.roomMembers.values()].filter(i=>t-i.lastSeen<15e3).map(({lastSeen:i,joinedAt:a,...o})=>o),{joinedAt:s,...r}=this.member();return[r,...e].sort((i,a)=>i.id-a.id)}stateSnapshot(t){const e={};for(const[s,r]of Object.entries(this.roomState))(!t||t.includes(s))&&(e[s]=r.value);return e}send(t){this.channel?.postMessage({...t,sender:this.participantId})}joinRoom(){if(this.channel)return;const t=this.manifest?.broadcast?.mock_mode??"first_in";this.role=this.options.role??(t==="first_in"?"facilitator":"participant"),!(typeof BroadcastChannel>"u")&&(this.channel=new BroadcastChannel(this.key("room")),this.channel.onmessage=e=>this.onRoomMessage(e.data),this.send({t:"hello",from:this.member()}),this.heartbeat=setInterval(()=>this.send({t:"here",from:this.member()}),5e3))}reconcileFacilitator(t){t.role==="facilitator"&&this.role==="facilitator"&&(t.joinedAt??0)<this.joinedAt&&(this.role="participant",this.pushEvent({channel:"$room",event:"role",payload:{role:"participant",participant_id:this.participantId}}))}onRoomMessage(t){const e=t.from;if(t.sender!==this.participantId)switch(t.t){case"hello":e&&(this.roomMembers.set(e.id,{...e,lastSeen:Date.now()}),this.reconcileFacilitator(e),this.send({t:"here",from:this.member()}),this.send({t:"state-sync",state:this.roomState}),this.pushEvent({channel:"$room",event:"presence",payload:{members:this.members()}}));break;case"here":if(e){const s=this.roomMembers.has(e.id);this.roomMembers.set(e.id,{...e,lastSeen:Date.now()}),this.reconcileFacilitator(e),s||this.pushEvent({channel:"$room",event:"presence",payload:{members:this.members()}})}break;case"bye":e&&(this.roomMembers.delete(e.id),this.pushEvent({channel:"$room",event:"presence",payload:{members:this.members()}}));break;case"event":this.pushEvent({channel:String(t.channel),event:String(t.event),payload:t.payload,from:{participant_id:Number(t.sender),role:e?.role??"participant"}});break;case"state":{const s=String(t.key),r=Number(t.version);(this.roomState[s]?.version??0)<=r&&(this.roomState[s]={value:t.value,version:r},this.pushEvent({channel:"$room",event:"state",payload:{key:s,value:t.value,version:r}}));break}case"state-sync":for(const[s,r]of Object.entries(t.state))(this.roomState[s]?.version??0)<r.version&&(this.roomState[s]=r);break;case"role":e&&(this.roomMembers.set(e.id,{...e,lastSeen:Date.now()}),e.role==="facilitator"&&this.role==="facilitator"&&(this.role="participant",this.pushEvent({channel:"$room",event:"role",payload:{role:"participant",participant_id:this.participantId}})),this.pushEvent({channel:"$room",event:"role",payload:{role:e.role,participant_id:e.id}}),this.pushEvent({channel:"$room",event:"presence",payload:{members:this.members()}}));break}}invite(){return{ok:!0,data:{code:"MOCK01",url:typeof location>"u"?"https://rack.test/r/MOCK01":location.origin+location.pathname}}}setRole(t){this.role=t,this.send({t:"role",from:this.member()}),this.pushEvent({channel:"$room",event:"role",payload:{role:t,participant_id:this.participantId}})}publish(t){const e=String(t.event);if((this.manifest?.broadcast?.facilitator_events??[]).includes(e)&&this.role!=="facilitator")return{ok:!1,error:"forbidden",message:`Event [${e}] may only be sent by the facilitator.`};const r={participant_id:this.participantId,role:this.role};return this.send({t:"event",channel:t.channel,event:e,payload:t.payload,from:this.member()}),this.pushEvent({channel:String(t.channel??"main"),event:e,payload:t.payload,from:r}),{ok:!0}}stateSet(t){const e=String(t.key),s=(this.roomState[e]?.version??0)+1;return this.roomState[e]={value:t.value,version:s},this.send({t:"state",key:e,value:t.value,version:s}),this.pushEvent({channel:"$room",event:"state",payload:{key:e,value:t.value,version:s}}),{ok:!0,data:{version:s}}}claim(){return this.setRole("facilitator"),{ok:!0,data:{role:"facilitator"}}}transfer(t){if(this.role!=="facilitator")return{ok:!1,error:"forbidden",message:"Only the facilitator can transfer the role."};const e=this.roomMembers.get(Number(t.to));return e?(this.role="participant",e.role="facilitator",this.send({t:"role",from:{...e}}),this.pushEvent({channel:"$room",event:"role",payload:{role:"participant",participant_id:this.participantId}}),this.pushEvent({channel:"$room",event:"role",payload:{role:"facilitator",participant_id:e.id}}),{ok:!0}):{ok:!1,error:"not_found"}}}function f(n,t){if(!n)return{summary:`Mock analysis of ${JSON.stringify(t).slice(0,60)}`};const e=n.type;if(e==="object"){const s={};for(const[r,i]of Object.entries(n.properties??{}))s[r]=f(i,t);return s}return e==="array"?[f(n.items,t)]:e==="number"||e==="integer"?typeof n.minimum=="number"?n.minimum:3:e==="boolean"?!0:Array.isArray(n.enum)?n.enum[0]:"mock"}class k{constructor(t){this.rack=t,this.kind="mock"}async send(t,e,s={}){const r=await this.rack.handle(t,e??{});if("stream"in r){for await(const i of r.stream)s.onPartial?.(i);return{ok:!0,data:r.final()}}return r}fire(t,e={}){this.rack.handle(t,e??{})}onEvent(t){return this.rack.onEvent(t)}dispose(){this.rack.dispose()}}class B{get(t){try{const e=window.localStorage.getItem(t);return e===null?null:JSON.parse(e)}catch{return null}}set(t,e){try{window.localStorage.setItem(t,JSON.stringify(e))}catch{}}remove(t){try{window.localStorage.removeItem(t)}catch{}}keys(t){try{const e=[];for(let s=0;s<window.localStorage.length;s++){const r=window.localStorage.key(s);r&&r.startsWith(t)&&e.push(r)}return e}catch{return[]}}}const K=3e3,J=5e3;class A{constructor(){this.transport=null,this.initPromise=null,this.launchData=null,this.queue=[],this.launchListeners=new Set,this.eventListeners=new Set,this.pendingAiMocks=[];const t={send:(e,s,r)=>this.send(e,s,r),fire:(e,s)=>this.fire(e,s),launch:()=>this.launchData,declares:e=>this.declares(e),onEvent:e=>this.on("event",e),mode:()=>this.mode};this.auth=new O(t),this.attempt=new L(t),this.activity=new R(t),this.db=new U(t),this.storage=new N(t),this.ai=new I(t),this.broadcast=new C(t),this.media=new P(t),this.mock={ai:(e,s)=>{const r=this.mockRack();r?r.mockAi(e,s):this.pendingAiMocks.push([e,s])},resetAttempt:()=>this.mockRack()?.resetAttempt(),rack:()=>this.mockRack()}}init(t={}){return this.initPromise??=this.performInit(t),this.initPromise}get launched(){return this.launchData!==null}get mode(){return this.transport?.kind??"pending"}getLaunchData(){return this.launchData}declares(t){return(this.launchData?.capabilities??[]).some(e=>e.split("@")[0]===t)}on(t,e){if(t==="launch"){const r=e;return this.launchListeners.add(r),this.launchData&&r(this.launchData),()=>{this.launchListeners.delete(r)}}const s=e;return this.eventListeners.add(s),()=>{this.eventListeners.delete(s)}}eject(){this.fire("rack:eject",{}),this.transport?.kind==="mock"&&console.info("[ziprack mock] eject: the Rack would navigate away now.")}send(t,e,s){return this.launchData&&!t.startsWith("rack:")&&!this.declares(p(t))?Promise.resolve(w(p(t))):this.transport?this.transport.send(t,e,s):new Promise(r=>{this.queue.push({type:t,payload:e,options:s,resolve:r})})}fire(t,e={}){if(this.transport){this.transport.fire(t,e);return}this.send(t,e)}dispose(){this.transport?.dispose(),this.transport=null,this.initPromise=null,this.launchData=null}async performInit(t){const e=t.ackTimeoutMs??J,s=t.launchTimeoutMs??K;if(t.transport){const o=t.launch??(t.transport instanceof k?t.transport.rack.launchData():null);if(!o)throw new Error("init({ transport }) needs `launch` data unless the transport is a MockTransport.");return this.activate(t.transport,o)}if(!(typeof window<"u"&&window.parent!==window))return this.activateMock(t,"not embedded in a Rack");const i=new H({ackTimeoutMs:e});i.announceReady();const a=await i.waitForLaunch(s);return a?this.activate(i,a):(i.dispose(),this.activateMock(t,`no rack:launch within ${s}ms`))}async activateMock(t,e){console.info(`[ziprack] running in mock mode (${e})`);const s=await W(t.manifestUrl??"./manifest.json",t.slug),r=new F(s,new B,{log:(i,a)=>console.info(`[ziprack mock] ${i}`,a)});for(const[i,a]of this.pendingAiMocks)r.mockAi(i,a);return this.pendingAiMocks=[],this.activate(new k(r),r.launchData(t.slug))}activate(t,e){return this.transport=t,this.launchData=e,t.onEvent(s=>this.eventListeners.forEach(r=>r(s))),this.launchListeners.forEach(s=>s(e)),this.flushQueue(),e}flushQueue(){if(this.transport)for(;this.queue.length>0;){const e=this.queue.shift();this.send(e.type,e.payload,e.options).then(e.resolve)}}mockRack(){return this.transport instanceof k?this.transport.rack:null}}async function W(n,t){if(typeof fetch!="function")return null;try{const e=await fetch(n,{cache:"no-store"});if(!e.ok)throw new Error(String(e.status));const s=await e.json();return t&&s.slug!==t&&console.warn(`[ziprack mock] manifest slug [${s.slug}] does not match init({ slug: '${t}' }).`),s}catch(e){return console.warn(`[ziprack mock] could not load ${n}; mock mode will accept every capability.`,e),null}}const g=new A;function $(){return new A}return window.Rack=g,window.ZipRack={Rack:g,createRack:$},u.Rack=g,u.createRack=$,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"}),u})({});
2
2
  //# sourceMappingURL=ziprack-sdk.iife.js.map