diff --git a/apps/sdk-api/src/index.ts b/apps/sdk-api/src/index.ts index 111afd32..1aacc645 100644 --- a/apps/sdk-api/src/index.ts +++ b/apps/sdk-api/src/index.ts @@ -38,7 +38,7 @@ const startServer = async () => { }); }); - fastify.register(eventRouter, { prefix: '/api/event' }); + fastify.register(eventRouter, { prefix: '/event' }); fastify.setErrorHandler((error, request, reply) => { fastify.log.error(error); }); diff --git a/apps/test/public/op.js b/apps/test/public/op.js new file mode 100644 index 00000000..a5ccdd10 --- /dev/null +++ b/apps/test/public/op.js @@ -0,0 +1,2 @@ +"use strict";(()=>{function y(u,t,e){return function(s,o,v){let m=`${u}${s}`,l,h={"mixan-client-id":t,"Content-Type":"application/json"};return e&&(h["mixan-client-secret"]=e),new Promise(a=>{let f=n=>{clearTimeout(l),fetch(m,{headers:h,method:"POST",body:JSON.stringify(o??{}),keepalive:!0,...v??{}}).then(async p=>{if(p.status!==200)return g(n,a);let w=await p.json();if(!w)return a(null);a(w)}).catch(()=>g(n,a))};function g(n,p){if(n>1)return p(null);l=setTimeout(()=>{f(n+1)},Math.pow(2,n)*500)}f(0)})}}var c=class{options;api;state={properties:{}};constructor(t){this.options=t,this.api=y(t.url,t.clientId,t.clientSecret)}init(t){this.state.properties=t??{}}getProfileId(){if(this.state.profileId)return this.state.profileId;this.options.getProfileId&&(this.state.profileId=this.options.getProfileId()||void 0)}async event(t,e){let i=await this.api("/event",{name:t,properties:{...this.state.properties,...e??{}},timestamp:this.timestamp(),profileId:this.getProfileId()});this.options.setProfileId&&i&&this.options.setProfileId(i)}setGlobalProperties(t){this.state.properties={...this.state.properties,...t}}clear(){this.state.profileId=void 0,this.options.removeProfileId&&this.options.removeProfileId()}setUserProperty(t,e,i=!0){}timestamp(){return new Date().toISOString()}};var d=class extends c{constructor(t){super(t),this.options.trackOutgoingLinks&&this.trackOutgoingLinks(),this.options.trackScreenViews&&this.trackScreenViews()}isServer(){return typeof document>"u"}getTimezone(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}trackOutgoingLinks(){this.isServer()||document.addEventListener("click",t=>{let e=t.target;if(e.tagName==="A"){let i=e.getAttribute("href");i?.startsWith("http")&&super.event("link_out",{href:i,text:e.innerText})}})}trackScreenViews(){if(this.isServer())return;let t=history.pushState;history.pushState=function(...s){let o=t.apply(this,s);return window.dispatchEvent(new Event("pushstate")),window.dispatchEvent(new Event("locationchange")),o};let e=history.replaceState;history.replaceState=function(...s){let o=e.apply(this,s);return window.dispatchEvent(new Event("replacestate")),window.dispatchEvent(new Event("locationchange")),o},window.addEventListener("popstate",()=>window.dispatchEvent(new Event("locationchange"))),this.options.hash?window.addEventListener("hashchange",()=>this.screenView()):window.addEventListener("locationchange",()=>this.screenView())}screenView(t){this.isServer()||super.event("screen_view",{...t??{},path:window.location.href,title:document.title,referrer:document.referrer})}};var r=document.currentScript;r&&(window.openpanel=new d({url:r?.getAttribute("data-url"),clientId:r?.getAttribute("data-client-id"),clientSecret:r?.getAttribute("data-client-secret"),trackOutgoingLinks:!!r?.getAttribute("data-track-outgoing-links"),trackScreenViews:!!r?.getAttribute("data-track-screen-views")}));})(); +//# sourceMappingURL=op.js.map \ No newline at end of file diff --git a/apps/test/public/op.js.map b/apps/test/public/op.js.map new file mode 100644 index 00000000..82f6866b --- /dev/null +++ b/apps/test/public/op.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../sdk/index.ts","../index.ts","../cdn.ts"],"sourcesContent":["import type { PostEventPayload } from '@mixan/types';\n\nexport interface MixanOptions {\n url: string;\n clientId: string;\n clientSecret?: string;\n verbose?: boolean;\n setProfileId?: (profileId: string) => void;\n getProfileId?: () => string | null | undefined;\n removeProfileId?: () => void;\n}\n\nexport interface MixanState {\n profileId?: string;\n properties: Record;\n}\n\nfunction createApi(_url: string, clientId: string, clientSecret?: string) {\n return function post(\n path: string,\n data: ReqBody,\n options?: RequestInit\n ): Promise {\n const url = `${_url}${path}`;\n let timer: ReturnType;\n const headers: Record = {\n 'mixan-client-id': clientId,\n 'Content-Type': 'application/json',\n };\n if (clientSecret) {\n headers['mixan-client-secret'] = clientSecret;\n }\n return new Promise((resolve) => {\n const wrappedFetch = (attempt: number) => {\n clearTimeout(timer);\n fetch(url, {\n headers,\n method: 'POST',\n body: JSON.stringify(data ?? {}),\n keepalive: true,\n ...(options ?? {}),\n })\n .then(async (res) => {\n if (res.status !== 200) {\n return retry(attempt, resolve);\n }\n\n const response = await res.json();\n\n if (!response) {\n return resolve(null);\n }\n\n resolve(response as ResBody);\n })\n .catch(() => {\n return retry(attempt, resolve);\n });\n };\n\n function retry(\n attempt: number,\n resolve: (value: ResBody | null) => void\n ) {\n if (attempt > 1) {\n return resolve(null);\n }\n\n timer = setTimeout(\n () => {\n wrappedFetch(attempt + 1);\n },\n Math.pow(2, attempt) * 500\n );\n }\n\n wrappedFetch(0);\n });\n };\n}\n\nexport class Mixan {\n public options: Options;\n private api: ReturnType;\n private state: MixanState = {\n properties: {},\n };\n\n constructor(options: Options) {\n this.options = options;\n this.api = createApi(options.url, options.clientId, options.clientSecret);\n }\n\n // Public\n\n public init(properties?: Record) {\n this.state.properties = properties ?? {};\n }\n\n // public setUser(payload: Omit) {\n // this.batcher.add({\n // type: 'update_profile',\n // payload: {\n // ...payload,\n // properties: payload.properties ?? {},\n // profileId: this.state.profileId,\n // },\n // });\n // }\n\n // public increment(name: string, value: number) {\n // this.batcher.add({\n // type: 'increment',\n // payload: {\n // name,\n // value,\n // profileId: this.state.profileId,\n // },\n // });\n // }\n\n // public decrement(name: string, value: number) {\n // this.batcher.add({\n // type: 'decrement',\n // payload: {\n // name,\n // value,\n // profileId: this.state.profileId,\n // },\n // });\n // }\n\n private getProfileId() {\n if (this.state.profileId) {\n return this.state.profileId;\n } else if (this.options.getProfileId) {\n this.state.profileId = this.options.getProfileId() || undefined;\n }\n }\n\n public async event(name: string, properties?: Record) {\n const profileId = await this.api('/event', {\n name,\n properties: {\n ...this.state.properties,\n ...(properties ?? {}),\n },\n timestamp: this.timestamp(),\n profileId: this.getProfileId(),\n });\n\n if (this.options.setProfileId && profileId) {\n this.options.setProfileId(profileId);\n }\n }\n\n public setGlobalProperties(properties: Record) {\n this.state.properties = {\n ...this.state.properties,\n ...properties,\n };\n }\n\n public clear() {\n this.state.profileId = undefined;\n if (this.options.removeProfileId) {\n this.options.removeProfileId();\n }\n }\n\n public setUserProperty(name: string, value: unknown, update = true) {\n // this.batcher.add({\n // type: 'set_profile_property',\n // payload: {\n // name,\n // value,\n // update,\n // profileId: this.state.profileId,\n // },\n // });\n }\n\n // Private\n\n private timestamp() {\n return new Date().toISOString();\n }\n}\n","import type { MixanOptions } from '@mixan/sdk';\nimport { Mixan } from '@mixan/sdk';\n\ntype MixanWebOptions = MixanOptions & {\n trackOutgoingLinks?: boolean;\n trackScreenViews?: boolean;\n hash?: boolean;\n};\n\nexport class MixanWeb extends Mixan {\n constructor(options: MixanWebOptions) {\n super(options);\n\n if (this.options.trackOutgoingLinks) {\n this.trackOutgoingLinks();\n }\n\n if (this.options.trackScreenViews) {\n this.trackScreenViews();\n }\n }\n\n private isServer() {\n return typeof document === 'undefined';\n }\n\n private getTimezone() {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone;\n } catch (e) {\n return undefined;\n }\n }\n\n public trackOutgoingLinks() {\n if (this.isServer()) {\n return;\n }\n\n document.addEventListener('click', (event) => {\n const target = event.target as HTMLElement;\n if (target.tagName === 'A') {\n const href = target.getAttribute('href');\n if (href?.startsWith('http')) {\n super.event('link_out', {\n href,\n text: target.innerText,\n });\n }\n }\n });\n }\n\n public trackScreenViews() {\n if (this.isServer()) {\n return;\n }\n\n const oldPushState = history.pushState;\n history.pushState = function pushState(...args) {\n const ret = oldPushState.apply(this, args);\n window.dispatchEvent(new Event('pushstate'));\n window.dispatchEvent(new Event('locationchange'));\n return ret;\n };\n\n const oldReplaceState = history.replaceState;\n history.replaceState = function replaceState(...args) {\n const ret = oldReplaceState.apply(this, args);\n window.dispatchEvent(new Event('replacestate'));\n window.dispatchEvent(new Event('locationchange'));\n return ret;\n };\n\n window.addEventListener('popstate', () =>\n window.dispatchEvent(new Event('locationchange'))\n );\n\n if (this.options.hash) {\n window.addEventListener('hashchange', () => this.screenView());\n } else {\n window.addEventListener('locationchange', () => this.screenView());\n }\n }\n\n public screenView(properties?: Record): void {\n if (this.isServer()) {\n return;\n }\n\n super.event('screen_view', {\n ...(properties ?? {}),\n path: window.location.href,\n title: document.title,\n referrer: document.referrer,\n });\n }\n}\n","// @ts-nocheck\n\nimport { MixanWeb as Openpanel } from './index';\n\nconst el = document.currentScript;\nif (el) {\n window.openpanel = new Openpanel({\n url: el?.getAttribute('data-url'),\n clientId: el?.getAttribute('data-client-id'),\n clientSecret: el?.getAttribute('data-client-secret'),\n trackOutgoingLinks: !!el?.getAttribute('data-track-outgoing-links'),\n trackScreenViews: !!el?.getAttribute('data-track-screen-views'),\n });\n}\n"],"mappings":"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","names":["createApi","_url","clientId","clientSecret","path","data","options","url","timer","headers","resolve","wrappedFetch","attempt","res","retry","response","Mixan","properties","name","profileId","value","update","MixanWeb","Mixan","options","event","target","href","oldPushState","args","ret","oldReplaceState","properties","el","MixanWeb"]} \ No newline at end of file diff --git a/apps/test/src/pages/_document.tsx b/apps/test/src/pages/_document.tsx index 32c9e027..4a2e17d0 100644 --- a/apps/test/src/pages/_document.tsx +++ b/apps/test/src/pages/_document.tsx @@ -6,10 +6,11 @@ export default function Document() {