@altertable/altertable-js 0.3.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/dist/index.d.mts +52 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.global.js +150 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +173 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +151 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +54 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
interface Config {
|
|
2
|
+
/**
|
|
3
|
+
* The base URL of the Altertable API.
|
|
4
|
+
* @default https://api.altertable.ai
|
|
5
|
+
*/
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
/**
|
|
8
|
+
* The environment of the application.
|
|
9
|
+
* @default "production"
|
|
10
|
+
*/
|
|
11
|
+
environment?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Whether to automatically capture page views and events.
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
autoCapture?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* The release ID of the application.
|
|
19
|
+
* This is helpful to identify the version of the application an event is coming from.
|
|
20
|
+
*/
|
|
21
|
+
release?: string;
|
|
22
|
+
}
|
|
23
|
+
type EventProperties = Record<string, unknown>;
|
|
24
|
+
declare class Altertable {
|
|
25
|
+
private _lastUrl;
|
|
26
|
+
private _apiKey;
|
|
27
|
+
private _config;
|
|
28
|
+
private _sessionId;
|
|
29
|
+
private _visitorId;
|
|
30
|
+
private _userId;
|
|
31
|
+
private _referrer;
|
|
32
|
+
constructor();
|
|
33
|
+
init(apiKey: string, config?: Config): void;
|
|
34
|
+
identify(userId: string): void;
|
|
35
|
+
page(url: string): void;
|
|
36
|
+
track(event: string, properties?: EventProperties): void;
|
|
37
|
+
private _checkForChanges;
|
|
38
|
+
private _request;
|
|
39
|
+
private _getSessionId;
|
|
40
|
+
private _getVisitorId;
|
|
41
|
+
private _generateId;
|
|
42
|
+
private _getViewport;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare global {
|
|
46
|
+
interface Window {
|
|
47
|
+
Altertable: Altertable | Array<Array<unknown>> | undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
declare const altertable: Altertable;
|
|
51
|
+
|
|
52
|
+
export { Altertable, altertable };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
interface Config {
|
|
2
|
+
/**
|
|
3
|
+
* The base URL of the Altertable API.
|
|
4
|
+
* @default https://api.altertable.ai
|
|
5
|
+
*/
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
/**
|
|
8
|
+
* The environment of the application.
|
|
9
|
+
* @default "production"
|
|
10
|
+
*/
|
|
11
|
+
environment?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Whether to automatically capture page views and events.
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
autoCapture?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* The release ID of the application.
|
|
19
|
+
* This is helpful to identify the version of the application an event is coming from.
|
|
20
|
+
*/
|
|
21
|
+
release?: string;
|
|
22
|
+
}
|
|
23
|
+
type EventProperties = Record<string, unknown>;
|
|
24
|
+
declare class Altertable {
|
|
25
|
+
private _lastUrl;
|
|
26
|
+
private _apiKey;
|
|
27
|
+
private _config;
|
|
28
|
+
private _sessionId;
|
|
29
|
+
private _visitorId;
|
|
30
|
+
private _userId;
|
|
31
|
+
private _referrer;
|
|
32
|
+
constructor();
|
|
33
|
+
init(apiKey: string, config?: Config): void;
|
|
34
|
+
identify(userId: string): void;
|
|
35
|
+
page(url: string): void;
|
|
36
|
+
track(event: string, properties?: EventProperties): void;
|
|
37
|
+
private _checkForChanges;
|
|
38
|
+
private _request;
|
|
39
|
+
private _getSessionId;
|
|
40
|
+
private _getVisitorId;
|
|
41
|
+
private _generateId;
|
|
42
|
+
private _getViewport;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare global {
|
|
46
|
+
interface Window {
|
|
47
|
+
Altertable: Altertable | Array<Array<unknown>> | undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
declare const altertable: Altertable;
|
|
51
|
+
|
|
52
|
+
export { Altertable, altertable };
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
// src/core.ts
|
|
3
|
+
var DEFAULT_BASE_URL = "https://api.altertable.ai";
|
|
4
|
+
var DEFAULT_ENVIRONMENT = "production";
|
|
5
|
+
var PAGEVIEW_EVENT = "$pageview";
|
|
6
|
+
var SESSION_STORAGE_KEY = "altertable-session-id";
|
|
7
|
+
var LOCAL_STORAGE_KEY = "altertable-visitor-id";
|
|
8
|
+
var AUTO_CAPTURE_INTERVAL = 100;
|
|
9
|
+
var PROPERTY_URL = "$url";
|
|
10
|
+
var PROPERTY_SESSION_ID = "$session_id";
|
|
11
|
+
var PROPERTY_VISITOR_ID = "$visitor_id";
|
|
12
|
+
var PROPERTY_VIEWPORT = "$viewport";
|
|
13
|
+
var PROPERTY_REFERER = "$referer";
|
|
14
|
+
var PROPERTY_RELEASE = "$release";
|
|
15
|
+
var PROPERTY_LIB = "$lib";
|
|
16
|
+
var PROPERTY_LIB_VERSION = "$lib_version";
|
|
17
|
+
var Altertable = class {
|
|
18
|
+
_lastUrl;
|
|
19
|
+
_apiKey;
|
|
20
|
+
_config;
|
|
21
|
+
_sessionId;
|
|
22
|
+
_visitorId;
|
|
23
|
+
_userId;
|
|
24
|
+
_referrer;
|
|
25
|
+
constructor() {
|
|
26
|
+
this._referrer = document.referrer || null;
|
|
27
|
+
this._lastUrl = window.location.href;
|
|
28
|
+
this._sessionId = this._generateId("session");
|
|
29
|
+
this._visitorId = this._generateId("visitor");
|
|
30
|
+
this._userId = this._generateId("anonymous");
|
|
31
|
+
}
|
|
32
|
+
init(apiKey, config = {}) {
|
|
33
|
+
this._apiKey = apiKey;
|
|
34
|
+
this._config = config;
|
|
35
|
+
if (config.autoCapture !== false) {
|
|
36
|
+
this.page(this._lastUrl);
|
|
37
|
+
setInterval(() => {
|
|
38
|
+
this._checkForChanges();
|
|
39
|
+
}, AUTO_CAPTURE_INTERVAL);
|
|
40
|
+
window.addEventListener("popstate", () => this._checkForChanges());
|
|
41
|
+
window.addEventListener("hashchange", () => this._checkForChanges());
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
identify(userId) {
|
|
45
|
+
this._userId = userId;
|
|
46
|
+
}
|
|
47
|
+
page(url) {
|
|
48
|
+
const parsedUrl = new URL(url);
|
|
49
|
+
const urlWithoutSearch = `${parsedUrl.origin}${parsedUrl.pathname}`;
|
|
50
|
+
this.track(PAGEVIEW_EVENT, {
|
|
51
|
+
[PROPERTY_URL]: urlWithoutSearch,
|
|
52
|
+
[PROPERTY_SESSION_ID]: this._getSessionId(),
|
|
53
|
+
[PROPERTY_VISITOR_ID]: this._getVisitorId(),
|
|
54
|
+
[PROPERTY_VIEWPORT]: this._getViewport(),
|
|
55
|
+
[PROPERTY_REFERER]: this._referrer,
|
|
56
|
+
...Object.fromEntries(parsedUrl.searchParams)
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
track(event, properties) {
|
|
60
|
+
this._request("/track", {
|
|
61
|
+
event,
|
|
62
|
+
user_id: this._userId,
|
|
63
|
+
environment: this._config.environment || DEFAULT_ENVIRONMENT,
|
|
64
|
+
properties: {
|
|
65
|
+
[PROPERTY_LIB]: "@altertable/altertable-js",
|
|
66
|
+
[PROPERTY_LIB_VERSION]: "0.3.0",
|
|
67
|
+
[PROPERTY_RELEASE]: this._config.release,
|
|
68
|
+
// The above properties might be overridden by user-provided fields
|
|
69
|
+
// and the React library
|
|
70
|
+
...properties || {}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
_checkForChanges() {
|
|
75
|
+
const currentUrl = window.location.href;
|
|
76
|
+
if (currentUrl !== this._lastUrl) {
|
|
77
|
+
this.page(currentUrl);
|
|
78
|
+
this._referrer = this._lastUrl;
|
|
79
|
+
this._lastUrl = currentUrl;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
_request(path, body) {
|
|
83
|
+
const url = `${this._config.baseUrl || DEFAULT_BASE_URL}${path}`;
|
|
84
|
+
const payload = JSON.stringify(body);
|
|
85
|
+
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
86
|
+
const beaconUrl = `${url}?apiKey=${encodeURIComponent(this._apiKey)}`;
|
|
87
|
+
const blob = new Blob([payload], { type: "application/json" });
|
|
88
|
+
navigator.sendBeacon(beaconUrl, blob);
|
|
89
|
+
} else {
|
|
90
|
+
fetch(url, {
|
|
91
|
+
method: "POST",
|
|
92
|
+
headers: {
|
|
93
|
+
"Content-Type": "application/json",
|
|
94
|
+
Authorization: `Bearer ${this._apiKey}`
|
|
95
|
+
},
|
|
96
|
+
body: payload
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
_getSessionId() {
|
|
101
|
+
try {
|
|
102
|
+
let id = sessionStorage.getItem(SESSION_STORAGE_KEY);
|
|
103
|
+
if (!id) {
|
|
104
|
+
id = this._sessionId;
|
|
105
|
+
sessionStorage.setItem(SESSION_STORAGE_KEY, id);
|
|
106
|
+
}
|
|
107
|
+
return id;
|
|
108
|
+
} catch {
|
|
109
|
+
return this._sessionId;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
_getVisitorId() {
|
|
113
|
+
try {
|
|
114
|
+
let id = localStorage.getItem(LOCAL_STORAGE_KEY);
|
|
115
|
+
if (!id) {
|
|
116
|
+
id = this._visitorId;
|
|
117
|
+
localStorage.setItem(LOCAL_STORAGE_KEY, id);
|
|
118
|
+
}
|
|
119
|
+
return id;
|
|
120
|
+
} catch {
|
|
121
|
+
return this._visitorId;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
_generateId(prefix) {
|
|
125
|
+
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.randomUUID === "function") {
|
|
126
|
+
try {
|
|
127
|
+
return `${prefix}-${crypto.randomUUID()}`;
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return `${prefix}-${Math.random().toString(36).substring(2)}`;
|
|
132
|
+
}
|
|
133
|
+
_getViewport() {
|
|
134
|
+
return `${window.innerWidth}x${window.innerHeight}`;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// src/index.ts
|
|
139
|
+
var altertable = new Altertable();
|
|
140
|
+
var stub = window.Altertable;
|
|
141
|
+
if (stub && Array.isArray(stub)) {
|
|
142
|
+
for (const item of stub) {
|
|
143
|
+
const method = item[0];
|
|
144
|
+
const args = item.slice(1);
|
|
145
|
+
altertable[method](...args);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
window.Altertable = altertable;
|
|
149
|
+
})();
|
|
150
|
+
//# sourceMappingURL=index.global.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../src/index.ts"],"sourcesContent":["export interface Config {\n /**\n * The base URL of the Altertable API.\n * @default https://api.altertable.ai\n */\n baseUrl?: string;\n /**\n * The environment of the application.\n * @default \"production\"\n */\n environment?: string;\n /**\n * Whether to automatically capture page views and events.\n * @default true\n */\n autoCapture?: boolean;\n /**\n * The release ID of the application.\n * This is helpful to identify the version of the application an event is coming from.\n */\n release?: string;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.altertable.ai';\nconst DEFAULT_ENVIRONMENT = 'production';\n\nexport type EventProperties = Record<string, unknown>;\n\nexport const PAGEVIEW_EVENT = '$pageview';\n\nexport const SESSION_STORAGE_KEY = 'altertable-session-id';\nexport const LOCAL_STORAGE_KEY = 'altertable-visitor-id';\nexport const AUTO_CAPTURE_INTERVAL = 100;\n\nexport const PROPERTY_URL = '$url';\nexport const PROPERTY_SESSION_ID = '$session_id';\nexport const PROPERTY_VISITOR_ID = '$visitor_id';\nexport const PROPERTY_VIEWPORT = '$viewport';\nexport const PROPERTY_REFERER = '$referer';\nexport const PROPERTY_RELEASE = '$release';\nexport const PROPERTY_LIB = '$lib';\nexport const PROPERTY_LIB_VERSION = '$lib_version';\n\nexport class Altertable {\n private _lastUrl: string;\n private _apiKey: string;\n private _config: Config;\n private _sessionId: string;\n private _visitorId: string;\n private _userId: string;\n private _referrer: string | null;\n\n constructor() {\n this._referrer = document.referrer || null;\n this._lastUrl = window.location.href;\n this._sessionId = this._generateId('session');\n this._visitorId = this._generateId('visitor');\n this._userId = this._generateId('anonymous');\n }\n\n init(apiKey: string, config: Config = {}) {\n this._apiKey = apiKey;\n this._config = config;\n\n if (config.autoCapture !== false) {\n this.page(this._lastUrl);\n\n setInterval(() => {\n this._checkForChanges();\n }, AUTO_CAPTURE_INTERVAL);\n\n window.addEventListener('popstate', () => this._checkForChanges());\n window.addEventListener('hashchange', () => this._checkForChanges());\n }\n }\n\n identify(userId: string) {\n // FIXME: dummy implementation\n this._userId = userId;\n }\n\n page(url: string) {\n const parsedUrl = new URL(url);\n const urlWithoutSearch = `${parsedUrl.origin}${parsedUrl.pathname}`;\n this.track(PAGEVIEW_EVENT, {\n [PROPERTY_URL]: urlWithoutSearch,\n [PROPERTY_SESSION_ID]: this._getSessionId(),\n [PROPERTY_VISITOR_ID]: this._getVisitorId(),\n [PROPERTY_VIEWPORT]: this._getViewport(),\n [PROPERTY_REFERER]: this._referrer,\n ...Object.fromEntries(parsedUrl.searchParams),\n });\n }\n\n track(event: string, properties?: EventProperties) {\n this._request('/track', {\n event,\n user_id: this._userId,\n environment: this._config.environment || DEFAULT_ENVIRONMENT,\n properties: {\n [PROPERTY_LIB]: __LIB__,\n [PROPERTY_LIB_VERSION]: __LIB_VERSION__,\n [PROPERTY_RELEASE]: this._config.release,\n // The above properties might be overridden by user-provided fields\n // and the React library\n ...(properties || {}),\n },\n });\n }\n\n private _checkForChanges() {\n const currentUrl = window.location.href;\n if (currentUrl !== this._lastUrl) {\n this.page(currentUrl);\n this._referrer = this._lastUrl;\n this._lastUrl = currentUrl;\n }\n }\n\n private _request(path: string, body: unknown): void {\n const url = `${this._config.baseUrl || DEFAULT_BASE_URL}${path}`;\n const payload = JSON.stringify(body);\n\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const beaconUrl = `${url}?apiKey=${encodeURIComponent(this._apiKey)}`;\n const blob = new Blob([payload], { type: 'application/json' });\n navigator.sendBeacon(beaconUrl, blob);\n } else {\n fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this._apiKey}`,\n },\n body: payload,\n });\n }\n }\n\n private _getSessionId(): string {\n try {\n let id = sessionStorage.getItem(SESSION_STORAGE_KEY);\n if (!id) {\n id = this._sessionId;\n sessionStorage.setItem(SESSION_STORAGE_KEY, id);\n }\n return id;\n } catch {\n return this._sessionId;\n }\n }\n\n private _getVisitorId(): string {\n try {\n let id = localStorage.getItem(LOCAL_STORAGE_KEY);\n if (!id) {\n id = this._visitorId;\n localStorage.setItem(LOCAL_STORAGE_KEY, id);\n }\n return id;\n } catch {\n return this._visitorId;\n }\n }\n\n private _generateId(prefix: string): string {\n if (\n typeof globalThis.crypto !== 'undefined' &&\n typeof globalThis.crypto.randomUUID === 'function'\n ) {\n try {\n return `${prefix}-${crypto.randomUUID()}`;\n } catch {\n // Continue with Math.random() fallback.\n }\n }\n return `${prefix}-${Math.random().toString(36).substring(2)}`;\n }\n\n private _getViewport(): string {\n return `${window.innerWidth}x${window.innerHeight}`;\n }\n}\n","import { Altertable } from './core';\n\nexport type { Altertable };\n\ndeclare global {\n interface Window {\n Altertable: Altertable | Array<Array<unknown>> | undefined;\n }\n}\n\nexport const altertable = new Altertable();\n\nconst stub = window.Altertable;\nif (stub && Array.isArray(stub)) {\n for (const item of stub) {\n const method = item[0];\n const args = item.slice(1);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (altertable[method as keyof Altertable] as any)(...args);\n }\n}\n\nwindow.Altertable = altertable;\n"],"mappings":"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","names":[]}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/index.ts
|
|
20
|
+
var index_exports = {};
|
|
21
|
+
__export(index_exports, {
|
|
22
|
+
altertable: () => altertable
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(index_exports);
|
|
25
|
+
|
|
26
|
+
// src/core.ts
|
|
27
|
+
var DEFAULT_BASE_URL = "https://api.altertable.ai";
|
|
28
|
+
var DEFAULT_ENVIRONMENT = "production";
|
|
29
|
+
var PAGEVIEW_EVENT = "$pageview";
|
|
30
|
+
var SESSION_STORAGE_KEY = "altertable-session-id";
|
|
31
|
+
var LOCAL_STORAGE_KEY = "altertable-visitor-id";
|
|
32
|
+
var AUTO_CAPTURE_INTERVAL = 100;
|
|
33
|
+
var PROPERTY_URL = "$url";
|
|
34
|
+
var PROPERTY_SESSION_ID = "$session_id";
|
|
35
|
+
var PROPERTY_VISITOR_ID = "$visitor_id";
|
|
36
|
+
var PROPERTY_VIEWPORT = "$viewport";
|
|
37
|
+
var PROPERTY_REFERER = "$referer";
|
|
38
|
+
var PROPERTY_RELEASE = "$release";
|
|
39
|
+
var PROPERTY_LIB = "$lib";
|
|
40
|
+
var PROPERTY_LIB_VERSION = "$lib_version";
|
|
41
|
+
var Altertable = class {
|
|
42
|
+
_lastUrl;
|
|
43
|
+
_apiKey;
|
|
44
|
+
_config;
|
|
45
|
+
_sessionId;
|
|
46
|
+
_visitorId;
|
|
47
|
+
_userId;
|
|
48
|
+
_referrer;
|
|
49
|
+
constructor() {
|
|
50
|
+
this._referrer = document.referrer || null;
|
|
51
|
+
this._lastUrl = window.location.href;
|
|
52
|
+
this._sessionId = this._generateId("session");
|
|
53
|
+
this._visitorId = this._generateId("visitor");
|
|
54
|
+
this._userId = this._generateId("anonymous");
|
|
55
|
+
}
|
|
56
|
+
init(apiKey, config = {}) {
|
|
57
|
+
this._apiKey = apiKey;
|
|
58
|
+
this._config = config;
|
|
59
|
+
if (config.autoCapture !== false) {
|
|
60
|
+
this.page(this._lastUrl);
|
|
61
|
+
setInterval(() => {
|
|
62
|
+
this._checkForChanges();
|
|
63
|
+
}, AUTO_CAPTURE_INTERVAL);
|
|
64
|
+
window.addEventListener("popstate", () => this._checkForChanges());
|
|
65
|
+
window.addEventListener("hashchange", () => this._checkForChanges());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
identify(userId) {
|
|
69
|
+
this._userId = userId;
|
|
70
|
+
}
|
|
71
|
+
page(url) {
|
|
72
|
+
const parsedUrl = new URL(url);
|
|
73
|
+
const urlWithoutSearch = `${parsedUrl.origin}${parsedUrl.pathname}`;
|
|
74
|
+
this.track(PAGEVIEW_EVENT, {
|
|
75
|
+
[PROPERTY_URL]: urlWithoutSearch,
|
|
76
|
+
[PROPERTY_SESSION_ID]: this._getSessionId(),
|
|
77
|
+
[PROPERTY_VISITOR_ID]: this._getVisitorId(),
|
|
78
|
+
[PROPERTY_VIEWPORT]: this._getViewport(),
|
|
79
|
+
[PROPERTY_REFERER]: this._referrer,
|
|
80
|
+
...Object.fromEntries(parsedUrl.searchParams)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
track(event, properties) {
|
|
84
|
+
this._request("/track", {
|
|
85
|
+
event,
|
|
86
|
+
user_id: this._userId,
|
|
87
|
+
environment: this._config.environment || DEFAULT_ENVIRONMENT,
|
|
88
|
+
properties: {
|
|
89
|
+
[PROPERTY_LIB]: "@altertable/altertable-js",
|
|
90
|
+
[PROPERTY_LIB_VERSION]: "0.3.0",
|
|
91
|
+
[PROPERTY_RELEASE]: this._config.release,
|
|
92
|
+
// The above properties might be overridden by user-provided fields
|
|
93
|
+
// and the React library
|
|
94
|
+
...properties || {}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
_checkForChanges() {
|
|
99
|
+
const currentUrl = window.location.href;
|
|
100
|
+
if (currentUrl !== this._lastUrl) {
|
|
101
|
+
this.page(currentUrl);
|
|
102
|
+
this._referrer = this._lastUrl;
|
|
103
|
+
this._lastUrl = currentUrl;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
_request(path, body) {
|
|
107
|
+
const url = `${this._config.baseUrl || DEFAULT_BASE_URL}${path}`;
|
|
108
|
+
const payload = JSON.stringify(body);
|
|
109
|
+
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
110
|
+
const beaconUrl = `${url}?apiKey=${encodeURIComponent(this._apiKey)}`;
|
|
111
|
+
const blob = new Blob([payload], { type: "application/json" });
|
|
112
|
+
navigator.sendBeacon(beaconUrl, blob);
|
|
113
|
+
} else {
|
|
114
|
+
fetch(url, {
|
|
115
|
+
method: "POST",
|
|
116
|
+
headers: {
|
|
117
|
+
"Content-Type": "application/json",
|
|
118
|
+
Authorization: `Bearer ${this._apiKey}`
|
|
119
|
+
},
|
|
120
|
+
body: payload
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
_getSessionId() {
|
|
125
|
+
try {
|
|
126
|
+
let id = sessionStorage.getItem(SESSION_STORAGE_KEY);
|
|
127
|
+
if (!id) {
|
|
128
|
+
id = this._sessionId;
|
|
129
|
+
sessionStorage.setItem(SESSION_STORAGE_KEY, id);
|
|
130
|
+
}
|
|
131
|
+
return id;
|
|
132
|
+
} catch {
|
|
133
|
+
return this._sessionId;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
_getVisitorId() {
|
|
137
|
+
try {
|
|
138
|
+
let id = localStorage.getItem(LOCAL_STORAGE_KEY);
|
|
139
|
+
if (!id) {
|
|
140
|
+
id = this._visitorId;
|
|
141
|
+
localStorage.setItem(LOCAL_STORAGE_KEY, id);
|
|
142
|
+
}
|
|
143
|
+
return id;
|
|
144
|
+
} catch {
|
|
145
|
+
return this._visitorId;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
_generateId(prefix) {
|
|
149
|
+
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.randomUUID === "function") {
|
|
150
|
+
try {
|
|
151
|
+
return `${prefix}-${crypto.randomUUID()}`;
|
|
152
|
+
} catch {
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return `${prefix}-${Math.random().toString(36).substring(2)}`;
|
|
156
|
+
}
|
|
157
|
+
_getViewport() {
|
|
158
|
+
return `${window.innerWidth}x${window.innerHeight}`;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// src/index.ts
|
|
163
|
+
var altertable = new Altertable();
|
|
164
|
+
var stub = window.Altertable;
|
|
165
|
+
if (stub && Array.isArray(stub)) {
|
|
166
|
+
for (const item of stub) {
|
|
167
|
+
const method = item[0];
|
|
168
|
+
const args = item.slice(1);
|
|
169
|
+
altertable[method](...args);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
window.Altertable = altertable;
|
|
173
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core.ts"],"sourcesContent":["import { Altertable } from './core';\n\nexport type { Altertable };\n\ndeclare global {\n interface Window {\n Altertable: Altertable | Array<Array<unknown>> | undefined;\n }\n}\n\nexport const altertable = new Altertable();\n\nconst stub = window.Altertable;\nif (stub && Array.isArray(stub)) {\n for (const item of stub) {\n const method = item[0];\n const args = item.slice(1);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (altertable[method as keyof Altertable] as any)(...args);\n }\n}\n\nwindow.Altertable = altertable;\n","export interface Config {\n /**\n * The base URL of the Altertable API.\n * @default https://api.altertable.ai\n */\n baseUrl?: string;\n /**\n * The environment of the application.\n * @default \"production\"\n */\n environment?: string;\n /**\n * Whether to automatically capture page views and events.\n * @default true\n */\n autoCapture?: boolean;\n /**\n * The release ID of the application.\n * This is helpful to identify the version of the application an event is coming from.\n */\n release?: string;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.altertable.ai';\nconst DEFAULT_ENVIRONMENT = 'production';\n\nexport type EventProperties = Record<string, unknown>;\n\nexport const PAGEVIEW_EVENT = '$pageview';\n\nexport const SESSION_STORAGE_KEY = 'altertable-session-id';\nexport const LOCAL_STORAGE_KEY = 'altertable-visitor-id';\nexport const AUTO_CAPTURE_INTERVAL = 100;\n\nexport const PROPERTY_URL = '$url';\nexport const PROPERTY_SESSION_ID = '$session_id';\nexport const PROPERTY_VISITOR_ID = '$visitor_id';\nexport const PROPERTY_VIEWPORT = '$viewport';\nexport const PROPERTY_REFERER = '$referer';\nexport const PROPERTY_RELEASE = '$release';\nexport const PROPERTY_LIB = '$lib';\nexport const PROPERTY_LIB_VERSION = '$lib_version';\n\nexport class Altertable {\n private _lastUrl: string;\n private _apiKey: string;\n private _config: Config;\n private _sessionId: string;\n private _visitorId: string;\n private _userId: string;\n private _referrer: string | null;\n\n constructor() {\n this._referrer = document.referrer || null;\n this._lastUrl = window.location.href;\n this._sessionId = this._generateId('session');\n this._visitorId = this._generateId('visitor');\n this._userId = this._generateId('anonymous');\n }\n\n init(apiKey: string, config: Config = {}) {\n this._apiKey = apiKey;\n this._config = config;\n\n if (config.autoCapture !== false) {\n this.page(this._lastUrl);\n\n setInterval(() => {\n this._checkForChanges();\n }, AUTO_CAPTURE_INTERVAL);\n\n window.addEventListener('popstate', () => this._checkForChanges());\n window.addEventListener('hashchange', () => this._checkForChanges());\n }\n }\n\n identify(userId: string) {\n // FIXME: dummy implementation\n this._userId = userId;\n }\n\n page(url: string) {\n const parsedUrl = new URL(url);\n const urlWithoutSearch = `${parsedUrl.origin}${parsedUrl.pathname}`;\n this.track(PAGEVIEW_EVENT, {\n [PROPERTY_URL]: urlWithoutSearch,\n [PROPERTY_SESSION_ID]: this._getSessionId(),\n [PROPERTY_VISITOR_ID]: this._getVisitorId(),\n [PROPERTY_VIEWPORT]: this._getViewport(),\n [PROPERTY_REFERER]: this._referrer,\n ...Object.fromEntries(parsedUrl.searchParams),\n });\n }\n\n track(event: string, properties?: EventProperties) {\n this._request('/track', {\n event,\n user_id: this._userId,\n environment: this._config.environment || DEFAULT_ENVIRONMENT,\n properties: {\n [PROPERTY_LIB]: __LIB__,\n [PROPERTY_LIB_VERSION]: __LIB_VERSION__,\n [PROPERTY_RELEASE]: this._config.release,\n // The above properties might be overridden by user-provided fields\n // and the React library\n ...(properties || {}),\n },\n });\n }\n\n private _checkForChanges() {\n const currentUrl = window.location.href;\n if (currentUrl !== this._lastUrl) {\n this.page(currentUrl);\n this._referrer = this._lastUrl;\n this._lastUrl = currentUrl;\n }\n }\n\n private _request(path: string, body: unknown): void {\n const url = `${this._config.baseUrl || DEFAULT_BASE_URL}${path}`;\n const payload = JSON.stringify(body);\n\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const beaconUrl = `${url}?apiKey=${encodeURIComponent(this._apiKey)}`;\n const blob = new Blob([payload], { type: 'application/json' });\n navigator.sendBeacon(beaconUrl, blob);\n } else {\n fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this._apiKey}`,\n },\n body: payload,\n });\n }\n }\n\n private _getSessionId(): string {\n try {\n let id = sessionStorage.getItem(SESSION_STORAGE_KEY);\n if (!id) {\n id = this._sessionId;\n sessionStorage.setItem(SESSION_STORAGE_KEY, id);\n }\n return id;\n } catch {\n return this._sessionId;\n }\n }\n\n private _getVisitorId(): string {\n try {\n let id = localStorage.getItem(LOCAL_STORAGE_KEY);\n if (!id) {\n id = this._visitorId;\n localStorage.setItem(LOCAL_STORAGE_KEY, id);\n }\n return id;\n } catch {\n return this._visitorId;\n }\n }\n\n private _generateId(prefix: string): string {\n if (\n typeof globalThis.crypto !== 'undefined' &&\n typeof globalThis.crypto.randomUUID === 'function'\n ) {\n try {\n return `${prefix}-${crypto.randomUUID()}`;\n } catch {\n // Continue with Math.random() fallback.\n }\n }\n return `${prefix}-${Math.random().toString(36).substring(2)}`;\n }\n\n private _getViewport(): string {\n return `${window.innerWidth}x${window.innerHeight}`;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// src/core.ts
|
|
2
|
+
var DEFAULT_BASE_URL = "https://api.altertable.ai";
|
|
3
|
+
var DEFAULT_ENVIRONMENT = "production";
|
|
4
|
+
var PAGEVIEW_EVENT = "$pageview";
|
|
5
|
+
var SESSION_STORAGE_KEY = "altertable-session-id";
|
|
6
|
+
var LOCAL_STORAGE_KEY = "altertable-visitor-id";
|
|
7
|
+
var AUTO_CAPTURE_INTERVAL = 100;
|
|
8
|
+
var PROPERTY_URL = "$url";
|
|
9
|
+
var PROPERTY_SESSION_ID = "$session_id";
|
|
10
|
+
var PROPERTY_VISITOR_ID = "$visitor_id";
|
|
11
|
+
var PROPERTY_VIEWPORT = "$viewport";
|
|
12
|
+
var PROPERTY_REFERER = "$referer";
|
|
13
|
+
var PROPERTY_RELEASE = "$release";
|
|
14
|
+
var PROPERTY_LIB = "$lib";
|
|
15
|
+
var PROPERTY_LIB_VERSION = "$lib_version";
|
|
16
|
+
var Altertable = class {
|
|
17
|
+
_lastUrl;
|
|
18
|
+
_apiKey;
|
|
19
|
+
_config;
|
|
20
|
+
_sessionId;
|
|
21
|
+
_visitorId;
|
|
22
|
+
_userId;
|
|
23
|
+
_referrer;
|
|
24
|
+
constructor() {
|
|
25
|
+
this._referrer = document.referrer || null;
|
|
26
|
+
this._lastUrl = window.location.href;
|
|
27
|
+
this._sessionId = this._generateId("session");
|
|
28
|
+
this._visitorId = this._generateId("visitor");
|
|
29
|
+
this._userId = this._generateId("anonymous");
|
|
30
|
+
}
|
|
31
|
+
init(apiKey, config = {}) {
|
|
32
|
+
this._apiKey = apiKey;
|
|
33
|
+
this._config = config;
|
|
34
|
+
if (config.autoCapture !== false) {
|
|
35
|
+
this.page(this._lastUrl);
|
|
36
|
+
setInterval(() => {
|
|
37
|
+
this._checkForChanges();
|
|
38
|
+
}, AUTO_CAPTURE_INTERVAL);
|
|
39
|
+
window.addEventListener("popstate", () => this._checkForChanges());
|
|
40
|
+
window.addEventListener("hashchange", () => this._checkForChanges());
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
identify(userId) {
|
|
44
|
+
this._userId = userId;
|
|
45
|
+
}
|
|
46
|
+
page(url) {
|
|
47
|
+
const parsedUrl = new URL(url);
|
|
48
|
+
const urlWithoutSearch = `${parsedUrl.origin}${parsedUrl.pathname}`;
|
|
49
|
+
this.track(PAGEVIEW_EVENT, {
|
|
50
|
+
[PROPERTY_URL]: urlWithoutSearch,
|
|
51
|
+
[PROPERTY_SESSION_ID]: this._getSessionId(),
|
|
52
|
+
[PROPERTY_VISITOR_ID]: this._getVisitorId(),
|
|
53
|
+
[PROPERTY_VIEWPORT]: this._getViewport(),
|
|
54
|
+
[PROPERTY_REFERER]: this._referrer,
|
|
55
|
+
...Object.fromEntries(parsedUrl.searchParams)
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
track(event, properties) {
|
|
59
|
+
this._request("/track", {
|
|
60
|
+
event,
|
|
61
|
+
user_id: this._userId,
|
|
62
|
+
environment: this._config.environment || DEFAULT_ENVIRONMENT,
|
|
63
|
+
properties: {
|
|
64
|
+
[PROPERTY_LIB]: "@altertable/altertable-js",
|
|
65
|
+
[PROPERTY_LIB_VERSION]: "0.3.0",
|
|
66
|
+
[PROPERTY_RELEASE]: this._config.release,
|
|
67
|
+
// The above properties might be overridden by user-provided fields
|
|
68
|
+
// and the React library
|
|
69
|
+
...properties || {}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
_checkForChanges() {
|
|
74
|
+
const currentUrl = window.location.href;
|
|
75
|
+
if (currentUrl !== this._lastUrl) {
|
|
76
|
+
this.page(currentUrl);
|
|
77
|
+
this._referrer = this._lastUrl;
|
|
78
|
+
this._lastUrl = currentUrl;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
_request(path, body) {
|
|
82
|
+
const url = `${this._config.baseUrl || DEFAULT_BASE_URL}${path}`;
|
|
83
|
+
const payload = JSON.stringify(body);
|
|
84
|
+
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
85
|
+
const beaconUrl = `${url}?apiKey=${encodeURIComponent(this._apiKey)}`;
|
|
86
|
+
const blob = new Blob([payload], { type: "application/json" });
|
|
87
|
+
navigator.sendBeacon(beaconUrl, blob);
|
|
88
|
+
} else {
|
|
89
|
+
fetch(url, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: {
|
|
92
|
+
"Content-Type": "application/json",
|
|
93
|
+
Authorization: `Bearer ${this._apiKey}`
|
|
94
|
+
},
|
|
95
|
+
body: payload
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
_getSessionId() {
|
|
100
|
+
try {
|
|
101
|
+
let id = sessionStorage.getItem(SESSION_STORAGE_KEY);
|
|
102
|
+
if (!id) {
|
|
103
|
+
id = this._sessionId;
|
|
104
|
+
sessionStorage.setItem(SESSION_STORAGE_KEY, id);
|
|
105
|
+
}
|
|
106
|
+
return id;
|
|
107
|
+
} catch {
|
|
108
|
+
return this._sessionId;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
_getVisitorId() {
|
|
112
|
+
try {
|
|
113
|
+
let id = localStorage.getItem(LOCAL_STORAGE_KEY);
|
|
114
|
+
if (!id) {
|
|
115
|
+
id = this._visitorId;
|
|
116
|
+
localStorage.setItem(LOCAL_STORAGE_KEY, id);
|
|
117
|
+
}
|
|
118
|
+
return id;
|
|
119
|
+
} catch {
|
|
120
|
+
return this._visitorId;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
_generateId(prefix) {
|
|
124
|
+
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.randomUUID === "function") {
|
|
125
|
+
try {
|
|
126
|
+
return `${prefix}-${crypto.randomUUID()}`;
|
|
127
|
+
} catch {
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return `${prefix}-${Math.random().toString(36).substring(2)}`;
|
|
131
|
+
}
|
|
132
|
+
_getViewport() {
|
|
133
|
+
return `${window.innerWidth}x${window.innerHeight}`;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// src/index.ts
|
|
138
|
+
var altertable = new Altertable();
|
|
139
|
+
var stub = window.Altertable;
|
|
140
|
+
if (stub && Array.isArray(stub)) {
|
|
141
|
+
for (const item of stub) {
|
|
142
|
+
const method = item[0];
|
|
143
|
+
const args = item.slice(1);
|
|
144
|
+
altertable[method](...args);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
window.Altertable = altertable;
|
|
148
|
+
export {
|
|
149
|
+
altertable
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../src/index.ts"],"sourcesContent":["export interface Config {\n /**\n * The base URL of the Altertable API.\n * @default https://api.altertable.ai\n */\n baseUrl?: string;\n /**\n * The environment of the application.\n * @default \"production\"\n */\n environment?: string;\n /**\n * Whether to automatically capture page views and events.\n * @default true\n */\n autoCapture?: boolean;\n /**\n * The release ID of the application.\n * This is helpful to identify the version of the application an event is coming from.\n */\n release?: string;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.altertable.ai';\nconst DEFAULT_ENVIRONMENT = 'production';\n\nexport type EventProperties = Record<string, unknown>;\n\nexport const PAGEVIEW_EVENT = '$pageview';\n\nexport const SESSION_STORAGE_KEY = 'altertable-session-id';\nexport const LOCAL_STORAGE_KEY = 'altertable-visitor-id';\nexport const AUTO_CAPTURE_INTERVAL = 100;\n\nexport const PROPERTY_URL = '$url';\nexport const PROPERTY_SESSION_ID = '$session_id';\nexport const PROPERTY_VISITOR_ID = '$visitor_id';\nexport const PROPERTY_VIEWPORT = '$viewport';\nexport const PROPERTY_REFERER = '$referer';\nexport const PROPERTY_RELEASE = '$release';\nexport const PROPERTY_LIB = '$lib';\nexport const PROPERTY_LIB_VERSION = '$lib_version';\n\nexport class Altertable {\n private _lastUrl: string;\n private _apiKey: string;\n private _config: Config;\n private _sessionId: string;\n private _visitorId: string;\n private _userId: string;\n private _referrer: string | null;\n\n constructor() {\n this._referrer = document.referrer || null;\n this._lastUrl = window.location.href;\n this._sessionId = this._generateId('session');\n this._visitorId = this._generateId('visitor');\n this._userId = this._generateId('anonymous');\n }\n\n init(apiKey: string, config: Config = {}) {\n this._apiKey = apiKey;\n this._config = config;\n\n if (config.autoCapture !== false) {\n this.page(this._lastUrl);\n\n setInterval(() => {\n this._checkForChanges();\n }, AUTO_CAPTURE_INTERVAL);\n\n window.addEventListener('popstate', () => this._checkForChanges());\n window.addEventListener('hashchange', () => this._checkForChanges());\n }\n }\n\n identify(userId: string) {\n // FIXME: dummy implementation\n this._userId = userId;\n }\n\n page(url: string) {\n const parsedUrl = new URL(url);\n const urlWithoutSearch = `${parsedUrl.origin}${parsedUrl.pathname}`;\n this.track(PAGEVIEW_EVENT, {\n [PROPERTY_URL]: urlWithoutSearch,\n [PROPERTY_SESSION_ID]: this._getSessionId(),\n [PROPERTY_VISITOR_ID]: this._getVisitorId(),\n [PROPERTY_VIEWPORT]: this._getViewport(),\n [PROPERTY_REFERER]: this._referrer,\n ...Object.fromEntries(parsedUrl.searchParams),\n });\n }\n\n track(event: string, properties?: EventProperties) {\n this._request('/track', {\n event,\n user_id: this._userId,\n environment: this._config.environment || DEFAULT_ENVIRONMENT,\n properties: {\n [PROPERTY_LIB]: __LIB__,\n [PROPERTY_LIB_VERSION]: __LIB_VERSION__,\n [PROPERTY_RELEASE]: this._config.release,\n // The above properties might be overridden by user-provided fields\n // and the React library\n ...(properties || {}),\n },\n });\n }\n\n private _checkForChanges() {\n const currentUrl = window.location.href;\n if (currentUrl !== this._lastUrl) {\n this.page(currentUrl);\n this._referrer = this._lastUrl;\n this._lastUrl = currentUrl;\n }\n }\n\n private _request(path: string, body: unknown): void {\n const url = `${this._config.baseUrl || DEFAULT_BASE_URL}${path}`;\n const payload = JSON.stringify(body);\n\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const beaconUrl = `${url}?apiKey=${encodeURIComponent(this._apiKey)}`;\n const blob = new Blob([payload], { type: 'application/json' });\n navigator.sendBeacon(beaconUrl, blob);\n } else {\n fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this._apiKey}`,\n },\n body: payload,\n });\n }\n }\n\n private _getSessionId(): string {\n try {\n let id = sessionStorage.getItem(SESSION_STORAGE_KEY);\n if (!id) {\n id = this._sessionId;\n sessionStorage.setItem(SESSION_STORAGE_KEY, id);\n }\n return id;\n } catch {\n return this._sessionId;\n }\n }\n\n private _getVisitorId(): string {\n try {\n let id = localStorage.getItem(LOCAL_STORAGE_KEY);\n if (!id) {\n id = this._visitorId;\n localStorage.setItem(LOCAL_STORAGE_KEY, id);\n }\n return id;\n } catch {\n return this._visitorId;\n }\n }\n\n private _generateId(prefix: string): string {\n if (\n typeof globalThis.crypto !== 'undefined' &&\n typeof globalThis.crypto.randomUUID === 'function'\n ) {\n try {\n return `${prefix}-${crypto.randomUUID()}`;\n } catch {\n // Continue with Math.random() fallback.\n }\n }\n return `${prefix}-${Math.random().toString(36).substring(2)}`;\n }\n\n private _getViewport(): string {\n return `${window.innerWidth}x${window.innerHeight}`;\n }\n}\n","import { Altertable } from './core';\n\nexport type { Altertable };\n\ndeclare global {\n interface Window {\n Altertable: Altertable | Array<Array<unknown>> | undefined;\n }\n}\n\nexport const altertable = new Altertable();\n\nconst stub = window.Altertable;\nif (stub && Array.isArray(stub)) {\n for (const item of stub) {\n const method = item[0];\n const args = item.slice(1);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (altertable[method as keyof Altertable] as any)(...args);\n }\n}\n\nwindow.Altertable = altertable;\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@altertable/altertable-js",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "JavaScript SDK to capture and send events to Altertable.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"private": false,
|
|
7
|
+
"module": "dist/index.mjs",
|
|
8
|
+
"browser": "dist/index.global.js",
|
|
9
|
+
"typings": "dist/index.d.ts",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"analytics",
|
|
12
|
+
"track",
|
|
13
|
+
"altertable"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"dev": "rollup -c --configPlugin typescript --watch",
|
|
17
|
+
"clean": "rm -rf dist",
|
|
18
|
+
"lint": "eslint src --ext ts",
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"test": "vitest --environment=jsdom --watch=false",
|
|
21
|
+
"test:watch": "vitest --environment=jsdom"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/altertable-ai/altertable-js"
|
|
29
|
+
},
|
|
30
|
+
"author": "Altertable AI Team",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"homepage": "https://github.com/altertable-ai/altertable-js#readme",
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@testing-library/dom": "^10.4.0",
|
|
35
|
+
"@types/node": "^22.13.0",
|
|
36
|
+
"@typescript-eslint/eslint-plugin": "^8.22.0",
|
|
37
|
+
"@typescript-eslint/parser": "^8.22.0",
|
|
38
|
+
"eslint": "^8.57.0",
|
|
39
|
+
"eslint-config-love": "^84.0.0",
|
|
40
|
+
"eslint-config-prettier": "^10.0.1",
|
|
41
|
+
"eslint-plugin-import": "^2.29.1",
|
|
42
|
+
"eslint-plugin-n": "^17.15.1",
|
|
43
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
44
|
+
"eslint-plugin-promise": "^7.2.1",
|
|
45
|
+
"jsdom": "^26.0.0",
|
|
46
|
+
"lint-staged": "^15.2.2",
|
|
47
|
+
"prettier": "3.5.0",
|
|
48
|
+
"ts-node": "^10.9.2",
|
|
49
|
+
"tslib": "^2.6.2",
|
|
50
|
+
"tsup": "^8.3.6",
|
|
51
|
+
"typescript": "^5.7.3",
|
|
52
|
+
"vitest": "^3.0.4"
|
|
53
|
+
}
|
|
54
|
+
}
|