@lukoweb/apitogo 0.2.54 → 0.2.56

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.
@@ -246,6 +246,27 @@ export interface DevPortalEmailDnsRecordDto {
246
246
  value: string;
247
247
  ttlSeconds?: number | null;
248
248
  }
249
+ export interface PlatformPlanDto {
250
+ id: string;
251
+ displayName: string;
252
+ tagline: string;
253
+ priceAmount: string;
254
+ pricePeriod: string;
255
+ ctaLabel: string;
256
+ sortOrder: number;
257
+ highlights: string[];
258
+ entitlements: {
259
+ featureKey: string;
260
+ valueType: string;
261
+ value: string;
262
+ }[];
263
+ }
264
+ export interface BillingWebhookStatusDto {
265
+ webhookSecretConfigured: boolean;
266
+ lastEventAtUtc: string | null;
267
+ lastEventType: string | null;
268
+ eventCount: number;
269
+ }
249
270
  export interface DevPortalBackendDto {
250
271
  fqdn: string | null;
251
272
  containerAppName: string;
@@ -573,6 +594,8 @@ export declare class PlatformApiClient {
573
594
  cancelUrl?: string;
574
595
  }): Promise<DevPortalBillingDto>;
575
596
  linkDevPortalIdentity(projectId: string): Promise<DevPortalIdentityLinkDto>;
597
+ listPlatformPlans(): Promise<PlatformPlanDto[]>;
598
+ getBillingWebhookStatus(projectId: string): Promise<BillingWebhookStatusDto | null>;
576
599
  getDevPortalBackend(projectId: string): Promise<DevPortalBackendDto | null>;
577
600
  getDevPortalSetupStatus(projectId: string): Promise<DevPortalSetupStatusDto>;
578
601
  getDevPortalSettings(projectId: string): Promise<DevPortalSettingsDto | null>;
@@ -1,3 +1,4 @@
1
+ import type { PlatformPlanDto } from "../client/platform-api.js";
1
2
  import type { FreeProjectsQuota } from "./free-projects-quota.js";
2
3
  export type PlatformPlanId = "free" | "starter" | "professional";
3
4
  export interface PlatformPlanOption {
@@ -12,6 +13,11 @@ export interface PlatformPlanOption {
12
13
  limitations: string[];
13
14
  }
14
15
  export declare const PLATFORM_PLAN_CATALOG: PlatformPlanOption[];
16
+ export type PlatformPlanDisplay = Pick<PlatformPlanOption, "displayName" | "priceLabel" | "summary" | "features" | "limitations"> & {
17
+ id: string;
18
+ };
19
+ export declare function toPlanDisplays(plans: readonly PlatformPlanDto[]): PlatformPlanDisplay[];
20
+ export declare function loadPlanDisplays(listPlatformPlans: () => Promise<PlatformPlanDto[]>): Promise<PlatformPlanDisplay[]>;
15
21
  export declare const PLATFORM_PLAN_USER_PROMPT = "Which API ToGo platform plan do you want for this project \u2014 Free, Starter, or Professional?";
16
22
  export declare function formatFreeProjectsQuotaHint(quota: FreeProjectsQuota): string;
17
23
  export declare function buildPlatformPlanSelectionAgentInstruction(quota?: FreeProjectsQuota | null): string;
@@ -49,7 +49,7 @@ npx apitogo add plans --template free
49
49
  `--create-spec` writes a small OpenAPI file with one `GET /health` operation, so the gateway has
50
50
  something to expose. For a real API, point `gateway.specs` at your own OpenAPI file instead.
51
51
 
52
- The Free template in `plans.json` allows 3 calls per minute and 1000 calls per 30 days. Change the
52
+ The Free template in `plans.json` allows 60 calls per minute and 1000 calls per 30 days. Change the
53
53
  numbers in `plans.json` and deploy again to change the limits.
54
54
 
55
55
  With plans in the project, customers call the gateway with their API key alone. Their login token is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo",
3
- "version": "0.2.54",
3
+ "version": "0.2.56",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -249,21 +249,23 @@ const PLAN_TEMPLATES: Record<
249
249
  displayName: "Free",
250
250
  default: true,
251
251
  limits: {
252
- // The platform's Free tier allows at most 3 calls per 60 seconds per plan; a higher number is refused.
253
- rateLimit: { calls: 3, periodSeconds: 60 },
252
+ // The platform's Free tier allows at most 60 calls per minute per plan (its rate_limit_per_minute ceiling).
253
+ rateLimit: { calls: 60, periodSeconds: 60 },
254
254
  quota: { calls: 1000, periodDays: 30 },
255
255
  },
256
256
  },
257
257
  ],
258
- // Sample plans for the starter: a free plan and two with a price on request, so they show on the pricing page and
259
- // the home page and still deploy on a Free project (a plan with a price needs a paid platform plan and billing).
258
+ // Sample plans for the full template: a free plan and one paid example (API-124). The example's price is in the file
259
+ // but it is a contact-us plan, so the first deploy works on a Free project (a plan with a price needs Starter or
260
+ // Professional and billing). `apitogo plan` names the command that starts selling it. It has no limits of its own,
261
+ // so it stays within any project's ceiling until the tenant sets them.
260
262
  starter: [
261
263
  {
262
264
  sku: "free",
263
265
  displayName: "Free",
264
266
  default: true,
265
267
  limits: {
266
- rateLimit: { calls: 3, periodSeconds: 60 },
268
+ rateLimit: { calls: 60, periodSeconds: 60 },
267
269
  quota: { calls: 1000, periodDays: 30 },
268
270
  },
269
271
  },
@@ -272,12 +274,9 @@ const PLAN_TEMPLATES: Record<
272
274
  displayName: "Pro",
273
275
  pricingModel: "custom",
274
276
  priceLabel: "Contact us",
275
- },
276
- {
277
- sku: "enterprise",
278
- displayName: "Enterprise",
279
- pricingModel: "custom",
280
- priceLabel: "Contact us",
277
+ priceAmount: 19,
278
+ priceCurrency: "usd",
279
+ billingInterval: "month",
281
280
  },
282
281
  ],
283
282
  tiered: [
@@ -286,8 +285,8 @@ const PLAN_TEMPLATES: Record<
286
285
  displayName: "Free",
287
286
  default: true,
288
287
  limits: {
289
- // The platform's Free tier allows at most 3 calls per 60 seconds per plan; a higher number is refused.
290
- rateLimit: { calls: 3, periodSeconds: 60 },
288
+ // The platform's Free tier allows at most 60 calls per minute per plan (its rate_limit_per_minute ceiling).
289
+ rateLimit: { calls: 60, periodSeconds: 60 },
291
290
  quota: { calls: 1000, periodDays: 30 },
292
291
  },
293
292
  },
@@ -94,6 +94,8 @@ export type PortalPlan = {
94
94
  keyHeader?: string;
95
95
  /** The gateway's address, for code samples on the portal's pages. */
96
96
  gatewayBaseUrl?: string;
97
+ /** The plan shown as "Popular": only the one the tenant marked `featured` in plans.json (API-124). */
98
+ featuredPlanSku?: string;
97
99
  };
98
100
  /** Account pages. */
99
101
  userPanel: boolean;
@@ -685,6 +687,18 @@ export async function preparePortal(
685
687
  for (const page of tsxPages)
686
688
  if (page.order !== undefined && !pageOrders.has(page.route))
687
689
  pageOrders.set(page.route, page.order);
690
+ // "Popular" is the tenant's choice, never guessed (API-124).
691
+ if (plan.plansPlugin) {
692
+ const featured = (await readDemoPlans(resolved.plans?.file))?.find(
693
+ (item): item is { sku: string } =>
694
+ typeof item === "object" &&
695
+ item !== null &&
696
+ (item as { featured?: unknown }).featured === true &&
697
+ typeof (item as { sku?: unknown }).sku === "string",
698
+ );
699
+ if (featured)
700
+ plan.plansPlugin.featuredPlanSku = featured.sku.trim().toLowerCase();
701
+ }
688
702
  // The account pages' "Documentation" link goes to a page the portal really has, or is left out.
689
703
  if (plan.plansPlugin && plan.docs) {
690
704
  const landing = hostedDocRoutes.some(