@kerne/react 0.1.3 → 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/dist/index.d.cts CHANGED
@@ -3,10 +3,10 @@ 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-BWTT1DWD.cjs';
9
- export { d as defaultLocalization } from './i18n-BWTT1DWD.cjs';
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;
@@ -160,7 +160,7 @@ declare class KerneClient {
160
160
  * caught everything and returned `false`) - a 401/500 must not be
161
161
  * indistinguishable from a real denial, that's exactly what let the
162
162
  * `has_access`/`allowed` mismatch below ship unnoticed. Callers that want
163
- * a fail-closed boolean regardless of the reason (e.g. `<Allows>`)
163
+ * a fail-closed boolean regardless of the reason (e.g. `<Access>`)
164
164
  * catch around this themselves.
165
165
  */
166
166
  allows(featureKey: string, requested?: number): Promise<boolean>;
@@ -177,6 +177,12 @@ declare class KerneClient {
177
177
  createPortal(returnUrl?: string): Promise<string>;
178
178
  openPortal(returnUrl?: string): Promise<void>;
179
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>;
180
186
  /** Public pricing data - omit `productIdOrSlug` for the tenant's default product. */
181
187
  getPlans(productIdOrSlug?: string): Promise<PublicPlan[]>;
182
188
  /** Every entitlement for the current user in one call - for a "your plan" screen. */
@@ -317,6 +323,27 @@ declare function usePortal(): {
317
323
  createPortal: (returnUrl?: string) => Promise<string>;
318
324
  openPortal: (returnUrl?: string) => Promise<void>;
319
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
+ };
320
347
  /**
321
348
  * Whether the current scope has access to one feature - the single entry
322
349
  * point for a live, per-feature decision. Deliberately not named after what
@@ -359,6 +386,7 @@ declare function useAccess(featureKey: string, requested?: number): {
359
386
  declare function useSubscription(productSlug?: string): {
360
387
  isActive: boolean;
361
388
  planSlug: string | undefined;
389
+ refetch: () => void;
362
390
  subscription: SubscriptionWithPlan | null;
363
391
  isLoading: boolean;
364
392
  error: Error | null;
@@ -552,24 +580,35 @@ interface EntitlementGuardProps {
552
580
  * `UpgradePrompt`, so the whole entitlement surface reads alike.
553
581
  */
554
582
  featureKey: string;
555
- /** Minimum value required (for limit features) */
556
- minimum?: number;
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;
557
591
  /** Fallback content when entitlement is not met */
558
592
  fallback?: ReactNode;
559
593
  }
560
594
  /**
561
- * Renders children only when the scope is entitled to a capability. Checks the
562
- * entitlement itself, never a plan name - a plan can be renamed or restructured
563
- * without breaking every place that gated on its slug.
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.
564
599
  *
565
600
  * @example
566
601
  * ```tsx
567
- * <Allows featureKey="advanced_analytics" fallback={<UpgradePrompt featureKey="advanced_analytics" />}>
602
+ * <Access featureKey="advanced_analytics" fallback={<UpgradePrompt featureKey="advanced_analytics" />}>
568
603
  * <AnalyticsDashboard />
569
- * </Allows>
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>
570
609
  * ```
571
610
  */
572
- declare function Allows({ children, featureKey, minimum, fallback, }: EntitlementGuardProps): ReactNode;
611
+ declare function Access({ children, featureKey, requested, fallback, }: EntitlementGuardProps): ReactNode;
573
612
  interface ProtectedProps {
574
613
  children: ReactNode;
575
614
  /** Require authentication */
@@ -652,4 +691,4 @@ declare function useKerneError(): {
652
691
  clearError: () => void;
653
692
  };
654
693
 
655
- 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 };
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,10 +3,10 @@ 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-BWTT1DWD.js';
9
- export { d as defaultLocalization } from './i18n-BWTT1DWD.js';
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;
@@ -160,7 +160,7 @@ declare class KerneClient {
160
160
  * caught everything and returned `false`) - a 401/500 must not be
161
161
  * indistinguishable from a real denial, that's exactly what let the
162
162
  * `has_access`/`allowed` mismatch below ship unnoticed. Callers that want
163
- * a fail-closed boolean regardless of the reason (e.g. `<Allows>`)
163
+ * a fail-closed boolean regardless of the reason (e.g. `<Access>`)
164
164
  * catch around this themselves.
165
165
  */
166
166
  allows(featureKey: string, requested?: number): Promise<boolean>;
@@ -177,6 +177,12 @@ declare class KerneClient {
177
177
  createPortal(returnUrl?: string): Promise<string>;
178
178
  openPortal(returnUrl?: string): Promise<void>;
179
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>;
180
186
  /** Public pricing data - omit `productIdOrSlug` for the tenant's default product. */
181
187
  getPlans(productIdOrSlug?: string): Promise<PublicPlan[]>;
182
188
  /** Every entitlement for the current user in one call - for a "your plan" screen. */
@@ -317,6 +323,27 @@ declare function usePortal(): {
317
323
  createPortal: (returnUrl?: string) => Promise<string>;
318
324
  openPortal: (returnUrl?: string) => Promise<void>;
319
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
+ };
320
347
  /**
321
348
  * Whether the current scope has access to one feature - the single entry
322
349
  * point for a live, per-feature decision. Deliberately not named after what
@@ -359,6 +386,7 @@ declare function useAccess(featureKey: string, requested?: number): {
359
386
  declare function useSubscription(productSlug?: string): {
360
387
  isActive: boolean;
361
388
  planSlug: string | undefined;
389
+ refetch: () => void;
362
390
  subscription: SubscriptionWithPlan | null;
363
391
  isLoading: boolean;
364
392
  error: Error | null;
@@ -552,24 +580,35 @@ interface EntitlementGuardProps {
552
580
  * `UpgradePrompt`, so the whole entitlement surface reads alike.
553
581
  */
554
582
  featureKey: string;
555
- /** Minimum value required (for limit features) */
556
- minimum?: number;
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;
557
591
  /** Fallback content when entitlement is not met */
558
592
  fallback?: ReactNode;
559
593
  }
560
594
  /**
561
- * Renders children only when the scope is entitled to a capability. Checks the
562
- * entitlement itself, never a plan name - a plan can be renamed or restructured
563
- * without breaking every place that gated on its slug.
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.
564
599
  *
565
600
  * @example
566
601
  * ```tsx
567
- * <Allows featureKey="advanced_analytics" fallback={<UpgradePrompt featureKey="advanced_analytics" />}>
602
+ * <Access featureKey="advanced_analytics" fallback={<UpgradePrompt featureKey="advanced_analytics" />}>
568
603
  * <AnalyticsDashboard />
569
- * </Allows>
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>
570
609
  * ```
571
610
  */
572
- declare function Allows({ children, featureKey, minimum, fallback, }: EntitlementGuardProps): ReactNode;
611
+ declare function Access({ children, featureKey, requested, fallback, }: EntitlementGuardProps): ReactNode;
573
612
  interface ProtectedProps {
574
613
  children: ReactNode;
575
614
  /** Require authentication */
@@ -652,4 +691,4 @@ declare function useKerneError(): {
652
691
  clearError: () => void;
653
692
  };
654
693
 
655
- 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 };
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-F6NV76OR.js";
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 Allows({
52
+ function Access({
51
53
  children,
52
54
  featureKey,
53
- minimum,
55
+ requested,
54
56
  fallback = null
55
57
  }) {
56
- const client = useClient();
57
- const [allowed, setAllowed] = React.useState(null);
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(Allows, { featureKey, fallback: billingFallback, children });
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
- Allows,
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,