@savant-realms/sentinel-analytics-realm 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,251 @@
1
+ # Sentinel Analytics Realm - TypeScript/JavaScript SDK
2
+
3
+ TypeScript/JavaScript SDK for the Sentinel Analytics Realm analytics platform. Works in both Node.js and browser environments.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @savant-realms/sentinel-analytics-realm
9
+ ```
10
+
11
+ or
12
+
13
+ ```bash
14
+ yarn add @savant-realms/sentinel-analytics-realm
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ### Basic Setup
20
+
21
+ ```typescript
22
+ import { SentinelAnalyticsRealmPlugin } from '@savant-realms/sentinel-analytics-realm';
23
+
24
+ const analytics = new SentinelAnalyticsRealmPlugin({
25
+ projectId: 'your-project-id',
26
+ appId: 'your-app-id',
27
+ env: 'prod', // or 'dev', 'staging'
28
+ accessToken: 'your-access-token', // optional
29
+ baseUrl: 'https://api.sentinelanalyticsrealm.com', // optional, defaults to this
30
+ });
31
+ ```
32
+
33
+ ### Environment Variables (Node.js only)
34
+
35
+ You can also configure the SDK using environment variables:
36
+
37
+ ```bash
38
+ export SENTINEL_ANALYTICS_REALM_PROJECT_ID="your-project-id"
39
+ export SENTINEL_ANALYTICS_REALM_APP_ID="your-app-id"
40
+ export SENTINEL_ANALYTICS_REALM_ENV="prod"
41
+ export SENTINEL_ANALYTICS_REALM_URL="https://api.sentinelanalyticsrealm.com"
42
+ ```
43
+
44
+ Then initialize without parameters:
45
+
46
+ ```typescript
47
+ const analytics = new SentinelAnalyticsRealmPlugin();
48
+ ```
49
+
50
+ ### Tracking Events
51
+
52
+ #### Single Event
53
+
54
+ ```typescript
55
+ await analytics.track('page_view', {
56
+ visitorId: 'visitor-123',
57
+ userId: 'user-456',
58
+ sessionId: 'session-789',
59
+ category: 'navigation',
60
+ action: 'view',
61
+ label: 'Home Page',
62
+ properties: {
63
+ path: '/home',
64
+ referrer: 'https://example.com',
65
+ },
66
+ context: {
67
+ userAgent: navigator.userAgent,
68
+ screenWidth: window.screen.width,
69
+ },
70
+ });
71
+ ```
72
+
73
+ #### Batch Events
74
+
75
+ ```typescript
76
+ await analytics.trackBatch([
77
+ {
78
+ name: 'page_view',
79
+ visitorId: 'visitor-123',
80
+ properties: { path: '/home' },
81
+ },
82
+ {
83
+ name: 'button_click',
84
+ visitorId: 'visitor-123',
85
+ properties: { button: 'signup' },
86
+ },
87
+ ]);
88
+ ```
89
+
90
+ ### Identity Linking
91
+
92
+ ```typescript
93
+ await analytics.identify('user-456', 'visitor-123', {
94
+ email: 'user@example.com',
95
+ name: 'John Doe',
96
+ plan: 'premium',
97
+ });
98
+ ```
99
+
100
+ ### JavaScript (CommonJS)
101
+
102
+ ```javascript
103
+ const { SentinelAnalyticsRealmPlugin } = require('@savant-realms/sentinel-analytics-realm');
104
+
105
+ const analytics = new SentinelAnalyticsRealmPlugin({
106
+ projectId: 'your-project-id',
107
+ appId: 'your-app-id',
108
+ });
109
+
110
+ analytics.track('page_view', {
111
+ visitorId: 'visitor-123',
112
+ });
113
+ ```
114
+
115
+ ### Browser Usage
116
+
117
+ For browser usage, you'll need a bundler like Webpack, Vite, or Rollup that supports CommonJS modules, or use a CDN that provides ES modules.
118
+
119
+ Alternatively, you can use a bundler to create a browser-compatible build:
120
+
121
+ ```typescript
122
+ // In your bundler configuration, ensure the SDK is included
123
+ import { SentinelAnalyticsRealmPlugin } from '@savant-realms/sentinel-analytics-realm';
124
+ ```
125
+
126
+ ## API Reference
127
+
128
+ ### `SentinelAnalyticsRealmPlugin`
129
+
130
+ Main class for interacting with the Sentinel Analytics Realm API.
131
+
132
+ #### Constructor
133
+
134
+ ```typescript
135
+ new SentinelAnalyticsRealmPlugin(config?: SentinelAnalyticsRealmConfig)
136
+ ```
137
+
138
+ **Config Options:**
139
+
140
+ - `projectId?: string` - Your project ID
141
+ - `appId?: string` - Your application ID
142
+ - `env?: string` - Environment ('prod', 'dev', 'staging'), defaults to 'prod'
143
+ - `accessToken?: string` - Optional access token for authentication
144
+ - `baseUrl?: string` - API base URL, defaults to '<https://api.sentinelanalyticsrealm.com>'
145
+
146
+ #### Methods
147
+
148
+ ##### `track(name, eventData?, timeout?)`
149
+
150
+ Track a single event.
151
+
152
+ - `name: string` - Event name (required)
153
+ - `eventData?: EventData` - Event data object
154
+ - `timeout?: number` - Request timeout in milliseconds (default: 5000)
155
+ - Returns: `Promise<ApiResponse | null>`
156
+
157
+ ##### `trackBatch(events, timeout?)`
158
+
159
+ Track multiple events in one call.
160
+
161
+ - `events: EventData[]` - Array of event data objects
162
+ - `timeout?: number` - Request timeout in milliseconds (default: 10000)
163
+ - Returns: `Promise<ApiResponse | null>`
164
+
165
+ ##### `identify(userId, visitorId?, traits?)`
166
+
167
+ Convenience method for identity linking.
168
+
169
+ - `userId: string` - User ID (required)
170
+ - `visitorId?: string` - Visitor ID (optional)
171
+ - `traits?: Record<string, any>` - User traits/properties
172
+ - Returns: `Promise<ApiResponse | null>`
173
+
174
+ ### EventData Interface
175
+
176
+ ```typescript
177
+ interface EventData {
178
+ name: string;
179
+ visitorId?: string;
180
+ userId?: string;
181
+ sessionId?: string;
182
+ category?: string;
183
+ action?: string;
184
+ label?: string;
185
+ value?: number;
186
+ properties?: Record<string, any>;
187
+ context?: Record<string, any>;
188
+ origin?: string; // 'real' or 'seed', defaults to 'real'
189
+ }
190
+ ```
191
+
192
+ ## Error Handling
193
+
194
+ The SDK follows a fail-silent approach similar to the Python SDK. If an error occurs during tracking, the methods return `null` instead of throwing an exception. This ensures your application continues to function even if analytics tracking fails.
195
+
196
+ ## TypeScript Support
197
+
198
+ This package includes full TypeScript type definitions. No additional `@types` package is required.
199
+
200
+ ## Development
201
+
202
+ ### Building
203
+
204
+ ```bash
205
+ npm install
206
+ npm run build
207
+ ```
208
+
209
+ ### Testing
210
+
211
+ ```bash
212
+ npm test
213
+ ```
214
+
215
+ ### Deployment
216
+
217
+ To publish the package to npm, use the deployment script:
218
+
219
+ ```bash
220
+ # Set your npm token (or add to .env file)
221
+ export NPM_TOKEN=your_npm_token_here
222
+
223
+ # Run deployment (builds, tests, and publishes)
224
+ ./scripts/deploy.sh
225
+
226
+ # Or run in dry-run mode (builds and validates, but doesn't publish)
227
+ DRY_RUN=true ./scripts/deploy.sh
228
+ ```
229
+
230
+ The deployment script will:
231
+ 1. Install dependencies
232
+ 2. Build the TypeScript package
233
+ 3. Run tests (if configured)
234
+ 4. Validate package.json
235
+ 5. Publish to npm (if `NPM_TOKEN` is set)
236
+ 6. Optionally publish to GitLab Package Registry (if `GITLAB_NPM_TOKEN` and `CI_PROJECT_URL` are set)
237
+
238
+ You can also publish manually:
239
+
240
+ ```bash
241
+ npm run build
242
+ npm publish
243
+ ```
244
+
245
+ ## License
246
+
247
+ MIT
248
+
249
+ ## Author
250
+
251
+ Savant Realms
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Configuration options for the Sentinel Analytics Realm plugin
3
+ */
4
+ export interface SentinelAnalyticsRealmConfig {
5
+ projectId?: string;
6
+ appId?: string;
7
+ env?: string;
8
+ accessToken?: string;
9
+ baseUrl?: string;
10
+ }
11
+ /**
12
+ * Event data structure for tracking
13
+ */
14
+ export interface EventData {
15
+ name: string;
16
+ visitorId?: string;
17
+ userId?: string;
18
+ sessionId?: string;
19
+ category?: string;
20
+ action?: string;
21
+ label?: string;
22
+ value?: number;
23
+ properties?: Record<string, any>;
24
+ context?: Record<string, any>;
25
+ origin?: string;
26
+ }
27
+ /**
28
+ * Response from the API
29
+ */
30
+ export interface ApiResponse {
31
+ status?: string;
32
+ event_id?: string;
33
+ events?: Array<{
34
+ event_id: string;
35
+ status: string;
36
+ }>;
37
+ }
38
+ /**
39
+ * Client for plugging an app into the Sentinel Analytics Realm.
40
+ * Supports Option A (HTTP Ingestion) with the canonical event taxonomy.
41
+ *
42
+ * Works in both Node.js and browser environments.
43
+ */
44
+ export declare class SentinelAnalyticsRealmPlugin {
45
+ private projectId?;
46
+ private appId?;
47
+ private env;
48
+ private accessToken?;
49
+ private baseUrl;
50
+ /**
51
+ * Creates a new instance of the Sentinel Analytics Realm plugin
52
+ *
53
+ * @param config Configuration options. Can also be read from environment variables:
54
+ * - SENTINEL_ANALYTICS_REALM_PROJECT_ID
55
+ * - SENTINEL_ANALYTICS_REALM_APP_ID
56
+ * - SENTINEL_ANALYTICS_REALM_ENV (defaults to 'prod')
57
+ * - SENTINEL_ANALYTICS_REALM_URL (defaults to 'https://api.sentinelanalyticsrealm.com')
58
+ */
59
+ constructor(config?: SentinelAnalyticsRealmConfig);
60
+ /**
61
+ * Track a single event
62
+ *
63
+ * @param name Event name (required)
64
+ * @param eventData Additional event data
65
+ * @param timeout Request timeout in milliseconds (default: 5000)
66
+ * @returns API response or null on error
67
+ */
68
+ track(name: string, eventData?: Omit<EventData, 'name'>, timeout?: number): Promise<ApiResponse | null>;
69
+ /**
70
+ * Track multiple events in one call
71
+ *
72
+ * @param events Array of event data objects, each must have a 'name' field
73
+ * @param timeout Request timeout in milliseconds (default: 10000)
74
+ * @returns API response or null on error
75
+ */
76
+ trackBatch(events: EventData[], timeout?: number): Promise<ApiResponse | null>;
77
+ /**
78
+ * Convenience method for identity linking
79
+ *
80
+ * @param userId User ID (required)
81
+ * @param visitorId Visitor ID (optional)
82
+ * @param traits Additional user traits/properties
83
+ * @returns API response or null on error
84
+ */
85
+ identify(userId: string, visitorId?: string, traits?: Record<string, any>): Promise<ApiResponse | null>;
86
+ /**
87
+ * Builds the event payload for the API
88
+ */
89
+ private buildEventPayload;
90
+ /**
91
+ * Normalizes base URL by removing trailing slashes
92
+ */
93
+ private normalizeBaseUrl;
94
+ /**
95
+ * Reads environment variable (Node.js only, returns undefined in browser)
96
+ */
97
+ private readEnv;
98
+ }
99
+ export default SentinelAnalyticsRealmPlugin;
package/dist/index.js ADDED
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SentinelAnalyticsRealmPlugin = void 0;
4
+ const uuid_1 = require("uuid");
5
+ /**
6
+ * Client for plugging an app into the Sentinel Analytics Realm.
7
+ * Supports Option A (HTTP Ingestion) with the canonical event taxonomy.
8
+ *
9
+ * Works in both Node.js and browser environments.
10
+ */
11
+ class SentinelAnalyticsRealmPlugin {
12
+ /**
13
+ * Creates a new instance of the Sentinel Analytics Realm plugin
14
+ *
15
+ * @param config Configuration options. Can also be read from environment variables:
16
+ * - SENTINEL_ANALYTICS_REALM_PROJECT_ID
17
+ * - SENTINEL_ANALYTICS_REALM_APP_ID
18
+ * - SENTINEL_ANALYTICS_REALM_ENV (defaults to 'prod')
19
+ * - SENTINEL_ANALYTICS_REALM_URL (defaults to 'https://api.sentinelanalyticsrealm.com')
20
+ */
21
+ constructor(config = {}) {
22
+ this.projectId = config.projectId || this.readEnv('SENTINEL_ANALYTICS_REALM_PROJECT_ID');
23
+ this.appId = config.appId || this.readEnv('SENTINEL_ANALYTICS_REALM_APP_ID');
24
+ this.env = config.env || this.readEnv('SENTINEL_ANALYTICS_REALM_ENV') || 'prod';
25
+ this.accessToken = config.accessToken;
26
+ this.baseUrl = this.normalizeBaseUrl(config.baseUrl ||
27
+ this.readEnv('SENTINEL_ANALYTICS_REALM_URL') ||
28
+ 'https://api.sentinelanalyticsrealm.com');
29
+ }
30
+ /**
31
+ * Track a single event
32
+ *
33
+ * @param name Event name (required)
34
+ * @param eventData Additional event data
35
+ * @param timeout Request timeout in milliseconds (default: 5000)
36
+ * @returns API response or null on error
37
+ */
38
+ async track(name, eventData = {}, timeout = 5000) {
39
+ const url = `${this.baseUrl}/api/v1/ingest/events`;
40
+ const headers = {
41
+ 'Content-Type': 'application/json',
42
+ };
43
+ if (this.accessToken) {
44
+ headers['Authorization'] = `Bearer ${this.accessToken}`;
45
+ }
46
+ const payload = this.buildEventPayload({
47
+ name,
48
+ ...eventData,
49
+ });
50
+ try {
51
+ const controller = new AbortController();
52
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
53
+ const response = await fetch(url, {
54
+ method: 'POST',
55
+ headers,
56
+ body: JSON.stringify(payload),
57
+ signal: controller.signal,
58
+ });
59
+ clearTimeout(timeoutId);
60
+ if (response.ok) {
61
+ const text = await response.text();
62
+ return text ? JSON.parse(text) : {};
63
+ }
64
+ // Swallow error; return null per Python SDK behavior
65
+ return null;
66
+ }
67
+ catch (error) {
68
+ // Swallow error; return null per Python SDK behavior
69
+ return null;
70
+ }
71
+ }
72
+ /**
73
+ * Track multiple events in one call
74
+ *
75
+ * @param events Array of event data objects, each must have a 'name' field
76
+ * @param timeout Request timeout in milliseconds (default: 10000)
77
+ * @returns API response or null on error
78
+ */
79
+ async trackBatch(events, timeout = 10000) {
80
+ const url = `${this.baseUrl}/api/v1/ingest/events/batch`;
81
+ const headers = {
82
+ 'Content-Type': 'application/json',
83
+ };
84
+ if (this.accessToken) {
85
+ headers['Authorization'] = `Bearer ${this.accessToken}`;
86
+ }
87
+ const batchPayload = {
88
+ project_id: this.projectId,
89
+ app_id: this.appId,
90
+ env: this.env,
91
+ events: events.map(event => this.buildEventPayload(event)),
92
+ };
93
+ try {
94
+ const controller = new AbortController();
95
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
96
+ const response = await fetch(url, {
97
+ method: 'POST',
98
+ headers,
99
+ body: JSON.stringify(batchPayload),
100
+ signal: controller.signal,
101
+ });
102
+ clearTimeout(timeoutId);
103
+ if (response.ok) {
104
+ const text = await response.text();
105
+ return text ? JSON.parse(text) : {};
106
+ }
107
+ return null;
108
+ }
109
+ catch (error) {
110
+ return null;
111
+ }
112
+ }
113
+ /**
114
+ * Convenience method for identity linking
115
+ *
116
+ * @param userId User ID (required)
117
+ * @param visitorId Visitor ID (optional)
118
+ * @param traits Additional user traits/properties
119
+ * @returns API response or null on error
120
+ */
121
+ async identify(userId, visitorId, traits) {
122
+ return this.track('identify', {
123
+ userId,
124
+ visitorId,
125
+ properties: traits,
126
+ });
127
+ }
128
+ /**
129
+ * Builds the event payload for the API
130
+ */
131
+ buildEventPayload(eventData) {
132
+ const now = new Date().toISOString();
133
+ const eventId = (0, uuid_1.v4)();
134
+ return {
135
+ project_id: this.projectId,
136
+ app_id: this.appId,
137
+ env: this.env,
138
+ event_id: eventId,
139
+ occurred_at: now,
140
+ sent_at: now,
141
+ origin: eventData.origin || 'real',
142
+ identity: {
143
+ visitor_id: eventData.visitorId || null,
144
+ user_id: eventData.userId || null,
145
+ session_id: eventData.sessionId || null,
146
+ },
147
+ event: {
148
+ name: eventData.name,
149
+ category: eventData.category || null,
150
+ action: eventData.action || null,
151
+ label: eventData.label || null,
152
+ value: eventData.value || null,
153
+ },
154
+ context: eventData.context || {},
155
+ properties: eventData.properties || {},
156
+ };
157
+ }
158
+ /**
159
+ * Normalizes base URL by removing trailing slashes
160
+ */
161
+ normalizeBaseUrl(url) {
162
+ return url.endsWith('/') ? url.slice(0, -1) : url;
163
+ }
164
+ /**
165
+ * Reads environment variable (Node.js only, returns undefined in browser)
166
+ */
167
+ readEnv(key) {
168
+ // In browser environment, process.env is not available
169
+ if (typeof process !== 'undefined' && process.env) {
170
+ return process.env[key];
171
+ }
172
+ return undefined;
173
+ }
174
+ }
175
+ exports.SentinelAnalyticsRealmPlugin = SentinelAnalyticsRealmPlugin;
176
+ // Default export
177
+ exports.default = SentinelAnalyticsRealmPlugin;
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@savant-realms/sentinel-analytics-realm",
3
+ "version": "0.0.3",
4
+ "description": "TypeScript/JavaScript SDK for Sentinel Analytics Realm",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "test": "jest",
14
+ "lint": "eslint src --ext .ts",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "keywords": [
18
+ "analytics",
19
+ "tracking",
20
+ "sentinel",
21
+ "savant-realms"
22
+ ],
23
+ "author": "Savant Realms",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://gitlab.com/savant-realms/sentinel-analytics-realm-ts-js.git"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "devDependencies": {
33
+ "@types/jest": "^29.5.0",
34
+ "@types/node": "^20.0.0",
35
+ "@types/uuid": "^9.0.0",
36
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
37
+ "@typescript-eslint/parser": "^6.0.0",
38
+ "eslint": "^8.0.0",
39
+ "jest": "^29.5.0",
40
+ "ts-jest": "^29.1.0",
41
+ "typescript": "^5.0.0"
42
+ },
43
+ "dependencies": {
44
+ "uuid": "^9.0.0"
45
+ },
46
+ "peerDependencies": {},
47
+ "engines": {
48
+ "node": ">=14.0.0"
49
+ }
50
+ }