@funnelsgrove/analytics 0.1.0 → 0.1.1
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 +2 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/services/bootstrap-public-analytics-user.d.ts +1 -1
- package/dist/services/bootstrap-public-analytics-user.js +1 -1
- package/dist/services/public-analytics-sdk.service.d.ts +16 -5
- package/dist/services/public-analytics-sdk.service.js +130 -43
- package/dist/services/public-env.d.ts +1 -0
- package/dist/services/public-env.js +9 -0
- package/dist/services/runtime-api.config.d.ts +5 -0
- package/dist/services/runtime-api.config.js +12 -4
- package/dist/services/sdk-user-id.storage.d.ts +5 -4
- package/dist/services/sdk-user-id.storage.js +28 -14
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -6,7 +6,8 @@ Shared public analytics SDK helpers for funnels.
|
|
|
6
6
|
|
|
7
7
|
- Build distributable output with `npm run build --workspace @funnelsgrove/analytics`.
|
|
8
8
|
- Publish from the repo root with `npm publish --workspace @funnelsgrove/analytics --access public`.
|
|
9
|
-
- Local repo installs still resolve this package through npm workspaces when another workspace depends on version `0.1.
|
|
9
|
+
- Local repo installs still resolve this package through npm workspaces when another workspace depends on version `0.1.1`.
|
|
10
|
+
- The build normalizes generated relative ESM imports to explicit `.js` files for published package consumers.
|
|
10
11
|
|
|
11
12
|
## Use This Package For
|
|
12
13
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from './services/runtime-api.config';
|
|
2
|
-
export * from './services/sdk-user-id.storage';
|
|
3
|
-
export * from './services/bootstrap-public-analytics-user';
|
|
4
|
-
export * from './services/public-analytics-sdk.service';
|
|
1
|
+
export * from './services/runtime-api.config.js';
|
|
2
|
+
export * from './services/sdk-user-id.storage.js';
|
|
3
|
+
export * from './services/bootstrap-public-analytics-user.js';
|
|
4
|
+
export * from './services/public-analytics-sdk.service.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from './services/runtime-api.config';
|
|
2
|
-
export * from './services/sdk-user-id.storage';
|
|
3
|
-
export * from './services/bootstrap-public-analytics-user';
|
|
4
|
-
export * from './services/public-analytics-sdk.service';
|
|
1
|
+
export * from './services/runtime-api.config.js';
|
|
2
|
+
export * from './services/sdk-user-id.storage.js';
|
|
3
|
+
export * from './services/bootstrap-public-analytics-user.js';
|
|
4
|
+
export * from './services/public-analytics-sdk.service.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PublicSdkIdentifyInput } from './public-analytics-sdk.service';
|
|
1
|
+
import type { PublicSdkIdentifyInput } from './public-analytics-sdk.service.js';
|
|
2
2
|
export type BootstrapPublicAnalyticsUserClient = {
|
|
3
3
|
identify(input?: PublicSdkIdentifyInput): Promise<string>;
|
|
4
4
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { publicAnalyticsSdk } from './public-analytics-sdk.service';
|
|
1
|
+
import { publicAnalyticsSdk } from './public-analytics-sdk.service.js';
|
|
2
2
|
export const bootstrapPublicAnalyticsUser = async (input = {}, client = publicAnalyticsSdk) => {
|
|
3
3
|
return client.identify(input);
|
|
4
4
|
};
|
|
@@ -10,11 +10,13 @@ export type PublicSdkTrackEventInput = {
|
|
|
10
10
|
payload?: Record<string, unknown>;
|
|
11
11
|
context?: Record<string, unknown>;
|
|
12
12
|
metadata?: Record<string, unknown>;
|
|
13
|
+
featureFlags?: Record<string, string | null | undefined>;
|
|
13
14
|
};
|
|
14
15
|
export type PublicSdkStepStartInput = {
|
|
15
16
|
stepId: string;
|
|
16
17
|
stepName: string;
|
|
17
18
|
startedAt: string;
|
|
19
|
+
featureFlags?: Record<string, string | null | undefined>;
|
|
18
20
|
};
|
|
19
21
|
export type PublicSdkStepEndInput = {
|
|
20
22
|
stepId: string;
|
|
@@ -22,9 +24,10 @@ export type PublicSdkStepEndInput = {
|
|
|
22
24
|
startedAt: string;
|
|
23
25
|
endedAt: string;
|
|
24
26
|
selected?: Record<string, unknown>;
|
|
27
|
+
featureFlags?: Record<string, string | null | undefined>;
|
|
25
28
|
};
|
|
26
29
|
export type PublicSdkIdentifyInput = {
|
|
27
|
-
|
|
30
|
+
user_id?: string;
|
|
28
31
|
email?: string;
|
|
29
32
|
fullName?: string;
|
|
30
33
|
metadata?: Record<string, unknown>;
|
|
@@ -35,19 +38,27 @@ export type PublicAnalyticsSdkOptions = {
|
|
|
35
38
|
};
|
|
36
39
|
export declare class PublicAnalyticsSdkService {
|
|
37
40
|
private readonly options;
|
|
38
|
-
private readonly endpoint;
|
|
39
41
|
private readonly eventQueue;
|
|
40
42
|
private flushTimerId;
|
|
41
43
|
private inFlightFlush;
|
|
42
|
-
private
|
|
44
|
+
private userId;
|
|
43
45
|
constructor(options?: Partial<PublicAnalyticsSdkOptions>);
|
|
44
|
-
|
|
46
|
+
getCurrentUserId(): string;
|
|
45
47
|
identify(input?: PublicSdkIdentifyInput): Promise<string>;
|
|
48
|
+
fetchCurrentUserState(input?: {
|
|
49
|
+
user_id?: string;
|
|
50
|
+
}): Promise<{
|
|
51
|
+
user: Record<string, unknown> | null;
|
|
52
|
+
events: unknown[];
|
|
53
|
+
subscriptions: unknown[];
|
|
54
|
+
payments: unknown[];
|
|
55
|
+
} | null>;
|
|
46
56
|
track(event: PublicSdkTrackEventInput): string | null;
|
|
47
57
|
trackStepStarted(input: PublicSdkStepStartInput): string | null;
|
|
48
58
|
trackStepCompleted(input: PublicSdkStepEndInput): string | null;
|
|
49
59
|
flush(): Promise<number>;
|
|
50
|
-
private
|
|
60
|
+
private ensureUserId;
|
|
61
|
+
private resolveBootstrapUserId;
|
|
51
62
|
private scheduleFlush;
|
|
52
63
|
private clearFlushTimer;
|
|
53
64
|
private flushQueue;
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { buildMainApiUrl, buildSdkHeaders, FUNNEL_ID, getFunnelSdkPublishableKey, } from './runtime-api.config';
|
|
2
|
-
import { canUseDom, getOrCreateUserId, persistUserId, } from './sdk-user-id.storage';
|
|
1
|
+
import { buildMainApiUrl, buildSdkHeaders, FUNNEL_ID, FUNNEL_VERSION_ID, getFunnelSdkPublishableKey, hasPostHogRuntimeConfig, POSTHOG_API_HOST, POSTHOG_PROJECT_API_KEY, PROJECT_ID, } from './runtime-api.config.js';
|
|
2
|
+
import { canUseDom, getOrCreateUserId, persistUserId, readStoredUserId, } from './sdk-user-id.storage.js';
|
|
3
3
|
const DEFAULT_OPTIONS = {
|
|
4
4
|
batchSize: 20,
|
|
5
5
|
flushIntervalMs: 1500,
|
|
6
6
|
};
|
|
7
|
+
const POSTHOG_BATCH_ENDPOINT = '/batch/';
|
|
7
8
|
const asRecord = (value) => {
|
|
8
|
-
if (!value || typeof value !==
|
|
9
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
9
10
|
return {};
|
|
10
11
|
}
|
|
11
12
|
return value;
|
|
12
13
|
};
|
|
13
14
|
const asString = (value) => {
|
|
14
|
-
if (typeof value !==
|
|
15
|
+
if (typeof value !== 'string') {
|
|
15
16
|
return null;
|
|
16
17
|
}
|
|
17
18
|
const trimmed = value.trim();
|
|
@@ -28,60 +29,118 @@ const toIsoOrNow = (value) => {
|
|
|
28
29
|
};
|
|
29
30
|
const buildEventId = () => {
|
|
30
31
|
var _a;
|
|
31
|
-
if (canUseDom() && typeof ((_a = window.crypto) === null || _a === void 0 ? void 0 : _a.randomUUID) ===
|
|
32
|
-
return `sdk_evt_${window.crypto.randomUUID().replace(/-/g,
|
|
32
|
+
if (canUseDom() && typeof ((_a = window.crypto) === null || _a === void 0 ? void 0 : _a.randomUUID) === 'function') {
|
|
33
|
+
return `sdk_evt_${window.crypto.randomUUID().replace(/-/g, '')}`;
|
|
33
34
|
}
|
|
34
35
|
return `sdk_evt_${Date.now()}_${Math.random().toString(16).slice(2, 10)}`;
|
|
35
36
|
};
|
|
37
|
+
const toPostHogEventName = (eventType) => {
|
|
38
|
+
switch (eventType) {
|
|
39
|
+
case 'funnel_start':
|
|
40
|
+
return 'funnel_started';
|
|
41
|
+
case 'step_start':
|
|
42
|
+
return 'step_started';
|
|
43
|
+
case 'step_end':
|
|
44
|
+
return 'step_completed';
|
|
45
|
+
default:
|
|
46
|
+
return eventType;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const buildDefinedRecord = (value) => {
|
|
50
|
+
return Object.fromEntries(Object.entries(value).filter(([, candidate]) => candidate !== undefined));
|
|
51
|
+
};
|
|
52
|
+
const normalizeFeatureFlags = (featureFlags) => {
|
|
53
|
+
if (!featureFlags) {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
return Object.fromEntries(Object.entries(featureFlags)
|
|
57
|
+
.map(([key, value]) => [key.trim(), (value === null || value === void 0 ? void 0 : value.trim()) || ''])
|
|
58
|
+
.filter(([key, value]) => Boolean(key && value)));
|
|
59
|
+
};
|
|
60
|
+
const buildFeatureFlagProperties = (featureFlags) => {
|
|
61
|
+
return Object.fromEntries(Object.entries(featureFlags).map(([key, value]) => [`$feature/${key}`, value]));
|
|
62
|
+
};
|
|
36
63
|
export class PublicAnalyticsSdkService {
|
|
37
64
|
constructor(options = {}) {
|
|
38
65
|
this.options = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
|
|
39
|
-
this.endpoint = buildMainApiUrl("/sdk/public/events");
|
|
40
66
|
this.eventQueue = [];
|
|
41
67
|
this.flushTimerId = null;
|
|
42
68
|
this.inFlightFlush = null;
|
|
43
|
-
this.
|
|
69
|
+
this.userId = null;
|
|
44
70
|
}
|
|
45
|
-
|
|
46
|
-
return this.
|
|
71
|
+
getCurrentUserId() {
|
|
72
|
+
return this.ensureUserId();
|
|
47
73
|
}
|
|
48
74
|
async identify(input = {}) {
|
|
49
75
|
var _a;
|
|
50
|
-
const
|
|
76
|
+
const userId = await this.resolveBootstrapUserId(input.user_id);
|
|
51
77
|
const publishableKey = getFunnelSdkPublishableKey();
|
|
52
78
|
if (!publishableKey) {
|
|
53
|
-
this.
|
|
54
|
-
return
|
|
79
|
+
this.userId = userId;
|
|
80
|
+
return userId;
|
|
55
81
|
}
|
|
56
82
|
try {
|
|
57
|
-
const response = await fetch(buildMainApiUrl(
|
|
58
|
-
method:
|
|
83
|
+
const response = await fetch(buildMainApiUrl('/sdk/public/users/bootstrap'), {
|
|
84
|
+
method: 'POST',
|
|
59
85
|
headers: buildSdkHeaders({
|
|
60
|
-
|
|
86
|
+
'Content-Type': 'application/json',
|
|
61
87
|
}),
|
|
62
88
|
body: JSON.stringify({
|
|
63
89
|
publishableKey,
|
|
64
90
|
funnelId: FUNNEL_ID || undefined,
|
|
65
|
-
|
|
91
|
+
funnelVersionId: FUNNEL_VERSION_ID || undefined,
|
|
92
|
+
user_id: userId,
|
|
66
93
|
email: asString(input.email) || undefined,
|
|
67
94
|
fullName: asString(input.fullName) || undefined,
|
|
68
95
|
metadata: asRecord(input.metadata),
|
|
69
96
|
}),
|
|
70
97
|
});
|
|
71
98
|
if (!response.ok) {
|
|
72
|
-
this.
|
|
73
|
-
return
|
|
99
|
+
this.userId = userId;
|
|
100
|
+
return userId;
|
|
74
101
|
}
|
|
75
102
|
const payload = (await response.json());
|
|
76
|
-
const persistedUserId = persistUserId(asString((_a = payload.user) === null || _a === void 0 ? void 0 : _a.
|
|
77
|
-
asString(payload.
|
|
78
|
-
|
|
79
|
-
this.
|
|
103
|
+
const persistedUserId = persistUserId(asString((_a = payload.user) === null || _a === void 0 ? void 0 : _a.user_id) ||
|
|
104
|
+
asString(payload.user_id) ||
|
|
105
|
+
userId, FUNNEL_ID) || userId;
|
|
106
|
+
this.userId = persistedUserId;
|
|
80
107
|
return persistedUserId;
|
|
81
108
|
}
|
|
82
109
|
catch (_b) {
|
|
83
|
-
this.
|
|
84
|
-
return
|
|
110
|
+
this.userId = userId;
|
|
111
|
+
return userId;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async fetchCurrentUserState(input = {}) {
|
|
115
|
+
const publishableKey = getFunnelSdkPublishableKey();
|
|
116
|
+
if (!publishableKey || !FUNNEL_ID) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const userId = asString(input.user_id) || this.getCurrentUserId();
|
|
120
|
+
if (!userId) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const url = new URL(buildMainApiUrl('/sdk/public/users/me'));
|
|
125
|
+
url.searchParams.set('funnelId', FUNNEL_ID);
|
|
126
|
+
url.searchParams.set('user_id', userId);
|
|
127
|
+
const response = await fetch(url.toString(), {
|
|
128
|
+
method: 'GET',
|
|
129
|
+
headers: buildSdkHeaders({}),
|
|
130
|
+
});
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const payload = (await response.json());
|
|
135
|
+
return {
|
|
136
|
+
user: payload.user || null,
|
|
137
|
+
events: Array.isArray(payload.events) ? payload.events : [],
|
|
138
|
+
subscriptions: Array.isArray(payload.subscriptions) ? payload.subscriptions : [],
|
|
139
|
+
payments: Array.isArray(payload.payments) ? payload.payments : [],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (_a) {
|
|
143
|
+
return null;
|
|
85
144
|
}
|
|
86
145
|
}
|
|
87
146
|
track(event) {
|
|
@@ -104,6 +163,7 @@ export class PublicAnalyticsSdkService {
|
|
|
104
163
|
payload: asRecord(event.payload),
|
|
105
164
|
context: asRecord(event.context),
|
|
106
165
|
metadata: asRecord(event.metadata),
|
|
166
|
+
featureFlags: normalizeFeatureFlags(event.featureFlags),
|
|
107
167
|
};
|
|
108
168
|
this.eventQueue.push(envelope);
|
|
109
169
|
if (this.eventQueue.length >= this.options.batchSize) {
|
|
@@ -115,22 +175,24 @@ export class PublicAnalyticsSdkService {
|
|
|
115
175
|
}
|
|
116
176
|
trackStepStarted(input) {
|
|
117
177
|
return this.track({
|
|
118
|
-
eventType:
|
|
178
|
+
eventType: 'step_start',
|
|
119
179
|
stepId: input.stepId,
|
|
120
180
|
stepName: input.stepName,
|
|
121
181
|
startedAt: input.startedAt,
|
|
122
182
|
occurredAt: input.startedAt,
|
|
183
|
+
featureFlags: input.featureFlags,
|
|
123
184
|
});
|
|
124
185
|
}
|
|
125
186
|
trackStepCompleted(input) {
|
|
126
187
|
return this.track({
|
|
127
|
-
eventType:
|
|
188
|
+
eventType: 'step_end',
|
|
128
189
|
stepId: input.stepId,
|
|
129
190
|
stepName: input.stepName,
|
|
130
191
|
startedAt: input.startedAt,
|
|
131
192
|
endedAt: input.endedAt,
|
|
132
193
|
occurredAt: input.endedAt,
|
|
133
194
|
selected: asRecord(input.selected),
|
|
195
|
+
featureFlags: input.featureFlags,
|
|
134
196
|
});
|
|
135
197
|
}
|
|
136
198
|
async flush() {
|
|
@@ -143,12 +205,32 @@ export class PublicAnalyticsSdkService {
|
|
|
143
205
|
});
|
|
144
206
|
return this.inFlightFlush;
|
|
145
207
|
}
|
|
146
|
-
|
|
147
|
-
if (this.
|
|
148
|
-
return this.
|
|
208
|
+
ensureUserId() {
|
|
209
|
+
if (this.userId) {
|
|
210
|
+
return this.userId;
|
|
149
211
|
}
|
|
150
|
-
this.
|
|
151
|
-
return this.
|
|
212
|
+
this.userId = persistUserId(getOrCreateUserId(FUNNEL_ID), FUNNEL_ID);
|
|
213
|
+
return this.userId;
|
|
214
|
+
}
|
|
215
|
+
async resolveBootstrapUserId(candidateUserId) {
|
|
216
|
+
var _a;
|
|
217
|
+
const storedUserId = readStoredUserId(FUNNEL_ID);
|
|
218
|
+
const fallbackUserId = storedUserId || getOrCreateUserId(FUNNEL_ID);
|
|
219
|
+
const normalizedCandidateUserId = asString(candidateUserId);
|
|
220
|
+
if (!normalizedCandidateUserId) {
|
|
221
|
+
return persistUserId(fallbackUserId, FUNNEL_ID);
|
|
222
|
+
}
|
|
223
|
+
if (normalizedCandidateUserId === storedUserId) {
|
|
224
|
+
return persistUserId(normalizedCandidateUserId, FUNNEL_ID);
|
|
225
|
+
}
|
|
226
|
+
if (!getFunnelSdkPublishableKey() || !FUNNEL_ID) {
|
|
227
|
+
return persistUserId(fallbackUserId, FUNNEL_ID);
|
|
228
|
+
}
|
|
229
|
+
const existingUserState = await this.fetchCurrentUserState({
|
|
230
|
+
user_id: normalizedCandidateUserId,
|
|
231
|
+
});
|
|
232
|
+
const validatedUserId = asString((_a = existingUserState === null || existingUserState === void 0 ? void 0 : existingUserState.user) === null || _a === void 0 ? void 0 : _a.user_id);
|
|
233
|
+
return persistUserId(validatedUserId || fallbackUserId, FUNNEL_ID);
|
|
152
234
|
}
|
|
153
235
|
scheduleFlush() {
|
|
154
236
|
if (!canUseDom() || this.flushTimerId !== null || this.eventQueue.length === 0) {
|
|
@@ -186,26 +268,31 @@ export class PublicAnalyticsSdkService {
|
|
|
186
268
|
if (events.length === 0) {
|
|
187
269
|
return true;
|
|
188
270
|
}
|
|
189
|
-
|
|
190
|
-
if (!publishableKey) {
|
|
271
|
+
if (!getFunnelSdkPublishableKey() || !hasPostHogRuntimeConfig()) {
|
|
191
272
|
return true;
|
|
192
273
|
}
|
|
274
|
+
const batch = events.map((event) => ({
|
|
275
|
+
event: toPostHogEventName(event.eventType),
|
|
276
|
+
properties: buildDefinedRecord(Object.assign(Object.assign({ distinct_id: this.ensureUserId(), $groups: {
|
|
277
|
+
project: PROJECT_ID,
|
|
278
|
+
}, $insert_id: event.eventId, project_id: PROJECT_ID, funnel_id: FUNNEL_ID || undefined, funnel_version_id: FUNNEL_VERSION_ID || undefined, step_id: event.stepId || undefined, step_name: event.stepName || undefined, event_source: 'client' }, buildFeatureFlagProperties(event.featureFlags)), { selected: Object.keys(event.selected).length > 0 ? event.selected : undefined, payload: Object.keys(event.payload).length > 0 ? event.payload : undefined, context: Object.keys(event.context).length > 0 ? event.context : undefined, metadata: Object.keys(event.metadata).length > 0 ? event.metadata : undefined })),
|
|
279
|
+
timestamp: event.occurredAt,
|
|
280
|
+
}));
|
|
193
281
|
const payload = JSON.stringify({
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
externalUserId: this.ensureExternalUserId(),
|
|
197
|
-
events,
|
|
282
|
+
api_key: POSTHOG_PROJECT_API_KEY,
|
|
283
|
+
batch,
|
|
198
284
|
});
|
|
199
|
-
|
|
200
|
-
|
|
285
|
+
const endpoint = `${POSTHOG_API_HOST}${POSTHOG_BATCH_ENDPOINT}`;
|
|
286
|
+
if (canUseDom() && typeof navigator.sendBeacon === 'function') {
|
|
287
|
+
const accepted = navigator.sendBeacon(endpoint, new Blob([payload], { type: 'application/json' }));
|
|
201
288
|
if (accepted) {
|
|
202
289
|
return true;
|
|
203
290
|
}
|
|
204
291
|
}
|
|
205
292
|
try {
|
|
206
|
-
const response = await fetch(
|
|
207
|
-
method:
|
|
208
|
-
headers:
|
|
293
|
+
const response = await fetch(endpoint, {
|
|
294
|
+
method: 'POST',
|
|
295
|
+
headers: { 'Content-Type': 'application/json' },
|
|
209
296
|
body: payload,
|
|
210
297
|
keepalive: true,
|
|
211
298
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const readRuntimeEnv: (reader: () => string | undefined) => string | undefined;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export declare const APP_DEALS_API_BASE_URL: string;
|
|
2
2
|
export declare const FUNNEL_SDK_PUBLISHABLE_KEY: string;
|
|
3
3
|
export declare const FUNNEL_ID: string;
|
|
4
|
+
export declare const FUNNEL_VERSION_ID: string;
|
|
5
|
+
export declare const PROJECT_ID: string;
|
|
6
|
+
export declare const POSTHOG_API_HOST = "https://us.i.posthog.com";
|
|
7
|
+
export declare const POSTHOG_PROJECT_API_KEY: string;
|
|
4
8
|
export declare const hasRealFunnelSdkPublishableKey: (value?: string) => boolean;
|
|
5
9
|
export declare const getFunnelSdkPublishableKey: () => string | null;
|
|
10
|
+
export declare const hasPostHogRuntimeConfig: () => boolean;
|
|
6
11
|
export declare const buildMainApiUrl: (path: string) => string;
|
|
7
12
|
export declare const buildSdkHeaders: (headers?: Record<string, string>) => Record<string, string>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
var _a, _b, _c, _d;
|
|
1
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2
|
+
import { runtimeEnvConfig } from '@funnelsgrove/runtime/config/env.config';
|
|
2
3
|
const DEFAULT_API_BASE_URL = 'https://sdk-api.funnelsgrove.com';
|
|
3
4
|
const DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY = 'pk_test_app_deals_seed_public';
|
|
4
5
|
const trimTrailingSlash = (value) => {
|
|
@@ -18,9 +19,13 @@ const isPreviewFrameRuntime = () => {
|
|
|
18
19
|
return false;
|
|
19
20
|
}
|
|
20
21
|
};
|
|
21
|
-
export const APP_DEALS_API_BASE_URL = trimTrailingSlash((_a =
|
|
22
|
-
export const FUNNEL_SDK_PUBLISHABLE_KEY = ((
|
|
23
|
-
export const FUNNEL_ID = ((
|
|
22
|
+
export const APP_DEALS_API_BASE_URL = trimTrailingSlash((_a = runtimeEnvConfig.appDealsApiBaseUrl) !== null && _a !== void 0 ? _a : DEFAULT_API_BASE_URL);
|
|
23
|
+
export const FUNNEL_SDK_PUBLISHABLE_KEY = ((_b = runtimeEnvConfig.funnelSdkPublishableKey) !== null && _b !== void 0 ? _b : DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY).trim();
|
|
24
|
+
export const FUNNEL_ID = ((_c = runtimeEnvConfig.funnelId) !== null && _c !== void 0 ? _c : '').trim();
|
|
25
|
+
export const FUNNEL_VERSION_ID = ((_d = runtimeEnvConfig.funnelVersionId) !== null && _d !== void 0 ? _d : '').trim();
|
|
26
|
+
export const PROJECT_ID = ((_e = runtimeEnvConfig.projectId) !== null && _e !== void 0 ? _e : '').trim();
|
|
27
|
+
export const POSTHOG_API_HOST = 'https://us.i.posthog.com';
|
|
28
|
+
export const POSTHOG_PROJECT_API_KEY = ((_f = runtimeEnvConfig.posthogProjectApiKey) !== null && _f !== void 0 ? _f : '').trim();
|
|
24
29
|
export const hasRealFunnelSdkPublishableKey = (value = FUNNEL_SDK_PUBLISHABLE_KEY) => {
|
|
25
30
|
const normalized = value.trim();
|
|
26
31
|
return Boolean(normalized) && normalized !== DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY;
|
|
@@ -31,6 +36,9 @@ export const getFunnelSdkPublishableKey = () => {
|
|
|
31
36
|
}
|
|
32
37
|
return hasRealFunnelSdkPublishableKey() ? FUNNEL_SDK_PUBLISHABLE_KEY : null;
|
|
33
38
|
};
|
|
39
|
+
export const hasPostHogRuntimeConfig = () => {
|
|
40
|
+
return Boolean(POSTHOG_API_HOST && POSTHOG_PROJECT_API_KEY && PROJECT_ID);
|
|
41
|
+
};
|
|
34
42
|
export const buildMainApiUrl = (path) => {
|
|
35
43
|
return `${APP_DEALS_API_BASE_URL}${toNormalizedPath(path)}`;
|
|
36
44
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
declare const canUseDom: () => boolean;
|
|
2
2
|
declare const normalizeUserId: (value: string | null | undefined) => string | null;
|
|
3
|
+
declare const buildUserIdStorageKey: (funnelId?: string | null) => string;
|
|
3
4
|
declare const generateUserId: () => string;
|
|
4
|
-
declare const readStoredUserId: () => string | null;
|
|
5
|
-
declare const persistUserId: (value: string) => string;
|
|
6
|
-
declare const getOrCreateUserId: () => string;
|
|
7
|
-
export { canUseDom, generateUserId, getOrCreateUserId, normalizeUserId, persistUserId, readStoredUserId, };
|
|
5
|
+
declare const readStoredUserId: (funnelId?: string | null) => string | null;
|
|
6
|
+
declare const persistUserId: (value: string, funnelId?: string | null) => string;
|
|
7
|
+
declare const getOrCreateUserId: (funnelId?: string | null) => string;
|
|
8
|
+
export { buildUserIdStorageKey, canUseDom, generateUserId, getOrCreateUserId, normalizeUserId, persistUserId, readStoredUserId, };
|
|
@@ -1,39 +1,53 @@
|
|
|
1
|
-
const
|
|
1
|
+
const DEFAULT_USER_ID_STORAGE_KEY = 'funnel:user-id';
|
|
2
2
|
const canUseDom = () => {
|
|
3
|
-
return typeof window !==
|
|
3
|
+
return typeof window !== 'undefined';
|
|
4
4
|
};
|
|
5
5
|
const normalizeUserId = (value) => {
|
|
6
|
-
if (!value || typeof value !==
|
|
6
|
+
if (!value || typeof value !== 'string') {
|
|
7
7
|
return null;
|
|
8
8
|
}
|
|
9
9
|
const trimmed = value.trim();
|
|
10
10
|
return trimmed || null;
|
|
11
11
|
};
|
|
12
|
+
const normalizeFunnelId = (value) => {
|
|
13
|
+
if (!value || typeof value !== 'string') {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const trimmed = value.trim();
|
|
17
|
+
return trimmed || null;
|
|
18
|
+
};
|
|
19
|
+
const buildUserIdStorageKey = (funnelId) => {
|
|
20
|
+
const normalizedFunnelId = normalizeFunnelId(funnelId);
|
|
21
|
+
if (!normalizedFunnelId) {
|
|
22
|
+
return DEFAULT_USER_ID_STORAGE_KEY;
|
|
23
|
+
}
|
|
24
|
+
return `funnel:${normalizedFunnelId}:user-id`;
|
|
25
|
+
};
|
|
12
26
|
const generateUserId = () => {
|
|
13
27
|
var _a;
|
|
14
|
-
if (canUseDom() && typeof ((_a = window.crypto) === null || _a === void 0 ? void 0 : _a.randomUUID) ===
|
|
15
|
-
return `
|
|
28
|
+
if (canUseDom() && typeof ((_a = window.crypto) === null || _a === void 0 ? void 0 : _a.randomUUID) === 'function') {
|
|
29
|
+
return `u_${window.crypto.randomUUID().replace(/-/g, '')}`;
|
|
16
30
|
}
|
|
17
|
-
return `
|
|
31
|
+
return `u_${Date.now()}_${Math.random().toString(16).slice(2, 10)}`;
|
|
18
32
|
};
|
|
19
|
-
const readStoredUserId = () => {
|
|
33
|
+
const readStoredUserId = (funnelId) => {
|
|
20
34
|
if (!canUseDom()) {
|
|
21
35
|
return null;
|
|
22
36
|
}
|
|
23
|
-
return normalizeUserId(window.localStorage.getItem(
|
|
37
|
+
return normalizeUserId(window.localStorage.getItem(buildUserIdStorageKey(funnelId)));
|
|
24
38
|
};
|
|
25
|
-
const persistUserId = (value) => {
|
|
39
|
+
const persistUserId = (value, funnelId) => {
|
|
26
40
|
const normalized = normalizeUserId(value) || generateUserId();
|
|
27
41
|
if (canUseDom()) {
|
|
28
|
-
window.localStorage.setItem(
|
|
42
|
+
window.localStorage.setItem(buildUserIdStorageKey(funnelId), normalized);
|
|
29
43
|
}
|
|
30
44
|
return normalized;
|
|
31
45
|
};
|
|
32
|
-
const getOrCreateUserId = () => {
|
|
33
|
-
const existing = readStoredUserId();
|
|
46
|
+
const getOrCreateUserId = (funnelId) => {
|
|
47
|
+
const existing = readStoredUserId(funnelId);
|
|
34
48
|
if (existing) {
|
|
35
49
|
return existing;
|
|
36
50
|
}
|
|
37
|
-
return persistUserId(generateUserId());
|
|
51
|
+
return persistUserId(generateUserId(), funnelId);
|
|
38
52
|
};
|
|
39
|
-
export { canUseDom, generateUserId, getOrCreateUserId, normalizeUserId, persistUserId, readStoredUserId, };
|
|
53
|
+
export { buildUserIdStorageKey, canUseDom, generateUserId, getOrCreateUserId, normalizeUserId, persistUserId, readStoredUserId, };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funnelsgrove/analytics",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,12 +20,14 @@
|
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
23
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/fix-esm-relative-imports.mjs dist",
|
|
24
24
|
"lint": "eslint .",
|
|
25
25
|
"prepublishOnly": "npm run build",
|
|
26
26
|
"test:run": "vitest run --passWithNoTests"
|
|
27
27
|
},
|
|
28
|
-
"dependencies": {
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@funnelsgrove/runtime": "0.1.1"
|
|
30
|
+
},
|
|
29
31
|
"devDependencies": {
|
|
30
32
|
"@types/node": "^20",
|
|
31
33
|
"typescript": "^5",
|