@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 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
@@ -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
- name: eventName,
53
- timestamp: new Date().getTime().toString(),
54
- userEmail: userProperties == null ? void 0 : userProperties.email,
55
- tenantId: configProperties.tenantId,
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
- if (!this.user) {
127
- throw new Error('No identified users to track');
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: this.user.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
- * @param baseUrl - required string value representing the API endpoint
166
- * @param config - required object value
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, baseUrl, config) {
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 inialised');
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
- this.config = config;
185
- var sanitisedUrl = sanitiseUrl(baseUrl);
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
- * @param baseUrl - required string value representing the API endpoint
215
- * @param options - optional object value
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, baseUrl, config) {
271
+ var initialise = function initialise(apiKey) {
219
272
  try {
220
- instance.initialise(apiKey, baseUrl, config);
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":"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"}
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=function(e){return t.test(e)};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 n in i)Object.prototype.hasOwnProperty.call(i,n)&&(t[n]=i[n])}return t}).apply(this,arguments)}var n=function(t){var e=t.eventProperties,i=t.userProperties,n=t.configProperties,r={name:t.eventName,timestamp:(new Date).getTime().toString(),userEmail:null==i?void 0:i.email,tenantId:n.tenantId,workspaceId:n.workspaceId,type:"event",data:e};return JSON.stringify(r)},r={"Content-Type":"application/json"},s=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,this.tenantId=o.tenantId}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){if(!this.user)throw new Error("No identified users to track");this.fetchWithConfig("sdk-prod/track",{method:"POST",body:n({eventName:t,eventProperties:e,configProperties:{workspaceId:this.getWorkspaceId(),tenantId:this.tenantId},userProperties:{email:this.user.email}})})},t}(),o=new(function(){function t(){return this.isInitialised=!1,this.config={workspaceId:""},t.instance||(this.config={workspaceId:""},this.isInitialised=!1,t.instance=this),t.instance}var i=t.prototype;return i.initialise=function(t,i,n){if(this.getIsInitialised())console.info("SDK is already initialised with API key");else{if(!t||"string"!=typeof t)throw new Error("SDK needs a valid API key to be inialised");if(!i||"string"!=typeof i||!e(i))throw new Error("SDK needs a valid base URL to be initialised");if(!n.workspaceId)throw new Error("Workspace ID must be provided");this.config=n;var r=function(t){var i=t.trim();if(!e(t))throw new Error("URL must start with 'http://' or 'https://'");try{var n=new URL(i);return n.hostname=n.hostname.toLowerCase(),"/"!==n.pathname&&(n.pathname=n.pathname.replace(/\/+$/,"")||""),n.toString().replace(/\/+$/,"")}catch(t){throw new Error("Invalid URL provided")}}(i);this.apiClient=new s({apiKey:t,baseUrl:r,config:n}),this.isInitialised=!0}},i.destroy=function(){this.config={workspaceId:""},this.isInitialised=!1},i.getIsInitialised=function(){return this.isInitialised},i.getApiClient=function(){return this.apiClient},i.getConfig=function(){return this.config},t}());exports.identify=function(t){var e;null==(e=o.getApiClient())||e.identify(t)},exports.initialise=function(t,e,i){try{return o.initialise(t,e,i),!0}catch(t){return console.info(null==t?void 0:t.message),!1}},exports.track=function(t,e){var i;o.getIsInitialised()?t&&"string"==typeof t?null==(i=o.getApiClient())||i.track(t,e):console.error("Event name must be provided"):console.error("SDK must be initialised first")};
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
- name: eventName,
49
- timestamp: new Date().getTime().toString(),
50
- userEmail: userProperties == null ? void 0 : userProperties.email,
51
- tenantId: configProperties.tenantId,
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
- if (!this.user) {
123
- throw new Error('No identified users to track');
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: this.user.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
- * @param baseUrl - required string value representing the API endpoint
162
- * @param config - required object value
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, baseUrl, config) {
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 inialised');
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
- this.config = config;
181
- var sanitisedUrl = sanitiseUrl(baseUrl);
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
- * @param baseUrl - required string value representing the API endpoint
211
- * @param options - optional object value
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, baseUrl, config) {
267
+ var initialise = function initialise(apiKey) {
215
268
  try {
216
- instance.initialise(apiKey, baseUrl, config);
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
- * @param baseUrl - required string value representing the API endpoint
8
- * @param options - optional object value
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, baseUrl: string, config: InitConfig) => boolean;
8
+ export declare const initialise: (apiKey: string) => boolean;
@@ -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
- * @param baseUrl - required string value representing the API endpoint
16
- * @param config - required object value
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, baseUrl: string, config: Config): void;
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, configProperties, }: TrackRequest) => string;
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
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.2",
2
+ "version": "0.1.4",
3
3
  "license": "ISC",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
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
- if (!this.user) {
99
- throw new Error('No identified users to track');
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: this.user.email,
109
+ email,
112
110
  },
113
111
  }),
114
112
  });
@@ -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
- const url = 'http://example.url';
16
- expect(initialise(apiKey, url, config)).toBe(false);
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 invalid base URL is provided', () => {
19
- const apiKey = 'example-api-key';
20
- const url = 'http//example.url';
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 true if valid params are provided', () => {
24
- const apiKey = 'example-api-key';
25
- const url = 'http://example.url';
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 false if workspace id is not provided', () => {
29
- const apiKey = 'example-api-key';
30
- const url = 'http://example.url';
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
  });
@@ -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
- * @param baseUrl - required string value representing the API endpoint
11
- * @param options - optional object value
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, baseUrl, config);
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 { isValidUrl, sanitiseUrl } from '../utils/url-validator';
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
- * @param baseUrl - required string value representing the API endpoint
29
- * @param config - required object value
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, baseUrl: string, config: Config) {
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 inialised');
40
+ throw new Error('SDK needs a valid API key to be initialised');
39
41
  }
40
- if (!baseUrl || typeof baseUrl !== 'string' || !isValidUrl(baseUrl)) {
41
- throw new Error('SDK needs a valid base URL to be initialised');
42
- }
43
- if (!config.workspaceId) {
44
- throw new Error('Workspace ID must be provided');
45
- }
46
- this.config = config;
47
- let sanitisedUrl = sanitiseUrl(baseUrl);
48
- this.apiClient = new ApiClient({ apiKey, baseUrl: sanitisedUrl, config });
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
- name: eventName,
23
- timestamp: new Date().getTime().toString(),
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
  };