@founderroute/analytics 0.1.0-beta.1 → 1.0.0-rc.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/collection.js +60 -0
- package/index.d.ts +6 -2
- package/index.js +88 -32
- package/next.d.ts +2 -0
- package/package.json +45 -9
- package/react.d.ts +3 -0
- package/react.js +4 -1
package/collection.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Collection permission never renders UI or implies a visitor consented.
|
|
2
|
+
export class CollectionState {
|
|
3
|
+
constructor({ mode, preferenceKey, storage, changed, error }) {
|
|
4
|
+
if (mode != null && !['automatic', 'consent'].includes(mode)) throw new Error('Invalid collection mode');
|
|
5
|
+
this.mode = mode ?? null;
|
|
6
|
+
this.permission = 'not_provided';
|
|
7
|
+
this.refused = false;
|
|
8
|
+
this.destroyed = false;
|
|
9
|
+
this.preferenceKey = preferenceKey;
|
|
10
|
+
this.storage = storage;
|
|
11
|
+
this.changed = changed;
|
|
12
|
+
this.error = error;
|
|
13
|
+
this.refresh();
|
|
14
|
+
}
|
|
15
|
+
get enabled() {
|
|
16
|
+
return !this.destroyed && !this.refused && (this.mode === 'automatic' || (this.mode === 'consent' && this.permission === 'granted'));
|
|
17
|
+
}
|
|
18
|
+
refresh() {
|
|
19
|
+
try { this.refused = this.storage()?.getItem(this.preferenceKey) === 'refused'; }
|
|
20
|
+
catch { this.error('preference_storage_unavailable'); }
|
|
21
|
+
}
|
|
22
|
+
saveRefusal(refused) {
|
|
23
|
+
this.refused = refused;
|
|
24
|
+
try {
|
|
25
|
+
const storage = this.storage();
|
|
26
|
+
if (!storage) throw new Error('Storage unavailable');
|
|
27
|
+
if (refused) storage.setItem(this.preferenceKey, 'refused');
|
|
28
|
+
else storage.removeItem(this.preferenceKey);
|
|
29
|
+
} catch { this.error('preference_storage_unavailable'); }
|
|
30
|
+
}
|
|
31
|
+
setMode(mode) {
|
|
32
|
+
if (!['automatic', 'consent'].includes(mode)) throw new Error('Invalid collection mode');
|
|
33
|
+
this.mode = mode;
|
|
34
|
+
this.changed();
|
|
35
|
+
}
|
|
36
|
+
setConsent(granted) {
|
|
37
|
+
this.permission = granted ? 'granted' : 'denied';
|
|
38
|
+
this.saveRefusal(!granted);
|
|
39
|
+
this.changed();
|
|
40
|
+
}
|
|
41
|
+
optOut() {
|
|
42
|
+
this.permission = 'denied';
|
|
43
|
+
this.saveRefusal(true);
|
|
44
|
+
this.changed();
|
|
45
|
+
}
|
|
46
|
+
optIn() {
|
|
47
|
+
this.saveRefusal(false);
|
|
48
|
+
if (this.permission === 'denied') this.permission = 'not_provided';
|
|
49
|
+
this.changed();
|
|
50
|
+
}
|
|
51
|
+
receiveRefusal() {
|
|
52
|
+
// Other tabs may stop this instance, but never grant permission on its behalf.
|
|
53
|
+
this.refused = true;
|
|
54
|
+
this.permission = 'denied';
|
|
55
|
+
this.changed();
|
|
56
|
+
}
|
|
57
|
+
metadata() {
|
|
58
|
+
return { collection_mode: this.mode, consent_state: this.permission === 'granted' ? 'granted' : 'not_provided' };
|
|
59
|
+
}
|
|
60
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export type Scalar = string | number | boolean | null;
|
|
2
|
-
export type AnalyticsOptions = { key: string; endpoint: string; verificationId?: string; autoPage?: boolean; allowedProperties?: string[]; allowedTraits?: string[]; storage?: Pick<Storage,"getItem"|"setItem"|"removeItem">; fetch?: typeof fetch };
|
|
2
|
+
export type AnalyticsOptions = { key: string; endpoint: string; propertyId?: string; environment?: "production" | "test"; collectionMode?: "automatic" | "consent"; verificationId?: string; autoPage?: boolean; allowedProperties?: string[]; allowedTraits?: string[]; storage?: Pick<Storage,"getItem"|"setItem"|"removeItem">; fetch?: typeof fetch };
|
|
3
3
|
export class FounderRouteAnalytics {
|
|
4
4
|
constructor(options: AnalyticsOptions);
|
|
5
|
+
readonly ready: Promise<void>;
|
|
5
6
|
setConsent(granted: boolean): void;
|
|
7
|
+
optOut(): void;
|
|
8
|
+
optIn(): void;
|
|
9
|
+
setCollectionMode(mode: "automatic" | "consent"): void;
|
|
6
10
|
identify(userId: string, options?: {token?: string;traits?: Record<string,Scalar>}): void;
|
|
7
11
|
setAccount(accountId: string | null): void;
|
|
8
12
|
track(name: string, properties?: Record<string,Scalar>, options?: {outcomeId?: string}): string | null;
|
|
@@ -12,7 +16,7 @@ export class FounderRouteAnalytics {
|
|
|
12
16
|
flush(): Promise<void>;
|
|
13
17
|
decorateLink(url:string,destinationPropertyId:string):Promise<string>;
|
|
14
18
|
consumeHandoff(token:string):Promise<void>;
|
|
15
|
-
getDiagnostics(): {consent:boolean;queued:number;dropped:number;rejected:number;acknowledged:number;lastError:string|null;anonymousId:string|null};
|
|
19
|
+
getDiagnostics(): {collectionMode:"automatic"|"consent"|null;consentState:"not_provided"|"granted"|"denied";optedOut:boolean;collectionEnabled:boolean;consent:boolean;queued:number;dropped:number;rejected:number;acknowledged:number;lastError:string|null;anonymousId:string|null};
|
|
16
20
|
destroy(): void;
|
|
17
21
|
}
|
|
18
22
|
export function init(options: AnalyticsOptions): FounderRouteAnalytics;
|
package/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { CollectionState } from "./collection.js";
|
|
2
|
+
const VERSION = "1.0.0-rc.1";
|
|
2
3
|
const MAX_BYTES = 1024 * 1024;
|
|
3
4
|
const MAX_EVENTS = 1000;
|
|
4
5
|
const TTL = 86400000;
|
|
@@ -14,34 +15,81 @@ export class FounderRouteAnalytics {
|
|
|
14
15
|
constructor(options) {
|
|
15
16
|
if (!options?.key || !options?.endpoint) throw new Error("FounderRoute requires a public key and collector origin.");
|
|
16
17
|
this.options = options; this.endpoint = options.endpoint.replace(/\/$/, "");
|
|
17
|
-
this.storageKey = `founderroute:${options.key}`; this.
|
|
18
|
+
this.configurationReady = false; this.storageKey = `founderroute:${options.key}`; this.collecting = false; this.queue = [];
|
|
18
19
|
this.identity = null; this.session = null; this.user = null; this.account = null; this.token = null; this.traits = {};
|
|
19
20
|
this.dropped = 0; this.rejected = 0; this.sent = 0; this.lastError = null; this.inFlight = null; this.retryAt = 0; this.failures = 0;
|
|
20
21
|
this.generation = 0; this.cleanups = []; this.timer = null; this.lastInteraction = 0; this.lastTick = 0; this.lastPage = null;
|
|
22
|
+
this.collection = new CollectionState({
|
|
23
|
+
mode: null, preferenceKey: `founderroute:${options.key}:preference`,
|
|
24
|
+
storage: () => options.storage ?? globalThis.localStorage,
|
|
25
|
+
error: reason => { this.lastError = reason; }, changed: () => this.reconcileCollection(),
|
|
26
|
+
});
|
|
27
|
+
this.preferenceListener = event => {
|
|
28
|
+
if (event.key === this.collection.preferenceKey && event.newValue === 'refused') this.collection.receiveRefusal();
|
|
29
|
+
};
|
|
30
|
+
globalThis.addEventListener?.('storage', this.preferenceListener);
|
|
31
|
+
if (options.propertyId && ['production','test'].includes(options.environment) && options.collectionMode) {
|
|
32
|
+
this.configureCollection(options.propertyId, options.environment, options.collectionMode);
|
|
33
|
+
this.ready = Promise.resolve();
|
|
34
|
+
} else {
|
|
35
|
+
this.ready = this.resolveConfiguration();
|
|
36
|
+
}
|
|
21
37
|
}
|
|
22
|
-
setConsent(granted) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
setConsent(granted) { this.collection.setConsent(Boolean(granted)); }
|
|
39
|
+
optOut() { this.collection.optOut(); }
|
|
40
|
+
optIn() { this.collection.optIn(); }
|
|
41
|
+
setCollectionMode(mode) { this.collection.setMode(mode); }
|
|
42
|
+
async resolveConfiguration() {
|
|
43
|
+
try {
|
|
44
|
+
const response = await (this.options.fetch ?? globalThis.fetch)(`${this.endpoint}/api/analytics/v2/config?key=${encodeURIComponent(this.options.key)}`);
|
|
45
|
+
if (!response.ok) throw new Error('Configuration unavailable');
|
|
46
|
+
const config = await response.json();
|
|
47
|
+
if (!config.property_id || !['production','test'].includes(config.environment) || !['automatic','consent'].includes(config.collection_mode)) throw new Error('Invalid configuration');
|
|
48
|
+
if (this.collection.destroyed) return;
|
|
49
|
+
this.configureCollection(config.property_id, config.environment, this.options.collectionMode ?? config.collection_mode);
|
|
50
|
+
} catch { this.lastError = 'configuration_unavailable'; }
|
|
51
|
+
}
|
|
52
|
+
configureCollection(propertyId, environment, mode) {
|
|
53
|
+
this.propertyId = propertyId; this.environment = environment;
|
|
54
|
+
this.storageKey = `founderroute:${propertyId}:${environment}:events`;
|
|
55
|
+
this.collection.preferenceKey = `founderroute:${propertyId}:${environment}:preference`;
|
|
56
|
+
const refusedHere = this.collection.refused;
|
|
57
|
+
this.collection.refresh();
|
|
58
|
+
if (refusedHere) this.collection.saveRefusal(true);
|
|
59
|
+
this.configurationReady = true;
|
|
60
|
+
this.collection.setMode(mode);
|
|
61
|
+
}
|
|
62
|
+
reconcileCollection() {
|
|
63
|
+
const granted = this.configurationReady && this.collection.enabled;
|
|
64
|
+
if (!granted && this.collection.refused) {
|
|
65
|
+
try { const storage = this.options.storage ?? globalThis.localStorage; storage?.removeItem(this.storageKey); storage?.removeItem(`founderroute:${this.options.key}`); }
|
|
66
|
+
catch { /* preference persistence reports storage failure separately */ }
|
|
67
|
+
}
|
|
68
|
+
if (granted === this.collecting) return;
|
|
69
|
+
this.collecting = granted; this.generation++;
|
|
70
|
+
if (!this.collecting) {
|
|
71
|
+
this.controller?.abort(); this.inFlight = null; clearInterval(this.timer); this.timer = null;
|
|
27
72
|
for (const remove of this.cleanups.splice(0)) remove();
|
|
28
73
|
this.queue = []; this.identity = null; this.session = null; this.user = null; this.token = null; this.account = null; this.traits = {}; this.lastPage = null;
|
|
29
|
-
try { this.options.storage?.removeItem(this.storageKey);
|
|
74
|
+
try { const storage = this.options.storage ?? globalThis.localStorage; storage?.removeItem(this.storageKey); storage?.removeItem(`founderroute:${this.options.key}`); } catch { /* unavailable storage is not fatal */ }
|
|
30
75
|
return;
|
|
31
76
|
}
|
|
32
77
|
try {
|
|
33
|
-
const
|
|
78
|
+
const storage = this.options.storage ?? globalThis.localStorage;
|
|
79
|
+
const stored = storage?.getItem(this.storageKey) ?? storage?.getItem(`founderroute:${this.options.key}`);
|
|
34
80
|
if (stored) { const saved = JSON.parse(stored); this.queue = Array.isArray(saved.queue) ? saved.queue : []; this.identity = saved.identity; }
|
|
35
81
|
} catch { this.queue = []; }
|
|
36
82
|
this.identity ||= uuid(); this.lastInteraction = Date.now(); this.lastTick = Date.now();
|
|
37
|
-
this.prune(); this.persist();
|
|
83
|
+
this.prune(); this.persist();
|
|
84
|
+
try { const storage = this.options.storage ?? globalThis.localStorage; if (storage?.getItem(this.storageKey)) storage.removeItem(`founderroute:${this.options.key}`); } catch { /* keep legacy delivery if migration did not persist */ }
|
|
85
|
+
this.attach();
|
|
38
86
|
this.timer = setInterval(() => { this.engagement(); void this.flush(); }, 15000);
|
|
39
87
|
this.timer?.unref?.();
|
|
40
88
|
if (this.options.autoPage !== false && globalThis.location) this.page();
|
|
41
89
|
void this.flush();
|
|
42
90
|
}
|
|
43
91
|
persist() {
|
|
44
|
-
if (!this.
|
|
92
|
+
if (!this.collecting) return;
|
|
45
93
|
try { (this.options.storage ?? globalThis.localStorage)?.setItem(this.storageKey, JSON.stringify({ identity:this.identity,queue:this.queue })); }
|
|
46
94
|
catch { this.lastError = "storage_unavailable"; }
|
|
47
95
|
}
|
|
@@ -52,14 +100,14 @@ export class FounderRouteAnalytics {
|
|
|
52
100
|
while (this.queue.length>MAX_EVENTS || encoder.encode(JSON.stringify(this.queue)).length>MAX_BYTES) { this.queue.shift(); this.dropped++; }
|
|
53
101
|
}
|
|
54
102
|
event(name, properties = {}, extra = {}) {
|
|
55
|
-
if (!this.
|
|
103
|
+
if (!this.collecting) return null;
|
|
56
104
|
const now = Date.now();
|
|
57
105
|
if (!this.session || now-this.session.last >= 1800000) this.session = { id:uuid(),last:now };
|
|
58
106
|
this.session.last = now;
|
|
59
107
|
const event = {
|
|
60
|
-
event_id:uuid(),protocol:
|
|
108
|
+
event_id:uuid(),protocol:2,name,kind:extra.kind ?? "custom",occurred_at:new Date(now).toISOString(),anonymous_id:this.identity,
|
|
61
109
|
...(this.user ? {user_id:this.user} : {}),...(this.token ? {identity_token:this.token} : {}),...(this.account ? {account_id:this.account} : {}),
|
|
62
|
-
session_id:this.session.id,
|
|
110
|
+
session_id:this.session.id,...this.collection.metadata(),properties:clean(properties,this.options.allowedProperties),traits:clean(this.traits,this.options.allowedTraits),
|
|
63
111
|
context:{sdk:"browser",sdk_version:VERSION,...(this.options.verificationId?{verification_id:this.options.verificationId}:{}),...this.campaignContext(),...extra.context},...(extra.outcomeId ? {outcome_id:extra.outcomeId} : {}),
|
|
64
112
|
};
|
|
65
113
|
if (encoder.encode(JSON.stringify(event)).length>8192) { this.rejected++; this.lastError="event_too_large"; return null; }
|
|
@@ -67,7 +115,7 @@ export class FounderRouteAnalytics {
|
|
|
67
115
|
}
|
|
68
116
|
track(name, properties = {}, options = {}) { return this.event(name,properties,{outcomeId:options.outcomeId}); }
|
|
69
117
|
page(path) {
|
|
70
|
-
if (!this.
|
|
118
|
+
if (!this.collecting) return null;
|
|
71
119
|
const route = normalizePath(path ?? globalThis.location?.pathname ?? "/");
|
|
72
120
|
if (route === this.lastPage) return null;
|
|
73
121
|
this.engagement(); this.lastPage=route;
|
|
@@ -75,14 +123,14 @@ export class FounderRouteAnalytics {
|
|
|
75
123
|
}
|
|
76
124
|
screen(name) { return this.event("screen_view",{},{kind:"screen",context:{screen:String(name).slice(0,150)}}); }
|
|
77
125
|
identify(userId, { token, traits = {} } = {}) {
|
|
78
|
-
if (!this.
|
|
126
|
+
if (!this.collecting || !userId) return;
|
|
79
127
|
if (this.user && this.user !== String(userId)) this.reset();
|
|
80
128
|
this.user=String(userId); this.token=token; this.traits={...traits};
|
|
81
129
|
this.event("fr_identify",{},{kind:"identify"});
|
|
82
130
|
}
|
|
83
|
-
setAccount(accountId) { if (this.
|
|
131
|
+
setAccount(accountId) { if (this.collecting) this.account=accountId ? String(accountId) : null; }
|
|
84
132
|
reset() {
|
|
85
|
-
if (!this.
|
|
133
|
+
if (!this.collecting) return;
|
|
86
134
|
this.engagement(); this.user=null; this.token=null; this.account=null; this.traits={}; this.identity=uuid(); this.session=null; this.lastPage=null; this.persist();
|
|
87
135
|
}
|
|
88
136
|
campaignContext() {
|
|
@@ -95,7 +143,7 @@ export class FounderRouteAnalytics {
|
|
|
95
143
|
return result;
|
|
96
144
|
}
|
|
97
145
|
engagement() {
|
|
98
|
-
if (!this.
|
|
146
|
+
if (!this.collecting) return;
|
|
99
147
|
const now=Date.now(); const elapsed=Math.min(15000,Math.max(0,now-this.lastTick)); this.lastTick=now;
|
|
100
148
|
if (globalThis.document?.visibilityState === "hidden" || now-this.lastInteraction>60000 || !elapsed) return;
|
|
101
149
|
this.event("fr_session",{},{kind:"session",context:{active_ms:elapsed,path:this.lastPage??"/"}});
|
|
@@ -124,44 +172,52 @@ export class FounderRouteAnalytics {
|
|
|
124
172
|
return events;
|
|
125
173
|
}
|
|
126
174
|
beacon() {
|
|
127
|
-
if(!this.
|
|
175
|
+
if(!this.collecting||!globalThis.navigator?.sendBeacon) return;
|
|
128
176
|
const events=this.batch(); if(!events.length)return;
|
|
129
|
-
try { navigator.sendBeacon(`${this.endpoint}/api/analytics/
|
|
177
|
+
try { navigator.sendBeacon(`${this.endpoint}/api/analytics/v2/collect`,new Blob([JSON.stringify({key:this.options.key,events})],{type:"text/plain"})); }
|
|
130
178
|
catch { /* retained until acknowledged by fetch */ }
|
|
131
179
|
}
|
|
132
180
|
async flush() {
|
|
133
|
-
if(!this.
|
|
181
|
+
if(!this.collecting||Date.now()<this.retryAt) return;
|
|
134
182
|
if(this.inFlight)return this.inFlight;
|
|
135
183
|
const generation=this.generation; const events=this.batch(); if(!events.length)return;
|
|
136
184
|
this.controller=new AbortController();
|
|
185
|
+
const delivery = {}; this.delivery = delivery;
|
|
137
186
|
this.inFlight=(async()=>{
|
|
138
187
|
try {
|
|
139
|
-
const response=await (this.options.fetch??globalThis.fetch)(`${this.endpoint}/api/analytics/
|
|
140
|
-
if(!this.
|
|
188
|
+
const response=await (this.options.fetch??globalThis.fetch)(`${this.endpoint}/api/analytics/v2/collect`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:this.options.key,events}),signal:this.controller.signal});
|
|
189
|
+
if(!this.collecting||this.generation!==generation)return;
|
|
141
190
|
if(response.status===429||response.status>=500){this.failures++;this.retryAt=Date.now()+Math.max(Number(response.headers.get("Retry-After")??0)*1000,Math.min(300000,1000*2**Math.min(this.failures,8)));this.lastError=`delivery_${response.status}`;return;}
|
|
142
191
|
const result=await response.json();
|
|
192
|
+
if(!this.collecting||this.generation!==generation)return;
|
|
143
193
|
if(!response.ok){this.lastError=result.error??"delivery_rejected";this.retryAt=Date.now()+300000;if([400,401,403,413].includes(response.status)){const ids=new Set(events.map(e=>e.event_id));this.queue=this.queue.filter(e=>!ids.has(e.event_id));this.rejected+=ids.size;}return;}
|
|
144
194
|
const acknowledged=new Set();
|
|
145
195
|
for(const receipt of result.results??[]){if(["accepted","duplicate","rejected"].includes(receipt.status))acknowledged.add(receipt.event_id);if(receipt.status==="rejected"){this.rejected++;this.lastError=receipt.reason;}else this.sent++;}
|
|
146
196
|
this.queue=this.queue.filter(e=>!acknowledged.has(e.event_id));this.failures=0;this.retryAt=0;
|
|
147
|
-
}catch(error){if(error?.name!=="AbortError"){this.lastError="network_unavailable";this.failures++;this.retryAt=Date.now()+Math.min(300000,1000*2**Math.min(this.failures,8));}}
|
|
148
|
-
finally{if(this.
|
|
197
|
+
}catch(error){if(this.collecting&&this.generation===generation&&error?.name!=="AbortError"){this.lastError="network_unavailable";this.failures++;this.retryAt=Date.now()+Math.min(300000,1000*2**Math.min(this.failures,8));}}
|
|
198
|
+
finally{if(this.collecting&&this.generation===generation)this.persist();if(this.delivery===delivery)this.inFlight=null;}
|
|
149
199
|
})();
|
|
150
200
|
return this.inFlight;
|
|
151
201
|
}
|
|
152
202
|
async decorateLink(url,destinationPropertyId) {
|
|
153
|
-
if(!this.
|
|
203
|
+
if(!this.collecting)return url;
|
|
154
204
|
const destination=new URL(url,globalThis.location?.href);if(!["http:","https:"].includes(destination.protocol)||destination.username||destination.password)return url;
|
|
155
|
-
const response=await (this.options.fetch??fetch)(`${this.endpoint}/api/analytics/
|
|
205
|
+
const response=await (this.options.fetch??fetch)(`${this.endpoint}/api/analytics/v2/link`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...this.collection.metadata(),key:this.options.key,anonymousId:this.identity,destinationPropertyId,destinationUrl:destination.origin})});
|
|
156
206
|
if(!response.ok)return url;
|
|
157
207
|
const {token}=await response.json();destination.searchParams.set("fr_handoff",token);return destination.toString();
|
|
158
208
|
}
|
|
159
209
|
async consumeHandoff(token) {
|
|
160
|
-
if(!this.
|
|
161
|
-
await (this.options.fetch??fetch)(`${this.endpoint}/api/analytics/
|
|
210
|
+
if(!this.collecting)return;
|
|
211
|
+
await (this.options.fetch??fetch)(`${this.endpoint}/api/analytics/v2/link`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...this.collection.metadata(),key:this.options.key,anonymousId:this.identity,token})});
|
|
212
|
+
}
|
|
213
|
+
getDiagnostics(){return {collectionMode:this.collection.mode,consentState:this.collection.permission,optedOut:this.collection.refused,collectionEnabled:this.collecting,consent:this.collection.permission==='granted',queued:this.queue.length,dropped:this.dropped,rejected:this.rejected,acknowledged:this.sent,lastError:this.lastError,anonymousId:this.collecting?this.identity:null};}
|
|
214
|
+
destroy(){
|
|
215
|
+
this.persist(); this.collection.destroyed = true; this.collecting = false; this.generation++;
|
|
216
|
+
this.controller?.abort(); clearInterval(this.timer); this.timer = null;
|
|
217
|
+
for (const remove of this.cleanups.splice(0)) remove();
|
|
218
|
+
globalThis.removeEventListener?.('storage', this.preferenceListener);
|
|
219
|
+
if (instances.get(this.options.key) === this) instances.delete(this.options.key);
|
|
162
220
|
}
|
|
163
|
-
getDiagnostics(){return {consent:this.consent,queued:this.queue.length,dropped:this.dropped,rejected:this.rejected,acknowledged:this.sent,lastError:this.lastError,anonymousId:this.consent?this.identity:null};}
|
|
164
|
-
destroy(){this.setConsent(false);}
|
|
165
221
|
}
|
|
166
222
|
|
|
167
223
|
const instances=new Map();
|
package/next.d.ts
ADDED
package/package.json
CHANGED
|
@@ -1,17 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@founderroute/analytics",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.0-rc.1",
|
|
4
|
+
"description": "FounderRoute analytics for browsers, React, and Next.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./index.d.ts",
|
|
11
|
+
"default": "./index.js"
|
|
12
|
+
},
|
|
13
|
+
"./react": {
|
|
14
|
+
"types": "./react.d.ts",
|
|
15
|
+
"default": "./react.js"
|
|
16
|
+
},
|
|
17
|
+
"./next": {
|
|
18
|
+
"types": "./next.d.ts",
|
|
19
|
+
"default": "./next.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"*.js",
|
|
24
|
+
"*.d.ts",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"react": ">=18 <20",
|
|
29
|
+
"next": ">=15 <17"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"react": {
|
|
33
|
+
"optional": true
|
|
34
|
+
},
|
|
35
|
+
"next": {
|
|
36
|
+
"optional": true
|
|
37
|
+
}
|
|
38
|
+
},
|
|
12
39
|
"license": "MIT",
|
|
13
|
-
"repository": {
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/itsreed/founderroute-analytics.git",
|
|
43
|
+
"directory": "packages/browser"
|
|
44
|
+
},
|
|
14
45
|
"homepage": "https://github.com/itsreed/founderroute-analytics#readme",
|
|
15
|
-
"bugs": {
|
|
16
|
-
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/itsreed/founderroute-analytics/issues"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public",
|
|
51
|
+
"provenance": true
|
|
52
|
+
}
|
|
17
53
|
}
|
package/react.d.ts
ADDED
package/react.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
|
-
|
|
3
|
+
// Integrates an application-owned permission decision without rendering anything.
|
|
4
|
+
export function useFounderRouteConsent(client, granted) {
|
|
5
|
+
useEffect(() => { client.setConsent(granted); }, [client, granted]);
|
|
6
|
+
}
|