@lukoweb/apitogo-plugin-dev-portal-billing 0.1.47 → 0.1.49

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.
Files changed (2) hide show
  1. package/dist/index.mjs +157 -20
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -12,6 +12,8 @@ import { Alert, AlertAction, AlertDescription } from "@lukoweb/apitogo/ui/Alert"
12
12
  import { Button as Button$1 } from "@lukoweb/apitogo/ui/Button";
13
13
  import { Secret } from "@lukoweb/apitogo/ui/Secret.js";
14
14
  import { Value } from "@lukoweb/apitogo/ui/Value.js";
15
+ import { access, readFile } from "node:fs/promises";
16
+ import path from "node:path";
15
17
 
16
18
  //#region src/utils/dev-portal-api.ts
17
19
  const DEV_PORTAL_PLACEHOLDER_API_URL = "https://dp-example.azurecontainerapps.io";
@@ -322,11 +324,19 @@ const AccountPlansPage = () => {
322
324
  children: [
323
325
  "Local preview uses plans from",
324
326
  " ",
327
+ /* @__PURE__ */ jsx("code", {
328
+ className: "rounded bg-muted px-1.5 py-0.5",
329
+ children: "apitogo.json"
330
+ }),
331
+ ` `,
332
+ "(with optional",
333
+ " ",
325
334
  /* @__PURE__ */ jsx("code", {
326
335
  className: "rounded bg-muted px-1.5 py-0.5",
327
336
  children: "apitogo.local.json"
328
337
  }),
329
- ". Subscribe and unsubscribe activate after the portal is published."
338
+ " ",
339
+ "overrides). Subscribe and unsubscribe activate after the portal is published."
330
340
  ]
331
341
  }) : null,
332
342
  cancelMutation.isError ? /* @__PURE__ */ jsx("div", {
@@ -705,14 +715,25 @@ const CredentialsPage = () => {
705
715
  });
706
716
  };
707
717
 
718
+ //#endregion
719
+ //#region src/utils/pricing-config.ts
720
+ function isPreviewPricingEnabled() {
721
+ return manifestCatalog?.pricingEnabled === true;
722
+ }
723
+
708
724
  //#endregion
709
725
  //#region src/pages/PricingPage.tsx
710
726
  const PricingPage = () => {
711
727
  const { pricing } = useDevPortalBillingConfig();
712
728
  const auth = useAuth();
713
729
  const isPreview = useDevPortalPreviewMode();
730
+ const pricingEnabled = isPreviewPricingEnabled();
714
731
  const { data: catalog, isLoading, isError } = usePlans();
715
732
  const { data: me } = useMySubscriptions();
733
+ if (!pricingEnabled) return /* @__PURE__ */ jsx(Navigate, {
734
+ to: "/",
735
+ replace: true
736
+ });
716
737
  if (auth.isAuthenticated) return /* @__PURE__ */ jsx(Navigate, {
717
738
  to: "/account/plans",
718
739
  replace: true
@@ -744,11 +765,19 @@ const PricingPage = () => {
744
765
  children: [
745
766
  "Local preview uses plans from",
746
767
  " ",
768
+ /* @__PURE__ */ jsx("code", {
769
+ className: "rounded bg-muted px-1.5 py-0.5",
770
+ children: "apitogo.json"
771
+ }),
772
+ ` `,
773
+ "(with optional",
774
+ " ",
747
775
  /* @__PURE__ */ jsx("code", {
748
776
  className: "rounded bg-muted px-1.5 py-0.5",
749
777
  children: "apitogo.local.json"
750
778
  }),
751
- ". Checkout activates after the portal is published."
779
+ " ",
780
+ "overrides). Checkout activates after the portal is published."
752
781
  ]
753
782
  }) : null,
754
783
  isLoading ? /* @__PURE__ */ jsx("p", {
@@ -766,9 +795,25 @@ const PricingPage = () => {
766
795
  mode: "public",
767
796
  isSubscribed: activeSkus.has(plan.sku)
768
797
  }, plan.sku))
769
- }) : !isLoading && !isError ? /* @__PURE__ */ jsx("div", {
798
+ }) : !isLoading && !isError ? /* @__PURE__ */ jsxs("div", {
770
799
  className: "mx-auto max-w-lg rounded-xl border border-dashed p-8 text-center text-sm text-muted-foreground",
771
- children: "No plans are available yet. Sync plans from your APItoGo manifest or platform gateway."
800
+ children: [
801
+ "No plans are configured yet. Add plans in",
802
+ " ",
803
+ /* @__PURE__ */ jsx("code", {
804
+ className: "rounded bg-muted px-1.5 py-0.5",
805
+ children: "apitogo.json"
806
+ }),
807
+ " ",
808
+ "and set",
809
+ " ",
810
+ /* @__PURE__ */ jsx("code", {
811
+ className: "rounded bg-muted px-1.5 py-0.5",
812
+ children: "pricing.enabled"
813
+ }),
814
+ " ",
815
+ "to true."
816
+ ]
772
817
  }) : null
773
818
  ]
774
819
  });
@@ -896,14 +941,103 @@ const SubscriptionsPage = () => {
896
941
  });
897
942
  };
898
943
 
944
+ //#endregion
945
+ //#region src/utils/manifest-pricing.ts
946
+ const APITOGO_MANIFEST_BASE_FILENAME = "apitogo.json";
947
+ const APITOGO_MANIFEST_ENV_FILES = {
948
+ development: "apitogo.local.json",
949
+ production: "apitogo.prod.json"
950
+ };
951
+ async function fileExists(filePath) {
952
+ try {
953
+ await access(filePath);
954
+ return true;
955
+ } catch {
956
+ return false;
957
+ }
958
+ }
959
+ async function readManifestFile(filePath) {
960
+ try {
961
+ const raw = await readFile(filePath, "utf8");
962
+ return JSON.parse(raw);
963
+ } catch {
964
+ return null;
965
+ }
966
+ }
967
+ function mergeManifest(base, override) {
968
+ if (!base) return override;
969
+ if (!override) return base;
970
+ return {
971
+ ...base,
972
+ ...override,
973
+ pricing: {
974
+ ...base.pricing,
975
+ ...override.pricing
976
+ }
977
+ };
978
+ }
979
+ async function readEffectiveManifest(rootDir, mode) {
980
+ const resolvedRoot = path.resolve(rootDir);
981
+ const env = mode === "production" ? "production" : "development";
982
+ const basePath = path.join(resolvedRoot, APITOGO_MANIFEST_BASE_FILENAME);
983
+ const overridePath = path.join(resolvedRoot, APITOGO_MANIFEST_ENV_FILES[env]);
984
+ const hasBase = await fileExists(basePath);
985
+ const hasOverride = await fileExists(overridePath);
986
+ if (!hasBase) {
987
+ const legacy = await readManifestFile(path.join(resolvedRoot, APITOGO_MANIFEST_ENV_FILES.development));
988
+ if (legacy) return legacy;
989
+ if (hasOverride) return readManifestFile(overridePath);
990
+ return null;
991
+ }
992
+ const base = await readManifestFile(basePath);
993
+ if (!hasOverride) return base;
994
+ return mergeManifest(base, await readManifestFile(overridePath));
995
+ }
996
+ async function manifestPricingEnabled(rootDir, mode = "development") {
997
+ return (await readEffectiveManifest(rootDir, mode))?.pricing?.enabled === true;
998
+ }
999
+
1000
+ //#endregion
1001
+ //#region src/utils/navigation-plans.ts
1002
+ function withoutPricingNavigation(items) {
1003
+ if (!items?.length) return [];
1004
+ return items.map((item) => {
1005
+ if (typeof item === "string") return item;
1006
+ if (item.type === "link" && item.to === PRICING_PATH) return null;
1007
+ if (item.items?.length) {
1008
+ const filteredItems = withoutPricingNavigation(item.items);
1009
+ return {
1010
+ ...item,
1011
+ items: filteredItems
1012
+ };
1013
+ }
1014
+ return item;
1015
+ }).filter((item) => item !== null);
1016
+ }
1017
+
899
1018
  //#endregion
900
1019
  //#region src/DevPortalBillingPlugin.tsx
1020
+ async function resolvePricingEnabled(config) {
1021
+ const meta = config;
1022
+ const rootDir = meta.__meta?.rootDir;
1023
+ if (rootDir) return manifestPricingEnabled(rootDir, meta.__meta?.mode);
1024
+ return isPreviewPricingEnabled();
1025
+ }
901
1026
  const devPortalBillingPlugin = createPlugin((options = {}) => ({
902
- transformConfig: ({ config, merge }) => merge({ header: { navigation: [...config.header?.navigation ?? [], {
903
- label: "Pricing",
904
- to: PRICING_PATH,
905
- display: "anon"
906
- }] } }),
1027
+ transformConfig: async ({ config, merge }) => {
1028
+ if (!await resolvePricingEnabled(config)) return merge({
1029
+ navigation: withoutPricingNavigation(config.navigation),
1030
+ header: {
1031
+ ...config.header,
1032
+ navigation: withoutPricingNavigation(config.header?.navigation)
1033
+ }
1034
+ });
1035
+ return merge({ header: { navigation: [...config.header?.navigation ?? [], {
1036
+ label: "Pricing",
1037
+ to: PRICING_PATH,
1038
+ display: "anon"
1039
+ }] } });
1040
+ },
907
1041
  getProfileMenuItems: () => [{
908
1042
  label: "My Subscriptions",
909
1043
  path: SUBSCRIPTIONS_PATH,
@@ -917,15 +1051,8 @@ const devPortalBillingPlugin = createPlugin((options = {}) => ({
917
1051
  category: "middle",
918
1052
  weight: 26
919
1053
  }],
920
- getRoutes: () => [{
921
- element: /* @__PURE__ */ jsx(DevPortalBillingWrapper, { options }),
922
- handle: { layout: "none" },
923
- children: [
924
- {
925
- path: PRICING_PATH,
926
- handle: { layout: "default" },
927
- element: /* @__PURE__ */ jsx(PricingPage, {})
928
- },
1054
+ getRoutes: () => {
1055
+ const children = [
929
1056
  {
930
1057
  path: CHECKOUT_PATH,
931
1058
  handle: { layout: "default" },
@@ -941,8 +1068,18 @@ const devPortalBillingPlugin = createPlugin((options = {}) => ({
941
1068
  handle: { layout: "default" },
942
1069
  element: /* @__PURE__ */ jsx(CredentialsPage, {})
943
1070
  }
944
- ]
945
- }],
1071
+ ];
1072
+ if (isPreviewPricingEnabled()) children.unshift({
1073
+ path: PRICING_PATH,
1074
+ handle: { layout: "default" },
1075
+ element: /* @__PURE__ */ jsx(PricingPage, {})
1076
+ });
1077
+ return [{
1078
+ element: /* @__PURE__ */ jsx(DevPortalBillingWrapper, { options }),
1079
+ handle: { layout: "none" },
1080
+ children
1081
+ }];
1082
+ },
946
1083
  getProtectedRoutes: () => [
947
1084
  CHECKOUT_PATH,
948
1085
  SUBSCRIPTIONS_PATH,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo-plugin-dev-portal-billing",
3
- "version": "0.1.47",
3
+ "version": "0.1.49",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/zuplo/zudoku",
@@ -46,7 +46,7 @@
46
46
  "react": "19.2.4",
47
47
  "react-dom": "19.2.4",
48
48
  "tsdown": "0.20.3",
49
- "@lukoweb/apitogo": "0.1.47"
49
+ "@lukoweb/apitogo": "0.1.49"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "react": ">=19.2.0",