@fanfare-io/fanfare-sdk-core 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.
Files changed (170) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +44 -0
  3. package/dist/adapters/google-analytics.d.ts +12 -0
  4. package/dist/adapters/google-analytics.js +1 -0
  5. package/dist/adapters/index.d.ts +9 -0
  6. package/dist/adapters/index.js +1 -0
  7. package/dist/adapters/types.d.ts +9 -0
  8. package/dist/appointments/appointment.module.d.ts +32 -0
  9. package/dist/appointments/appointment.module.js +1 -0
  10. package/dist/appointments/index.d.ts +2 -0
  11. package/dist/appointments/public.d.ts +1 -0
  12. package/dist/appointments/public.js +1 -0
  13. package/dist/appointments/types.d.ts +47 -0
  14. package/dist/auctions/auction.module.d.ts +58 -0
  15. package/dist/auctions/auction.module.js +1 -0
  16. package/dist/auctions/index.d.ts +5 -0
  17. package/dist/auctions/public.d.ts +5 -0
  18. package/dist/auctions/public.js +1 -0
  19. package/dist/auctions/types.d.ts +97 -0
  20. package/dist/auth/auth.module.d.ts +71 -0
  21. package/dist/auth/auth.module.js +1 -0
  22. package/dist/auth/index.d.ts +1 -0
  23. package/dist/auth/index.js +1 -0
  24. package/dist/auth/types.d.ts +112 -0
  25. package/dist/beacon/batching.d.ts +52 -0
  26. package/dist/beacon/batching.js +1 -0
  27. package/dist/beacon/beacon.module.d.ts +58 -0
  28. package/dist/beacon/beacon.module.js +1 -0
  29. package/dist/beacon/enrichment.d.ts +16 -0
  30. package/dist/beacon/enrichment.js +1 -0
  31. package/dist/beacon/index.d.ts +12 -0
  32. package/dist/beacon/public.d.ts +9 -0
  33. package/dist/beacon/public.js +1 -0
  34. package/dist/beacon/types.d.ts +103 -0
  35. package/dist/beacon/validation.d.ts +24 -0
  36. package/dist/beacon/validation.js +1 -0
  37. package/dist/challenges/challenge.module.d.ts +9 -0
  38. package/dist/challenges/challenge.module.js +1 -0
  39. package/dist/challenges/index.d.ts +3 -0
  40. package/dist/challenges/public.d.ts +9 -0
  41. package/dist/challenges/public.js +1 -0
  42. package/dist/challenges/types.d.ts +33 -0
  43. package/dist/config/index.d.ts +44 -0
  44. package/dist/config/index.js +1 -0
  45. package/dist/core/client.d.ts +15 -0
  46. package/dist/core/client.js +2 -0
  47. package/dist/core/errors.d.ts +175 -0
  48. package/dist/core/errors.js +1 -0
  49. package/dist/core/http.d.ts +87 -0
  50. package/dist/core/http.js +1 -0
  51. package/dist/core/logger.d.ts +46 -0
  52. package/dist/core/logger.js +1 -0
  53. package/dist/core/money.d.ts +10 -0
  54. package/dist/core/money.js +1 -0
  55. package/dist/core/parse-response.d.ts +62 -0
  56. package/dist/core/parse-response.js +1 -0
  57. package/dist/core/utils.d.ts +67 -0
  58. package/dist/core/utils.js +1 -0
  59. package/dist/draws/draw.module.d.ts +40 -0
  60. package/dist/draws/draw.module.js +1 -0
  61. package/dist/draws/index.d.ts +5 -0
  62. package/dist/draws/public.d.ts +6 -0
  63. package/dist/draws/public.js +1 -0
  64. package/dist/draws/types.d.ts +90 -0
  65. package/dist/draws/types.js +1 -0
  66. package/dist/errors.d.ts +5 -0
  67. package/dist/errors.js +1 -0
  68. package/dist/events.d.ts +5 -0
  69. package/dist/events.js +1 -0
  70. package/dist/experiences/distribution-monitor.runtime.d.ts +12 -0
  71. package/dist/experiences/distribution-monitor.runtime.js +1 -0
  72. package/dist/experiences/distribution-monitor.types.d.ts +62 -0
  73. package/dist/experiences/experience.module.d.ts +88 -0
  74. package/dist/experiences/experience.module.js +1 -0
  75. package/dist/experiences/index.d.ts +3 -0
  76. package/dist/experiences/index.js +1 -0
  77. package/dist/experiences/journey-contract.fixtures.d.ts +9 -0
  78. package/dist/experiences/journey-view.d.ts +22 -0
  79. package/dist/experiences/journey-view.js +1 -0
  80. package/dist/experiences/journey.d.ts +89 -0
  81. package/dist/experiences/journey.js +1 -0
  82. package/dist/experiences/journey.machine.d.ts +79 -0
  83. package/dist/experiences/journey.machine.js +1 -0
  84. package/dist/experiences/journey.types.d.ts +395 -0
  85. package/dist/experiences/public.d.ts +13 -0
  86. package/dist/experiences/public.js +1 -0
  87. package/dist/experiences/types.d.ts +161 -0
  88. package/dist/handoff/handoff.module.d.ts +184 -0
  89. package/dist/handoff/handoff.module.js +1 -0
  90. package/dist/handoff/index.d.ts +5 -0
  91. package/dist/handoff/index.js +1 -0
  92. package/dist/index.d.ts +11 -0
  93. package/dist/index.js +1 -0
  94. package/dist/internals.d.ts +11 -0
  95. package/dist/internals.js +1 -0
  96. package/dist/queues/index.d.ts +5 -0
  97. package/dist/queues/index.js +1 -0
  98. package/dist/queues/qualitative-bucket.d.ts +12 -0
  99. package/dist/queues/qualitative-bucket.js +1 -0
  100. package/dist/queues/queue.module.d.ts +42 -0
  101. package/dist/queues/queue.module.js +1 -0
  102. package/dist/queues/types.d.ts +112 -0
  103. package/dist/queues/types.js +1 -0
  104. package/dist/security/admission-proof.d.ts +15 -0
  105. package/dist/security/admission-proof.js +1 -0
  106. package/dist/state/capability-token-registry.d.ts +44 -0
  107. package/dist/state/capability-token-registry.js +1 -0
  108. package/dist/state/events.d.ts +342 -0
  109. package/dist/state/events.js +1 -0
  110. package/dist/state/store.d.ts +48 -0
  111. package/dist/state/store.js +1 -0
  112. package/dist/sync/broadcast-transport.d.ts +19 -0
  113. package/dist/sync/broadcast-transport.js +1 -0
  114. package/dist/sync/cross-tab-coordinator.d.ts +44 -0
  115. package/dist/sync/cross-tab-coordinator.js +1 -0
  116. package/dist/sync/index.d.ts +8 -0
  117. package/dist/sync/localstorage-transport.d.ts +32 -0
  118. package/dist/sync/localstorage-transport.js +1 -0
  119. package/dist/sync/protocol-transport.d.ts +51 -0
  120. package/dist/sync/protocol-transport.js +1 -0
  121. package/dist/sync/protocol.d.ts +254 -0
  122. package/dist/sync/protocol.js +1 -0
  123. package/dist/sync/recovery.d.ts +68 -0
  124. package/dist/sync/transport-factory.d.ts +16 -0
  125. package/dist/sync/transport-factory.js +1 -0
  126. package/dist/sync/transport-interface.d.ts +33 -0
  127. package/dist/sync/transport.d.ts +49 -0
  128. package/dist/sync/transport.js +1 -0
  129. package/dist/test-utils/harness-scenarios.d.ts +71 -0
  130. package/dist/test-utils/harness-scenarios.js +1 -0
  131. package/dist/test-utils/index.d.ts +12 -0
  132. package/dist/test-utils/index.js +1 -0
  133. package/dist/test-utils/mock-journey.d.ts +63 -0
  134. package/dist/test-utils/mock-journey.js +1 -0
  135. package/dist/test-utils/mock-sdk-vitest.d.ts +80 -0
  136. package/dist/test-utils/mock-sdk-vitest.js +1 -0
  137. package/dist/test-utils/mock-sdk.d.ts +22 -0
  138. package/dist/test-utils/mock-sdk.js +1 -0
  139. package/dist/test-utils/mock-server.d.ts +105 -0
  140. package/dist/test-utils/mock-server.js +1 -0
  141. package/dist/test-utils/sdk-factory.d.ts +3 -0
  142. package/dist/test-utils/sdk-factory.js +1 -0
  143. package/dist/test-utils/verification-scenarios.d.ts +43 -0
  144. package/dist/test-utils/verification-scenarios.js +1 -0
  145. package/dist/test-utils/verification-state.d.ts +1 -0
  146. package/dist/test-utils/verification-state.js +1 -0
  147. package/dist/theme.d.ts +8 -0
  148. package/dist/theme.js +1 -0
  149. package/dist/timed-releases/index.d.ts +6 -0
  150. package/dist/timed-releases/public.d.ts +6 -0
  151. package/dist/timed-releases/public.js +1 -0
  152. package/dist/timed-releases/timed-release.module.d.ts +31 -0
  153. package/dist/timed-releases/timed-release.module.js +1 -0
  154. package/dist/timed-releases/types.d.ts +70 -0
  155. package/dist/timed-releases/types.js +1 -0
  156. package/dist/types/distribution-type.d.ts +45 -0
  157. package/dist/types/index.d.ts +178 -0
  158. package/dist/utils/fingerprint.module.d.ts +27 -0
  159. package/dist/utils/fingerprint.module.js +1 -0
  160. package/dist/utils/index.d.ts +4 -0
  161. package/dist/version.d.ts +5 -0
  162. package/dist/version.js +1 -0
  163. package/dist/waitlists/index.d.ts +2 -0
  164. package/dist/waitlists/public.d.ts +6 -0
  165. package/dist/waitlists/public.js +1 -0
  166. package/dist/waitlists/types.d.ts +50 -0
  167. package/dist/waitlists/types.js +1 -0
  168. package/dist/waitlists/waitlist.module.d.ts +17 -0
  169. package/dist/waitlists/waitlist.module.js +1 -0
  170. package/package.json +205 -0
@@ -0,0 +1,71 @@
1
+ import { HttpClient } from '../core/http';
2
+ import { AuthenticatedSession, AuthModule, AuthStatus, ExternalExchange, GuestSession, LoginOptions, OtpRequest, OtpVerify, Session } from './types';
3
+ export declare class AuthenticationModule implements AuthModule {
4
+ private http;
5
+ private beaconHttp;
6
+ private logger;
7
+ private store;
8
+ private events;
9
+ private refreshTimer?;
10
+ private refreshPromise?;
11
+ constructor(http: HttpClient, beaconHttp: HttpClient);
12
+ /**
13
+ * Check current authentication status
14
+ */
15
+ check(): AuthStatus;
16
+ /**
17
+ * Create a guest session
18
+ */
19
+ guest(): Promise<GuestSession>;
20
+ /**
21
+ * Request OTP for email login
22
+ */
23
+ requestOtp(options: OtpRequest | string): Promise<void>;
24
+ /**
25
+ * Verify OTP and login
26
+ */
27
+ verifyOtp(options: OtpVerify | {
28
+ email: string;
29
+ code: string;
30
+ }): Promise<AuthenticatedSession>;
31
+ /**
32
+ * Exchange a one-time external auth code for an authenticated session
33
+ */
34
+ exchangeExternal(options: ExternalExchange | string): Promise<AuthenticatedSession>;
35
+ /**
36
+ * Login with email (combined OTP flow)
37
+ */
38
+ login(options: LoginOptions): Promise<void>;
39
+ /**
40
+ * Logout current session
41
+ */
42
+ logout(): Promise<void>;
43
+ /**
44
+ * Get current session
45
+ */
46
+ getSession(): Session | null;
47
+ /**
48
+ * Refresh the access token
49
+ */
50
+ refresh(): Promise<void>;
51
+ /**
52
+ * Internal refresh implementation
53
+ */
54
+ private _doRefresh;
55
+ /**
56
+ * Handle 401 responses
57
+ */
58
+ private handleUnauthorized;
59
+ /**
60
+ * Schedule token refresh 5 minutes before expiry
61
+ */
62
+ private scheduleTokenRefresh;
63
+ /**
64
+ * Cancel scheduled token refresh
65
+ */
66
+ private cancelTokenRefresh;
67
+ /**
68
+ * Clean up resources
69
+ */
70
+ destroy(): void;
71
+ }
@@ -0,0 +1 @@
1
+ import{getLogger as e}from"../core/logger.js";import{getEventBus as t}from"../state/events.js";import{getSDKStore as s}from"../state/store.js";class r{constructor(r,o){this.logger=e(),this.store=s(),this.events=t(),this.http=r,this.beaconHttp=o,this.http.updateConfig({...this.http.getConfig(),onUnauthorized:async()=>{await this.handleUnauthorized()}});const i=this.store.beaconToken;i&&this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":i}})}check(){const e=this.store.session;return e?{isAuthenticated:!0,session:e,isGuest:"guest"===e.type}:{isAuthenticated:!1}}async guest(){try{this.logger.info("Creating guest session");const{session:e,accessToken:t,refreshToken:s,beaconToken:r}=await this.http.post("/auth/guest");return this.store.setSession(e),this.store.setRefreshToken(s??null),this.store.setBeaconToken(r),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${t}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":r}}),this.scheduleTokenRefresh(e.expiresAt),this.events.emit("auth:authenticated",{session:e,isNew:!0}),e}catch(e){const t=e instanceof Error?e:new Error(String(e));throw this.logger.error("Failed to create guest session",t),this.events.emit("auth:error",{error:t,context:"guest"}),t}}async requestOtp(e){try{this.logger.info("Requesting OTP");const t="string"==typeof e?{email:e}:e;await this.http.post("/auth/otp/request",t)}catch(t){const e=t instanceof Error?t:new Error(String(t));throw this.logger.error("Failed to request OTP",e),this.events.emit("auth:error",{error:e,context:"requestOtp"}),e}}async verifyOtp(e){try{const t=e;this.logger.info("Verifying OTP",{email:t.email,phone:t.phone});const{session:s,accessToken:r,refreshToken:o,beaconToken:i}=await this.http.post("/auth/otp/verify",t);return this.store.setSession(s),this.store.setRefreshToken(o??null),this.store.setBeaconToken(i),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${r}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":i}}),this.scheduleTokenRefresh(s.expiresAt),this.events.emit("auth:authenticated",{session:s,isNew:!0}),s}catch(t){const e=t instanceof Error?t:new Error(String(t));throw this.logger.error("Failed to verify OTP",e),this.events.emit("auth:error",{error:e,context:"verifyOtp"}),e}}async exchangeExternal(e){try{const t="string"==typeof e?{exchangeCode:e}:e;this.logger.info("Exchanging external auth code");const{session:s,accessToken:r,refreshToken:o,beaconToken:i}=await this.http.post("/auth/external/exchange",t);return this.store.setSession(s),this.store.setRefreshToken(o??null),this.store.setBeaconToken(i),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${r}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":i}}),this.scheduleTokenRefresh(s.expiresAt),this.events.emit("auth:authenticated",{session:s,isNew:!0}),s}catch(t){const e=t instanceof Error?t:new Error(String(t));throw this.logger.error("Failed to exchange external auth code",e),this.events.emit("auth:error",{error:e,context:"exchangeExternal"}),e}}async login(e){await this.requestOtp({email:e.email})}async logout(){try{if(!this.store.session)return;this.cancelTokenRefresh(),await this.http.post("/auth/logout",{},{skipUnauthorizedHandler:!0}).catch(()=>{}),this.store.clearAll();const e=this.http.getConfig(),{Authorization:t,...s}=e.headers||{};this.http.updateConfig({...e,headers:s});const r=this.beaconHttp.getConfig(),{"X-Beacon-Token":o,...i}=r.headers||{};this.beaconHttp.updateConfig({...r,headers:i}),this.events.emit("auth:logout",{reason:"user"})}catch(e){const t=e instanceof Error?e:new Error(String(e));throw this.logger.error("Error during logout",t),this.store.clearAll(),t}}getSession(){return this.store.session}async refresh(){return this.refreshPromise?(this.logger.debug("Refresh already in progress, returning existing promise"),this.refreshPromise):(this.refreshPromise=this._doRefresh().finally(()=>{this.refreshPromise=void 0}),this.refreshPromise)}async _doRefresh(){try{this.logger.info("Refreshing access token");const{accessToken:e,refreshToken:t,beaconToken:s}=await this.http.post("/auth/refresh",{});this.store.setRefreshToken(t??null),this.store.setBeaconToken(s),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${e}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":s}});const r=this.store.session;r&&r.expiresAt&&this.scheduleTokenRefresh(r.expiresAt),this.store.session&&this.events.emit("auth:refreshed",{session:this.store.session})}catch(e){const t=e instanceof Error?e:new Error(String(e));throw this.logger.error("Failed to refresh token",t),await this.logout(),t}}async handleUnauthorized(){this.logger.warn("Received 401, attempting to refresh token");try{await this.refresh()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.logger.error("Failed to handle 401",t),this.events.emit("auth:error",{error:new Error("Session expired"),context:"unauthorized"})}}scheduleTokenRefresh(e){this.cancelTokenRefresh();const t=Date.now(),s=new Date(e).getTime()-3e5,r=s-t;r<=0?this.refresh().catch(e=>{this.logger.error("Failed to refresh expired token",e)}):(this.logger.debug("Scheduling token refresh",{expiresAt:new Date(e).toISOString(),refreshAt:new Date(s).toISOString(),delayMs:r}),this.refreshTimer=setTimeout(()=>{this.refresh().catch(e=>{this.logger.error("Scheduled token refresh failed",e)})},r))}cancelTokenRefresh(){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=void 0,this.logger.debug("Cancelled token refresh timer"))}destroy(){this.cancelTokenRefresh(),this.refreshPromise=void 0}}export{r as AuthenticationModule};
@@ -0,0 +1 @@
1
+ export type { AuthModule, AuthStatus, AuthenticatedSession, Consumer, ExternalExchange, GuestSession, LoginOptions, OtpRequest, OtpVerify, Session, } from './types';
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Authentication type definitions
3
+ */
4
+ /**
5
+ * Authentication module interface
6
+ */
7
+ export interface AuthModule {
8
+ /**
9
+ * Check current authentication status
10
+ */
11
+ check(): AuthStatus;
12
+ /**
13
+ * Create a guest session
14
+ */
15
+ guest(): Promise<GuestSession>;
16
+ /**
17
+ * Request OTP for email authentication
18
+ */
19
+ requestOtp(options: OtpRequest | string): Promise<void>;
20
+ /**
21
+ * Verify OTP and authenticate
22
+ */
23
+ verifyOtp(options: OtpVerify | {
24
+ email: string;
25
+ code: string;
26
+ }): Promise<AuthenticatedSession>;
27
+ /**
28
+ * Exchange a one-time external auth code for an authenticated session
29
+ */
30
+ exchangeExternal(options: ExternalExchange | string): Promise<AuthenticatedSession>;
31
+ /**
32
+ * Login with email (combined OTP flow)
33
+ */
34
+ login(options: LoginOptions): Promise<void>;
35
+ /**
36
+ * Logout and clear session
37
+ */
38
+ logout(): Promise<void>;
39
+ /**
40
+ * Get current session
41
+ */
42
+ getSession(): Session | null;
43
+ /**
44
+ * Refresh the access token
45
+ */
46
+ refresh(): Promise<void>;
47
+ }
48
+ /**
49
+ * Login options
50
+ */
51
+ export interface LoginOptions {
52
+ email: string;
53
+ }
54
+ export interface OtpRequest {
55
+ email?: string;
56
+ phone?: string;
57
+ defaultCountry?: string;
58
+ name?: string;
59
+ }
60
+ export interface OtpVerify {
61
+ email?: string;
62
+ phone?: string;
63
+ code: string;
64
+ defaultCountry?: string;
65
+ }
66
+ export interface ExternalExchange {
67
+ exchangeCode: string;
68
+ }
69
+ /**
70
+ * Current authentication status
71
+ */
72
+ export interface AuthStatus {
73
+ isAuthenticated: boolean;
74
+ isGuest?: boolean;
75
+ session?: Session;
76
+ }
77
+ /**
78
+ * Base session interface
79
+ */
80
+ export interface Session {
81
+ type: "guest" | "authenticated";
82
+ consumerId: string;
83
+ email?: string;
84
+ phone?: string;
85
+ expiresAt: string;
86
+ deviceFingerprint?: string;
87
+ }
88
+ /**
89
+ * Guest session
90
+ */
91
+ export interface GuestSession extends Session {
92
+ type: "guest";
93
+ guestId: string;
94
+ email: undefined;
95
+ }
96
+ /**
97
+ * Authenticated session
98
+ */
99
+ export interface AuthenticatedSession extends Session {
100
+ type: "authenticated";
101
+ email?: string;
102
+ phone?: string;
103
+ }
104
+ /**
105
+ * Consumer information
106
+ */
107
+ export interface Consumer {
108
+ id: string;
109
+ email?: string;
110
+ createdAt: string;
111
+ metadata?: Record<string, unknown>;
112
+ }
@@ -0,0 +1,52 @@
1
+ import { BeaconConfig, BeaconEventInput } from './types';
2
+ /**
3
+ * Smart event batcher with requestIdleCallback and visibility change support
4
+ *
5
+ * Features:
6
+ * - Batches events in memory
7
+ * - Auto-flushes when batch size threshold reached
8
+ * - Auto-flushes after time threshold
9
+ * - Uses requestIdleCallback for smart batching during idle time
10
+ * - Auto-flushes on visibility change (tab hidden)
11
+ * - Auto-flushes on page unload (beforeunload/pagehide)
12
+ *
13
+ * @internal
14
+ */
15
+ export declare class EventBatcher {
16
+ private queue;
17
+ private flushTimer;
18
+ private config;
19
+ private flushCallback;
20
+ private visibilityHandler;
21
+ private beforeUnloadHandler;
22
+ private pageHideHandler;
23
+ constructor(config: BeaconConfig, flushCallback: (events: BeaconEventInput[]) => Promise<void>);
24
+ /**
25
+ * Add an event to the batch queue
26
+ *
27
+ * @param event - Event to queue
28
+ */
29
+ add(event: BeaconEventInput): void;
30
+ /**
31
+ * Schedule a flush using requestIdleCallback for smart batching
32
+ *
33
+ * @private
34
+ */
35
+ private scheduleFlush;
36
+ /**
37
+ * Flush all pending events immediately
38
+ *
39
+ * @returns Promise that resolves when flush completes
40
+ */
41
+ flush(): Promise<void>;
42
+ /**
43
+ * Set up event listeners for auto-flush behavior
44
+ *
45
+ * @private
46
+ */
47
+ private setupListeners;
48
+ /**
49
+ * Remove event listeners and flush pending events
50
+ */
51
+ destroy(): void;
52
+ }
@@ -0,0 +1 @@
1
+ class e{constructor(e,i){this.queue=[],this.flushTimer=null,this.visibilityHandler=null,this.beforeUnloadHandler=null,this.pageHideHandler=null,this.config={maxBatchSize:e.maxBatchSize??50,maxBatchTime:e.maxBatchTime??5e3,flushOnVisibilityChange:e.flushOnVisibilityChange??!0,flushOnUnload:e.flushOnUnload??!0,disablePageContext:e.disablePageContext??!1,disableMarketingParams:e.disableMarketingParams??!1},this.flushCallback=i,this.setupListeners()}add(e){this.queue.push(e),this.queue.length>=this.config.maxBatchSize?this.flush():this.flushTimer||this.scheduleFlush()}scheduleFlush(){"undefined"!=typeof requestIdleCallback?requestIdleCallback(()=>{this.flushTimer=setTimeout(()=>this.flush(),this.config.maxBatchTime)},{timeout:this.config.maxBatchTime}):this.flushTimer=setTimeout(()=>this.flush(),this.config.maxBatchTime)}async flush(){if(this.flushTimer&&(clearTimeout(this.flushTimer),this.flushTimer=null),0===this.queue.length)return;const e=[...this.queue];this.queue=[],await this.flushCallback(e)}setupListeners(){"undefined"!=typeof window&&"undefined"!=typeof document&&(this.config.flushOnVisibilityChange&&(this.visibilityHandler=()=>{"hidden"===document.visibilityState&&this.flush()},document.addEventListener("visibilitychange",this.visibilityHandler)),this.config.flushOnUnload&&(this.pageHideHandler=()=>{this.flush()},window.addEventListener("pagehide",this.pageHideHandler),this.beforeUnloadHandler=()=>{this.flush()},window.addEventListener("beforeunload",this.beforeUnloadHandler)))}destroy(){"undefined"!=typeof window&&"undefined"!=typeof document&&(this.visibilityHandler&&document.removeEventListener("visibilitychange",this.visibilityHandler),this.beforeUnloadHandler&&window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.pageHideHandler&&window.removeEventListener("pagehide",this.pageHideHandler)),this.flushTimer&&clearTimeout(this.flushTimer),this.flush()}}export{e as EventBatcher};
@@ -0,0 +1,58 @@
1
+ import { HttpClient } from '../core/http';
2
+ import { BeaconConfig, BeaconEventInput, BeaconModule } from './types';
3
+ /**
4
+ * Beacon tracking module for client-side event tracking
5
+ *
6
+ * @internal
7
+ */
8
+ export declare class BeaconTrackingModule implements BeaconModule {
9
+ private http;
10
+ private logger;
11
+ private events;
12
+ private store;
13
+ private batcher;
14
+ private config;
15
+ constructor(http: HttpClient, config?: BeaconConfig);
16
+ /**
17
+ * Track a single event
18
+ *
19
+ * Events are enriched with auto-generated fields and browser context,
20
+ * then queued for batching. The batch is sent when size/time thresholds
21
+ * are reached or on visibility change/page unload.
22
+ *
23
+ * @param event - Event to track
24
+ */
25
+ track(event: BeaconEventInput): Promise<void>;
26
+ /**
27
+ * Track multiple events in a batch
28
+ *
29
+ * Events are enriched and validated, then sent immediately
30
+ * (bypassing the batching queue).
31
+ *
32
+ * @param events - Events to track
33
+ */
34
+ trackBatch(events: BeaconEventInput[]): Promise<void>;
35
+ /**
36
+ * Flush pending events immediately
37
+ *
38
+ * Forces all queued events to be sent to the beacon endpoint.
39
+ */
40
+ flush(): Promise<void>;
41
+ /**
42
+ * Send events to beacon endpoint
43
+ *
44
+ * Uses single event endpoint for one event, batch endpoint for multiple.
45
+ * Gracefully degrades on errors (tracking failures don't break app).
46
+ *
47
+ * @param events - Events to send
48
+ * @returns true if sent successfully, false if failed
49
+ * @private
50
+ */
51
+ private sendBatch;
52
+ /**
53
+ * Cleanup and destroy the module
54
+ *
55
+ * Removes event listeners and flushes pending events.
56
+ */
57
+ destroy(): void;
58
+ }
@@ -0,0 +1 @@
1
+ import{getLogger as t}from"../core/logger.js";import{getEventBus as e}from"../state/events.js";import{getSDKStore as r}from"../state/store.js";import{EventBatcher as s}from"./batching.js";import{enrichEvent as n}from"./enrichment.js";import{validateEvent as o}from"./validation.js";class i{constructor(n,o={}){this.logger=t(),this.events=e(),this.store=r(),this.http=n,this.config=o,this.batcher=new s(o,async t=>{await this.sendBatch(t)}),this.logger.debug("Beacon tracking module initialized",{config:o})}async track(t){try{const e=n(t,this.config);o(e),this.batcher.add(e),this.events.emit("beacon:queued",{event:e}),this.logger.debug("Event queued for tracking",{event:e})}catch(e){throw this.logger.error("Failed to queue event",{error:e,event:t}),this.events.emit("beacon:error",{error:e,event:t}),e}}async trackBatch(t){try{const e=t.map(t=>{const e=n(t,this.config);return o(e),e}),r=await this.sendBatch(e);this.logger[r?"info":"warn"](r?"Batch tracked successfully":"Batch queued but send failed",{count:e.length})}catch(e){throw this.logger.error("Failed to track batch",{error:e,count:t.length}),this.events.emit("beacon:error",{error:e,events:t}),e}}async flush(){await this.batcher.flush()}async sendBatch(t){if(0===t.length)return!0;try{const e=1===t.length?"/events":"/events/batch",r=1===t.length?t[0]:t,s=this.http.getConfig().headers?.["X-Organization-Id"],n="string"==typeof s?s:void 0,o=this.store.session?.consumerId,i=t=>({...t,...n?{organizationId:n}:{},...o?{consumerId:o}:{}}),a=Array.isArray(r)?r.map(i):i(r);return await this.http.post(e,a),this.events.emit("beacon:sent",{count:t.length}),this.logger.info("Events sent to beacon",{count:t.length}),!0}catch(e){return this.logger.error("Failed to send events",{error:e,count:t.length}),this.events.emit("beacon:error",{error:e,events:t}),!1}}destroy(){this.batcher.destroy()}}export{i as BeaconTrackingModule};
@@ -0,0 +1,16 @@
1
+ import { BeaconConfig, BeaconEventInput } from './types';
2
+ /**
3
+ * Auto-enrich beacon event with generated fields and browser context
4
+ *
5
+ * Adds the following if not already present:
6
+ * - eventId (UUID v7 for time-based sorting)
7
+ * - eventTimestamp (ISO 8601, canonical event time)
8
+ * - eventProperties (empty object)
9
+ * - Page context (url, title, referrer)
10
+ * - Marketing parameters (UTM params from URL)
11
+ *
12
+ * @param event - Original event to enrich
13
+ * @param config - Beacon configuration
14
+ * @returns Enriched event
15
+ */
16
+ export declare function enrichEvent(event: BeaconEventInput, config: BeaconConfig): BeaconEventInput;
@@ -0,0 +1 @@
1
+ import{v7 as e}from"uuid";const t=1e6;function n(n,r){const o={...n};if(o.eventId||(o.eventId=e()),!o.eventTimestamp){const e=function(){const e=Date.now(),n=new Date(e).toISOString(),r=`${e}${function(){if("undefined"==typeof performance||"function"!=typeof performance.now)return"000000";return Math.floor(performance.now()%1*t).toString().padStart(6,"0")}()}`;return{eventTime:n,eventTimeNs:r}}();o.eventTimestamp=e.eventTime,o.eventTimestampNs=e.eventTimeNs}if(o.eventProperties||(o.eventProperties={}),!r.disablePageContext&&"undefined"!=typeof window&&(o.pageUrl||(o.pageUrl=window.location.href),o.pagePath||(o.pagePath=window.location.pathname),o.pageTitle||"undefined"==typeof document||(o.pageTitle=document.title),"undefined"!=typeof document&&document.referrer&&(o.referrerUrl||(o.referrerUrl=document.referrer),!o.referringDomain)))try{const e=new URL(document.referrer);o.referringDomain=e.hostname}catch{}if(!r.disableMarketingParams&&"undefined"!=typeof window)try{const e=new URLSearchParams(window.location.search);if(!o.utmSource){const t=e.get("utm_source");t&&(o.utmSource=t)}if(!o.utmMedium){const t=e.get("utm_medium");t&&(o.utmMedium=t)}if(!o.utmCampaign){const t=e.get("utm_campaign");t&&(o.utmCampaign=t)}if(!o.utmTerm){const t=e.get("utm_term");t&&(o.utmTerm=t)}if(!o.utmContent){const t=e.get("utm_content");t&&(o.utmContent=t)}}catch{}return o}export{n as enrichEvent};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Beacon Event Tracking Module
3
+ *
4
+ * Provides client-side event tracking with automatic enrichment,
5
+ * smart batching, and lightweight validation.
6
+ *
7
+ * @module beacon
8
+ */
9
+ export { BeaconTrackingModule } from './beacon.module';
10
+ export { enrichEvent } from './enrichment';
11
+ export type { BeaconConfig, BeaconEvent, BeaconEventInput, BeaconModule } from './types';
12
+ export { ValidationError, validateEvent } from './validation';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Public beacon type surface.
3
+ *
4
+ * Mirrors exactly the beacon symbols on the root barrel — the runtime
5
+ * `BeaconTrackingModule`/`enrichEvent`/`validateEvent` are deliberately not
6
+ * re-exported here. `ValidationError` is kept because it is on the root barrel.
7
+ */
8
+ export type { BeaconConfig, BeaconEvent, BeaconEventInput, BeaconModule } from './types';
9
+ export { ValidationError } from './validation';
@@ -0,0 +1 @@
1
+ import{ValidationError as o}from"./validation.js";export{o as ValidationError};
@@ -0,0 +1,103 @@
1
+ import { BeaconEvent, BeaconEventInput } from '@fanfare-io/fanfare-sdk-contracts/beacon';
2
+ export type { BeaconEvent, BeaconEventInput };
3
+ /**
4
+ * Beacon tracking module interface
5
+ */
6
+ export interface BeaconModule {
7
+ /**
8
+ * Track a single event
9
+ *
10
+ * Events are automatically enriched with eventId, timestamp, page context,
11
+ * and marketing parameters before being queued for batch sending.
12
+ *
13
+ * @param event - Event to track
14
+ * @returns Promise that resolves when event is queued (not sent)
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * await fanfare.beacon.track({
19
+ * eventName: "pageView",
20
+ * eventProperties: {
21
+ * pageType: "home"
22
+ * }
23
+ * });
24
+ * ```
25
+ */
26
+ track(event: BeaconEventInput): Promise<void>;
27
+ /**
28
+ * Track multiple events in a batch
29
+ *
30
+ * Sends events immediately, bypassing the batching queue.
31
+ * Useful for tracking multiple related events atomically.
32
+ *
33
+ * @param events - Events to track
34
+ * @returns Promise that resolves when batch is sent
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * await fanfare.beacon.trackBatch([
39
+ * { eventName: "pageView" },
40
+ * { eventName: "productImpression", eventProperties: { productId: "123" } }
41
+ * ]);
42
+ * ```
43
+ */
44
+ trackBatch(events: BeaconEventInput[]): Promise<void>;
45
+ /**
46
+ * Flush pending events immediately
47
+ *
48
+ * Forces all queued events to be sent to the beacon endpoint.
49
+ * Automatically called on visibility change and page unload.
50
+ *
51
+ * @returns Promise that resolves when flush completes
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * await fanfare.beacon.flush();
56
+ * ```
57
+ */
58
+ flush(): Promise<void>;
59
+ }
60
+ /**
61
+ * Beacon module configuration
62
+ */
63
+ export interface BeaconConfig {
64
+ /**
65
+ * Base URL for beacon API
66
+ * When not specified, uses the environment-specific beacon URL
67
+ * @example "http://localhost:4803" (development)
68
+ * @example "https://beacon.fanfare.io" (production)
69
+ */
70
+ baseUrl?: string;
71
+ /**
72
+ * Maximum events to batch before auto-flush
73
+ * @default 50
74
+ */
75
+ maxBatchSize?: number;
76
+ /**
77
+ * Maximum time (ms) to wait before auto-flush
78
+ * @default 5000
79
+ */
80
+ maxBatchTime?: number;
81
+ /**
82
+ * Enable auto-flush on visibility change (tab hidden)
83
+ * @default true
84
+ */
85
+ flushOnVisibilityChange?: boolean;
86
+ /**
87
+ * Enable auto-flush on page unload
88
+ * @default true
89
+ */
90
+ flushOnUnload?: boolean;
91
+ /**
92
+ * Disable automatic page context enrichment
93
+ * When true, pageUrl, pageTitle, referrer, etc. will not be auto-populated
94
+ * @default false
95
+ */
96
+ disablePageContext?: boolean;
97
+ /**
98
+ * Disable automatic UTM parameter extraction
99
+ * When true, utm_* fields will not be auto-extracted from URL
100
+ * @default false
101
+ */
102
+ disableMarketingParams?: boolean;
103
+ }
@@ -0,0 +1,24 @@
1
+ import { BeaconEventName } from '@fanfare-io/fanfare-sdk-contracts/beacon/names';
2
+ import { BeaconEventInput } from './types';
3
+ /**
4
+ * Validation error for beacon events
5
+ */
6
+ export declare class ValidationError extends Error {
7
+ constructor(message: string);
8
+ }
9
+ /**
10
+ * Lightweight validation for beacon events
11
+ *
12
+ * Only validates essential fields:
13
+ * - eventName is required and non-empty
14
+ * - eventId exists after enrichment
15
+ * - eventTimestamp exists after enrichment
16
+ * - eventProperties is an object if provided
17
+ *
18
+ * Server-side validation handles comprehensive validation.
19
+ *
20
+ * @param event - Event to validate
21
+ * @throws {ValidationError} If validation fails
22
+ */
23
+ export declare function validateEvent(event: BeaconEventInput): void;
24
+ export declare function isBeaconEventName(eventName: string): eventName is BeaconEventName;
@@ -0,0 +1 @@
1
+ import{BeaconEventNames as e}from"@fanfare-io/fanfare-sdk-contracts/beacon/names";const t=new Set(e);class n extends Error{constructor(e){super(e),this.name="ValidationError"}}function r(r){if(!r.eventName||"string"!=typeof r.eventName)throw new n("eventName is required and must be a string");if(0===r.eventName.trim().length)throw new n("eventName cannot be empty");if(!t.has(r.eventName))throw new n(`eventName must be one of: ${e.join(", ")}`);if(!r.eventId)throw new n("eventId is required");if(!r.eventTimestamp)throw new n("eventTimestamp is required");if(void 0!==r.eventProperties&&"object"!=typeof r.eventProperties)throw new n("eventProperties must be an object");if(null===r.eventProperties)throw new n("eventProperties cannot be null")}export{n as ValidationError,r as validateEvent};
@@ -0,0 +1,9 @@
1
+ import { HttpClient } from '../core/http';
2
+ import { ChallengeInitiateRequest, ChallengeInitiateResponse, ChallengeModule, ChallengeVerifyRequest, ChallengeVerifyResponse } from './types';
3
+ export declare class ChallengeManagementModule implements ChallengeModule {
4
+ private http;
5
+ private logger;
6
+ constructor(http: HttpClient);
7
+ initiate(input?: ChallengeInitiateRequest): Promise<ChallengeInitiateResponse>;
8
+ verify(input: ChallengeVerifyRequest): Promise<ChallengeVerifyResponse>;
9
+ }
@@ -0,0 +1 @@
1
+ import{getLogger as t}from"../core/logger.js";class r{constructor(r){this.logger=t(),this.http=r}async initiate(t={}){try{return await this.http.post("/challenges/initiate",t)}catch(r){throw this.logger.error("Failed to initiate challenge",{error:r}),r}}async verify(t){try{return await this.http.post("/challenges/verify",t)}catch(r){throw this.logger.error("Failed to verify challenge",{error:r}),r}}}export{r as ChallengeManagementModule};
@@ -0,0 +1,3 @@
1
+ export { ChallengeManagementModule } from './challenge.module';
2
+ export { ChallengeProvider, ChallengePurpose, ChallengeType } from './types';
3
+ export type { BotMitigationState, ChallengeInitiateRequest, ChallengeInitiateResponse, ChallengeModule, ChallengeVerifyRequest, ChallengeVerifyResponse, RoutingChallenge, } from './types';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Public challenge type surface.
3
+ *
4
+ * Mirrors exactly the challenge symbols on the root barrel — the runtime
5
+ * `ChallengeManagementModule` from `challenge.module` is deliberately not
6
+ * re-exported here.
7
+ */
8
+ export { ChallengeProvider, ChallengePurpose, ChallengeType } from './types';
9
+ export type { BotMitigationState, ChallengeInitiateRequest, ChallengeInitiateResponse, ChallengeModule, ChallengeVerifyRequest, ChallengeVerifyResponse, RoutingChallenge, } from './types';
@@ -0,0 +1 @@
1
+ import{ChallengeProvider as a,ChallengePurpose as r,ChallengeType as e}from"@fanfare-io/fanfare-sdk-contracts/challenges";export{a as ChallengeProvider,r as ChallengePurpose,e as ChallengeType};
@@ -0,0 +1,33 @@
1
+ import { BotMitigationState, ChallengePurpose, ChallengeType, RoutingChallenge } from '@fanfare-io/fanfare-sdk-contracts/challenges';
2
+ export { ChallengeProvider, ChallengePurpose, ChallengeType } from '@fanfare-io/fanfare-sdk-contracts/challenges';
3
+ export type { BotMitigationState, RoutingChallenge } from '@fanfare-io/fanfare-sdk-contracts/challenges';
4
+ export type ChallengeInitiateRequest = {
5
+ purpose?: ChallengePurpose;
6
+ type?: ChallengeType;
7
+ };
8
+ export type ChallengeInitiateResponse = {
9
+ challenge?: RoutingChallenge;
10
+ botMitigation?: BotMitigationState;
11
+ };
12
+ export type ChallengeVerifyRequest = {
13
+ challengeId: string;
14
+ token: string;
15
+ };
16
+ export type ChallengeVerifyResponse = {
17
+ status: "passed";
18
+ passedUntil: string;
19
+ } | {
20
+ status: "failed";
21
+ attemptsRemaining: number;
22
+ message?: string;
23
+ errorCodes?: string[];
24
+ } | {
25
+ status: "blocked";
26
+ blockedUntil: string;
27
+ message: string;
28
+ errorCodes?: string[];
29
+ };
30
+ export interface ChallengeModule {
31
+ initiate(input?: ChallengeInitiateRequest): Promise<ChallengeInitiateResponse>;
32
+ verify(input: ChallengeVerifyRequest): Promise<ChallengeVerifyResponse>;
33
+ }
@@ -0,0 +1,44 @@
1
+ import { FanfareConfig } from '../types';
2
+ /**
3
+ * API URLs by environment
4
+ */
5
+ export declare const API_URLS: {
6
+ readonly production: "https://consumer.fanfare.io/api";
7
+ readonly development: "http://localhost:4802";
8
+ };
9
+ /**
10
+ * Beacon API URLs by environment
11
+ */
12
+ export declare const BEACON_URLS: {
13
+ readonly production: "https://beacon.fanfare.io";
14
+ readonly development: "http://localhost:4803";
15
+ };
16
+ /**
17
+ * Default configuration values
18
+ */
19
+ export declare const DEFAULT_CONFIG: Partial<FanfareConfig>;
20
+ /**
21
+ * Configuration manager
22
+ */
23
+ export declare class Config {
24
+ private readonly config;
25
+ constructor(userConfig: FanfareConfig);
26
+ /**
27
+ * Get the API URL for the current environment
28
+ */
29
+ get apiUrl(): string;
30
+ /**
31
+ * Get the Beacon API URL for the current environment
32
+ */
33
+ get beaconUrl(): string;
34
+ /**
35
+ * Get the full configuration
36
+ */
37
+ get(): FanfareConfig & {
38
+ apiUrl?: string;
39
+ };
40
+ /**
41
+ * Get a specific configuration value
42
+ */
43
+ getValue<K extends keyof FanfareConfig>(key: K): K extends "apiUrl" ? string | undefined : Required<FanfareConfig>[K];
44
+ }
@@ -0,0 +1 @@
1
+ import{createError as i}from"../core/errors.js";const n={production:"https://consumer.fanfare.io/api",development:"http://localhost:4802"},o={production:"https://beacon.fanfare.io",development:"http://localhost:4803"},t=/* @__PURE__ */new Set(["https://consumer.fanfare.io"]),e=/* @__PURE__ */new Set(["https://beacon.fanfare.io"]),r={environment:"production",auth:{persistSession:!0,sessionDuration:3600},logging:{level:"error"}};class a{constructor(n){if(this.config={organizationId:n.organizationId,publishableKey:n.publishableKey,environment:n.environment??r.environment??"production",apiUrl:n.apiUrl,credentials:n.credentials??"include",debug:n.debug??!1,auth:{...r.auth,...n.auth},logging:{...r.logging,...n.logging},sync:n.sync,beacon:n.beacon,features:{fingerprinting:n.features?.fingerprinting??!0}},!this.config.organizationId)throw i.invalidConfig("organizationId is required");if(!this.config.publishableKey)throw i.invalidConfig("publishableKey is required");"production"===this.config.environment&&(s("apiUrl",this.apiUrl,t),s("beacon.baseUrl",this.beaconUrl,e))}get apiUrl(){return this.config.apiUrl||n[this.config.environment]}get beaconUrl(){return this.config.beacon?.baseUrl||o[this.config.environment]}get(){return this.config}getValue(i){return this.config[i]}}function s(n,o,t){let e;try{e=new URL(o)}catch{throw i.invalidConfig(`${n} must be a valid URL in production`)}if("https:"!==e.protocol)throw i.invalidConfig(`${n} must use HTTPS in production`);if(!t.has(e.origin))throw i.invalidConfig(`${n} must use a trusted Fanfare destination in production`)}export{n as API_URLS,o as BEACON_URLS,a as Config,r as DEFAULT_CONFIG};
@@ -0,0 +1,15 @@
1
+ import { FanfareConfig, FanfareSDK } from '../types';
2
+ /** @internal Reset active instance guard (for testing only) */
3
+ export declare function resetActiveInstance(): void;
4
+ /**
5
+ * Initialize the Fanfare SDK
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const fanfare = await Fanfare.init({
10
+ * organizationId: 'org_123',
11
+ * publishableKey: 'pk_live_xyz789'
12
+ * });
13
+ * ```
14
+ */
15
+ export declare function init(config: FanfareConfig): Promise<FanfareSDK>;