@levo-so/core 0.1.60 → 0.1.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +528 -502
- package/dist/index.js +239 -1204
- package/package.json +9 -15
package/dist/index.js
CHANGED
|
@@ -1,973 +1,6 @@
|
|
|
1
|
-
import { onUserActivity } from '@analytics/activity-utils';
|
|
2
|
-
import { getVisitorSource } from '@analytics/visitor-source';
|
|
3
|
-
import { Analytics } from 'analytics';
|
|
4
|
-
import getUserLocale from 'get-user-locale';
|
|
5
|
-
import { WretchError } from 'wretch/resolver';
|
|
6
1
|
import wretch from 'wretch';
|
|
7
2
|
import queryStringAddon from 'wretch/addons/queryString';
|
|
8
|
-
|
|
9
|
-
// src/control/audience.ts
|
|
10
|
-
|
|
11
|
-
// src/utils/isIncognito.ts
|
|
12
|
-
var isIncognito = () => new Promise((resolve, reject) => {
|
|
13
|
-
let browserName = "Unknown";
|
|
14
|
-
function __callback(isPrivate) {
|
|
15
|
-
resolve({
|
|
16
|
-
isPrivate,
|
|
17
|
-
browserName
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
function identifyChromium() {
|
|
21
|
-
const ua = navigator.userAgent;
|
|
22
|
-
if (ua.match(/Chrome/)) {
|
|
23
|
-
if (navigator.brave !== void 0) {
|
|
24
|
-
return "Brave";
|
|
25
|
-
}
|
|
26
|
-
if (ua.match(/Edg/)) {
|
|
27
|
-
return "Edge";
|
|
28
|
-
}
|
|
29
|
-
if (ua.match(/OPR/)) {
|
|
30
|
-
return "Opera";
|
|
31
|
-
}
|
|
32
|
-
return "Chrome";
|
|
33
|
-
}
|
|
34
|
-
return "Chromium";
|
|
35
|
-
}
|
|
36
|
-
function assertEvalToString(value) {
|
|
37
|
-
return value === eval.toString().length;
|
|
38
|
-
}
|
|
39
|
-
function isSafari() {
|
|
40
|
-
const v = navigator.vendor;
|
|
41
|
-
return v !== void 0 && v.indexOf("Apple") === 0 && assertEvalToString(37);
|
|
42
|
-
}
|
|
43
|
-
function isChrome() {
|
|
44
|
-
const v = navigator.vendor;
|
|
45
|
-
return v !== void 0 && v.indexOf("Google") === 0 && assertEvalToString(33);
|
|
46
|
-
}
|
|
47
|
-
function isFirefox() {
|
|
48
|
-
return document.documentElement !== void 0 && document.documentElement.style.MozAppearance !== void 0 && assertEvalToString(37);
|
|
49
|
-
}
|
|
50
|
-
function isMSIE() {
|
|
51
|
-
return navigator.msSaveBlob !== void 0 && assertEvalToString(39);
|
|
52
|
-
}
|
|
53
|
-
function newSafariTest() {
|
|
54
|
-
const tmp_name = String(Math.random());
|
|
55
|
-
try {
|
|
56
|
-
const db = window.indexedDB.open(tmp_name, 1);
|
|
57
|
-
db.onupgradeneeded = (i) => {
|
|
58
|
-
const res = i.target?.result;
|
|
59
|
-
try {
|
|
60
|
-
res.createObjectStore("test", {
|
|
61
|
-
autoIncrement: true
|
|
62
|
-
}).put(new Blob());
|
|
63
|
-
__callback(false);
|
|
64
|
-
} catch (e) {
|
|
65
|
-
let message = e;
|
|
66
|
-
if (e instanceof Error) {
|
|
67
|
-
message = e.message ?? e;
|
|
68
|
-
}
|
|
69
|
-
if (typeof message !== "string") {
|
|
70
|
-
return __callback(false);
|
|
71
|
-
}
|
|
72
|
-
const matchesExpectedError = /BlobURLs are not yet supported/.test(
|
|
73
|
-
message
|
|
74
|
-
);
|
|
75
|
-
return __callback(matchesExpectedError);
|
|
76
|
-
} finally {
|
|
77
|
-
res.close();
|
|
78
|
-
window.indexedDB.deleteDatabase(tmp_name);
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
} catch (e) {
|
|
82
|
-
return __callback(false);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
function oldSafariTest() {
|
|
86
|
-
const openDB = window.openDatabase;
|
|
87
|
-
const storage = window.localStorage;
|
|
88
|
-
try {
|
|
89
|
-
openDB(null, null, null, null);
|
|
90
|
-
} catch (e) {
|
|
91
|
-
return __callback(true);
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
storage.setItem("test", "1");
|
|
95
|
-
storage.removeItem("test");
|
|
96
|
-
} catch (e) {
|
|
97
|
-
return __callback(true);
|
|
98
|
-
}
|
|
99
|
-
return __callback(false);
|
|
100
|
-
}
|
|
101
|
-
function safariPrivateTest() {
|
|
102
|
-
if (navigator.maxTouchPoints !== void 0) {
|
|
103
|
-
newSafariTest();
|
|
104
|
-
} else {
|
|
105
|
-
oldSafariTest();
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
function getQuotaLimit() {
|
|
109
|
-
const w = window;
|
|
110
|
-
if (w.performance !== void 0 && w.performance.memory !== void 0 && w.performance.memory.jsHeapSizeLimit !== void 0) {
|
|
111
|
-
return performance.memory.jsHeapSizeLimit;
|
|
112
|
-
}
|
|
113
|
-
return 1073741824;
|
|
114
|
-
}
|
|
115
|
-
function storageQuotaChromePrivateTest() {
|
|
116
|
-
navigator.webkitTemporaryStorage.queryUsageAndQuota(
|
|
117
|
-
(_, quota) => {
|
|
118
|
-
const quotaInMib = Math.round(quota / (1024 * 1024));
|
|
119
|
-
const quotaLimitInMib = Math.round(getQuotaLimit() / (1024 * 1024)) * 2;
|
|
120
|
-
__callback(quotaInMib < quotaLimitInMib);
|
|
121
|
-
},
|
|
122
|
-
(e) => {
|
|
123
|
-
reject(
|
|
124
|
-
new Error(
|
|
125
|
-
`detectIncognito somehow failed to query storage quota: ${e.message}`
|
|
126
|
-
)
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
function oldChromePrivateTest() {
|
|
132
|
-
const fs = window.webkitRequestFileSystem;
|
|
133
|
-
const success = () => {
|
|
134
|
-
__callback(false);
|
|
135
|
-
};
|
|
136
|
-
const error = () => {
|
|
137
|
-
__callback(true);
|
|
138
|
-
};
|
|
139
|
-
fs(0, 1, success, error);
|
|
140
|
-
}
|
|
141
|
-
function chromePrivateTest() {
|
|
142
|
-
if (self.Promise !== void 0 && self.Promise.allSettled !== void 0) {
|
|
143
|
-
storageQuotaChromePrivateTest();
|
|
144
|
-
} else {
|
|
145
|
-
oldChromePrivateTest();
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
function firefoxPrivateTest() {
|
|
149
|
-
__callback(navigator.serviceWorker === void 0);
|
|
150
|
-
}
|
|
151
|
-
function msiePrivateTest() {
|
|
152
|
-
__callback(window.indexedDB === void 0);
|
|
153
|
-
}
|
|
154
|
-
function main() {
|
|
155
|
-
if (isSafari()) {
|
|
156
|
-
browserName = "Safari";
|
|
157
|
-
safariPrivateTest();
|
|
158
|
-
} else if (isChrome()) {
|
|
159
|
-
browserName = identifyChromium();
|
|
160
|
-
chromePrivateTest();
|
|
161
|
-
} else if (isFirefox()) {
|
|
162
|
-
browserName = "Firefox";
|
|
163
|
-
firefoxPrivateTest();
|
|
164
|
-
} else if (isMSIE()) {
|
|
165
|
-
browserName = "Internet Explorer";
|
|
166
|
-
msiePrivateTest();
|
|
167
|
-
} else {
|
|
168
|
-
reject(new Error("detectIncognito cannot determine the browser"));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
main();
|
|
172
|
-
});
|
|
173
|
-
var isIncognito_default = isIncognito;
|
|
174
|
-
|
|
175
|
-
// src/utils/getUserProperties.ts
|
|
176
|
-
var getGPUInfo = () => {
|
|
177
|
-
const canvas = document.createElement("canvas");
|
|
178
|
-
const gl = canvas?.getContext("webgl") || canvas?.getContext("experimental-webgl");
|
|
179
|
-
if (!gl) {
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
const debugInfo = gl?.getExtension("WEBGL_debug_renderer_info");
|
|
183
|
-
return debugInfo ? gl?.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : null;
|
|
184
|
-
};
|
|
185
|
-
var getUserProperties = async () => {
|
|
186
|
-
let locale = "";
|
|
187
|
-
let timezone = "";
|
|
188
|
-
let dark_mode = false;
|
|
189
|
-
let private_mode = false;
|
|
190
|
-
const properties = {};
|
|
191
|
-
if (!window) {
|
|
192
|
-
return {
|
|
193
|
-
locale,
|
|
194
|
-
timezone,
|
|
195
|
-
dark_mode,
|
|
196
|
-
private_mode,
|
|
197
|
-
properties
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
try {
|
|
201
|
-
({ isPrivate: private_mode } = await isIncognito_default());
|
|
202
|
-
} catch (e) {
|
|
203
|
-
console.error(e);
|
|
204
|
-
}
|
|
205
|
-
try {
|
|
206
|
-
dark_mode = window.matchMedia?.("(prefers-color-scheme: dark)").matches;
|
|
207
|
-
} catch (e) {
|
|
208
|
-
console.error(e);
|
|
209
|
-
}
|
|
210
|
-
try {
|
|
211
|
-
timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
212
|
-
} catch (e) {
|
|
213
|
-
console.error(e);
|
|
214
|
-
}
|
|
215
|
-
try {
|
|
216
|
-
locale = getUserLocale();
|
|
217
|
-
} catch (e) {
|
|
218
|
-
console.error(e);
|
|
219
|
-
}
|
|
220
|
-
if (navigator && "connection" in navigator) {
|
|
221
|
-
const connection = navigator.connection || navigator?.mozConnection || navigator.webkitConnection;
|
|
222
|
-
properties.network_type = connection.effectiveType;
|
|
223
|
-
}
|
|
224
|
-
properties.width = window?.innerWidth;
|
|
225
|
-
properties.height = window?.innerHeight;
|
|
226
|
-
properties.pixel_ratio = window?.devicePixelRatio;
|
|
227
|
-
if (screen) {
|
|
228
|
-
properties.screen_width = screen?.width;
|
|
229
|
-
properties.screen_height = screen?.height;
|
|
230
|
-
properties.color_depth = screen?.colorDepth;
|
|
231
|
-
properties.orientation = screen?.orientation?.type;
|
|
232
|
-
}
|
|
233
|
-
if (navigator && "getBattery" in navigator) {
|
|
234
|
-
const battery = await navigator?.getBattery();
|
|
235
|
-
if (battery) {
|
|
236
|
-
properties.battery_level = Number((battery?.level * 100).toFixed(0));
|
|
237
|
-
properties.battery_charging = battery?.charging;
|
|
238
|
-
properties.battery_discharging_time = battery?.dischargingTime;
|
|
239
|
-
properties.battery_full_charge_capacity = battery?.fullChargeCapacity;
|
|
240
|
-
properties.battery_charging_time = battery?.chargingTime;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
if (navigator && "deviceMemory" in navigator) {
|
|
244
|
-
properties.device_memory = navigator?.deviceMemory;
|
|
245
|
-
}
|
|
246
|
-
const supportsTouch = "ontouchstart" in window || typeof navigator?.maxTouchPoints !== "undefined" && navigator?.maxTouchPoints > 0;
|
|
247
|
-
properties.touch_supported = supportsTouch;
|
|
248
|
-
if ("hardwareConcurrency" in navigator) {
|
|
249
|
-
properties.hardware_concurrency = navigator?.hardwareConcurrency;
|
|
250
|
-
}
|
|
251
|
-
if (navigator && "mediaDevices" in navigator) {
|
|
252
|
-
const mediaDevices = navigator?.mediaDevices;
|
|
253
|
-
const devices = await mediaDevices?.enumerateDevices();
|
|
254
|
-
const hasCamera = devices.some((device) => device?.kind === "videoinput");
|
|
255
|
-
properties.camera_supported = hasCamera;
|
|
256
|
-
const hasMicrophone = devices.some(
|
|
257
|
-
(device) => device?.kind === "audioinput"
|
|
258
|
-
);
|
|
259
|
-
properties.microphone_supported = hasMicrophone;
|
|
260
|
-
}
|
|
261
|
-
properties.gpu = getGPUInfo();
|
|
262
|
-
return {
|
|
263
|
-
locale,
|
|
264
|
-
timezone,
|
|
265
|
-
dark_mode,
|
|
266
|
-
private_mode,
|
|
267
|
-
properties
|
|
268
|
-
};
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
// src/utils/LevoError.ts
|
|
272
|
-
var LevoError = class extends Error {
|
|
273
|
-
code;
|
|
274
|
-
title;
|
|
275
|
-
status;
|
|
276
|
-
type;
|
|
277
|
-
errors;
|
|
278
|
-
constructor(data) {
|
|
279
|
-
super(data.description);
|
|
280
|
-
this.name = "Levo Request Error";
|
|
281
|
-
this.status = data.status;
|
|
282
|
-
this.code = data.code;
|
|
283
|
-
this.title = data.title;
|
|
284
|
-
this.type = data.type;
|
|
285
|
-
this.errors = data.errors;
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Get validation errors for form fields
|
|
289
|
-
*/
|
|
290
|
-
get fieldErrors() {
|
|
291
|
-
return this.errors || [];
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Check if this is a validation error with field-specific errors
|
|
295
|
-
*/
|
|
296
|
-
get hasFieldErrors() {
|
|
297
|
-
return Boolean(this.errors?.length);
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
// src/utils/getLevoError.ts
|
|
302
|
-
var getLevoError = (error) => {
|
|
303
|
-
if (error instanceof LevoError) {
|
|
304
|
-
return error;
|
|
305
|
-
}
|
|
306
|
-
if (error instanceof WretchError && error?.json) {
|
|
307
|
-
const errorResponse = error?.json;
|
|
308
|
-
return new LevoError({
|
|
309
|
-
status: errorResponse?.content?.status || 500,
|
|
310
|
-
code: errorResponse?.content?.code || `Unknown`,
|
|
311
|
-
title: errorResponse?.content?.title || `HTTP ${error?.status || 500} Error`,
|
|
312
|
-
description: errorResponse?.content?.description || `Request failed with status ${error?.status || 500}`,
|
|
313
|
-
type: errorResponse?.content?.type,
|
|
314
|
-
errors: errorResponse?.content?.errors
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
if (error instanceof TypeError) {
|
|
318
|
-
if (error?.message.includes("Failed to fetch") || error?.message.includes("Network request failed") || error?.message.includes("fetch")) {
|
|
319
|
-
return new LevoError({
|
|
320
|
-
status: 0,
|
|
321
|
-
code: "NETWORK_ERROR",
|
|
322
|
-
title: "Network Error",
|
|
323
|
-
description: "Unable to connect to the server. Please check your internet connection."
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
if (error?.message.includes("CORS") || error?.message.includes("cross-origin")) {
|
|
327
|
-
return new LevoError({
|
|
328
|
-
status: 0,
|
|
329
|
-
code: "CORS_ERROR",
|
|
330
|
-
title: "CORS Error",
|
|
331
|
-
description: "Cross-origin request blocked. Please contact support."
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
if (error?.name === "AbortError" || error instanceof DOMException) {
|
|
336
|
-
return new LevoError({
|
|
337
|
-
status: 408,
|
|
338
|
-
code: "REQUEST_TIMEOUT",
|
|
339
|
-
title: "Request Timeout",
|
|
340
|
-
description: "The request was cancelled or timed out. Please try again."
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
if (error?.request) {
|
|
344
|
-
return new LevoError({
|
|
345
|
-
status: error?.response?.status || 500,
|
|
346
|
-
code: error?.code || "REQUEST_ERROR",
|
|
347
|
-
title: "Request Error",
|
|
348
|
-
description: error?.message || "An error occurred while making the request"
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
return new LevoError({
|
|
352
|
-
status: 500,
|
|
353
|
-
code: "UNKNOWN_ERROR",
|
|
354
|
-
title: "Unexpected Error",
|
|
355
|
-
description: error?.message || "Something unexpected happened"
|
|
356
|
-
});
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
// src/control/audience.ts
|
|
360
|
-
var BATCH_SIZE = 50;
|
|
361
|
-
var BATCH_INTERVAL_MS = 5e3;
|
|
362
|
-
var MAX_EVENTS_PER_PAGE = 500;
|
|
363
|
-
var BROADCAST_CHANNEL_NAME = "levo_analytics";
|
|
364
|
-
var createLevoAudienceModule = (core, httpClient) => {
|
|
365
|
-
const _IDLE_THROTTLE = 1 * 1e3;
|
|
366
|
-
const _IDLE_TIMEOUT = 60 * 1e3;
|
|
367
|
-
let instance = null;
|
|
368
|
-
let idle_listener = null;
|
|
369
|
-
const storage_keys = {
|
|
370
|
-
/** Legacy: Lock key for identify operation (no longer used internally) */
|
|
371
|
-
identify: "lock:lv_aud_identify",
|
|
372
|
-
/** Key for tracking open tab count */
|
|
373
|
-
open_tabs: "lv_insights_ot",
|
|
374
|
-
/** Key for current tab ID (sessionStorage) */
|
|
375
|
-
current_tab: "lv_insights_ct",
|
|
376
|
-
/** Key for all tabs registry */
|
|
377
|
-
all_tabs: "lv_insights_at",
|
|
378
|
-
/** Device JWT for direct mode (1yr expiry, rotated on /welcome, analytics-only scope) */
|
|
379
|
-
device_token: "lv_aud_device",
|
|
380
|
-
/** Session JWT for direct mode (1yr expiry, rotated on /welcome, analytics-only scope) */
|
|
381
|
-
session_token: "lv_aud_session",
|
|
382
|
-
/** Cached insights mode ('proxy' | 'direct') */
|
|
383
|
-
mode: "lv_aud_mode"
|
|
384
|
-
};
|
|
385
|
-
let _insightsMode = "proxy";
|
|
386
|
-
let _insightsBaseUrl = "";
|
|
387
|
-
let _localStorageAvailable = true;
|
|
388
|
-
const _memoryStorage = /* @__PURE__ */ new Map();
|
|
389
|
-
const checkLocalStorageAvailable = () => {
|
|
390
|
-
if (typeof window === "undefined" || !("localStorage" in window)) {
|
|
391
|
-
return false;
|
|
392
|
-
}
|
|
393
|
-
try {
|
|
394
|
-
const testKey = "__levo_test__";
|
|
395
|
-
window.localStorage.setItem(testKey, "test");
|
|
396
|
-
window.localStorage.removeItem(testKey);
|
|
397
|
-
return true;
|
|
398
|
-
} catch {
|
|
399
|
-
return false;
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
const getFromStorage = (key) => {
|
|
403
|
-
if (typeof window === "undefined") {
|
|
404
|
-
return _memoryStorage.get(key) || null;
|
|
405
|
-
}
|
|
406
|
-
if (_localStorageAvailable) {
|
|
407
|
-
try {
|
|
408
|
-
return window.localStorage.getItem(key);
|
|
409
|
-
} catch {
|
|
410
|
-
return _memoryStorage.get(key) || null;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
return _memoryStorage.get(key) || null;
|
|
414
|
-
};
|
|
415
|
-
const setInStorage = (key, value) => {
|
|
416
|
-
if (typeof window === "undefined") {
|
|
417
|
-
_memoryStorage.set(key, value);
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
if (_localStorageAvailable) {
|
|
421
|
-
try {
|
|
422
|
-
window.localStorage.setItem(key, value);
|
|
423
|
-
return;
|
|
424
|
-
} catch {
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
_memoryStorage.set(key, value);
|
|
428
|
-
};
|
|
429
|
-
const removeFromStorage = (key) => {
|
|
430
|
-
_memoryStorage.delete(key);
|
|
431
|
-
if (typeof window === "undefined" || !_localStorageAvailable) {
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
try {
|
|
435
|
-
window.localStorage.removeItem(key);
|
|
436
|
-
} catch {
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
const getAuthHeaders = () => {
|
|
440
|
-
const headers = {};
|
|
441
|
-
if (_insightsMode === "direct") {
|
|
442
|
-
const deviceToken = getFromStorage(storage_keys.device_token);
|
|
443
|
-
const sessionToken = getFromStorage(storage_keys.session_token);
|
|
444
|
-
if (deviceToken) headers["Levo-Audience-Device"] = deviceToken;
|
|
445
|
-
if (sessionToken) headers["Levo-Audience-Session"] = sessionToken;
|
|
446
|
-
}
|
|
447
|
-
return headers;
|
|
448
|
-
};
|
|
449
|
-
const pingEndpoint = async (url, timeout) => {
|
|
450
|
-
let timeoutId = null;
|
|
451
|
-
try {
|
|
452
|
-
const controller = new AbortController();
|
|
453
|
-
timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
454
|
-
const response = await fetch(url, {
|
|
455
|
-
method: "GET",
|
|
456
|
-
signal: controller.signal,
|
|
457
|
-
credentials: "omit"
|
|
458
|
-
});
|
|
459
|
-
return response.ok;
|
|
460
|
-
} catch {
|
|
461
|
-
return false;
|
|
462
|
-
} finally {
|
|
463
|
-
if (timeoutId) {
|
|
464
|
-
clearTimeout(timeoutId);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
const detectInsightsMode = async () => {
|
|
469
|
-
if (core.insightsMode === "proxy") {
|
|
470
|
-
_insightsBaseUrl = core.insightsUrl;
|
|
471
|
-
return "proxy";
|
|
472
|
-
}
|
|
473
|
-
if (core.insightsMode === "direct") {
|
|
474
|
-
if (!core.directInsightsUrl) {
|
|
475
|
-
console.warn(
|
|
476
|
-
"[@levo-so/core] insightsMode='direct' but directInsightsUrl not set. Falling back to proxy."
|
|
477
|
-
);
|
|
478
|
-
_insightsBaseUrl = core.insightsUrl;
|
|
479
|
-
return "proxy";
|
|
480
|
-
}
|
|
481
|
-
_insightsBaseUrl = core.directInsightsUrl;
|
|
482
|
-
setInStorage(storage_keys.mode, "direct");
|
|
483
|
-
return "direct";
|
|
484
|
-
}
|
|
485
|
-
const cachedMode = getFromStorage(storage_keys.mode);
|
|
486
|
-
if (cachedMode === "proxy" || cachedMode === "direct") {
|
|
487
|
-
if (cachedMode === "direct" && !core.directInsightsUrl) {
|
|
488
|
-
removeFromStorage(storage_keys.mode);
|
|
489
|
-
_insightsBaseUrl = "";
|
|
490
|
-
} else {
|
|
491
|
-
const pingUrl = cachedMode === "proxy" ? `${core.insightsUrl}/ping` : `${core.directInsightsUrl}/ping`;
|
|
492
|
-
if (await pingEndpoint(pingUrl, core.pingTimeout)) {
|
|
493
|
-
_insightsBaseUrl = cachedMode === "proxy" ? core.insightsUrl : core.directInsightsUrl;
|
|
494
|
-
return cachedMode;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
if (await pingEndpoint(`${core.insightsUrl}/ping`, core.pingTimeout)) {
|
|
499
|
-
setInStorage(storage_keys.mode, "proxy");
|
|
500
|
-
_insightsBaseUrl = core.insightsUrl;
|
|
501
|
-
return "proxy";
|
|
502
|
-
}
|
|
503
|
-
if (core.directInsightsUrl) {
|
|
504
|
-
setInStorage(storage_keys.mode, "direct");
|
|
505
|
-
_insightsBaseUrl = core.directInsightsUrl;
|
|
506
|
-
return "direct";
|
|
507
|
-
}
|
|
508
|
-
_insightsBaseUrl = core.insightsUrl;
|
|
509
|
-
return "proxy";
|
|
510
|
-
};
|
|
511
|
-
let _session = "";
|
|
512
|
-
let _device = "";
|
|
513
|
-
let is_identified = false;
|
|
514
|
-
let _eventCount = 0;
|
|
515
|
-
let _eventQueue = [];
|
|
516
|
-
let _flushTimer = null;
|
|
517
|
-
let _channel = null;
|
|
518
|
-
let _pendingIdentify = false;
|
|
519
|
-
const buildSessionSyncMessage = () => {
|
|
520
|
-
const message = {
|
|
521
|
-
type: "SESSION_SYNC",
|
|
522
|
-
session: _session,
|
|
523
|
-
device: _device
|
|
524
|
-
};
|
|
525
|
-
if (_insightsMode === "direct") {
|
|
526
|
-
const storedDeviceToken = getFromStorage(storage_keys.device_token);
|
|
527
|
-
const storedSessionToken = getFromStorage(storage_keys.session_token);
|
|
528
|
-
if (storedDeviceToken) message.deviceToken = storedDeviceToken;
|
|
529
|
-
if (storedSessionToken) message.sessionToken = storedSessionToken;
|
|
530
|
-
}
|
|
531
|
-
return message;
|
|
532
|
-
};
|
|
533
|
-
const initBroadcastChannel = () => {
|
|
534
|
-
if (typeof BroadcastChannel === "undefined") {
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
try {
|
|
538
|
-
_channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME);
|
|
539
|
-
_channel.onmessage = (event) => {
|
|
540
|
-
const { type, session, device, deviceToken, sessionToken } = event.data;
|
|
541
|
-
if (type === "SESSION_SYNC" && !is_identified) {
|
|
542
|
-
_session = session;
|
|
543
|
-
_device = device;
|
|
544
|
-
is_identified = true;
|
|
545
|
-
_pendingIdentify = false;
|
|
546
|
-
if (_insightsMode === "direct" && deviceToken && sessionToken) {
|
|
547
|
-
setInStorage(storage_keys.device_token, deviceToken);
|
|
548
|
-
setInStorage(storage_keys.session_token, sessionToken);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
if (type === "SESSION_REQUEST" && is_identified) {
|
|
552
|
-
_channel?.postMessage(buildSessionSyncMessage());
|
|
553
|
-
}
|
|
554
|
-
};
|
|
555
|
-
} catch (e) {
|
|
556
|
-
}
|
|
557
|
-
};
|
|
558
|
-
const requestSessionFromOtherTabs = () => {
|
|
559
|
-
_channel?.postMessage({ type: "SESSION_REQUEST" });
|
|
560
|
-
};
|
|
561
|
-
const broadcastSession = () => {
|
|
562
|
-
_channel?.postMessage(buildSessionSyncMessage());
|
|
563
|
-
};
|
|
564
|
-
const flushEventQueue = () => {
|
|
565
|
-
const baseUrl = _insightsBaseUrl || core?.insightsUrl;
|
|
566
|
-
if (_eventQueue.length === 0 || !baseUrl || !core.workspace) {
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
const batch = _eventQueue.map((event) => ({
|
|
570
|
-
...event,
|
|
571
|
-
session_id: event.session_id || _session,
|
|
572
|
-
device_id: event.device_id || _device
|
|
573
|
-
}));
|
|
574
|
-
_eventQueue = [];
|
|
575
|
-
const url = `${baseUrl}/v1/insights/event/bulk?workspace=${core.workspace}`;
|
|
576
|
-
const headers = {
|
|
577
|
-
"Content-Type": "application/json",
|
|
578
|
-
...getAuthHeaders()
|
|
579
|
-
};
|
|
580
|
-
if (_insightsMode === "proxy" && navigator.sendBeacon) {
|
|
581
|
-
const success = navigator.sendBeacon(url, JSON.stringify(batch));
|
|
582
|
-
if (success) {
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
fetch(url, {
|
|
587
|
-
method: "POST",
|
|
588
|
-
body: JSON.stringify(batch),
|
|
589
|
-
headers,
|
|
590
|
-
keepalive: true,
|
|
591
|
-
credentials: _insightsMode === "proxy" ? "include" : "omit"
|
|
592
|
-
}).catch(() => {
|
|
593
|
-
});
|
|
594
|
-
};
|
|
595
|
-
const startFlushTimer = () => {
|
|
596
|
-
if (_flushTimer) {
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
_flushTimer = setInterval(() => {
|
|
600
|
-
flushEventQueue();
|
|
601
|
-
}, BATCH_INTERVAL_MS);
|
|
602
|
-
if (typeof document !== "undefined") {
|
|
603
|
-
document.addEventListener("visibilitychange", () => {
|
|
604
|
-
if (document.visibilityState === "hidden") {
|
|
605
|
-
flushEventQueue();
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
window.addEventListener("beforeunload", () => {
|
|
609
|
-
flushEventQueue();
|
|
610
|
-
});
|
|
611
|
-
window.addEventListener("pagehide", () => {
|
|
612
|
-
flushEventQueue();
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
};
|
|
616
|
-
const stopFlushTimer = () => {
|
|
617
|
-
if (_flushTimer) {
|
|
618
|
-
clearInterval(_flushTimer);
|
|
619
|
-
_flushTimer = null;
|
|
620
|
-
}
|
|
621
|
-
};
|
|
622
|
-
const queueEvent = (collectInput) => {
|
|
623
|
-
if (_eventCount >= MAX_EVENTS_PER_PAGE) {
|
|
624
|
-
console.warn(
|
|
625
|
-
`[@levo-so/core] Event limit (${MAX_EVENTS_PER_PAGE}) reached for this page`
|
|
626
|
-
);
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
_eventCount++;
|
|
630
|
-
_eventQueue.push(collectInput);
|
|
631
|
-
if (_eventQueue.length >= BATCH_SIZE) {
|
|
632
|
-
flushEventQueue();
|
|
633
|
-
}
|
|
634
|
-
};
|
|
635
|
-
const getReferrer = () => {
|
|
636
|
-
if (typeof document === "undefined") {
|
|
637
|
-
return {};
|
|
638
|
-
}
|
|
639
|
-
const {
|
|
640
|
-
type,
|
|
641
|
-
referrer,
|
|
642
|
-
data = {}
|
|
643
|
-
} = getVisitorSource({
|
|
644
|
-
referrer: document.referrer,
|
|
645
|
-
currentUrl: window.location.href,
|
|
646
|
-
mapper: null
|
|
647
|
-
});
|
|
648
|
-
const dataKeys = Object.keys(data);
|
|
649
|
-
for (const key of dataKeys) {
|
|
650
|
-
if (data[key] && Array.isArray(data[key]) && data[key].length > 0) {
|
|
651
|
-
data[key] = data[key]?.[0];
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
return { type, referrer, data, raw: document.referrer };
|
|
655
|
-
};
|
|
656
|
-
const getCurrentTabId = () => {
|
|
657
|
-
try {
|
|
658
|
-
if (typeof window === "undefined" || !("sessionStorage" in window)) {
|
|
659
|
-
return "";
|
|
660
|
-
}
|
|
661
|
-
const CURRENT_TAB_LOCAL_STORAGE_KEY = storage_keys.current_tab;
|
|
662
|
-
return window.sessionStorage.getItem(CURRENT_TAB_LOCAL_STORAGE_KEY) || "";
|
|
663
|
-
} catch (_) {
|
|
664
|
-
return "";
|
|
665
|
-
}
|
|
666
|
-
};
|
|
667
|
-
const getTabCount = () => {
|
|
668
|
-
try {
|
|
669
|
-
if (typeof window === "undefined" || !("localStorage" in window)) {
|
|
670
|
-
return 0;
|
|
671
|
-
}
|
|
672
|
-
const OT_LOCAL_STORAGE_KEY = storage_keys.open_tabs;
|
|
673
|
-
return Number(window.localStorage.getItem(OT_LOCAL_STORAGE_KEY)) || 0;
|
|
674
|
-
} catch (_) {
|
|
675
|
-
return 0;
|
|
676
|
-
}
|
|
677
|
-
};
|
|
678
|
-
const getActivityStatus = () => {
|
|
679
|
-
const status = idle_listener?.getStatus();
|
|
680
|
-
if (!status) {
|
|
681
|
-
return null;
|
|
682
|
-
}
|
|
683
|
-
return {
|
|
684
|
-
status: status.isIdle ? "idle" : "active",
|
|
685
|
-
seconds: status.isIdle ? status.idle : status.active
|
|
686
|
-
};
|
|
687
|
-
};
|
|
688
|
-
const identify = async (options) => {
|
|
689
|
-
if (!instance) {
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
const withLock = options?.withLock ?? true;
|
|
693
|
-
const referrer = getReferrer();
|
|
694
|
-
const traits = await getUserProperties();
|
|
695
|
-
await instance.identify("", {
|
|
696
|
-
...traits,
|
|
697
|
-
referrer,
|
|
698
|
-
withLock
|
|
699
|
-
});
|
|
700
|
-
};
|
|
701
|
-
const request = async (url, data) => {
|
|
702
|
-
const headers = {
|
|
703
|
-
Accept: "application/json",
|
|
704
|
-
"Content-Type": "application/json",
|
|
705
|
-
...getAuthHeaders()
|
|
706
|
-
};
|
|
707
|
-
try {
|
|
708
|
-
const response = await httpClient.instance.url(_insightsBaseUrl || core?.insightsUrl, true).url(url).options({
|
|
709
|
-
keepAlive: true,
|
|
710
|
-
credentials: _insightsMode === "proxy" ? "include" : "omit"
|
|
711
|
-
}).headers(headers).post(data).json();
|
|
712
|
-
return response;
|
|
713
|
-
} catch (error) {
|
|
714
|
-
console.warn(`Analytics request failed for ${url}:`, error);
|
|
715
|
-
return null;
|
|
716
|
-
}
|
|
717
|
-
};
|
|
718
|
-
const initiate = async (properties) => {
|
|
719
|
-
if (core.NODE_ENV === "development") {
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
_eventCount = 0;
|
|
723
|
-
_localStorageAvailable = checkLocalStorageAvailable();
|
|
724
|
-
_insightsMode = await detectInsightsMode();
|
|
725
|
-
initBroadcastChannel();
|
|
726
|
-
requestSessionFromOtherTabs();
|
|
727
|
-
startFlushTimer();
|
|
728
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
729
|
-
const plugin = {
|
|
730
|
-
name: core.workspace,
|
|
731
|
-
/**
|
|
732
|
-
* Handle track events (custom events like clicks, scrolls, etc.)
|
|
733
|
-
*
|
|
734
|
-
* Enriches the event with:
|
|
735
|
-
* - Session/device IDs
|
|
736
|
-
* - Page metadata (URL, title, etc.)
|
|
737
|
-
* - Tab information
|
|
738
|
-
* - Timestamp
|
|
739
|
-
*
|
|
740
|
-
* Then queues for batched sending.
|
|
741
|
-
*/
|
|
742
|
-
track: ({ payload }) => {
|
|
743
|
-
if (!core?.insightsUrl || !core.workspace) {
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
|
-
const payloadProperties = payload?.properties || {};
|
|
747
|
-
const collectInput = {
|
|
748
|
-
session_id: _session,
|
|
749
|
-
device_id: _device,
|
|
750
|
-
event: payload.event,
|
|
751
|
-
version: payload.version || 1,
|
|
752
|
-
workspace_id: core.workspace,
|
|
753
|
-
site_id: core.site || void 0,
|
|
754
|
-
properties: {
|
|
755
|
-
...properties,
|
|
756
|
-
...payloadProperties,
|
|
757
|
-
// Remove metadata fields from properties (they're top-level)
|
|
758
|
-
version: void 0,
|
|
759
|
-
resource: void 0,
|
|
760
|
-
identifier: void 0,
|
|
761
|
-
hostname: void 0,
|
|
762
|
-
pathname: void 0,
|
|
763
|
-
url: void 0,
|
|
764
|
-
page_title: void 0,
|
|
765
|
-
created_at: void 0
|
|
766
|
-
},
|
|
767
|
-
hostname: payload?.hostname || window.location.hostname,
|
|
768
|
-
pathname: payload?.pathname || window.location.pathname,
|
|
769
|
-
url: payload?.url || window.location.href,
|
|
770
|
-
page_title: payload?.page_title || document.title,
|
|
771
|
-
resource: payloadProperties?.resource || "page",
|
|
772
|
-
identifier: payloadProperties?.identifier || properties?.id,
|
|
773
|
-
created_at: payload?.created_at || (/* @__PURE__ */ new Date()).toISOString(),
|
|
774
|
-
tab_id: getCurrentTabId(),
|
|
775
|
-
tab_count: getTabCount()
|
|
776
|
-
};
|
|
777
|
-
queueEvent(collectInput);
|
|
778
|
-
},
|
|
779
|
-
/**
|
|
780
|
-
* Handle page view events.
|
|
781
|
-
*
|
|
782
|
-
* Called when navigating to a new page (in SPAs) or on initial load.
|
|
783
|
-
* Resets the rate limit counter and tracks a page.view event.
|
|
784
|
-
*/
|
|
785
|
-
page: ({ payload }) => {
|
|
786
|
-
if (!core?.insightsUrl) {
|
|
787
|
-
return;
|
|
788
|
-
}
|
|
789
|
-
_eventCount = 0;
|
|
790
|
-
const payloadProperties = payload?.properties || {};
|
|
791
|
-
track("page.view", {
|
|
792
|
-
...properties,
|
|
793
|
-
...payloadProperties,
|
|
794
|
-
title: document.title,
|
|
795
|
-
identifier: properties.id,
|
|
796
|
-
resource: "page"
|
|
797
|
-
});
|
|
798
|
-
},
|
|
799
|
-
/**
|
|
800
|
-
* Handle identify events (session establishment).
|
|
801
|
-
*
|
|
802
|
-
* This is called when identify() is invoked. It:
|
|
803
|
-
* 1. Checks if already identified (via BroadcastChannel) - if so, skip
|
|
804
|
-
* 2. Calls /welcome API to get session/device IDs
|
|
805
|
-
* 3. On success, broadcasts session to other tabs
|
|
806
|
-
*
|
|
807
|
-
* The /welcome API uses HttpOnly cookies to maintain persistent
|
|
808
|
-
* device identity across sessions.
|
|
809
|
-
*/
|
|
810
|
-
identify: ({ payload }) => {
|
|
811
|
-
if (!core?.insightsUrl || !core.workspace) {
|
|
812
|
-
return;
|
|
813
|
-
}
|
|
814
|
-
if (is_identified) {
|
|
815
|
-
return;
|
|
816
|
-
}
|
|
817
|
-
if (_pendingIdentify) {
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
_pendingIdentify = true;
|
|
821
|
-
const welcomeInput = {
|
|
822
|
-
private_mode: payload.traits.private_mode,
|
|
823
|
-
dark_mode: payload.traits.dark_mode,
|
|
824
|
-
timezone: payload.traits.timezone,
|
|
825
|
-
locale: payload.traits.locale,
|
|
826
|
-
referrer: payload.traits.referrer,
|
|
827
|
-
properties: payload.traits.properties,
|
|
828
|
-
workspace_id: core.workspace,
|
|
829
|
-
site_id: core.site || void 0,
|
|
830
|
-
hostname: window.location.hostname,
|
|
831
|
-
pathname: window.location.pathname,
|
|
832
|
-
page_title: document.title,
|
|
833
|
-
url: window.location.href,
|
|
834
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
835
|
-
tab_id: getCurrentTabId(),
|
|
836
|
-
tab_count: getTabCount()
|
|
837
|
-
};
|
|
838
|
-
request(`/v1/insights/event/welcome`, welcomeInput).then((data) => {
|
|
839
|
-
if (is_identified) {
|
|
840
|
-
_pendingIdentify = false;
|
|
841
|
-
return;
|
|
842
|
-
}
|
|
843
|
-
is_identified = true;
|
|
844
|
-
_pendingIdentify = false;
|
|
845
|
-
if (data?.content?.data?.session) {
|
|
846
|
-
_session = data?.content.data.session;
|
|
847
|
-
}
|
|
848
|
-
if (data?.content?.data?.device) {
|
|
849
|
-
_device = data?.content.data.device;
|
|
850
|
-
}
|
|
851
|
-
if (_insightsMode === "direct" && data?.content?.meta) {
|
|
852
|
-
const meta = data.content.meta;
|
|
853
|
-
if (meta.device_token) {
|
|
854
|
-
setInStorage(storage_keys.device_token, meta.device_token);
|
|
855
|
-
}
|
|
856
|
-
if (meta.session_token) {
|
|
857
|
-
setInStorage(storage_keys.session_token, meta.session_token);
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
broadcastSession();
|
|
861
|
-
}).catch((e) => {
|
|
862
|
-
_pendingIdentify = false;
|
|
863
|
-
return getLevoError(e);
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
};
|
|
867
|
-
instance = Analytics({
|
|
868
|
-
app: core.workspace || "",
|
|
869
|
-
plugins: [plugin]
|
|
870
|
-
});
|
|
871
|
-
await identify();
|
|
872
|
-
startActivityListener();
|
|
873
|
-
return instance.page({
|
|
874
|
-
resource: "page",
|
|
875
|
-
identifier: properties.id
|
|
876
|
-
});
|
|
877
|
-
};
|
|
878
|
-
const startActivityListener = () => {
|
|
879
|
-
if (typeof document !== "undefined" && !idle_listener) {
|
|
880
|
-
idle_listener = onUserActivity({
|
|
881
|
-
timeout: _IDLE_TIMEOUT,
|
|
882
|
-
throttle: _IDLE_THROTTLE,
|
|
883
|
-
onIdle: (activeForSeconds) => {
|
|
884
|
-
track("user.idle", {
|
|
885
|
-
seconds_active: activeForSeconds
|
|
886
|
-
});
|
|
887
|
-
},
|
|
888
|
-
onWakeUp: (idleForSeconds) => {
|
|
889
|
-
track("user.active", {
|
|
890
|
-
seconds_idle: idleForSeconds
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
});
|
|
894
|
-
}
|
|
895
|
-
return null;
|
|
896
|
-
};
|
|
897
|
-
const bounce = async (properties) => {
|
|
898
|
-
const activity_status = getActivityStatus();
|
|
899
|
-
if (activity_status) {
|
|
900
|
-
if (activity_status?.status === "idle") {
|
|
901
|
-
properties.seconds_idle = activity_status.seconds;
|
|
902
|
-
} else {
|
|
903
|
-
properties.seconds_active = activity_status.seconds;
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
track("page.bounce", properties);
|
|
907
|
-
};
|
|
908
|
-
const track = async (event, properties) => {
|
|
909
|
-
if (!core.workspace) {
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
if (instance) {
|
|
913
|
-
return instance.track(event, properties);
|
|
914
|
-
}
|
|
915
|
-
const collectInput = {
|
|
916
|
-
event,
|
|
917
|
-
version: properties?.version || 1,
|
|
918
|
-
properties: {
|
|
919
|
-
...properties,
|
|
920
|
-
version: void 0,
|
|
921
|
-
resource: void 0,
|
|
922
|
-
identifier: void 0,
|
|
923
|
-
hostname: void 0,
|
|
924
|
-
pathname: void 0,
|
|
925
|
-
url: void 0,
|
|
926
|
-
page_title: void 0,
|
|
927
|
-
created_at: void 0
|
|
928
|
-
},
|
|
929
|
-
hostname: window.location.hostname,
|
|
930
|
-
pathname: window.location.pathname,
|
|
931
|
-
url: window.location.href,
|
|
932
|
-
page_title: document.title,
|
|
933
|
-
resource: properties?.resource || "page",
|
|
934
|
-
identifier: properties?.identifier || properties?.id,
|
|
935
|
-
workspace_id: core.workspace,
|
|
936
|
-
site_id: core.site || void 0,
|
|
937
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
938
|
-
tab_id: getCurrentTabId(),
|
|
939
|
-
tab_count: getTabCount()
|
|
940
|
-
};
|
|
941
|
-
queueEvent(collectInput);
|
|
942
|
-
};
|
|
943
|
-
const destroy = () => {
|
|
944
|
-
flushEventQueue();
|
|
945
|
-
stopFlushTimer();
|
|
946
|
-
_channel?.close();
|
|
947
|
-
};
|
|
948
|
-
return {
|
|
949
|
-
/** Get the underlying analytics instance (read-only) */
|
|
950
|
-
get instance() {
|
|
951
|
-
return instance;
|
|
952
|
-
},
|
|
953
|
-
/** Check if session has been established (read-only) */
|
|
954
|
-
get is_identified() {
|
|
955
|
-
return is_identified;
|
|
956
|
-
},
|
|
957
|
-
/** Storage keys used by the module */
|
|
958
|
-
storage_keys,
|
|
959
|
-
/** Initialize the module for a page */
|
|
960
|
-
initiate,
|
|
961
|
-
/** Identify/establish session */
|
|
962
|
-
identify,
|
|
963
|
-
/** Track a custom event */
|
|
964
|
-
track,
|
|
965
|
-
/** Track a bounce event */
|
|
966
|
-
bounce,
|
|
967
|
-
/** Cleanup the module */
|
|
968
|
-
destroy
|
|
969
|
-
};
|
|
970
|
-
};
|
|
3
|
+
import { WretchError } from 'wretch/resolver';
|
|
971
4
|
|
|
972
5
|
// src/control/index.ts
|
|
973
6
|
var workspaceIdRegex = /^W[A-Z0-9]{7}$/;
|
|
@@ -975,16 +8,10 @@ var createLevoControl = (options) => {
|
|
|
975
8
|
const {
|
|
976
9
|
appName,
|
|
977
10
|
workspace = null,
|
|
978
|
-
site = null,
|
|
979
11
|
apiUrl = "https://public-api.levo.so",
|
|
980
|
-
insightsUrl = "/.levo/insights/api",
|
|
981
|
-
directInsightsUrl,
|
|
982
|
-
insightsMode = "auto",
|
|
983
|
-
pingTimeout = 3e3,
|
|
984
12
|
NODE_ENV = "",
|
|
985
13
|
APP_MODE = "production"
|
|
986
14
|
} = options;
|
|
987
|
-
const sanitizedPingTimeout = Number.isFinite(pingTimeout) && pingTimeout >= 0 ? pingTimeout : 3e3;
|
|
988
15
|
if (workspace) {
|
|
989
16
|
if (!workspaceIdRegex.test(workspace)) {
|
|
990
17
|
throw new Error(
|
|
@@ -993,7 +20,6 @@ var createLevoControl = (options) => {
|
|
|
993
20
|
}
|
|
994
21
|
}
|
|
995
22
|
let _workspace = workspace;
|
|
996
|
-
let _site = site;
|
|
997
23
|
return {
|
|
998
24
|
get workspace() {
|
|
999
25
|
return _workspace;
|
|
@@ -1008,30 +34,12 @@ var createLevoControl = (options) => {
|
|
|
1008
34
|
}
|
|
1009
35
|
_workspace = value;
|
|
1010
36
|
},
|
|
1011
|
-
get site() {
|
|
1012
|
-
return _site;
|
|
1013
|
-
},
|
|
1014
|
-
set site(value) {
|
|
1015
|
-
_site = value;
|
|
1016
|
-
},
|
|
1017
37
|
get appName() {
|
|
1018
38
|
return appName;
|
|
1019
39
|
},
|
|
1020
40
|
get apiUrl() {
|
|
1021
41
|
return apiUrl;
|
|
1022
42
|
},
|
|
1023
|
-
get insightsUrl() {
|
|
1024
|
-
return insightsUrl;
|
|
1025
|
-
},
|
|
1026
|
-
get directInsightsUrl() {
|
|
1027
|
-
return directInsightsUrl || "";
|
|
1028
|
-
},
|
|
1029
|
-
get insightsMode() {
|
|
1030
|
-
return insightsMode;
|
|
1031
|
-
},
|
|
1032
|
-
get pingTimeout() {
|
|
1033
|
-
return sanitizedPingTimeout;
|
|
1034
|
-
},
|
|
1035
43
|
get NODE_ENV() {
|
|
1036
44
|
return NODE_ENV;
|
|
1037
45
|
},
|
|
@@ -1256,8 +264,96 @@ var createLevoLogger = (core, options = {}) => {
|
|
|
1256
264
|
};
|
|
1257
265
|
};
|
|
1258
266
|
|
|
267
|
+
// src/utils/LevoError.ts
|
|
268
|
+
var LevoError = class extends Error {
|
|
269
|
+
code;
|
|
270
|
+
title;
|
|
271
|
+
status;
|
|
272
|
+
type;
|
|
273
|
+
errors;
|
|
274
|
+
constructor(data) {
|
|
275
|
+
super(data.description);
|
|
276
|
+
this.name = "Levo Request Error";
|
|
277
|
+
this.status = data.status;
|
|
278
|
+
this.code = data.code;
|
|
279
|
+
this.title = data.title;
|
|
280
|
+
this.type = data.type;
|
|
281
|
+
this.errors = data.errors;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get validation errors for form fields
|
|
285
|
+
*/
|
|
286
|
+
get fieldErrors() {
|
|
287
|
+
return this.errors || [];
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Check if this is a validation error with field-specific errors
|
|
291
|
+
*/
|
|
292
|
+
get hasFieldErrors() {
|
|
293
|
+
return Boolean(this.errors?.length);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// src/utils/getLevoError.ts
|
|
298
|
+
var getLevoError = (error) => {
|
|
299
|
+
if (error instanceof LevoError) {
|
|
300
|
+
return error;
|
|
301
|
+
}
|
|
302
|
+
if (error instanceof WretchError && error?.json) {
|
|
303
|
+
const errorResponse = error?.json;
|
|
304
|
+
return new LevoError({
|
|
305
|
+
status: errorResponse?.content?.status || 500,
|
|
306
|
+
code: errorResponse?.content?.code || `Unknown`,
|
|
307
|
+
title: errorResponse?.content?.title || `HTTP ${error?.status || 500} Error`,
|
|
308
|
+
description: errorResponse?.content?.description || `Request failed with status ${error?.status || 500}`,
|
|
309
|
+
type: errorResponse?.content?.type,
|
|
310
|
+
errors: errorResponse?.content?.errors
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
if (error instanceof TypeError) {
|
|
314
|
+
if (error?.message.includes("Failed to fetch") || error?.message.includes("Network request failed") || error?.message.includes("fetch")) {
|
|
315
|
+
return new LevoError({
|
|
316
|
+
status: 0,
|
|
317
|
+
code: "NETWORK_ERROR",
|
|
318
|
+
title: "Network Error",
|
|
319
|
+
description: "Unable to connect to the server. Please check your internet connection."
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
if (error?.message.includes("CORS") || error?.message.includes("cross-origin")) {
|
|
323
|
+
return new LevoError({
|
|
324
|
+
status: 0,
|
|
325
|
+
code: "CORS_ERROR",
|
|
326
|
+
title: "CORS Error",
|
|
327
|
+
description: "Cross-origin request blocked. Please contact support."
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (error?.name === "AbortError" || error instanceof DOMException) {
|
|
332
|
+
return new LevoError({
|
|
333
|
+
status: 408,
|
|
334
|
+
code: "REQUEST_TIMEOUT",
|
|
335
|
+
title: "Request Timeout",
|
|
336
|
+
description: "The request was cancelled or timed out. Please try again."
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
if (error?.request) {
|
|
340
|
+
return new LevoError({
|
|
341
|
+
status: error?.response?.status || 500,
|
|
342
|
+
code: error?.code || "REQUEST_ERROR",
|
|
343
|
+
title: "Request Error",
|
|
344
|
+
description: error?.message || "An error occurred while making the request"
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
return new LevoError({
|
|
348
|
+
status: 500,
|
|
349
|
+
code: "UNKNOWN_ERROR",
|
|
350
|
+
title: "Unexpected Error",
|
|
351
|
+
description: error?.message || "Something unexpected happened"
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
|
|
1259
355
|
// src/modules/blog.ts
|
|
1260
|
-
var createLevoBlogModule = (core, httpClient
|
|
356
|
+
var createLevoBlogModule = (core, httpClient) => {
|
|
1261
357
|
const getAllBlogs = async (blog_key, data = {}, options) => {
|
|
1262
358
|
return httpClient.instance.url(`/v1/blog/${blog_key}/post/query`).query({ ...options?.params || {} }).options({
|
|
1263
359
|
headers: {
|
|
@@ -1283,7 +379,7 @@ var createLevoBlogModule = (core, httpClient, audience = null) => {
|
|
|
1283
379
|
};
|
|
1284
380
|
|
|
1285
381
|
// src/modules/collection.ts
|
|
1286
|
-
var createLevoCollectionModule = (core, httpsClient
|
|
382
|
+
var createLevoCollectionModule = (core, httpsClient) => {
|
|
1287
383
|
const getAllCollections = async (data = {}, options = {}) => {
|
|
1288
384
|
return httpsClient.instance.url("/v1/bevy/collection/query").options(options).post(data).json().then((response) => response).catch((error) => {
|
|
1289
385
|
throw getLevoError(error);
|
|
@@ -1339,6 +435,11 @@ var createLevoCollectionModule = (core, httpsClient, audience = null) => {
|
|
|
1339
435
|
throw getLevoError(error);
|
|
1340
436
|
});
|
|
1341
437
|
};
|
|
438
|
+
const getFilters = async (collectionId, options = {}) => {
|
|
439
|
+
return httpsClient.instance.url(`/v1/bevy/collection/${collectionId}/get-filters`).options(options).get().json().then((response) => response).catch((error) => {
|
|
440
|
+
throw getLevoError(error);
|
|
441
|
+
});
|
|
442
|
+
};
|
|
1342
443
|
const submitDraftEntry = async (collection_key, data) => {
|
|
1343
444
|
return httpsClient.instance.url(`/v1/bevy/content/${collection_key}/submit-entry`).post(data).json().then((response) => response).catch((error) => {
|
|
1344
445
|
throw getLevoError(error);
|
|
@@ -1353,12 +454,13 @@ var createLevoCollectionModule = (core, httpsClient, audience = null) => {
|
|
|
1353
454
|
saveDraftEntry,
|
|
1354
455
|
editDraftEntry,
|
|
1355
456
|
getDraftEntry,
|
|
1356
|
-
submitDraftEntry
|
|
457
|
+
submitDraftEntry,
|
|
458
|
+
getFilters
|
|
1357
459
|
};
|
|
1358
460
|
};
|
|
1359
461
|
|
|
1360
462
|
// src/modules/media.ts
|
|
1361
|
-
var createLevoMediaModule = (core, httpsClient
|
|
463
|
+
var createLevoMediaModule = (core, httpsClient) => {
|
|
1362
464
|
const mediaBulkUpload = async (formData, options = {}) => {
|
|
1363
465
|
return httpsClient.instance.url("/v1/assets/media/bulk").options(options).post(formData).json().then((response) => response).catch((error) => {
|
|
1364
466
|
throw getLevoError(error);
|
|
@@ -1376,14 +478,10 @@ var createLevoMediaModule = (core, httpsClient, audience = null) => {
|
|
|
1376
478
|
};
|
|
1377
479
|
|
|
1378
480
|
// src/constants/oauthProvider.ts
|
|
1379
|
-
var LevoOAuthProviderList = [
|
|
1380
|
-
"google",
|
|
1381
|
-
"linkedin",
|
|
1382
|
-
"microsoft"
|
|
1383
|
-
];
|
|
481
|
+
var LevoOAuthProviderList = ["google", "linkedin", "microsoft"];
|
|
1384
482
|
|
|
1385
483
|
// src/modules/membership.ts
|
|
1386
|
-
var createLevoMembershipModule = (core, httpsClient
|
|
484
|
+
var createLevoMembershipModule = (core, httpsClient) => {
|
|
1387
485
|
const getOAuthURL = async (options = {}) => {
|
|
1388
486
|
return httpsClient.instance.url("/v1/membership/auth/oauth/get-redirect-url").options(options).get().json().then((response) => {
|
|
1389
487
|
const result = response?.content?.data;
|
|
@@ -1399,10 +497,7 @@ var createLevoMembershipModule = (core, httpsClient, audience = null) => {
|
|
|
1399
497
|
url_params.set("workspace_id", core.workspace);
|
|
1400
498
|
}
|
|
1401
499
|
if (httpsClient.pageContext) {
|
|
1402
|
-
url_params.set(
|
|
1403
|
-
"page_context",
|
|
1404
|
-
JSON.stringify(httpsClient.pageContext)
|
|
1405
|
-
);
|
|
500
|
+
url_params.set("page_context", JSON.stringify(httpsClient.pageContext));
|
|
1406
501
|
}
|
|
1407
502
|
result[provider] = url.toString();
|
|
1408
503
|
}
|
|
@@ -1498,11 +593,10 @@ var createLevoClient = (options) => {
|
|
|
1498
593
|
let _pageContext = pageContext;
|
|
1499
594
|
const core = createLevoControl(coreOptions);
|
|
1500
595
|
const httpsClient = createHttpClient(core, _pageContext);
|
|
1501
|
-
const
|
|
1502
|
-
const
|
|
1503
|
-
const
|
|
1504
|
-
const
|
|
1505
|
-
const media = createLevoMediaModule(core, httpsClient, audience);
|
|
596
|
+
const blog = createLevoBlogModule(core, httpsClient);
|
|
597
|
+
const membership = createLevoMembershipModule(core, httpsClient);
|
|
598
|
+
const collection = createLevoCollectionModule(core, httpsClient);
|
|
599
|
+
const media = createLevoMediaModule(core, httpsClient);
|
|
1506
600
|
const logger = createLevoLogger(core, loggerOptions);
|
|
1507
601
|
const updatePageContext = (newContext) => {
|
|
1508
602
|
_pageContext = Object.assign({}, _pageContext, newContext);
|
|
@@ -1514,15 +608,9 @@ var createLevoClient = (options) => {
|
|
|
1514
608
|
get apiUrl() {
|
|
1515
609
|
return core.apiUrl;
|
|
1516
610
|
},
|
|
1517
|
-
get insightsUrl() {
|
|
1518
|
-
return core.insightsUrl;
|
|
1519
|
-
},
|
|
1520
611
|
get workspace() {
|
|
1521
612
|
return core.workspace;
|
|
1522
613
|
},
|
|
1523
|
-
get audience() {
|
|
1524
|
-
return audience;
|
|
1525
|
-
},
|
|
1526
614
|
get blog() {
|
|
1527
615
|
return blog;
|
|
1528
616
|
},
|
|
@@ -1544,6 +632,9 @@ var createLevoClient = (options) => {
|
|
|
1544
632
|
get APP_MODE() {
|
|
1545
633
|
return core.APP_MODE;
|
|
1546
634
|
},
|
|
635
|
+
get NODE_ENV() {
|
|
636
|
+
return core.NODE_ENV;
|
|
637
|
+
},
|
|
1547
638
|
get pageContext() {
|
|
1548
639
|
return _pageContext;
|
|
1549
640
|
},
|
|
@@ -1552,67 +643,36 @@ var createLevoClient = (options) => {
|
|
|
1552
643
|
};
|
|
1553
644
|
};
|
|
1554
645
|
|
|
1555
|
-
// src/
|
|
1556
|
-
var
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
"
|
|
1579
|
-
|
|
1580
|
-
"
|
|
1581
|
-
"
|
|
1582
|
-
|
|
1583
|
-
"
|
|
1584
|
-
"
|
|
1585
|
-
"membership.account.signup",
|
|
1586
|
-
// UI Events
|
|
1587
|
-
"button.click",
|
|
1588
|
-
"page.bounce",
|
|
1589
|
-
"page.click",
|
|
1590
|
-
"page.copy",
|
|
1591
|
-
"page.impression",
|
|
1592
|
-
"page.scroll",
|
|
1593
|
-
"page.view",
|
|
1594
|
-
"page.selection",
|
|
1595
|
-
// User Activity Events
|
|
1596
|
-
"user.active",
|
|
1597
|
-
"user.idle"
|
|
1598
|
-
];
|
|
1599
|
-
var AnalyticsEvents = listToRecord(AnalyticsEventsList);
|
|
1600
|
-
|
|
1601
|
-
// src/types/forum/post.ts
|
|
1602
|
-
var FORUM_POST_KIND = {
|
|
1603
|
-
post: "post",
|
|
1604
|
-
poll: "poll",
|
|
1605
|
-
gallery: "gallery",
|
|
1606
|
-
link: "link"
|
|
1607
|
-
};
|
|
1608
|
-
var ForumPostKindExpandedList = Object.keys(FORUM_POST_KIND);
|
|
1609
|
-
|
|
1610
|
-
// src/types/forum/comment.ts
|
|
1611
|
-
var COMMENT_STATUS = {
|
|
1612
|
-
active: "active",
|
|
1613
|
-
edited: "edited",
|
|
1614
|
-
deleted: "deleted",
|
|
1615
|
-
pending_moderation: "pending_moderation"
|
|
646
|
+
// src/constants/collection/defaultByKind.ts
|
|
647
|
+
var defaultByKinds = {
|
|
648
|
+
"public-id": "",
|
|
649
|
+
string: "",
|
|
650
|
+
number: null,
|
|
651
|
+
boolean: false,
|
|
652
|
+
location: null,
|
|
653
|
+
file: null,
|
|
654
|
+
date: null,
|
|
655
|
+
identifier: null,
|
|
656
|
+
record: null,
|
|
657
|
+
group: [],
|
|
658
|
+
collection: {
|
|
659
|
+
m2o: null,
|
|
660
|
+
o2o: null,
|
|
661
|
+
m2m: []
|
|
662
|
+
},
|
|
663
|
+
richtext: {
|
|
664
|
+
html: "",
|
|
665
|
+
text: "",
|
|
666
|
+
json: []
|
|
667
|
+
},
|
|
668
|
+
json: {},
|
|
669
|
+
"array-string": [],
|
|
670
|
+
"array-number": [],
|
|
671
|
+
"array-file": [],
|
|
672
|
+
"array-json": [],
|
|
673
|
+
"array-date": [],
|
|
674
|
+
"array-boolean": [],
|
|
675
|
+
"array-location": []
|
|
1616
676
|
};
|
|
1617
677
|
|
|
1618
678
|
// src/constants/collection/fieldInterfaceFormat.ts
|
|
@@ -1632,6 +692,7 @@ var formatsByInterface = {
|
|
|
1632
692
|
PhoneWidget: ["phone"],
|
|
1633
693
|
URLWidget: ["url"],
|
|
1634
694
|
DateWidget: [],
|
|
695
|
+
TimeWidget: [],
|
|
1635
696
|
SwitchWidget: [],
|
|
1636
697
|
RichTextWidget: [],
|
|
1637
698
|
DateTimeWidget: [],
|
|
@@ -1647,37 +708,58 @@ var formatsByInterface = {
|
|
|
1647
708
|
SlugWidget: []
|
|
1648
709
|
};
|
|
1649
710
|
|
|
1650
|
-
// src/constants/collection/
|
|
1651
|
-
var
|
|
1652
|
-
"
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
711
|
+
// src/constants/collection/fieldInterfaces.ts
|
|
712
|
+
var CommonFieldInterfacesList = [
|
|
713
|
+
"TextWidget",
|
|
714
|
+
"CurrencyWidget",
|
|
715
|
+
"TextareaWidget",
|
|
716
|
+
"GeocoderWidget",
|
|
717
|
+
"MultiGeocoderWidget",
|
|
718
|
+
"DropdownWidget",
|
|
719
|
+
"ToggleCheckboxWidget",
|
|
720
|
+
"RadioWidget",
|
|
721
|
+
"NumberWidget",
|
|
722
|
+
"EmailWidget",
|
|
723
|
+
"PhoneWidget",
|
|
724
|
+
"URLWidget",
|
|
725
|
+
"DateWidget",
|
|
726
|
+
"TimeWidget",
|
|
727
|
+
"SwitchWidget",
|
|
728
|
+
"RichTextWidget",
|
|
729
|
+
"DateTimeWidget",
|
|
730
|
+
"CheckboxWidget",
|
|
731
|
+
"ImageUploadWidget",
|
|
732
|
+
"MultiImageUploadWidget",
|
|
733
|
+
"FileUploadWidget",
|
|
734
|
+
"MultiFileUploadWidget",
|
|
735
|
+
"MultiDropdownWidget",
|
|
736
|
+
"MultiTextWidget"
|
|
737
|
+
];
|
|
738
|
+
var ComplexFieldInterfacesList = [
|
|
739
|
+
"CollectionWidget",
|
|
740
|
+
"ArrayWidget",
|
|
741
|
+
"RecordWidget",
|
|
742
|
+
"JSONWidget",
|
|
743
|
+
"SlugWidget"
|
|
744
|
+
];
|
|
745
|
+
var FieldInterfacesList = [
|
|
746
|
+
...CommonFieldInterfacesList,
|
|
747
|
+
...ComplexFieldInterfacesList
|
|
748
|
+
];
|
|
749
|
+
var CommonFieldIntefaces = CommonFieldInterfacesList.reduce(
|
|
750
|
+
(acc, curr) => {
|
|
751
|
+
acc[curr] = curr;
|
|
752
|
+
return acc;
|
|
1666
753
|
},
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
754
|
+
{}
|
|
755
|
+
);
|
|
756
|
+
var FieldInterfaces = FieldInterfacesList.reduce(
|
|
757
|
+
(acc, curr) => {
|
|
758
|
+
acc[curr] = curr;
|
|
759
|
+
return acc;
|
|
1671
760
|
},
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
"array-number": [],
|
|
1675
|
-
"array-file": [],
|
|
1676
|
-
"array-json": [],
|
|
1677
|
-
"array-date": [],
|
|
1678
|
-
"array-boolean": [],
|
|
1679
|
-
"array-location": []
|
|
1680
|
-
};
|
|
761
|
+
{}
|
|
762
|
+
);
|
|
1681
763
|
|
|
1682
764
|
// src/constants/collection/fieldKind.ts
|
|
1683
765
|
var FieldKindList = [
|
|
@@ -1737,6 +819,7 @@ var interfaceKinds = {
|
|
|
1737
819
|
SlugWidget: FieldKind.string,
|
|
1738
820
|
RichTextWidget: FieldKind.string,
|
|
1739
821
|
DateWidget: FieldKind.date,
|
|
822
|
+
TimeWidget: FieldKind.string,
|
|
1740
823
|
DateTimeWidget: FieldKind.date,
|
|
1741
824
|
RadioWidget: FieldKind.string,
|
|
1742
825
|
CheckboxWidget: FieldKind["array-string"],
|
|
@@ -1755,57 +838,17 @@ var interfaceKinds = {
|
|
|
1755
838
|
JSONWidget: FieldKind.json
|
|
1756
839
|
};
|
|
1757
840
|
|
|
1758
|
-
// src/constants/
|
|
1759
|
-
var
|
|
1760
|
-
"
|
|
1761
|
-
"
|
|
1762
|
-
"
|
|
1763
|
-
"
|
|
1764
|
-
"
|
|
1765
|
-
"
|
|
1766
|
-
"
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
"EmailWidget",
|
|
1770
|
-
"PhoneWidget",
|
|
1771
|
-
"URLWidget",
|
|
1772
|
-
"DateWidget",
|
|
1773
|
-
"SwitchWidget",
|
|
1774
|
-
"RichTextWidget",
|
|
1775
|
-
"DateTimeWidget",
|
|
1776
|
-
"CheckboxWidget",
|
|
1777
|
-
"ImageUploadWidget",
|
|
1778
|
-
"MultiImageUploadWidget",
|
|
1779
|
-
"FileUploadWidget",
|
|
1780
|
-
"MultiFileUploadWidget",
|
|
1781
|
-
"MultiDropdownWidget",
|
|
1782
|
-
"MultiTextWidget"
|
|
1783
|
-
];
|
|
1784
|
-
var ComplexFieldInterfacesList = [
|
|
1785
|
-
"CollectionWidget",
|
|
1786
|
-
"ArrayWidget",
|
|
1787
|
-
"RecordWidget",
|
|
1788
|
-
"JSONWidget",
|
|
1789
|
-
"SlugWidget"
|
|
1790
|
-
];
|
|
1791
|
-
var FieldInterfacesList = [
|
|
1792
|
-
...CommonFieldInterfacesList,
|
|
1793
|
-
...ComplexFieldInterfacesList
|
|
1794
|
-
];
|
|
1795
|
-
var CommonFieldIntefaces = CommonFieldInterfacesList.reduce(
|
|
1796
|
-
(acc, curr) => {
|
|
1797
|
-
acc[curr] = curr;
|
|
1798
|
-
return acc;
|
|
1799
|
-
},
|
|
1800
|
-
{}
|
|
1801
|
-
);
|
|
1802
|
-
var FieldInterfaces = FieldInterfacesList.reduce(
|
|
1803
|
-
(acc, curr) => {
|
|
1804
|
-
acc[curr] = curr;
|
|
1805
|
-
return acc;
|
|
1806
|
-
},
|
|
1807
|
-
{}
|
|
1808
|
-
);
|
|
841
|
+
// src/constants/integration.ts
|
|
842
|
+
var LEVO_INTEGRATIONS = {
|
|
843
|
+
beacon: "so.levo.beacon",
|
|
844
|
+
blog: "so.levo.blog",
|
|
845
|
+
event: "so.levo.event",
|
|
846
|
+
membership: "so.levo.membership",
|
|
847
|
+
payment: "so.levo.payment",
|
|
848
|
+
community: "so.levo.community",
|
|
849
|
+
search_console: "com.google.search_console"
|
|
850
|
+
};
|
|
851
|
+
var LEVO_INTEGRATIONS_LIST = Object.values(LEVO_INTEGRATIONS);
|
|
1809
852
|
|
|
1810
853
|
// src/constants/media.ts
|
|
1811
854
|
var MediaKindList = ["image", "video", "audio", "document"];
|
|
@@ -1817,17 +860,31 @@ var MediaKind = MediaKindList.reduce(
|
|
|
1817
860
|
{}
|
|
1818
861
|
);
|
|
1819
862
|
|
|
1820
|
-
// src/
|
|
1821
|
-
var
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
863
|
+
// src/types/forum/comment.ts
|
|
864
|
+
var COMMENT_STATUS = {
|
|
865
|
+
published: "published",
|
|
866
|
+
unpublished: "unpublished",
|
|
867
|
+
archived: "archived",
|
|
868
|
+
flagged: "flagged",
|
|
869
|
+
pending_moderation: "pending_moderation"
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
// src/types/forum/post.ts
|
|
873
|
+
var FORUM_POST_KIND = {
|
|
874
|
+
post: "post",
|
|
875
|
+
poll: "poll",
|
|
876
|
+
gallery: "gallery",
|
|
877
|
+
link: "link"
|
|
878
|
+
};
|
|
879
|
+
var ForumPostKindExpandedList = Object.keys(FORUM_POST_KIND);
|
|
880
|
+
var FORUM_POST_STATUS = {
|
|
881
|
+
draft: "draft",
|
|
882
|
+
published: "published",
|
|
883
|
+
unpublished: "unpublished",
|
|
884
|
+
archived: "archived",
|
|
885
|
+
flagged: "flagged",
|
|
886
|
+
pending_moderation: "pending_moderation"
|
|
1829
887
|
};
|
|
1830
|
-
var LEVO_INTEGRATIONS_LIST = Object.values(LEVO_INTEGRATIONS);
|
|
1831
888
|
|
|
1832
889
|
// src/utils/formatImagePath.ts
|
|
1833
890
|
var formatImagePath = (string) => {
|
|
@@ -1840,40 +897,18 @@ var formatImagePath = (string) => {
|
|
|
1840
897
|
return "";
|
|
1841
898
|
};
|
|
1842
899
|
|
|
1843
|
-
// src/utils/
|
|
1844
|
-
var
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
return false;
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
|
-
localStorage.setItem(this.key, now.toString());
|
|
1859
|
-
setTimeout(() => {
|
|
1860
|
-
if (this.isLocked(expiryMs)) {
|
|
1861
|
-
this.releaseLock();
|
|
1862
|
-
}
|
|
1863
|
-
}, expiryMs);
|
|
1864
|
-
return true;
|
|
1865
|
-
}
|
|
1866
|
-
releaseLock() {
|
|
1867
|
-
localStorage.removeItem(this.key);
|
|
1868
|
-
}
|
|
1869
|
-
isLocked(expiryMs = 5e3) {
|
|
1870
|
-
const lockValue = localStorage.getItem(this.key);
|
|
1871
|
-
if (!lockValue) {
|
|
1872
|
-
return false;
|
|
1873
|
-
}
|
|
1874
|
-
const timestamp = Number(lockValue);
|
|
1875
|
-
return Date.now() - timestamp < expiryMs;
|
|
1876
|
-
}
|
|
900
|
+
// src/utils/listToX.ts
|
|
901
|
+
var listToColumn = (columns) => {
|
|
902
|
+
return columns.reduce((prev, current) => {
|
|
903
|
+
prev[current] = current;
|
|
904
|
+
return prev;
|
|
905
|
+
}, {});
|
|
906
|
+
};
|
|
907
|
+
var listToRecord = (columns) => {
|
|
908
|
+
return columns.reduce((prev, current) => {
|
|
909
|
+
prev[current] = current;
|
|
910
|
+
return prev;
|
|
911
|
+
}, {});
|
|
1877
912
|
};
|
|
1878
913
|
|
|
1879
|
-
export {
|
|
914
|
+
export { COMMENT_STATUS, CommonFieldIntefaces, CommonFieldInterfacesList, ComplexFieldInterfacesList, FORUM_POST_KIND, FORUM_POST_STATUS, FieldInterfaces, FieldInterfacesList, FieldKind, FieldKindList, ForumPostKindExpandedList, LEVO_INTEGRATIONS, LEVO_INTEGRATIONS_LIST, LevoError, LevoOAuthProviderList, MediaKind, MediaKindList, createLevoClient, defaultByKinds, formatImagePath, formatsByInterface, getLevoError, interfaceKinds, listToColumn, listToRecord };
|