@kerne/react 1.0.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.
- package/dist/{chunk-NFJ2S5VJ.js → chunk-IHNWY4ML.js} +108 -3
- package/dist/{chunk-IMZFZ3P5.cjs → chunk-N6VPGJ36.cjs} +115 -10
- package/dist/{i18n-Di5qlnL1.d.cts → i18n-CXEhPK58.d.cts} +6 -1
- package/dist/{i18n-Di5qlnL1.d.ts → i18n-CXEhPK58.d.ts} +6 -1
- package/dist/index.cjs +12 -8
- package/dist/index.d.cts +76 -6
- package/dist/index.d.ts +76 -6
- package/dist/index.js +5 -1
- package/dist/ui/index.cjs +220 -194
- package/dist/ui/index.d.cts +82 -10
- package/dist/ui/index.d.ts +82 -10
- package/dist/ui/index.js +110 -84
- package/package.json +11 -5
|
@@ -194,6 +194,11 @@ var defaultLocalization = {
|
|
|
194
194
|
changePlan: "Change plan",
|
|
195
195
|
current: "Current",
|
|
196
196
|
switchTo: "Switch",
|
|
197
|
+
switching: "Switching...",
|
|
198
|
+
switchConfirmTitle: "Switch to {plan}?",
|
|
199
|
+
switchConfirmBody: "This applies right away, and your next invoice reflects the price difference for the rest of this period.",
|
|
200
|
+
switchConfirmButton: "Yes, switch",
|
|
201
|
+
switchConfirmDismiss: "Never mind",
|
|
197
202
|
noPlans: "No other plans are available right now.",
|
|
198
203
|
notOnInterval: "Not available on this interval",
|
|
199
204
|
loadFailed: "Could not load your plan right now.",
|
|
@@ -373,6 +378,11 @@ var KerneClient = class {
|
|
|
373
378
|
baseUrl: this.baseUrl,
|
|
374
379
|
appId: this.appId,
|
|
375
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,
|
|
376
386
|
onUnauthorized: () => {
|
|
377
387
|
if (process.env.NODE_ENV === "development") {
|
|
378
388
|
console.warn("[Kerne] 401 received. Auto-logout disabled in dev.");
|
|
@@ -690,8 +700,9 @@ var KerneClient = class {
|
|
|
690
700
|
return response;
|
|
691
701
|
}
|
|
692
702
|
// Cross-Origin Session Handoff
|
|
693
|
-
// For
|
|
694
|
-
// a successful auth flow
|
|
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
|
|
695
706
|
// a plain `window.location.href = url` whenever `url` isn't same-origin -
|
|
696
707
|
// the exchange on the other end is picked up automatically by
|
|
697
708
|
// `exchangeHandoffOnLoad()`, no code required on the tenant app's side.
|
|
@@ -743,6 +754,24 @@ var KerneClient = class {
|
|
|
743
754
|
async check(featureKey, requested) {
|
|
744
755
|
return this.kerne.check(featureKey, { requested });
|
|
745
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
|
+
}
|
|
746
775
|
async createCheckout(planPriceId, options) {
|
|
747
776
|
const { url } = await this.kerne.billing.checkout(planPriceId, options);
|
|
748
777
|
return url;
|
|
@@ -925,7 +954,7 @@ function useAuth() {
|
|
|
925
954
|
(token2, password) => client.completeActivation(token2, password),
|
|
926
955
|
[client]
|
|
927
956
|
),
|
|
928
|
-
// Cross-origin handoff (
|
|
957
|
+
// Cross-origin handoff (hosted auth origin -> tenant app_url) - see redirectWithSession on KerneClient.
|
|
929
958
|
redirectWithSession: useCallback2((url) => client.redirectWithSession(url), [client])
|
|
930
959
|
};
|
|
931
960
|
}
|
|
@@ -981,6 +1010,63 @@ function useChangePlan() {
|
|
|
981
1010
|
);
|
|
982
1011
|
return { changePlan, isChanging };
|
|
983
1012
|
}
|
|
1013
|
+
function usePlanSwitch(subscription, refetchSubscription, options) {
|
|
1014
|
+
const { openCheckout } = useCheckout();
|
|
1015
|
+
const { changePlan } = useChangePlan();
|
|
1016
|
+
const [pending, setPending] = useState(null);
|
|
1017
|
+
const [switching, setSwitching] = useState(false);
|
|
1018
|
+
const [switchError, setSwitchError] = useState(null);
|
|
1019
|
+
const [checkoutPriceId, setCheckoutPriceId] = useState(null);
|
|
1020
|
+
const [checkoutError, setCheckoutError] = useState(null);
|
|
1021
|
+
const successUrl = options?.successUrl;
|
|
1022
|
+
const cancelUrl = options?.cancelUrl;
|
|
1023
|
+
const selectPlan = useCallback2(
|
|
1024
|
+
async (plan, price) => {
|
|
1025
|
+
if (subscription) {
|
|
1026
|
+
setSwitchError(null);
|
|
1027
|
+
setPending({ plan, price });
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
setCheckoutError(null);
|
|
1031
|
+
setCheckoutPriceId(price.id);
|
|
1032
|
+
try {
|
|
1033
|
+
await openCheckout(price.id, { successUrl, cancelUrl });
|
|
1034
|
+
} catch (e) {
|
|
1035
|
+
setCheckoutError(e);
|
|
1036
|
+
setCheckoutPriceId(null);
|
|
1037
|
+
}
|
|
1038
|
+
},
|
|
1039
|
+
[subscription, openCheckout, successUrl, cancelUrl]
|
|
1040
|
+
);
|
|
1041
|
+
const confirmSwitch = useCallback2(async () => {
|
|
1042
|
+
if (!pending || !subscription) return;
|
|
1043
|
+
setSwitching(true);
|
|
1044
|
+
setSwitchError(null);
|
|
1045
|
+
try {
|
|
1046
|
+
await changePlan(subscription.id, pending.price.id);
|
|
1047
|
+
setPending(null);
|
|
1048
|
+
refetchSubscription();
|
|
1049
|
+
} catch (e) {
|
|
1050
|
+
setSwitchError(e);
|
|
1051
|
+
} finally {
|
|
1052
|
+
setSwitching(false);
|
|
1053
|
+
}
|
|
1054
|
+
}, [pending, subscription, changePlan, refetchSubscription]);
|
|
1055
|
+
const dismissSwitch = useCallback2(() => {
|
|
1056
|
+
setPending(null);
|
|
1057
|
+
setSwitchError(null);
|
|
1058
|
+
}, []);
|
|
1059
|
+
return {
|
|
1060
|
+
pending,
|
|
1061
|
+
switching,
|
|
1062
|
+
switchError,
|
|
1063
|
+
checkoutPriceId,
|
|
1064
|
+
checkoutError,
|
|
1065
|
+
selectPlan,
|
|
1066
|
+
confirmSwitch,
|
|
1067
|
+
dismissSwitch
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
984
1070
|
function useAccess(featureKey, requested) {
|
|
985
1071
|
const client = useClient();
|
|
986
1072
|
const [state, setState] = useState({
|
|
@@ -1145,6 +1231,23 @@ function useUsage(featureKey) {
|
|
|
1145
1231
|
}, [client, featureKey]);
|
|
1146
1232
|
return state;
|
|
1147
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
|
+
}
|
|
1148
1251
|
function useWaitlist() {
|
|
1149
1252
|
const client = useClient();
|
|
1150
1253
|
return {
|
|
@@ -1210,12 +1313,14 @@ export {
|
|
|
1210
1313
|
usePortal,
|
|
1211
1314
|
useCancelSubscription,
|
|
1212
1315
|
useChangePlan,
|
|
1316
|
+
usePlanSwitch,
|
|
1213
1317
|
useAccess,
|
|
1214
1318
|
useSubscription,
|
|
1215
1319
|
usePlans,
|
|
1216
1320
|
useAuthConfig,
|
|
1217
1321
|
useEntitlements,
|
|
1218
1322
|
useUsage,
|
|
1323
|
+
useReportUsage,
|
|
1219
1324
|
useWaitlist,
|
|
1220
1325
|
useInvitation,
|
|
1221
1326
|
useUser
|
|
@@ -194,6 +194,11 @@ var defaultLocalization = {
|
|
|
194
194
|
changePlan: "Change plan",
|
|
195
195
|
current: "Current",
|
|
196
196
|
switchTo: "Switch",
|
|
197
|
+
switching: "Switching...",
|
|
198
|
+
switchConfirmTitle: "Switch to {plan}?",
|
|
199
|
+
switchConfirmBody: "This applies right away, and your next invoice reflects the price difference for the rest of this period.",
|
|
200
|
+
switchConfirmButton: "Yes, switch",
|
|
201
|
+
switchConfirmDismiss: "Never mind",
|
|
197
202
|
noPlans: "No other plans are available right now.",
|
|
198
203
|
notOnInterval: "Not available on this interval",
|
|
199
204
|
loadFailed: "Could not load your plan right now.",
|
|
@@ -373,6 +378,11 @@ var KerneClient = (_class = class {
|
|
|
373
378
|
baseUrl: this.baseUrl,
|
|
374
379
|
appId: this.appId,
|
|
375
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,
|
|
376
386
|
onUnauthorized: () => {
|
|
377
387
|
if (process.env.NODE_ENV === "development") {
|
|
378
388
|
console.warn("[Kerne] 401 received. Auto-logout disabled in dev.");
|
|
@@ -690,8 +700,9 @@ var KerneClient = (_class = class {
|
|
|
690
700
|
return response;
|
|
691
701
|
}
|
|
692
702
|
// Cross-Origin Session Handoff
|
|
693
|
-
// For
|
|
694
|
-
// a successful auth flow
|
|
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
|
|
695
706
|
// a plain `window.location.href = url` whenever `url` isn't same-origin -
|
|
696
707
|
// the exchange on the other end is picked up automatically by
|
|
697
708
|
// `exchangeHandoffOnLoad()`, no code required on the tenant app's side.
|
|
@@ -743,6 +754,24 @@ var KerneClient = (_class = class {
|
|
|
743
754
|
async check(featureKey, requested) {
|
|
744
755
|
return this.kerne.check(featureKey, { requested });
|
|
745
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
|
+
}
|
|
746
775
|
async createCheckout(planPriceId, options) {
|
|
747
776
|
const { url } = await this.kerne.billing.checkout(planPriceId, options);
|
|
748
777
|
return url;
|
|
@@ -824,7 +853,7 @@ function KerneProvider({ children, localization, ...config }) {
|
|
|
824
853
|
}, [config.appId, config.baseUrl]);
|
|
825
854
|
_react.useEffect.call(void 0, () => {
|
|
826
855
|
return () => {
|
|
827
|
-
_optionalChain([client, 'access',
|
|
856
|
+
_optionalChain([client, 'access', _23 => _23.cancelRefresh, 'optionalCall', _24 => _24()]);
|
|
828
857
|
};
|
|
829
858
|
}, [client]);
|
|
830
859
|
const l10n = _react.useMemo.call(void 0, () => mergeLocalization(localization), [localization]);
|
|
@@ -925,7 +954,7 @@ function useAuth() {
|
|
|
925
954
|
(token2, password) => client.completeActivation(token2, password),
|
|
926
955
|
[client]
|
|
927
956
|
),
|
|
928
|
-
// Cross-origin handoff (
|
|
957
|
+
// Cross-origin handoff (hosted auth origin -> tenant app_url) - see redirectWithSession on KerneClient.
|
|
929
958
|
redirectWithSession: _react.useCallback.call(void 0, (url) => client.redirectWithSession(url), [client])
|
|
930
959
|
};
|
|
931
960
|
}
|
|
@@ -981,6 +1010,63 @@ function useChangePlan() {
|
|
|
981
1010
|
);
|
|
982
1011
|
return { changePlan, isChanging };
|
|
983
1012
|
}
|
|
1013
|
+
function usePlanSwitch(subscription, refetchSubscription, options) {
|
|
1014
|
+
const { openCheckout } = useCheckout();
|
|
1015
|
+
const { changePlan } = useChangePlan();
|
|
1016
|
+
const [pending, setPending] = _react.useState.call(void 0, null);
|
|
1017
|
+
const [switching, setSwitching] = _react.useState.call(void 0, false);
|
|
1018
|
+
const [switchError, setSwitchError] = _react.useState.call(void 0, null);
|
|
1019
|
+
const [checkoutPriceId, setCheckoutPriceId] = _react.useState.call(void 0, null);
|
|
1020
|
+
const [checkoutError, setCheckoutError] = _react.useState.call(void 0, null);
|
|
1021
|
+
const successUrl = _optionalChain([options, 'optionalAccess', _25 => _25.successUrl]);
|
|
1022
|
+
const cancelUrl = _optionalChain([options, 'optionalAccess', _26 => _26.cancelUrl]);
|
|
1023
|
+
const selectPlan = _react.useCallback.call(void 0,
|
|
1024
|
+
async (plan, price) => {
|
|
1025
|
+
if (subscription) {
|
|
1026
|
+
setSwitchError(null);
|
|
1027
|
+
setPending({ plan, price });
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
setCheckoutError(null);
|
|
1031
|
+
setCheckoutPriceId(price.id);
|
|
1032
|
+
try {
|
|
1033
|
+
await openCheckout(price.id, { successUrl, cancelUrl });
|
|
1034
|
+
} catch (e) {
|
|
1035
|
+
setCheckoutError(e);
|
|
1036
|
+
setCheckoutPriceId(null);
|
|
1037
|
+
}
|
|
1038
|
+
},
|
|
1039
|
+
[subscription, openCheckout, successUrl, cancelUrl]
|
|
1040
|
+
);
|
|
1041
|
+
const confirmSwitch = _react.useCallback.call(void 0, async () => {
|
|
1042
|
+
if (!pending || !subscription) return;
|
|
1043
|
+
setSwitching(true);
|
|
1044
|
+
setSwitchError(null);
|
|
1045
|
+
try {
|
|
1046
|
+
await changePlan(subscription.id, pending.price.id);
|
|
1047
|
+
setPending(null);
|
|
1048
|
+
refetchSubscription();
|
|
1049
|
+
} catch (e) {
|
|
1050
|
+
setSwitchError(e);
|
|
1051
|
+
} finally {
|
|
1052
|
+
setSwitching(false);
|
|
1053
|
+
}
|
|
1054
|
+
}, [pending, subscription, changePlan, refetchSubscription]);
|
|
1055
|
+
const dismissSwitch = _react.useCallback.call(void 0, () => {
|
|
1056
|
+
setPending(null);
|
|
1057
|
+
setSwitchError(null);
|
|
1058
|
+
}, []);
|
|
1059
|
+
return {
|
|
1060
|
+
pending,
|
|
1061
|
+
switching,
|
|
1062
|
+
switchError,
|
|
1063
|
+
checkoutPriceId,
|
|
1064
|
+
checkoutError,
|
|
1065
|
+
selectPlan,
|
|
1066
|
+
confirmSwitch,
|
|
1067
|
+
dismissSwitch
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
984
1070
|
function useAccess(featureKey, requested) {
|
|
985
1071
|
const client = useClient();
|
|
986
1072
|
const [state, setState] = _react.useState.call(void 0, {
|
|
@@ -1040,8 +1126,8 @@ function useSubscription(productSlug) {
|
|
|
1040
1126
|
setState((prev) => ({ ...prev, isLoading: true }));
|
|
1041
1127
|
fetchOnce({ current: true });
|
|
1042
1128
|
}, [fetchOnce]);
|
|
1043
|
-
const isActive = _optionalChain([state, 'access',
|
|
1044
|
-
const planSlug = _optionalChain([state, 'access',
|
|
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]);
|
|
1045
1131
|
return {
|
|
1046
1132
|
...state,
|
|
1047
1133
|
isActive,
|
|
@@ -1145,6 +1231,23 @@ function useUsage(featureKey) {
|
|
|
1145
1231
|
}, [client, featureKey]);
|
|
1146
1232
|
return state;
|
|
1147
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
|
+
}
|
|
1148
1251
|
function useWaitlist() {
|
|
1149
1252
|
const client = useClient();
|
|
1150
1253
|
return {
|
|
@@ -1189,9 +1292,9 @@ function useUser() {
|
|
|
1189
1292
|
update: updateProfile,
|
|
1190
1293
|
// Computed properties
|
|
1191
1294
|
fullName: user ? `${user.first_name || ""} ${user.last_name || ""}`.trim() : null,
|
|
1192
|
-
initials: user ? `${_optionalChain([user, 'access',
|
|
1193
|
-
email: _optionalChain([user, 'optionalAccess',
|
|
1194
|
-
emailVerified: _optionalChain([user, 'optionalAccess',
|
|
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
|
|
1195
1298
|
};
|
|
1196
1299
|
}
|
|
1197
1300
|
|
|
@@ -1219,4 +1322,6 @@ function useUser() {
|
|
|
1219
1322
|
|
|
1220
1323
|
|
|
1221
1324
|
|
|
1222
|
-
|
|
1325
|
+
|
|
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;
|
|
@@ -195,6 +195,11 @@ interface KerneLocalization {
|
|
|
195
195
|
changePlan: string;
|
|
196
196
|
current: string;
|
|
197
197
|
switchTo: string;
|
|
198
|
+
switching: string;
|
|
199
|
+
switchConfirmTitle: string;
|
|
200
|
+
switchConfirmBody: string;
|
|
201
|
+
switchConfirmButton: string;
|
|
202
|
+
switchConfirmDismiss: string;
|
|
198
203
|
noPlans: string;
|
|
199
204
|
notOnInterval: string;
|
|
200
205
|
loadFailed: string;
|
|
@@ -268,7 +273,7 @@ interface KerneLocalization {
|
|
|
268
273
|
signOut: string;
|
|
269
274
|
};
|
|
270
275
|
/**
|
|
271
|
-
* Keyed by the API's stable ErrorCode
|
|
276
|
+
* Keyed by the API's stable ErrorCode.
|
|
272
277
|
* Anything absent falls back to `generic` - never to the raw server message,
|
|
273
278
|
* which is written for an integrator and is always English.
|
|
274
279
|
*/
|
|
@@ -195,6 +195,11 @@ interface KerneLocalization {
|
|
|
195
195
|
changePlan: string;
|
|
196
196
|
current: string;
|
|
197
197
|
switchTo: string;
|
|
198
|
+
switching: string;
|
|
199
|
+
switchConfirmTitle: string;
|
|
200
|
+
switchConfirmBody: string;
|
|
201
|
+
switchConfirmButton: string;
|
|
202
|
+
switchConfirmDismiss: string;
|
|
198
203
|
noPlans: string;
|
|
199
204
|
notOnInterval: string;
|
|
200
205
|
loadFailed: string;
|
|
@@ -268,7 +273,7 @@ interface KerneLocalization {
|
|
|
268
273
|
signOut: string;
|
|
269
274
|
};
|
|
270
275
|
/**
|
|
271
|
-
* Keyed by the API's stable ErrorCode
|
|
276
|
+
* Keyed by the API's stable ErrorCode.
|
|
272
277
|
* Anything absent falls back to `generic` - never to the raw server message,
|
|
273
278
|
* which is written for an integrator and is always English.
|
|
274
279
|
*/
|
package/dist/index.cjs
CHANGED
|
@@ -19,25 +19,27 @@
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
var _chunkN6VPGJ36cjs = require('./chunk-N6VPGJ36.cjs');
|
|
23
25
|
|
|
24
26
|
// src/components.tsx
|
|
25
27
|
var _react = require('react'); var _react2 = _interopRequireDefault(_react);
|
|
26
28
|
var _jsxruntime = require('react/jsx-runtime');
|
|
27
29
|
function Authenticated({ children, fallback = null }) {
|
|
28
|
-
const { isAuthenticated } =
|
|
30
|
+
const { isAuthenticated } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
|
|
29
31
|
return isAuthenticated ? children : fallback;
|
|
30
32
|
}
|
|
31
33
|
function Unauthenticated({ children, fallback = null }) {
|
|
32
|
-
const { isAuthenticated } =
|
|
34
|
+
const { isAuthenticated } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
|
|
33
35
|
return !isAuthenticated ? children : fallback;
|
|
34
36
|
}
|
|
35
37
|
function AuthLoading({ children }) {
|
|
36
|
-
const client =
|
|
38
|
+
const client = _chunkN6VPGJ36cjs.useClient.call(void 0, );
|
|
37
39
|
return client.isLoading ? children : null;
|
|
38
40
|
}
|
|
39
41
|
function HasSubscription({ children, fallback = null }) {
|
|
40
|
-
const client =
|
|
42
|
+
const client = _chunkN6VPGJ36cjs.useClient.call(void 0, );
|
|
41
43
|
const [hasSubscription, setHasSubscription] = _react2.default.useState(null);
|
|
42
44
|
_react2.default.useEffect(() => {
|
|
43
45
|
client.getSubscription().then((sub) => {
|
|
@@ -55,7 +57,7 @@ function Access({
|
|
|
55
57
|
requested,
|
|
56
58
|
fallback = null
|
|
57
59
|
}) {
|
|
58
|
-
const { allowed, isLoading } =
|
|
60
|
+
const { allowed, isLoading } = _chunkN6VPGJ36cjs.useAccess.call(void 0, featureKey, requested);
|
|
59
61
|
if (isLoading) return null;
|
|
60
62
|
return allowed ? children : fallback;
|
|
61
63
|
}
|
|
@@ -66,7 +68,7 @@ function Protected({
|
|
|
66
68
|
authFallback = null,
|
|
67
69
|
billingFallback = null
|
|
68
70
|
}) {
|
|
69
|
-
const { isAuthenticated } =
|
|
71
|
+
const { isAuthenticated } = _chunkN6VPGJ36cjs.useAuth.call(void 0, );
|
|
70
72
|
if (auth && !isAuthenticated) {
|
|
71
73
|
return authFallback;
|
|
72
74
|
}
|
|
@@ -179,4 +181,6 @@ function useKerneError() {
|
|
|
179
181
|
|
|
180
182
|
|
|
181
183
|
|
|
182
|
-
|
|
184
|
+
|
|
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,
|
|
7
|
-
export { ActivationContext, AuthConfig, AuthResponse, EntitlementCheck, Entitlements, PublicPlan, Subscription, SubscriptionWithPlan, UsageRecord, User } from '@kerne/types';
|
|
8
|
-
import { D as DeepPartial, K as KerneLocalization } from './i18n-
|
|
9
|
-
export { d as defaultLocalization } from './i18n-
|
|
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;
|
|
@@ -344,6 +351,51 @@ declare function useChangePlan(): {
|
|
|
344
351
|
}) => Promise<SubscriptionWithPlan>;
|
|
345
352
|
isChanging: boolean;
|
|
346
353
|
};
|
|
354
|
+
interface PlanSwitchTarget {
|
|
355
|
+
plan: PublicPlan;
|
|
356
|
+
price: PublicPlanPrice;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* The decision every "pick a plan" surface needs, shared so it is only
|
|
360
|
+
* decided once: an existing subscriber switching plans has to swap in place
|
|
361
|
+
* (`changePlanPrice`), never start a fresh checkout - Stripe/Polar don't know
|
|
362
|
+
* about the subscription already on file, so a new checkout session creates
|
|
363
|
+
* a SECOND, independent one instead of replacing it. The old one stays live
|
|
364
|
+
* and billed at the provider while Kerne only ever tracks the new one. A
|
|
365
|
+
* first-time subscriber has nothing to swap, so that case goes straight to
|
|
366
|
+
* checkout, same as before.
|
|
367
|
+
*
|
|
368
|
+
* The switch is staged rather than applied on `selectPlan` alone - it bills
|
|
369
|
+
* immediately on confirmation (unlike checkout, which only starts billing
|
|
370
|
+
* once the provider's own page is completed), so the caller's UI gets a
|
|
371
|
+
* chance to say so before `confirmSwitch()` commits it.
|
|
372
|
+
*
|
|
373
|
+
* @param subscription the subject's current subscription, or null - pass
|
|
374
|
+
* what `useSubscription()` already returned rather than fetching it again
|
|
375
|
+
* here, so a page showing the current plan and this hook always agree.
|
|
376
|
+
* @param refetchSubscription called after a successful switch, typically
|
|
377
|
+
* that same `useSubscription()` call's own `refetch()`.
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* ```tsx
|
|
381
|
+
* const { subscription, refetch } = useSubscription();
|
|
382
|
+
* const { pending, switching, selectPlan, confirmSwitch, dismissSwitch } =
|
|
383
|
+
* usePlanSwitch(subscription, refetch);
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
declare function usePlanSwitch(subscription: SubscriptionWithPlan | null, refetchSubscription: () => void, options?: {
|
|
387
|
+
successUrl?: string;
|
|
388
|
+
cancelUrl?: string;
|
|
389
|
+
}): {
|
|
390
|
+
pending: PlanSwitchTarget | null;
|
|
391
|
+
switching: boolean;
|
|
392
|
+
switchError: unknown;
|
|
393
|
+
checkoutPriceId: string | null;
|
|
394
|
+
checkoutError: unknown;
|
|
395
|
+
selectPlan: (plan: PublicPlan, price: PublicPlanPrice) => Promise<void>;
|
|
396
|
+
confirmSwitch: () => Promise<void>;
|
|
397
|
+
dismissSwitch: () => void;
|
|
398
|
+
};
|
|
347
399
|
/**
|
|
348
400
|
* Whether the current scope has access to one feature - the single entry
|
|
349
401
|
* point for a live, per-feature decision. Deliberately not named after what
|
|
@@ -450,6 +502,24 @@ declare function useUsage(featureKey?: string): {
|
|
|
450
502
|
isLoading: boolean;
|
|
451
503
|
error: Error | null;
|
|
452
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
|
+
};
|
|
453
523
|
/**
|
|
454
524
|
* Waitlist signup form - a mutation, not a data fetch, same shape as
|
|
455
525
|
* `useCheckout`/`usePortal`.
|
|
@@ -691,4 +761,4 @@ declare function useKerneError(): {
|
|
|
691
761
|
clearError: () => void;
|
|
692
762
|
};
|
|
693
763
|
|
|
694
|
-
export { Access, AuthLoading, Authenticated, HasSubscription, KerneClient, KerneContext, type KerneError, KerneErrorBoundary, KerneLocalization, KerneProvider, type KerneProviderProps, type KerneReactConfig, Protected, Unauthenticated, useAccess, useAuth, useAuthConfig, useCancelSubscription, useChangePlan, useCheckout, useClient, useEntitlements, useInvitation, useKerneError, usePlans, usePortal, useSubscription, useUsage, useUser, useWaitlist };
|
|
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 };
|