@jitsu/js 1.0.0-canary-20230211030946 → 1.0.0-canary-20230219230011
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/.turbo/turbo-build.log +65 -33
- package/.turbo/turbo-clean.log +5 -5
- package/__tests__/node/method-queue.test.ts +66 -0
- package/__tests__/playwright/integration.test.ts +6 -12
- package/dist/analytics-plugin.d.ts +17 -0
- package/dist/destination-plugins.d.ts +13 -0
- package/dist/index.d.ts +1 -1
- package/dist/jitsu.cjs.js +238 -66
- package/dist/jitsu.es.js +238 -67
- package/dist/method-queue.d.ts +17 -0
- package/dist/script-loader.d.ts +1 -0
- package/dist/web/p.js.txt +237 -66
- package/package.json +4 -4
- package/src/analytics-plugin.ts +108 -63
- package/src/destination-plugins.ts +103 -0
- package/src/index.ts +40 -6
- package/src/method-queue.ts +70 -0
- package/src/script-loader.ts +51 -0
package/dist/jitsu.es.js
CHANGED
|
@@ -65,7 +65,151 @@ function analyticsLib() {
|
|
|
65
65
|
return ke(_objectSpread2(_objectSpread2({}, defaultSettings), opts));
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
var __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
69
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
70
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
71
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
72
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
73
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
74
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
function satisfyFilter(filter, subject) {
|
|
78
|
+
return filter === "*" || filter.toLowerCase().trim() === (subject || "").trim().toLowerCase();
|
|
79
|
+
}
|
|
80
|
+
function applyFilters(event, creds) {
|
|
81
|
+
const { hosts = ["*"], events = ["*"] } = creds;
|
|
82
|
+
return (!!hosts.find(hostFilter => { var _a; return satisfyFilter(hostFilter, (_a = event.context) === null || _a === void 0 ? void 0 : _a.host); }) &&
|
|
83
|
+
!!events.find(eventFilter => satisfyFilter(eventFilter, event.type)));
|
|
84
|
+
}
|
|
85
|
+
const tagPlugin = {
|
|
86
|
+
id: "tag",
|
|
87
|
+
handle(config, payload) {
|
|
88
|
+
return __awaiter$1(this, void 0, void 0, function* () {
|
|
89
|
+
if (!applyFilters(payload, config)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
insertTags(config.code, payload);
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
function insertTags(code, event, opts = {}) {
|
|
97
|
+
let tag;
|
|
98
|
+
try {
|
|
99
|
+
tag = JSON.parse(code);
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
tag = { code, lang: "javascript" };
|
|
103
|
+
}
|
|
104
|
+
const debug = opts.debug || false;
|
|
105
|
+
if (isInBrowser()) {
|
|
106
|
+
if (tag.lang === "javascript") {
|
|
107
|
+
execJs(tag.code, event);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
const codeHolder = document.createElement("span");
|
|
111
|
+
codeHolder.innerHTML = replaceMacro(tag.code, event);
|
|
112
|
+
document.body.insertAdjacentElement("beforeend", codeHolder);
|
|
113
|
+
const scripts = codeHolder.querySelectorAll("script");
|
|
114
|
+
scripts.forEach(script => {
|
|
115
|
+
const scriptClone = document.createElement("script");
|
|
116
|
+
scriptClone.type = scriptClone.type || "text/javascript";
|
|
117
|
+
if (script.hasAttribute("src")) {
|
|
118
|
+
scriptClone.src = script.src;
|
|
119
|
+
}
|
|
120
|
+
scriptClone.text = script.text;
|
|
121
|
+
if (debug) {
|
|
122
|
+
console.log(`[JITSU] Executing script${script.hasAttribute("src") ? ` ${script.src}` : ""}`, scriptClone.text);
|
|
123
|
+
}
|
|
124
|
+
document.head.appendChild(scriptClone);
|
|
125
|
+
document.head.removeChild(scriptClone);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
if (debug) {
|
|
131
|
+
console.log(`[JITSU] insertTags(): cannot insert tags in non-browser environment`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function execJs(code, event) {
|
|
136
|
+
const varName = `jitsu_event_${randomId()}`;
|
|
137
|
+
window[varName] = event;
|
|
138
|
+
const iif = `(function(){
|
|
139
|
+
const event = ${varName};
|
|
140
|
+
${code}
|
|
141
|
+
})()`;
|
|
142
|
+
try {
|
|
143
|
+
eval(iif);
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
console.error(`[JITSU] Error executing JS code: ${e.message}. Code: `, iif);
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
delete window[varName];
|
|
150
|
+
}
|
|
151
|
+
return iif;
|
|
152
|
+
}
|
|
153
|
+
function replaceMacro(code, event) {
|
|
154
|
+
return code.replace(/{{\s*event\s*}}/g, JSON.stringify(event));
|
|
155
|
+
}
|
|
156
|
+
const internalDestinationPlugins = {
|
|
157
|
+
[tagPlugin.id]: tagPlugin,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
function findScript(src) {
|
|
161
|
+
const scripts = Array.prototype.slice.call(window.document.querySelectorAll("script"));
|
|
162
|
+
return scripts.find(s => s.src === src);
|
|
163
|
+
}
|
|
164
|
+
function loadScript(src, attributes) {
|
|
165
|
+
const found = findScript(src);
|
|
166
|
+
if (found !== undefined) {
|
|
167
|
+
const status = found === null || found === void 0 ? void 0 : found.getAttribute("status");
|
|
168
|
+
if (status === "loaded") {
|
|
169
|
+
return Promise.resolve(found);
|
|
170
|
+
}
|
|
171
|
+
if (status === "loading") {
|
|
172
|
+
return new Promise((resolve, reject) => {
|
|
173
|
+
found.addEventListener("load", () => resolve(found));
|
|
174
|
+
found.addEventListener("error", err => reject(err));
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return new Promise((resolve, reject) => {
|
|
179
|
+
var _a;
|
|
180
|
+
const script = window.document.createElement("script");
|
|
181
|
+
script.type = "text/javascript";
|
|
182
|
+
script.src = src;
|
|
183
|
+
script.async = true;
|
|
184
|
+
script.setAttribute("status", "loading");
|
|
185
|
+
for (const [k, v] of Object.entries(attributes !== null && attributes !== void 0 ? attributes : {})) {
|
|
186
|
+
script.setAttribute(k, v);
|
|
187
|
+
}
|
|
188
|
+
script.onload = () => {
|
|
189
|
+
script.onerror = script.onload = null;
|
|
190
|
+
script.setAttribute("status", "loaded");
|
|
191
|
+
resolve(script);
|
|
192
|
+
};
|
|
193
|
+
script.onerror = () => {
|
|
194
|
+
script.onerror = script.onload = null;
|
|
195
|
+
script.setAttribute("status", "error");
|
|
196
|
+
reject(new Error(`Failed to load ${src}`));
|
|
197
|
+
};
|
|
198
|
+
const tag = window.document.getElementsByTagName("script")[0];
|
|
199
|
+
(_a = tag.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(script, tag);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
68
203
|
/* global analytics */
|
|
204
|
+
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
205
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
206
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
207
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
208
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
209
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
210
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
211
|
+
});
|
|
212
|
+
};
|
|
69
213
|
const config = {
|
|
70
214
|
/* Your segment writeKey */
|
|
71
215
|
writeKey: null,
|
|
@@ -277,7 +421,7 @@ function deepMerge(target, source) {
|
|
|
277
421
|
}, target);
|
|
278
422
|
}
|
|
279
423
|
function isInBrowser() {
|
|
280
|
-
return typeof document !== "undefined";
|
|
424
|
+
return typeof document !== "undefined" && typeof window !== "undefined";
|
|
281
425
|
}
|
|
282
426
|
function adjustPayload(payload, config, storage) {
|
|
283
427
|
var _a, _b;
|
|
@@ -315,62 +459,62 @@ function adjustPayload(payload, config, storage) {
|
|
|
315
459
|
delete withContext.options;
|
|
316
460
|
return withContext;
|
|
317
461
|
}
|
|
318
|
-
function
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
function replaceMacro(code, event) {
|
|
337
|
-
return code.replace(/{{\s*event\s*}}/g, JSON.stringify(event));
|
|
338
|
-
}
|
|
339
|
-
function insertTags(tags, event, opts = {}) {
|
|
340
|
-
const debug = opts.debug || false;
|
|
341
|
-
if (isInBrowser()) {
|
|
342
|
-
Object.values(tags).forEach(tag => {
|
|
343
|
-
if (tag.mode === "javascript") {
|
|
344
|
-
execJs(tag.code, event);
|
|
462
|
+
function processDestinations(destinations, method, event, debug, analyticsInstance) {
|
|
463
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
464
|
+
const promises = [];
|
|
465
|
+
for (const destination of destinations) {
|
|
466
|
+
if (destination.deviceOptions.type === "internal-plugin") {
|
|
467
|
+
const plugin = internalDestinationPlugins[destination.deviceOptions.name];
|
|
468
|
+
if (plugin) {
|
|
469
|
+
try {
|
|
470
|
+
promises.push(plugin.handle(destination.credentials, event));
|
|
471
|
+
}
|
|
472
|
+
catch (e) {
|
|
473
|
+
console.warn(`[JITSU] Error processing event with internal plugin '${destination.deviceOptions.name}': ${e === null || e === void 0 ? void 0 : e.message}`, e);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
console.warn(`[JITSU] Unknown internal plugin '${destination.deviceOptions.name}' for destination '${destination.id}'`);
|
|
478
|
+
}
|
|
345
479
|
}
|
|
346
|
-
else {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
480
|
+
else if (destination.deviceOptions.type === "analytics-plugin") {
|
|
481
|
+
yield loadScript(destination.deviceOptions.packageCdn);
|
|
482
|
+
const plugin = window[destination.deviceOptions.moduleVarName];
|
|
483
|
+
if (!plugin) {
|
|
484
|
+
console.warn(`[JITSU] Broken plugin '${destination.deviceOptions.packageCdn}' for destination '${destination.id}' - it doesn't export '${destination.deviceOptions.moduleVarName}' variable`);
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
let pluginInstance;
|
|
488
|
+
try {
|
|
489
|
+
pluginInstance = (typeof plugin === "function" ? plugin : plugin.init)(destination.credentials);
|
|
490
|
+
}
|
|
491
|
+
catch (e) {
|
|
492
|
+
console.warn(`[JITSU] Error creating plugin '${destination.deviceOptions.moduleVarName}@${destination.deviceOptions.packageCdn}' for destination '${destination.id}': ${e === null || e === void 0 ? void 0 : e.message}`, e);
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
if (debug) {
|
|
496
|
+
console.log(`[JITSU] Plugin '${destination.deviceOptions.moduleVarName}@${destination.deviceOptions.packageCdn}' for destination '${destination.id}' initialized with config:`, pluginInstance.config);
|
|
497
|
+
}
|
|
498
|
+
pluginInstance.initialize({ config: pluginInstance.config, instance: analyticsInstance });
|
|
356
499
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
500
|
+
catch (e) {
|
|
501
|
+
console.warn(`[JITSU] Error initializing plugin '${destination.deviceOptions.moduleVarName}@${destination.deviceOptions.packageCdn}' for destination '${destination.id}': ${e === null || e === void 0 ? void 0 : e.message}. Config: ${JSON.stringify(pluginInstance.config)}`, e);
|
|
502
|
+
continue;
|
|
360
503
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
504
|
+
if (pluginInstance[method]) {
|
|
505
|
+
try {
|
|
506
|
+
pluginInstance[method]({ payload: event, config: pluginInstance.config, instance: analyticsInstance });
|
|
507
|
+
}
|
|
508
|
+
catch (e) {
|
|
509
|
+
console.warn(`[JITSU] Error processing ${method}() with plugin '${destination.deviceOptions.moduleVarName}@${destination.deviceOptions.packageCdn}' for destination '${destination.id}': ${e === null || e === void 0 ? void 0 : e.message}`, e);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
364
513
|
}
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
else {
|
|
368
|
-
if (debug) {
|
|
369
|
-
console.log(`insertTags: cannot insert tags in non-browser environment`);
|
|
370
514
|
}
|
|
371
|
-
}
|
|
515
|
+
});
|
|
372
516
|
}
|
|
373
|
-
function send(method, payload, jitsuConfig, store) {
|
|
517
|
+
function send(method, payload, jitsuConfig, instance, store) {
|
|
374
518
|
if (jitsuConfig.echoEvents) {
|
|
375
519
|
console.log(`[JITSU] sending '${method}' event:`, payload);
|
|
376
520
|
return;
|
|
@@ -403,18 +547,19 @@ function send(method, payload, jitsuConfig, store) {
|
|
|
403
547
|
}
|
|
404
548
|
})
|
|
405
549
|
.then(responseText => {
|
|
550
|
+
let response;
|
|
406
551
|
try {
|
|
407
|
-
|
|
408
|
-
if (response.tags) {
|
|
409
|
-
if (jitsuConfig.debug) {
|
|
410
|
-
console.log(`[JITSU] Response Tags: `, JSON.stringify(response.tags, null, 2));
|
|
411
|
-
}
|
|
412
|
-
insertTags(response.tags, payload, { debug: jitsuConfig.debug });
|
|
413
|
-
}
|
|
552
|
+
response = JSON.parse(responseText);
|
|
414
553
|
}
|
|
415
554
|
catch (e) {
|
|
416
555
|
return Promise.reject(`Can't parse JSON: ${responseText}: ${e === null || e === void 0 ? void 0 : e.message}`);
|
|
417
556
|
}
|
|
557
|
+
if (response.destinations) {
|
|
558
|
+
if (jitsuConfig.debug) {
|
|
559
|
+
console.log(`[JITSU] Processing device destianations: `, JSON.stringify(response.destinations, null, 2));
|
|
560
|
+
}
|
|
561
|
+
return processDestinations(response.destinations, method, adjustedPayload, !!jitsuConfig.debug, instance);
|
|
562
|
+
}
|
|
418
563
|
})
|
|
419
564
|
.catch(err => {
|
|
420
565
|
if (jitsuConfig.debug) {
|
|
@@ -444,9 +589,9 @@ const jitsuAnalyticsPlugin = (pluginConfig = {}) => {
|
|
|
444
589
|
name: "jitsu",
|
|
445
590
|
config: Object.assign(Object.assign({}, config), pluginConfig),
|
|
446
591
|
initialize: args => {
|
|
447
|
-
const { config
|
|
592
|
+
const { config } = args;
|
|
448
593
|
if (config.debug) {
|
|
449
|
-
console.debug("Initializing Jitsu plugin with config: ", JSON.stringify(config));
|
|
594
|
+
console.debug("[JITSU] Initializing Jitsu plugin with config: ", JSON.stringify(config));
|
|
450
595
|
}
|
|
451
596
|
if (!config.host && !config.echoEvents) {
|
|
452
597
|
throw new Error("Please specify host variable in jitsu plugin initialization, or set echoEvents to true");
|
|
@@ -454,17 +599,17 @@ const jitsuAnalyticsPlugin = (pluginConfig = {}) => {
|
|
|
454
599
|
},
|
|
455
600
|
page: args => {
|
|
456
601
|
const { payload, config, instance } = args;
|
|
457
|
-
return send("page", payload, config, cachingStorageWrapper(instance.storage));
|
|
602
|
+
return send("page", payload, config, instance, cachingStorageWrapper(instance.storage));
|
|
458
603
|
},
|
|
459
604
|
track: args => {
|
|
460
605
|
const { payload, config, instance } = args;
|
|
461
|
-
return send("track", payload, config, cachingStorageWrapper(instance.storage));
|
|
606
|
+
return send("track", payload, config, instance, cachingStorageWrapper(instance.storage));
|
|
462
607
|
},
|
|
463
608
|
identify: args => {
|
|
464
609
|
const { payload, config, instance } = args;
|
|
465
610
|
// Store traits in cache to be able to use them in page and track events that run asynchronously with current identify.
|
|
466
611
|
storageCache["__user_traits"] = payload.traits;
|
|
467
|
-
return send("identify", payload, config, cachingStorageWrapper(instance.storage));
|
|
612
|
+
return send("identify", payload, config, instance, cachingStorageWrapper(instance.storage));
|
|
468
613
|
},
|
|
469
614
|
reset: args => {
|
|
470
615
|
//clear storage cache
|
|
@@ -517,13 +662,12 @@ function parse(input) {
|
|
|
517
662
|
}
|
|
518
663
|
return value;
|
|
519
664
|
}
|
|
520
|
-
function
|
|
521
|
-
const rt = opts.runtime || (typeof window === "undefined" ? emptyRuntime(opts) : windowRuntime(opts));
|
|
665
|
+
function createUnderlyingAnalyticsInstance(opts, rt, plugins = []) {
|
|
522
666
|
const analytics = analyticsLib({
|
|
523
667
|
app: "test",
|
|
524
668
|
debug: !!opts.debug,
|
|
525
669
|
storage: rt.store(),
|
|
526
|
-
plugins: [jitsuAnalyticsPlugin(opts)],
|
|
670
|
+
plugins: [jitsuAnalyticsPlugin(opts), ...plugins],
|
|
527
671
|
});
|
|
528
672
|
const originalPage = analytics.page;
|
|
529
673
|
analytics.page = (...args) => {
|
|
@@ -536,5 +680,32 @@ function jitsuAnalytics(opts) {
|
|
|
536
680
|
};
|
|
537
681
|
return analytics;
|
|
538
682
|
}
|
|
683
|
+
function jitsuAnalytics(opts) {
|
|
684
|
+
const inBrowser = isInBrowser();
|
|
685
|
+
const rt = opts.runtime || (inBrowser ? windowRuntime(opts) : emptyRuntime(opts));
|
|
686
|
+
return createUnderlyingAnalyticsInstance(opts, rt);
|
|
687
|
+
// if (inBrowser) {
|
|
688
|
+
// const fetch = opts.fetch || globalThis.fetch;
|
|
689
|
+
// if (!fetch) {
|
|
690
|
+
// throw new Error(
|
|
691
|
+
// "Please specify fetch function in jitsu plugin initialization, fetch isn't available in global scope"
|
|
692
|
+
// );
|
|
693
|
+
// }
|
|
694
|
+
// const url = `${opts.host}/api/s/cfg`;
|
|
695
|
+
// const authHeader = {};
|
|
696
|
+
// const debugHeader = opts.debug ? { "X-Enable-Debug": "true" } : {};
|
|
697
|
+
// fetch(url)
|
|
698
|
+
// .then(res => res.json())
|
|
699
|
+
// .then(res => {
|
|
700
|
+
// result.loaded(createUnderlyingAnalyticsInstance(opts, rt, []));
|
|
701
|
+
// })
|
|
702
|
+
// .catch(e => {
|
|
703
|
+
// console.warn(`[JITSU] error getting device-destinations from ${url}`, e);
|
|
704
|
+
// result.loaded(createUnderlyingAnalyticsInstance(opts, rt));
|
|
705
|
+
// });
|
|
706
|
+
// } else {
|
|
707
|
+
// result.loaded(createUnderlyingAnalyticsInstance(opts, rt));
|
|
708
|
+
// }
|
|
709
|
+
}
|
|
539
710
|
|
|
540
|
-
export { emptyRuntime, getTopLevelDomain, jitsuAnalytics, parseQuery, randomId, windowRuntime };
|
|
711
|
+
export { emptyRuntime, getTopLevelDomain, isInBrowser, jitsuAnalytics, parseQuery, randomId, windowRuntime };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type InterfaceWrapper<T> = {
|
|
2
|
+
get(): WithAsyncMethods<T>;
|
|
3
|
+
loaded(instance: T): any;
|
|
4
|
+
};
|
|
5
|
+
type WithAsyncMethods<T> = {
|
|
6
|
+
[K in keyof T]: T[K] extends (...args: infer A) => infer R ? (...args: A) => R extends Promise<any> ? R : Promise<R> : T[K];
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* This function creates a wrapper around an interface that allows to call methods on it, but all methods will go to queue. Once actual instance
|
|
10
|
+
* implementation becomes available, all queued methods will be called on it.
|
|
11
|
+
*
|
|
12
|
+
*
|
|
13
|
+
* @param methods of methods that should be wrapped. Per each method of you should specify if it should be wrapped. You'll need to mark all
|
|
14
|
+
* methods for type safety. If method is not wrapped, it will throw an error when called.
|
|
15
|
+
*/
|
|
16
|
+
export declare function delayMethodExec<T>(methods: Record<keyof T, boolean>): InterfaceWrapper<T>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loadScript(src: string, attributes?: Record<string, string>): Promise<HTMLScriptElement>;
|