@codatum/embed 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 +482 -0
- package/dist/index.cjs +648 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +218 -0
- package/dist/index.d.ts +218 -0
- package/dist/index.global.min.js +640 -0
- package/dist/index.global.min.js.map +1 -0
- package/dist/index.js +618 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __spreadValues = (a, b) => {
|
|
7
|
+
for (var prop in b || (b = {}))
|
|
8
|
+
if (__hasOwnProp.call(b, prop))
|
|
9
|
+
__defNormalProp(a, prop, b[prop]);
|
|
10
|
+
if (__getOwnPropSymbols)
|
|
11
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
+
if (__propIsEnum.call(b, prop))
|
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
|
14
|
+
}
|
|
15
|
+
return a;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/types.ts
|
|
19
|
+
var SDK_VERSION = "0.1.0";
|
|
20
|
+
var SqlDisplayValues = ["SHOW", "RESULT_ONLY", "HIDE"];
|
|
21
|
+
var ThemeValues = ["LIGHT", "DARK", "SYSTEM"];
|
|
22
|
+
var TokenProviderTriggers = {
|
|
23
|
+
INIT: "INIT",
|
|
24
|
+
RELOAD: "RELOAD",
|
|
25
|
+
REFRESH: "REFRESH"
|
|
26
|
+
};
|
|
27
|
+
var EmbedErrorCodes = {
|
|
28
|
+
INVALID_OPTIONS: "INVALID_OPTIONS",
|
|
29
|
+
CONTAINER_NOT_FOUND: "CONTAINER_NOT_FOUND",
|
|
30
|
+
INIT_TIMEOUT: "INIT_TIMEOUT",
|
|
31
|
+
TOKEN_PROVIDER_FAILED: "TOKEN_PROVIDER_FAILED",
|
|
32
|
+
MISSING_REQUIRED_PARAM: "MISSING_REQUIRED_PARAM",
|
|
33
|
+
INVALID_PARAM_VALUE: "INVALID_PARAM_VALUE",
|
|
34
|
+
UNEXPECTED_ERROR: "UNEXPECTED_ERROR"
|
|
35
|
+
};
|
|
36
|
+
var EmbedError = class extends Error {
|
|
37
|
+
constructor(code, message, options) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "EmbedError";
|
|
40
|
+
this.code = code;
|
|
41
|
+
if ((options == null ? void 0 : options.cause) !== void 0) {
|
|
42
|
+
this.cause = options.cause;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var RESET_TO_DEFAULT = "_RESET_TO_DEFAULT_";
|
|
47
|
+
var EmbedStatuses = {
|
|
48
|
+
CREATED: "CREATED",
|
|
49
|
+
INITIALIZING: "INITIALIZING",
|
|
50
|
+
READY: "READY",
|
|
51
|
+
DESTROYED: "DESTROYED"
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// src/utils.ts
|
|
55
|
+
var deepClone = (source) => {
|
|
56
|
+
if (source === null || source === void 0 || typeof source !== "object") return source;
|
|
57
|
+
if (source instanceof Date) return new Date(source.getTime());
|
|
58
|
+
if (typeof Node !== "undefined" && source instanceof Node) return source;
|
|
59
|
+
if (Array.isArray(source)) return source.map(deepClone);
|
|
60
|
+
const result = {};
|
|
61
|
+
for (const key of Object.keys(source)) {
|
|
62
|
+
result[key] = deepClone(source[key]);
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
};
|
|
66
|
+
var EMBED_URL_REGEX = /^https:\/\/app\.codatum\.com\/protected\/workspace\/[a-fA-F0-9]{24}\/notebook\/[a-fA-F0-9]{24}(\?.*)?$/;
|
|
67
|
+
var isValidEmbedUrl = (url) => {
|
|
68
|
+
return EMBED_URL_REGEX.test(url);
|
|
69
|
+
};
|
|
70
|
+
var buildIframeSrc = (embedUrl, iframeOptions) => {
|
|
71
|
+
const url = new URL(embedUrl);
|
|
72
|
+
if (iframeOptions == null ? void 0 : iframeOptions.theme) {
|
|
73
|
+
url.searchParams.set("theme", iframeOptions.theme);
|
|
74
|
+
}
|
|
75
|
+
if (iframeOptions == null ? void 0 : iframeOptions.locale) {
|
|
76
|
+
url.searchParams.set("locale", iframeOptions.locale);
|
|
77
|
+
}
|
|
78
|
+
return url.toString();
|
|
79
|
+
};
|
|
80
|
+
var BASE_IFRAME_CLASS = "codatum-embed-iframe";
|
|
81
|
+
var getIframeClassName = (iframeOptions) => {
|
|
82
|
+
return BASE_IFRAME_CLASS + ((iframeOptions == null ? void 0 : iframeOptions.className) ? ` ${iframeOptions.className}` : "");
|
|
83
|
+
};
|
|
84
|
+
var getTokenTtlMs = (token) => {
|
|
85
|
+
try {
|
|
86
|
+
const base64 = token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
87
|
+
const payload = JSON.parse(atob(base64));
|
|
88
|
+
return payload.exp * 1e3 - Date.now();
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error(`Failed to parse token. Auto-refresh is disabled.`, error);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var validateEmbedOptions = (options) => {
|
|
95
|
+
const throwError = (message) => {
|
|
96
|
+
throw new EmbedError(EmbedErrorCodes.INVALID_OPTIONS, message);
|
|
97
|
+
};
|
|
98
|
+
if (!options.container || typeof options.container !== "string" && !(options.container instanceof HTMLElement)) {
|
|
99
|
+
throwError("container must be an HTMLElement or a string selector");
|
|
100
|
+
}
|
|
101
|
+
if (!options.embedUrl || !isValidEmbedUrl(options.embedUrl)) {
|
|
102
|
+
throwError(
|
|
103
|
+
"embedUrl must match https://app.codatum.com/protected/workspace/{workspaceId}/notebook/{notebookId}"
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (!options.tokenProvider || typeof options.tokenProvider !== "function") {
|
|
107
|
+
throwError("tokenProvider must be a function");
|
|
108
|
+
}
|
|
109
|
+
if (options.iframeOptions) {
|
|
110
|
+
if (typeof options.iframeOptions !== "object") {
|
|
111
|
+
throwError("iframeOptions must be an object");
|
|
112
|
+
}
|
|
113
|
+
const theme = options.iframeOptions.theme;
|
|
114
|
+
if (theme !== void 0 && !ThemeValues.includes(theme)) {
|
|
115
|
+
throwError(`iframeOptions.theme must be one of ${ThemeValues.join(", ")}`);
|
|
116
|
+
}
|
|
117
|
+
const locale = options.iframeOptions.locale;
|
|
118
|
+
if (locale !== void 0 && typeof locale !== "string") {
|
|
119
|
+
throwError("iframeOptions.locale must be a string");
|
|
120
|
+
}
|
|
121
|
+
const className = options.iframeOptions.className;
|
|
122
|
+
if (className !== void 0 && typeof className !== "string") {
|
|
123
|
+
throwError("iframeOptions.className must be a string");
|
|
124
|
+
}
|
|
125
|
+
const style = options.iframeOptions.style;
|
|
126
|
+
if (style !== void 0 && typeof style !== "object") {
|
|
127
|
+
throwError("iframeOptions.style must be a CSSStyleDeclaration");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (options.tokenOptions) {
|
|
131
|
+
if (typeof options.tokenOptions !== "object") {
|
|
132
|
+
throwError("tokenOptions must be an object");
|
|
133
|
+
}
|
|
134
|
+
const refreshBuffer = options.tokenOptions.refreshBuffer;
|
|
135
|
+
if (refreshBuffer !== void 0 && (typeof refreshBuffer !== "number" || refreshBuffer < 0)) {
|
|
136
|
+
throwError("tokenOptions.refreshBuffer must be a non-negative number");
|
|
137
|
+
}
|
|
138
|
+
const retryCount = options.tokenOptions.retryCount;
|
|
139
|
+
if (retryCount !== void 0 && (typeof retryCount !== "number" || retryCount < 0)) {
|
|
140
|
+
throwError("tokenOptions.retryCount must be a non-negative number");
|
|
141
|
+
}
|
|
142
|
+
const initTimeout = options.tokenOptions.initTimeout;
|
|
143
|
+
if (initTimeout !== void 0 && (typeof initTimeout !== "number" || initTimeout < 0)) {
|
|
144
|
+
throwError("tokenOptions.initTimeout must be a non-negative number");
|
|
145
|
+
}
|
|
146
|
+
const onRefreshError = options.tokenOptions.onRefreshError;
|
|
147
|
+
if (onRefreshError !== void 0 && typeof onRefreshError !== "function") {
|
|
148
|
+
throwError("tokenOptions.onRefreshError must be a function");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (options.displayOptions) {
|
|
152
|
+
if (typeof options.displayOptions !== "object") {
|
|
153
|
+
throwError("displayOptions must be an object");
|
|
154
|
+
}
|
|
155
|
+
const sqlDisplay = options.displayOptions.sqlDisplay;
|
|
156
|
+
if (sqlDisplay !== void 0 && !SqlDisplayValues.includes(sqlDisplay)) {
|
|
157
|
+
throwError(`displayOptions.sqlDisplay must be one of ${SqlDisplayValues.join(", ")}`);
|
|
158
|
+
}
|
|
159
|
+
const hideParamsForm = options.displayOptions.hideParamsForm;
|
|
160
|
+
if (hideParamsForm !== void 0 && typeof hideParamsForm !== "boolean") {
|
|
161
|
+
throwError("displayOptions.hideParamsForm must be a boolean");
|
|
162
|
+
}
|
|
163
|
+
const expandParamsFormByDefault = options.displayOptions.expandParamsFormByDefault;
|
|
164
|
+
if (expandParamsFormByDefault !== void 0 && typeof expandParamsFormByDefault !== "boolean") {
|
|
165
|
+
throwError("displayOptions.expandParamsFormByDefault must be a boolean");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// src/Embed.ts
|
|
171
|
+
var DEFAULT_REFRESH_BUFFER = 60;
|
|
172
|
+
var DEFAULT_RETRY_COUNT = 2;
|
|
173
|
+
var DEFAULT_INIT_TIMEOUT = 30;
|
|
174
|
+
var SHORT_TTL_THRESHOLD = 10 * 1e3;
|
|
175
|
+
var SHORT_TTL_MAX_CONSECUTIVE = 3;
|
|
176
|
+
var EmbedInstance = class {
|
|
177
|
+
constructor(options) {
|
|
178
|
+
this.iframeEl = null;
|
|
179
|
+
this.shortTtlCount = 0;
|
|
180
|
+
this._status = EmbedStatuses.CREATED;
|
|
181
|
+
this.initTimeoutId = null;
|
|
182
|
+
this.refreshTimerId = null;
|
|
183
|
+
this.reloadInProgress = false;
|
|
184
|
+
this.readyForTokenHandled = false;
|
|
185
|
+
this.eventHandlers = {
|
|
186
|
+
paramChanged: [],
|
|
187
|
+
executeSqlsTriggered: []
|
|
188
|
+
};
|
|
189
|
+
this.boundHandleMessage = (event) => this.handleMessage(event);
|
|
190
|
+
var _a, _b, _c;
|
|
191
|
+
validateEmbedOptions(options);
|
|
192
|
+
this.options = deepClone(options);
|
|
193
|
+
this.expectedOrigin = new URL(this.options.embedUrl).origin;
|
|
194
|
+
const tokenOptions = (_a = this.options.tokenOptions) != null ? _a : {};
|
|
195
|
+
this.refreshBuffer = ((_b = tokenOptions.refreshBuffer) != null ? _b : DEFAULT_REFRESH_BUFFER) * 1e3;
|
|
196
|
+
this.retryCount = (_c = tokenOptions.retryCount) != null ? _c : DEFAULT_RETRY_COUNT;
|
|
197
|
+
this.onRefreshError = tokenOptions.onRefreshError;
|
|
198
|
+
this.initPromise = new Promise((resolve, reject) => {
|
|
199
|
+
this.resolveInit = resolve;
|
|
200
|
+
this.rejectInit = reject;
|
|
201
|
+
});
|
|
202
|
+
window.addEventListener("message", this.boundHandleMessage);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Creates the iframe, appends it to the container, and starts the token/connection flow.
|
|
206
|
+
* Call this after createEmbed() to complete initialization.
|
|
207
|
+
*/
|
|
208
|
+
init() {
|
|
209
|
+
var _a, _b;
|
|
210
|
+
if (this.isDestroyed) {
|
|
211
|
+
return Promise.resolve();
|
|
212
|
+
}
|
|
213
|
+
if (this._status !== EmbedStatuses.CREATED) {
|
|
214
|
+
return this.initPromise;
|
|
215
|
+
}
|
|
216
|
+
this._status = EmbedStatuses.INITIALIZING;
|
|
217
|
+
const container = typeof this.options.container === "string" ? document.querySelector(this.options.container) : this.options.container;
|
|
218
|
+
if (!container) {
|
|
219
|
+
this.rejectInit(
|
|
220
|
+
new EmbedError(EmbedErrorCodes.CONTAINER_NOT_FOUND, "Container element not found")
|
|
221
|
+
);
|
|
222
|
+
return this.initPromise;
|
|
223
|
+
}
|
|
224
|
+
const iframeOptions = this.options.iframeOptions;
|
|
225
|
+
const iframe = document.createElement("iframe");
|
|
226
|
+
iframe.src = buildIframeSrc(this.options.embedUrl, iframeOptions);
|
|
227
|
+
iframe.className = getIframeClassName(iframeOptions);
|
|
228
|
+
iframe.setAttribute("allow", "fullscreen; clipboard-write");
|
|
229
|
+
Object.assign(iframe.style, __spreadValues({
|
|
230
|
+
width: "100%",
|
|
231
|
+
height: "100%",
|
|
232
|
+
border: "none"
|
|
233
|
+
}, iframeOptions == null ? void 0 : iframeOptions.style));
|
|
234
|
+
this.iframeEl = iframe;
|
|
235
|
+
container.appendChild(iframe);
|
|
236
|
+
const initTimeoutMs = ((_b = (_a = this.options.tokenOptions) == null ? void 0 : _a.initTimeout) != null ? _b : DEFAULT_INIT_TIMEOUT) * 1e3;
|
|
237
|
+
if (initTimeoutMs > 0) {
|
|
238
|
+
this.initTimeoutId = setTimeout(() => {
|
|
239
|
+
this.initTimeoutId = null;
|
|
240
|
+
if (this._status === EmbedStatuses.INITIALIZING) {
|
|
241
|
+
this.destroy();
|
|
242
|
+
this.rejectInit(
|
|
243
|
+
new EmbedError(
|
|
244
|
+
EmbedErrorCodes.INIT_TIMEOUT,
|
|
245
|
+
`Initialization did not complete within ${initTimeoutMs}ms`
|
|
246
|
+
)
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}, initTimeoutMs);
|
|
250
|
+
}
|
|
251
|
+
return this.initPromise;
|
|
252
|
+
}
|
|
253
|
+
get iframe() {
|
|
254
|
+
return this.isDestroyed ? null : this.iframeEl;
|
|
255
|
+
}
|
|
256
|
+
get status() {
|
|
257
|
+
return this._status;
|
|
258
|
+
}
|
|
259
|
+
get isDestroyed() {
|
|
260
|
+
return this._status === EmbedStatuses.DESTROYED;
|
|
261
|
+
}
|
|
262
|
+
clearInitTimeout() {
|
|
263
|
+
if (this.initTimeoutId !== null) {
|
|
264
|
+
clearTimeout(this.initTimeoutId);
|
|
265
|
+
this.initTimeoutId = null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
clearRefreshTimer() {
|
|
269
|
+
if (this.refreshTimerId !== null) {
|
|
270
|
+
clearTimeout(this.refreshTimerId);
|
|
271
|
+
this.refreshTimerId = null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
sendSetToken(result) {
|
|
275
|
+
if (!this.iframeEl) return;
|
|
276
|
+
const win = this.iframeEl.contentWindow;
|
|
277
|
+
if (!win || this.isDestroyed) return;
|
|
278
|
+
const payload = __spreadValues({
|
|
279
|
+
displayOptions: this.options.displayOptions
|
|
280
|
+
}, result.params != null && result.params.length > 0 ? { params: result.params } : {});
|
|
281
|
+
try {
|
|
282
|
+
const serialized = Object.keys(payload).length ? JSON.parse(JSON.stringify(payload)) : void 0;
|
|
283
|
+
win.postMessage(
|
|
284
|
+
__spreadValues({
|
|
285
|
+
type: "SET_TOKEN",
|
|
286
|
+
token: result.token,
|
|
287
|
+
sdkVersion: SDK_VERSION
|
|
288
|
+
}, serialized),
|
|
289
|
+
this.expectedOrigin
|
|
290
|
+
);
|
|
291
|
+
} catch (err) {
|
|
292
|
+
throw new EmbedError(
|
|
293
|
+
EmbedErrorCodes.UNEXPECTED_ERROR,
|
|
294
|
+
err instanceof Error ? err.message : String(err),
|
|
295
|
+
{ cause: err }
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Calls tokenProvider with context and retries with exponential backoff up to retryCount on failure,
|
|
301
|
+
* unless context.markNonRetryable() was called. On success, schedules the next token refresh.
|
|
302
|
+
*/
|
|
303
|
+
fetchSessionWithRetry(trigger, attempt = 0, delayMs = 1e3) {
|
|
304
|
+
let nonRetryable = false;
|
|
305
|
+
const context = {
|
|
306
|
+
trigger,
|
|
307
|
+
markNonRetryable: () => {
|
|
308
|
+
nonRetryable = true;
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
return this.options.tokenProvider(context).then((session) => {
|
|
312
|
+
const ttlMs = getTokenTtlMs(session.token);
|
|
313
|
+
if (ttlMs !== null) {
|
|
314
|
+
this.scheduleRefresh(ttlMs);
|
|
315
|
+
}
|
|
316
|
+
return session;
|
|
317
|
+
}).catch((err) => {
|
|
318
|
+
if (this.isDestroyed) return Promise.reject(err);
|
|
319
|
+
if (nonRetryable || attempt >= this.retryCount) {
|
|
320
|
+
return Promise.reject(err);
|
|
321
|
+
}
|
|
322
|
+
return new Promise((resolve, reject) => {
|
|
323
|
+
setTimeout(() => {
|
|
324
|
+
this.fetchSessionWithRetry(trigger, attempt + 1, delayMs * 2).then(resolve, reject);
|
|
325
|
+
}, delayMs);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
scheduleRefresh(ttlMs) {
|
|
330
|
+
if (this.isDestroyed) return;
|
|
331
|
+
this.clearRefreshTimer();
|
|
332
|
+
const delayMs = Math.max(0, ttlMs - this.refreshBuffer);
|
|
333
|
+
if (delayMs < SHORT_TTL_THRESHOLD) {
|
|
334
|
+
this.shortTtlCount++;
|
|
335
|
+
if (this.shortTtlCount > SHORT_TTL_MAX_CONSECUTIVE) {
|
|
336
|
+
console.warn(
|
|
337
|
+
`Auto-refresh disabled: token refresh interval has been too short (< ${SHORT_TTL_THRESHOLD}ms) for ${SHORT_TTL_MAX_CONSECUTIVE} consecutive attempts.`
|
|
338
|
+
);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
this.shortTtlCount = 0;
|
|
343
|
+
}
|
|
344
|
+
this.refreshTimerId = setTimeout(() => {
|
|
345
|
+
this.refreshTimerId = null;
|
|
346
|
+
this.runRefreshWithRetry();
|
|
347
|
+
}, delayMs);
|
|
348
|
+
}
|
|
349
|
+
runRefreshWithRetry() {
|
|
350
|
+
if (this.isDestroyed) return;
|
|
351
|
+
this.fetchSessionWithRetry(TokenProviderTriggers.REFRESH).then((result) => {
|
|
352
|
+
if (this.isDestroyed) return;
|
|
353
|
+
this.sendSetToken(result);
|
|
354
|
+
}).catch((err) => {
|
|
355
|
+
var _a;
|
|
356
|
+
if (this.isDestroyed) return;
|
|
357
|
+
(_a = this.onRefreshError) == null ? void 0 : _a.call(
|
|
358
|
+
this,
|
|
359
|
+
new EmbedError(
|
|
360
|
+
EmbedErrorCodes.TOKEN_PROVIDER_FAILED,
|
|
361
|
+
err instanceof Error ? err.message : String(err),
|
|
362
|
+
{ cause: err }
|
|
363
|
+
)
|
|
364
|
+
);
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
handleMessage(event) {
|
|
368
|
+
if (!this.iframeEl || event.source !== this.iframeEl.contentWindow || event.origin !== this.expectedOrigin) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const data = event.data;
|
|
372
|
+
if (!data || typeof data !== "object" || typeof data.type !== "string") {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
if (data.type === "READY_FOR_TOKEN") {
|
|
376
|
+
this.onReadyForToken();
|
|
377
|
+
} else if (data.type === "PARAM_CHANGED") {
|
|
378
|
+
for (const h of this.eventHandlers.paramChanged) {
|
|
379
|
+
h(deepClone(data));
|
|
380
|
+
}
|
|
381
|
+
} else if (data.type === "EXECUTE_SQLS_TRIGGERED") {
|
|
382
|
+
for (const h of this.eventHandlers.executeSqlsTriggered) {
|
|
383
|
+
h(deepClone(data));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
onReadyForToken() {
|
|
388
|
+
if (this.readyForTokenHandled || this._status !== EmbedStatuses.INITIALIZING) return;
|
|
389
|
+
this.readyForTokenHandled = true;
|
|
390
|
+
this.fetchSessionWithRetry(TokenProviderTriggers.INIT).then((result) => {
|
|
391
|
+
if (this.isDestroyed) return;
|
|
392
|
+
this._status = EmbedStatuses.READY;
|
|
393
|
+
this.clearInitTimeout();
|
|
394
|
+
this.sendSetToken(result);
|
|
395
|
+
this.resolveInit();
|
|
396
|
+
}).catch((err) => {
|
|
397
|
+
if (this.isDestroyed) return;
|
|
398
|
+
this.clearInitTimeout();
|
|
399
|
+
this.rejectInit(
|
|
400
|
+
new EmbedError(
|
|
401
|
+
EmbedErrorCodes.TOKEN_PROVIDER_FAILED,
|
|
402
|
+
err instanceof Error ? err.message : String(err),
|
|
403
|
+
{ cause: err }
|
|
404
|
+
)
|
|
405
|
+
);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
reload() {
|
|
409
|
+
if (this.isDestroyed) return Promise.resolve();
|
|
410
|
+
if (this._status !== EmbedStatuses.READY) return Promise.resolve();
|
|
411
|
+
if (this.reloadInProgress) return Promise.resolve();
|
|
412
|
+
this.reloadInProgress = true;
|
|
413
|
+
return this.fetchSessionWithRetry(TokenProviderTriggers.RELOAD).then(
|
|
414
|
+
(result) => {
|
|
415
|
+
this.reloadInProgress = false;
|
|
416
|
+
if (this.isDestroyed) return;
|
|
417
|
+
this.sendSetToken(result);
|
|
418
|
+
},
|
|
419
|
+
(err) => {
|
|
420
|
+
this.reloadInProgress = false;
|
|
421
|
+
if (this.isDestroyed) return;
|
|
422
|
+
throw new EmbedError(
|
|
423
|
+
EmbedErrorCodes.TOKEN_PROVIDER_FAILED,
|
|
424
|
+
err instanceof Error ? err.message : String(err),
|
|
425
|
+
{ cause: err }
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
on(event, handler) {
|
|
431
|
+
if (this.isDestroyed) return;
|
|
432
|
+
this.eventHandlers[event].push(handler);
|
|
433
|
+
}
|
|
434
|
+
off(event, handler) {
|
|
435
|
+
const list = this.eventHandlers[event];
|
|
436
|
+
const i = list.indexOf(handler);
|
|
437
|
+
if (i !== -1) list.splice(i, 1);
|
|
438
|
+
}
|
|
439
|
+
destroy() {
|
|
440
|
+
var _a;
|
|
441
|
+
if (this.isDestroyed) return;
|
|
442
|
+
this._status = EmbedStatuses.DESTROYED;
|
|
443
|
+
this.clearInitTimeout();
|
|
444
|
+
this.clearRefreshTimer();
|
|
445
|
+
window.removeEventListener("message", this.boundHandleMessage);
|
|
446
|
+
(_a = this.iframeEl) == null ? void 0 : _a.remove();
|
|
447
|
+
this.iframeEl = null;
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
function createEmbed(options) {
|
|
451
|
+
return new EmbedInstance(options);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// src/ParamMapper.ts
|
|
455
|
+
var DATE_FORMAT_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
456
|
+
var isValidDateFormat = (s) => {
|
|
457
|
+
return DATE_FORMAT_REGEX.test(s);
|
|
458
|
+
};
|
|
459
|
+
var validateDatatype = (value, datatype, key) => {
|
|
460
|
+
if (value === RESET_TO_DEFAULT) return;
|
|
461
|
+
switch (datatype) {
|
|
462
|
+
case "STRING":
|
|
463
|
+
if (typeof value !== "string") {
|
|
464
|
+
throw new EmbedError(
|
|
465
|
+
EmbedErrorCodes.INVALID_PARAM_VALUE,
|
|
466
|
+
`Parameter ${key} must be a string, got ${typeof value}`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
return;
|
|
470
|
+
case "NUMBER":
|
|
471
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
472
|
+
throw new EmbedError(
|
|
473
|
+
EmbedErrorCodes.INVALID_PARAM_VALUE,
|
|
474
|
+
`Parameter ${key} must be a number, got ${typeof value}`
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
return;
|
|
478
|
+
case "BOOLEAN":
|
|
479
|
+
if (typeof value !== "boolean") {
|
|
480
|
+
throw new EmbedError(
|
|
481
|
+
EmbedErrorCodes.INVALID_PARAM_VALUE,
|
|
482
|
+
`Parameter ${key} must be a boolean, got ${typeof value}`
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
return;
|
|
486
|
+
case "DATE":
|
|
487
|
+
if (typeof value !== "string") {
|
|
488
|
+
throw new EmbedError(
|
|
489
|
+
EmbedErrorCodes.INVALID_PARAM_VALUE,
|
|
490
|
+
`Parameter ${key} must be a date string, got ${typeof value}`
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
if (!isValidDateFormat(value)) {
|
|
494
|
+
throw new EmbedError(
|
|
495
|
+
EmbedErrorCodes.INVALID_PARAM_VALUE,
|
|
496
|
+
`Parameter ${key} must be a date in YYYY-MM-DD format, got: ${value}`
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
return;
|
|
500
|
+
case "STRING[]":
|
|
501
|
+
if (!Array.isArray(value) || !value.every((v) => typeof v === "string")) {
|
|
502
|
+
throw new EmbedError(
|
|
503
|
+
EmbedErrorCodes.INVALID_PARAM_VALUE,
|
|
504
|
+
`Parameter ${key} must be a string array`
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
return;
|
|
508
|
+
case "[DATE, DATE]":
|
|
509
|
+
if (!Array.isArray(value) || value.length !== 2 || typeof value[0] !== "string" || typeof value[1] !== "string") {
|
|
510
|
+
throw new EmbedError(
|
|
511
|
+
EmbedErrorCodes.INVALID_PARAM_VALUE,
|
|
512
|
+
`Parameter ${key} must be a [string, string] tuple (date range)`
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
if (!isValidDateFormat(value[0]) || !isValidDateFormat(value[1])) {
|
|
516
|
+
throw new EmbedError(
|
|
517
|
+
EmbedErrorCodes.INVALID_PARAM_VALUE,
|
|
518
|
+
`Parameter ${key} must be a date range in YYYY-MM-DD format, got: [${value[0]}, ${value[1]}]`
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
return;
|
|
522
|
+
default: {
|
|
523
|
+
const _ = datatype;
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
var ParamMapper = class {
|
|
529
|
+
constructor(mapping, meta) {
|
|
530
|
+
this.defs = {};
|
|
531
|
+
for (const key of Object.keys(mapping)) {
|
|
532
|
+
const paramId = mapping[key];
|
|
533
|
+
const m = meta == null ? void 0 : meta[key];
|
|
534
|
+
this.defs[key] = __spreadValues({ paramId }, m);
|
|
535
|
+
}
|
|
536
|
+
this.aliasByParamId = /* @__PURE__ */ new Map();
|
|
537
|
+
for (const [alias, paramId] of Object.entries(mapping)) {
|
|
538
|
+
this.aliasByParamId.set(paramId, alias);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
encode(values, options) {
|
|
542
|
+
var _a;
|
|
543
|
+
const result = [];
|
|
544
|
+
const keys = (_a = options == null ? void 0 : options.only) != null ? _a : Object.keys(this.defs);
|
|
545
|
+
const valuesByKey = values;
|
|
546
|
+
const skipValidation = (options == null ? void 0 : options.noValidate) === true;
|
|
547
|
+
for (const key of keys) {
|
|
548
|
+
const def = this.defs[key];
|
|
549
|
+
if (def == null) continue;
|
|
550
|
+
const raw = valuesByKey[key];
|
|
551
|
+
if (raw == null) {
|
|
552
|
+
if (!skipValidation && def.required) {
|
|
553
|
+
throw new EmbedError(
|
|
554
|
+
EmbedErrorCodes.MISSING_REQUIRED_PARAM,
|
|
555
|
+
`Missing required parameter: ${key}`
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
if (!skipValidation && def.datatype) {
|
|
561
|
+
validateDatatype(raw, def.datatype, key);
|
|
562
|
+
}
|
|
563
|
+
result.push({
|
|
564
|
+
param_id: def.paramId,
|
|
565
|
+
param_value: raw === RESET_TO_DEFAULT ? RESET_TO_DEFAULT : JSON.stringify(raw),
|
|
566
|
+
is_hidden: def.hidden ? true : void 0
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
return result;
|
|
570
|
+
}
|
|
571
|
+
decode(params, options) {
|
|
572
|
+
var _a;
|
|
573
|
+
const result = {};
|
|
574
|
+
const keys = (_a = options == null ? void 0 : options.only) != null ? _a : Object.keys(this.defs);
|
|
575
|
+
const skipValidation = (options == null ? void 0 : options.noValidate) === true;
|
|
576
|
+
for (const key of keys) {
|
|
577
|
+
const def = this.defs[key];
|
|
578
|
+
if (def == null) continue;
|
|
579
|
+
const encodedParam = params.find((p) => p.param_id === def.paramId);
|
|
580
|
+
if (encodedParam == null) {
|
|
581
|
+
if (!skipValidation && def.required) {
|
|
582
|
+
throw new EmbedError(
|
|
583
|
+
EmbedErrorCodes.MISSING_REQUIRED_PARAM,
|
|
584
|
+
`Missing required parameter: ${key}`
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
let decoded;
|
|
590
|
+
try {
|
|
591
|
+
decoded = JSON.parse(encodedParam.param_value);
|
|
592
|
+
} catch (e) {
|
|
593
|
+
throw new EmbedError(
|
|
594
|
+
EmbedErrorCodes.INVALID_PARAM_VALUE,
|
|
595
|
+
`Invalid parameter value: ${encodedParam.param_value}`
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
if (!skipValidation && def.datatype) {
|
|
599
|
+
validateDatatype(decoded, def.datatype, key);
|
|
600
|
+
}
|
|
601
|
+
result[key] = decoded;
|
|
602
|
+
}
|
|
603
|
+
return result;
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
function createParamMapper(mapping, meta) {
|
|
607
|
+
return new ParamMapper(mapping, meta);
|
|
608
|
+
}
|
|
609
|
+
export {
|
|
610
|
+
EmbedError,
|
|
611
|
+
EmbedErrorCodes,
|
|
612
|
+
EmbedStatuses,
|
|
613
|
+
RESET_TO_DEFAULT,
|
|
614
|
+
SDK_VERSION,
|
|
615
|
+
createEmbed,
|
|
616
|
+
createParamMapper
|
|
617
|
+
};
|
|
618
|
+
//# sourceMappingURL=index.js.map
|