krisspy-sdk 0.4.0 → 0.4.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/dist/index.d.mts +102 -1
- package/dist/index.d.ts +102 -1
- package/dist/index.js +208 -0
- package/dist/index.mjs +207 -0
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -699,6 +699,89 @@ declare class KrisspyRealtime {
|
|
|
699
699
|
private log;
|
|
700
700
|
}
|
|
701
701
|
|
|
702
|
+
/**
|
|
703
|
+
* Krisspy Analytics - Lightweight event tracking module
|
|
704
|
+
*
|
|
705
|
+
* Tracks page views, sessions, custom events. Sends batched events
|
|
706
|
+
* to the analytics ingest endpoint. Works with SPAs (intercepts pushState).
|
|
707
|
+
*
|
|
708
|
+
* @example
|
|
709
|
+
* const krisspy = createClient({ backendId: '...', anonKey: '...' })
|
|
710
|
+
* krisspy.analytics.init({ autoTrackPageViews: true })
|
|
711
|
+
* krisspy.analytics.track('button_click', { label: 'signup' })
|
|
712
|
+
*/
|
|
713
|
+
|
|
714
|
+
interface AnalyticsInitOptions {
|
|
715
|
+
/** Auto-track page views on init + navigation (default: true) */
|
|
716
|
+
autoTrackPageViews?: boolean;
|
|
717
|
+
/** Auto-track SPA navigation via pushState/popstate (default: true) */
|
|
718
|
+
autoTrackNavigation?: boolean;
|
|
719
|
+
/** Flush interval in ms (default: 5000) */
|
|
720
|
+
flushInterval?: number;
|
|
721
|
+
/** Enable debug logging (default: false) */
|
|
722
|
+
debug?: boolean;
|
|
723
|
+
}
|
|
724
|
+
interface AnalyticsEvent {
|
|
725
|
+
eventName: string;
|
|
726
|
+
sessionId: string;
|
|
727
|
+
userId?: string;
|
|
728
|
+
properties?: Record<string, any>;
|
|
729
|
+
timestamp: string;
|
|
730
|
+
}
|
|
731
|
+
declare class KrisspyAnalytics {
|
|
732
|
+
private http;
|
|
733
|
+
private backendId;
|
|
734
|
+
private queue;
|
|
735
|
+
private sessionId;
|
|
736
|
+
private userId;
|
|
737
|
+
private userTraits;
|
|
738
|
+
private initialized;
|
|
739
|
+
private flushTimer;
|
|
740
|
+
private options;
|
|
741
|
+
private originalPushState;
|
|
742
|
+
private popstateHandler;
|
|
743
|
+
private unloadHandler;
|
|
744
|
+
constructor(http: HttpClient, backendId: string);
|
|
745
|
+
/**
|
|
746
|
+
* Initialize analytics tracking
|
|
747
|
+
*
|
|
748
|
+
* @example
|
|
749
|
+
* krisspy.analytics.init()
|
|
750
|
+
* krisspy.analytics.init({ autoTrackPageViews: true, flushInterval: 3000 })
|
|
751
|
+
*/
|
|
752
|
+
init(options?: AnalyticsInitOptions): void;
|
|
753
|
+
/**
|
|
754
|
+
* Track a custom event
|
|
755
|
+
*
|
|
756
|
+
* @example
|
|
757
|
+
* krisspy.analytics.track('button_click', { label: 'signup', variant: 'blue' })
|
|
758
|
+
* krisspy.analytics.track('purchase', { amount: 29.99, plan: 'pro' })
|
|
759
|
+
*/
|
|
760
|
+
track(eventName: string, properties?: Record<string, any>): void;
|
|
761
|
+
/**
|
|
762
|
+
* Identify a user (attach userId + traits to all future events)
|
|
763
|
+
*
|
|
764
|
+
* @example
|
|
765
|
+
* krisspy.analytics.identify('user_123', { name: 'John', plan: 'pro' })
|
|
766
|
+
*/
|
|
767
|
+
identify(userId: string, traits?: Record<string, any>): void;
|
|
768
|
+
/**
|
|
769
|
+
* Flush event queue to the server (async)
|
|
770
|
+
*/
|
|
771
|
+
flush(): Promise<void>;
|
|
772
|
+
/**
|
|
773
|
+
* Synchronous flush using sendBeacon (for page unload)
|
|
774
|
+
*/
|
|
775
|
+
private flushSync;
|
|
776
|
+
/**
|
|
777
|
+
* Stop tracking and cleanup all listeners/timers
|
|
778
|
+
*/
|
|
779
|
+
destroy(): void;
|
|
780
|
+
private getOrCreateSessionId;
|
|
781
|
+
private getIngestUrl;
|
|
782
|
+
private setupNavigationTracking;
|
|
783
|
+
}
|
|
784
|
+
|
|
702
785
|
/**
|
|
703
786
|
* Query Builder - Supabase-style fluent API for database queries
|
|
704
787
|
*/
|
|
@@ -850,6 +933,7 @@ declare class KrisspyClient {
|
|
|
850
933
|
private _auth;
|
|
851
934
|
private _storage;
|
|
852
935
|
private _realtime;
|
|
936
|
+
private _analytics;
|
|
853
937
|
private useRLS;
|
|
854
938
|
private debug;
|
|
855
939
|
constructor(options: KrisspyClientOptions);
|
|
@@ -898,6 +982,23 @@ declare class KrisspyClient {
|
|
|
898
982
|
* const { error } = await krisspy.storage.from('uploads').remove(['old.jpg'])
|
|
899
983
|
*/
|
|
900
984
|
get storage(): KrisspyStorage;
|
|
985
|
+
/**
|
|
986
|
+
* Analytics module for event tracking
|
|
987
|
+
*
|
|
988
|
+
* @example
|
|
989
|
+
* // Initialize tracking
|
|
990
|
+
* krisspy.analytics.init()
|
|
991
|
+
*
|
|
992
|
+
* // Track custom events
|
|
993
|
+
* krisspy.analytics.track('button_click', { label: 'signup' })
|
|
994
|
+
*
|
|
995
|
+
* // Identify a user
|
|
996
|
+
* krisspy.analytics.identify('user_123', { plan: 'pro' })
|
|
997
|
+
*
|
|
998
|
+
* // Cleanup
|
|
999
|
+
* krisspy.analytics.destroy()
|
|
1000
|
+
*/
|
|
1001
|
+
get analytics(): KrisspyAnalytics;
|
|
901
1002
|
/**
|
|
902
1003
|
* Create a realtime channel for subscribing to database changes
|
|
903
1004
|
*
|
|
@@ -1081,4 +1182,4 @@ declare class KrisspyClient {
|
|
|
1081
1182
|
*/
|
|
1082
1183
|
declare function createClient(options: KrisspyClientOptions): KrisspyClient;
|
|
1083
1184
|
|
|
1084
|
-
export { type AuthChangeEvent, type AuthResponse, type ChannelState, type FileObject, type FileUploadOptions, type Filter, type FilterOperator, type FunctionInvokeOptions, type FunctionResponse, HttpClient, KrisspyAuth, KrisspyClient, type KrisspyClientOptions, type KrisspyError, KrisspyRealtime, KrisspyStorage, type MutationResponse, type OAuthProvider, type OrderBy, type PostgresChangesConfig, QueryBuilder, type QueryOptions, type QueryResponse, RealtimeChannel, type RealtimeEvent, type RealtimePayload, type Session, type SignInCredentials, type SignUpCredentials, type SingleResponse, StorageBucket, type UploadAndLinkOptions, type UploadAndLinkResponse, type User, createClient };
|
|
1185
|
+
export { type AnalyticsEvent, type AnalyticsInitOptions, type AuthChangeEvent, type AuthResponse, type ChannelState, type FileObject, type FileUploadOptions, type Filter, type FilterOperator, type FunctionInvokeOptions, type FunctionResponse, HttpClient, KrisspyAnalytics, KrisspyAuth, KrisspyClient, type KrisspyClientOptions, type KrisspyError, KrisspyRealtime, KrisspyStorage, type MutationResponse, type OAuthProvider, type OrderBy, type PostgresChangesConfig, QueryBuilder, type QueryOptions, type QueryResponse, RealtimeChannel, type RealtimeEvent, type RealtimePayload, type Session, type SignInCredentials, type SignUpCredentials, type SingleResponse, StorageBucket, type UploadAndLinkOptions, type UploadAndLinkResponse, type User, createClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -699,6 +699,89 @@ declare class KrisspyRealtime {
|
|
|
699
699
|
private log;
|
|
700
700
|
}
|
|
701
701
|
|
|
702
|
+
/**
|
|
703
|
+
* Krisspy Analytics - Lightweight event tracking module
|
|
704
|
+
*
|
|
705
|
+
* Tracks page views, sessions, custom events. Sends batched events
|
|
706
|
+
* to the analytics ingest endpoint. Works with SPAs (intercepts pushState).
|
|
707
|
+
*
|
|
708
|
+
* @example
|
|
709
|
+
* const krisspy = createClient({ backendId: '...', anonKey: '...' })
|
|
710
|
+
* krisspy.analytics.init({ autoTrackPageViews: true })
|
|
711
|
+
* krisspy.analytics.track('button_click', { label: 'signup' })
|
|
712
|
+
*/
|
|
713
|
+
|
|
714
|
+
interface AnalyticsInitOptions {
|
|
715
|
+
/** Auto-track page views on init + navigation (default: true) */
|
|
716
|
+
autoTrackPageViews?: boolean;
|
|
717
|
+
/** Auto-track SPA navigation via pushState/popstate (default: true) */
|
|
718
|
+
autoTrackNavigation?: boolean;
|
|
719
|
+
/** Flush interval in ms (default: 5000) */
|
|
720
|
+
flushInterval?: number;
|
|
721
|
+
/** Enable debug logging (default: false) */
|
|
722
|
+
debug?: boolean;
|
|
723
|
+
}
|
|
724
|
+
interface AnalyticsEvent {
|
|
725
|
+
eventName: string;
|
|
726
|
+
sessionId: string;
|
|
727
|
+
userId?: string;
|
|
728
|
+
properties?: Record<string, any>;
|
|
729
|
+
timestamp: string;
|
|
730
|
+
}
|
|
731
|
+
declare class KrisspyAnalytics {
|
|
732
|
+
private http;
|
|
733
|
+
private backendId;
|
|
734
|
+
private queue;
|
|
735
|
+
private sessionId;
|
|
736
|
+
private userId;
|
|
737
|
+
private userTraits;
|
|
738
|
+
private initialized;
|
|
739
|
+
private flushTimer;
|
|
740
|
+
private options;
|
|
741
|
+
private originalPushState;
|
|
742
|
+
private popstateHandler;
|
|
743
|
+
private unloadHandler;
|
|
744
|
+
constructor(http: HttpClient, backendId: string);
|
|
745
|
+
/**
|
|
746
|
+
* Initialize analytics tracking
|
|
747
|
+
*
|
|
748
|
+
* @example
|
|
749
|
+
* krisspy.analytics.init()
|
|
750
|
+
* krisspy.analytics.init({ autoTrackPageViews: true, flushInterval: 3000 })
|
|
751
|
+
*/
|
|
752
|
+
init(options?: AnalyticsInitOptions): void;
|
|
753
|
+
/**
|
|
754
|
+
* Track a custom event
|
|
755
|
+
*
|
|
756
|
+
* @example
|
|
757
|
+
* krisspy.analytics.track('button_click', { label: 'signup', variant: 'blue' })
|
|
758
|
+
* krisspy.analytics.track('purchase', { amount: 29.99, plan: 'pro' })
|
|
759
|
+
*/
|
|
760
|
+
track(eventName: string, properties?: Record<string, any>): void;
|
|
761
|
+
/**
|
|
762
|
+
* Identify a user (attach userId + traits to all future events)
|
|
763
|
+
*
|
|
764
|
+
* @example
|
|
765
|
+
* krisspy.analytics.identify('user_123', { name: 'John', plan: 'pro' })
|
|
766
|
+
*/
|
|
767
|
+
identify(userId: string, traits?: Record<string, any>): void;
|
|
768
|
+
/**
|
|
769
|
+
* Flush event queue to the server (async)
|
|
770
|
+
*/
|
|
771
|
+
flush(): Promise<void>;
|
|
772
|
+
/**
|
|
773
|
+
* Synchronous flush using sendBeacon (for page unload)
|
|
774
|
+
*/
|
|
775
|
+
private flushSync;
|
|
776
|
+
/**
|
|
777
|
+
* Stop tracking and cleanup all listeners/timers
|
|
778
|
+
*/
|
|
779
|
+
destroy(): void;
|
|
780
|
+
private getOrCreateSessionId;
|
|
781
|
+
private getIngestUrl;
|
|
782
|
+
private setupNavigationTracking;
|
|
783
|
+
}
|
|
784
|
+
|
|
702
785
|
/**
|
|
703
786
|
* Query Builder - Supabase-style fluent API for database queries
|
|
704
787
|
*/
|
|
@@ -850,6 +933,7 @@ declare class KrisspyClient {
|
|
|
850
933
|
private _auth;
|
|
851
934
|
private _storage;
|
|
852
935
|
private _realtime;
|
|
936
|
+
private _analytics;
|
|
853
937
|
private useRLS;
|
|
854
938
|
private debug;
|
|
855
939
|
constructor(options: KrisspyClientOptions);
|
|
@@ -898,6 +982,23 @@ declare class KrisspyClient {
|
|
|
898
982
|
* const { error } = await krisspy.storage.from('uploads').remove(['old.jpg'])
|
|
899
983
|
*/
|
|
900
984
|
get storage(): KrisspyStorage;
|
|
985
|
+
/**
|
|
986
|
+
* Analytics module for event tracking
|
|
987
|
+
*
|
|
988
|
+
* @example
|
|
989
|
+
* // Initialize tracking
|
|
990
|
+
* krisspy.analytics.init()
|
|
991
|
+
*
|
|
992
|
+
* // Track custom events
|
|
993
|
+
* krisspy.analytics.track('button_click', { label: 'signup' })
|
|
994
|
+
*
|
|
995
|
+
* // Identify a user
|
|
996
|
+
* krisspy.analytics.identify('user_123', { plan: 'pro' })
|
|
997
|
+
*
|
|
998
|
+
* // Cleanup
|
|
999
|
+
* krisspy.analytics.destroy()
|
|
1000
|
+
*/
|
|
1001
|
+
get analytics(): KrisspyAnalytics;
|
|
901
1002
|
/**
|
|
902
1003
|
* Create a realtime channel for subscribing to database changes
|
|
903
1004
|
*
|
|
@@ -1081,4 +1182,4 @@ declare class KrisspyClient {
|
|
|
1081
1182
|
*/
|
|
1082
1183
|
declare function createClient(options: KrisspyClientOptions): KrisspyClient;
|
|
1083
1184
|
|
|
1084
|
-
export { type AuthChangeEvent, type AuthResponse, type ChannelState, type FileObject, type FileUploadOptions, type Filter, type FilterOperator, type FunctionInvokeOptions, type FunctionResponse, HttpClient, KrisspyAuth, KrisspyClient, type KrisspyClientOptions, type KrisspyError, KrisspyRealtime, KrisspyStorage, type MutationResponse, type OAuthProvider, type OrderBy, type PostgresChangesConfig, QueryBuilder, type QueryOptions, type QueryResponse, RealtimeChannel, type RealtimeEvent, type RealtimePayload, type Session, type SignInCredentials, type SignUpCredentials, type SingleResponse, StorageBucket, type UploadAndLinkOptions, type UploadAndLinkResponse, type User, createClient };
|
|
1185
|
+
export { type AnalyticsEvent, type AnalyticsInitOptions, type AuthChangeEvent, type AuthResponse, type ChannelState, type FileObject, type FileUploadOptions, type Filter, type FilterOperator, type FunctionInvokeOptions, type FunctionResponse, HttpClient, KrisspyAnalytics, KrisspyAuth, KrisspyClient, type KrisspyClientOptions, type KrisspyError, KrisspyRealtime, KrisspyStorage, type MutationResponse, type OAuthProvider, type OrderBy, type PostgresChangesConfig, QueryBuilder, type QueryOptions, type QueryResponse, RealtimeChannel, type RealtimeEvent, type RealtimePayload, type Session, type SignInCredentials, type SignUpCredentials, type SingleResponse, StorageBucket, type UploadAndLinkOptions, type UploadAndLinkResponse, type User, createClient };
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
HttpClient: () => HttpClient,
|
|
24
|
+
KrisspyAnalytics: () => KrisspyAnalytics,
|
|
24
25
|
KrisspyAuth: () => KrisspyAuth,
|
|
25
26
|
KrisspyClient: () => KrisspyClient,
|
|
26
27
|
KrisspyRealtime: () => KrisspyRealtime,
|
|
@@ -1144,6 +1145,192 @@ var KrisspyRealtime = class {
|
|
|
1144
1145
|
}
|
|
1145
1146
|
};
|
|
1146
1147
|
|
|
1148
|
+
// src/analytics.ts
|
|
1149
|
+
var KrisspyAnalytics = class {
|
|
1150
|
+
constructor(http, backendId) {
|
|
1151
|
+
this.queue = [];
|
|
1152
|
+
this.sessionId = null;
|
|
1153
|
+
this.userId = null;
|
|
1154
|
+
this.userTraits = {};
|
|
1155
|
+
this.initialized = false;
|
|
1156
|
+
this.flushTimer = null;
|
|
1157
|
+
this.options = {};
|
|
1158
|
+
this.originalPushState = null;
|
|
1159
|
+
this.popstateHandler = null;
|
|
1160
|
+
this.unloadHandler = null;
|
|
1161
|
+
this.http = http;
|
|
1162
|
+
this.backendId = backendId;
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Initialize analytics tracking
|
|
1166
|
+
*
|
|
1167
|
+
* @example
|
|
1168
|
+
* krisspy.analytics.init()
|
|
1169
|
+
* krisspy.analytics.init({ autoTrackPageViews: true, flushInterval: 3000 })
|
|
1170
|
+
*/
|
|
1171
|
+
init(options) {
|
|
1172
|
+
if (this.initialized) return;
|
|
1173
|
+
if (typeof window === "undefined") return;
|
|
1174
|
+
this.options = {
|
|
1175
|
+
autoTrackPageViews: true,
|
|
1176
|
+
autoTrackNavigation: true,
|
|
1177
|
+
flushInterval: 5e3,
|
|
1178
|
+
debug: false,
|
|
1179
|
+
...options
|
|
1180
|
+
};
|
|
1181
|
+
this.sessionId = this.getOrCreateSessionId();
|
|
1182
|
+
this.initialized = true;
|
|
1183
|
+
if (this.options.autoTrackPageViews) {
|
|
1184
|
+
this.track("session_start", {});
|
|
1185
|
+
this.track("page_view", { url: location.href, referrer: document.referrer });
|
|
1186
|
+
}
|
|
1187
|
+
if (this.options.autoTrackNavigation) {
|
|
1188
|
+
this.setupNavigationTracking();
|
|
1189
|
+
}
|
|
1190
|
+
this.unloadHandler = () => {
|
|
1191
|
+
this.track("session_end", {});
|
|
1192
|
+
this.flushSync();
|
|
1193
|
+
};
|
|
1194
|
+
window.addEventListener("beforeunload", this.unloadHandler);
|
|
1195
|
+
this.flushTimer = setInterval(() => this.flush(), this.options.flushInterval);
|
|
1196
|
+
if (this.options.debug) {
|
|
1197
|
+
console.log("[Krisspy Analytics] Initialized", { backendId: this.backendId, sessionId: this.sessionId });
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* Track a custom event
|
|
1202
|
+
*
|
|
1203
|
+
* @example
|
|
1204
|
+
* krisspy.analytics.track('button_click', { label: 'signup', variant: 'blue' })
|
|
1205
|
+
* krisspy.analytics.track('purchase', { amount: 29.99, plan: 'pro' })
|
|
1206
|
+
*/
|
|
1207
|
+
track(eventName, properties) {
|
|
1208
|
+
if (!this.initialized || typeof window === "undefined") return;
|
|
1209
|
+
const event = {
|
|
1210
|
+
eventName,
|
|
1211
|
+
sessionId: this.sessionId,
|
|
1212
|
+
userId: this.userId || void 0,
|
|
1213
|
+
properties: {
|
|
1214
|
+
...properties,
|
|
1215
|
+
...this.userId ? { _user_traits: this.userTraits } : {}
|
|
1216
|
+
},
|
|
1217
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1218
|
+
};
|
|
1219
|
+
this.queue.push(event);
|
|
1220
|
+
if (this.options.debug) {
|
|
1221
|
+
console.log("[Krisspy Analytics] Tracked:", eventName, properties);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Identify a user (attach userId + traits to all future events)
|
|
1226
|
+
*
|
|
1227
|
+
* @example
|
|
1228
|
+
* krisspy.analytics.identify('user_123', { name: 'John', plan: 'pro' })
|
|
1229
|
+
*/
|
|
1230
|
+
identify(userId, traits) {
|
|
1231
|
+
this.userId = userId;
|
|
1232
|
+
this.userTraits = traits || {};
|
|
1233
|
+
if (this.initialized) {
|
|
1234
|
+
this.track("identify", { userId, ...traits });
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Flush event queue to the server (async)
|
|
1239
|
+
*/
|
|
1240
|
+
async flush() {
|
|
1241
|
+
if (!this.queue.length) return;
|
|
1242
|
+
const events = this.queue.splice(0, 50);
|
|
1243
|
+
const payload = { appId: this.backendId, events };
|
|
1244
|
+
try {
|
|
1245
|
+
await this.http.post("/api/v1/analytics/events", payload);
|
|
1246
|
+
if (this.options.debug) {
|
|
1247
|
+
console.log(`[Krisspy Analytics] Flushed ${events.length} events`);
|
|
1248
|
+
}
|
|
1249
|
+
} catch {
|
|
1250
|
+
this.queue.unshift(...events);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Synchronous flush using sendBeacon (for page unload)
|
|
1255
|
+
*/
|
|
1256
|
+
flushSync() {
|
|
1257
|
+
if (!this.queue.length) return;
|
|
1258
|
+
const events = this.queue.splice(0, 50);
|
|
1259
|
+
const payload = JSON.stringify({ appId: this.backendId, events });
|
|
1260
|
+
try {
|
|
1261
|
+
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
1262
|
+
const blob = new Blob([payload], { type: "application/json" });
|
|
1263
|
+
navigator.sendBeacon(this.getIngestUrl(), blob);
|
|
1264
|
+
} else {
|
|
1265
|
+
fetch(this.getIngestUrl(), {
|
|
1266
|
+
method: "POST",
|
|
1267
|
+
headers: { "Content-Type": "application/json" },
|
|
1268
|
+
body: payload,
|
|
1269
|
+
keepalive: true
|
|
1270
|
+
}).catch(() => {
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
} catch {
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Stop tracking and cleanup all listeners/timers
|
|
1278
|
+
*/
|
|
1279
|
+
destroy() {
|
|
1280
|
+
if (!this.initialized) return;
|
|
1281
|
+
this.flushSync();
|
|
1282
|
+
if (this.flushTimer) {
|
|
1283
|
+
clearInterval(this.flushTimer);
|
|
1284
|
+
this.flushTimer = null;
|
|
1285
|
+
}
|
|
1286
|
+
if (this.originalPushState) {
|
|
1287
|
+
history.pushState = this.originalPushState;
|
|
1288
|
+
this.originalPushState = null;
|
|
1289
|
+
}
|
|
1290
|
+
if (this.popstateHandler) {
|
|
1291
|
+
window.removeEventListener("popstate", this.popstateHandler);
|
|
1292
|
+
this.popstateHandler = null;
|
|
1293
|
+
}
|
|
1294
|
+
if (this.unloadHandler) {
|
|
1295
|
+
window.removeEventListener("beforeunload", this.unloadHandler);
|
|
1296
|
+
this.unloadHandler = null;
|
|
1297
|
+
}
|
|
1298
|
+
this.initialized = false;
|
|
1299
|
+
this.queue = [];
|
|
1300
|
+
if (this.options.debug) {
|
|
1301
|
+
console.log("[Krisspy Analytics] Destroyed");
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
// ── Private helpers ──────────────────────────────────────────────────────
|
|
1305
|
+
getOrCreateSessionId() {
|
|
1306
|
+
const key = "_ka_sid";
|
|
1307
|
+
try {
|
|
1308
|
+
const existing = localStorage.getItem(key);
|
|
1309
|
+
if (existing) return existing;
|
|
1310
|
+
const id = Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
|
|
1311
|
+
localStorage.setItem(key, id);
|
|
1312
|
+
return id;
|
|
1313
|
+
} catch {
|
|
1314
|
+
return Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
getIngestUrl() {
|
|
1318
|
+
return `${this.http.baseUrl}/api/v1/analytics/events`;
|
|
1319
|
+
}
|
|
1320
|
+
setupNavigationTracking() {
|
|
1321
|
+
this.originalPushState = history.pushState;
|
|
1322
|
+
const self = this;
|
|
1323
|
+
history.pushState = function(...args) {
|
|
1324
|
+
self.originalPushState.apply(history, args);
|
|
1325
|
+
self.track("page_view", { url: location.href });
|
|
1326
|
+
};
|
|
1327
|
+
this.popstateHandler = () => {
|
|
1328
|
+
this.track("page_view", { url: location.href });
|
|
1329
|
+
};
|
|
1330
|
+
window.addEventListener("popstate", this.popstateHandler);
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
|
|
1147
1334
|
// src/query-builder.ts
|
|
1148
1335
|
var QueryBuilder = class {
|
|
1149
1336
|
constructor(http, backendId, tableName, useRLS = true) {
|
|
@@ -1438,6 +1625,7 @@ var KrisspyClient = class {
|
|
|
1438
1625
|
this._auth = new KrisspyAuth(this.http, this.backendId);
|
|
1439
1626
|
this._storage = new KrisspyStorage(this.http, this.backendId);
|
|
1440
1627
|
this._realtime = new KrisspyRealtime(this.baseUrl, this.backendId, this.debug);
|
|
1628
|
+
this._analytics = new KrisspyAnalytics(this.http, this.backendId);
|
|
1441
1629
|
this._auth.onAuthStateChange((event) => {
|
|
1442
1630
|
if (event === "SIGNED_IN") {
|
|
1443
1631
|
const session = this._auth.session();
|
|
@@ -1498,6 +1686,25 @@ var KrisspyClient = class {
|
|
|
1498
1686
|
get storage() {
|
|
1499
1687
|
return this._storage;
|
|
1500
1688
|
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Analytics module for event tracking
|
|
1691
|
+
*
|
|
1692
|
+
* @example
|
|
1693
|
+
* // Initialize tracking
|
|
1694
|
+
* krisspy.analytics.init()
|
|
1695
|
+
*
|
|
1696
|
+
* // Track custom events
|
|
1697
|
+
* krisspy.analytics.track('button_click', { label: 'signup' })
|
|
1698
|
+
*
|
|
1699
|
+
* // Identify a user
|
|
1700
|
+
* krisspy.analytics.identify('user_123', { plan: 'pro' })
|
|
1701
|
+
*
|
|
1702
|
+
* // Cleanup
|
|
1703
|
+
* krisspy.analytics.destroy()
|
|
1704
|
+
*/
|
|
1705
|
+
get analytics() {
|
|
1706
|
+
return this._analytics;
|
|
1707
|
+
}
|
|
1501
1708
|
/**
|
|
1502
1709
|
* Create a realtime channel for subscribing to database changes
|
|
1503
1710
|
*
|
|
@@ -1666,6 +1873,7 @@ function createClient(options) {
|
|
|
1666
1873
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1667
1874
|
0 && (module.exports = {
|
|
1668
1875
|
HttpClient,
|
|
1876
|
+
KrisspyAnalytics,
|
|
1669
1877
|
KrisspyAuth,
|
|
1670
1878
|
KrisspyClient,
|
|
1671
1879
|
KrisspyRealtime,
|
package/dist/index.mjs
CHANGED
|
@@ -1110,6 +1110,192 @@ var KrisspyRealtime = class {
|
|
|
1110
1110
|
}
|
|
1111
1111
|
};
|
|
1112
1112
|
|
|
1113
|
+
// src/analytics.ts
|
|
1114
|
+
var KrisspyAnalytics = class {
|
|
1115
|
+
constructor(http, backendId) {
|
|
1116
|
+
this.queue = [];
|
|
1117
|
+
this.sessionId = null;
|
|
1118
|
+
this.userId = null;
|
|
1119
|
+
this.userTraits = {};
|
|
1120
|
+
this.initialized = false;
|
|
1121
|
+
this.flushTimer = null;
|
|
1122
|
+
this.options = {};
|
|
1123
|
+
this.originalPushState = null;
|
|
1124
|
+
this.popstateHandler = null;
|
|
1125
|
+
this.unloadHandler = null;
|
|
1126
|
+
this.http = http;
|
|
1127
|
+
this.backendId = backendId;
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Initialize analytics tracking
|
|
1131
|
+
*
|
|
1132
|
+
* @example
|
|
1133
|
+
* krisspy.analytics.init()
|
|
1134
|
+
* krisspy.analytics.init({ autoTrackPageViews: true, flushInterval: 3000 })
|
|
1135
|
+
*/
|
|
1136
|
+
init(options) {
|
|
1137
|
+
if (this.initialized) return;
|
|
1138
|
+
if (typeof window === "undefined") return;
|
|
1139
|
+
this.options = {
|
|
1140
|
+
autoTrackPageViews: true,
|
|
1141
|
+
autoTrackNavigation: true,
|
|
1142
|
+
flushInterval: 5e3,
|
|
1143
|
+
debug: false,
|
|
1144
|
+
...options
|
|
1145
|
+
};
|
|
1146
|
+
this.sessionId = this.getOrCreateSessionId();
|
|
1147
|
+
this.initialized = true;
|
|
1148
|
+
if (this.options.autoTrackPageViews) {
|
|
1149
|
+
this.track("session_start", {});
|
|
1150
|
+
this.track("page_view", { url: location.href, referrer: document.referrer });
|
|
1151
|
+
}
|
|
1152
|
+
if (this.options.autoTrackNavigation) {
|
|
1153
|
+
this.setupNavigationTracking();
|
|
1154
|
+
}
|
|
1155
|
+
this.unloadHandler = () => {
|
|
1156
|
+
this.track("session_end", {});
|
|
1157
|
+
this.flushSync();
|
|
1158
|
+
};
|
|
1159
|
+
window.addEventListener("beforeunload", this.unloadHandler);
|
|
1160
|
+
this.flushTimer = setInterval(() => this.flush(), this.options.flushInterval);
|
|
1161
|
+
if (this.options.debug) {
|
|
1162
|
+
console.log("[Krisspy Analytics] Initialized", { backendId: this.backendId, sessionId: this.sessionId });
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Track a custom event
|
|
1167
|
+
*
|
|
1168
|
+
* @example
|
|
1169
|
+
* krisspy.analytics.track('button_click', { label: 'signup', variant: 'blue' })
|
|
1170
|
+
* krisspy.analytics.track('purchase', { amount: 29.99, plan: 'pro' })
|
|
1171
|
+
*/
|
|
1172
|
+
track(eventName, properties) {
|
|
1173
|
+
if (!this.initialized || typeof window === "undefined") return;
|
|
1174
|
+
const event = {
|
|
1175
|
+
eventName,
|
|
1176
|
+
sessionId: this.sessionId,
|
|
1177
|
+
userId: this.userId || void 0,
|
|
1178
|
+
properties: {
|
|
1179
|
+
...properties,
|
|
1180
|
+
...this.userId ? { _user_traits: this.userTraits } : {}
|
|
1181
|
+
},
|
|
1182
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1183
|
+
};
|
|
1184
|
+
this.queue.push(event);
|
|
1185
|
+
if (this.options.debug) {
|
|
1186
|
+
console.log("[Krisspy Analytics] Tracked:", eventName, properties);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Identify a user (attach userId + traits to all future events)
|
|
1191
|
+
*
|
|
1192
|
+
* @example
|
|
1193
|
+
* krisspy.analytics.identify('user_123', { name: 'John', plan: 'pro' })
|
|
1194
|
+
*/
|
|
1195
|
+
identify(userId, traits) {
|
|
1196
|
+
this.userId = userId;
|
|
1197
|
+
this.userTraits = traits || {};
|
|
1198
|
+
if (this.initialized) {
|
|
1199
|
+
this.track("identify", { userId, ...traits });
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Flush event queue to the server (async)
|
|
1204
|
+
*/
|
|
1205
|
+
async flush() {
|
|
1206
|
+
if (!this.queue.length) return;
|
|
1207
|
+
const events = this.queue.splice(0, 50);
|
|
1208
|
+
const payload = { appId: this.backendId, events };
|
|
1209
|
+
try {
|
|
1210
|
+
await this.http.post("/api/v1/analytics/events", payload);
|
|
1211
|
+
if (this.options.debug) {
|
|
1212
|
+
console.log(`[Krisspy Analytics] Flushed ${events.length} events`);
|
|
1213
|
+
}
|
|
1214
|
+
} catch {
|
|
1215
|
+
this.queue.unshift(...events);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Synchronous flush using sendBeacon (for page unload)
|
|
1220
|
+
*/
|
|
1221
|
+
flushSync() {
|
|
1222
|
+
if (!this.queue.length) return;
|
|
1223
|
+
const events = this.queue.splice(0, 50);
|
|
1224
|
+
const payload = JSON.stringify({ appId: this.backendId, events });
|
|
1225
|
+
try {
|
|
1226
|
+
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
1227
|
+
const blob = new Blob([payload], { type: "application/json" });
|
|
1228
|
+
navigator.sendBeacon(this.getIngestUrl(), blob);
|
|
1229
|
+
} else {
|
|
1230
|
+
fetch(this.getIngestUrl(), {
|
|
1231
|
+
method: "POST",
|
|
1232
|
+
headers: { "Content-Type": "application/json" },
|
|
1233
|
+
body: payload,
|
|
1234
|
+
keepalive: true
|
|
1235
|
+
}).catch(() => {
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
} catch {
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Stop tracking and cleanup all listeners/timers
|
|
1243
|
+
*/
|
|
1244
|
+
destroy() {
|
|
1245
|
+
if (!this.initialized) return;
|
|
1246
|
+
this.flushSync();
|
|
1247
|
+
if (this.flushTimer) {
|
|
1248
|
+
clearInterval(this.flushTimer);
|
|
1249
|
+
this.flushTimer = null;
|
|
1250
|
+
}
|
|
1251
|
+
if (this.originalPushState) {
|
|
1252
|
+
history.pushState = this.originalPushState;
|
|
1253
|
+
this.originalPushState = null;
|
|
1254
|
+
}
|
|
1255
|
+
if (this.popstateHandler) {
|
|
1256
|
+
window.removeEventListener("popstate", this.popstateHandler);
|
|
1257
|
+
this.popstateHandler = null;
|
|
1258
|
+
}
|
|
1259
|
+
if (this.unloadHandler) {
|
|
1260
|
+
window.removeEventListener("beforeunload", this.unloadHandler);
|
|
1261
|
+
this.unloadHandler = null;
|
|
1262
|
+
}
|
|
1263
|
+
this.initialized = false;
|
|
1264
|
+
this.queue = [];
|
|
1265
|
+
if (this.options.debug) {
|
|
1266
|
+
console.log("[Krisspy Analytics] Destroyed");
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
// ── Private helpers ──────────────────────────────────────────────────────
|
|
1270
|
+
getOrCreateSessionId() {
|
|
1271
|
+
const key = "_ka_sid";
|
|
1272
|
+
try {
|
|
1273
|
+
const existing = localStorage.getItem(key);
|
|
1274
|
+
if (existing) return existing;
|
|
1275
|
+
const id = Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
|
|
1276
|
+
localStorage.setItem(key, id);
|
|
1277
|
+
return id;
|
|
1278
|
+
} catch {
|
|
1279
|
+
return Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
getIngestUrl() {
|
|
1283
|
+
return `${this.http.baseUrl}/api/v1/analytics/events`;
|
|
1284
|
+
}
|
|
1285
|
+
setupNavigationTracking() {
|
|
1286
|
+
this.originalPushState = history.pushState;
|
|
1287
|
+
const self = this;
|
|
1288
|
+
history.pushState = function(...args) {
|
|
1289
|
+
self.originalPushState.apply(history, args);
|
|
1290
|
+
self.track("page_view", { url: location.href });
|
|
1291
|
+
};
|
|
1292
|
+
this.popstateHandler = () => {
|
|
1293
|
+
this.track("page_view", { url: location.href });
|
|
1294
|
+
};
|
|
1295
|
+
window.addEventListener("popstate", this.popstateHandler);
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
|
|
1113
1299
|
// src/query-builder.ts
|
|
1114
1300
|
var QueryBuilder = class {
|
|
1115
1301
|
constructor(http, backendId, tableName, useRLS = true) {
|
|
@@ -1404,6 +1590,7 @@ var KrisspyClient = class {
|
|
|
1404
1590
|
this._auth = new KrisspyAuth(this.http, this.backendId);
|
|
1405
1591
|
this._storage = new KrisspyStorage(this.http, this.backendId);
|
|
1406
1592
|
this._realtime = new KrisspyRealtime(this.baseUrl, this.backendId, this.debug);
|
|
1593
|
+
this._analytics = new KrisspyAnalytics(this.http, this.backendId);
|
|
1407
1594
|
this._auth.onAuthStateChange((event) => {
|
|
1408
1595
|
if (event === "SIGNED_IN") {
|
|
1409
1596
|
const session = this._auth.session();
|
|
@@ -1464,6 +1651,25 @@ var KrisspyClient = class {
|
|
|
1464
1651
|
get storage() {
|
|
1465
1652
|
return this._storage;
|
|
1466
1653
|
}
|
|
1654
|
+
/**
|
|
1655
|
+
* Analytics module for event tracking
|
|
1656
|
+
*
|
|
1657
|
+
* @example
|
|
1658
|
+
* // Initialize tracking
|
|
1659
|
+
* krisspy.analytics.init()
|
|
1660
|
+
*
|
|
1661
|
+
* // Track custom events
|
|
1662
|
+
* krisspy.analytics.track('button_click', { label: 'signup' })
|
|
1663
|
+
*
|
|
1664
|
+
* // Identify a user
|
|
1665
|
+
* krisspy.analytics.identify('user_123', { plan: 'pro' })
|
|
1666
|
+
*
|
|
1667
|
+
* // Cleanup
|
|
1668
|
+
* krisspy.analytics.destroy()
|
|
1669
|
+
*/
|
|
1670
|
+
get analytics() {
|
|
1671
|
+
return this._analytics;
|
|
1672
|
+
}
|
|
1467
1673
|
/**
|
|
1468
1674
|
* Create a realtime channel for subscribing to database changes
|
|
1469
1675
|
*
|
|
@@ -1631,6 +1837,7 @@ function createClient(options) {
|
|
|
1631
1837
|
}
|
|
1632
1838
|
export {
|
|
1633
1839
|
HttpClient,
|
|
1840
|
+
KrisspyAnalytics,
|
|
1634
1841
|
KrisspyAuth,
|
|
1635
1842
|
KrisspyClient,
|
|
1636
1843
|
KrisspyRealtime,
|