@lukoweb/apitogo-plugin-dev-portal-billing 0.2.56 → 0.2.57

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.
@@ -1,9 +1,9 @@
1
- import { f as parseManifestPlans, n as useDevPortalPreviewMode, r as useIsDemoSession, s as isActiveSubscription, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
2
- import { t as PricingCard } from "./PricingCard-DFuQEw7Y.mjs";
1
+ import { f as parseManifestPlans, n as useDevPortalPreviewMode, r as useIsDemoSession, s as isActiveSubscription, t as useDevPortalApiBaseUrl, y as withPlanFeatures } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
2
+ import { t as PricingCard } from "./PricingCard-CRlUGh0k.mjs";
3
3
  import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
4
4
  import { n as readProblemDetail } from "./problem-details-n20-dy3w.mjs";
5
- import { t as useMySubscriptions } from "./useMySubscriptions-BYVIIWbe.mjs";
6
- import { t as usePlans } from "./usePlans-HyhIIuJZ.mjs";
5
+ import { t as useMySubscriptions } from "./useMySubscriptions-C1UMb8rJ.mjs";
6
+ import { t as usePlans } from "./usePlans-D_Khso7B.mjs";
7
7
  import { n as readPendingCheckout, t as clearPendingCheckout } from "./pending-checkout-DpVaNnTu.mjs";
8
8
  import { Head } from "@lukoweb/apitogo/components";
9
9
  import { CreditCardIcon } from "@lukoweb/apitogo/icons";
@@ -60,11 +60,11 @@ var AccountPlansPage_exports = /* @__PURE__ */ __exportAll({ default: () => Acco
60
60
  const AccountPlansPage = () => {
61
61
  const [searchParams] = useSearchParams();
62
62
  const checkoutCancelled = searchParams.get("checkout") === "cancelled";
63
- const { previewPlans } = useDevPortalBillingConfig();
63
+ const { previewPlans, planFeatures } = useDevPortalBillingConfig();
64
64
  const isPreview = useDevPortalPreviewMode();
65
65
  const isDemoSession = useIsDemoSession();
66
66
  const livePlans = usePlans();
67
- const catalog = isPreview ? parseManifestPlans(previewPlans) : livePlans.data;
67
+ const catalog = isPreview ? withPlanFeatures(parseManifestPlans(previewPlans), planFeatures) : livePlans.data;
68
68
  const isLoading = isPreview ? false : livePlans.isLoading;
69
69
  const isError = isPreview ? false : livePlans.isError;
70
70
  const { data: me } = useMySubscriptions();
@@ -1,8 +1,8 @@
1
1
  import { n as ALREADY_SUBSCRIBED_PARAM, s as PRICING_PATH, t as ACCOUNT_PLANS_PATH } from "./paths-DaVyTIDJ.mjs";
2
- import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
2
+ import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
3
3
  import { n as readProblemDetail, t as isAlreadySubscribedMessage } from "./problem-details-n20-dy3w.mjs";
4
4
  import { r as rememberPendingCheckout } from "./pending-checkout-DpVaNnTu.mjs";
5
- import { t as FREE_CHECKOUT_MARKER } from "./billing-state-DBQR2tad.mjs";
5
+ import { t as FREE_CHECKOUT_MARKER } from "./billing-state-CJmy96zI.mjs";
6
6
  import { ShieldIcon } from "@lukoweb/apitogo/icons";
7
7
  import { Link, useNavigate, useSearchParams } from "@lukoweb/apitogo/router";
8
8
  import { useEffect } from "react";
@@ -1,8 +1,8 @@
1
1
  import { s as PRICING_PATH } from "./paths-DaVyTIDJ.mjs";
2
- import { l as isPlanCustomPriced, n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
2
+ import { l as isPlanCustomPriced, n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
3
3
  import "./DevPortalBillingContext-CR83s4LO.mjs";
4
4
  import { n as readProblemDetail } from "./problem-details-n20-dy3w.mjs";
5
- import { t as usePlans } from "./usePlans-HyhIIuJZ.mjs";
5
+ import { t as usePlans } from "./usePlans-D_Khso7B.mjs";
6
6
  import { Link, useSearchParams } from "@lukoweb/apitogo/router";
7
7
  import { useState } from "react";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,9 +1,9 @@
1
1
  import { t as ACCOUNT_PLANS_PATH } from "./paths-DaVyTIDJ.mjs";
2
- import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
2
+ import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
3
3
  import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
4
4
  import { r as createDemoCredentials, t as DEMO_GATEWAY_KEY_HEADER } from "./demo-session-yK9qEqV-.mjs";
5
- import { n as useApiKeys } from "./useApiKeys-BOY3vIOr.mjs";
6
- import { n as apiReferencePath, r as ApiKeysPanel } from "./account-links-Drofs-f-.mjs";
5
+ import { n as useApiKeys } from "./useApiKeys-CnyxipLc.mjs";
6
+ import { n as apiReferencePath, r as ApiKeysPanel } from "./account-links-CpTOzb2V.mjs";
7
7
  import { i as ChoosePlanNotice, r as keyPlanNote, t as apiKeysPageState } from "./page-state-BLL2JJBc.mjs";
8
8
  import { Head } from "@lukoweb/apitogo/components";
9
9
  import { KeyRoundIcon } from "@lukoweb/apitogo/icons";
@@ -1,5 +1,5 @@
1
1
  import { i as CONTACT_SALES_PATH, r as CHECKOUT_PATH } from "./paths-DaVyTIDJ.mjs";
2
- import { d as listPlanFeatureLines, i as formatPlanDisplayPrice, l as isPlanCustomPriced, o as formatPlanPriceUnit, s as isActiveSubscription, u as isPlanFree } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
2
+ import { d as listPlanFeatureLines, i as formatPlanDisplayPrice, l as isPlanCustomPriced, o as formatPlanPriceUnit, s as isActiveSubscription, u as isPlanFree } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
3
3
  import { Button } from "@lukoweb/apitogo/components";
4
4
  import { Link as Link$1 } from "@lukoweb/apitogo/router";
5
5
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@lukoweb/apitogo/ui/AlertDialog.js";
@@ -1,9 +1,9 @@
1
- import { f as parseManifestPlans, h as pickFeaturedPlanSku, n as useDevPortalPreviewMode } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
2
- import { t as PricingCard } from "./PricingCard-DFuQEw7Y.mjs";
1
+ import { f as parseManifestPlans, h as pickFeaturedPlanSku, n as useDevPortalPreviewMode, y as withPlanFeatures } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
2
+ import { t as PricingCard } from "./PricingCard-CRlUGh0k.mjs";
3
3
  import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
4
- import { t as usePlans } from "./usePlans-HyhIIuJZ.mjs";
4
+ import { t as usePlans } from "./usePlans-D_Khso7B.mjs";
5
5
  import { t as isPreviewPricingEnabled } from "./pricing-config-Bc6eWFUO.mjs";
6
- import { a as isPricingLoading, n as buildCompareRows } from "./billing-state-DBQR2tad.mjs";
6
+ import { a as isPricingLoading, n as buildCompareRows } from "./billing-state-CJmy96zI.mjs";
7
7
  import { Head, Heading } from "@lukoweb/apitogo/components";
8
8
  import { Navigate } from "@lukoweb/apitogo/router";
9
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -75,11 +75,11 @@ const DEFAULT_FAQS = [{
75
75
  answer: "Yes. Each plan has its own API keys and limits: create a key for a plan on the API keys page and use it in your app."
76
76
  }];
77
77
  const PricingPage = () => {
78
- const { pricing, previewPlans, featuredPlanSku } = useDevPortalBillingConfig();
78
+ const { pricing, previewPlans, featuredPlanSku, planFeatures } = useDevPortalBillingConfig();
79
79
  const isPreview = useDevPortalPreviewMode();
80
80
  const pricingEnabled = isPreviewPricingEnabled() || isPreview;
81
81
  const livePlans = usePlans();
82
- const catalog = isPreview ? parseManifestPlans(previewPlans) : livePlans.data;
82
+ const catalog = isPreview ? withPlanFeatures(parseManifestPlans(previewPlans), planFeatures) : livePlans.data;
83
83
  const { isPending: authPending } = useAuth();
84
84
  const isLoading = isPricingLoading({
85
85
  isPreview,
@@ -1,12 +1,12 @@
1
1
  import { a as CREDENTIALS_PATH, n as ALREADY_SUBSCRIBED_PARAM, t as ACCOUNT_PLANS_PATH } from "./paths-DaVyTIDJ.mjs";
2
- import { a as formatPlanPrice, p as parseMonthlyQuotaCalls, s as isActiveSubscription, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
2
+ import { a as formatPlanPrice, p as parseMonthlyQuotaCalls, s as isActiveSubscription, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
3
3
  import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
4
- import { t as useMySubscriptions } from "./useMySubscriptions-BYVIIWbe.mjs";
5
- import { t as usePlans } from "./usePlans-HyhIIuJZ.mjs";
4
+ import { t as useMySubscriptions } from "./useMySubscriptions-C1UMb8rJ.mjs";
5
+ import { t as usePlans } from "./usePlans-D_Khso7B.mjs";
6
6
  import { n as readPendingCheckout, t as clearPendingCheckout } from "./pending-checkout-DpVaNnTu.mjs";
7
- import { n as useApiKeys } from "./useApiKeys-BOY3vIOr.mjs";
8
- import { t as useUsage } from "./useUsage-B5x9YuKa.mjs";
9
- import { i as hasPaymentProvider, r as checkoutConfirmingText } from "./billing-state-DBQR2tad.mjs";
7
+ import { n as useApiKeys } from "./useApiKeys-CnyxipLc.mjs";
8
+ import { t as useUsage } from "./useUsage-DeEDgDdj.mjs";
9
+ import { i as hasPaymentProvider, r as checkoutConfirmingText } from "./billing-state-CJmy96zI.mjs";
10
10
  import { i as ChoosePlanNotice, n as billingPageState, r as keyPlanNote } from "./page-state-BLL2JJBc.mjs";
11
11
  import { Head, Spinner } from "@lukoweb/apitogo/components";
12
12
  import { Link as Link$1, useSearchParams } from "@lukoweb/apitogo/router";
@@ -1,8 +1,8 @@
1
- import { m as parseQuotaPeriodDays, p as parseMonthlyQuotaCalls, s as isActiveSubscription } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
1
+ import { m as parseQuotaPeriodDays, p as parseMonthlyQuotaCalls, s as isActiveSubscription } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
2
2
  import "./DevPortalBillingContext-CR83s4LO.mjs";
3
- import { t as useMySubscriptions } from "./useMySubscriptions-BYVIIWbe.mjs";
4
- import { t as usePlans } from "./usePlans-HyhIIuJZ.mjs";
5
- import { n as useUsageRecent, t as useUsage } from "./useUsage-B5x9YuKa.mjs";
3
+ import { t as useMySubscriptions } from "./useMySubscriptions-C1UMb8rJ.mjs";
4
+ import { t as usePlans } from "./usePlans-D_Khso7B.mjs";
5
+ import { n as useUsageRecent, t as useUsage } from "./useUsage-DeEDgDdj.mjs";
6
6
  import { Head } from "@lukoweb/apitogo/components";
7
7
  import { Link as Link$1 } from "@lukoweb/apitogo/router";
8
8
  import { useState } from "react";
@@ -1,4 +1,4 @@
1
- import { i as useRevokeApiKey, r as useCreateApiKey, t as API_KEY_CAP_REACHED } from "./useApiKeys-BOY3vIOr.mjs";
1
+ import { i as useRevokeApiKey, r as useCreateApiKey, t as API_KEY_CAP_REACHED } from "./useApiKeys-CnyxipLc.mjs";
2
2
  import { Spinner } from "@lukoweb/apitogo/components";
3
3
  import { KeyRoundIcon, TrashIcon } from "@lukoweb/apitogo/icons";
4
4
  import { useState } from "react";
@@ -1,4 +1,4 @@
1
- import { _ as rateLimitCell, g as quotaCell } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
1
+ import { _ as rateLimitCell, g as quotaCell } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
2
2
 
3
3
  //#region src/utils/billing-state.ts
4
4
  /** Whether the pricing page has no plans to show yet because the answer is still on its way. */
package/dist/index.d.mts CHANGED
@@ -59,7 +59,13 @@ type DevPortalBillingConfig = {
59
59
  * backend's answer, which only knows the platform default.
60
60
  */
61
61
  keyHeader?: string; /** The plan shown as "Popular"; none unless the tenant marked one (API-124). */
62
- featuredPlanSku?: string; /** Local preview plan catalog (from `apitogo.config.json` `plans`). */
62
+ featuredPlanSku?: string; /** Lines each plan's card lists, by lowercased sku, from `features` in plans.json (API-129). */
63
+ planFeatures?: Record<string, string[]>;
64
+ /**
65
+ * The gateway checks tokens (`gateway.auth`), so Try it offers "Signed-in user". False for a gateway that checks keys,
66
+ * where the token only gets a 401 (API-129). Absent (an older wrapper): offered, as before.
67
+ */
68
+ tokenIdentity?: boolean; /** Local preview plan catalog (from `apitogo.config.json` `plans`). */
63
69
  previewPlans?: Array<{
64
70
  sku: string;
65
71
  displayName: string;
package/dist/index.mjs CHANGED
@@ -1,15 +1,15 @@
1
- import { t as AccountPlansPage } from "./AccountPlansPage-BuM7fe82.mjs";
1
+ import { t as AccountPlansPage } from "./AccountPlansPage-DBBVHGuw.mjs";
2
2
  import { a as CREDENTIALS_PATH, c as SUBSCRIPTIONS_PATH, i as CONTACT_SALES_PATH, l as USAGE_PATH, o as LOGIN_NAV_PATH, r as CHECKOUT_PATH, s as PRICING_PATH, t as ACCOUNT_PLANS_PATH } from "./paths-DaVyTIDJ.mjs";
3
- import { d as listPlanFeatureLines, f as parseManifestPlans, h as pickFeaturedPlanSku, i as formatPlanDisplayPrice, o as formatPlanPriceUnit, s as isActiveSubscription, v as resolveDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
4
- import "./PricingCard-DFuQEw7Y.mjs";
3
+ import { d as listPlanFeatureLines, f as parseManifestPlans, h as pickFeaturedPlanSku, i as formatPlanDisplayPrice, o as formatPlanPriceUnit, s as isActiveSubscription, v as resolveDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
4
+ import "./PricingCard-CRlUGh0k.mjs";
5
5
  import { n as useDevPortalBillingConfig, t as DevPortalBillingProvider } from "./DevPortalBillingContext-CR83s4LO.mjs";
6
6
  import { t as DEMO_GATEWAY_KEY_HEADER } from "./demo-session-yK9qEqV-.mjs";
7
- import { t as useMySubscriptions } from "./useMySubscriptions-BYVIIWbe.mjs";
8
- import { n as startPlansPrefetch, t as usePlans } from "./usePlans-HyhIIuJZ.mjs";
7
+ import { t as useMySubscriptions } from "./useMySubscriptions-C1UMb8rJ.mjs";
8
+ import { n as startPlansPrefetch, t as usePlans } from "./usePlans-D_Khso7B.mjs";
9
9
  import "./pending-checkout-DpVaNnTu.mjs";
10
- import { a as playgroundKeyIdentities, i as useRevokeApiKey, n as useApiKeys, r as useCreateApiKey, t as API_KEY_CAP_REACHED } from "./useApiKeys-BOY3vIOr.mjs";
11
- import { n as useUsageRecent, t as useUsage } from "./useUsage-B5x9YuKa.mjs";
12
- import { r as ApiKeysPanel, t as accountLayoutProps } from "./account-links-Drofs-f-.mjs";
10
+ import { a as playgroundKeyIdentities, i as useRevokeApiKey, n as useApiKeys, r as useCreateApiKey, t as API_KEY_CAP_REACHED } from "./useApiKeys-CnyxipLc.mjs";
11
+ import { n as useUsageRecent, t as useUsage } from "./useUsage-DeEDgDdj.mjs";
12
+ import { r as ApiKeysPanel, t as accountLayoutProps } from "./account-links-CpTOzb2V.mjs";
13
13
  import { t as isPreviewPricingEnabled } from "./pricing-config-Bc6eWFUO.mjs";
14
14
  import { Head, Link } from "@lukoweb/apitogo/components";
15
15
  import { ArrowRightIcon, KeyRoundIcon, StarsIcon } from "@lukoweb/apitogo/icons";
@@ -367,13 +367,13 @@ async function getSignedInIdentities(apiBaseUrl, fetchImpl = fetch) {
367
367
 
368
368
  //#endregion
369
369
  //#region src/DevPortalBillingPlugin.tsx
370
- const AccountPlansPage$1 = lazy(() => import("./AccountPlansPage-BuM7fe82.mjs").then((n) => n.n));
371
- const CheckoutPage = lazy(() => import("./CheckoutPage-BSiADcI6.mjs"));
372
- const ContactSalesPage = lazy(() => import("./ContactSalesPage-BqqDmorF.mjs"));
373
- const CredentialsPage = lazy(() => import("./CredentialsPage-DjNvBBLb.mjs"));
374
- const PricingPage = lazy(() => import("./PricingPage-SpL81vpd.mjs"));
375
- const SubscriptionsPage = lazy(() => import("./SubscriptionsPage-CwsBtNld.mjs"));
376
- const UsagePage = lazy(() => import("./UsagePage-Dw5Uepg3.mjs"));
370
+ const AccountPlansPage$1 = lazy(() => import("./AccountPlansPage-DBBVHGuw.mjs").then((n) => n.n));
371
+ const CheckoutPage = lazy(() => import("./CheckoutPage-CZ9NXp8z.mjs"));
372
+ const ContactSalesPage = lazy(() => import("./ContactSalesPage-D4jX0rtt.mjs"));
373
+ const CredentialsPage = lazy(() => import("./CredentialsPage-CVecoIuQ.mjs"));
374
+ const PricingPage = lazy(() => import("./PricingPage-BM7NH4VQ.mjs"));
375
+ const SubscriptionsPage = lazy(() => import("./SubscriptionsPage-C58HnDmQ.mjs"));
376
+ const UsagePage = lazy(() => import("./UsagePage-C5opT1KD.mjs"));
377
377
  function resolvePricingEnabled(config) {
378
378
  if (config.__meta?.pricingEnabled !== void 0) return config.__meta.pricingEnabled;
379
379
  return isPreviewPricingEnabled();
@@ -398,7 +398,7 @@ const devPortalBillingPlugin = createPlugin((options = {}) => {
398
398
  }
399
399
  });
400
400
  },
401
- getIdentities: async () => [...playgroundKeyIdentities(), ...await getSignedInIdentities(resolveDevPortalApiBaseUrl(options.apiBaseUrl))],
401
+ getIdentities: async () => [...playgroundKeyIdentities(), ...options.tokenIdentity === false ? [] : await getSignedInIdentities(resolveDevPortalApiBaseUrl(options.apiBaseUrl))],
402
402
  getProfileMenuItems: () => [{
403
403
  label: "Billing",
404
404
  path: SUBSCRIPTIONS_PATH,
@@ -1,4 +1,4 @@
1
- import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
1
+ import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
2
2
  import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
3
3
  import { n as readProblemDetail } from "./problem-details-n20-dy3w.mjs";
4
4
  import { n as createDemoApiKeys, t as DEMO_GATEWAY_KEY_HEADER } from "./demo-session-yK9qEqV-.mjs";
@@ -126,7 +126,9 @@ function pickFeaturedPlanSku(plans, featuredPlanSku) {
126
126
  }
127
127
  function formatRateLimit(rate) {
128
128
  if (!rate || typeof rate !== "object") return null;
129
- const { calls, renewalPeriodSeconds } = rate;
129
+ const limit = rate;
130
+ const calls = limit.calls;
131
+ const renewalPeriodSeconds = limit.renewalPeriodSeconds ?? limit.periodSeconds;
130
132
  if (calls == null || renewalPeriodSeconds == null) return null;
131
133
  if (renewalPeriodSeconds === 1) return `${calls} req/sec rate limit`;
132
134
  const window = renewalPeriodSeconds === 60 ? "minute" : `${renewalPeriodSeconds} seconds`;
@@ -134,7 +136,9 @@ function formatRateLimit(rate) {
134
136
  }
135
137
  function formatQuota(quota) {
136
138
  if (!quota || typeof quota !== "object") return null;
137
- const { calls, renewalPeriodDays } = quota;
139
+ const limit = quota;
140
+ const calls = limit.calls;
141
+ const renewalPeriodDays = limit.renewalPeriodDays ?? limit.periodDays;
138
142
  if (calls == null) return null;
139
143
  if (calls < 0) return "Unlimited requests";
140
144
  const formatted = calls.toLocaleString();
@@ -183,8 +187,21 @@ function parseLimits(limitsJson) {
183
187
  * tenant wrote.
184
188
  */
185
189
  const SAMPLE_PLAN_FEATURE_EXTRAS = {};
190
+ /** Gives each plan the feature lines plans.json names for its sku (API-129). Plans without any keep their own. */
191
+ function withPlanFeatures(catalog, planFeatures) {
192
+ if (!planFeatures || Object.keys(planFeatures).length === 0) return catalog;
193
+ return {
194
+ ...catalog,
195
+ plans: catalog.plans.map((plan) => {
196
+ const features = planFeatures[plan.sku.trim().toLowerCase()];
197
+ return features?.length ? {
198
+ ...plan,
199
+ features
200
+ } : plan;
201
+ })
202
+ };
203
+ }
186
204
  function listPlanFeatureLines(plan) {
187
- if (plan.features && plan.features.length > 0) return plan.features;
188
205
  const lines = [];
189
206
  if (plan.limitsJson?.trim()) try {
190
207
  const limits = JSON.parse(plan.limitsJson);
@@ -193,7 +210,8 @@ function listPlanFeatureLines(plan) {
193
210
  const rateLine = formatRateLimit(limits.rateLimit);
194
211
  if (rateLine) lines.push(rateLine);
195
212
  } catch {}
196
- const extras = SAMPLE_PLAN_FEATURE_EXTRAS[plan.sku.toLowerCase()];
213
+ for (const feature of plan.features ?? []) if (!lines.includes(feature)) lines.push(feature);
214
+ const extras = plan.features?.length ? void 0 : SAMPLE_PLAN_FEATURE_EXTRAS[plan.sku.toLowerCase()];
197
215
  if (extras) {
198
216
  for (const extra of extras) if (!lines.includes(extra)) lines.push(extra);
199
217
  }
@@ -250,4 +268,4 @@ function useDevPortalPreviewMode() {
250
268
  }
251
269
 
252
270
  //#endregion
253
- export { rateLimitCell as _, formatPlanPrice as a, isPlaceholderDevPortalApiUrl as c, listPlanFeatureLines as d, parseManifestPlans as f, quotaCell as g, pickFeaturedPlanSku as h, formatPlanDisplayPrice as i, isPlanCustomPriced as l, parseQuotaPeriodDays as m, useDevPortalPreviewMode as n, formatPlanPriceUnit as o, parseMonthlyQuotaCalls as p, useIsDemoSession as r, isActiveSubscription as s, useDevPortalApiBaseUrl as t, isPlanFree as u, resolveDevPortalApiBaseUrl as v };
271
+ export { rateLimitCell as _, formatPlanPrice as a, isPlaceholderDevPortalApiUrl as c, listPlanFeatureLines as d, parseManifestPlans as f, quotaCell as g, pickFeaturedPlanSku as h, formatPlanDisplayPrice as i, isPlanCustomPriced as l, parseQuotaPeriodDays as m, useDevPortalPreviewMode as n, formatPlanPriceUnit as o, parseMonthlyQuotaCalls as p, useIsDemoSession as r, isActiveSubscription as s, useDevPortalApiBaseUrl as t, isPlanFree as u, resolveDevPortalApiBaseUrl as v, withPlanFeatures as y };
@@ -1,4 +1,4 @@
1
- import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
1
+ import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
2
2
  import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
3
3
  import { i as createDemoMeResponse } from "./demo-session-yK9qEqV-.mjs";
4
4
  import { useQuery } from "@lukoweb/apitogo/react-query";
@@ -1,4 +1,4 @@
1
- import { c as isPlaceholderDevPortalApiUrl, f as parseManifestPlans, n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
1
+ import { c as isPlaceholderDevPortalApiUrl, f as parseManifestPlans, n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl, y as withPlanFeatures } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
2
2
  import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
3
3
  import { useQuery } from "@lukoweb/apitogo/react-query";
4
4
 
@@ -37,17 +37,18 @@ function usePlans() {
37
37
  const apiBaseUrl = useDevPortalApiBaseUrl();
38
38
  const isPreview = useDevPortalPreviewMode();
39
39
  const isDemoSession = useIsDemoSession();
40
- const { previewPlans } = useDevPortalBillingConfig();
40
+ const { previewPlans, planFeatures } = useDevPortalBillingConfig();
41
41
  const liveQuery = useQuery({
42
42
  queryKey: ["dev-portal-plans", apiBaseUrl],
43
43
  enabled: !isPreview && !isDemoSession && Boolean(apiBaseUrl),
44
44
  queryFn: async () => {
45
45
  if (!apiBaseUrl) return { plans: [] };
46
46
  return loadPlans(apiBaseUrl);
47
- }
47
+ },
48
+ select: (catalog) => withPlanFeatures(catalog, planFeatures)
48
49
  });
49
50
  if (isPreview || isDemoSession) {
50
- const data = parseManifestPlans(previewPlans);
51
+ const data = withPlanFeatures(parseManifestPlans(previewPlans), planFeatures);
51
52
  return {
52
53
  ...liveQuery,
53
54
  data,
@@ -1,4 +1,4 @@
1
- import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BiI2jFjU.mjs";
1
+ import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-BfpUBayO.mjs";
2
2
  import { useQuery } from "@lukoweb/apitogo/react-query";
3
3
  import { useAuth } from "@lukoweb/apitogo/hooks";
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo-plugin-dev-portal-billing",
3
- "version": "0.2.56",
3
+ "version": "0.2.57",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/zuplo/zudoku",
@@ -46,8 +46,8 @@
46
46
  "react": "19.2.4",
47
47
  "react-dom": "19.2.4",
48
48
  "tsdown": "0.20.3",
49
- "@lukoweb/apitogo": "0.2.56",
50
- "@lukoweb/apitogo-module-user-panel": "0.2.56"
49
+ "@lukoweb/apitogo": "0.2.57",
50
+ "@lukoweb/apitogo-module-user-panel": "0.2.57"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "react": ">=19.2.0",