@kerne/react 0.1.0 → 0.1.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/LICENSE +1 -1
- package/README.md +83 -0
- package/dist/index.cjs +791 -106
- package/dist/index.d.cts +503 -23
- package/dist/index.d.ts +503 -23
- package/dist/index.js +757 -103
- package/package.json +19 -12
package/dist/index.d.ts
CHANGED
|
@@ -1,87 +1,567 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import
|
|
3
|
-
import { KerneConfig,
|
|
4
|
-
|
|
5
|
-
import
|
|
2
|
+
import React, { ReactNode, Component, ErrorInfo } from 'react';
|
|
3
|
+
import { KerneConfig, JoinWaitlistParams, ValidateTokenResponse, ValidateCodeResponse } from '@kerne/server';
|
|
4
|
+
export { JoinWaitlistParams, ValidateCodeResponse, ValidateTokenResponse, WaitlistEntry } from '@kerne/server';
|
|
5
|
+
import * as _kerne_types from '@kerne/types';
|
|
6
|
+
import { User, AuthResponse, EntitlementCheck, SubscriptionWithPlan, PublicPlan, Entitlements, UsageRecord, Subscription } from '@kerne/types';
|
|
7
|
+
export { AuthResponse, EntitlementCheck, Entitlements, PublicPlan, Subscription, SubscriptionWithPlan, UsageRecord, User } from '@kerne/types';
|
|
6
8
|
|
|
7
9
|
interface KerneReactConfig extends Omit<KerneConfig, 'secretKey'> {
|
|
8
10
|
storage?: Storage;
|
|
9
11
|
storageKey?: string;
|
|
10
12
|
onAuthChange?: (user: User | null) => void;
|
|
13
|
+
/** Auto-refresh session before expiry (default: true) */
|
|
14
|
+
autoRefresh?: boolean;
|
|
15
|
+
/** Refresh buffer in seconds before expiry (default: 60) */
|
|
16
|
+
refreshBuffer?: number;
|
|
11
17
|
}
|
|
18
|
+
|
|
19
|
+
type Listener$1 = () => void;
|
|
12
20
|
declare class KerneClient {
|
|
13
21
|
private kerne;
|
|
14
22
|
private storage;
|
|
15
23
|
private storageKey;
|
|
16
24
|
private onAuthChange?;
|
|
17
|
-
private
|
|
18
|
-
private
|
|
25
|
+
private autoRefresh;
|
|
26
|
+
private refreshBuffer;
|
|
27
|
+
private refreshTimer;
|
|
28
|
+
private listeners;
|
|
29
|
+
private _user;
|
|
30
|
+
private _token;
|
|
31
|
+
private _refreshToken;
|
|
32
|
+
private _expiresAt;
|
|
33
|
+
private _isLoading;
|
|
34
|
+
readonly baseUrl: string;
|
|
35
|
+
readonly appId: string;
|
|
19
36
|
constructor(config: KerneReactConfig);
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
private clearSession;
|
|
37
|
+
subscribe(listener: Listener$1): () => void;
|
|
38
|
+
private notify;
|
|
23
39
|
get user(): User | null;
|
|
24
40
|
get token(): string | null;
|
|
25
41
|
get isAuthenticated(): boolean;
|
|
26
|
-
get
|
|
27
|
-
|
|
42
|
+
get isLoading(): boolean;
|
|
43
|
+
private loadSession;
|
|
44
|
+
/**
|
|
45
|
+
* `AuthResponse.user` is the minimal `{id, email, role}` claim set the
|
|
46
|
+
* token endpoint returns, not the full profile (`User`) - so this always
|
|
47
|
+
* follows up with `users.me()` rather than assigning it directly (that
|
|
48
|
+
* used to leave `_user` looking like a `User` while actually missing
|
|
49
|
+
* `email_verified`/`first_name`/`avatar`/etc).
|
|
50
|
+
*/
|
|
51
|
+
private saveSession;
|
|
52
|
+
private clearSession;
|
|
53
|
+
private scheduleRefresh;
|
|
54
|
+
private cancelRefresh;
|
|
55
|
+
private silentRefresh;
|
|
28
56
|
register(params: {
|
|
29
57
|
email: string;
|
|
30
58
|
password: string;
|
|
31
59
|
name?: string;
|
|
60
|
+
invitationToken?: string;
|
|
61
|
+
invitationCode?: string;
|
|
32
62
|
}): Promise<AuthResponse>;
|
|
33
63
|
login(params: {
|
|
34
64
|
email: string;
|
|
35
65
|
password: string;
|
|
36
66
|
}): Promise<AuthResponse>;
|
|
67
|
+
/** Manually trigger a token refresh - normally handled automatically by `autoRefresh`. */
|
|
37
68
|
refreshToken(): Promise<AuthResponse | null>;
|
|
38
69
|
logout(): void;
|
|
39
70
|
refreshUser(): Promise<User | null>;
|
|
40
|
-
|
|
41
|
-
|
|
71
|
+
updateProfile(params: {
|
|
72
|
+
first_name?: string;
|
|
73
|
+
last_name?: string;
|
|
74
|
+
}): Promise<User>;
|
|
75
|
+
updatePassword(params: {
|
|
76
|
+
currentPassword: string;
|
|
77
|
+
newPassword: string;
|
|
78
|
+
}): Promise<void>;
|
|
79
|
+
/** Forgot-password flow (logged out) - sends the reset email. */
|
|
80
|
+
requestPasswordReset(email: string): Promise<void>;
|
|
81
|
+
/** Confirms a password reset with the token from the email. */
|
|
82
|
+
confirmPasswordReset(token: string, password: string): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Errors are not swallowed here (unlike the old implementation, which
|
|
85
|
+
* caught everything and returned `false`) - a 401/500 must not be
|
|
86
|
+
* indistinguishable from a real denial, that's exactly what let the
|
|
87
|
+
* `has_access`/`allowed` mismatch below ship unnoticed. Callers that want
|
|
88
|
+
* a fail-closed boolean regardless of the reason (e.g. `<Allows>`)
|
|
89
|
+
* catch around this themselves.
|
|
90
|
+
*/
|
|
91
|
+
allows(featureKey: string, requested?: number): Promise<boolean>;
|
|
92
|
+
/** Full entitlement detail (limit/used/remaining/overage) - use `allows()` for a plain boolean. */
|
|
93
|
+
check(featureKey: string, requested?: number): Promise<EntitlementCheck>;
|
|
94
|
+
createCheckout(planPriceId: string, options?: {
|
|
42
95
|
successUrl?: string;
|
|
43
96
|
cancelUrl?: string;
|
|
44
97
|
}): Promise<string>;
|
|
45
|
-
openCheckout(
|
|
98
|
+
openCheckout(planPriceId: string, options?: {
|
|
46
99
|
successUrl?: string;
|
|
47
100
|
cancelUrl?: string;
|
|
48
101
|
}): Promise<void>;
|
|
49
102
|
createPortal(returnUrl?: string): Promise<string>;
|
|
50
103
|
openPortal(returnUrl?: string): Promise<void>;
|
|
104
|
+
getSubscription(productSlug?: string): Promise<SubscriptionWithPlan | null>;
|
|
105
|
+
/** Public pricing data - omit `productIdOrSlug` for the tenant's default product. */
|
|
106
|
+
getPlans(productIdOrSlug?: string): Promise<PublicPlan[]>;
|
|
107
|
+
/** Every entitlement for the current user in one call - for a "your plan" screen. */
|
|
108
|
+
getEntitlements(): Promise<Entitlements>;
|
|
109
|
+
/** Usage for the current user - all QUOTA features, or one via `featureKey`. */
|
|
110
|
+
getUsage(featureKey?: string): Promise<UsageRecord[]>;
|
|
111
|
+
joinWaitlist(params: JoinWaitlistParams): Promise<{
|
|
112
|
+
success: boolean;
|
|
113
|
+
}>;
|
|
114
|
+
/** Validate an invitation token before showing the registration form. */
|
|
115
|
+
validateInvitationToken(token: string): Promise<ValidateTokenResponse>;
|
|
116
|
+
/** Validate an invitation code before showing the registration form. */
|
|
117
|
+
validateInvitationCode(code: string): Promise<ValidateCodeResponse>;
|
|
118
|
+
sendVerificationEmail(verificationType?: 'code' | 'link'): Promise<void>;
|
|
119
|
+
verifyEmailWithCode(code: string): Promise<{
|
|
120
|
+
success: boolean;
|
|
121
|
+
needsTokenRefresh: boolean;
|
|
122
|
+
}>;
|
|
123
|
+
verifyEmailWithLink(token: string): Promise<{
|
|
124
|
+
success: boolean;
|
|
125
|
+
needsTokenRefresh: boolean;
|
|
126
|
+
}>;
|
|
127
|
+
getVerificationStatus(): Promise<{
|
|
128
|
+
emailVerified: boolean;
|
|
129
|
+
needsVerification: boolean;
|
|
130
|
+
email: string;
|
|
131
|
+
}>;
|
|
51
132
|
}
|
|
52
|
-
declare
|
|
133
|
+
declare const KerneContext: React.Context<KerneClient | null>;
|
|
53
134
|
interface KerneProviderProps extends KerneReactConfig {
|
|
54
135
|
children: React.ReactNode;
|
|
55
136
|
}
|
|
56
137
|
declare function KerneProvider({ children, ...config }: KerneProviderProps): react_jsx_runtime.JSX.Element;
|
|
57
|
-
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Access the Kerne client instance
|
|
141
|
+
*/
|
|
142
|
+
declare function useClient(): KerneClient;
|
|
143
|
+
/**
|
|
144
|
+
* Authentication hook with optimized re-renders
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```tsx
|
|
148
|
+
* const { user, isAuthenticated, login, logout } = useAuth();
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
58
151
|
declare function useAuth(): {
|
|
59
152
|
user: User | null;
|
|
153
|
+
token: string | null;
|
|
60
154
|
isAuthenticated: boolean;
|
|
155
|
+
isLoading: boolean;
|
|
61
156
|
login: (params: {
|
|
62
157
|
email: string;
|
|
63
158
|
password: string;
|
|
64
|
-
}) => Promise<AuthResponse>;
|
|
159
|
+
}) => Promise<_kerne_types.AuthResponse>;
|
|
65
160
|
register: (params: {
|
|
66
161
|
email: string;
|
|
67
162
|
password: string;
|
|
68
163
|
name?: string;
|
|
69
|
-
|
|
164
|
+
invitationToken?: string;
|
|
165
|
+
invitationCode?: string;
|
|
166
|
+
}) => Promise<_kerne_types.AuthResponse>;
|
|
70
167
|
logout: () => void;
|
|
71
168
|
refreshUser: () => Promise<User | null>;
|
|
169
|
+
refreshToken: () => Promise<_kerne_types.AuthResponse | null>;
|
|
170
|
+
updateProfile: (params: {
|
|
171
|
+
first_name?: string;
|
|
172
|
+
last_name?: string;
|
|
173
|
+
avatar?: string;
|
|
174
|
+
}) => Promise<User>;
|
|
175
|
+
updatePassword: (params: {
|
|
176
|
+
currentPassword: string;
|
|
177
|
+
newPassword: string;
|
|
178
|
+
}) => Promise<void>;
|
|
179
|
+
requestPasswordReset: (email: string) => Promise<void>;
|
|
180
|
+
confirmPasswordReset: (token: string, password: string) => Promise<void>;
|
|
181
|
+
sendVerificationEmail: (verificationType?: "code" | "link") => Promise<void>;
|
|
182
|
+
verifyEmailWithCode: (code: string) => Promise<{
|
|
183
|
+
success: boolean;
|
|
184
|
+
needsTokenRefresh: boolean;
|
|
185
|
+
}>;
|
|
186
|
+
verifyEmailWithLink: (token: string) => Promise<{
|
|
187
|
+
success: boolean;
|
|
188
|
+
needsTokenRefresh: boolean;
|
|
189
|
+
}>;
|
|
190
|
+
getVerificationStatus: () => Promise<{
|
|
191
|
+
emailVerified: boolean;
|
|
192
|
+
needsVerification: boolean;
|
|
193
|
+
email: string;
|
|
194
|
+
}>;
|
|
72
195
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Checkout hook - starts a Stripe/Polar checkout session for a plan price.
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```tsx
|
|
201
|
+
* const { openCheckout } = useCheckout();
|
|
202
|
+
* <button onClick={() => openCheckout(planPriceId)}>Upgrade</button>
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
declare function useCheckout(): {
|
|
206
|
+
createCheckout: (planPriceId: string, options?: {
|
|
76
207
|
successUrl?: string;
|
|
77
208
|
cancelUrl?: string;
|
|
78
209
|
}) => Promise<string>;
|
|
79
|
-
openCheckout: (
|
|
210
|
+
openCheckout: (planPriceId: string, options?: {
|
|
80
211
|
successUrl?: string;
|
|
81
212
|
cancelUrl?: string;
|
|
82
213
|
}) => Promise<void>;
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Billing portal hook - lets a user manage their subscription/payment method.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```tsx
|
|
220
|
+
* const { openPortal } = usePortal();
|
|
221
|
+
* <button onClick={() => openPortal()}>Manage billing</button>
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
declare function usePortal(): {
|
|
83
225
|
createPortal: (returnUrl?: string) => Promise<string>;
|
|
84
226
|
openPortal: (returnUrl?: string) => Promise<void>;
|
|
85
227
|
};
|
|
228
|
+
/**
|
|
229
|
+
* Hook for a plain allowed/denied entitlement check.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```tsx
|
|
233
|
+
* const { allowed, isLoading } = useAllows('advanced_analytics');
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
declare function useAllows(featureKey: string, requested?: number): {
|
|
237
|
+
allowed: boolean;
|
|
238
|
+
isLoading: boolean;
|
|
239
|
+
error: Error | null;
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* Hook for the full entitlement detail (limit/used/remaining/overage) -
|
|
243
|
+
* use `useAllows` when a plain boolean is all you need.
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```tsx
|
|
247
|
+
* const { check, isLoading } = useCheck('api_calls');
|
|
248
|
+
* // check?.remaining, check?.limit, check?.overage_behavior
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
declare function useCheck(featureKey: string, requested?: number): {
|
|
252
|
+
check: EntitlementCheck | null;
|
|
253
|
+
isLoading: boolean;
|
|
254
|
+
error: Error | null;
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Hook for subscription state
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```tsx
|
|
261
|
+
* const { subscription, isLoading, isPro } = useSubscription();
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
declare function useSubscription(productSlug?: string): {
|
|
265
|
+
isActive: boolean;
|
|
266
|
+
planSlug: any;
|
|
267
|
+
subscription: any | null;
|
|
268
|
+
isLoading: boolean;
|
|
269
|
+
error: Error | null;
|
|
270
|
+
};
|
|
271
|
+
/**
|
|
272
|
+
* Public pricing data for a pricing/checkout page - omit `productIdOrSlug`
|
|
273
|
+
* for the tenant's default product.
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```tsx
|
|
277
|
+
* const { plans, isLoading } = usePlans();
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
declare function usePlans(productIdOrSlug?: string): {
|
|
281
|
+
plans: PublicPlan[];
|
|
282
|
+
isLoading: boolean;
|
|
283
|
+
error: Error | null;
|
|
284
|
+
};
|
|
285
|
+
/**
|
|
286
|
+
* Every entitlement for the current user in one call - for a "your plan"
|
|
287
|
+
* screen. Use `useAllows`/`useCheck` instead for a single feature - this
|
|
288
|
+
* response never carries `allowed` (see docs: the list is cached, a stale
|
|
289
|
+
* verdict would be unsafe).
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```tsx
|
|
293
|
+
* const { entitlements, isLoading } = useEntitlements();
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
declare function useEntitlements(): {
|
|
297
|
+
entitlements: Entitlements | null;
|
|
298
|
+
isLoading: boolean;
|
|
299
|
+
error: Error | null;
|
|
300
|
+
};
|
|
301
|
+
/**
|
|
302
|
+
* Usage for the current user - every QUOTA feature, or one via `featureKey`.
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```tsx
|
|
306
|
+
* const { usage, isLoading } = useUsage('api_calls');
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
309
|
+
declare function useUsage(featureKey?: string): {
|
|
310
|
+
usage: UsageRecord[];
|
|
311
|
+
isLoading: boolean;
|
|
312
|
+
error: Error | null;
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Waitlist signup form - a mutation, not a data fetch, same shape as
|
|
316
|
+
* `useCheckout`/`usePortal`.
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* ```tsx
|
|
320
|
+
* const { join } = useWaitlist();
|
|
321
|
+
* await join({ email });
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
declare function useWaitlist(): {
|
|
325
|
+
join: (params: JoinWaitlistParams) => Promise<{
|
|
326
|
+
success: boolean;
|
|
327
|
+
}>;
|
|
328
|
+
};
|
|
329
|
+
/**
|
|
330
|
+
* Validates an invitation token/code before showing the registration form -
|
|
331
|
+
* pass whichever one is present in the URL.
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```tsx
|
|
335
|
+
* const { result, isLoading } = useInvitation({ token });
|
|
336
|
+
* if (!isLoading && !result?.valid) return <InvalidInvite />;
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
declare function useInvitation(params: {
|
|
340
|
+
token?: string;
|
|
341
|
+
code?: string;
|
|
342
|
+
}): {
|
|
343
|
+
result: ValidateTokenResponse | ValidateCodeResponse | null;
|
|
344
|
+
isLoading: boolean;
|
|
345
|
+
error: Error | null;
|
|
346
|
+
};
|
|
347
|
+
/**
|
|
348
|
+
* Hook for user profile data
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* ```tsx
|
|
352
|
+
* const { user, isLoading, update } = useUser();
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
declare function useUser(): {
|
|
356
|
+
user: User | null;
|
|
357
|
+
isLoading: boolean;
|
|
358
|
+
update: (params: {
|
|
359
|
+
first_name?: string;
|
|
360
|
+
last_name?: string;
|
|
361
|
+
avatar?: string;
|
|
362
|
+
}) => Promise<User>;
|
|
363
|
+
fullName: string | null;
|
|
364
|
+
initials: string | null;
|
|
365
|
+
email: string | null;
|
|
366
|
+
emailVerified: boolean;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Guardian Components
|
|
371
|
+
*
|
|
372
|
+
* Conditional rendering based on auth/billing state.
|
|
373
|
+
* Inspired by Clerk's <SignedIn>, <SignedOut> pattern.
|
|
374
|
+
*/
|
|
375
|
+
|
|
376
|
+
interface AuthGuardProps {
|
|
377
|
+
children: ReactNode;
|
|
378
|
+
/** Fallback content when condition is not met */
|
|
379
|
+
fallback?: ReactNode;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Renders children only when user is authenticated
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```tsx
|
|
386
|
+
* <Authenticated>
|
|
387
|
+
* <Dashboard />
|
|
388
|
+
* </Authenticated>
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
declare function Authenticated({ children, fallback }: AuthGuardProps): ReactNode;
|
|
392
|
+
/**
|
|
393
|
+
* Renders children only when user is NOT authenticated
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* ```tsx
|
|
397
|
+
* <Unauthenticated>
|
|
398
|
+
* <LoginPrompt />
|
|
399
|
+
* </Unauthenticated>
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
402
|
+
declare function Unauthenticated({ children, fallback }: AuthGuardProps): ReactNode;
|
|
403
|
+
/**
|
|
404
|
+
* Renders children while auth state is loading
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* ```tsx
|
|
408
|
+
* <AuthLoading>
|
|
409
|
+
* <Spinner />
|
|
410
|
+
* </AuthLoading>
|
|
411
|
+
* ```
|
|
412
|
+
*/
|
|
413
|
+
declare function AuthLoading({ children }: {
|
|
414
|
+
children: ReactNode;
|
|
415
|
+
}): ReactNode;
|
|
416
|
+
interface SubscriptionGuardProps {
|
|
417
|
+
children: ReactNode;
|
|
418
|
+
/** Fallback content when there's no active subscription */
|
|
419
|
+
fallback?: ReactNode;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Renders children only when user has an active subscription
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* ```tsx
|
|
426
|
+
* <HasSubscription fallback={<UpgradePrompt />}>
|
|
427
|
+
* <PremiumFeature />
|
|
428
|
+
* </HasSubscription>
|
|
429
|
+
* ```
|
|
430
|
+
*/
|
|
431
|
+
declare function HasSubscription({ children, fallback }: SubscriptionGuardProps): ReactNode;
|
|
432
|
+
interface EntitlementGuardProps {
|
|
433
|
+
children: ReactNode;
|
|
434
|
+
/** Capability key to check */
|
|
435
|
+
key: string;
|
|
436
|
+
/** Minimum value required (for limit features) */
|
|
437
|
+
minimum?: number;
|
|
438
|
+
/** Fallback content when entitlement is not met */
|
|
439
|
+
fallback?: ReactNode;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Renders children only when the scope is entitled to a capability. Checks the
|
|
443
|
+
* entitlement itself, never a plan name - a plan can be renamed or restructured
|
|
444
|
+
* without breaking every place that gated on its slug.
|
|
445
|
+
*
|
|
446
|
+
* @example
|
|
447
|
+
* ```tsx
|
|
448
|
+
* <Allows key="advanced_analytics" fallback={<UpgradePrompt feature="analytics" />}>
|
|
449
|
+
* <AnalyticsDashboard />
|
|
450
|
+
* </Allows>
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
declare function Allows({ children, key, minimum, fallback, }: EntitlementGuardProps): ReactNode;
|
|
454
|
+
interface ProtectedProps {
|
|
455
|
+
children: ReactNode;
|
|
456
|
+
/** Require authentication */
|
|
457
|
+
auth?: boolean;
|
|
458
|
+
/** Require specific capability key */
|
|
459
|
+
key?: string;
|
|
460
|
+
/** Fallback for unauthenticated */
|
|
461
|
+
authFallback?: ReactNode;
|
|
462
|
+
/** Fallback for missing feature */
|
|
463
|
+
billingFallback?: ReactNode;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Combined guard for auth + entitlement requirements
|
|
467
|
+
*
|
|
468
|
+
* @example
|
|
469
|
+
* ```tsx
|
|
470
|
+
* <Protected auth key="advanced_analytics" authFallback={<Login />} billingFallback={<Upgrade />}>
|
|
471
|
+
* <ProFeature />
|
|
472
|
+
* </Protected>
|
|
473
|
+
* ```
|
|
474
|
+
*/
|
|
475
|
+
declare function Protected({ children, auth, key, authFallback, billingFallback, }: ProtectedProps): ReactNode;
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Kerne Error Boundary
|
|
479
|
+
*
|
|
480
|
+
* Catches auth/billing errors and provides recovery UI
|
|
481
|
+
*/
|
|
482
|
+
|
|
483
|
+
interface KerneError extends Error {
|
|
484
|
+
code?: string;
|
|
485
|
+
statusCode?: number;
|
|
486
|
+
details?: Record<string, any>;
|
|
487
|
+
}
|
|
488
|
+
interface ErrorBoundaryProps {
|
|
489
|
+
children: ReactNode;
|
|
490
|
+
/** Custom fallback UI */
|
|
491
|
+
fallback?: ReactNode | ((error: KerneError, reset: () => void) => ReactNode);
|
|
492
|
+
/** Called when error is caught */
|
|
493
|
+
onError?: (error: KerneError, errorInfo: ErrorInfo) => void;
|
|
494
|
+
/** Called when user clicks reset */
|
|
495
|
+
onReset?: () => void;
|
|
496
|
+
}
|
|
497
|
+
interface ErrorBoundaryState {
|
|
498
|
+
error: KerneError | null;
|
|
499
|
+
hasError: boolean;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Error boundary for Kerne SDK errors
|
|
503
|
+
*
|
|
504
|
+
* @example
|
|
505
|
+
* ```tsx
|
|
506
|
+
* <KerneErrorBoundary
|
|
507
|
+
* fallback={(error, reset) => (
|
|
508
|
+
* <div>
|
|
509
|
+
* <p>Something went wrong: {error.message}</p>
|
|
510
|
+
* <button onClick={reset}>Try again</button>
|
|
511
|
+
* </div>
|
|
512
|
+
* )}
|
|
513
|
+
* onError={(error) => console.error('Kerne error:', error)}
|
|
514
|
+
* >
|
|
515
|
+
* <App />
|
|
516
|
+
* </KerneErrorBoundary>
|
|
517
|
+
* ```
|
|
518
|
+
*/
|
|
519
|
+
declare class KerneErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
520
|
+
constructor(props: ErrorBoundaryProps);
|
|
521
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
522
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
523
|
+
reset: () => void;
|
|
524
|
+
render(): ReactNode;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Hook version for functional components that need error state
|
|
528
|
+
*/
|
|
529
|
+
declare function useKerneError(): {
|
|
530
|
+
error: KerneError | null;
|
|
531
|
+
hasError: boolean;
|
|
532
|
+
handleError: (err: Error | KerneError) => void;
|
|
533
|
+
clearError: () => void;
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* KerneStore - External store for React 18+ useSyncExternalStore
|
|
538
|
+
*
|
|
539
|
+
* Optimized for minimal re-renders:
|
|
540
|
+
* - Subscribe to specific slices of state
|
|
541
|
+
* - Only notifies when subscribed values change
|
|
542
|
+
*/
|
|
543
|
+
|
|
544
|
+
interface KerneState {
|
|
545
|
+
user: User | null;
|
|
546
|
+
isAuthenticated: boolean;
|
|
547
|
+
isLoading: boolean;
|
|
548
|
+
subscription: Subscription | null;
|
|
549
|
+
error: Error | null;
|
|
550
|
+
}
|
|
551
|
+
type Listener = () => void;
|
|
552
|
+
type Selector<T> = (state: KerneState) => T;
|
|
553
|
+
declare class KerneStore {
|
|
554
|
+
private state;
|
|
555
|
+
private listeners;
|
|
556
|
+
constructor(initialState?: Partial<KerneState>);
|
|
557
|
+
getState(): KerneState;
|
|
558
|
+
getSnapshot(): KerneState;
|
|
559
|
+
getServerSnapshot(): KerneState;
|
|
560
|
+
setState(partial: Partial<KerneState>): void;
|
|
561
|
+
private hasChanged;
|
|
562
|
+
subscribe(listener: Listener): () => void;
|
|
563
|
+
private notify;
|
|
564
|
+
select<T>(selector: Selector<T>): T;
|
|
565
|
+
}
|
|
86
566
|
|
|
87
|
-
export { KerneClient, KerneProvider, type KerneProviderProps, type KerneReactConfig,
|
|
567
|
+
export { Allows, AuthLoading, Authenticated, HasSubscription, KerneClient, KerneContext, type KerneError, KerneErrorBoundary, KerneProvider, type KerneProviderProps, type KerneReactConfig, type KerneState, KerneStore, Protected, Unauthenticated, useAllows, useAuth, useCheck, useCheckout, useClient, useEntitlements, useInvitation, useKerneError, usePlans, usePortal, useSubscription, useUsage, useUser, useWaitlist };
|