@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.
@@ -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,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
+ }