@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
@@ -0,0 +1,182 @@
1
+ # Building a zipRACK applet (ziprack/1)
2
+
3
+ You are building an **applet**: a self-contained static web app that a Rack hosts inside a sandboxed
4
+ iframe. The Rack owns login, persistence, AI access, real-time coordination and reporting. The applet
5
+ owns everything the participant sees. It never talks to a database, never calls an API, never holds a
6
+ credential. It asks the Rack over `postMessage` through `@ziprack/sdk`, and the Rack decides, enforces
7
+ and persists.
8
+
9
+ Follow this document exactly. The Rack rejects packages and messages that break it.
10
+
11
+ ## 1. Mental model
12
+
13
+ | Object | What it is |
14
+ |---|---|
15
+ | Applet | One experience or app, identified by an immutable `slug`. Has many versions. |
16
+ | Version | One uploaded zip. Immutable. Exactly one is current. |
17
+ | Attempt | One participant's run (`attempt@1`). Pinned to the version it started on. Holds `status`, `state`, `segment`. |
18
+ | Room | A shared session: an applet plus its participants (`broadcast@1`). Two roles: `participant`, `facilitator`. |
19
+ | Statement | Append-only "actor did verb on activity with result" (`activity@1`). The audit trail and the uniform export. |
20
+ | Record | A mutable row in a declared collection at a declared scope (`db@1`). Working state. |
21
+ | Profile attribute | A Rack-owned fact about the participant, released only if declared and permitted (`auth@1`). |
22
+
23
+ Rules that do not bend:
24
+
25
+ 1. **Declare every capability you call.** `requires.capabilities` is mandatory, not advisory. An undeclared call is refused at runtime with `undeclared_capability`.
26
+ 2. **Nothing persists inside the iframe.** No cookies, no `localStorage`, no `fetch` to the Rack. If it matters, send it through `Rack.*`.
27
+ 3. **Answers are statements, not state.** State is for resuming position. Every committed answer is `Rack.activity.answer()`.
28
+ 4. **Resume must work.** Read `Rack.attempt.state()` and `Rack.attempt.status` at launch and put the participant back where they were.
29
+ 5. **Degrade gracefully.** A requested profile attribute may be withheld. A room may have no facilitator yet. Design for absence.
30
+
31
+ ## 2. Package layout
32
+
33
+ ```
34
+ {slug}-{version}.zip
35
+ ├── manifest.json required, at the zip root
36
+ ├── sbom.json required: CycloneDX (or package-lock.json)
37
+ ├── index.html the entry (configurable via manifest.entry)
38
+ └── assets/… everything the entry references, by RELATIVE path
39
+ ```
40
+
41
+ Installer rules: static files only (`html js mjs css map json wasm txt vtt svg png jpg jpeg gif webp ico woff woff2 ttf mp3 mp4 wav webm ogg pdf`); no `..`, absolute paths or symlinks; dotfiles and `__MACOSX/` skipped; 50 MB default cap; `entry` must exist and end in `.html`; every asset URL relative (`base: './'` in Vite); a `version` uploads once per applet.
42
+
43
+ `npm run package` in the starter builds all of this, including the SBOM from your lockfile.
44
+
45
+ ## 3. manifest.json
46
+
47
+ ```json
48
+ {
49
+ "schema": "ziprack/1",
50
+ "slug": "my-applet",
51
+ "name": "My Applet",
52
+ "version": "1.0.0",
53
+ "entry": "index.html",
54
+ "requires": {
55
+ "capabilities": ["auth@1", "attempt@1", "activity@1"],
56
+ "profile": ["display_name", "locale"]
57
+ },
58
+ "media": [{ "id": "intro", "provider": "youtube", "key": "dQw4w9WgXcQ" }],
59
+ "segments": [],
60
+ "activities": [],
61
+ "reporting": { "metrics": [] }
62
+ }
63
+ ```
64
+
65
+ | Field | Rules |
66
+ |---|---|
67
+ | `schema` | Required, `ziprack/1`. |
68
+ | `slug` | Required. `^[a-z0-9]+(-[a-z0-9]+)*$`, max 100. Immutable. Launch URL `/a/{slug}`. |
69
+ | `name`, `version` | Required. Semver with optional prerelease. |
70
+ | `entry` | Optional, default `index.html`. Relative, ends in `.html`. |
71
+ | `requires.capabilities` | Required (may be empty). Each `id@version`; the Rack must implement every one. |
72
+ | `requires.profile` | Attribute keys, only with `auth@1`. Withheld keys are simply absent. |
73
+ | `media[]` | `provider` in the Rack's allowlist (vimeo, youtube); `key` `^\d+$` (Vimeo) or `^[\w-]{11}$` (YouTube). The Rack builds the URL. |
74
+
75
+ Every other top-level block belongs to a capability and is invalid unless that capability is declared:
76
+
77
+ | Block | Capability | Shape |
78
+ |---|---|---|
79
+ | `segments[]` | `attempt@1` | `{key, label, values[{key,label}]}`; the first segment drives admin filtering |
80
+ | `activities[]`, `reporting.metrics[]` | `activity@1` | as in cn-sim/1: `{id, type, label, scale?, segment?, sequence?}`; metrics pair a pre and post rating and need `attempt@1` |
81
+ | `collections[]` | `db@1` | `{name, scope, schema, indexed?, quota?, permissions?}` |
82
+ | `uploads[]` | `storage@1` | `{name, scope, mime[], max_bytes?, max_files?}` |
83
+ | `prompts[]` | `ai@1` | `{id, mode, template, vars?, output_schema?, budget?}` |
84
+ | `broadcast` | `broadcast@1` | `{channels?, facilitator_events?}` |
85
+
86
+ Scopes (`db@1`, `storage@1`): `attempt` (frozen at completion; needs `attempt@1`), `user` (outlives attempts), `room` (shared; needs `broadcast@1`), `applet` (per version, read-mostly).
87
+
88
+ ## 4. The SDK
89
+
90
+ ```ts
91
+ import { Rack } from '@ziprack/sdk';
92
+
93
+ const launch = await Rack.init({ slug: 'my-applet' }); // mock mode when not inside a Rack
94
+ ```
95
+
96
+ | Namespace | Calls | Notes |
97
+ |---|---|---|
98
+ | `Rack` | `init()`, `on('launch'|'event')`, `mode`, `eject()` | `eject()` needs no capability |
99
+ | `Rack.auth` | `user()`, `attribute(key, fallback)` | Released profile from launch data |
100
+ | `Rack.attempt` | `status`, `state()`, `segment()`, `setState()`, `setSegment()`, `complete()` | State max 64 KB; `complete()` idempotent; after it, state and segment are refused with `completed` |
101
+ | `Rack.activity` | `answer(id, value)`, `progress(stepId)`, `statement({...})` | Verbs `^[a-z][a-z0-9_.:-]*$`; answers put the value in `result.response` |
102
+ | `Rack.db.collection(name)` | `create`, `get`, `update`, `delete`, `list`, `count` | Filters and sorting only on `indexed` paths; 100 per page |
103
+ | `Rack.storage` | `put(upload, file)`, `url(id)`, `list()`, `delete(id)` | Files are addressable only once scanned clean |
104
+ | `Rack.ai` | `ask(promptId, vars)`, `chat(promptId).send(text, onDelta)`, `chat(promptId).reset()` | You supply variables, never instructions |
105
+ | `Rack.broadcast` | `join(channel)` → `{listen, publish, leave}`, `presence()`, `state.get/set`, `claim()`, `transfer(id)`, `on('presence'|'state'|'role'|'status')` | Facilitator-only events are declared in the manifest and enforced by the Rack |
106
+ | `Rack.media` | `embedUrl(id)` | Rack-built player URL |
107
+
108
+ Every call resolves to `{ ok, data?, error?, message? }` and never throws. Error codes: `timeout`,
109
+ `persist_failed`, `undeclared_capability`, `unknown_type`, `validation`, `forbidden`, `completed`,
110
+ `quota_exceeded`, `budget_exceeded`, `not_found`, `conflict`. Do not block the participant on acks;
111
+ `await` only what you must confirm (for example the final answer before `complete()`).
112
+
113
+ ### Mock mode
114
+
115
+ Outside a Rack (`npm run dev`), or if the Rack does not answer within 3 s, the SDK runs a local mock
116
+ that reads `./manifest.json` so declared capabilities, activities, collections and prompts behave as
117
+ they will in production. State and records persist in `localStorage` under `ziprack-mock:{slug}:*`.
118
+ Rooms use `BroadcastChannel`, so two tabs of your dev server form a room. `Rack.mock.ai(promptId, fn)`
119
+ answers prompts yourself; `Rack.mock.resetAttempt()` replays a completed attempt.
120
+
121
+ For tests, `import { FakeHost } from '@ziprack/sdk/testing'` and pass `host.transport()` to
122
+ `Rack.init({ transport })`; then inspect `host.statements`, `host.answers()`, `host.records()`.
123
+
124
+ ## 5. Studio, and its MCP server
125
+
126
+ If the project has an `applet.json` rather than hand-written source, it is a
127
+ [zipRACK Studio](studio.md) applet. That document is the applet: `index.html` and `manifest.json` are
128
+ compiled from it, and the manifest is **derived** from the blocks in the tree, so there is no
129
+ `requires.capabilities` to maintain and nothing that can drift. Editing the compiled output is
130
+ pointless — the next build overwrites it.
131
+
132
+ Studio ships an MCP server, and it is the right way to change such a project:
133
+
134
+ ```sh
135
+ npx ziprack-studio mcp # stdio, run in the project directory
136
+ ```
137
+
138
+ `ziprack_list_blocks` and `ziprack_get_block_schema` tell you what exists and what each block accepts;
139
+ `ziprack_compose` builds structure from a skeleton plus separate content maps; `ziprack_patch` makes
140
+ edits; `ziprack_validate` answers with per-key errors, each carrying the JSON Schema that would have
141
+ been valid. Every mutating tool takes `dry_run`, and every refusal explains itself, so you can converge
142
+ in one round trip instead of guessing.
143
+
144
+ Prefer it to writing `applet.json` directly. The tools go through the same registry, command bus and
145
+ validator as the editor, which is what keeps the result openable by the non-technical colleague who
146
+ will maintain it. When no block fits, write a real module and point a `custom.code` block at it: your
147
+ code runs, and the tree still has a node the editor can show and move.
148
+
149
+ ## 6. Runtime constraints
150
+
151
+ The Rack renders you with `sandbox="allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox"`
152
+ and no `allow-same-origin`: an opaque origin. Cookies, `localStorage`, `IndexedDB` and `fetch` to the
153
+ Rack all fail. `window.open` works. The entry document's CSP is:
154
+
155
+ | Directive | Allowed |
156
+ |---|---|
157
+ | `script-src` | `'self' 'unsafe-inline' 'unsafe-eval' blob:` — bundle every dependency |
158
+ | `style-src`, `font-src` | `'self'` + Bunny/Google fonts |
159
+ | `img-src`, `media-src` | `'self' data: blob:` + the deployment's allowlist. **Not `https:`.** |
160
+ | `connect-src` | `'self'` — no external APIs, no analytics |
161
+ | `frame-src` | Vimeo and YouTube players, via declared `media` only |
162
+
163
+ Ship your images and media inside the zip. A persistent Rack bar sits above the frame and cannot be
164
+ spoofed; never ask participants for credentials inside an applet.
165
+
166
+ ## 7. Publishing
167
+
168
+ Uploads land unpublished. Promoting a version is a deliberate admin action. A new version that adds a
169
+ capability or a profile attribute waits for explicit approval; adding or removing `attempt@1` changes
170
+ the applet from rolling to pinned (or back) and is flagged prominently. Known vulnerabilities from your
171
+ SBOM appear in the review queue. Changing an activity `id` between versions splits export columns;
172
+ changing a `label` only changes headers.
173
+
174
+ ## 8. Pre-upload checklist
175
+
176
+ - [ ] `schema: "ziprack/1"`, version bumped, `requires.capabilities` lists exactly what you call.
177
+ - [ ] `sbom.json` present (`npm run package` does it).
178
+ - [ ] Every question has an `activities[]` entry with a stable `id`, a `type`, a full-sentence `label`.
179
+ - [ ] `setState()` after every transition; refreshing mid-way in mock mode resumes.
180
+ - [ ] `complete()` exactly once; reloading a completed attempt shows the final screen.
181
+ - [ ] All URLs relative, no external `<script>`, no external images or APIs.
182
+ - [ ] Layout works at phone width and leaves the Rack bar alone.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { n as f, c as h, i as g, a as u, M as l, b as w, L as b } from "./store-DSV5q7C7.js";
2
- import { F as K, P as V, d as W } from "./store-DSV5q7C7.js";
1
+ import { n as f, c as h, i as g, a as u, M as l, b as w, L as b } from "./store-BXGon4iJ.js";
2
+ import { F as K, P as V, d as W } from "./store-BXGon4iJ.js";
3
3
  class v {
4
4
  constructor(t) {
5
5
  this.link = t;
@@ -189,6 +189,15 @@ class T {
189
189
  const t = await this.link.send("broadcast:presence", {});
190
190
  return t.ok && t.data && (this.localMembers = t.data.members), t;
191
191
  }
192
+ /**
193
+ * Where the next person scans to get in. Only the Rack knows its own host
194
+ * and join prefix, so it hands back the code and the URL; rendering them as
195
+ * a slide, a link or a QR is yours. Refused with `conflict` once the room is
196
+ * locked or closed, so a stale QR can be taken down.
197
+ */
198
+ invite() {
199
+ return this.link.send("broadcast:invite", {});
200
+ }
192
201
  /** Claim the facilitator role (opt_in rooms). */
193
202
  claim() {
194
203
  return this.link.send("broadcast:claim", {});
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/activity/index.ts","../src/ai/index.ts","../src/attempt/index.ts","../src/auth/index.ts","../src/broadcast/index.ts","../src/core/link.ts","../src/db/index.ts","../src/media/index.ts","../src/storage/index.ts","../src/core/bridge.ts","../src/core/rack.ts","../src/index.ts"],"sourcesContent":["import type { Link } from '../core/link';\nimport type { AckResult, JsonObject, LaunchActivity } from '../core/types';\n\nexport interface StatementInput {\n verb: string;\n activity: { id: string; type?: string };\n result?: JsonObject;\n context?: JsonObject;\n occurred_at?: string;\n}\n\nexport interface AnswerOptions {\n type?: string;\n context?: JsonObject;\n}\n\n/**\n * activity@1 — append-only statements. Answers are statements, not state.\n */\nexport class ActivityApi {\n public constructor(private readonly link: Link) {}\n\n /** The manifest's declared activities for this version. */\n public activities(): LaunchActivity[] {\n return this.link.launch()?.activity?.activities ?? [];\n }\n\n public activity(id: string): LaunchActivity | null {\n return this.activities().find((a) => a.id === id) ?? null;\n }\n\n public statement(input: StatementInput): Promise<AckResult<{ id: string }>> {\n return this.link.send<{ id: string }>('activity:statement', {\n ...input,\n occurred_at: input.occurred_at ?? new Date().toISOString(),\n });\n }\n\n /** `answered` statement with the value in result.response — the only field reporting reads. */\n public answer(activityId: string, response: unknown, options: AnswerOptions = {}): Promise<AckResult<{ id: string }>> {\n return this.statement({\n verb: 'answered',\n activity: { id: activityId, type: options.type ?? this.activity(activityId)?.type },\n result: { response },\n context: options.context,\n });\n }\n\n /** `progressed` statement with activity.type = 'step'. */\n public progress(stepId: string, context?: JsonObject): Promise<AckResult<{ id: string }>> {\n return this.statement({ verb: 'progressed', activity: { id: stepId, type: 'step' }, context });\n }\n}\n","import type { Link } from '../core/link';\nimport type { AckResult, JsonObject } from '../core/types';\n\nexport interface Usage {\n input_tokens: number;\n output_tokens: number;\n}\n\nexport interface ChatReply {\n message_id: string;\n text: string;\n usage: Usage;\n}\n\nexport class Chat {\n public constructor(private readonly link: Link, public readonly promptId: string) {}\n\n /**\n * Send a participant message. onDelta receives text as it streams; the\n * resolved result carries the full text.\n */\n public async send(message: string, onDelta?: (delta: string, soFar: string) => void): Promise<AckResult<ChatReply>> {\n let text = '';\n const result = await this.link.send<Partial<ChatReply>>('ai:chat', { prompt: this.promptId, message }, {\n timeoutMs: 60_000,\n onPartial: (chunk) => {\n const delta = typeof chunk.delta === 'string' ? chunk.delta : '';\n text += delta;\n onDelta?.(delta, text);\n },\n });\n\n if (!result.ok) {\n return result as AckResult<ChatReply>;\n }\n\n return {\n ok: true,\n data: {\n message_id: String(result.data?.message_id ?? ''),\n text: result.data?.text ?? text,\n usage: result.data?.usage ?? { input_tokens: 0, output_tokens: 0 },\n },\n };\n }\n\n public reset(): Promise<AckResult> {\n return this.link.send('ai:chat.reset', { prompt: this.promptId });\n }\n}\n\n/**\n * ai@1 — declared prompt templates. The applet supplies variables, never\n * instructions; model, prompt and budget are Rack-owned.\n */\nexport class AiApi {\n public constructor(private readonly link: Link) {}\n\n public prompts(): Array<{ id: string; mode: 'analysis' | 'chat' }> {\n return this.link.launch()?.ai?.prompts ?? [];\n }\n\n /** Analysis prompt: structured JSON conforming to the declared output_schema. */\n public ask<T extends object = JsonObject>(promptId: string, vars: JsonObject = {}): Promise<AckResult<{ output: T; usage: Usage }>> {\n return this.link.send<{ output: T; usage: Usage }>('ai:ask', { prompt: promptId, vars }, { timeoutMs: 60_000 });\n }\n\n /** Chat prompt: conversation history is held by the Rack. */\n public chat(promptId: string): Chat {\n return new Chat(this.link, promptId);\n }\n}\n","import type { Link } from '../core/link';\nimport type { AckResult, AttemptStatus, JsonObject, LaunchAttempt } from '../core/types';\n\n/**\n * attempt@1 — lifecycle, resume state, completion. Declaring it also pins\n * every participant to the version they started on.\n */\nexport class AttemptApi {\n private local: Partial<LaunchAttempt> = {};\n\n public constructor(private readonly link: Link) {}\n\n /** The attempt as launched, with local updates applied. */\n public current(): LaunchAttempt | null {\n const launched = this.link.launch()?.attempt;\n\n return launched ? { ...launched, ...this.local } : null;\n }\n\n public get status(): AttemptStatus | null {\n return this.current()?.status ?? null;\n }\n\n public get id(): number | null {\n return this.current()?.id ?? null;\n }\n\n /** Resume state exactly as last saved. */\n public state<T extends object = JsonObject>(): T | null {\n return (this.current()?.state as T | null) ?? null;\n }\n\n public segment(): Record<string, string> | null {\n return this.current()?.segment ?? null;\n }\n\n /** Save resume state (max 64 KB). Send after every transition; the shell coalesces bursts. */\n public setState(state: JsonObject): Promise<AckResult> {\n this.local.state = state;\n\n return this.link.send('attempt:state', { state });\n }\n\n /** Set segment dimensions (max 20 keys, string values). */\n public setSegment(segment: Record<string, string>): Promise<AckResult> {\n this.local.segment = segment;\n\n return this.link.send('attempt:segment', { segment });\n }\n\n /** Mark the attempt completed. Idempotent. */\n public async complete(summary?: JsonObject): Promise<AckResult<{ status: AttemptStatus; completed_at: string | null }>> {\n const result = await this.link.send<{ status: AttemptStatus; completed_at: string | null }>('attempt:complete', { summary: summary ?? null });\n if (result.ok) {\n this.local.status = 'completed';\n }\n\n return result;\n }\n}\n","import type { Link } from '../core/link';\nimport type { LaunchUser } from '../core/types';\n\n/**\n * auth@1 — who the participant is and the profile attributes the Rack chose to\n * release. Attributes the manifest requested but the Rack withheld are simply\n * absent; degrade gracefully.\n */\nexport class AuthApi {\n public constructor(private readonly link: Link) {}\n\n public user(): LaunchUser | null {\n return this.link.launch()?.auth?.user ?? null;\n }\n\n /** A released profile attribute, or the fallback when withheld. */\n public attribute<T = unknown>(key: string, fallback: T | null = null): T | null {\n const user = this.user();\n if (!user || !(key in user)) {\n return fallback;\n }\n\n return user[key] as T;\n }\n}\n","import type { Link } from '../core/link';\nimport type { AckResult, JsonObject, LaunchBroadcast, LaunchRoom, RackEvent, RoomMember, RoomRole, RoomStatus } from '../core/types';\n\nexport const ROOM_CHANNEL = '$room';\n\ntype Listener = (payload: unknown, from: RackEvent['from']) => void;\n\nexport type RoomEventName = 'presence' | 'state' | 'role' | 'status';\n\nexport interface RoomEvents {\n presence: { members: RoomMember[] };\n state: { key: string; value: unknown; version: number };\n role: { role: RoomRole; participant_id: number };\n status: { status: RoomStatus };\n}\n\nexport class Channel {\n private readonly listeners = new Map<string, Set<Listener>>();\n\n private unsubscribe: (() => void) | null = null;\n\n public constructor(private readonly link: Link, public readonly name: string) {}\n\n /** @internal */\n public attach(): void {\n this.unsubscribe ??= this.link.onEvent((event) => {\n if (event.channel !== this.name) {\n return;\n }\n this.listeners.get(event.event)?.forEach((l) => l(event.payload, event.from));\n this.listeners.get('*')?.forEach((l) => l({ event: event.event, payload: event.payload }, event.from));\n });\n }\n\n /** Listen for an event on this channel ('*' for all). Returns an unsubscribe function. */\n public listen<T = unknown>(event: string, callback: (payload: T, from: RackEvent['from']) => void): () => void {\n const set = this.listeners.get(event) ?? new Set<Listener>();\n set.add(callback as Listener);\n this.listeners.set(event, set);\n\n return () => {\n set.delete(callback as Listener);\n };\n }\n\n /** Publish to everyone in the room. Facilitator-only events are enforced by the Rack. */\n public publish(event: string, payload: unknown = {}): Promise<AckResult> {\n return this.link.send('broadcast:publish', { channel: this.name, event, payload });\n }\n\n public async leave(): Promise<AckResult> {\n this.unsubscribe?.();\n this.unsubscribe = null;\n this.listeners.clear();\n\n return this.link.send('broadcast:leave', { channel: this.name });\n }\n}\n\n/**\n * broadcast@1 — rooms, presence, facilitator control and shared state. The\n * Rack holds the WebSocket; the applet only ever sees postMessage.\n */\nexport class BroadcastApi {\n private readonly channels = new Map<string, Channel>();\n\n private readonly roomListeners = new Map<RoomEventName, Set<(payload: unknown) => void>>();\n\n private localRole: RoomRole | null = null;\n\n private localMembers: RoomMember[] | null = null;\n\n private localState: JsonObject | null = null;\n\n private detach: (() => void) | null = null;\n\n public constructor(private readonly link: Link) {}\n\n public get room(): LaunchRoom | null {\n return this.launch()?.room ?? null;\n }\n\n public get role(): RoomRole | null {\n return this.localRole ?? this.launch()?.role ?? null;\n }\n\n public get isFacilitator(): boolean {\n return this.role === 'facilitator';\n }\n\n public members(): RoomMember[] {\n return this.localMembers ?? this.launch()?.members ?? [];\n }\n\n /** Join a named channel within the room (default \"main\"). */\n public async join(channel = 'main'): Promise<Channel> {\n this.ensureRoomListener();\n let instance = this.channels.get(channel);\n if (!instance) {\n instance = new Channel(this.link, channel);\n this.channels.set(channel, instance);\n }\n instance.attach();\n await this.link.send('broadcast:join', { channel });\n\n return instance;\n }\n\n /** Who is in the room, by role. */\n public async presence(): Promise<AckResult<{ members: RoomMember[] }>> {\n const result = await this.link.send<{ members: RoomMember[] }>('broadcast:presence', {});\n if (result.ok && result.data) {\n this.localMembers = result.data.members;\n }\n\n return result;\n }\n\n public readonly state = {\n /** Server-arbitrated shared state. */\n get: async (keys?: string[]): Promise<AckResult<{ state: JsonObject }>> => {\n const result = await this.link.send<{ state: JsonObject }>('broadcast:state.get', keys ? { keys } : {});\n if (result.ok && result.data) {\n this.localState = { ...(this.localState ?? {}), ...result.data.state };\n }\n\n return result;\n },\n /** Last write wins at the key; the Rack stamps the version. */\n set: (key: string, value: unknown): Promise<AckResult<{ version: number }>> => {\n this.localState = { ...(this.localState ?? this.launch()?.state ?? {}), [key]: value };\n\n return this.link.send<{ version: number }>('broadcast:state.set', { key, value });\n },\n /** Locally known state (launch snapshot plus pushed updates). */\n snapshot: (): JsonObject => this.localState ?? this.launch()?.state ?? {},\n };\n\n /** Claim the facilitator role (opt_in rooms). */\n public claim(): Promise<AckResult<{ role: RoomRole }>> {\n return this.link.send('broadcast:claim', {});\n }\n\n /** Hand the facilitator role to another member. */\n public transfer(participantId: number): Promise<AckResult> {\n return this.link.send('broadcast:transfer', { to: participantId });\n }\n\n /** Room-level events: presence, state, role, status. */\n public on<K extends RoomEventName>(event: K, callback: (payload: RoomEvents[K]) => void): () => void {\n this.ensureRoomListener();\n const set = this.roomListeners.get(event) ?? new Set();\n set.add(callback as (payload: unknown) => void);\n this.roomListeners.set(event, set);\n\n return () => {\n set.delete(callback as (payload: unknown) => void);\n };\n }\n\n private launch(): LaunchBroadcast | null {\n return this.link.launch()?.broadcast ?? null;\n }\n\n private ensureRoomListener(): void {\n this.detach ??= this.link.onEvent((event) => {\n if (event.channel !== ROOM_CHANNEL) {\n return;\n }\n const payload = event.payload as JsonObject;\n if (event.event === 'presence' && Array.isArray(payload.members)) {\n this.localMembers = payload.members as RoomMember[];\n }\n if (event.event === 'state' && typeof payload.key === 'string') {\n this.localState = { ...(this.localState ?? this.launch()?.state ?? {}), [payload.key]: payload.value };\n }\n if (event.event === 'role' && payload.participant_id === this.link.launch()?.actor.id) {\n this.localRole = payload.role as RoomRole;\n }\n this.roomListeners.get(event.event as RoomEventName)?.forEach((l) => l(payload));\n });\n }\n}\n","import type { AckResult, JsonObject, LaunchData, RackEvent, SendOptions } from './types';\n\n/**\n * What a capability module needs from the client: send/fire, the launch data,\n * event subscription and the declared-capability check.\n */\nexport interface Link {\n send<T = JsonObject>(type: string, payload: unknown, options?: SendOptions): Promise<AckResult<T>>;\n fire(type: string, payload?: unknown): void;\n launch(): LaunchData | null;\n declares(capability: string): boolean;\n onEvent(listener: (event: RackEvent) => void): () => void;\n mode(): 'host' | 'mock' | 'pending';\n}\n\nexport function notDeclared<T = JsonObject>(capability: string): AckResult<T> {\n return { ok: false, error: 'undeclared_capability', message: `The manifest does not declare the [${capability}] capability.` };\n}\n","import { notDeclared, type Link } from '../core/link';\nimport type { AckResult, JsonObject } from '../core/types';\n\nexport type WhereOperator = '=' | '!=' | '<' | '<=' | '>' | '>=' | 'in';\n\nexport type WhereClause = [path: string, operator: WhereOperator, value: unknown];\n\nexport interface ListQuery {\n /** Filters on paths declared in the collection's `indexed` list. */\n where?: WhereClause[];\n orderBy?: string;\n direction?: 'asc' | 'desc';\n /** Max 100. */\n limit?: number;\n cursor?: string | null;\n}\n\nexport interface RecordRow<T extends object = JsonObject> {\n id: string;\n data: T;\n owner_id: number;\n created_at: string;\n updated_at: string;\n}\n\nexport interface ListResult<T extends object = JsonObject> {\n records: RecordRow<T>[];\n nextCursor: string | null;\n}\n\nexport class Collection<T extends object = JsonObject> {\n public constructor(private readonly link: Link, public readonly name: string) {}\n\n public create(data: T): Promise<AckResult<{ record: RecordRow<T> }>> {\n return this.send('db:create', { data });\n }\n\n public get(id: string): Promise<AckResult<{ record: RecordRow<T> }>> {\n return this.send('db:get', { id });\n }\n\n /** Merge by default; pass { merge: false } to replace the whole document. */\n public update(id: string, data: Partial<T>, options: { merge?: boolean } = {}): Promise<AckResult<{ record: RecordRow<T> }>> {\n return this.send('db:update', { id, data, merge: options.merge ?? true });\n }\n\n public delete(id: string): Promise<AckResult<{ id: string }>> {\n return this.send('db:delete', { id });\n }\n\n public list(query: ListQuery = {}): Promise<AckResult<ListResult<T>>> {\n return this.send('db:list', query);\n }\n\n public count(query: Pick<ListQuery, 'where'> = {}): Promise<AckResult<{ count: number }>> {\n return this.send('db:count', query);\n }\n\n private send<R>(type: string, payload: object): Promise<AckResult<R>> {\n if (this.link.launch() && !this.link.declares('db')) {\n return Promise.resolve(notDeclared<R>('db'));\n }\n\n return this.link.send<R>(type, { collection: this.name, ...payload });\n }\n}\n\n/**\n * db@1 — CRUD records in collections the manifest declares, each with a JSON\n * Schema and a scope (attempt | user | room | applet).\n */\nexport class DbApi {\n public constructor(private readonly link: Link) {}\n\n public collection<T extends object = JsonObject>(name: string): Collection<T> {\n return new Collection<T>(this.link, name);\n }\n\n public collections(): Array<{ name: string; scope: string }> {\n return this.link.launch()?.db?.collections ?? [];\n }\n}\n","import type { Link } from '../core/link';\nimport type { MediaEmbed } from '../core/types';\n\n/**\n * Declared media. The Rack builds every player URL; the applet references\n * media by id only. Needs no capability.\n */\nexport class MediaApi {\n public constructor(private readonly link: Link) {}\n\n public get(id: string): MediaEmbed | null {\n return this.link.launch()?.media?.[id] ?? null;\n }\n\n /** Player URL for an <iframe src>. Null when the id is not declared. */\n public embedUrl(id: string): string | null {\n return this.get(id)?.embedUrl ?? null;\n }\n\n public all(): Record<string, MediaEmbed> {\n return this.link.launch()?.media ?? {};\n }\n}\n","import type { Link } from '../core/link';\nimport type { AckResult } from '../core/types';\n\nexport type FileStatus = 'pending' | 'clean' | 'infected' | 'failed';\n\nexport interface StoredFile {\n id: string;\n upload: string;\n name: string;\n mime: string;\n bytes: number;\n status: FileStatus;\n created_at: string;\n}\n\n/**\n * storage@1 — participant uploads. The File travels to the shell by\n * structured clone; the Rack scans it before it becomes addressable.\n */\nexport class StorageApi {\n public constructor(private readonly link: Link) {}\n\n public uploads(): Array<{ name: string; scope: string; mime: string[]; max_bytes: number }> {\n return this.link.launch()?.storage?.uploads ?? [];\n }\n\n /** Upload into a declared upload slot. Resolves once stored (status may still be pending scan). */\n public put(upload: string, file: File | Blob, meta: Record<string, unknown> = {}): Promise<AckResult<{ file: StoredFile }>> {\n const name = file instanceof File ? file.name : 'blob';\n\n return this.link.send<{ file: StoredFile }>('storage:put', { upload, file, name, mime: file.type, bytes: file.size, meta }, { timeoutMs: 120_000 });\n }\n\n /** Short-lived URL for a clean file. Never an object key. */\n public url(id: string): Promise<AckResult<{ url: string; expires_at: string }>> {\n return this.link.send('storage:url', { id });\n }\n\n public list(upload?: string): Promise<AckResult<{ files: StoredFile[] }>> {\n return this.link.send('storage:list', upload ? { upload } : {});\n }\n\n public delete(id: string): Promise<AckResult<{ id: string }>> {\n return this.link.send('storage:delete', { id });\n }\n}\n","import { createEnvelope, isEnvelope, newRequestId, type Envelope } from './protocol';\nimport type { AckPayload, AckResult, JsonObject, LaunchData, RackEvent, SendOptions, Transport } from './types';\n\ninterface PendingRequest {\n resolve: (result: AckResult) => void;\n timer: ReturnType<typeof setTimeout>;\n timeoutMs: number;\n onPartial?: (chunk: JsonObject) => void;\n}\n\nexport interface BridgeOptions {\n ackTimeoutMs: number;\n /** Window-like object to post to. Defaults to window.parent. */\n target?: Pick<Window, 'postMessage'>;\n /** Whether a MessageEvent came from the host. Defaults to event.source === window.parent. */\n isHost?: (source: MessageEventSource | null) => boolean;\n /** Where to listen for messages. Defaults to window. */\n listenOn?: Pick<Window, 'addEventListener' | 'removeEventListener'>;\n}\n\n/**\n * postMessage transport used inside the Rack's iframe.\n *\n * Only messages whose source is the parent window are honoured; the applet\n * never inspects event.origin because its own origin is opaque by design.\n */\nexport class HostBridge implements Transport {\n public readonly kind = 'host' as const;\n\n private readonly pending = new Map<string, PendingRequest>();\n\n private readonly eventListeners = new Set<(event: RackEvent) => void>();\n\n private launchResolvers: Array<(data: LaunchData) => void> = [];\n\n private launchData: LaunchData | null = null;\n\n private readonly target: Pick<Window, 'postMessage'>;\n\n private readonly isHost: (source: MessageEventSource | null) => boolean;\n\n private readonly listenOn: Pick<Window, 'addEventListener' | 'removeEventListener'>;\n\n private readonly listener = (event: MessageEvent): void => this.onMessage(event);\n\n public constructor(private readonly options: BridgeOptions) {\n this.target = options.target ?? window.parent;\n this.isHost = options.isHost ?? ((source) => source === window.parent);\n this.listenOn = options.listenOn ?? window;\n this.listenOn.addEventListener('message', this.listener as EventListener);\n }\n\n public announceReady(): void {\n this.fire('rack:ready', {});\n }\n\n public waitForLaunch(timeoutMs: number): Promise<LaunchData | null> {\n if (this.launchData) {\n return Promise.resolve(this.launchData);\n }\n\n return new Promise((resolve) => {\n const timer = setTimeout(() => {\n this.launchResolvers = this.launchResolvers.filter((r) => r !== onLaunch);\n resolve(null);\n }, timeoutMs);\n const onLaunch = (data: LaunchData): void => {\n clearTimeout(timer);\n resolve(data);\n };\n this.launchResolvers.push(onLaunch);\n });\n }\n\n public send<T = JsonObject>(type: string, payload: unknown, options: SendOptions = {}): Promise<AckResult<T>> {\n const requestId = newRequestId();\n const timeoutMs = options.timeoutMs ?? this.options.ackTimeoutMs;\n\n return new Promise((resolve) => {\n const request: PendingRequest = {\n resolve: resolve as (result: AckResult) => void,\n timer: setTimeout(() => this.expire(requestId), timeoutMs),\n timeoutMs,\n onPartial: options.onPartial,\n };\n this.pending.set(requestId, request);\n this.post(createEnvelope(type, payload, requestId));\n });\n }\n\n public fire(type: string, payload: unknown = {}): void {\n this.post(createEnvelope(type, payload));\n }\n\n public onEvent(listener: (event: RackEvent) => void): () => void {\n this.eventListeners.add(listener);\n\n return () => {\n this.eventListeners.delete(listener);\n };\n }\n\n public dispose(): void {\n this.listenOn.removeEventListener('message', this.listener as EventListener);\n for (const [requestId, request] of this.pending) {\n clearTimeout(request.timer);\n request.resolve({ ok: false, error: 'disposed' });\n this.pending.delete(requestId);\n }\n }\n\n private expire(requestId: string): void {\n const request = this.pending.get(requestId);\n if (!request) {\n return;\n }\n this.pending.delete(requestId);\n request.resolve({ ok: false, error: 'timeout' });\n }\n\n private post(envelope: Envelope): void {\n this.target.postMessage(envelope, '*');\n }\n\n private onMessage(event: MessageEvent): void {\n if (!this.isHost(event.source) || !isEnvelope(event.data)) {\n return;\n }\n const envelope = event.data;\n\n if (envelope.type === 'rack:launch') {\n this.launchData = envelope.payload as LaunchData;\n const resolvers = this.launchResolvers;\n this.launchResolvers = [];\n resolvers.forEach((resolve) => resolve(this.launchData as LaunchData));\n\n return;\n }\n\n if (envelope.type === 'rack:event') {\n const payload = envelope.payload as RackEvent;\n this.eventListeners.forEach((listener) => listener(payload));\n\n return;\n }\n\n if (envelope.type === 'rack:ack') {\n this.onAck(envelope.payload as AckPayload);\n }\n }\n\n private onAck(ack: AckPayload): void {\n const request = this.pending.get(ack.requestId);\n if (!request) {\n return;\n }\n\n // A Rack that opens a stream acks the request twice: once to say the\n // stream is live, then once more when it finishes. Only the second one\n // settles the promise.\n if (!ack.done && (ack.data as { stream?: boolean } | undefined)?.stream === true) {\n clearTimeout(request.timer);\n request.timer = setTimeout(() => this.expire(ack.requestId), request.timeoutMs);\n\n return;\n }\n\n if (ack.partial && !ack.done) {\n const { requestId: _id, ok: _ok, partial: _partial, ...chunk } = ack;\n request.onPartial?.(chunk as JsonObject);\n clearTimeout(request.timer);\n request.timer = setTimeout(() => this.expire(ack.requestId), request.timeoutMs);\n\n return;\n }\n\n clearTimeout(request.timer);\n this.pending.delete(ack.requestId);\n\n const { requestId: _id, ok, data, error, message, details, partial: _partial, done: _done, ...rest } = ack;\n request.resolve({ ok, data: (data ?? (Object.keys(rest).length ? rest : undefined)) as JsonObject | undefined, error, message, details });\n }\n}\n","import { ActivityApi } from '../activity';\nimport { AiApi } from '../ai';\nimport { AttemptApi } from '../attempt';\nimport { AuthApi } from '../auth';\nimport { BroadcastApi } from '../broadcast';\nimport { DbApi } from '../db';\nimport { MediaApi } from '../media';\nimport { StorageApi } from '../storage';\nimport { HostBridge } from './bridge';\nimport { notDeclared, type Link } from './link';\nimport { MockRack, type AiMockHandler, type MockManifest } from './mock/MockRack';\nimport { MockTransport } from './mock/MockTransport';\nimport { LocalStore } from './mock/store';\nimport { capabilityOf } from './protocol';\nimport type { AckResult, InitOptions, JsonObject, LaunchData, LaunchListener, RackEvent, SendOptions, Transport } from './types';\n\nconst DEFAULT_LAUNCH_TIMEOUT_MS = 3000;\nconst DEFAULT_ACK_TIMEOUT_MS = 5000;\n\ninterface QueuedCall {\n type: string;\n payload: unknown;\n options?: SendOptions;\n resolve: (result: AckResult) => void;\n}\n\n/**\n * The SDK entry point. One instance per applet; `Rack` is the default singleton.\n *\n * Every capability is a message the Rack services. The applet asks; the Rack\n * decides, enforces and persists. Nothing here ever throws on a Rack error:\n * calls resolve to { ok: false, error }.\n */\nexport class RackClient {\n public readonly auth: AuthApi;\n\n public readonly attempt: AttemptApi;\n\n public readonly activity: ActivityApi;\n\n public readonly db: DbApi;\n\n public readonly storage: StorageApi;\n\n public readonly ai: AiApi;\n\n public readonly broadcast: BroadcastApi;\n\n public readonly media: MediaApi;\n\n /** Dev-only hooks that only take effect in mock mode. */\n public readonly mock: {\n /** Answer an ai prompt yourself instead of the schema stub. */\n ai: (promptId: string, handler: AiMockHandler) => void;\n /** Clear a completed mock attempt so the experience can be replayed. */\n resetAttempt: () => void;\n /** The MockRack when running standalone, else null. */\n rack: () => MockRack | null;\n };\n\n private transport: Transport | null = null;\n\n private initPromise: Promise<LaunchData> | null = null;\n\n private launchData: LaunchData | null = null;\n\n private readonly queue: QueuedCall[] = [];\n\n private readonly launchListeners = new Set<LaunchListener>();\n\n private readonly eventListeners = new Set<(event: RackEvent) => void>();\n\n private pendingAiMocks: Array<[string, AiMockHandler]> = [];\n\n public constructor() {\n const link: Link = {\n send: (type, payload, options) => this.send(type, payload, options),\n fire: (type, payload) => this.fire(type, payload),\n launch: () => this.launchData,\n declares: (capability) => this.declares(capability),\n onEvent: (listener) => this.on('event', listener),\n mode: () => this.mode,\n };\n\n this.auth = new AuthApi(link);\n this.attempt = new AttemptApi(link);\n this.activity = new ActivityApi(link);\n this.db = new DbApi(link);\n this.storage = new StorageApi(link);\n this.ai = new AiApi(link);\n this.broadcast = new BroadcastApi(link);\n this.media = new MediaApi(link);\n\n this.mock = {\n ai: (promptId, handler) => {\n const rack = this.mockRack();\n rack ? rack.mockAi(promptId, handler) : this.pendingAiMocks.push([promptId, handler]);\n },\n resetAttempt: () => this.mockRack()?.resetAttempt(),\n rack: () => this.mockRack(),\n };\n }\n\n /**\n * Handshake with the Rack. Resolves with launch data once the Rack answers,\n * or with mock launch data when running standalone. Safe to call repeatedly.\n */\n public init(options: InitOptions = {}): Promise<LaunchData> {\n this.initPromise ??= this.performInit(options);\n\n return this.initPromise;\n }\n\n public get launched(): boolean {\n return this.launchData !== null;\n }\n\n public get mode(): 'host' | 'mock' | 'pending' {\n return this.transport?.kind ?? 'pending';\n }\n\n public getLaunchData(): LaunchData | null {\n return this.launchData;\n }\n\n /** Whether the manifest declared a capability (any version). */\n public declares(capability: string): boolean {\n return (this.launchData?.capabilities ?? []).some((ref) => ref.split('@')[0] === capability);\n }\n\n public on(event: 'launch', listener: LaunchListener): () => void;\n\n public on(event: 'event', listener: (event: RackEvent) => void): () => void;\n\n public on(event: 'launch' | 'event', listener: LaunchListener | ((event: RackEvent) => void)): () => void {\n if (event === 'launch') {\n const l = listener as LaunchListener;\n this.launchListeners.add(l);\n if (this.launchData) {\n l(this.launchData);\n }\n\n return () => {\n this.launchListeners.delete(l);\n };\n }\n\n const l = listener as (event: RackEvent) => void;\n this.eventListeners.add(l);\n\n return () => {\n this.eventListeners.delete(l);\n };\n }\n\n /** Ask the Rack to leave the applet. Needs no capability. */\n public eject(): void {\n this.fire('rack:eject', {});\n if (this.transport?.kind === 'mock') {\n console.info('[ziprack mock] eject: the Rack would navigate away now.');\n }\n }\n\n /** Low-level: send any message and await its ack. Capability calls go through the typed APIs. */\n public send<T = JsonObject>(type: string, payload: unknown, options?: SendOptions): Promise<AckResult<T>> {\n if (this.launchData && !type.startsWith('rack:') && !this.declares(capabilityOf(type))) {\n return Promise.resolve(notDeclared<T>(capabilityOf(type)));\n }\n if (this.transport) {\n return this.transport.send<T>(type, payload, options);\n }\n\n return new Promise((resolve) => {\n this.queue.push({ type, payload, options, resolve: resolve as (result: AckResult) => void });\n });\n }\n\n public fire(type: string, payload: unknown = {}): void {\n if (this.transport) {\n this.transport.fire(type, payload);\n\n return;\n }\n void this.send(type, payload);\n }\n\n /** Tear down (tests). */\n public dispose(): void {\n this.transport?.dispose();\n this.transport = null;\n this.initPromise = null;\n this.launchData = null;\n }\n\n private async performInit(options: InitOptions): Promise<LaunchData> {\n const ackTimeoutMs = options.ackTimeoutMs ?? DEFAULT_ACK_TIMEOUT_MS;\n const launchTimeoutMs = options.launchTimeoutMs ?? DEFAULT_LAUNCH_TIMEOUT_MS;\n\n if (options.transport) {\n const launch = options.launch\n ?? (options.transport instanceof MockTransport ? options.transport.rack.launchData() : null);\n if (!launch) {\n throw new Error('init({ transport }) needs `launch` data unless the transport is a MockTransport.');\n }\n\n return this.activate(options.transport, launch);\n }\n\n const embedded = typeof window !== 'undefined' && window.parent !== window;\n\n if (!embedded) {\n return this.activateMock(options, 'not embedded in a Rack');\n }\n\n const bridge = new HostBridge({ ackTimeoutMs });\n bridge.announceReady();\n const launch = await bridge.waitForLaunch(launchTimeoutMs);\n\n if (!launch) {\n bridge.dispose();\n\n return this.activateMock(options, `no rack:launch within ${launchTimeoutMs}ms`);\n }\n\n return this.activate(bridge, launch);\n }\n\n private async activateMock(options: InitOptions, reason: string): Promise<LaunchData> {\n console.info(`[ziprack] running in mock mode (${reason})`);\n\n const manifest = await loadManifest(options.manifestUrl ?? './manifest.json', options.slug);\n const rack = new MockRack(manifest, new LocalStore(), {\n log: (type, payload) => console.info(`[ziprack mock] ${type}`, payload),\n });\n for (const [promptId, handler] of this.pendingAiMocks) {\n rack.mockAi(promptId, handler);\n }\n this.pendingAiMocks = [];\n\n return this.activate(new MockTransport(rack), rack.launchData(options.slug));\n }\n\n private activate(transport: Transport, launch: LaunchData): LaunchData {\n this.transport = transport;\n this.launchData = launch;\n transport.onEvent((event) => this.eventListeners.forEach((l) => l(event)));\n\n this.launchListeners.forEach((listener) => listener(launch));\n this.flushQueue();\n\n return launch;\n }\n\n private flushQueue(): void {\n const transport = this.transport;\n if (!transport) {\n return;\n }\n while (this.queue.length > 0) {\n const call = this.queue.shift() as QueuedCall;\n void this.send(call.type, call.payload, call.options).then(call.resolve);\n }\n }\n\n private mockRack(): MockRack | null {\n return this.transport instanceof MockTransport ? this.transport.rack : null;\n }\n}\n\nasync function loadManifest(url: string, slug?: string): Promise<MockManifest | null> {\n if (typeof fetch !== 'function') {\n return null;\n }\n try {\n const response = await fetch(url, { cache: 'no-store' });\n if (!response.ok) {\n throw new Error(String(response.status));\n }\n const manifest = (await response.json()) as MockManifest;\n if (slug && manifest.slug !== slug) {\n console.warn(`[ziprack mock] manifest slug [${manifest.slug}] does not match init({ slug: '${slug}' }).`);\n }\n\n return manifest;\n } catch (error) {\n console.warn(`[ziprack mock] could not load ${url}; mock mode will accept every capability.`, error);\n\n return null;\n }\n}\n","import { RackClient } from './core/rack';\n\nexport * from './core/types';\nexport { PROTOCOL_NAMESPACE, PROTOCOL_VERSION, FORMAT } from './core/protocol';\nexport { RackClient } from './core/rack';\nexport { HostBridge } from './core/bridge';\nexport type { AnswerOptions, StatementInput } from './activity';\nexport type { Collection, ListQuery, ListResult, RecordRow, WhereClause, WhereOperator } from './db';\nexport type { StoredFile, FileStatus } from './storage';\nexport type { Chat, ChatReply, Usage } from './ai';\nexport type { Channel, RoomEventName, RoomEvents } from './broadcast';\nexport type { MockManifest, AiMockHandler } from './core/mock/MockRack';\n\n/** The singleton every applet talks through. */\nexport const Rack = new RackClient();\n\n/** A separate client (tests, or unusual multi-frame setups). */\nexport function createRack(): RackClient {\n return new RackClient();\n}\n"],"names":["ActivityApi","link","id","a","input","activityId","response","options","stepId","context","Chat","promptId","message","onDelta","text","result","chunk","delta","AiApi","vars","AttemptApi","launched","state","segment","summary","AuthApi","key","fallback","user","ROOM_CHANNEL","Channel","name","event","l","callback","set","payload","BroadcastApi","keys","value","channel","instance","participantId","notDeclared","capability","Collection","data","query","type","DbApi","MediaApi","StorageApi","upload","file","meta","HostBridge","source","timeoutMs","resolve","timer","r","onLaunch","requestId","newRequestId","request","createEnvelope","listener","envelope","isEnvelope","resolvers","ack","_id","_ok","_partial","ok","error","details","_done","rest","DEFAULT_LAUNCH_TIMEOUT_MS","DEFAULT_ACK_TIMEOUT_MS","RackClient","handler","rack","ref","capabilityOf","ackTimeoutMs","launchTimeoutMs","launch","MockTransport","bridge","reason","manifest","loadManifest","MockRack","LocalStore","transport","call","url","slug","Rack","createRack"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/activity/index.ts","../src/ai/index.ts","../src/attempt/index.ts","../src/auth/index.ts","../src/broadcast/index.ts","../src/core/link.ts","../src/db/index.ts","../src/media/index.ts","../src/storage/index.ts","../src/core/bridge.ts","../src/core/rack.ts","../src/index.ts"],"sourcesContent":["import type { Link } from '../core/link';\nimport type { AckResult, JsonObject, LaunchActivity } from '../core/types';\n\nexport interface StatementInput {\n verb: string;\n activity: { id: string; type?: string };\n result?: JsonObject;\n context?: JsonObject;\n occurred_at?: string;\n}\n\nexport interface AnswerOptions {\n type?: string;\n context?: JsonObject;\n}\n\n/**\n * activity@1 — append-only statements. Answers are statements, not state.\n */\nexport class ActivityApi {\n public constructor(private readonly link: Link) {}\n\n /** The manifest's declared activities for this version. */\n public activities(): LaunchActivity[] {\n return this.link.launch()?.activity?.activities ?? [];\n }\n\n public activity(id: string): LaunchActivity | null {\n return this.activities().find((a) => a.id === id) ?? null;\n }\n\n public statement(input: StatementInput): Promise<AckResult<{ id: string }>> {\n return this.link.send<{ id: string }>('activity:statement', {\n ...input,\n occurred_at: input.occurred_at ?? new Date().toISOString(),\n });\n }\n\n /** `answered` statement with the value in result.response — the only field reporting reads. */\n public answer(activityId: string, response: unknown, options: AnswerOptions = {}): Promise<AckResult<{ id: string }>> {\n return this.statement({\n verb: 'answered',\n activity: { id: activityId, type: options.type ?? this.activity(activityId)?.type },\n result: { response },\n context: options.context,\n });\n }\n\n /** `progressed` statement with activity.type = 'step'. */\n public progress(stepId: string, context?: JsonObject): Promise<AckResult<{ id: string }>> {\n return this.statement({ verb: 'progressed', activity: { id: stepId, type: 'step' }, context });\n }\n}\n","import type { Link } from '../core/link';\nimport type { AckResult, JsonObject } from '../core/types';\n\nexport interface Usage {\n input_tokens: number;\n output_tokens: number;\n}\n\nexport interface ChatReply {\n message_id: string;\n text: string;\n usage: Usage;\n}\n\nexport class Chat {\n public constructor(private readonly link: Link, public readonly promptId: string) {}\n\n /**\n * Send a participant message. onDelta receives text as it streams; the\n * resolved result carries the full text.\n */\n public async send(message: string, onDelta?: (delta: string, soFar: string) => void): Promise<AckResult<ChatReply>> {\n let text = '';\n const result = await this.link.send<Partial<ChatReply>>('ai:chat', { prompt: this.promptId, message }, {\n timeoutMs: 60_000,\n onPartial: (chunk) => {\n const delta = typeof chunk.delta === 'string' ? chunk.delta : '';\n text += delta;\n onDelta?.(delta, text);\n },\n });\n\n if (!result.ok) {\n return result as AckResult<ChatReply>;\n }\n\n return {\n ok: true,\n data: {\n message_id: String(result.data?.message_id ?? ''),\n text: result.data?.text ?? text,\n usage: result.data?.usage ?? { input_tokens: 0, output_tokens: 0 },\n },\n };\n }\n\n public reset(): Promise<AckResult> {\n return this.link.send('ai:chat.reset', { prompt: this.promptId });\n }\n}\n\n/**\n * ai@1 — declared prompt templates. The applet supplies variables, never\n * instructions; model, prompt and budget are Rack-owned.\n */\nexport class AiApi {\n public constructor(private readonly link: Link) {}\n\n public prompts(): Array<{ id: string; mode: 'analysis' | 'chat' }> {\n return this.link.launch()?.ai?.prompts ?? [];\n }\n\n /** Analysis prompt: structured JSON conforming to the declared output_schema. */\n public ask<T extends object = JsonObject>(promptId: string, vars: JsonObject = {}): Promise<AckResult<{ output: T; usage: Usage }>> {\n return this.link.send<{ output: T; usage: Usage }>('ai:ask', { prompt: promptId, vars }, { timeoutMs: 60_000 });\n }\n\n /** Chat prompt: conversation history is held by the Rack. */\n public chat(promptId: string): Chat {\n return new Chat(this.link, promptId);\n }\n}\n","import type { Link } from '../core/link';\nimport type { AckResult, AttemptStatus, JsonObject, LaunchAttempt } from '../core/types';\n\n/**\n * attempt@1 — lifecycle, resume state, completion. Declaring it also pins\n * every participant to the version they started on.\n */\nexport class AttemptApi {\n private local: Partial<LaunchAttempt> = {};\n\n public constructor(private readonly link: Link) {}\n\n /** The attempt as launched, with local updates applied. */\n public current(): LaunchAttempt | null {\n const launched = this.link.launch()?.attempt;\n\n return launched ? { ...launched, ...this.local } : null;\n }\n\n public get status(): AttemptStatus | null {\n return this.current()?.status ?? null;\n }\n\n public get id(): number | null {\n return this.current()?.id ?? null;\n }\n\n /** Resume state exactly as last saved. */\n public state<T extends object = JsonObject>(): T | null {\n return (this.current()?.state as T | null) ?? null;\n }\n\n public segment(): Record<string, string> | null {\n return this.current()?.segment ?? null;\n }\n\n /** Save resume state (max 64 KB). Send after every transition; the shell coalesces bursts. */\n public setState(state: JsonObject): Promise<AckResult> {\n this.local.state = state;\n\n return this.link.send('attempt:state', { state });\n }\n\n /** Set segment dimensions (max 20 keys, string values). */\n public setSegment(segment: Record<string, string>): Promise<AckResult> {\n this.local.segment = segment;\n\n return this.link.send('attempt:segment', { segment });\n }\n\n /** Mark the attempt completed. Idempotent. */\n public async complete(summary?: JsonObject): Promise<AckResult<{ status: AttemptStatus; completed_at: string | null }>> {\n const result = await this.link.send<{ status: AttemptStatus; completed_at: string | null }>('attempt:complete', { summary: summary ?? null });\n if (result.ok) {\n this.local.status = 'completed';\n }\n\n return result;\n }\n}\n","import type { Link } from '../core/link';\nimport type { LaunchUser } from '../core/types';\n\n/**\n * auth@1 — who the participant is and the profile attributes the Rack chose to\n * release. Attributes the manifest requested but the Rack withheld are simply\n * absent; degrade gracefully.\n */\nexport class AuthApi {\n public constructor(private readonly link: Link) {}\n\n public user(): LaunchUser | null {\n return this.link.launch()?.auth?.user ?? null;\n }\n\n /** A released profile attribute, or the fallback when withheld. */\n public attribute<T = unknown>(key: string, fallback: T | null = null): T | null {\n const user = this.user();\n if (!user || !(key in user)) {\n return fallback;\n }\n\n return user[key] as T;\n }\n}\n","import type { Link } from '../core/link';\nimport type { AckResult, JsonObject, LaunchBroadcast, LaunchRoom, RackEvent, RoomInvite, RoomMember, RoomRole, RoomStatus } from '../core/types';\n\nexport const ROOM_CHANNEL = '$room';\n\ntype Listener = (payload: unknown, from: RackEvent['from']) => void;\n\nexport type RoomEventName = 'presence' | 'state' | 'role' | 'status';\n\nexport interface RoomEvents {\n presence: { members: RoomMember[] };\n state: { key: string; value: unknown; version: number };\n role: { role: RoomRole; participant_id: number };\n status: { status: RoomStatus };\n}\n\nexport class Channel {\n private readonly listeners = new Map<string, Set<Listener>>();\n\n private unsubscribe: (() => void) | null = null;\n\n public constructor(private readonly link: Link, public readonly name: string) {}\n\n /** @internal */\n public attach(): void {\n this.unsubscribe ??= this.link.onEvent((event) => {\n if (event.channel !== this.name) {\n return;\n }\n this.listeners.get(event.event)?.forEach((l) => l(event.payload, event.from));\n this.listeners.get('*')?.forEach((l) => l({ event: event.event, payload: event.payload }, event.from));\n });\n }\n\n /** Listen for an event on this channel ('*' for all). Returns an unsubscribe function. */\n public listen<T = unknown>(event: string, callback: (payload: T, from: RackEvent['from']) => void): () => void {\n const set = this.listeners.get(event) ?? new Set<Listener>();\n set.add(callback as Listener);\n this.listeners.set(event, set);\n\n return () => {\n set.delete(callback as Listener);\n };\n }\n\n /** Publish to everyone in the room. Facilitator-only events are enforced by the Rack. */\n public publish(event: string, payload: unknown = {}): Promise<AckResult> {\n return this.link.send('broadcast:publish', { channel: this.name, event, payload });\n }\n\n public async leave(): Promise<AckResult> {\n this.unsubscribe?.();\n this.unsubscribe = null;\n this.listeners.clear();\n\n return this.link.send('broadcast:leave', { channel: this.name });\n }\n}\n\n/**\n * broadcast@1 — rooms, presence, facilitator control and shared state. The\n * Rack holds the WebSocket; the applet only ever sees postMessage.\n */\nexport class BroadcastApi {\n private readonly channels = new Map<string, Channel>();\n\n private readonly roomListeners = new Map<RoomEventName, Set<(payload: unknown) => void>>();\n\n private localRole: RoomRole | null = null;\n\n private localMembers: RoomMember[] | null = null;\n\n private localState: JsonObject | null = null;\n\n private detach: (() => void) | null = null;\n\n public constructor(private readonly link: Link) {}\n\n public get room(): LaunchRoom | null {\n return this.launch()?.room ?? null;\n }\n\n public get role(): RoomRole | null {\n return this.localRole ?? this.launch()?.role ?? null;\n }\n\n public get isFacilitator(): boolean {\n return this.role === 'facilitator';\n }\n\n public members(): RoomMember[] {\n return this.localMembers ?? this.launch()?.members ?? [];\n }\n\n /** Join a named channel within the room (default \"main\"). */\n public async join(channel = 'main'): Promise<Channel> {\n this.ensureRoomListener();\n let instance = this.channels.get(channel);\n if (!instance) {\n instance = new Channel(this.link, channel);\n this.channels.set(channel, instance);\n }\n instance.attach();\n await this.link.send('broadcast:join', { channel });\n\n return instance;\n }\n\n /** Who is in the room, by role. */\n public async presence(): Promise<AckResult<{ members: RoomMember[] }>> {\n const result = await this.link.send<{ members: RoomMember[] }>('broadcast:presence', {});\n if (result.ok && result.data) {\n this.localMembers = result.data.members;\n }\n\n return result;\n }\n\n public readonly state = {\n /** Server-arbitrated shared state. */\n get: async (keys?: string[]): Promise<AckResult<{ state: JsonObject }>> => {\n const result = await this.link.send<{ state: JsonObject }>('broadcast:state.get', keys ? { keys } : {});\n if (result.ok && result.data) {\n this.localState = { ...(this.localState ?? {}), ...result.data.state };\n }\n\n return result;\n },\n /** Last write wins at the key; the Rack stamps the version. */\n set: (key: string, value: unknown): Promise<AckResult<{ version: number }>> => {\n this.localState = { ...(this.localState ?? this.launch()?.state ?? {}), [key]: value };\n\n return this.link.send<{ version: number }>('broadcast:state.set', { key, value });\n },\n /** Locally known state (launch snapshot plus pushed updates). */\n snapshot: (): JsonObject => this.localState ?? this.launch()?.state ?? {},\n };\n\n /**\n * Where the next person scans to get in. Only the Rack knows its own host\n * and join prefix, so it hands back the code and the URL; rendering them as\n * a slide, a link or a QR is yours. Refused with `conflict` once the room is\n * locked or closed, so a stale QR can be taken down.\n */\n public invite(): Promise<AckResult<RoomInvite>> {\n return this.link.send('broadcast:invite', {});\n }\n\n /** Claim the facilitator role (opt_in rooms). */\n public claim(): Promise<AckResult<{ role: RoomRole }>> {\n return this.link.send('broadcast:claim', {});\n }\n\n /** Hand the facilitator role to another member. */\n public transfer(participantId: number): Promise<AckResult> {\n return this.link.send('broadcast:transfer', { to: participantId });\n }\n\n /** Room-level events: presence, state, role, status. */\n public on<K extends RoomEventName>(event: K, callback: (payload: RoomEvents[K]) => void): () => void {\n this.ensureRoomListener();\n const set = this.roomListeners.get(event) ?? new Set();\n set.add(callback as (payload: unknown) => void);\n this.roomListeners.set(event, set);\n\n return () => {\n set.delete(callback as (payload: unknown) => void);\n };\n }\n\n private launch(): LaunchBroadcast | null {\n return this.link.launch()?.broadcast ?? null;\n }\n\n private ensureRoomListener(): void {\n this.detach ??= this.link.onEvent((event) => {\n if (event.channel !== ROOM_CHANNEL) {\n return;\n }\n const payload = event.payload as JsonObject;\n if (event.event === 'presence' && Array.isArray(payload.members)) {\n this.localMembers = payload.members as RoomMember[];\n }\n if (event.event === 'state' && typeof payload.key === 'string') {\n this.localState = { ...(this.localState ?? this.launch()?.state ?? {}), [payload.key]: payload.value };\n }\n if (event.event === 'role' && payload.participant_id === this.link.launch()?.actor.id) {\n this.localRole = payload.role as RoomRole;\n }\n this.roomListeners.get(event.event as RoomEventName)?.forEach((l) => l(payload));\n });\n }\n}\n","import type { AckResult, JsonObject, LaunchData, RackEvent, SendOptions } from './types';\n\n/**\n * What a capability module needs from the client: send/fire, the launch data,\n * event subscription and the declared-capability check.\n */\nexport interface Link {\n send<T = JsonObject>(type: string, payload: unknown, options?: SendOptions): Promise<AckResult<T>>;\n fire(type: string, payload?: unknown): void;\n launch(): LaunchData | null;\n declares(capability: string): boolean;\n onEvent(listener: (event: RackEvent) => void): () => void;\n mode(): 'host' | 'mock' | 'pending';\n}\n\nexport function notDeclared<T = JsonObject>(capability: string): AckResult<T> {\n return { ok: false, error: 'undeclared_capability', message: `The manifest does not declare the [${capability}] capability.` };\n}\n","import { notDeclared, type Link } from '../core/link';\nimport type { AckResult, JsonObject } from '../core/types';\n\nexport type WhereOperator = '=' | '!=' | '<' | '<=' | '>' | '>=' | 'in';\n\nexport type WhereClause = [path: string, operator: WhereOperator, value: unknown];\n\nexport interface ListQuery {\n /** Filters on paths declared in the collection's `indexed` list. */\n where?: WhereClause[];\n orderBy?: string;\n direction?: 'asc' | 'desc';\n /** Max 100. */\n limit?: number;\n cursor?: string | null;\n}\n\nexport interface RecordRow<T extends object = JsonObject> {\n id: string;\n data: T;\n owner_id: number;\n created_at: string;\n updated_at: string;\n}\n\nexport interface ListResult<T extends object = JsonObject> {\n records: RecordRow<T>[];\n nextCursor: string | null;\n}\n\nexport class Collection<T extends object = JsonObject> {\n public constructor(private readonly link: Link, public readonly name: string) {}\n\n public create(data: T): Promise<AckResult<{ record: RecordRow<T> }>> {\n return this.send('db:create', { data });\n }\n\n public get(id: string): Promise<AckResult<{ record: RecordRow<T> }>> {\n return this.send('db:get', { id });\n }\n\n /** Merge by default; pass { merge: false } to replace the whole document. */\n public update(id: string, data: Partial<T>, options: { merge?: boolean } = {}): Promise<AckResult<{ record: RecordRow<T> }>> {\n return this.send('db:update', { id, data, merge: options.merge ?? true });\n }\n\n public delete(id: string): Promise<AckResult<{ id: string }>> {\n return this.send('db:delete', { id });\n }\n\n public list(query: ListQuery = {}): Promise<AckResult<ListResult<T>>> {\n return this.send('db:list', query);\n }\n\n public count(query: Pick<ListQuery, 'where'> = {}): Promise<AckResult<{ count: number }>> {\n return this.send('db:count', query);\n }\n\n private send<R>(type: string, payload: object): Promise<AckResult<R>> {\n if (this.link.launch() && !this.link.declares('db')) {\n return Promise.resolve(notDeclared<R>('db'));\n }\n\n return this.link.send<R>(type, { collection: this.name, ...payload });\n }\n}\n\n/**\n * db@1 — CRUD records in collections the manifest declares, each with a JSON\n * Schema and a scope (attempt | user | room | applet).\n */\nexport class DbApi {\n public constructor(private readonly link: Link) {}\n\n public collection<T extends object = JsonObject>(name: string): Collection<T> {\n return new Collection<T>(this.link, name);\n }\n\n public collections(): Array<{ name: string; scope: string }> {\n return this.link.launch()?.db?.collections ?? [];\n }\n}\n","import type { Link } from '../core/link';\nimport type { MediaEmbed } from '../core/types';\n\n/**\n * Declared media. The Rack builds every player URL; the applet references\n * media by id only. Needs no capability.\n */\nexport class MediaApi {\n public constructor(private readonly link: Link) {}\n\n public get(id: string): MediaEmbed | null {\n return this.link.launch()?.media?.[id] ?? null;\n }\n\n /** Player URL for an <iframe src>. Null when the id is not declared. */\n public embedUrl(id: string): string | null {\n return this.get(id)?.embedUrl ?? null;\n }\n\n public all(): Record<string, MediaEmbed> {\n return this.link.launch()?.media ?? {};\n }\n}\n","import type { Link } from '../core/link';\nimport type { AckResult } from '../core/types';\n\nexport type FileStatus = 'pending' | 'clean' | 'infected' | 'failed';\n\nexport interface StoredFile {\n id: string;\n upload: string;\n name: string;\n mime: string;\n bytes: number;\n status: FileStatus;\n created_at: string;\n}\n\n/**\n * storage@1 — participant uploads. The File travels to the shell by\n * structured clone; the Rack scans it before it becomes addressable.\n */\nexport class StorageApi {\n public constructor(private readonly link: Link) {}\n\n public uploads(): Array<{ name: string; scope: string; mime: string[]; max_bytes: number }> {\n return this.link.launch()?.storage?.uploads ?? [];\n }\n\n /** Upload into a declared upload slot. Resolves once stored (status may still be pending scan). */\n public put(upload: string, file: File | Blob, meta: Record<string, unknown> = {}): Promise<AckResult<{ file: StoredFile }>> {\n const name = file instanceof File ? file.name : 'blob';\n\n return this.link.send<{ file: StoredFile }>('storage:put', { upload, file, name, mime: file.type, bytes: file.size, meta }, { timeoutMs: 120_000 });\n }\n\n /** Short-lived URL for a clean file. Never an object key. */\n public url(id: string): Promise<AckResult<{ url: string; expires_at: string }>> {\n return this.link.send('storage:url', { id });\n }\n\n public list(upload?: string): Promise<AckResult<{ files: StoredFile[] }>> {\n return this.link.send('storage:list', upload ? { upload } : {});\n }\n\n public delete(id: string): Promise<AckResult<{ id: string }>> {\n return this.link.send('storage:delete', { id });\n }\n}\n","import { createEnvelope, isEnvelope, newRequestId, type Envelope } from './protocol';\nimport type { AckPayload, AckResult, JsonObject, LaunchData, RackEvent, SendOptions, Transport } from './types';\n\ninterface PendingRequest {\n resolve: (result: AckResult) => void;\n timer: ReturnType<typeof setTimeout>;\n timeoutMs: number;\n onPartial?: (chunk: JsonObject) => void;\n}\n\nexport interface BridgeOptions {\n ackTimeoutMs: number;\n /** Window-like object to post to. Defaults to window.parent. */\n target?: Pick<Window, 'postMessage'>;\n /** Whether a MessageEvent came from the host. Defaults to event.source === window.parent. */\n isHost?: (source: MessageEventSource | null) => boolean;\n /** Where to listen for messages. Defaults to window. */\n listenOn?: Pick<Window, 'addEventListener' | 'removeEventListener'>;\n}\n\n/**\n * postMessage transport used inside the Rack's iframe.\n *\n * Only messages whose source is the parent window are honoured; the applet\n * never inspects event.origin because its own origin is opaque by design.\n */\nexport class HostBridge implements Transport {\n public readonly kind = 'host' as const;\n\n private readonly pending = new Map<string, PendingRequest>();\n\n private readonly eventListeners = new Set<(event: RackEvent) => void>();\n\n private launchResolvers: Array<(data: LaunchData) => void> = [];\n\n private launchData: LaunchData | null = null;\n\n private readonly target: Pick<Window, 'postMessage'>;\n\n private readonly isHost: (source: MessageEventSource | null) => boolean;\n\n private readonly listenOn: Pick<Window, 'addEventListener' | 'removeEventListener'>;\n\n private readonly listener = (event: MessageEvent): void => this.onMessage(event);\n\n public constructor(private readonly options: BridgeOptions) {\n this.target = options.target ?? window.parent;\n this.isHost = options.isHost ?? ((source) => source === window.parent);\n this.listenOn = options.listenOn ?? window;\n this.listenOn.addEventListener('message', this.listener as EventListener);\n }\n\n public announceReady(): void {\n this.fire('rack:ready', {});\n }\n\n public waitForLaunch(timeoutMs: number): Promise<LaunchData | null> {\n if (this.launchData) {\n return Promise.resolve(this.launchData);\n }\n\n return new Promise((resolve) => {\n const timer = setTimeout(() => {\n this.launchResolvers = this.launchResolvers.filter((r) => r !== onLaunch);\n resolve(null);\n }, timeoutMs);\n const onLaunch = (data: LaunchData): void => {\n clearTimeout(timer);\n resolve(data);\n };\n this.launchResolvers.push(onLaunch);\n });\n }\n\n public send<T = JsonObject>(type: string, payload: unknown, options: SendOptions = {}): Promise<AckResult<T>> {\n const requestId = newRequestId();\n const timeoutMs = options.timeoutMs ?? this.options.ackTimeoutMs;\n\n return new Promise((resolve) => {\n const request: PendingRequest = {\n resolve: resolve as (result: AckResult) => void,\n timer: setTimeout(() => this.expire(requestId), timeoutMs),\n timeoutMs,\n onPartial: options.onPartial,\n };\n this.pending.set(requestId, request);\n this.post(createEnvelope(type, payload, requestId));\n });\n }\n\n public fire(type: string, payload: unknown = {}): void {\n this.post(createEnvelope(type, payload));\n }\n\n public onEvent(listener: (event: RackEvent) => void): () => void {\n this.eventListeners.add(listener);\n\n return () => {\n this.eventListeners.delete(listener);\n };\n }\n\n public dispose(): void {\n this.listenOn.removeEventListener('message', this.listener as EventListener);\n for (const [requestId, request] of this.pending) {\n clearTimeout(request.timer);\n request.resolve({ ok: false, error: 'disposed' });\n this.pending.delete(requestId);\n }\n }\n\n private expire(requestId: string): void {\n const request = this.pending.get(requestId);\n if (!request) {\n return;\n }\n this.pending.delete(requestId);\n request.resolve({ ok: false, error: 'timeout' });\n }\n\n private post(envelope: Envelope): void {\n this.target.postMessage(envelope, '*');\n }\n\n private onMessage(event: MessageEvent): void {\n if (!this.isHost(event.source) || !isEnvelope(event.data)) {\n return;\n }\n const envelope = event.data;\n\n if (envelope.type === 'rack:launch') {\n this.launchData = envelope.payload as LaunchData;\n const resolvers = this.launchResolvers;\n this.launchResolvers = [];\n resolvers.forEach((resolve) => resolve(this.launchData as LaunchData));\n\n return;\n }\n\n if (envelope.type === 'rack:event') {\n const payload = envelope.payload as RackEvent;\n this.eventListeners.forEach((listener) => listener(payload));\n\n return;\n }\n\n if (envelope.type === 'rack:ack') {\n this.onAck(envelope.payload as AckPayload);\n }\n }\n\n private onAck(ack: AckPayload): void {\n const request = this.pending.get(ack.requestId);\n if (!request) {\n return;\n }\n\n // A Rack that opens a stream acks the request twice: once to say the\n // stream is live, then once more when it finishes. Only the second one\n // settles the promise.\n if (!ack.done && (ack.data as { stream?: boolean } | undefined)?.stream === true) {\n clearTimeout(request.timer);\n request.timer = setTimeout(() => this.expire(ack.requestId), request.timeoutMs);\n\n return;\n }\n\n if (ack.partial && !ack.done) {\n const { requestId: _id, ok: _ok, partial: _partial, ...chunk } = ack;\n request.onPartial?.(chunk as JsonObject);\n clearTimeout(request.timer);\n request.timer = setTimeout(() => this.expire(ack.requestId), request.timeoutMs);\n\n return;\n }\n\n clearTimeout(request.timer);\n this.pending.delete(ack.requestId);\n\n const { requestId: _id, ok, data, error, message, details, partial: _partial, done: _done, ...rest } = ack;\n request.resolve({ ok, data: (data ?? (Object.keys(rest).length ? rest : undefined)) as JsonObject | undefined, error, message, details });\n }\n}\n","import { ActivityApi } from '../activity';\nimport { AiApi } from '../ai';\nimport { AttemptApi } from '../attempt';\nimport { AuthApi } from '../auth';\nimport { BroadcastApi } from '../broadcast';\nimport { DbApi } from '../db';\nimport { MediaApi } from '../media';\nimport { StorageApi } from '../storage';\nimport { HostBridge } from './bridge';\nimport { notDeclared, type Link } from './link';\nimport { MockRack, type AiMockHandler, type MockManifest } from './mock/MockRack';\nimport { MockTransport } from './mock/MockTransport';\nimport { LocalStore } from './mock/store';\nimport { capabilityOf } from './protocol';\nimport type { AckResult, InitOptions, JsonObject, LaunchData, LaunchListener, RackEvent, SendOptions, Transport } from './types';\n\nconst DEFAULT_LAUNCH_TIMEOUT_MS = 3000;\nconst DEFAULT_ACK_TIMEOUT_MS = 5000;\n\ninterface QueuedCall {\n type: string;\n payload: unknown;\n options?: SendOptions;\n resolve: (result: AckResult) => void;\n}\n\n/**\n * The SDK entry point. One instance per applet; `Rack` is the default singleton.\n *\n * Every capability is a message the Rack services. The applet asks; the Rack\n * decides, enforces and persists. Nothing here ever throws on a Rack error:\n * calls resolve to { ok: false, error }.\n */\nexport class RackClient {\n public readonly auth: AuthApi;\n\n public readonly attempt: AttemptApi;\n\n public readonly activity: ActivityApi;\n\n public readonly db: DbApi;\n\n public readonly storage: StorageApi;\n\n public readonly ai: AiApi;\n\n public readonly broadcast: BroadcastApi;\n\n public readonly media: MediaApi;\n\n /** Dev-only hooks that only take effect in mock mode. */\n public readonly mock: {\n /** Answer an ai prompt yourself instead of the schema stub. */\n ai: (promptId: string, handler: AiMockHandler) => void;\n /** Clear a completed mock attempt so the experience can be replayed. */\n resetAttempt: () => void;\n /** The MockRack when running standalone, else null. */\n rack: () => MockRack | null;\n };\n\n private transport: Transport | null = null;\n\n private initPromise: Promise<LaunchData> | null = null;\n\n private launchData: LaunchData | null = null;\n\n private readonly queue: QueuedCall[] = [];\n\n private readonly launchListeners = new Set<LaunchListener>();\n\n private readonly eventListeners = new Set<(event: RackEvent) => void>();\n\n private pendingAiMocks: Array<[string, AiMockHandler]> = [];\n\n public constructor() {\n const link: Link = {\n send: (type, payload, options) => this.send(type, payload, options),\n fire: (type, payload) => this.fire(type, payload),\n launch: () => this.launchData,\n declares: (capability) => this.declares(capability),\n onEvent: (listener) => this.on('event', listener),\n mode: () => this.mode,\n };\n\n this.auth = new AuthApi(link);\n this.attempt = new AttemptApi(link);\n this.activity = new ActivityApi(link);\n this.db = new DbApi(link);\n this.storage = new StorageApi(link);\n this.ai = new AiApi(link);\n this.broadcast = new BroadcastApi(link);\n this.media = new MediaApi(link);\n\n this.mock = {\n ai: (promptId, handler) => {\n const rack = this.mockRack();\n rack ? rack.mockAi(promptId, handler) : this.pendingAiMocks.push([promptId, handler]);\n },\n resetAttempt: () => this.mockRack()?.resetAttempt(),\n rack: () => this.mockRack(),\n };\n }\n\n /**\n * Handshake with the Rack. Resolves with launch data once the Rack answers,\n * or with mock launch data when running standalone. Safe to call repeatedly.\n */\n public init(options: InitOptions = {}): Promise<LaunchData> {\n this.initPromise ??= this.performInit(options);\n\n return this.initPromise;\n }\n\n public get launched(): boolean {\n return this.launchData !== null;\n }\n\n public get mode(): 'host' | 'mock' | 'pending' {\n return this.transport?.kind ?? 'pending';\n }\n\n public getLaunchData(): LaunchData | null {\n return this.launchData;\n }\n\n /** Whether the manifest declared a capability (any version). */\n public declares(capability: string): boolean {\n return (this.launchData?.capabilities ?? []).some((ref) => ref.split('@')[0] === capability);\n }\n\n public on(event: 'launch', listener: LaunchListener): () => void;\n\n public on(event: 'event', listener: (event: RackEvent) => void): () => void;\n\n public on(event: 'launch' | 'event', listener: LaunchListener | ((event: RackEvent) => void)): () => void {\n if (event === 'launch') {\n const l = listener as LaunchListener;\n this.launchListeners.add(l);\n if (this.launchData) {\n l(this.launchData);\n }\n\n return () => {\n this.launchListeners.delete(l);\n };\n }\n\n const l = listener as (event: RackEvent) => void;\n this.eventListeners.add(l);\n\n return () => {\n this.eventListeners.delete(l);\n };\n }\n\n /** Ask the Rack to leave the applet. Needs no capability. */\n public eject(): void {\n this.fire('rack:eject', {});\n if (this.transport?.kind === 'mock') {\n console.info('[ziprack mock] eject: the Rack would navigate away now.');\n }\n }\n\n /** Low-level: send any message and await its ack. Capability calls go through the typed APIs. */\n public send<T = JsonObject>(type: string, payload: unknown, options?: SendOptions): Promise<AckResult<T>> {\n if (this.launchData && !type.startsWith('rack:') && !this.declares(capabilityOf(type))) {\n return Promise.resolve(notDeclared<T>(capabilityOf(type)));\n }\n if (this.transport) {\n return this.transport.send<T>(type, payload, options);\n }\n\n return new Promise((resolve) => {\n this.queue.push({ type, payload, options, resolve: resolve as (result: AckResult) => void });\n });\n }\n\n public fire(type: string, payload: unknown = {}): void {\n if (this.transport) {\n this.transport.fire(type, payload);\n\n return;\n }\n void this.send(type, payload);\n }\n\n /** Tear down (tests). */\n public dispose(): void {\n this.transport?.dispose();\n this.transport = null;\n this.initPromise = null;\n this.launchData = null;\n }\n\n private async performInit(options: InitOptions): Promise<LaunchData> {\n const ackTimeoutMs = options.ackTimeoutMs ?? DEFAULT_ACK_TIMEOUT_MS;\n const launchTimeoutMs = options.launchTimeoutMs ?? DEFAULT_LAUNCH_TIMEOUT_MS;\n\n if (options.transport) {\n const launch = options.launch\n ?? (options.transport instanceof MockTransport ? options.transport.rack.launchData() : null);\n if (!launch) {\n throw new Error('init({ transport }) needs `launch` data unless the transport is a MockTransport.');\n }\n\n return this.activate(options.transport, launch);\n }\n\n const embedded = typeof window !== 'undefined' && window.parent !== window;\n\n if (!embedded) {\n return this.activateMock(options, 'not embedded in a Rack');\n }\n\n const bridge = new HostBridge({ ackTimeoutMs });\n bridge.announceReady();\n const launch = await bridge.waitForLaunch(launchTimeoutMs);\n\n if (!launch) {\n bridge.dispose();\n\n return this.activateMock(options, `no rack:launch within ${launchTimeoutMs}ms`);\n }\n\n return this.activate(bridge, launch);\n }\n\n private async activateMock(options: InitOptions, reason: string): Promise<LaunchData> {\n console.info(`[ziprack] running in mock mode (${reason})`);\n\n const manifest = await loadManifest(options.manifestUrl ?? './manifest.json', options.slug);\n const rack = new MockRack(manifest, new LocalStore(), {\n log: (type, payload) => console.info(`[ziprack mock] ${type}`, payload),\n });\n for (const [promptId, handler] of this.pendingAiMocks) {\n rack.mockAi(promptId, handler);\n }\n this.pendingAiMocks = [];\n\n return this.activate(new MockTransport(rack), rack.launchData(options.slug));\n }\n\n private activate(transport: Transport, launch: LaunchData): LaunchData {\n this.transport = transport;\n this.launchData = launch;\n transport.onEvent((event) => this.eventListeners.forEach((l) => l(event)));\n\n this.launchListeners.forEach((listener) => listener(launch));\n this.flushQueue();\n\n return launch;\n }\n\n private flushQueue(): void {\n const transport = this.transport;\n if (!transport) {\n return;\n }\n while (this.queue.length > 0) {\n const call = this.queue.shift() as QueuedCall;\n void this.send(call.type, call.payload, call.options).then(call.resolve);\n }\n }\n\n private mockRack(): MockRack | null {\n return this.transport instanceof MockTransport ? this.transport.rack : null;\n }\n}\n\nasync function loadManifest(url: string, slug?: string): Promise<MockManifest | null> {\n if (typeof fetch !== 'function') {\n return null;\n }\n try {\n const response = await fetch(url, { cache: 'no-store' });\n if (!response.ok) {\n throw new Error(String(response.status));\n }\n const manifest = (await response.json()) as MockManifest;\n if (slug && manifest.slug !== slug) {\n console.warn(`[ziprack mock] manifest slug [${manifest.slug}] does not match init({ slug: '${slug}' }).`);\n }\n\n return manifest;\n } catch (error) {\n console.warn(`[ziprack mock] could not load ${url}; mock mode will accept every capability.`, error);\n\n return null;\n }\n}\n","import { RackClient } from './core/rack';\n\nexport * from './core/types';\nexport { PROTOCOL_NAMESPACE, PROTOCOL_VERSION, FORMAT } from './core/protocol';\nexport { RackClient } from './core/rack';\nexport { HostBridge } from './core/bridge';\nexport type { AnswerOptions, StatementInput } from './activity';\nexport type { Collection, ListQuery, ListResult, RecordRow, WhereClause, WhereOperator } from './db';\nexport type { StoredFile, FileStatus } from './storage';\nexport type { Chat, ChatReply, Usage } from './ai';\nexport type { Channel, RoomEventName, RoomEvents } from './broadcast';\nexport type { MockManifest, AiMockHandler } from './core/mock/MockRack';\n\n/** The singleton every applet talks through. */\nexport const Rack = new RackClient();\n\n/** A separate client (tests, or unusual multi-frame setups). */\nexport function createRack(): RackClient {\n return new RackClient();\n}\n"],"names":["ActivityApi","link","id","a","input","activityId","response","options","stepId","context","Chat","promptId","message","onDelta","text","result","chunk","delta","AiApi","vars","AttemptApi","launched","state","segment","summary","AuthApi","key","fallback","user","ROOM_CHANNEL","Channel","name","event","l","callback","set","payload","BroadcastApi","keys","value","channel","instance","participantId","notDeclared","capability","Collection","data","query","type","DbApi","MediaApi","StorageApi","upload","file","meta","HostBridge","source","timeoutMs","resolve","timer","r","onLaunch","requestId","newRequestId","request","createEnvelope","listener","envelope","isEnvelope","resolvers","ack","_id","_ok","_partial","ok","error","details","_done","rest","DEFAULT_LAUNCH_TIMEOUT_MS","DEFAULT_ACK_TIMEOUT_MS","RackClient","handler","rack","ref","capabilityOf","ackTimeoutMs","launchTimeoutMs","launch","MockTransport","bridge","reason","manifest","loadManifest","MockRack","LocalStore","transport","call","url","slug","Rack","createRack"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { startDocsMcp } from './server.js';
3
+ await startDocsMcp();
@@ -0,0 +1,31 @@
1
+ export interface DocSection {
2
+ /** The `<a name>` anchor docs link to, or `null` for the text above the first heading. */
3
+ anchor: string | null;
4
+ heading: string;
5
+ text: string;
6
+ }
7
+ export interface Doc {
8
+ /** File name without `.md`, e.g. `sdk`. */
9
+ name: string;
10
+ title: string;
11
+ text: string;
12
+ sections: DocSection[];
13
+ }
14
+ export declare const GUIDELINES = "ziprack-1-authoring";
15
+ /**
16
+ * The docs are copied into `dist/docs/` when the SDK is built, so the set an
17
+ * applet project reads is the set published with the SDK version it installed.
18
+ */
19
+ export declare function loadDocs(directory?: string): Doc[];
20
+ export declare function parseDoc(name: string, text: string): Doc;
21
+ export interface SearchHit {
22
+ doc: Doc;
23
+ section: DocSection;
24
+ score: number;
25
+ }
26
+ /**
27
+ * Plain term frequency, with heading and title hits weighted up and an exact
28
+ * phrase match weighted most. The corpus is a few hundred sections; anything
29
+ * cleverer would be harder to predict than it is worth.
30
+ */
31
+ export declare function searchDocs(docs: Doc[], queries: string[], limit: number): SearchHit[];