@funnelsgrove/analytics 0.1.0
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 +37 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/services/bootstrap-public-analytics-user.d.ts +5 -0
- package/dist/services/bootstrap-public-analytics-user.js +4 -0
- package/dist/services/public-analytics-sdk.service.d.ts +56 -0
- package/dist/services/public-analytics-sdk.service.js +219 -0
- package/dist/services/runtime-api.config.d.ts +7 -0
- package/dist/services/runtime-api.config.js +40 -0
- package/dist/services/sdk-user-id.storage.d.ts +7 -0
- package/dist/services/sdk-user-id.storage.js +39 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @funnelsgrove/analytics
|
|
2
|
+
|
|
3
|
+
Shared public analytics SDK helpers for funnels.
|
|
4
|
+
|
|
5
|
+
## Build And Publish
|
|
6
|
+
|
|
7
|
+
- Build distributable output with `npm run build --workspace @funnelsgrove/analytics`.
|
|
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.0`.
|
|
10
|
+
|
|
11
|
+
## Use This Package For
|
|
12
|
+
|
|
13
|
+
- browser user-id persistence
|
|
14
|
+
- public analytics event batching and transport
|
|
15
|
+
- identify/track helpers for the public SDK
|
|
16
|
+
- identify bootstrap helper for funnel identity
|
|
17
|
+
- local-only fallback when only the seed publishable key is present
|
|
18
|
+
|
|
19
|
+
## Responsibilities
|
|
20
|
+
|
|
21
|
+
- keep client identity stable across funnels
|
|
22
|
+
- own the browser storage contract for the external user id
|
|
23
|
+
- send analytics events through the shared API contract
|
|
24
|
+
- avoid noisy network calls when a real publishable key is not configured
|
|
25
|
+
|
|
26
|
+
## What Belongs Here
|
|
27
|
+
|
|
28
|
+
- cookie/localStorage identity helpers
|
|
29
|
+
- public SDK wrappers
|
|
30
|
+
- event normalization and transport
|
|
31
|
+
|
|
32
|
+
## What Does Not Belong Here
|
|
33
|
+
|
|
34
|
+
- step routing
|
|
35
|
+
- manifest logic
|
|
36
|
+
- Stripe checkout UI
|
|
37
|
+
- funnel-specific step components
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { PublicSdkIdentifyInput } from './public-analytics-sdk.service';
|
|
2
|
+
export type BootstrapPublicAnalyticsUserClient = {
|
|
3
|
+
identify(input?: PublicSdkIdentifyInput): Promise<string>;
|
|
4
|
+
};
|
|
5
|
+
export declare const bootstrapPublicAnalyticsUser: (input?: PublicSdkIdentifyInput, client?: BootstrapPublicAnalyticsUserClient) => Promise<string>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type PublicSdkTrackEventInput = {
|
|
2
|
+
eventType: string;
|
|
3
|
+
eventId?: string;
|
|
4
|
+
occurredAt?: string;
|
|
5
|
+
stepId?: string;
|
|
6
|
+
stepName?: string;
|
|
7
|
+
startedAt?: string;
|
|
8
|
+
endedAt?: string;
|
|
9
|
+
selected?: Record<string, unknown>;
|
|
10
|
+
payload?: Record<string, unknown>;
|
|
11
|
+
context?: Record<string, unknown>;
|
|
12
|
+
metadata?: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
export type PublicSdkStepStartInput = {
|
|
15
|
+
stepId: string;
|
|
16
|
+
stepName: string;
|
|
17
|
+
startedAt: string;
|
|
18
|
+
};
|
|
19
|
+
export type PublicSdkStepEndInput = {
|
|
20
|
+
stepId: string;
|
|
21
|
+
stepName: string;
|
|
22
|
+
startedAt: string;
|
|
23
|
+
endedAt: string;
|
|
24
|
+
selected?: Record<string, unknown>;
|
|
25
|
+
};
|
|
26
|
+
export type PublicSdkIdentifyInput = {
|
|
27
|
+
externalUserId?: string;
|
|
28
|
+
email?: string;
|
|
29
|
+
fullName?: string;
|
|
30
|
+
metadata?: Record<string, unknown>;
|
|
31
|
+
};
|
|
32
|
+
export type PublicAnalyticsSdkOptions = {
|
|
33
|
+
batchSize: number;
|
|
34
|
+
flushIntervalMs: number;
|
|
35
|
+
};
|
|
36
|
+
export declare class PublicAnalyticsSdkService {
|
|
37
|
+
private readonly options;
|
|
38
|
+
private readonly endpoint;
|
|
39
|
+
private readonly eventQueue;
|
|
40
|
+
private flushTimerId;
|
|
41
|
+
private inFlightFlush;
|
|
42
|
+
private externalUserId;
|
|
43
|
+
constructor(options?: Partial<PublicAnalyticsSdkOptions>);
|
|
44
|
+
getCurrentExternalUserId(): string;
|
|
45
|
+
identify(input?: PublicSdkIdentifyInput): Promise<string>;
|
|
46
|
+
track(event: PublicSdkTrackEventInput): string | null;
|
|
47
|
+
trackStepStarted(input: PublicSdkStepStartInput): string | null;
|
|
48
|
+
trackStepCompleted(input: PublicSdkStepEndInput): string | null;
|
|
49
|
+
flush(): Promise<number>;
|
|
50
|
+
private ensureExternalUserId;
|
|
51
|
+
private scheduleFlush;
|
|
52
|
+
private clearFlushTimer;
|
|
53
|
+
private flushQueue;
|
|
54
|
+
private sendBatch;
|
|
55
|
+
}
|
|
56
|
+
export declare const publicAnalyticsSdk: PublicAnalyticsSdkService;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { buildMainApiUrl, buildSdkHeaders, FUNNEL_ID, getFunnelSdkPublishableKey, } from './runtime-api.config';
|
|
2
|
+
import { canUseDom, getOrCreateUserId, persistUserId, } from './sdk-user-id.storage';
|
|
3
|
+
const DEFAULT_OPTIONS = {
|
|
4
|
+
batchSize: 20,
|
|
5
|
+
flushIntervalMs: 1500,
|
|
6
|
+
};
|
|
7
|
+
const asRecord = (value) => {
|
|
8
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
9
|
+
return {};
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
};
|
|
13
|
+
const asString = (value) => {
|
|
14
|
+
if (typeof value !== "string") {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const trimmed = value.trim();
|
|
18
|
+
return trimmed || null;
|
|
19
|
+
};
|
|
20
|
+
const toIsoOrNow = (value) => {
|
|
21
|
+
if (value) {
|
|
22
|
+
const parsed = new Date(value);
|
|
23
|
+
if (!Number.isNaN(parsed.getTime())) {
|
|
24
|
+
return parsed.toISOString();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return new Date().toISOString();
|
|
28
|
+
};
|
|
29
|
+
const buildEventId = () => {
|
|
30
|
+
var _a;
|
|
31
|
+
if (canUseDom() && typeof ((_a = window.crypto) === null || _a === void 0 ? void 0 : _a.randomUUID) === "function") {
|
|
32
|
+
return `sdk_evt_${window.crypto.randomUUID().replace(/-/g, "")}`;
|
|
33
|
+
}
|
|
34
|
+
return `sdk_evt_${Date.now()}_${Math.random().toString(16).slice(2, 10)}`;
|
|
35
|
+
};
|
|
36
|
+
export class PublicAnalyticsSdkService {
|
|
37
|
+
constructor(options = {}) {
|
|
38
|
+
this.options = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
|
|
39
|
+
this.endpoint = buildMainApiUrl("/sdk/public/events");
|
|
40
|
+
this.eventQueue = [];
|
|
41
|
+
this.flushTimerId = null;
|
|
42
|
+
this.inFlightFlush = null;
|
|
43
|
+
this.externalUserId = null;
|
|
44
|
+
}
|
|
45
|
+
getCurrentExternalUserId() {
|
|
46
|
+
return this.ensureExternalUserId();
|
|
47
|
+
}
|
|
48
|
+
async identify(input = {}) {
|
|
49
|
+
var _a;
|
|
50
|
+
const externalUserId = persistUserId(input.externalUserId || this.ensureExternalUserId());
|
|
51
|
+
const publishableKey = getFunnelSdkPublishableKey();
|
|
52
|
+
if (!publishableKey) {
|
|
53
|
+
this.externalUserId = externalUserId;
|
|
54
|
+
return externalUserId;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const response = await fetch(buildMainApiUrl("/sdk/public/users/bootstrap"), {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: buildSdkHeaders({
|
|
60
|
+
"Content-Type": "application/json",
|
|
61
|
+
}),
|
|
62
|
+
body: JSON.stringify({
|
|
63
|
+
publishableKey,
|
|
64
|
+
funnelId: FUNNEL_ID || undefined,
|
|
65
|
+
externalUserId,
|
|
66
|
+
email: asString(input.email) || undefined,
|
|
67
|
+
fullName: asString(input.fullName) || undefined,
|
|
68
|
+
metadata: asRecord(input.metadata),
|
|
69
|
+
}),
|
|
70
|
+
});
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
this.externalUserId = externalUserId;
|
|
73
|
+
return externalUserId;
|
|
74
|
+
}
|
|
75
|
+
const payload = (await response.json());
|
|
76
|
+
const persistedUserId = persistUserId(asString((_a = payload.user) === null || _a === void 0 ? void 0 : _a.externalUserId) ||
|
|
77
|
+
asString(payload.externalUserId) ||
|
|
78
|
+
externalUserId) || externalUserId;
|
|
79
|
+
this.externalUserId = persistedUserId;
|
|
80
|
+
return persistedUserId;
|
|
81
|
+
}
|
|
82
|
+
catch (_b) {
|
|
83
|
+
this.externalUserId = externalUserId;
|
|
84
|
+
return externalUserId;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
track(event) {
|
|
88
|
+
const eventType = asString(event.eventType);
|
|
89
|
+
if (!eventType) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (!getFunnelSdkPublishableKey()) {
|
|
93
|
+
return asString(event.eventId) || buildEventId();
|
|
94
|
+
}
|
|
95
|
+
const envelope = {
|
|
96
|
+
eventType,
|
|
97
|
+
eventId: asString(event.eventId) || buildEventId(),
|
|
98
|
+
occurredAt: toIsoOrNow(event.occurredAt),
|
|
99
|
+
stepId: asString(event.stepId),
|
|
100
|
+
stepName: asString(event.stepName),
|
|
101
|
+
startedAt: asString(event.startedAt),
|
|
102
|
+
endedAt: asString(event.endedAt),
|
|
103
|
+
selected: asRecord(event.selected),
|
|
104
|
+
payload: asRecord(event.payload),
|
|
105
|
+
context: asRecord(event.context),
|
|
106
|
+
metadata: asRecord(event.metadata),
|
|
107
|
+
};
|
|
108
|
+
this.eventQueue.push(envelope);
|
|
109
|
+
if (this.eventQueue.length >= this.options.batchSize) {
|
|
110
|
+
void this.flush();
|
|
111
|
+
return envelope.eventId;
|
|
112
|
+
}
|
|
113
|
+
this.scheduleFlush();
|
|
114
|
+
return envelope.eventId;
|
|
115
|
+
}
|
|
116
|
+
trackStepStarted(input) {
|
|
117
|
+
return this.track({
|
|
118
|
+
eventType: "step_start",
|
|
119
|
+
stepId: input.stepId,
|
|
120
|
+
stepName: input.stepName,
|
|
121
|
+
startedAt: input.startedAt,
|
|
122
|
+
occurredAt: input.startedAt,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
trackStepCompleted(input) {
|
|
126
|
+
return this.track({
|
|
127
|
+
eventType: "step_end",
|
|
128
|
+
stepId: input.stepId,
|
|
129
|
+
stepName: input.stepName,
|
|
130
|
+
startedAt: input.startedAt,
|
|
131
|
+
endedAt: input.endedAt,
|
|
132
|
+
occurredAt: input.endedAt,
|
|
133
|
+
selected: asRecord(input.selected),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
async flush() {
|
|
137
|
+
if (this.inFlightFlush) {
|
|
138
|
+
return this.inFlightFlush;
|
|
139
|
+
}
|
|
140
|
+
this.clearFlushTimer();
|
|
141
|
+
this.inFlightFlush = this.flushQueue().finally(() => {
|
|
142
|
+
this.inFlightFlush = null;
|
|
143
|
+
});
|
|
144
|
+
return this.inFlightFlush;
|
|
145
|
+
}
|
|
146
|
+
ensureExternalUserId() {
|
|
147
|
+
if (this.externalUserId) {
|
|
148
|
+
return this.externalUserId;
|
|
149
|
+
}
|
|
150
|
+
this.externalUserId = persistUserId(getOrCreateUserId());
|
|
151
|
+
return this.externalUserId;
|
|
152
|
+
}
|
|
153
|
+
scheduleFlush() {
|
|
154
|
+
if (!canUseDom() || this.flushTimerId !== null || this.eventQueue.length === 0) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
this.flushTimerId = window.setTimeout(() => {
|
|
158
|
+
this.flushTimerId = null;
|
|
159
|
+
void this.flush();
|
|
160
|
+
}, this.options.flushIntervalMs);
|
|
161
|
+
}
|
|
162
|
+
clearFlushTimer() {
|
|
163
|
+
if (!canUseDom() || this.flushTimerId === null) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
window.clearTimeout(this.flushTimerId);
|
|
167
|
+
this.flushTimerId = null;
|
|
168
|
+
}
|
|
169
|
+
async flushQueue() {
|
|
170
|
+
let sentEvents = 0;
|
|
171
|
+
while (this.eventQueue.length > 0) {
|
|
172
|
+
const batch = this.eventQueue.splice(0, this.options.batchSize);
|
|
173
|
+
const delivered = await this.sendBatch(batch);
|
|
174
|
+
if (!delivered) {
|
|
175
|
+
this.eventQueue.unshift(...batch);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
sentEvents += batch.length;
|
|
179
|
+
}
|
|
180
|
+
if (this.eventQueue.length > 0) {
|
|
181
|
+
this.scheduleFlush();
|
|
182
|
+
}
|
|
183
|
+
return sentEvents;
|
|
184
|
+
}
|
|
185
|
+
async sendBatch(events) {
|
|
186
|
+
if (events.length === 0) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
const publishableKey = getFunnelSdkPublishableKey();
|
|
190
|
+
if (!publishableKey) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
const payload = JSON.stringify({
|
|
194
|
+
publishableKey,
|
|
195
|
+
funnelId: FUNNEL_ID || undefined,
|
|
196
|
+
externalUserId: this.ensureExternalUserId(),
|
|
197
|
+
events,
|
|
198
|
+
});
|
|
199
|
+
if (canUseDom() && typeof navigator.sendBeacon === "function") {
|
|
200
|
+
const accepted = navigator.sendBeacon(this.endpoint, new Blob([payload], { type: "application/json" }));
|
|
201
|
+
if (accepted) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
const response = await fetch(this.endpoint, {
|
|
207
|
+
method: "POST",
|
|
208
|
+
headers: buildSdkHeaders({ "Content-Type": "application/json" }),
|
|
209
|
+
body: payload,
|
|
210
|
+
keepalive: true,
|
|
211
|
+
});
|
|
212
|
+
return response.ok;
|
|
213
|
+
}
|
|
214
|
+
catch (_a) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
export const publicAnalyticsSdk = new PublicAnalyticsSdkService();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const APP_DEALS_API_BASE_URL: string;
|
|
2
|
+
export declare const FUNNEL_SDK_PUBLISHABLE_KEY: string;
|
|
3
|
+
export declare const FUNNEL_ID: string;
|
|
4
|
+
export declare const hasRealFunnelSdkPublishableKey: (value?: string) => boolean;
|
|
5
|
+
export declare const getFunnelSdkPublishableKey: () => string | null;
|
|
6
|
+
export declare const buildMainApiUrl: (path: string) => string;
|
|
7
|
+
export declare const buildSdkHeaders: (headers?: Record<string, string>) => Record<string, string>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
var _a, _b, _c, _d;
|
|
2
|
+
const DEFAULT_API_BASE_URL = 'https://sdk-api.funnelsgrove.com';
|
|
3
|
+
const DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY = 'pk_test_app_deals_seed_public';
|
|
4
|
+
const trimTrailingSlash = (value) => {
|
|
5
|
+
return value.replace(/\/+$/, '');
|
|
6
|
+
};
|
|
7
|
+
const toNormalizedPath = (path) => {
|
|
8
|
+
return path.startsWith('/') ? path : `/${path}`;
|
|
9
|
+
};
|
|
10
|
+
const isPreviewFrameRuntime = () => {
|
|
11
|
+
if (typeof window === 'undefined') {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return new URLSearchParams(window.location.search).get('previewFrame') === '1';
|
|
16
|
+
}
|
|
17
|
+
catch (_a) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
export const APP_DEALS_API_BASE_URL = trimTrailingSlash((_a = process.env.NEXT_PUBLIC_APP_DEALS_API_BASE_URL) !== null && _a !== void 0 ? _a : DEFAULT_API_BASE_URL);
|
|
22
|
+
export const FUNNEL_SDK_PUBLISHABLE_KEY = ((_c = (_b = process.env.NEXT_PUBLIC_FUNNEL_SDK_PUBLISHABLE_KEY) !== null && _b !== void 0 ? _b : process.env.NEXT_PUBLIC_FUNNEL_PUBLISHABLE_KEY) !== null && _c !== void 0 ? _c : DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY).trim();
|
|
23
|
+
export const FUNNEL_ID = ((_d = process.env.NEXT_PUBLIC_FUNNEL_ID) !== null && _d !== void 0 ? _d : '').trim();
|
|
24
|
+
export const hasRealFunnelSdkPublishableKey = (value = FUNNEL_SDK_PUBLISHABLE_KEY) => {
|
|
25
|
+
const normalized = value.trim();
|
|
26
|
+
return Boolean(normalized) && normalized !== DEFAULT_FUNNEL_SDK_PUBLISHABLE_KEY;
|
|
27
|
+
};
|
|
28
|
+
export const getFunnelSdkPublishableKey = () => {
|
|
29
|
+
if (isPreviewFrameRuntime()) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return hasRealFunnelSdkPublishableKey() ? FUNNEL_SDK_PUBLISHABLE_KEY : null;
|
|
33
|
+
};
|
|
34
|
+
export const buildMainApiUrl = (path) => {
|
|
35
|
+
return `${APP_DEALS_API_BASE_URL}${toNormalizedPath(path)}`;
|
|
36
|
+
};
|
|
37
|
+
export const buildSdkHeaders = (headers) => {
|
|
38
|
+
const publishableKey = getFunnelSdkPublishableKey();
|
|
39
|
+
return Object.assign(Object.assign({}, (publishableKey ? { 'x-sdk-publishable-key': publishableKey } : {})), (headers !== null && headers !== void 0 ? headers : {}));
|
|
40
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare const canUseDom: () => boolean;
|
|
2
|
+
declare const normalizeUserId: (value: string | null | undefined) => string | null;
|
|
3
|
+
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, };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const USER_ID_STORAGE_KEY = "funnel:external-user-id";
|
|
2
|
+
const canUseDom = () => {
|
|
3
|
+
return typeof window !== "undefined";
|
|
4
|
+
};
|
|
5
|
+
const normalizeUserId = (value) => {
|
|
6
|
+
if (!value || typeof value !== "string") {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const trimmed = value.trim();
|
|
10
|
+
return trimmed || null;
|
|
11
|
+
};
|
|
12
|
+
const generateUserId = () => {
|
|
13
|
+
var _a;
|
|
14
|
+
if (canUseDom() && typeof ((_a = window.crypto) === null || _a === void 0 ? void 0 : _a.randomUUID) === "function") {
|
|
15
|
+
return `sdk_user_${window.crypto.randomUUID().replace(/-/g, "")}`;
|
|
16
|
+
}
|
|
17
|
+
return `sdk_user_${Date.now()}_${Math.random().toString(16).slice(2, 10)}`;
|
|
18
|
+
};
|
|
19
|
+
const readStoredUserId = () => {
|
|
20
|
+
if (!canUseDom()) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return normalizeUserId(window.localStorage.getItem(USER_ID_STORAGE_KEY));
|
|
24
|
+
};
|
|
25
|
+
const persistUserId = (value) => {
|
|
26
|
+
const normalized = normalizeUserId(value) || generateUserId();
|
|
27
|
+
if (canUseDom()) {
|
|
28
|
+
window.localStorage.setItem(USER_ID_STORAGE_KEY, normalized);
|
|
29
|
+
}
|
|
30
|
+
return normalized;
|
|
31
|
+
};
|
|
32
|
+
const getOrCreateUserId = () => {
|
|
33
|
+
const existing = readStoredUserId();
|
|
34
|
+
if (existing) {
|
|
35
|
+
return existing;
|
|
36
|
+
}
|
|
37
|
+
return persistUserId(generateUserId());
|
|
38
|
+
};
|
|
39
|
+
export { canUseDom, generateUserId, getOrCreateUserId, normalizeUserId, persistUserId, readStoredUserId, };
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@funnelsgrove/analytics",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": false,
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
24
|
+
"lint": "eslint .",
|
|
25
|
+
"prepublishOnly": "npm run build",
|
|
26
|
+
"test:run": "vitest run --passWithNoTests"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20",
|
|
31
|
+
"typescript": "^5",
|
|
32
|
+
"vitest": "^3.2.4"
|
|
33
|
+
}
|
|
34
|
+
}
|