@kerne/react 1.1.0 → 1.3.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
@@ -1,12 +1,12 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React, { ReactNode, Component, ErrorInfo } from 'react';
3
- import { KerneConfig, JoinWaitlistParams, ValidateTokenResponse, ValidateCodeResponse } from '@kerne/server';
3
+ import { KerneConfig, ConsumeParams, ReportUsageParams, ReportUsageBatchEvent, BatchUsageResult, 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, Subscription, PublicPlan, Entitlements, UsageRecord, PublicPlanPrice } 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-QilNgwqe.cjs';
9
- export { d as defaultLocalization } from './i18n-QilNgwqe.cjs';
6
+ import { User, AuthResponse, ActivationContext, AuthConfig, EntitlementCheck, UsageRecord, DriverSelection, SubscriptionWithPlan, Subscription, PublicPlan, Entitlements, PublicPlanPrice } from '@kerne/types';
7
+ export { ActivationContext, AuthConfig, AuthResponse, BatchUsageResult, EntitlementCheck, Entitlements, PublicPlan, Subscription, SubscriptionWithPlan, UsageRecord, User, ValidationFieldError } from '@kerne/types';
8
+ import { D as DeepPartial, K as KerneLocalization, a as KerneAppearance } from './fr-E5mmylSH.cjs';
9
+ export { d as defaultLocalization, f as fr, m as mergeLocalization } from './fr-E5mmylSH.cjs';
10
10
 
11
11
  interface KerneReactConfig extends Omit<KerneConfig, 'secretKey'> {
12
12
  storage?: Storage;
@@ -119,14 +119,14 @@ declare class KerneClient {
119
119
  /** Soft-deletes (deactivates) the caller's own account, then clears the local session. */
120
120
  requestAccountDeletion(): Promise<void>;
121
121
  /** Forgot-password flow (logged out) - sends the reset email. */
122
- requestPasswordReset(email: string, callbackUrl?: string): Promise<void>;
122
+ requestPasswordReset(email: string): Promise<void>;
123
123
  /** Confirms a password reset with the token from the email. */
124
124
  confirmPasswordReset(token: string, password: string): Promise<void>;
125
125
  /**
126
126
  * Sends a magic-link email (logged out). Anti-enumeration: always resolves,
127
127
  * regardless of whether the email is registered.
128
128
  */
129
- startPasswordless(email: string, callbackUrl?: string, invitation?: {
129
+ startPasswordless(email: string, invitation?: {
130
130
  invitationToken?: string;
131
131
  invitationCode?: string;
132
132
  }): Promise<void>;
@@ -166,16 +166,17 @@ declare class KerneClient {
166
166
  allows(featureKey: string, requested?: number): Promise<boolean>;
167
167
  /** Full entitlement detail (limit/used/remaining/overage) - use `allows()` for a plain boolean. */
168
168
  check(featureKey: string, requested?: number): Promise<EntitlementCheck>;
169
- createCheckout(planPriceId: string, options?: {
170
- successUrl?: string;
171
- cancelUrl?: string;
172
- }): Promise<string>;
173
- openCheckout(planPriceId: string, options?: {
174
- successUrl?: string;
175
- cancelUrl?: string;
176
- }): Promise<void>;
177
- createPortal(returnUrl?: string): Promise<string>;
178
- openPortal(returnUrl?: string): Promise<void>;
169
+ /** Atomically check a QUOTA limit and commit the consumption. Reports for the signed-in subject. */
170
+ consume(featureKey: string, params?: Omit<ConsumeParams, 'subject'>): Promise<EntitlementCheck>;
171
+ /** Report usage without a limit check, for the signed-in subject. See `reportUsageBatch()` for many at once. */
172
+ reportUsage(featureKey: string, params?: Omit<ReportUsageParams, 'subject'>): Promise<UsageRecord>;
173
+ /** Several usage events in one request instead of one call each - kills the manual request staggering. */
174
+ reportUsageBatch(events: Array<Omit<ReportUsageBatchEvent, 'subject'>>): Promise<BatchUsageResult[]>;
175
+ private requireUserId;
176
+ createCheckout(planPriceId: string, drivers?: DriverSelection[]): Promise<string>;
177
+ openCheckout(planPriceId: string, drivers?: DriverSelection[]): Promise<void>;
178
+ createPortal(): Promise<string>;
179
+ openPortal(): Promise<void>;
179
180
  getSubscription(productSlug?: string): Promise<SubscriptionWithPlan | null>;
180
181
  /** `immediately: true` ends it now; otherwise cancels at period end (API default). */
181
182
  cancelSubscription(subscriptionId: string, immediately?: boolean): Promise<Subscription>;
@@ -196,7 +197,7 @@ declare class KerneClient {
196
197
  validateInvitationToken(token: string): Promise<ValidateTokenResponse>;
197
198
  /** Validate an invitation code before showing the registration form. */
198
199
  validateInvitationCode(code: string): Promise<ValidateCodeResponse>;
199
- sendVerificationEmail(verificationType?: 'code' | 'link', callbackUrl?: string): Promise<void>;
200
+ sendVerificationEmail(verificationType?: 'code' | 'link'): Promise<void>;
200
201
  verifyEmailWithCode(code: string): Promise<{
201
202
  success: boolean;
202
203
  needsTokenRefresh: boolean;
@@ -222,8 +223,14 @@ interface KerneProviderProps extends KerneReactConfig {
222
223
  * override keeps everything it does not mention.
223
224
  */
224
225
  localization?: DeepPartial<KerneLocalization>;
226
+ /**
227
+ * Appearance for every prebuilt component, set once. A host with its own dark
228
+ * mode would otherwise pass it to each one and keep them in sync by hand; a
229
+ * component's own `appearance` still wins, field by field.
230
+ */
231
+ appearance?: KerneAppearance;
225
232
  }
226
- declare function KerneProvider({ children, localization, ...config }: KerneProviderProps): react_jsx_runtime.JSX.Element;
233
+ declare function KerneProvider({ children, localization, appearance, ...config }: KerneProviderProps): react_jsx_runtime.JSX.Element;
227
234
 
228
235
  /**
229
236
  * Access the Kerne client instance
@@ -266,14 +273,14 @@ declare function useAuth(): {
266
273
  newPassword: string;
267
274
  }) => Promise<void>;
268
275
  requestAccountDeletion: () => Promise<void>;
269
- requestPasswordReset: (email: string, callbackUrl?: string) => Promise<void>;
276
+ requestPasswordReset: (email: string) => Promise<void>;
270
277
  confirmPasswordReset: (token: string, password: string) => Promise<void>;
271
- startPasswordless: (email: string, callbackUrl?: string, invitation?: {
278
+ startPasswordless: (email: string, invitation?: {
272
279
  invitationToken?: string;
273
280
  invitationCode?: string;
274
281
  }) => Promise<void>;
275
282
  loginWithMagicLink: (token: string) => Promise<_kerne_types.AuthResponse>;
276
- sendVerificationEmail: (verificationType?: "code" | "link", callbackUrl?: string) => Promise<void>;
283
+ sendVerificationEmail: (verificationType?: "code" | "link") => Promise<void>;
277
284
  verifyEmailWithCode: (code: string) => Promise<{
278
285
  success: boolean;
279
286
  needsTokenRefresh: boolean;
@@ -301,14 +308,8 @@ declare function useAuth(): {
301
308
  * ```
302
309
  */
303
310
  declare function useCheckout(): {
304
- createCheckout: (planPriceId: string, options?: {
305
- successUrl?: string;
306
- cancelUrl?: string;
307
- }) => Promise<string>;
308
- openCheckout: (planPriceId: string, options?: {
309
- successUrl?: string;
310
- cancelUrl?: string;
311
- }) => Promise<void>;
311
+ createCheckout: (planPriceId: string, drivers?: DriverSelection[]) => Promise<string>;
312
+ openCheckout: (planPriceId: string, drivers?: DriverSelection[]) => Promise<void>;
312
313
  };
313
314
  /**
314
315
  * Billing portal hook - lets a user manage their subscription/payment method.
@@ -320,8 +321,8 @@ declare function useCheckout(): {
320
321
  * ```
321
322
  */
322
323
  declare function usePortal(): {
323
- createPortal: (returnUrl?: string) => Promise<string>;
324
- openPortal: (returnUrl?: string) => Promise<void>;
324
+ createPortal: () => Promise<string>;
325
+ openPortal: () => Promise<void>;
325
326
  };
326
327
  /**
327
328
  * Cancels or changes the current scope's own subscription - JWT-scoped like every other hook
@@ -376,16 +377,13 @@ interface PlanSwitchTarget {
376
377
  * usePlanSwitch(subscription, refetch);
377
378
  * ```
378
379
  */
379
- declare function usePlanSwitch(subscription: SubscriptionWithPlan | null, refetchSubscription: () => void, options?: {
380
- successUrl?: string;
381
- cancelUrl?: string;
382
- }): {
380
+ declare function usePlanSwitch(subscription: SubscriptionWithPlan | null, refetchSubscription: () => void): {
383
381
  pending: PlanSwitchTarget | null;
384
382
  switching: boolean;
385
383
  switchError: unknown;
386
384
  checkoutPriceId: string | null;
387
385
  checkoutError: unknown;
388
- selectPlan: (plan: PublicPlan, price: PublicPlanPrice) => Promise<void>;
386
+ selectPlan: (plan: PublicPlan, price: PublicPlanPrice, drivers?: DriverSelection[]) => Promise<void>;
389
387
  confirmSwitch: () => Promise<void>;
390
388
  dismissSwitch: () => void;
391
389
  };
@@ -468,9 +466,13 @@ declare function useAuthConfig(): {
468
466
  };
469
467
  /**
470
468
  * Every entitlement for the current user in one call - for a "your plan"
471
- * screen. Use `useAccess` instead for a single feature - this response
472
- * never carries `allowed` (see docs: the list is cached, a stale verdict
473
- * would be unsafe).
469
+ * screen. `EntitlementSummary` carries no `allowed` field, on purpose: this
470
+ * list is a cached snapshot for display ("12/50 calls used"), not a live
471
+ * verdict. **Never gate access by reading `limit`/`remaining`/`enabled`
472
+ * yourself** - that silently reimplements override precedence, overage
473
+ * rules, and the race against concurrent usage that `useAccess()` already
474
+ * handles server-side. Use `useAccess()` (or `kerne.allows()`/`enforce()`)
475
+ * for every actual gate, and this hook only to render numbers.
474
476
  *
475
477
  * @example
476
478
  * ```tsx
@@ -495,6 +497,24 @@ declare function useUsage(featureKey?: string): {
495
497
  isLoading: boolean;
496
498
  error: Error | null;
497
499
  };
500
+ /**
501
+ * Usage write path for the signed-in user: `consume()` gates a QUOTA and commits
502
+ * in one step, `report()`/`reportBatch()` record usage without a check.
503
+ * `reportBatch()` sends many events in one request - the fix for hand-rolled
504
+ * request staggering in a component reporting several rows at once.
505
+ *
506
+ * @example
507
+ * ```tsx
508
+ * const { consume, reportBatch } = useReportUsage();
509
+ * await consume('exports', { delta: 1 });
510
+ * await reportBatch(machines.map((m) => ({ featureKey: 'machine_minutes', delta: m.minutes })));
511
+ * ```
512
+ */
513
+ declare function useReportUsage(): {
514
+ consume: (featureKey: string, params?: Omit<ConsumeParams, "subject">) => Promise<EntitlementCheck>;
515
+ report: (featureKey: string, params?: Omit<ReportUsageParams, "subject">) => Promise<UsageRecord>;
516
+ reportBatch: (events: Array<Omit<ReportUsageBatchEvent, "subject">>) => Promise<_kerne_types.BatchUsageResult[]>;
517
+ };
498
518
  /**
499
519
  * Waitlist signup form - a mutation, not a data fetch, same shape as
500
520
  * `useCheckout`/`usePortal`.
@@ -736,4 +756,4 @@ declare function useKerneError(): {
736
756
  clearError: () => void;
737
757
  };
738
758
 
739
- export { Access, AuthLoading, Authenticated, HasSubscription, KerneClient, KerneContext, type KerneError, KerneErrorBoundary, KerneLocalization, KerneProvider, type KerneProviderProps, type KerneReactConfig, type PlanSwitchTarget, Protected, Unauthenticated, useAccess, useAuth, useAuthConfig, useCancelSubscription, useChangePlan, useCheckout, useClient, useEntitlements, useInvitation, useKerneError, usePlanSwitch, usePlans, usePortal, useSubscription, useUsage, useUser, useWaitlist };
759
+ export { Access, AuthLoading, Authenticated, HasSubscription, KerneClient, KerneContext, type KerneError, KerneErrorBoundary, KerneLocalization, KerneProvider, type KerneProviderProps, type KerneReactConfig, type PlanSwitchTarget, Protected, Unauthenticated, useAccess, useAuth, useAuthConfig, useCancelSubscription, useChangePlan, useCheckout, useClient, useEntitlements, useInvitation, useKerneError, usePlanSwitch, usePlans, usePortal, useReportUsage, useSubscription, useUsage, useUser, useWaitlist };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React, { ReactNode, Component, ErrorInfo } from 'react';
3
- import { KerneConfig, JoinWaitlistParams, ValidateTokenResponse, ValidateCodeResponse } from '@kerne/server';
3
+ import { KerneConfig, ConsumeParams, ReportUsageParams, ReportUsageBatchEvent, BatchUsageResult, 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, Subscription, PublicPlan, Entitlements, UsageRecord, PublicPlanPrice } 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-QilNgwqe.js';
9
- export { d as defaultLocalization } from './i18n-QilNgwqe.js';
6
+ import { User, AuthResponse, ActivationContext, AuthConfig, EntitlementCheck, UsageRecord, DriverSelection, SubscriptionWithPlan, Subscription, PublicPlan, Entitlements, PublicPlanPrice } from '@kerne/types';
7
+ export { ActivationContext, AuthConfig, AuthResponse, BatchUsageResult, EntitlementCheck, Entitlements, PublicPlan, Subscription, SubscriptionWithPlan, UsageRecord, User, ValidationFieldError } from '@kerne/types';
8
+ import { D as DeepPartial, K as KerneLocalization, a as KerneAppearance } from './fr-E5mmylSH.js';
9
+ export { d as defaultLocalization, f as fr, m as mergeLocalization } from './fr-E5mmylSH.js';
10
10
 
11
11
  interface KerneReactConfig extends Omit<KerneConfig, 'secretKey'> {
12
12
  storage?: Storage;
@@ -119,14 +119,14 @@ declare class KerneClient {
119
119
  /** Soft-deletes (deactivates) the caller's own account, then clears the local session. */
120
120
  requestAccountDeletion(): Promise<void>;
121
121
  /** Forgot-password flow (logged out) - sends the reset email. */
122
- requestPasswordReset(email: string, callbackUrl?: string): Promise<void>;
122
+ requestPasswordReset(email: string): Promise<void>;
123
123
  /** Confirms a password reset with the token from the email. */
124
124
  confirmPasswordReset(token: string, password: string): Promise<void>;
125
125
  /**
126
126
  * Sends a magic-link email (logged out). Anti-enumeration: always resolves,
127
127
  * regardless of whether the email is registered.
128
128
  */
129
- startPasswordless(email: string, callbackUrl?: string, invitation?: {
129
+ startPasswordless(email: string, invitation?: {
130
130
  invitationToken?: string;
131
131
  invitationCode?: string;
132
132
  }): Promise<void>;
@@ -166,16 +166,17 @@ declare class KerneClient {
166
166
  allows(featureKey: string, requested?: number): Promise<boolean>;
167
167
  /** Full entitlement detail (limit/used/remaining/overage) - use `allows()` for a plain boolean. */
168
168
  check(featureKey: string, requested?: number): Promise<EntitlementCheck>;
169
- createCheckout(planPriceId: string, options?: {
170
- successUrl?: string;
171
- cancelUrl?: string;
172
- }): Promise<string>;
173
- openCheckout(planPriceId: string, options?: {
174
- successUrl?: string;
175
- cancelUrl?: string;
176
- }): Promise<void>;
177
- createPortal(returnUrl?: string): Promise<string>;
178
- openPortal(returnUrl?: string): Promise<void>;
169
+ /** Atomically check a QUOTA limit and commit the consumption. Reports for the signed-in subject. */
170
+ consume(featureKey: string, params?: Omit<ConsumeParams, 'subject'>): Promise<EntitlementCheck>;
171
+ /** Report usage without a limit check, for the signed-in subject. See `reportUsageBatch()` for many at once. */
172
+ reportUsage(featureKey: string, params?: Omit<ReportUsageParams, 'subject'>): Promise<UsageRecord>;
173
+ /** Several usage events in one request instead of one call each - kills the manual request staggering. */
174
+ reportUsageBatch(events: Array<Omit<ReportUsageBatchEvent, 'subject'>>): Promise<BatchUsageResult[]>;
175
+ private requireUserId;
176
+ createCheckout(planPriceId: string, drivers?: DriverSelection[]): Promise<string>;
177
+ openCheckout(planPriceId: string, drivers?: DriverSelection[]): Promise<void>;
178
+ createPortal(): Promise<string>;
179
+ openPortal(): Promise<void>;
179
180
  getSubscription(productSlug?: string): Promise<SubscriptionWithPlan | null>;
180
181
  /** `immediately: true` ends it now; otherwise cancels at period end (API default). */
181
182
  cancelSubscription(subscriptionId: string, immediately?: boolean): Promise<Subscription>;
@@ -196,7 +197,7 @@ declare class KerneClient {
196
197
  validateInvitationToken(token: string): Promise<ValidateTokenResponse>;
197
198
  /** Validate an invitation code before showing the registration form. */
198
199
  validateInvitationCode(code: string): Promise<ValidateCodeResponse>;
199
- sendVerificationEmail(verificationType?: 'code' | 'link', callbackUrl?: string): Promise<void>;
200
+ sendVerificationEmail(verificationType?: 'code' | 'link'): Promise<void>;
200
201
  verifyEmailWithCode(code: string): Promise<{
201
202
  success: boolean;
202
203
  needsTokenRefresh: boolean;
@@ -222,8 +223,14 @@ interface KerneProviderProps extends KerneReactConfig {
222
223
  * override keeps everything it does not mention.
223
224
  */
224
225
  localization?: DeepPartial<KerneLocalization>;
226
+ /**
227
+ * Appearance for every prebuilt component, set once. A host with its own dark
228
+ * mode would otherwise pass it to each one and keep them in sync by hand; a
229
+ * component's own `appearance` still wins, field by field.
230
+ */
231
+ appearance?: KerneAppearance;
225
232
  }
226
- declare function KerneProvider({ children, localization, ...config }: KerneProviderProps): react_jsx_runtime.JSX.Element;
233
+ declare function KerneProvider({ children, localization, appearance, ...config }: KerneProviderProps): react_jsx_runtime.JSX.Element;
227
234
 
228
235
  /**
229
236
  * Access the Kerne client instance
@@ -266,14 +273,14 @@ declare function useAuth(): {
266
273
  newPassword: string;
267
274
  }) => Promise<void>;
268
275
  requestAccountDeletion: () => Promise<void>;
269
- requestPasswordReset: (email: string, callbackUrl?: string) => Promise<void>;
276
+ requestPasswordReset: (email: string) => Promise<void>;
270
277
  confirmPasswordReset: (token: string, password: string) => Promise<void>;
271
- startPasswordless: (email: string, callbackUrl?: string, invitation?: {
278
+ startPasswordless: (email: string, invitation?: {
272
279
  invitationToken?: string;
273
280
  invitationCode?: string;
274
281
  }) => Promise<void>;
275
282
  loginWithMagicLink: (token: string) => Promise<_kerne_types.AuthResponse>;
276
- sendVerificationEmail: (verificationType?: "code" | "link", callbackUrl?: string) => Promise<void>;
283
+ sendVerificationEmail: (verificationType?: "code" | "link") => Promise<void>;
277
284
  verifyEmailWithCode: (code: string) => Promise<{
278
285
  success: boolean;
279
286
  needsTokenRefresh: boolean;
@@ -301,14 +308,8 @@ declare function useAuth(): {
301
308
  * ```
302
309
  */
303
310
  declare function useCheckout(): {
304
- createCheckout: (planPriceId: string, options?: {
305
- successUrl?: string;
306
- cancelUrl?: string;
307
- }) => Promise<string>;
308
- openCheckout: (planPriceId: string, options?: {
309
- successUrl?: string;
310
- cancelUrl?: string;
311
- }) => Promise<void>;
311
+ createCheckout: (planPriceId: string, drivers?: DriverSelection[]) => Promise<string>;
312
+ openCheckout: (planPriceId: string, drivers?: DriverSelection[]) => Promise<void>;
312
313
  };
313
314
  /**
314
315
  * Billing portal hook - lets a user manage their subscription/payment method.
@@ -320,8 +321,8 @@ declare function useCheckout(): {
320
321
  * ```
321
322
  */
322
323
  declare function usePortal(): {
323
- createPortal: (returnUrl?: string) => Promise<string>;
324
- openPortal: (returnUrl?: string) => Promise<void>;
324
+ createPortal: () => Promise<string>;
325
+ openPortal: () => Promise<void>;
325
326
  };
326
327
  /**
327
328
  * Cancels or changes the current scope's own subscription - JWT-scoped like every other hook
@@ -376,16 +377,13 @@ interface PlanSwitchTarget {
376
377
  * usePlanSwitch(subscription, refetch);
377
378
  * ```
378
379
  */
379
- declare function usePlanSwitch(subscription: SubscriptionWithPlan | null, refetchSubscription: () => void, options?: {
380
- successUrl?: string;
381
- cancelUrl?: string;
382
- }): {
380
+ declare function usePlanSwitch(subscription: SubscriptionWithPlan | null, refetchSubscription: () => void): {
383
381
  pending: PlanSwitchTarget | null;
384
382
  switching: boolean;
385
383
  switchError: unknown;
386
384
  checkoutPriceId: string | null;
387
385
  checkoutError: unknown;
388
- selectPlan: (plan: PublicPlan, price: PublicPlanPrice) => Promise<void>;
386
+ selectPlan: (plan: PublicPlan, price: PublicPlanPrice, drivers?: DriverSelection[]) => Promise<void>;
389
387
  confirmSwitch: () => Promise<void>;
390
388
  dismissSwitch: () => void;
391
389
  };
@@ -468,9 +466,13 @@ declare function useAuthConfig(): {
468
466
  };
469
467
  /**
470
468
  * Every entitlement for the current user in one call - for a "your plan"
471
- * screen. Use `useAccess` instead for a single feature - this response
472
- * never carries `allowed` (see docs: the list is cached, a stale verdict
473
- * would be unsafe).
469
+ * screen. `EntitlementSummary` carries no `allowed` field, on purpose: this
470
+ * list is a cached snapshot for display ("12/50 calls used"), not a live
471
+ * verdict. **Never gate access by reading `limit`/`remaining`/`enabled`
472
+ * yourself** - that silently reimplements override precedence, overage
473
+ * rules, and the race against concurrent usage that `useAccess()` already
474
+ * handles server-side. Use `useAccess()` (or `kerne.allows()`/`enforce()`)
475
+ * for every actual gate, and this hook only to render numbers.
474
476
  *
475
477
  * @example
476
478
  * ```tsx
@@ -495,6 +497,24 @@ declare function useUsage(featureKey?: string): {
495
497
  isLoading: boolean;
496
498
  error: Error | null;
497
499
  };
500
+ /**
501
+ * Usage write path for the signed-in user: `consume()` gates a QUOTA and commits
502
+ * in one step, `report()`/`reportBatch()` record usage without a check.
503
+ * `reportBatch()` sends many events in one request - the fix for hand-rolled
504
+ * request staggering in a component reporting several rows at once.
505
+ *
506
+ * @example
507
+ * ```tsx
508
+ * const { consume, reportBatch } = useReportUsage();
509
+ * await consume('exports', { delta: 1 });
510
+ * await reportBatch(machines.map((m) => ({ featureKey: 'machine_minutes', delta: m.minutes })));
511
+ * ```
512
+ */
513
+ declare function useReportUsage(): {
514
+ consume: (featureKey: string, params?: Omit<ConsumeParams, "subject">) => Promise<EntitlementCheck>;
515
+ report: (featureKey: string, params?: Omit<ReportUsageParams, "subject">) => Promise<UsageRecord>;
516
+ reportBatch: (events: Array<Omit<ReportUsageBatchEvent, "subject">>) => Promise<_kerne_types.BatchUsageResult[]>;
517
+ };
498
518
  /**
499
519
  * Waitlist signup form - a mutation, not a data fetch, same shape as
500
520
  * `useCheckout`/`usePortal`.
@@ -736,4 +756,4 @@ declare function useKerneError(): {
736
756
  clearError: () => void;
737
757
  };
738
758
 
739
- export { Access, AuthLoading, Authenticated, HasSubscription, KerneClient, KerneContext, type KerneError, KerneErrorBoundary, KerneLocalization, KerneProvider, type KerneProviderProps, type KerneReactConfig, type PlanSwitchTarget, Protected, Unauthenticated, useAccess, useAuth, useAuthConfig, useCancelSubscription, useChangePlan, useCheckout, useClient, useEntitlements, useInvitation, useKerneError, usePlanSwitch, usePlans, usePortal, useSubscription, useUsage, useUser, useWaitlist };
759
+ export { Access, AuthLoading, Authenticated, HasSubscription, KerneClient, KerneContext, type KerneError, KerneErrorBoundary, KerneLocalization, KerneProvider, type KerneProviderProps, type KerneReactConfig, type PlanSwitchTarget, Protected, Unauthenticated, useAccess, useAuth, useAuthConfig, useCancelSubscription, useChangePlan, useCheckout, useClient, useEntitlements, useInvitation, useKerneError, usePlanSwitch, usePlans, usePortal, useReportUsage, useSubscription, useUsage, useUser, useWaitlist };
package/dist/index.js CHANGED
@@ -4,6 +4,8 @@ import {
4
4
  KerneContext,
5
5
  KerneProvider,
6
6
  defaultLocalization,
7
+ fr,
8
+ mergeLocalization,
7
9
  useAccess,
8
10
  useAuth,
9
11
  useAuthConfig,
@@ -16,11 +18,12 @@ import {
16
18
  usePlanSwitch,
17
19
  usePlans,
18
20
  usePortal,
21
+ useReportUsage,
19
22
  useSubscription,
20
23
  useUsage,
21
24
  useUser,
22
25
  useWaitlist
23
- } from "./chunk-IZMRK3WK.js";
26
+ } from "./chunk-ZVL47U4B.js";
24
27
 
25
28
  // src/components.tsx
26
29
  import React from "react";
@@ -80,6 +83,37 @@ function Protected({
80
83
  // src/error-boundary.tsx
81
84
  import React2, { Component } from "react";
82
85
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
86
+ var STYLE_ELEMENT_ID = "kerne-error-boundary-styles";
87
+ var CSS = `
88
+ .kerne-eb { padding: 24px; text-align: center; color: #16171a; }
89
+ .kerne-eb-title { font-size: 20px; font-weight: 600; letter-spacing: -.01em; margin: 0 0 8px; }
90
+ .kerne-eb-body { color: #6b7280; margin: 0 auto 20px; max-width: 40ch; font-size: 14px; line-height: 1.5; }
91
+ .kerne-eb-code { color: #6f7681; margin: -12px 0 20px; font-size: 12px; }
92
+ .kerne-eb-button {
93
+ font: inherit; font-size: 14px; font-weight: 500; height: 44px;
94
+ padding: 0 20px; border-radius: 8px; border: none; cursor: pointer;
95
+ background: #16171a; color: #fff;
96
+ transition: background-color .12s ease;
97
+ }
98
+ .kerne-eb-button:hover { background: #2c2e33; }
99
+ .kerne-eb-button:focus-visible { outline: 2px solid #16171a; outline-offset: 2px; }
100
+ @media (prefers-color-scheme: dark) {
101
+ .kerne-eb { color: #f0f1f3; }
102
+ .kerne-eb-body { color: #9ba2ad; }
103
+ .kerne-eb-code { color: #838b96; }
104
+ .kerne-eb-button { background: #f0f1f3; color: #16181d; }
105
+ .kerne-eb-button:hover { background: #d8dade; }
106
+ .kerne-eb-button:focus-visible { outline-color: #f0f1f3; }
107
+ }
108
+ `;
109
+ function ensureErrorBoundaryStylesInjected() {
110
+ if (typeof document === "undefined") return;
111
+ if (document.getElementById(STYLE_ELEMENT_ID)) return;
112
+ const style = document.createElement("style");
113
+ style.id = STYLE_ELEMENT_ID;
114
+ style.textContent = CSS;
115
+ document.head.appendChild(style);
116
+ }
83
117
  var KerneErrorBoundary = class extends Component {
84
118
  constructor(props) {
85
119
  super(props);
@@ -110,28 +144,15 @@ var KerneErrorBoundary = class extends Component {
110
144
  }
111
145
  return fallback;
112
146
  }
113
- return /* @__PURE__ */ jsxs("div", { style: { padding: "20px", textAlign: "center" }, children: [
114
- /* @__PURE__ */ jsx2("h2", { style: { color: "#dc2626", marginBottom: "10px" }, children: "Something went wrong" }),
115
- /* @__PURE__ */ jsx2("p", { style: { color: "#6b7280", marginBottom: "20px" }, children: error.message || "An unexpected error occurred" }),
116
- error.code && /* @__PURE__ */ jsxs("p", { style: { color: "#9ca3af", fontSize: "12px", marginBottom: "20px" }, children: [
147
+ ensureErrorBoundaryStylesInjected();
148
+ return /* @__PURE__ */ jsxs("div", { className: "kerne-eb", children: [
149
+ /* @__PURE__ */ jsx2("h2", { className: "kerne-eb-title", children: "Something went wrong" }),
150
+ /* @__PURE__ */ jsx2("p", { className: "kerne-eb-body", children: error.message || "An unexpected error occurred" }),
151
+ error.code && /* @__PURE__ */ jsxs("p", { className: "kerne-eb-code", children: [
117
152
  "Error code: ",
118
153
  error.code
119
154
  ] }),
120
- /* @__PURE__ */ jsx2(
121
- "button",
122
- {
123
- onClick: this.reset,
124
- style: {
125
- padding: "10px 20px",
126
- backgroundColor: "#3b82f6",
127
- color: "white",
128
- border: "none",
129
- borderRadius: "6px",
130
- cursor: "pointer"
131
- },
132
- children: "Try again"
133
- }
134
- )
155
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "kerne-eb-button", onClick: this.reset, children: "Try again" })
135
156
  ] });
136
157
  }
137
158
  return this.props.children;
@@ -164,6 +185,8 @@ export {
164
185
  Protected,
165
186
  Unauthenticated,
166
187
  defaultLocalization,
188
+ fr,
189
+ mergeLocalization,
167
190
  useAccess,
168
191
  useAuth,
169
192
  useAuthConfig,
@@ -177,6 +200,7 @@ export {
177
200
  usePlanSwitch,
178
201
  usePlans,
179
202
  usePortal,
203
+ useReportUsage,
180
204
  useSubscription,
181
205
  useUsage,
182
206
  useUser,