saasco-sdk 0.1.12 → 0.1.14
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 +8 -2
- package/index.cjs.js +115 -43
- package/index.esm.js +110 -44
- package/package.json +1 -1
- package/src/index.d.ts +2 -0
- package/src/lib/analytics.d.ts +3 -2
- package/src/lib/getBrowserContext.d.ts +2 -0
- package/src/lib/tracking/index.d.ts +1 -0
- package/src/lib/tracking/types.d.ts +2798 -0
- package/src/lib/types.d.ts +69 -0
package/README.md
CHANGED
|
@@ -35,8 +35,8 @@ export const saasco = new Saasco({ projectId: 'YOUR-PROJECT-ID' });
|
|
|
35
35
|
|
|
36
36
|
## Init Automatic Page View Tracking
|
|
37
37
|
|
|
38
|
-
When you
|
|
39
|
-
You can also [manually track pages](https://www.notion.so/Manual-Page-Tracking-in-SPAs-2442fc7586dc4208ae8f669eb7561b1a?pvs=21) by opting
|
|
38
|
+
When you initiate the lib Saasco will automatically start tracking all page views in your app. There is no other configuration. It will automatically track url changes, even for SPAs like NextJs and Vue.
|
|
39
|
+
You can also [manually track pages](https://www.notion.so/Manual-Page-Tracking-in-SPAs-2442fc7586dc4208ae8f669eb7561b1a?pvs=21) by opting out of automatic page tracking. For most use cases you don't need to do this.
|
|
40
40
|
|
|
41
41
|
```ts
|
|
42
42
|
// ./app/index
|
|
@@ -228,6 +228,12 @@ Any property that starts with `$` is a property that has been generated by the S
|
|
|
228
228
|
| $utmContent | Last Touch UTM Content | The UTM content tag from the URL a customer clicked during their last interaction. |
|
|
229
229
|
| $unsubscribed | Unsubscribed | Whether the user has unsubscribed from all notifications |
|
|
230
230
|
|
|
231
|
+
### Reserved Integration Properties
|
|
232
|
+
|
|
233
|
+
| Property | Display Name | Description |
|
|
234
|
+
| ----------------- | ------------------ | ------------------------------------------------ |
|
|
235
|
+
| $stripeCustomerId | Stripe Customer Id | The Stripe customer ID associated with this user |
|
|
236
|
+
|
|
231
237
|
### Reserved event properties
|
|
232
238
|
|
|
233
239
|
Properties used to calculate revenue for different traffic sources and LTV for users.
|
package/index.cjs.js
CHANGED
|
@@ -6,6 +6,8 @@ var tslib = require('tslib');
|
|
|
6
6
|
var uuid = require('@lukeed/uuid');
|
|
7
7
|
var zod = require('zod');
|
|
8
8
|
|
|
9
|
+
var version = "0.1.14";
|
|
10
|
+
|
|
9
11
|
const timezones = {
|
|
10
12
|
'Asia/Barnaul': 'RU',
|
|
11
13
|
'Africa/Nouakchott': 'MR',
|
|
@@ -434,10 +436,39 @@ const timezones = {
|
|
|
434
436
|
'Asia/Calcutta': 'IN'
|
|
435
437
|
};
|
|
436
438
|
|
|
437
|
-
|
|
439
|
+
function getBrowserContext() {
|
|
440
|
+
var _a;
|
|
441
|
+
const isBrowser = typeof window !== 'undefined';
|
|
442
|
+
if (!isBrowser) return;
|
|
443
|
+
const customNavigator = navigator;
|
|
444
|
+
const $locale = (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.languages) && (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.languages.length) ? customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.languages[0] : (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.userLanguage) || (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.language) || (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.browserLanguage) || 'en';
|
|
445
|
+
// https://caniuse.com/?search=Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
446
|
+
// Only has 96.63% global support, so we need to check for undefined
|
|
447
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
448
|
+
const $location = timezones[timezone] || ((_a = $locale === null || $locale === void 0 ? void 0 : $locale.split('-')) === null || _a === void 0 ? void 0 : _a[1]) || 'undefined';
|
|
449
|
+
const $href = window.location.href;
|
|
450
|
+
const $pathname = window.location.pathname;
|
|
451
|
+
const $referrer = document.referrer;
|
|
452
|
+
const $screenDPI = window.devicePixelRatio;
|
|
453
|
+
const $screenHeight = screen.height;
|
|
454
|
+
const $screenWidth = screen.width;
|
|
455
|
+
const $title = document.title;
|
|
456
|
+
const $userAgent = window.navigator.userAgent;
|
|
457
|
+
return {
|
|
458
|
+
$locale,
|
|
459
|
+
$location,
|
|
460
|
+
$href,
|
|
461
|
+
$pathname,
|
|
462
|
+
$referrer,
|
|
463
|
+
$screenDPI,
|
|
464
|
+
$screenHeight,
|
|
465
|
+
$screenWidth,
|
|
466
|
+
$title,
|
|
467
|
+
$userAgent
|
|
468
|
+
};
|
|
469
|
+
}
|
|
438
470
|
|
|
439
471
|
const isBrowser = typeof window !== 'undefined';
|
|
440
|
-
let userId = null;
|
|
441
472
|
const PREF = 'saasco-sdk';
|
|
442
473
|
const data = {};
|
|
443
474
|
function storeData(key, value, ttl) {
|
|
@@ -503,35 +534,11 @@ function setAnonmousId({
|
|
|
503
534
|
storeData(`anonymous-id`, anonymousId, 1000 * 60 * 60 * 24 * 365);
|
|
504
535
|
return anonymousId;
|
|
505
536
|
}
|
|
506
|
-
function
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
// https://caniuse.com/?search=Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
512
|
-
// Only has 96.63% global support, so we need to check for undefined
|
|
513
|
-
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
514
|
-
const $location = timezones[timezone] || ((_a = $locale === null || $locale === void 0 ? void 0 : $locale.split('-')) === null || _a === void 0 ? void 0 : _a[1]) || 'undefined';
|
|
515
|
-
const $href = window.location.href;
|
|
516
|
-
const $pathname = window.location.pathname;
|
|
517
|
-
const $referrer = document.referrer;
|
|
518
|
-
const $screenDPI = window.devicePixelRatio;
|
|
519
|
-
const $screenHeight = screen.height;
|
|
520
|
-
const $screenWidth = screen.width;
|
|
521
|
-
const $title = document.title;
|
|
522
|
-
const $userAgent = window.navigator.userAgent;
|
|
523
|
-
return {
|
|
524
|
-
$locale,
|
|
525
|
-
$location,
|
|
526
|
-
$href,
|
|
527
|
-
$pathname,
|
|
528
|
-
$referrer,
|
|
529
|
-
$screenDPI,
|
|
530
|
-
$screenHeight,
|
|
531
|
-
$screenWidth,
|
|
532
|
-
$title,
|
|
533
|
-
$userAgent
|
|
534
|
-
};
|
|
537
|
+
function getUserId() {
|
|
538
|
+
return retrieveData(`user-id`);
|
|
539
|
+
}
|
|
540
|
+
function setUserId(userId) {
|
|
541
|
+
storeData(`user-id`, userId, 1000 * 60 * 60 * 24 * 365);
|
|
535
542
|
}
|
|
536
543
|
class Saasco {
|
|
537
544
|
/**
|
|
@@ -555,9 +562,6 @@ class Saasco {
|
|
|
555
562
|
this.config.enabled = this.config.enabled === undefined ? true : this.config.enabled;
|
|
556
563
|
}
|
|
557
564
|
init() {
|
|
558
|
-
if (!this.config.projectId) {
|
|
559
|
-
return this.error("Unable to initialize Saasco. Project ID is required but has not been provided. If you are using an env variable make sure it's set correctly.");
|
|
560
|
-
}
|
|
561
565
|
if (this.isInitialized) {
|
|
562
566
|
this.log('Saasco is already initialized. Please check your code to ensure that init() is not being called multiple times.');
|
|
563
567
|
return;
|
|
@@ -613,9 +617,9 @@ class Saasco {
|
|
|
613
617
|
version,
|
|
614
618
|
sessionId: getSessionId(),
|
|
615
619
|
anonymousId: getAnonymousId(),
|
|
616
|
-
distinctId:
|
|
620
|
+
distinctId: getUserId(),
|
|
617
621
|
projectId: this.config.projectId,
|
|
618
|
-
payload: JSON.stringify(Object.assign(Object.assign({}, browserContext), {
|
|
622
|
+
payload: JSON.stringify(Object.assign(Object.assign({}, browserContext || {}), {
|
|
619
623
|
properties: properties || {}
|
|
620
624
|
})),
|
|
621
625
|
context: JSON.stringify(context || {})
|
|
@@ -642,7 +646,7 @@ class Saasco {
|
|
|
642
646
|
// When the user gets identified as null this will reset the users session and anonymous id
|
|
643
647
|
// This should only be called when distinctId is null and there is an existing userId.
|
|
644
648
|
// This means the user has logged out and we should reset the session and anonymous IDs
|
|
645
|
-
const userIdChangedToNull = distinctId === null && !!
|
|
649
|
+
const userIdChangedToNull = distinctId === null && !!getUserId();
|
|
646
650
|
if (userIdChangedToNull) this.log('User ID change to null. Session reset');
|
|
647
651
|
const reset = userIdChangedToNull;
|
|
648
652
|
setSessionId({
|
|
@@ -652,7 +656,7 @@ class Saasco {
|
|
|
652
656
|
reset
|
|
653
657
|
});
|
|
654
658
|
// set the distinct Id to the userId
|
|
655
|
-
|
|
659
|
+
setUserId(distinctId);
|
|
656
660
|
// No distinctId provided so we don't track the user
|
|
657
661
|
if (!distinctId) return;
|
|
658
662
|
const data = {
|
|
@@ -691,13 +695,13 @@ class Saasco {
|
|
|
691
695
|
body: JSON.stringify(data)
|
|
692
696
|
});
|
|
693
697
|
if (!response.ok) {
|
|
694
|
-
|
|
698
|
+
const errorBody = yield response.json();
|
|
699
|
+
throw new Error(`${errorBody.message}: ${errorBody.errors ? `[${errorBody.errors.join(', ')}]` : ''}` || JSON.stringify(errorBody));
|
|
695
700
|
}
|
|
696
701
|
return response;
|
|
697
702
|
} catch (error) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
return errorMessage;
|
|
703
|
+
this.error(`\nError Message: "${error.message}"`, `\nPath: "/${path}"`, `\nRequest Data: ${JSON.stringify(data, null, 2)}`);
|
|
704
|
+
return error.message;
|
|
701
705
|
}
|
|
702
706
|
});
|
|
703
707
|
}
|
|
@@ -812,6 +816,9 @@ function coerceReservedProperties(properties) {
|
|
|
812
816
|
}, {
|
|
813
817
|
key: 'displayName',
|
|
814
818
|
coerceFrom: ['display_name', 'displayName', 'user_display_name', 'userDisplayName', 'full_name', 'fullName', 'user_full_name', 'userFullName', 'complete_name', 'completeName', 'user_complete_name', 'userCompleteName', 'name', 'username']
|
|
819
|
+
}, {
|
|
820
|
+
key: '$stripeCustomerId',
|
|
821
|
+
coerceFrom: ['stripe_customer_id', 'stripeCustomerId', 'stripe_customer_id', 'stripeCustomerId', 'stripe_customer_id', 'stripeCustomerId', 'stripe_customer_id', 'stripeCustomerId', 'stripe_customer_id', 'stripeCustomerId']
|
|
815
822
|
}];
|
|
816
823
|
return coerce.reduce((acc, {
|
|
817
824
|
key,
|
|
@@ -859,6 +866,61 @@ function coerceValue(properties, keys, schema) {
|
|
|
859
866
|
return coercedValue;
|
|
860
867
|
}
|
|
861
868
|
|
|
869
|
+
const browserContextSchema = zod.z.object({
|
|
870
|
+
$locale: zod.z.string(),
|
|
871
|
+
$location: zod.z.string(),
|
|
872
|
+
$href: zod.z.string(),
|
|
873
|
+
$pathname: zod.z.string(),
|
|
874
|
+
$referrer: zod.z.string(),
|
|
875
|
+
$screenDPI: zod.z.number(),
|
|
876
|
+
$screenHeight: zod.z.number(),
|
|
877
|
+
$screenWidth: zod.z.number(),
|
|
878
|
+
$title: zod.z.string(),
|
|
879
|
+
$userAgent: zod.z.string()
|
|
880
|
+
});
|
|
881
|
+
const serverContextSchema = zod.z.object({
|
|
882
|
+
$city: zod.z.string().optional(),
|
|
883
|
+
$country: zod.z.string().optional(),
|
|
884
|
+
$continent: zod.z.string().optional(),
|
|
885
|
+
$latitude: zod.z.number().optional(),
|
|
886
|
+
$longitude: zod.z.number().optional(),
|
|
887
|
+
$timezone: zod.z.string().optional(),
|
|
888
|
+
$userAgent: zod.z.string().optional(),
|
|
889
|
+
$referrer: zod.z.string().optional(),
|
|
890
|
+
$ip: zod.z.string().optional(),
|
|
891
|
+
$processedAt: zod.z.string().datetime().optional(),
|
|
892
|
+
$identityHash: zod.z.string().optional()
|
|
893
|
+
});
|
|
894
|
+
const trackedEventTypesSchema = zod.z.enum(['SHORTLINK_REDIRECT']);
|
|
895
|
+
const trackedEventSchema = zod.z.object({
|
|
896
|
+
id: zod.z.string().uuid(),
|
|
897
|
+
timestamp: zod.z.string().datetime(),
|
|
898
|
+
projectId: zod.z.string(),
|
|
899
|
+
version: zod.z.string(),
|
|
900
|
+
type: trackedEventTypesSchema,
|
|
901
|
+
payload: zod.z.object({
|
|
902
|
+
browserContext: browserContextSchema.optional(),
|
|
903
|
+
serverContext: serverContextSchema.optional(),
|
|
904
|
+
properties: zod.z.record(zod.z.string(), zod.z.any()).optional()
|
|
905
|
+
})
|
|
906
|
+
});
|
|
907
|
+
/**
|
|
908
|
+
* Creates a zod for tracking any type of event.
|
|
909
|
+
* Includes the base tracking schema and extends with any new schema
|
|
910
|
+
* @param type
|
|
911
|
+
* @param payloadSchema
|
|
912
|
+
* @returns
|
|
913
|
+
*/
|
|
914
|
+
function createTrackedEventTypeSchema(type, payloadSchema) {
|
|
915
|
+
const typeSchema = zod.z.literal(type);
|
|
916
|
+
return trackedEventSchema.extend({
|
|
917
|
+
type: typeSchema,
|
|
918
|
+
payload: zod.z.object(Object.assign(Object.assign({}, trackedEventSchema.shape.payload.shape), {
|
|
919
|
+
properties: payloadSchema
|
|
920
|
+
}))
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
|
|
862
924
|
const tbAny = zod.z.union([zod.z.string(), zod.z.number(), zod.z.array(zod.z.union([zod.z.string(), zod.z.number()]))]);
|
|
863
925
|
const tinyBirdBaseSchema = zod.z.object({
|
|
864
926
|
meta: zod.z.array(zod.z.object({
|
|
@@ -1037,7 +1099,11 @@ const reservedPropertiesSchema = zod.z.object({
|
|
|
1037
1099
|
username: zod.z.string().nullish(),
|
|
1038
1100
|
website: zod.z.string().nullish(),
|
|
1039
1101
|
$id: zod.z.string().nullish(),
|
|
1040
|
-
$lastSeen: zod.z.string().nullish()
|
|
1102
|
+
$lastSeen: zod.z.string().nullish(),
|
|
1103
|
+
// Stripe Properties
|
|
1104
|
+
$stripeCustomerId: zod.z.string().nullish(),
|
|
1105
|
+
$stripeTotalPayments: zod.z.number().or(zod.z.string()).nullish(),
|
|
1106
|
+
$stripeTotalSpent: zod.z.number().or(zod.z.string()).nullish()
|
|
1041
1107
|
});
|
|
1042
1108
|
const contactPropertiesSchema = reservedPropertiesSchema.optional().and(zod.z.record(zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.date(), zod.z.null(), zod.z.array(zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.date(), zod.z.null()]))])));
|
|
1043
1109
|
const analyticsUserRawSchema = zod.z.object({
|
|
@@ -1088,11 +1154,14 @@ exports.analyticsEventSchema = analyticsEventSchema;
|
|
|
1088
1154
|
exports.analyticsUserRawSchema = analyticsUserRawSchema;
|
|
1089
1155
|
exports.analyticsUserSchema = analyticsUserSchema;
|
|
1090
1156
|
exports.baseTopRequestSchema = baseTopRequestSchema;
|
|
1157
|
+
exports.browserContextSchema = browserContextSchema;
|
|
1091
1158
|
exports.coerceReservedProperties = coerceReservedProperties;
|
|
1092
1159
|
exports.contactPropertiesSchema = contactPropertiesSchema;
|
|
1160
|
+
exports.createTrackedEventTypeSchema = createTrackedEventTypeSchema;
|
|
1093
1161
|
exports.currentUsersRequestSchema = currentUsersRequestSchema;
|
|
1094
1162
|
exports.currentUsersResponseSchema = currentUsersResponseSchema;
|
|
1095
1163
|
exports.getAnalyticsUserParamsSchema = getAnalyticsUserParamsSchema;
|
|
1164
|
+
exports.getBrowserContext = getBrowserContext;
|
|
1096
1165
|
exports.idenfitySchema = idenfitySchema;
|
|
1097
1166
|
exports.kpiSchema = kpiSchema;
|
|
1098
1167
|
exports.kpisRequestSchema = kpisRequestSchema;
|
|
@@ -1104,6 +1173,7 @@ exports.latestIdentifiesRequestSchema = latestIdentifiesRequestSchema;
|
|
|
1104
1173
|
exports.latestIdentifiesResponseSchema = latestIdentifiesResponseSchema;
|
|
1105
1174
|
exports.listUsersParamsSchema = listUsersParamsSchema;
|
|
1106
1175
|
exports.reservedPropertiesSchema = reservedPropertiesSchema;
|
|
1176
|
+
exports.serverContextSchema = serverContextSchema;
|
|
1107
1177
|
exports.tbAny = tbAny;
|
|
1108
1178
|
exports.timezones = timezones;
|
|
1109
1179
|
exports.tinyBirdBaseSchema = tinyBirdBaseSchema;
|
|
@@ -1114,4 +1184,6 @@ exports.topLocationsResponseSchema = topLocationsResponseSchema;
|
|
|
1114
1184
|
exports.topPagesResponseSchema = topPagesResponseSchema;
|
|
1115
1185
|
exports.topSourcesResponseSchema = topSourcesResponseSchema;
|
|
1116
1186
|
exports.topSourcesSchema = topSourcesSchema;
|
|
1187
|
+
exports.trackedEventSchema = trackedEventSchema;
|
|
1188
|
+
exports.trackedEventTypesSchema = trackedEventTypesSchema;
|
|
1117
1189
|
exports.userEventsResponseSchema = userEventsResponseSchema;
|
package/index.esm.js
CHANGED
|
@@ -2,6 +2,8 @@ import { __awaiter } from 'tslib';
|
|
|
2
2
|
import { v4 } from '@lukeed/uuid';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
|
+
var version = "0.1.14";
|
|
6
|
+
|
|
5
7
|
const timezones = {
|
|
6
8
|
'Asia/Barnaul': 'RU',
|
|
7
9
|
'Africa/Nouakchott': 'MR',
|
|
@@ -430,10 +432,39 @@ const timezones = {
|
|
|
430
432
|
'Asia/Calcutta': 'IN'
|
|
431
433
|
};
|
|
432
434
|
|
|
433
|
-
|
|
435
|
+
function getBrowserContext() {
|
|
436
|
+
var _a;
|
|
437
|
+
const isBrowser = typeof window !== 'undefined';
|
|
438
|
+
if (!isBrowser) return;
|
|
439
|
+
const customNavigator = navigator;
|
|
440
|
+
const $locale = (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.languages) && (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.languages.length) ? customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.languages[0] : (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.userLanguage) || (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.language) || (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.browserLanguage) || 'en';
|
|
441
|
+
// https://caniuse.com/?search=Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
442
|
+
// Only has 96.63% global support, so we need to check for undefined
|
|
443
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
444
|
+
const $location = timezones[timezone] || ((_a = $locale === null || $locale === void 0 ? void 0 : $locale.split('-')) === null || _a === void 0 ? void 0 : _a[1]) || 'undefined';
|
|
445
|
+
const $href = window.location.href;
|
|
446
|
+
const $pathname = window.location.pathname;
|
|
447
|
+
const $referrer = document.referrer;
|
|
448
|
+
const $screenDPI = window.devicePixelRatio;
|
|
449
|
+
const $screenHeight = screen.height;
|
|
450
|
+
const $screenWidth = screen.width;
|
|
451
|
+
const $title = document.title;
|
|
452
|
+
const $userAgent = window.navigator.userAgent;
|
|
453
|
+
return {
|
|
454
|
+
$locale,
|
|
455
|
+
$location,
|
|
456
|
+
$href,
|
|
457
|
+
$pathname,
|
|
458
|
+
$referrer,
|
|
459
|
+
$screenDPI,
|
|
460
|
+
$screenHeight,
|
|
461
|
+
$screenWidth,
|
|
462
|
+
$title,
|
|
463
|
+
$userAgent
|
|
464
|
+
};
|
|
465
|
+
}
|
|
434
466
|
|
|
435
467
|
const isBrowser = typeof window !== 'undefined';
|
|
436
|
-
let userId = null;
|
|
437
468
|
const PREF = 'saasco-sdk';
|
|
438
469
|
const data = {};
|
|
439
470
|
function storeData(key, value, ttl) {
|
|
@@ -499,35 +530,11 @@ function setAnonmousId({
|
|
|
499
530
|
storeData(`anonymous-id`, anonymousId, 1000 * 60 * 60 * 24 * 365);
|
|
500
531
|
return anonymousId;
|
|
501
532
|
}
|
|
502
|
-
function
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
// https://caniuse.com/?search=Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
508
|
-
// Only has 96.63% global support, so we need to check for undefined
|
|
509
|
-
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
510
|
-
const $location = timezones[timezone] || ((_a = $locale === null || $locale === void 0 ? void 0 : $locale.split('-')) === null || _a === void 0 ? void 0 : _a[1]) || 'undefined';
|
|
511
|
-
const $href = window.location.href;
|
|
512
|
-
const $pathname = window.location.pathname;
|
|
513
|
-
const $referrer = document.referrer;
|
|
514
|
-
const $screenDPI = window.devicePixelRatio;
|
|
515
|
-
const $screenHeight = screen.height;
|
|
516
|
-
const $screenWidth = screen.width;
|
|
517
|
-
const $title = document.title;
|
|
518
|
-
const $userAgent = window.navigator.userAgent;
|
|
519
|
-
return {
|
|
520
|
-
$locale,
|
|
521
|
-
$location,
|
|
522
|
-
$href,
|
|
523
|
-
$pathname,
|
|
524
|
-
$referrer,
|
|
525
|
-
$screenDPI,
|
|
526
|
-
$screenHeight,
|
|
527
|
-
$screenWidth,
|
|
528
|
-
$title,
|
|
529
|
-
$userAgent
|
|
530
|
-
};
|
|
533
|
+
function getUserId() {
|
|
534
|
+
return retrieveData(`user-id`);
|
|
535
|
+
}
|
|
536
|
+
function setUserId(userId) {
|
|
537
|
+
storeData(`user-id`, userId, 1000 * 60 * 60 * 24 * 365);
|
|
531
538
|
}
|
|
532
539
|
class Saasco {
|
|
533
540
|
/**
|
|
@@ -551,9 +558,6 @@ class Saasco {
|
|
|
551
558
|
this.config.enabled = this.config.enabled === undefined ? true : this.config.enabled;
|
|
552
559
|
}
|
|
553
560
|
init() {
|
|
554
|
-
if (!this.config.projectId) {
|
|
555
|
-
return this.error("Unable to initialize Saasco. Project ID is required but has not been provided. If you are using an env variable make sure it's set correctly.");
|
|
556
|
-
}
|
|
557
561
|
if (this.isInitialized) {
|
|
558
562
|
this.log('Saasco is already initialized. Please check your code to ensure that init() is not being called multiple times.');
|
|
559
563
|
return;
|
|
@@ -609,9 +613,9 @@ class Saasco {
|
|
|
609
613
|
version,
|
|
610
614
|
sessionId: getSessionId(),
|
|
611
615
|
anonymousId: getAnonymousId(),
|
|
612
|
-
distinctId:
|
|
616
|
+
distinctId: getUserId(),
|
|
613
617
|
projectId: this.config.projectId,
|
|
614
|
-
payload: JSON.stringify(Object.assign(Object.assign({}, browserContext), {
|
|
618
|
+
payload: JSON.stringify(Object.assign(Object.assign({}, browserContext || {}), {
|
|
615
619
|
properties: properties || {}
|
|
616
620
|
})),
|
|
617
621
|
context: JSON.stringify(context || {})
|
|
@@ -638,7 +642,7 @@ class Saasco {
|
|
|
638
642
|
// When the user gets identified as null this will reset the users session and anonymous id
|
|
639
643
|
// This should only be called when distinctId is null and there is an existing userId.
|
|
640
644
|
// This means the user has logged out and we should reset the session and anonymous IDs
|
|
641
|
-
const userIdChangedToNull = distinctId === null && !!
|
|
645
|
+
const userIdChangedToNull = distinctId === null && !!getUserId();
|
|
642
646
|
if (userIdChangedToNull) this.log('User ID change to null. Session reset');
|
|
643
647
|
const reset = userIdChangedToNull;
|
|
644
648
|
setSessionId({
|
|
@@ -648,7 +652,7 @@ class Saasco {
|
|
|
648
652
|
reset
|
|
649
653
|
});
|
|
650
654
|
// set the distinct Id to the userId
|
|
651
|
-
|
|
655
|
+
setUserId(distinctId);
|
|
652
656
|
// No distinctId provided so we don't track the user
|
|
653
657
|
if (!distinctId) return;
|
|
654
658
|
const data = {
|
|
@@ -687,13 +691,13 @@ class Saasco {
|
|
|
687
691
|
body: JSON.stringify(data)
|
|
688
692
|
});
|
|
689
693
|
if (!response.ok) {
|
|
690
|
-
|
|
694
|
+
const errorBody = yield response.json();
|
|
695
|
+
throw new Error(`${errorBody.message}: ${errorBody.errors ? `[${errorBody.errors.join(', ')}]` : ''}` || JSON.stringify(errorBody));
|
|
691
696
|
}
|
|
692
697
|
return response;
|
|
693
698
|
} catch (error) {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
return errorMessage;
|
|
699
|
+
this.error(`\nError Message: "${error.message}"`, `\nPath: "/${path}"`, `\nRequest Data: ${JSON.stringify(data, null, 2)}`);
|
|
700
|
+
return error.message;
|
|
697
701
|
}
|
|
698
702
|
});
|
|
699
703
|
}
|
|
@@ -808,6 +812,9 @@ function coerceReservedProperties(properties) {
|
|
|
808
812
|
}, {
|
|
809
813
|
key: 'displayName',
|
|
810
814
|
coerceFrom: ['display_name', 'displayName', 'user_display_name', 'userDisplayName', 'full_name', 'fullName', 'user_full_name', 'userFullName', 'complete_name', 'completeName', 'user_complete_name', 'userCompleteName', 'name', 'username']
|
|
815
|
+
}, {
|
|
816
|
+
key: '$stripeCustomerId',
|
|
817
|
+
coerceFrom: ['stripe_customer_id', 'stripeCustomerId', 'stripe_customer_id', 'stripeCustomerId', 'stripe_customer_id', 'stripeCustomerId', 'stripe_customer_id', 'stripeCustomerId', 'stripe_customer_id', 'stripeCustomerId']
|
|
811
818
|
}];
|
|
812
819
|
return coerce.reduce((acc, {
|
|
813
820
|
key,
|
|
@@ -855,6 +862,61 @@ function coerceValue(properties, keys, schema) {
|
|
|
855
862
|
return coercedValue;
|
|
856
863
|
}
|
|
857
864
|
|
|
865
|
+
const browserContextSchema = z.object({
|
|
866
|
+
$locale: z.string(),
|
|
867
|
+
$location: z.string(),
|
|
868
|
+
$href: z.string(),
|
|
869
|
+
$pathname: z.string(),
|
|
870
|
+
$referrer: z.string(),
|
|
871
|
+
$screenDPI: z.number(),
|
|
872
|
+
$screenHeight: z.number(),
|
|
873
|
+
$screenWidth: z.number(),
|
|
874
|
+
$title: z.string(),
|
|
875
|
+
$userAgent: z.string()
|
|
876
|
+
});
|
|
877
|
+
const serverContextSchema = z.object({
|
|
878
|
+
$city: z.string().optional(),
|
|
879
|
+
$country: z.string().optional(),
|
|
880
|
+
$continent: z.string().optional(),
|
|
881
|
+
$latitude: z.number().optional(),
|
|
882
|
+
$longitude: z.number().optional(),
|
|
883
|
+
$timezone: z.string().optional(),
|
|
884
|
+
$userAgent: z.string().optional(),
|
|
885
|
+
$referrer: z.string().optional(),
|
|
886
|
+
$ip: z.string().optional(),
|
|
887
|
+
$processedAt: z.string().datetime().optional(),
|
|
888
|
+
$identityHash: z.string().optional()
|
|
889
|
+
});
|
|
890
|
+
const trackedEventTypesSchema = z.enum(['SHORTLINK_REDIRECT']);
|
|
891
|
+
const trackedEventSchema = z.object({
|
|
892
|
+
id: z.string().uuid(),
|
|
893
|
+
timestamp: z.string().datetime(),
|
|
894
|
+
projectId: z.string(),
|
|
895
|
+
version: z.string(),
|
|
896
|
+
type: trackedEventTypesSchema,
|
|
897
|
+
payload: z.object({
|
|
898
|
+
browserContext: browserContextSchema.optional(),
|
|
899
|
+
serverContext: serverContextSchema.optional(),
|
|
900
|
+
properties: z.record(z.string(), z.any()).optional()
|
|
901
|
+
})
|
|
902
|
+
});
|
|
903
|
+
/**
|
|
904
|
+
* Creates a zod for tracking any type of event.
|
|
905
|
+
* Includes the base tracking schema and extends with any new schema
|
|
906
|
+
* @param type
|
|
907
|
+
* @param payloadSchema
|
|
908
|
+
* @returns
|
|
909
|
+
*/
|
|
910
|
+
function createTrackedEventTypeSchema(type, payloadSchema) {
|
|
911
|
+
const typeSchema = z.literal(type);
|
|
912
|
+
return trackedEventSchema.extend({
|
|
913
|
+
type: typeSchema,
|
|
914
|
+
payload: z.object(Object.assign(Object.assign({}, trackedEventSchema.shape.payload.shape), {
|
|
915
|
+
properties: payloadSchema
|
|
916
|
+
}))
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
|
|
858
920
|
const tbAny = z.union([z.string(), z.number(), z.array(z.union([z.string(), z.number()]))]);
|
|
859
921
|
const tinyBirdBaseSchema = z.object({
|
|
860
922
|
meta: z.array(z.object({
|
|
@@ -1033,7 +1095,11 @@ const reservedPropertiesSchema = z.object({
|
|
|
1033
1095
|
username: z.string().nullish(),
|
|
1034
1096
|
website: z.string().nullish(),
|
|
1035
1097
|
$id: z.string().nullish(),
|
|
1036
|
-
$lastSeen: z.string().nullish()
|
|
1098
|
+
$lastSeen: z.string().nullish(),
|
|
1099
|
+
// Stripe Properties
|
|
1100
|
+
$stripeCustomerId: z.string().nullish(),
|
|
1101
|
+
$stripeTotalPayments: z.number().or(z.string()).nullish(),
|
|
1102
|
+
$stripeTotalSpent: z.number().or(z.string()).nullish()
|
|
1037
1103
|
});
|
|
1038
1104
|
const contactPropertiesSchema = reservedPropertiesSchema.optional().and(z.record(z.union([z.string(), z.number(), z.boolean(), z.date(), z.null(), z.array(z.union([z.string(), z.number(), z.boolean(), z.date(), z.null()]))])));
|
|
1039
1105
|
const analyticsUserRawSchema = z.object({
|
|
@@ -1078,4 +1144,4 @@ const latestIdentifiesResponseSchema = tinyBirdBaseSchema.extend({
|
|
|
1078
1144
|
}))
|
|
1079
1145
|
});
|
|
1080
1146
|
|
|
1081
|
-
export { Saasco, TinybirdJobResponseSchema, analyticsEventSchema, analyticsUserRawSchema, analyticsUserSchema, baseTopRequestSchema, coerceReservedProperties, contactPropertiesSchema, currentUsersRequestSchema, currentUsersResponseSchema, getAnalyticsUserParamsSchema, idenfitySchema, kpiSchema, kpisRequestSchema, kpisResponseSchema, latestEventItemSchema, latestEventsRequestSchema, latestEventsResponseSchema, latestIdentifiesRequestSchema, latestIdentifiesResponseSchema, listUsersParamsSchema, reservedPropertiesSchema, tbAny, timezones, tinyBirdBaseSchema, topActionsResponseSchema, topBrowsersResponseSchema, topDevicesResponseSchema, topLocationsResponseSchema, topPagesResponseSchema, topSourcesResponseSchema, topSourcesSchema, userEventsResponseSchema };
|
|
1147
|
+
export { Saasco, TinybirdJobResponseSchema, analyticsEventSchema, analyticsUserRawSchema, analyticsUserSchema, baseTopRequestSchema, browserContextSchema, coerceReservedProperties, contactPropertiesSchema, createTrackedEventTypeSchema, currentUsersRequestSchema, currentUsersResponseSchema, getAnalyticsUserParamsSchema, getBrowserContext, idenfitySchema, kpiSchema, kpisRequestSchema, kpisResponseSchema, latestEventItemSchema, latestEventsRequestSchema, latestEventsResponseSchema, latestIdentifiesRequestSchema, latestIdentifiesResponseSchema, listUsersParamsSchema, reservedPropertiesSchema, serverContextSchema, tbAny, timezones, tinyBirdBaseSchema, topActionsResponseSchema, topBrowsersResponseSchema, topDevicesResponseSchema, topLocationsResponseSchema, topPagesResponseSchema, topSourcesResponseSchema, topSourcesSchema, trackedEventSchema, trackedEventTypesSchema, userEventsResponseSchema };
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
package/src/lib/analytics.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
declare global {
|
|
2
2
|
interface Window {
|
|
3
3
|
saascoAutoPageTrackingActive?: boolean;
|
|
4
|
+
saasco: Saasco;
|
|
4
5
|
}
|
|
5
6
|
}
|
|
6
7
|
export declare class Saasco {
|
|
@@ -37,14 +38,14 @@ export declare class Saasco {
|
|
|
37
38
|
*/
|
|
38
39
|
track(name: string, properties?: Record<string, any>, context?: {
|
|
39
40
|
active?: boolean;
|
|
40
|
-
}): void | Promise<
|
|
41
|
+
}): void | Promise<any>;
|
|
41
42
|
/**
|
|
42
43
|
* The page method lets you record page views on your website
|
|
43
44
|
* This records the page title and path and names the event useing the reserved property "Page Viewed"
|
|
44
45
|
*
|
|
45
46
|
* Before implementing this make sure you have disabled the autoPageTracking in the config or you will get duplicate page views
|
|
46
47
|
*/
|
|
47
|
-
page(): void | Promise<
|
|
48
|
+
page(): void | Promise<any>;
|
|
48
49
|
/**
|
|
49
50
|
* The identify method lets you tie a user to their actions and record traits about them.
|
|
50
51
|
* We recommend you call this when the user logs in and when any traits get updated.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types';
|