@kerne/react 1.1.0 → 1.2.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.
@@ -378,6 +378,11 @@ var KerneClient = class {
378
378
  baseUrl: this.baseUrl,
379
379
  appId: this.appId,
380
380
  timeout: config.timeout,
381
+ // Forwarded so <KerneProvider defaultRedirects={...} headers={...}> is
382
+ // actually honored - the type inherited them from KerneConfig, but the
383
+ // client was dropping both silently.
384
+ headers: config.headers,
385
+ defaultRedirects: config.defaultRedirects,
381
386
  onUnauthorized: () => {
382
387
  if (process.env.NODE_ENV === "development") {
383
388
  console.warn("[Kerne] 401 received. Auto-logout disabled in dev.");
@@ -695,8 +700,9 @@ var KerneClient = class {
695
700
  return response;
696
701
  }
697
702
  // Cross-Origin Session Handoff
698
- // For apps/portal (auth hosted on its own origin): call this at the end of
699
- // a successful auth flow (login/magic-link/register/activation) instead of
703
+ // For a hosted auth origin (auth on its own domain, separate from the
704
+ // tenant's app): call this at the end of a successful auth flow
705
+ // (login/magic-link/register/activation) instead of
700
706
  // a plain `window.location.href = url` whenever `url` isn't same-origin -
701
707
  // the exchange on the other end is picked up automatically by
702
708
  // `exchangeHandoffOnLoad()`, no code required on the tenant app's side.
@@ -748,6 +754,24 @@ var KerneClient = class {
748
754
  async check(featureKey, requested) {
749
755
  return this.kerne.check(featureKey, { requested });
750
756
  }
757
+ /** Atomically check a QUOTA limit and commit the consumption. Reports for the signed-in subject. */
758
+ async consume(featureKey, params) {
759
+ return this.kerne.consume(featureKey, params);
760
+ }
761
+ /** Report usage without a limit check, for the signed-in subject. See `reportUsageBatch()` for many at once. */
762
+ async reportUsage(featureKey, params) {
763
+ return this.kerne.reportUsage(featureKey, { subject: this.requireUserId(), ...params });
764
+ }
765
+ /** Several usage events in one request instead of one call each - kills the manual request staggering. */
766
+ async reportUsageBatch(events) {
767
+ const subject = this.requireUserId();
768
+ return this.kerne.reportUsageBatch(events.map((e) => ({ subject, ...e })));
769
+ }
770
+ requireUserId() {
771
+ const id = this.user?.id;
772
+ if (!id) throw new Error("reportUsage requires a signed-in user; none is loaded yet.");
773
+ return id;
774
+ }
751
775
  async createCheckout(planPriceId, options) {
752
776
  const { url } = await this.kerne.billing.checkout(planPriceId, options);
753
777
  return url;
@@ -930,7 +954,7 @@ function useAuth() {
930
954
  (token2, password) => client.completeActivation(token2, password),
931
955
  [client]
932
956
  ),
933
- // Cross-origin handoff (apps/portal -> tenant app_url) - see redirectWithSession on KerneClient.
957
+ // Cross-origin handoff (hosted auth origin -> tenant app_url) - see redirectWithSession on KerneClient.
934
958
  redirectWithSession: useCallback2((url) => client.redirectWithSession(url), [client])
935
959
  };
936
960
  }
@@ -1207,6 +1231,23 @@ function useUsage(featureKey) {
1207
1231
  }, [client, featureKey]);
1208
1232
  return state;
1209
1233
  }
1234
+ function useReportUsage() {
1235
+ const client = useClient();
1236
+ return {
1237
+ consume: useCallback2(
1238
+ (featureKey, params) => client.consume(featureKey, params),
1239
+ [client]
1240
+ ),
1241
+ report: useCallback2(
1242
+ (featureKey, params) => client.reportUsage(featureKey, params),
1243
+ [client]
1244
+ ),
1245
+ reportBatch: useCallback2(
1246
+ (events) => client.reportUsageBatch(events),
1247
+ [client]
1248
+ )
1249
+ };
1250
+ }
1210
1251
  function useWaitlist() {
1211
1252
  const client = useClient();
1212
1253
  return {
@@ -1279,6 +1320,7 @@ export {
1279
1320
  useAuthConfig,
1280
1321
  useEntitlements,
1281
1322
  useUsage,
1323
+ useReportUsage,
1282
1324
  useWaitlist,
1283
1325
  useInvitation,
1284
1326
  useUser
@@ -378,6 +378,11 @@ var KerneClient = (_class = class {
378
378
  baseUrl: this.baseUrl,
379
379
  appId: this.appId,
380
380
  timeout: config.timeout,
381
+ // Forwarded so <KerneProvider defaultRedirects={...} headers={...}> is
382
+ // actually honored - the type inherited them from KerneConfig, but the
383
+ // client was dropping both silently.
384
+ headers: config.headers,
385
+ defaultRedirects: config.defaultRedirects,
381
386
  onUnauthorized: () => {
382
387
  if (process.env.NODE_ENV === "development") {
383
388
  console.warn("[Kerne] 401 received. Auto-logout disabled in dev.");
@@ -695,8 +700,9 @@ var KerneClient = (_class = class {
695
700
  return response;
696
701
  }
697
702
  // Cross-Origin Session Handoff
698
- // For apps/portal (auth hosted on its own origin): call this at the end of
699
- // a successful auth flow (login/magic-link/register/activation) instead of
703
+ // For a hosted auth origin (auth on its own domain, separate from the
704
+ // tenant's app): call this at the end of a successful auth flow
705
+ // (login/magic-link/register/activation) instead of
700
706
  // a plain `window.location.href = url` whenever `url` isn't same-origin -
701
707
  // the exchange on the other end is picked up automatically by
702
708
  // `exchangeHandoffOnLoad()`, no code required on the tenant app's side.
@@ -748,6 +754,24 @@ var KerneClient = (_class = class {
748
754
  async check(featureKey, requested) {
749
755
  return this.kerne.check(featureKey, { requested });
750
756
  }
757
+ /** Atomically check a QUOTA limit and commit the consumption. Reports for the signed-in subject. */
758
+ async consume(featureKey, params) {
759
+ return this.kerne.consume(featureKey, params);
760
+ }
761
+ /** Report usage without a limit check, for the signed-in subject. See `reportUsageBatch()` for many at once. */
762
+ async reportUsage(featureKey, params) {
763
+ return this.kerne.reportUsage(featureKey, { subject: this.requireUserId(), ...params });
764
+ }
765
+ /** Several usage events in one request instead of one call each - kills the manual request staggering. */
766
+ async reportUsageBatch(events) {
767
+ const subject = this.requireUserId();
768
+ return this.kerne.reportUsageBatch(events.map((e) => ({ subject, ...e })));
769
+ }
770
+ requireUserId() {
771
+ const id = _optionalChain([this, 'access', _21 => _21.user, 'optionalAccess', _22 => _22.id]);
772
+ if (!id) throw new Error("reportUsage requires a signed-in user; none is loaded yet.");
773
+ return id;
774
+ }
751
775
  async createCheckout(planPriceId, options) {
752
776
  const { url } = await this.kerne.billing.checkout(planPriceId, options);
753
777
  return url;
@@ -829,7 +853,7 @@ function KerneProvider({ children, localization, ...config }) {
829
853
  }, [config.appId, config.baseUrl]);
830
854
  _react.useEffect.call(void 0, () => {
831
855
  return () => {
832
- _optionalChain([client, 'access', _21 => _21.cancelRefresh, 'optionalCall', _22 => _22()]);
856
+ _optionalChain([client, 'access', _23 => _23.cancelRefresh, 'optionalCall', _24 => _24()]);
833
857
  };
834
858
  }, [client]);
835
859
  const l10n = _react.useMemo.call(void 0, () => mergeLocalization(localization), [localization]);
@@ -930,7 +954,7 @@ function useAuth() {
930
954
  (token2, password) => client.completeActivation(token2, password),
931
955
  [client]
932
956
  ),
933
- // Cross-origin handoff (apps/portal -> tenant app_url) - see redirectWithSession on KerneClient.
957
+ // Cross-origin handoff (hosted auth origin -> tenant app_url) - see redirectWithSession on KerneClient.
934
958
  redirectWithSession: _react.useCallback.call(void 0, (url) => client.redirectWithSession(url), [client])
935
959
  };
936
960
  }
@@ -994,8 +1018,8 @@ function usePlanSwitch(subscription, refetchSubscription, options) {
994
1018
  const [switchError, setSwitchError] = _react.useState.call(void 0, null);
995
1019
  const [checkoutPriceId, setCheckoutPriceId] = _react.useState.call(void 0, null);
996
1020
  const [checkoutError, setCheckoutError] = _react.useState.call(void 0, null);
997
- const successUrl = _optionalChain([options, 'optionalAccess', _23 => _23.successUrl]);
998
- const cancelUrl = _optionalChain([options, 'optionalAccess', _24 => _24.cancelUrl]);
1021
+ const successUrl = _optionalChain([options, 'optionalAccess', _25 => _25.successUrl]);
1022
+ const cancelUrl = _optionalChain([options, 'optionalAccess', _26 => _26.cancelUrl]);
999
1023
  const selectPlan = _react.useCallback.call(void 0,
1000
1024
  async (plan, price) => {
1001
1025
  if (subscription) {
@@ -1102,8 +1126,8 @@ function useSubscription(productSlug) {
1102
1126
  setState((prev) => ({ ...prev, isLoading: true }));
1103
1127
  fetchOnce({ current: true });
1104
1128
  }, [fetchOnce]);
1105
- const isActive = _optionalChain([state, 'access', _25 => _25.subscription, 'optionalAccess', _26 => _26.status]) === "ACTIVE" || _optionalChain([state, 'access', _27 => _27.subscription, 'optionalAccess', _28 => _28.status]) === "TRIALING";
1106
- const planSlug = _optionalChain([state, 'access', _29 => _29.subscription, 'optionalAccess', _30 => _30.plan, 'optionalAccess', _31 => _31.slug]);
1129
+ const isActive = _optionalChain([state, 'access', _27 => _27.subscription, 'optionalAccess', _28 => _28.status]) === "ACTIVE" || _optionalChain([state, 'access', _29 => _29.subscription, 'optionalAccess', _30 => _30.status]) === "TRIALING";
1130
+ const planSlug = _optionalChain([state, 'access', _31 => _31.subscription, 'optionalAccess', _32 => _32.plan, 'optionalAccess', _33 => _33.slug]);
1107
1131
  return {
1108
1132
  ...state,
1109
1133
  isActive,
@@ -1207,6 +1231,23 @@ function useUsage(featureKey) {
1207
1231
  }, [client, featureKey]);
1208
1232
  return state;
1209
1233
  }
1234
+ function useReportUsage() {
1235
+ const client = useClient();
1236
+ return {
1237
+ consume: _react.useCallback.call(void 0,
1238
+ (featureKey, params) => client.consume(featureKey, params),
1239
+ [client]
1240
+ ),
1241
+ report: _react.useCallback.call(void 0,
1242
+ (featureKey, params) => client.reportUsage(featureKey, params),
1243
+ [client]
1244
+ ),
1245
+ reportBatch: _react.useCallback.call(void 0,
1246
+ (events) => client.reportUsageBatch(events),
1247
+ [client]
1248
+ )
1249
+ };
1250
+ }
1210
1251
  function useWaitlist() {
1211
1252
  const client = useClient();
1212
1253
  return {
@@ -1251,9 +1292,9 @@ function useUser() {
1251
1292
  update: updateProfile,
1252
1293
  // Computed properties
1253
1294
  fullName: user ? `${user.first_name || ""} ${user.last_name || ""}`.trim() : null,
1254
- initials: user ? `${_optionalChain([user, 'access', _32 => _32.first_name, 'optionalAccess', _33 => _33[0]]) || ""}${_optionalChain([user, 'access', _34 => _34.last_name, 'optionalAccess', _35 => _35[0]]) || ""}`.toUpperCase() : null,
1255
- email: _optionalChain([user, 'optionalAccess', _36 => _36.email]) || null,
1256
- emailVerified: _optionalChain([user, 'optionalAccess', _37 => _37.email_verified]) || false
1295
+ initials: user ? `${_optionalChain([user, 'access', _34 => _34.first_name, 'optionalAccess', _35 => _35[0]]) || ""}${_optionalChain([user, 'access', _36 => _36.last_name, 'optionalAccess', _37 => _37[0]]) || ""}`.toUpperCase() : null,
1296
+ email: _optionalChain([user, 'optionalAccess', _38 => _38.email]) || null,
1297
+ emailVerified: _optionalChain([user, 'optionalAccess', _39 => _39.email_verified]) || false
1257
1298
  };
1258
1299
  }
1259
1300
 
@@ -1282,4 +1323,5 @@ function useUser() {
1282
1323
 
1283
1324
 
1284
1325
 
1285
- exports.defaultLocalization = defaultLocalization; exports.useLocalization = useLocalization; exports.fill = fill; exports.fillNode = fillNode; exports.useErrorResolver = useErrorResolver; exports.KerneClient = KerneClient; exports.KerneContext = KerneContext; exports.KerneProvider = KerneProvider; exports.useClient = useClient; exports.useAuth = useAuth; exports.useCheckout = useCheckout; exports.usePortal = usePortal; exports.useCancelSubscription = useCancelSubscription; exports.useChangePlan = useChangePlan; exports.usePlanSwitch = usePlanSwitch; exports.useAccess = useAccess; exports.useSubscription = useSubscription; exports.usePlans = usePlans; exports.useAuthConfig = useAuthConfig; exports.useEntitlements = useEntitlements; exports.useUsage = useUsage; exports.useWaitlist = useWaitlist; exports.useInvitation = useInvitation; exports.useUser = useUser;
1326
+
1327
+ exports.defaultLocalization = defaultLocalization; exports.useLocalization = useLocalization; exports.fill = fill; exports.fillNode = fillNode; exports.useErrorResolver = useErrorResolver; exports.KerneClient = KerneClient; exports.KerneContext = KerneContext; exports.KerneProvider = KerneProvider; exports.useClient = useClient; exports.useAuth = useAuth; exports.useCheckout = useCheckout; exports.usePortal = usePortal; exports.useCancelSubscription = useCancelSubscription; exports.useChangePlan = useChangePlan; exports.usePlanSwitch = usePlanSwitch; exports.useAccess = useAccess; exports.useSubscription = useSubscription; exports.usePlans = usePlans; exports.useAuthConfig = useAuthConfig; exports.useEntitlements = useEntitlements; exports.useUsage = useUsage; exports.useReportUsage = useReportUsage; exports.useWaitlist = useWaitlist; exports.useInvitation = useInvitation; exports.useUser = useUser;
@@ -273,7 +273,7 @@ interface KerneLocalization {
273
273
  signOut: string;
274
274
  };
275
275
  /**
276
- * Keyed by the API's stable ErrorCode (see packages/core/src/errors).
276
+ * Keyed by the API's stable ErrorCode.
277
277
  * Anything absent falls back to `generic` - never to the raw server message,
278
278
  * which is written for an integrator and is always English.
279
279
  */
@@ -273,7 +273,7 @@ interface KerneLocalization {
273
273
  signOut: string;
274
274
  };
275
275
  /**
276
- * Keyed by the API's stable ErrorCode (see packages/core/src/errors).
276
+ * Keyed by the API's stable ErrorCode.
277
277
  * Anything absent falls back to `generic` - never to the raw server message,
278
278
  * which is written for an integrator and is always English.
279
279
  */
package/dist/index.cjs CHANGED
@@ -20,25 +20,26 @@
20
20
 
21
21
 
22
22
 
23
- var _chunkTYNZ2WU7cjs = require('./chunk-TYNZ2WU7.cjs');
23
+
24
+ var _chunkN6VPGJ36cjs = require('./chunk-N6VPGJ36.cjs');
24
25
 
25
26
  // src/components.tsx
26
27
  var _react = require('react'); var _react2 = _interopRequireDefault(_react);
27
28
  var _jsxruntime = require('react/jsx-runtime');
28
29
  function Authenticated({ children, fallback = null }) {
29
- const { isAuthenticated } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
30
+ const { isAuthenticated } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
30
31
  return isAuthenticated ? children : fallback;
31
32
  }
32
33
  function Unauthenticated({ children, fallback = null }) {
33
- const { isAuthenticated } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
34
+ const { isAuthenticated } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
34
35
  return !isAuthenticated ? children : fallback;
35
36
  }
36
37
  function AuthLoading({ children }) {
37
- const client = _chunkTYNZ2WU7cjs.useClient.call(void 0, );
38
+ const client = _chunkN6VPGJ36cjs.useClient.call(void 0, );
38
39
  return client.isLoading ? children : null;
39
40
  }
40
41
  function HasSubscription({ children, fallback = null }) {
41
- const client = _chunkTYNZ2WU7cjs.useClient.call(void 0, );
42
+ const client = _chunkN6VPGJ36cjs.useClient.call(void 0, );
42
43
  const [hasSubscription, setHasSubscription] = _react2.default.useState(null);
43
44
  _react2.default.useEffect(() => {
44
45
  client.getSubscription().then((sub) => {
@@ -56,7 +57,7 @@ function Access({
56
57
  requested,
57
58
  fallback = null
58
59
  }) {
59
- const { allowed, isLoading } = _chunkTYNZ2WU7cjs.useAccess.call(void 0, featureKey, requested);
60
+ const { allowed, isLoading } = _chunkN6VPGJ36cjs.useAccess.call(void 0, featureKey, requested);
60
61
  if (isLoading) return null;
61
62
  return allowed ? children : fallback;
62
63
  }
@@ -67,7 +68,7 @@ function Protected({
67
68
  authFallback = null,
68
69
  billingFallback = null
69
70
  }) {
70
- const { isAuthenticated } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
71
+ const { isAuthenticated } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
71
72
  if (auth && !isAuthenticated) {
72
73
  return authFallback;
73
74
  }
@@ -181,4 +182,5 @@ function useKerneError() {
181
182
 
182
183
 
183
184
 
184
- exports.Access = Access; exports.AuthLoading = AuthLoading; exports.Authenticated = Authenticated; exports.HasSubscription = HasSubscription; exports.KerneClient = _chunkTYNZ2WU7cjs.KerneClient; exports.KerneContext = _chunkTYNZ2WU7cjs.KerneContext; exports.KerneErrorBoundary = KerneErrorBoundary; exports.KerneProvider = _chunkTYNZ2WU7cjs.KerneProvider; exports.Protected = Protected; exports.Unauthenticated = Unauthenticated; exports.defaultLocalization = _chunkTYNZ2WU7cjs.defaultLocalization; exports.useAccess = _chunkTYNZ2WU7cjs.useAccess; exports.useAuth = _chunkTYNZ2WU7cjs.useAuth; exports.useAuthConfig = _chunkTYNZ2WU7cjs.useAuthConfig; exports.useCancelSubscription = _chunkTYNZ2WU7cjs.useCancelSubscription; exports.useChangePlan = _chunkTYNZ2WU7cjs.useChangePlan; exports.useCheckout = _chunkTYNZ2WU7cjs.useCheckout; exports.useClient = _chunkTYNZ2WU7cjs.useClient; exports.useEntitlements = _chunkTYNZ2WU7cjs.useEntitlements; exports.useInvitation = _chunkTYNZ2WU7cjs.useInvitation; exports.useKerneError = useKerneError; exports.usePlanSwitch = _chunkTYNZ2WU7cjs.usePlanSwitch; exports.usePlans = _chunkTYNZ2WU7cjs.usePlans; exports.usePortal = _chunkTYNZ2WU7cjs.usePortal; exports.useSubscription = _chunkTYNZ2WU7cjs.useSubscription; exports.useUsage = _chunkTYNZ2WU7cjs.useUsage; exports.useUser = _chunkTYNZ2WU7cjs.useUser; exports.useWaitlist = _chunkTYNZ2WU7cjs.useWaitlist;
185
+
186
+ exports.Access = Access; exports.AuthLoading = AuthLoading; exports.Authenticated = Authenticated; exports.HasSubscription = HasSubscription; exports.KerneClient = _chunkN6VPGJ36cjs.KerneClient; exports.KerneContext = _chunkN6VPGJ36cjs.KerneContext; exports.KerneErrorBoundary = KerneErrorBoundary; exports.KerneProvider = _chunkN6VPGJ36cjs.KerneProvider; exports.Protected = Protected; exports.Unauthenticated = Unauthenticated; exports.defaultLocalization = _chunkN6VPGJ36cjs.defaultLocalization; exports.useAccess = _chunkN6VPGJ36cjs.useAccess; exports.useAuth = _chunkN6VPGJ36cjs.useAuth; exports.useAuthConfig = _chunkN6VPGJ36cjs.useAuthConfig; exports.useCancelSubscription = _chunkN6VPGJ36cjs.useCancelSubscription; exports.useChangePlan = _chunkN6VPGJ36cjs.useChangePlan; exports.useCheckout = _chunkN6VPGJ36cjs.useCheckout; exports.useClient = _chunkN6VPGJ36cjs.useClient; exports.useEntitlements = _chunkN6VPGJ36cjs.useEntitlements; exports.useInvitation = _chunkN6VPGJ36cjs.useInvitation; exports.useKerneError = useKerneError; exports.usePlanSwitch = _chunkN6VPGJ36cjs.usePlanSwitch; exports.usePlans = _chunkN6VPGJ36cjs.usePlans; exports.usePortal = _chunkN6VPGJ36cjs.usePortal; exports.useReportUsage = _chunkN6VPGJ36cjs.useReportUsage; exports.useSubscription = _chunkN6VPGJ36cjs.useSubscription; exports.useUsage = _chunkN6VPGJ36cjs.useUsage; exports.useUser = _chunkN6VPGJ36cjs.useUser; exports.useWaitlist = _chunkN6VPGJ36cjs.useWaitlist;
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, 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 } from './i18n-CXEhPK58.cjs';
9
+ export { d as defaultLocalization } from './i18n-CXEhPK58.cjs';
10
10
 
11
11
  interface KerneReactConfig extends Omit<KerneConfig, 'secretKey'> {
12
12
  storage?: Storage;
@@ -166,6 +166,13 @@ 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
+ /** 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;
169
176
  createCheckout(planPriceId: string, options?: {
170
177
  successUrl?: string;
171
178
  cancelUrl?: string;
@@ -495,6 +502,24 @@ declare function useUsage(featureKey?: string): {
495
502
  isLoading: boolean;
496
503
  error: Error | null;
497
504
  };
505
+ /**
506
+ * Usage write path for the signed-in user: `consume()` gates a QUOTA and commits
507
+ * in one step, `report()`/`reportBatch()` record usage without a check.
508
+ * `reportBatch()` sends many events in one request - the fix for hand-rolled
509
+ * request staggering in a component reporting several rows at once.
510
+ *
511
+ * @example
512
+ * ```tsx
513
+ * const { consume, reportBatch } = useReportUsage();
514
+ * await consume('exports', { delta: 1 });
515
+ * await reportBatch(machines.map((m) => ({ featureKey: 'machine_minutes', delta: m.minutes })));
516
+ * ```
517
+ */
518
+ declare function useReportUsage(): {
519
+ consume: (featureKey: string, params?: Omit<ConsumeParams, "subject">) => Promise<EntitlementCheck>;
520
+ report: (featureKey: string, params?: Omit<ReportUsageParams, "subject">) => Promise<UsageRecord>;
521
+ reportBatch: (events: Array<Omit<ReportUsageBatchEvent, "subject">>) => Promise<_kerne_types.BatchUsageResult[]>;
522
+ };
498
523
  /**
499
524
  * Waitlist signup form - a mutation, not a data fetch, same shape as
500
525
  * `useCheckout`/`usePortal`.
@@ -736,4 +761,4 @@ declare function useKerneError(): {
736
761
  clearError: () => void;
737
762
  };
738
763
 
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 };
764
+ 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, 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 } from './i18n-CXEhPK58.js';
9
+ export { d as defaultLocalization } from './i18n-CXEhPK58.js';
10
10
 
11
11
  interface KerneReactConfig extends Omit<KerneConfig, 'secretKey'> {
12
12
  storage?: Storage;
@@ -166,6 +166,13 @@ 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
+ /** 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;
169
176
  createCheckout(planPriceId: string, options?: {
170
177
  successUrl?: string;
171
178
  cancelUrl?: string;
@@ -495,6 +502,24 @@ declare function useUsage(featureKey?: string): {
495
502
  isLoading: boolean;
496
503
  error: Error | null;
497
504
  };
505
+ /**
506
+ * Usage write path for the signed-in user: `consume()` gates a QUOTA and commits
507
+ * in one step, `report()`/`reportBatch()` record usage without a check.
508
+ * `reportBatch()` sends many events in one request - the fix for hand-rolled
509
+ * request staggering in a component reporting several rows at once.
510
+ *
511
+ * @example
512
+ * ```tsx
513
+ * const { consume, reportBatch } = useReportUsage();
514
+ * await consume('exports', { delta: 1 });
515
+ * await reportBatch(machines.map((m) => ({ featureKey: 'machine_minutes', delta: m.minutes })));
516
+ * ```
517
+ */
518
+ declare function useReportUsage(): {
519
+ consume: (featureKey: string, params?: Omit<ConsumeParams, "subject">) => Promise<EntitlementCheck>;
520
+ report: (featureKey: string, params?: Omit<ReportUsageParams, "subject">) => Promise<UsageRecord>;
521
+ reportBatch: (events: Array<Omit<ReportUsageBatchEvent, "subject">>) => Promise<_kerne_types.BatchUsageResult[]>;
522
+ };
498
523
  /**
499
524
  * Waitlist signup form - a mutation, not a data fetch, same shape as
500
525
  * `useCheckout`/`usePortal`.
@@ -736,4 +761,4 @@ declare function useKerneError(): {
736
761
  clearError: () => void;
737
762
  };
738
763
 
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 };
764
+ 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
@@ -16,11 +16,12 @@ import {
16
16
  usePlanSwitch,
17
17
  usePlans,
18
18
  usePortal,
19
+ useReportUsage,
19
20
  useSubscription,
20
21
  useUsage,
21
22
  useUser,
22
23
  useWaitlist
23
- } from "./chunk-IZMRK3WK.js";
24
+ } from "./chunk-IHNWY4ML.js";
24
25
 
25
26
  // src/components.tsx
26
27
  import React from "react";
@@ -177,6 +178,7 @@ export {
177
178
  usePlanSwitch,
178
179
  usePlans,
179
180
  usePortal,
181
+ useReportUsage,
180
182
  useSubscription,
181
183
  useUsage,
182
184
  useUser,
package/dist/ui/index.cjs CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
 
19
19
 
20
- var _chunkTYNZ2WU7cjs = require('../chunk-TYNZ2WU7.cjs');
20
+ var _chunkN6VPGJ36cjs = require('../chunk-N6VPGJ36.cjs');
21
21
 
22
22
  // src/ui/AuthFlow.tsx
23
23
  var _react = require('react');
@@ -903,7 +903,7 @@ function Field({
903
903
  const id = _react.useId.call(void 0, );
904
904
  const errorId = `${id}-error`;
905
905
  const noteId = `${id}-note`;
906
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
906
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
907
907
  const isPassword = type === "password";
908
908
  const [revealed, setRevealed] = _react.useState.call(void 0, false);
909
909
  const [capsLock, setCapsLock] = _react.useState.call(void 0, false);
@@ -1031,11 +1031,11 @@ function PasswordStrength({
1031
1031
  password,
1032
1032
  policy
1033
1033
  }) {
1034
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
1034
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
1035
1035
  const resolved = _nullishCoalesce(policy, () => ( resolvePolicy(null)));
1036
1036
  if (!meetsPolicy(password, resolved)) {
1037
1037
  const labels2 = {
1038
- min_length: _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.common.passwordHint, { count: resolved.min_length }),
1038
+ min_length: _chunkN6VPGJ36cjs.fill.call(void 0, l10n.common.passwordHint, { count: resolved.min_length }),
1039
1039
  require_uppercase: l10n.common.passwordRequireUppercase,
1040
1040
  require_number: l10n.common.passwordRequireNumber,
1041
1041
  require_symbol: l10n.common.passwordRequireSymbol
@@ -1231,10 +1231,10 @@ function OAuthButtons({
1231
1231
  disabled,
1232
1232
  onSelect
1233
1233
  }) {
1234
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
1234
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
1235
1235
  if (providers.length === 0) return null;
1236
1236
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-oauth-group", children: providers.map((provider) => {
1237
- const label = _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.common.continueWith, {
1237
+ const label = _chunkN6VPGJ36cjs.fill.call(void 0, l10n.common.continueWith, {
1238
1238
  provider: _nullishCoalesce(OAUTH_LABELS[provider], () => ( provider))
1239
1239
  });
1240
1240
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
@@ -1255,12 +1255,12 @@ function OAuthButtons({
1255
1255
  }) });
1256
1256
  }
1257
1257
  function KerneBranding({ show }) {
1258
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
1258
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
1259
1259
  if (!show) return null;
1260
1260
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "a", { className: "kerne-branding", href: "https://kerne.io", target: "_blank", rel: "noopener noreferrer", children: l10n.common.securedByKerne });
1261
1261
  }
1262
1262
  function FormSkeleton() {
1263
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
1263
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
1264
1264
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-form", "aria-busy": "true", "aria-label": l10n.common.loading, children: [
1265
1265
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-skeleton" }),
1266
1266
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-skeleton" })
@@ -1285,11 +1285,11 @@ function LoginForm({
1285
1285
  logo,
1286
1286
  className
1287
1287
  }) {
1288
- const client = _chunkTYNZ2WU7cjs.useClient.call(void 0, );
1289
- const { login, startPasswordless } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
1290
- const { config, isLoading: configLoading, error: configError } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
1291
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
1292
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
1288
+ const client = _chunkN6VPGJ36cjs.useClient.call(void 0, );
1289
+ const { login, startPasswordless } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
1290
+ const { config, isLoading: configLoading, error: configError } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
1291
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
1292
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
1293
1293
  const [email, setEmail] = _react.useState.call(void 0, "");
1294
1294
  const [password, setPassword] = _react.useState.call(void 0, "");
1295
1295
  const [submitting, setSubmitting] = _react.useState.call(void 0, false);
@@ -1353,7 +1353,7 @@ function LoginForm({
1353
1353
  const signUpLine = canSignUp && (signUpUrl || onSignUpClick) ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
1354
1354
  l10n.signIn.signUpPrompt,
1355
1355
  " ",
1356
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TextLink, { href: signUpUrl, onClick: onSignUpClick, children: appName ? _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.signIn.signUpForApp, { app: appName }) : l10n.signIn.signUpLink }),
1356
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TextLink, { href: signUpUrl, onClick: onSignUpClick, children: appName ? _chunkN6VPGJ36cjs.fill.call(void 0, l10n.signIn.signUpForApp, { app: appName }) : l10n.signIn.signUpLink }),
1357
1357
  "."
1358
1358
  ] }) : void 0;
1359
1359
  const submitLabel = submitting ? usePassword ? l10n.signIn.submitting : l10n.signIn.magicSubmitting : usePassword ? l10n.signIn.submit : l10n.signIn.magicSubmit;
@@ -1362,7 +1362,7 @@ function LoginForm({
1362
1362
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Masthead, { logo, title: heading, subtitle: _nullishCoalesce(subtitle, () => ( signUpLine)) }),
1363
1363
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-stack", children: [
1364
1364
  error ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: error }) : null,
1365
- linkSentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkTYNZ2WU7cjs.fillNode.call(void 0, l10n.signIn.linkSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: linkSentTo }) }) }) : hasEmailMethod ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1365
+ linkSentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkN6VPGJ36cjs.fillNode.call(void 0, l10n.signIn.linkSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: linkSentTo }) }) }) : hasEmailMethod ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1366
1366
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1367
1367
  Field,
1368
1368
  {
@@ -1438,13 +1438,13 @@ function RegisterForm({
1438
1438
  logo,
1439
1439
  className
1440
1440
  }) {
1441
- const client = _chunkTYNZ2WU7cjs.useClient.call(void 0, );
1442
- const { register, startPasswordless } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
1443
- const { config, isLoading: configLoading, error: configError } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
1441
+ const client = _chunkN6VPGJ36cjs.useClient.call(void 0, );
1442
+ const { register, startPasswordless } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
1443
+ const { config, isLoading: configLoading, error: configError } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
1444
1444
  const policy = resolvePolicy(config);
1445
1445
  const invitationToken = useUrlToken("token", invitationTokenProp) || void 0;
1446
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
1447
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
1446
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
1447
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
1448
1448
  const [email, setEmail] = _react.useState.call(void 0, "");
1449
1449
  const [password, setPassword] = _react.useState.call(void 0, "");
1450
1450
  const [name, setName] = _react.useState.call(void 0, "");
@@ -1455,7 +1455,7 @@ function RegisterForm({
1455
1455
  const [passwordMode, setPasswordMode] = _react.useState.call(void 0, false);
1456
1456
  const shell = (children) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Shell, { appearance, className, children });
1457
1457
  const appName = _optionalChain([config, 'optionalAccess', _13 => _13.app_name]);
1458
- const heading = _nullishCoalesce(title, () => ( (appName ? _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.signUp.titleWithApp, { app: appName }) : l10n.signUp.title)));
1458
+ const heading = _nullishCoalesce(title, () => ( (appName ? _chunkN6VPGJ36cjs.fill.call(void 0, l10n.signUp.titleWithApp, { app: appName }) : l10n.signUp.title)));
1459
1459
  const signInLine = signInUrl || onSignInClick ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
1460
1460
  l10n.signUp.signInPrompt,
1461
1461
  " ",
@@ -1482,7 +1482,7 @@ function RegisterForm({
1482
1482
  return shell(
1483
1483
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
1484
1484
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Masthead, { logo, title: l10n.signUp.closedTitle, subtitle: signInLine }),
1485
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.signUp.closedBody, { app: _nullishCoalesce(appName, () => ( l10n.common.thisApp)) }) }),
1485
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkN6VPGJ36cjs.fill.call(void 0, l10n.signUp.closedBody, { app: _nullishCoalesce(appName, () => ( l10n.common.thisApp)) }) }),
1486
1486
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, KerneBranding, { show: config.show_kerne_branding })
1487
1487
  ] })
1488
1488
  );
@@ -1544,7 +1544,7 @@ function RegisterForm({
1544
1544
  ),
1545
1545
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-stack", children: [
1546
1546
  error ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: error }) : null,
1547
- linkSentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkTYNZ2WU7cjs.fillNode.call(void 0, l10n.signUp.linkSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: linkSentTo }) }) }) : hasEmailMethod ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1547
+ linkSentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkN6VPGJ36cjs.fillNode.call(void 0, l10n.signUp.linkSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: linkSentTo }) }) }) : hasEmailMethod ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1548
1548
  needsCode ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1549
1549
  Field,
1550
1550
  {
@@ -1649,10 +1649,10 @@ function ForgotPasswordForm({
1649
1649
  logo,
1650
1650
  className
1651
1651
  }) {
1652
- const { requestPasswordReset } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
1653
- const { config } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
1654
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
1655
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
1652
+ const { requestPasswordReset } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
1653
+ const { config } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
1654
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
1655
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
1656
1656
  const [email, setEmail] = _react.useState.call(void 0, "");
1657
1657
  const [submitting, setSubmitting] = _react.useState.call(void 0, false);
1658
1658
  const [error, setError] = _react.useState.call(void 0, null);
@@ -1688,7 +1688,7 @@ function ForgotPasswordForm({
1688
1688
  ),
1689
1689
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-stack", children: [
1690
1690
  error ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: error }) : null,
1691
- sentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkTYNZ2WU7cjs.fillNode.call(void 0, l10n.resetPassword.requestSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: sentTo }) }) }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1691
+ sentTo ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { children: _chunkN6VPGJ36cjs.fillNode.call(void 0, l10n.resetPassword.requestSent, { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: sentTo }) }) }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "kerne-form", onSubmit: handleSubmit, children: [
1692
1692
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1693
1693
  Field,
1694
1694
  {
@@ -1725,12 +1725,12 @@ function ResetPasswordForm({
1725
1725
  logo,
1726
1726
  className
1727
1727
  }) {
1728
- const { confirmPasswordReset } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
1729
- const { config } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
1728
+ const { confirmPasswordReset } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
1729
+ const { config } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
1730
1730
  const policy = resolvePolicy(config);
1731
1731
  const token = useUrlToken("token", tokenProp);
1732
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
1733
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
1732
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
1733
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
1734
1734
  const [password, setPassword] = _react.useState.call(void 0, "");
1735
1735
  const [confirm, setConfirm] = _react.useState.call(void 0, "");
1736
1736
  const [submitting, setSubmitting] = _react.useState.call(void 0, false);
@@ -1845,12 +1845,12 @@ function MagicLinkCallback({
1845
1845
  logo,
1846
1846
  className
1847
1847
  }) {
1848
- const client = _chunkTYNZ2WU7cjs.useClient.call(void 0, );
1849
- const { loginWithMagicLink } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
1850
- const { config } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
1848
+ const client = _chunkN6VPGJ36cjs.useClient.call(void 0, );
1849
+ const { loginWithMagicLink } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
1850
+ const { config } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
1851
1851
  const token = useUrlToken("token", tokenProp);
1852
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
1853
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
1852
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
1853
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
1854
1854
  const [state, setState] = _react.useState.call(void 0, "pending");
1855
1855
  const [message, setMessage] = _react.useState.call(void 0, null);
1856
1856
  const consumed = _react.useRef.call(void 0, false);
@@ -1904,11 +1904,11 @@ function VerifyEmailForm({
1904
1904
  logo,
1905
1905
  className
1906
1906
  }) {
1907
- const { verifyEmailWithCode, verifyEmailWithLink, sendVerificationEmail, refreshToken } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
1908
- const { config } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
1907
+ const { verifyEmailWithCode, verifyEmailWithLink, sendVerificationEmail, refreshToken } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
1908
+ const { config } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
1909
1909
  const token = useUrlToken("token", tokenProp);
1910
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
1911
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
1910
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
1911
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
1912
1912
  const [code, setCode] = _react.useState.call(void 0, "");
1913
1913
  const [submitting, setSubmitting] = _react.useState.call(void 0, false);
1914
1914
  const [error, setError] = _react.useState.call(void 0, null);
@@ -2030,13 +2030,13 @@ function ActivateAccountForm({
2030
2030
  logo,
2031
2031
  className
2032
2032
  }) {
2033
- const client = _chunkTYNZ2WU7cjs.useClient.call(void 0, );
2034
- const { getActivationContext, completeActivation } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
2035
- const { config } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
2033
+ const client = _chunkN6VPGJ36cjs.useClient.call(void 0, );
2034
+ const { getActivationContext, completeActivation } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
2035
+ const { config } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
2036
2036
  const policy = resolvePolicy(config);
2037
2037
  const token = useUrlToken("token", tokenProp);
2038
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
2039
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
2038
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
2039
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
2040
2040
  const [context, setContext] = _react.useState.call(void 0, null);
2041
2041
  const [loadError, setLoadError] = _react.useState.call(void 0, null);
2042
2042
  const [password, setPassword] = _react.useState.call(void 0, "");
@@ -2106,7 +2106,7 @@ function ActivateAccountForm({
2106
2106
  {
2107
2107
  logo,
2108
2108
  title: heading,
2109
- subtitle: _chunkTYNZ2WU7cjs.fillNode.call(void 0,
2109
+ subtitle: _chunkN6VPGJ36cjs.fillNode.call(void 0,
2110
2110
  credentialsMode ? l10n.activate.credentialsSubtitle : l10n.activate.magicSubtitle,
2111
2111
  { email: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { children: context.email }) }
2112
2112
  )
@@ -2178,7 +2178,7 @@ function AuthFlow({
2178
2178
  logo,
2179
2179
  className
2180
2180
  }) {
2181
- const { config } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
2181
+ const { config } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
2182
2182
  const token = useUrlToken("token");
2183
2183
  const [internalStep, setInternalStep] = _react.useState.call(void 0, null);
2184
2184
  const go = (next) => {
@@ -2265,11 +2265,11 @@ function ChangePasswordForm({
2265
2265
  appearance,
2266
2266
  className
2267
2267
  }) {
2268
- const { updatePassword } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
2269
- const { config } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
2268
+ const { updatePassword } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
2269
+ const { config } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
2270
2270
  const policy = resolvePolicy(config);
2271
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
2272
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
2271
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
2272
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
2273
2273
  const heading = title === null ? null : _nullishCoalesce(title, () => ( l10n.security.submit));
2274
2274
  const [currentPassword, setCurrentPassword] = _react.useState.call(void 0, "");
2275
2275
  const [newPassword, setNewPassword] = _react.useState.call(void 0, "");
@@ -2375,10 +2375,10 @@ function WaitlistForm({
2375
2375
  logo,
2376
2376
  className
2377
2377
  }) {
2378
- const { join } = _chunkTYNZ2WU7cjs.useWaitlist.call(void 0, );
2379
- const { config } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
2380
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
2381
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
2378
+ const { join } = _chunkN6VPGJ36cjs.useWaitlist.call(void 0, );
2379
+ const { config } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
2380
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
2381
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
2382
2382
  const [email, setEmail] = _react.useState.call(void 0, "");
2383
2383
  const [name, setName] = _react.useState.call(void 0, "");
2384
2384
  const [submitting, setSubmitting] = _react.useState.call(void 0, false);
@@ -2459,8 +2459,8 @@ function SignOutButton({
2459
2459
  appearance
2460
2460
  }) {
2461
2461
  ensureStylesInjected();
2462
- const { logout } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
2463
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
2462
+ const { logout } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
2463
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
2464
2464
  const handle = () => {
2465
2465
  logout();
2466
2466
  _optionalChain([onSignedOut, 'optionalCall', _36 => _36()]);
@@ -2494,9 +2494,9 @@ function UserButton({
2494
2494
  className
2495
2495
  }) {
2496
2496
  ensureStylesInjected();
2497
- const { logout, isAuthenticated } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
2498
- const { user, fullName, initials, email } = _chunkTYNZ2WU7cjs.useUser.call(void 0, );
2499
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
2497
+ const { logout, isAuthenticated } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
2498
+ const { user, fullName, initials, email } = _chunkN6VPGJ36cjs.useUser.call(void 0, );
2499
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
2500
2500
  const [open, setOpen] = _react.useState.call(void 0, false);
2501
2501
  const wrapRef = _react.useRef.call(void 0, null);
2502
2502
  _react.useEffect.call(void 0, () => {
@@ -2587,10 +2587,10 @@ function UserButton({
2587
2587
 
2588
2588
 
2589
2589
  function ProfileSection({ verificationCallbackUrl, onSaved }) {
2590
- const { updateProfile, sendVerificationEmail, getVerificationStatus } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
2591
- const { user, fullName, initials, email } = _chunkTYNZ2WU7cjs.useUser.call(void 0, );
2592
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
2593
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
2590
+ const { updateProfile, sendVerificationEmail, getVerificationStatus } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
2591
+ const { user, fullName, initials, email } = _chunkN6VPGJ36cjs.useUser.call(void 0, );
2592
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
2593
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
2594
2594
  const [firstName, setFirstName] = _react.useState.call(void 0, "");
2595
2595
  const [lastName, setLastName] = _react.useState.call(void 0, "");
2596
2596
  const [saving, setSaving] = _react.useState.call(void 0, false);
@@ -2701,10 +2701,10 @@ function ProfileSection({ verificationCallbackUrl, onSaved }) {
2701
2701
 
2702
2702
 
2703
2703
  function SecuritySection({ showDeleteAccount = true, onDeleted }) {
2704
- const { requestAccountDeletion } = _chunkTYNZ2WU7cjs.useAuth.call(void 0, );
2705
- const { config } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
2706
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
2707
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
2704
+ const { requestAccountDeletion } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
2705
+ const { config } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
2706
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
2707
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
2708
2708
  const [error, setError] = _react.useState.call(void 0, null);
2709
2709
  const [confirmingDelete, setConfirmingDelete] = _react.useState.call(void 0, false);
2710
2710
  const [deleting, setDeleting] = _react.useState.call(void 0, false);
@@ -2886,9 +2886,9 @@ function UsageMeters({
2886
2886
  appearance,
2887
2887
  className
2888
2888
  }) {
2889
- const { entitlements, isLoading, error } = _chunkTYNZ2WU7cjs.useEntitlements.call(void 0, );
2890
- const { subscription } = _chunkTYNZ2WU7cjs.useSubscription.call(void 0, );
2891
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
2889
+ const { entitlements, isLoading, error } = _chunkN6VPGJ36cjs.useEntitlements.call(void 0, );
2890
+ const { subscription } = _chunkN6VPGJ36cjs.useSubscription.call(void 0, );
2891
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
2892
2892
  const heading = title === null ? null : _nullishCoalesce(title, () => ( l10n.usage.title));
2893
2893
  const nameFor = (key) => {
2894
2894
  if (_optionalChain([labels, 'optionalAccess', _56 => _56[key]])) return labels[key];
@@ -2939,7 +2939,7 @@ function UsageMeters({
2939
2939
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-meter", "data-level": level, children: [
2940
2940
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-meter-head", children: [
2941
2941
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-meter-name", children: nameFor(e.feature_key) }),
2942
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-meter-value", children: unlimited ? _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.usage.used, { count: formatNumber(e.used) }) : `${formatNumber(e.used)} / ${formatNumber(e.limit)}` })
2942
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-meter-value", children: unlimited ? _chunkN6VPGJ36cjs.fill.call(void 0, l10n.usage.used, { count: formatNumber(e.used) }) : `${formatNumber(e.used)} / ${formatNumber(e.limit)}` })
2943
2943
  ] }),
2944
2944
  !unlimited ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2945
2945
  "div",
@@ -2954,14 +2954,14 @@ function UsageMeters({
2954
2954
  }
2955
2955
  ) : null,
2956
2956
  over && softOverage ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
2957
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-meter-note", children: _chunkTYNZ2WU7cjs.fill.call(void 0,
2957
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-meter-note", children: _chunkN6VPGJ36cjs.fill.call(void 0,
2958
2958
  // capReached inverts overageBilled's meaning (billing stopped,
2959
2959
  // access didn't) - a variant string would say the opposite of
2960
2960
  // the truth for the one plan that just stopped costing money.
2961
2961
  capReached ? l10n.usage.capReached : billed ? l10n.usage.overageBilled : l10n.usage.overage,
2962
2962
  { count: formatNumber(e.used - e.limit) }
2963
2963
  ) }),
2964
- billed && _optionalChain([standing, 'optionalAccess', _65 => _65.billed_amount]) != null && standing.currency ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-meter-note kerne-meter-cost", children: _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.usage.billedAmount, {
2964
+ billed && _optionalChain([standing, 'optionalAccess', _65 => _65.billed_amount]) != null && standing.currency ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-meter-note kerne-meter-cost", children: _chunkN6VPGJ36cjs.fill.call(void 0, l10n.usage.billedAmount, {
2965
2965
  amount: formatMoney(standing.billed_amount, standing.currency)
2966
2966
  }) }) : null
2967
2967
  ] }) : over ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-meter-note", children: l10n.usage.limitReached }) : null
@@ -2992,11 +2992,11 @@ function SubscriptionCard({
2992
2992
  appearance,
2993
2993
  className
2994
2994
  }) {
2995
- const { subscription, isLoading, error, refetch } = _chunkTYNZ2WU7cjs.useSubscription.call(void 0, productSlug);
2996
- const { openPortal } = _chunkTYNZ2WU7cjs.usePortal.call(void 0, );
2997
- const { cancelSubscription, isCanceling } = _chunkTYNZ2WU7cjs.useCancelSubscription.call(void 0, );
2998
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
2999
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
2995
+ const { subscription, isLoading, error, refetch } = _chunkN6VPGJ36cjs.useSubscription.call(void 0, productSlug);
2996
+ const { openPortal } = _chunkN6VPGJ36cjs.usePortal.call(void 0, );
2997
+ const { cancelSubscription, isCanceling } = _chunkN6VPGJ36cjs.useCancelSubscription.call(void 0, );
2998
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
2999
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
3000
3000
  const heading = title === null ? null : _nullishCoalesce(title, () => ( l10n.billing.plan));
3001
3001
  const statusLabels = {
3002
3002
  ACTIVE: l10n.billing.statusActive,
@@ -3143,10 +3143,10 @@ function BillingSection({
3143
3143
  usageFeatureKeys,
3144
3144
  usageLabels
3145
3145
  }) {
3146
- const { subscription, refetch: refetchSubscription } = _chunkTYNZ2WU7cjs.useSubscription.call(void 0, productSlug);
3147
- const { plans, isLoading: plansLoading } = _chunkTYNZ2WU7cjs.usePlans.call(void 0, pricingProductIdOrSlug);
3148
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
3149
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
3146
+ const { subscription, refetch: refetchSubscription } = _chunkN6VPGJ36cjs.useSubscription.call(void 0, productSlug);
3147
+ const { plans, isLoading: plansLoading } = _chunkN6VPGJ36cjs.usePlans.call(void 0, pricingProductIdOrSlug);
3148
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
3149
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
3150
3150
  const {
3151
3151
  pending: pendingPlan,
3152
3152
  switching,
@@ -3156,7 +3156,7 @@ function BillingSection({
3156
3156
  selectPlan,
3157
3157
  confirmSwitch,
3158
3158
  dismissSwitch
3159
- } = _chunkTYNZ2WU7cjs.usePlanSwitch.call(void 0, subscription, refetchSubscription, {
3159
+ } = _chunkN6VPGJ36cjs.usePlanSwitch.call(void 0, subscription, refetchSubscription, {
3160
3160
  successUrl: checkoutSuccessUrl,
3161
3161
  cancelUrl: checkoutCancelUrl
3162
3162
  });
@@ -3167,7 +3167,7 @@ function BillingSection({
3167
3167
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
3168
3168
  error ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: showError(error) }) : null,
3169
3169
  pendingPlan ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Panel, { children: [
3170
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { children: _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.billing.switchConfirmTitle, { plan: pendingPlan.plan.name }) }),
3170
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { children: _chunkN6VPGJ36cjs.fill.call(void 0, l10n.billing.switchConfirmTitle, { plan: pendingPlan.plan.name }) }),
3171
3171
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-subsection-desc", children: l10n.billing.switchConfirmBody }),
3172
3172
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-inline-actions", children: [
3173
3173
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Button, { variant: "outline", type: "button", onClick: dismissSwitch, disabled: switching, children: l10n.billing.switchConfirmDismiss }),
@@ -3270,7 +3270,7 @@ function UserProfile({
3270
3270
  appearance,
3271
3271
  className
3272
3272
  }) {
3273
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
3273
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
3274
3274
  const builtins = {
3275
3275
  profile: {
3276
3276
  label: l10n.profile.tabProfile,
@@ -3380,10 +3380,10 @@ function PricingTable({
3380
3380
  className,
3381
3381
  children
3382
3382
  }) {
3383
- const { plans, isLoading, error } = _chunkTYNZ2WU7cjs.usePlans.call(void 0, product);
3384
- const { subscription, refetch: refetchSubscription } = _chunkTYNZ2WU7cjs.useSubscription.call(void 0, product);
3385
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
3386
- const showError = _chunkTYNZ2WU7cjs.useErrorResolver.call(void 0, );
3383
+ const { plans, isLoading, error } = _chunkN6VPGJ36cjs.usePlans.call(void 0, product);
3384
+ const { subscription, refetch: refetchSubscription } = _chunkN6VPGJ36cjs.useSubscription.call(void 0, product);
3385
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
3386
+ const showError = _chunkN6VPGJ36cjs.useErrorResolver.call(void 0, );
3387
3387
  const [selectedLabelSlug, setSelectedLabelSlug] = _react.useState.call(void 0, _nullishCoalesce(defaultLabel, () => ( null)));
3388
3388
  const {
3389
3389
  pending: pendingSwitch,
@@ -3394,7 +3394,7 @@ function PricingTable({
3394
3394
  selectPlan,
3395
3395
  confirmSwitch,
3396
3396
  dismissSwitch
3397
- } = _chunkTYNZ2WU7cjs.usePlanSwitch.call(void 0, subscription, refetchSubscription, {
3397
+ } = _chunkN6VPGJ36cjs.usePlanSwitch.call(void 0, subscription, refetchSubscription, {
3398
3398
  successUrl: checkoutSuccessUrl,
3399
3399
  cancelUrl: checkoutCancelUrl
3400
3400
  });
@@ -3427,7 +3427,7 @@ function PricingTable({
3427
3427
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Shell, { appearance, className, size: "wide", children: isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-pricing-skeleton", "aria-busy": "true", "aria-label": l10n.pricing.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-skeleton", style: { height: 320 } }) }) : error ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: l10n.pricing.loadFailed }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, PricingTableContext.Provider, { value: ctx, children: [
3428
3428
  checkoutError ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: showError(checkoutError) }) : null,
3429
3429
  pendingSwitch ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Panel, { children: [
3430
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { children: _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.billing.switchConfirmTitle, { plan: pendingSwitch.plan.name }) }),
3430
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { children: _chunkN6VPGJ36cjs.fill.call(void 0, l10n.billing.switchConfirmTitle, { plan: pendingSwitch.plan.name }) }),
3431
3431
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "kerne-subsection-desc", children: l10n.billing.switchConfirmBody }),
3432
3432
  switchError ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { variant: "danger", children: showError(switchError) }) : null,
3433
3433
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-inline-actions", children: [
@@ -3449,7 +3449,7 @@ function PricingTable({
3449
3449
  }
3450
3450
  function LabelSwitch() {
3451
3451
  const { labels, selectedLabelSlug, setSelectedLabelSlug, savings } = usePricingTable();
3452
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
3452
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
3453
3453
  const containerRef = _react.useRef.call(void 0, null);
3454
3454
  const buttonRefs = _react.useRef.call(void 0, /* @__PURE__ */ new Map());
3455
3455
  const [thumb, setThumb] = _react.useState.call(void 0, null);
@@ -3496,7 +3496,7 @@ function LabelSwitch() {
3496
3496
  onClick: () => setSelectedLabelSlug(label.slug),
3497
3497
  children: [
3498
3498
  label.name,
3499
- savings && savings.labelSlug === label.slug ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-pricing-switch-save", children: _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.pricing.save, { percent: savings.percent }) }) : null
3499
+ savings && savings.labelSlug === label.slug ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "kerne-pricing-switch-save", children: _chunkN6VPGJ36cjs.fill.call(void 0, l10n.pricing.save, { percent: savings.percent }) }) : null
3500
3500
  ]
3501
3501
  },
3502
3502
  label.slug
@@ -3536,7 +3536,7 @@ function useAnimatedAmount(target, durationMs = 420) {
3536
3536
  }
3537
3537
  function Plan({ slug, featured, badge, maxFeatures }) {
3538
3538
  const { plans, selectedLabelSlug, currentPlanId, busyPriceId, onSelectPlan } = usePricingTable();
3539
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
3539
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
3540
3540
  const plan = plans.find((p) => p.slug === slug);
3541
3541
  _react.useEffect.call(void 0, () => {
3542
3542
  if (!plan && process.env.NODE_ENV === "development") {
@@ -3553,7 +3553,7 @@ function Plan({ slug, featured, badge, maxFeatures }) {
3553
3553
  const isBusy = !!price && busyPriceId === price.id;
3554
3554
  const shownEntitlements = maxFeatures ? plan.entitlements.slice(0, maxFeatures) : plan.entitlements;
3555
3555
  const remaining = plan.entitlements.length - shownEntitlements.length;
3556
- const billedCaption = resolved && resolved.displayInterval !== resolved.billedInterval ? _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.pricing.billedAt, {
3556
+ const billedCaption = resolved && resolved.displayInterval !== resolved.billedInterval ? _chunkN6VPGJ36cjs.fill.call(void 0, l10n.pricing.billedAt, {
3557
3557
  amount: formatMoney(resolved.billedAmount, price.currency),
3558
3558
  interval: formatInterval(resolved.billedInterval, l10n.billing)
3559
3559
  }) : null;
@@ -3584,7 +3584,7 @@ function Plan({ slug, featured, badge, maxFeatures }) {
3584
3584
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, CheckIcon2, {}),
3585
3585
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: formatEntitlement(e, l10n.pricing.unlimited) })
3586
3586
  ] }, e.feature_key)),
3587
- remaining > 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "li", { className: "kerne-pricing-feature-more", children: _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.pricing.moreFeatures, { count: remaining }) }) : null
3587
+ remaining > 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "li", { className: "kerne-pricing-feature-more", children: _chunkN6VPGJ36cjs.fill.call(void 0, l10n.pricing.moreFeatures, { count: remaining }) }) : null
3588
3588
  ] }) : null,
3589
3589
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-pricing-cta", children: isCurrent ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Button, { variant: "outline", type: "button", disabled: true, children: l10n.billing.current }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3590
3590
  Button,
@@ -3650,9 +3650,9 @@ function CheckoutResult({
3650
3650
  logo,
3651
3651
  className
3652
3652
  }) {
3653
- const client = _chunkTYNZ2WU7cjs.useClient.call(void 0, );
3654
- const { config } = _chunkTYNZ2WU7cjs.useAuthConfig.call(void 0, );
3655
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
3653
+ const client = _chunkN6VPGJ36cjs.useClient.call(void 0, );
3654
+ const { config } = _chunkN6VPGJ36cjs.useAuthConfig.call(void 0, );
3655
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
3656
3656
  const [state, setState] = _react.useState.call(void 0, "checking");
3657
3657
  const confirmed = _react.useRef.call(void 0, false);
3658
3658
  _react.useEffect.call(void 0, () => {
@@ -3736,8 +3736,8 @@ function UpgradePrompt({
3736
3736
  appearance,
3737
3737
  className
3738
3738
  }) {
3739
- const { details, allowed, isLoading } = _chunkTYNZ2WU7cjs.useAccess.call(void 0, featureKey, requested);
3740
- const l10n = _chunkTYNZ2WU7cjs.useLocalization.call(void 0, );
3739
+ const { details, allowed, isLoading } = _chunkN6VPGJ36cjs.useAccess.call(void 0, featureKey, requested);
3740
+ const l10n = _chunkN6VPGJ36cjs.useLocalization.call(void 0, );
3741
3741
  if (isLoading) {
3742
3742
  if (hideWhileLoading) return null;
3743
3743
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Shell, { appearance, className, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "kerne-skeleton", style: { height: 80 }, "aria-label": l10n.upgrade.checking }) });
@@ -3745,14 +3745,14 @@ function UpgradePrompt({
3745
3745
  if (allowed) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children });
3746
3746
  const name = _nullishCoalesce(featureName, () => ( humanizeFeatureKey(featureKey).toLowerCase()));
3747
3747
  const quotaExhausted = _optionalChain([details, 'optionalAccess', _92 => _92.feature_type]) === "QUOTA" && typeof details.limit === "number" && typeof details.used === "number";
3748
- const heading = _nullishCoalesce(title, () => ( _chunkTYNZ2WU7cjs.fill.call(void 0, quotaExhausted ? l10n.upgrade.quotaTitle : l10n.upgrade.featureTitle, { feature: name })));
3749
- const body = _nullishCoalesce(description, () => ( (quotaExhausted ? _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.upgrade.quotaBody, {
3748
+ const heading = _nullishCoalesce(title, () => ( _chunkN6VPGJ36cjs.fill.call(void 0, quotaExhausted ? l10n.upgrade.quotaTitle : l10n.upgrade.featureTitle, { feature: name })));
3749
+ const body = _nullishCoalesce(description, () => ( (quotaExhausted ? _chunkN6VPGJ36cjs.fill.call(void 0, l10n.upgrade.quotaBody, {
3750
3750
  used: formatNumber(details.used),
3751
3751
  // `access_limit` is where access actually stops; `limit` is only the
3752
3752
  // included allocation and can understate it past an overage ceiling.
3753
3753
  // Falls back to `limit` defensively - should never be needed here.
3754
3754
  limit: formatNumber(_nullishCoalesce(details.access_limit, () => ( details.limit)))
3755
- }) : _chunkTYNZ2WU7cjs.fill.call(void 0, l10n.upgrade.featureBody, { feature: name }))));
3755
+ }) : _chunkN6VPGJ36cjs.fill.call(void 0, l10n.upgrade.featureBody, { feature: name }))));
3756
3756
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Shell, { appearance, className, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-status", children: [
3757
3757
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "kerne-heading", children: [
3758
3758
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: "kerne-section-title", style: { fontSize: 17 }, children: heading }),
@@ -3803,4 +3803,4 @@ function UpgradePrompt({
3803
3803
 
3804
3804
 
3805
3805
 
3806
- exports.ActivateAccountForm = ActivateAccountForm; exports.AuthFlow = AuthFlow; exports.BillingSection = BillingSection; exports.ChangePasswordForm = ChangePasswordForm; exports.CheckoutResult = CheckoutResult; exports.ForgotPasswordForm = ForgotPasswordForm; exports.LoginForm = LoginForm; exports.MagicLinkCallback = MagicLinkCallback; exports.PricingTable = PricingTable; exports.ProfileSection = ProfileSection; exports.RegisterForm = RegisterForm; exports.ResetPasswordForm = ResetPasswordForm; exports.SecuritySection = SecuritySection; exports.SignOutButton = SignOutButton; exports.SubscriptionCard = SubscriptionCard; exports.UpgradePrompt = UpgradePrompt; exports.UsageMeters = UsageMeters; exports.UserButton = UserButton; exports.UserProfile = UserProfile; exports.VerifyEmailForm = VerifyEmailForm; exports.WaitlistForm = WaitlistForm; exports.collectPriceLabels = collectPriceLabels; exports.defaultLocalization = _chunkTYNZ2WU7cjs.defaultLocalization; exports.formatDate = formatDate; exports.formatInterval = formatInterval; exports.formatMoney = formatMoney; exports.formatNumber = formatNumber; exports.humanizeFeatureKey = humanizeFeatureKey; exports.resolveLabelSavings = resolveLabelSavings; exports.resolvePriceDisplay = resolvePriceDisplay; exports.useLocalization = _chunkTYNZ2WU7cjs.useLocalization; exports.usePricingTable = usePricingTable;
3806
+ exports.ActivateAccountForm = ActivateAccountForm; exports.AuthFlow = AuthFlow; exports.BillingSection = BillingSection; exports.ChangePasswordForm = ChangePasswordForm; exports.CheckoutResult = CheckoutResult; exports.ForgotPasswordForm = ForgotPasswordForm; exports.LoginForm = LoginForm; exports.MagicLinkCallback = MagicLinkCallback; exports.PricingTable = PricingTable; exports.ProfileSection = ProfileSection; exports.RegisterForm = RegisterForm; exports.ResetPasswordForm = ResetPasswordForm; exports.SecuritySection = SecuritySection; exports.SignOutButton = SignOutButton; exports.SubscriptionCard = SubscriptionCard; exports.UpgradePrompt = UpgradePrompt; exports.UsageMeters = UsageMeters; exports.UserButton = UserButton; exports.UserProfile = UserProfile; exports.VerifyEmailForm = VerifyEmailForm; exports.WaitlistForm = WaitlistForm; exports.collectPriceLabels = collectPriceLabels; exports.defaultLocalization = _chunkN6VPGJ36cjs.defaultLocalization; exports.formatDate = formatDate; exports.formatInterval = formatInterval; exports.formatMoney = formatMoney; exports.formatNumber = formatNumber; exports.humanizeFeatureKey = humanizeFeatureKey; exports.resolveLabelSavings = resolveLabelSavings; exports.resolvePriceDisplay = resolvePriceDisplay; exports.useLocalization = _chunkN6VPGJ36cjs.useLocalization; exports.usePricingTable = usePricingTable;
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
3
  import { PublicPlan, PublicPriceLabel, PublicPlanPrice, SubscriptionWithPlan } from '@kerne/types';
4
- import { K as KerneLocalization } from '../i18n-QilNgwqe.cjs';
5
- export { d as defaultLocalization, u as useLocalization } from '../i18n-QilNgwqe.cjs';
4
+ import { K as KerneLocalization } from '../i18n-CXEhPK58.cjs';
5
+ export { d as defaultLocalization, u as useLocalization } from '../i18n-CXEhPK58.cjs';
6
6
 
7
7
  /**
8
8
  * Per-instance overrides of the same custom properties the stylesheet reads,
@@ -300,9 +300,9 @@ declare function SecuritySection({ showDeleteAccount, onDeleted }: SecuritySecti
300
300
  * lists what this scope can move to and starts a checkout, and deliberately
301
301
  * does not sell. `<PricingTable>` is the surface that sells.
302
302
  *
303
- * Interval handling is the honest minimum until PriceLabel exists (see
304
- * 34-ENTITLEMENT-ENGINE.md §9.2): prices are grouped by `interval`, and the
305
- * segmented control only renders when a plan genuinely has more than one. A
303
+ * Interval handling is the honest minimum until PriceLabel exists here too:
304
+ * prices are grouped by `interval`, and the segmented control only renders
305
+ * when a plan genuinely has more than one. A
306
306
  * plan carrying a third price (early-bird, non-profit) still has no way to be
307
307
  * described here - which is exactly the gap PriceLabel closes.
308
308
  */
@@ -377,7 +377,7 @@ declare function formatDate(iso: string | null): string | null;
377
377
  * Normalises the provider's interval wording, then localizes it.
378
378
  *
379
379
  * `PlanPrice.interval` is Kerne's own "N+cycle" encoding ("1M", "3M", "1Y",
380
- * "30D" - see apps/api's interval.ts), checked first since it's what every
380
+ * "30D"), checked first since it's what every
381
381
  * subscription/plan payload actually carries. The word-based fallback below
382
382
  * stays for any raw provider string passed directly (Stripe says `month`,
383
383
  * Polar says `monthly` - same thing to a reader). An interval matching
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
3
  import { PublicPlan, PublicPriceLabel, PublicPlanPrice, SubscriptionWithPlan } from '@kerne/types';
4
- import { K as KerneLocalization } from '../i18n-QilNgwqe.js';
5
- export { d as defaultLocalization, u as useLocalization } from '../i18n-QilNgwqe.js';
4
+ import { K as KerneLocalization } from '../i18n-CXEhPK58.js';
5
+ export { d as defaultLocalization, u as useLocalization } from '../i18n-CXEhPK58.js';
6
6
 
7
7
  /**
8
8
  * Per-instance overrides of the same custom properties the stylesheet reads,
@@ -300,9 +300,9 @@ declare function SecuritySection({ showDeleteAccount, onDeleted }: SecuritySecti
300
300
  * lists what this scope can move to and starts a checkout, and deliberately
301
301
  * does not sell. `<PricingTable>` is the surface that sells.
302
302
  *
303
- * Interval handling is the honest minimum until PriceLabel exists (see
304
- * 34-ENTITLEMENT-ENGINE.md §9.2): prices are grouped by `interval`, and the
305
- * segmented control only renders when a plan genuinely has more than one. A
303
+ * Interval handling is the honest minimum until PriceLabel exists here too:
304
+ * prices are grouped by `interval`, and the segmented control only renders
305
+ * when a plan genuinely has more than one. A
306
306
  * plan carrying a third price (early-bird, non-profit) still has no way to be
307
307
  * described here - which is exactly the gap PriceLabel closes.
308
308
  */
@@ -377,7 +377,7 @@ declare function formatDate(iso: string | null): string | null;
377
377
  * Normalises the provider's interval wording, then localizes it.
378
378
  *
379
379
  * `PlanPrice.interval` is Kerne's own "N+cycle" encoding ("1M", "3M", "1Y",
380
- * "30D" - see apps/api's interval.ts), checked first since it's what every
380
+ * "30D"), checked first since it's what every
381
381
  * subscription/plan payload actually carries. The word-based fallback below
382
382
  * stays for any raw provider string passed directly (Stripe says `month`,
383
383
  * Polar says `monthly` - same thing to a reader). An interval matching
package/dist/ui/index.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  useSubscription,
18
18
  useUser,
19
19
  useWaitlist
20
- } from "../chunk-IZMRK3WK.js";
20
+ } from "../chunk-IHNWY4ML.js";
21
21
 
22
22
  // src/ui/AuthFlow.tsx
23
23
  import { useState as useState10 } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerne/react",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Kerne React SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -32,8 +32,8 @@
32
32
  "dist"
33
33
  ],
34
34
  "dependencies": {
35
- "@kerne/server": "1.1.0",
36
- "@kerne/types": "1.1.0"
35
+ "@kerne/server": "1.2.0",
36
+ "@kerne/types": "1.2.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "react": "^18.0.0 || ^19.0.0"
@@ -41,19 +41,25 @@
41
41
  "devDependencies": {
42
42
  "@types/jest": "^29.5.12",
43
43
  "@types/react": "^18.0.0 || ^19.0.0",
44
+ "eslint": "^9.9.0",
45
+ "eslint-plugin-react-hooks": "^5.1.0-rc.0",
44
46
  "jest": "^29.7.0",
45
47
  "react": "^19.0.0",
46
48
  "ts-jest": "^29.2.5",
47
49
  "tsup": "^8.3.0",
48
- "typescript": "^5.7.0"
50
+ "typescript": "^5.7.0",
51
+ "typescript-eslint": "^8.0.1",
52
+ "eslint-plugin-kerne": "0.1.0"
49
53
  },
50
54
  "publishConfig": {
51
55
  "access": "public"
52
56
  },
53
57
  "scripts": {
54
- "build": "tsup",
58
+ "build": "tsup && pnpm check:dist-hygiene",
55
59
  "dev": "tsup --watch",
56
60
  "typecheck": "tsc --noEmit",
61
+ "lint": "eslint src",
62
+ "check:dist-hygiene": "node ./node_modules/eslint-plugin-kerne/check-dist-hygiene.js dist",
57
63
  "test": "jest"
58
64
  }
59
65
  }