@churnsignal/churnsignal-sdk-web 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,68 @@
1
- # RAVEN SDK WEB
2
- This SDK allows developers to seamlessly integrate Raven into their Web applications
1
+ # CHURNSIGNAL SDK WEB
2
+ This SDK allows developers to seamlessly integrate ChurnSignal into their Web applications
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
+ ### Identify a User
28
+
29
+ Before tracking events, you need to identify the user:
30
+
31
+ ```typescript
32
+ identify({ email: 'user@example.com' });
33
+ ```
34
+
35
+ ### Track Events
36
+
37
+ Track user events with optional properties:
38
+
39
+ ```typescript
40
+ // Track a simple event
41
+ track('button_clicked');
42
+
43
+ // Track an event with properties
44
+ track('purchase_completed', {
45
+ productId: '123',
46
+ amount: 99.99,
47
+ currency: 'USD'
48
+ });
49
+ ```
50
+
51
+ ### Complete Example
52
+
53
+ ```typescript
54
+ import { initialise, identify, track } from '@churnsignal/churnsignal-sdk-web';
55
+
56
+ // Initialize
57
+ initialise('your-api-key-here');
58
+
59
+ // Identify user
60
+ identify({ email: 'user@example.com' });
61
+
62
+ // Track events
63
+ track('page_viewed', { page: '/home' });
64
+ track('product_viewed', { productId: 'abc123' });
65
+ ```
3
66
 
4
67
  ## Commands
5
68
 
@@ -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>;
@@ -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() {
@@ -131,10 +182,6 @@ var ApiClient = /*#__PURE__*/function () {
131
182
  body: transformTrackRequestData({
132
183
  eventName: eventName,
133
184
  eventProperties: eventProperties,
134
- configProperties: {
135
- workspaceId: this.getWorkspaceId(),
136
- tenantId: this.tenantId
137
- },
138
185
  userProperties: {
139
186
  email: this.user.email
140
187
  }
@@ -162,31 +209,33 @@ var SDKInstanceManager = /*#__PURE__*/function () {
162
209
  /**
163
210
  * @throws Error in case validation of parameters fails
164
211
  * @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
212
+ * Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
213
+ * The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
214
+ * but the original unparsed API key is sent to the API in requests
167
215
  * @returns void
168
216
  */
169
217
  var _proto = SDKInstanceManager.prototype;
170
- _proto.initialise = function initialise(apiKey, baseUrl, config) {
218
+ _proto.initialise = function initialise(apiKey) {
171
219
  if (this.getIsInitialised()) {
172
220
  console.info('SDK is already initialised with API key');
173
221
  return;
174
222
  }
175
223
  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');
224
+ throw new Error('SDK needs a valid API key to be initialised');
183
225
  }
184
- this.config = config;
185
- var sanitisedUrl = sanitiseUrl(baseUrl);
226
+ // Parse the API key to extract workspaceId, tenantId, and baseUrl for SDK configuration
227
+ // The original unparsed API key will be sent to the API
228
+ var parsed = parseApiKey(apiKey);
229
+ this.config = {
230
+ workspaceId: parsed.workspaceId,
231
+ tenantId: parsed.tenantId
232
+ };
233
+ var sanitisedUrl = sanitiseUrl(parsed.baseUrl);
234
+ // Pass the original unparsed API key to ApiClient - it will be sent as-is to the API
186
235
  this.apiClient = new ApiClient({
187
236
  apiKey: apiKey,
188
237
  baseUrl: sanitisedUrl,
189
- config: config
238
+ config: this.config
190
239
  });
191
240
  this.isInitialised = true;
192
241
  };
@@ -211,13 +260,14 @@ var instance = /*#__PURE__*/new SDKInstanceManager();
211
260
 
212
261
  /**
213
262
  * @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
263
+ * Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
264
+ * The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
265
+ * but the original unparsed API key is sent to the API in requests
216
266
  * @returns boolean indicating whether the initialisation of the sdk was successful or not
217
267
  */
218
- var initialise = function initialise(apiKey, baseUrl, config) {
268
+ var initialise = function initialise(apiKey) {
219
269
  try {
220
- instance.initialise(apiKey, baseUrl, config);
270
+ instance.initialise(apiKey);
221
271
  return true;
222
272
  } catch (e) {
223
273
  console.info(e == null ? void 0 : e.message);
@@ -227,6 +277,14 @@ var initialise = function initialise(apiKey, baseUrl, config) {
227
277
 
228
278
  var identify = function identify(user) {
229
279
  var _sdkInstanceManager$g;
280
+ if (!instance.getIsInitialised()) {
281
+ console.error('SDK must be initialised first');
282
+ return;
283
+ }
284
+ if (!(user != null && user.email)) {
285
+ console.error('User email must be provided');
286
+ return;
287
+ }
230
288
  (_sdkInstanceManager$g = instance.getApiClient()) == null || _sdkInstanceManager$g.identify(user);
231
289
  };
232
290
 
@@ -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) {\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 userProperties: {\n email: this.user.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) => {\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","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","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){if(!this.user)throw new Error("No identified users to track");var i,r,n;this.fetchWithConfig("sdk-prod/track",{method:"POST",body:(i={eventName:t,eventProperties:e,userProperties:{email:this.user.email}},r=i.userProperties,n={type:i.eventName,data:i.eventProperties||{},timestamp:Date.now(),userEmail:null==r?void 0:r.email},JSON.stringify(n))})},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){var i;s.getIsInitialised()?t&&"string"==typeof t?null==(i=s.getApiClient())||i.track(t,e):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) {\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 userProperties: {\n email: this.user.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) => {\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","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","Error","userProperties","transformedEvent","fetchWithConfig","API_PREFIX","body","email","type","data","timestamp","Date","now","userEmail","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() {
@@ -127,10 +178,6 @@ var ApiClient = /*#__PURE__*/function () {
127
178
  body: transformTrackRequestData({
128
179
  eventName: eventName,
129
180
  eventProperties: eventProperties,
130
- configProperties: {
131
- workspaceId: this.getWorkspaceId(),
132
- tenantId: this.tenantId
133
- },
134
181
  userProperties: {
135
182
  email: this.user.email
136
183
  }
@@ -158,31 +205,33 @@ var SDKInstanceManager = /*#__PURE__*/function () {
158
205
  /**
159
206
  * @throws Error in case validation of parameters fails
160
207
  * @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
208
+ * Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
209
+ * The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
210
+ * but the original unparsed API key is sent to the API in requests
163
211
  * @returns void
164
212
  */
165
213
  var _proto = SDKInstanceManager.prototype;
166
- _proto.initialise = function initialise(apiKey, baseUrl, config) {
214
+ _proto.initialise = function initialise(apiKey) {
167
215
  if (this.getIsInitialised()) {
168
216
  console.info('SDK is already initialised with API key');
169
217
  return;
170
218
  }
171
219
  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');
220
+ throw new Error('SDK needs a valid API key to be initialised');
179
221
  }
180
- this.config = config;
181
- var sanitisedUrl = sanitiseUrl(baseUrl);
222
+ // Parse the API key to extract workspaceId, tenantId, and baseUrl for SDK configuration
223
+ // The original unparsed API key will be sent to the API
224
+ var parsed = parseApiKey(apiKey);
225
+ this.config = {
226
+ workspaceId: parsed.workspaceId,
227
+ tenantId: parsed.tenantId
228
+ };
229
+ var sanitisedUrl = sanitiseUrl(parsed.baseUrl);
230
+ // Pass the original unparsed API key to ApiClient - it will be sent as-is to the API
182
231
  this.apiClient = new ApiClient({
183
232
  apiKey: apiKey,
184
233
  baseUrl: sanitisedUrl,
185
- config: config
234
+ config: this.config
186
235
  });
187
236
  this.isInitialised = true;
188
237
  };
@@ -207,13 +256,14 @@ var instance = /*#__PURE__*/new SDKInstanceManager();
207
256
 
208
257
  /**
209
258
  * @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
259
+ * Expected format: cs_{environment}_{workspaceId}_{tenantId}_{secret}
260
+ * The API key is parsed to extract configuration data (workspaceId, tenantId, baseUrl)
261
+ * but the original unparsed API key is sent to the API in requests
212
262
  * @returns boolean indicating whether the initialisation of the sdk was successful or not
213
263
  */
214
- var initialise = function initialise(apiKey, baseUrl, config) {
264
+ var initialise = function initialise(apiKey) {
215
265
  try {
216
- instance.initialise(apiKey, baseUrl, config);
266
+ instance.initialise(apiKey);
217
267
  return true;
218
268
  } catch (e) {
219
269
  console.info(e == null ? void 0 : e.message);
@@ -223,6 +273,14 @@ var initialise = function initialise(apiKey, baseUrl, config) {
223
273
 
224
274
  var identify = function identify(user) {
225
275
  var _sdkInstanceManager$g;
276
+ if (!instance.getIsInitialised()) {
277
+ console.error('SDK must be initialised first');
278
+ return;
279
+ }
280
+ if (!(user != null && user.email)) {
281
+ console.error('User email must be provided');
282
+ return;
283
+ }
226
284
  (_sdkInstanceManager$g = instance.getApiClient()) == null || _sdkInstanceManager$g.identify(user);
227
285
  };
228
286
 
@@ -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) {\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 userProperties: {\n email: this.user.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) => {\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","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","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;
@@ -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.1",
2
+ "version": "0.1.3",
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
@@ -103,10 +101,6 @@ class ApiClient {
103
101
  body: transformTrackRequestData({
104
102
  eventName,
105
103
  eventProperties,
106
- configProperties: {
107
- workspaceId: this.getWorkspaceId(),
108
- tenantId: this.tenantId,
109
- },
110
104
  userProperties: {
111
105
  email: this.user.email,
112
106
  },
@@ -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);
@@ -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
  };