@goflowstate/widget-sdk 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -5
- package/dist/canvas-widget-sdk.js +48 -15
- package/dist/canvas-widget-sdk.js.map +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +60 -17
- package/dist/index.mjs.map +2 -2
- package/dist/session.d.ts +79 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/session.request.test.d.ts +2 -0
- package/dist/session.request.test.d.ts.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -104,10 +104,13 @@ for await (const ev of widget.ai.stream({
|
|
|
104
104
|
}
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
Available aliases: `widget.
|
|
108
|
-
(
|
|
109
|
-
(
|
|
110
|
-
|
|
107
|
+
Available aliases: `widget.haiku` (Anthropic, fast), `widget.sonnet`
|
|
108
|
+
(Anthropic, strongest), `widget.gpt` (OpenAI, strongest), `widget.gpt-mini`
|
|
109
|
+
(OpenAI, fast), `widget.gemini` (Google, strongest), `widget.gemini-mini`
|
|
110
|
+
(Google, fast), `widget.sonar` (Perplexity, answers grounded in live web
|
|
111
|
+
search), `widget.sonar-pro` (Perplexity, web-grounded, deeper research).
|
|
112
|
+
Aliases are platform-managed; the model behind one can improve without a
|
|
113
|
+
code change on your side.
|
|
111
114
|
|
|
112
115
|
Notes:
|
|
113
116
|
|
|
@@ -138,9 +141,24 @@ if (result.granted) {
|
|
|
138
141
|
widget.notify('Saved!', { level: 'info', duration: 3000 });
|
|
139
142
|
```
|
|
140
143
|
|
|
144
|
+
## Viewer claims (shell guests)
|
|
145
|
+
|
|
146
|
+
A session opened from a unishell shell page belongs to a guest whose name,
|
|
147
|
+
email or phone the shell has already verified. Those facts arrive on every
|
|
148
|
+
state read as `state.viewer.claims`, and the latest ones are on
|
|
149
|
+
`session.claims`:
|
|
150
|
+
|
|
151
|
+
const session = CanvasWidget.initSession();
|
|
152
|
+
const state = await session.join();
|
|
153
|
+
const email = session.claims?.email; // { type, value, source, verifiedAt? } | undefined
|
|
154
|
+
|
|
155
|
+
Claims are only ever the reader's own, and they are context, not
|
|
156
|
+
authorization. `session.claims` is `null` for members, for PWA guests and
|
|
157
|
+
before the first state arrives.
|
|
158
|
+
|
|
141
159
|
## Full API Reference
|
|
142
160
|
|
|
143
|
-
See [
|
|
161
|
+
See the [Flowstate developer docs](https://developer.goflowstate.com/docs/) for the complete API reference, the manifest format, the AI integration guide, and the submission pipeline.
|
|
144
162
|
|
|
145
163
|
## License
|
|
146
164
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @goflowstate/widget-sdk v0.
|
|
1
|
+
// @goflowstate/widget-sdk v0.3.0
|
|
2
2
|
// https://github.com/goflowstate/goflowstate-sdk
|
|
3
3
|
|
|
4
4
|
"use strict";
|
|
@@ -69,23 +69,27 @@ var CanvasWidgetSDK = (() => {
|
|
|
69
69
|
const slug = q.get("slug") ?? q.get("tripId") ?? "";
|
|
70
70
|
if (!slug) return null;
|
|
71
71
|
const mobile = q.get("mobile");
|
|
72
|
+
const webBase = (q.get("web") ?? "http://localhost:14321").replace(/\/$/, "");
|
|
73
|
+
const host = q.get("host");
|
|
72
74
|
return {
|
|
73
75
|
slug,
|
|
74
76
|
ticket: q.get("ticket"),
|
|
75
77
|
apiBase: (q.get("api") ?? "http://localhost:13002").replace(/\/$/, ""),
|
|
76
|
-
webBase
|
|
77
|
-
mobileBase: mobile ? mobile.replace(/\/$/, "") : null
|
|
78
|
+
webBase,
|
|
79
|
+
mobileBase: mobile ? mobile.replace(/\/$/, "") : null,
|
|
80
|
+
hostBase: host ? host.replace(/\/$/, "") : webBase
|
|
78
81
|
};
|
|
79
82
|
}
|
|
83
|
+
var hostOriginOf = (params) => new URL(params.hostBase ?? params.webBase).origin;
|
|
80
84
|
var SESSION_INIT_TIMEOUT_MS = 1e4;
|
|
81
85
|
var DEFAULT_POLL_INTERVAL_MS = 4e3;
|
|
82
|
-
function awaitInitTicket(
|
|
86
|
+
function awaitInitTicket(hostOrigin) {
|
|
83
87
|
return new Promise((resolve, reject) => {
|
|
84
88
|
if (typeof window === "undefined" || window.parent === window) {
|
|
85
89
|
reject(new Error("no parent page to receive a ticket from"));
|
|
86
90
|
return;
|
|
87
91
|
}
|
|
88
|
-
const webOrigin =
|
|
92
|
+
const webOrigin = hostOrigin;
|
|
89
93
|
const timer = setTimeout(() => {
|
|
90
94
|
window.removeEventListener("message", onMessage);
|
|
91
95
|
reject(new Error("timed out waiting for the session handoff"));
|
|
@@ -124,13 +128,14 @@ var CanvasWidgetSDK = (() => {
|
|
|
124
128
|
myContribution: s.myWishlist ?? null,
|
|
125
129
|
doc: s.plan ?? null,
|
|
126
130
|
artifactId: s.artifactId,
|
|
127
|
-
extension: s.extension ?? null
|
|
131
|
+
extension: s.extension ?? null,
|
|
132
|
+
viewer: s.viewer ? { userId: s.viewer.userId, guest: s.viewer.guest, claims: s.viewer.claims ?? null } : null
|
|
128
133
|
};
|
|
129
134
|
}
|
|
130
|
-
var parentOpener = (
|
|
135
|
+
var parentOpener = (hostOrigin) => {
|
|
131
136
|
return (url) => {
|
|
132
137
|
if (typeof window !== "undefined" && window.parent !== window) {
|
|
133
|
-
window.parent.postMessage({ type: "flowstate:navigate", url },
|
|
138
|
+
window.parent.postMessage({ type: "flowstate:navigate", url }, hostOrigin);
|
|
134
139
|
return;
|
|
135
140
|
}
|
|
136
141
|
if (typeof window !== "undefined") window.open(url, "_blank", "noopener");
|
|
@@ -140,6 +145,7 @@ var CanvasWidgetSDK = (() => {
|
|
|
140
145
|
slug;
|
|
141
146
|
webBase;
|
|
142
147
|
mobileBase;
|
|
148
|
+
latestClaims = null;
|
|
143
149
|
apiBase;
|
|
144
150
|
tokenPromise;
|
|
145
151
|
opener;
|
|
@@ -148,12 +154,12 @@ var CanvasWidgetSDK = (() => {
|
|
|
148
154
|
this.webBase = params.webBase;
|
|
149
155
|
this.mobileBase = params.mobileBase ?? null;
|
|
150
156
|
this.apiBase = params.apiBase;
|
|
151
|
-
this.opener = opener ?? parentOpener(params
|
|
157
|
+
this.opener = opener ?? parentOpener(hostOriginOf(params));
|
|
152
158
|
this.tokenPromise = this.acquireTicket(params).then((t) => this.redeem(t));
|
|
153
159
|
}
|
|
154
160
|
async acquireTicket(params) {
|
|
155
161
|
if (params.ticket) return params.ticket;
|
|
156
|
-
return awaitInitTicket(params
|
|
162
|
+
return awaitInitTicket(hostOriginOf(params));
|
|
157
163
|
}
|
|
158
164
|
async redeem(ticket) {
|
|
159
165
|
const res = await fetch(`${this.apiBase}/api/auth/ticket/redeem`, {
|
|
@@ -181,7 +187,12 @@ var CanvasWidgetSDK = (() => {
|
|
|
181
187
|
}
|
|
182
188
|
const json = await res.json();
|
|
183
189
|
const data = json.data;
|
|
184
|
-
|
|
190
|
+
const view = toStateView(data.state ?? data);
|
|
191
|
+
this.latestClaims = view.viewer?.claims ?? null;
|
|
192
|
+
return view;
|
|
193
|
+
}
|
|
194
|
+
get claims() {
|
|
195
|
+
return this.latestClaims;
|
|
185
196
|
}
|
|
186
197
|
join() {
|
|
187
198
|
return this.call("/enter", { method: "POST" });
|
|
@@ -223,15 +234,37 @@ var CanvasWidgetSDK = (() => {
|
|
|
223
234
|
body: JSON.stringify(body ?? {})
|
|
224
235
|
});
|
|
225
236
|
}
|
|
226
|
-
async request(verb, body) {
|
|
227
|
-
const
|
|
237
|
+
async request(verb, body, options) {
|
|
238
|
+
const signal = options?.signal;
|
|
239
|
+
if (signal?.aborted) {
|
|
240
|
+
void this.tokenPromise.catch(() => {
|
|
241
|
+
});
|
|
242
|
+
signal.throwIfAborted();
|
|
243
|
+
}
|
|
244
|
+
let onAbort;
|
|
245
|
+
let token;
|
|
246
|
+
try {
|
|
247
|
+
token = await (signal ? Promise.race([
|
|
248
|
+
this.tokenPromise,
|
|
249
|
+
new Promise((_resolve, reject) => {
|
|
250
|
+
onAbort = () => reject(signal.reason);
|
|
251
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
252
|
+
})
|
|
253
|
+
]) : this.tokenPromise);
|
|
254
|
+
} finally {
|
|
255
|
+
if (signal && onAbort) signal.removeEventListener("abort", onAbort);
|
|
256
|
+
}
|
|
228
257
|
const res = await fetch(`${this.apiBase}/api/w/${this.slug}/${verb.replace(/^\//, "")}`, {
|
|
258
|
+
signal,
|
|
229
259
|
method: "POST",
|
|
230
260
|
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
231
261
|
body: JSON.stringify(body ?? {})
|
|
232
262
|
});
|
|
233
263
|
if (!res.ok) {
|
|
234
|
-
const errBody = await res.json().catch(() =>
|
|
264
|
+
const errBody = await res.json().catch(() => {
|
|
265
|
+
signal?.throwIfAborted();
|
|
266
|
+
return {};
|
|
267
|
+
});
|
|
235
268
|
throw new Error(errBody.message ?? `session service ${res.status}`);
|
|
236
269
|
}
|
|
237
270
|
const json = await res.json();
|
|
@@ -241,7 +274,7 @@ var CanvasWidgetSDK = (() => {
|
|
|
241
274
|
this.opener(url);
|
|
242
275
|
}
|
|
243
276
|
revealUrl(artifactId) {
|
|
244
|
-
return `${this.webBase}/a/${artifactId}`;
|
|
277
|
+
return this.mobileBase ? `${this.mobileBase}/?a=${artifactId}` : `${this.webBase}/a/${artifactId}`;
|
|
245
278
|
}
|
|
246
279
|
};
|
|
247
280
|
function initSession() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/sdk.ts", "../src/service.ts", "../src/session.ts"],
|
|
4
|
-
"sourcesContent": ["// ============================================\n// Canvas Widget SDK \u2014 runs inside widget iframes\n// ============================================\n//\n// Usage in a widget:\n// <script src=\"canvas-widget-sdk.js\"></script>\n// <script>\n// const widget = await CanvasWidget.init();\n// widget.onStateChange(state => { ... });\n// </script>\n\nimport type {\n CanvasWidgetSDK,\n WidgetDisplayState,\n WidgetEvent,\n InitPayload,\n StateChangePayload,\n EventBroadcastPayload,\n ConsentResult,\n ConsentResultPayload,\n UserContext,\n StorageAPI,\n SettingsAPI,\n WidgetServiceClient,\n AiAPI,\n AiCallOptions,\n AiCompleteResult,\n AiStreamEvent,\n DbAPI,\n DbCollection,\n DbRecord,\n DbListOptions,\n} from './types';\nimport { createServiceClient } from './service';\nimport { initSession } from './session';\n\nconst PROTOCOL = 'canvas-widget-v1';\nconst DEV_MODE_TOKEN = 'dev-mode-skip';\nconst INIT_TIMEOUT_MS = 5000;\n\n// crypto.randomUUID only exists in SECURE contexts (HTTPS or localhost). When\n// a widget iframe is served over plain http on a LAN IP (e.g. dev-on-phone at\n// http://10.0.0.x), it's undefined and init() throws \u2014 the widget never\n// hands shakes and the host times it out (\"failed to load\"). Fall back to a\n// getRandomValues-based v4 UUID (also secure-context-independent).\nfunction safeRandomUUID(): string {\n const c = globalThis.crypto;\n if (c && typeof c.randomUUID === 'function') return c.randomUUID();\n const b = new Uint8Array(16);\n if (c && typeof c.getRandomValues === 'function') c.getRandomValues(b);\n else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);\n b[6] = (b[6] & 0x0f) | 0x40;\n b[8] = (b[8] & 0x3f) | 0x80;\n const h = Array.from(b, (x) => x.toString(16).padStart(2, '0'));\n return `${h[0]}${h[1]}${h[2]}${h[3]}-${h[4]}${h[5]}-${h[6]}${h[7]}-${h[8]}${h[9]}-${h[10]}${h[11]}${h[12]}${h[13]}${h[14]}${h[15]}`;\n}\n\ninterface CanvasMessage {\n protocol: string;\n type: string;\n widgetId: string;\n nonce: string;\n timestamp: number;\n token: string;\n payload: Record<string, unknown>;\n}\n\n// ============================================\n// HMAC utilities (mirrored from host side)\n// ============================================\n\nlet cachedKey: { secret: string; key: CryptoKey } | null = null;\n\nasync function getHmacKey(secret: string): Promise<CryptoKey> {\n if (cachedKey && cachedKey.secret === secret) return cachedKey.key;\n\n const encoded = new TextEncoder().encode(secret);\n const buf = new ArrayBuffer(encoded.byteLength);\n new Uint8Array(buf).set(encoded);\n\n const key = await crypto.subtle.importKey('raw', buf, { name: 'HMAC', hash: 'SHA-256' }, false, [\n 'sign',\n ]);\n\n cachedKey = { secret, key };\n return key;\n}\n\nasync function signToken(\n secret: string,\n type: string,\n widgetId: string,\n nonce: string,\n timestamp: number\n): Promise<string> {\n const key = await getHmacKey(secret);\n const data = new TextEncoder().encode(`${type}:${widgetId}:${nonce}:${timestamp}`);\n const buf = new ArrayBuffer(data.byteLength);\n new Uint8Array(buf).set(data);\n const sig = await crypto.subtle.sign('HMAC', key, buf);\n return Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n// ============================================\n// SDK Implementation\n// ============================================\n\n/** Internal SDK implementation \u2014 not exposed directly to widget authors. */\nexport class CanvasWidgetSDKImpl implements CanvasWidgetSDK {\n private _widgetId: string;\n private _state: WidgetDisplayState;\n private _hmacSecret: string | null;\n private _user: UserContext | null;\n private _namespace: { officeId: string; memberCount: number } | null;\n private _widgetApiUrl: string | null;\n private _widgetApiToken: string | null;\n private _installationId: string;\n private _pendingConsents = new Map<\n string,\n { resolve: (r: ConsentResult) => void; reject: (e: Error) => void }\n >();\n private _cachedJwt: { token: string; expiresAt: number } | null = null;\n private _stateListeners: Array<(state: WidgetDisplayState) => void> = [];\n private _displayState: WidgetDisplayState = 'collapsed';\n private _displayStateCallbacks: Array<(state: WidgetDisplayState) => void> = [];\n private _focused = false;\n private _focusCallbacks: Array<(focused: boolean) => void> = [];\n private _settingsRemoteListeners: Array<(settings: Record<string, unknown>) => void> = [];\n private _eventListeners = new Map<string, Array<(event: WidgetEvent) => void>>();\n private _settings: Record<string, unknown>;\n private _messageHandler: (event: MessageEvent) => void;\n\n /** Authenticated HTTP client scoped to the widget's declared service URL. Undefined when no service is declared. */\n readonly service: WidgetServiceClient | undefined;\n\n constructor(init: InitPayload) {\n this._widgetId = init.widgetId;\n this._state = init.state;\n this._hmacSecret = init.hmacSecret;\n this._user = init.user;\n this._namespace = init.namespace ?? null;\n this._widgetApiUrl = init.widgetApiUrl;\n this._widgetApiToken = init.widgetApiToken ?? null;\n this._installationId = init.installationId;\n this._settings = init.settings ?? {};\n\n // Wire sdk.service when the host provides a service URL from the manifest\n if (init.serviceUrl) {\n this.service = createServiceClient({\n baseUrl: init.serviceUrl,\n getJwt: (forceRefresh?: boolean) => this.getJwt(forceRefresh),\n });\n }\n\n this._messageHandler = this.handleMessage.bind(this);\n window.addEventListener('message', this._messageHandler);\n }\n\n /** The unique identifier for this widget instance on the canvas. */\n get widgetId(): string {\n return this._widgetId;\n }\n\n /** The current display state of the widget (collapsed, expanding, expanded, collapsing). */\n get state(): WidgetDisplayState {\n return this._state;\n }\n\n /** The authenticated user context, or null when user data access has not been granted. */\n get user(): UserContext | null {\n return this._user;\n }\n\n /** Namespace (office) context \u2014 id and owner-inclusive member count \u2014 or\n * null on a personal canvas. What the `canvas.context` permission grants. */\n get namespace(): { officeId: string; memberCount: number } | null {\n return this._namespace;\n }\n\n /** Subscribe to display state changes; returns an unsubscribe function. */\n onStateChange(cb: (state: WidgetDisplayState) => void): () => void {\n this._stateListeners.push(cb);\n return () => {\n const idx = this._stateListeners.indexOf(cb);\n if (idx !== -1) this._stateListeners.splice(idx, 1);\n };\n }\n\n /** Ask the canvas host to expand this widget into its modal view. */\n requestExpand(): void {\n this.send('EXPAND_REQUEST', {});\n }\n\n /** Ask the canvas host to collapse this widget back to its tile view. */\n requestCollapse(): void {\n this.send('COLLAPSE_REQUEST', {});\n }\n\n /** Get the current display state */\n getDisplayState(): WidgetDisplayState {\n return this._displayState;\n }\n\n /** Register a callback for display state changes */\n onDisplayStateChange(callback: (state: WidgetDisplayState) => void): () => void {\n this._displayStateCallbacks.push(callback);\n return () => {\n this._displayStateCallbacks = this._displayStateCallbacks.filter((cb) => cb !== callback);\n };\n }\n\n /** Is this widget the user's current focus? Updates via FOCUS_CHANGE. */\n isFocused(): boolean {\n return this._focused;\n }\n\n /** Subscribe to focus transitions. Returns an unsubscribe function. */\n onFocusChange(callback: (focused: boolean) => void): () => void {\n this._focusCallbacks.push(callback);\n return () => {\n this._focusCallbacks = this._focusCallbacks.filter((cb) => cb !== callback);\n };\n }\n\n /** Request promotion to fullscreen from partial */\n requestFullscreen(): void {\n this.send('FULLSCREEN_REQUEST', {});\n }\n\n /** Broadcast an event on the given topic to all subscribed widgets on the canvas. */\n emit(topic: string, payload: unknown): void {\n this.send('EVENT_EMIT', { topic, payload });\n }\n\n /** Subscribe to events on the given topic; returns an unsubscribe function. */\n subscribe(topic: string, cb: (event: WidgetEvent) => void): () => void {\n const list = this._eventListeners.get(topic) ?? [];\n list.push(cb);\n this._eventListeners.set(topic, list);\n return () => {\n const current = this._eventListeners.get(topic);\n if (!current) return;\n const idx = current.indexOf(cb);\n if (idx !== -1) current.splice(idx, 1);\n };\n }\n\n /** Send a structured log entry to the canvas host for centralized debugging. */\n log(level: 'info' | 'warn' | 'error', message: string, data?: unknown): void {\n this.send('LOG', { level, message, data: data ?? null });\n }\n\n /** Request user consent for a named data scope; resolves with the consent decision. */\n async requestConsent(scope: string, reason: string): Promise<ConsentResult> {\n return new Promise<ConsentResult>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this._pendingConsents.delete(scope);\n reject(new Error('Consent request timed out'));\n }, 60000);\n\n this._pendingConsents.set(scope, {\n resolve: (r: ConsentResult) => {\n clearTimeout(timeout);\n resolve(r);\n },\n reject: (e: Error) => {\n clearTimeout(timeout);\n reject(e);\n },\n });\n\n this.send('CONSENT_REQUEST', { scope, reason });\n });\n }\n\n /** Return the cached JWT for authenticating requests to external APIs; throws if no token is available. Pass forceRefresh=true to bypass the cache. */\n async getJwt(forceRefresh?: boolean): Promise<string> {\n if (!forceRefresh && this._cachedJwt && Date.now() < this._cachedJwt.expiresAt - 30000) {\n return this._cachedJwt.token;\n }\n throw new Error('No cached JWT \u2014 widget must use OAuth flow for token refresh');\n }\n\n /** Persistent key-value storage scoped to this widget installation. */\n get storage(): StorageAPI {\n return {\n get: (key: string) => this.storageRequest('GET', key),\n set: (key: string, value: unknown) => this.storageRequest('PUT', key, value).then(() => {}),\n delete: (key: string) => this.storageRequest('DELETE', key).then(() => {}),\n };\n }\n\n /** Ad-hoc collections/records backend scoped to the canvas/room. */\n get db(): DbAPI {\n const req = (path: string, method: string, body?: unknown) => this.widgetApiRequest(path, method, body);\n return {\n collection(name: string): DbCollection {\n const base = `/widget-api/db/${encodeURIComponent(name)}`;\n return {\n create: (data) => req(base, 'POST', data) as Promise<DbRecord>,\n get: (id) => req(`${base}/${encodeURIComponent(id)}`, 'GET') as Promise<DbRecord>,\n list: (opts?: DbListOptions) => {\n const qs = new URLSearchParams();\n if (opts?.limit != null) qs.set('limit', String(opts.limit));\n if (opts?.cursor) qs.set('cursor', opts.cursor);\n if (opts?.sort) qs.set('sort', opts.sort);\n if (opts?.mine) qs.set('mine', 'true');\n const suffix = qs.toString() ? `?${qs.toString()}` : '';\n return req(`${base}${suffix}`, 'GET') as Promise<DbRecord[]>;\n },\n update: (id, patch) => req(`${base}/${encodeURIComponent(id)}`, 'PATCH', patch) as Promise<DbRecord>,\n delete: (id) => (req(`${base}/${encodeURIComponent(id)}`, 'DELETE') as Promise<unknown>).then(() => {}),\n };\n },\n };\n }\n\n /** Platform LLM access (widget.* aliases only). Requests ride the same\n * widget-gateway auth as db/storage; the platform meters every call and\n * attributes it to this widget's publishing org. */\n get ai(): AiAPI {\n const complete = (opts: AiCallOptions): Promise<AiCompleteResult> =>\n this.widgetApiRequest('/widget-api/ai/complete', 'POST', opts) as Promise<AiCompleteResult>;\n const streamRequest = (opts: AiCallOptions): Promise<Response> => this.widgetApiStream(opts);\n return {\n complete,\n // Async generator over the gateway's SSE frames. Ends on [DONE] or\n // stream end; returning early (break) cancels the fetch body, which\n // the gateway sees as a disconnect and aborts the upstream call, so\n // an abandoned stream stops generating and billing.\n async *stream(opts: AiCallOptions): AsyncGenerator<AiStreamEvent> {\n const response = await streamRequest(opts);\n const reader = response.body?.getReader();\n if (!reader) throw new Error('ai.stream: response has no body');\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const frames = buffer.split('\\n\\n');\n buffer = frames.pop() ?? '';\n for (const frame of frames) {\n const dataLine = frame.split('\\n').find((l) => l.startsWith('data:'));\n if (!dataLine) continue;\n const payload = dataLine.slice(5).trim();\n if (!payload || payload === '[DONE]') continue;\n let parsed: AiStreamEvent;\n try {\n parsed = JSON.parse(payload) as AiStreamEvent;\n } catch {\n continue;\n }\n yield parsed;\n }\n }\n } finally {\n // Break/return path: releases the connection so the gateway's\n // req.on('close') fires and the upstream call aborts.\n await reader.cancel().catch(() => {});\n }\n },\n };\n }\n\n /** Widget configuration settings persisted by the canvas host. */\n get settings(): SettingsAPI {\n return {\n get: () => ({ ...this._settings }),\n set: (newSettings: Record<string, unknown>) => {\n this._settings = { ...this._settings, ...newSettings };\n this.send('SETTINGS_WRITE', { settings: this._settings });\n },\n onRemoteChange: (cb: (settings: Record<string, unknown>) => void) => {\n this._settingsRemoteListeners.push(cb);\n return () => {\n this._settingsRemoteListeners = this._settingsRemoteListeners.filter((fn) => fn !== cb);\n };\n },\n };\n }\n\n /** Display a toast notification in the canvas host UI. */\n async notify(message: string, opts?: { level?: string; duration?: number }): Promise<void> {\n this.send('NOTIFICATION', {\n message,\n level: opts?.level ?? 'info',\n duration: opts?.duration ?? 5000,\n });\n }\n\n // \u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Raw streaming POST against the widget gateway \u2014 same auth as\n * widgetApiRequest, but returns the Response for SSE consumption\n * instead of parsing a JSON envelope. */\n private async widgetApiStream(body: unknown): Promise<Response> {\n if (!this._widgetApiUrl) {\n throw new Error('Widget API Gateway URL not available');\n }\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this._widgetApiToken) {\n headers['Authorization'] = `Bearer ${this._widgetApiToken}`;\n }\n const response = await fetch(`${this._widgetApiUrl}/widget-api/ai/stream`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n if (!response.ok) {\n const err = await response.json().catch(() => ({}));\n throw new Error((err as Record<string, string>).message ?? 'ai.stream request failed');\n }\n return response;\n }\n\n private async widgetApiRequest(path: string, method: string, body?: unknown): Promise<unknown> {\n if (!this._widgetApiUrl) {\n throw new Error('Widget API Gateway URL not available');\n }\n const url = `${this._widgetApiUrl}${path}`;\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this._widgetApiToken) {\n headers['Authorization'] = `Bearer ${this._widgetApiToken}`;\n }\n const opts: RequestInit = { method, headers };\n if (body !== undefined && method !== 'GET' && method !== 'DELETE') {\n opts.body = JSON.stringify(body);\n }\n const response = await fetch(url, opts);\n if (!response.ok) {\n const err = await response.json().catch(() => ({}));\n throw new Error((err as Record<string, string>).message ?? `Request ${method} ${path} failed`);\n }\n if (method === 'GET' || method === 'POST' || method === 'PATCH') {\n const result = await response.json();\n return (result as Record<string, unknown>).data;\n }\n return undefined;\n }\n\n private storageRequest(method: string, key: string, value?: unknown): Promise<unknown> {\n return this.widgetApiRequest(`/widget-api/storage/${encodeURIComponent(key)}`, method, value);\n }\n\n private handleMessage(event: MessageEvent): void {\n const msg = event.data;\n if (!msg || typeof msg !== 'object' || msg.protocol !== PROTOCOL) return;\n\n switch (msg.type) {\n case 'STATE_CHANGE': {\n const payload = msg.payload as StateChangePayload;\n this._state = payload.state;\n for (const listener of this._stateListeners) {\n listener(payload.state);\n }\n break;\n }\n case 'EVENT_BROADCAST': {\n const payload = msg.payload as EventBroadcastPayload;\n const listeners = this._eventListeners.get(payload.topic);\n if (listeners) {\n const evt: WidgetEvent = {\n topic: payload.topic,\n payload: payload.payload,\n emittedAt: payload.emittedAt,\n };\n for (const listener of listeners) {\n listener(evt);\n }\n }\n break;\n }\n case 'CONSENT_RESULT': {\n const payload = msg.payload as ConsentResultPayload;\n const pending = this._pendingConsents.get(payload.scope);\n if (pending) {\n this._pendingConsents.delete(payload.scope);\n pending.resolve({\n scope: payload.scope,\n granted: payload.granted,\n grantToken: payload.grantToken,\n expiresIn: payload.expiresIn,\n });\n }\n break;\n }\n case 'AUTH_TOKEN_REFRESH': {\n const payload = msg.payload as { token: string; expiresIn: number };\n this._cachedJwt = {\n token: payload.token,\n expiresAt: Date.now() + payload.expiresIn * 1000,\n };\n break;\n }\n case 'DISPLAY_STATE_CHANGE': {\n const newState = msg.payload?.displayState as WidgetDisplayState;\n if (newState) {\n this._displayState = newState;\n this._displayStateCallbacks.forEach((cb) => cb(newState));\n }\n break;\n }\n case 'FOCUS_CHANGE': {\n const next = !!msg.payload?.focused;\n if (next !== this._focused) {\n this._focused = next;\n this._focusCallbacks.forEach((cb) => cb(next));\n }\n break;\n }\n case 'SETTINGS_REMOTE_UPDATE': {\n const p = msg.payload as { settings: Record<string, unknown> };\n this._settings = { ...p.settings };\n for (const fn of this._settingsRemoteListeners) {\n try {\n fn({ ...this._settings });\n } catch {\n // a listener throwing must not break message handling\n }\n }\n break;\n }\n case 'USER_CONTEXT':\n case 'CANVAS_CONTEXT':\n // Phase 3+ \u2014 reserved for future use\n break;\n }\n }\n\n private send(type: string, payload: Record<string, unknown>): void {\n // Fire and forget \u2014 sign is async but we don't need to await delivery\n this.buildMessage(type, payload).then((msg) => {\n window.parent.postMessage(msg, '*');\n });\n }\n\n private async buildMessage(\n type: string,\n payload: Record<string, unknown>\n ): Promise<CanvasMessage> {\n const nonce = safeRandomUUID();\n const timestamp = Date.now();\n\n let token: string;\n if (this._hmacSecret) {\n token = await signToken(this._hmacSecret, type, this._widgetId, nonce, timestamp);\n } else {\n token = DEV_MODE_TOKEN;\n }\n\n return {\n protocol: PROTOCOL,\n type,\n widgetId: this._widgetId,\n nonce,\n timestamp,\n token,\n payload,\n };\n }\n\n /** Tear down the SDK instance, remove all listeners, and reject pending consent requests. */\n destroy(): void {\n window.removeEventListener('message', this._messageHandler);\n this._stateListeners.length = 0;\n this._eventListeners.clear();\n for (const [, pending] of this._pendingConsents) {\n pending.reject(new Error('Widget destroyed'));\n }\n this._pendingConsents.clear();\n }\n}\n\n// ============================================\n// Public API: CanvasWidget.init()\n// ============================================\n\n/**\n * Entry point for the Flowstate Canvas Widget SDK.\n *\n * Usage:\n * ```ts\n * import { CanvasWidget } from '@goflowstate/widget-sdk';\n * const widget = await CanvasWidget.init();\n * ```\n */\nexport const CanvasWidget = {\n /**\n * Initialize a group-session client for widgets hosted on the /w/[slug]\n * session page (ticket handshake + session REST rails). Returns null when\n * the launch context carries no session params \u2014 see `initSession` in\n * ./session for details and `createMockSession` for standalone dev.\n */\n initSession,\n\n /**\n * Initialize the SDK and establish communication with the canvas host.\n *\n * Sends a READY message to the host and waits for an INIT response.\n * Resolves with a fully initialized {@link CanvasWidgetSDK} instance.\n * Rejects after 5 seconds if no INIT is received from the host.\n */\n init(): Promise<CanvasWidgetSDK> {\n return new Promise<CanvasWidgetSDK>((resolve, reject) => {\n const timeout = setTimeout(() => {\n window.removeEventListener('message', handler);\n reject(new Error('CanvasWidget.init() timed out \u2014 no INIT received from host within 5s'));\n }, INIT_TIMEOUT_MS);\n\n const handler = (event: MessageEvent) => {\n const msg = event.data;\n if (!msg || typeof msg !== 'object' || msg.protocol !== PROTOCOL) return;\n if (msg.type !== 'INIT') return;\n\n clearTimeout(timeout);\n window.removeEventListener('message', handler);\n\n const payload = msg.payload as InitPayload;\n const sdk = new CanvasWidgetSDKImpl(payload);\n resolve(sdk);\n };\n\n window.addEventListener('message', handler);\n\n // Send READY to host \u2014 the host will respond with INIT\n const readyMsg: CanvasMessage = {\n protocol: PROTOCOL,\n type: 'READY',\n widgetId: '', // Not yet known \u2014 host identifies us by iframe source\n nonce: safeRandomUUID(),\n timestamp: Date.now(),\n token: DEV_MODE_TOKEN, // READY is always allowed, token doesn't matter\n payload: { manifestVersion: '1' },\n };\n\n window.parent.postMessage(readyMsg, '*');\n });\n },\n};\n", "// packages/widget-sdk/src/service.ts\n\nimport type { WidgetServiceClient } from './types';\n\nexport interface CreateServiceClientOptions {\n baseUrl: string;\n getJwt: (forceRefresh?: boolean) => Promise<string>;\n fetchImpl?: typeof fetch;\n}\n\nexport function createServiceClient(opts: CreateServiceClientOptions): WidgetServiceClient {\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);\n const baseUrl = opts.baseUrl.replace(/\\/+$/, '');\n\n async function call(path: string, init: RequestInit, token: string): Promise<Response> {\n const url = `${baseUrl}${path.startsWith('/') ? path : `/${path}`}`;\n const headers = new Headers(init.headers);\n headers.set('authorization', `Bearer ${token}`);\n if (init.body && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n return fetchImpl(url, { ...init, headers });\n }\n\n return {\n async fetch<T = unknown>(path: string, init: RequestInit = {}): Promise<T> {\n let token = await opts.getJwt(false);\n let res = await call(path, init, token);\n\n if (res.status === 401) {\n token = await opts.getJwt(true);\n res = await call(path, init, token);\n }\n\n if (!res.ok) {\n let body: { message?: string } = {};\n try {\n body = (await res.json()) as { message?: string };\n } catch {\n // non-JSON body \u2014 ignore\n }\n throw new Error(`Widget service request failed (${res.status}): ${body.message ?? ''}`);\n }\n return (await res.json()) as T;\n },\n };\n}\n", "// Session module \u2014 the official client for Flowstate widget-session rails.\n//\n// Group-session widgets (decision sprint, and any future multi-contributor\n// widget) run inside the app's /w/[slug] page. The page hands the iframe a\n// single-use auth ticket over postMessage (ADR-025: never in the URL on the\n// production path), the ticket is redeemed for a session token, and every\n// session verb is a REST call against /api/w/:slug on the widget gateway.\n//\n// This module wraps that whole contract so a widget never hand-rolls the\n// bridge:\n//\n// ```ts\n// import { CanvasWidget } from '@goflowstate/widget-sdk';\n// const session = await CanvasWidget.initSession();\n// if (session) {\n// const state = await session.join();\n// const stop = session.poll((s) => render(s));\n// await session.contribute({ evidence: { pins } });\n// } else {\n// // no launch params \u2014 standalone dev; use createMockSession()\n// }\n// ```\n//\n// The wire vocabulary is the generic session shape the rails speak\n// (historically named after the expedition: `tripId`, `myWishlist`, `plan`).\n// This module maps it onto neutral names once, here, so widgets never see the\n// legacy keys.\n\n// ============================================\n// State view\n// ============================================\n\nexport interface SessionMember {\n userId: string;\n name: string;\n /** Deterministic per-user accent color (hex), stable across sessions. */\n color: string;\n /** True once this member has contributed (sealed) in the current round. */\n sealed: boolean;\n isMe: boolean;\n isHost: boolean;\n}\n\n/** Gate policy surfaced to widgets (ADR-047). Optional server-side \u2014 links\n * minted without a gate report null. */\nexport interface SessionGateView {\n deadlineAt: string | null;\n quorum?: { mode: 'all' | 'count' | 'pct'; min?: number };\n}\n\n/** Flow context when this session is a node in a running flow (ADR-047). */\nexport interface SessionFlowView {\n runId: string;\n nodeIndex: number;\n nodeCount: number;\n nodes: Array<{\n key: string;\n title: string;\n status: 'pending' | 'active' | 'resolved' | 'expired';\n artifactId?: string;\n linkSlug?: string;\n }>;\n}\n\n/** The rails' phase model: `setup` until the host commits framing, `open`\n * while contributions are being collected, `sealed` once quorum is met and\n * the session awaits close, `resolved` after synthesis produced the\n * artifact. (Wire values `wishlist`/`plan` are mapped to `open`/`resolved`.) */\nexport type SessionPhase = 'setup' | 'open' | 'sealed' | 'resolved';\n\nexport interface SessionStateView<\n TSetup = Record<string, unknown>,\n TContribution = unknown,\n TDoc = Record<string, unknown>,\n> {\n /** The widget-link slug \u2014 the session's identity and its /w/:slug URL key. */\n slug: string;\n phase: SessionPhase;\n status: string;\n setup: TSetup | null;\n members: SessionMember[];\n sealedCount: number;\n memberCount: number;\n /** True when every member has sealed (legacy all-hands quorum). */\n allSealed: boolean;\n /** True when the configured quorum policy is satisfied (falls back to\n * allSealed when the link carries no gate). */\n quorumMet: boolean;\n gate: SessionGateView | null;\n flow: SessionFlowView | null;\n /** Own contribution only \u2014 others stay blind until synthesis. */\n myContribution: TContribution | null;\n /** The synthesized document, present once the session resolved. */\n doc: TDoc | null;\n artifactId: string | null;\n /** Widget-specific state-view extension (e.g. the decision sprint's stage\n * + ballot), passed through verbatim when the server provides one. */\n extension: Record<string, unknown> | null;\n}\n\n// ============================================\n// Client interface\n// ============================================\n\nexport interface SessionPollOptions {\n /** Poll cadence in ms. Default 4000 (matches the historical widgets). */\n intervalMs?: number;\n}\n\nexport interface SessionClient<\n TSetup = Record<string, unknown>,\n TContribution = unknown,\n TDoc = Record<string, unknown>,\n> {\n readonly slug: string;\n /** Base URL of the web app (for reveal links and parent navigation). */\n readonly webBase: string;\n /** Base URL of the mobile PWA, when the host provided one via the optional\n * `mobile` iframe param. Participant-facing join links (e.g. a projected\n * QR) should prefer it and fall back to `webBase` when absent. Optional so\n * hand-rolled SessionClient implementations keep compiling. */\n readonly mobileBase?: string | null;\n\n /** Enter the session: the first identified enter creates the group. */\n join(): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Fetch the current blind-aware state. */\n state(): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Poll state on a cadence. Returns a stop function. Errors are passed to\n * onError (or swallowed) so a transient failure never kills the loop. */\n poll(\n onState: (s: SessionStateView<TSetup, TContribution, TDoc>) => void,\n opts?: SessionPollOptions & { onError?: (err: unknown) => void }\n ): () => void;\n /** Host-only: commit the session framing. */\n commitSetup(setup: TSetup): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Upsert my contribution (seal). One contribution row per member; widgets\n * with multi-stage payloads post the merged object every time. */\n contribute(payload: TContribution): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Host-only close: synthesis runs, the artifact is born. Idempotent. */\n close(): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Widget-specific verb that returns a session STATE (e.g. the sprint's\n * /advance). Body is JSON-encoded; the response's state is returned. */\n action(verb: string, body?: unknown): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Widget-specific verb with a NON-state response (e.g. /assist). Returns\n * the response's raw `data` value. */\n request<T = unknown>(verb: string, body?: unknown): Promise<T>;\n /** Open a Flowstate URL outside the widget (parent navigation preferred \u2014\n * the /w page owns the session cookie). */\n openExternal(url: string): void;\n /** Deep link to an artifact reveal on the web app. */\n revealUrl(artifactId: string): string;\n}\n\n// ============================================\n// Launch params + ticket handshake (ADR-025)\n// ============================================\n\nexport interface SessionLaunchParams {\n slug: string;\n /** Dev-only fallback: a ticket in the URL. Production receives the ticket\n * via the parent's `flowstate:init` postMessage \u2014 URLs leak into\n * history/referrers/logs. */\n ticket: string | null;\n apiBase: string;\n webBase: string;\n /** Base URL of the mobile PWA (the optional `mobile` iframe param). Null\n * when the host didn't send one \u2014 legacy hosts never do \u2014 so consumers\n * fall back to `webBase` links. Optional so hand-built params keep\n * compiling. */\n mobileBase?: string | null;\n}\n\nexport function readSessionLaunchParams(): SessionLaunchParams | null {\n if (typeof window === 'undefined') return null;\n const q = new URLSearchParams(window.location.search);\n // `tripId` is the historical param name the /w page passes; keep reading it\n // so hosts don't have to change. `slug` is accepted as the modern alias.\n const slug = q.get('slug') ?? q.get('tripId') ?? '';\n if (!slug) return null;\n // `mobile` is optional and has NO default: absent means the host predates\n // the PWA doors (or has none configured) and consumers must keep minting\n // legacy webBase links.\n const mobile = q.get('mobile');\n return {\n slug,\n ticket: q.get('ticket'),\n apiBase: (q.get('api') ?? 'http://localhost:13002').replace(/\\/$/, ''),\n webBase: (q.get('web') ?? 'http://localhost:14321').replace(/\\/$/, ''),\n mobileBase: mobile ? mobile.replace(/\\/$/, '') : null,\n };\n}\n\nconst SESSION_INIT_TIMEOUT_MS = 10_000;\nconst DEFAULT_POLL_INTERVAL_MS = 4_000;\n\n/** Announce readiness to the parent /w page and wait for it to hand over the\n * single-use session ticket. */\nfunction awaitInitTicket(webBase: string): Promise<string> {\n return new Promise((resolve, reject) => {\n if (typeof window === 'undefined' || window.parent === window) {\n reject(new Error('no parent page to receive a ticket from'));\n return;\n }\n const webOrigin = new URL(webBase).origin;\n const timer = setTimeout(() => {\n window.removeEventListener('message', onMessage);\n reject(new Error('timed out waiting for the session handoff'));\n }, SESSION_INIT_TIMEOUT_MS);\n const onMessage = (e: MessageEvent) => {\n if (e.origin !== webOrigin) return;\n const data = e.data as { type?: string; ticket?: string } | null;\n if (data?.type !== 'flowstate:init' || typeof data.ticket !== 'string') return;\n clearTimeout(timer);\n window.removeEventListener('message', onMessage);\n resolve(data.ticket);\n };\n window.addEventListener('message', onMessage);\n window.parent.postMessage({ type: 'flowstate:ready' }, webOrigin);\n });\n}\n\n// ============================================\n// Wire mapping\n// ============================================\n\ninterface WireMember {\n userId: string;\n name: string;\n color: string;\n sealed: boolean;\n isMe: boolean;\n isHost: boolean;\n}\n\ninterface WireSessionState {\n tripId: string;\n phase: 'setup' | 'wishlist' | 'sealed' | 'plan';\n status: string;\n setup: Record<string, unknown> | null;\n members: WireMember[];\n sealedCount: number;\n memberCount: number;\n allSealed: boolean;\n quorumMet?: boolean;\n gate?: SessionGateView | null;\n flow?: SessionFlowView | null;\n myWishlist: unknown;\n plan: Record<string, unknown> | null;\n artifactId: string | null;\n extension?: Record<string, unknown> | null;\n}\n\nconst WIRE_PHASE: Record<WireSessionState['phase'], SessionPhase> = {\n setup: 'setup',\n wishlist: 'open',\n sealed: 'sealed',\n plan: 'resolved',\n};\n\nfunction toStateView<TSetup, TContribution, TDoc>(\n s: WireSessionState\n): SessionStateView<TSetup, TContribution, TDoc> {\n return {\n slug: s.tripId,\n phase: WIRE_PHASE[s.phase] ?? 'open',\n status: s.status,\n setup: (s.setup as TSetup | null) ?? null,\n members: s.members,\n sealedCount: s.sealedCount,\n memberCount: s.memberCount,\n allSealed: s.allSealed,\n quorumMet: s.quorumMet ?? s.allSealed,\n gate: s.gate ?? null,\n flow: s.flow ?? null,\n myContribution: (s.myWishlist as TContribution | null) ?? null,\n doc: (s.plan as TDoc | null) ?? null,\n artifactId: s.artifactId,\n extension: s.extension ?? null,\n };\n}\n\n// ============================================\n// HTTP client over the session rails\n// ============================================\n\ntype ExternalOpener = (url: string) => void;\n\nconst parentOpener = (webBase: string): ExternalOpener => {\n return (url) => {\n if (typeof window !== 'undefined' && window.parent !== window) {\n // Ask the /w page (same session cookie) to navigate itself.\n window.parent.postMessage({ type: 'flowstate:navigate', url }, webBase);\n return;\n }\n if (typeof window !== 'undefined') window.open(url, '_blank', 'noopener');\n };\n};\n\nclass HttpSessionClient<TSetup, TContribution, TDoc>\n implements SessionClient<TSetup, TContribution, TDoc>\n{\n readonly slug: string;\n readonly webBase: string;\n readonly mobileBase: string | null;\n private readonly apiBase: string;\n private readonly tokenPromise: Promise<string>;\n private readonly opener: ExternalOpener;\n\n constructor(params: SessionLaunchParams, opener?: ExternalOpener) {\n this.slug = params.slug;\n this.webBase = params.webBase;\n this.mobileBase = params.mobileBase ?? null;\n this.apiBase = params.apiBase;\n this.opener = opener ?? parentOpener(params.webBase);\n // Redeem eagerly: the ticket is single-use + short-lived; trade it for a\n // session token before the user spends time in the widget.\n this.tokenPromise = this.acquireTicket(params).then((t) => this.redeem(t));\n }\n\n private async acquireTicket(params: SessionLaunchParams): Promise<string> {\n if (params.ticket) return params.ticket; // dev fallback\n return awaitInitTicket(params.webBase);\n }\n\n private async redeem(ticket: string): Promise<string> {\n const res = await fetch(`${this.apiBase}/api/auth/ticket/redeem`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ticket }),\n });\n if (!res.ok) throw new Error(`ticket redeem failed (${res.status})`);\n const json = (await res.json()) as { data: { token: string } };\n return json.data.token;\n }\n\n private async call(\n path: string,\n init: RequestInit = {}\n ): Promise<SessionStateView<TSetup, TContribution, TDoc>> {\n const token = await this.tokenPromise;\n const res = await fetch(`${this.apiBase}/api/w/${this.slug}${path}`, {\n ...init,\n headers: {\n authorization: `Bearer ${token}`,\n ...(init.body ? { 'content-type': 'application/json' } : {}),\n ...(init.headers ?? {}),\n },\n });\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { message?: string };\n throw new Error(body.message ?? `session service ${res.status}`);\n }\n const json = (await res.json()) as {\n data: WireSessionState | { state: WireSessionState };\n };\n const data = json.data as Record<string, unknown>;\n return toStateView((data.state ?? data) as WireSessionState);\n }\n\n join() {\n return this.call('/enter', { method: 'POST' });\n }\n\n state() {\n return this.call('/state');\n }\n\n poll(\n onState: (s: SessionStateView<TSetup, TContribution, TDoc>) => void,\n opts?: SessionPollOptions & { onError?: (err: unknown) => void }\n ): () => void {\n const interval = opts?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n const tick = async () => {\n try {\n const s = await this.state();\n if (!stopped) onState(s);\n } catch (err) {\n opts?.onError?.(err);\n }\n if (!stopped) timer = setTimeout(tick, interval);\n };\n void tick();\n return () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n };\n }\n\n commitSetup(setup: TSetup) {\n return this.call('/setup', { method: 'POST', body: JSON.stringify({ setup }) });\n }\n\n contribute(payload: TContribution) {\n return this.call('/contribute', { method: 'POST', body: JSON.stringify({ payload }) });\n }\n\n close() {\n return this.call('/close', { method: 'POST' });\n }\n\n action(verb: string, body?: unknown) {\n return this.call(`/${verb.replace(/^\\//, '')}`, {\n method: 'POST',\n body: JSON.stringify(body ?? {}),\n });\n }\n\n async request<T = unknown>(verb: string, body?: unknown): Promise<T> {\n const token = await this.tokenPromise;\n const res = await fetch(`${this.apiBase}/api/w/${this.slug}/${verb.replace(/^\\//, '')}`, {\n method: 'POST',\n headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },\n body: JSON.stringify(body ?? {}),\n });\n if (!res.ok) {\n const errBody = (await res.json().catch(() => ({}))) as { message?: string };\n throw new Error(errBody.message ?? `session service ${res.status}`);\n }\n const json = (await res.json()) as { data: T };\n return json.data;\n }\n\n openExternal(url: string): void {\n this.opener(url);\n }\n\n revealUrl(artifactId: string): string {\n return `${this.webBase}/a/${artifactId}`;\n }\n}\n\n// ============================================\n// Standalone mock (widget dev outside the canvas)\n// ============================================\n\nexport interface MockSessionOptions<TSetup, TDoc> {\n /** Display name for the single mock member. Default \"You\". */\n me?: string;\n /** Extra fake members (all unsealed initially). */\n others?: string[];\n setup?: TSetup | null;\n /** Called on close() to synthesize the mock doc. */\n synthesize?: (contributions: unknown[]) => TDoc;\n}\n\n/** In-memory SessionClient for standalone dev \u2014 same interface, no host, no\n * network. The mock's \"host\" is always you. */\nexport function createMockSession<\n TSetup = Record<string, unknown>,\n TContribution = unknown,\n TDoc = Record<string, unknown>,\n>(opts: MockSessionOptions<TSetup, TDoc> = {}): SessionClient<TSetup, TContribution, TDoc> {\n const palette = ['#8b7ef8', '#f8a34d', '#4dc9a4', '#f26d8f', '#5aa9e6'];\n const members: SessionMember[] = [\n { userId: 'mock-me', name: opts.me ?? 'You', color: palette[0], sealed: false, isMe: true, isHost: true },\n ...(opts.others ?? []).map((name, i) => ({\n userId: `mock-${i + 1}`,\n name,\n color: palette[(i + 1) % palette.length],\n sealed: false,\n isMe: false,\n isHost: false,\n })),\n ];\n let setup: TSetup | null = opts.setup ?? null;\n let myContribution: TContribution | null = null;\n let doc: TDoc | null = null;\n let status = 'open';\n\n const view = (): SessionStateView<TSetup, TContribution, TDoc> => {\n const sealedCount = members.filter((m) => m.sealed).length;\n const allSealed = members.length > 0 && sealedCount >= members.length;\n const phase: SessionPhase =\n status === 'closed' && doc ? 'resolved' : !setup ? 'setup' : allSealed ? 'sealed' : 'open';\n return {\n slug: 'mock-session',\n phase,\n status,\n setup,\n members: members.map((m) => ({ ...m })),\n sealedCount,\n memberCount: members.length,\n allSealed,\n quorumMet: allSealed,\n gate: null,\n flow: null,\n myContribution,\n doc,\n artifactId: doc ? 'mock-artifact' : null,\n extension: null,\n };\n };\n\n return {\n slug: 'mock-session',\n webBase: 'http://localhost:14321',\n async join() {\n return view();\n },\n async state() {\n return view();\n },\n poll(onState, pollOpts) {\n const interval = pollOpts?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const timer = setInterval(() => onState(view()), interval);\n onState(view());\n return () => clearInterval(timer);\n },\n async commitSetup(s: TSetup) {\n setup = s;\n return view();\n },\n async contribute(payload: TContribution) {\n myContribution = payload;\n const me = members.find((m) => m.isMe);\n if (me) me.sealed = true;\n return view();\n },\n async close() {\n status = 'closed';\n doc = opts.synthesize\n ? opts.synthesize([myContribution])\n : ({ mock: true } as unknown as TDoc);\n return view();\n },\n async action() {\n return view();\n },\n async request<T = unknown>() {\n return {} as T;\n },\n openExternal(url: string) {\n if (typeof window !== 'undefined') window.open(url, '_blank', 'noopener');\n },\n revealUrl(artifactId: string) {\n return `http://localhost:14321/a/${artifactId}`;\n },\n };\n}\n\n// ============================================\n// Entry point\n// ============================================\n\n/**\n * Initialize a widget-session client from the launch context.\n *\n * Returns `null` when the page carries no session launch params (standalone\n * dev, or a canvas-hosted widget that has no group session) \u2014 callers fall\n * back to {@link createMockSession} or their own local state.\n */\nexport function initSession<\n TSetup = Record<string, unknown>,\n TContribution = unknown,\n TDoc = Record<string, unknown>,\n>(): SessionClient<TSetup, TContribution, TDoc> | null {\n const params = readSessionLaunchParams();\n if (!params) return null;\n return new HttpSessionClient<TSetup, TContribution, TDoc>(params);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["// ============================================\n// Canvas Widget SDK \u2014 runs inside widget iframes\n// ============================================\n//\n// Usage in a widget:\n// <script src=\"canvas-widget-sdk.js\"></script>\n// <script>\n// const widget = await CanvasWidget.init();\n// widget.onStateChange(state => { ... });\n// </script>\n\nimport type {\n CanvasWidgetSDK,\n WidgetDisplayState,\n WidgetEvent,\n InitPayload,\n StateChangePayload,\n EventBroadcastPayload,\n ConsentResult,\n ConsentResultPayload,\n UserContext,\n StorageAPI,\n SettingsAPI,\n WidgetServiceClient,\n AiAPI,\n AiCallOptions,\n AiCompleteResult,\n AiStreamEvent,\n DbAPI,\n DbCollection,\n DbRecord,\n DbListOptions,\n} from './types';\nimport { createServiceClient } from './service';\nimport { initSession } from './session';\n\nconst PROTOCOL = 'canvas-widget-v1';\nconst DEV_MODE_TOKEN = 'dev-mode-skip';\nconst INIT_TIMEOUT_MS = 5000;\n\n// crypto.randomUUID only exists in SECURE contexts (HTTPS or localhost). When\n// a widget iframe is served over plain http on a LAN IP (e.g. dev-on-phone at\n// http://10.0.0.x), it's undefined and init() throws \u2014 the widget never\n// hands shakes and the host times it out (\"failed to load\"). Fall back to a\n// getRandomValues-based v4 UUID (also secure-context-independent).\nfunction safeRandomUUID(): string {\n const c = globalThis.crypto;\n if (c && typeof c.randomUUID === 'function') return c.randomUUID();\n const b = new Uint8Array(16);\n if (c && typeof c.getRandomValues === 'function') c.getRandomValues(b);\n else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);\n b[6] = (b[6] & 0x0f) | 0x40;\n b[8] = (b[8] & 0x3f) | 0x80;\n const h = Array.from(b, (x) => x.toString(16).padStart(2, '0'));\n return `${h[0]}${h[1]}${h[2]}${h[3]}-${h[4]}${h[5]}-${h[6]}${h[7]}-${h[8]}${h[9]}-${h[10]}${h[11]}${h[12]}${h[13]}${h[14]}${h[15]}`;\n}\n\ninterface CanvasMessage {\n protocol: string;\n type: string;\n widgetId: string;\n nonce: string;\n timestamp: number;\n token: string;\n payload: Record<string, unknown>;\n}\n\n// ============================================\n// HMAC utilities (mirrored from host side)\n// ============================================\n\nlet cachedKey: { secret: string; key: CryptoKey } | null = null;\n\nasync function getHmacKey(secret: string): Promise<CryptoKey> {\n if (cachedKey && cachedKey.secret === secret) return cachedKey.key;\n\n const encoded = new TextEncoder().encode(secret);\n const buf = new ArrayBuffer(encoded.byteLength);\n new Uint8Array(buf).set(encoded);\n\n const key = await crypto.subtle.importKey('raw', buf, { name: 'HMAC', hash: 'SHA-256' }, false, [\n 'sign',\n ]);\n\n cachedKey = { secret, key };\n return key;\n}\n\nasync function signToken(\n secret: string,\n type: string,\n widgetId: string,\n nonce: string,\n timestamp: number\n): Promise<string> {\n const key = await getHmacKey(secret);\n const data = new TextEncoder().encode(`${type}:${widgetId}:${nonce}:${timestamp}`);\n const buf = new ArrayBuffer(data.byteLength);\n new Uint8Array(buf).set(data);\n const sig = await crypto.subtle.sign('HMAC', key, buf);\n return Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n// ============================================\n// SDK Implementation\n// ============================================\n\n/** Internal SDK implementation \u2014 not exposed directly to widget authors. */\nexport class CanvasWidgetSDKImpl implements CanvasWidgetSDK {\n private _widgetId: string;\n private _state: WidgetDisplayState;\n private _hmacSecret: string | null;\n private _user: UserContext | null;\n private _namespace: { officeId: string; memberCount: number } | null;\n private _widgetApiUrl: string | null;\n private _widgetApiToken: string | null;\n private _installationId: string;\n private _pendingConsents = new Map<\n string,\n { resolve: (r: ConsentResult) => void; reject: (e: Error) => void }\n >();\n private _cachedJwt: { token: string; expiresAt: number } | null = null;\n private _stateListeners: Array<(state: WidgetDisplayState) => void> = [];\n private _displayState: WidgetDisplayState = 'collapsed';\n private _displayStateCallbacks: Array<(state: WidgetDisplayState) => void> = [];\n private _focused = false;\n private _focusCallbacks: Array<(focused: boolean) => void> = [];\n private _settingsRemoteListeners: Array<(settings: Record<string, unknown>) => void> = [];\n private _eventListeners = new Map<string, Array<(event: WidgetEvent) => void>>();\n private _settings: Record<string, unknown>;\n private _messageHandler: (event: MessageEvent) => void;\n\n /** Authenticated HTTP client scoped to the widget's declared service URL. Undefined when no service is declared. */\n readonly service: WidgetServiceClient | undefined;\n\n constructor(init: InitPayload) {\n this._widgetId = init.widgetId;\n this._state = init.state;\n this._hmacSecret = init.hmacSecret;\n this._user = init.user;\n this._namespace = init.namespace ?? null;\n this._widgetApiUrl = init.widgetApiUrl;\n this._widgetApiToken = init.widgetApiToken ?? null;\n this._installationId = init.installationId;\n this._settings = init.settings ?? {};\n\n // Wire sdk.service when the host provides a service URL from the manifest\n if (init.serviceUrl) {\n this.service = createServiceClient({\n baseUrl: init.serviceUrl,\n getJwt: (forceRefresh?: boolean) => this.getJwt(forceRefresh),\n });\n }\n\n this._messageHandler = this.handleMessage.bind(this);\n window.addEventListener('message', this._messageHandler);\n }\n\n /** The unique identifier for this widget instance on the canvas. */\n get widgetId(): string {\n return this._widgetId;\n }\n\n /** The current display state of the widget (collapsed, expanding, expanded, collapsing). */\n get state(): WidgetDisplayState {\n return this._state;\n }\n\n /** The authenticated user context, or null when user data access has not been granted. */\n get user(): UserContext | null {\n return this._user;\n }\n\n /** Namespace (office) context \u2014 id and owner-inclusive member count \u2014 or\n * null on a personal canvas. What the `canvas.context` permission grants. */\n get namespace(): { officeId: string; memberCount: number } | null {\n return this._namespace;\n }\n\n /** Subscribe to display state changes; returns an unsubscribe function. */\n onStateChange(cb: (state: WidgetDisplayState) => void): () => void {\n this._stateListeners.push(cb);\n return () => {\n const idx = this._stateListeners.indexOf(cb);\n if (idx !== -1) this._stateListeners.splice(idx, 1);\n };\n }\n\n /** Ask the canvas host to expand this widget into its modal view. */\n requestExpand(): void {\n this.send('EXPAND_REQUEST', {});\n }\n\n /** Ask the canvas host to collapse this widget back to its tile view. */\n requestCollapse(): void {\n this.send('COLLAPSE_REQUEST', {});\n }\n\n /** Get the current display state */\n getDisplayState(): WidgetDisplayState {\n return this._displayState;\n }\n\n /** Register a callback for display state changes */\n onDisplayStateChange(callback: (state: WidgetDisplayState) => void): () => void {\n this._displayStateCallbacks.push(callback);\n return () => {\n this._displayStateCallbacks = this._displayStateCallbacks.filter((cb) => cb !== callback);\n };\n }\n\n /** Is this widget the user's current focus? Updates via FOCUS_CHANGE. */\n isFocused(): boolean {\n return this._focused;\n }\n\n /** Subscribe to focus transitions. Returns an unsubscribe function. */\n onFocusChange(callback: (focused: boolean) => void): () => void {\n this._focusCallbacks.push(callback);\n return () => {\n this._focusCallbacks = this._focusCallbacks.filter((cb) => cb !== callback);\n };\n }\n\n /** Request promotion to fullscreen from partial */\n requestFullscreen(): void {\n this.send('FULLSCREEN_REQUEST', {});\n }\n\n /** Broadcast an event on the given topic to all subscribed widgets on the canvas. */\n emit(topic: string, payload: unknown): void {\n this.send('EVENT_EMIT', { topic, payload });\n }\n\n /** Subscribe to events on the given topic; returns an unsubscribe function. */\n subscribe(topic: string, cb: (event: WidgetEvent) => void): () => void {\n const list = this._eventListeners.get(topic) ?? [];\n list.push(cb);\n this._eventListeners.set(topic, list);\n return () => {\n const current = this._eventListeners.get(topic);\n if (!current) return;\n const idx = current.indexOf(cb);\n if (idx !== -1) current.splice(idx, 1);\n };\n }\n\n /** Send a structured log entry to the canvas host for centralized debugging. */\n log(level: 'info' | 'warn' | 'error', message: string, data?: unknown): void {\n this.send('LOG', { level, message, data: data ?? null });\n }\n\n /** Request user consent for a named data scope; resolves with the consent decision. */\n async requestConsent(scope: string, reason: string): Promise<ConsentResult> {\n return new Promise<ConsentResult>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this._pendingConsents.delete(scope);\n reject(new Error('Consent request timed out'));\n }, 60000);\n\n this._pendingConsents.set(scope, {\n resolve: (r: ConsentResult) => {\n clearTimeout(timeout);\n resolve(r);\n },\n reject: (e: Error) => {\n clearTimeout(timeout);\n reject(e);\n },\n });\n\n this.send('CONSENT_REQUEST', { scope, reason });\n });\n }\n\n /** Return the cached JWT for authenticating requests to external APIs; throws if no token is available. Pass forceRefresh=true to bypass the cache. */\n async getJwt(forceRefresh?: boolean): Promise<string> {\n if (!forceRefresh && this._cachedJwt && Date.now() < this._cachedJwt.expiresAt - 30000) {\n return this._cachedJwt.token;\n }\n throw new Error('No cached JWT \u2014 widget must use OAuth flow for token refresh');\n }\n\n /** Persistent key-value storage scoped to this widget installation. */\n get storage(): StorageAPI {\n return {\n get: (key: string) => this.storageRequest('GET', key),\n set: (key: string, value: unknown) => this.storageRequest('PUT', key, value).then(() => {}),\n delete: (key: string) => this.storageRequest('DELETE', key).then(() => {}),\n };\n }\n\n /** Ad-hoc collections/records backend scoped to the canvas/room. */\n get db(): DbAPI {\n const req = (path: string, method: string, body?: unknown) => this.widgetApiRequest(path, method, body);\n return {\n collection(name: string): DbCollection {\n const base = `/widget-api/db/${encodeURIComponent(name)}`;\n return {\n create: (data) => req(base, 'POST', data) as Promise<DbRecord>,\n get: (id) => req(`${base}/${encodeURIComponent(id)}`, 'GET') as Promise<DbRecord>,\n list: (opts?: DbListOptions) => {\n const qs = new URLSearchParams();\n if (opts?.limit != null) qs.set('limit', String(opts.limit));\n if (opts?.cursor) qs.set('cursor', opts.cursor);\n if (opts?.sort) qs.set('sort', opts.sort);\n if (opts?.mine) qs.set('mine', 'true');\n const suffix = qs.toString() ? `?${qs.toString()}` : '';\n return req(`${base}${suffix}`, 'GET') as Promise<DbRecord[]>;\n },\n update: (id, patch) => req(`${base}/${encodeURIComponent(id)}`, 'PATCH', patch) as Promise<DbRecord>,\n delete: (id) => (req(`${base}/${encodeURIComponent(id)}`, 'DELETE') as Promise<unknown>).then(() => {}),\n };\n },\n };\n }\n\n /** Platform LLM access (widget.* aliases only). Requests ride the same\n * widget-gateway auth as db/storage; the platform meters every call and\n * attributes it to this widget's publishing org. */\n get ai(): AiAPI {\n const complete = (opts: AiCallOptions): Promise<AiCompleteResult> =>\n this.widgetApiRequest('/widget-api/ai/complete', 'POST', opts) as Promise<AiCompleteResult>;\n const streamRequest = (opts: AiCallOptions): Promise<Response> => this.widgetApiStream(opts);\n return {\n complete,\n // Async generator over the gateway's SSE frames. Ends on [DONE] or\n // stream end; returning early (break) cancels the fetch body, which\n // the gateway sees as a disconnect and aborts the upstream call, so\n // an abandoned stream stops generating and billing.\n async *stream(opts: AiCallOptions): AsyncGenerator<AiStreamEvent> {\n const response = await streamRequest(opts);\n const reader = response.body?.getReader();\n if (!reader) throw new Error('ai.stream: response has no body');\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const frames = buffer.split('\\n\\n');\n buffer = frames.pop() ?? '';\n for (const frame of frames) {\n const dataLine = frame.split('\\n').find((l) => l.startsWith('data:'));\n if (!dataLine) continue;\n const payload = dataLine.slice(5).trim();\n if (!payload || payload === '[DONE]') continue;\n let parsed: AiStreamEvent;\n try {\n parsed = JSON.parse(payload) as AiStreamEvent;\n } catch {\n continue;\n }\n yield parsed;\n }\n }\n } finally {\n // Break/return path: releases the connection so the gateway's\n // req.on('close') fires and the upstream call aborts.\n await reader.cancel().catch(() => {});\n }\n },\n };\n }\n\n /** Widget configuration settings persisted by the canvas host. */\n get settings(): SettingsAPI {\n return {\n get: () => ({ ...this._settings }),\n set: (newSettings: Record<string, unknown>) => {\n this._settings = { ...this._settings, ...newSettings };\n this.send('SETTINGS_WRITE', { settings: this._settings });\n },\n onRemoteChange: (cb: (settings: Record<string, unknown>) => void) => {\n this._settingsRemoteListeners.push(cb);\n return () => {\n this._settingsRemoteListeners = this._settingsRemoteListeners.filter((fn) => fn !== cb);\n };\n },\n };\n }\n\n /** Display a toast notification in the canvas host UI. */\n async notify(message: string, opts?: { level?: string; duration?: number }): Promise<void> {\n this.send('NOTIFICATION', {\n message,\n level: opts?.level ?? 'info',\n duration: opts?.duration ?? 5000,\n });\n }\n\n // \u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Raw streaming POST against the widget gateway \u2014 same auth as\n * widgetApiRequest, but returns the Response for SSE consumption\n * instead of parsing a JSON envelope. */\n private async widgetApiStream(body: unknown): Promise<Response> {\n if (!this._widgetApiUrl) {\n throw new Error('Widget API Gateway URL not available');\n }\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this._widgetApiToken) {\n headers['Authorization'] = `Bearer ${this._widgetApiToken}`;\n }\n const response = await fetch(`${this._widgetApiUrl}/widget-api/ai/stream`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n if (!response.ok) {\n const err = await response.json().catch(() => ({}));\n throw new Error((err as Record<string, string>).message ?? 'ai.stream request failed');\n }\n return response;\n }\n\n private async widgetApiRequest(path: string, method: string, body?: unknown): Promise<unknown> {\n if (!this._widgetApiUrl) {\n throw new Error('Widget API Gateway URL not available');\n }\n const url = `${this._widgetApiUrl}${path}`;\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this._widgetApiToken) {\n headers['Authorization'] = `Bearer ${this._widgetApiToken}`;\n }\n const opts: RequestInit = { method, headers };\n if (body !== undefined && method !== 'GET' && method !== 'DELETE') {\n opts.body = JSON.stringify(body);\n }\n const response = await fetch(url, opts);\n if (!response.ok) {\n const err = await response.json().catch(() => ({}));\n throw new Error((err as Record<string, string>).message ?? `Request ${method} ${path} failed`);\n }\n if (method === 'GET' || method === 'POST' || method === 'PATCH') {\n const result = await response.json();\n return (result as Record<string, unknown>).data;\n }\n return undefined;\n }\n\n private storageRequest(method: string, key: string, value?: unknown): Promise<unknown> {\n return this.widgetApiRequest(`/widget-api/storage/${encodeURIComponent(key)}`, method, value);\n }\n\n private handleMessage(event: MessageEvent): void {\n const msg = event.data;\n if (!msg || typeof msg !== 'object' || msg.protocol !== PROTOCOL) return;\n\n switch (msg.type) {\n case 'STATE_CHANGE': {\n const payload = msg.payload as StateChangePayload;\n this._state = payload.state;\n for (const listener of this._stateListeners) {\n listener(payload.state);\n }\n break;\n }\n case 'EVENT_BROADCAST': {\n const payload = msg.payload as EventBroadcastPayload;\n const listeners = this._eventListeners.get(payload.topic);\n if (listeners) {\n const evt: WidgetEvent = {\n topic: payload.topic,\n payload: payload.payload,\n emittedAt: payload.emittedAt,\n };\n for (const listener of listeners) {\n listener(evt);\n }\n }\n break;\n }\n case 'CONSENT_RESULT': {\n const payload = msg.payload as ConsentResultPayload;\n const pending = this._pendingConsents.get(payload.scope);\n if (pending) {\n this._pendingConsents.delete(payload.scope);\n pending.resolve({\n scope: payload.scope,\n granted: payload.granted,\n grantToken: payload.grantToken,\n expiresIn: payload.expiresIn,\n });\n }\n break;\n }\n case 'AUTH_TOKEN_REFRESH': {\n const payload = msg.payload as { token: string; expiresIn: number };\n this._cachedJwt = {\n token: payload.token,\n expiresAt: Date.now() + payload.expiresIn * 1000,\n };\n break;\n }\n case 'DISPLAY_STATE_CHANGE': {\n const newState = msg.payload?.displayState as WidgetDisplayState;\n if (newState) {\n this._displayState = newState;\n this._displayStateCallbacks.forEach((cb) => cb(newState));\n }\n break;\n }\n case 'FOCUS_CHANGE': {\n const next = !!msg.payload?.focused;\n if (next !== this._focused) {\n this._focused = next;\n this._focusCallbacks.forEach((cb) => cb(next));\n }\n break;\n }\n case 'SETTINGS_REMOTE_UPDATE': {\n const p = msg.payload as { settings: Record<string, unknown> };\n this._settings = { ...p.settings };\n for (const fn of this._settingsRemoteListeners) {\n try {\n fn({ ...this._settings });\n } catch {\n // a listener throwing must not break message handling\n }\n }\n break;\n }\n case 'USER_CONTEXT':\n case 'CANVAS_CONTEXT':\n // Phase 3+ \u2014 reserved for future use\n break;\n }\n }\n\n private send(type: string, payload: Record<string, unknown>): void {\n // Fire and forget \u2014 sign is async but we don't need to await delivery\n this.buildMessage(type, payload).then((msg) => {\n window.parent.postMessage(msg, '*');\n });\n }\n\n private async buildMessage(\n type: string,\n payload: Record<string, unknown>\n ): Promise<CanvasMessage> {\n const nonce = safeRandomUUID();\n const timestamp = Date.now();\n\n let token: string;\n if (this._hmacSecret) {\n token = await signToken(this._hmacSecret, type, this._widgetId, nonce, timestamp);\n } else {\n token = DEV_MODE_TOKEN;\n }\n\n return {\n protocol: PROTOCOL,\n type,\n widgetId: this._widgetId,\n nonce,\n timestamp,\n token,\n payload,\n };\n }\n\n /** Tear down the SDK instance, remove all listeners, and reject pending consent requests. */\n destroy(): void {\n window.removeEventListener('message', this._messageHandler);\n this._stateListeners.length = 0;\n this._eventListeners.clear();\n for (const [, pending] of this._pendingConsents) {\n pending.reject(new Error('Widget destroyed'));\n }\n this._pendingConsents.clear();\n }\n}\n\n// ============================================\n// Public API: CanvasWidget.init()\n// ============================================\n\n/**\n * Entry point for the Flowstate Canvas Widget SDK.\n *\n * Usage:\n * ```ts\n * import { CanvasWidget } from '@goflowstate/widget-sdk';\n * const widget = await CanvasWidget.init();\n * ```\n */\nexport const CanvasWidget = {\n /**\n * Initialize a group-session client for widgets hosted on the /w/[slug]\n * session page (ticket handshake + session REST rails). Returns null when\n * the launch context carries no session params \u2014 see `initSession` in\n * ./session for details and `createMockSession` for standalone dev.\n */\n initSession,\n\n /**\n * Initialize the SDK and establish communication with the canvas host.\n *\n * Sends a READY message to the host and waits for an INIT response.\n * Resolves with a fully initialized {@link CanvasWidgetSDK} instance.\n * Rejects after 5 seconds if no INIT is received from the host.\n */\n init(): Promise<CanvasWidgetSDK> {\n return new Promise<CanvasWidgetSDK>((resolve, reject) => {\n const timeout = setTimeout(() => {\n window.removeEventListener('message', handler);\n reject(new Error('CanvasWidget.init() timed out \u2014 no INIT received from host within 5s'));\n }, INIT_TIMEOUT_MS);\n\n const handler = (event: MessageEvent) => {\n const msg = event.data;\n if (!msg || typeof msg !== 'object' || msg.protocol !== PROTOCOL) return;\n if (msg.type !== 'INIT') return;\n\n clearTimeout(timeout);\n window.removeEventListener('message', handler);\n\n const payload = msg.payload as InitPayload;\n const sdk = new CanvasWidgetSDKImpl(payload);\n resolve(sdk);\n };\n\n window.addEventListener('message', handler);\n\n // Send READY to host \u2014 the host will respond with INIT\n const readyMsg: CanvasMessage = {\n protocol: PROTOCOL,\n type: 'READY',\n widgetId: '', // Not yet known \u2014 host identifies us by iframe source\n nonce: safeRandomUUID(),\n timestamp: Date.now(),\n token: DEV_MODE_TOKEN, // READY is always allowed, token doesn't matter\n payload: { manifestVersion: '1' },\n };\n\n window.parent.postMessage(readyMsg, '*');\n });\n },\n};\n", "// packages/widget-sdk/src/service.ts\n\nimport type { WidgetServiceClient } from './types';\n\nexport interface CreateServiceClientOptions {\n baseUrl: string;\n getJwt: (forceRefresh?: boolean) => Promise<string>;\n fetchImpl?: typeof fetch;\n}\n\nexport function createServiceClient(opts: CreateServiceClientOptions): WidgetServiceClient {\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);\n const baseUrl = opts.baseUrl.replace(/\\/+$/, '');\n\n async function call(path: string, init: RequestInit, token: string): Promise<Response> {\n const url = `${baseUrl}${path.startsWith('/') ? path : `/${path}`}`;\n const headers = new Headers(init.headers);\n headers.set('authorization', `Bearer ${token}`);\n if (init.body && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n return fetchImpl(url, { ...init, headers });\n }\n\n return {\n async fetch<T = unknown>(path: string, init: RequestInit = {}): Promise<T> {\n let token = await opts.getJwt(false);\n let res = await call(path, init, token);\n\n if (res.status === 401) {\n token = await opts.getJwt(true);\n res = await call(path, init, token);\n }\n\n if (!res.ok) {\n let body: { message?: string } = {};\n try {\n body = (await res.json()) as { message?: string };\n } catch {\n // non-JSON body \u2014 ignore\n }\n throw new Error(`Widget service request failed (${res.status}): ${body.message ?? ''}`);\n }\n return (await res.json()) as T;\n },\n };\n}\n", "// Session module \u2014 the official client for Flowstate widget-session rails.\n//\n// Group-session widgets (decision sprint, and any future multi-contributor\n// widget) run inside the app's /w/[slug] page. The page hands the iframe a\n// single-use auth ticket over postMessage (ADR-025: never in the URL on the\n// production path), the ticket is redeemed for a session token, and every\n// session verb is a REST call against /api/w/:slug on the widget gateway.\n//\n// This module wraps that whole contract so a widget never hand-rolls the\n// bridge:\n//\n// ```ts\n// import { CanvasWidget } from '@goflowstate/widget-sdk';\n// const session = await CanvasWidget.initSession();\n// if (session) {\n// const state = await session.join();\n// const stop = session.poll((s) => render(s));\n// await session.contribute({ evidence: { pins } });\n// } else {\n// // no launch params \u2014 standalone dev; use createMockSession()\n// }\n// ```\n//\n// The wire vocabulary is the generic session shape the rails speak\n// (historically named after the expedition: `tripId`, `myWishlist`, `plan`).\n// This module maps it onto neutral names once, here, so widgets never see the\n// legacy keys.\n\n// ============================================\n// State view\n// ============================================\n\nexport interface SessionMember {\n userId: string;\n name: string;\n /** Deterministic per-user accent color (hex), stable across sessions. */\n color: string;\n /** True once this member has contributed (sealed) in the current round. */\n sealed: boolean;\n isMe: boolean;\n isHost: boolean;\n}\n\n/** Gate policy surfaced to widgets (ADR-047). Optional server-side \u2014 links\n * minted without a gate report null. */\nexport interface SessionGateView {\n deadlineAt: string | null;\n quorum?: { mode: 'all' | 'count' | 'pct'; min?: number };\n}\n\n/** Flow context when this session is a node in a running flow (ADR-047). */\nexport interface SessionFlowView {\n runId: string;\n nodeIndex: number;\n nodeCount: number;\n nodes: Array<{\n key: string;\n title: string;\n status: 'pending' | 'active' | 'resolved' | 'expired';\n artifactId?: string;\n linkSlug?: string;\n }>;\n}\n\n/** One verified (or user-entered) fact about the viewer, from a unishell\n * shell. Read-only context: never use it to authorize anything. */\nexport interface SessionClaim {\n type: 'name' | 'email' | 'phone';\n value: string;\n /** 'user-entered', 'otp', or a surface id such as 'discord'. */\n source: string;\n /** Present only when the value was proven. */\n verifiedAt?: string;\n}\n\nexport interface SessionClaimSet {\n name?: SessionClaim;\n email?: SessionClaim;\n phone?: SessionClaim;\n}\n\n/** Who this state was read for. `claims` is set only for a shell guest\n * reading their own state; it is never another participant's. */\nexport interface SessionViewer {\n userId: string;\n guest: boolean;\n claims: SessionClaimSet | null;\n}\n\n/** The rails' phase model: `setup` until the host commits framing, `open`\n * while contributions are being collected, `sealed` once quorum is met and\n * the session awaits close, `resolved` after synthesis produced the\n * artifact. (Wire values `wishlist`/`plan` are mapped to `open`/`resolved`.) */\nexport type SessionPhase = 'setup' | 'open' | 'sealed' | 'resolved';\n\nexport interface SessionStateView<\n TSetup = Record<string, unknown>,\n TContribution = unknown,\n TDoc = Record<string, unknown>,\n> {\n /** The widget-link slug \u2014 the session's identity and its /w/:slug URL key. */\n slug: string;\n phase: SessionPhase;\n status: string;\n setup: TSetup | null;\n members: SessionMember[];\n sealedCount: number;\n memberCount: number;\n /** True when every member has sealed (legacy all-hands quorum). */\n allSealed: boolean;\n /** True when the configured quorum policy is satisfied (falls back to\n * allSealed when the link carries no gate). */\n quorumMet: boolean;\n gate: SessionGateView | null;\n flow: SessionFlowView | null;\n /** Own contribution only \u2014 others stay blind until synthesis. */\n myContribution: TContribution | null;\n /** The synthesized document, present once the session resolved. */\n doc: TDoc | null;\n artifactId: string | null;\n /** Widget-specific state-view extension (e.g. the decision sprint's stage\n * + ballot), passed through verbatim when the server provides one. */\n extension: Record<string, unknown> | null;\n /** The reader of this state; null when the server sent none (servers\n * before the shell endpoints). Optional so hand-built views keep compiling. */\n viewer?: SessionViewer | null;\n}\n\n// ============================================\n// Client interface\n// ============================================\n\nexport interface SessionPollOptions {\n /** Poll cadence in ms. Default 4000 (matches the historical widgets). */\n intervalMs?: number;\n}\n\nexport interface SessionClient<\n TSetup = Record<string, unknown>,\n TContribution = unknown,\n TDoc = Record<string, unknown>,\n> {\n readonly slug: string;\n /** Base URL of the web app (for reveal links and parent navigation). */\n readonly webBase: string;\n /** Base URL of the mobile PWA, when the host provided one via the optional\n * `mobile` iframe param. Participant-facing join links (e.g. a projected\n * QR) should prefer it and fall back to `webBase` when absent. Optional so\n * hand-rolled SessionClient implementations keep compiling. */\n readonly mobileBase?: string | null;\n /** The viewer's own shell claims from the most recent state read; null\n * until a state arrives or when there are none. Optional so hand-rolled\n * SessionClient implementations keep compiling. */\n readonly claims?: SessionClaimSet | null;\n\n /** Enter the session: the first identified enter creates the group. */\n join(): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Fetch the current blind-aware state. */\n state(): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Poll state on a cadence. Returns a stop function. Errors are passed to\n * onError (or swallowed) so a transient failure never kills the loop. */\n poll(\n onState: (s: SessionStateView<TSetup, TContribution, TDoc>) => void,\n opts?: SessionPollOptions & { onError?: (err: unknown) => void }\n ): () => void;\n /** Host-only: commit the session framing. */\n commitSetup(setup: TSetup): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Upsert my contribution (seal). One contribution row per member; widgets\n * with multi-stage payloads post the merged object every time. */\n contribute(payload: TContribution): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Host-only close: synthesis runs, the artifact is born. Idempotent. */\n close(): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Widget-specific verb that returns a session STATE (e.g. the sprint's\n * /advance). Body is JSON-encoded; the response's state is returned. */\n action(verb: string, body?: unknown): Promise<SessionStateView<TSetup, TContribution, TDoc>>;\n /** Widget-specific verb with a NON-state response (e.g. /assist). Returns\n * the response's raw `data` value. Optional cancellation stops this request,\n * including its wait for the shared ticket; it does not cancel ticket redemption. */\n request<T = unknown>(\n verb: string,\n body?: unknown,\n options?: { signal?: AbortSignal }\n ): Promise<T>;\n /** Open a Flowstate URL outside the widget (parent navigation preferred \u2014\n * the /w page owns the session cookie). */\n openExternal(url: string): void;\n /** Deep link to an artifact reveal on the web app. */\n revealUrl(artifactId: string): string;\n}\n\n// ============================================\n// Launch params + ticket handshake (ADR-025)\n// ============================================\n\nexport interface SessionLaunchParams {\n slug: string;\n /** Dev-only fallback: a ticket in the URL. Production receives the ticket\n * via the parent's `flowstate:init` postMessage \u2014 URLs leak into\n * history/referrers/logs. */\n ticket: string | null;\n apiBase: string;\n webBase: string;\n /** Base URL of the mobile PWA (the optional `mobile` iframe param). Null\n * when the host didn't send one \u2014 legacy hosts never do \u2014 so consumers\n * fall back to `webBase` links. Optional so hand-built params keep\n * compiling. */\n mobileBase?: string | null;\n /** Origin of the page that FRAMES the widget \u2014 the `host` iframe param.\n * The ready/init ticket handshake and `flowstate:navigate` are addressed\n * to this origin (0.2.2). Defaults to `webBase` when absent, which is\n * what every pre-0.2.2 host is: the web /w page framing the widget. The\n * mobile PWA frames the widget itself (its native lobby) and names itself\n * here \u2014 without it the browser drops every postMessage between them\n * (target-origin mismatch) and the widget never boots. */\n hostBase?: string;\n}\n\nexport function readSessionLaunchParams(): SessionLaunchParams | null {\n if (typeof window === 'undefined') return null;\n const q = new URLSearchParams(window.location.search);\n // `tripId` is the historical param name the /w page passes; keep reading it\n // so hosts don't have to change. `slug` is accepted as the modern alias.\n const slug = q.get('slug') ?? q.get('tripId') ?? '';\n if (!slug) return null;\n // `mobile` is optional and has NO default: absent means the host predates\n // the PWA doors (or has none configured) and consumers must keep minting\n // legacy webBase links.\n const mobile = q.get('mobile');\n const webBase = (q.get('web') ?? 'http://localhost:14321').replace(/\\/$/, '');\n const host = q.get('host');\n return {\n slug,\n ticket: q.get('ticket'),\n apiBase: (q.get('api') ?? 'http://localhost:13002').replace(/\\/$/, ''),\n webBase,\n mobileBase: mobile ? mobile.replace(/\\/$/, '') : null,\n hostBase: host ? host.replace(/\\/$/, '') : webBase,\n };\n}\n\n/** The origin the handshake is addressed to: the framing page. */\nexport const hostOriginOf = (params: Pick<SessionLaunchParams, 'webBase' | 'hostBase'>): string =>\n new URL(params.hostBase ?? params.webBase).origin;\n\nconst SESSION_INIT_TIMEOUT_MS = 10_000;\nconst DEFAULT_POLL_INTERVAL_MS = 4_000;\n\n/** Announce readiness to the parent (host) page and wait for it to hand over\n * the single-use session ticket. `hostOrigin` is the framing page's origin\n * (`hostOriginOf`) \u2014 the web /w page or the mobile PWA's native lobby. */\nfunction awaitInitTicket(hostOrigin: string): Promise<string> {\n return new Promise((resolve, reject) => {\n if (typeof window === 'undefined' || window.parent === window) {\n reject(new Error('no parent page to receive a ticket from'));\n return;\n }\n const webOrigin = hostOrigin;\n const timer = setTimeout(() => {\n window.removeEventListener('message', onMessage);\n reject(new Error('timed out waiting for the session handoff'));\n }, SESSION_INIT_TIMEOUT_MS);\n const onMessage = (e: MessageEvent) => {\n if (e.origin !== webOrigin) return;\n const data = e.data as { type?: string; ticket?: string } | null;\n if (data?.type !== 'flowstate:init' || typeof data.ticket !== 'string') return;\n clearTimeout(timer);\n window.removeEventListener('message', onMessage);\n resolve(data.ticket);\n };\n window.addEventListener('message', onMessage);\n window.parent.postMessage({ type: 'flowstate:ready' }, webOrigin);\n });\n}\n\n// ============================================\n// Wire mapping\n// ============================================\n\ninterface WireMember {\n userId: string;\n name: string;\n color: string;\n sealed: boolean;\n isMe: boolean;\n isHost: boolean;\n}\n\ninterface WireSessionState {\n tripId: string;\n phase: 'setup' | 'wishlist' | 'sealed' | 'plan';\n status: string;\n setup: Record<string, unknown> | null;\n members: WireMember[];\n sealedCount: number;\n memberCount: number;\n allSealed: boolean;\n quorumMet?: boolean;\n gate?: SessionGateView | null;\n flow?: SessionFlowView | null;\n myWishlist: unknown;\n plan: Record<string, unknown> | null;\n artifactId: string | null;\n extension?: Record<string, unknown> | null;\n viewer?: { userId: string; guest: boolean; claims?: SessionClaimSet };\n}\n\nconst WIRE_PHASE: Record<WireSessionState['phase'], SessionPhase> = {\n setup: 'setup',\n wishlist: 'open',\n sealed: 'sealed',\n plan: 'resolved',\n};\n\n/** Exported for tests; not part of the package entry point. */\nexport function toStateView<TSetup, TContribution, TDoc>(\n s: WireSessionState\n): SessionStateView<TSetup, TContribution, TDoc> {\n return {\n slug: s.tripId,\n phase: WIRE_PHASE[s.phase] ?? 'open',\n status: s.status,\n setup: (s.setup as TSetup | null) ?? null,\n members: s.members,\n sealedCount: s.sealedCount,\n memberCount: s.memberCount,\n allSealed: s.allSealed,\n quorumMet: s.quorumMet ?? s.allSealed,\n gate: s.gate ?? null,\n flow: s.flow ?? null,\n myContribution: (s.myWishlist as TContribution | null) ?? null,\n doc: (s.plan as TDoc | null) ?? null,\n artifactId: s.artifactId,\n extension: s.extension ?? null,\n viewer: s.viewer\n ? { userId: s.viewer.userId, guest: s.viewer.guest, claims: s.viewer.claims ?? null }\n : null,\n };\n}\n\n// ============================================\n// HTTP client over the session rails\n// ============================================\n\ntype ExternalOpener = (url: string) => void;\n\nconst parentOpener = (hostOrigin: string): ExternalOpener => {\n return (url) => {\n if (typeof window !== 'undefined' && window.parent !== window) {\n // Ask the host page to navigate itself (the web /w page shares the\n // session cookie; the PWA lobby routes reveals natively).\n window.parent.postMessage({ type: 'flowstate:navigate', url }, hostOrigin);\n return;\n }\n if (typeof window !== 'undefined') window.open(url, '_blank', 'noopener');\n };\n};\n\nclass HttpSessionClient<TSetup, TContribution, TDoc> implements SessionClient<\n TSetup,\n TContribution,\n TDoc\n> {\n readonly slug: string;\n readonly webBase: string;\n readonly mobileBase: string | null;\n private latestClaims: SessionClaimSet | null = null;\n private readonly apiBase: string;\n private readonly tokenPromise: Promise<string>;\n private readonly opener: ExternalOpener;\n\n constructor(params: SessionLaunchParams, opener?: ExternalOpener) {\n this.slug = params.slug;\n this.webBase = params.webBase;\n this.mobileBase = params.mobileBase ?? null;\n this.apiBase = params.apiBase;\n this.opener = opener ?? parentOpener(hostOriginOf(params));\n // Redeem eagerly: the ticket is single-use + short-lived; trade it for a\n // session token before the user spends time in the widget.\n this.tokenPromise = this.acquireTicket(params).then((t) => this.redeem(t));\n }\n\n private async acquireTicket(params: SessionLaunchParams): Promise<string> {\n if (params.ticket) return params.ticket; // dev fallback\n return awaitInitTicket(hostOriginOf(params));\n }\n\n private async redeem(ticket: string): Promise<string> {\n const res = await fetch(`${this.apiBase}/api/auth/ticket/redeem`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ticket }),\n });\n if (!res.ok) throw new Error(`ticket redeem failed (${res.status})`);\n const json = (await res.json()) as { data: { token: string } };\n return json.data.token;\n }\n\n private async call(\n path: string,\n init: RequestInit = {}\n ): Promise<SessionStateView<TSetup, TContribution, TDoc>> {\n const token = await this.tokenPromise;\n const res = await fetch(`${this.apiBase}/api/w/${this.slug}${path}`, {\n ...init,\n headers: {\n authorization: `Bearer ${token}`,\n ...(init.body ? { 'content-type': 'application/json' } : {}),\n ...(init.headers ?? {}),\n },\n });\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { message?: string };\n throw new Error(body.message ?? `session service ${res.status}`);\n }\n const json = (await res.json()) as {\n data: WireSessionState | { state: WireSessionState };\n };\n const data = json.data as Record<string, unknown>;\n const view = toStateView<TSetup, TContribution, TDoc>((data.state ?? data) as WireSessionState);\n this.latestClaims = view.viewer?.claims ?? null;\n return view;\n }\n\n get claims(): SessionClaimSet | null {\n return this.latestClaims;\n }\n\n join() {\n return this.call('/enter', { method: 'POST' });\n }\n\n state() {\n return this.call('/state');\n }\n\n poll(\n onState: (s: SessionStateView<TSetup, TContribution, TDoc>) => void,\n opts?: SessionPollOptions & { onError?: (err: unknown) => void }\n ): () => void {\n const interval = opts?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n const tick = async () => {\n try {\n const s = await this.state();\n if (!stopped) onState(s);\n } catch (err) {\n opts?.onError?.(err);\n }\n if (!stopped) timer = setTimeout(tick, interval);\n };\n void tick();\n return () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n };\n }\n\n commitSetup(setup: TSetup) {\n return this.call('/setup', { method: 'POST', body: JSON.stringify({ setup }) });\n }\n\n contribute(payload: TContribution) {\n return this.call('/contribute', { method: 'POST', body: JSON.stringify({ payload }) });\n }\n\n close() {\n return this.call('/close', { method: 'POST' });\n }\n\n action(verb: string, body?: unknown) {\n return this.call(`/${verb.replace(/^\\//, '')}`, {\n method: 'POST',\n body: JSON.stringify(body ?? {}),\n });\n }\n\n async request<T = unknown>(\n verb: string,\n body?: unknown,\n options?: { signal?: AbortSignal }\n ): Promise<T> {\n const signal = options?.signal;\n if (signal?.aborted) {\n // Observe eager redemption even when this caller never waits for it.\n // Retain the original promise so other callers still receive its failure.\n void this.tokenPromise.catch(() => {});\n signal.throwIfAborted();\n }\n let onAbort: (() => void) | undefined;\n let token: string;\n try {\n token = await (signal\n ? Promise.race([\n this.tokenPromise,\n new Promise<never>((_resolve, reject) => {\n onAbort = () => reject(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n }),\n ])\n : this.tokenPromise);\n } finally {\n if (signal && onAbort) signal.removeEventListener('abort', onAbort);\n }\n const res = await fetch(`${this.apiBase}/api/w/${this.slug}/${verb.replace(/^\\//, '')}`, {\n signal,\n method: 'POST',\n headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },\n body: JSON.stringify(body ?? {}),\n });\n if (!res.ok) {\n const errBody = (await res.json().catch(() => {\n signal?.throwIfAborted();\n return {};\n })) as { message?: string };\n throw new Error(errBody.message ?? `session service ${res.status}`);\n }\n const json = (await res.json()) as { data: T };\n return json.data;\n }\n\n openExternal(url: string): void {\n this.opener(url);\n }\n\n revealUrl(artifactId: string): string {\n // FB-133: honour the PWA base like the wall's take-home QR does \u2014 a\n // phone-born session's reveal opens in the PWA, not the legacy web page.\n return this.mobileBase\n ? `${this.mobileBase}/?a=${artifactId}`\n : `${this.webBase}/a/${artifactId}`;\n }\n}\n\n// ============================================\n// Standalone mock (widget dev outside the canvas)\n// ============================================\n\nexport interface MockSessionOptions<TSetup, TDoc> {\n /** Display name for the single mock member. Default \"You\". */\n me?: string;\n /** Extra fake members (all unsealed initially). */\n others?: string[];\n setup?: TSetup | null;\n /** Claims the mock viewer carries (shell-guest dev). Default null. */\n claims?: SessionClaimSet | null;\n /** Called on close() to synthesize the mock doc. */\n synthesize?: (contributions: unknown[]) => TDoc;\n}\n\n/** In-memory SessionClient for standalone dev \u2014 same interface, no host, no\n * network. The mock's \"host\" is always you. */\nexport function createMockSession<\n TSetup = Record<string, unknown>,\n TContribution = unknown,\n TDoc = Record<string, unknown>,\n>(opts: MockSessionOptions<TSetup, TDoc> = {}): SessionClient<TSetup, TContribution, TDoc> {\n const palette = ['#8b7ef8', '#f8a34d', '#4dc9a4', '#f26d8f', '#5aa9e6'];\n const members: SessionMember[] = [\n {\n userId: 'mock-me',\n name: opts.me ?? 'You',\n color: palette[0],\n sealed: false,\n isMe: true,\n isHost: true,\n },\n ...(opts.others ?? []).map((name, i) => ({\n userId: `mock-${i + 1}`,\n name,\n color: palette[(i + 1) % palette.length],\n sealed: false,\n isMe: false,\n isHost: false,\n })),\n ];\n let setup: TSetup | null = opts.setup ?? null;\n let myContribution: TContribution | null = null;\n let doc: TDoc | null = null;\n let status = 'open';\n\n const view = (): SessionStateView<TSetup, TContribution, TDoc> => {\n const sealedCount = members.filter((m) => m.sealed).length;\n const allSealed = members.length > 0 && sealedCount >= members.length;\n const phase: SessionPhase =\n status === 'closed' && doc ? 'resolved' : !setup ? 'setup' : allSealed ? 'sealed' : 'open';\n return {\n slug: 'mock-session',\n phase,\n status,\n setup,\n members: members.map((m) => ({ ...m })),\n sealedCount,\n memberCount: members.length,\n allSealed,\n quorumMet: allSealed,\n gate: null,\n flow: null,\n myContribution,\n doc,\n artifactId: doc ? 'mock-artifact' : null,\n extension: null,\n viewer: { userId: 'mock-me', guest: false, claims: opts.claims ?? null },\n };\n };\n\n return {\n slug: 'mock-session',\n webBase: 'http://localhost:14321',\n claims: opts.claims ?? null,\n async join() {\n return view();\n },\n async state() {\n return view();\n },\n poll(onState, pollOpts) {\n const interval = pollOpts?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const timer = setInterval(() => onState(view()), interval);\n onState(view());\n return () => clearInterval(timer);\n },\n async commitSetup(s: TSetup) {\n setup = s;\n return view();\n },\n async contribute(payload: TContribution) {\n myContribution = payload;\n const me = members.find((m) => m.isMe);\n if (me) me.sealed = true;\n return view();\n },\n async close() {\n status = 'closed';\n doc = opts.synthesize\n ? opts.synthesize([myContribution])\n : ({ mock: true } as unknown as TDoc);\n return view();\n },\n async action() {\n return view();\n },\n async request<T = unknown>() {\n return {} as T;\n },\n openExternal(url: string) {\n if (typeof window !== 'undefined') window.open(url, '_blank', 'noopener');\n },\n revealUrl(artifactId: string) {\n return `http://localhost:14321/a/${artifactId}`;\n },\n };\n}\n\n// ============================================\n// Entry point\n// ============================================\n\n/**\n * Initialize a widget-session client from the launch context.\n *\n * Returns `null` when the page carries no session launch params (standalone\n * dev, or a canvas-hosted widget that has no group session) \u2014 callers fall\n * back to {@link createMockSession} or their own local state.\n */\nexport function initSession<\n TSetup = Record<string, unknown>,\n TContribution = unknown,\n TDoc = Record<string, unknown>,\n>(): SessionClient<TSetup, TContribution, TDoc> | null {\n const params = readSessionLaunchParams();\n if (!params) return null;\n return new HttpSessionClient<TSetup, TContribution, TDoc>(params);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ export type { CanvasWidgetSDK, WidgetDisplayState, WidgetEvent, SettingsAPI } fr
|
|
|
3
3
|
export { createServiceClient } from './service';
|
|
4
4
|
export type { WidgetManifestServiceBlock, WidgetManifestServiceScope, WidgetServiceClient, DbRecord, DbCollection, DbAPI, DbListOptions, } from './types';
|
|
5
5
|
export type { CreateServiceClientOptions } from './service';
|
|
6
|
-
export { initSession, createMockSession, readSessionLaunchParams } from './session';
|
|
7
|
-
export type { SessionClient, SessionStateView, SessionMember, SessionPhase, SessionGateView, SessionFlowView, SessionPollOptions, SessionLaunchParams, MockSessionOptions, } from './session';
|
|
6
|
+
export { initSession, createMockSession, hostOriginOf, readSessionLaunchParams } from './session';
|
|
7
|
+
export type { SessionClient, SessionStateView, SessionMember, SessionPhase, SessionGateView, SessionFlowView, SessionClaim, SessionClaimSet, SessionViewer, SessionPollOptions, SessionLaunchParams, MockSessionOptions, } from './session';
|
|
8
8
|
export { getInitialTheme, onThemeChange, applyWidgetTheme, initWidgetTheme } from './theme';
|
|
9
9
|
export type { WidgetTheme } from './theme';
|
|
10
10
|
export { onArtifactIngested, INGEST_MESSAGE_TYPE } from './ingest';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AACrC,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,YAAY,EACV,0BAA0B,EAC1B,0BAA0B,EAC1B,mBAAmB,EACnB,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AACrC,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,YAAY,EACV,0BAA0B,EAC1B,0BAA0B,EAC1B,mBAAmB,EACnB,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAClG,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,eAAe,EACf,eAAe,EACf,YAAY,EACZ,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC5F,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACnE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC"}
|