@goflowstate/widget-sdk 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3,4 +3,10 @@ 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';
8
+ export { getInitialTheme, onThemeChange, applyWidgetTheme, initWidgetTheme } from './theme';
9
+ export type { WidgetTheme } from './theme';
10
+ export { onArtifactIngested, INGEST_MESSAGE_TYPE } from './ingest';
11
+ export type { IngestedArtifact } from './ingest';
6
12
  //# sourceMappingURL=index.d.ts.map
@@ -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"}
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;AACpF,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,eAAe,EACf,eAAe,EACf,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"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- // @goflowstate/widget-sdk v0.1.1
2
- // https://github.com/starteryllp/goflowstate-sdk
1
+ // @goflowstate/widget-sdk v0.2.1
2
+ // https://github.com/goflowstate/goflowstate-sdk
3
3
 
4
4
  var __defProp = Object.defineProperty;
5
5
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -39,10 +39,292 @@ function createServiceClient(opts) {
39
39
  };
40
40
  }
41
41
 
42
+ // src/session.ts
43
+ function readSessionLaunchParams() {
44
+ if (typeof window === "undefined") return null;
45
+ const q = new URLSearchParams(window.location.search);
46
+ const slug = q.get("slug") ?? q.get("tripId") ?? "";
47
+ if (!slug) return null;
48
+ const mobile = q.get("mobile");
49
+ return {
50
+ slug,
51
+ ticket: q.get("ticket"),
52
+ apiBase: (q.get("api") ?? "http://localhost:13002").replace(/\/$/, ""),
53
+ webBase: (q.get("web") ?? "http://localhost:14321").replace(/\/$/, ""),
54
+ mobileBase: mobile ? mobile.replace(/\/$/, "") : null
55
+ };
56
+ }
57
+ var SESSION_INIT_TIMEOUT_MS = 1e4;
58
+ var DEFAULT_POLL_INTERVAL_MS = 4e3;
59
+ function awaitInitTicket(webBase) {
60
+ return new Promise((resolve, reject) => {
61
+ if (typeof window === "undefined" || window.parent === window) {
62
+ reject(new Error("no parent page to receive a ticket from"));
63
+ return;
64
+ }
65
+ const webOrigin = new URL(webBase).origin;
66
+ const timer = setTimeout(() => {
67
+ window.removeEventListener("message", onMessage);
68
+ reject(new Error("timed out waiting for the session handoff"));
69
+ }, SESSION_INIT_TIMEOUT_MS);
70
+ const onMessage = (e) => {
71
+ if (e.origin !== webOrigin) return;
72
+ const data = e.data;
73
+ if (data?.type !== "flowstate:init" || typeof data.ticket !== "string") return;
74
+ clearTimeout(timer);
75
+ window.removeEventListener("message", onMessage);
76
+ resolve(data.ticket);
77
+ };
78
+ window.addEventListener("message", onMessage);
79
+ window.parent.postMessage({ type: "flowstate:ready" }, webOrigin);
80
+ });
81
+ }
82
+ var WIRE_PHASE = {
83
+ setup: "setup",
84
+ wishlist: "open",
85
+ sealed: "sealed",
86
+ plan: "resolved"
87
+ };
88
+ function toStateView(s) {
89
+ return {
90
+ slug: s.tripId,
91
+ phase: WIRE_PHASE[s.phase] ?? "open",
92
+ status: s.status,
93
+ setup: s.setup ?? null,
94
+ members: s.members,
95
+ sealedCount: s.sealedCount,
96
+ memberCount: s.memberCount,
97
+ allSealed: s.allSealed,
98
+ quorumMet: s.quorumMet ?? s.allSealed,
99
+ gate: s.gate ?? null,
100
+ flow: s.flow ?? null,
101
+ myContribution: s.myWishlist ?? null,
102
+ doc: s.plan ?? null,
103
+ artifactId: s.artifactId,
104
+ extension: s.extension ?? null
105
+ };
106
+ }
107
+ var parentOpener = (webBase) => {
108
+ return (url) => {
109
+ if (typeof window !== "undefined" && window.parent !== window) {
110
+ window.parent.postMessage({ type: "flowstate:navigate", url }, webBase);
111
+ return;
112
+ }
113
+ if (typeof window !== "undefined") window.open(url, "_blank", "noopener");
114
+ };
115
+ };
116
+ var HttpSessionClient = class {
117
+ constructor(params, opener) {
118
+ __publicField(this, "slug");
119
+ __publicField(this, "webBase");
120
+ __publicField(this, "mobileBase");
121
+ __publicField(this, "apiBase");
122
+ __publicField(this, "tokenPromise");
123
+ __publicField(this, "opener");
124
+ this.slug = params.slug;
125
+ this.webBase = params.webBase;
126
+ this.mobileBase = params.mobileBase ?? null;
127
+ this.apiBase = params.apiBase;
128
+ this.opener = opener ?? parentOpener(params.webBase);
129
+ this.tokenPromise = this.acquireTicket(params).then((t) => this.redeem(t));
130
+ }
131
+ async acquireTicket(params) {
132
+ if (params.ticket) return params.ticket;
133
+ return awaitInitTicket(params.webBase);
134
+ }
135
+ async redeem(ticket) {
136
+ const res = await fetch(`${this.apiBase}/api/auth/ticket/redeem`, {
137
+ method: "POST",
138
+ headers: { "content-type": "application/json" },
139
+ body: JSON.stringify({ ticket })
140
+ });
141
+ if (!res.ok) throw new Error(`ticket redeem failed (${res.status})`);
142
+ const json = await res.json();
143
+ return json.data.token;
144
+ }
145
+ async call(path, init = {}) {
146
+ const token = await this.tokenPromise;
147
+ const res = await fetch(`${this.apiBase}/api/w/${this.slug}${path}`, {
148
+ ...init,
149
+ headers: {
150
+ authorization: `Bearer ${token}`,
151
+ ...init.body ? { "content-type": "application/json" } : {},
152
+ ...init.headers ?? {}
153
+ }
154
+ });
155
+ if (!res.ok) {
156
+ const body = await res.json().catch(() => ({}));
157
+ throw new Error(body.message ?? `session service ${res.status}`);
158
+ }
159
+ const json = await res.json();
160
+ const data = json.data;
161
+ return toStateView(data.state ?? data);
162
+ }
163
+ join() {
164
+ return this.call("/enter", { method: "POST" });
165
+ }
166
+ state() {
167
+ return this.call("/state");
168
+ }
169
+ poll(onState, opts) {
170
+ const interval = opts?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
171
+ let stopped = false;
172
+ let timer = null;
173
+ const tick = async () => {
174
+ try {
175
+ const s = await this.state();
176
+ if (!stopped) onState(s);
177
+ } catch (err) {
178
+ opts?.onError?.(err);
179
+ }
180
+ if (!stopped) timer = setTimeout(tick, interval);
181
+ };
182
+ void tick();
183
+ return () => {
184
+ stopped = true;
185
+ if (timer) clearTimeout(timer);
186
+ };
187
+ }
188
+ commitSetup(setup) {
189
+ return this.call("/setup", { method: "POST", body: JSON.stringify({ setup }) });
190
+ }
191
+ contribute(payload) {
192
+ return this.call("/contribute", { method: "POST", body: JSON.stringify({ payload }) });
193
+ }
194
+ close() {
195
+ return this.call("/close", { method: "POST" });
196
+ }
197
+ action(verb, body) {
198
+ return this.call(`/${verb.replace(/^\//, "")}`, {
199
+ method: "POST",
200
+ body: JSON.stringify(body ?? {})
201
+ });
202
+ }
203
+ async request(verb, body) {
204
+ const token = await this.tokenPromise;
205
+ const res = await fetch(`${this.apiBase}/api/w/${this.slug}/${verb.replace(/^\//, "")}`, {
206
+ method: "POST",
207
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
208
+ body: JSON.stringify(body ?? {})
209
+ });
210
+ if (!res.ok) {
211
+ const errBody = await res.json().catch(() => ({}));
212
+ throw new Error(errBody.message ?? `session service ${res.status}`);
213
+ }
214
+ const json = await res.json();
215
+ return json.data;
216
+ }
217
+ openExternal(url) {
218
+ this.opener(url);
219
+ }
220
+ revealUrl(artifactId) {
221
+ return `${this.webBase}/a/${artifactId}`;
222
+ }
223
+ };
224
+ function createMockSession(opts = {}) {
225
+ const palette = ["#8b7ef8", "#f8a34d", "#4dc9a4", "#f26d8f", "#5aa9e6"];
226
+ const members = [
227
+ { userId: "mock-me", name: opts.me ?? "You", color: palette[0], sealed: false, isMe: true, isHost: true },
228
+ ...(opts.others ?? []).map((name, i) => ({
229
+ userId: `mock-${i + 1}`,
230
+ name,
231
+ color: palette[(i + 1) % palette.length],
232
+ sealed: false,
233
+ isMe: false,
234
+ isHost: false
235
+ }))
236
+ ];
237
+ let setup = opts.setup ?? null;
238
+ let myContribution = null;
239
+ let doc = null;
240
+ let status = "open";
241
+ const view = () => {
242
+ const sealedCount = members.filter((m) => m.sealed).length;
243
+ const allSealed = members.length > 0 && sealedCount >= members.length;
244
+ const phase = status === "closed" && doc ? "resolved" : !setup ? "setup" : allSealed ? "sealed" : "open";
245
+ return {
246
+ slug: "mock-session",
247
+ phase,
248
+ status,
249
+ setup,
250
+ members: members.map((m) => ({ ...m })),
251
+ sealedCount,
252
+ memberCount: members.length,
253
+ allSealed,
254
+ quorumMet: allSealed,
255
+ gate: null,
256
+ flow: null,
257
+ myContribution,
258
+ doc,
259
+ artifactId: doc ? "mock-artifact" : null,
260
+ extension: null
261
+ };
262
+ };
263
+ return {
264
+ slug: "mock-session",
265
+ webBase: "http://localhost:14321",
266
+ async join() {
267
+ return view();
268
+ },
269
+ async state() {
270
+ return view();
271
+ },
272
+ poll(onState, pollOpts) {
273
+ const interval = pollOpts?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
274
+ const timer = setInterval(() => onState(view()), interval);
275
+ onState(view());
276
+ return () => clearInterval(timer);
277
+ },
278
+ async commitSetup(s) {
279
+ setup = s;
280
+ return view();
281
+ },
282
+ async contribute(payload) {
283
+ myContribution = payload;
284
+ const me = members.find((m) => m.isMe);
285
+ if (me) me.sealed = true;
286
+ return view();
287
+ },
288
+ async close() {
289
+ status = "closed";
290
+ doc = opts.synthesize ? opts.synthesize([myContribution]) : { mock: true };
291
+ return view();
292
+ },
293
+ async action() {
294
+ return view();
295
+ },
296
+ async request() {
297
+ return {};
298
+ },
299
+ openExternal(url) {
300
+ if (typeof window !== "undefined") window.open(url, "_blank", "noopener");
301
+ },
302
+ revealUrl(artifactId) {
303
+ return `http://localhost:14321/a/${artifactId}`;
304
+ }
305
+ };
306
+ }
307
+ function initSession() {
308
+ const params = readSessionLaunchParams();
309
+ if (!params) return null;
310
+ return new HttpSessionClient(params);
311
+ }
312
+
42
313
  // src/sdk.ts
43
314
  var PROTOCOL = "canvas-widget-v1";
44
315
  var DEV_MODE_TOKEN = "dev-mode-skip";
45
316
  var INIT_TIMEOUT_MS = 5e3;
317
+ function safeRandomUUID() {
318
+ const c = globalThis.crypto;
319
+ if (c && typeof c.randomUUID === "function") return c.randomUUID();
320
+ const b = new Uint8Array(16);
321
+ if (c && typeof c.getRandomValues === "function") c.getRandomValues(b);
322
+ else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);
323
+ b[6] = b[6] & 15 | 64;
324
+ b[8] = b[8] & 63 | 128;
325
+ const h = Array.from(b, (x) => x.toString(16).padStart(2, "0"));
326
+ 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]}`;
327
+ }
46
328
  var cachedKey = null;
47
329
  async function getHmacKey(secret) {
48
330
  if (cachedKey && cachedKey.secret === secret) return cachedKey.key;
@@ -69,6 +351,7 @@ var CanvasWidgetSDKImpl = class {
69
351
  __publicField(this, "_state");
70
352
  __publicField(this, "_hmacSecret");
71
353
  __publicField(this, "_user");
354
+ __publicField(this, "_namespace");
72
355
  __publicField(this, "_widgetApiUrl");
73
356
  __publicField(this, "_widgetApiToken");
74
357
  __publicField(this, "_installationId");
@@ -89,6 +372,7 @@ var CanvasWidgetSDKImpl = class {
89
372
  this._state = init.state;
90
373
  this._hmacSecret = init.hmacSecret;
91
374
  this._user = init.user;
375
+ this._namespace = init.namespace ?? null;
92
376
  this._widgetApiUrl = init.widgetApiUrl;
93
377
  this._widgetApiToken = init.widgetApiToken ?? null;
94
378
  this._installationId = init.installationId;
@@ -114,6 +398,11 @@ var CanvasWidgetSDKImpl = class {
114
398
  get user() {
115
399
  return this._user;
116
400
  }
401
+ /** Namespace (office) context — id and owner-inclusive member count — or
402
+ * null on a personal canvas. What the `canvas.context` permission grants. */
403
+ get namespace() {
404
+ return this._namespace;
405
+ }
117
406
  /** Subscribe to display state changes; returns an unsubscribe function. */
118
407
  onStateChange(cb) {
119
408
  this._stateListeners.push(cb);
@@ -238,6 +527,52 @@ var CanvasWidgetSDKImpl = class {
238
527
  }
239
528
  };
240
529
  }
530
+ /** Platform LLM access (widget.* aliases only). Requests ride the same
531
+ * widget-gateway auth as db/storage; the platform meters every call and
532
+ * attributes it to this widget's publishing org. */
533
+ get ai() {
534
+ const complete = (opts) => this.widgetApiRequest("/widget-api/ai/complete", "POST", opts);
535
+ const streamRequest = (opts) => this.widgetApiStream(opts);
536
+ return {
537
+ complete,
538
+ // Async generator over the gateway's SSE frames. Ends on [DONE] or
539
+ // stream end; returning early (break) cancels the fetch body, which
540
+ // the gateway sees as a disconnect and aborts the upstream call, so
541
+ // an abandoned stream stops generating and billing.
542
+ async *stream(opts) {
543
+ const response = await streamRequest(opts);
544
+ const reader = response.body?.getReader();
545
+ if (!reader) throw new Error("ai.stream: response has no body");
546
+ const decoder = new TextDecoder();
547
+ let buffer = "";
548
+ try {
549
+ for (; ; ) {
550
+ const { done, value } = await reader.read();
551
+ if (done) break;
552
+ buffer += decoder.decode(value, { stream: true });
553
+ const frames = buffer.split("\n\n");
554
+ buffer = frames.pop() ?? "";
555
+ for (const frame of frames) {
556
+ const dataLine = frame.split("\n").find((l) => l.startsWith("data:"));
557
+ if (!dataLine) continue;
558
+ const payload = dataLine.slice(5).trim();
559
+ if (!payload || payload === "[DONE]") continue;
560
+ let parsed;
561
+ try {
562
+ parsed = JSON.parse(payload);
563
+ } catch {
564
+ continue;
565
+ }
566
+ yield parsed;
567
+ }
568
+ }
569
+ } finally {
570
+ await reader.cancel().catch(() => {
571
+ });
572
+ }
573
+ }
574
+ };
575
+ }
241
576
  /** Widget configuration settings persisted by the canvas host. */
242
577
  get settings() {
243
578
  return {
@@ -263,6 +598,28 @@ var CanvasWidgetSDKImpl = class {
263
598
  });
264
599
  }
265
600
  // ── Private ─────────────────────────────────────────────────
601
+ /** Raw streaming POST against the widget gateway — same auth as
602
+ * widgetApiRequest, but returns the Response for SSE consumption
603
+ * instead of parsing a JSON envelope. */
604
+ async widgetApiStream(body) {
605
+ if (!this._widgetApiUrl) {
606
+ throw new Error("Widget API Gateway URL not available");
607
+ }
608
+ const headers = { "Content-Type": "application/json" };
609
+ if (this._widgetApiToken) {
610
+ headers["Authorization"] = `Bearer ${this._widgetApiToken}`;
611
+ }
612
+ const response = await fetch(`${this._widgetApiUrl}/widget-api/ai/stream`, {
613
+ method: "POST",
614
+ headers,
615
+ body: JSON.stringify(body)
616
+ });
617
+ if (!response.ok) {
618
+ const err = await response.json().catch(() => ({}));
619
+ throw new Error(err.message ?? "ai.stream request failed");
620
+ }
621
+ return response;
622
+ }
266
623
  async widgetApiRequest(path, method, body) {
267
624
  if (!this._widgetApiUrl) {
268
625
  throw new Error("Widget API Gateway URL not available");
@@ -377,7 +734,7 @@ var CanvasWidgetSDKImpl = class {
377
734
  });
378
735
  }
379
736
  async buildMessage(type, payload) {
380
- const nonce = crypto.randomUUID();
737
+ const nonce = safeRandomUUID();
381
738
  const timestamp = Date.now();
382
739
  let token;
383
740
  if (this._hmacSecret) {
@@ -407,6 +764,13 @@ var CanvasWidgetSDKImpl = class {
407
764
  }
408
765
  };
409
766
  var CanvasWidget = {
767
+ /**
768
+ * Initialize a group-session client for widgets hosted on the /w/[slug]
769
+ * session page (ticket handshake + session REST rails). Returns null when
770
+ * the launch context carries no session params — see `initSession` in
771
+ * ./session for details and `createMockSession` for standalone dev.
772
+ */
773
+ initSession,
410
774
  /**
411
775
  * Initialize the SDK and establish communication with the canvas host.
412
776
  *
@@ -436,7 +800,7 @@ var CanvasWidget = {
436
800
  type: "READY",
437
801
  widgetId: "",
438
802
  // Not yet known — host identifies us by iframe source
439
- nonce: crypto.randomUUID(),
803
+ nonce: safeRandomUUID(),
440
804
  timestamp: Date.now(),
441
805
  token: DEV_MODE_TOKEN,
442
806
  // READY is always allowed, token doesn't matter
@@ -446,8 +810,84 @@ var CanvasWidget = {
446
810
  });
447
811
  }
448
812
  };
813
+
814
+ // src/theme.ts
815
+ var THEME_MESSAGE_TYPE = "flowstate:theme";
816
+ var DEFAULT_THEME = "glass";
817
+ function isWidgetTheme(value) {
818
+ return value === "light" || value === "dark" || value === "glass";
819
+ }
820
+ function getInitialTheme() {
821
+ if (typeof window === "undefined") return DEFAULT_THEME;
822
+ try {
823
+ const param = new URLSearchParams(window.location.search).get("theme");
824
+ return isWidgetTheme(param) ? param : DEFAULT_THEME;
825
+ } catch {
826
+ return DEFAULT_THEME;
827
+ }
828
+ }
829
+ function onThemeChange(cb, opts) {
830
+ if (typeof window === "undefined") return () => {
831
+ };
832
+ const listener = (event) => {
833
+ if (event.source !== window.parent) return;
834
+ if (opts?.allowedOrigin && event.origin !== opts.allowedOrigin) return;
835
+ const data = event.data;
836
+ if (typeof data !== "object" || data === null) return;
837
+ const message = data;
838
+ if (message.type !== THEME_MESSAGE_TYPE) return;
839
+ if (!isWidgetTheme(message.theme)) return;
840
+ cb(message.theme);
841
+ };
842
+ window.addEventListener("message", listener);
843
+ return () => window.removeEventListener("message", listener);
844
+ }
845
+ function applyWidgetTheme(theme) {
846
+ if (typeof document === "undefined") return;
847
+ if (theme === "glass") {
848
+ delete document.documentElement.dataset.theme;
849
+ } else {
850
+ document.documentElement.dataset.theme = theme;
851
+ }
852
+ }
853
+ function initWidgetTheme() {
854
+ applyWidgetTheme(getInitialTheme());
855
+ return onThemeChange(applyWidgetTheme);
856
+ }
857
+
858
+ // src/ingest.ts
859
+ var INGEST_MESSAGE_TYPE = "flowstate:ingest";
860
+ function onArtifactIngested(cb, opts) {
861
+ if (typeof window === "undefined") return () => {
862
+ };
863
+ const listener = (event) => {
864
+ if (event.source !== window.parent) return;
865
+ if (opts?.allowedOrigin && event.origin !== opts.allowedOrigin) return;
866
+ const data = event.data;
867
+ if (typeof data !== "object" || data === null) return;
868
+ const message = data;
869
+ if (message.type !== INGEST_MESSAGE_TYPE) return;
870
+ if (typeof message.artifactId !== "string" || !message.artifactId) return;
871
+ cb({
872
+ artifactId: message.artifactId,
873
+ kind: typeof message.kind === "string" ? message.kind : void 0,
874
+ payload: message.payload
875
+ });
876
+ };
877
+ window.addEventListener("message", listener);
878
+ return () => window.removeEventListener("message", listener);
879
+ }
449
880
  export {
450
881
  CanvasWidget,
451
- createServiceClient
882
+ INGEST_MESSAGE_TYPE,
883
+ applyWidgetTheme,
884
+ createMockSession,
885
+ createServiceClient,
886
+ getInitialTheme,
887
+ initSession,
888
+ initWidgetTheme,
889
+ onArtifactIngested,
890
+ onThemeChange,
891
+ readSessionLaunchParams
452
892
  };
453
893
  //# sourceMappingURL=index.mjs.map