@lukoweb/apitogo 0.1.126 → 0.1.127

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
@@ -736,6 +736,8 @@ var init_apitogo_config_core = __esm({
736
736
  DEMOMODE: "demoMode",
737
737
  DISPLAYNAME: "displayName",
738
738
  PRICEAMOUNT: "priceAmount",
739
+ PRICINGMODEL: "pricingModel",
740
+ PRICELABEL: "priceLabel",
739
741
  PRICECURRENCY: "priceCurrency",
740
742
  BILLINGPERIOD: "billingPeriod",
741
743
  LIMITSJSON: "limitsJson",
@@ -1020,6 +1022,10 @@ var init_ModulesSchema = __esm({
1020
1022
  sku: z3.string(),
1021
1023
  displayName: z3.string(),
1022
1024
  priceAmount: z3.number().default(0),
1025
+ pricingModel: z3.enum(["fixed", "custom"]).optional().describe(
1026
+ '"custom" shows a contact-sales form instead of checkout; priceAmount must be 0.'
1027
+ ),
1028
+ priceLabel: z3.string().max(64).optional().describe('Text shown instead of the price, e.g. "Custom".'),
1023
1029
  priceCurrency: z3.string().trim().toLowerCase().regex(
1024
1030
  /^[a-z]{3}$/,
1025
1031
  "priceCurrency must be a 3-letter ISO 4217 code (lowercase)"
@@ -1127,6 +1133,8 @@ function plansToPreviewCatalog(plans) {
1127
1133
  sku: plan.sku,
1128
1134
  displayName: plan.displayName,
1129
1135
  priceAmount: plan.priceAmount ?? 0,
1136
+ pricingModel: plan.pricingModel ?? "fixed",
1137
+ ...plan.priceLabel ? { priceLabel: plan.priceLabel } : {},
1130
1138
  priceCurrency: plan.priceCurrency ?? "usd",
1131
1139
  billingPeriod: normalizeBillingPeriod(plan.billingPeriod),
1132
1140
  limits: coerceGatewayLimits(plan) ?? null,
@@ -1175,13 +1183,14 @@ async function readEffectiveManifest(rootDir, _env) {
1175
1183
  return null;
1176
1184
  }
1177
1185
  }
1178
- var BillingPeriodSchema, GatewayLimitsSchema, PriceCurrencySchema, ALLOWED_RUNTIME_VERSIONS, ManifestPlanInputSchema, DevPortalLocalManifestSchema;
1186
+ var BillingPeriodSchema, PlanPricingModelSchema, GatewayLimitsSchema, PriceCurrencySchema, ALLOWED_RUNTIME_VERSIONS, ManifestPlanInputSchema, DevPortalLocalManifestSchema;
1179
1187
  var init_local_manifest = __esm({
1180
1188
  "src/config/local-manifest.ts"() {
1181
1189
  init_apitogo_config_loader();
1182
1190
  init_gateway_limits();
1183
1191
  init_landing_manifest();
1184
1192
  BillingPeriodSchema = z4.enum(["month", "year", "monthly", "annual"]);
1193
+ PlanPricingModelSchema = z4.enum(["fixed", "custom"]);
1185
1194
  GatewayLimitsSchema = z4.object({
1186
1195
  rateLimit: z4.object({
1187
1196
  calls: z4.number(),
@@ -1212,6 +1221,9 @@ var init_local_manifest = __esm({
1212
1221
  sku: z4.string().min(1),
1213
1222
  displayName: z4.string().min(1),
1214
1223
  priceAmount: z4.number().default(0),
1224
+ /** `custom` plans show a contact-sales form instead of checkout. */
1225
+ pricingModel: PlanPricingModelSchema.optional(),
1226
+ priceLabel: z4.string().max(64).optional(),
1215
1227
  priceCurrency: PriceCurrencySchema.optional(),
1216
1228
  billingPeriod: BillingPeriodSchema.optional(),
1217
1229
  limits: GatewayLimitsSchema.optional(),
@@ -5797,7 +5809,7 @@ import {
5797
5809
  // package.json
5798
5810
  var package_default = {
5799
5811
  name: "@lukoweb/apitogo",
5800
- version: "0.1.126",
5812
+ version: "0.1.127",
5801
5813
  type: "module",
5802
5814
  sideEffects: [
5803
5815
  "**/*.css",
@@ -13134,7 +13146,25 @@ function validateManifestForPlatformPublish(manifest) {
13134
13146
  if (plan.priceAmount !== void 0 && plan.priceAmount < 0) {
13135
13147
  issues.push({
13136
13148
  field: `plans[${index}].priceAmount`,
13137
- message: "priceAmount must be >= 0."
13149
+ message: 'priceAmount must be >= 0. For negotiated pricing use pricingModel: "custom".'
13150
+ });
13151
+ }
13152
+ if (plan.pricingModel !== void 0 && plan.pricingModel !== "fixed" && plan.pricingModel !== "custom") {
13153
+ issues.push({
13154
+ field: `plans[${index}].pricingModel`,
13155
+ message: "pricingModel must be 'fixed' or 'custom'."
13156
+ });
13157
+ }
13158
+ if (plan.pricingModel === "custom" && (plan.priceAmount ?? 0) !== 0) {
13159
+ issues.push({
13160
+ field: `plans[${index}].priceAmount`,
13161
+ message: "priceAmount must be 0 (or omitted) for custom-priced plans."
13162
+ });
13163
+ }
13164
+ if (plan.priceLabel !== void 0 && plan.priceLabel.length > 64) {
13165
+ issues.push({
13166
+ field: `plans[${index}].priceLabel`,
13167
+ message: "priceLabel must be at most 64 characters."
13138
13168
  });
13139
13169
  }
13140
13170
  const currencyResult = tryNormalizePriceCurrency(plan.priceCurrency);
@@ -15582,6 +15612,11 @@ function buildPlanItemFromWizard(input5) {
15582
15612
  priceAmount: input5.priceAmount,
15583
15613
  limits: buildLimits(input5.rateLimitCalls, input5.monthlyQuota)
15584
15614
  };
15615
+ if (input5.pricingModel === "custom") {
15616
+ item.priceAmount = 0;
15617
+ item.pricingModel = "custom";
15618
+ return item;
15619
+ }
15585
15620
  if (input5.priceAmount > 0) {
15586
15621
  item.priceCurrency = "usd";
15587
15622
  item.billingPeriod = "month";
@@ -15946,11 +15981,13 @@ async function promptMakeConfigInteractive(options) {
15946
15981
  printDiagnosticsToConsole("Plan title is required.");
15947
15982
  continue;
15948
15983
  }
15949
- const priceAmount = await askNonNegativeNumber(
15984
+ const pricingAnswer = (await ask(
15950
15985
  rl,
15951
- "Price (USD/month, 0 for free)",
15952
- 0
15953
- );
15986
+ "Pricing: fixed price or custom (contact sales)? (fixed/custom)",
15987
+ "fixed"
15988
+ )).toLowerCase();
15989
+ const pricingModel = pricingAnswer.startsWith("c") ? "custom" : "fixed";
15990
+ const priceAmount = pricingModel === "custom" ? 0 : await askNonNegativeNumber(rl, "Price (USD/month, 0 for free)", 0);
15954
15991
  const rateLimitCalls = await askNonNegativeNumber(
15955
15992
  rl,
15956
15993
  "Rate limit (calls per minute)",
@@ -15964,6 +16001,7 @@ async function promptMakeConfigInteractive(options) {
15964
16001
  const planItem = buildPlanItemFromWizard({
15965
16002
  displayName: displayName.trim(),
15966
16003
  priceAmount,
16004
+ pricingModel,
15967
16005
  rateLimitCalls,
15968
16006
  monthlyQuota
15969
16007
  });
@@ -17203,6 +17241,8 @@ async function handleCreateGatewayPlan(client, config2, args) {
17203
17241
  limitsJson: args.limitsJson ?? null,
17204
17242
  includedActionKeys,
17205
17243
  priceAmount: args.priceAmount,
17244
+ pricingModel: args.pricingModel,
17245
+ priceLabel: args.priceLabel,
17206
17246
  priceCurrency: args.priceCurrency,
17207
17247
  billingPeriod: args.billingPeriod,
17208
17248
  sortOrder: args.sortOrder
@@ -17229,6 +17269,8 @@ function buildUpdateGatewayPlanBody(existing, patch) {
17229
17269
  limitsJson: patch.limitsJson !== void 0 ? patch.limitsJson : existing.limitsJson ?? null,
17230
17270
  includedActionKeys: patch.includedActionKeys !== void 0 ? patch.includedActionKeys : existingActionKeys(existing),
17231
17271
  priceAmount: patch.priceAmount ?? existing.priceAmount,
17272
+ pricingModel: patch.pricingModel ?? existing.pricingModel,
17273
+ priceLabel: patch.priceLabel ?? existing.priceLabel ?? void 0,
17232
17274
  priceCurrency: patch.priceCurrency ?? existing.priceCurrency,
17233
17275
  billingPeriod: patch.billingPeriod ?? existing.billingPeriod,
17234
17276
  sortOrder: patch.sortOrder ?? existing.sortOrder
@@ -17509,6 +17551,9 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
17509
17551
  isActive: true,
17510
17552
  limitsJson: planLimitsForPlatform(input5),
17511
17553
  priceAmount: input5.priceAmount,
17554
+ // Config is the source of truth: dropping pricingModel/priceLabel reverts to fixed / no label.
17555
+ pricingModel: input5.pricingModel ?? "fixed",
17556
+ priceLabel: input5.priceLabel ?? "",
17512
17557
  priceCurrency: input5.priceCurrency,
17513
17558
  billingPeriod: input5.billingPeriod ? normalizeBillingPeriod2(input5.billingPeriod) : void 0
17514
17559
  };
@@ -17536,6 +17581,8 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
17536
17581
  sku: input5.sku,
17537
17582
  displayName: input5.displayName,
17538
17583
  priceAmount: input5.priceAmount,
17584
+ pricingModel: input5.pricingModel,
17585
+ priceLabel: input5.priceLabel,
17539
17586
  priceCurrency: input5.priceCurrency ?? "usd",
17540
17587
  billingPeriod: normalizeBillingPeriod2(input5.billingPeriod),
17541
17588
  limitsJson: planLimitsForPlatform(input5),
@@ -106,6 +106,8 @@ export interface GatewayPlanDto {
106
106
  displayName: string;
107
107
  isActive: boolean;
108
108
  priceAmount?: number;
109
+ pricingModel?: "fixed" | "custom";
110
+ priceLabel?: string | null;
109
111
  priceCurrency?: string;
110
112
  billingPeriod?: string;
111
113
  limitsJson?: string | null;
@@ -120,6 +122,8 @@ export interface CreateGatewayPlanRequest {
120
122
  limitsJson?: string | null;
121
123
  includedActionKeys: string[];
122
124
  priceAmount?: number;
125
+ pricingModel?: "fixed" | "custom";
126
+ priceLabel?: string;
123
127
  priceCurrency?: string;
124
128
  billingPeriod?: string;
125
129
  sortOrder?: number;
@@ -325,6 +329,8 @@ export interface UpdateGatewayPlanRequest {
325
329
  limitsJson?: string | null;
326
330
  includedActionKeys: string[];
327
331
  priceAmount?: number;
332
+ pricingModel?: "fixed" | "custom";
333
+ priceLabel?: string;
328
334
  priceCurrency?: string;
329
335
  billingPeriod?: string;
330
336
  sortOrder?: number;
@@ -7,6 +7,8 @@ export interface ManifestPlanInput {
7
7
  sku: string;
8
8
  displayName: string;
9
9
  priceAmount?: number;
10
+ pricingModel?: "fixed" | "custom";
11
+ priceLabel?: string;
10
12
  priceCurrency?: string;
11
13
  billingPeriod?: "month" | "year" | "monthly" | "annual";
12
14
  limits?: GatewayLimits;
@@ -5,6 +5,8 @@ export interface PublishPlanInput {
5
5
  sku: string;
6
6
  displayName: string;
7
7
  priceAmount?: number;
8
+ pricingModel?: string;
9
+ priceLabel?: string;
8
10
  priceCurrency?: string;
9
11
  billingPeriod?: string;
10
12
  limits?: GatewayLimits;
@@ -42,6 +42,8 @@ export declare function handleCreateGatewayPlan(client: PlatformApiClient, confi
42
42
  limitsJson?: string;
43
43
  includedActionKeys?: string[];
44
44
  priceAmount?: number;
45
+ pricingModel?: "fixed" | "custom";
46
+ priceLabel?: string;
45
47
  priceCurrency?: string;
46
48
  billingPeriod?: string;
47
49
  sortOrder?: number;
@@ -59,6 +61,8 @@ export declare function handleUpdateGatewayPlan(client: PlatformApiClient, confi
59
61
  limitsJson?: string;
60
62
  includedActionKeys?: string[];
61
63
  priceAmount?: number;
64
+ pricingModel?: "fixed" | "custom";
65
+ priceLabel?: string;
62
66
  priceCurrency?: string;
63
67
  billingPeriod?: string;
64
68
  sortOrder?: number;
@@ -71,6 +75,8 @@ export declare function handlePatchGatewayPlan(client: PlatformApiClient, config
71
75
  limitsJson?: string | null;
72
76
  includedActionKeys?: string[];
73
77
  priceAmount?: number;
78
+ pricingModel?: "fixed" | "custom";
79
+ priceLabel?: string;
74
80
  priceCurrency?: string;
75
81
  billingPeriod?: string;
76
82
  sortOrder?: number;
@@ -9,11 +9,21 @@ export declare const APITOGO_MANIFEST_ENV_FILES: {
9
9
  };
10
10
  export { APITOGO_CONFIG_FILENAME };
11
11
  export type ApitogoManifestEnv = keyof typeof APITOGO_MANIFEST_ENV_FILES;
12
+ export declare const PlanPricingModelSchema: z.ZodEnum<{
13
+ fixed: "fixed";
14
+ custom: "custom";
15
+ }>;
16
+ export type PlanPricingModel = z.infer<typeof PlanPricingModelSchema>;
12
17
  export declare const ALLOWED_RUNTIME_VERSIONS: readonly ["DOTNET_10", "DOTNET_9", "DOTNET_8", "NODE_24_LTS", "NODE_22_LTS", "PYTHON_314", "PYTHON_313", "PYTHON_312", "PYTHON_311", "PYTHON_310"];
13
18
  export declare const ManifestPlanInputSchema: z.ZodObject<{
14
19
  sku: z.ZodString;
15
20
  displayName: z.ZodString;
16
21
  priceAmount: z.ZodDefault<z.ZodNumber>;
22
+ pricingModel: z.ZodOptional<z.ZodEnum<{
23
+ fixed: "fixed";
24
+ custom: "custom";
25
+ }>>;
26
+ priceLabel: z.ZodOptional<z.ZodString>;
17
27
  priceCurrency: z.ZodOptional<z.ZodString>;
18
28
  billingPeriod: z.ZodOptional<z.ZodEnum<{
19
29
  month: "month";
@@ -34,6 +44,9 @@ export declare const ManifestPlanInputSchema: z.ZodObject<{
34
44
  limitsJson: z.ZodOptional<z.ZodString>;
35
45
  includedActionKeys: z.ZodOptional<z.ZodArray<z.ZodString>>;
36
46
  }, z.core.$strip>;
47
+ export declare function isPlanCustomPriced(plan: {
48
+ pricingModel?: string | null;
49
+ }): boolean;
37
50
  export declare function isPlanFree(plan: {
38
51
  priceAmount?: number;
39
52
  }): boolean;
@@ -68,6 +81,11 @@ export declare const DevPortalLocalManifestSchema: z.ZodObject<{
68
81
  sku: z.ZodString;
69
82
  displayName: z.ZodString;
70
83
  priceAmount: z.ZodDefault<z.ZodNumber>;
84
+ pricingModel: z.ZodOptional<z.ZodEnum<{
85
+ fixed: "fixed";
86
+ custom: "custom";
87
+ }>>;
88
+ priceLabel: z.ZodOptional<z.ZodString>;
71
89
  priceCurrency: z.ZodOptional<z.ZodString>;
72
90
  billingPeriod: z.ZodOptional<z.ZodEnum<{
73
91
  month: "month";
@@ -230,6 +248,8 @@ export type DevPortalPreviewPlan = {
230
248
  sku: string;
231
249
  displayName: string;
232
250
  priceAmount: number;
251
+ pricingModel: PlanPricingModel;
252
+ priceLabel?: string;
233
253
  priceCurrency: string;
234
254
  billingPeriod: string;
235
255
  limits: GatewayLimits | null;
@@ -388,6 +388,11 @@ declare const PlanCatalogItemSchema: z.ZodObject<{
388
388
  sku: z.ZodString;
389
389
  displayName: z.ZodString;
390
390
  priceAmount: z.ZodDefault<z.ZodNumber>;
391
+ pricingModel: z.ZodOptional<z.ZodEnum<{
392
+ fixed: "fixed";
393
+ custom: "custom";
394
+ }>>;
395
+ priceLabel: z.ZodOptional<z.ZodString>;
391
396
  priceCurrency: z.ZodOptional<z.ZodString>;
392
397
  billingPeriod: z.ZodOptional<z.ZodString>;
393
398
  limits: z.ZodOptional<z.ZodObject<{
@@ -421,6 +426,11 @@ export declare const PlansModuleSchema: z.ZodObject<{
421
426
  sku: z.ZodString;
422
427
  displayName: z.ZodString;
423
428
  priceAmount: z.ZodDefault<z.ZodNumber>;
429
+ pricingModel: z.ZodOptional<z.ZodEnum<{
430
+ fixed: "fixed";
431
+ custom: "custom";
432
+ }>>;
433
+ priceLabel: z.ZodOptional<z.ZodString>;
424
434
  priceCurrency: z.ZodOptional<z.ZodString>;
425
435
  billingPeriod: z.ZodOptional<z.ZodString>;
426
436
  limits: z.ZodOptional<z.ZodObject<{
@@ -507,6 +517,11 @@ export declare const UserPanelModuleSchema: z.ZodObject<{
507
517
  sku: z.ZodString;
508
518
  displayName: z.ZodString;
509
519
  priceAmount: z.ZodDefault<z.ZodNumber>;
520
+ pricingModel: z.ZodOptional<z.ZodEnum<{
521
+ fixed: "fixed";
522
+ custom: "custom";
523
+ }>>;
524
+ priceLabel: z.ZodOptional<z.ZodString>;
510
525
  priceCurrency: z.ZodOptional<z.ZodString>;
511
526
  billingPeriod: z.ZodOptional<z.ZodString>;
512
527
  limits: z.ZodOptional<z.ZodObject<{
@@ -705,6 +720,11 @@ export declare const ModulesSchema: z.ZodOptional<z.ZodObject<{
705
720
  sku: z.ZodString;
706
721
  displayName: z.ZodString;
707
722
  priceAmount: z.ZodDefault<z.ZodNumber>;
723
+ pricingModel: z.ZodOptional<z.ZodEnum<{
724
+ fixed: "fixed";
725
+ custom: "custom";
726
+ }>>;
727
+ priceLabel: z.ZodOptional<z.ZodString>;
708
728
  priceCurrency: z.ZodOptional<z.ZodString>;
709
729
  billingPeriod: z.ZodOptional<z.ZodString>;
710
730
  limits: z.ZodOptional<z.ZodObject<{
@@ -7039,6 +7039,11 @@ export declare const ZudokuConfig: z.ZodObject<{
7039
7039
  sku: z.ZodString;
7040
7040
  displayName: z.ZodString;
7041
7041
  priceAmount: z.ZodDefault<z.ZodNumber>;
7042
+ pricingModel: z.ZodOptional<z.ZodEnum<{
7043
+ fixed: "fixed";
7044
+ custom: "custom";
7045
+ }>>;
7046
+ priceLabel: z.ZodOptional<z.ZodString>;
7042
7047
  priceCurrency: z.ZodOptional<z.ZodString>;
7043
7048
  billingPeriod: z.ZodOptional<z.ZodString>;
7044
7049
  limits: z.ZodOptional<z.ZodObject<{
@@ -68,6 +68,14 @@ export type _Schema37 = {
68
68
  sku: string
69
69
  displayName: string
70
70
  priceAmount?: number
71
+ /**
72
+ * "custom" shows a contact-sales form instead of checkout; priceAmount must be 0.
73
+ */
74
+ pricingModel?: ("fixed" | "custom")
75
+ /**
76
+ * Text shown instead of the price, e.g. "Custom".
77
+ */
78
+ priceLabel?: string
71
79
  priceCurrency?: string
72
80
  billingPeriod?: string
73
81
  limits?: {
@@ -86,31 +94,31 @@ export type _Schema37 = {
86
94
  limitsJson?: string
87
95
  includedActionKeys?: string[]
88
96
  }[]
89
- export type _Schema38 = ({
97
+ export type _Schema40 = ({
90
98
  type: "url"
91
- input: (string | _Schema39[])
92
- server?: _Schema40
93
- path?: _Schema41
94
- categories?: _Schema42
95
- options?: _Schema44
99
+ input: (string | _Schema41[])
100
+ server?: _Schema42
101
+ path?: _Schema43
102
+ categories?: _Schema44
103
+ options?: _Schema46
96
104
  } | {
97
105
  type: "file"
98
- input: (string | (string | _Schema39)[])
99
- server?: _Schema40
100
- path?: _Schema41
101
- categories?: _Schema42
102
- options?: _Schema44
106
+ input: (string | (string | _Schema41)[])
107
+ server?: _Schema42
108
+ path?: _Schema43
109
+ categories?: _Schema44
110
+ options?: _Schema46
103
111
  } | {
104
112
  type: "raw"
105
113
  input: string
106
- server?: _Schema40
107
- path?: _Schema41
108
- categories?: _Schema42
109
- options?: _Schema44
114
+ server?: _Schema42
115
+ path?: _Schema43
116
+ categories?: _Schema44
117
+ options?: _Schema46
110
118
  })
111
- export type _Schema40 = string
112
- export type _Schema41 = string
113
- export type _Schema42 = {
119
+ export type _Schema42 = string
120
+ export type _Schema43 = string
121
+ export type _Schema44 = {
114
122
  label: string
115
123
  tags: string[]
116
124
  }[]
@@ -412,8 +420,8 @@ export interface FlatZudokuConfig {
412
420
  landing?: _Schema16
413
421
  userPanel?: _Schema25
414
422
  }
415
- apis?: (_Schema38 | _Schema38[])
416
- catalogs?: (_Schema45 | _Schema45[])
423
+ apis?: (_Schema40 | _Schema40[])
424
+ catalogs?: (_Schema47 | _Schema47[])
417
425
  apiKeys?: {
418
426
  enabled: boolean
419
427
  getConsumers?: unknown
@@ -441,7 +449,7 @@ export interface FlatZudokuConfig {
441
449
  }
442
450
  enableStatusPages?: boolean
443
451
  defaults?: {
444
- apis: _Schema44
452
+ apis: _Schema46
445
453
  examplesLanguage?: string
446
454
  }
447
455
  __pluginDirs?: string[]
@@ -731,12 +739,12 @@ export interface _Schema36 {
731
739
  description?: string
732
740
  }[]
733
741
  }
734
- export interface _Schema39 {
742
+ export interface _Schema41 {
735
743
  path: string
736
744
  input: string
737
745
  label?: string
738
746
  }
739
- export interface _Schema44 {
747
+ export interface _Schema46 {
740
748
  examplesLanguage?: string
741
749
  supportedLanguages?: {
742
750
  value: string
@@ -753,7 +761,7 @@ export interface _Schema44 {
753
761
  transformExamples?: unknown
754
762
  generateCodeSnippet?: unknown
755
763
  }
756
- export interface _Schema45 {
764
+ export interface _Schema47 {
757
765
  path: string
758
766
  label: string
759
767
  items?: string[]
@@ -67,6 +67,10 @@ npx apitogo config
67
67
  - **`sku`** — stable identifier (used in checkout URLs and gateway sync).
68
68
  - **`displayName`** — label on `/pricing`.
69
69
  - **`priceAmount`** — `0` for free tiers; greater than zero for paid plans (USD by default).
70
+ - **`pricingModel`** — `fixed` (default) or `custom`. See
71
+ [Custom pricing](#custom-pricing-contact-sales).
72
+ - **`priceLabel`** — optional text shown instead of the price, such as `"Let's talk"` (max 64
73
+ characters).
70
74
  - **`limits`** — rate limit and quota policies (see below). Encoded `limitsJson` strings are still
71
75
  accepted on load.
72
76
 
@@ -90,6 +94,28 @@ OpenAPI is deployed, you can scope a plan to specific operations with `includedA
90
94
  example `get:/orders/{id}`). Edit those fields in `apitogo.config.json` — see
91
95
  [Manifest](./manifest.md).
92
96
 
97
+ ## Custom pricing (contact sales)
98
+
99
+ Set `pricingModel: "custom"` for plans with negotiated prices, such as an Enterprise tier. Leave
100
+ `priceAmount` at `0` or omit it.
101
+
102
+ ```json
103
+ {
104
+ "sku": "enterprise",
105
+ "displayName": "Enterprise",
106
+ "pricingModel": "custom",
107
+ "priceLabel": "Let's talk"
108
+ }
109
+ ```
110
+
111
+ On `/pricing`, custom plans show `priceLabel` (or **Custom**) and a **Contact sales** button. The
112
+ button opens a public `/contact-sales` form instead of Stripe checkout or registration. Visitors
113
+ don't need an account.
114
+
115
+ Each submission is emailed to the project's **sales contact email**, or to the project owner's email
116
+ when none is set. Custom plans can't be subscribed to through checkout. Custom plans are available
117
+ on every platform plan, including Free.
118
+
93
119
  ## Publish
94
120
 
95
121
  Gateway plans are applied when you run:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo",
3
- "version": "0.1.126",
3
+ "version": "0.1.127",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -167,6 +167,16 @@
167
167
  "default": "./src/cli/platform/apply/index.ts"
168
168
  }
169
169
  },
170
+ "scripts": {
171
+ "build": "esbuild src/cli/cli.ts src/vite/prerender/worker.ts --outdir=dist/cli --entry-names=[name] --bundle --format=esm --packages=external --target=node20 --platform=node --log-level=error",
172
+ "postbuild": "tsx scripts/check-external-deps.ts",
173
+ "typecheck": "tsc --project tsconfig.app.json --noEmit",
174
+ "generate:types": "tsx scripts/generate-types.js && tsx scripts/generate-flat-config.js",
175
+ "build:standalone": "vite build --mode standalone --config vite.standalone.config.ts --log-level=error",
176
+ "prepack": "npm run build && tsx scripts/generate-publish-exports.ts",
177
+ "clean": "rm -rf dist",
178
+ "codegen": "graphql-codegen --config ./src/codegen.ts"
179
+ },
170
180
  "dependencies": {
171
181
  "@apidevtools/json-schema-ref-parser": "15.3.1",
172
182
  "@apidevtools/swagger-parser": "^12.1.0",
@@ -346,13 +356,148 @@
346
356
  "optional": true
347
357
  }
348
358
  },
349
- "scripts": {
350
- "build": "esbuild src/cli/cli.ts src/vite/prerender/worker.ts --outdir=dist/cli --entry-names=[name] --bundle --format=esm --packages=external --target=node20 --platform=node --log-level=error",
351
- "postbuild": "tsx scripts/check-external-deps.ts",
352
- "typecheck": "tsc --project tsconfig.app.json --noEmit",
353
- "generate:types": "tsx scripts/generate-types.js && tsx scripts/generate-flat-config.js",
354
- "build:standalone": "vite build --mode standalone --config vite.standalone.config.ts --log-level=error",
355
- "clean": "rm -rf dist",
356
- "codegen": "graphql-codegen --config ./src/codegen.ts"
359
+ "publishConfig": {
360
+ "exports": {
361
+ ".": {
362
+ "types": "./dist/declarations/index.d.ts",
363
+ "default": "./src/index.ts"
364
+ },
365
+ "./package.json": "./package.json",
366
+ "./ui/*": {
367
+ "types": "./dist/declarations/lib/ui/*.d.ts",
368
+ "default": "./src/lib/ui/*.tsx"
369
+ },
370
+ "./ui/*.js": {
371
+ "types": "./dist/declarations/lib/ui/*.d.ts",
372
+ "default": "./src/lib/ui/*.tsx"
373
+ },
374
+ "./client": {
375
+ "types": "./client.d.ts"
376
+ },
377
+ "./auth/clerk": {
378
+ "types": "./dist/declarations/lib/authentication/providers/clerk.d.ts",
379
+ "default": "./src/lib/authentication/providers/clerk.tsx"
380
+ },
381
+ "./auth/auth0": {
382
+ "types": "./dist/declarations/lib/authentication/providers/auth0.d.ts",
383
+ "default": "./src/lib/authentication/providers/auth0.tsx"
384
+ },
385
+ "./auth/openid": {
386
+ "types": "./dist/declarations/lib/authentication/providers/openid.d.ts",
387
+ "default": "./src/lib/authentication/providers/openid.tsx"
388
+ },
389
+ "./auth/supabase": {
390
+ "types": "./dist/declarations/lib/authentication/providers/supabase.d.ts",
391
+ "default": "./src/lib/authentication/providers/supabase.tsx"
392
+ },
393
+ "./auth/azureb2c": {
394
+ "types": "./dist/declarations/lib/authentication/providers/azureb2c.d.ts",
395
+ "default": "./src/lib/authentication/providers/azureb2c.tsx"
396
+ },
397
+ "./auth/firebase": {
398
+ "types": "./dist/declarations/lib/authentication/providers/firebase.d.ts",
399
+ "default": "./src/lib/authentication/providers/firebase.tsx"
400
+ },
401
+ "./auth/dev-portal": {
402
+ "types": "./dist/declarations/lib/authentication/providers/dev-portal.d.ts",
403
+ "default": "./src/lib/authentication/providers/dev-portal.tsx"
404
+ },
405
+ "./plugins": {
406
+ "types": "./dist/declarations/lib/core/plugins.d.ts",
407
+ "default": "./src/lib/core/plugins.ts"
408
+ },
409
+ "./plugins/api-keys": {
410
+ "types": "./dist/declarations/lib/plugins/api-keys/index.d.ts",
411
+ "default": "./src/lib/plugins/api-keys/index.tsx"
412
+ },
413
+ "./plugins/markdown": {
414
+ "types": "./dist/declarations/lib/plugins/markdown/index.d.ts",
415
+ "default": "./src/lib/plugins/markdown/index.tsx"
416
+ },
417
+ "./plugins/openapi": {
418
+ "types": "./dist/declarations/lib/plugins/openapi/index.d.ts",
419
+ "default": "./src/lib/plugins/openapi/index.tsx"
420
+ },
421
+ "./plugins/custom-pages": {
422
+ "types": "./dist/declarations/lib/plugins/custom-pages/index.d.ts",
423
+ "default": "./src/lib/plugins/custom-pages/index.tsx"
424
+ },
425
+ "./plugins/search-inkeep": {
426
+ "types": "./dist/declarations/lib/plugins/search-inkeep/index.d.ts",
427
+ "default": "./src/lib/plugins/search-inkeep/index.tsx"
428
+ },
429
+ "./plugins/search-pagefind": {
430
+ "types": "./dist/declarations/lib/plugins/search-pagefind/index.d.ts",
431
+ "default": "./src/lib/plugins/search-pagefind/index.tsx"
432
+ },
433
+ "./plugins/api-catalog": {
434
+ "types": "./dist/declarations/lib/plugins/api-catalog/index.d.ts",
435
+ "default": "./src/lib/plugins/api-catalog/index.tsx"
436
+ },
437
+ "./modules/landing": "@lukoweb/apitogo-module-landing",
438
+ "./modules/user-panel": "@lukoweb/apitogo-module-user-panel",
439
+ "./components": {
440
+ "types": "./dist/declarations/lib/components/index.d.ts",
441
+ "default": "./src/lib/components/index.ts"
442
+ },
443
+ "./mermaid": {
444
+ "types": "./dist/declarations/lib/components/Mermaid.d.ts",
445
+ "default": "./src/lib/components/Mermaid.tsx"
446
+ },
447
+ "./router": {
448
+ "types": "./dist/declarations/lib/core/router.d.ts",
449
+ "default": "./src/lib/core/router.ts"
450
+ },
451
+ "./react-query": {
452
+ "types": "./dist/declarations/lib/core/react-query.d.ts",
453
+ "default": "./src/lib/core/react-query.ts"
454
+ },
455
+ "./icons": {
456
+ "types": "./dist/declarations/lib/icons.d.ts",
457
+ "default": "./src/lib/icons.ts"
458
+ },
459
+ "./vite": {
460
+ "types": "./dist/declarations/vite/index.d.ts",
461
+ "default": "./src/vite/index.ts"
462
+ },
463
+ "./app/*": {
464
+ "types": "./dist/declarations/app/*.d.ts",
465
+ "default": "./src/app/*"
466
+ },
467
+ "./hooks": {
468
+ "types": "./dist/declarations/lib/hooks/index.d.ts",
469
+ "default": "./src/lib/hooks/index.ts"
470
+ },
471
+ "./main.css": "./src/app/main.css",
472
+ "./processors/*": {
473
+ "types": "./dist/declarations/lib/plugins/openapi/processors/*.d.ts",
474
+ "default": "./src/lib/plugins/openapi/processors/*.ts"
475
+ },
476
+ "./with-zuplo": {
477
+ "types": "./dist/declarations/zuplo/with-zuplo.d.ts",
478
+ "default": "./src/zuplo/with-zuplo.ts"
479
+ },
480
+ "./testing": {
481
+ "types": "./dist/declarations/lib/testing/index.d.ts",
482
+ "default": "./src/lib/testing/index.tsx"
483
+ },
484
+ "./local-manifest": {
485
+ "types": "./dist/declarations/config/local-manifest.d.ts",
486
+ "default": "./src/config/local-manifest.ts"
487
+ },
488
+ "./build-config": {
489
+ "types": "./dist/declarations/config/build-apitogo-config.d.ts",
490
+ "default": "./src/config/build-apitogo-config.ts"
491
+ },
492
+ "./config-loader": {
493
+ "types": "./dist/declarations/config/apitogo-config-loader.d.ts",
494
+ "default": "./src/config/apitogo-config-loader.ts"
495
+ },
496
+ "./apply": {
497
+ "types": "./dist/declarations/cli/platform/apply/index.d.ts",
498
+ "default": "./src/cli/platform/apply/index.ts"
499
+ }
500
+ },
501
+ "access": "public"
357
502
  }
358
- }
503
+ }
@@ -106,6 +106,8 @@ const COLLAPSED_UPPERCASE_ALIASES: Record<string, string> = {
106
106
  DEMOMODE: "demoMode",
107
107
  DISPLAYNAME: "displayName",
108
108
  PRICEAMOUNT: "priceAmount",
109
+ PRICINGMODEL: "pricingModel",
110
+ PRICELABEL: "priceLabel",
109
111
  PRICECURRENCY: "priceCurrency",
110
112
  BILLINGPERIOD: "billingPeriod",
111
113
  LIMITSJSON: "limitsJson",
@@ -36,6 +36,10 @@ export type ApitogoManifestEnv = keyof typeof APITOGO_MANIFEST_ENV_FILES;
36
36
 
37
37
  const BillingPeriodSchema = z.enum(["month", "year", "monthly", "annual"]);
38
38
 
39
+ export const PlanPricingModelSchema = z.enum(["fixed", "custom"]);
40
+
41
+ export type PlanPricingModel = z.infer<typeof PlanPricingModelSchema>;
42
+
39
43
  const GatewayLimitsSchema = z
40
44
  .object({
41
45
  rateLimit: z
@@ -81,6 +85,9 @@ export const ManifestPlanInputSchema = z.object({
81
85
  sku: z.string().min(1),
82
86
  displayName: z.string().min(1),
83
87
  priceAmount: z.number().default(0),
88
+ /** `custom` plans show a contact-sales form instead of checkout. */
89
+ pricingModel: PlanPricingModelSchema.optional(),
90
+ priceLabel: z.string().max(64).optional(),
84
91
  priceCurrency: PriceCurrencySchema.optional(),
85
92
  billingPeriod: BillingPeriodSchema.optional(),
86
93
  limits: GatewayLimitsSchema.optional(),
@@ -89,6 +96,12 @@ export const ManifestPlanInputSchema = z.object({
89
96
  includedActionKeys: z.array(z.string()).optional(),
90
97
  });
91
98
 
99
+ export function isPlanCustomPriced(plan: {
100
+ pricingModel?: string | null;
101
+ }): boolean {
102
+ return plan.pricingModel === "custom";
103
+ }
104
+
92
105
  /** Free plans have zero price; paid plans have priceAmount greater than zero. */
93
106
  export function isPlanFree(plan: { priceAmount?: number }): boolean {
94
107
  return (plan.priceAmount ?? 0) <= 0;
@@ -198,6 +211,8 @@ export type DevPortalPreviewPlan = {
198
211
  sku: string;
199
212
  displayName: string;
200
213
  priceAmount: number;
214
+ pricingModel: PlanPricingModel;
215
+ priceLabel?: string;
201
216
  priceCurrency: string;
202
217
  billingPeriod: string;
203
218
  limits: GatewayLimits | null;
@@ -272,6 +287,8 @@ export function plansToPreviewCatalog(
272
287
  sku: plan.sku,
273
288
  displayName: plan.displayName,
274
289
  priceAmount: plan.priceAmount ?? 0,
290
+ pricingModel: plan.pricingModel ?? "fixed",
291
+ ...(plan.priceLabel ? { priceLabel: plan.priceLabel } : {}),
275
292
  priceCurrency: plan.priceCurrency ?? "usd",
276
293
  billingPeriod: normalizeBillingPeriod(plan.billingPeriod),
277
294
  limits: coerceGatewayLimits(plan) ?? null,
@@ -264,6 +264,17 @@ const PlanCatalogItemSchema = z.object({
264
264
  sku: z.string(),
265
265
  displayName: z.string(),
266
266
  priceAmount: z.number().default(0),
267
+ pricingModel: z
268
+ .enum(["fixed", "custom"])
269
+ .optional()
270
+ .describe(
271
+ '"custom" shows a contact-sales form instead of checkout; priceAmount must be 0.',
272
+ ),
273
+ priceLabel: z
274
+ .string()
275
+ .max(64)
276
+ .optional()
277
+ .describe('Text shown instead of the price, e.g. "Custom".'),
267
278
  priceCurrency: z
268
279
  .string()
269
280
  .trim()
package/LICENSE.md DELETED
@@ -1,18 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) Zuplo, Inc.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
- associated documentation files (the "Software"), to deal in the Software without restriction,
7
- including without limitation the rights to use, copy, modify, merge, publish, distribute,
8
- sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
9
- furnished to do so, subject to the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be included in all copies or substantial
12
- portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
17
- OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.