@churnsignal/churnsignal-sdk-web 0.1.2 → 0.1.4
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 +68 -0
- package/dist/api-client.d.ts +1 -2
- package/dist/churnsignal-sdk-web.cjs.development.js +98 -37
- package/dist/churnsignal-sdk-web.cjs.development.js.map +1 -1
- package/dist/churnsignal-sdk-web.cjs.production.min.js +1 -1
- package/dist/churnsignal-sdk-web.cjs.production.min.js.map +1 -1
- package/dist/churnsignal-sdk-web.esm.js +98 -37
- package/dist/churnsignal-sdk-web.esm.js.map +1 -1
- package/dist/core/initialise.d.ts +4 -7
- package/dist/core/track.d.ts +1 -1
- package/dist/managers/sdk-instance-manager.d.ts +4 -3
- package/dist/utils/api-key-parser.d.ts +17 -0
- package/dist/utils/request-transformations.d.ts +1 -5
- package/package.json +1 -1
- package/src/api-client.ts +8 -10
- package/src/core/identify.ts +8 -0
- package/src/core/initialise.test.ts +14 -19
- package/src/core/initialise.ts +5 -13
- package/src/core/track.ts +2 -2
- package/src/managers/sdk-instance-manager.ts +20 -14
- package/src/utils/api-key-parser.ts +76 -0
- package/src/utils/request-transformations.ts +4 -12
package/README.md
CHANGED
|
@@ -1,6 +1,74 @@
|
|
|
1
1
|
# CHURNSIGNAL SDK WEB
|
|
2
2
|
This SDK allows developers to seamlessly integrate ChurnSignal into their Web applications
|
|
3
3
|
|
|
4
|
+
## Installation
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install @churnsignal/churnsignal-sdk-web
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
### Initialize the SDK
|
|
13
|
+
|
|
14
|
+
The SDK only requires your API key. All configuration (workspaceId, tenantId, baseUrl) is automatically extracted from the API key.
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { initialise, identify, track } from '@churnsignal/churnsignal-sdk-web';
|
|
18
|
+
|
|
19
|
+
// Initialize with your API key
|
|
20
|
+
const success = initialise(API_KEY);
|
|
21
|
+
|
|
22
|
+
if (success) {
|
|
23
|
+
console.log('SDK initialized successfully');
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Track Events
|
|
28
|
+
|
|
29
|
+
Track user events with optional properties and user email:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// Track an event with email and properties
|
|
33
|
+
track('page_viewed', { page: '/home' }, 'user@example.com');
|
|
34
|
+
|
|
35
|
+
// Track an event with just email
|
|
36
|
+
track('button_clicked', undefined, 'user@example.com');
|
|
37
|
+
|
|
38
|
+
// Track an event with properties and email
|
|
39
|
+
track('purchase_completed', {
|
|
40
|
+
productId: '123',
|
|
41
|
+
amount: 99.99,
|
|
42
|
+
currency: 'USD'
|
|
43
|
+
}, 'user@example.com');
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Note:** You can also use `identify()` to set the user email once, then call `track()` without the email parameter:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { initialise, identify, track } from '@churnsignal/churnsignal-sdk-web';
|
|
50
|
+
|
|
51
|
+
initialise('your-api-key-here');
|
|
52
|
+
identify({ email: 'user@example.com' });
|
|
53
|
+
|
|
54
|
+
// Now you can track without passing email each time
|
|
55
|
+
track('page_viewed', { page: '/home' });
|
|
56
|
+
track('product_viewed', { productId: 'abc123' });
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Complete Example
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { initialise, track } from '@churnsignal/churnsignal-sdk-web';
|
|
63
|
+
|
|
64
|
+
// Initialize
|
|
65
|
+
initialise('your-api-key-here');
|
|
66
|
+
|
|
67
|
+
// Track events with email
|
|
68
|
+
track('page_viewed', { page: '/home' }, 'user@example.com');
|
|
69
|
+
track('product_viewed', { productId: 'abc123' }, 'user@example.com');
|
|
70
|
+
```
|
|
71
|
+
|
|
4
72
|
## Commands
|
|
5
73
|
|
|
6
74
|
### Install dependencies
|
package/dist/api-client.d.ts
CHANGED
|
@@ -14,7 +14,6 @@ declare class ApiClient {
|
|
|
14
14
|
private apiKey;
|
|
15
15
|
private baseUrl?;
|
|
16
16
|
private workspaceId;
|
|
17
|
-
private tenantId?;
|
|
18
17
|
private user?;
|
|
19
18
|
constructor({ apiKey, baseUrl, config }: ApiClientProps);
|
|
20
19
|
fetchWithConfig: (endpoint: string, options?: RequestInit) => Promise<Response>;
|
|
@@ -23,6 +22,6 @@ declare class ApiClient {
|
|
|
23
22
|
getWorkspaceId(): string;
|
|
24
23
|
getUser(): User | undefined;
|
|
25
24
|
identify(user: User): void;
|
|
26
|
-
track(eventName: string, eventProperties?: EventProperties): void;
|
|
25
|
+
track(eventName: string, eventProperties?: EventProperties, userEmail?: string): void;
|
|
27
26
|
}
|
|
28
27
|
export default ApiClient;
|
|
@@ -28,6 +28,62 @@ var sanitiseUrl = function sanitiseUrl(url) {
|
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
var DEFAULT_BASE_URLS = {
|
|
32
|
+
live: 'https://rjwzrk7gs1.execute-api.eu-central-1.amazonaws.com/'
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Parses an API key to extract workspaceId, tenantId, and baseUrl
|
|
36
|
+
* Expected format: cs_{environment}_tenantId_workspaceId_secret
|
|
37
|
+
* Matches backend parsing logic
|
|
38
|
+
*
|
|
39
|
+
* @param apiKey - The API key to parse
|
|
40
|
+
* @returns ParsedApiKey object with extracted data
|
|
41
|
+
* @throws Error if the API key format is invalid
|
|
42
|
+
*/
|
|
43
|
+
var parseApiKey = function parseApiKey(apiKey) {
|
|
44
|
+
if (!apiKey || typeof apiKey !== 'string') {
|
|
45
|
+
throw new Error('API key must be a non-empty string');
|
|
46
|
+
}
|
|
47
|
+
// Check if it starts with cs_ prefix
|
|
48
|
+
if (!apiKey.startsWith('cs_')) {
|
|
49
|
+
throw new Error('Invalid API key format. Must start with "cs_"');
|
|
50
|
+
}
|
|
51
|
+
// Find the environment (cs_live_, cs_test_, cs_dev_, etc.)
|
|
52
|
+
var parts = apiKey.split('_');
|
|
53
|
+
if (parts.length < 2) {
|
|
54
|
+
throw new Error('Invalid API key format. Must include environment (e.g., cs_live_)');
|
|
55
|
+
}
|
|
56
|
+
var environment = parts[1];
|
|
57
|
+
var prefix = "cs_" + environment + "_";
|
|
58
|
+
if (!apiKey.startsWith(prefix)) {
|
|
59
|
+
throw new Error("Invalid API key format. Must start with \"" + prefix + "\"");
|
|
60
|
+
}
|
|
61
|
+
// Remove prefix (e.g., 'cs_live_')
|
|
62
|
+
var withoutPrefix = apiKey.substring(prefix.length);
|
|
63
|
+
// Split by underscore - format: tenantId_workspaceId_secret
|
|
64
|
+
var keyParts = withoutPrefix.split('_');
|
|
65
|
+
if (keyParts.length < 3) {
|
|
66
|
+
throw new Error('Invalid API key format. Expected format: cs_{environment}_tenantId_workspaceId_secret');
|
|
67
|
+
}
|
|
68
|
+
var tenantId = keyParts[0];
|
|
69
|
+
var workspaceId = keyParts[1];
|
|
70
|
+
// The rest (keyParts[2] onwards) is the secret, but we don't need it for parsing
|
|
71
|
+
if (!tenantId) {
|
|
72
|
+
throw new Error('Tenant ID not found in API key');
|
|
73
|
+
}
|
|
74
|
+
if (!workspaceId) {
|
|
75
|
+
throw new Error('Workspace ID not found in API key');
|
|
76
|
+
}
|
|
77
|
+
// Determine baseUrl from environment
|
|
78
|
+
var baseUrl = DEFAULT_BASE_URLS[environment] || DEFAULT_BASE_URLS.live;
|
|
79
|
+
return {
|
|
80
|
+
environment: environment,
|
|
81
|
+
workspaceId: workspaceId,
|
|
82
|
+
tenantId: tenantId,
|
|
83
|
+
baseUrl: baseUrl
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
31
87
|
function _extends() {
|
|
32
88
|
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
33
89
|
for (var i = 1; i < arguments.length; i++) {
|
|
@@ -46,16 +102,12 @@ function _extends() {
|
|
|
46
102
|
var transformTrackRequestData = function transformTrackRequestData(_ref) {
|
|
47
103
|
var eventName = _ref.eventName,
|
|
48
104
|
eventProperties = _ref.eventProperties,
|
|
49
|
-
userProperties = _ref.userProperties
|
|
50
|
-
configProperties = _ref.configProperties;
|
|
105
|
+
userProperties = _ref.userProperties;
|
|
51
106
|
var transformedEvent = {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
workspaceId: configProperties.workspaceId,
|
|
57
|
-
type: 'event',
|
|
58
|
-
data: eventProperties
|
|
107
|
+
type: eventName,
|
|
108
|
+
data: eventProperties || {},
|
|
109
|
+
timestamp: Date.now(),
|
|
110
|
+
userEmail: userProperties == null ? void 0 : userProperties.email
|
|
59
111
|
};
|
|
60
112
|
return JSON.stringify(transformedEvent);
|
|
61
113
|
};
|
|
@@ -98,7 +150,6 @@ var ApiClient = /*#__PURE__*/function () {
|
|
|
98
150
|
this.apiKey = apiKey;
|
|
99
151
|
this.baseUrl = baseUrl;
|
|
100
152
|
this.workspaceId = config.workspaceId;
|
|
101
|
-
this.tenantId = config.tenantId;
|
|
102
153
|
}
|
|
103
154
|
var _proto = ApiClient.prototype;
|
|
104
155
|
_proto.getApiKey = function getApiKey() {
|
|
@@ -122,21 +173,20 @@ var ApiClient = /*#__PURE__*/function () {
|
|
|
122
173
|
// }),
|
|
123
174
|
// });
|
|
124
175
|
};
|
|
125
|
-
_proto.track = function track(eventName, eventProperties) {
|
|
126
|
-
|
|
127
|
-
|
|
176
|
+
_proto.track = function track(eventName, eventProperties, userEmail) {
|
|
177
|
+
var _this$user;
|
|
178
|
+
// Use provided email, or fall back to identified user's email
|
|
179
|
+
var email = userEmail || ((_this$user = this.user) == null ? void 0 : _this$user.email);
|
|
180
|
+
if (!email) {
|
|
181
|
+
throw new Error('User email must be provided either via identify() or as a parameter to track()');
|
|
128
182
|
}
|
|
129
183
|
this.fetchWithConfig(TRACK_ENDPOINT, {
|
|
130
184
|
method: 'POST',
|
|
131
185
|
body: transformTrackRequestData({
|
|
132
186
|
eventName: eventName,
|
|
133
187
|
eventProperties: eventProperties,
|
|
134
|
-
configProperties: {
|
|
135
|
-
workspaceId: this.getWorkspaceId(),
|
|
136
|
-
tenantId: this.tenantId
|
|
137
|
-
},
|
|
138
188
|
userProperties: {
|
|
139
|
-
email:
|
|
189
|
+
email: email
|
|
140
190
|
}
|
|
141
191
|
})
|
|
142
192
|
});
|
|
@@ -162,31 +212,33 @@ var SDKInstanceManager = /*#__PURE__*/function () {
|
|
|
162
212
|
/**
|
|
163
213
|
* @throws Error in case validation of parameters fails
|
|
164
214
|
* @param apiKey - required string value representing the API key
|
|
165
|
-
*
|
|
166
|
-
*
|
|
215
|
+
* Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
|
|
216
|
+
* The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
|
|
217
|
+
* but the original unparsed API key is sent to the API in requests
|
|
167
218
|
* @returns void
|
|
168
219
|
*/
|
|
169
220
|
var _proto = SDKInstanceManager.prototype;
|
|
170
|
-
_proto.initialise = function initialise(apiKey
|
|
221
|
+
_proto.initialise = function initialise(apiKey) {
|
|
171
222
|
if (this.getIsInitialised()) {
|
|
172
223
|
console.info('SDK is already initialised with API key');
|
|
173
224
|
return;
|
|
174
225
|
}
|
|
175
226
|
if (!apiKey || typeof apiKey !== 'string') {
|
|
176
|
-
throw new Error('SDK needs a valid API key to be
|
|
177
|
-
}
|
|
178
|
-
if (!baseUrl || typeof baseUrl !== 'string' || !isValidUrl(baseUrl)) {
|
|
179
|
-
throw new Error('SDK needs a valid base URL to be initialised');
|
|
180
|
-
}
|
|
181
|
-
if (!config.workspaceId) {
|
|
182
|
-
throw new Error('Workspace ID must be provided');
|
|
227
|
+
throw new Error('SDK needs a valid API key to be initialised');
|
|
183
228
|
}
|
|
184
|
-
|
|
185
|
-
|
|
229
|
+
// Parse the API key to extract workspaceId, tenantId, and baseUrl for SDK configuration
|
|
230
|
+
// The original unparsed API key will be sent to the API
|
|
231
|
+
var parsed = parseApiKey(apiKey);
|
|
232
|
+
this.config = {
|
|
233
|
+
workspaceId: parsed.workspaceId,
|
|
234
|
+
tenantId: parsed.tenantId
|
|
235
|
+
};
|
|
236
|
+
var sanitisedUrl = sanitiseUrl(parsed.baseUrl);
|
|
237
|
+
// Pass the original unparsed API key to ApiClient - it will be sent as-is to the API
|
|
186
238
|
this.apiClient = new ApiClient({
|
|
187
239
|
apiKey: apiKey,
|
|
188
240
|
baseUrl: sanitisedUrl,
|
|
189
|
-
config: config
|
|
241
|
+
config: this.config
|
|
190
242
|
});
|
|
191
243
|
this.isInitialised = true;
|
|
192
244
|
};
|
|
@@ -211,13 +263,14 @@ var instance = /*#__PURE__*/new SDKInstanceManager();
|
|
|
211
263
|
|
|
212
264
|
/**
|
|
213
265
|
* @param apiKey - required string value representing the API key
|
|
214
|
-
*
|
|
215
|
-
*
|
|
266
|
+
* Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
|
|
267
|
+
* The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
|
|
268
|
+
* but the original unparsed API key is sent to the API in requests
|
|
216
269
|
* @returns boolean indicating whether the initialisation of the sdk was successful or not
|
|
217
270
|
*/
|
|
218
|
-
var initialise = function initialise(apiKey
|
|
271
|
+
var initialise = function initialise(apiKey) {
|
|
219
272
|
try {
|
|
220
|
-
instance.initialise(apiKey
|
|
273
|
+
instance.initialise(apiKey);
|
|
221
274
|
return true;
|
|
222
275
|
} catch (e) {
|
|
223
276
|
console.info(e == null ? void 0 : e.message);
|
|
@@ -227,10 +280,18 @@ var initialise = function initialise(apiKey, baseUrl, config) {
|
|
|
227
280
|
|
|
228
281
|
var identify = function identify(user) {
|
|
229
282
|
var _sdkInstanceManager$g;
|
|
283
|
+
if (!instance.getIsInitialised()) {
|
|
284
|
+
console.error('SDK must be initialised first');
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (!(user != null && user.email)) {
|
|
288
|
+
console.error('User email must be provided');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
230
291
|
(_sdkInstanceManager$g = instance.getApiClient()) == null || _sdkInstanceManager$g.identify(user);
|
|
231
292
|
};
|
|
232
293
|
|
|
233
|
-
var track = function track(eventName, eventProperties) {
|
|
294
|
+
var track = function track(eventName, eventProperties, userEmail) {
|
|
234
295
|
var _sdkInstanceManager$g;
|
|
235
296
|
if (!instance.getIsInitialised()) {
|
|
236
297
|
console.error('SDK must be initialised first');
|
|
@@ -240,7 +301,7 @@ var track = function track(eventName, eventProperties) {
|
|
|
240
301
|
console.error('Event name must be provided');
|
|
241
302
|
return;
|
|
242
303
|
}
|
|
243
|
-
(_sdkInstanceManager$g = instance.getApiClient()) == null || _sdkInstanceManager$g.track(eventName, eventProperties);
|
|
304
|
+
(_sdkInstanceManager$g = instance.getApiClient()) == null || _sdkInstanceManager$g.track(eventName, eventProperties, userEmail);
|
|
244
305
|
};
|
|
245
306
|
|
|
246
307
|
exports.identify = identify;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"churnsignal-sdk-web.cjs.development.js","sources":["../src/utils/url-validator.ts","../src/utils/request-transformations.ts","../src/api-client.ts","../src/managers/sdk-instance-manager.ts","../src/core/initialise.ts","../src/core/identify.ts","../src/core/track.ts"],"sourcesContent":["const urlRegex = /^https?:\\/\\//;\n\nexport const isValidUrl = (url: string) => urlRegex.test(url);\n\nexport const sanitiseUrl = (url: string) => {\n // Trim whitespace\n const sanitisedUrl = url.trim();\n\n // Validate scheme\n if (!isValidUrl(url)) {\n throw new Error(\"URL must start with 'http://' or 'https://'\");\n }\n\n try {\n let urlObject = new URL(sanitisedUrl);\n\n urlObject.hostname = urlObject.hostname.toLowerCase();\n\n // Remove trailing slash\n if (urlObject.pathname !== '/') {\n urlObject.pathname = urlObject.pathname.replace(/\\/+$/, '') || '';\n }\n\n // Reconstruct the URL from its components\n // Note: The URL interface automatically handles encoding of the pathname\n return urlObject.toString().replace(/\\/+$/, '');\n } catch (e) {\n throw new Error('Invalid URL provided');\n }\n};\n","import { EventProperties } from '../api-client';\n\ntype TrackRequest = {\n eventName: string;\n eventProperties?: EventProperties;\n userProperties?: {\n email: string;\n };\n configProperties: {\n workspaceId: string;\n tenantId?: string\n };\n};\n\nexport const transformTrackRequestData = ({\n eventName,\n eventProperties,\n userProperties,\n configProperties,\n}: TrackRequest) => {\n const transformedEvent = {\n name: eventName,\n timestamp: new Date().getTime().toString(),\n userEmail: userProperties?.email,\n tenantId: configProperties.tenantId, // TODO this will be handled on the API side, for now pass it in SDK setup\n workspaceId: configProperties.workspaceId,\n type: 'event',\n data: eventProperties,\n };\n return JSON.stringify(transformedEvent);\n};\n\ntype IdentifyRequest = {\n email: string\n}\n\nexport const transformIdentifyRequestData = ({\n email\n}: IdentifyRequest) => {\n const transformedIdentify = {\n userEmail: email\n }\n return JSON.stringify(transformedIdentify);\n}","import {\n transformTrackRequestData,\n // transformIdentifyRequestData,\n} from './utils/request-transformations';\n\ntype ApiClientProps = {\n apiKey: string;\n baseUrl?: string;\n config: {\n workspaceId: string;\n tenantId?: string;\n };\n};\n\nexport type EventProperties = Record<string, any>;\n\ntype User = {\n email: string;\n};\n\nconst sdkConfig = {\n defaultHeaders: {\n 'Content-Type': 'application/json',\n },\n};\n\nconst API_PREFIX = 'sdk-prod';\n\nconst TRACK_ENDPOINT = `${API_PREFIX}/track`;\n// const IDENTIFY_USER_ENDPOINT = `${API_PREFIX}/identify`;\n\nclass ApiClient {\n private apiKey: string;\n private baseUrl?: string;\n private workspaceId: string = '';\n private tenantId?: string;\n private user?: User;\n\n constructor({ apiKey, baseUrl, config }: ApiClientProps) {\n this.apiKey = apiKey;\n this.baseUrl = baseUrl;\n this.workspaceId = config.workspaceId;\n this.tenantId = config.tenantId;\n }\n\n // Wrapper function for fetch\n fetchWithConfig = (\n endpoint: string,\n options: RequestInit = { method: 'GET' }\n ) => {\n // Construct the full URL\n const url = `${this.getBaseUrl()}/${endpoint}`;\n\n // Merge the default headers with any headers provided in the options\n const headers = {\n ...sdkConfig.defaultHeaders,\n 'x-api-key': this.getApiKey(),\n ...(options.headers || {}),\n };\n\n // Merge the rest of the options with the headers\n const config = {\n ...options,\n headers,\n };\n\n // Execute the fetch call with the merged configuration\n return fetch(url, config);\n };\n\n getApiKey() {\n return this.apiKey;\n }\n\n getBaseUrl() {\n return this.baseUrl;\n }\n\n getWorkspaceId() {\n return this.workspaceId;\n }\n\n getUser() {\n return this.user;\n }\n\n identify(user: User) {\n this.user = user;\n // this.fetchWithConfig(IDENTIFY_USER_ENDPOINT, {\n // method: 'POST',\n // body: transformIdentifyRequestData({\n // ...user,\n // }),\n // });\n }\n\n track(eventName: string, eventProperties?: EventProperties) {\n if (!this.user) {\n throw new Error('No identified users to track');\n }\n this.fetchWithConfig(TRACK_ENDPOINT, {\n method: 'POST',\n body: transformTrackRequestData({\n eventName,\n eventProperties,\n configProperties: {\n workspaceId: this.getWorkspaceId(),\n tenantId: this.tenantId,\n },\n userProperties: {\n email: this.user.email,\n },\n }),\n });\n }\n}\n\nexport default ApiClient;\n","import { isValidUrl, sanitiseUrl } from '../utils/url-validator';\nimport ApiClient from './../api-client';\n\ntype Config = {\n workspaceId: string;\n tenantId?: string;\n};\n\nclass SDKInstanceManager {\n private static instance: SDKInstanceManager;\n private isInitialised: boolean = false;\n private config: Config = { workspaceId: '' };\n private apiClient?: ApiClient;\n\n constructor() {\n if (!SDKInstanceManager.instance) {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n SDKInstanceManager.instance = this;\n }\n\n return SDKInstanceManager.instance;\n }\n\n /**\n * @throws Error in case validation of parameters fails\n * @param apiKey - required string value representing the API key\n * @param baseUrl - required string value representing the API endpoint\n * @param config - required object value\n * @returns void\n */\n initialise(apiKey: string, baseUrl: string, config: Config) {\n if (this.getIsInitialised()) {\n console.info('SDK is already initialised with API key');\n return;\n }\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('SDK needs a valid API key to be inialised');\n }\n if (!baseUrl || typeof baseUrl !== 'string' || !isValidUrl(baseUrl)) {\n throw new Error('SDK needs a valid base URL to be initialised');\n }\n if (!config.workspaceId) {\n throw new Error('Workspace ID must be provided');\n }\n this.config = config;\n let sanitisedUrl = sanitiseUrl(baseUrl);\n this.apiClient = new ApiClient({ apiKey, baseUrl: sanitisedUrl, config });\n this.isInitialised = true;\n }\n\n destroy() {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n }\n\n getIsInitialised() {\n return this.isInitialised;\n }\n\n getApiClient() {\n return this.apiClient;\n }\n\n getConfig() {\n return this.config;\n }\n}\n\nconst instance = new SDKInstanceManager();\nexport { instance as default };\n","import sdkInstanceManager from './../managers/sdk-instance-manager';\n\nexport type InitConfig = {\n workspaceId: string\n tenantId: string; // TODO to be removed should be done on the API side\n};\n\n/**\n * @param apiKey - required string value representing the API key\n * @param baseUrl - required string value representing the API endpoint\n * @param options - optional object value\n * @returns boolean indicating whether the initialisation of the sdk was successful or not\n */\nexport const initialise = (\n apiKey: string,\n baseUrl: string,\n config: InitConfig\n) => {\n try {\n sdkInstanceManager.initialise(apiKey, baseUrl, config);\n return true;\n } catch (e) {\n console.info((e as any)?.message);\n return false;\n }\n};\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\nexport const identify = (user: { email: string }) => {\n sdkInstanceManager.getApiClient()?.identify(user);\n};\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\ntype Properties = Record<string, any>;\n\nexport const track = (eventName: string, eventProperties?: Properties) => {\n if (!sdkInstanceManager.getIsInitialised()) {\n console.error('SDK must be initialised first');\n return;\n }\n if (!eventName || typeof eventName !== 'string') {\n console.error('Event name must be provided');\n return;\n }\n sdkInstanceManager.getApiClient()?.track(eventName, eventProperties);\n};\n"],"names":["urlRegex","isValidUrl","url","test","sanitiseUrl","sanitisedUrl","trim","Error","urlObject","URL","hostname","toLowerCase","pathname","replace","toString","e","transformTrackRequestData","_ref","eventName","eventProperties","userProperties","configProperties","transformedEvent","name","timestamp","Date","getTime","userEmail","email","tenantId","workspaceId","type","data","JSON","stringify","sdkConfig","defaultHeaders","API_PREFIX","TRACK_ENDPOINT","ApiClient","apiKey","baseUrl","config","endpoint","options","method","_this","getBaseUrl","headers","_extends","getApiKey","fetch","_proto","prototype","getWorkspaceId","getUser","user","identify","track","fetchWithConfig","body","SDKInstanceManager","instance","isInitialised","initialise","getIsInitialised","console","info","apiClient","destroy","getApiClient","getConfig","sdkInstanceManager","message","_sdkInstanceManager$g","error"],"mappings":";;;;AAAA,IAAMA,QAAQ,GAAG,cAAc;AAExB,IAAMC,UAAU,GAAG,SAAbA,UAAUA,CAAIC,GAAW;EAAA,OAAKF,QAAQ,CAACG,IAAI,CAACD,GAAG,CAAC;AAAA;AAEtD,IAAME,WAAW,GAAG,SAAdA,WAAWA,CAAIF,GAAW;;EAErC,IAAMG,YAAY,GAAGH,GAAG,CAACI,IAAI,EAAE;;EAG/B,IAAI,CAACL,UAAU,CAACC,GAAG,CAAC,EAAE;IACpB,MAAM,IAAIK,KAAK,CAAC,6CAA6C,CAAC;;EAGhE,IAAI;IACF,IAAIC,SAAS,GAAG,IAAIC,GAAG,CAACJ,YAAY,CAAC;IAErCG,SAAS,CAACE,QAAQ,GAAGF,SAAS,CAACE,QAAQ,CAACC,WAAW,EAAE;;IAGrD,IAAIH,SAAS,CAACI,QAAQ,KAAK,GAAG,EAAE;MAC9BJ,SAAS,CAACI,QAAQ,GAAGJ,SAAS,CAACI,QAAQ,CAACC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,EAAE;;;;IAKnE,OAAOL,SAAS,CAACM,QAAQ,EAAE,CAACD,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;GAChD,CAAC,OAAOE,CAAC,EAAE;IACV,MAAM,IAAIR,KAAK,CAAC,sBAAsB,CAAC;;AAE3C,CAAC;;;;;;;;;;;;;;;;;ACfM,IAAMS,yBAAyB,GAAG,SAA5BA,yBAAyBA,CAAAC,IAAA;MACpCC,SAAS,GAAAD,IAAA,CAATC,SAAS;IACTC,eAAe,GAAAF,IAAA,CAAfE,eAAe;IACfC,cAAc,GAAAH,IAAA,CAAdG,cAAc;IACdC,gBAAgB,GAAAJ,IAAA,CAAhBI,gBAAgB;EAEhB,IAAMC,gBAAgB,GAAG;IACvBC,IAAI,EAAEL,SAAS;IACfM,SAAS,EAAE,IAAIC,IAAI,EAAE,CAACC,OAAO,EAAE,CAACZ,QAAQ,EAAE;IAC1Ca,SAAS,EAAEP,cAAc,oBAAdA,cAAc,CAAEQ,KAAK;IAChCC,QAAQ,EAAER,gBAAgB,CAACQ,QAAQ;IACnCC,WAAW,EAAET,gBAAgB,CAACS,WAAW;IACzCC,IAAI,EAAE,OAAO;IACbC,IAAI,EAAEb;GACP;EACD,OAAOc,IAAI,CAACC,SAAS,CAACZ,gBAAgB,CAAC;AACzC,CAAC;;ACVD,IAAMa,SAAS,GAAG;EAChBC,cAAc,EAAE;IACd,cAAc,EAAE;;CAEnB;AAED,IAAMC,UAAU,GAAG,UAAU;AAE7B,IAAMC,cAAc,GAAMD,UAAU,WAAQ;AAC5C;AAAA,IAEME,SAAS;EAOb,SAAAA,UAAAtB,IAAA;;QAAcuB,MAAM,GAAAvB,IAAA,CAANuB,MAAM;MAAEC,OAAO,GAAAxB,IAAA,CAAPwB,OAAO;MAAEC,MAAM,GAAAzB,IAAA,CAANyB,MAAM;IAJ7B,gBAAW,GAAW,EAAE;;IAYhC,oBAAe,GAAG,UAChBC,QAAgB,EAChBC;UAAAA;QAAAA,UAAuB;UAAEC,MAAM,EAAE;SAAO;;;MAGxC,IAAM3C,GAAG,GAAM4C,KAAI,CAACC,UAAU,EAAE,SAAIJ,QAAU;;MAG9C,IAAMK,OAAO,GAAAC,QAAA,KACRd,SAAS,CAACC,cAAc;QAC3B,WAAW,EAAEU,KAAI,CAACI,SAAS;SACvBN,OAAO,CAACI,OAAO,IAAI,EAAE,CAC1B;;MAGD,IAAMN,MAAM,GAAAO,QAAA,KACPL,OAAO;QACVI,OAAO,EAAPA;QACD;;MAGD,OAAOG,KAAK,CAACjD,GAAG,EAAEwC,MAAM,CAAC;KAC1B;IA7BC,IAAI,CAACF,MAAM,GAAGA,MAAM;IACpB,IAAI,CAACC,OAAO,GAAGA,OAAO;IACtB,IAAI,CAACX,WAAW,GAAGY,MAAM,CAACZ,WAAW;IACrC,IAAI,CAACD,QAAQ,GAAGa,MAAM,CAACb,QAAQ;;EAChC,IAAAuB,MAAA,GAAAb,SAAA,CAAAc,SAAA;EAAAD,MAAA,CA2BDF,SAAS,GAAT,SAAAA;IACE,OAAO,IAAI,CAACV,MAAM;GACnB;EAAAY,MAAA,CAEDL,UAAU,GAAV,SAAAA;IACE,OAAO,IAAI,CAACN,OAAO;GACpB;EAAAW,MAAA,CAEDE,cAAc,GAAd,SAAAA;IACE,OAAO,IAAI,CAACxB,WAAW;GACxB;EAAAsB,MAAA,CAEDG,OAAO,GAAP,SAAAA;IACE,OAAO,IAAI,CAACC,IAAI;GACjB;EAAAJ,MAAA,CAEDK,QAAQ,GAAR,SAAAA,SAASD,IAAU;IACjB,IAAI,CAACA,IAAI,GAAGA,IAAI;;;;;;;GAOjB;EAAAJ,MAAA,CAEDM,KAAK,GAAL,SAAAA,MAAMxC,SAAiB,EAAEC,eAAiC;IACxD,IAAI,CAAC,IAAI,CAACqC,IAAI,EAAE;MACd,MAAM,IAAIjD,KAAK,CAAC,8BAA8B,CAAC;;IAEjD,IAAI,CAACoD,eAAe,CAACrB,cAAc,EAAE;MACnCO,MAAM,EAAE,MAAM;MACde,IAAI,EAAE5C,yBAAyB,CAAC;QAC9BE,SAAS,EAATA,SAAS;QACTC,eAAe,EAAfA,eAAe;QACfE,gBAAgB,EAAE;UAChBS,WAAW,EAAE,IAAI,CAACwB,cAAc,EAAE;UAClCzB,QAAQ,EAAE,IAAI,CAACA;SAChB;QACDT,cAAc,EAAE;UACdQ,KAAK,EAAE,IAAI,CAAC4B,IAAI,CAAC5B;;OAEpB;KACF,CAAC;GACH;EAAA,OAAAW,SAAA;AAAA;;ACjHqC,IAOlCsB,kBAAkB;EAMtB,SAAAA;IAJQ,kBAAa,GAAY,KAAK;IAC9B,WAAM,GAAW;MAAE/B,WAAW,EAAE;KAAI;IAI1C,IAAI,CAAC+B,kBAAkB,CAACC,QAAQ,EAAE;MAChC,IAAI,CAACpB,MAAM,GAAG;QAAEZ,WAAW,EAAE;OAAI;MACjC,IAAI,CAACiC,aAAa,GAAG,KAAK;MAC1BF,kBAAkB,CAACC,QAAQ,GAAG,IAAI;;IAGpC,OAAOD,kBAAkB,CAACC,QAAQ;;;;;;;;;EAGpC,IAAAV,MAAA,GAAAS,kBAAA,CAAAR,SAAA;EAAAD,MAAA,CAOAY,UAAU,GAAV,SAAAA,WAAWxB,MAAc,EAAEC,OAAe,EAAEC,MAAc;IACxD,IAAI,IAAI,CAACuB,gBAAgB,EAAE,EAAE;MAC3BC,OAAO,CAACC,IAAI,CAAC,yCAAyC,CAAC;MACvD;;IAEF,IAAI,CAAC3B,MAAM,IAAI,OAAOA,MAAM,KAAK,QAAQ,EAAE;MACzC,MAAM,IAAIjC,KAAK,CAAC,2CAA2C,CAAC;;IAE9D,IAAI,CAACkC,OAAO,IAAI,OAAOA,OAAO,KAAK,QAAQ,IAAI,CAACxC,UAAU,CAACwC,OAAO,CAAC,EAAE;MACnE,MAAM,IAAIlC,KAAK,CAAC,8CAA8C,CAAC;;IAEjE,IAAI,CAACmC,MAAM,CAACZ,WAAW,EAAE;MACvB,MAAM,IAAIvB,KAAK,CAAC,+BAA+B,CAAC;;IAElD,IAAI,CAACmC,MAAM,GAAGA,MAAM;IACpB,IAAIrC,YAAY,GAAGD,WAAW,CAACqC,OAAO,CAAC;IACvC,IAAI,CAAC2B,SAAS,GAAG,IAAI7B,SAAS,CAAC;MAAEC,MAAM,EAANA,MAAM;MAAEC,OAAO,EAAEpC,YAAY;MAAEqC,MAAM,EAANA;KAAQ,CAAC;IACzE,IAAI,CAACqB,aAAa,GAAG,IAAI;GAC1B;EAAAX,MAAA,CAEDiB,OAAO,GAAP,SAAAA;IACE,IAAI,CAAC3B,MAAM,GAAG;MAAEZ,WAAW,EAAE;KAAI;IACjC,IAAI,CAACiC,aAAa,GAAG,KAAK;GAC3B;EAAAX,MAAA,CAEDa,gBAAgB,GAAhB,SAAAA;IACE,OAAO,IAAI,CAACF,aAAa;GAC1B;EAAAX,MAAA,CAEDkB,YAAY,GAAZ,SAAAA;IACE,OAAO,IAAI,CAACF,SAAS;GACtB;EAAAhB,MAAA,CAEDmB,SAAS,GAAT,SAAAA;IACE,OAAO,IAAI,CAAC7B,MAAM;GACnB;EAAA,OAAAmB,kBAAA;AAAA;AAGH,IAAMC,QAAQ,gBAAG,IAAID,kBAAkB,EAAE;;AC9DzC;;;;;;AAMA,IAAaG,UAAU,GAAG,SAAbA,UAAUA,CACrBxB,MAAc,EACdC,OAAe,EACfC,MAAkB;EAElB,IAAI;IACF8B,QAAkB,CAACR,UAAU,CAACxB,MAAM,EAAEC,OAAO,EAAEC,MAAM,CAAC;IACtD,OAAO,IAAI;GACZ,CAAC,OAAO3B,CAAC,EAAE;IACVmD,OAAO,CAACC,IAAI,CAAEpD,CAAS,oBAATA,CAAS,CAAE0D,OAAO,CAAC;IACjC,OAAO,KAAK;;AAEhB,CAAC;;ICvBYhB,QAAQ,GAAG,SAAXA,QAAQA,CAAID,IAAuB;;EAC9C,CAAAkB,qBAAA,GAAAF,QAAkB,CAACF,YAAY,EAAE,aAAjCI,qBAAA,CAAmCjB,QAAQ,CAACD,IAAI,CAAC;AACnD,CAAC;;ICAYE,KAAK,GAAG,SAARA,KAAKA,CAAIxC,SAAiB,EAAEC,eAA4B;;EACnE,IAAI,CAACqD,QAAkB,CAACP,gBAAgB,EAAE,EAAE;IAC1CC,OAAO,CAACS,KAAK,CAAC,+BAA+B,CAAC;IAC9C;;EAEF,IAAI,CAACzD,SAAS,IAAI,OAAOA,SAAS,KAAK,QAAQ,EAAE;IAC/CgD,OAAO,CAACS,KAAK,CAAC,6BAA6B,CAAC;IAC5C;;EAEF,CAAAD,qBAAA,GAAAF,QAAkB,CAACF,YAAY,EAAE,aAAjCI,qBAAA,CAAmChB,KAAK,CAACxC,SAAS,EAAEC,eAAe,CAAC;AACtE,CAAC;;;;;;"}
|
|
1
|
+
{"version":3,"file":"churnsignal-sdk-web.cjs.development.js","sources":["../src/utils/url-validator.ts","../src/utils/api-key-parser.ts","../src/utils/request-transformations.ts","../src/api-client.ts","../src/managers/sdk-instance-manager.ts","../src/core/initialise.ts","../src/core/identify.ts","../src/core/track.ts"],"sourcesContent":["const urlRegex = /^https?:\\/\\//;\n\nexport const isValidUrl = (url: string) => urlRegex.test(url);\n\nexport const sanitiseUrl = (url: string) => {\n // Trim whitespace\n const sanitisedUrl = url.trim();\n\n // Validate scheme\n if (!isValidUrl(url)) {\n throw new Error(\"URL must start with 'http://' or 'https://'\");\n }\n\n try {\n let urlObject = new URL(sanitisedUrl);\n\n urlObject.hostname = urlObject.hostname.toLowerCase();\n\n // Remove trailing slash\n if (urlObject.pathname !== '/') {\n urlObject.pathname = urlObject.pathname.replace(/\\/+$/, '') || '';\n }\n\n // Reconstruct the URL from its components\n // Note: The URL interface automatically handles encoding of the pathname\n return urlObject.toString().replace(/\\/+$/, '');\n } catch (e) {\n throw new Error('Invalid URL provided');\n }\n};\n","type ParsedApiKey = {\n environment: string;\n workspaceId: string;\n tenantId: string;\n baseUrl: string;\n};\n\nconst DEFAULT_BASE_URLS: Record<string, string> = {\n live: 'https://rjwzrk7gs1.execute-api.eu-central-1.amazonaws.com/'\n};\n\n/**\n * Parses an API key to extract workspaceId, tenantId, and baseUrl\n * Expected format: cs_{environment}_tenantId_workspaceId_secret\n * Matches backend parsing logic\n * \n * @param apiKey - The API key to parse\n * @returns ParsedApiKey object with extracted data\n * @throws Error if the API key format is invalid\n */\nexport const parseApiKey = (apiKey: string): ParsedApiKey => {\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('API key must be a non-empty string');\n }\n\n // Check if it starts with cs_ prefix\n if (!apiKey.startsWith('cs_')) {\n throw new Error('Invalid API key format. Must start with \"cs_\"');\n }\n\n // Find the environment (cs_live_, cs_test_, cs_dev_, etc.)\n const parts = apiKey.split('_');\n if (parts.length < 2) {\n throw new Error('Invalid API key format. Must include environment (e.g., cs_live_)');\n }\n\n const environment = parts[1];\n const prefix = `cs_${environment}_`;\n \n if (!apiKey.startsWith(prefix)) {\n throw new Error(`Invalid API key format. Must start with \"${prefix}\"`);\n }\n\n // Remove prefix (e.g., 'cs_live_')\n const withoutPrefix = apiKey.substring(prefix.length);\n\n // Split by underscore - format: tenantId_workspaceId_secret\n const keyParts = withoutPrefix.split('_');\n\n if (keyParts.length < 3) {\n throw new Error('Invalid API key format. Expected format: cs_{environment}_tenantId_workspaceId_secret');\n }\n\n const tenantId = keyParts[0];\n const workspaceId = keyParts[1];\n // The rest (keyParts[2] onwards) is the secret, but we don't need it for parsing\n\n if (!tenantId) {\n throw new Error('Tenant ID not found in API key');\n }\n\n if (!workspaceId) {\n throw new Error('Workspace ID not found in API key');\n }\n\n // Determine baseUrl from environment\n const baseUrl = DEFAULT_BASE_URLS[environment] || DEFAULT_BASE_URLS.live;\n\n return {\n environment,\n workspaceId,\n tenantId,\n baseUrl,\n };\n};\n\n","import { EventProperties } from '../api-client';\n\ntype TrackRequest = {\n eventName: string;\n eventProperties?: EventProperties;\n userProperties?: {\n email: string;\n }\n};\n\nexport const transformTrackRequestData = ({\n eventName,\n eventProperties,\n userProperties,\n}: TrackRequest) => {\n const transformedEvent = {\n type: eventName,\n data: eventProperties || {},\n timestamp: Date.now(),\n userEmail: userProperties?.email,\n };\n return JSON.stringify(transformedEvent);\n};\n\ntype IdentifyRequest = {\n email: string\n}\n\nexport const transformIdentifyRequestData = ({\n email\n}: IdentifyRequest) => {\n const transformedIdentify = {\n userEmail: email\n }\n return JSON.stringify(transformedIdentify);\n}","import {\n transformTrackRequestData,\n // transformIdentifyRequestData,\n} from './utils/request-transformations';\n\ntype ApiClientProps = {\n apiKey: string;\n baseUrl?: string;\n config: {\n workspaceId: string;\n tenantId?: string;\n };\n};\n\nexport type EventProperties = Record<string, any>;\n\ntype User = {\n email: string;\n};\n\nconst sdkConfig = {\n defaultHeaders: {\n 'Content-Type': 'application/json',\n },\n};\n\nconst API_PREFIX = 'sdk-prod';\n\nconst TRACK_ENDPOINT = `${API_PREFIX}/track`;\n// const IDENTIFY_USER_ENDPOINT = `${API_PREFIX}/identify`;\n\nclass ApiClient {\n private apiKey: string;\n private baseUrl?: string;\n private workspaceId: string = '';\n private user?: User;\n\n constructor({ apiKey, baseUrl, config }: ApiClientProps) {\n this.apiKey = apiKey;\n this.baseUrl = baseUrl;\n this.workspaceId = config.workspaceId;\n }\n\n // Wrapper function for fetch\n fetchWithConfig = (\n endpoint: string,\n options: RequestInit = { method: 'GET' }\n ) => {\n // Construct the full URL\n const url = `${this.getBaseUrl()}/${endpoint}`;\n\n // Merge the default headers with any headers provided in the options\n const headers = {\n ...sdkConfig.defaultHeaders,\n 'x-api-key': this.getApiKey(),\n ...(options.headers || {}),\n };\n\n // Merge the rest of the options with the headers\n const config = {\n ...options,\n headers,\n };\n\n // Execute the fetch call with the merged configuration\n return fetch(url, config);\n };\n\n getApiKey() {\n return this.apiKey;\n }\n\n getBaseUrl() {\n return this.baseUrl;\n }\n\n getWorkspaceId() {\n return this.workspaceId;\n }\n\n getUser() {\n return this.user;\n }\n\n identify(user: User) {\n this.user = user;\n // this.fetchWithConfig(IDENTIFY_USER_ENDPOINT, {\n // method: 'POST',\n // body: transformIdentifyRequestData({\n // ...user,\n // }),\n // });\n }\n\n track(eventName: string, eventProperties?: EventProperties, userEmail?: string) {\n // Use provided email, or fall back to identified user's email\n const email = userEmail || this.user?.email;\n \n if (!email) {\n throw new Error('User email must be provided either via identify() or as a parameter to track()');\n }\n \n this.fetchWithConfig(TRACK_ENDPOINT, {\n method: 'POST',\n body: transformTrackRequestData({\n eventName,\n eventProperties,\n userProperties: {\n email,\n },\n }),\n });\n }\n}\n\nexport default ApiClient;\n","import { sanitiseUrl } from '../utils/url-validator';\nimport { parseApiKey } from '../utils/api-key-parser';\nimport ApiClient from './../api-client';\n\ntype Config = {\n workspaceId: string;\n tenantId?: string;\n};\n\nclass SDKInstanceManager {\n private static instance: SDKInstanceManager;\n private isInitialised: boolean = false;\n private config: Config = { workspaceId: '' };\n private apiClient?: ApiClient;\n\n constructor() {\n if (!SDKInstanceManager.instance) {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n SDKInstanceManager.instance = this;\n }\n\n return SDKInstanceManager.instance;\n }\n\n /**\n * @throws Error in case validation of parameters fails\n * @param apiKey - required string value representing the API key\n * Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}\n * The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)\n * but the original unparsed API key is sent to the API in requests\n * @returns void\n */\n initialise(apiKey: string) {\n if (this.getIsInitialised()) {\n console.info('SDK is already initialised with API key');\n return;\n }\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('SDK needs a valid API key to be initialised');\n }\n\n // Parse the API key to extract workspaceId, tenantId, and baseUrl for SDK configuration\n // The original unparsed API key will be sent to the API\n const parsed = parseApiKey(apiKey);\n \n this.config = {\n workspaceId: parsed.workspaceId,\n tenantId: parsed.tenantId,\n };\n \n const sanitisedUrl = sanitiseUrl(parsed.baseUrl);\n // Pass the original unparsed API key to ApiClient - it will be sent as-is to the API\n this.apiClient = new ApiClient({ apiKey, baseUrl: sanitisedUrl, config: this.config });\n this.isInitialised = true;\n }\n\n destroy() {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n }\n\n getIsInitialised() {\n return this.isInitialised;\n }\n\n getApiClient() {\n return this.apiClient;\n }\n\n getConfig() {\n return this.config;\n }\n}\n\nconst instance = new SDKInstanceManager();\nexport { instance as default };\n","import sdkInstanceManager from './../managers/sdk-instance-manager';\n\n/**\n * @param apiKey - required string value representing the API key\n * Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}\n * The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)\n * but the original unparsed API key is sent to the API in requests\n * @returns boolean indicating whether the initialisation of the sdk was successful or not\n */\nexport const initialise = (apiKey: string) => {\n try {\n sdkInstanceManager.initialise(apiKey);\n return true;\n } catch (e) {\n console.info((e as any)?.message);\n return false;\n }\n};\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\nexport const identify = (user: { email: string }) => {\n if (!sdkInstanceManager.getIsInitialised()) {\n console.error('SDK must be initialised first');\n return;\n }\n if (!user?.email) {\n console.error('User email must be provided');\n return;\n }\n sdkInstanceManager.getApiClient()?.identify(user);\n};\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\ntype Properties = Record<string, any>;\n\nexport const track = (eventName: string, eventProperties?: Properties, userEmail?: string) => {\n if (!sdkInstanceManager.getIsInitialised()) {\n console.error('SDK must be initialised first');\n return;\n }\n if (!eventName || typeof eventName !== 'string') {\n console.error('Event name must be provided');\n return;\n }\n sdkInstanceManager.getApiClient()?.track(eventName, eventProperties, userEmail);\n};\n"],"names":["urlRegex","isValidUrl","url","test","sanitiseUrl","sanitisedUrl","trim","Error","urlObject","URL","hostname","toLowerCase","pathname","replace","toString","e","DEFAULT_BASE_URLS","live","parseApiKey","apiKey","startsWith","parts","split","length","environment","prefix","withoutPrefix","substring","keyParts","tenantId","workspaceId","baseUrl","transformTrackRequestData","_ref","eventName","eventProperties","userProperties","transformedEvent","type","data","timestamp","Date","now","userEmail","email","JSON","stringify","sdkConfig","defaultHeaders","API_PREFIX","TRACK_ENDPOINT","ApiClient","config","endpoint","options","method","_this","getBaseUrl","headers","_extends","getApiKey","fetch","_proto","prototype","getWorkspaceId","getUser","user","identify","track","_this$user","fetchWithConfig","body","SDKInstanceManager","instance","isInitialised","initialise","getIsInitialised","console","info","parsed","apiClient","destroy","getApiClient","getConfig","sdkInstanceManager","message","error","_sdkInstanceManager$g"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=/^https?:\/\//,e=
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=/^https?:\/\//,e={live:"https://rjwzrk7gs1.execute-api.eu-central-1.amazonaws.com/"};function i(){return(i=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var i=arguments[e];for(var r in i)Object.prototype.hasOwnProperty.call(i,r)&&(t[r]=i[r])}return t}).apply(this,arguments)}var r={"Content-Type":"application/json"},n=function(){function t(t){var e=this,n=t.apiKey,s=t.baseUrl,o=t.config;this.workspaceId="",this.fetchWithConfig=function(t,n){void 0===n&&(n={method:"GET"});var s=e.getBaseUrl()+"/"+t,o=i({},r,{"x-api-key":e.getApiKey()},n.headers||{}),a=i({},n,{headers:o});return fetch(s,a)},this.apiKey=n,this.baseUrl=s,this.workspaceId=o.workspaceId}var e=t.prototype;return e.getApiKey=function(){return this.apiKey},e.getBaseUrl=function(){return this.baseUrl},e.getWorkspaceId=function(){return this.workspaceId},e.getUser=function(){return this.user},e.identify=function(t){this.user=t},e.track=function(t,e,i){var r,n,s,o,a=i||(null==(r=this.user)?void 0:r.email);if(!a)throw new Error("User email must be provided either via identify() or as a parameter to track()");this.fetchWithConfig("sdk-prod/track",{method:"POST",body:(n={eventName:t,eventProperties:e,userProperties:{email:a}},s=n.userProperties,o={type:n.eventName,data:n.eventProperties||{},timestamp:Date.now(),userEmail:null==s?void 0:s.email},JSON.stringify(o))})},t}(),s=new(function(){function i(){return this.isInitialised=!1,this.config={workspaceId:""},i.instance||(this.config={workspaceId:""},this.isInitialised=!1,i.instance=this),i.instance}var r=i.prototype;return r.initialise=function(i){if(this.getIsInitialised())console.info("SDK is already initialised with API key");else{if(!i||"string"!=typeof i)throw new Error("SDK needs a valid API key to be initialised");var r=function(t){if(!t||"string"!=typeof t)throw new Error("API key must be a non-empty string");if(!t.startsWith("cs_"))throw new Error('Invalid API key format. Must start with "cs_"');var i=t.split("_");if(i.length<2)throw new Error("Invalid API key format. Must include environment (e.g., cs_live_)");var r=i[1],n="cs_"+r+"_";if(!t.startsWith(n))throw new Error('Invalid API key format. Must start with "'+n+'"');var s=t.substring(n.length).split("_");if(s.length<3)throw new Error("Invalid API key format. Expected format: cs_{environment}_tenantId_workspaceId_secret");var o=s[0],a=s[1];if(!o)throw new Error("Tenant ID not found in API key");if(!a)throw new Error("Workspace ID not found in API key");return{environment:r,workspaceId:a,tenantId:o,baseUrl:e[r]||e.live}}(i);this.config={workspaceId:r.workspaceId,tenantId:r.tenantId};var s=function(e){var i=e.trim();if(!function(e){return t.test(e)}(e))throw new Error("URL must start with 'http://' or 'https://'");try{var r=new URL(i);return r.hostname=r.hostname.toLowerCase(),"/"!==r.pathname&&(r.pathname=r.pathname.replace(/\/+$/,"")||""),r.toString().replace(/\/+$/,"")}catch(t){throw new Error("Invalid URL provided")}}(r.baseUrl);this.apiClient=new n({apiKey:i,baseUrl:s,config:this.config}),this.isInitialised=!0}},r.destroy=function(){this.config={workspaceId:""},this.isInitialised=!1},r.getIsInitialised=function(){return this.isInitialised},r.getApiClient=function(){return this.apiClient},r.getConfig=function(){return this.config},i}());exports.identify=function(t){var e;s.getIsInitialised()?null!=t&&t.email?null==(e=s.getApiClient())||e.identify(t):console.error("User email must be provided"):console.error("SDK must be initialised first")},exports.initialise=function(t){try{return s.initialise(t),!0}catch(t){return console.info(null==t?void 0:t.message),!1}},exports.track=function(t,e,i){var r;s.getIsInitialised()?t&&"string"==typeof t?null==(r=s.getApiClient())||r.track(t,e,i):console.error("Event name must be provided"):console.error("SDK must be initialised first")};
|
|
2
2
|
//# sourceMappingURL=churnsignal-sdk-web.cjs.production.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"churnsignal-sdk-web.cjs.production.min.js","sources":["../src/utils/url-validator.ts","../src/utils/request-transformations.ts","../src/api-client.ts","../src/managers/sdk-instance-manager.ts","../src/core/identify.ts","../src/core/initialise.ts","../src/core/track.ts"],"sourcesContent":["const urlRegex = /^https?:\\/\\//;\n\nexport const isValidUrl = (url: string) => urlRegex.test(url);\n\nexport const sanitiseUrl = (url: string) => {\n // Trim whitespace\n const sanitisedUrl = url.trim();\n\n // Validate scheme\n if (!isValidUrl(url)) {\n throw new Error(\"URL must start with 'http://' or 'https://'\");\n }\n\n try {\n let urlObject = new URL(sanitisedUrl);\n\n urlObject.hostname = urlObject.hostname.toLowerCase();\n\n // Remove trailing slash\n if (urlObject.pathname !== '/') {\n urlObject.pathname = urlObject.pathname.replace(/\\/+$/, '') || '';\n }\n\n // Reconstruct the URL from its components\n // Note: The URL interface automatically handles encoding of the pathname\n return urlObject.toString().replace(/\\/+$/, '');\n } catch (e) {\n throw new Error('Invalid URL provided');\n }\n};\n","import { EventProperties } from '../api-client';\n\ntype TrackRequest = {\n eventName: string;\n eventProperties?: EventProperties;\n userProperties?: {\n email: string;\n };\n configProperties: {\n workspaceId: string;\n tenantId?: string\n };\n};\n\nexport const transformTrackRequestData = ({\n eventName,\n eventProperties,\n userProperties,\n configProperties,\n}: TrackRequest) => {\n const transformedEvent = {\n name: eventName,\n timestamp: new Date().getTime().toString(),\n userEmail: userProperties?.email,\n tenantId: configProperties.tenantId, // TODO this will be handled on the API side, for now pass it in SDK setup\n workspaceId: configProperties.workspaceId,\n type: 'event',\n data: eventProperties,\n };\n return JSON.stringify(transformedEvent);\n};\n\ntype IdentifyRequest = {\n email: string\n}\n\nexport const transformIdentifyRequestData = ({\n email\n}: IdentifyRequest) => {\n const transformedIdentify = {\n userEmail: email\n }\n return JSON.stringify(transformedIdentify);\n}","import {\n transformTrackRequestData,\n // transformIdentifyRequestData,\n} from './utils/request-transformations';\n\ntype ApiClientProps = {\n apiKey: string;\n baseUrl?: string;\n config: {\n workspaceId: string;\n tenantId?: string;\n };\n};\n\nexport type EventProperties = Record<string, any>;\n\ntype User = {\n email: string;\n};\n\nconst sdkConfig = {\n defaultHeaders: {\n 'Content-Type': 'application/json',\n },\n};\n\nconst API_PREFIX = 'sdk-prod';\n\nconst TRACK_ENDPOINT = `${API_PREFIX}/track`;\n// const IDENTIFY_USER_ENDPOINT = `${API_PREFIX}/identify`;\n\nclass ApiClient {\n private apiKey: string;\n private baseUrl?: string;\n private workspaceId: string = '';\n private tenantId?: string;\n private user?: User;\n\n constructor({ apiKey, baseUrl, config }: ApiClientProps) {\n this.apiKey = apiKey;\n this.baseUrl = baseUrl;\n this.workspaceId = config.workspaceId;\n this.tenantId = config.tenantId;\n }\n\n // Wrapper function for fetch\n fetchWithConfig = (\n endpoint: string,\n options: RequestInit = { method: 'GET' }\n ) => {\n // Construct the full URL\n const url = `${this.getBaseUrl()}/${endpoint}`;\n\n // Merge the default headers with any headers provided in the options\n const headers = {\n ...sdkConfig.defaultHeaders,\n 'x-api-key': this.getApiKey(),\n ...(options.headers || {}),\n };\n\n // Merge the rest of the options with the headers\n const config = {\n ...options,\n headers,\n };\n\n // Execute the fetch call with the merged configuration\n return fetch(url, config);\n };\n\n getApiKey() {\n return this.apiKey;\n }\n\n getBaseUrl() {\n return this.baseUrl;\n }\n\n getWorkspaceId() {\n return this.workspaceId;\n }\n\n getUser() {\n return this.user;\n }\n\n identify(user: User) {\n this.user = user;\n // this.fetchWithConfig(IDENTIFY_USER_ENDPOINT, {\n // method: 'POST',\n // body: transformIdentifyRequestData({\n // ...user,\n // }),\n // });\n }\n\n track(eventName: string, eventProperties?: EventProperties) {\n if (!this.user) {\n throw new Error('No identified users to track');\n }\n this.fetchWithConfig(TRACK_ENDPOINT, {\n method: 'POST',\n body: transformTrackRequestData({\n eventName,\n eventProperties,\n configProperties: {\n workspaceId: this.getWorkspaceId(),\n tenantId: this.tenantId,\n },\n userProperties: {\n email: this.user.email,\n },\n }),\n });\n }\n}\n\nexport default ApiClient;\n","import { isValidUrl, sanitiseUrl } from '../utils/url-validator';\nimport ApiClient from './../api-client';\n\ntype Config = {\n workspaceId: string;\n tenantId?: string;\n};\n\nclass SDKInstanceManager {\n private static instance: SDKInstanceManager;\n private isInitialised: boolean = false;\n private config: Config = { workspaceId: '' };\n private apiClient?: ApiClient;\n\n constructor() {\n if (!SDKInstanceManager.instance) {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n SDKInstanceManager.instance = this;\n }\n\n return SDKInstanceManager.instance;\n }\n\n /**\n * @throws Error in case validation of parameters fails\n * @param apiKey - required string value representing the API key\n * @param baseUrl - required string value representing the API endpoint\n * @param config - required object value\n * @returns void\n */\n initialise(apiKey: string, baseUrl: string, config: Config) {\n if (this.getIsInitialised()) {\n console.info('SDK is already initialised with API key');\n return;\n }\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('SDK needs a valid API key to be inialised');\n }\n if (!baseUrl || typeof baseUrl !== 'string' || !isValidUrl(baseUrl)) {\n throw new Error('SDK needs a valid base URL to be initialised');\n }\n if (!config.workspaceId) {\n throw new Error('Workspace ID must be provided');\n }\n this.config = config;\n let sanitisedUrl = sanitiseUrl(baseUrl);\n this.apiClient = new ApiClient({ apiKey, baseUrl: sanitisedUrl, config });\n this.isInitialised = true;\n }\n\n destroy() {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n }\n\n getIsInitialised() {\n return this.isInitialised;\n }\n\n getApiClient() {\n return this.apiClient;\n }\n\n getConfig() {\n return this.config;\n }\n}\n\nconst instance = new SDKInstanceManager();\nexport { instance as default };\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\nexport const identify = (user: { email: string }) => {\n sdkInstanceManager.getApiClient()?.identify(user);\n};\n","import sdkInstanceManager from './../managers/sdk-instance-manager';\n\nexport type InitConfig = {\n workspaceId: string\n tenantId: string; // TODO to be removed should be done on the API side\n};\n\n/**\n * @param apiKey - required string value representing the API key\n * @param baseUrl - required string value representing the API endpoint\n * @param options - optional object value\n * @returns boolean indicating whether the initialisation of the sdk was successful or not\n */\nexport const initialise = (\n apiKey: string,\n baseUrl: string,\n config: InitConfig\n) => {\n try {\n sdkInstanceManager.initialise(apiKey, baseUrl, config);\n return true;\n } catch (e) {\n console.info((e as any)?.message);\n return false;\n }\n};\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\ntype Properties = Record<string, any>;\n\nexport const track = (eventName: string, eventProperties?: Properties) => {\n if (!sdkInstanceManager.getIsInitialised()) {\n console.error('SDK must be initialised first');\n return;\n }\n if (!eventName || typeof eventName !== 'string') {\n console.error('Event name must be provided');\n return;\n }\n sdkInstanceManager.getApiClient()?.track(eventName, eventProperties);\n};\n"],"names":["urlRegex","isValidUrl","url","test","transformTrackRequestData","_ref","eventProperties","userProperties","configProperties","transformedEvent","name","eventName","timestamp","Date","getTime","toString","userEmail","email","tenantId","workspaceId","type","data","JSON","stringify","sdkConfig","Content-Type","ApiClient","apiKey","baseUrl","config","this","endpoint","options","method","_this","getBaseUrl","headers","_extends","x-api-key","getApiKey","fetch","_proto","prototype","getWorkspaceId","getUser","user","identify","track","Error","fetchWithConfig","API_PREFIX","body","instance","SDKInstanceManager","isInitialised","initialise","getIsInitialised","console","info","sanitisedUrl","trim","urlObject","URL","hostname","toLowerCase","pathname","replace","e","sanitiseUrl","apiClient","destroy","getApiClient","getConfig","_sdkInstanceManager$g","sdkInstanceManager","message","error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"churnsignal-sdk-web.cjs.production.min.js","sources":["../src/utils/url-validator.ts","../src/utils/api-key-parser.ts","../src/utils/request-transformations.ts","../src/api-client.ts","../src/managers/sdk-instance-manager.ts","../src/core/identify.ts","../src/core/initialise.ts","../src/core/track.ts"],"sourcesContent":["const urlRegex = /^https?:\\/\\//;\n\nexport const isValidUrl = (url: string) => urlRegex.test(url);\n\nexport const sanitiseUrl = (url: string) => {\n // Trim whitespace\n const sanitisedUrl = url.trim();\n\n // Validate scheme\n if (!isValidUrl(url)) {\n throw new Error(\"URL must start with 'http://' or 'https://'\");\n }\n\n try {\n let urlObject = new URL(sanitisedUrl);\n\n urlObject.hostname = urlObject.hostname.toLowerCase();\n\n // Remove trailing slash\n if (urlObject.pathname !== '/') {\n urlObject.pathname = urlObject.pathname.replace(/\\/+$/, '') || '';\n }\n\n // Reconstruct the URL from its components\n // Note: The URL interface automatically handles encoding of the pathname\n return urlObject.toString().replace(/\\/+$/, '');\n } catch (e) {\n throw new Error('Invalid URL provided');\n }\n};\n","type ParsedApiKey = {\n environment: string;\n workspaceId: string;\n tenantId: string;\n baseUrl: string;\n};\n\nconst DEFAULT_BASE_URLS: Record<string, string> = {\n live: 'https://rjwzrk7gs1.execute-api.eu-central-1.amazonaws.com/'\n};\n\n/**\n * Parses an API key to extract workspaceId, tenantId, and baseUrl\n * Expected format: cs_{environment}_tenantId_workspaceId_secret\n * Matches backend parsing logic\n * \n * @param apiKey - The API key to parse\n * @returns ParsedApiKey object with extracted data\n * @throws Error if the API key format is invalid\n */\nexport const parseApiKey = (apiKey: string): ParsedApiKey => {\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('API key must be a non-empty string');\n }\n\n // Check if it starts with cs_ prefix\n if (!apiKey.startsWith('cs_')) {\n throw new Error('Invalid API key format. Must start with \"cs_\"');\n }\n\n // Find the environment (cs_live_, cs_test_, cs_dev_, etc.)\n const parts = apiKey.split('_');\n if (parts.length < 2) {\n throw new Error('Invalid API key format. Must include environment (e.g., cs_live_)');\n }\n\n const environment = parts[1];\n const prefix = `cs_${environment}_`;\n \n if (!apiKey.startsWith(prefix)) {\n throw new Error(`Invalid API key format. Must start with \"${prefix}\"`);\n }\n\n // Remove prefix (e.g., 'cs_live_')\n const withoutPrefix = apiKey.substring(prefix.length);\n\n // Split by underscore - format: tenantId_workspaceId_secret\n const keyParts = withoutPrefix.split('_');\n\n if (keyParts.length < 3) {\n throw new Error('Invalid API key format. Expected format: cs_{environment}_tenantId_workspaceId_secret');\n }\n\n const tenantId = keyParts[0];\n const workspaceId = keyParts[1];\n // The rest (keyParts[2] onwards) is the secret, but we don't need it for parsing\n\n if (!tenantId) {\n throw new Error('Tenant ID not found in API key');\n }\n\n if (!workspaceId) {\n throw new Error('Workspace ID not found in API key');\n }\n\n // Determine baseUrl from environment\n const baseUrl = DEFAULT_BASE_URLS[environment] || DEFAULT_BASE_URLS.live;\n\n return {\n environment,\n workspaceId,\n tenantId,\n baseUrl,\n };\n};\n\n","import { EventProperties } from '../api-client';\n\ntype TrackRequest = {\n eventName: string;\n eventProperties?: EventProperties;\n userProperties?: {\n email: string;\n }\n};\n\nexport const transformTrackRequestData = ({\n eventName,\n eventProperties,\n userProperties,\n}: TrackRequest) => {\n const transformedEvent = {\n type: eventName,\n data: eventProperties || {},\n timestamp: Date.now(),\n userEmail: userProperties?.email,\n };\n return JSON.stringify(transformedEvent);\n};\n\ntype IdentifyRequest = {\n email: string\n}\n\nexport const transformIdentifyRequestData = ({\n email\n}: IdentifyRequest) => {\n const transformedIdentify = {\n userEmail: email\n }\n return JSON.stringify(transformedIdentify);\n}","import {\n transformTrackRequestData,\n // transformIdentifyRequestData,\n} from './utils/request-transformations';\n\ntype ApiClientProps = {\n apiKey: string;\n baseUrl?: string;\n config: {\n workspaceId: string;\n tenantId?: string;\n };\n};\n\nexport type EventProperties = Record<string, any>;\n\ntype User = {\n email: string;\n};\n\nconst sdkConfig = {\n defaultHeaders: {\n 'Content-Type': 'application/json',\n },\n};\n\nconst API_PREFIX = 'sdk-prod';\n\nconst TRACK_ENDPOINT = `${API_PREFIX}/track`;\n// const IDENTIFY_USER_ENDPOINT = `${API_PREFIX}/identify`;\n\nclass ApiClient {\n private apiKey: string;\n private baseUrl?: string;\n private workspaceId: string = '';\n private user?: User;\n\n constructor({ apiKey, baseUrl, config }: ApiClientProps) {\n this.apiKey = apiKey;\n this.baseUrl = baseUrl;\n this.workspaceId = config.workspaceId;\n }\n\n // Wrapper function for fetch\n fetchWithConfig = (\n endpoint: string,\n options: RequestInit = { method: 'GET' }\n ) => {\n // Construct the full URL\n const url = `${this.getBaseUrl()}/${endpoint}`;\n\n // Merge the default headers with any headers provided in the options\n const headers = {\n ...sdkConfig.defaultHeaders,\n 'x-api-key': this.getApiKey(),\n ...(options.headers || {}),\n };\n\n // Merge the rest of the options with the headers\n const config = {\n ...options,\n headers,\n };\n\n // Execute the fetch call with the merged configuration\n return fetch(url, config);\n };\n\n getApiKey() {\n return this.apiKey;\n }\n\n getBaseUrl() {\n return this.baseUrl;\n }\n\n getWorkspaceId() {\n return this.workspaceId;\n }\n\n getUser() {\n return this.user;\n }\n\n identify(user: User) {\n this.user = user;\n // this.fetchWithConfig(IDENTIFY_USER_ENDPOINT, {\n // method: 'POST',\n // body: transformIdentifyRequestData({\n // ...user,\n // }),\n // });\n }\n\n track(eventName: string, eventProperties?: EventProperties, userEmail?: string) {\n // Use provided email, or fall back to identified user's email\n const email = userEmail || this.user?.email;\n \n if (!email) {\n throw new Error('User email must be provided either via identify() or as a parameter to track()');\n }\n \n this.fetchWithConfig(TRACK_ENDPOINT, {\n method: 'POST',\n body: transformTrackRequestData({\n eventName,\n eventProperties,\n userProperties: {\n email,\n },\n }),\n });\n }\n}\n\nexport default ApiClient;\n","import { sanitiseUrl } from '../utils/url-validator';\nimport { parseApiKey } from '../utils/api-key-parser';\nimport ApiClient from './../api-client';\n\ntype Config = {\n workspaceId: string;\n tenantId?: string;\n};\n\nclass SDKInstanceManager {\n private static instance: SDKInstanceManager;\n private isInitialised: boolean = false;\n private config: Config = { workspaceId: '' };\n private apiClient?: ApiClient;\n\n constructor() {\n if (!SDKInstanceManager.instance) {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n SDKInstanceManager.instance = this;\n }\n\n return SDKInstanceManager.instance;\n }\n\n /**\n * @throws Error in case validation of parameters fails\n * @param apiKey - required string value representing the API key\n * Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}\n * The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)\n * but the original unparsed API key is sent to the API in requests\n * @returns void\n */\n initialise(apiKey: string) {\n if (this.getIsInitialised()) {\n console.info('SDK is already initialised with API key');\n return;\n }\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('SDK needs a valid API key to be initialised');\n }\n\n // Parse the API key to extract workspaceId, tenantId, and baseUrl for SDK configuration\n // The original unparsed API key will be sent to the API\n const parsed = parseApiKey(apiKey);\n \n this.config = {\n workspaceId: parsed.workspaceId,\n tenantId: parsed.tenantId,\n };\n \n const sanitisedUrl = sanitiseUrl(parsed.baseUrl);\n // Pass the original unparsed API key to ApiClient - it will be sent as-is to the API\n this.apiClient = new ApiClient({ apiKey, baseUrl: sanitisedUrl, config: this.config });\n this.isInitialised = true;\n }\n\n destroy() {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n }\n\n getIsInitialised() {\n return this.isInitialised;\n }\n\n getApiClient() {\n return this.apiClient;\n }\n\n getConfig() {\n return this.config;\n }\n}\n\nconst instance = new SDKInstanceManager();\nexport { instance as default };\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\nexport const identify = (user: { email: string }) => {\n if (!sdkInstanceManager.getIsInitialised()) {\n console.error('SDK must be initialised first');\n return;\n }\n if (!user?.email) {\n console.error('User email must be provided');\n return;\n }\n sdkInstanceManager.getApiClient()?.identify(user);\n};\n","import sdkInstanceManager from './../managers/sdk-instance-manager';\n\n/**\n * @param apiKey - required string value representing the API key\n * Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}\n * The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)\n * but the original unparsed API key is sent to the API in requests\n * @returns boolean indicating whether the initialisation of the sdk was successful or not\n */\nexport const initialise = (apiKey: string) => {\n try {\n sdkInstanceManager.initialise(apiKey);\n return true;\n } catch (e) {\n console.info((e as any)?.message);\n return false;\n }\n};\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\ntype Properties = Record<string, any>;\n\nexport const track = (eventName: string, eventProperties?: Properties, userEmail?: string) => {\n if (!sdkInstanceManager.getIsInitialised()) {\n console.error('SDK must be initialised first');\n return;\n }\n if (!eventName || typeof eventName !== 'string') {\n console.error('Event name must be provided');\n return;\n }\n sdkInstanceManager.getApiClient()?.track(eventName, eventProperties, userEmail);\n};\n"],"names":["urlRegex","DEFAULT_BASE_URLS","live","sdkConfig","Content-Type","ApiClient","_ref","apiKey","baseUrl","config","this","endpoint","options","method","url","_this","getBaseUrl","headers","_extends","x-api-key","getApiKey","fetch","workspaceId","_proto","prototype","getWorkspaceId","getUser","user","identify","track","eventName","eventProperties","userEmail","userProperties","transformedEvent","email","_this$user","Error","fetchWithConfig","API_PREFIX","body","type","data","timestamp","Date","now","JSON","stringify","instance","SDKInstanceManager","isInitialised","initialise","getIsInitialised","console","info","parsed","startsWith","parts","split","length","environment","prefix","keyParts","substring","tenantId","parseApiKey","sanitisedUrl","trim","test","isValidUrl","urlObject","URL","hostname","toLowerCase","pathname","replace","toString","e","sanitiseUrl","apiClient","destroy","getApiClient","getConfig","sdkInstanceManager","_sdkInstanceManager$g","error","message"],"mappings":"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"}
|
|
@@ -24,6 +24,62 @@ var sanitiseUrl = function sanitiseUrl(url) {
|
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
var DEFAULT_BASE_URLS = {
|
|
28
|
+
live: 'https://rjwzrk7gs1.execute-api.eu-central-1.amazonaws.com/'
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Parses an API key to extract workspaceId, tenantId, and baseUrl
|
|
32
|
+
* Expected format: cs_{environment}_tenantId_workspaceId_secret
|
|
33
|
+
* Matches backend parsing logic
|
|
34
|
+
*
|
|
35
|
+
* @param apiKey - The API key to parse
|
|
36
|
+
* @returns ParsedApiKey object with extracted data
|
|
37
|
+
* @throws Error if the API key format is invalid
|
|
38
|
+
*/
|
|
39
|
+
var parseApiKey = function parseApiKey(apiKey) {
|
|
40
|
+
if (!apiKey || typeof apiKey !== 'string') {
|
|
41
|
+
throw new Error('API key must be a non-empty string');
|
|
42
|
+
}
|
|
43
|
+
// Check if it starts with cs_ prefix
|
|
44
|
+
if (!apiKey.startsWith('cs_')) {
|
|
45
|
+
throw new Error('Invalid API key format. Must start with "cs_"');
|
|
46
|
+
}
|
|
47
|
+
// Find the environment (cs_live_, cs_test_, cs_dev_, etc.)
|
|
48
|
+
var parts = apiKey.split('_');
|
|
49
|
+
if (parts.length < 2) {
|
|
50
|
+
throw new Error('Invalid API key format. Must include environment (e.g., cs_live_)');
|
|
51
|
+
}
|
|
52
|
+
var environment = parts[1];
|
|
53
|
+
var prefix = "cs_" + environment + "_";
|
|
54
|
+
if (!apiKey.startsWith(prefix)) {
|
|
55
|
+
throw new Error("Invalid API key format. Must start with \"" + prefix + "\"");
|
|
56
|
+
}
|
|
57
|
+
// Remove prefix (e.g., 'cs_live_')
|
|
58
|
+
var withoutPrefix = apiKey.substring(prefix.length);
|
|
59
|
+
// Split by underscore - format: tenantId_workspaceId_secret
|
|
60
|
+
var keyParts = withoutPrefix.split('_');
|
|
61
|
+
if (keyParts.length < 3) {
|
|
62
|
+
throw new Error('Invalid API key format. Expected format: cs_{environment}_tenantId_workspaceId_secret');
|
|
63
|
+
}
|
|
64
|
+
var tenantId = keyParts[0];
|
|
65
|
+
var workspaceId = keyParts[1];
|
|
66
|
+
// The rest (keyParts[2] onwards) is the secret, but we don't need it for parsing
|
|
67
|
+
if (!tenantId) {
|
|
68
|
+
throw new Error('Tenant ID not found in API key');
|
|
69
|
+
}
|
|
70
|
+
if (!workspaceId) {
|
|
71
|
+
throw new Error('Workspace ID not found in API key');
|
|
72
|
+
}
|
|
73
|
+
// Determine baseUrl from environment
|
|
74
|
+
var baseUrl = DEFAULT_BASE_URLS[environment] || DEFAULT_BASE_URLS.live;
|
|
75
|
+
return {
|
|
76
|
+
environment: environment,
|
|
77
|
+
workspaceId: workspaceId,
|
|
78
|
+
tenantId: tenantId,
|
|
79
|
+
baseUrl: baseUrl
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
27
83
|
function _extends() {
|
|
28
84
|
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
29
85
|
for (var i = 1; i < arguments.length; i++) {
|
|
@@ -42,16 +98,12 @@ function _extends() {
|
|
|
42
98
|
var transformTrackRequestData = function transformTrackRequestData(_ref) {
|
|
43
99
|
var eventName = _ref.eventName,
|
|
44
100
|
eventProperties = _ref.eventProperties,
|
|
45
|
-
userProperties = _ref.userProperties
|
|
46
|
-
configProperties = _ref.configProperties;
|
|
101
|
+
userProperties = _ref.userProperties;
|
|
47
102
|
var transformedEvent = {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
workspaceId: configProperties.workspaceId,
|
|
53
|
-
type: 'event',
|
|
54
|
-
data: eventProperties
|
|
103
|
+
type: eventName,
|
|
104
|
+
data: eventProperties || {},
|
|
105
|
+
timestamp: Date.now(),
|
|
106
|
+
userEmail: userProperties == null ? void 0 : userProperties.email
|
|
55
107
|
};
|
|
56
108
|
return JSON.stringify(transformedEvent);
|
|
57
109
|
};
|
|
@@ -94,7 +146,6 @@ var ApiClient = /*#__PURE__*/function () {
|
|
|
94
146
|
this.apiKey = apiKey;
|
|
95
147
|
this.baseUrl = baseUrl;
|
|
96
148
|
this.workspaceId = config.workspaceId;
|
|
97
|
-
this.tenantId = config.tenantId;
|
|
98
149
|
}
|
|
99
150
|
var _proto = ApiClient.prototype;
|
|
100
151
|
_proto.getApiKey = function getApiKey() {
|
|
@@ -118,21 +169,20 @@ var ApiClient = /*#__PURE__*/function () {
|
|
|
118
169
|
// }),
|
|
119
170
|
// });
|
|
120
171
|
};
|
|
121
|
-
_proto.track = function track(eventName, eventProperties) {
|
|
122
|
-
|
|
123
|
-
|
|
172
|
+
_proto.track = function track(eventName, eventProperties, userEmail) {
|
|
173
|
+
var _this$user;
|
|
174
|
+
// Use provided email, or fall back to identified user's email
|
|
175
|
+
var email = userEmail || ((_this$user = this.user) == null ? void 0 : _this$user.email);
|
|
176
|
+
if (!email) {
|
|
177
|
+
throw new Error('User email must be provided either via identify() or as a parameter to track()');
|
|
124
178
|
}
|
|
125
179
|
this.fetchWithConfig(TRACK_ENDPOINT, {
|
|
126
180
|
method: 'POST',
|
|
127
181
|
body: transformTrackRequestData({
|
|
128
182
|
eventName: eventName,
|
|
129
183
|
eventProperties: eventProperties,
|
|
130
|
-
configProperties: {
|
|
131
|
-
workspaceId: this.getWorkspaceId(),
|
|
132
|
-
tenantId: this.tenantId
|
|
133
|
-
},
|
|
134
184
|
userProperties: {
|
|
135
|
-
email:
|
|
185
|
+
email: email
|
|
136
186
|
}
|
|
137
187
|
})
|
|
138
188
|
});
|
|
@@ -158,31 +208,33 @@ var SDKInstanceManager = /*#__PURE__*/function () {
|
|
|
158
208
|
/**
|
|
159
209
|
* @throws Error in case validation of parameters fails
|
|
160
210
|
* @param apiKey - required string value representing the API key
|
|
161
|
-
*
|
|
162
|
-
*
|
|
211
|
+
* Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
|
|
212
|
+
* The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
|
|
213
|
+
* but the original unparsed API key is sent to the API in requests
|
|
163
214
|
* @returns void
|
|
164
215
|
*/
|
|
165
216
|
var _proto = SDKInstanceManager.prototype;
|
|
166
|
-
_proto.initialise = function initialise(apiKey
|
|
217
|
+
_proto.initialise = function initialise(apiKey) {
|
|
167
218
|
if (this.getIsInitialised()) {
|
|
168
219
|
console.info('SDK is already initialised with API key');
|
|
169
220
|
return;
|
|
170
221
|
}
|
|
171
222
|
if (!apiKey || typeof apiKey !== 'string') {
|
|
172
|
-
throw new Error('SDK needs a valid API key to be
|
|
173
|
-
}
|
|
174
|
-
if (!baseUrl || typeof baseUrl !== 'string' || !isValidUrl(baseUrl)) {
|
|
175
|
-
throw new Error('SDK needs a valid base URL to be initialised');
|
|
176
|
-
}
|
|
177
|
-
if (!config.workspaceId) {
|
|
178
|
-
throw new Error('Workspace ID must be provided');
|
|
223
|
+
throw new Error('SDK needs a valid API key to be initialised');
|
|
179
224
|
}
|
|
180
|
-
|
|
181
|
-
|
|
225
|
+
// Parse the API key to extract workspaceId, tenantId, and baseUrl for SDK configuration
|
|
226
|
+
// The original unparsed API key will be sent to the API
|
|
227
|
+
var parsed = parseApiKey(apiKey);
|
|
228
|
+
this.config = {
|
|
229
|
+
workspaceId: parsed.workspaceId,
|
|
230
|
+
tenantId: parsed.tenantId
|
|
231
|
+
};
|
|
232
|
+
var sanitisedUrl = sanitiseUrl(parsed.baseUrl);
|
|
233
|
+
// Pass the original unparsed API key to ApiClient - it will be sent as-is to the API
|
|
182
234
|
this.apiClient = new ApiClient({
|
|
183
235
|
apiKey: apiKey,
|
|
184
236
|
baseUrl: sanitisedUrl,
|
|
185
|
-
config: config
|
|
237
|
+
config: this.config
|
|
186
238
|
});
|
|
187
239
|
this.isInitialised = true;
|
|
188
240
|
};
|
|
@@ -207,13 +259,14 @@ var instance = /*#__PURE__*/new SDKInstanceManager();
|
|
|
207
259
|
|
|
208
260
|
/**
|
|
209
261
|
* @param apiKey - required string value representing the API key
|
|
210
|
-
*
|
|
211
|
-
*
|
|
262
|
+
* Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
|
|
263
|
+
* The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
|
|
264
|
+
* but the original unparsed API key is sent to the API in requests
|
|
212
265
|
* @returns boolean indicating whether the initialisation of the sdk was successful or not
|
|
213
266
|
*/
|
|
214
|
-
var initialise = function initialise(apiKey
|
|
267
|
+
var initialise = function initialise(apiKey) {
|
|
215
268
|
try {
|
|
216
|
-
instance.initialise(apiKey
|
|
269
|
+
instance.initialise(apiKey);
|
|
217
270
|
return true;
|
|
218
271
|
} catch (e) {
|
|
219
272
|
console.info(e == null ? void 0 : e.message);
|
|
@@ -223,10 +276,18 @@ var initialise = function initialise(apiKey, baseUrl, config) {
|
|
|
223
276
|
|
|
224
277
|
var identify = function identify(user) {
|
|
225
278
|
var _sdkInstanceManager$g;
|
|
279
|
+
if (!instance.getIsInitialised()) {
|
|
280
|
+
console.error('SDK must be initialised first');
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (!(user != null && user.email)) {
|
|
284
|
+
console.error('User email must be provided');
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
226
287
|
(_sdkInstanceManager$g = instance.getApiClient()) == null || _sdkInstanceManager$g.identify(user);
|
|
227
288
|
};
|
|
228
289
|
|
|
229
|
-
var track = function track(eventName, eventProperties) {
|
|
290
|
+
var track = function track(eventName, eventProperties, userEmail) {
|
|
230
291
|
var _sdkInstanceManager$g;
|
|
231
292
|
if (!instance.getIsInitialised()) {
|
|
232
293
|
console.error('SDK must be initialised first');
|
|
@@ -236,7 +297,7 @@ var track = function track(eventName, eventProperties) {
|
|
|
236
297
|
console.error('Event name must be provided');
|
|
237
298
|
return;
|
|
238
299
|
}
|
|
239
|
-
(_sdkInstanceManager$g = instance.getApiClient()) == null || _sdkInstanceManager$g.track(eventName, eventProperties);
|
|
300
|
+
(_sdkInstanceManager$g = instance.getApiClient()) == null || _sdkInstanceManager$g.track(eventName, eventProperties, userEmail);
|
|
240
301
|
};
|
|
241
302
|
|
|
242
303
|
export { identify, initialise, track };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"churnsignal-sdk-web.esm.js","sources":["../src/utils/url-validator.ts","../src/utils/request-transformations.ts","../src/api-client.ts","../src/managers/sdk-instance-manager.ts","../src/core/initialise.ts","../src/core/identify.ts","../src/core/track.ts"],"sourcesContent":["const urlRegex = /^https?:\\/\\//;\n\nexport const isValidUrl = (url: string) => urlRegex.test(url);\n\nexport const sanitiseUrl = (url: string) => {\n // Trim whitespace\n const sanitisedUrl = url.trim();\n\n // Validate scheme\n if (!isValidUrl(url)) {\n throw new Error(\"URL must start with 'http://' or 'https://'\");\n }\n\n try {\n let urlObject = new URL(sanitisedUrl);\n\n urlObject.hostname = urlObject.hostname.toLowerCase();\n\n // Remove trailing slash\n if (urlObject.pathname !== '/') {\n urlObject.pathname = urlObject.pathname.replace(/\\/+$/, '') || '';\n }\n\n // Reconstruct the URL from its components\n // Note: The URL interface automatically handles encoding of the pathname\n return urlObject.toString().replace(/\\/+$/, '');\n } catch (e) {\n throw new Error('Invalid URL provided');\n }\n};\n","import { EventProperties } from '../api-client';\n\ntype TrackRequest = {\n eventName: string;\n eventProperties?: EventProperties;\n userProperties?: {\n email: string;\n };\n configProperties: {\n workspaceId: string;\n tenantId?: string\n };\n};\n\nexport const transformTrackRequestData = ({\n eventName,\n eventProperties,\n userProperties,\n configProperties,\n}: TrackRequest) => {\n const transformedEvent = {\n name: eventName,\n timestamp: new Date().getTime().toString(),\n userEmail: userProperties?.email,\n tenantId: configProperties.tenantId, // TODO this will be handled on the API side, for now pass it in SDK setup\n workspaceId: configProperties.workspaceId,\n type: 'event',\n data: eventProperties,\n };\n return JSON.stringify(transformedEvent);\n};\n\ntype IdentifyRequest = {\n email: string\n}\n\nexport const transformIdentifyRequestData = ({\n email\n}: IdentifyRequest) => {\n const transformedIdentify = {\n userEmail: email\n }\n return JSON.stringify(transformedIdentify);\n}","import {\n transformTrackRequestData,\n // transformIdentifyRequestData,\n} from './utils/request-transformations';\n\ntype ApiClientProps = {\n apiKey: string;\n baseUrl?: string;\n config: {\n workspaceId: string;\n tenantId?: string;\n };\n};\n\nexport type EventProperties = Record<string, any>;\n\ntype User = {\n email: string;\n};\n\nconst sdkConfig = {\n defaultHeaders: {\n 'Content-Type': 'application/json',\n },\n};\n\nconst API_PREFIX = 'sdk-prod';\n\nconst TRACK_ENDPOINT = `${API_PREFIX}/track`;\n// const IDENTIFY_USER_ENDPOINT = `${API_PREFIX}/identify`;\n\nclass ApiClient {\n private apiKey: string;\n private baseUrl?: string;\n private workspaceId: string = '';\n private tenantId?: string;\n private user?: User;\n\n constructor({ apiKey, baseUrl, config }: ApiClientProps) {\n this.apiKey = apiKey;\n this.baseUrl = baseUrl;\n this.workspaceId = config.workspaceId;\n this.tenantId = config.tenantId;\n }\n\n // Wrapper function for fetch\n fetchWithConfig = (\n endpoint: string,\n options: RequestInit = { method: 'GET' }\n ) => {\n // Construct the full URL\n const url = `${this.getBaseUrl()}/${endpoint}`;\n\n // Merge the default headers with any headers provided in the options\n const headers = {\n ...sdkConfig.defaultHeaders,\n 'x-api-key': this.getApiKey(),\n ...(options.headers || {}),\n };\n\n // Merge the rest of the options with the headers\n const config = {\n ...options,\n headers,\n };\n\n // Execute the fetch call with the merged configuration\n return fetch(url, config);\n };\n\n getApiKey() {\n return this.apiKey;\n }\n\n getBaseUrl() {\n return this.baseUrl;\n }\n\n getWorkspaceId() {\n return this.workspaceId;\n }\n\n getUser() {\n return this.user;\n }\n\n identify(user: User) {\n this.user = user;\n // this.fetchWithConfig(IDENTIFY_USER_ENDPOINT, {\n // method: 'POST',\n // body: transformIdentifyRequestData({\n // ...user,\n // }),\n // });\n }\n\n track(eventName: string, eventProperties?: EventProperties) {\n if (!this.user) {\n throw new Error('No identified users to track');\n }\n this.fetchWithConfig(TRACK_ENDPOINT, {\n method: 'POST',\n body: transformTrackRequestData({\n eventName,\n eventProperties,\n configProperties: {\n workspaceId: this.getWorkspaceId(),\n tenantId: this.tenantId,\n },\n userProperties: {\n email: this.user.email,\n },\n }),\n });\n }\n}\n\nexport default ApiClient;\n","import { isValidUrl, sanitiseUrl } from '../utils/url-validator';\nimport ApiClient from './../api-client';\n\ntype Config = {\n workspaceId: string;\n tenantId?: string;\n};\n\nclass SDKInstanceManager {\n private static instance: SDKInstanceManager;\n private isInitialised: boolean = false;\n private config: Config = { workspaceId: '' };\n private apiClient?: ApiClient;\n\n constructor() {\n if (!SDKInstanceManager.instance) {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n SDKInstanceManager.instance = this;\n }\n\n return SDKInstanceManager.instance;\n }\n\n /**\n * @throws Error in case validation of parameters fails\n * @param apiKey - required string value representing the API key\n * @param baseUrl - required string value representing the API endpoint\n * @param config - required object value\n * @returns void\n */\n initialise(apiKey: string, baseUrl: string, config: Config) {\n if (this.getIsInitialised()) {\n console.info('SDK is already initialised with API key');\n return;\n }\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('SDK needs a valid API key to be inialised');\n }\n if (!baseUrl || typeof baseUrl !== 'string' || !isValidUrl(baseUrl)) {\n throw new Error('SDK needs a valid base URL to be initialised');\n }\n if (!config.workspaceId) {\n throw new Error('Workspace ID must be provided');\n }\n this.config = config;\n let sanitisedUrl = sanitiseUrl(baseUrl);\n this.apiClient = new ApiClient({ apiKey, baseUrl: sanitisedUrl, config });\n this.isInitialised = true;\n }\n\n destroy() {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n }\n\n getIsInitialised() {\n return this.isInitialised;\n }\n\n getApiClient() {\n return this.apiClient;\n }\n\n getConfig() {\n return this.config;\n }\n}\n\nconst instance = new SDKInstanceManager();\nexport { instance as default };\n","import sdkInstanceManager from './../managers/sdk-instance-manager';\n\nexport type InitConfig = {\n workspaceId: string\n tenantId: string; // TODO to be removed should be done on the API side\n};\n\n/**\n * @param apiKey - required string value representing the API key\n * @param baseUrl - required string value representing the API endpoint\n * @param options - optional object value\n * @returns boolean indicating whether the initialisation of the sdk was successful or not\n */\nexport const initialise = (\n apiKey: string,\n baseUrl: string,\n config: InitConfig\n) => {\n try {\n sdkInstanceManager.initialise(apiKey, baseUrl, config);\n return true;\n } catch (e) {\n console.info((e as any)?.message);\n return false;\n }\n};\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\nexport const identify = (user: { email: string }) => {\n sdkInstanceManager.getApiClient()?.identify(user);\n};\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\ntype Properties = Record<string, any>;\n\nexport const track = (eventName: string, eventProperties?: Properties) => {\n if (!sdkInstanceManager.getIsInitialised()) {\n console.error('SDK must be initialised first');\n return;\n }\n if (!eventName || typeof eventName !== 'string') {\n console.error('Event name must be provided');\n return;\n }\n sdkInstanceManager.getApiClient()?.track(eventName, eventProperties);\n};\n"],"names":["urlRegex","isValidUrl","url","test","sanitiseUrl","sanitisedUrl","trim","Error","urlObject","URL","hostname","toLowerCase","pathname","replace","toString","e","transformTrackRequestData","_ref","eventName","eventProperties","userProperties","configProperties","transformedEvent","name","timestamp","Date","getTime","userEmail","email","tenantId","workspaceId","type","data","JSON","stringify","sdkConfig","defaultHeaders","API_PREFIX","TRACK_ENDPOINT","ApiClient","apiKey","baseUrl","config","endpoint","options","method","_this","getBaseUrl","headers","_extends","getApiKey","fetch","_proto","prototype","getWorkspaceId","getUser","user","identify","track","fetchWithConfig","body","SDKInstanceManager","instance","isInitialised","initialise","getIsInitialised","console","info","apiClient","destroy","getApiClient","getConfig","sdkInstanceManager","message","_sdkInstanceManager$g","error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"churnsignal-sdk-web.esm.js","sources":["../src/utils/url-validator.ts","../src/utils/api-key-parser.ts","../src/utils/request-transformations.ts","../src/api-client.ts","../src/managers/sdk-instance-manager.ts","../src/core/initialise.ts","../src/core/identify.ts","../src/core/track.ts"],"sourcesContent":["const urlRegex = /^https?:\\/\\//;\n\nexport const isValidUrl = (url: string) => urlRegex.test(url);\n\nexport const sanitiseUrl = (url: string) => {\n // Trim whitespace\n const sanitisedUrl = url.trim();\n\n // Validate scheme\n if (!isValidUrl(url)) {\n throw new Error(\"URL must start with 'http://' or 'https://'\");\n }\n\n try {\n let urlObject = new URL(sanitisedUrl);\n\n urlObject.hostname = urlObject.hostname.toLowerCase();\n\n // Remove trailing slash\n if (urlObject.pathname !== '/') {\n urlObject.pathname = urlObject.pathname.replace(/\\/+$/, '') || '';\n }\n\n // Reconstruct the URL from its components\n // Note: The URL interface automatically handles encoding of the pathname\n return urlObject.toString().replace(/\\/+$/, '');\n } catch (e) {\n throw new Error('Invalid URL provided');\n }\n};\n","type ParsedApiKey = {\n environment: string;\n workspaceId: string;\n tenantId: string;\n baseUrl: string;\n};\n\nconst DEFAULT_BASE_URLS: Record<string, string> = {\n live: 'https://rjwzrk7gs1.execute-api.eu-central-1.amazonaws.com/'\n};\n\n/**\n * Parses an API key to extract workspaceId, tenantId, and baseUrl\n * Expected format: cs_{environment}_tenantId_workspaceId_secret\n * Matches backend parsing logic\n * \n * @param apiKey - The API key to parse\n * @returns ParsedApiKey object with extracted data\n * @throws Error if the API key format is invalid\n */\nexport const parseApiKey = (apiKey: string): ParsedApiKey => {\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('API key must be a non-empty string');\n }\n\n // Check if it starts with cs_ prefix\n if (!apiKey.startsWith('cs_')) {\n throw new Error('Invalid API key format. Must start with \"cs_\"');\n }\n\n // Find the environment (cs_live_, cs_test_, cs_dev_, etc.)\n const parts = apiKey.split('_');\n if (parts.length < 2) {\n throw new Error('Invalid API key format. Must include environment (e.g., cs_live_)');\n }\n\n const environment = parts[1];\n const prefix = `cs_${environment}_`;\n \n if (!apiKey.startsWith(prefix)) {\n throw new Error(`Invalid API key format. Must start with \"${prefix}\"`);\n }\n\n // Remove prefix (e.g., 'cs_live_')\n const withoutPrefix = apiKey.substring(prefix.length);\n\n // Split by underscore - format: tenantId_workspaceId_secret\n const keyParts = withoutPrefix.split('_');\n\n if (keyParts.length < 3) {\n throw new Error('Invalid API key format. Expected format: cs_{environment}_tenantId_workspaceId_secret');\n }\n\n const tenantId = keyParts[0];\n const workspaceId = keyParts[1];\n // The rest (keyParts[2] onwards) is the secret, but we don't need it for parsing\n\n if (!tenantId) {\n throw new Error('Tenant ID not found in API key');\n }\n\n if (!workspaceId) {\n throw new Error('Workspace ID not found in API key');\n }\n\n // Determine baseUrl from environment\n const baseUrl = DEFAULT_BASE_URLS[environment] || DEFAULT_BASE_URLS.live;\n\n return {\n environment,\n workspaceId,\n tenantId,\n baseUrl,\n };\n};\n\n","import { EventProperties } from '../api-client';\n\ntype TrackRequest = {\n eventName: string;\n eventProperties?: EventProperties;\n userProperties?: {\n email: string;\n }\n};\n\nexport const transformTrackRequestData = ({\n eventName,\n eventProperties,\n userProperties,\n}: TrackRequest) => {\n const transformedEvent = {\n type: eventName,\n data: eventProperties || {},\n timestamp: Date.now(),\n userEmail: userProperties?.email,\n };\n return JSON.stringify(transformedEvent);\n};\n\ntype IdentifyRequest = {\n email: string\n}\n\nexport const transformIdentifyRequestData = ({\n email\n}: IdentifyRequest) => {\n const transformedIdentify = {\n userEmail: email\n }\n return JSON.stringify(transformedIdentify);\n}","import {\n transformTrackRequestData,\n // transformIdentifyRequestData,\n} from './utils/request-transformations';\n\ntype ApiClientProps = {\n apiKey: string;\n baseUrl?: string;\n config: {\n workspaceId: string;\n tenantId?: string;\n };\n};\n\nexport type EventProperties = Record<string, any>;\n\ntype User = {\n email: string;\n};\n\nconst sdkConfig = {\n defaultHeaders: {\n 'Content-Type': 'application/json',\n },\n};\n\nconst API_PREFIX = 'sdk-prod';\n\nconst TRACK_ENDPOINT = `${API_PREFIX}/track`;\n// const IDENTIFY_USER_ENDPOINT = `${API_PREFIX}/identify`;\n\nclass ApiClient {\n private apiKey: string;\n private baseUrl?: string;\n private workspaceId: string = '';\n private user?: User;\n\n constructor({ apiKey, baseUrl, config }: ApiClientProps) {\n this.apiKey = apiKey;\n this.baseUrl = baseUrl;\n this.workspaceId = config.workspaceId;\n }\n\n // Wrapper function for fetch\n fetchWithConfig = (\n endpoint: string,\n options: RequestInit = { method: 'GET' }\n ) => {\n // Construct the full URL\n const url = `${this.getBaseUrl()}/${endpoint}`;\n\n // Merge the default headers with any headers provided in the options\n const headers = {\n ...sdkConfig.defaultHeaders,\n 'x-api-key': this.getApiKey(),\n ...(options.headers || {}),\n };\n\n // Merge the rest of the options with the headers\n const config = {\n ...options,\n headers,\n };\n\n // Execute the fetch call with the merged configuration\n return fetch(url, config);\n };\n\n getApiKey() {\n return this.apiKey;\n }\n\n getBaseUrl() {\n return this.baseUrl;\n }\n\n getWorkspaceId() {\n return this.workspaceId;\n }\n\n getUser() {\n return this.user;\n }\n\n identify(user: User) {\n this.user = user;\n // this.fetchWithConfig(IDENTIFY_USER_ENDPOINT, {\n // method: 'POST',\n // body: transformIdentifyRequestData({\n // ...user,\n // }),\n // });\n }\n\n track(eventName: string, eventProperties?: EventProperties, userEmail?: string) {\n // Use provided email, or fall back to identified user's email\n const email = userEmail || this.user?.email;\n \n if (!email) {\n throw new Error('User email must be provided either via identify() or as a parameter to track()');\n }\n \n this.fetchWithConfig(TRACK_ENDPOINT, {\n method: 'POST',\n body: transformTrackRequestData({\n eventName,\n eventProperties,\n userProperties: {\n email,\n },\n }),\n });\n }\n}\n\nexport default ApiClient;\n","import { sanitiseUrl } from '../utils/url-validator';\nimport { parseApiKey } from '../utils/api-key-parser';\nimport ApiClient from './../api-client';\n\ntype Config = {\n workspaceId: string;\n tenantId?: string;\n};\n\nclass SDKInstanceManager {\n private static instance: SDKInstanceManager;\n private isInitialised: boolean = false;\n private config: Config = { workspaceId: '' };\n private apiClient?: ApiClient;\n\n constructor() {\n if (!SDKInstanceManager.instance) {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n SDKInstanceManager.instance = this;\n }\n\n return SDKInstanceManager.instance;\n }\n\n /**\n * @throws Error in case validation of parameters fails\n * @param apiKey - required string value representing the API key\n * Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}\n * The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)\n * but the original unparsed API key is sent to the API in requests\n * @returns void\n */\n initialise(apiKey: string) {\n if (this.getIsInitialised()) {\n console.info('SDK is already initialised with API key');\n return;\n }\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('SDK needs a valid API key to be initialised');\n }\n\n // Parse the API key to extract workspaceId, tenantId, and baseUrl for SDK configuration\n // The original unparsed API key will be sent to the API\n const parsed = parseApiKey(apiKey);\n \n this.config = {\n workspaceId: parsed.workspaceId,\n tenantId: parsed.tenantId,\n };\n \n const sanitisedUrl = sanitiseUrl(parsed.baseUrl);\n // Pass the original unparsed API key to ApiClient - it will be sent as-is to the API\n this.apiClient = new ApiClient({ apiKey, baseUrl: sanitisedUrl, config: this.config });\n this.isInitialised = true;\n }\n\n destroy() {\n this.config = { workspaceId: '' };\n this.isInitialised = false;\n }\n\n getIsInitialised() {\n return this.isInitialised;\n }\n\n getApiClient() {\n return this.apiClient;\n }\n\n getConfig() {\n return this.config;\n }\n}\n\nconst instance = new SDKInstanceManager();\nexport { instance as default };\n","import sdkInstanceManager from './../managers/sdk-instance-manager';\n\n/**\n * @param apiKey - required string value representing the API key\n * Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}\n * The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)\n * but the original unparsed API key is sent to the API in requests\n * @returns boolean indicating whether the initialisation of the sdk was successful or not\n */\nexport const initialise = (apiKey: string) => {\n try {\n sdkInstanceManager.initialise(apiKey);\n return true;\n } catch (e) {\n console.info((e as any)?.message);\n return false;\n }\n};\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\nexport const identify = (user: { email: string }) => {\n if (!sdkInstanceManager.getIsInitialised()) {\n console.error('SDK must be initialised first');\n return;\n }\n if (!user?.email) {\n console.error('User email must be provided');\n return;\n }\n sdkInstanceManager.getApiClient()?.identify(user);\n};\n","import sdkInstanceManager from '../managers/sdk-instance-manager';\n\ntype Properties = Record<string, any>;\n\nexport const track = (eventName: string, eventProperties?: Properties, userEmail?: string) => {\n if (!sdkInstanceManager.getIsInitialised()) {\n console.error('SDK must be initialised first');\n return;\n }\n if (!eventName || typeof eventName !== 'string') {\n console.error('Event name must be provided');\n return;\n }\n sdkInstanceManager.getApiClient()?.track(eventName, eventProperties, userEmail);\n};\n"],"names":["urlRegex","isValidUrl","url","test","sanitiseUrl","sanitisedUrl","trim","Error","urlObject","URL","hostname","toLowerCase","pathname","replace","toString","e","DEFAULT_BASE_URLS","live","parseApiKey","apiKey","startsWith","parts","split","length","environment","prefix","withoutPrefix","substring","keyParts","tenantId","workspaceId","baseUrl","transformTrackRequestData","_ref","eventName","eventProperties","userProperties","transformedEvent","type","data","timestamp","Date","now","userEmail","email","JSON","stringify","sdkConfig","defaultHeaders","API_PREFIX","TRACK_ENDPOINT","ApiClient","config","endpoint","options","method","_this","getBaseUrl","headers","_extends","getApiKey","fetch","_proto","prototype","getWorkspaceId","getUser","user","identify","track","_this$user","fetchWithConfig","body","SDKInstanceManager","instance","isInitialised","initialise","getIsInitialised","console","info","parsed","apiClient","destroy","getApiClient","getConfig","sdkInstanceManager","message","error","_sdkInstanceManager$g"],"mappings":"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"}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
export declare type InitConfig = {
|
|
2
|
-
workspaceId: string;
|
|
3
|
-
tenantId: string;
|
|
4
|
-
};
|
|
5
1
|
/**
|
|
6
2
|
* @param apiKey - required string value representing the API key
|
|
7
|
-
*
|
|
8
|
-
*
|
|
3
|
+
* Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
|
|
4
|
+
* The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
|
|
5
|
+
* but the original unparsed API key is sent to the API in requests
|
|
9
6
|
* @returns boolean indicating whether the initialisation of the sdk was successful or not
|
|
10
7
|
*/
|
|
11
|
-
export declare const initialise: (apiKey: string
|
|
8
|
+
export declare const initialise: (apiKey: string) => boolean;
|
package/dist/core/track.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const track: (eventName: string, eventProperties?: Record<string, any> | undefined) => void;
|
|
1
|
+
export declare const track: (eventName: string, eventProperties?: Record<string, any> | undefined, userEmail?: string | undefined) => void;
|
|
@@ -12,11 +12,12 @@ declare class SDKInstanceManager {
|
|
|
12
12
|
/**
|
|
13
13
|
* @throws Error in case validation of parameters fails
|
|
14
14
|
* @param apiKey - required string value representing the API key
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
|
|
16
|
+
* The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
|
|
17
|
+
* but the original unparsed API key is sent to the API in requests
|
|
17
18
|
* @returns void
|
|
18
19
|
*/
|
|
19
|
-
initialise(apiKey: string
|
|
20
|
+
initialise(apiKey: string): void;
|
|
20
21
|
destroy(): void;
|
|
21
22
|
getIsInitialised(): boolean;
|
|
22
23
|
getApiClient(): ApiClient | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare type ParsedApiKey = {
|
|
2
|
+
environment: string;
|
|
3
|
+
workspaceId: string;
|
|
4
|
+
tenantId: string;
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Parses an API key to extract workspaceId, tenantId, and baseUrl
|
|
9
|
+
* Expected format: cs_{environment}_tenantId_workspaceId_secret
|
|
10
|
+
* Matches backend parsing logic
|
|
11
|
+
*
|
|
12
|
+
* @param apiKey - The API key to parse
|
|
13
|
+
* @returns ParsedApiKey object with extracted data
|
|
14
|
+
* @throws Error if the API key format is invalid
|
|
15
|
+
*/
|
|
16
|
+
export declare const parseApiKey: (apiKey: string) => ParsedApiKey;
|
|
17
|
+
export {};
|
|
@@ -5,12 +5,8 @@ declare type TrackRequest = {
|
|
|
5
5
|
userProperties?: {
|
|
6
6
|
email: string;
|
|
7
7
|
};
|
|
8
|
-
configProperties: {
|
|
9
|
-
workspaceId: string;
|
|
10
|
-
tenantId?: string;
|
|
11
|
-
};
|
|
12
8
|
};
|
|
13
|
-
export declare const transformTrackRequestData: ({ eventName, eventProperties, userProperties,
|
|
9
|
+
export declare const transformTrackRequestData: ({ eventName, eventProperties, userProperties, }: TrackRequest) => string;
|
|
14
10
|
declare type IdentifyRequest = {
|
|
15
11
|
email: string;
|
|
16
12
|
};
|
package/package.json
CHANGED
package/src/api-client.ts
CHANGED
|
@@ -33,14 +33,12 @@ class ApiClient {
|
|
|
33
33
|
private apiKey: string;
|
|
34
34
|
private baseUrl?: string;
|
|
35
35
|
private workspaceId: string = '';
|
|
36
|
-
private tenantId?: string;
|
|
37
36
|
private user?: User;
|
|
38
37
|
|
|
39
38
|
constructor({ apiKey, baseUrl, config }: ApiClientProps) {
|
|
40
39
|
this.apiKey = apiKey;
|
|
41
40
|
this.baseUrl = baseUrl;
|
|
42
41
|
this.workspaceId = config.workspaceId;
|
|
43
|
-
this.tenantId = config.tenantId;
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
// Wrapper function for fetch
|
|
@@ -94,21 +92,21 @@ class ApiClient {
|
|
|
94
92
|
// });
|
|
95
93
|
}
|
|
96
94
|
|
|
97
|
-
track(eventName: string, eventProperties?: EventProperties) {
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
track(eventName: string, eventProperties?: EventProperties, userEmail?: string) {
|
|
96
|
+
// Use provided email, or fall back to identified user's email
|
|
97
|
+
const email = userEmail || this.user?.email;
|
|
98
|
+
|
|
99
|
+
if (!email) {
|
|
100
|
+
throw new Error('User email must be provided either via identify() or as a parameter to track()');
|
|
100
101
|
}
|
|
102
|
+
|
|
101
103
|
this.fetchWithConfig(TRACK_ENDPOINT, {
|
|
102
104
|
method: 'POST',
|
|
103
105
|
body: transformTrackRequestData({
|
|
104
106
|
eventName,
|
|
105
107
|
eventProperties,
|
|
106
|
-
configProperties: {
|
|
107
|
-
workspaceId: this.getWorkspaceId(),
|
|
108
|
-
tenantId: this.tenantId,
|
|
109
|
-
},
|
|
110
108
|
userProperties: {
|
|
111
|
-
email
|
|
109
|
+
email,
|
|
112
110
|
},
|
|
113
111
|
}),
|
|
114
112
|
});
|
package/src/core/identify.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import sdkInstanceManager from '../managers/sdk-instance-manager';
|
|
2
2
|
|
|
3
3
|
export const identify = (user: { email: string }) => {
|
|
4
|
+
if (!sdkInstanceManager.getIsInitialised()) {
|
|
5
|
+
console.error('SDK must be initialised first');
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
if (!user?.email) {
|
|
9
|
+
console.error('User email must be provided');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
4
12
|
sdkInstanceManager.getApiClient()?.identify(user);
|
|
5
13
|
};
|
|
@@ -1,33 +1,28 @@
|
|
|
1
1
|
import { destroy } from './destroy';
|
|
2
2
|
import { initialise } from './initialise';
|
|
3
3
|
|
|
4
|
-
const config = {
|
|
5
|
-
workspaceId: 'test-workspace-id',
|
|
6
|
-
tenantId: ''
|
|
7
|
-
};
|
|
8
|
-
|
|
9
4
|
describe('Initialise', () => {
|
|
10
5
|
afterEach(() => {
|
|
11
6
|
destroy();
|
|
12
7
|
})
|
|
13
8
|
it('returns false if no API key is provided', () => {
|
|
14
9
|
const apiKey = '';
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
expect(initialise(apiKey)).toBe(false);
|
|
11
|
+
});
|
|
12
|
+
it('returns false if invalid API key format is provided', () => {
|
|
13
|
+
const apiKey = 'invalid-api-key';
|
|
14
|
+
expect(initialise(apiKey)).toBe(false);
|
|
17
15
|
});
|
|
18
|
-
it('returns false if
|
|
19
|
-
const apiKey = '
|
|
20
|
-
|
|
21
|
-
expect(initialise(apiKey, url, config)).toBe(false);
|
|
16
|
+
it('returns false if API key does not start with cs_', () => {
|
|
17
|
+
const apiKey = 'invalid_live_workspace_tenant_secret';
|
|
18
|
+
expect(initialise(apiKey)).toBe(false);
|
|
22
19
|
});
|
|
23
|
-
it('returns
|
|
24
|
-
const apiKey = '
|
|
25
|
-
|
|
26
|
-
expect(initialise(apiKey, url, config)).toBe(true);
|
|
20
|
+
it('returns false if API key is missing required parts', () => {
|
|
21
|
+
const apiKey = 'cs_live_workspace';
|
|
22
|
+
expect(initialise(apiKey)).toBe(false);
|
|
27
23
|
});
|
|
28
|
-
it('returns
|
|
29
|
-
const apiKey = '
|
|
30
|
-
|
|
31
|
-
expect(initialise(apiKey, url, {} as typeof config)).toBe(false);
|
|
24
|
+
it('returns true if valid API key is provided', () => {
|
|
25
|
+
const apiKey = 'cs_live_932d7af4c96211f09fe863681df5cd13_11ee2a25c7b811f0b155e51f5e29f8c8_633fd81b71d2b69543ac02b3e4ad1fa1f5fa14d5d4b2a517f2d917b37184abbf';
|
|
26
|
+
expect(initialise(apiKey)).toBe(true);
|
|
32
27
|
});
|
|
33
28
|
});
|
package/src/core/initialise.ts
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
import sdkInstanceManager from './../managers/sdk-instance-manager';
|
|
2
2
|
|
|
3
|
-
export type InitConfig = {
|
|
4
|
-
workspaceId: string
|
|
5
|
-
tenantId: string; // TODO to be removed should be done on the API side
|
|
6
|
-
};
|
|
7
|
-
|
|
8
3
|
/**
|
|
9
4
|
* @param apiKey - required string value representing the API key
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
|
|
6
|
+
* The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
|
|
7
|
+
* but the original unparsed API key is sent to the API in requests
|
|
12
8
|
* @returns boolean indicating whether the initialisation of the sdk was successful or not
|
|
13
9
|
*/
|
|
14
|
-
export const initialise = (
|
|
15
|
-
apiKey: string,
|
|
16
|
-
baseUrl: string,
|
|
17
|
-
config: InitConfig
|
|
18
|
-
) => {
|
|
10
|
+
export const initialise = (apiKey: string) => {
|
|
19
11
|
try {
|
|
20
|
-
sdkInstanceManager.initialise(apiKey
|
|
12
|
+
sdkInstanceManager.initialise(apiKey);
|
|
21
13
|
return true;
|
|
22
14
|
} catch (e) {
|
|
23
15
|
console.info((e as any)?.message);
|
package/src/core/track.ts
CHANGED
|
@@ -2,7 +2,7 @@ import sdkInstanceManager from '../managers/sdk-instance-manager';
|
|
|
2
2
|
|
|
3
3
|
type Properties = Record<string, any>;
|
|
4
4
|
|
|
5
|
-
export const track = (eventName: string, eventProperties?: Properties) => {
|
|
5
|
+
export const track = (eventName: string, eventProperties?: Properties, userEmail?: string) => {
|
|
6
6
|
if (!sdkInstanceManager.getIsInitialised()) {
|
|
7
7
|
console.error('SDK must be initialised first');
|
|
8
8
|
return;
|
|
@@ -11,5 +11,5 @@ export const track = (eventName: string, eventProperties?: Properties) => {
|
|
|
11
11
|
console.error('Event name must be provided');
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
|
-
sdkInstanceManager.getApiClient()?.track(eventName, eventProperties);
|
|
14
|
+
sdkInstanceManager.getApiClient()?.track(eventName, eventProperties, userEmail);
|
|
15
15
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sanitiseUrl } from '../utils/url-validator';
|
|
2
|
+
import { parseApiKey } from '../utils/api-key-parser';
|
|
2
3
|
import ApiClient from './../api-client';
|
|
3
4
|
|
|
4
5
|
type Config = {
|
|
@@ -25,27 +26,32 @@ class SDKInstanceManager {
|
|
|
25
26
|
/**
|
|
26
27
|
* @throws Error in case validation of parameters fails
|
|
27
28
|
* @param apiKey - required string value representing the API key
|
|
28
|
-
*
|
|
29
|
-
*
|
|
29
|
+
* Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
|
|
30
|
+
* The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
|
|
31
|
+
* but the original unparsed API key is sent to the API in requests
|
|
30
32
|
* @returns void
|
|
31
33
|
*/
|
|
32
|
-
initialise(apiKey: string
|
|
34
|
+
initialise(apiKey: string) {
|
|
33
35
|
if (this.getIsInitialised()) {
|
|
34
36
|
console.info('SDK is already initialised with API key');
|
|
35
37
|
return;
|
|
36
38
|
}
|
|
37
39
|
if (!apiKey || typeof apiKey !== 'string') {
|
|
38
|
-
throw new Error('SDK needs a valid API key to be
|
|
40
|
+
throw new Error('SDK needs a valid API key to be initialised');
|
|
39
41
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
|
|
43
|
+
// Parse the API key to extract workspaceId, tenantId, and baseUrl for SDK configuration
|
|
44
|
+
// The original unparsed API key will be sent to the API
|
|
45
|
+
const parsed = parseApiKey(apiKey);
|
|
46
|
+
|
|
47
|
+
this.config = {
|
|
48
|
+
workspaceId: parsed.workspaceId,
|
|
49
|
+
tenantId: parsed.tenantId,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const sanitisedUrl = sanitiseUrl(parsed.baseUrl);
|
|
53
|
+
// Pass the original unparsed API key to ApiClient - it will be sent as-is to the API
|
|
54
|
+
this.apiClient = new ApiClient({ apiKey, baseUrl: sanitisedUrl, config: this.config });
|
|
49
55
|
this.isInitialised = true;
|
|
50
56
|
}
|
|
51
57
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
type ParsedApiKey = {
|
|
2
|
+
environment: string;
|
|
3
|
+
workspaceId: string;
|
|
4
|
+
tenantId: string;
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const DEFAULT_BASE_URLS: Record<string, string> = {
|
|
9
|
+
live: 'https://rjwzrk7gs1.execute-api.eu-central-1.amazonaws.com/'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parses an API key to extract workspaceId, tenantId, and baseUrl
|
|
14
|
+
* Expected format: cs_{environment}_tenantId_workspaceId_secret
|
|
15
|
+
* Matches backend parsing logic
|
|
16
|
+
*
|
|
17
|
+
* @param apiKey - The API key to parse
|
|
18
|
+
* @returns ParsedApiKey object with extracted data
|
|
19
|
+
* @throws Error if the API key format is invalid
|
|
20
|
+
*/
|
|
21
|
+
export const parseApiKey = (apiKey: string): ParsedApiKey => {
|
|
22
|
+
if (!apiKey || typeof apiKey !== 'string') {
|
|
23
|
+
throw new Error('API key must be a non-empty string');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check if it starts with cs_ prefix
|
|
27
|
+
if (!apiKey.startsWith('cs_')) {
|
|
28
|
+
throw new Error('Invalid API key format. Must start with "cs_"');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Find the environment (cs_live_, cs_test_, cs_dev_, etc.)
|
|
32
|
+
const parts = apiKey.split('_');
|
|
33
|
+
if (parts.length < 2) {
|
|
34
|
+
throw new Error('Invalid API key format. Must include environment (e.g., cs_live_)');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const environment = parts[1];
|
|
38
|
+
const prefix = `cs_${environment}_`;
|
|
39
|
+
|
|
40
|
+
if (!apiKey.startsWith(prefix)) {
|
|
41
|
+
throw new Error(`Invalid API key format. Must start with "${prefix}"`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Remove prefix (e.g., 'cs_live_')
|
|
45
|
+
const withoutPrefix = apiKey.substring(prefix.length);
|
|
46
|
+
|
|
47
|
+
// Split by underscore - format: tenantId_workspaceId_secret
|
|
48
|
+
const keyParts = withoutPrefix.split('_');
|
|
49
|
+
|
|
50
|
+
if (keyParts.length < 3) {
|
|
51
|
+
throw new Error('Invalid API key format. Expected format: cs_{environment}_tenantId_workspaceId_secret');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const tenantId = keyParts[0];
|
|
55
|
+
const workspaceId = keyParts[1];
|
|
56
|
+
// The rest (keyParts[2] onwards) is the secret, but we don't need it for parsing
|
|
57
|
+
|
|
58
|
+
if (!tenantId) {
|
|
59
|
+
throw new Error('Tenant ID not found in API key');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!workspaceId) {
|
|
63
|
+
throw new Error('Workspace ID not found in API key');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Determine baseUrl from environment
|
|
67
|
+
const baseUrl = DEFAULT_BASE_URLS[environment] || DEFAULT_BASE_URLS.live;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
environment,
|
|
71
|
+
workspaceId,
|
|
72
|
+
tenantId,
|
|
73
|
+
baseUrl,
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
|
|
@@ -5,27 +5,19 @@ type TrackRequest = {
|
|
|
5
5
|
eventProperties?: EventProperties;
|
|
6
6
|
userProperties?: {
|
|
7
7
|
email: string;
|
|
8
|
-
}
|
|
9
|
-
configProperties: {
|
|
10
|
-
workspaceId: string;
|
|
11
|
-
tenantId?: string
|
|
12
|
-
};
|
|
8
|
+
}
|
|
13
9
|
};
|
|
14
10
|
|
|
15
11
|
export const transformTrackRequestData = ({
|
|
16
12
|
eventName,
|
|
17
13
|
eventProperties,
|
|
18
14
|
userProperties,
|
|
19
|
-
configProperties,
|
|
20
15
|
}: TrackRequest) => {
|
|
21
16
|
const transformedEvent = {
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
type: eventName,
|
|
18
|
+
data: eventProperties || {},
|
|
19
|
+
timestamp: Date.now(),
|
|
24
20
|
userEmail: userProperties?.email,
|
|
25
|
-
tenantId: configProperties.tenantId, // TODO this will be handled on the API side, for now pass it in SDK setup
|
|
26
|
-
workspaceId: configProperties.workspaceId,
|
|
27
|
-
type: 'event',
|
|
28
|
-
data: eventProperties,
|
|
29
21
|
};
|
|
30
22
|
return JSON.stringify(transformedEvent);
|
|
31
23
|
};
|