@kerne/react 0.1.1 → 0.1.3

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.js CHANGED
@@ -1,655 +1,27 @@
1
- // src/provider.tsx
2
- import { createContext, useEffect, useMemo, useRef } from "react";
3
- import { Kerne } from "@kerne/server";
4
- import { jsx } from "react/jsx-runtime";
5
- var DEFAULT_STORAGE_KEY = "kerne_auth";
6
- var DEFAULT_REFRESH_BUFFER = 60;
7
- var KerneClient = class {
8
- kerne;
9
- storage;
10
- storageKey;
11
- onAuthChange;
12
- autoRefresh;
13
- refreshBuffer;
14
- refreshTimer = null;
15
- listeners = /* @__PURE__ */ new Set();
16
- _user = null;
17
- _token = null;
18
- _refreshToken = null;
19
- _expiresAt = null;
20
- _isLoading = true;
21
- // Public config properties
22
- baseUrl;
23
- appId;
24
- constructor(config) {
25
- this.baseUrl = config.baseUrl ?? "https://api.kerne.io";
26
- this.appId = config.appId;
27
- this.storage = config.storage ?? (typeof window !== "undefined" ? localStorage : null);
28
- this.storageKey = config.storageKey ?? DEFAULT_STORAGE_KEY;
29
- this.onAuthChange = config.onAuthChange;
30
- this.autoRefresh = config.autoRefresh ?? true;
31
- this.refreshBuffer = config.refreshBuffer ?? DEFAULT_REFRESH_BUFFER;
32
- this.kerne = new Kerne({
33
- baseUrl: this.baseUrl,
34
- appId: this.appId,
35
- timeout: config.timeout,
36
- onUnauthorized: () => {
37
- if (process.env.NODE_ENV === "development") {
38
- console.warn("[Kerne] 401 received. Auto-logout disabled in dev.");
39
- return;
40
- }
41
- this.clearSession();
42
- }
43
- });
44
- this.loadSession();
45
- }
46
- // ============================================================================
47
- // Reactive State (useSyncExternalStore compatible)
48
- // ============================================================================
49
- subscribe(listener) {
50
- this.listeners.add(listener);
51
- return () => this.listeners.delete(listener);
52
- }
53
- notify() {
54
- this.listeners.forEach((l) => l());
55
- }
56
- get user() {
57
- return this._user;
58
- }
59
- get token() {
60
- return this._token;
61
- }
62
- get isAuthenticated() {
63
- return this._token !== null;
64
- }
65
- get isLoading() {
66
- return this._isLoading;
67
- }
68
- // ============================================================================
69
- // Session Management
70
- // ============================================================================
71
- loadSession() {
72
- if (!this.storage) {
73
- this._isLoading = false;
74
- this.notify();
75
- return;
76
- }
77
- try {
78
- const data = this.storage.getItem(this.storageKey);
79
- if (data) {
80
- const { user, token, refresh_token, expires_at } = JSON.parse(data);
81
- const expiresAt = expires_at ? new Date(expires_at) : null;
82
- if (!expiresAt || expiresAt > /* @__PURE__ */ new Date()) {
83
- this._user = user;
84
- this._token = token;
85
- this._refreshToken = refresh_token ?? null;
86
- this._expiresAt = expiresAt;
87
- this.kerne = this.kerne.withToken(token);
88
- this.scheduleRefresh();
89
- } else {
90
- this.storage.removeItem(this.storageKey);
91
- }
92
- }
93
- } catch (e) {
94
- console.error("[Kerne] Failed to load session:", e);
95
- this.storage?.removeItem(this.storageKey);
96
- }
97
- this._isLoading = false;
98
- this.notify();
99
- }
100
- /**
101
- * `AuthResponse.user` is the minimal `{id, email, role}` claim set the
102
- * token endpoint returns, not the full profile (`User`) - so this always
103
- * follows up with `users.me()` rather than assigning it directly (that
104
- * used to leave `_user` looking like a `User` while actually missing
105
- * `email_verified`/`first_name`/`avatar`/etc).
106
- */
107
- async saveSession(response) {
108
- this._token = response.token;
109
- this._refreshToken = response.refresh_token;
110
- this._expiresAt = response.expires_at ? new Date(response.expires_at) : null;
111
- this.kerne = this.kerne.withToken(response.token);
112
- this._user = await this.kerne.users.me();
113
- if (this.storage) {
114
- this.storage.setItem(
115
- this.storageKey,
116
- JSON.stringify({
117
- user: this._user,
118
- token: response.token,
119
- refresh_token: response.refresh_token,
120
- expires_at: response.expires_at
121
- })
122
- );
123
- }
124
- this.scheduleRefresh();
125
- this.onAuthChange?.(this._user);
126
- this.notify();
127
- }
128
- clearSession() {
129
- this._user = null;
130
- this._token = null;
131
- this._refreshToken = null;
132
- this._expiresAt = null;
133
- this.cancelRefresh();
134
- if (this.storage) {
135
- this.storage.removeItem(this.storageKey);
136
- }
137
- this.onAuthChange?.(null);
138
- this.notify();
139
- }
140
- // ============================================================================
141
- // Auto-Refresh
142
- // ============================================================================
143
- scheduleRefresh() {
144
- if (!this.autoRefresh || !this._expiresAt) return;
145
- this.cancelRefresh();
146
- const now = Date.now();
147
- const expiry = this._expiresAt.getTime();
148
- const refreshAt = expiry - this.refreshBuffer * 1e3;
149
- const delay = Math.max(0, refreshAt - now);
150
- if (delay > 0) {
151
- this.refreshTimer = setTimeout(() => this.silentRefresh(), delay);
152
- }
153
- }
154
- cancelRefresh() {
155
- if (this.refreshTimer) {
156
- clearTimeout(this.refreshTimer);
157
- this.refreshTimer = null;
158
- }
159
- }
160
- async silentRefresh() {
161
- if (!this._refreshToken) return;
162
- try {
163
- const response = await this.kerne.auth.refreshToken({ refresh_token: this._refreshToken });
164
- await this.saveSession(response);
165
- console.debug("[Kerne] Session refreshed silently");
166
- } catch (e) {
167
- console.warn("[Kerne] Silent refresh failed:", e);
168
- }
169
- }
170
- // ============================================================================
171
- // Auth Methods
172
- // ============================================================================
173
- async register(params) {
174
- const [firstName, ...lastNameParts] = (params.name || "").split(" ");
175
- const response = await this.kerne.auth.signup({
176
- email: params.email,
177
- password: params.password,
178
- first_name: firstName || void 0,
179
- last_name: lastNameParts.join(" ") || void 0,
180
- invitationToken: params.invitationToken,
181
- invitationCode: params.invitationCode
182
- });
183
- await this.saveSession(response);
184
- return response;
185
- }
186
- async login(params) {
187
- const response = await this.kerne.auth.login(params);
188
- await this.saveSession(response);
189
- return response;
190
- }
191
- /** Manually trigger a token refresh - normally handled automatically by `autoRefresh`. */
192
- async refreshToken() {
193
- if (!this._refreshToken) return null;
194
- const response = await this.kerne.auth.refreshToken({ refresh_token: this._refreshToken });
195
- await this.saveSession(response);
196
- return response;
197
- }
198
- logout() {
199
- this.clearSession();
200
- }
201
- async refreshUser() {
202
- if (!this._token) return null;
203
- try {
204
- const user = await this.kerne.users.me();
205
- this._user = user;
206
- this.onAuthChange?.(user);
207
- this.notify();
208
- return user;
209
- } catch {
210
- this.clearSession();
211
- return null;
212
- }
213
- }
214
- // No `avatar` here - UpdateUserDto doesn't accept it server-side
215
- // (`forbidNonWhitelisted` rejects the field with a 400).
216
- async updateProfile(params) {
217
- if (!this._user) throw new Error("User not authenticated");
218
- const updated = await this.kerne.users.update(this._user.id, params);
219
- this._user = updated;
220
- if (this.storage && this._token) {
221
- try {
222
- const data = this.storage.getItem(this.storageKey);
223
- if (data) {
224
- const session = JSON.parse(data);
225
- session.user = updated;
226
- this.storage.setItem(this.storageKey, JSON.stringify(session));
227
- }
228
- } catch (e) {
229
- }
230
- }
231
- this.onAuthChange?.(updated);
232
- this.notify();
233
- return updated;
234
- }
235
- async updatePassword(params) {
236
- await this.kerne.auth.changePassword(params);
237
- }
238
- /** Forgot-password flow (logged out) - sends the reset email. */
239
- async requestPasswordReset(email) {
240
- await this.kerne.auth.requestPasswordReset(email);
241
- }
242
- /** Confirms a password reset with the token from the email. */
243
- async confirmPasswordReset(token, password) {
244
- await this.kerne.auth.confirmPasswordReset(token, password);
245
- }
246
- // ============================================================================
247
- // Billing Methods
248
- // ============================================================================
249
- /**
250
- * Errors are not swallowed here (unlike the old implementation, which
251
- * caught everything and returned `false`) - a 401/500 must not be
252
- * indistinguishable from a real denial, that's exactly what let the
253
- * `has_access`/`allowed` mismatch below ship unnoticed. Callers that want
254
- * a fail-closed boolean regardless of the reason (e.g. `<Allows>`)
255
- * catch around this themselves.
256
- */
257
- async allows(featureKey, requested) {
258
- const check = await this.kerne.check(featureKey, { requested });
259
- return check.allowed;
260
- }
261
- /** Full entitlement detail (limit/used/remaining/overage) - use `allows()` for a plain boolean. */
262
- async check(featureKey, requested) {
263
- return this.kerne.check(featureKey, { requested });
264
- }
265
- async createCheckout(planPriceId, options) {
266
- const { url } = await this.kerne.billing.checkout(planPriceId, options);
267
- return url;
268
- }
269
- async openCheckout(planPriceId, options) {
270
- const url = await this.createCheckout(planPriceId, options);
271
- if (typeof window !== "undefined") window.location.href = url;
272
- }
273
- async createPortal(returnUrl) {
274
- const { url } = await this.kerne.billing.portal({ returnUrl });
275
- return url;
276
- }
277
- async openPortal(returnUrl) {
278
- const url = await this.createPortal(returnUrl);
279
- if (typeof window !== "undefined") window.location.href = url;
280
- }
281
- async getSubscription(productSlug) {
282
- const subs = await this.kerne.billing.subscriptions.list({
283
- productSlug,
284
- scopeType: "USER"
285
- });
286
- const active = subs.find(
287
- (s) => s.status === "ACTIVE" || s.status === "TRIALING" || s.status === "PAST_DUE"
288
- );
289
- return active ?? null;
290
- }
291
- /** Public pricing data - omit `productIdOrSlug` for the tenant's default product. */
292
- async getPlans(productIdOrSlug) {
293
- return this.kerne.billing.plans(productIdOrSlug);
294
- }
295
- /** Every entitlement for the current user in one call - for a "your plan" screen. */
296
- async getEntitlements() {
297
- return this.kerne.entitlements();
298
- }
299
- /** Usage for the current user - all QUOTA features, or one via `featureKey`. */
300
- async getUsage(featureKey) {
301
- return this.kerne.usage({ featureKey });
302
- }
303
- // ============================================================================
304
- // Growth Methods (waitlist, invitations)
305
- // ============================================================================
306
- async joinWaitlist(params) {
307
- return this.kerne.waitlist.join(params);
308
- }
309
- /** Validate an invitation token before showing the registration form. */
310
- async validateInvitationToken(token) {
311
- return this.kerne.invitations.validateToken(token);
312
- }
313
- /** Validate an invitation code before showing the registration form. */
314
- async validateInvitationCode(code) {
315
- return this.kerne.invitations.validateCode(code);
316
- }
317
- // ============================================================================
318
- // Email Verification Methods
319
- // ============================================================================
320
- async sendVerificationEmail(verificationType = "code") {
321
- await this.kerne.auth.sendVerificationEmail(verificationType);
322
- }
323
- async verifyEmailWithCode(code) {
324
- return await this.kerne.auth.verifyEmailWithCode(code);
325
- }
326
- async verifyEmailWithLink(token) {
327
- return await this.kerne.auth.verifyEmailWithLink(token);
328
- }
329
- async getVerificationStatus() {
330
- return await this.kerne.auth.getVerificationStatus();
331
- }
332
- };
333
- var KerneContext = createContext(null);
334
- function KerneProvider({ children, ...config }) {
335
- const clientRef = useRef(null);
336
- const client = useMemo(() => {
337
- if (!clientRef.current) {
338
- clientRef.current = new KerneClient(config);
339
- }
340
- return clientRef.current;
341
- }, [config.appId, config.baseUrl]);
342
- useEffect(() => {
343
- return () => {
344
- client.cancelRefresh?.();
345
- };
346
- }, [client]);
347
- return /* @__PURE__ */ jsx(KerneContext.Provider, { value: client, children });
348
- }
349
-
350
- // src/hooks.ts
351
- import { useContext, useSyncExternalStore, useCallback, useEffect as useEffect2, useState } from "react";
352
- function useClient() {
353
- const context = useContext(KerneContext);
354
- if (!context) {
355
- throw new Error("useClient must be used within a KerneProvider");
356
- }
357
- return context;
358
- }
359
- function useAuth() {
360
- const client = useClient();
361
- const user = useSyncExternalStore(
362
- (callback) => client.subscribe(callback),
363
- () => client.user,
364
- () => client.user
365
- // Server snapshot
366
- );
367
- const token = useSyncExternalStore(
368
- (callback) => client.subscribe(callback),
369
- () => client.token,
370
- () => null
371
- // Server always null
372
- );
373
- const isAuthenticated = useSyncExternalStore(
374
- (callback) => client.subscribe(callback),
375
- () => client.isAuthenticated,
376
- () => false
377
- // Server always unauthenticated
378
- );
379
- const isLoading = useSyncExternalStore(
380
- (callback) => client.subscribe(callback),
381
- () => client.isLoading,
382
- () => true
383
- // Server always loading
384
- );
385
- return {
386
- user,
387
- token,
388
- isAuthenticated,
389
- isLoading,
390
- login: useCallback(
391
- (params) => client.login(params),
392
- [client]
393
- ),
394
- register: useCallback(
395
- (params) => client.register(params),
396
- [client]
397
- ),
398
- logout: useCallback(() => client.logout(), [client]),
399
- refreshUser: useCallback(() => client.refreshUser(), [client]),
400
- refreshToken: useCallback(() => client.refreshToken(), [client]),
401
- updateProfile: useCallback(
402
- (params) => client.updateProfile(params),
403
- [client]
404
- ),
405
- updatePassword: useCallback(
406
- (params) => client.updatePassword(params),
407
- [client]
408
- ),
409
- requestPasswordReset: useCallback(
410
- (email) => client.requestPasswordReset(email),
411
- [client]
412
- ),
413
- confirmPasswordReset: useCallback(
414
- (token2, password) => client.confirmPasswordReset(token2, password),
415
- [client]
416
- ),
417
- // Email verification
418
- sendVerificationEmail: useCallback(
419
- (verificationType = "code") => client.sendVerificationEmail(verificationType),
420
- [client]
421
- ),
422
- verifyEmailWithCode: useCallback((code) => client.verifyEmailWithCode(code), [client]),
423
- verifyEmailWithLink: useCallback(
424
- (token2) => client.verifyEmailWithLink(token2),
425
- [client]
426
- ),
427
- getVerificationStatus: useCallback(() => client.getVerificationStatus(), [client])
428
- };
429
- }
430
- function useCheckout() {
431
- const client = useClient();
432
- return {
433
- createCheckout: useCallback(
434
- (planPriceId, options) => client.createCheckout(planPriceId, options),
435
- [client]
436
- ),
437
- openCheckout: useCallback(
438
- (planPriceId, options) => client.openCheckout(planPriceId, options),
439
- [client]
440
- )
441
- };
442
- }
443
- function usePortal() {
444
- const client = useClient();
445
- return {
446
- createPortal: useCallback((returnUrl) => client.createPortal(returnUrl), [client]),
447
- openPortal: useCallback((returnUrl) => client.openPortal(returnUrl), [client])
448
- };
449
- }
450
- function useAllows(featureKey, requested) {
451
- const client = useClient();
452
- const [state, setState] = useState({
453
- allowed: false,
454
- isLoading: true,
455
- error: null
456
- });
457
- useEffect2(() => {
458
- let mounted = true;
459
- client.allows(featureKey, requested).then((allowed) => {
460
- if (mounted) {
461
- setState({ allowed, isLoading: false, error: null });
462
- }
463
- }).catch((error) => {
464
- if (mounted) {
465
- setState({ allowed: false, isLoading: false, error });
466
- }
467
- });
468
- return () => {
469
- mounted = false;
470
- };
471
- }, [client, featureKey, requested]);
472
- return state;
473
- }
474
- function useCheck(featureKey, requested) {
475
- const client = useClient();
476
- const [state, setState] = useState({
477
- check: null,
478
- isLoading: true,
479
- error: null
480
- });
481
- useEffect2(() => {
482
- let mounted = true;
483
- client.check(featureKey, requested).then((check) => {
484
- if (mounted) {
485
- setState({ check, isLoading: false, error: null });
486
- }
487
- }).catch((error) => {
488
- if (mounted) {
489
- setState({ check: null, isLoading: false, error });
490
- }
491
- });
492
- return () => {
493
- mounted = false;
494
- };
495
- }, [client, featureKey, requested]);
496
- return state;
497
- }
498
- function useSubscription(productSlug) {
499
- const client = useClient();
500
- const [state, setState] = useState({
501
- subscription: null,
502
- isLoading: true,
503
- error: null
504
- });
505
- useEffect2(() => {
506
- let mounted = true;
507
- client.getSubscription(productSlug).then((subscription) => {
508
- if (mounted) {
509
- setState({ subscription, isLoading: false, error: null });
510
- }
511
- }).catch((error) => {
512
- if (mounted) {
513
- setState({ subscription: null, isLoading: false, error });
514
- }
515
- });
516
- return () => {
517
- mounted = false;
518
- };
519
- }, [client, productSlug]);
520
- const isActive = state.subscription?.status === "ACTIVE" || state.subscription?.status === "TRIALING";
521
- const planSlug = state.subscription?.plan?.slug;
522
- return {
523
- ...state,
524
- isActive,
525
- planSlug
526
- };
527
- }
528
- function usePlans(productIdOrSlug) {
529
- const client = useClient();
530
- const [state, setState] = useState({
531
- plans: [],
532
- isLoading: true,
533
- error: null
534
- });
535
- useEffect2(() => {
536
- let mounted = true;
537
- client.getPlans(productIdOrSlug).then((plans) => {
538
- if (mounted) {
539
- setState({ plans, isLoading: false, error: null });
540
- }
541
- }).catch((error) => {
542
- if (mounted) {
543
- setState({ plans: [], isLoading: false, error });
544
- }
545
- });
546
- return () => {
547
- mounted = false;
548
- };
549
- }, [client, productIdOrSlug]);
550
- return state;
551
- }
552
- function useEntitlements() {
553
- const client = useClient();
554
- const [state, setState] = useState({
555
- entitlements: null,
556
- isLoading: true,
557
- error: null
558
- });
559
- useEffect2(() => {
560
- let mounted = true;
561
- client.getEntitlements().then((entitlements) => {
562
- if (mounted) {
563
- setState({ entitlements, isLoading: false, error: null });
564
- }
565
- }).catch((error) => {
566
- if (mounted) {
567
- setState({ entitlements: null, isLoading: false, error });
568
- }
569
- });
570
- return () => {
571
- mounted = false;
572
- };
573
- }, [client]);
574
- return state;
575
- }
576
- function useUsage(featureKey) {
577
- const client = useClient();
578
- const [state, setState] = useState({
579
- usage: [],
580
- isLoading: true,
581
- error: null
582
- });
583
- useEffect2(() => {
584
- let mounted = true;
585
- client.getUsage(featureKey).then((usage) => {
586
- if (mounted) {
587
- setState({ usage, isLoading: false, error: null });
588
- }
589
- }).catch((error) => {
590
- if (mounted) {
591
- setState({ usage: [], isLoading: false, error });
592
- }
593
- });
594
- return () => {
595
- mounted = false;
596
- };
597
- }, [client, featureKey]);
598
- return state;
599
- }
600
- function useWaitlist() {
601
- const client = useClient();
602
- return {
603
- join: useCallback((params) => client.joinWaitlist(params), [client])
604
- };
605
- }
606
- function useInvitation(params) {
607
- const client = useClient();
608
- const { token, code } = params;
609
- const [state, setState] = useState({
610
- result: null,
611
- isLoading: !!(token || code),
612
- error: null
613
- });
614
- useEffect2(() => {
615
- if (!token && !code) {
616
- setState({ result: null, isLoading: false, error: null });
617
- return;
618
- }
619
- let mounted = true;
620
- const validate = token ? client.validateInvitationToken(token) : client.validateInvitationCode(code);
621
- validate.then((result) => {
622
- if (mounted) {
623
- setState({ result, isLoading: false, error: null });
624
- }
625
- }).catch((error) => {
626
- if (mounted) {
627
- setState({ result: null, isLoading: false, error });
628
- }
629
- });
630
- return () => {
631
- mounted = false;
632
- };
633
- }, [client, token, code]);
634
- return state;
635
- }
636
- function useUser() {
637
- const { user, isLoading, updateProfile } = useAuth();
638
- return {
639
- user,
640
- isLoading,
641
- update: updateProfile,
642
- // Computed properties
643
- fullName: user ? `${user.first_name || ""} ${user.last_name || ""}`.trim() : null,
644
- initials: user ? `${user.first_name?.[0] || ""}${user.last_name?.[0] || ""}`.toUpperCase() : null,
645
- email: user?.email || null,
646
- emailVerified: user?.email_verified || false
647
- };
648
- }
1
+ "use client";
2
+ import {
3
+ KerneClient,
4
+ KerneContext,
5
+ KerneProvider,
6
+ defaultLocalization,
7
+ useAccess,
8
+ useAuth,
9
+ useAuthConfig,
10
+ useCheckout,
11
+ useClient,
12
+ useEntitlements,
13
+ useInvitation,
14
+ usePlans,
15
+ usePortal,
16
+ useSubscription,
17
+ useUsage,
18
+ useUser,
19
+ useWaitlist
20
+ } from "./chunk-F6NV76OR.js";
649
21
 
650
22
  // src/components.tsx
651
- import React2 from "react";
652
- import { jsx as jsx2 } from "react/jsx-runtime";
23
+ import React from "react";
24
+ import { jsx } from "react/jsx-runtime";
653
25
  function Authenticated({ children, fallback = null }) {
654
26
  const { isAuthenticated } = useAuth();
655
27
  return isAuthenticated ? children : fallback;
@@ -664,8 +36,8 @@ function AuthLoading({ children }) {
664
36
  }
665
37
  function HasSubscription({ children, fallback = null }) {
666
38
  const client = useClient();
667
- const [hasSubscription, setHasSubscription] = React2.useState(null);
668
- React2.useEffect(() => {
39
+ const [hasSubscription, setHasSubscription] = React.useState(null);
40
+ React.useEffect(() => {
669
41
  client.getSubscription().then((sub) => {
670
42
  setHasSubscription(sub !== null && ["ACTIVE", "TRIALING"].includes(sub.status));
671
43
  }).catch(() => {
@@ -677,26 +49,26 @@ function HasSubscription({ children, fallback = null }) {
677
49
  }
678
50
  function Allows({
679
51
  children,
680
- key,
52
+ featureKey,
681
53
  minimum,
682
54
  fallback = null
683
55
  }) {
684
56
  const client = useClient();
685
- const [allowed, setAllowed] = React2.useState(null);
686
- React2.useEffect(() => {
687
- client.allows(key, minimum).then((result) => {
57
+ const [allowed, setAllowed] = React.useState(null);
58
+ React.useEffect(() => {
59
+ client.allows(featureKey, minimum).then((result) => {
688
60
  setAllowed(result);
689
61
  }).catch(() => {
690
62
  setAllowed(false);
691
63
  });
692
- }, [client, key, minimum]);
64
+ }, [client, featureKey, minimum]);
693
65
  if (allowed === null) return null;
694
66
  return allowed ? children : fallback;
695
67
  }
696
68
  function Protected({
697
69
  children,
698
70
  auth = true,
699
- key,
71
+ featureKey,
700
72
  authFallback = null,
701
73
  billingFallback = null
702
74
  }) {
@@ -704,15 +76,15 @@ function Protected({
704
76
  if (auth && !isAuthenticated) {
705
77
  return authFallback;
706
78
  }
707
- if (key) {
708
- return /* @__PURE__ */ jsx2(Allows, { fallback: billingFallback, children }, key);
79
+ if (featureKey) {
80
+ return /* @__PURE__ */ jsx(Allows, { featureKey, fallback: billingFallback, children });
709
81
  }
710
82
  return children;
711
83
  }
712
84
 
713
85
  // src/error-boundary.tsx
714
- import React3, { Component } from "react";
715
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
86
+ import React2, { Component } from "react";
87
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
716
88
  var KerneErrorBoundary = class extends Component {
717
89
  constructor(props) {
718
90
  super(props);
@@ -744,13 +116,13 @@ var KerneErrorBoundary = class extends Component {
744
116
  return fallback;
745
117
  }
746
118
  return /* @__PURE__ */ jsxs("div", { style: { padding: "20px", textAlign: "center" }, children: [
747
- /* @__PURE__ */ jsx3("h2", { style: { color: "#dc2626", marginBottom: "10px" }, children: "Something went wrong" }),
748
- /* @__PURE__ */ jsx3("p", { style: { color: "#6b7280", marginBottom: "20px" }, children: error.message || "An unexpected error occurred" }),
119
+ /* @__PURE__ */ jsx2("h2", { style: { color: "#dc2626", marginBottom: "10px" }, children: "Something went wrong" }),
120
+ /* @__PURE__ */ jsx2("p", { style: { color: "#6b7280", marginBottom: "20px" }, children: error.message || "An unexpected error occurred" }),
749
121
  error.code && /* @__PURE__ */ jsxs("p", { style: { color: "#9ca3af", fontSize: "12px", marginBottom: "20px" }, children: [
750
122
  "Error code: ",
751
123
  error.code
752
124
  ] }),
753
- /* @__PURE__ */ jsx3(
125
+ /* @__PURE__ */ jsx2(
754
126
  "button",
755
127
  {
756
128
  onClick: this.reset,
@@ -771,11 +143,11 @@ var KerneErrorBoundary = class extends Component {
771
143
  }
772
144
  };
773
145
  function useKerneError() {
774
- const [error, setError] = React3.useState(null);
775
- const handleError = React3.useCallback((err) => {
146
+ const [error, setError] = React2.useState(null);
147
+ const handleError = React2.useCallback((err) => {
776
148
  setError(err);
777
149
  }, []);
778
- const clearError = React3.useCallback(() => {
150
+ const clearError = React2.useCallback(() => {
779
151
  setError(null);
780
152
  }, []);
781
153
  return {
@@ -785,54 +157,6 @@ function useKerneError() {
785
157
  clearError
786
158
  };
787
159
  }
788
-
789
- // src/store.ts
790
- var KerneStore = class {
791
- state;
792
- listeners = /* @__PURE__ */ new Set();
793
- constructor(initialState) {
794
- this.state = {
795
- user: null,
796
- isAuthenticated: false,
797
- isLoading: true,
798
- subscription: null,
799
- error: null,
800
- ...initialState
801
- };
802
- }
803
- getState() {
804
- return this.state;
805
- }
806
- getSnapshot() {
807
- return this.state;
808
- }
809
- getServerSnapshot() {
810
- return this.state;
811
- }
812
- setState(partial) {
813
- const prevState = this.state;
814
- this.state = { ...this.state, ...partial };
815
- if (this.hasChanged(prevState, this.state)) {
816
- this.notify();
817
- }
818
- }
819
- hasChanged(prev, next) {
820
- return prev.user !== next.user || prev.isAuthenticated !== next.isAuthenticated || prev.isLoading !== next.isLoading || prev.subscription !== next.subscription || prev.error !== next.error;
821
- }
822
- subscribe(listener) {
823
- this.listeners.add(listener);
824
- return () => {
825
- this.listeners.delete(listener);
826
- };
827
- }
828
- notify() {
829
- this.listeners.forEach((listener) => listener());
830
- }
831
- // Selector-based subscription for optimized re-renders
832
- select(selector) {
833
- return selector(this.state);
834
- }
835
- };
836
160
  export {
837
161
  Allows,
838
162
  AuthLoading,
@@ -842,12 +166,12 @@ export {
842
166
  KerneContext,
843
167
  KerneErrorBoundary,
844
168
  KerneProvider,
845
- KerneStore,
846
169
  Protected,
847
170
  Unauthenticated,
848
- useAllows,
171
+ defaultLocalization,
172
+ useAccess,
849
173
  useAuth,
850
- useCheck,
174
+ useAuthConfig,
851
175
  useCheckout,
852
176
  useClient,
853
177
  useEntitlements,