@tekibo/feedpulse-sdk 0.7.3 → 0.8.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 +83 -83
- package/dist/core/cookies.d.ts +37 -0
- package/dist/core/struggle-detector.d.ts +33 -0
- package/dist/core/tracker.d.ts +38 -0
- package/dist/core/types.d.ts +59 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +127 -176
- package/dist/nuxt/BotPanel.vue.d.ts +17 -0
- package/dist/nuxt/FeedPulseBot.vue.d.ts +24 -0
- package/dist/nuxt/FeedPulseProvider.vue.d.ts +21 -0
- package/dist/nuxt/FeedPulseWidget.vue.d.ts +10 -0
- package/dist/nuxt/composables.d.ts +4 -0
- package/dist/nuxt/consent-banner.vue.d.ts +16 -0
- package/dist/nuxt/index.d.ts +5 -0
- package/dist/nuxt.d.ts +2 -0
- package/dist/nuxt.js +2 -2
- package/dist/nuxt.mjs +283 -336
- package/dist/react/FeedPulseBot.d.ts +21 -0
- package/dist/react/FeedPulseProvider.d.ts +13 -0
- package/dist/react/FeedPulseWidget.d.ts +8 -0
- package/dist/react/consent-banner.d.ts +11 -0
- package/dist/react/hooks.d.ts +1 -0
- package/dist/react/index.d.ts +4 -0
- package/dist/react.d.ts +2 -0
- package/dist/react.js +3 -3
- package/dist/react.mjs +275 -315
- package/dist/struggle-detector-CT3YShuG.js +1 -0
- package/dist/{struggle-detector-BqTHgw95.mjs → struggle-detector-Cwe3PD9s.mjs} +40 -25
- package/package.json +4 -11
- package/templates/nextjs-proxy.ts +36 -36
- package/templates/nuxt-bot-proxy.ts +60 -93
- package/templates/nuxt-proxy.ts +44 -44
- package/bin/feedpulse-bot-build.mjs +0 -121
- package/dist/server.js +0 -1
- package/dist/server.mjs +0 -18
- package/dist/struggle-detector-DV52qDLp.js +0 -1
- package/templates/nuxt-config-id-route.ts +0 -15
package/README.md
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
# @tekibo/feedpulse-sdk
|
|
2
|
-
|
|
3
|
-
FeedPulse analytics and feedback SDK for Nuxt and React.
|
|
4
|
-
|
|
5
|
-
## Setup
|
|
6
|
-
|
|
7
|
-
### 1) Create a proxy route in your own app
|
|
8
|
-
|
|
9
|
-
- Nuxt: copy `templates/nuxt-proxy.ts` to `server/api/fp-proxy.post.ts`
|
|
10
|
-
- Next.js App Router: copy `templates/nextjs-proxy.ts` to `app/api/fp-proxy/route.ts`
|
|
11
|
-
|
|
12
|
-
### 2) Set server-only environment variables
|
|
13
|
-
|
|
14
|
-
```env
|
|
15
|
-
FEEDPULSE_API_KEY=fp_live_your_key
|
|
16
|
-
FEEDPULSE_WORKER_URL=https://feedpulse-workers.tekibohelp.workers.dev/ingest
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Never use `NUXT_PUBLIC_` or `NEXT_PUBLIC_` for the FeedPulse API key.
|
|
20
|
-
|
|
21
|
-
### 3) Use SDK without exposing key in client
|
|
22
|
-
|
|
23
|
-
```vue
|
|
24
|
-
<script setup lang="ts">
|
|
25
|
-
import { FeedPulseProvider, FeedPulseWidget } from "@tekibo/feedpulse-sdk/nuxt";
|
|
26
|
-
</script>
|
|
27
|
-
|
|
28
|
-
<template>
|
|
29
|
-
<FeedPulseProvider :debug="true">
|
|
30
|
-
<NuxtPage />
|
|
31
|
-
<FeedPulseWidget position="bottom-right" />
|
|
32
|
-
</FeedPulseProvider>
|
|
33
|
-
</template>
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
```tsx
|
|
37
|
-
import { FeedPulseProvider, FeedPulseWidget } from "@tekibo/feedpulse-sdk/react";
|
|
38
|
-
|
|
39
|
-
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
40
|
-
return (
|
|
41
|
-
<FeedPulseProvider debug>
|
|
42
|
-
{children}
|
|
43
|
-
<FeedPulseWidget position="bottom-right" />
|
|
44
|
-
</FeedPulseProvider>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## How it works
|
|
50
|
-
|
|
51
|
-
- Browser sends events to `/api/fp-proxy` by default
|
|
52
|
-
- Your server route injects `FEEDPULSE_API_KEY`
|
|
53
|
-
- Server forwards to FeedPulse worker ingest endpoint
|
|
54
|
-
- API key never appears in browser source or client payload
|
|
55
|
-
|
|
56
|
-
## Provider props
|
|
57
|
-
|
|
58
|
-
- `proxyEndpoint?: string` default `/api/fp-proxy`
|
|
59
|
-
- `debug?: boolean`
|
|
60
|
-
- `consentRequired?: boolean` default `true`
|
|
61
|
-
|
|
62
|
-
## Tracker config
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
type FeedPulseConfig = {
|
|
66
|
-
proxyEndpoint?: string;
|
|
67
|
-
batchInterval?: number;
|
|
68
|
-
debug?: boolean;
|
|
69
|
-
};
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## What is tracked
|
|
73
|
-
|
|
74
|
-
- `click`
|
|
75
|
-
- `hover`
|
|
76
|
-
- `scroll_into_view`
|
|
77
|
-
- `feedback`
|
|
78
|
-
- `consent`
|
|
79
|
-
|
|
80
|
-
## Templates included in package
|
|
81
|
-
|
|
82
|
-
- `templates/nuxt-proxy.ts`
|
|
83
|
-
- `templates/nextjs-proxy.ts`
|
|
1
|
+
# @tekibo/feedpulse-sdk
|
|
2
|
+
|
|
3
|
+
FeedPulse analytics and feedback SDK for Nuxt and React.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
### 1) Create a proxy route in your own app
|
|
8
|
+
|
|
9
|
+
- Nuxt: copy `templates/nuxt-proxy.ts` to `server/api/fp-proxy.post.ts`
|
|
10
|
+
- Next.js App Router: copy `templates/nextjs-proxy.ts` to `app/api/fp-proxy/route.ts`
|
|
11
|
+
|
|
12
|
+
### 2) Set server-only environment variables
|
|
13
|
+
|
|
14
|
+
```env
|
|
15
|
+
FEEDPULSE_API_KEY=fp_live_your_key
|
|
16
|
+
FEEDPULSE_WORKER_URL=https://feedpulse-workers.tekibohelp.workers.dev/ingest
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Never use `NUXT_PUBLIC_` or `NEXT_PUBLIC_` for the FeedPulse API key.
|
|
20
|
+
|
|
21
|
+
### 3) Use SDK without exposing key in client
|
|
22
|
+
|
|
23
|
+
```vue
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import { FeedPulseProvider, FeedPulseWidget } from "@tekibo/feedpulse-sdk/nuxt";
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<FeedPulseProvider :debug="true">
|
|
30
|
+
<NuxtPage />
|
|
31
|
+
<FeedPulseWidget position="bottom-right" />
|
|
32
|
+
</FeedPulseProvider>
|
|
33
|
+
</template>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { FeedPulseProvider, FeedPulseWidget } from "@tekibo/feedpulse-sdk/react";
|
|
38
|
+
|
|
39
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
40
|
+
return (
|
|
41
|
+
<FeedPulseProvider debug>
|
|
42
|
+
{children}
|
|
43
|
+
<FeedPulseWidget position="bottom-right" />
|
|
44
|
+
</FeedPulseProvider>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## How it works
|
|
50
|
+
|
|
51
|
+
- Browser sends events to `/api/fp-proxy` by default
|
|
52
|
+
- Your server route injects `FEEDPULSE_API_KEY`
|
|
53
|
+
- Server forwards to FeedPulse worker ingest endpoint
|
|
54
|
+
- API key never appears in browser source or client payload
|
|
55
|
+
|
|
56
|
+
## Provider props
|
|
57
|
+
|
|
58
|
+
- `proxyEndpoint?: string` default `/api/fp-proxy`
|
|
59
|
+
- `debug?: boolean`
|
|
60
|
+
- `consentRequired?: boolean` default `true`
|
|
61
|
+
|
|
62
|
+
## Tracker config
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
type FeedPulseConfig = {
|
|
66
|
+
proxyEndpoint?: string;
|
|
67
|
+
batchInterval?: number;
|
|
68
|
+
debug?: boolean;
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## What is tracked
|
|
73
|
+
|
|
74
|
+
- `click`
|
|
75
|
+
- `hover`
|
|
76
|
+
- `scroll_into_view`
|
|
77
|
+
- `feedback`
|
|
78
|
+
- `consent`
|
|
79
|
+
|
|
80
|
+
## Templates included in package
|
|
81
|
+
|
|
82
|
+
- `templates/nuxt-proxy.ts`
|
|
83
|
+
- `templates/nextjs-proxy.ts`
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare const COOKIE_KEYS: {
|
|
2
|
+
readonly USER_ID: "__fp_uid";
|
|
3
|
+
readonly SESSION_ID: "__fp_sid";
|
|
4
|
+
readonly SESSION_START: "__fp_ss";
|
|
5
|
+
readonly CONSENT: "__fp_consent";
|
|
6
|
+
readonly UTM: "__fp_utm";
|
|
7
|
+
};
|
|
8
|
+
export type ConsentState = {
|
|
9
|
+
necessary: true;
|
|
10
|
+
analytics: boolean;
|
|
11
|
+
marketing: boolean;
|
|
12
|
+
version: string;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
};
|
|
15
|
+
export type CookieData = {
|
|
16
|
+
userId: string | null;
|
|
17
|
+
sessionId: string;
|
|
18
|
+
sessionStart: number;
|
|
19
|
+
consent: ConsentState | null;
|
|
20
|
+
utm: Record<string, string> | null;
|
|
21
|
+
isReturningUser: boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare class CookieManager {
|
|
24
|
+
private transientSession;
|
|
25
|
+
initSession(): {
|
|
26
|
+
sessionId: string;
|
|
27
|
+
sessionStart: number;
|
|
28
|
+
isReturningUser: boolean;
|
|
29
|
+
};
|
|
30
|
+
initUser(): string;
|
|
31
|
+
getConsent(): ConsentState | null;
|
|
32
|
+
setConsent(state: Omit<ConsentState, "necessary" | "timestamp">): ConsentState;
|
|
33
|
+
clearAnalyticsCookies(): void;
|
|
34
|
+
isConsentExpired(): boolean;
|
|
35
|
+
captureUTM(): Record<string, string> | null;
|
|
36
|
+
getAllData(analyticsConsented: boolean): CookieData;
|
|
37
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
type TriggerReason = "rage_click" | "dead_click" | "idle" | "rapid_scroll" | "long_page_time" | "back_navigation" | "js_error" | "form_abandonment" | "repeated_page";
|
|
2
|
+
export declare class StruggleDetector {
|
|
3
|
+
private opts;
|
|
4
|
+
private score;
|
|
5
|
+
private triggered;
|
|
6
|
+
private cooldownUntil;
|
|
7
|
+
private reasons;
|
|
8
|
+
private clickMap;
|
|
9
|
+
private lastActivity;
|
|
10
|
+
private scrollPositions;
|
|
11
|
+
private idleTimer;
|
|
12
|
+
private clickHandler;
|
|
13
|
+
private scrollHandler;
|
|
14
|
+
private activityHandler;
|
|
15
|
+
private errorHandler;
|
|
16
|
+
private popstateHandler;
|
|
17
|
+
private formListeners;
|
|
18
|
+
constructor(opts: {
|
|
19
|
+
onTrigger: (score: number, reasons: TriggerReason[]) => void;
|
|
20
|
+
});
|
|
21
|
+
start(): void;
|
|
22
|
+
private onClick;
|
|
23
|
+
private onScroll;
|
|
24
|
+
private onError;
|
|
25
|
+
private onBackNavigation;
|
|
26
|
+
private onActivity;
|
|
27
|
+
private onFormBlur;
|
|
28
|
+
private checkIdle;
|
|
29
|
+
private addScore;
|
|
30
|
+
reset(): void;
|
|
31
|
+
stop(): void;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { FeedPulseConfig, FeedPulseEvent } from './types';
|
|
2
|
+
import { CookieManager } from './cookies';
|
|
3
|
+
export declare class FeedPulseTracker {
|
|
4
|
+
private config;
|
|
5
|
+
private queue;
|
|
6
|
+
private flushInterval;
|
|
7
|
+
private sessionId;
|
|
8
|
+
private observer;
|
|
9
|
+
private intersectionObserver;
|
|
10
|
+
private scrollFallbackListener;
|
|
11
|
+
private resizeFallbackListener;
|
|
12
|
+
private seenInView;
|
|
13
|
+
private cookieManager;
|
|
14
|
+
private cookieData;
|
|
15
|
+
private analyticsConsented;
|
|
16
|
+
private isInitialized;
|
|
17
|
+
private beforeUnloadListener;
|
|
18
|
+
private visibilityListener;
|
|
19
|
+
private clickListener;
|
|
20
|
+
private hoverStartListener;
|
|
21
|
+
private hoverEndListener;
|
|
22
|
+
constructor(config: FeedPulseConfig);
|
|
23
|
+
init(): void;
|
|
24
|
+
private attachGlobalListeners;
|
|
25
|
+
private observeDOM;
|
|
26
|
+
track(eventType: FeedPulseEvent["eventType"], elementId: string, metadata?: Record<string, unknown>): void;
|
|
27
|
+
trackFeedback(elementId: string | null, rating: number | null, message: string | null): void;
|
|
28
|
+
private flush;
|
|
29
|
+
private sendImmediate;
|
|
30
|
+
private startFlushInterval;
|
|
31
|
+
updateCookieData(data: ReturnType<CookieManager["getAllData"]>): void;
|
|
32
|
+
private refreshCookieData;
|
|
33
|
+
private isBrowser;
|
|
34
|
+
private getDeviceType;
|
|
35
|
+
private getBrowser;
|
|
36
|
+
private getOS;
|
|
37
|
+
destroy(): void;
|
|
38
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type FeedPulseEventType = "click" | "hover" | "scroll_into_view" | "feedback" | "consent";
|
|
2
|
+
export type FeedPulseEvent = {
|
|
3
|
+
elementId: string;
|
|
4
|
+
eventType: FeedPulseEventType;
|
|
5
|
+
sessionId: string;
|
|
6
|
+
visitorId: string | null;
|
|
7
|
+
isReturningUser?: boolean;
|
|
8
|
+
sessionStart?: string | null;
|
|
9
|
+
utmSource?: string | null;
|
|
10
|
+
utmMedium?: string | null;
|
|
11
|
+
utmCampaign?: string | null;
|
|
12
|
+
utmTerm?: string | null;
|
|
13
|
+
utmContent?: string | null;
|
|
14
|
+
consentAnalytics?: boolean;
|
|
15
|
+
page: string;
|
|
16
|
+
referrer?: string;
|
|
17
|
+
device: string;
|
|
18
|
+
browser?: string;
|
|
19
|
+
os?: string;
|
|
20
|
+
metadata?: Record<string, unknown>;
|
|
21
|
+
timestamp: string;
|
|
22
|
+
};
|
|
23
|
+
export type FeedPulseConfig = {
|
|
24
|
+
proxyEndpoint?: string;
|
|
25
|
+
batchInterval?: number;
|
|
26
|
+
debug?: boolean;
|
|
27
|
+
};
|
|
28
|
+
export type BotFaqItem = {
|
|
29
|
+
q: string;
|
|
30
|
+
a: string;
|
|
31
|
+
};
|
|
32
|
+
export type BotNavigableElement = {
|
|
33
|
+
label: string;
|
|
34
|
+
description: string;
|
|
35
|
+
instruction: string;
|
|
36
|
+
};
|
|
37
|
+
export type BotFormField = {
|
|
38
|
+
label: string;
|
|
39
|
+
hint: string;
|
|
40
|
+
};
|
|
41
|
+
export type BotFeature = {
|
|
42
|
+
name: string;
|
|
43
|
+
description: string;
|
|
44
|
+
};
|
|
45
|
+
export type BotConfig = {
|
|
46
|
+
pageContext?: string;
|
|
47
|
+
systemPrompt?: string;
|
|
48
|
+
faq?: BotFaqItem[];
|
|
49
|
+
canNavigate?: boolean;
|
|
50
|
+
navigableElements?: BotNavigableElement[];
|
|
51
|
+
canHelpForms?: boolean;
|
|
52
|
+
formGuide?: BotFormField[];
|
|
53
|
+
features?: BotFeature[];
|
|
54
|
+
escalateTo?: {
|
|
55
|
+
label: string;
|
|
56
|
+
url: string;
|
|
57
|
+
};
|
|
58
|
+
initialMessage?: string;
|
|
59
|
+
};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function p(o){for(var e=1;e<arguments.length;e++){var i=arguments[e];for(var s in i)o[s]=i[s]}return o}var y={read:function(o){return o[0]==='"'&&(o=o.slice(1,-1)),o.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(o){return encodeURIComponent(o).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}};function m(o,e){function i(t,n,r){if(!(typeof document>"u")){r=p({},e,r),typeof r.expires=="number"&&(r.expires=new Date(Date.now()+r.expires*864e5)),r.expires&&(r.expires=r.expires.toUTCString()),t=encodeURIComponent(t).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var a="";for(var l in r)r[l]&&(a+="; "+l,r[l]!==!0&&(a+="="+r[l].split(";")[0]));return document.cookie=t+"="+o.write(n,t)+a}}function s(t){if(!(typeof document>"u"||arguments.length&&!t)){for(var n=document.cookie?document.cookie.split("; "):[],r={},a=0;a<n.length;a++){var l=n[a].split("="),d=l.slice(1).join("=");try{var h=decodeURIComponent(l[0]);if(r[h]=o.read(d,h),t===h)break}catch{}}return t?r[t]:r}}return Object.create({set:i,get:s,remove:function(t,n){i(t,"",p({},n,{expires:-1}))},withAttributes:function(t){return m(this.converter,p({},this.attributes,t))},withConverter:function(t){return m(p({},this.converter,t),this.attributes)}},{attributes:{value:Object.freeze(e)},converter:{value:Object.freeze(o)}})}var u=m(y,{path:"/"});const c={USER_ID:"__fp_uid",SESSION_ID:"__fp_sid",SESSION_START:"__fp_ss",CONSENT:"__fp_consent",UTM:"__fp_utm"},g=1/48,I=365,b=365,k=30;function S(){return typeof window<"u"}function v(o){if(!o)return null;try{return JSON.parse(o)}catch{return null}}class w{initSession(){const e=!!u.get(c.USER_ID);let i=u.get(c.SESSION_ID)??null,s=Number(u.get(c.SESSION_START)||Date.now());return i||(i=crypto.randomUUID(),s=Date.now()),u.set(c.SESSION_ID,i,{expires:g}),u.set(c.SESSION_START,String(s),{expires:g}),{sessionId:i,sessionStart:s,isReturningUser:e}}initUser(){let e=u.get(c.USER_ID)??null;return e||(e=crypto.randomUUID(),u.set(c.USER_ID,e,{expires:b})),e}getConsent(){return v(u.get(c.CONSENT))}setConsent(e){const i={necessary:!0,analytics:e.analytics,marketing:e.marketing,version:e.version,timestamp:Date.now()};return u.set(c.CONSENT,JSON.stringify(i),{expires:I}),i}clearAnalyticsCookies(){u.remove(c.USER_ID)}isConsentExpired(){const e=this.getConsent();if(!e)return!0;const i=365*24*60*60*1e3;return Date.now()-e.timestamp>i}captureUTM(){if(!S())return null;const e=new URLSearchParams(window.location.search),i={};for(const s of["utm_source","utm_medium","utm_campaign","utm_term","utm_content"]){const t=e.get(s);t&&(i[s]=t)}return Object.keys(i).length===0?v(u.get(c.UTM)):(u.set(c.UTM,JSON.stringify(i),{expires:k}),i)}getAllData(e){const{sessionId:i,sessionStart:s,isReturningUser:t}=this.initSession();return{userId:e?this.initUser():null,sessionId:i,sessionStart:s,consent:this.getConsent(),utm:this.captureUTM(),isReturningUser:e?t:!1}}}class _{constructor(){this.cursorEl=null}createCursor(){if(this.cursorEl)return this.cursorEl;const e=document.createElement("div");return e.id="__fp_bot_cursor",e.innerHTML='<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M5 3l14 9-7 1-4 7L5 3z" fill="#6366f1" stroke="white" stroke-width="1.5"/></svg>',Object.assign(e.style,{position:"fixed",zIndex:"999999",pointerEvents:"none",transition:"left 0.6s ease, top 0.6s ease",transform:"translate(-4px,-4px)",left:"50%",top:"50%",opacity:"0"}),document.body.appendChild(e),this.cursorEl=e,e}async moveTo(e,i){const s=this.createCursor();s.style.left=`${e}px`,s.style.top=`${i}px`,await new Promise(t=>setTimeout(t,700))}async clickAnimation(e,i){if(!document.getElementById("__fp_cursor_styles")){const t=document.createElement("style");t.id="__fp_cursor_styles",t.textContent="@keyframes fp-ripple{from{transform:scale(0);opacity:1}to{transform:scale(2);opacity:0}}",document.head.appendChild(t)}const s=document.createElement("div");Object.assign(s.style,{position:"fixed",left:`${e-16}px`,top:`${i-16}px`,width:"32px",height:"32px",borderRadius:"50%",background:"rgba(99,102,241,0.4)",zIndex:"999998",pointerEvents:"none",animation:"fp-ripple 0.4s ease-out forwards"}),document.body.appendChild(s),await new Promise(t=>setTimeout(t,400)),s.remove()}async typeText(e,i){e.focus(),(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement)&&(e.value="");for(const s of i)await new Promise(t=>setTimeout(t,60+Math.random()*40)),(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement)&&(e.value+=s,e.dispatchEvent(new Event("input",{bubbles:!0})))}async perform(e){try{const i=this.createCursor();if(i.style.opacity="1",e.type==="navigate"&&e.value)return await this.moveTo(window.innerWidth/2,window.innerHeight/2),await new Promise(d=>setTimeout(d,300)),this.destroy(),window.location.href=e.value,{success:!0,message:`Navigating to ${e.value}...`};if(!e.selector)return{success:!1,message:"No target element specified."};const t=!/[#.\[\]\s>:+~]/.test(e.selector)?`[data-fp-id="${e.selector}"], #${e.selector}, [name="${e.selector}"]`:e.selector,n=document.querySelector(t);if(!n)return{success:!1,message:`Could not find "${e.label}" on the page.`};const r=n.getBoundingClientRect(),a=r.left+r.width/2,l=r.top+r.height/2;if(await this.moveTo(a,l),e.type==="click"&&(await this.clickAnimation(a,l),n.click(),n.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0}))),e.type==="scroll"){n.scrollIntoView({behavior:"smooth",block:"center"});const d=n.style.outline,h=n.style.outlineOffset;n.style.outline="2px solid #6366f1",n.style.outlineOffset="3px",await new Promise(f=>setTimeout(f,1500)),n.style.outline=d,n.style.outlineOffset=h}if(e.type==="fill"&&e.value&&await this.typeText(n,e.value),e.type==="highlight"){n.scrollIntoView({behavior:"smooth",block:"center"});const d=n.style.outline,h=n.style.outlineOffset;n.style.outline="2px solid #6366f1",n.style.outlineOffset="3px",await new Promise(f=>setTimeout(f,1500)),n.style.outline=d,n.style.outlineOffset=h}return await new Promise(d=>setTimeout(d,400)),this.destroy(),{success:!0,message:`Done! I ${e.type}ed "${e.label}".`}}catch{return this.destroy(),{success:!1,message:"Something went wrong performing that action."}}}destroy(){this.cursorEl?.remove(),this.cursorEl=null}}class D{constructor(e){this.queue=[],this.flushInterval=null,this.sessionId="",this.observer=null,this.intersectionObserver=null,this.scrollFallbackListener=null,this.resizeFallbackListener=null,this.seenInView=new Set,this.cookieManager=new w,this.cookieData=null,this.analyticsConsented=!1,this.isInitialized=!1,this.beforeUnloadListener=null,this.visibilityListener=null,this.config={proxyEndpoint:e.proxyEndpoint??"/api/fp-proxy",batchInterval:e.batchInterval??5e3,debug:e.debug??!1}}init(){!this.isBrowser()||this.isInitialized||(this.isInitialized=!0,this.refreshCookieData(),this.startFlushInterval(),this.attachGlobalListeners(),this.observeDOM())}attachGlobalListeners(){document.addEventListener("click",s=>{const t=s.target.closest("[data-fp-id]");t&&this.track("click",t.getAttribute("data-fp-id"),{x:s.clientX,y:s.clientY})},{passive:!0});let e=0,i=null;document.addEventListener("mouseover",s=>{const t=s.target.closest("[data-fp-id]");t&&(e=Date.now(),i=t.getAttribute("data-fp-id"))},{passive:!0}),document.addEventListener("mouseout",s=>{if(!s.target.closest("[data-fp-id]")||!i)return;const n=Date.now()-e;n>500&&this.track("hover",i,{hoverDuration:n}),i=null},{passive:!0})}observeDOM(){const e=t=>{this.seenInView.has(t)||(this.seenInView.add(t),this.track("scroll_into_view",t,{scrollDepth:Math.round(window.scrollY/Math.max(1,document.body.scrollHeight)*100)}))};this.intersectionObserver=new IntersectionObserver(t=>{for(const n of t){const r=n.target.dataset.fpId;!r||!n.isIntersecting||e(r)}},{threshold:.1});const i=()=>{document.querySelectorAll("[data-fp-id]").forEach(t=>{this.intersectionObserver?.observe(t)})},s=()=>{const t=window.innerHeight*.1,n=window.innerHeight*.9;document.querySelectorAll("[data-fp-id]").forEach(r=>{const a=r.dataset.fpId;if(!a||this.seenInView.has(a))return;const l=r.getBoundingClientRect();l.bottom>=t&&l.top<=n&&e(a)})};i(),s(),this.scrollFallbackListener=()=>s(),this.resizeFallbackListener=()=>s(),window.addEventListener("scroll",this.scrollFallbackListener,{passive:!0}),window.addEventListener("resize",this.resizeFallbackListener,{passive:!0}),this.observer=new MutationObserver(()=>i()),this.observer.observe(document.body,{childList:!0,subtree:!0})}track(e,i,s){this.isBrowser()&&(this.refreshCookieData(),this.queue.push({elementId:i,eventType:e,sessionId:this.cookieData?.sessionId??this.sessionId,visitorId:this.cookieData?.userId??null,isReturningUser:this.cookieData?.isReturningUser??!1,sessionStart:this.cookieData?.sessionStart?new Date(this.cookieData.sessionStart).toISOString():null,utmSource:this.cookieData?.utm?.utm_source??null,utmMedium:this.cookieData?.utm?.utm_medium??null,utmCampaign:this.cookieData?.utm?.utm_campaign??null,utmTerm:this.cookieData?.utm?.utm_term??null,utmContent:this.cookieData?.utm?.utm_content??null,consentAnalytics:this.cookieData?.consent?.analytics??!1,page:window.location.pathname,referrer:document.referrer,device:this.getDeviceType(),browser:this.getBrowser(),os:this.getOS(),metadata:s,timestamp:new Date().toISOString()}))}trackFeedback(e,i,s){this.isBrowser()&&(!i&&!s||(this.refreshCookieData(),this.sendImmediate([{elementId:e??"__page__",eventType:"feedback",sessionId:this.cookieData?.sessionId??this.sessionId,visitorId:this.cookieData?.userId??null,isReturningUser:this.cookieData?.isReturningUser??!1,sessionStart:this.cookieData?.sessionStart?new Date(this.cookieData.sessionStart).toISOString():null,utmSource:this.cookieData?.utm?.utm_source??null,utmMedium:this.cookieData?.utm?.utm_medium??null,utmCampaign:this.cookieData?.utm?.utm_campaign??null,utmTerm:this.cookieData?.utm?.utm_term??null,utmContent:this.cookieData?.utm?.utm_content??null,consentAnalytics:this.cookieData?.consent?.analytics??!1,page:window.location.pathname,device:this.getDeviceType(),metadata:{rating:i,message:s},timestamp:new Date().toISOString()}])))}async flush(){if(this.queue.length===0)return;const e=[...this.queue];this.queue=[],await this.sendImmediate(e)}async sendImmediate(e){try{const i=await fetch(this.config.proxyEndpoint,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({events:e}),keepalive:!0});if(this.config.debug&&i.ok){const s=await i.json().catch(()=>null);s?.ingested!==void 0&&s?.pipeline&&globalThis.console.debug(`[FeedPulse] ${s.ingested} events via ${s.pipeline} pipeline`)}}catch{}}startFlushInterval(){this.flushInterval=setInterval(()=>this.flush(),this.config.batchInterval),this.beforeUnloadListener=()=>this.flush(),this.visibilityListener=()=>{document.visibilityState==="hidden"&&this.flush()},window.addEventListener("beforeunload",this.beforeUnloadListener),document.addEventListener("visibilitychange",this.visibilityListener)}updateCookieData(e){this.cookieData=e,this.sessionId=e.sessionId,this.analyticsConsented=!!e.consent?.analytics}refreshCookieData(){const e=this.cookieManager.getAllData(this.analyticsConsented);this.cookieData=e,this.sessionId=e.sessionId}isBrowser(){return typeof window<"u"&&typeof document<"u"}getDeviceType(){const e=navigator.userAgent;return/Mobi|Android/i.test(e)?"mobile":/Tablet|iPad/i.test(e)?"tablet":"desktop"}getBrowser(){const e=navigator.userAgent;return e.includes("Edg")?"Edge":e.includes("Chrome")?"Chrome":e.includes("Firefox")?"Firefox":e.includes("Safari")?"Safari":"Other"}getOS(){const e=navigator.userAgent;return e.includes("Windows")?"Windows":e.includes("Mac")?"macOS":e.includes("Linux")?"Linux":e.includes("Android")?"Android":/iPhone|iOS/.test(e)?"iOS":"Other"}destroy(){this.flushInterval&&clearInterval(this.flushInterval),this.beforeUnloadListener&&window.removeEventListener("beforeunload",this.beforeUnloadListener),this.visibilityListener&&document.removeEventListener("visibilitychange",this.visibilityListener),this.scrollFallbackListener&&window.removeEventListener("scroll",this.scrollFallbackListener),this.resizeFallbackListener&&window.removeEventListener("resize",this.resizeFallbackListener),this.observer?.disconnect(),this.intersectionObserver?.disconnect(),this.isInitialized=!1,this.flush()}}exports.COOKIE_KEYS=c;exports.CookieManager=w;exports.CursorActor=_;exports.FeedPulseTracker=D;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function h(r){for(var e=1;e<arguments.length;e++){var i=arguments[e];for(var t in i)t!=="__proto__"&&(r[t]=i[t])}return r}var S={read:function(r){return r[0]==='"'&&(r=r.slice(1,-1)),r.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(r){return encodeURIComponent(r).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}};function f(r,e){function i(n,a,s){if(!(typeof document>"u")){s=h({},e,s),typeof s.expires=="number"&&(s.expires=new Date(Date.now()+s.expires*864e5)),s.expires&&(s.expires=s.expires.toUTCString()),n=encodeURIComponent(n).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var l="";for(var u in s)s[u]&&(l+="; "+u,s[u]!==!0&&(l+="="+s[u].split(";")[0]));return document.cookie=n+"="+r.write(a,n)+l}}function t(n){if(!(typeof document>"u"||arguments.length&&!n)){for(var a=document.cookie?document.cookie.split("; "):[],s={},l=0;l<a.length;l++){var u=a[l].split("="),m=u.slice(1).join("=");try{var d=decodeURIComponent(u[0]);if(d in s||(s[d]=r.read(m,d)),n===d)break}catch{}}return n?s[n]:s}}return Object.create({set:i,get:t,remove:function(n,a){i(n,"",h({},a,{expires:-1}))},withAttributes:function(n){return f(this.converter,h({},this.attributes,n))},withConverter:function(n){return f(h({},this.converter,n),this.attributes)}},{attributes:{value:Object.freeze(e)},converter:{value:Object.freeze(r)}})}var c=f(S,{path:"/"});const o={USER_ID:"__fp_uid",SESSION_ID:"__fp_sid",SESSION_START:"__fp_ss",CONSENT:"__fp_consent",UTM:"__fp_utm"},p=1/48,I=365,k=365,w=30;function D(){return typeof window<"u"}function v(r){if(!r)return null;try{return JSON.parse(r)}catch{return null}}class g{transientSession=null;initSession(){const e=!!c.get(o.USER_ID);let i=c.get(o.SESSION_ID)??null,t=Number(c.get(o.SESSION_START)||Date.now());return i||(i=crypto.randomUUID(),t=Date.now()),c.set(o.SESSION_ID,i,{expires:p}),c.set(o.SESSION_START,String(t),{expires:p}),{sessionId:i,sessionStart:t,isReturningUser:e}}initUser(){let e=c.get(o.USER_ID)??null;return e||(e=crypto.randomUUID(),c.set(o.USER_ID,e,{expires:k})),e}getConsent(){return v(c.get(o.CONSENT))}setConsent(e){const i={necessary:!0,analytics:e.analytics,marketing:e.marketing,version:e.version,timestamp:Date.now()};return c.set(o.CONSENT,JSON.stringify(i),{expires:I}),i}clearAnalyticsCookies(){for(const e of[o.USER_ID,o.SESSION_ID,o.SESSION_START,o.UTM])c.remove(e);this.transientSession=null}isConsentExpired(){const e=this.getConsent();if(!e)return!0;const i=365*24*60*60*1e3;return Date.now()-e.timestamp>i}captureUTM(){if(!D())return null;const e=new URLSearchParams(window.location.search),i={};for(const t of["utm_source","utm_medium","utm_campaign","utm_term","utm_content"]){const n=e.get(t);n&&(i[t]=n)}return Object.keys(i).length===0?v(c.get(o.UTM)):(c.set(o.UTM,JSON.stringify(i),{expires:w}),i)}getAllData(e){if(!e)return this.transientSession===null&&(this.transientSession={sessionId:crypto.randomUUID(),sessionStart:Date.now()}),{userId:null,...this.transientSession,consent:this.getConsent(),utm:null,isReturningUser:!1};const{sessionId:i,sessionStart:t,isReturningUser:n}=this.initSession();return{userId:e?this.initUser():null,sessionId:i,sessionStart:t,consent:this.getConsent(),utm:this.captureUTM(),isReturningUser:e?n:!1}}}class b{config;queue=[];flushInterval=null;sessionId="";observer=null;intersectionObserver=null;scrollFallbackListener=null;resizeFallbackListener=null;seenInView=new Set;cookieManager=new g;cookieData=null;analyticsConsented=!1;isInitialized=!1;beforeUnloadListener=null;visibilityListener=null;clickListener=null;hoverStartListener=null;hoverEndListener=null;constructor(e){this.config={proxyEndpoint:e.proxyEndpoint??"/api/fp-proxy",batchInterval:e.batchInterval??5e3,debug:e.debug??!1}}init(){!this.isBrowser()||this.isInitialized||(this.isInitialized=!0,this.refreshCookieData(),this.startFlushInterval(),this.attachGlobalListeners(),this.observeDOM())}attachGlobalListeners(){this.clickListener=t=>{const n=t.target instanceof Element?t.target.closest("[data-fp-id]"):null;n&&this.track("click",n.getAttribute("data-fp-id"),{x:t.clientX,y:t.clientY})},document.addEventListener("click",this.clickListener,{passive:!0});let e=0,i=null;this.hoverStartListener=t=>{const n=t.target instanceof Element?t.target.closest("[data-fp-id]"):null;n&&(e=Date.now(),i=n.getAttribute("data-fp-id"))},document.addEventListener("mouseover",this.hoverStartListener,{passive:!0}),this.hoverEndListener=t=>{if(!(t.target instanceof Element?t.target.closest("[data-fp-id]"):null)||!i)return;const a=Date.now()-e;a>500&&this.track("hover",i,{hoverDuration:a}),i=null},document.addEventListener("mouseout",this.hoverEndListener,{passive:!0})}observeDOM(){const e=n=>{this.seenInView.has(n)||(this.seenInView.add(n),this.track("scroll_into_view",n,{scrollDepth:Math.round(window.scrollY/Math.max(1,document.body.scrollHeight)*100)}))};this.intersectionObserver=new IntersectionObserver(n=>{for(const a of n){const s=a.target.dataset.fpId;!s||!a.isIntersecting||e(s)}},{threshold:.1});const i=()=>{document.querySelectorAll("[data-fp-id]").forEach(n=>{this.intersectionObserver?.observe(n)})},t=()=>{const n=window.innerHeight*.1,a=window.innerHeight*.9;document.querySelectorAll("[data-fp-id]").forEach(s=>{const l=s.dataset.fpId;if(!l||this.seenInView.has(l))return;const u=s.getBoundingClientRect();u.bottom>=n&&u.top<=a&&e(l)})};i(),t(),this.scrollFallbackListener=()=>t(),this.resizeFallbackListener=()=>t(),window.addEventListener("scroll",this.scrollFallbackListener,{passive:!0}),window.addEventListener("resize",this.resizeFallbackListener,{passive:!0}),this.observer=new MutationObserver(()=>i()),this.observer.observe(document.body,{childList:!0,subtree:!0})}track(e,i,t){!this.isBrowser()||!this.analyticsConsented||(this.refreshCookieData(),this.queue.push({elementId:i,eventType:e,sessionId:this.cookieData?.sessionId??this.sessionId,visitorId:this.cookieData?.userId??null,isReturningUser:this.cookieData?.isReturningUser??!1,sessionStart:this.cookieData?.sessionStart?new Date(this.cookieData.sessionStart).toISOString():null,utmSource:this.cookieData?.utm?.utm_source??null,utmMedium:this.cookieData?.utm?.utm_medium??null,utmCampaign:this.cookieData?.utm?.utm_campaign??null,utmTerm:this.cookieData?.utm?.utm_term??null,utmContent:this.cookieData?.utm?.utm_content??null,consentAnalytics:this.cookieData?.consent?.analytics??!1,page:window.location.pathname,referrer:document.referrer,device:this.getDeviceType(),browser:this.getBrowser(),os:this.getOS(),metadata:t,timestamp:new Date().toISOString()}))}trackFeedback(e,i,t){this.isBrowser()&&(!i&&!t||(this.refreshCookieData(),this.sendImmediate([{elementId:e??"__page__",eventType:"feedback",sessionId:this.cookieData?.sessionId??this.sessionId,visitorId:this.cookieData?.userId??null,isReturningUser:this.cookieData?.isReturningUser??!1,sessionStart:this.cookieData?.sessionStart?new Date(this.cookieData.sessionStart).toISOString():null,utmSource:this.cookieData?.utm?.utm_source??null,utmMedium:this.cookieData?.utm?.utm_medium??null,utmCampaign:this.cookieData?.utm?.utm_campaign??null,utmTerm:this.cookieData?.utm?.utm_term??null,utmContent:this.cookieData?.utm?.utm_content??null,consentAnalytics:this.cookieData?.consent?.analytics??!1,page:window.location.pathname,device:this.getDeviceType(),metadata:{rating:i,message:t},timestamp:new Date().toISOString()}])))}async flush(){if(this.queue.length===0)return;const e=[...this.queue];this.queue=[],await this.sendImmediate(e)}async sendImmediate(e){try{const i=await fetch(this.config.proxyEndpoint,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({events:e}),keepalive:!0});if(this.config.debug&&i.ok){const t=await i.json().catch(()=>null);t?.ingested!==void 0&&t?.pipeline&&globalThis.console.debug(`[FeedPulse] ${t.ingested} events via ${t.pipeline} pipeline`)}}catch{}}startFlushInterval(){this.flushInterval=setInterval(()=>this.flush(),this.config.batchInterval),this.beforeUnloadListener=()=>this.flush(),this.visibilityListener=()=>{document.visibilityState==="hidden"&&this.flush()},window.addEventListener("beforeunload",this.beforeUnloadListener),document.addEventListener("visibilitychange",this.visibilityListener)}updateCookieData(e){this.cookieData=e,this.sessionId=e.sessionId,this.analyticsConsented=!!e.consent?.analytics}refreshCookieData(){const e=this.cookieManager.getAllData(this.analyticsConsented);this.cookieData=e,this.sessionId=e.sessionId}isBrowser(){return typeof window<"u"&&typeof document<"u"}getDeviceType(){const e=navigator.userAgent;return/Mobi|Android/i.test(e)?"mobile":/Tablet|iPad/i.test(e)?"tablet":"desktop"}getBrowser(){const e=navigator.userAgent;return e.includes("Edg")?"Edge":e.includes("Chrome")?"Chrome":e.includes("Firefox")?"Firefox":e.includes("Safari")?"Safari":"Other"}getOS(){const e=navigator.userAgent;return e.includes("Windows")?"Windows":e.includes("Mac")?"macOS":e.includes("Linux")?"Linux":e.includes("Android")?"Android":/iPhone|iOS/.test(e)?"iOS":"Other"}destroy(){this.flushInterval&&clearInterval(this.flushInterval),this.beforeUnloadListener&&window.removeEventListener("beforeunload",this.beforeUnloadListener),this.visibilityListener&&document.removeEventListener("visibilitychange",this.visibilityListener),this.scrollFallbackListener&&window.removeEventListener("scroll",this.scrollFallbackListener),this.resizeFallbackListener&&window.removeEventListener("resize",this.resizeFallbackListener),this.clickListener&&document.removeEventListener("click",this.clickListener),this.hoverStartListener&&document.removeEventListener("mouseover",this.hoverStartListener),this.hoverEndListener&&document.removeEventListener("mouseout",this.hoverEndListener),this.observer?.disconnect(),this.intersectionObserver?.disconnect(),this.isInitialized=!1,this.flush()}}exports.COOKIE_KEYS=o;exports.CookieManager=g;exports.FeedPulseTracker=b;
|