@kerne/react 0.1.2 → 1.0.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/README.md +18 -5
- package/dist/{chunk-3PROPZWW.cjs → chunk-IMZFZ3P5.cjs} +122 -30
- package/dist/{chunk-AIUIBAYB.js → chunk-NFJ2S5VJ.js} +110 -18
- package/dist/{i18n-Qketn1RF.d.cts → i18n-Di5qlnL1.d.cts} +44 -0
- package/dist/{i18n-Qketn1RF.d.ts → i18n-Di5qlnL1.d.ts} +44 -0
- package/dist/index.cjs +16 -20
- package/dist/index.d.cts +63 -13
- package/dist/index.d.ts +63 -13
- package/dist/index.js +11 -15
- package/dist/ui/index.cjs +990 -194
- package/dist/ui/index.d.cts +67 -4
- package/dist/ui/index.d.ts +67 -4
- package/dist/ui/index.js +970 -174
- package/package.json +3 -3
|
@@ -16,6 +16,22 @@ interface KerneLocalization {
|
|
|
16
16
|
continueWith: string;
|
|
17
17
|
securedByKerne: string;
|
|
18
18
|
loading: string;
|
|
19
|
+
showPassword: string;
|
|
20
|
+
hidePassword: string;
|
|
21
|
+
capsLockOn: string;
|
|
22
|
+
/**
|
|
23
|
+
* The tenant's password rules, stated before the server can reject them.
|
|
24
|
+
* Only the rules that tenant actually enforces are rendered.
|
|
25
|
+
*/
|
|
26
|
+
passwordHint: string;
|
|
27
|
+
passwordRequireUppercase: string;
|
|
28
|
+
passwordRequireNumber: string;
|
|
29
|
+
passwordRequireSymbol: string;
|
|
30
|
+
strengthLabel: string;
|
|
31
|
+
strengthWeak: string;
|
|
32
|
+
strengthFair: string;
|
|
33
|
+
strengthGood: string;
|
|
34
|
+
strengthStrong: string;
|
|
19
35
|
};
|
|
20
36
|
signIn: {
|
|
21
37
|
title: string;
|
|
@@ -170,6 +186,12 @@ interface KerneLocalization {
|
|
|
170
186
|
freePlan: string;
|
|
171
187
|
seePlans: string;
|
|
172
188
|
cancelNotice: string;
|
|
189
|
+
cancelSubscription: string;
|
|
190
|
+
canceling: string;
|
|
191
|
+
cancelConfirmTitle: string;
|
|
192
|
+
cancelConfirmBody: string;
|
|
193
|
+
cancelConfirmButton: string;
|
|
194
|
+
cancelConfirmDismiss: string;
|
|
173
195
|
changePlan: string;
|
|
174
196
|
current: string;
|
|
175
197
|
switchTo: string;
|
|
@@ -195,8 +217,30 @@ interface KerneLocalization {
|
|
|
195
217
|
subtitle: string;
|
|
196
218
|
used: string;
|
|
197
219
|
nothingMetered: string;
|
|
220
|
+
/** Overage ALLOW: past the included amount, tolerated and free. */
|
|
198
221
|
overage: string;
|
|
222
|
+
/** Overage BILL: past the included amount, charged on top of the plan. */
|
|
223
|
+
overageBilled: string;
|
|
224
|
+
/** Overage BLOCK only - ALLOW and BILL never "reach" a limit, they keep going. */
|
|
199
225
|
limitReached: string;
|
|
226
|
+
/** Overage BILL whose billing cap was hit: charging stopped, access didn't - the opposite of limitReached, not a variant of overageBilled. */
|
|
227
|
+
capReached: string;
|
|
228
|
+
/** Shown under a BILL entitlement once something has actually been billed this period. */
|
|
229
|
+
billedAmount: string;
|
|
230
|
+
loading: string;
|
|
231
|
+
loadFailed: string;
|
|
232
|
+
};
|
|
233
|
+
/** `<PricingTable>` - the marketing surface. Distinct from `billing.*`, the settings-page plan switcher. */
|
|
234
|
+
pricing: {
|
|
235
|
+
getStarted: string;
|
|
236
|
+
/** "{amount} / {interval}" - the caption under a price shown at a different cadence than it's billed. */
|
|
237
|
+
billedAt: string;
|
|
238
|
+
/** "Save {percent}%" - the label-switch hint. */
|
|
239
|
+
save: string;
|
|
240
|
+
/** Prefixes a QUOTA entitlement with no ceiling, e.g. "Unlimited projects". */
|
|
241
|
+
unlimited: string;
|
|
242
|
+
/** "+{count} more" - shown when `maxFeatures` truncates a plan's entitlement list. */
|
|
243
|
+
moreFeatures: string;
|
|
200
244
|
loading: string;
|
|
201
245
|
loadFailed: string;
|
|
202
246
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -17,25 +17,27 @@
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
var _chunkIMZFZ3P5cjs = require('./chunk-IMZFZ3P5.cjs');
|
|
21
23
|
|
|
22
24
|
// src/components.tsx
|
|
23
25
|
var _react = require('react'); var _react2 = _interopRequireDefault(_react);
|
|
24
26
|
var _jsxruntime = require('react/jsx-runtime');
|
|
25
27
|
function Authenticated({ children, fallback = null }) {
|
|
26
|
-
const { isAuthenticated } =
|
|
28
|
+
const { isAuthenticated } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
|
|
27
29
|
return isAuthenticated ? children : fallback;
|
|
28
30
|
}
|
|
29
31
|
function Unauthenticated({ children, fallback = null }) {
|
|
30
|
-
const { isAuthenticated } =
|
|
32
|
+
const { isAuthenticated } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
|
|
31
33
|
return !isAuthenticated ? children : fallback;
|
|
32
34
|
}
|
|
33
35
|
function AuthLoading({ children }) {
|
|
34
|
-
const client =
|
|
36
|
+
const client = _chunkIMZFZ3P5cjs.useClient.call(void 0, );
|
|
35
37
|
return client.isLoading ? children : null;
|
|
36
38
|
}
|
|
37
39
|
function HasSubscription({ children, fallback = null }) {
|
|
38
|
-
const client =
|
|
40
|
+
const client = _chunkIMZFZ3P5cjs.useClient.call(void 0, );
|
|
39
41
|
const [hasSubscription, setHasSubscription] = _react2.default.useState(null);
|
|
40
42
|
_react2.default.useEffect(() => {
|
|
41
43
|
client.getSubscription().then((sub) => {
|
|
@@ -47,22 +49,14 @@ function HasSubscription({ children, fallback = null }) {
|
|
|
47
49
|
if (hasSubscription === null) return null;
|
|
48
50
|
return hasSubscription ? children : fallback;
|
|
49
51
|
}
|
|
50
|
-
function
|
|
52
|
+
function Access({
|
|
51
53
|
children,
|
|
52
54
|
featureKey,
|
|
53
|
-
|
|
55
|
+
requested,
|
|
54
56
|
fallback = null
|
|
55
57
|
}) {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
_react2.default.useEffect(() => {
|
|
59
|
-
client.allows(featureKey, minimum).then((result) => {
|
|
60
|
-
setAllowed(result);
|
|
61
|
-
}).catch(() => {
|
|
62
|
-
setAllowed(false);
|
|
63
|
-
});
|
|
64
|
-
}, [client, featureKey, minimum]);
|
|
65
|
-
if (allowed === null) return null;
|
|
58
|
+
const { allowed, isLoading } = _chunkIMZFZ3P5cjs.useAccess.call(void 0, featureKey, requested);
|
|
59
|
+
if (isLoading) return null;
|
|
66
60
|
return allowed ? children : fallback;
|
|
67
61
|
}
|
|
68
62
|
function Protected({
|
|
@@ -72,12 +66,12 @@ function Protected({
|
|
|
72
66
|
authFallback = null,
|
|
73
67
|
billingFallback = null
|
|
74
68
|
}) {
|
|
75
|
-
const { isAuthenticated } =
|
|
69
|
+
const { isAuthenticated } = _chunkIMZFZ3P5cjs.useAuth.call(void 0, );
|
|
76
70
|
if (auth && !isAuthenticated) {
|
|
77
71
|
return authFallback;
|
|
78
72
|
}
|
|
79
73
|
if (featureKey) {
|
|
80
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
74
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Access, { featureKey, fallback: billingFallback, children });
|
|
81
75
|
}
|
|
82
76
|
return children;
|
|
83
77
|
}
|
|
@@ -183,4 +177,6 @@ function useKerneError() {
|
|
|
183
177
|
|
|
184
178
|
|
|
185
179
|
|
|
186
|
-
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
exports.Access = Access; exports.AuthLoading = AuthLoading; exports.Authenticated = Authenticated; exports.HasSubscription = HasSubscription; exports.KerneClient = _chunkIMZFZ3P5cjs.KerneClient; exports.KerneContext = _chunkIMZFZ3P5cjs.KerneContext; exports.KerneErrorBoundary = KerneErrorBoundary; exports.KerneProvider = _chunkIMZFZ3P5cjs.KerneProvider; exports.Protected = Protected; exports.Unauthenticated = Unauthenticated; exports.defaultLocalization = _chunkIMZFZ3P5cjs.defaultLocalization; exports.useAccess = _chunkIMZFZ3P5cjs.useAccess; exports.useAuth = _chunkIMZFZ3P5cjs.useAuth; exports.useAuthConfig = _chunkIMZFZ3P5cjs.useAuthConfig; exports.useCancelSubscription = _chunkIMZFZ3P5cjs.useCancelSubscription; exports.useChangePlan = _chunkIMZFZ3P5cjs.useChangePlan; exports.useCheckout = _chunkIMZFZ3P5cjs.useCheckout; exports.useClient = _chunkIMZFZ3P5cjs.useClient; exports.useEntitlements = _chunkIMZFZ3P5cjs.useEntitlements; exports.useInvitation = _chunkIMZFZ3P5cjs.useInvitation; exports.useKerneError = useKerneError; exports.usePlans = _chunkIMZFZ3P5cjs.usePlans; exports.usePortal = _chunkIMZFZ3P5cjs.usePortal; exports.useSubscription = _chunkIMZFZ3P5cjs.useSubscription; exports.useUsage = _chunkIMZFZ3P5cjs.useUsage; exports.useUser = _chunkIMZFZ3P5cjs.useUser; exports.useWaitlist = _chunkIMZFZ3P5cjs.useWaitlist;
|
package/dist/index.d.cts
CHANGED
|
@@ -3,15 +3,25 @@ import React, { ReactNode, Component, ErrorInfo } from 'react';
|
|
|
3
3
|
import { KerneConfig, JoinWaitlistParams, ValidateTokenResponse, ValidateCodeResponse } from '@kerne/server';
|
|
4
4
|
export { JoinWaitlistParams, ValidateCodeResponse, ValidateTokenResponse, WaitlistEntry } from '@kerne/server';
|
|
5
5
|
import * as _kerne_types from '@kerne/types';
|
|
6
|
-
import { User, AuthResponse, ActivationContext, AuthConfig, EntitlementCheck, SubscriptionWithPlan, PublicPlan, Entitlements, UsageRecord } from '@kerne/types';
|
|
6
|
+
import { User, AuthResponse, ActivationContext, AuthConfig, EntitlementCheck, SubscriptionWithPlan, Subscription, PublicPlan, Entitlements, UsageRecord } from '@kerne/types';
|
|
7
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-
|
|
9
|
-
export { d as defaultLocalization } from './i18n-
|
|
8
|
+
import { D as DeepPartial, K as KerneLocalization } from './i18n-Di5qlnL1.cjs';
|
|
9
|
+
export { d as defaultLocalization } from './i18n-Di5qlnL1.cjs';
|
|
10
10
|
|
|
11
11
|
interface KerneReactConfig extends Omit<KerneConfig, 'secretKey'> {
|
|
12
12
|
storage?: Storage;
|
|
13
13
|
storageKey?: string;
|
|
14
14
|
onAuthChange?: (user: User | null) => void;
|
|
15
|
+
/**
|
|
16
|
+
* A cross-origin handoff arrived but could not be exchanged for a session -
|
|
17
|
+
* an expired one-time code, or an origin this tenant does not allow.
|
|
18
|
+
*
|
|
19
|
+
* Worth handling: the SDK cannot render UI, so without this the user
|
|
20
|
+
* completes a magic link, lands on your app, and is simply not signed in
|
|
21
|
+
* with nothing on screen explaining why. Typically a toast plus a link back
|
|
22
|
+
* to sign in.
|
|
23
|
+
*/
|
|
24
|
+
onHandoffError?: (error: unknown) => void;
|
|
15
25
|
/** Auto-refresh session before expiry (default: true) */
|
|
16
26
|
autoRefresh?: boolean;
|
|
17
27
|
/** Refresh buffer in seconds before expiry (default: 60) */
|
|
@@ -24,6 +34,7 @@ declare class KerneClient {
|
|
|
24
34
|
private storage;
|
|
25
35
|
private storageKey;
|
|
26
36
|
private onAuthChange?;
|
|
37
|
+
private onHandoffError?;
|
|
27
38
|
private autoRefresh;
|
|
28
39
|
private refreshBuffer;
|
|
29
40
|
private refreshTimer;
|
|
@@ -149,7 +160,7 @@ declare class KerneClient {
|
|
|
149
160
|
* caught everything and returned `false`) - a 401/500 must not be
|
|
150
161
|
* indistinguishable from a real denial, that's exactly what let the
|
|
151
162
|
* `has_access`/`allowed` mismatch below ship unnoticed. Callers that want
|
|
152
|
-
* a fail-closed boolean regardless of the reason (e.g. `<
|
|
163
|
+
* a fail-closed boolean regardless of the reason (e.g. `<Access>`)
|
|
153
164
|
* catch around this themselves.
|
|
154
165
|
*/
|
|
155
166
|
allows(featureKey: string, requested?: number): Promise<boolean>;
|
|
@@ -166,6 +177,12 @@ declare class KerneClient {
|
|
|
166
177
|
createPortal(returnUrl?: string): Promise<string>;
|
|
167
178
|
openPortal(returnUrl?: string): Promise<void>;
|
|
168
179
|
getSubscription(productSlug?: string): Promise<SubscriptionWithPlan | null>;
|
|
180
|
+
/** `immediately: true` ends it now; otherwise cancels at period end (API default). */
|
|
181
|
+
cancelSubscription(subscriptionId: string, immediately?: boolean): Promise<Subscription>;
|
|
182
|
+
/** Swaps the plan/price, effective immediately - deferring a downgrade isn't supported yet. */
|
|
183
|
+
changeSubscriptionPlan(subscriptionId: string, planPriceId: string, options?: {
|
|
184
|
+
prorationBehavior?: 'none' | 'create_prorations';
|
|
185
|
+
}): Promise<SubscriptionWithPlan>;
|
|
169
186
|
/** Public pricing data - omit `productIdOrSlug` for the tenant's default product. */
|
|
170
187
|
getPlans(productIdOrSlug?: string): Promise<PublicPlan[]>;
|
|
171
188
|
/** Every entitlement for the current user in one call - for a "your plan" screen. */
|
|
@@ -306,6 +323,27 @@ declare function usePortal(): {
|
|
|
306
323
|
createPortal: (returnUrl?: string) => Promise<string>;
|
|
307
324
|
openPortal: (returnUrl?: string) => Promise<void>;
|
|
308
325
|
};
|
|
326
|
+
/**
|
|
327
|
+
* Cancels or changes the current scope's own subscription - JWT-scoped like every other hook
|
|
328
|
+
* here, so this can only ever act on the caller's own subscription (see `SubscriptionCard`).
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* ```tsx
|
|
332
|
+
* const { cancelSubscription, isCanceling } = useCancelSubscription();
|
|
333
|
+
* <button onClick={() => cancelSubscription(subscriptionId)}>Cancel</button>
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
declare function useCancelSubscription(): {
|
|
337
|
+
cancelSubscription: (subscriptionId: string, immediately?: boolean) => Promise<_kerne_types.Subscription>;
|
|
338
|
+
isCanceling: boolean;
|
|
339
|
+
};
|
|
340
|
+
/** Changes the current scope's own subscription to a different plan/price, effective immediately. */
|
|
341
|
+
declare function useChangePlan(): {
|
|
342
|
+
changePlan: (subscriptionId: string, planPriceId: string, options?: {
|
|
343
|
+
prorationBehavior?: "none" | "create_prorations";
|
|
344
|
+
}) => Promise<SubscriptionWithPlan>;
|
|
345
|
+
isChanging: boolean;
|
|
346
|
+
};
|
|
309
347
|
/**
|
|
310
348
|
* Whether the current scope has access to one feature - the single entry
|
|
311
349
|
* point for a live, per-feature decision. Deliberately not named after what
|
|
@@ -348,6 +386,7 @@ declare function useAccess(featureKey: string, requested?: number): {
|
|
|
348
386
|
declare function useSubscription(productSlug?: string): {
|
|
349
387
|
isActive: boolean;
|
|
350
388
|
planSlug: string | undefined;
|
|
389
|
+
refetch: () => void;
|
|
351
390
|
subscription: SubscriptionWithPlan | null;
|
|
352
391
|
isLoading: boolean;
|
|
353
392
|
error: Error | null;
|
|
@@ -541,24 +580,35 @@ interface EntitlementGuardProps {
|
|
|
541
580
|
* `UpgradePrompt`, so the whole entitlement surface reads alike.
|
|
542
581
|
*/
|
|
543
582
|
featureKey: string;
|
|
544
|
-
/**
|
|
545
|
-
|
|
583
|
+
/**
|
|
584
|
+
* Units the gated action is about to consume - checked before it runs, not
|
|
585
|
+
* after. A QUOTA feature with 3 remaining still passes a plain `<Access>`
|
|
586
|
+
* (>0 left), but fails `requested={5}` up front, so a batch action can be
|
|
587
|
+
* blocked before the user commits to it instead of after a partial
|
|
588
|
+
* `consume()` fails mid-way. Omit for a plain yes/no gate.
|
|
589
|
+
*/
|
|
590
|
+
requested?: number;
|
|
546
591
|
/** Fallback content when entitlement is not met */
|
|
547
592
|
fallback?: ReactNode;
|
|
548
593
|
}
|
|
549
594
|
/**
|
|
550
|
-
* Renders children only when the
|
|
551
|
-
*
|
|
552
|
-
*
|
|
595
|
+
* Renders children only when the subject is entitled to a capability - and,
|
|
596
|
+
* with `requested`, entitled to that many units of it right now. Checks the
|
|
597
|
+
* entitlement itself, never a plan name - a plan can be renamed or
|
|
598
|
+
* restructured without breaking every place that gated on its slug.
|
|
553
599
|
*
|
|
554
600
|
* @example
|
|
555
601
|
* ```tsx
|
|
556
|
-
* <
|
|
602
|
+
* <Access featureKey="advanced_analytics" fallback={<UpgradePrompt featureKey="advanced_analytics" />}>
|
|
557
603
|
* <AnalyticsDashboard />
|
|
558
|
-
* </
|
|
604
|
+
* </Access>
|
|
605
|
+
*
|
|
606
|
+
* <Access featureKey="ai_credits" requested={5} fallback={<UpgradePrompt reason="Not enough credits for batch processing" />}>
|
|
607
|
+
* <BatchProcessButton count={5} />
|
|
608
|
+
* </Access>
|
|
559
609
|
* ```
|
|
560
610
|
*/
|
|
561
|
-
declare function
|
|
611
|
+
declare function Access({ children, featureKey, requested, fallback, }: EntitlementGuardProps): ReactNode;
|
|
562
612
|
interface ProtectedProps {
|
|
563
613
|
children: ReactNode;
|
|
564
614
|
/** Require authentication */
|
|
@@ -641,4 +691,4 @@ declare function useKerneError(): {
|
|
|
641
691
|
clearError: () => void;
|
|
642
692
|
};
|
|
643
693
|
|
|
644
|
-
export {
|
|
694
|
+
export { Access, AuthLoading, Authenticated, HasSubscription, KerneClient, KerneContext, type KerneError, KerneErrorBoundary, KerneLocalization, KerneProvider, type KerneProviderProps, type KerneReactConfig, Protected, Unauthenticated, useAccess, useAuth, useAuthConfig, useCancelSubscription, useChangePlan, useCheckout, useClient, useEntitlements, useInvitation, useKerneError, usePlans, usePortal, useSubscription, useUsage, useUser, useWaitlist };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,15 +3,25 @@ import React, { ReactNode, Component, ErrorInfo } from 'react';
|
|
|
3
3
|
import { KerneConfig, JoinWaitlistParams, ValidateTokenResponse, ValidateCodeResponse } from '@kerne/server';
|
|
4
4
|
export { JoinWaitlistParams, ValidateCodeResponse, ValidateTokenResponse, WaitlistEntry } from '@kerne/server';
|
|
5
5
|
import * as _kerne_types from '@kerne/types';
|
|
6
|
-
import { User, AuthResponse, ActivationContext, AuthConfig, EntitlementCheck, SubscriptionWithPlan, PublicPlan, Entitlements, UsageRecord } from '@kerne/types';
|
|
6
|
+
import { User, AuthResponse, ActivationContext, AuthConfig, EntitlementCheck, SubscriptionWithPlan, Subscription, PublicPlan, Entitlements, UsageRecord } from '@kerne/types';
|
|
7
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-
|
|
9
|
-
export { d as defaultLocalization } from './i18n-
|
|
8
|
+
import { D as DeepPartial, K as KerneLocalization } from './i18n-Di5qlnL1.js';
|
|
9
|
+
export { d as defaultLocalization } from './i18n-Di5qlnL1.js';
|
|
10
10
|
|
|
11
11
|
interface KerneReactConfig extends Omit<KerneConfig, 'secretKey'> {
|
|
12
12
|
storage?: Storage;
|
|
13
13
|
storageKey?: string;
|
|
14
14
|
onAuthChange?: (user: User | null) => void;
|
|
15
|
+
/**
|
|
16
|
+
* A cross-origin handoff arrived but could not be exchanged for a session -
|
|
17
|
+
* an expired one-time code, or an origin this tenant does not allow.
|
|
18
|
+
*
|
|
19
|
+
* Worth handling: the SDK cannot render UI, so without this the user
|
|
20
|
+
* completes a magic link, lands on your app, and is simply not signed in
|
|
21
|
+
* with nothing on screen explaining why. Typically a toast plus a link back
|
|
22
|
+
* to sign in.
|
|
23
|
+
*/
|
|
24
|
+
onHandoffError?: (error: unknown) => void;
|
|
15
25
|
/** Auto-refresh session before expiry (default: true) */
|
|
16
26
|
autoRefresh?: boolean;
|
|
17
27
|
/** Refresh buffer in seconds before expiry (default: 60) */
|
|
@@ -24,6 +34,7 @@ declare class KerneClient {
|
|
|
24
34
|
private storage;
|
|
25
35
|
private storageKey;
|
|
26
36
|
private onAuthChange?;
|
|
37
|
+
private onHandoffError?;
|
|
27
38
|
private autoRefresh;
|
|
28
39
|
private refreshBuffer;
|
|
29
40
|
private refreshTimer;
|
|
@@ -149,7 +160,7 @@ declare class KerneClient {
|
|
|
149
160
|
* caught everything and returned `false`) - a 401/500 must not be
|
|
150
161
|
* indistinguishable from a real denial, that's exactly what let the
|
|
151
162
|
* `has_access`/`allowed` mismatch below ship unnoticed. Callers that want
|
|
152
|
-
* a fail-closed boolean regardless of the reason (e.g. `<
|
|
163
|
+
* a fail-closed boolean regardless of the reason (e.g. `<Access>`)
|
|
153
164
|
* catch around this themselves.
|
|
154
165
|
*/
|
|
155
166
|
allows(featureKey: string, requested?: number): Promise<boolean>;
|
|
@@ -166,6 +177,12 @@ declare class KerneClient {
|
|
|
166
177
|
createPortal(returnUrl?: string): Promise<string>;
|
|
167
178
|
openPortal(returnUrl?: string): Promise<void>;
|
|
168
179
|
getSubscription(productSlug?: string): Promise<SubscriptionWithPlan | null>;
|
|
180
|
+
/** `immediately: true` ends it now; otherwise cancels at period end (API default). */
|
|
181
|
+
cancelSubscription(subscriptionId: string, immediately?: boolean): Promise<Subscription>;
|
|
182
|
+
/** Swaps the plan/price, effective immediately - deferring a downgrade isn't supported yet. */
|
|
183
|
+
changeSubscriptionPlan(subscriptionId: string, planPriceId: string, options?: {
|
|
184
|
+
prorationBehavior?: 'none' | 'create_prorations';
|
|
185
|
+
}): Promise<SubscriptionWithPlan>;
|
|
169
186
|
/** Public pricing data - omit `productIdOrSlug` for the tenant's default product. */
|
|
170
187
|
getPlans(productIdOrSlug?: string): Promise<PublicPlan[]>;
|
|
171
188
|
/** Every entitlement for the current user in one call - for a "your plan" screen. */
|
|
@@ -306,6 +323,27 @@ declare function usePortal(): {
|
|
|
306
323
|
createPortal: (returnUrl?: string) => Promise<string>;
|
|
307
324
|
openPortal: (returnUrl?: string) => Promise<void>;
|
|
308
325
|
};
|
|
326
|
+
/**
|
|
327
|
+
* Cancels or changes the current scope's own subscription - JWT-scoped like every other hook
|
|
328
|
+
* here, so this can only ever act on the caller's own subscription (see `SubscriptionCard`).
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* ```tsx
|
|
332
|
+
* const { cancelSubscription, isCanceling } = useCancelSubscription();
|
|
333
|
+
* <button onClick={() => cancelSubscription(subscriptionId)}>Cancel</button>
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
declare function useCancelSubscription(): {
|
|
337
|
+
cancelSubscription: (subscriptionId: string, immediately?: boolean) => Promise<_kerne_types.Subscription>;
|
|
338
|
+
isCanceling: boolean;
|
|
339
|
+
};
|
|
340
|
+
/** Changes the current scope's own subscription to a different plan/price, effective immediately. */
|
|
341
|
+
declare function useChangePlan(): {
|
|
342
|
+
changePlan: (subscriptionId: string, planPriceId: string, options?: {
|
|
343
|
+
prorationBehavior?: "none" | "create_prorations";
|
|
344
|
+
}) => Promise<SubscriptionWithPlan>;
|
|
345
|
+
isChanging: boolean;
|
|
346
|
+
};
|
|
309
347
|
/**
|
|
310
348
|
* Whether the current scope has access to one feature - the single entry
|
|
311
349
|
* point for a live, per-feature decision. Deliberately not named after what
|
|
@@ -348,6 +386,7 @@ declare function useAccess(featureKey: string, requested?: number): {
|
|
|
348
386
|
declare function useSubscription(productSlug?: string): {
|
|
349
387
|
isActive: boolean;
|
|
350
388
|
planSlug: string | undefined;
|
|
389
|
+
refetch: () => void;
|
|
351
390
|
subscription: SubscriptionWithPlan | null;
|
|
352
391
|
isLoading: boolean;
|
|
353
392
|
error: Error | null;
|
|
@@ -541,24 +580,35 @@ interface EntitlementGuardProps {
|
|
|
541
580
|
* `UpgradePrompt`, so the whole entitlement surface reads alike.
|
|
542
581
|
*/
|
|
543
582
|
featureKey: string;
|
|
544
|
-
/**
|
|
545
|
-
|
|
583
|
+
/**
|
|
584
|
+
* Units the gated action is about to consume - checked before it runs, not
|
|
585
|
+
* after. A QUOTA feature with 3 remaining still passes a plain `<Access>`
|
|
586
|
+
* (>0 left), but fails `requested={5}` up front, so a batch action can be
|
|
587
|
+
* blocked before the user commits to it instead of after a partial
|
|
588
|
+
* `consume()` fails mid-way. Omit for a plain yes/no gate.
|
|
589
|
+
*/
|
|
590
|
+
requested?: number;
|
|
546
591
|
/** Fallback content when entitlement is not met */
|
|
547
592
|
fallback?: ReactNode;
|
|
548
593
|
}
|
|
549
594
|
/**
|
|
550
|
-
* Renders children only when the
|
|
551
|
-
*
|
|
552
|
-
*
|
|
595
|
+
* Renders children only when the subject is entitled to a capability - and,
|
|
596
|
+
* with `requested`, entitled to that many units of it right now. Checks the
|
|
597
|
+
* entitlement itself, never a plan name - a plan can be renamed or
|
|
598
|
+
* restructured without breaking every place that gated on its slug.
|
|
553
599
|
*
|
|
554
600
|
* @example
|
|
555
601
|
* ```tsx
|
|
556
|
-
* <
|
|
602
|
+
* <Access featureKey="advanced_analytics" fallback={<UpgradePrompt featureKey="advanced_analytics" />}>
|
|
557
603
|
* <AnalyticsDashboard />
|
|
558
|
-
* </
|
|
604
|
+
* </Access>
|
|
605
|
+
*
|
|
606
|
+
* <Access featureKey="ai_credits" requested={5} fallback={<UpgradePrompt reason="Not enough credits for batch processing" />}>
|
|
607
|
+
* <BatchProcessButton count={5} />
|
|
608
|
+
* </Access>
|
|
559
609
|
* ```
|
|
560
610
|
*/
|
|
561
|
-
declare function
|
|
611
|
+
declare function Access({ children, featureKey, requested, fallback, }: EntitlementGuardProps): ReactNode;
|
|
562
612
|
interface ProtectedProps {
|
|
563
613
|
children: ReactNode;
|
|
564
614
|
/** Require authentication */
|
|
@@ -641,4 +691,4 @@ declare function useKerneError(): {
|
|
|
641
691
|
clearError: () => void;
|
|
642
692
|
};
|
|
643
693
|
|
|
644
|
-
export {
|
|
694
|
+
export { Access, AuthLoading, Authenticated, HasSubscription, KerneClient, KerneContext, type KerneError, KerneErrorBoundary, KerneLocalization, KerneProvider, type KerneProviderProps, type KerneReactConfig, Protected, Unauthenticated, useAccess, useAuth, useAuthConfig, useCancelSubscription, useChangePlan, useCheckout, useClient, useEntitlements, useInvitation, useKerneError, usePlans, usePortal, useSubscription, useUsage, useUser, useWaitlist };
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
useAccess,
|
|
8
8
|
useAuth,
|
|
9
9
|
useAuthConfig,
|
|
10
|
+
useCancelSubscription,
|
|
11
|
+
useChangePlan,
|
|
10
12
|
useCheckout,
|
|
11
13
|
useClient,
|
|
12
14
|
useEntitlements,
|
|
@@ -17,7 +19,7 @@ import {
|
|
|
17
19
|
useUsage,
|
|
18
20
|
useUser,
|
|
19
21
|
useWaitlist
|
|
20
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-NFJ2S5VJ.js";
|
|
21
23
|
|
|
22
24
|
// src/components.tsx
|
|
23
25
|
import React from "react";
|
|
@@ -47,22 +49,14 @@ function HasSubscription({ children, fallback = null }) {
|
|
|
47
49
|
if (hasSubscription === null) return null;
|
|
48
50
|
return hasSubscription ? children : fallback;
|
|
49
51
|
}
|
|
50
|
-
function
|
|
52
|
+
function Access({
|
|
51
53
|
children,
|
|
52
54
|
featureKey,
|
|
53
|
-
|
|
55
|
+
requested,
|
|
54
56
|
fallback = null
|
|
55
57
|
}) {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
React.useEffect(() => {
|
|
59
|
-
client.allows(featureKey, minimum).then((result) => {
|
|
60
|
-
setAllowed(result);
|
|
61
|
-
}).catch(() => {
|
|
62
|
-
setAllowed(false);
|
|
63
|
-
});
|
|
64
|
-
}, [client, featureKey, minimum]);
|
|
65
|
-
if (allowed === null) return null;
|
|
58
|
+
const { allowed, isLoading } = useAccess(featureKey, requested);
|
|
59
|
+
if (isLoading) return null;
|
|
66
60
|
return allowed ? children : fallback;
|
|
67
61
|
}
|
|
68
62
|
function Protected({
|
|
@@ -77,7 +71,7 @@ function Protected({
|
|
|
77
71
|
return authFallback;
|
|
78
72
|
}
|
|
79
73
|
if (featureKey) {
|
|
80
|
-
return /* @__PURE__ */ jsx(
|
|
74
|
+
return /* @__PURE__ */ jsx(Access, { featureKey, fallback: billingFallback, children });
|
|
81
75
|
}
|
|
82
76
|
return children;
|
|
83
77
|
}
|
|
@@ -158,7 +152,7 @@ function useKerneError() {
|
|
|
158
152
|
};
|
|
159
153
|
}
|
|
160
154
|
export {
|
|
161
|
-
|
|
155
|
+
Access,
|
|
162
156
|
AuthLoading,
|
|
163
157
|
Authenticated,
|
|
164
158
|
HasSubscription,
|
|
@@ -172,6 +166,8 @@ export {
|
|
|
172
166
|
useAccess,
|
|
173
167
|
useAuth,
|
|
174
168
|
useAuthConfig,
|
|
169
|
+
useCancelSubscription,
|
|
170
|
+
useChangePlan,
|
|
175
171
|
useCheckout,
|
|
176
172
|
useClient,
|
|
177
173
|
useEntitlements,
|