@lukoweb/apitogo 0.1.133 → 0.1.135

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/cli/cli.js CHANGED
@@ -5809,7 +5809,7 @@ import {
5809
5809
  // package.json
5810
5810
  var package_default = {
5811
5811
  name: "@lukoweb/apitogo",
5812
- version: "0.1.133",
5812
+ version: "0.1.135",
5813
5813
  type: "module",
5814
5814
  sideEffects: [
5815
5815
  "**/*.css",
@@ -11252,6 +11252,17 @@ var PlatformApiClient = class {
11252
11252
  }
11253
11253
  );
11254
11254
  }
11255
+ /**
11256
+ * Check every plan at once without persisting. Without `projectId` (site not deployed yet)
11257
+ * the project-less endpoint is used; `planTier` overrides the tier used for paid-plan checks.
11258
+ */
11259
+ validateGatewayPlans(plans, options = {}) {
11260
+ const path73 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
11261
+ return this.request("POST", path73, {
11262
+ headers: { "Content-Type": "application/json" },
11263
+ body: JSON.stringify({ plans, planTier: options.planTier })
11264
+ });
11265
+ }
11255
11266
  getGatewayPlan(projectId, planId) {
11256
11267
  return this.request(
11257
11268
  "GET",
@@ -16805,6 +16816,73 @@ var config_default = {
16805
16816
  // src/cli/apply/handler.ts
16806
16817
  import path63 from "node:path";
16807
16818
 
16819
+ // src/cli/platform/helpers/gateway-plan-validation.ts
16820
+ init_gateway_limits();
16821
+ function buildGatewayPlansValidationRequest(plans) {
16822
+ return plans.map((plan, index) => {
16823
+ const currency = tryNormalizePriceCurrency(plan.priceCurrency);
16824
+ return {
16825
+ sku: plan.sku?.trim() ?? "",
16826
+ displayName: plan.displayName?.trim() ?? "",
16827
+ limitsJson: planLimitsForPlatform(plan) ?? null,
16828
+ includedActionKeys: plan.includedActionKeys,
16829
+ priceAmount: plan.priceAmount ?? 0,
16830
+ pricingModel: plan.pricingModel ?? "fixed",
16831
+ priceLabel: plan.priceLabel,
16832
+ priceCurrency: currency.ok ? currency.value : plan.priceCurrency ?? "",
16833
+ billingPeriod: normalizeBillingPeriod2(plan.billingPeriod),
16834
+ sortOrder: index
16835
+ };
16836
+ });
16837
+ }
16838
+ function toConfigIssue(issue) {
16839
+ return {
16840
+ ...issue,
16841
+ field: issue.field.replace(/\.limitsJson$/, ".limits")
16842
+ };
16843
+ }
16844
+ function formatGatewayPlanIssue(issue) {
16845
+ const sku = issue.sku ? ` (${issue.sku})` : "";
16846
+ return `${issue.field}${sku}: ${issue.message}`;
16847
+ }
16848
+ async function checkGatewayPlansOnPlatform(client, args) {
16849
+ let result;
16850
+ try {
16851
+ result = await client.validateGatewayPlans(
16852
+ buildGatewayPlansValidationRequest(args.plans),
16853
+ { projectId: args.projectId, planTier: args.planTier }
16854
+ );
16855
+ } catch (error) {
16856
+ if (error instanceof PlatformApiError && (error.status === 404 || error.status === 405)) {
16857
+ return null;
16858
+ }
16859
+ throw error;
16860
+ }
16861
+ return {
16862
+ errors: result.errors.map(toConfigIssue),
16863
+ warnings: result.warnings.map(toConfigIssue)
16864
+ };
16865
+ }
16866
+ async function assertGatewayPlansValidForPublish(client, args) {
16867
+ const check2 = await checkGatewayPlansOnPlatform(client, args);
16868
+ if (!check2 || check2.errors.length === 0) {
16869
+ return { blocked: false };
16870
+ }
16871
+ const { errors, warnings } = check2;
16872
+ return {
16873
+ blocked: true,
16874
+ payload: {
16875
+ status: "blocked",
16876
+ reason: "gateway_plans_invalid",
16877
+ ...args.projectId ? { projectId: args.projectId } : {},
16878
+ errors,
16879
+ warnings,
16880
+ userMessage: `Deploy is blocked: apitogo.config.json has ${errors.length} plan ${errors.length === 1 ? "error" : "errors"}.`,
16881
+ nextStep: "Fix every issue listed in errors in apitogo.config.json plans, then retry publish."
16882
+ }
16883
+ };
16884
+ }
16885
+
16808
16886
  // src/cli/platform/helpers/progress.ts
16809
16887
  var RECENT_JOB_MESSAGE_LIMIT = 5;
16810
16888
  function recentJobMessages(messages) {
@@ -20983,6 +21061,25 @@ async function runApplyFromSiteDirectory(client, config2, args, progress) {
20983
21061
  emailOptions
20984
21062
  );
20985
21063
  await writeManifest(args.sourceDirectory, manifest);
21064
+ if (includeApihub) {
21065
+ await publishProgress?.onStep("Validating gateway plans");
21066
+ const plansGate = await assertGatewayPlansValidForPublish(client, {
21067
+ projectId: args.createNewProject ? void 0 : projectIdHint,
21068
+ plans: manifest.plans ?? [],
21069
+ planTier: args.platformPlan
21070
+ });
21071
+ if (plansGate.blocked) {
21072
+ return JSON.stringify(
21073
+ {
21074
+ ...plansGate.payload,
21075
+ completedSteps,
21076
+ sourceDirectory: args.sourceDirectory
21077
+ },
21078
+ null,
21079
+ 2
21080
+ );
21081
+ }
21082
+ }
20986
21083
  const stripeRequired = manifestRequiresStripe(manifest.plans);
20987
21084
  const ledgerResume = resolveResumeStepFromLedger(ledger, PUBLISH_STEPS);
20988
21085
  const effectiveResumeFromStep = args.resumeFromStep ?? ledgerResume;
@@ -21899,6 +21996,26 @@ function formatBlockedOrFailedResultForHuman(parsed) {
21899
21996
  );
21900
21997
  return lines;
21901
21998
  }
21999
+ if (status2 === "blocked" && parsed.reason === "gateway_plans_invalid") {
22000
+ const lines = [
22001
+ typeof parsed.userMessage === "string" ? parsed.userMessage : "Deploy is blocked: apitogo.config.json has plan errors."
22002
+ ];
22003
+ for (const issue of Array.isArray(parsed.errors) ? parsed.errors : []) {
22004
+ lines.push(` - ${formatGatewayPlanIssue(issue)}`);
22005
+ }
22006
+ const warnings = Array.isArray(parsed.warnings) ? parsed.warnings : [];
22007
+ if (warnings.length > 0) {
22008
+ lines.push("", "Warnings:");
22009
+ for (const issue of warnings) {
22010
+ lines.push(` - ${formatGatewayPlanIssue(issue)}`);
22011
+ }
22012
+ }
22013
+ lines.push(
22014
+ "",
22015
+ "Fix these in apitogo.config.json, then re-run `apitogo deploy`."
22016
+ );
22017
+ return lines;
22018
+ }
21902
22019
  if (status2 === "blocked") {
21903
22020
  return [
21904
22021
  typeof parsed.userMessage === "string" ? parsed.userMessage : `Deploy is blocked${typeof parsed.reason === "string" ? `: ${parsed.reason}` : "."}`
@@ -21981,7 +22098,25 @@ async function askPlanChoice() {
21981
22098
  rl.close();
21982
22099
  }
21983
22100
  }
21984
- async function resolvePlatformPlanInteractively(client, config2, payload) {
22101
+ async function askValidPlanChoice(validatePlansForTier) {
22102
+ while (true) {
22103
+ const choice = await askPlanChoice();
22104
+ if (!choice || !validatePlansForTier) return choice;
22105
+ printDiagnosticsToConsole(`Checking your API plans against ${choice}...`);
22106
+ const errors = await validatePlansForTier(choice);
22107
+ if (errors.length === 0) return choice;
22108
+ printWarningToConsole(
22109
+ `apitogo.config.json has ${errors.length} plan ${errors.length === 1 ? "error" : "errors"} for the ${choice} plan:`
22110
+ );
22111
+ for (const error of errors) {
22112
+ printDiagnosticsToConsole(` - ${error}`);
22113
+ }
22114
+ printDiagnosticsToConsole(
22115
+ "Pick a different plan, or leave empty to cancel and fix apitogo.config.json."
22116
+ );
22117
+ }
22118
+ }
22119
+ async function resolvePlatformPlanInteractively(client, config2, payload, validatePlansForTier) {
21985
22120
  const projectId = typeof payload.projectId === "string" ? payload.projectId : "";
21986
22121
  if (!projectId) {
21987
22122
  return false;
@@ -21990,7 +22125,7 @@ async function resolvePlatformPlanInteractively(client, config2, payload) {
21990
22125
  if (typeof payload.platformPlanUserPrompt === "string") {
21991
22126
  printDiagnosticsToConsole(payload.platformPlanUserPrompt);
21992
22127
  }
21993
- const choice = await askPlanChoice();
22128
+ const choice = await askValidPlanChoice(validatePlansForTier);
21994
22129
  if (!choice) {
21995
22130
  printWarningToConsole(
21996
22131
  "No plan selected. Re-run with --platform-plan free|starter|professional when you're ready."
@@ -22177,10 +22312,20 @@ async function apply(argv) {
22177
22312
  }
22178
22313
  }
22179
22314
  if (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "platform_plan_unselected") {
22315
+ const blockedProjectId = typeof parsed.projectId === "string" ? parsed.projectId : void 0;
22180
22316
  const resolved = await resolvePlatformPlanInteractively(
22181
22317
  client,
22182
22318
  config2,
22183
- parsed
22319
+ parsed,
22320
+ async (planTier) => {
22321
+ const manifest = await readManifest(sourceDirectory);
22322
+ const check2 = await checkGatewayPlansOnPlatform(client, {
22323
+ projectId: blockedProjectId,
22324
+ plans: manifest?.plans ?? [],
22325
+ planTier
22326
+ });
22327
+ return (check2?.errors ?? []).map(formatGatewayPlanIssue);
22328
+ }
22184
22329
  );
22185
22330
  if (resolved) {
22186
22331
  try {
@@ -343,6 +343,20 @@ export interface BackendDeploymentDetailDto {
343
343
  runtimeVersion: string;
344
344
  backendUrl?: string | null;
345
345
  }
346
+ export interface GatewayPlanValidationIssueDto {
347
+ field: string;
348
+ message: string;
349
+ planIndex?: number | null;
350
+ sku?: string | null;
351
+ }
352
+ export interface GatewayPlansValidationResultDto {
353
+ valid: boolean;
354
+ errors: GatewayPlanValidationIssueDto[];
355
+ warnings: GatewayPlanValidationIssueDto[];
356
+ }
357
+ export type ValidateGatewayPlanInput = Omit<CreateGatewayPlanRequest, "includedActionKeys"> & {
358
+ includedActionKeys?: string[];
359
+ };
346
360
  export declare class PlatformApiClient {
347
361
  private readonly config;
348
362
  constructor(config: ApitogoConfig);
@@ -389,6 +403,10 @@ export declare class PlatformApiClient {
389
403
  deployBackend(projectId: string, options: DeployBackendOptions): Promise<StartBackendDeploymentResponse>;
390
404
  listGatewayPlans(projectId: string): Promise<GatewayPlanDto[]>;
391
405
  createGatewayPlan(projectId: string, body: CreateGatewayPlanRequest): Promise<GatewayPlanDto>;
406
+ validateGatewayPlans(plans: ValidateGatewayPlanInput[], options?: {
407
+ projectId?: string;
408
+ planTier?: string;
409
+ }): Promise<GatewayPlansValidationResultDto>;
392
410
  getGatewayPlan(projectId: string, planId: string): Promise<GatewayPlanDto>;
393
411
  updateGatewayPlan(projectId: string, planId: string, body: UpdateGatewayPlanRequest): Promise<GatewayPlanDto>;
394
412
  deleteGatewayPlan(projectId: string, planId: string): Promise<void>;
@@ -0,0 +1,23 @@
1
+ import type { GatewayPlanValidationIssueDto, PlatformApiClient, ValidateGatewayPlanInput } from "../client/platform-api.js";
2
+ import { type ManifestPlanInput } from "./dev-portal-manifest.js";
3
+ export declare function buildGatewayPlansValidationRequest(plans: ManifestPlanInput[]): ValidateGatewayPlanInput[];
4
+ export declare function formatGatewayPlanIssue(issue: GatewayPlanValidationIssueDto): string;
5
+ export type GatewayPlansCheck = {
6
+ errors: GatewayPlanValidationIssueDto[];
7
+ warnings: GatewayPlanValidationIssueDto[];
8
+ };
9
+ export declare function checkGatewayPlansOnPlatform(client: PlatformApiClient, args: {
10
+ projectId?: string;
11
+ plans: ManifestPlanInput[];
12
+ planTier?: string;
13
+ }): Promise<GatewayPlansCheck | null>;
14
+ export declare function assertGatewayPlansValidForPublish(client: PlatformApiClient, args: {
15
+ projectId?: string;
16
+ plans: ManifestPlanInput[];
17
+ planTier?: string;
18
+ }): Promise<{
19
+ blocked: true;
20
+ payload: Record<string, unknown>;
21
+ } | {
22
+ blocked: false;
23
+ }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo",
3
- "version": "0.1.133",
3
+ "version": "0.1.135",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",