krisspy-sdk 0.4.1 → 0.5.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/dist/index.d.mts +37 -13
- package/dist/index.d.ts +37 -13
- package/dist/index.js +172 -73
- package/dist/index.mjs +169 -72
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -26,6 +26,20 @@ interface KrisspyClientOptions {
|
|
|
26
26
|
* When false, uses legacy /data/* endpoints which require backend owner auth.
|
|
27
27
|
*/
|
|
28
28
|
useRLS?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Custom storage adapter for session persistence.
|
|
31
|
+
* Use this in React Native with AsyncStorage or MMKV.
|
|
32
|
+
* Falls back to localStorage (browser) or in-memory storage.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* import AsyncStorage from '@react-native-async-storage/async-storage'
|
|
36
|
+
* const krisspy = createClient({ ..., storage: AsyncStorage })
|
|
37
|
+
*/
|
|
38
|
+
storage?: {
|
|
39
|
+
getItem(key: string): string | null | Promise<string | null>;
|
|
40
|
+
setItem(key: string, value: string): void | Promise<void>;
|
|
41
|
+
removeItem(key: string): void | Promise<void>;
|
|
42
|
+
};
|
|
29
43
|
}
|
|
30
44
|
interface User {
|
|
31
45
|
id: string;
|
|
@@ -184,8 +198,24 @@ declare class HttpClient {
|
|
|
184
198
|
delete<T>(path: string, params?: Record<string, any>): Promise<HttpResponse<T>>;
|
|
185
199
|
}
|
|
186
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Platform detection and cross-platform utilities
|
|
203
|
+
* Safe to evaluate in any JS environment (browser, Node, React Native, Expo Snack)
|
|
204
|
+
*/
|
|
205
|
+
/** Storage adapter interface - allows custom storage (AsyncStorage, MMKV, etc.) */
|
|
206
|
+
interface StorageAdapter {
|
|
207
|
+
getItem(key: string): string | null | Promise<string | null>;
|
|
208
|
+
setItem(key: string, value: string): void | Promise<void>;
|
|
209
|
+
removeItem(key: string): void | Promise<void>;
|
|
210
|
+
}
|
|
211
|
+
/** Check if running in a browser with DOM APIs */
|
|
212
|
+
declare function isBrowser(): boolean;
|
|
213
|
+
/** Check if running in React Native */
|
|
214
|
+
declare function isReactNative(): boolean;
|
|
215
|
+
|
|
187
216
|
/**
|
|
188
217
|
* Auth Module - User authentication for Krisspy backends
|
|
218
|
+
* Compatible with Browser, React Native, and Node.js
|
|
189
219
|
*/
|
|
190
220
|
|
|
191
221
|
declare class KrisspyAuth {
|
|
@@ -195,11 +225,13 @@ declare class KrisspyAuth {
|
|
|
195
225
|
private currentUser;
|
|
196
226
|
private listeners;
|
|
197
227
|
private refreshInterval?;
|
|
198
|
-
|
|
228
|
+
private storage;
|
|
229
|
+
constructor(http: HttpClient, backendId: string, storage?: StorageAdapter);
|
|
199
230
|
/**
|
|
200
231
|
* Get current session from storage
|
|
201
232
|
*/
|
|
202
233
|
private restoreSession;
|
|
234
|
+
private hydrateSession;
|
|
203
235
|
/**
|
|
204
236
|
* Store session
|
|
205
237
|
*/
|
|
@@ -299,6 +331,7 @@ declare class KrisspyAuth {
|
|
|
299
331
|
};
|
|
300
332
|
/**
|
|
301
333
|
* Set session from external source (e.g., OAuth callback)
|
|
334
|
+
* Browser-only: parses tokens from URL hash
|
|
302
335
|
*/
|
|
303
336
|
setSessionFromUrl(): Promise<AuthResponse>;
|
|
304
337
|
}
|
|
@@ -704,6 +737,7 @@ declare class KrisspyRealtime {
|
|
|
704
737
|
*
|
|
705
738
|
* Tracks page views, sessions, custom events. Sends batched events
|
|
706
739
|
* to the analytics ingest endpoint. Works with SPAs (intercepts pushState).
|
|
740
|
+
* Browser-only module - safely no-ops in React Native / Node.js.
|
|
707
741
|
*
|
|
708
742
|
* @example
|
|
709
743
|
* const krisspy = createClient({ backendId: '...', anonKey: '...' })
|
|
@@ -744,25 +778,15 @@ declare class KrisspyAnalytics {
|
|
|
744
778
|
constructor(http: HttpClient, backendId: string);
|
|
745
779
|
/**
|
|
746
780
|
* Initialize analytics tracking
|
|
747
|
-
*
|
|
748
|
-
* @example
|
|
749
|
-
* krisspy.analytics.init()
|
|
750
|
-
* krisspy.analytics.init({ autoTrackPageViews: true, flushInterval: 3000 })
|
|
781
|
+
* No-ops in non-browser environments (React Native, Node.js)
|
|
751
782
|
*/
|
|
752
783
|
init(options?: AnalyticsInitOptions): void;
|
|
753
784
|
/**
|
|
754
785
|
* 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
786
|
*/
|
|
760
787
|
track(eventName: string, properties?: Record<string, any>): void;
|
|
761
788
|
/**
|
|
762
789
|
* Identify a user (attach userId + traits to all future events)
|
|
763
|
-
*
|
|
764
|
-
* @example
|
|
765
|
-
* krisspy.analytics.identify('user_123', { name: 'John', plan: 'pro' })
|
|
766
790
|
*/
|
|
767
791
|
identify(userId: string, traits?: Record<string, any>): void;
|
|
768
792
|
/**
|
|
@@ -1182,4 +1206,4 @@ declare class KrisspyClient {
|
|
|
1182
1206
|
*/
|
|
1183
1207
|
declare function createClient(options: KrisspyClientOptions): KrisspyClient;
|
|
1184
1208
|
|
|
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 };
|
|
1209
|
+
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, type StorageAdapter, StorageBucket, type UploadAndLinkOptions, type UploadAndLinkResponse, type User, createClient, isBrowser, isReactNative };
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,20 @@ interface KrisspyClientOptions {
|
|
|
26
26
|
* When false, uses legacy /data/* endpoints which require backend owner auth.
|
|
27
27
|
*/
|
|
28
28
|
useRLS?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Custom storage adapter for session persistence.
|
|
31
|
+
* Use this in React Native with AsyncStorage or MMKV.
|
|
32
|
+
* Falls back to localStorage (browser) or in-memory storage.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* import AsyncStorage from '@react-native-async-storage/async-storage'
|
|
36
|
+
* const krisspy = createClient({ ..., storage: AsyncStorage })
|
|
37
|
+
*/
|
|
38
|
+
storage?: {
|
|
39
|
+
getItem(key: string): string | null | Promise<string | null>;
|
|
40
|
+
setItem(key: string, value: string): void | Promise<void>;
|
|
41
|
+
removeItem(key: string): void | Promise<void>;
|
|
42
|
+
};
|
|
29
43
|
}
|
|
30
44
|
interface User {
|
|
31
45
|
id: string;
|
|
@@ -184,8 +198,24 @@ declare class HttpClient {
|
|
|
184
198
|
delete<T>(path: string, params?: Record<string, any>): Promise<HttpResponse<T>>;
|
|
185
199
|
}
|
|
186
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Platform detection and cross-platform utilities
|
|
203
|
+
* Safe to evaluate in any JS environment (browser, Node, React Native, Expo Snack)
|
|
204
|
+
*/
|
|
205
|
+
/** Storage adapter interface - allows custom storage (AsyncStorage, MMKV, etc.) */
|
|
206
|
+
interface StorageAdapter {
|
|
207
|
+
getItem(key: string): string | null | Promise<string | null>;
|
|
208
|
+
setItem(key: string, value: string): void | Promise<void>;
|
|
209
|
+
removeItem(key: string): void | Promise<void>;
|
|
210
|
+
}
|
|
211
|
+
/** Check if running in a browser with DOM APIs */
|
|
212
|
+
declare function isBrowser(): boolean;
|
|
213
|
+
/** Check if running in React Native */
|
|
214
|
+
declare function isReactNative(): boolean;
|
|
215
|
+
|
|
187
216
|
/**
|
|
188
217
|
* Auth Module - User authentication for Krisspy backends
|
|
218
|
+
* Compatible with Browser, React Native, and Node.js
|
|
189
219
|
*/
|
|
190
220
|
|
|
191
221
|
declare class KrisspyAuth {
|
|
@@ -195,11 +225,13 @@ declare class KrisspyAuth {
|
|
|
195
225
|
private currentUser;
|
|
196
226
|
private listeners;
|
|
197
227
|
private refreshInterval?;
|
|
198
|
-
|
|
228
|
+
private storage;
|
|
229
|
+
constructor(http: HttpClient, backendId: string, storage?: StorageAdapter);
|
|
199
230
|
/**
|
|
200
231
|
* Get current session from storage
|
|
201
232
|
*/
|
|
202
233
|
private restoreSession;
|
|
234
|
+
private hydrateSession;
|
|
203
235
|
/**
|
|
204
236
|
* Store session
|
|
205
237
|
*/
|
|
@@ -299,6 +331,7 @@ declare class KrisspyAuth {
|
|
|
299
331
|
};
|
|
300
332
|
/**
|
|
301
333
|
* Set session from external source (e.g., OAuth callback)
|
|
334
|
+
* Browser-only: parses tokens from URL hash
|
|
302
335
|
*/
|
|
303
336
|
setSessionFromUrl(): Promise<AuthResponse>;
|
|
304
337
|
}
|
|
@@ -704,6 +737,7 @@ declare class KrisspyRealtime {
|
|
|
704
737
|
*
|
|
705
738
|
* Tracks page views, sessions, custom events. Sends batched events
|
|
706
739
|
* to the analytics ingest endpoint. Works with SPAs (intercepts pushState).
|
|
740
|
+
* Browser-only module - safely no-ops in React Native / Node.js.
|
|
707
741
|
*
|
|
708
742
|
* @example
|
|
709
743
|
* const krisspy = createClient({ backendId: '...', anonKey: '...' })
|
|
@@ -744,25 +778,15 @@ declare class KrisspyAnalytics {
|
|
|
744
778
|
constructor(http: HttpClient, backendId: string);
|
|
745
779
|
/**
|
|
746
780
|
* Initialize analytics tracking
|
|
747
|
-
*
|
|
748
|
-
* @example
|
|
749
|
-
* krisspy.analytics.init()
|
|
750
|
-
* krisspy.analytics.init({ autoTrackPageViews: true, flushInterval: 3000 })
|
|
781
|
+
* No-ops in non-browser environments (React Native, Node.js)
|
|
751
782
|
*/
|
|
752
783
|
init(options?: AnalyticsInitOptions): void;
|
|
753
784
|
/**
|
|
754
785
|
* 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
786
|
*/
|
|
760
787
|
track(eventName: string, properties?: Record<string, any>): void;
|
|
761
788
|
/**
|
|
762
789
|
* Identify a user (attach userId + traits to all future events)
|
|
763
|
-
*
|
|
764
|
-
* @example
|
|
765
|
-
* krisspy.analytics.identify('user_123', { name: 'John', plan: 'pro' })
|
|
766
790
|
*/
|
|
767
791
|
identify(userId: string, traits?: Record<string, any>): void;
|
|
768
792
|
/**
|
|
@@ -1182,4 +1206,4 @@ declare class KrisspyClient {
|
|
|
1182
1206
|
*/
|
|
1183
1207
|
declare function createClient(options: KrisspyClientOptions): KrisspyClient;
|
|
1184
1208
|
|
|
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 };
|
|
1209
|
+
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, type StorageAdapter, StorageBucket, type UploadAndLinkOptions, type UploadAndLinkResponse, type User, createClient, isBrowser, isReactNative };
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,9 @@ __export(index_exports, {
|
|
|
29
29
|
QueryBuilder: () => QueryBuilder,
|
|
30
30
|
RealtimeChannel: () => RealtimeChannel,
|
|
31
31
|
StorageBucket: () => StorageBucket,
|
|
32
|
-
createClient: () => createClient
|
|
32
|
+
createClient: () => createClient,
|
|
33
|
+
isBrowser: () => isBrowser,
|
|
34
|
+
isReactNative: () => isReactNative
|
|
33
35
|
});
|
|
34
36
|
module.exports = __toCommonJS(index_exports);
|
|
35
37
|
|
|
@@ -162,43 +164,136 @@ var HttpClient = class {
|
|
|
162
164
|
}
|
|
163
165
|
};
|
|
164
166
|
|
|
167
|
+
// src/platform.ts
|
|
168
|
+
function isBrowser() {
|
|
169
|
+
return typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement === "function";
|
|
170
|
+
}
|
|
171
|
+
function isReactNative() {
|
|
172
|
+
return typeof navigator !== "undefined" && typeof navigator.product === "string" && navigator.product === "ReactNative";
|
|
173
|
+
}
|
|
174
|
+
function getLocalStorage() {
|
|
175
|
+
if (!isBrowser()) return null;
|
|
176
|
+
try {
|
|
177
|
+
const testKey = "__krisspy_test__";
|
|
178
|
+
window.localStorage.setItem(testKey, "1");
|
|
179
|
+
window.localStorage.removeItem(testKey);
|
|
180
|
+
return window.localStorage;
|
|
181
|
+
} catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
var MemoryStorage = class {
|
|
186
|
+
constructor() {
|
|
187
|
+
this.store = /* @__PURE__ */ new Map();
|
|
188
|
+
}
|
|
189
|
+
getItem(key) {
|
|
190
|
+
return this.store.get(key) ?? null;
|
|
191
|
+
}
|
|
192
|
+
setItem(key, value) {
|
|
193
|
+
this.store.set(key, value);
|
|
194
|
+
}
|
|
195
|
+
removeItem(key) {
|
|
196
|
+
this.store.delete(key);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
function resolveStorage(custom) {
|
|
200
|
+
if (custom) return custom;
|
|
201
|
+
return getLocalStorage() ?? new MemoryStorage();
|
|
202
|
+
}
|
|
203
|
+
function base64Encode(input) {
|
|
204
|
+
if (typeof btoa === "function") {
|
|
205
|
+
return btoa(input);
|
|
206
|
+
}
|
|
207
|
+
if (typeof Buffer !== "undefined") {
|
|
208
|
+
return Buffer.from(input, "binary").toString("base64");
|
|
209
|
+
}
|
|
210
|
+
return manualBase64Encode(input);
|
|
211
|
+
}
|
|
212
|
+
function base64Decode(input) {
|
|
213
|
+
if (typeof atob === "function") {
|
|
214
|
+
return atob(input);
|
|
215
|
+
}
|
|
216
|
+
if (typeof Buffer !== "undefined") {
|
|
217
|
+
return Buffer.from(input, "base64").toString("binary");
|
|
218
|
+
}
|
|
219
|
+
return manualBase64Decode(input);
|
|
220
|
+
}
|
|
221
|
+
var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
|
222
|
+
function manualBase64Encode(input) {
|
|
223
|
+
let output = "";
|
|
224
|
+
for (let i = 0; i < input.length; i += 3) {
|
|
225
|
+
const a = input.charCodeAt(i);
|
|
226
|
+
const b = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
|
|
227
|
+
const c = i + 2 < input.length ? input.charCodeAt(i + 2) : 0;
|
|
228
|
+
output += CHARS[a >> 2];
|
|
229
|
+
output += CHARS[(a & 3) << 4 | b >> 4];
|
|
230
|
+
output += i + 1 < input.length ? CHARS[(b & 15) << 2 | c >> 6] : "=";
|
|
231
|
+
output += i + 2 < input.length ? CHARS[c & 63] : "=";
|
|
232
|
+
}
|
|
233
|
+
return output;
|
|
234
|
+
}
|
|
235
|
+
function manualBase64Decode(input) {
|
|
236
|
+
let output = "";
|
|
237
|
+
const str = input.replace(/=+$/, "");
|
|
238
|
+
for (let i = 0; i < str.length; i += 4) {
|
|
239
|
+
const a = CHARS.indexOf(str[i]);
|
|
240
|
+
const b = CHARS.indexOf(str[i + 1]);
|
|
241
|
+
const c = CHARS.indexOf(str[i + 2]);
|
|
242
|
+
const d = CHARS.indexOf(str[i + 3]);
|
|
243
|
+
output += String.fromCharCode(a << 2 | b >> 4);
|
|
244
|
+
if (c !== -1) output += String.fromCharCode((b & 15) << 4 | c >> 2);
|
|
245
|
+
if (d !== -1) output += String.fromCharCode((c & 3) << 6 | d);
|
|
246
|
+
}
|
|
247
|
+
return output;
|
|
248
|
+
}
|
|
249
|
+
|
|
165
250
|
// src/auth.ts
|
|
166
251
|
var STORAGE_KEY = "krisspy-auth-session";
|
|
167
252
|
var KrisspyAuth = class {
|
|
168
|
-
constructor(http, backendId) {
|
|
253
|
+
constructor(http, backendId, storage) {
|
|
169
254
|
this.currentSession = null;
|
|
170
255
|
this.currentUser = null;
|
|
171
256
|
this.listeners = [];
|
|
172
257
|
this.http = http;
|
|
173
258
|
this.backendId = backendId;
|
|
259
|
+
this.storage = resolveStorage(storage);
|
|
174
260
|
this.restoreSession();
|
|
175
261
|
}
|
|
176
262
|
/**
|
|
177
263
|
* Get current session from storage
|
|
178
264
|
*/
|
|
179
265
|
restoreSession() {
|
|
180
|
-
if (typeof window === "undefined") return;
|
|
181
266
|
try {
|
|
182
|
-
const
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
this.
|
|
267
|
+
const result = this.storage.getItem(`${STORAGE_KEY}-${this.backendId}`);
|
|
268
|
+
if (result instanceof Promise) {
|
|
269
|
+
result.then((stored) => {
|
|
270
|
+
if (stored) this.hydrateSession(stored);
|
|
271
|
+
}).catch(() => {
|
|
272
|
+
});
|
|
273
|
+
} else if (result) {
|
|
274
|
+
this.hydrateSession(result);
|
|
190
275
|
}
|
|
191
276
|
} catch (err) {
|
|
192
277
|
console.warn("[Krisspy Auth] Failed to restore session:", err);
|
|
193
278
|
}
|
|
194
279
|
}
|
|
280
|
+
hydrateSession(stored) {
|
|
281
|
+
try {
|
|
282
|
+
const session = JSON.parse(stored);
|
|
283
|
+
if (session.expires_at && Date.now() > session.expires_at * 1e3) {
|
|
284
|
+
this.clearSession();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
this.setSession(session);
|
|
288
|
+
} catch {
|
|
289
|
+
}
|
|
290
|
+
}
|
|
195
291
|
/**
|
|
196
292
|
* Store session
|
|
197
293
|
*/
|
|
198
294
|
saveSession(session) {
|
|
199
|
-
if (typeof window === "undefined") return;
|
|
200
295
|
try {
|
|
201
|
-
|
|
296
|
+
this.storage.setItem(`${STORAGE_KEY}-${this.backendId}`, JSON.stringify(session));
|
|
202
297
|
} catch (err) {
|
|
203
298
|
console.warn("[Krisspy Auth] Failed to save session:", err);
|
|
204
299
|
}
|
|
@@ -214,8 +309,9 @@ var KrisspyAuth = class {
|
|
|
214
309
|
clearInterval(this.refreshInterval);
|
|
215
310
|
this.refreshInterval = void 0;
|
|
216
311
|
}
|
|
217
|
-
|
|
218
|
-
|
|
312
|
+
try {
|
|
313
|
+
this.storage.removeItem(`${STORAGE_KEY}-${this.backendId}`);
|
|
314
|
+
} catch {
|
|
219
315
|
}
|
|
220
316
|
}
|
|
221
317
|
/**
|
|
@@ -320,7 +416,7 @@ var KrisspyAuth = class {
|
|
|
320
416
|
if (response.error) {
|
|
321
417
|
return { data: null, error: response.error };
|
|
322
418
|
}
|
|
323
|
-
if (
|
|
419
|
+
if (isBrowser() && response.data?.url) {
|
|
324
420
|
window.location.href = response.data.url;
|
|
325
421
|
}
|
|
326
422
|
return { data: response.data, error: null };
|
|
@@ -443,9 +539,10 @@ var KrisspyAuth = class {
|
|
|
443
539
|
}
|
|
444
540
|
/**
|
|
445
541
|
* Set session from external source (e.g., OAuth callback)
|
|
542
|
+
* Browser-only: parses tokens from URL hash
|
|
446
543
|
*/
|
|
447
544
|
async setSessionFromUrl() {
|
|
448
|
-
if (
|
|
545
|
+
if (!isBrowser()) {
|
|
449
546
|
return { data: { user: null, session: null }, error: { message: "Not in browser" } };
|
|
450
547
|
}
|
|
451
548
|
const hash = window.location.hash.substring(1);
|
|
@@ -456,7 +553,7 @@ var KrisspyAuth = class {
|
|
|
456
553
|
return { data: { user: null, session: null }, error: { message: "No access token in URL" } };
|
|
457
554
|
}
|
|
458
555
|
try {
|
|
459
|
-
const payload = JSON.parse(
|
|
556
|
+
const payload = JSON.parse(base64Decode(accessToken.split(".")[1]));
|
|
460
557
|
const session = {
|
|
461
558
|
access_token: accessToken,
|
|
462
559
|
refresh_token: refreshToken ?? void 0,
|
|
@@ -764,27 +861,31 @@ var StorageBucket = class {
|
|
|
764
861
|
};
|
|
765
862
|
}
|
|
766
863
|
}
|
|
767
|
-
// Helper: Convert Blob to base64
|
|
768
|
-
blobToBase64(blob) {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
864
|
+
// Helper: Convert Blob to base64 (cross-platform)
|
|
865
|
+
async blobToBase64(blob) {
|
|
866
|
+
if (typeof FileReader !== "undefined") {
|
|
867
|
+
return new Promise((resolve, reject) => {
|
|
868
|
+
const reader = new FileReader();
|
|
869
|
+
reader.onloadend = () => {
|
|
870
|
+
const result = reader.result;
|
|
871
|
+
const base64 = result.includes(",") ? result.split(",")[1] : result;
|
|
872
|
+
resolve(base64);
|
|
873
|
+
};
|
|
874
|
+
reader.onerror = reject;
|
|
875
|
+
reader.readAsDataURL(blob);
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
const buffer = await blob.arrayBuffer();
|
|
879
|
+
return this.arrayBufferToBase64(buffer);
|
|
779
880
|
}
|
|
780
|
-
// Helper: Convert ArrayBuffer to base64
|
|
881
|
+
// Helper: Convert ArrayBuffer to base64 (cross-platform)
|
|
781
882
|
arrayBufferToBase64(buffer) {
|
|
782
|
-
let binary = "";
|
|
783
883
|
const bytes = new Uint8Array(buffer);
|
|
884
|
+
let binary = "";
|
|
784
885
|
for (let i = 0; i < bytes.length; i++) {
|
|
785
886
|
binary += String.fromCharCode(bytes[i]);
|
|
786
887
|
}
|
|
787
|
-
return
|
|
888
|
+
return base64Encode(binary);
|
|
788
889
|
}
|
|
789
890
|
// Helper: Detect MIME type from file extension
|
|
790
891
|
detectMimeType(path) {
|
|
@@ -1163,14 +1264,11 @@ var KrisspyAnalytics = class {
|
|
|
1163
1264
|
}
|
|
1164
1265
|
/**
|
|
1165
1266
|
* Initialize analytics tracking
|
|
1166
|
-
*
|
|
1167
|
-
* @example
|
|
1168
|
-
* krisspy.analytics.init()
|
|
1169
|
-
* krisspy.analytics.init({ autoTrackPageViews: true, flushInterval: 3000 })
|
|
1267
|
+
* No-ops in non-browser environments (React Native, Node.js)
|
|
1170
1268
|
*/
|
|
1171
1269
|
init(options) {
|
|
1172
1270
|
if (this.initialized) return;
|
|
1173
|
-
if (
|
|
1271
|
+
if (!isBrowser()) return;
|
|
1174
1272
|
this.options = {
|
|
1175
1273
|
autoTrackPageViews: true,
|
|
1176
1274
|
autoTrackNavigation: true,
|
|
@@ -1182,7 +1280,7 @@ var KrisspyAnalytics = class {
|
|
|
1182
1280
|
this.initialized = true;
|
|
1183
1281
|
if (this.options.autoTrackPageViews) {
|
|
1184
1282
|
this.track("session_start", {});
|
|
1185
|
-
this.track("page_view", { url: location.href, referrer: document.referrer });
|
|
1283
|
+
this.track("page_view", { url: window.location.href, referrer: document.referrer });
|
|
1186
1284
|
}
|
|
1187
1285
|
if (this.options.autoTrackNavigation) {
|
|
1188
1286
|
this.setupNavigationTracking();
|
|
@@ -1199,13 +1297,9 @@ var KrisspyAnalytics = class {
|
|
|
1199
1297
|
}
|
|
1200
1298
|
/**
|
|
1201
1299
|
* 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
1300
|
*/
|
|
1207
1301
|
track(eventName, properties) {
|
|
1208
|
-
if (!this.initialized
|
|
1302
|
+
if (!this.initialized) return;
|
|
1209
1303
|
const event = {
|
|
1210
1304
|
eventName,
|
|
1211
1305
|
sessionId: this.sessionId,
|
|
@@ -1223,9 +1317,6 @@ var KrisspyAnalytics = class {
|
|
|
1223
1317
|
}
|
|
1224
1318
|
/**
|
|
1225
1319
|
* Identify a user (attach userId + traits to all future events)
|
|
1226
|
-
*
|
|
1227
|
-
* @example
|
|
1228
|
-
* krisspy.analytics.identify('user_123', { name: 'John', plan: 'pro' })
|
|
1229
1320
|
*/
|
|
1230
1321
|
identify(userId, traits) {
|
|
1231
1322
|
this.userId = userId;
|
|
@@ -1255,6 +1346,7 @@ var KrisspyAnalytics = class {
|
|
|
1255
1346
|
*/
|
|
1256
1347
|
flushSync() {
|
|
1257
1348
|
if (!this.queue.length) return;
|
|
1349
|
+
if (!isBrowser()) return;
|
|
1258
1350
|
const events = this.queue.splice(0, 50);
|
|
1259
1351
|
const payload = JSON.stringify({ appId: this.backendId, events });
|
|
1260
1352
|
try {
|
|
@@ -1283,17 +1375,19 @@ var KrisspyAnalytics = class {
|
|
|
1283
1375
|
clearInterval(this.flushTimer);
|
|
1284
1376
|
this.flushTimer = null;
|
|
1285
1377
|
}
|
|
1286
|
-
if (
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1378
|
+
if (isBrowser()) {
|
|
1379
|
+
if (this.originalPushState) {
|
|
1380
|
+
history.pushState = this.originalPushState;
|
|
1381
|
+
this.originalPushState = null;
|
|
1382
|
+
}
|
|
1383
|
+
if (this.popstateHandler) {
|
|
1384
|
+
window.removeEventListener("popstate", this.popstateHandler);
|
|
1385
|
+
this.popstateHandler = null;
|
|
1386
|
+
}
|
|
1387
|
+
if (this.unloadHandler) {
|
|
1388
|
+
window.removeEventListener("beforeunload", this.unloadHandler);
|
|
1389
|
+
this.unloadHandler = null;
|
|
1390
|
+
}
|
|
1297
1391
|
}
|
|
1298
1392
|
this.initialized = false;
|
|
1299
1393
|
this.queue = [];
|
|
@@ -1301,31 +1395,34 @@ var KrisspyAnalytics = class {
|
|
|
1301
1395
|
console.log("[Krisspy Analytics] Destroyed");
|
|
1302
1396
|
}
|
|
1303
1397
|
}
|
|
1304
|
-
//
|
|
1398
|
+
// -- Private helpers --------------------------------------------------------
|
|
1305
1399
|
getOrCreateSessionId() {
|
|
1306
1400
|
const key = "_ka_sid";
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1401
|
+
if (isBrowser()) {
|
|
1402
|
+
try {
|
|
1403
|
+
const existing = window.localStorage.getItem(key);
|
|
1404
|
+
if (existing) return existing;
|
|
1405
|
+
const id = Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
|
|
1406
|
+
window.localStorage.setItem(key, id);
|
|
1407
|
+
return id;
|
|
1408
|
+
} catch {
|
|
1409
|
+
}
|
|
1315
1410
|
}
|
|
1411
|
+
return Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
|
|
1316
1412
|
}
|
|
1317
1413
|
getIngestUrl() {
|
|
1318
1414
|
return `${this.http.baseUrl}/api/v1/analytics/events`;
|
|
1319
1415
|
}
|
|
1320
1416
|
setupNavigationTracking() {
|
|
1321
|
-
|
|
1417
|
+
if (!isBrowser()) return;
|
|
1418
|
+
this.originalPushState = history.pushState.bind(history);
|
|
1322
1419
|
const self = this;
|
|
1323
1420
|
history.pushState = function(...args) {
|
|
1324
|
-
self.originalPushState
|
|
1325
|
-
self.track("page_view", { url: location.href });
|
|
1421
|
+
self.originalPushState(...args);
|
|
1422
|
+
self.track("page_view", { url: window.location.href });
|
|
1326
1423
|
};
|
|
1327
1424
|
this.popstateHandler = () => {
|
|
1328
|
-
this.track("page_view", { url: location.href });
|
|
1425
|
+
this.track("page_view", { url: window.location.href });
|
|
1329
1426
|
};
|
|
1330
1427
|
window.addEventListener("popstate", this.popstateHandler);
|
|
1331
1428
|
}
|
|
@@ -1622,7 +1719,7 @@ var KrisspyClient = class {
|
|
|
1622
1719
|
},
|
|
1623
1720
|
debug: options.debug
|
|
1624
1721
|
});
|
|
1625
|
-
this._auth = new KrisspyAuth(this.http, this.backendId);
|
|
1722
|
+
this._auth = new KrisspyAuth(this.http, this.backendId, options.storage);
|
|
1626
1723
|
this._storage = new KrisspyStorage(this.http, this.backendId);
|
|
1627
1724
|
this._realtime = new KrisspyRealtime(this.baseUrl, this.backendId, this.debug);
|
|
1628
1725
|
this._analytics = new KrisspyAnalytics(this.http, this.backendId);
|
|
@@ -1881,5 +1978,7 @@ function createClient(options) {
|
|
|
1881
1978
|
QueryBuilder,
|
|
1882
1979
|
RealtimeChannel,
|
|
1883
1980
|
StorageBucket,
|
|
1884
|
-
createClient
|
|
1981
|
+
createClient,
|
|
1982
|
+
isBrowser,
|
|
1983
|
+
isReactNative
|
|
1885
1984
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -127,43 +127,136 @@ var HttpClient = class {
|
|
|
127
127
|
}
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
+
// src/platform.ts
|
|
131
|
+
function isBrowser() {
|
|
132
|
+
return typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement === "function";
|
|
133
|
+
}
|
|
134
|
+
function isReactNative() {
|
|
135
|
+
return typeof navigator !== "undefined" && typeof navigator.product === "string" && navigator.product === "ReactNative";
|
|
136
|
+
}
|
|
137
|
+
function getLocalStorage() {
|
|
138
|
+
if (!isBrowser()) return null;
|
|
139
|
+
try {
|
|
140
|
+
const testKey = "__krisspy_test__";
|
|
141
|
+
window.localStorage.setItem(testKey, "1");
|
|
142
|
+
window.localStorage.removeItem(testKey);
|
|
143
|
+
return window.localStorage;
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
var MemoryStorage = class {
|
|
149
|
+
constructor() {
|
|
150
|
+
this.store = /* @__PURE__ */ new Map();
|
|
151
|
+
}
|
|
152
|
+
getItem(key) {
|
|
153
|
+
return this.store.get(key) ?? null;
|
|
154
|
+
}
|
|
155
|
+
setItem(key, value) {
|
|
156
|
+
this.store.set(key, value);
|
|
157
|
+
}
|
|
158
|
+
removeItem(key) {
|
|
159
|
+
this.store.delete(key);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
function resolveStorage(custom) {
|
|
163
|
+
if (custom) return custom;
|
|
164
|
+
return getLocalStorage() ?? new MemoryStorage();
|
|
165
|
+
}
|
|
166
|
+
function base64Encode(input) {
|
|
167
|
+
if (typeof btoa === "function") {
|
|
168
|
+
return btoa(input);
|
|
169
|
+
}
|
|
170
|
+
if (typeof Buffer !== "undefined") {
|
|
171
|
+
return Buffer.from(input, "binary").toString("base64");
|
|
172
|
+
}
|
|
173
|
+
return manualBase64Encode(input);
|
|
174
|
+
}
|
|
175
|
+
function base64Decode(input) {
|
|
176
|
+
if (typeof atob === "function") {
|
|
177
|
+
return atob(input);
|
|
178
|
+
}
|
|
179
|
+
if (typeof Buffer !== "undefined") {
|
|
180
|
+
return Buffer.from(input, "base64").toString("binary");
|
|
181
|
+
}
|
|
182
|
+
return manualBase64Decode(input);
|
|
183
|
+
}
|
|
184
|
+
var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
|
185
|
+
function manualBase64Encode(input) {
|
|
186
|
+
let output = "";
|
|
187
|
+
for (let i = 0; i < input.length; i += 3) {
|
|
188
|
+
const a = input.charCodeAt(i);
|
|
189
|
+
const b = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
|
|
190
|
+
const c = i + 2 < input.length ? input.charCodeAt(i + 2) : 0;
|
|
191
|
+
output += CHARS[a >> 2];
|
|
192
|
+
output += CHARS[(a & 3) << 4 | b >> 4];
|
|
193
|
+
output += i + 1 < input.length ? CHARS[(b & 15) << 2 | c >> 6] : "=";
|
|
194
|
+
output += i + 2 < input.length ? CHARS[c & 63] : "=";
|
|
195
|
+
}
|
|
196
|
+
return output;
|
|
197
|
+
}
|
|
198
|
+
function manualBase64Decode(input) {
|
|
199
|
+
let output = "";
|
|
200
|
+
const str = input.replace(/=+$/, "");
|
|
201
|
+
for (let i = 0; i < str.length; i += 4) {
|
|
202
|
+
const a = CHARS.indexOf(str[i]);
|
|
203
|
+
const b = CHARS.indexOf(str[i + 1]);
|
|
204
|
+
const c = CHARS.indexOf(str[i + 2]);
|
|
205
|
+
const d = CHARS.indexOf(str[i + 3]);
|
|
206
|
+
output += String.fromCharCode(a << 2 | b >> 4);
|
|
207
|
+
if (c !== -1) output += String.fromCharCode((b & 15) << 4 | c >> 2);
|
|
208
|
+
if (d !== -1) output += String.fromCharCode((c & 3) << 6 | d);
|
|
209
|
+
}
|
|
210
|
+
return output;
|
|
211
|
+
}
|
|
212
|
+
|
|
130
213
|
// src/auth.ts
|
|
131
214
|
var STORAGE_KEY = "krisspy-auth-session";
|
|
132
215
|
var KrisspyAuth = class {
|
|
133
|
-
constructor(http, backendId) {
|
|
216
|
+
constructor(http, backendId, storage) {
|
|
134
217
|
this.currentSession = null;
|
|
135
218
|
this.currentUser = null;
|
|
136
219
|
this.listeners = [];
|
|
137
220
|
this.http = http;
|
|
138
221
|
this.backendId = backendId;
|
|
222
|
+
this.storage = resolveStorage(storage);
|
|
139
223
|
this.restoreSession();
|
|
140
224
|
}
|
|
141
225
|
/**
|
|
142
226
|
* Get current session from storage
|
|
143
227
|
*/
|
|
144
228
|
restoreSession() {
|
|
145
|
-
if (typeof window === "undefined") return;
|
|
146
229
|
try {
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
this.
|
|
230
|
+
const result = this.storage.getItem(`${STORAGE_KEY}-${this.backendId}`);
|
|
231
|
+
if (result instanceof Promise) {
|
|
232
|
+
result.then((stored) => {
|
|
233
|
+
if (stored) this.hydrateSession(stored);
|
|
234
|
+
}).catch(() => {
|
|
235
|
+
});
|
|
236
|
+
} else if (result) {
|
|
237
|
+
this.hydrateSession(result);
|
|
155
238
|
}
|
|
156
239
|
} catch (err) {
|
|
157
240
|
console.warn("[Krisspy Auth] Failed to restore session:", err);
|
|
158
241
|
}
|
|
159
242
|
}
|
|
243
|
+
hydrateSession(stored) {
|
|
244
|
+
try {
|
|
245
|
+
const session = JSON.parse(stored);
|
|
246
|
+
if (session.expires_at && Date.now() > session.expires_at * 1e3) {
|
|
247
|
+
this.clearSession();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
this.setSession(session);
|
|
251
|
+
} catch {
|
|
252
|
+
}
|
|
253
|
+
}
|
|
160
254
|
/**
|
|
161
255
|
* Store session
|
|
162
256
|
*/
|
|
163
257
|
saveSession(session) {
|
|
164
|
-
if (typeof window === "undefined") return;
|
|
165
258
|
try {
|
|
166
|
-
|
|
259
|
+
this.storage.setItem(`${STORAGE_KEY}-${this.backendId}`, JSON.stringify(session));
|
|
167
260
|
} catch (err) {
|
|
168
261
|
console.warn("[Krisspy Auth] Failed to save session:", err);
|
|
169
262
|
}
|
|
@@ -179,8 +272,9 @@ var KrisspyAuth = class {
|
|
|
179
272
|
clearInterval(this.refreshInterval);
|
|
180
273
|
this.refreshInterval = void 0;
|
|
181
274
|
}
|
|
182
|
-
|
|
183
|
-
|
|
275
|
+
try {
|
|
276
|
+
this.storage.removeItem(`${STORAGE_KEY}-${this.backendId}`);
|
|
277
|
+
} catch {
|
|
184
278
|
}
|
|
185
279
|
}
|
|
186
280
|
/**
|
|
@@ -285,7 +379,7 @@ var KrisspyAuth = class {
|
|
|
285
379
|
if (response.error) {
|
|
286
380
|
return { data: null, error: response.error };
|
|
287
381
|
}
|
|
288
|
-
if (
|
|
382
|
+
if (isBrowser() && response.data?.url) {
|
|
289
383
|
window.location.href = response.data.url;
|
|
290
384
|
}
|
|
291
385
|
return { data: response.data, error: null };
|
|
@@ -408,9 +502,10 @@ var KrisspyAuth = class {
|
|
|
408
502
|
}
|
|
409
503
|
/**
|
|
410
504
|
* Set session from external source (e.g., OAuth callback)
|
|
505
|
+
* Browser-only: parses tokens from URL hash
|
|
411
506
|
*/
|
|
412
507
|
async setSessionFromUrl() {
|
|
413
|
-
if (
|
|
508
|
+
if (!isBrowser()) {
|
|
414
509
|
return { data: { user: null, session: null }, error: { message: "Not in browser" } };
|
|
415
510
|
}
|
|
416
511
|
const hash = window.location.hash.substring(1);
|
|
@@ -421,7 +516,7 @@ var KrisspyAuth = class {
|
|
|
421
516
|
return { data: { user: null, session: null }, error: { message: "No access token in URL" } };
|
|
422
517
|
}
|
|
423
518
|
try {
|
|
424
|
-
const payload = JSON.parse(
|
|
519
|
+
const payload = JSON.parse(base64Decode(accessToken.split(".")[1]));
|
|
425
520
|
const session = {
|
|
426
521
|
access_token: accessToken,
|
|
427
522
|
refresh_token: refreshToken ?? void 0,
|
|
@@ -729,27 +824,31 @@ var StorageBucket = class {
|
|
|
729
824
|
};
|
|
730
825
|
}
|
|
731
826
|
}
|
|
732
|
-
// Helper: Convert Blob to base64
|
|
733
|
-
blobToBase64(blob) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
827
|
+
// Helper: Convert Blob to base64 (cross-platform)
|
|
828
|
+
async blobToBase64(blob) {
|
|
829
|
+
if (typeof FileReader !== "undefined") {
|
|
830
|
+
return new Promise((resolve, reject) => {
|
|
831
|
+
const reader = new FileReader();
|
|
832
|
+
reader.onloadend = () => {
|
|
833
|
+
const result = reader.result;
|
|
834
|
+
const base64 = result.includes(",") ? result.split(",")[1] : result;
|
|
835
|
+
resolve(base64);
|
|
836
|
+
};
|
|
837
|
+
reader.onerror = reject;
|
|
838
|
+
reader.readAsDataURL(blob);
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
const buffer = await blob.arrayBuffer();
|
|
842
|
+
return this.arrayBufferToBase64(buffer);
|
|
744
843
|
}
|
|
745
|
-
// Helper: Convert ArrayBuffer to base64
|
|
844
|
+
// Helper: Convert ArrayBuffer to base64 (cross-platform)
|
|
746
845
|
arrayBufferToBase64(buffer) {
|
|
747
|
-
let binary = "";
|
|
748
846
|
const bytes = new Uint8Array(buffer);
|
|
847
|
+
let binary = "";
|
|
749
848
|
for (let i = 0; i < bytes.length; i++) {
|
|
750
849
|
binary += String.fromCharCode(bytes[i]);
|
|
751
850
|
}
|
|
752
|
-
return
|
|
851
|
+
return base64Encode(binary);
|
|
753
852
|
}
|
|
754
853
|
// Helper: Detect MIME type from file extension
|
|
755
854
|
detectMimeType(path) {
|
|
@@ -1128,14 +1227,11 @@ var KrisspyAnalytics = class {
|
|
|
1128
1227
|
}
|
|
1129
1228
|
/**
|
|
1130
1229
|
* Initialize analytics tracking
|
|
1131
|
-
*
|
|
1132
|
-
* @example
|
|
1133
|
-
* krisspy.analytics.init()
|
|
1134
|
-
* krisspy.analytics.init({ autoTrackPageViews: true, flushInterval: 3000 })
|
|
1230
|
+
* No-ops in non-browser environments (React Native, Node.js)
|
|
1135
1231
|
*/
|
|
1136
1232
|
init(options) {
|
|
1137
1233
|
if (this.initialized) return;
|
|
1138
|
-
if (
|
|
1234
|
+
if (!isBrowser()) return;
|
|
1139
1235
|
this.options = {
|
|
1140
1236
|
autoTrackPageViews: true,
|
|
1141
1237
|
autoTrackNavigation: true,
|
|
@@ -1147,7 +1243,7 @@ var KrisspyAnalytics = class {
|
|
|
1147
1243
|
this.initialized = true;
|
|
1148
1244
|
if (this.options.autoTrackPageViews) {
|
|
1149
1245
|
this.track("session_start", {});
|
|
1150
|
-
this.track("page_view", { url: location.href, referrer: document.referrer });
|
|
1246
|
+
this.track("page_view", { url: window.location.href, referrer: document.referrer });
|
|
1151
1247
|
}
|
|
1152
1248
|
if (this.options.autoTrackNavigation) {
|
|
1153
1249
|
this.setupNavigationTracking();
|
|
@@ -1164,13 +1260,9 @@ var KrisspyAnalytics = class {
|
|
|
1164
1260
|
}
|
|
1165
1261
|
/**
|
|
1166
1262
|
* 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
1263
|
*/
|
|
1172
1264
|
track(eventName, properties) {
|
|
1173
|
-
if (!this.initialized
|
|
1265
|
+
if (!this.initialized) return;
|
|
1174
1266
|
const event = {
|
|
1175
1267
|
eventName,
|
|
1176
1268
|
sessionId: this.sessionId,
|
|
@@ -1188,9 +1280,6 @@ var KrisspyAnalytics = class {
|
|
|
1188
1280
|
}
|
|
1189
1281
|
/**
|
|
1190
1282
|
* Identify a user (attach userId + traits to all future events)
|
|
1191
|
-
*
|
|
1192
|
-
* @example
|
|
1193
|
-
* krisspy.analytics.identify('user_123', { name: 'John', plan: 'pro' })
|
|
1194
1283
|
*/
|
|
1195
1284
|
identify(userId, traits) {
|
|
1196
1285
|
this.userId = userId;
|
|
@@ -1220,6 +1309,7 @@ var KrisspyAnalytics = class {
|
|
|
1220
1309
|
*/
|
|
1221
1310
|
flushSync() {
|
|
1222
1311
|
if (!this.queue.length) return;
|
|
1312
|
+
if (!isBrowser()) return;
|
|
1223
1313
|
const events = this.queue.splice(0, 50);
|
|
1224
1314
|
const payload = JSON.stringify({ appId: this.backendId, events });
|
|
1225
1315
|
try {
|
|
@@ -1248,17 +1338,19 @@ var KrisspyAnalytics = class {
|
|
|
1248
1338
|
clearInterval(this.flushTimer);
|
|
1249
1339
|
this.flushTimer = null;
|
|
1250
1340
|
}
|
|
1251
|
-
if (
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1341
|
+
if (isBrowser()) {
|
|
1342
|
+
if (this.originalPushState) {
|
|
1343
|
+
history.pushState = this.originalPushState;
|
|
1344
|
+
this.originalPushState = null;
|
|
1345
|
+
}
|
|
1346
|
+
if (this.popstateHandler) {
|
|
1347
|
+
window.removeEventListener("popstate", this.popstateHandler);
|
|
1348
|
+
this.popstateHandler = null;
|
|
1349
|
+
}
|
|
1350
|
+
if (this.unloadHandler) {
|
|
1351
|
+
window.removeEventListener("beforeunload", this.unloadHandler);
|
|
1352
|
+
this.unloadHandler = null;
|
|
1353
|
+
}
|
|
1262
1354
|
}
|
|
1263
1355
|
this.initialized = false;
|
|
1264
1356
|
this.queue = [];
|
|
@@ -1266,31 +1358,34 @@ var KrisspyAnalytics = class {
|
|
|
1266
1358
|
console.log("[Krisspy Analytics] Destroyed");
|
|
1267
1359
|
}
|
|
1268
1360
|
}
|
|
1269
|
-
//
|
|
1361
|
+
// -- Private helpers --------------------------------------------------------
|
|
1270
1362
|
getOrCreateSessionId() {
|
|
1271
1363
|
const key = "_ka_sid";
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1364
|
+
if (isBrowser()) {
|
|
1365
|
+
try {
|
|
1366
|
+
const existing = window.localStorage.getItem(key);
|
|
1367
|
+
if (existing) return existing;
|
|
1368
|
+
const id = Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
|
|
1369
|
+
window.localStorage.setItem(key, id);
|
|
1370
|
+
return id;
|
|
1371
|
+
} catch {
|
|
1372
|
+
}
|
|
1280
1373
|
}
|
|
1374
|
+
return Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
|
|
1281
1375
|
}
|
|
1282
1376
|
getIngestUrl() {
|
|
1283
1377
|
return `${this.http.baseUrl}/api/v1/analytics/events`;
|
|
1284
1378
|
}
|
|
1285
1379
|
setupNavigationTracking() {
|
|
1286
|
-
|
|
1380
|
+
if (!isBrowser()) return;
|
|
1381
|
+
this.originalPushState = history.pushState.bind(history);
|
|
1287
1382
|
const self = this;
|
|
1288
1383
|
history.pushState = function(...args) {
|
|
1289
|
-
self.originalPushState
|
|
1290
|
-
self.track("page_view", { url: location.href });
|
|
1384
|
+
self.originalPushState(...args);
|
|
1385
|
+
self.track("page_view", { url: window.location.href });
|
|
1291
1386
|
};
|
|
1292
1387
|
this.popstateHandler = () => {
|
|
1293
|
-
this.track("page_view", { url: location.href });
|
|
1388
|
+
this.track("page_view", { url: window.location.href });
|
|
1294
1389
|
};
|
|
1295
1390
|
window.addEventListener("popstate", this.popstateHandler);
|
|
1296
1391
|
}
|
|
@@ -1587,7 +1682,7 @@ var KrisspyClient = class {
|
|
|
1587
1682
|
},
|
|
1588
1683
|
debug: options.debug
|
|
1589
1684
|
});
|
|
1590
|
-
this._auth = new KrisspyAuth(this.http, this.backendId);
|
|
1685
|
+
this._auth = new KrisspyAuth(this.http, this.backendId, options.storage);
|
|
1591
1686
|
this._storage = new KrisspyStorage(this.http, this.backendId);
|
|
1592
1687
|
this._realtime = new KrisspyRealtime(this.baseUrl, this.backendId, this.debug);
|
|
1593
1688
|
this._analytics = new KrisspyAnalytics(this.http, this.backendId);
|
|
@@ -1845,5 +1940,7 @@ export {
|
|
|
1845
1940
|
QueryBuilder,
|
|
1846
1941
|
RealtimeChannel,
|
|
1847
1942
|
StorageBucket,
|
|
1848
|
-
createClient
|
|
1943
|
+
createClient,
|
|
1944
|
+
isBrowser,
|
|
1945
|
+
isReactNative
|
|
1849
1946
|
};
|