@kerne/react 0.1.0 → 0.1.2
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 +87 -0
- package/dist/chunk-3PROPZWW.cjs +1130 -0
- package/dist/chunk-AIUIBAYB.js +1130 -0
- package/dist/i18n-Qketn1RF.d.cts +241 -0
- package/dist/i18n-Qketn1RF.d.ts +241 -0
- package/dist/index.cjs +177 -229
- package/dist/index.d.cts +583 -26
- package/dist/index.d.ts +583 -26
- package/dist/index.js +170 -192
- package/dist/ui/index.cjs +2984 -0
- package/dist/ui/index.d.cts +391 -0
- package/dist/ui/index.d.ts +391 -0
- package/dist/ui/index.js +2984 -0
- package/package.json +29 -12
package/dist/index.d.cts
CHANGED
|
@@ -1,87 +1,644 @@
|
|
|
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, ActivationContext, AuthConfig, EntitlementCheck, SubscriptionWithPlan, PublicPlan, Entitlements, UsageRecord } from '@kerne/types';
|
|
7
|
+
export { ActivationContext, AuthConfig, AuthResponse, EntitlementCheck, Entitlements, PublicPlan, Subscription, SubscriptionWithPlan, UsageRecord, User } from '@kerne/types';
|
|
8
|
+
import { D as DeepPartial, K as KerneLocalization } from './i18n-Qketn1RF.cjs';
|
|
9
|
+
export { d as defaultLocalization } from './i18n-Qketn1RF.cjs';
|
|
6
10
|
|
|
7
11
|
interface KerneReactConfig extends Omit<KerneConfig, 'secretKey'> {
|
|
8
12
|
storage?: Storage;
|
|
9
13
|
storageKey?: string;
|
|
10
14
|
onAuthChange?: (user: User | null) => void;
|
|
15
|
+
/** Auto-refresh session before expiry (default: true) */
|
|
16
|
+
autoRefresh?: boolean;
|
|
17
|
+
/** Refresh buffer in seconds before expiry (default: 60) */
|
|
18
|
+
refreshBuffer?: number;
|
|
11
19
|
}
|
|
20
|
+
|
|
21
|
+
type Listener = () => void;
|
|
12
22
|
declare class KerneClient {
|
|
13
23
|
private kerne;
|
|
14
24
|
private storage;
|
|
15
25
|
private storageKey;
|
|
16
26
|
private onAuthChange?;
|
|
17
|
-
private
|
|
18
|
-
private
|
|
27
|
+
private autoRefresh;
|
|
28
|
+
private refreshBuffer;
|
|
29
|
+
private refreshTimer;
|
|
30
|
+
private listeners;
|
|
31
|
+
private _user;
|
|
32
|
+
private _token;
|
|
33
|
+
private _refreshToken;
|
|
34
|
+
private _expiresAt;
|
|
35
|
+
private _isLoading;
|
|
36
|
+
private _authConfigPromise;
|
|
37
|
+
readonly baseUrl: string;
|
|
38
|
+
readonly appId: string;
|
|
19
39
|
constructor(config: KerneReactConfig);
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
private clearSession;
|
|
40
|
+
subscribe(listener: Listener): () => void;
|
|
41
|
+
private notify;
|
|
23
42
|
get user(): User | null;
|
|
24
43
|
get token(): string | null;
|
|
25
44
|
get isAuthenticated(): boolean;
|
|
26
|
-
get
|
|
27
|
-
|
|
45
|
+
get isLoading(): boolean;
|
|
46
|
+
private loadSession;
|
|
47
|
+
private getHandoffCodeFromUrl;
|
|
48
|
+
/**
|
|
49
|
+
* Strips only `kerne_handoff` from the URL, leaving the rest of the
|
|
50
|
+
* path/query (e.g. a `return_to` the host app added) untouched - and
|
|
51
|
+
* without adding a history entry, so back-navigation doesn't resurrect it.
|
|
52
|
+
*/
|
|
53
|
+
private stripHandoffParamFromUrl;
|
|
54
|
+
/**
|
|
55
|
+
* Same shape as `refreshOnLoad`: a bootstrap-only path invoked once from
|
|
56
|
+
* `loadSession()` when a `kerne_handoff` code is present in the URL,
|
|
57
|
+
* entirely invisible to the host app - no code on their end required.
|
|
58
|
+
*
|
|
59
|
+
* Works no matter which page the browser lands on: `redirectUrl` is a hard
|
|
60
|
+
* navigation (`window.location.href`), so the destination reloads the SDK
|
|
61
|
+
* fresh and `loadSession()` reads the code straight off that page's own
|
|
62
|
+
* URL - there is no dependency on a specific "callback route", as long as
|
|
63
|
+
* `KerneProvider` wraps whatever the host renders there (normally true when
|
|
64
|
+
* it is mounted once at the app root).
|
|
65
|
+
*
|
|
66
|
+
* One retry on a transient failure (network blip, 5xx) - worth it because
|
|
67
|
+
* the code was never reached by the server in that case, so it is still
|
|
68
|
+
* unspent. A 4xx (invalid/expired/already-consumed) is not retried: the
|
|
69
|
+
* code is gone, retrying only delays the "logged out" outcome.
|
|
70
|
+
*/
|
|
71
|
+
private exchangeHandoffOnLoad;
|
|
72
|
+
private refreshOnLoad;
|
|
73
|
+
/**
|
|
74
|
+
* `AuthResponse.user` is the minimal `{id, email, role}` claim set the
|
|
75
|
+
* token endpoint returns, not the full profile (`User`) - so this always
|
|
76
|
+
* follows up with `users.me()` rather than assigning it directly (that
|
|
77
|
+
* used to leave `_user` looking like a `User` while actually missing
|
|
78
|
+
* `email_verified`/`first_name`/`avatar`/etc).
|
|
79
|
+
*/
|
|
80
|
+
private saveSession;
|
|
81
|
+
private clearSession;
|
|
82
|
+
private scheduleRefresh;
|
|
83
|
+
private cancelRefresh;
|
|
84
|
+
private silentRefresh;
|
|
28
85
|
register(params: {
|
|
29
86
|
email: string;
|
|
30
87
|
password: string;
|
|
31
88
|
name?: string;
|
|
89
|
+
invitationToken?: string;
|
|
90
|
+
invitationCode?: string;
|
|
32
91
|
}): Promise<AuthResponse>;
|
|
33
92
|
login(params: {
|
|
34
93
|
email: string;
|
|
35
94
|
password: string;
|
|
36
95
|
}): Promise<AuthResponse>;
|
|
96
|
+
/** Manually trigger a token refresh - normally handled automatically by `autoRefresh`. */
|
|
37
97
|
refreshToken(): Promise<AuthResponse | null>;
|
|
38
98
|
logout(): void;
|
|
39
99
|
refreshUser(): Promise<User | null>;
|
|
40
|
-
|
|
41
|
-
|
|
100
|
+
updateProfile(params: {
|
|
101
|
+
first_name?: string;
|
|
102
|
+
last_name?: string;
|
|
103
|
+
}): Promise<User>;
|
|
104
|
+
updatePassword(params: {
|
|
105
|
+
currentPassword: string;
|
|
106
|
+
newPassword: string;
|
|
107
|
+
}): Promise<void>;
|
|
108
|
+
/** Soft-deletes (deactivates) the caller's own account, then clears the local session. */
|
|
109
|
+
requestAccountDeletion(): Promise<void>;
|
|
110
|
+
/** Forgot-password flow (logged out) - sends the reset email. */
|
|
111
|
+
requestPasswordReset(email: string, callbackUrl?: string): Promise<void>;
|
|
112
|
+
/** Confirms a password reset with the token from the email. */
|
|
113
|
+
confirmPasswordReset(token: string, password: string): Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Sends a magic-link email (logged out). Anti-enumeration: always resolves,
|
|
116
|
+
* regardless of whether the email is registered.
|
|
117
|
+
*/
|
|
118
|
+
startPasswordless(email: string, callbackUrl?: string, invitation?: {
|
|
119
|
+
invitationToken?: string;
|
|
120
|
+
invitationCode?: string;
|
|
121
|
+
}): Promise<void>;
|
|
122
|
+
/** Completes a magic-link login using the `token` query param from the emailed link. */
|
|
123
|
+
loginWithMagicLink(token: string): Promise<AuthResponse>;
|
|
124
|
+
/** Read-only: which UI to render for an activation token, without consuming it. */
|
|
125
|
+
getActivationContext(token: string): Promise<ActivationContext>;
|
|
126
|
+
/** Completes activation (sets a password or confirms magic-link mode), then logs the user in. */
|
|
127
|
+
completeActivation(token: string, password?: string): Promise<AuthResponse>;
|
|
128
|
+
/**
|
|
129
|
+
* Mints a handoff code for the current session and redirects the browser
|
|
130
|
+
* to `url` with it attached (`?kerne_handoff=...`) - never the real
|
|
131
|
+
* token/refresh_token, which would otherwise leak into server logs,
|
|
132
|
+
* browser history, and third-party `Referer` headers on the landing page.
|
|
133
|
+
*/
|
|
134
|
+
redirectWithSession(url: string): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* The tenant's public auth config - which sign-in methods to render, whether
|
|
137
|
+
* registration is open, whether to show Kerne branding.
|
|
138
|
+
*
|
|
139
|
+
* Deduped on the in-flight promise rather than the resolved value: the
|
|
140
|
+
* prebuilt forms all read this on mount, and two of them mounted together
|
|
141
|
+
* (a login screen with a register link prefetching) would otherwise fire two
|
|
142
|
+
* identical requests before either resolved. Cached for the client's
|
|
143
|
+
* lifetime - this config changes on the order of "the owner edited their
|
|
144
|
+
* settings", not per render.
|
|
145
|
+
*/
|
|
146
|
+
getAuthConfig(): Promise<AuthConfig>;
|
|
147
|
+
/**
|
|
148
|
+
* Errors are not swallowed here (unlike the old implementation, which
|
|
149
|
+
* caught everything and returned `false`) - a 401/500 must not be
|
|
150
|
+
* indistinguishable from a real denial, that's exactly what let the
|
|
151
|
+
* `has_access`/`allowed` mismatch below ship unnoticed. Callers that want
|
|
152
|
+
* a fail-closed boolean regardless of the reason (e.g. `<Allows>`)
|
|
153
|
+
* catch around this themselves.
|
|
154
|
+
*/
|
|
155
|
+
allows(featureKey: string, requested?: number): Promise<boolean>;
|
|
156
|
+
/** Full entitlement detail (limit/used/remaining/overage) - use `allows()` for a plain boolean. */
|
|
157
|
+
check(featureKey: string, requested?: number): Promise<EntitlementCheck>;
|
|
158
|
+
createCheckout(planPriceId: string, options?: {
|
|
42
159
|
successUrl?: string;
|
|
43
160
|
cancelUrl?: string;
|
|
44
161
|
}): Promise<string>;
|
|
45
|
-
openCheckout(
|
|
162
|
+
openCheckout(planPriceId: string, options?: {
|
|
46
163
|
successUrl?: string;
|
|
47
164
|
cancelUrl?: string;
|
|
48
165
|
}): Promise<void>;
|
|
49
166
|
createPortal(returnUrl?: string): Promise<string>;
|
|
50
167
|
openPortal(returnUrl?: string): Promise<void>;
|
|
168
|
+
getSubscription(productSlug?: string): Promise<SubscriptionWithPlan | null>;
|
|
169
|
+
/** Public pricing data - omit `productIdOrSlug` for the tenant's default product. */
|
|
170
|
+
getPlans(productIdOrSlug?: string): Promise<PublicPlan[]>;
|
|
171
|
+
/** Every entitlement for the current user in one call - for a "your plan" screen. */
|
|
172
|
+
getEntitlements(): Promise<Entitlements>;
|
|
173
|
+
/** Usage for the current user - all QUOTA features, or one via `featureKey`. */
|
|
174
|
+
getUsage(featureKey?: string): Promise<UsageRecord[]>;
|
|
175
|
+
joinWaitlist(params: JoinWaitlistParams): Promise<{
|
|
176
|
+
success: boolean;
|
|
177
|
+
}>;
|
|
178
|
+
/** Validate an invitation token before showing the registration form. */
|
|
179
|
+
validateInvitationToken(token: string): Promise<ValidateTokenResponse>;
|
|
180
|
+
/** Validate an invitation code before showing the registration form. */
|
|
181
|
+
validateInvitationCode(code: string): Promise<ValidateCodeResponse>;
|
|
182
|
+
sendVerificationEmail(verificationType?: 'code' | 'link', callbackUrl?: string): Promise<void>;
|
|
183
|
+
verifyEmailWithCode(code: string): Promise<{
|
|
184
|
+
success: boolean;
|
|
185
|
+
needsTokenRefresh: boolean;
|
|
186
|
+
}>;
|
|
187
|
+
verifyEmailWithLink(token: string): Promise<{
|
|
188
|
+
success: boolean;
|
|
189
|
+
needsTokenRefresh: boolean;
|
|
190
|
+
}>;
|
|
191
|
+
getVerificationStatus(): Promise<{
|
|
192
|
+
emailVerified: boolean;
|
|
193
|
+
needsVerification: boolean;
|
|
194
|
+
email: string;
|
|
195
|
+
}>;
|
|
51
196
|
}
|
|
52
|
-
declare
|
|
197
|
+
declare const KerneContext: React.Context<KerneClient | null>;
|
|
53
198
|
interface KerneProviderProps extends KerneReactConfig {
|
|
54
199
|
children: React.ReactNode;
|
|
200
|
+
/**
|
|
201
|
+
* Overrides for the prebuilt components' copy, including the error-code
|
|
202
|
+
* messages. Set once here rather than per component - the strings are shared,
|
|
203
|
+
* and a per-component prop would mean re-passing the same dictionary to every
|
|
204
|
+
* screen. Merged group-by-group over the English defaults, so a partial
|
|
205
|
+
* override keeps everything it does not mention.
|
|
206
|
+
*/
|
|
207
|
+
localization?: DeepPartial<KerneLocalization>;
|
|
55
208
|
}
|
|
56
|
-
declare function KerneProvider({ children, ...config }: KerneProviderProps): react_jsx_runtime.JSX.Element;
|
|
57
|
-
|
|
209
|
+
declare function KerneProvider({ children, localization, ...config }: KerneProviderProps): react_jsx_runtime.JSX.Element;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Access the Kerne client instance
|
|
213
|
+
*/
|
|
214
|
+
declare function useClient(): KerneClient;
|
|
215
|
+
/**
|
|
216
|
+
* Authentication hook with optimized re-renders
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```tsx
|
|
220
|
+
* const { user, isAuthenticated, login, logout } = useAuth();
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
58
223
|
declare function useAuth(): {
|
|
59
|
-
user: User | null;
|
|
224
|
+
user: _kerne_types.User | null;
|
|
225
|
+
token: string | null;
|
|
60
226
|
isAuthenticated: boolean;
|
|
227
|
+
isLoading: boolean;
|
|
61
228
|
login: (params: {
|
|
62
229
|
email: string;
|
|
63
230
|
password: string;
|
|
64
|
-
}) => Promise<AuthResponse>;
|
|
231
|
+
}) => Promise<_kerne_types.AuthResponse>;
|
|
65
232
|
register: (params: {
|
|
66
233
|
email: string;
|
|
67
234
|
password: string;
|
|
68
235
|
name?: string;
|
|
69
|
-
|
|
236
|
+
invitationToken?: string;
|
|
237
|
+
invitationCode?: string;
|
|
238
|
+
}) => Promise<_kerne_types.AuthResponse>;
|
|
70
239
|
logout: () => void;
|
|
71
|
-
refreshUser: () => Promise<User | null>;
|
|
240
|
+
refreshUser: () => Promise<_kerne_types.User | null>;
|
|
241
|
+
refreshToken: () => Promise<_kerne_types.AuthResponse | null>;
|
|
242
|
+
updateProfile: (params: {
|
|
243
|
+
first_name?: string;
|
|
244
|
+
last_name?: string;
|
|
245
|
+
avatar?: string;
|
|
246
|
+
}) => Promise<_kerne_types.User>;
|
|
247
|
+
updatePassword: (params: {
|
|
248
|
+
currentPassword: string;
|
|
249
|
+
newPassword: string;
|
|
250
|
+
}) => Promise<void>;
|
|
251
|
+
requestAccountDeletion: () => Promise<void>;
|
|
252
|
+
requestPasswordReset: (email: string, callbackUrl?: string) => Promise<void>;
|
|
253
|
+
confirmPasswordReset: (token: string, password: string) => Promise<void>;
|
|
254
|
+
startPasswordless: (email: string, callbackUrl?: string, invitation?: {
|
|
255
|
+
invitationToken?: string;
|
|
256
|
+
invitationCode?: string;
|
|
257
|
+
}) => Promise<void>;
|
|
258
|
+
loginWithMagicLink: (token: string) => Promise<_kerne_types.AuthResponse>;
|
|
259
|
+
sendVerificationEmail: (verificationType?: "code" | "link", callbackUrl?: string) => Promise<void>;
|
|
260
|
+
verifyEmailWithCode: (code: string) => Promise<{
|
|
261
|
+
success: boolean;
|
|
262
|
+
needsTokenRefresh: boolean;
|
|
263
|
+
}>;
|
|
264
|
+
verifyEmailWithLink: (token: string) => Promise<{
|
|
265
|
+
success: boolean;
|
|
266
|
+
needsTokenRefresh: boolean;
|
|
267
|
+
}>;
|
|
268
|
+
getVerificationStatus: () => Promise<{
|
|
269
|
+
emailVerified: boolean;
|
|
270
|
+
needsVerification: boolean;
|
|
271
|
+
email: string;
|
|
272
|
+
}>;
|
|
273
|
+
getActivationContext: (token: string) => Promise<_kerne_types.ActivationContext>;
|
|
274
|
+
completeActivation: (token: string, password?: string) => Promise<_kerne_types.AuthResponse>;
|
|
275
|
+
redirectWithSession: (url: string) => Promise<void>;
|
|
72
276
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
277
|
+
/**
|
|
278
|
+
* Checkout hook - starts a Stripe/Polar checkout session for a plan price.
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```tsx
|
|
282
|
+
* const { openCheckout } = useCheckout();
|
|
283
|
+
* <button onClick={() => openCheckout(planPriceId)}>Upgrade</button>
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
286
|
+
declare function useCheckout(): {
|
|
287
|
+
createCheckout: (planPriceId: string, options?: {
|
|
76
288
|
successUrl?: string;
|
|
77
289
|
cancelUrl?: string;
|
|
78
290
|
}) => Promise<string>;
|
|
79
|
-
openCheckout: (
|
|
291
|
+
openCheckout: (planPriceId: string, options?: {
|
|
80
292
|
successUrl?: string;
|
|
81
293
|
cancelUrl?: string;
|
|
82
294
|
}) => Promise<void>;
|
|
295
|
+
};
|
|
296
|
+
/**
|
|
297
|
+
* Billing portal hook - lets a user manage their subscription/payment method.
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```tsx
|
|
301
|
+
* const { openPortal } = usePortal();
|
|
302
|
+
* <button onClick={() => openPortal()}>Manage billing</button>
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
declare function usePortal(): {
|
|
83
306
|
createPortal: (returnUrl?: string) => Promise<string>;
|
|
84
307
|
openPortal: (returnUrl?: string) => Promise<void>;
|
|
85
308
|
};
|
|
309
|
+
/**
|
|
310
|
+
* Whether the current scope has access to one feature - the single entry
|
|
311
|
+
* point for a live, per-feature decision. Deliberately not named after what
|
|
312
|
+
* it checks TODAY: this gates entitlements for now, but the same shape is
|
|
313
|
+
* meant to answer for a future access-rule engine (custom RBAC, feature
|
|
314
|
+
* flags) without a rename - "access" is the word that survives that.
|
|
315
|
+
* `details` (not `entitlement`) follows the same logic on the return value.
|
|
316
|
+
*
|
|
317
|
+
* Not `useCheck` either - too close to `useCheckout` to skim safely, and
|
|
318
|
+
* unrelated to it in every other way. Not folded into `useEntitlements()`
|
|
319
|
+
* (the cached list, for display): the two hit different endpoints with
|
|
320
|
+
* different consistency guarantees, and merging them would make the network
|
|
321
|
+
* behavior branch silently on whether a key was passed.
|
|
322
|
+
*
|
|
323
|
+
* Replaces the former `useAllows`/`useCheck` pair, which were two names for
|
|
324
|
+
* one request: `client.allows()` is literally `check().allowed`, hitting the
|
|
325
|
+
* same endpoint and discarding the rest.
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```tsx
|
|
329
|
+
* const { allowed } = useAccess('advanced_analytics');
|
|
330
|
+
* const { details } = useAccess('api_calls');
|
|
331
|
+
* // details?.remaining, details?.limit, details?.overage_behavior
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
declare function useAccess(featureKey: string, requested?: number): {
|
|
335
|
+
details: EntitlementCheck | null;
|
|
336
|
+
allowed: boolean;
|
|
337
|
+
isLoading: boolean;
|
|
338
|
+
error: Error | null;
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* Hook for subscription state
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```tsx
|
|
345
|
+
* const { subscription, isLoading, isPro } = useSubscription();
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
declare function useSubscription(productSlug?: string): {
|
|
349
|
+
isActive: boolean;
|
|
350
|
+
planSlug: string | undefined;
|
|
351
|
+
subscription: SubscriptionWithPlan | null;
|
|
352
|
+
isLoading: boolean;
|
|
353
|
+
error: Error | null;
|
|
354
|
+
};
|
|
355
|
+
/**
|
|
356
|
+
* Public pricing data for a pricing/checkout page - omit `productIdOrSlug`
|
|
357
|
+
* for the tenant's default product.
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```tsx
|
|
361
|
+
* const { plans, isLoading } = usePlans();
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
declare function usePlans(productIdOrSlug?: string): {
|
|
365
|
+
plans: PublicPlan[];
|
|
366
|
+
isLoading: boolean;
|
|
367
|
+
error: Error | null;
|
|
368
|
+
};
|
|
369
|
+
/**
|
|
370
|
+
* The tenant's public auth config - which sign-in methods are enabled, whether
|
|
371
|
+
* registration is open, whether to show Kerne branding. Drives what the
|
|
372
|
+
* prebuilt forms render, so a tenant that turns magic link on gets the button
|
|
373
|
+
* without shipping any code.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```tsx
|
|
377
|
+
* const { config, isLoading } = useAuthConfig();
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
380
|
+
declare function useAuthConfig(): {
|
|
381
|
+
config: AuthConfig | null;
|
|
382
|
+
isLoading: boolean;
|
|
383
|
+
error: Error | null;
|
|
384
|
+
};
|
|
385
|
+
/**
|
|
386
|
+
* Every entitlement for the current user in one call - for a "your plan"
|
|
387
|
+
* screen. Use `useAccess` instead for a single feature - this response
|
|
388
|
+
* never carries `allowed` (see docs: the list is cached, a stale verdict
|
|
389
|
+
* would be unsafe).
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* ```tsx
|
|
393
|
+
* const { entitlements, isLoading } = useEntitlements();
|
|
394
|
+
* ```
|
|
395
|
+
*/
|
|
396
|
+
declare function useEntitlements(): {
|
|
397
|
+
entitlements: Entitlements | null;
|
|
398
|
+
isLoading: boolean;
|
|
399
|
+
error: Error | null;
|
|
400
|
+
};
|
|
401
|
+
/**
|
|
402
|
+
* Usage for the current user - every QUOTA feature, or one via `featureKey`.
|
|
403
|
+
*
|
|
404
|
+
* @example
|
|
405
|
+
* ```tsx
|
|
406
|
+
* const { usage, isLoading } = useUsage('api_calls');
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
declare function useUsage(featureKey?: string): {
|
|
410
|
+
usage: UsageRecord[];
|
|
411
|
+
isLoading: boolean;
|
|
412
|
+
error: Error | null;
|
|
413
|
+
};
|
|
414
|
+
/**
|
|
415
|
+
* Waitlist signup form - a mutation, not a data fetch, same shape as
|
|
416
|
+
* `useCheckout`/`usePortal`.
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* ```tsx
|
|
420
|
+
* const { join } = useWaitlist();
|
|
421
|
+
* await join({ email });
|
|
422
|
+
* ```
|
|
423
|
+
*/
|
|
424
|
+
declare function useWaitlist(): {
|
|
425
|
+
join: (params: JoinWaitlistParams) => Promise<{
|
|
426
|
+
success: boolean;
|
|
427
|
+
}>;
|
|
428
|
+
};
|
|
429
|
+
/**
|
|
430
|
+
* Validates an invitation token/code before showing the registration form -
|
|
431
|
+
* pass whichever one is present in the URL.
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* ```tsx
|
|
435
|
+
* const { result, isLoading } = useInvitation({ token });
|
|
436
|
+
* if (!isLoading && !result?.valid) return <InvalidInvite />;
|
|
437
|
+
* ```
|
|
438
|
+
*/
|
|
439
|
+
declare function useInvitation(params: {
|
|
440
|
+
token?: string;
|
|
441
|
+
code?: string;
|
|
442
|
+
}): {
|
|
443
|
+
result: ValidateTokenResponse | ValidateCodeResponse | null;
|
|
444
|
+
isLoading: boolean;
|
|
445
|
+
error: Error | null;
|
|
446
|
+
};
|
|
447
|
+
/**
|
|
448
|
+
* Hook for user profile data
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* ```tsx
|
|
452
|
+
* const { user, isLoading, update } = useUser();
|
|
453
|
+
* ```
|
|
454
|
+
*/
|
|
455
|
+
declare function useUser(): {
|
|
456
|
+
user: _kerne_types.User | null;
|
|
457
|
+
isLoading: boolean;
|
|
458
|
+
update: (params: {
|
|
459
|
+
first_name?: string;
|
|
460
|
+
last_name?: string;
|
|
461
|
+
avatar?: string;
|
|
462
|
+
}) => Promise<_kerne_types.User>;
|
|
463
|
+
fullName: string | null;
|
|
464
|
+
initials: string | null;
|
|
465
|
+
email: string | null;
|
|
466
|
+
emailVerified: boolean;
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Guardian Components
|
|
471
|
+
*
|
|
472
|
+
* Conditional rendering based on auth/billing state.
|
|
473
|
+
* Inspired by Clerk's <SignedIn>, <SignedOut> pattern.
|
|
474
|
+
*/
|
|
475
|
+
|
|
476
|
+
interface AuthGuardProps {
|
|
477
|
+
children: ReactNode;
|
|
478
|
+
/** Fallback content when condition is not met */
|
|
479
|
+
fallback?: ReactNode;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Renders children only when user is authenticated
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```tsx
|
|
486
|
+
* <Authenticated>
|
|
487
|
+
* <Dashboard />
|
|
488
|
+
* </Authenticated>
|
|
489
|
+
* ```
|
|
490
|
+
*/
|
|
491
|
+
declare function Authenticated({ children, fallback }: AuthGuardProps): ReactNode;
|
|
492
|
+
/**
|
|
493
|
+
* Renders children only when user is NOT authenticated
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* ```tsx
|
|
497
|
+
* <Unauthenticated>
|
|
498
|
+
* <LoginPrompt />
|
|
499
|
+
* </Unauthenticated>
|
|
500
|
+
* ```
|
|
501
|
+
*/
|
|
502
|
+
declare function Unauthenticated({ children, fallback }: AuthGuardProps): ReactNode;
|
|
503
|
+
/**
|
|
504
|
+
* Renders children while auth state is loading
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* ```tsx
|
|
508
|
+
* <AuthLoading>
|
|
509
|
+
* <Spinner />
|
|
510
|
+
* </AuthLoading>
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
declare function AuthLoading({ children }: {
|
|
514
|
+
children: ReactNode;
|
|
515
|
+
}): ReactNode;
|
|
516
|
+
interface SubscriptionGuardProps {
|
|
517
|
+
children: ReactNode;
|
|
518
|
+
/** Fallback content when there's no active subscription */
|
|
519
|
+
fallback?: ReactNode;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Renders children only when user has an active subscription
|
|
523
|
+
*
|
|
524
|
+
* @example
|
|
525
|
+
* ```tsx
|
|
526
|
+
* <HasSubscription fallback={<UpgradePrompt />}>
|
|
527
|
+
* <PremiumFeature />
|
|
528
|
+
* </HasSubscription>
|
|
529
|
+
* ```
|
|
530
|
+
*/
|
|
531
|
+
declare function HasSubscription({ children, fallback }: SubscriptionGuardProps): ReactNode;
|
|
532
|
+
interface EntitlementGuardProps {
|
|
533
|
+
children: ReactNode;
|
|
534
|
+
/**
|
|
535
|
+
* Capability key to check.
|
|
536
|
+
*
|
|
537
|
+
* NOT named `key`: React reserves that prop for reconciliation and strips it
|
|
538
|
+
* before the component sees it, so the previous `key="..."` signature could
|
|
539
|
+
* only ever read `undefined` - the check then failed and the guard rendered
|
|
540
|
+
* its fallback forever. Same name as `useAccess(featureKey)` and
|
|
541
|
+
* `UpgradePrompt`, so the whole entitlement surface reads alike.
|
|
542
|
+
*/
|
|
543
|
+
featureKey: string;
|
|
544
|
+
/** Minimum value required (for limit features) */
|
|
545
|
+
minimum?: number;
|
|
546
|
+
/** Fallback content when entitlement is not met */
|
|
547
|
+
fallback?: ReactNode;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Renders children only when the scope is entitled to a capability. Checks the
|
|
551
|
+
* entitlement itself, never a plan name - a plan can be renamed or restructured
|
|
552
|
+
* without breaking every place that gated on its slug.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```tsx
|
|
556
|
+
* <Allows featureKey="advanced_analytics" fallback={<UpgradePrompt featureKey="advanced_analytics" />}>
|
|
557
|
+
* <AnalyticsDashboard />
|
|
558
|
+
* </Allows>
|
|
559
|
+
* ```
|
|
560
|
+
*/
|
|
561
|
+
declare function Allows({ children, featureKey, minimum, fallback, }: EntitlementGuardProps): ReactNode;
|
|
562
|
+
interface ProtectedProps {
|
|
563
|
+
children: ReactNode;
|
|
564
|
+
/** Require authentication */
|
|
565
|
+
auth?: boolean;
|
|
566
|
+
/** Require a specific capability. Not `key` - React reserves that prop name. */
|
|
567
|
+
featureKey?: string;
|
|
568
|
+
/** Fallback for unauthenticated */
|
|
569
|
+
authFallback?: ReactNode;
|
|
570
|
+
/** Fallback for missing feature */
|
|
571
|
+
billingFallback?: ReactNode;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Combined guard for auth + entitlement requirements
|
|
575
|
+
*
|
|
576
|
+
* @example
|
|
577
|
+
* ```tsx
|
|
578
|
+
* <Protected auth featureKey="advanced_analytics" authFallback={<Login />} billingFallback={<Upgrade />}>
|
|
579
|
+
* <ProFeature />
|
|
580
|
+
* </Protected>
|
|
581
|
+
* ```
|
|
582
|
+
*/
|
|
583
|
+
declare function Protected({ children, auth, featureKey, authFallback, billingFallback, }: ProtectedProps): ReactNode;
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Kerne Error Boundary
|
|
587
|
+
*
|
|
588
|
+
* Catches auth/billing errors and provides recovery UI
|
|
589
|
+
*/
|
|
590
|
+
|
|
591
|
+
interface KerneError extends Error {
|
|
592
|
+
code?: string;
|
|
593
|
+
statusCode?: number;
|
|
594
|
+
details?: Record<string, any>;
|
|
595
|
+
}
|
|
596
|
+
interface ErrorBoundaryProps {
|
|
597
|
+
children: ReactNode;
|
|
598
|
+
/** Custom fallback UI */
|
|
599
|
+
fallback?: ReactNode | ((error: KerneError, reset: () => void) => ReactNode);
|
|
600
|
+
/** Called when error is caught */
|
|
601
|
+
onError?: (error: KerneError, errorInfo: ErrorInfo) => void;
|
|
602
|
+
/** Called when user clicks reset */
|
|
603
|
+
onReset?: () => void;
|
|
604
|
+
}
|
|
605
|
+
interface ErrorBoundaryState {
|
|
606
|
+
error: KerneError | null;
|
|
607
|
+
hasError: boolean;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Error boundary for Kerne SDK errors
|
|
611
|
+
*
|
|
612
|
+
* @example
|
|
613
|
+
* ```tsx
|
|
614
|
+
* <KerneErrorBoundary
|
|
615
|
+
* fallback={(error, reset) => (
|
|
616
|
+
* <div>
|
|
617
|
+
* <p>Something went wrong: {error.message}</p>
|
|
618
|
+
* <button onClick={reset}>Try again</button>
|
|
619
|
+
* </div>
|
|
620
|
+
* )}
|
|
621
|
+
* onError={(error) => console.error('Kerne error:', error)}
|
|
622
|
+
* >
|
|
623
|
+
* <App />
|
|
624
|
+
* </KerneErrorBoundary>
|
|
625
|
+
* ```
|
|
626
|
+
*/
|
|
627
|
+
declare class KerneErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
628
|
+
constructor(props: ErrorBoundaryProps);
|
|
629
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
630
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
631
|
+
reset: () => void;
|
|
632
|
+
render(): ReactNode;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Hook version for functional components that need error state
|
|
636
|
+
*/
|
|
637
|
+
declare function useKerneError(): {
|
|
638
|
+
error: KerneError | null;
|
|
639
|
+
hasError: boolean;
|
|
640
|
+
handleError: (err: Error | KerneError) => void;
|
|
641
|
+
clearError: () => void;
|
|
642
|
+
};
|
|
86
643
|
|
|
87
|
-
export { KerneClient, KerneProvider, type KerneProviderProps, type KerneReactConfig,
|
|
644
|
+
export { Allows, AuthLoading, Authenticated, HasSubscription, KerneClient, KerneContext, type KerneError, KerneErrorBoundary, KerneLocalization, KerneProvider, type KerneProviderProps, type KerneReactConfig, Protected, Unauthenticated, useAccess, useAuth, useAuthConfig, useCheckout, useClient, useEntitlements, useInvitation, useKerneError, usePlans, usePortal, useSubscription, useUsage, useUser, useWaitlist };
|