@goflowstate/widget-sdk 0.1.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 +104 -0
- package/dist/canvas-widget-sdk.js +477 -0
- package/dist/canvas-widget-sdk.js.map +7 -0
- package/dist/db.test.d.ts +2 -0
- package/dist/db.test.d.ts.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +453 -0
- package/dist/index.mjs.map +7 -0
- package/dist/sdk.d.ts +95 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.focus.test.d.ts +2 -0
- package/dist/sdk.focus.test.d.ts.map +1 -0
- package/dist/service.d.ts +8 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.test.d.ts +2 -0
- package/dist/service.test.d.ts.map +1 -0
- package/dist/types.d.ts +182 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +40 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
// @goflowstate/widget-sdk v0.1.0
|
|
2
|
+
// https://github.com/starteryllp/goflowstate-sdk
|
|
3
|
+
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
7
|
+
|
|
8
|
+
// src/service.ts
|
|
9
|
+
function createServiceClient(opts) {
|
|
10
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
11
|
+
const baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
12
|
+
async function call(path, init, token) {
|
|
13
|
+
const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
14
|
+
const headers = new Headers(init.headers);
|
|
15
|
+
headers.set("authorization", `Bearer ${token}`);
|
|
16
|
+
if (init.body && !headers.has("content-type")) {
|
|
17
|
+
headers.set("content-type", "application/json");
|
|
18
|
+
}
|
|
19
|
+
return fetchImpl(url, { ...init, headers });
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
async fetch(path, init = {}) {
|
|
23
|
+
let token = await opts.getJwt(false);
|
|
24
|
+
let res = await call(path, init, token);
|
|
25
|
+
if (res.status === 401) {
|
|
26
|
+
token = await opts.getJwt(true);
|
|
27
|
+
res = await call(path, init, token);
|
|
28
|
+
}
|
|
29
|
+
if (!res.ok) {
|
|
30
|
+
let body = {};
|
|
31
|
+
try {
|
|
32
|
+
body = await res.json();
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`Widget service request failed (${res.status}): ${body.message ?? ""}`);
|
|
36
|
+
}
|
|
37
|
+
return await res.json();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/sdk.ts
|
|
43
|
+
var PROTOCOL = "canvas-widget-v1";
|
|
44
|
+
var DEV_MODE_TOKEN = "dev-mode-skip";
|
|
45
|
+
var INIT_TIMEOUT_MS = 5e3;
|
|
46
|
+
var cachedKey = null;
|
|
47
|
+
async function getHmacKey(secret) {
|
|
48
|
+
if (cachedKey && cachedKey.secret === secret) return cachedKey.key;
|
|
49
|
+
const encoded = new TextEncoder().encode(secret);
|
|
50
|
+
const buf = new ArrayBuffer(encoded.byteLength);
|
|
51
|
+
new Uint8Array(buf).set(encoded);
|
|
52
|
+
const key = await crypto.subtle.importKey("raw", buf, { name: "HMAC", hash: "SHA-256" }, false, [
|
|
53
|
+
"sign"
|
|
54
|
+
]);
|
|
55
|
+
cachedKey = { secret, key };
|
|
56
|
+
return key;
|
|
57
|
+
}
|
|
58
|
+
async function signToken(secret, type, widgetId, nonce, timestamp) {
|
|
59
|
+
const key = await getHmacKey(secret);
|
|
60
|
+
const data = new TextEncoder().encode(`${type}:${widgetId}:${nonce}:${timestamp}`);
|
|
61
|
+
const buf = new ArrayBuffer(data.byteLength);
|
|
62
|
+
new Uint8Array(buf).set(data);
|
|
63
|
+
const sig = await crypto.subtle.sign("HMAC", key, buf);
|
|
64
|
+
return Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
65
|
+
}
|
|
66
|
+
var CanvasWidgetSDKImpl = class {
|
|
67
|
+
constructor(init) {
|
|
68
|
+
__publicField(this, "_widgetId");
|
|
69
|
+
__publicField(this, "_state");
|
|
70
|
+
__publicField(this, "_hmacSecret");
|
|
71
|
+
__publicField(this, "_user");
|
|
72
|
+
__publicField(this, "_widgetApiUrl");
|
|
73
|
+
__publicField(this, "_widgetApiToken");
|
|
74
|
+
__publicField(this, "_installationId");
|
|
75
|
+
__publicField(this, "_pendingConsents", /* @__PURE__ */ new Map());
|
|
76
|
+
__publicField(this, "_cachedJwt", null);
|
|
77
|
+
__publicField(this, "_stateListeners", []);
|
|
78
|
+
__publicField(this, "_displayState", "collapsed");
|
|
79
|
+
__publicField(this, "_displayStateCallbacks", []);
|
|
80
|
+
__publicField(this, "_focused", false);
|
|
81
|
+
__publicField(this, "_focusCallbacks", []);
|
|
82
|
+
__publicField(this, "_settingsRemoteListeners", []);
|
|
83
|
+
__publicField(this, "_eventListeners", /* @__PURE__ */ new Map());
|
|
84
|
+
__publicField(this, "_settings");
|
|
85
|
+
__publicField(this, "_messageHandler");
|
|
86
|
+
/** Authenticated HTTP client scoped to the widget's declared service URL. Undefined when no service is declared. */
|
|
87
|
+
__publicField(this, "service");
|
|
88
|
+
this._widgetId = init.widgetId;
|
|
89
|
+
this._state = init.state;
|
|
90
|
+
this._hmacSecret = init.hmacSecret;
|
|
91
|
+
this._user = init.user;
|
|
92
|
+
this._widgetApiUrl = init.widgetApiUrl;
|
|
93
|
+
this._widgetApiToken = init.widgetApiToken ?? null;
|
|
94
|
+
this._installationId = init.installationId;
|
|
95
|
+
this._settings = init.settings ?? {};
|
|
96
|
+
if (init.serviceUrl) {
|
|
97
|
+
this.service = createServiceClient({
|
|
98
|
+
baseUrl: init.serviceUrl,
|
|
99
|
+
getJwt: (forceRefresh) => this.getJwt(forceRefresh)
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
this._messageHandler = this.handleMessage.bind(this);
|
|
103
|
+
window.addEventListener("message", this._messageHandler);
|
|
104
|
+
}
|
|
105
|
+
/** The unique identifier for this widget instance on the canvas. */
|
|
106
|
+
get widgetId() {
|
|
107
|
+
return this._widgetId;
|
|
108
|
+
}
|
|
109
|
+
/** The current display state of the widget (collapsed, expanding, expanded, collapsing). */
|
|
110
|
+
get state() {
|
|
111
|
+
return this._state;
|
|
112
|
+
}
|
|
113
|
+
/** The authenticated user context, or null when user data access has not been granted. */
|
|
114
|
+
get user() {
|
|
115
|
+
return this._user;
|
|
116
|
+
}
|
|
117
|
+
/** Subscribe to display state changes; returns an unsubscribe function. */
|
|
118
|
+
onStateChange(cb) {
|
|
119
|
+
this._stateListeners.push(cb);
|
|
120
|
+
return () => {
|
|
121
|
+
const idx = this._stateListeners.indexOf(cb);
|
|
122
|
+
if (idx !== -1) this._stateListeners.splice(idx, 1);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** Ask the canvas host to expand this widget into its modal view. */
|
|
126
|
+
requestExpand() {
|
|
127
|
+
this.send("EXPAND_REQUEST", {});
|
|
128
|
+
}
|
|
129
|
+
/** Ask the canvas host to collapse this widget back to its tile view. */
|
|
130
|
+
requestCollapse() {
|
|
131
|
+
this.send("COLLAPSE_REQUEST", {});
|
|
132
|
+
}
|
|
133
|
+
/** Get the current display state */
|
|
134
|
+
getDisplayState() {
|
|
135
|
+
return this._displayState;
|
|
136
|
+
}
|
|
137
|
+
/** Register a callback for display state changes */
|
|
138
|
+
onDisplayStateChange(callback) {
|
|
139
|
+
this._displayStateCallbacks.push(callback);
|
|
140
|
+
return () => {
|
|
141
|
+
this._displayStateCallbacks = this._displayStateCallbacks.filter((cb) => cb !== callback);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Is this widget the user's current focus? Updates via FOCUS_CHANGE. */
|
|
145
|
+
isFocused() {
|
|
146
|
+
return this._focused;
|
|
147
|
+
}
|
|
148
|
+
/** Subscribe to focus transitions. Returns an unsubscribe function. */
|
|
149
|
+
onFocusChange(callback) {
|
|
150
|
+
this._focusCallbacks.push(callback);
|
|
151
|
+
return () => {
|
|
152
|
+
this._focusCallbacks = this._focusCallbacks.filter((cb) => cb !== callback);
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/** Request promotion to fullscreen from partial */
|
|
156
|
+
requestFullscreen() {
|
|
157
|
+
this.send("FULLSCREEN_REQUEST", {});
|
|
158
|
+
}
|
|
159
|
+
/** Broadcast an event on the given topic to all subscribed widgets on the canvas. */
|
|
160
|
+
emit(topic, payload) {
|
|
161
|
+
this.send("EVENT_EMIT", { topic, payload });
|
|
162
|
+
}
|
|
163
|
+
/** Subscribe to events on the given topic; returns an unsubscribe function. */
|
|
164
|
+
subscribe(topic, cb) {
|
|
165
|
+
const list = this._eventListeners.get(topic) ?? [];
|
|
166
|
+
list.push(cb);
|
|
167
|
+
this._eventListeners.set(topic, list);
|
|
168
|
+
return () => {
|
|
169
|
+
const current = this._eventListeners.get(topic);
|
|
170
|
+
if (!current) return;
|
|
171
|
+
const idx = current.indexOf(cb);
|
|
172
|
+
if (idx !== -1) current.splice(idx, 1);
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** Send a structured log entry to the canvas host for centralized debugging. */
|
|
176
|
+
log(level, message, data) {
|
|
177
|
+
this.send("LOG", { level, message, data: data ?? null });
|
|
178
|
+
}
|
|
179
|
+
/** Request user consent for a named data scope; resolves with the consent decision. */
|
|
180
|
+
async requestConsent(scope, reason) {
|
|
181
|
+
return new Promise((resolve, reject) => {
|
|
182
|
+
const timeout = setTimeout(() => {
|
|
183
|
+
this._pendingConsents.delete(scope);
|
|
184
|
+
reject(new Error("Consent request timed out"));
|
|
185
|
+
}, 6e4);
|
|
186
|
+
this._pendingConsents.set(scope, {
|
|
187
|
+
resolve: (r) => {
|
|
188
|
+
clearTimeout(timeout);
|
|
189
|
+
resolve(r);
|
|
190
|
+
},
|
|
191
|
+
reject: (e) => {
|
|
192
|
+
clearTimeout(timeout);
|
|
193
|
+
reject(e);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
this.send("CONSENT_REQUEST", { scope, reason });
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/** Return the cached JWT for authenticating requests to external APIs; throws if no token is available. Pass forceRefresh=true to bypass the cache. */
|
|
200
|
+
async getJwt(forceRefresh) {
|
|
201
|
+
if (!forceRefresh && this._cachedJwt && Date.now() < this._cachedJwt.expiresAt - 3e4) {
|
|
202
|
+
return this._cachedJwt.token;
|
|
203
|
+
}
|
|
204
|
+
throw new Error("No cached JWT \u2014 widget must use OAuth flow for token refresh");
|
|
205
|
+
}
|
|
206
|
+
/** Persistent key-value storage scoped to this widget installation. */
|
|
207
|
+
get storage() {
|
|
208
|
+
return {
|
|
209
|
+
get: (key) => this.storageRequest("GET", key),
|
|
210
|
+
set: (key, value) => this.storageRequest("PUT", key, value).then(() => {
|
|
211
|
+
}),
|
|
212
|
+
delete: (key) => this.storageRequest("DELETE", key).then(() => {
|
|
213
|
+
})
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/** Ad-hoc collections/records backend scoped to the canvas/room. */
|
|
217
|
+
get db() {
|
|
218
|
+
const req = (path, method, body) => this.widgetApiRequest(path, method, body);
|
|
219
|
+
return {
|
|
220
|
+
collection(name) {
|
|
221
|
+
const base = `/widget-api/db/${encodeURIComponent(name)}`;
|
|
222
|
+
return {
|
|
223
|
+
create: (data) => req(base, "POST", data),
|
|
224
|
+
get: (id) => req(`${base}/${encodeURIComponent(id)}`, "GET"),
|
|
225
|
+
list: (opts) => {
|
|
226
|
+
const qs = new URLSearchParams();
|
|
227
|
+
if (opts?.limit != null) qs.set("limit", String(opts.limit));
|
|
228
|
+
if (opts?.cursor) qs.set("cursor", opts.cursor);
|
|
229
|
+
if (opts?.sort) qs.set("sort", opts.sort);
|
|
230
|
+
if (opts?.mine) qs.set("mine", "true");
|
|
231
|
+
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
232
|
+
return req(`${base}${suffix}`, "GET");
|
|
233
|
+
},
|
|
234
|
+
update: (id, patch) => req(`${base}/${encodeURIComponent(id)}`, "PATCH", patch),
|
|
235
|
+
delete: (id) => req(`${base}/${encodeURIComponent(id)}`, "DELETE").then(() => {
|
|
236
|
+
})
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/** Widget configuration settings persisted by the canvas host. */
|
|
242
|
+
get settings() {
|
|
243
|
+
return {
|
|
244
|
+
get: () => ({ ...this._settings }),
|
|
245
|
+
set: (newSettings) => {
|
|
246
|
+
this._settings = { ...this._settings, ...newSettings };
|
|
247
|
+
this.send("SETTINGS_WRITE", { settings: this._settings });
|
|
248
|
+
},
|
|
249
|
+
onRemoteChange: (cb) => {
|
|
250
|
+
this._settingsRemoteListeners.push(cb);
|
|
251
|
+
return () => {
|
|
252
|
+
this._settingsRemoteListeners = this._settingsRemoteListeners.filter((fn) => fn !== cb);
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/** Display a toast notification in the canvas host UI. */
|
|
258
|
+
async notify(message, opts) {
|
|
259
|
+
this.send("NOTIFICATION", {
|
|
260
|
+
message,
|
|
261
|
+
level: opts?.level ?? "info",
|
|
262
|
+
duration: opts?.duration ?? 5e3
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// ── Private ─────────────────────────────────────────────────
|
|
266
|
+
async widgetApiRequest(path, method, body) {
|
|
267
|
+
if (!this._widgetApiUrl) {
|
|
268
|
+
throw new Error("Widget API Gateway URL not available");
|
|
269
|
+
}
|
|
270
|
+
const url = `${this._widgetApiUrl}${path}`;
|
|
271
|
+
const headers = { "Content-Type": "application/json" };
|
|
272
|
+
if (this._widgetApiToken) {
|
|
273
|
+
headers["Authorization"] = `Bearer ${this._widgetApiToken}`;
|
|
274
|
+
}
|
|
275
|
+
const opts = { method, headers };
|
|
276
|
+
if (body !== void 0 && method !== "GET" && method !== "DELETE") {
|
|
277
|
+
opts.body = JSON.stringify(body);
|
|
278
|
+
}
|
|
279
|
+
const response = await fetch(url, opts);
|
|
280
|
+
if (!response.ok) {
|
|
281
|
+
const err = await response.json().catch(() => ({}));
|
|
282
|
+
throw new Error(err.message ?? `Request ${method} ${path} failed`);
|
|
283
|
+
}
|
|
284
|
+
if (method === "GET" || method === "POST" || method === "PATCH") {
|
|
285
|
+
const result = await response.json();
|
|
286
|
+
return result.data;
|
|
287
|
+
}
|
|
288
|
+
return void 0;
|
|
289
|
+
}
|
|
290
|
+
storageRequest(method, key, value) {
|
|
291
|
+
return this.widgetApiRequest(`/widget-api/storage/${encodeURIComponent(key)}`, method, value);
|
|
292
|
+
}
|
|
293
|
+
handleMessage(event) {
|
|
294
|
+
const msg = event.data;
|
|
295
|
+
if (!msg || typeof msg !== "object" || msg.protocol !== PROTOCOL) return;
|
|
296
|
+
switch (msg.type) {
|
|
297
|
+
case "STATE_CHANGE": {
|
|
298
|
+
const payload = msg.payload;
|
|
299
|
+
this._state = payload.state;
|
|
300
|
+
for (const listener of this._stateListeners) {
|
|
301
|
+
listener(payload.state);
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
case "EVENT_BROADCAST": {
|
|
306
|
+
const payload = msg.payload;
|
|
307
|
+
const listeners = this._eventListeners.get(payload.topic);
|
|
308
|
+
if (listeners) {
|
|
309
|
+
const evt = {
|
|
310
|
+
topic: payload.topic,
|
|
311
|
+
payload: payload.payload,
|
|
312
|
+
emittedAt: payload.emittedAt
|
|
313
|
+
};
|
|
314
|
+
for (const listener of listeners) {
|
|
315
|
+
listener(evt);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
case "CONSENT_RESULT": {
|
|
321
|
+
const payload = msg.payload;
|
|
322
|
+
const pending = this._pendingConsents.get(payload.scope);
|
|
323
|
+
if (pending) {
|
|
324
|
+
this._pendingConsents.delete(payload.scope);
|
|
325
|
+
pending.resolve({
|
|
326
|
+
scope: payload.scope,
|
|
327
|
+
granted: payload.granted,
|
|
328
|
+
grantToken: payload.grantToken,
|
|
329
|
+
expiresIn: payload.expiresIn
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
case "AUTH_TOKEN_REFRESH": {
|
|
335
|
+
const payload = msg.payload;
|
|
336
|
+
this._cachedJwt = {
|
|
337
|
+
token: payload.token,
|
|
338
|
+
expiresAt: Date.now() + payload.expiresIn * 1e3
|
|
339
|
+
};
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
case "DISPLAY_STATE_CHANGE": {
|
|
343
|
+
const newState = msg.payload?.displayState;
|
|
344
|
+
if (newState) {
|
|
345
|
+
this._displayState = newState;
|
|
346
|
+
this._displayStateCallbacks.forEach((cb) => cb(newState));
|
|
347
|
+
}
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
case "FOCUS_CHANGE": {
|
|
351
|
+
const next = !!msg.payload?.focused;
|
|
352
|
+
if (next !== this._focused) {
|
|
353
|
+
this._focused = next;
|
|
354
|
+
this._focusCallbacks.forEach((cb) => cb(next));
|
|
355
|
+
}
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
case "SETTINGS_REMOTE_UPDATE": {
|
|
359
|
+
const p = msg.payload;
|
|
360
|
+
this._settings = { ...p.settings };
|
|
361
|
+
for (const fn of this._settingsRemoteListeners) {
|
|
362
|
+
try {
|
|
363
|
+
fn({ ...this._settings });
|
|
364
|
+
} catch {
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
case "USER_CONTEXT":
|
|
370
|
+
case "CANVAS_CONTEXT":
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
send(type, payload) {
|
|
375
|
+
this.buildMessage(type, payload).then((msg) => {
|
|
376
|
+
window.parent.postMessage(msg, "*");
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
async buildMessage(type, payload) {
|
|
380
|
+
const nonce = crypto.randomUUID();
|
|
381
|
+
const timestamp = Date.now();
|
|
382
|
+
let token;
|
|
383
|
+
if (this._hmacSecret) {
|
|
384
|
+
token = await signToken(this._hmacSecret, type, this._widgetId, nonce, timestamp);
|
|
385
|
+
} else {
|
|
386
|
+
token = DEV_MODE_TOKEN;
|
|
387
|
+
}
|
|
388
|
+
return {
|
|
389
|
+
protocol: PROTOCOL,
|
|
390
|
+
type,
|
|
391
|
+
widgetId: this._widgetId,
|
|
392
|
+
nonce,
|
|
393
|
+
timestamp,
|
|
394
|
+
token,
|
|
395
|
+
payload
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
/** Tear down the SDK instance, remove all listeners, and reject pending consent requests. */
|
|
399
|
+
destroy() {
|
|
400
|
+
window.removeEventListener("message", this._messageHandler);
|
|
401
|
+
this._stateListeners.length = 0;
|
|
402
|
+
this._eventListeners.clear();
|
|
403
|
+
for (const [, pending] of this._pendingConsents) {
|
|
404
|
+
pending.reject(new Error("Widget destroyed"));
|
|
405
|
+
}
|
|
406
|
+
this._pendingConsents.clear();
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
var CanvasWidget = {
|
|
410
|
+
/**
|
|
411
|
+
* Initialize the SDK and establish communication with the canvas host.
|
|
412
|
+
*
|
|
413
|
+
* Sends a READY message to the host and waits for an INIT response.
|
|
414
|
+
* Resolves with a fully initialized {@link CanvasWidgetSDK} instance.
|
|
415
|
+
* Rejects after 5 seconds if no INIT is received from the host.
|
|
416
|
+
*/
|
|
417
|
+
init() {
|
|
418
|
+
return new Promise((resolve, reject) => {
|
|
419
|
+
const timeout = setTimeout(() => {
|
|
420
|
+
window.removeEventListener("message", handler);
|
|
421
|
+
reject(new Error("CanvasWidget.init() timed out \u2014 no INIT received from host within 5s"));
|
|
422
|
+
}, INIT_TIMEOUT_MS);
|
|
423
|
+
const handler = (event) => {
|
|
424
|
+
const msg = event.data;
|
|
425
|
+
if (!msg || typeof msg !== "object" || msg.protocol !== PROTOCOL) return;
|
|
426
|
+
if (msg.type !== "INIT") return;
|
|
427
|
+
clearTimeout(timeout);
|
|
428
|
+
window.removeEventListener("message", handler);
|
|
429
|
+
const payload = msg.payload;
|
|
430
|
+
const sdk = new CanvasWidgetSDKImpl(payload);
|
|
431
|
+
resolve(sdk);
|
|
432
|
+
};
|
|
433
|
+
window.addEventListener("message", handler);
|
|
434
|
+
const readyMsg = {
|
|
435
|
+
protocol: PROTOCOL,
|
|
436
|
+
type: "READY",
|
|
437
|
+
widgetId: "",
|
|
438
|
+
// Not yet known — host identifies us by iframe source
|
|
439
|
+
nonce: crypto.randomUUID(),
|
|
440
|
+
timestamp: Date.now(),
|
|
441
|
+
token: DEV_MODE_TOKEN,
|
|
442
|
+
// READY is always allowed, token doesn't matter
|
|
443
|
+
payload: { manifestVersion: "1" }
|
|
444
|
+
};
|
|
445
|
+
window.parent.postMessage(readyMsg, "*");
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
export {
|
|
450
|
+
CanvasWidget,
|
|
451
|
+
createServiceClient
|
|
452
|
+
};
|
|
453
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/service.ts", "../src/sdk.ts"],
|
|
4
|
+
"sourcesContent": ["// 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", "// ============================================\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 DbAPI,\n DbCollection,\n DbRecord,\n DbListOptions,\n} from './types';\nimport { createServiceClient } from './service';\n\nconst PROTOCOL = 'canvas-widget-v1';\nconst DEV_MODE_TOKEN = 'dev-mode-skip';\nconst INIT_TIMEOUT_MS = 5000;\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 _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._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 /** 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 /** 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 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 = crypto.randomUUID();\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 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: crypto.randomUUID(),\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"],
|
|
5
|
+
"mappings": ";;;;;;;;AAUO,SAAS,oBAAoB,MAAuD;AACzF,QAAM,YAAY,KAAK,aAAa,WAAW,MAAM,KAAK,UAAU;AACpE,QAAM,UAAU,KAAK,QAAQ,QAAQ,QAAQ,EAAE;AAE/C,iBAAe,KAAK,MAAc,MAAmB,OAAkC;AACrF,UAAM,MAAM,GAAG,OAAO,GAAG,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI,EAAE;AACjE,UAAM,UAAU,IAAI,QAAQ,KAAK,OAAO;AACxC,YAAQ,IAAI,iBAAiB,UAAU,KAAK,EAAE;AAC9C,QAAI,KAAK,QAAQ,CAAC,QAAQ,IAAI,cAAc,GAAG;AAC7C,cAAQ,IAAI,gBAAgB,kBAAkB;AAAA,IAChD;AACA,WAAO,UAAU,KAAK,EAAE,GAAG,MAAM,QAAQ,CAAC;AAAA,EAC5C;AAEA,SAAO;AAAA,IACL,MAAM,MAAmB,MAAc,OAAoB,CAAC,GAAe;AACzE,UAAI,QAAQ,MAAM,KAAK,OAAO,KAAK;AACnC,UAAI,MAAM,MAAM,KAAK,MAAM,MAAM,KAAK;AAEtC,UAAI,IAAI,WAAW,KAAK;AACtB,gBAAQ,MAAM,KAAK,OAAO,IAAI;AAC9B,cAAM,MAAM,KAAK,MAAM,MAAM,KAAK;AAAA,MACpC;AAEA,UAAI,CAAC,IAAI,IAAI;AACX,YAAI,OAA6B,CAAC;AAClC,YAAI;AACF,iBAAQ,MAAM,IAAI,KAAK;AAAA,QACzB,QAAQ;AAAA,QAER;AACA,cAAM,IAAI,MAAM,kCAAkC,IAAI,MAAM,MAAM,KAAK,WAAW,EAAE,EAAE;AAAA,MACxF;AACA,aAAQ,MAAM,IAAI,KAAK;AAAA,IACzB;AAAA,EACF;AACF;;;ACfA,IAAM,WAAW;AACjB,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AAgBxB,IAAI,YAAuD;AAE3D,eAAe,WAAW,QAAoC;AAC5D,MAAI,aAAa,UAAU,WAAW,OAAQ,QAAO,UAAU;AAE/D,QAAM,UAAU,IAAI,YAAY,EAAE,OAAO,MAAM;AAC/C,QAAM,MAAM,IAAI,YAAY,QAAQ,UAAU;AAC9C,MAAI,WAAW,GAAG,EAAE,IAAI,OAAO;AAE/B,QAAM,MAAM,MAAM,OAAO,OAAO,UAAU,OAAO,KAAK,EAAE,MAAM,QAAQ,MAAM,UAAU,GAAG,OAAO;AAAA,IAC9F;AAAA,EACF,CAAC;AAED,cAAY,EAAE,QAAQ,IAAI;AAC1B,SAAO;AACT;AAEA,eAAe,UACb,QACA,MACA,UACA,OACA,WACiB;AACjB,QAAM,MAAM,MAAM,WAAW,MAAM;AACnC,QAAM,OAAO,IAAI,YAAY,EAAE,OAAO,GAAG,IAAI,IAAI,QAAQ,IAAI,KAAK,IAAI,SAAS,EAAE;AACjF,QAAM,MAAM,IAAI,YAAY,KAAK,UAAU;AAC3C,MAAI,WAAW,GAAG,EAAE,IAAI,IAAI;AAC5B,QAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,GAAG;AACrD,SAAO,MAAM,KAAK,IAAI,WAAW,GAAG,CAAC,EAClC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AACZ;AAOO,IAAM,sBAAN,MAAqD;AAAA,EA0B1D,YAAY,MAAmB;AAzB/B,wBAAQ;AACR,wBAAQ;AACR,wBAAQ;AACR,wBAAQ;AACR,wBAAQ;AACR,wBAAQ;AACR,wBAAQ;AACR,wBAAQ,oBAAmB,oBAAI,IAG7B;AACF,wBAAQ,cAA0D;AAClE,wBAAQ,mBAA8D,CAAC;AACvE,wBAAQ,iBAAoC;AAC5C,wBAAQ,0BAAqE,CAAC;AAC9E,wBAAQ,YAAW;AACnB,wBAAQ,mBAAqD,CAAC;AAC9D,wBAAQ,4BAA+E,CAAC;AACxF,wBAAQ,mBAAkB,oBAAI,IAAiD;AAC/E,wBAAQ;AACR,wBAAQ;AAGR;AAAA,wBAAS;AAGP,SAAK,YAAY,KAAK;AACtB,SAAK,SAAS,KAAK;AACnB,SAAK,cAAc,KAAK;AACxB,SAAK,QAAQ,KAAK;AAClB,SAAK,gBAAgB,KAAK;AAC1B,SAAK,kBAAkB,KAAK,kBAAkB;AAC9C,SAAK,kBAAkB,KAAK;AAC5B,SAAK,YAAY,KAAK,YAAY,CAAC;AAGnC,QAAI,KAAK,YAAY;AACnB,WAAK,UAAU,oBAAoB;AAAA,QACjC,SAAS,KAAK;AAAA,QACd,QAAQ,CAAC,iBAA2B,KAAK,OAAO,YAAY;AAAA,MAC9D,CAAC;AAAA,IACH;AAEA,SAAK,kBAAkB,KAAK,cAAc,KAAK,IAAI;AACnD,WAAO,iBAAiB,WAAW,KAAK,eAAe;AAAA,EACzD;AAAA;AAAA,EAGA,IAAI,WAAmB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,QAA4B;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,OAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,cAAc,IAAqD;AACjE,SAAK,gBAAgB,KAAK,EAAE;AAC5B,WAAO,MAAM;AACX,YAAM,MAAM,KAAK,gBAAgB,QAAQ,EAAE;AAC3C,UAAI,QAAQ,GAAI,MAAK,gBAAgB,OAAO,KAAK,CAAC;AAAA,IACpD;AAAA,EACF;AAAA;AAAA,EAGA,gBAAsB;AACpB,SAAK,KAAK,kBAAkB,CAAC,CAAC;AAAA,EAChC;AAAA;AAAA,EAGA,kBAAwB;AACtB,SAAK,KAAK,oBAAoB,CAAC,CAAC;AAAA,EAClC;AAAA;AAAA,EAGA,kBAAsC;AACpC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,qBAAqB,UAA2D;AAC9E,SAAK,uBAAuB,KAAK,QAAQ;AACzC,WAAO,MAAM;AACX,WAAK,yBAAyB,KAAK,uBAAuB,OAAO,CAAC,OAAO,OAAO,QAAQ;AAAA,IAC1F;AAAA,EACF;AAAA;AAAA,EAGA,YAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,cAAc,UAAkD;AAC9D,SAAK,gBAAgB,KAAK,QAAQ;AAClC,WAAO,MAAM;AACX,WAAK,kBAAkB,KAAK,gBAAgB,OAAO,CAAC,OAAO,OAAO,QAAQ;AAAA,IAC5E;AAAA,EACF;AAAA;AAAA,EAGA,oBAA0B;AACxB,SAAK,KAAK,sBAAsB,CAAC,CAAC;AAAA,EACpC;AAAA;AAAA,EAGA,KAAK,OAAe,SAAwB;AAC1C,SAAK,KAAK,cAAc,EAAE,OAAO,QAAQ,CAAC;AAAA,EAC5C;AAAA;AAAA,EAGA,UAAU,OAAe,IAA8C;AACrE,UAAM,OAAO,KAAK,gBAAgB,IAAI,KAAK,KAAK,CAAC;AACjD,SAAK,KAAK,EAAE;AACZ,SAAK,gBAAgB,IAAI,OAAO,IAAI;AACpC,WAAO,MAAM;AACX,YAAM,UAAU,KAAK,gBAAgB,IAAI,KAAK;AAC9C,UAAI,CAAC,QAAS;AACd,YAAM,MAAM,QAAQ,QAAQ,EAAE;AAC9B,UAAI,QAAQ,GAAI,SAAQ,OAAO,KAAK,CAAC;AAAA,IACvC;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,OAAkC,SAAiB,MAAsB;AAC3E,SAAK,KAAK,OAAO,EAAE,OAAO,SAAS,MAAM,QAAQ,KAAK,CAAC;AAAA,EACzD;AAAA;AAAA,EAGA,MAAM,eAAe,OAAe,QAAwC;AAC1E,WAAO,IAAI,QAAuB,CAAC,SAAS,WAAW;AACrD,YAAM,UAAU,WAAW,MAAM;AAC/B,aAAK,iBAAiB,OAAO,KAAK;AAClC,eAAO,IAAI,MAAM,2BAA2B,CAAC;AAAA,MAC/C,GAAG,GAAK;AAER,WAAK,iBAAiB,IAAI,OAAO;AAAA,QAC/B,SAAS,CAAC,MAAqB;AAC7B,uBAAa,OAAO;AACpB,kBAAQ,CAAC;AAAA,QACX;AAAA,QACA,QAAQ,CAAC,MAAa;AACpB,uBAAa,OAAO;AACpB,iBAAO,CAAC;AAAA,QACV;AAAA,MACF,CAAC;AAED,WAAK,KAAK,mBAAmB,EAAE,OAAO,OAAO,CAAC;AAAA,IAChD,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,OAAO,cAAyC;AACpD,QAAI,CAAC,gBAAgB,KAAK,cAAc,KAAK,IAAI,IAAI,KAAK,WAAW,YAAY,KAAO;AACtF,aAAO,KAAK,WAAW;AAAA,IACzB;AACA,UAAM,IAAI,MAAM,mEAA8D;AAAA,EAChF;AAAA;AAAA,EAGA,IAAI,UAAsB;AACxB,WAAO;AAAA,MACL,KAAK,CAAC,QAAgB,KAAK,eAAe,OAAO,GAAG;AAAA,MACpD,KAAK,CAAC,KAAa,UAAmB,KAAK,eAAe,OAAO,KAAK,KAAK,EAAE,KAAK,MAAM;AAAA,MAAC,CAAC;AAAA,MAC1F,QAAQ,CAAC,QAAgB,KAAK,eAAe,UAAU,GAAG,EAAE,KAAK,MAAM;AAAA,MAAC,CAAC;AAAA,IAC3E;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,KAAY;AACd,UAAM,MAAM,CAAC,MAAc,QAAgB,SAAmB,KAAK,iBAAiB,MAAM,QAAQ,IAAI;AACtG,WAAO;AAAA,MACL,WAAW,MAA4B;AACrC,cAAM,OAAO,kBAAkB,mBAAmB,IAAI,CAAC;AACvD,eAAO;AAAA,UACL,QAAQ,CAAC,SAAS,IAAI,MAAM,QAAQ,IAAI;AAAA,UACxC,KAAK,CAAC,OAAO,IAAI,GAAG,IAAI,IAAI,mBAAmB,EAAE,CAAC,IAAI,KAAK;AAAA,UAC3D,MAAM,CAAC,SAAyB;AAC9B,kBAAM,KAAK,IAAI,gBAAgB;AAC/B,gBAAI,MAAM,SAAS,KAAM,IAAG,IAAI,SAAS,OAAO,KAAK,KAAK,CAAC;AAC3D,gBAAI,MAAM,OAAQ,IAAG,IAAI,UAAU,KAAK,MAAM;AAC9C,gBAAI,MAAM,KAAM,IAAG,IAAI,QAAQ,KAAK,IAAI;AACxC,gBAAI,MAAM,KAAM,IAAG,IAAI,QAAQ,MAAM;AACrC,kBAAM,SAAS,GAAG,SAAS,IAAI,IAAI,GAAG,SAAS,CAAC,KAAK;AACrD,mBAAO,IAAI,GAAG,IAAI,GAAG,MAAM,IAAI,KAAK;AAAA,UACtC;AAAA,UACA,QAAQ,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,IAAI,mBAAmB,EAAE,CAAC,IAAI,SAAS,KAAK;AAAA,UAC9E,QAAQ,CAAC,OAAQ,IAAI,GAAG,IAAI,IAAI,mBAAmB,EAAE,CAAC,IAAI,QAAQ,EAAuB,KAAK,MAAM;AAAA,UAAC,CAAC;AAAA,QACxG;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,WAAwB;AAC1B,WAAO;AAAA,MACL,KAAK,OAAO,EAAE,GAAG,KAAK,UAAU;AAAA,MAChC,KAAK,CAAC,gBAAyC;AAC7C,aAAK,YAAY,EAAE,GAAG,KAAK,WAAW,GAAG,YAAY;AACrD,aAAK,KAAK,kBAAkB,EAAE,UAAU,KAAK,UAAU,CAAC;AAAA,MAC1D;AAAA,MACA,gBAAgB,CAAC,OAAoD;AACnE,aAAK,yBAAyB,KAAK,EAAE;AACrC,eAAO,MAAM;AACX,eAAK,2BAA2B,KAAK,yBAAyB,OAAO,CAAC,OAAO,OAAO,EAAE;AAAA,QACxF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,OAAO,SAAiB,MAA6D;AACzF,SAAK,KAAK,gBAAgB;AAAA,MACxB;AAAA,MACA,OAAO,MAAM,SAAS;AAAA,MACtB,UAAU,MAAM,YAAY;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA;AAAA,EAIA,MAAc,iBAAiB,MAAc,QAAgB,MAAkC;AAC7F,QAAI,CAAC,KAAK,eAAe;AACvB,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AACA,UAAM,MAAM,GAAG,KAAK,aAAa,GAAG,IAAI;AACxC,UAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,QAAI,KAAK,iBAAiB;AACxB,cAAQ,eAAe,IAAI,UAAU,KAAK,eAAe;AAAA,IAC3D;AACA,UAAM,OAAoB,EAAE,QAAQ,QAAQ;AAC5C,QAAI,SAAS,UAAa,WAAW,SAAS,WAAW,UAAU;AACjE,WAAK,OAAO,KAAK,UAAU,IAAI;AAAA,IACjC;AACA,UAAM,WAAW,MAAM,MAAM,KAAK,IAAI;AACtC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,MAAM,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAClD,YAAM,IAAI,MAAO,IAA+B,WAAW,WAAW,MAAM,IAAI,IAAI,SAAS;AAAA,IAC/F;AACA,QAAI,WAAW,SAAS,WAAW,UAAU,WAAW,SAAS;AAC/D,YAAM,SAAS,MAAM,SAAS,KAAK;AACnC,aAAQ,OAAmC;AAAA,IAC7C;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,eAAe,QAAgB,KAAa,OAAmC;AACrF,WAAO,KAAK,iBAAiB,uBAAuB,mBAAmB,GAAG,CAAC,IAAI,QAAQ,KAAK;AAAA,EAC9F;AAAA,EAEQ,cAAc,OAA2B;AAC/C,UAAM,MAAM,MAAM;AAClB,QAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,IAAI,aAAa,SAAU;AAElE,YAAQ,IAAI,MAAM;AAAA,MAChB,KAAK,gBAAgB;AACnB,cAAM,UAAU,IAAI;AACpB,aAAK,SAAS,QAAQ;AACtB,mBAAW,YAAY,KAAK,iBAAiB;AAC3C,mBAAS,QAAQ,KAAK;AAAA,QACxB;AACA;AAAA,MACF;AAAA,MACA,KAAK,mBAAmB;AACtB,cAAM,UAAU,IAAI;AACpB,cAAM,YAAY,KAAK,gBAAgB,IAAI,QAAQ,KAAK;AACxD,YAAI,WAAW;AACb,gBAAM,MAAmB;AAAA,YACvB,OAAO,QAAQ;AAAA,YACf,SAAS,QAAQ;AAAA,YACjB,WAAW,QAAQ;AAAA,UACrB;AACA,qBAAW,YAAY,WAAW;AAChC,qBAAS,GAAG;AAAA,UACd;AAAA,QACF;AACA;AAAA,MACF;AAAA,MACA,KAAK,kBAAkB;AACrB,cAAM,UAAU,IAAI;AACpB,cAAM,UAAU,KAAK,iBAAiB,IAAI,QAAQ,KAAK;AACvD,YAAI,SAAS;AACX,eAAK,iBAAiB,OAAO,QAAQ,KAAK;AAC1C,kBAAQ,QAAQ;AAAA,YACd,OAAO,QAAQ;AAAA,YACf,SAAS,QAAQ;AAAA,YACjB,YAAY,QAAQ;AAAA,YACpB,WAAW,QAAQ;AAAA,UACrB,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAAA,MACA,KAAK,sBAAsB;AACzB,cAAM,UAAU,IAAI;AACpB,aAAK,aAAa;AAAA,UAChB,OAAO,QAAQ;AAAA,UACf,WAAW,KAAK,IAAI,IAAI,QAAQ,YAAY;AAAA,QAC9C;AACA;AAAA,MACF;AAAA,MACA,KAAK,wBAAwB;AAC3B,cAAM,WAAW,IAAI,SAAS;AAC9B,YAAI,UAAU;AACZ,eAAK,gBAAgB;AACrB,eAAK,uBAAuB,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC;AAAA,QAC1D;AACA;AAAA,MACF;AAAA,MACA,KAAK,gBAAgB;AACnB,cAAM,OAAO,CAAC,CAAC,IAAI,SAAS;AAC5B,YAAI,SAAS,KAAK,UAAU;AAC1B,eAAK,WAAW;AAChB,eAAK,gBAAgB,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;AAAA,QAC/C;AACA;AAAA,MACF;AAAA,MACA,KAAK,0BAA0B;AAC7B,cAAM,IAAI,IAAI;AACd,aAAK,YAAY,EAAE,GAAG,EAAE,SAAS;AACjC,mBAAW,MAAM,KAAK,0BAA0B;AAC9C,cAAI;AACF,eAAG,EAAE,GAAG,KAAK,UAAU,CAAC;AAAA,UAC1B,QAAQ;AAAA,UAER;AAAA,QACF;AACA;AAAA,MACF;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAEH;AAAA,IACJ;AAAA,EACF;AAAA,EAEQ,KAAK,MAAc,SAAwC;AAEjE,SAAK,aAAa,MAAM,OAAO,EAAE,KAAK,CAAC,QAAQ;AAC7C,aAAO,OAAO,YAAY,KAAK,GAAG;AAAA,IACpC,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,aACZ,MACA,SACwB;AACxB,UAAM,QAAQ,OAAO,WAAW;AAChC,UAAM,YAAY,KAAK,IAAI;AAE3B,QAAI;AACJ,QAAI,KAAK,aAAa;AACpB,cAAQ,MAAM,UAAU,KAAK,aAAa,MAAM,KAAK,WAAW,OAAO,SAAS;AAAA,IAClF,OAAO;AACL,cAAQ;AAAA,IACV;AAEA,WAAO;AAAA,MACL,UAAU;AAAA,MACV;AAAA,MACA,UAAU,KAAK;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,UAAgB;AACd,WAAO,oBAAoB,WAAW,KAAK,eAAe;AAC1D,SAAK,gBAAgB,SAAS;AAC9B,SAAK,gBAAgB,MAAM;AAC3B,eAAW,CAAC,EAAE,OAAO,KAAK,KAAK,kBAAkB;AAC/C,cAAQ,OAAO,IAAI,MAAM,kBAAkB,CAAC;AAAA,IAC9C;AACA,SAAK,iBAAiB,MAAM;AAAA,EAC9B;AACF;AAeO,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ1B,OAAiC;AAC/B,WAAO,IAAI,QAAyB,CAAC,SAAS,WAAW;AACvD,YAAM,UAAU,WAAW,MAAM;AAC/B,eAAO,oBAAoB,WAAW,OAAO;AAC7C,eAAO,IAAI,MAAM,2EAAsE,CAAC;AAAA,MAC1F,GAAG,eAAe;AAElB,YAAM,UAAU,CAAC,UAAwB;AACvC,cAAM,MAAM,MAAM;AAClB,YAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,IAAI,aAAa,SAAU;AAClE,YAAI,IAAI,SAAS,OAAQ;AAEzB,qBAAa,OAAO;AACpB,eAAO,oBAAoB,WAAW,OAAO;AAE7C,cAAM,UAAU,IAAI;AACpB,cAAM,MAAM,IAAI,oBAAoB,OAAO;AAC3C,gBAAQ,GAAG;AAAA,MACb;AAEA,aAAO,iBAAiB,WAAW,OAAO;AAG1C,YAAM,WAA0B;AAAA,QAC9B,UAAU;AAAA,QACV,MAAM;AAAA,QACN,UAAU;AAAA;AAAA,QACV,OAAO,OAAO,WAAW;AAAA,QACzB,WAAW,KAAK,IAAI;AAAA,QACpB,OAAO;AAAA;AAAA,QACP,SAAS,EAAE,iBAAiB,IAAI;AAAA,MAClC;AAEA,aAAO,OAAO,YAAY,UAAU,GAAG;AAAA,IACzC,CAAC;AAAA,EACH;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/sdk.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { CanvasWidgetSDK, WidgetDisplayState, WidgetEvent, InitPayload, ConsentResult, UserContext, StorageAPI, SettingsAPI, WidgetServiceClient, DbAPI } from './types';
|
|
2
|
+
/** Internal SDK implementation — not exposed directly to widget authors. */
|
|
3
|
+
export declare class CanvasWidgetSDKImpl implements CanvasWidgetSDK {
|
|
4
|
+
private _widgetId;
|
|
5
|
+
private _state;
|
|
6
|
+
private _hmacSecret;
|
|
7
|
+
private _user;
|
|
8
|
+
private _widgetApiUrl;
|
|
9
|
+
private _widgetApiToken;
|
|
10
|
+
private _installationId;
|
|
11
|
+
private _pendingConsents;
|
|
12
|
+
private _cachedJwt;
|
|
13
|
+
private _stateListeners;
|
|
14
|
+
private _displayState;
|
|
15
|
+
private _displayStateCallbacks;
|
|
16
|
+
private _focused;
|
|
17
|
+
private _focusCallbacks;
|
|
18
|
+
private _settingsRemoteListeners;
|
|
19
|
+
private _eventListeners;
|
|
20
|
+
private _settings;
|
|
21
|
+
private _messageHandler;
|
|
22
|
+
/** Authenticated HTTP client scoped to the widget's declared service URL. Undefined when no service is declared. */
|
|
23
|
+
readonly service: WidgetServiceClient | undefined;
|
|
24
|
+
constructor(init: InitPayload);
|
|
25
|
+
/** The unique identifier for this widget instance on the canvas. */
|
|
26
|
+
get widgetId(): string;
|
|
27
|
+
/** The current display state of the widget (collapsed, expanding, expanded, collapsing). */
|
|
28
|
+
get state(): WidgetDisplayState;
|
|
29
|
+
/** The authenticated user context, or null when user data access has not been granted. */
|
|
30
|
+
get user(): UserContext | null;
|
|
31
|
+
/** Subscribe to display state changes; returns an unsubscribe function. */
|
|
32
|
+
onStateChange(cb: (state: WidgetDisplayState) => void): () => void;
|
|
33
|
+
/** Ask the canvas host to expand this widget into its modal view. */
|
|
34
|
+
requestExpand(): void;
|
|
35
|
+
/** Ask the canvas host to collapse this widget back to its tile view. */
|
|
36
|
+
requestCollapse(): void;
|
|
37
|
+
/** Get the current display state */
|
|
38
|
+
getDisplayState(): WidgetDisplayState;
|
|
39
|
+
/** Register a callback for display state changes */
|
|
40
|
+
onDisplayStateChange(callback: (state: WidgetDisplayState) => void): () => void;
|
|
41
|
+
/** Is this widget the user's current focus? Updates via FOCUS_CHANGE. */
|
|
42
|
+
isFocused(): boolean;
|
|
43
|
+
/** Subscribe to focus transitions. Returns an unsubscribe function. */
|
|
44
|
+
onFocusChange(callback: (focused: boolean) => void): () => void;
|
|
45
|
+
/** Request promotion to fullscreen from partial */
|
|
46
|
+
requestFullscreen(): void;
|
|
47
|
+
/** Broadcast an event on the given topic to all subscribed widgets on the canvas. */
|
|
48
|
+
emit(topic: string, payload: unknown): void;
|
|
49
|
+
/** Subscribe to events on the given topic; returns an unsubscribe function. */
|
|
50
|
+
subscribe(topic: string, cb: (event: WidgetEvent) => void): () => void;
|
|
51
|
+
/** Send a structured log entry to the canvas host for centralized debugging. */
|
|
52
|
+
log(level: 'info' | 'warn' | 'error', message: string, data?: unknown): void;
|
|
53
|
+
/** Request user consent for a named data scope; resolves with the consent decision. */
|
|
54
|
+
requestConsent(scope: string, reason: string): Promise<ConsentResult>;
|
|
55
|
+
/** Return the cached JWT for authenticating requests to external APIs; throws if no token is available. Pass forceRefresh=true to bypass the cache. */
|
|
56
|
+
getJwt(forceRefresh?: boolean): Promise<string>;
|
|
57
|
+
/** Persistent key-value storage scoped to this widget installation. */
|
|
58
|
+
get storage(): StorageAPI;
|
|
59
|
+
/** Ad-hoc collections/records backend scoped to the canvas/room. */
|
|
60
|
+
get db(): DbAPI;
|
|
61
|
+
/** Widget configuration settings persisted by the canvas host. */
|
|
62
|
+
get settings(): SettingsAPI;
|
|
63
|
+
/** Display a toast notification in the canvas host UI. */
|
|
64
|
+
notify(message: string, opts?: {
|
|
65
|
+
level?: string;
|
|
66
|
+
duration?: number;
|
|
67
|
+
}): Promise<void>;
|
|
68
|
+
private widgetApiRequest;
|
|
69
|
+
private storageRequest;
|
|
70
|
+
private handleMessage;
|
|
71
|
+
private send;
|
|
72
|
+
private buildMessage;
|
|
73
|
+
/** Tear down the SDK instance, remove all listeners, and reject pending consent requests. */
|
|
74
|
+
destroy(): void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Entry point for the Flowstate Canvas Widget SDK.
|
|
78
|
+
*
|
|
79
|
+
* Usage:
|
|
80
|
+
* ```ts
|
|
81
|
+
* import { CanvasWidget } from '@goflowstate/widget-sdk';
|
|
82
|
+
* const widget = await CanvasWidget.init();
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare const CanvasWidget: {
|
|
86
|
+
/**
|
|
87
|
+
* Initialize the SDK and establish communication with the canvas host.
|
|
88
|
+
*
|
|
89
|
+
* Sends a READY message to the host and waits for an INIT response.
|
|
90
|
+
* Resolves with a fully initialized {@link CanvasWidgetSDK} instance.
|
|
91
|
+
* Rejects after 5 seconds if no INIT is received from the host.
|
|
92
|
+
*/
|
|
93
|
+
init(): Promise<CanvasWidgetSDK>;
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk.focus.test.d.ts","sourceRoot":"","sources":["../src/sdk.focus.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { WidgetServiceClient } from './types';
|
|
2
|
+
export interface CreateServiceClientOptions {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
getJwt: (forceRefresh?: boolean) => Promise<string>;
|
|
5
|
+
fetchImpl?: typeof fetch;
|
|
6
|
+
}
|
|
7
|
+
export declare function createServiceClient(opts: CreateServiceClientOptions): WidgetServiceClient;
|
|
8
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,0BAA0B,GAAG,mBAAmB,CAoCzF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.test.d.ts","sourceRoot":"","sources":["../src/service.test.ts"],"names":[],"mappings":""}
|