@deway-ai/web-sdk 0.80.0 → 0.82.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 +107 -0
- package/dist/loader.es.js +92 -67
- package/dist/loader.umd.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ TASKS:
|
|
|
34
34
|
IMPLEMENTATION NOTES:
|
|
35
35
|
- Follow my existing project structure and code style conventions
|
|
36
36
|
- Use appropriate lifecycle hooks: React (useEffect), Vue (onMounted), Angular (ngOnInit)
|
|
37
|
+
- Ensure mobile compatibility support for the side panel integration feature.
|
|
37
38
|
|
|
38
39
|
FIRST analyze my project structure and detect the framework and package manager. Then and only then implement the Deway Web SDK integration accordingly.
|
|
39
40
|
```
|
|
@@ -94,6 +95,112 @@ Deway.init({
|
|
|
94
95
|
|
|
95
96
|
**Why so simple?** Themes, feature flags, AI prompts, and behavior settings are managed remotely and adapt automatically. This lets you iterate on user experiences without SDK updates or app deployments.
|
|
96
97
|
|
|
98
|
+
## Responsive Behavior with the Side Panel
|
|
99
|
+
|
|
100
|
+
> **Read this if your host page uses `@media` queries, `100vw`, or `window.innerWidth`-based layout logic.** When the AI assistant opens in side-panel mode, the host page is squeezed to make room for the panel. Existing responsive rules may not react the way you expect — this section explains the contract and the recommended migration.
|
|
101
|
+
|
|
102
|
+
### What changes when the panel opens
|
|
103
|
+
|
|
104
|
+
When the side panel opens (default width: `420px`), the SDK squeezes the host page by setting `<html>` to `width: calc(100vw - 420px)`. The browser's *real* viewport does not change — only the rendered width of `<html>` does. This has one important consequence:
|
|
105
|
+
|
|
106
|
+
**`@media` queries do NOT fire against the squeezed width.** Media queries evaluate against the real viewport, which is unchanged. A rule like `@media (max-width: 768px)` will not activate when the side panel reduces your content area to `760px` — your desktop layout will render in a 760px box.
|
|
107
|
+
|
|
108
|
+
### CSS custom properties published on `:root`
|
|
109
|
+
|
|
110
|
+
The SDK publishes two custom properties on `:root` while the panel is open:
|
|
111
|
+
|
|
112
|
+
| Property | Value when open | Value when closed |
|
|
113
|
+
|-----------------------|-----------------------|-----------------------------------------|
|
|
114
|
+
| `--deway-panel-width` | `"420px"` | *unset* (property removed) |
|
|
115
|
+
| `--deway-panel-side` | `"right"` or `"left"` | *unset* (property removed) |
|
|
116
|
+
|
|
117
|
+
Because the properties are removed (not set to `0px`) when the panel closes, host CSS should use a fallback:
|
|
118
|
+
|
|
119
|
+
```css
|
|
120
|
+
:root {
|
|
121
|
+
/* Read with fallback — works whether the panel is open or closed */
|
|
122
|
+
--my-effective-panel: var(--deway-panel-width, 0px);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Recommended: use `@container` for in-content breakpoints
|
|
127
|
+
|
|
128
|
+
Replace viewport `@media` queries with container queries scoped to your main content. The container will shrink along with the host page when the side panel opens, so breakpoints behave intuitively:
|
|
129
|
+
|
|
130
|
+
```css
|
|
131
|
+
/* Before — does not react when the side panel opens */
|
|
132
|
+
@media (max-width: 768px) {
|
|
133
|
+
.sidebar { display: none; }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* After — reacts to the content area, not the real viewport */
|
|
137
|
+
.content { container-type: inline-size; }
|
|
138
|
+
|
|
139
|
+
@container (max-width: 768px) {
|
|
140
|
+
.sidebar { display: none; }
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Browser support:** `@container` is supported in Chrome 105+, Safari 16+, Firefox 110+, and Edge 105+. Hosts on older browsers fall back to no breakpoint adjustment (same behavior as before this change).
|
|
145
|
+
|
|
146
|
+
### Fixed-position and `100vw` recipes
|
|
147
|
+
|
|
148
|
+
Use the custom property with a `0px` fallback to make full-bleed elements and fixed overlays panel-aware:
|
|
149
|
+
|
|
150
|
+
```css
|
|
151
|
+
/* Full-bleed hero that respects the side panel */
|
|
152
|
+
.hero {
|
|
153
|
+
width: calc(100vw - var(--deway-panel-width, 0px));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Fixed modal/toast that stays clear of the side panel */
|
|
157
|
+
.modal {
|
|
158
|
+
position: fixed;
|
|
159
|
+
right: calc(var(--deway-panel-width, 0px));
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### JS: replace `window.innerWidth` reads with the event
|
|
164
|
+
|
|
165
|
+
For layout logic that runs in JavaScript, listen for the `deway:panel-resize` event on `window` instead of (or in addition to) `resize`:
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
window.addEventListener("deway:panel-resize", (event) => {
|
|
169
|
+
const { width, side, isOpen } = event.detail;
|
|
170
|
+
// Re-render anything that depended on window.innerWidth
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Or read the current state synchronously:
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
const { width, side, isOpen } = Deway.getPanelState();
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Opt-out
|
|
181
|
+
|
|
182
|
+
If you want full control over your layout, opt out of the host squeeze:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
Deway.init({
|
|
186
|
+
appKey: 'your-app-key',
|
|
187
|
+
sidePanel: { hostSqueeze: false }
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
When `hostSqueeze` is `false`, the SDK still publishes `--deway-panel-width`, `--deway-panel-side`, and the `deway:panel-resize` event — only the automatic `<html>` squeeze is skipped, so you can apply your own reflow logic.
|
|
192
|
+
|
|
193
|
+
### What this does NOT solve
|
|
194
|
+
|
|
195
|
+
The contract above covers CSS- and JS-driven layout in code you control. It does **not** intercept:
|
|
196
|
+
|
|
197
|
+
- `window.innerWidth` reads in third-party scripts you don't control
|
|
198
|
+
- `<picture>` and `srcset` size queries (resolved by the browser against the real viewport)
|
|
199
|
+
- Print stylesheets and `@page` rules
|
|
200
|
+
- Browser-native viewport units (`100vw`, `100dvw`) in third-party or non-modifiable CSS
|
|
201
|
+
|
|
202
|
+
For these cases, use the opt-out above and reflow with your own logic, or migrate the affected code to read from `--deway-panel-width` / `Deway.getPanelState()`.
|
|
203
|
+
|
|
97
204
|
## API Reference
|
|
98
205
|
|
|
99
206
|
### `Deway.init(config)`
|
package/dist/loader.es.js
CHANGED
|
@@ -12,43 +12,43 @@ function L(o) {
|
|
|
12
12
|
function U(o, e) {
|
|
13
13
|
return o instanceof Error ? { name: o.name, message: o.message, stack: o.stack } : e.has(o) ? "[Circular]" : (e.add(o), o);
|
|
14
14
|
}
|
|
15
|
-
function
|
|
15
|
+
function T(o) {
|
|
16
16
|
const e = /* @__PURE__ */ new WeakSet();
|
|
17
17
|
return JSON.stringify(o, (t, n) => {
|
|
18
18
|
const i = L(n);
|
|
19
19
|
return i !== void 0 ? i : typeof n == "object" && n !== null ? U(n, e) : n;
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
|
-
function
|
|
22
|
+
function O(o, e) {
|
|
23
23
|
const t = o !== void 0 && Object.keys(o).length > 0, n = e !== void 0;
|
|
24
24
|
if (!t && !n) return;
|
|
25
25
|
const i = typeof e == "object" && e !== null && !Array.isArray(e) ? e : n ? { value: e } : {};
|
|
26
26
|
return { ...o, ...i };
|
|
27
27
|
}
|
|
28
|
-
function
|
|
28
|
+
function w(o, e, t, n, i) {
|
|
29
29
|
const r = e;
|
|
30
30
|
if (I[o] < r) return;
|
|
31
31
|
const s = {
|
|
32
32
|
level: o,
|
|
33
33
|
time: Date.now(),
|
|
34
34
|
msg: t
|
|
35
|
-
},
|
|
36
|
-
|
|
35
|
+
}, d = O(n, i);
|
|
36
|
+
d !== void 0 && (s.ctx = d), T(s);
|
|
37
37
|
}
|
|
38
38
|
function u(o) {
|
|
39
39
|
const e = o !== void 0 && "minLevel" in o ? o.minLevel : void 0, t = e === void 0 ? x : I[e], n = o?.context;
|
|
40
40
|
return {
|
|
41
41
|
debug(i, r) {
|
|
42
|
-
|
|
42
|
+
w("debug", t, i, n, r);
|
|
43
43
|
},
|
|
44
44
|
info(i, r) {
|
|
45
|
-
|
|
45
|
+
w("info", t, i, n, r);
|
|
46
46
|
},
|
|
47
47
|
warn(i, r) {
|
|
48
|
-
|
|
48
|
+
w("warn", t, i, n, r);
|
|
49
49
|
},
|
|
50
50
|
error(i, r) {
|
|
51
|
-
|
|
51
|
+
w("error", t, i, n, r);
|
|
52
52
|
},
|
|
53
53
|
child(i) {
|
|
54
54
|
return u({
|
|
@@ -60,8 +60,8 @@ function u(o) {
|
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
-
const k = u({ context: { module: "sdk-config-store" } }), A = "deway-sdk-config",
|
|
64
|
-
class
|
|
63
|
+
const k = u({ context: { module: "sdk-config-store" } }), A = "deway-sdk-config", _ = ["Understanding intent", "Reading web page", "Browsing the docs", "Enriching context", "Validating understanding", "Crafting response"];
|
|
64
|
+
class Q {
|
|
65
65
|
saveConfig(e) {
|
|
66
66
|
if (window?.localStorage)
|
|
67
67
|
try {
|
|
@@ -91,7 +91,7 @@ class O {
|
|
|
91
91
|
return this.loadConfig()?.aiDisclaimer;
|
|
92
92
|
}
|
|
93
93
|
getThinkingMessages() {
|
|
94
|
-
return this.loadConfig()?.thinkingMessages ??
|
|
94
|
+
return this.loadConfig()?.thinkingMessages ?? _;
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
97
97
|
* Get the support handoff configuration
|
|
@@ -149,9 +149,6 @@ class O {
|
|
|
149
149
|
getEmptyChatStateIconInlineSvg() {
|
|
150
150
|
return this.getCustomIcons()?.empty_chat_state_icon_inline_svg ?? void 0;
|
|
151
151
|
}
|
|
152
|
-
getEntrypointWidgetIconSize() {
|
|
153
|
-
return this.loadConfig()?.entrypointWidgetIconSize ?? N;
|
|
154
|
-
}
|
|
155
152
|
getUIAlignment() {
|
|
156
153
|
return this.loadConfig()?.uiAlignment ?? null;
|
|
157
154
|
}
|
|
@@ -161,6 +158,16 @@ class O {
|
|
|
161
158
|
getUIAlignmentSide() {
|
|
162
159
|
return this.getUIAlignment()?.side ?? "right";
|
|
163
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Whether the SDK should inject the `<html>` squeeze stylesheet when the
|
|
163
|
+
* side-panel opens. Default is `true` — hosts can opt out via
|
|
164
|
+
* `Deway.init({ sidePanel: { hostSqueeze: false } })`. When `false`, the
|
|
165
|
+
* SDK still publishes the responsive contract (CSS custom properties +
|
|
166
|
+
* `deway:panel-resize` event) but skips the squeeze.
|
|
167
|
+
*/
|
|
168
|
+
getSidePanelHostSqueezeEnabled() {
|
|
169
|
+
return this.loadConfig()?.sidePanel?.hostSqueeze ?? !0;
|
|
170
|
+
}
|
|
164
171
|
}
|
|
165
172
|
const f = u({ context: { module: "sdk-cache-manager" } });
|
|
166
173
|
class l {
|
|
@@ -170,9 +177,9 @@ class l {
|
|
|
170
177
|
static STORE_NAME = "sdk";
|
|
171
178
|
async cacheSDK(e, t, n, i) {
|
|
172
179
|
try {
|
|
173
|
-
const
|
|
180
|
+
const d = (await this.openIndexedDB()).transaction([l.STORE_NAME], "readwrite").objectStore(l.STORE_NAME);
|
|
174
181
|
await new Promise((E, K) => {
|
|
175
|
-
const
|
|
182
|
+
const g = d.put({
|
|
176
183
|
id: "latest",
|
|
177
184
|
code: e,
|
|
178
185
|
version: t,
|
|
@@ -180,7 +187,7 @@ class l {
|
|
|
180
187
|
signature: i,
|
|
181
188
|
timestamp: Date.now()
|
|
182
189
|
});
|
|
183
|
-
|
|
190
|
+
g.onsuccess = () => E(g.result), g.onerror = () => K(g.error);
|
|
184
191
|
}), f.debug("SDK cached in IndexedDB");
|
|
185
192
|
} catch {
|
|
186
193
|
try {
|
|
@@ -200,8 +207,8 @@ class l {
|
|
|
200
207
|
async getCachedSDK() {
|
|
201
208
|
try {
|
|
202
209
|
const n = (await this.openIndexedDB()).transaction([l.STORE_NAME], "readonly").objectStore(l.STORE_NAME), i = await new Promise((r, s) => {
|
|
203
|
-
const
|
|
204
|
-
|
|
210
|
+
const d = n.get("latest");
|
|
211
|
+
d.onsuccess = () => r(d.result || null), d.onerror = () => s(d.error);
|
|
205
212
|
});
|
|
206
213
|
if (i)
|
|
207
214
|
return f.debug("Found cached SDK in IndexedDB"), i;
|
|
@@ -227,8 +234,8 @@ class l {
|
|
|
227
234
|
});
|
|
228
235
|
}
|
|
229
236
|
}
|
|
230
|
-
const
|
|
231
|
-
async function
|
|
237
|
+
const N = u({ context: { module: "sdk-verifier" } });
|
|
238
|
+
async function R(o) {
|
|
232
239
|
const t = new TextEncoder().encode(o), n = await crypto.subtle.digest("SHA-256", t);
|
|
233
240
|
return Array.from(new Uint8Array(n)).map((r) => r.toString(16).padStart(2, "0")).join("");
|
|
234
241
|
}
|
|
@@ -238,30 +245,30 @@ function F(o) {
|
|
|
238
245
|
t[n] = e.charCodeAt(n);
|
|
239
246
|
return t;
|
|
240
247
|
}
|
|
241
|
-
function
|
|
248
|
+
function P(o) {
|
|
242
249
|
const e = new Uint8Array(o.length / 2);
|
|
243
250
|
for (let t = 0; t < o.length; t += 2)
|
|
244
251
|
e[t / 2] = Number.parseInt(o.substr(t, 2), 16);
|
|
245
252
|
return e;
|
|
246
253
|
}
|
|
247
|
-
async function
|
|
254
|
+
async function $(o, e, t) {
|
|
248
255
|
try {
|
|
249
|
-
const n = F(o), i =
|
|
256
|
+
const n = F(o), i = P(e), r = F(t), s = await crypto.subtle.importKey("raw", n, { name: "Ed25519" }, !1, ["verify"]);
|
|
250
257
|
return await crypto.subtle.verify("Ed25519", s, r, i);
|
|
251
258
|
} catch (n) {
|
|
252
|
-
return
|
|
259
|
+
return N.error("Ed25519 signature verification failed", n), !1;
|
|
253
260
|
}
|
|
254
261
|
}
|
|
255
|
-
async function
|
|
262
|
+
async function q(o, e, t, n) {
|
|
256
263
|
if (!e || !t)
|
|
257
264
|
throw new Error("SDK verification failed: Missing security headers");
|
|
258
|
-
if (await
|
|
265
|
+
if (await R(o) !== e)
|
|
259
266
|
throw new Error("SDK verification failed: Checksum mismatch - content tampered");
|
|
260
|
-
if (!await
|
|
267
|
+
if (!await $(n, e, t))
|
|
261
268
|
throw new Error("SDK verification failed: Invalid signature - content tampered");
|
|
262
269
|
}
|
|
263
270
|
const m = u({ context: { module: "sdk-fetcher" } });
|
|
264
|
-
class
|
|
271
|
+
class z {
|
|
265
272
|
cleanApiEndpoint(e) {
|
|
266
273
|
return e.trim().replace(/\/+$/, "");
|
|
267
274
|
}
|
|
@@ -284,26 +291,26 @@ class M {
|
|
|
284
291
|
async handleSuccessfulFetch(e, t) {
|
|
285
292
|
const n = e.headers.get("x-sdk-checksum"), i = e.headers.get("x-sdk-version"), r = e.headers.get("x-sdk-signature"), s = await e.text();
|
|
286
293
|
if (m.info(`Fetched Deway SDK version ${i}`), !i || !s || !n) {
|
|
287
|
-
const
|
|
288
|
-
throw m.error("Failed to get required data from sdk fetch", { headers:
|
|
294
|
+
const d = Object.fromEntries(e.headers.entries());
|
|
295
|
+
throw m.error("Failed to get required data from sdk fetch", { headers: d }), new Error("Invalid SDK response: missing version, code, or checksum");
|
|
289
296
|
}
|
|
290
|
-
return await
|
|
297
|
+
return await q(s, n, r, t), { code: s, version: i, checksum: n, signature: r || "" };
|
|
291
298
|
}
|
|
292
299
|
}
|
|
293
|
-
const
|
|
300
|
+
const h = u({ context: { module: "command-queue" } });
|
|
294
301
|
class C {
|
|
295
302
|
static MAX_QUEUE_SIZE = 50;
|
|
296
303
|
commandQueue = [];
|
|
297
304
|
queueCommand(e, ...t) {
|
|
298
|
-
this.commandQueue.length >= C.MAX_QUEUE_SIZE && (
|
|
305
|
+
this.commandQueue.length >= C.MAX_QUEUE_SIZE && (h.warn(`Command queue full (${C.MAX_QUEUE_SIZE} commands). Discarding oldest command.`), this.commandQueue.shift()), this.commandQueue.push({ method: e, args: t }), h.debug(`Queued command: ${e} (queue size: ${this.commandQueue.length})`);
|
|
299
306
|
}
|
|
300
307
|
replayQueuedCommands() {
|
|
301
308
|
if (this.commandQueue.length === 0)
|
|
302
309
|
return;
|
|
303
|
-
|
|
310
|
+
h.info(`Replaying ${this.commandQueue.length} queued commands`);
|
|
304
311
|
const e = [...this.commandQueue];
|
|
305
312
|
if (this.commandQueue = [], !this.isSDKAvailable()) {
|
|
306
|
-
|
|
313
|
+
h.warn("Deway SDK not available for command replay");
|
|
307
314
|
return;
|
|
308
315
|
}
|
|
309
316
|
for (const t of e)
|
|
@@ -318,13 +325,13 @@ class C {
|
|
|
318
325
|
replayCommand(e) {
|
|
319
326
|
try {
|
|
320
327
|
const t = window.Deway, n = t?.[e.method];
|
|
321
|
-
typeof n == "function" ? n.apply(t, e.args) :
|
|
328
|
+
typeof n == "function" ? n.apply(t, e.args) : h.warn(`Method ${e.method} not found on Deway SDK`);
|
|
322
329
|
} catch (t) {
|
|
323
|
-
|
|
330
|
+
h.error(`Failed to replay command ${e.method}`, t);
|
|
324
331
|
}
|
|
325
332
|
}
|
|
326
333
|
}
|
|
327
|
-
const
|
|
334
|
+
const p = u({ context: { module: "remote-config-cache" } });
|
|
328
335
|
class b {
|
|
329
336
|
static CACHE_KEY = "deway-remote-config-cache";
|
|
330
337
|
/**
|
|
@@ -341,9 +348,9 @@ class b {
|
|
|
341
348
|
ttl_seconds: t,
|
|
342
349
|
timestamp: Date.now()
|
|
343
350
|
};
|
|
344
|
-
localStorage.setItem(b.CACHE_KEY, JSON.stringify(n)),
|
|
351
|
+
localStorage.setItem(b.CACHE_KEY, JSON.stringify(n)), p.debug("Remote configuration cached in localStorage");
|
|
345
352
|
} catch (n) {
|
|
346
|
-
|
|
353
|
+
p.warn("Failed to cache remote config", n);
|
|
347
354
|
}
|
|
348
355
|
}
|
|
349
356
|
/**
|
|
@@ -358,9 +365,9 @@ class b {
|
|
|
358
365
|
if (!e)
|
|
359
366
|
return null;
|
|
360
367
|
const t = JSON.parse(e);
|
|
361
|
-
return
|
|
368
|
+
return p.debug("Found cached remote configuration"), t;
|
|
362
369
|
} catch (e) {
|
|
363
|
-
return
|
|
370
|
+
return p.warn("Failed to read cached remote config", e), null;
|
|
364
371
|
}
|
|
365
372
|
}
|
|
366
373
|
/**
|
|
@@ -377,7 +384,7 @@ class b {
|
|
|
377
384
|
}
|
|
378
385
|
}
|
|
379
386
|
const S = u({ context: { module: "remote-config-fetcher" } });
|
|
380
|
-
class
|
|
387
|
+
class M {
|
|
381
388
|
cleanApiEndpoint(e) {
|
|
382
389
|
return e.trim().replace(/\/+$/, "");
|
|
383
390
|
}
|
|
@@ -411,7 +418,7 @@ class V {
|
|
|
411
418
|
}
|
|
412
419
|
}
|
|
413
420
|
const y = u({ context: { module: "script-executor" } });
|
|
414
|
-
class
|
|
421
|
+
class H {
|
|
415
422
|
async executeSDK(e) {
|
|
416
423
|
return new Promise((t) => {
|
|
417
424
|
try {
|
|
@@ -450,7 +457,7 @@ class j {
|
|
|
450
457
|
}
|
|
451
458
|
}
|
|
452
459
|
const D = u({ context: { module: "config-validator" } });
|
|
453
|
-
class
|
|
460
|
+
class V {
|
|
454
461
|
validateConfig(e) {
|
|
455
462
|
return e ? !e.appKey || e.appKey.trim().length === 0 ? (D.error("Config.appKey is required and must be a non-empty string"), !1) : e.apiEndpoint !== void 0 && !this.isValidUrl(e.apiEndpoint) ? (D.error("Config.apiEndpoint must be a valid URL"), !1) : e.publicKey !== void 0 && e.publicKey.trim().length === 0 ? (D.error("Config.publicKey must be a non-empty string if provided"), !1) : !0 : (D.error("Config is required"), !1);
|
|
456
463
|
}
|
|
@@ -466,7 +473,7 @@ const a = u({ context: { module: "sdk-loader" } }), v = {
|
|
|
466
473
|
apiEndpoint: "https://service.deway.app",
|
|
467
474
|
publicKey: "9d3dBUvqyUQ7egd5j5uORdHSqZ7VFWOu+ud/SWt9WUY="
|
|
468
475
|
};
|
|
469
|
-
class
|
|
476
|
+
class j {
|
|
470
477
|
isLoaded = !1;
|
|
471
478
|
isLoading = !1;
|
|
472
479
|
cacheManager;
|
|
@@ -478,7 +485,7 @@ class B {
|
|
|
478
485
|
remoteConfigCache;
|
|
479
486
|
sdkConfigStore;
|
|
480
487
|
constructor() {
|
|
481
|
-
this.cacheManager = new l(), this.scriptExecutor = new
|
|
488
|
+
this.cacheManager = new l(), this.scriptExecutor = new H(), this.commandQueue = new C(), this.sdkFetcher = new z(), this.configValidator = new V(), this.sdkConfigStore = new Q(), this.remoteConfigFetcher = new M(), this.remoteConfigCache = new b();
|
|
482
489
|
}
|
|
483
490
|
init(e) {
|
|
484
491
|
this.performInit(e).catch((t) => {
|
|
@@ -495,10 +502,10 @@ class B {
|
|
|
495
502
|
return;
|
|
496
503
|
}
|
|
497
504
|
this.isLoading = !0;
|
|
498
|
-
const i = n.apiEndpoint || v.apiEndpoint, r = n.publicKey || v.publicKey, [s,
|
|
505
|
+
const i = n.apiEndpoint || v.apiEndpoint, r = n.publicKey || v.publicKey, [s, d] = await Promise.all([this.fetchOrLoadSDK(n.appKey, i, r), this.fetchRemoteConfigWithCache(n, i)]);
|
|
499
506
|
if (!s)
|
|
500
507
|
return;
|
|
501
|
-
const E = t ? e : this.createInitializationPayload(
|
|
508
|
+
const E = t ? e : this.createInitializationPayload(d, n);
|
|
502
509
|
if (!await this.scriptExecutor.executeSDK(s.code)) {
|
|
503
510
|
a.error("SDK execution failed");
|
|
504
511
|
return;
|
|
@@ -624,6 +631,23 @@ class B {
|
|
|
624
631
|
return a.error("Failed to check initialization status", e), !1;
|
|
625
632
|
}
|
|
626
633
|
}
|
|
634
|
+
/**
|
|
635
|
+
* Returns the current side-panel state synchronously. This call must NOT be
|
|
636
|
+
* queued — when the backend SDK isn't loaded yet, return the default
|
|
637
|
+
* immediately so hosts can call this during render.
|
|
638
|
+
*/
|
|
639
|
+
getPanelState() {
|
|
640
|
+
try {
|
|
641
|
+
if (this.isLoaded && this.isSDKAvailable()) {
|
|
642
|
+
const e = window.Deway?.getPanelState;
|
|
643
|
+
if (typeof e == "function")
|
|
644
|
+
return e();
|
|
645
|
+
}
|
|
646
|
+
return { width: 0, side: "right", isOpen: !1 };
|
|
647
|
+
} catch (e) {
|
|
648
|
+
return a.error("Failed to read panel state", e), { width: 0, side: "right", isOpen: !1 };
|
|
649
|
+
}
|
|
650
|
+
}
|
|
627
651
|
registerSupportCallback(e) {
|
|
628
652
|
try {
|
|
629
653
|
this.isLoaded && this.isSDKAvailable() ? window.Deway?.registerSupportCallback(e) : this.commandQueue.queueCommand("registerSupportCallback", e);
|
|
@@ -649,22 +673,23 @@ class B {
|
|
|
649
673
|
return typeof window < "u" && "Deway" in window && !!window.Deway;
|
|
650
674
|
}
|
|
651
675
|
}
|
|
652
|
-
const
|
|
653
|
-
init: (o) =>
|
|
654
|
-
identify: (o) =>
|
|
655
|
-
reportEvent: (o, e) =>
|
|
656
|
-
setUserProfile: (o) =>
|
|
657
|
-
show: (o) =>
|
|
658
|
-
hide: () =>
|
|
659
|
-
openChat: () =>
|
|
660
|
-
resetUserLocally: () =>
|
|
661
|
-
registerSupportCallback: (o) =>
|
|
662
|
-
unregisterSupportCallback: () =>
|
|
663
|
-
isVisible: () =>
|
|
664
|
-
isInitialized: () =>
|
|
665
|
-
destroy: () =>
|
|
676
|
+
const c = new j(), B = {
|
|
677
|
+
init: (o) => c.init(o),
|
|
678
|
+
identify: (o) => c.identify(o),
|
|
679
|
+
reportEvent: (o, e) => c.reportEvent(o, e),
|
|
680
|
+
setUserProfile: (o) => c.setUserProfile(o),
|
|
681
|
+
show: (o) => c.show(o),
|
|
682
|
+
hide: () => c.hide(),
|
|
683
|
+
openChat: () => c.openChat(),
|
|
684
|
+
resetUserLocally: () => c.resetUserLocally(),
|
|
685
|
+
registerSupportCallback: (o) => c.registerSupportCallback(o),
|
|
686
|
+
unregisterSupportCallback: () => c.unregisterSupportCallback(),
|
|
687
|
+
isVisible: () => c.isVisible(),
|
|
688
|
+
isInitialized: () => c.isInitialized(),
|
|
689
|
+
destroy: () => c.destroy(),
|
|
690
|
+
getPanelState: () => c.getPanelState()
|
|
666
691
|
};
|
|
667
|
-
typeof window < "u" && (window.Deway =
|
|
692
|
+
typeof window < "u" && (window.Deway = B);
|
|
668
693
|
export {
|
|
669
|
-
|
|
694
|
+
B as default
|
|
670
695
|
};
|
package/dist/loader.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(h,m){typeof exports=="object"&&typeof module<"u"?module.exports=m():typeof define=="function"&&define.amd?define(m):(h=typeof globalThis<"u"?globalThis:h||self,h.Deway=m())})(this,(function(){"use strict";const h={debug:0,info:1,warn:2,error:3},m=4;function U(o){if(typeof o=="bigint")return`${o}n`;if(typeof o=="symbol")return o.toString();if(typeof o=="function")return"[Function]"}function T(o,e){return o instanceof Error?{name:o.name,message:o.message,stack:o.stack}:e.has(o)?"[Circular]":(e.add(o),o)}function _(o){const e=new WeakSet;return JSON.stringify(o,(t,n)=>{const i=U(n);return i!==void 0?i:typeof n=="object"&&n!==null?T(n,e):n})}function Q(o,e){const t=o!==void 0&&Object.keys(o).length>0,n=e!==void 0;if(!t&&!n)return;const i=typeof e=="object"&&e!==null&&!Array.isArray(e)?e:n?{value:e}:{};return{...o,...i}}function S(o,e,t,n,i){const r=e;if(h[o]<r)return;const s={level:o,time:Date.now(),msg:t},d=Q(n,i);d!==void 0&&(s.ctx=d),_(s)}function u(o){const e=o!==void 0&&"minLevel"in o?o.minLevel:void 0,t=e===void 0?m:h[e],n=o?.context;return{debug(i,r){S("debug",t,i,n,r)},info(i,r){S("info",t,i,n,r)},warn(i,r){S("warn",t,i,n,r)},error(i,r){S("error",t,i,n,r)},child(i){return u({minLevel:e,context:{...n,...i}})},alwaysOnLog(i){}}}const A=u({context:{module:"sdk-config-store"}}),F="deway-sdk-config",N=["Understanding intent","Reading web page","Browsing the docs","Enriching context","Validating understanding","Crafting response"],O=18;class R{saveConfig(e){if(window?.localStorage)try{const t=JSON.stringify(e);window.localStorage.setItem(F,t)}catch(t){A.warn("Failed to save SDK config to localStorage",t)}}loadConfig(){if(window?.localStorage)try{const e=window.localStorage.getItem(F);return e?JSON.parse(e):null}catch(e){return A.warn("Failed to load SDK config from localStorage",e),null}return null}getExcludedVendors(){return this.loadConfig()?.excludedVendors??[]}getAssistantName(){return this.loadConfig()?.assistantName??"Assistant"}getAiDisclaimer(){return this.loadConfig()?.aiDisclaimer}getThinkingMessages(){return this.loadConfig()?.thinkingMessages??N}getSupportHandoff(){return this.loadConfig()?.supportHandoff??null}getSupportHandoffButtonText(){return this.getSupportHandoff()?.button_text??"talk to Support"}getFeatureFlags(){return this.loadConfig()?.featureFlags??{}}getFeatureFlag(e){return this.getFeatureFlags()[e]??!1}getPromptSuggestions(){return this.loadConfig()?.promptSuggestions}getWelcomeTitle(){return this.loadConfig()?.welcomeTitle??"How can I help you today?"}getWelcomeSubtitle(){return this.loadConfig()?.welcomeSubtitle??"I'm ready to help you navigate, learn, and get things done."}getEntrypointWidgetAppearanceMode(){return this.loadConfig()?.entrypointWidgetAppearanceMode??"bookmark"}getChatAppearanceMode(){return this.loadConfig()?.chatAppearanceMode??"floating-window"}getTenantTheme(){return this.loadConfig()?.tenantTheme??null}getTenantThemeForMode(e){const t=this.getTenantTheme();return t?e==="dark"?t.dark:t.light:null}getCustomIcons(){return this.loadConfig()?.customIcons??null}getEntrypointWidgetIconInlineSvg(){return this.getCustomIcons()?.entrypoint_widget_icon_inline_svg??void 0}getEmptyChatStateIconInlineSvg(){return this.getCustomIcons()?.empty_chat_state_icon_inline_svg??void 0}getEntrypointWidgetIconSize(){return this.loadConfig()?.entrypointWidgetIconSize??O}getUIAlignment(){return this.loadConfig()?.uiAlignment??null}getUIAlignmentAnchorSelector(){return this.getUIAlignment()?.anchor_selector??null}getUIAlignmentSide(){return this.getUIAlignment()?.side??"right"}}const f=u({context:{module:"sdk-cache-manager"}});class l{static CACHE_KEY="deway-sdk-cache";static DB_NAME="DewaySdk";static DB_VERSION=1;static STORE_NAME="sdk";async cacheSDK(e,t,n,i){try{const d=(await this.openIndexedDB()).transaction([l.STORE_NAME],"readwrite").objectStore(l.STORE_NAME);await new Promise((k,L)=>{const w=d.put({id:"latest",code:e,version:t,checksum:n,signature:i,timestamp:Date.now()});w.onsuccess=()=>k(w.result),w.onerror=()=>L(w.error)}),f.debug("SDK cached in IndexedDB")}catch{try{const s=JSON.stringify({code:e,version:t,checksum:n,signature:i,timestamp:Date.now()});localStorage.setItem(l.CACHE_KEY,s),f.debug("SDK cached in localStorage")}catch(s){f.warn("Unable to cache SDK",s)}}}async getCachedSDK(){try{const n=(await this.openIndexedDB()).transaction([l.STORE_NAME],"readonly").objectStore(l.STORE_NAME),i=await new Promise((r,s)=>{const d=n.get("latest");d.onsuccess=()=>r(d.result||null),d.onerror=()=>s(d.error)});if(i)return f.debug("Found cached SDK in IndexedDB"),i}catch{f.debug("IndexedDB unavailable, trying localStorage")}try{const e=localStorage.getItem(l.CACHE_KEY);if(e)return f.debug("Found cached SDK in localStorage"),JSON.parse(e)}catch(e){f.warn("Failed to read from localStorage",e)}return null}openIndexedDB(){return new Promise((e,t)=>{const n=indexedDB.open(l.DB_NAME,l.DB_VERSION);n.onerror=()=>t(n.error),n.onsuccess=()=>e(n.result),n.onupgradeneeded=i=>{const r=i.target.result;r.objectStoreNames.contains(l.STORE_NAME)||r.createObjectStore(l.STORE_NAME,{keyPath:"id"})}})}}const z=u({context:{module:"sdk-verifier"}});async function $(o){const t=new TextEncoder().encode(o),n=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(n)).map(r=>r.toString(16).padStart(2,"0")).join("")}function I(o){const e=atob(o),t=new Uint8Array(e.length);for(let n=0;n<e.length;n++)t[n]=e.charCodeAt(n);return t}function q(o){const e=new Uint8Array(o.length/2);for(let t=0;t<o.length;t+=2)e[t/2]=Number.parseInt(o.substr(t,2),16);return e}async function P(o,e,t){try{const n=I(o),i=q(e),r=I(t),s=await crypto.subtle.importKey("raw",n,{name:"Ed25519"},!1,["verify"]);return await crypto.subtle.verify("Ed25519",s,r,i)}catch(n){return z.error("Ed25519 signature verification failed",n),!1}}async function M(o,e,t,n){if(!e||!t)throw new Error("SDK verification failed: Missing security headers");if(await $(o)!==e)throw new Error("SDK verification failed: Checksum mismatch - content tampered");if(!await P(n,e,t))throw new Error("SDK verification failed: Invalid signature - content tampered")}const y=u({context:{module:"sdk-fetcher"}});class j{cleanApiEndpoint(e){return e.trim().replace(/\/+$/,"")}async fetchSDK(e,t,n){try{y.info("Fetching Deway SDK from backend...");const i=this.cleanApiEndpoint(t),r=await fetch(`${i}/sdk-serve/sdk/v0`,{method:"GET",headers:{Accept:"application/javascript","deway-app-key":e,Origin:window?.location?.origin||""}});return r.ok?this.handleSuccessfulFetch(r,n):(y.warn(`Failed to fetch SDK: HTTP ${r.status}: ${r.statusText}`),null)}catch(i){return y.warn("Failed to fetch SDK from server",i),null}}async handleSuccessfulFetch(e,t){const n=e.headers.get("x-sdk-checksum"),i=e.headers.get("x-sdk-version"),r=e.headers.get("x-sdk-signature"),s=await e.text();if(y.info(`Fetched Deway SDK version ${i}`),!i||!s||!n){const d=Object.fromEntries(e.headers.entries());throw y.error("Failed to get required data from sdk fetch",{headers:d}),new Error("Invalid SDK response: missing version, code, or checksum")}return await M(s,n,r,t),{code:s,version:i,checksum:n,signature:r||""}}}const g=u({context:{module:"command-queue"}});class D{static MAX_QUEUE_SIZE=50;commandQueue=[];queueCommand(e,...t){this.commandQueue.length>=D.MAX_QUEUE_SIZE&&(g.warn(`Command queue full (${D.MAX_QUEUE_SIZE} commands). Discarding oldest command.`),this.commandQueue.shift()),this.commandQueue.push({method:e,args:t}),g.debug(`Queued command: ${e} (queue size: ${this.commandQueue.length})`)}replayQueuedCommands(){if(this.commandQueue.length===0)return;g.info(`Replaying ${this.commandQueue.length} queued commands`);const e=[...this.commandQueue];if(this.commandQueue=[],!this.isSDKAvailable()){g.warn("Deway SDK not available for command replay");return}for(const t of e)this.replayCommand(t)}clearQueue(){this.commandQueue=[]}isSDKAvailable(){return typeof window<"u"&&"Deway"in window&&!!window.Deway}replayCommand(e){try{const t=window.Deway,n=t?.[e.method];typeof n=="function"?n.apply(t,e.args):g.warn(`Method ${e.method} not found on Deway SDK`)}catch(t){g.error(`Failed to replay command ${e.method}`,t)}}}const C=u({context:{module:"remote-config-cache"}});class b{static CACHE_KEY="deway-remote-config-cache";async cacheRemoteConfig(e,t){try{const n={config:e,ttl_seconds:t,timestamp:Date.now()};localStorage.setItem(b.CACHE_KEY,JSON.stringify(n)),C.debug("Remote configuration cached in localStorage")}catch(n){C.warn("Failed to cache remote config",n)}}async getCachedRemoteConfig(){try{const e=localStorage.getItem(b.CACHE_KEY);if(!e)return null;const t=JSON.parse(e);return C.debug("Found cached remote configuration"),t}catch(e){return C.warn("Failed to read cached remote config",e),null}}isCacheValid(e,t){const n=Date.now(),i=e+t*1e3;return n<i}}const E=u({context:{module:"remote-config-fetcher"}});class V{cleanApiEndpoint(e){return e.trim().replace(/\/+$/,"")}async fetchRemoteConfig(e,t){try{E.info("Fetching remote configuration from backend...");const n=this.cleanApiEndpoint(t),i=await fetch(`${n}/sdk-remote-config-serve/`,{method:"GET",headers:{Accept:"application/json","deway-app-key":e,Origin:window?.location?.origin||""}});if(i.ok){const r=await i.json();return E.info("Remote configuration fetched successfully"),r}return E.warn(`Failed to fetch remote config: HTTP ${i.status}: ${i.statusText}`),null}catch(n){return E.warn("Failed to fetch remote config from server",n),null}}}const p=u({context:{module:"script-executor"}});class H{async executeSDK(e){return new Promise(t=>{try{if(!this.isDocumentReady()){p.error("Document is not available for script execution"),t(!1);return}const n=document.createElement("script");n.textContent=e,n.type="text/javascript";let i=!1;const r=s=>{i||(i=!0,this.cleanupScript(n),t(s))};n.onerror=()=>{p.error("Script execution failed"),r(!1)},n.onload=()=>r(!0),document.head.appendChild(n),setTimeout(()=>{!i&&this.verifySDKLoaded()?r(!0):i||(p.error("SDK execution timeout - Deway object not found"),r(!1))},100)}catch(n){p.error("Failed to execute SDK script",n),t(!1)}})}isDocumentReady(){return typeof document<"u"&&document.head!=null}verifySDKLoaded(){return typeof window<"u"&&"Deway"in window&&!!window.Deway}cleanupScript(e){try{e.parentNode&&e.parentNode.removeChild(e)}catch(t){p.debug("Failed to cleanup script element",t)}}}const v=u({context:{module:"config-validator"}});class B{validateConfig(e){return e?!e.appKey||e.appKey.trim().length===0?(v.error("Config.appKey is required and must be a non-empty string"),!1):e.apiEndpoint!==void 0&&!this.isValidUrl(e.apiEndpoint)?(v.error("Config.apiEndpoint must be a valid URL"),!1):e.publicKey!==void 0&&e.publicKey.trim().length===0?(v.error("Config.publicKey must be a non-empty string if provided"),!1):!0:(v.error("Config is required"),!1)}isValidUrl(e){try{return new URL(e),!0}catch{return!1}}}const a=u({context:{module:"sdk-loader"}}),K={apiEndpoint:"https://service.deway.app",publicKey:"9d3dBUvqyUQ7egd5j5uORdHSqZ7VFWOu+ud/SWt9WUY="};class W{isLoaded=!1;isLoading=!1;cacheManager;scriptExecutor;commandQueue;sdkFetcher;configValidator;remoteConfigFetcher;remoteConfigCache;sdkConfigStore;constructor(){this.cacheManager=new l,this.scriptExecutor=new H,this.commandQueue=new D,this.sdkFetcher=new j,this.configValidator=new B,this.sdkConfigStore=new R,this.remoteConfigFetcher=new V,this.remoteConfigCache=new b}init(e){this.performInit(e).catch(t=>{a.error("Failed to initialize Deway SDK",t)})}async performInit(e){try{if(!this.canInitialize())return;const t=this.isInitializationPayload(e),n=t?e.localConfig:e;if(!this.configValidator.validateConfig(n)){a.error("Invalid config provided to Deway SDK");return}this.isLoading=!0;const i=n.apiEndpoint||K.apiEndpoint,r=n.publicKey||K.publicKey,[s,d]=await Promise.all([this.fetchOrLoadSDK(n.appKey,i,r),this.fetchRemoteConfigWithCache(n,i)]);if(!s)return;const k=t?e:this.createInitializationPayload(d,n);if(!await this.scriptExecutor.executeSDK(s.code)){a.error("SDK execution failed");return}if(!this.initializeSDK(k))return;this.commandQueue.replayQueuedCommands(),this.isLoaded=!0}finally{this.isLoading=!1}}isInitializationPayload(e){return"localConfig"in e&&"remoteConfig"in e&&"defaults"in e}canInitialize(){return this.isLoaded?(a.warn("Deway SDK already initialized"),!1):this.isLoading?(a.warn("Deway SDK initialization already in progress"),!1):!0}async fetchRemoteConfigWithCache(e,t){this.sdkConfigStore.saveConfig(e);const n=await this.remoteConfigFetcher.fetchRemoteConfig(e.appKey,t);if(n)return await this.remoteConfigCache.cacheRemoteConfig(n.config,n.ttl_seconds),n;a.info("Using cached remote config as fallback");const i=await this.remoteConfigCache.getCachedRemoteConfig();return i?{config:i.config,ttl_seconds:i.ttl_seconds}:(a.warn("No remote config available (fetch failed and no cache)"),null)}createInitializationPayload(e,t){return{localConfig:t,remoteConfig:e?.config??null,defaults:K}}async fetchOrLoadSDK(e,t,n){const i=await this.sdkFetcher.fetchSDK(e,t,n);return i?(await this.cacheManager.cacheSDK(i.code,i.version,i.checksum,i.signature),i):(a.warn("Failed to fetch SDK from server, attempting cache fallback"),this.loadFromCache())}async loadFromCache(){const e=await this.cacheManager.getCachedSDK();return e?(a.info(`Loading cached Deway SDK version ${e.version}`),{code:e.code,version:e.version}):(a.error("SDK unavailable: Network error and no cached version found"),null)}initializeSDK(e){if(!this.isSDKAvailable())return a.error("SDK execution failed: Deway object not found after loading"),!1;try{return window.Deway?.init(e),a.info("Deway SDK initialized successfully with payload"),!0}catch(t){return a.error("Failed to initialize SDK with payload",t),!1}}identify(e){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.identify(e):this.commandQueue.queueCommand("identify",e)}catch(t){a.error("Failed to identify user",t)}}reportEvent(e,t){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.reportEvent(e,t):this.commandQueue.queueCommand("reportEvent",e,t)}catch(n){a.error("Failed to report event",n)}}setUserProfile(e){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.setUserProfile(e):this.commandQueue.queueCommand("setUserProfile",e)}catch(t){a.error("Failed to set user profile",t)}}show(e){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.show(e):this.commandQueue.queueCommand("show",e)}catch(t){a.error("Failed to show bookmark",t)}}hide(){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.hide():this.commandQueue.queueCommand("hide")}catch(e){a.error("Failed to hide bookmark",e)}}openChat(){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.openChat():this.commandQueue.queueCommand("openChat")}catch(e){a.error("Failed to open chat",e)}}resetUserLocally(){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.resetUserLocally():this.commandQueue.queueCommand("resetUserLocally")}catch(e){a.error("Failed to reset user locally",e)}}isVisible(){try{return this.isLoaded&&this.isSDKAvailable()?window.Deway?.isVisible()??!1:!1}catch(e){return a.error("Failed to check bookmark visibility",e),!1}}isInitialized(){try{return this.isLoaded&&this.isSDKAvailable()?window.Deway?.isInitialized()??!1:!1}catch(e){return a.error("Failed to check initialization status",e),!1}}registerSupportCallback(e){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.registerSupportCallback(e):this.commandQueue.queueCommand("registerSupportCallback",e)}catch(t){a.error("Failed to register support callback",t)}}unregisterSupportCallback(){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.unregisterSupportCallback():this.commandQueue.queueCommand("unregisterSupportCallback")}catch(e){a.error("Failed to unregister support callback",e)}}destroy(){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.destroy():this.commandQueue.queueCommand("destroy"),this.commandQueue.clearQueue()}catch(e){a.error("Failed to destroy SDK",e)}}isSDKAvailable(){return typeof window<"u"&&"Deway"in window&&!!window.Deway}}const c=new W,x={init:o=>c.init(o),identify:o=>c.identify(o),reportEvent:(o,e)=>c.reportEvent(o,e),setUserProfile:o=>c.setUserProfile(o),show:o=>c.show(o),hide:()=>c.hide(),openChat:()=>c.openChat(),resetUserLocally:()=>c.resetUserLocally(),registerSupportCallback:o=>c.registerSupportCallback(o),unregisterSupportCallback:()=>c.unregisterSupportCallback(),isVisible:()=>c.isVisible(),isInitialized:()=>c.isInitialized(),destroy:()=>c.destroy()};return typeof window<"u"&&(window.Deway=x),x}));
|
|
1
|
+
(function(h,m){typeof exports=="object"&&typeof module<"u"?module.exports=m():typeof define=="function"&&define.amd?define(m):(h=typeof globalThis<"u"?globalThis:h||self,h.Deway=m())})(this,(function(){"use strict";const h={debug:0,info:1,warn:2,error:3},m=4;function U(o){if(typeof o=="bigint")return`${o}n`;if(typeof o=="symbol")return o.toString();if(typeof o=="function")return"[Function]"}function T(o,e){return o instanceof Error?{name:o.name,message:o.message,stack:o.stack}:e.has(o)?"[Circular]":(e.add(o),o)}function O(o){const e=new WeakSet;return JSON.stringify(o,(t,n)=>{const i=U(n);return i!==void 0?i:typeof n=="object"&&n!==null?T(n,e):n})}function _(o,e){const t=o!==void 0&&Object.keys(o).length>0,n=e!==void 0;if(!t&&!n)return;const i=typeof e=="object"&&e!==null&&!Array.isArray(e)?e:n?{value:e}:{};return{...o,...i}}function S(o,e,t,n,i){const r=e;if(h[o]<r)return;const s={level:o,time:Date.now(),msg:t},d=_(n,i);d!==void 0&&(s.ctx=d),O(s)}function u(o){const e=o!==void 0&&"minLevel"in o?o.minLevel:void 0,t=e===void 0?m:h[e],n=o?.context;return{debug(i,r){S("debug",t,i,n,r)},info(i,r){S("info",t,i,n,r)},warn(i,r){S("warn",t,i,n,r)},error(i,r){S("error",t,i,n,r)},child(i){return u({minLevel:e,context:{...n,...i}})},alwaysOnLog(i){}}}const A=u({context:{module:"sdk-config-store"}}),F="deway-sdk-config",Q=["Understanding intent","Reading web page","Browsing the docs","Enriching context","Validating understanding","Crafting response"];class N{saveConfig(e){if(window?.localStorage)try{const t=JSON.stringify(e);window.localStorage.setItem(F,t)}catch(t){A.warn("Failed to save SDK config to localStorage",t)}}loadConfig(){if(window?.localStorage)try{const e=window.localStorage.getItem(F);return e?JSON.parse(e):null}catch(e){return A.warn("Failed to load SDK config from localStorage",e),null}return null}getExcludedVendors(){return this.loadConfig()?.excludedVendors??[]}getAssistantName(){return this.loadConfig()?.assistantName??"Assistant"}getAiDisclaimer(){return this.loadConfig()?.aiDisclaimer}getThinkingMessages(){return this.loadConfig()?.thinkingMessages??Q}getSupportHandoff(){return this.loadConfig()?.supportHandoff??null}getSupportHandoffButtonText(){return this.getSupportHandoff()?.button_text??"talk to Support"}getFeatureFlags(){return this.loadConfig()?.featureFlags??{}}getFeatureFlag(e){return this.getFeatureFlags()[e]??!1}getPromptSuggestions(){return this.loadConfig()?.promptSuggestions}getWelcomeTitle(){return this.loadConfig()?.welcomeTitle??"How can I help you today?"}getWelcomeSubtitle(){return this.loadConfig()?.welcomeSubtitle??"I'm ready to help you navigate, learn, and get things done."}getEntrypointWidgetAppearanceMode(){return this.loadConfig()?.entrypointWidgetAppearanceMode??"bookmark"}getChatAppearanceMode(){return this.loadConfig()?.chatAppearanceMode??"floating-window"}getTenantTheme(){return this.loadConfig()?.tenantTheme??null}getTenantThemeForMode(e){const t=this.getTenantTheme();return t?e==="dark"?t.dark:t.light:null}getCustomIcons(){return this.loadConfig()?.customIcons??null}getEntrypointWidgetIconInlineSvg(){return this.getCustomIcons()?.entrypoint_widget_icon_inline_svg??void 0}getEmptyChatStateIconInlineSvg(){return this.getCustomIcons()?.empty_chat_state_icon_inline_svg??void 0}getUIAlignment(){return this.loadConfig()?.uiAlignment??null}getUIAlignmentAnchorSelector(){return this.getUIAlignment()?.anchor_selector??null}getUIAlignmentSide(){return this.getUIAlignment()?.side??"right"}getSidePanelHostSqueezeEnabled(){return this.loadConfig()?.sidePanel?.hostSqueeze??!0}}const f=u({context:{module:"sdk-cache-manager"}});class l{static CACHE_KEY="deway-sdk-cache";static DB_NAME="DewaySdk";static DB_VERSION=1;static STORE_NAME="sdk";async cacheSDK(e,t,n,i){try{const d=(await this.openIndexedDB()).transaction([l.STORE_NAME],"readwrite").objectStore(l.STORE_NAME);await new Promise((k,L)=>{const w=d.put({id:"latest",code:e,version:t,checksum:n,signature:i,timestamp:Date.now()});w.onsuccess=()=>k(w.result),w.onerror=()=>L(w.error)}),f.debug("SDK cached in IndexedDB")}catch{try{const s=JSON.stringify({code:e,version:t,checksum:n,signature:i,timestamp:Date.now()});localStorage.setItem(l.CACHE_KEY,s),f.debug("SDK cached in localStorage")}catch(s){f.warn("Unable to cache SDK",s)}}}async getCachedSDK(){try{const n=(await this.openIndexedDB()).transaction([l.STORE_NAME],"readonly").objectStore(l.STORE_NAME),i=await new Promise((r,s)=>{const d=n.get("latest");d.onsuccess=()=>r(d.result||null),d.onerror=()=>s(d.error)});if(i)return f.debug("Found cached SDK in IndexedDB"),i}catch{f.debug("IndexedDB unavailable, trying localStorage")}try{const e=localStorage.getItem(l.CACHE_KEY);if(e)return f.debug("Found cached SDK in localStorage"),JSON.parse(e)}catch(e){f.warn("Failed to read from localStorage",e)}return null}openIndexedDB(){return new Promise((e,t)=>{const n=indexedDB.open(l.DB_NAME,l.DB_VERSION);n.onerror=()=>t(n.error),n.onsuccess=()=>e(n.result),n.onupgradeneeded=i=>{const r=i.target.result;r.objectStoreNames.contains(l.STORE_NAME)||r.createObjectStore(l.STORE_NAME,{keyPath:"id"})}})}}const R=u({context:{module:"sdk-verifier"}});async function P(o){const t=new TextEncoder().encode(o),n=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(n)).map(r=>r.toString(16).padStart(2,"0")).join("")}function I(o){const e=atob(o),t=new Uint8Array(e.length);for(let n=0;n<e.length;n++)t[n]=e.charCodeAt(n);return t}function $(o){const e=new Uint8Array(o.length/2);for(let t=0;t<o.length;t+=2)e[t/2]=Number.parseInt(o.substr(t,2),16);return e}async function q(o,e,t){try{const n=I(o),i=$(e),r=I(t),s=await crypto.subtle.importKey("raw",n,{name:"Ed25519"},!1,["verify"]);return await crypto.subtle.verify("Ed25519",s,r,i)}catch(n){return R.error("Ed25519 signature verification failed",n),!1}}async function z(o,e,t,n){if(!e||!t)throw new Error("SDK verification failed: Missing security headers");if(await P(o)!==e)throw new Error("SDK verification failed: Checksum mismatch - content tampered");if(!await q(n,e,t))throw new Error("SDK verification failed: Invalid signature - content tampered")}const y=u({context:{module:"sdk-fetcher"}});class M{cleanApiEndpoint(e){return e.trim().replace(/\/+$/,"")}async fetchSDK(e,t,n){try{y.info("Fetching Deway SDK from backend...");const i=this.cleanApiEndpoint(t),r=await fetch(`${i}/sdk-serve/sdk/v0`,{method:"GET",headers:{Accept:"application/javascript","deway-app-key":e,Origin:window?.location?.origin||""}});return r.ok?this.handleSuccessfulFetch(r,n):(y.warn(`Failed to fetch SDK: HTTP ${r.status}: ${r.statusText}`),null)}catch(i){return y.warn("Failed to fetch SDK from server",i),null}}async handleSuccessfulFetch(e,t){const n=e.headers.get("x-sdk-checksum"),i=e.headers.get("x-sdk-version"),r=e.headers.get("x-sdk-signature"),s=await e.text();if(y.info(`Fetched Deway SDK version ${i}`),!i||!s||!n){const d=Object.fromEntries(e.headers.entries());throw y.error("Failed to get required data from sdk fetch",{headers:d}),new Error("Invalid SDK response: missing version, code, or checksum")}return await z(s,n,r,t),{code:s,version:i,checksum:n,signature:r||""}}}const g=u({context:{module:"command-queue"}});class D{static MAX_QUEUE_SIZE=50;commandQueue=[];queueCommand(e,...t){this.commandQueue.length>=D.MAX_QUEUE_SIZE&&(g.warn(`Command queue full (${D.MAX_QUEUE_SIZE} commands). Discarding oldest command.`),this.commandQueue.shift()),this.commandQueue.push({method:e,args:t}),g.debug(`Queued command: ${e} (queue size: ${this.commandQueue.length})`)}replayQueuedCommands(){if(this.commandQueue.length===0)return;g.info(`Replaying ${this.commandQueue.length} queued commands`);const e=[...this.commandQueue];if(this.commandQueue=[],!this.isSDKAvailable()){g.warn("Deway SDK not available for command replay");return}for(const t of e)this.replayCommand(t)}clearQueue(){this.commandQueue=[]}isSDKAvailable(){return typeof window<"u"&&"Deway"in window&&!!window.Deway}replayCommand(e){try{const t=window.Deway,n=t?.[e.method];typeof n=="function"?n.apply(t,e.args):g.warn(`Method ${e.method} not found on Deway SDK`)}catch(t){g.error(`Failed to replay command ${e.method}`,t)}}}const C=u({context:{module:"remote-config-cache"}});class b{static CACHE_KEY="deway-remote-config-cache";async cacheRemoteConfig(e,t){try{const n={config:e,ttl_seconds:t,timestamp:Date.now()};localStorage.setItem(b.CACHE_KEY,JSON.stringify(n)),C.debug("Remote configuration cached in localStorage")}catch(n){C.warn("Failed to cache remote config",n)}}async getCachedRemoteConfig(){try{const e=localStorage.getItem(b.CACHE_KEY);if(!e)return null;const t=JSON.parse(e);return C.debug("Found cached remote configuration"),t}catch(e){return C.warn("Failed to read cached remote config",e),null}}isCacheValid(e,t){const n=Date.now(),i=e+t*1e3;return n<i}}const E=u({context:{module:"remote-config-fetcher"}});class j{cleanApiEndpoint(e){return e.trim().replace(/\/+$/,"")}async fetchRemoteConfig(e,t){try{E.info("Fetching remote configuration from backend...");const n=this.cleanApiEndpoint(t),i=await fetch(`${n}/sdk-remote-config-serve/`,{method:"GET",headers:{Accept:"application/json","deway-app-key":e,Origin:window?.location?.origin||""}});if(i.ok){const r=await i.json();return E.info("Remote configuration fetched successfully"),r}return E.warn(`Failed to fetch remote config: HTTP ${i.status}: ${i.statusText}`),null}catch(n){return E.warn("Failed to fetch remote config from server",n),null}}}const p=u({context:{module:"script-executor"}});class H{async executeSDK(e){return new Promise(t=>{try{if(!this.isDocumentReady()){p.error("Document is not available for script execution"),t(!1);return}const n=document.createElement("script");n.textContent=e,n.type="text/javascript";let i=!1;const r=s=>{i||(i=!0,this.cleanupScript(n),t(s))};n.onerror=()=>{p.error("Script execution failed"),r(!1)},n.onload=()=>r(!0),document.head.appendChild(n),setTimeout(()=>{!i&&this.verifySDKLoaded()?r(!0):i||(p.error("SDK execution timeout - Deway object not found"),r(!1))},100)}catch(n){p.error("Failed to execute SDK script",n),t(!1)}})}isDocumentReady(){return typeof document<"u"&&document.head!=null}verifySDKLoaded(){return typeof window<"u"&&"Deway"in window&&!!window.Deway}cleanupScript(e){try{e.parentNode&&e.parentNode.removeChild(e)}catch(t){p.debug("Failed to cleanup script element",t)}}}const v=u({context:{module:"config-validator"}});class V{validateConfig(e){return e?!e.appKey||e.appKey.trim().length===0?(v.error("Config.appKey is required and must be a non-empty string"),!1):e.apiEndpoint!==void 0&&!this.isValidUrl(e.apiEndpoint)?(v.error("Config.apiEndpoint must be a valid URL"),!1):e.publicKey!==void 0&&e.publicKey.trim().length===0?(v.error("Config.publicKey must be a non-empty string if provided"),!1):!0:(v.error("Config is required"),!1)}isValidUrl(e){try{return new URL(e),!0}catch{return!1}}}const a=u({context:{module:"sdk-loader"}}),K={apiEndpoint:"https://service.deway.app",publicKey:"9d3dBUvqyUQ7egd5j5uORdHSqZ7VFWOu+ud/SWt9WUY="};class B{isLoaded=!1;isLoading=!1;cacheManager;scriptExecutor;commandQueue;sdkFetcher;configValidator;remoteConfigFetcher;remoteConfigCache;sdkConfigStore;constructor(){this.cacheManager=new l,this.scriptExecutor=new H,this.commandQueue=new D,this.sdkFetcher=new M,this.configValidator=new V,this.sdkConfigStore=new N,this.remoteConfigFetcher=new j,this.remoteConfigCache=new b}init(e){this.performInit(e).catch(t=>{a.error("Failed to initialize Deway SDK",t)})}async performInit(e){try{if(!this.canInitialize())return;const t=this.isInitializationPayload(e),n=t?e.localConfig:e;if(!this.configValidator.validateConfig(n)){a.error("Invalid config provided to Deway SDK");return}this.isLoading=!0;const i=n.apiEndpoint||K.apiEndpoint,r=n.publicKey||K.publicKey,[s,d]=await Promise.all([this.fetchOrLoadSDK(n.appKey,i,r),this.fetchRemoteConfigWithCache(n,i)]);if(!s)return;const k=t?e:this.createInitializationPayload(d,n);if(!await this.scriptExecutor.executeSDK(s.code)){a.error("SDK execution failed");return}if(!this.initializeSDK(k))return;this.commandQueue.replayQueuedCommands(),this.isLoaded=!0}finally{this.isLoading=!1}}isInitializationPayload(e){return"localConfig"in e&&"remoteConfig"in e&&"defaults"in e}canInitialize(){return this.isLoaded?(a.warn("Deway SDK already initialized"),!1):this.isLoading?(a.warn("Deway SDK initialization already in progress"),!1):!0}async fetchRemoteConfigWithCache(e,t){this.sdkConfigStore.saveConfig(e);const n=await this.remoteConfigFetcher.fetchRemoteConfig(e.appKey,t);if(n)return await this.remoteConfigCache.cacheRemoteConfig(n.config,n.ttl_seconds),n;a.info("Using cached remote config as fallback");const i=await this.remoteConfigCache.getCachedRemoteConfig();return i?{config:i.config,ttl_seconds:i.ttl_seconds}:(a.warn("No remote config available (fetch failed and no cache)"),null)}createInitializationPayload(e,t){return{localConfig:t,remoteConfig:e?.config??null,defaults:K}}async fetchOrLoadSDK(e,t,n){const i=await this.sdkFetcher.fetchSDK(e,t,n);return i?(await this.cacheManager.cacheSDK(i.code,i.version,i.checksum,i.signature),i):(a.warn("Failed to fetch SDK from server, attempting cache fallback"),this.loadFromCache())}async loadFromCache(){const e=await this.cacheManager.getCachedSDK();return e?(a.info(`Loading cached Deway SDK version ${e.version}`),{code:e.code,version:e.version}):(a.error("SDK unavailable: Network error and no cached version found"),null)}initializeSDK(e){if(!this.isSDKAvailable())return a.error("SDK execution failed: Deway object not found after loading"),!1;try{return window.Deway?.init(e),a.info("Deway SDK initialized successfully with payload"),!0}catch(t){return a.error("Failed to initialize SDK with payload",t),!1}}identify(e){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.identify(e):this.commandQueue.queueCommand("identify",e)}catch(t){a.error("Failed to identify user",t)}}reportEvent(e,t){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.reportEvent(e,t):this.commandQueue.queueCommand("reportEvent",e,t)}catch(n){a.error("Failed to report event",n)}}setUserProfile(e){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.setUserProfile(e):this.commandQueue.queueCommand("setUserProfile",e)}catch(t){a.error("Failed to set user profile",t)}}show(e){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.show(e):this.commandQueue.queueCommand("show",e)}catch(t){a.error("Failed to show bookmark",t)}}hide(){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.hide():this.commandQueue.queueCommand("hide")}catch(e){a.error("Failed to hide bookmark",e)}}openChat(){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.openChat():this.commandQueue.queueCommand("openChat")}catch(e){a.error("Failed to open chat",e)}}resetUserLocally(){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.resetUserLocally():this.commandQueue.queueCommand("resetUserLocally")}catch(e){a.error("Failed to reset user locally",e)}}isVisible(){try{return this.isLoaded&&this.isSDKAvailable()?window.Deway?.isVisible()??!1:!1}catch(e){return a.error("Failed to check bookmark visibility",e),!1}}isInitialized(){try{return this.isLoaded&&this.isSDKAvailable()?window.Deway?.isInitialized()??!1:!1}catch(e){return a.error("Failed to check initialization status",e),!1}}getPanelState(){try{if(this.isLoaded&&this.isSDKAvailable()){const e=window.Deway?.getPanelState;if(typeof e=="function")return e()}return{width:0,side:"right",isOpen:!1}}catch(e){return a.error("Failed to read panel state",e),{width:0,side:"right",isOpen:!1}}}registerSupportCallback(e){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.registerSupportCallback(e):this.commandQueue.queueCommand("registerSupportCallback",e)}catch(t){a.error("Failed to register support callback",t)}}unregisterSupportCallback(){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.unregisterSupportCallback():this.commandQueue.queueCommand("unregisterSupportCallback")}catch(e){a.error("Failed to unregister support callback",e)}}destroy(){try{this.isLoaded&&this.isSDKAvailable()?window.Deway?.destroy():this.commandQueue.queueCommand("destroy"),this.commandQueue.clearQueue()}catch(e){a.error("Failed to destroy SDK",e)}}isSDKAvailable(){return typeof window<"u"&&"Deway"in window&&!!window.Deway}}const c=new B,x={init:o=>c.init(o),identify:o=>c.identify(o),reportEvent:(o,e)=>c.reportEvent(o,e),setUserProfile:o=>c.setUserProfile(o),show:o=>c.show(o),hide:()=>c.hide(),openChat:()=>c.openChat(),resetUserLocally:()=>c.resetUserLocally(),registerSupportCallback:o=>c.registerSupportCallback(o),unregisterSupportCallback:()=>c.unregisterSupportCallback(),isVisible:()=>c.isVisible(),isInitialized:()=>c.isInitialized(),destroy:()=>c.destroy(),getPanelState:()=>c.getPanelState()};return typeof window<"u"&&(window.Deway=x),x}));
|