@lukoweb/apitogo 0.2.40 → 0.2.42
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 +617 -134
- package/dist/declarations/cli/platform/helpers/progress.d.ts +1 -0
- package/dist/declarations/cli/platform/tools/lifecycle.d.ts +4 -0
- package/docs/configuration/gateway.md +276 -273
- package/package.json +1 -1
- package/src/config/v2/resolve.ts +5 -0
- package/src/config/v2/rules.ts +14 -1
- package/src/config/v2/schema.ts +2 -0
- package/src/config/v2/services.ts +147 -17
- package/src/config/v2/starter-landing.ts +150 -0
- package/src/config/v2/wrapper.ts +58 -17
package/dist/cli/cli.js
CHANGED
|
@@ -5463,6 +5463,7 @@ import colors4 from "picocolors";
|
|
|
5463
5463
|
import { gt } from "semver";
|
|
5464
5464
|
|
|
5465
5465
|
// src/cli/common/json-only-deploy.ts
|
|
5466
|
+
init_logger();
|
|
5466
5467
|
function isDeployJsonOnlyArgv() {
|
|
5467
5468
|
const args = process.argv;
|
|
5468
5469
|
let cmdIdx = -1;
|
|
@@ -5478,6 +5479,14 @@ function isDeployJsonOnlyArgv() {
|
|
|
5478
5479
|
}
|
|
5479
5480
|
return args.slice(cmdIdx).some((a) => a === "--json-only" || a.startsWith("--json-only="));
|
|
5480
5481
|
}
|
|
5482
|
+
function applyJsonOnlyDeployQuietMode() {
|
|
5483
|
+
process.env.APITOGO_JSON_ONLY = "1";
|
|
5484
|
+
const noop = () => {
|
|
5485
|
+
};
|
|
5486
|
+
logger.info = noop;
|
|
5487
|
+
logger.warn = noop;
|
|
5488
|
+
logger.warnOnce = noop;
|
|
5489
|
+
}
|
|
5481
5490
|
|
|
5482
5491
|
// src/cli/common/output.ts
|
|
5483
5492
|
import * as Sentry from "@sentry/node";
|
|
@@ -5886,6 +5895,8 @@ var PortalSchema = z.strictObject({
|
|
|
5886
5895
|
}).optional(),
|
|
5887
5896
|
console: z.strictObject({ enabled }).optional(),
|
|
5888
5897
|
theme: path3.optional(),
|
|
5898
|
+
/** A custom home page: a React component file that the portal serves at `/`. */
|
|
5899
|
+
landing: path3.optional(),
|
|
5889
5900
|
requireAuth: z.union([z.boolean(), z.strictObject({ exclude: pathList })], {
|
|
5890
5901
|
error: 'must be true, false or { "exclude": ["/route", ...] }'
|
|
5891
5902
|
}).optional(),
|
|
@@ -6447,6 +6458,7 @@ function resolveConfigV2(config2, configPath) {
|
|
|
6447
6458
|
},
|
|
6448
6459
|
console: { enabled: portal.console?.enabled ?? true },
|
|
6449
6460
|
theme: portal.theme ? path7.resolve(configDir, portal.theme) : path7.join(dir, "theme.json"),
|
|
6461
|
+
...portal.landing ? { landing: path7.resolve(configDir, portal.landing) } : {},
|
|
6450
6462
|
requireAuth: portal.requireAuth ?? false,
|
|
6451
6463
|
search: provider === "external" ? "none" : portal.search ?? "pagefind"
|
|
6452
6464
|
};
|
|
@@ -6563,7 +6575,7 @@ function loadConfigV2(pathOrDir) {
|
|
|
6563
6575
|
}
|
|
6564
6576
|
|
|
6565
6577
|
// src/config/v2/rules.ts
|
|
6566
|
-
import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
|
|
6578
|
+
import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "node:fs/promises";
|
|
6567
6579
|
import path9 from "node:path";
|
|
6568
6580
|
import { glob } from "glob";
|
|
6569
6581
|
import { parse as parseYaml } from "yaml";
|
|
@@ -6575,6 +6587,10 @@ async function isFile(target) {
|
|
|
6575
6587
|
async function isDirectory(target) {
|
|
6576
6588
|
return (await stat2(target).catch(() => void 0))?.isDirectory() === true;
|
|
6577
6589
|
}
|
|
6590
|
+
async function isEmptyFolder(target) {
|
|
6591
|
+
const entries = await readdir2(target).catch(() => []);
|
|
6592
|
+
return entries.every((name) => name === ".gitkeep");
|
|
6593
|
+
}
|
|
6578
6594
|
function collectSecretRefs(config2) {
|
|
6579
6595
|
const found = [];
|
|
6580
6596
|
const walk = (value, at) => {
|
|
@@ -6816,6 +6832,13 @@ async function gatewayRules(resolved, out) {
|
|
|
6816
6832
|
message: `gateway.origin.source does not exist: ${inside === "" ? "." : inside}.`,
|
|
6817
6833
|
hint: "Point it at the folder that holds your backend code."
|
|
6818
6834
|
});
|
|
6835
|
+
} else if (await isEmptyFolder(origin.source)) {
|
|
6836
|
+
out.warn({
|
|
6837
|
+
code: "GATEWAY_BACKEND_EMPTY",
|
|
6838
|
+
path: "gateway.origin.source",
|
|
6839
|
+
message: `gateway.origin.source (${inside}) is empty: there is no code to run yet.`,
|
|
6840
|
+
hint: "Put your backend code in that folder before you deploy."
|
|
6841
|
+
});
|
|
6819
6842
|
}
|
|
6820
6843
|
}
|
|
6821
6844
|
if (origin.url !== void 0) {
|
|
@@ -7934,7 +7957,9 @@ function planPortal(profile, manifest) {
|
|
|
7934
7957
|
keysApiBaseUrl: plans.keysApiBaseUrl,
|
|
7935
7958
|
billing: plans.billing,
|
|
7936
7959
|
links: {
|
|
7937
|
-
|
|
7960
|
+
// Hosted docs are linked once their pages are known (see `buildPortalWorkspace`): there is no page to
|
|
7961
|
+
// name before that, and a guessed one was a 404 on every portal whose first page is not called `introduction`.
|
|
7962
|
+
...!profile.modules.docs && profile.docs.url ? { docs: profile.docs.url } : {},
|
|
7938
7963
|
...plan2.apiReference ? { api: `/${API_REFERENCE_PATH}` } : {}
|
|
7939
7964
|
}
|
|
7940
7965
|
};
|
|
@@ -7964,6 +7989,15 @@ function buildSiteConfig(resolved, plan2, theme, extras = {}) {
|
|
|
7964
7989
|
},
|
|
7965
7990
|
modules: {
|
|
7966
7991
|
docs: { enabled: plan2.docs },
|
|
7992
|
+
// A custom home page (`portal.landing`) is served at `/`; the build imports the component from its absolute path.
|
|
7993
|
+
...portal?.landing ? {
|
|
7994
|
+
landing: {
|
|
7995
|
+
enabled: true,
|
|
7996
|
+
path: "/",
|
|
7997
|
+
layout: "landing",
|
|
7998
|
+
page: portal.landing.split(path12.sep).join("/")
|
|
7999
|
+
}
|
|
8000
|
+
} : {},
|
|
7967
8001
|
// The plans pages (pricing, choosing a plan) exist only when billing is on, and the plans service is what makes it so.
|
|
7968
8002
|
// Switching billing on also makes the user panel register its own placeholder plans page, at `/account/<path>`.
|
|
7969
8003
|
// Its default path is `plans`, which is where the billing plugin puts the real page (subscribe, switch, cancel),
|
|
@@ -7992,6 +8026,14 @@ function buildSiteConfig(resolved, plan2, theme, extras = {}) {
|
|
|
7992
8026
|
]
|
|
7993
8027
|
} : {},
|
|
7994
8028
|
...navigation.length > 0 ? { navigation } : {},
|
|
8029
|
+
// The sidebar link above is only seen inside the docs; the header is what every page shows.
|
|
8030
|
+
...plan2.apiReference ? {
|
|
8031
|
+
header: {
|
|
8032
|
+
navigation: [
|
|
8033
|
+
{ label: "API Reference", to: `/${API_REFERENCE_PATH}` }
|
|
8034
|
+
]
|
|
8035
|
+
}
|
|
8036
|
+
} : {},
|
|
7995
8037
|
...redirects.length > 0 ? { redirects } : {},
|
|
7996
8038
|
...extras.protectedRoutes?.length ? { protectedRoutes: extras.protectedRoutes } : {},
|
|
7997
8039
|
...theme !== void 0 ? { theme } : {},
|
|
@@ -8100,7 +8142,14 @@ async function preparePortal(resolved, manifest, mode, options = {}) {
|
|
|
8100
8142
|
const hostedDocRoutes = plan2.docs ? await docRoutes(profile.docs.content, {
|
|
8101
8143
|
includeDrafts: mode === "development"
|
|
8102
8144
|
}) : [];
|
|
8103
|
-
|
|
8145
|
+
if (plan2.plansPlugin && plan2.docs) {
|
|
8146
|
+
const landing = hostedDocRoutes.some(
|
|
8147
|
+
(route) => route === "/" || route === "/index"
|
|
8148
|
+
) ? "/" : pickLandingRoute(hostedDocRoutes);
|
|
8149
|
+
if (landing)
|
|
8150
|
+
plan2.plansPlugin.links = { ...plan2.plansPlugin.links, docs: landing };
|
|
8151
|
+
}
|
|
8152
|
+
const home = resolved.portal?.landing ? void 0 : homeRedirect({
|
|
8104
8153
|
hostsDocs: plan2.docs,
|
|
8105
8154
|
docRoutes: hostedDocRoutes,
|
|
8106
8155
|
externalDocsUrl: profile.docs.url,
|
|
@@ -8277,8 +8326,156 @@ async function portalArtifactHash(resolved, manifestText, cliVersion, wrapper =
|
|
|
8277
8326
|
|
|
8278
8327
|
// src/config/v2/services.ts
|
|
8279
8328
|
init_invariant();
|
|
8280
|
-
import {
|
|
8329
|
+
import {
|
|
8330
|
+
access as access3,
|
|
8331
|
+
mkdir as mkdir5,
|
|
8332
|
+
readdir as readdir3,
|
|
8333
|
+
rename as rename3,
|
|
8334
|
+
rm as rm4,
|
|
8335
|
+
stat as stat3,
|
|
8336
|
+
writeFile as writeFile7
|
|
8337
|
+
} from "node:fs/promises";
|
|
8281
8338
|
import path14 from "node:path";
|
|
8339
|
+
|
|
8340
|
+
// src/config/v2/starter-landing.ts
|
|
8341
|
+
function starterLandingPage(options) {
|
|
8342
|
+
const name = JSON.stringify(options.name);
|
|
8343
|
+
const plansImport = options.withPlans ? 'import { usePlansPreview } from "@lukoweb/apitogo-plugin-dev-portal-billing";\n' : "";
|
|
8344
|
+
const plansHook = options.withPlans ? " // The plans of the project's catalog: the same ones the pricing page shows.\n const plans = usePlansPreview();\n\n" : "";
|
|
8345
|
+
const plansSection = options.withPlans ? " <PlansPreviewSection plans={plans} />\n" : "";
|
|
8346
|
+
return `import {
|
|
8347
|
+
ApiShowcaseSection,
|
|
8348
|
+
FeaturesSection,
|
|
8349
|
+
HeroSection,
|
|
8350
|
+
LandingShell,
|
|
8351
|
+
${options.withPlans ? "PlansPreviewSection,\n " : ""}PricingCtaSection,
|
|
8352
|
+
StepsSection,
|
|
8353
|
+
} from "@lukoweb/apitogo-module-landing";
|
|
8354
|
+
${plansImport}
|
|
8355
|
+
const NAME = ${name};
|
|
8356
|
+
|
|
8357
|
+
export default function LandingPage() {
|
|
8358
|
+
${plansHook} return (
|
|
8359
|
+
<LandingShell
|
|
8360
|
+
title={NAME}
|
|
8361
|
+
description="Documentation, an API reference, sign-in, API keys and plans for your API, in one place."
|
|
8362
|
+
>
|
|
8363
|
+
<HeroSection
|
|
8364
|
+
hero={{
|
|
8365
|
+
badge: "API PORTAL",
|
|
8366
|
+
title: NAME,
|
|
8367
|
+
description:
|
|
8368
|
+
"Everything your developers need to use your API: documentation, an interactive reference, sign-in, API keys and plans.",
|
|
8369
|
+
}}
|
|
8370
|
+
primaryAction={{ label: "Get started", href: "/signup" }}
|
|
8371
|
+
secondaryAction={{ label: "Read the docs", href: "/introduction" }}
|
|
8372
|
+
codeExamples={[
|
|
8373
|
+
{
|
|
8374
|
+
filename: "Call the API",
|
|
8375
|
+
language: "bash",
|
|
8376
|
+
code: \`$ curl https://your-api.example.com/health \\\\
|
|
8377
|
+
-H "x-api-key: YOUR_API_KEY"
|
|
8378
|
+
# { "status": "ok" }\`,
|
|
8379
|
+
},
|
|
8380
|
+
]}
|
|
8381
|
+
/>
|
|
8382
|
+
<FeaturesSection
|
|
8383
|
+
section={{
|
|
8384
|
+
label: "What you get",
|
|
8385
|
+
title: "A developer portal that is ready on day one",
|
|
8386
|
+
description:
|
|
8387
|
+
"Edit this page in apitogo/portal/landing/LandingPage.tsx to make it yours.",
|
|
8388
|
+
}}
|
|
8389
|
+
features={[
|
|
8390
|
+
{
|
|
8391
|
+
title: "Documentation",
|
|
8392
|
+
description:
|
|
8393
|
+
"Write pages in Markdown or MDX and they appear in the portal with a sidebar and search.",
|
|
8394
|
+
icon: "book-open",
|
|
8395
|
+
},
|
|
8396
|
+
{
|
|
8397
|
+
title: "API reference",
|
|
8398
|
+
description:
|
|
8399
|
+
"An interactive reference built from your OpenAPI file, with a try-it console.",
|
|
8400
|
+
icon: "code",
|
|
8401
|
+
},
|
|
8402
|
+
{
|
|
8403
|
+
title: "API keys",
|
|
8404
|
+
description:
|
|
8405
|
+
"People sign up, pick a plan and create keys themselves, with no work for you.",
|
|
8406
|
+
icon: "shield-check",
|
|
8407
|
+
},
|
|
8408
|
+
{
|
|
8409
|
+
title: "Plans and limits",
|
|
8410
|
+
description:
|
|
8411
|
+
"Rate limits and monthly quotas per plan, enforced at the gateway.",
|
|
8412
|
+
icon: "gauge",
|
|
8413
|
+
},
|
|
8414
|
+
{
|
|
8415
|
+
title: "Usage",
|
|
8416
|
+
description:
|
|
8417
|
+
"Each developer sees their own request counts and how much of the plan is left.",
|
|
8418
|
+
icon: "activity",
|
|
8419
|
+
},
|
|
8420
|
+
{
|
|
8421
|
+
title: "Sign-in",
|
|
8422
|
+
description:
|
|
8423
|
+
"Accounts and login pages that carry your name and theme.",
|
|
8424
|
+
icon: "boxes",
|
|
8425
|
+
},
|
|
8426
|
+
]}
|
|
8427
|
+
/>
|
|
8428
|
+
<StepsSection
|
|
8429
|
+
steps={[
|
|
8430
|
+
{
|
|
8431
|
+
step: "01",
|
|
8432
|
+
title: "Create an account",
|
|
8433
|
+
description: "Sign up in a minute, no approval queue.",
|
|
8434
|
+
},
|
|
8435
|
+
{
|
|
8436
|
+
step: "02",
|
|
8437
|
+
title: "Pick a plan",
|
|
8438
|
+
description: "Start on the free plan and move up when you need more.",
|
|
8439
|
+
},
|
|
8440
|
+
{
|
|
8441
|
+
step: "03",
|
|
8442
|
+
title: "Call the API",
|
|
8443
|
+
description: "Create a key and make your first request.",
|
|
8444
|
+
},
|
|
8445
|
+
]}
|
|
8446
|
+
/>
|
|
8447
|
+
<ApiShowcaseSection
|
|
8448
|
+
apiShowcase={{
|
|
8449
|
+
title: "Sample endpoints",
|
|
8450
|
+
subtitle: "A slice of what is available. The full list is in the API reference.",
|
|
8451
|
+
browseAllHref: "/api",
|
|
8452
|
+
browseAllLabel: "Browse all APIs",
|
|
8453
|
+
layout: "list",
|
|
8454
|
+
items: [
|
|
8455
|
+
{
|
|
8456
|
+
name: "Health check",
|
|
8457
|
+
path: "/health",
|
|
8458
|
+
method: "GET",
|
|
8459
|
+
summary: "Tells you the service is up.",
|
|
8460
|
+
},
|
|
8461
|
+
],
|
|
8462
|
+
}}
|
|
8463
|
+
/>
|
|
8464
|
+
${plansSection} <PricingCtaSection
|
|
8465
|
+
variant="banner"
|
|
8466
|
+
pricingCta={{
|
|
8467
|
+
title: "Start building today",
|
|
8468
|
+
description: "Create an account and get your API key in minutes.",
|
|
8469
|
+
primaryAction: { label: "Get started", href: "/signup" },
|
|
8470
|
+
}}
|
|
8471
|
+
/>
|
|
8472
|
+
</LandingShell>
|
|
8473
|
+
);
|
|
8474
|
+
}
|
|
8475
|
+
`;
|
|
8476
|
+
}
|
|
8477
|
+
|
|
8478
|
+
// src/config/v2/services.ts
|
|
8282
8479
|
var SERVICES = ["portal", "gateway", "identity", "plans"];
|
|
8283
8480
|
var ServiceEditError = class extends ApitogoError {
|
|
8284
8481
|
constructor(message, developerHint, missingServices = []) {
|
|
@@ -8288,11 +8485,34 @@ var ServiceEditError = class extends ApitogoError {
|
|
|
8288
8485
|
}
|
|
8289
8486
|
};
|
|
8290
8487
|
var httpsProblem = (value) => /^https:\/\/\S+$/i.test(value) ? void 0 : "The address must start with https://";
|
|
8291
|
-
var
|
|
8292
|
-
nodejs:
|
|
8293
|
-
|
|
8294
|
-
|
|
8488
|
+
var RUNTIME_VERSIONS = {
|
|
8489
|
+
nodejs: [
|
|
8490
|
+
{ value: "NODE_22_LTS", label: "Node.js 22 (LTS)" },
|
|
8491
|
+
{ value: "NODE_24_LTS", label: "Node.js 24 (LTS)" }
|
|
8492
|
+
],
|
|
8493
|
+
python: [
|
|
8494
|
+
{ value: "PYTHON_312", label: "Python 3.12" },
|
|
8495
|
+
{ value: "PYTHON_310", label: "Python 3.10" },
|
|
8496
|
+
{ value: "PYTHON_311", label: "Python 3.11" },
|
|
8497
|
+
{ value: "PYTHON_313", label: "Python 3.13" },
|
|
8498
|
+
{ value: "PYTHON_314", label: "Python 3.14" }
|
|
8499
|
+
],
|
|
8500
|
+
dotnet: [
|
|
8501
|
+
{ value: "DOTNET_8", label: ".NET 8" },
|
|
8502
|
+
{ value: "DOTNET_9", label: ".NET 9" },
|
|
8503
|
+
{ value: "DOTNET_10", label: ".NET 10" }
|
|
8504
|
+
]
|
|
8295
8505
|
};
|
|
8506
|
+
function runtimeVersionSlug(runtime, value) {
|
|
8507
|
+
const upper = value.trim().toUpperCase();
|
|
8508
|
+
const known = RUNTIME_VERSIONS[runtime].map(
|
|
8509
|
+
(choice) => choice.value
|
|
8510
|
+
);
|
|
8511
|
+
if (known.includes(upper)) return upper;
|
|
8512
|
+
const digits = value.trim().toLowerCase().replace(/^(v|python|\.net|dotnet|node(js)?)[ _-]?/, "").replace(/(\.x|_lts| lts)$/, "");
|
|
8513
|
+
const candidate = runtime === "nodejs" ? `NODE_${digits.split(".")[0]}_LTS` : runtime === "python" ? `PYTHON_${digits.replace(".", "")}` : `DOTNET_${digits.split(".")[0]}`;
|
|
8514
|
+
return known.includes(candidate) ? candidate : value;
|
|
8515
|
+
}
|
|
8296
8516
|
var SERVICE_LABELS = {
|
|
8297
8517
|
portal: "Developer website",
|
|
8298
8518
|
gateway: "API protection",
|
|
@@ -8341,6 +8561,31 @@ var PLAN_TEMPLATES = {
|
|
|
8341
8561
|
}
|
|
8342
8562
|
}
|
|
8343
8563
|
],
|
|
8564
|
+
// Sample plans for the starter: a free plan and two with a price on request, so they show on the pricing page and
|
|
8565
|
+
// the home page and still deploy on a Free project (a plan with a price needs a paid platform plan and billing).
|
|
8566
|
+
starter: [
|
|
8567
|
+
{
|
|
8568
|
+
sku: "free",
|
|
8569
|
+
displayName: "Free",
|
|
8570
|
+
default: true,
|
|
8571
|
+
limits: {
|
|
8572
|
+
rateLimit: { calls: 3, periodSeconds: 60 },
|
|
8573
|
+
quota: { calls: 1e3, periodDays: 30 }
|
|
8574
|
+
}
|
|
8575
|
+
},
|
|
8576
|
+
{
|
|
8577
|
+
sku: "pro",
|
|
8578
|
+
displayName: "Pro",
|
|
8579
|
+
pricingModel: "custom",
|
|
8580
|
+
priceLabel: "Contact us"
|
|
8581
|
+
},
|
|
8582
|
+
{
|
|
8583
|
+
sku: "enterprise",
|
|
8584
|
+
displayName: "Enterprise",
|
|
8585
|
+
pricingModel: "custom",
|
|
8586
|
+
priceLabel: "Contact us"
|
|
8587
|
+
}
|
|
8588
|
+
],
|
|
8344
8589
|
tiered: [
|
|
8345
8590
|
{
|
|
8346
8591
|
sku: "free",
|
|
@@ -8411,6 +8656,7 @@ async function buildPortal(context) {
|
|
|
8411
8656
|
}
|
|
8412
8657
|
const name = o.name?.trim() || resolved.project.name;
|
|
8413
8658
|
let content = [];
|
|
8659
|
+
let landing;
|
|
8414
8660
|
if (!external) {
|
|
8415
8661
|
if (Array.isArray(o.docs) && o.docs.length > 0) {
|
|
8416
8662
|
content = await docsFromPaths(dir, o.docs);
|
|
@@ -8442,7 +8688,28 @@ async function buildPortal(context) {
|
|
|
8442
8688
|
const pages = path14.join(resolved.root, "portal", "pages");
|
|
8443
8689
|
if (template === "blank")
|
|
8444
8690
|
await addFile(context, path14.join(pages, ".gitkeep"), "");
|
|
8445
|
-
else
|
|
8691
|
+
else if (options.template === "starter") {
|
|
8692
|
+
await addFile(
|
|
8693
|
+
context,
|
|
8694
|
+
path14.join(pages, "introduction.mdx"),
|
|
8695
|
+
starterPage(name)
|
|
8696
|
+
);
|
|
8697
|
+
const landingFile = path14.join(
|
|
8698
|
+
resolved.root,
|
|
8699
|
+
"portal",
|
|
8700
|
+
"landing",
|
|
8701
|
+
"LandingPage.tsx"
|
|
8702
|
+
);
|
|
8703
|
+
await addFile(
|
|
8704
|
+
context,
|
|
8705
|
+
landingFile,
|
|
8706
|
+
starterLandingPage({
|
|
8707
|
+
name,
|
|
8708
|
+
withPlans: context.config.plans !== void 0 && context.config.plans.enabled !== false
|
|
8709
|
+
})
|
|
8710
|
+
);
|
|
8711
|
+
landing = rel(dir, landingFile);
|
|
8712
|
+
} else
|
|
8446
8713
|
await addFile(
|
|
8447
8714
|
context,
|
|
8448
8715
|
path14.join(pages, "index.mdx"),
|
|
@@ -8454,11 +8721,22 @@ async function buildPortal(context) {
|
|
|
8454
8721
|
...options.disabled ? { enabled: false } : {},
|
|
8455
8722
|
name,
|
|
8456
8723
|
...external ? { docs: { provider: "external", url: docsUrl } } : content.length > 0 ? { docs: { content } } : {},
|
|
8724
|
+
...landing ? { landing } : {},
|
|
8457
8725
|
...o.noConsole ? { console: { enabled: false } } : {},
|
|
8458
8726
|
...o.requireAuth ? { requireAuth: true } : {},
|
|
8459
8727
|
...o.search && !external ? { search: o.search } : {}
|
|
8460
8728
|
};
|
|
8461
8729
|
}
|
|
8730
|
+
async function createSourceFolder(context, sourcePath) {
|
|
8731
|
+
const folder = path14.join(context.dir, sourcePath);
|
|
8732
|
+
const entries = await readdir3(folder).catch(() => []);
|
|
8733
|
+
if (entries.some((name) => name !== ".gitkeep")) return;
|
|
8734
|
+
if (entries.length === 0)
|
|
8735
|
+
await addFile(context, path14.join(folder, ".gitkeep"), "");
|
|
8736
|
+
context.notes.push(
|
|
8737
|
+
`The folder ${sourcePath}/ is empty: put your backend code there before you deploy.`
|
|
8738
|
+
);
|
|
8739
|
+
}
|
|
8462
8740
|
async function buildGateway(context) {
|
|
8463
8741
|
const { options, dir, resolved } = context;
|
|
8464
8742
|
const o = options.gateway ?? {};
|
|
@@ -8499,22 +8777,28 @@ async function buildGateway(context) {
|
|
|
8499
8777
|
{ value: "python", label: "Python" },
|
|
8500
8778
|
{ value: "dotnet", label: ".NET" }
|
|
8501
8779
|
]) : void 0);
|
|
8502
|
-
const runtimeVersion = o.runtimeVersion
|
|
8503
|
-
|
|
8504
|
-
|
|
8780
|
+
const runtimeVersion = o.runtimeVersion !== void 0 && runtime ? runtimeVersionSlug(runtime, o.runtimeVersion) : ask4 && runtime ? await ask4.choose(
|
|
8781
|
+
"Runtime version",
|
|
8782
|
+
RUNTIME_VERSIONS[runtime].map((choice, index) => ({
|
|
8783
|
+
value: choice.value,
|
|
8784
|
+
label: index === 0 ? `${choice.label} (suggested)` : choice.label
|
|
8785
|
+
}))
|
|
8786
|
+
) : void 0;
|
|
8505
8787
|
if (!source || !runtime || !runtimeVersion) {
|
|
8506
8788
|
throw new ServiceEditError(
|
|
8507
8789
|
"--origin managed needs --source, --runtime and --runtime-version.",
|
|
8508
|
-
"For example: --source server --runtime nodejs --runtime-version NODE_22_LTS."
|
|
8790
|
+
runtime ? `For ${runtime}, --runtime-version is one of: ${RUNTIME_VERSIONS[runtime].map((choice) => choice.value).join(", ")} (a short form such as ${runtime === "nodejs" ? "22" : runtime === "python" ? "3.12" : "8"} works too).` : "For example: --source server --runtime nodejs --runtime-version NODE_22_LTS."
|
|
8509
8791
|
);
|
|
8510
8792
|
}
|
|
8793
|
+
const sourcePath = insideProject(dir, source, "Source folder");
|
|
8511
8794
|
origin = {
|
|
8512
8795
|
mode,
|
|
8513
|
-
source:
|
|
8796
|
+
source: sourcePath,
|
|
8514
8797
|
runtime,
|
|
8515
8798
|
runtimeVersion,
|
|
8516
8799
|
...o.exclude?.length ? { exclude: o.exclude } : {}
|
|
8517
8800
|
};
|
|
8801
|
+
await createSourceFolder(context, sourcePath);
|
|
8518
8802
|
}
|
|
8519
8803
|
let specs;
|
|
8520
8804
|
let requestedSpecs = o.spec ?? [];
|
|
@@ -8637,7 +8921,7 @@ async function buildIdentity(context) {
|
|
|
8637
8921
|
async function buildPlans(context) {
|
|
8638
8922
|
const { options, dir, resolved } = context;
|
|
8639
8923
|
const o = options.plans ?? {};
|
|
8640
|
-
const template = o.template ?? (options.template === "blank" ? "none" : options.template === "starter" ? "
|
|
8924
|
+
const template = o.template ?? (options.template === "blank" ? "none" : options.template === "starter" ? "starter" : "free");
|
|
8641
8925
|
const billing = o.billing ?? (template === "tiered" ? "stripe" : "none");
|
|
8642
8926
|
if (template === "tiered" && billing === "none") {
|
|
8643
8927
|
throw new ServiceEditError(
|
|
@@ -9010,7 +9294,7 @@ init_invariant();
|
|
|
9010
9294
|
|
|
9011
9295
|
// src/cli/check/v2.ts
|
|
9012
9296
|
init_apitogo_config_core();
|
|
9013
|
-
import { readFile as
|
|
9297
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
9014
9298
|
import path49 from "node:path";
|
|
9015
9299
|
|
|
9016
9300
|
// src/cli/platform/client/platform-api.ts
|
|
@@ -9833,8 +10117,8 @@ var PlatformApiClient = class {
|
|
|
9833
10117
|
}
|
|
9834
10118
|
};
|
|
9835
10119
|
async function readFileBytes(filePath) {
|
|
9836
|
-
const { readFile:
|
|
9837
|
-
const buffer = await
|
|
10120
|
+
const { readFile: readFile29 } = await import("node:fs/promises");
|
|
10121
|
+
const buffer = await readFile29(filePath);
|
|
9838
10122
|
const arrayBuffer = new ArrayBuffer(buffer.byteLength);
|
|
9839
10123
|
new Uint8Array(arrayBuffer).set(buffer);
|
|
9840
10124
|
return arrayBuffer;
|
|
@@ -9931,10 +10215,10 @@ function resolveProjectId(explicit, config2) {
|
|
|
9931
10215
|
import {
|
|
9932
10216
|
access as access4,
|
|
9933
10217
|
mkdir as mkdir12,
|
|
9934
|
-
readFile as
|
|
10218
|
+
readFile as readFile12,
|
|
9935
10219
|
rename as rename6,
|
|
9936
10220
|
rm as rm8,
|
|
9937
|
-
writeFile as
|
|
10221
|
+
writeFile as writeFile15
|
|
9938
10222
|
} from "node:fs/promises";
|
|
9939
10223
|
import path48 from "node:path";
|
|
9940
10224
|
|
|
@@ -10087,7 +10371,7 @@ import {
|
|
|
10087
10371
|
// package.json
|
|
10088
10372
|
var package_default = {
|
|
10089
10373
|
name: "@lukoweb/apitogo",
|
|
10090
|
-
version: "0.2.
|
|
10374
|
+
version: "0.2.42",
|
|
10091
10375
|
type: "module",
|
|
10092
10376
|
sideEffects: [
|
|
10093
10377
|
"**/*.css",
|
|
@@ -14935,6 +15219,7 @@ init_package_json();
|
|
|
14935
15219
|
|
|
14936
15220
|
// src/cli/init/v2-packages.ts
|
|
14937
15221
|
import { existsSync as existsSync5 } from "node:fs";
|
|
15222
|
+
import { readFile as readFile11, writeFile as writeFile14 } from "node:fs/promises";
|
|
14938
15223
|
import path47 from "node:path";
|
|
14939
15224
|
var CORE_PACKAGES = [
|
|
14940
15225
|
"@lukoweb/apitogo",
|
|
@@ -14947,6 +15232,29 @@ var portalPackageSpecs = (version) => [
|
|
|
14947
15232
|
"react-dom@19"
|
|
14948
15233
|
];
|
|
14949
15234
|
var installCommand = (version) => `npm install --save-dev ${portalPackageSpecs(version).join(" ")}`;
|
|
15235
|
+
function packageNameFrom(name) {
|
|
15236
|
+
const slug = name.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
|
|
15237
|
+
return slug || "portal";
|
|
15238
|
+
}
|
|
15239
|
+
async function ensurePackageJson(dir, name) {
|
|
15240
|
+
const file = path47.join(dir, "package.json");
|
|
15241
|
+
if (existsSync5(file)) return false;
|
|
15242
|
+
await writeFile14(
|
|
15243
|
+
file,
|
|
15244
|
+
`${JSON.stringify(
|
|
15245
|
+
{
|
|
15246
|
+
name: packageNameFrom(name),
|
|
15247
|
+
version: "0.0.1",
|
|
15248
|
+
private: true,
|
|
15249
|
+
type: "module"
|
|
15250
|
+
},
|
|
15251
|
+
null,
|
|
15252
|
+
2
|
|
15253
|
+
)}
|
|
15254
|
+
`
|
|
15255
|
+
);
|
|
15256
|
+
return true;
|
|
15257
|
+
}
|
|
14950
15258
|
function isPackageInstalled(startDir, name) {
|
|
14951
15259
|
let dir = path47.resolve(startDir);
|
|
14952
15260
|
for (; ; ) {
|
|
@@ -15028,7 +15336,7 @@ function portalBuildStep(context, options = {}, build2 = runBuild) {
|
|
|
15028
15336
|
await mkdir12(path48.dirname(buildDir), { recursive: true });
|
|
15029
15337
|
await rename6(path48.join(root, "dist"), buildDir);
|
|
15030
15338
|
if (prepared.publishedManifest) {
|
|
15031
|
-
await
|
|
15339
|
+
await writeFile15(
|
|
15032
15340
|
path48.join(buildDir, MANIFEST_FILE),
|
|
15033
15341
|
`${JSON.stringify(prepared.publishedManifest, null, 2)}
|
|
15034
15342
|
`
|
|
@@ -15058,7 +15366,7 @@ async function buildV2(options) {
|
|
|
15058
15366
|
}
|
|
15059
15367
|
|
|
15060
15368
|
// src/cli/check/gateway-online.ts
|
|
15061
|
-
import { readFile as
|
|
15369
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
15062
15370
|
var SPEC_SEND_LIMIT = 5 * 1024 * 1024 + 64 * 1024;
|
|
15063
15371
|
var issue2 = (code, path101, message, hint) => ({ code, path: path101, message, hint });
|
|
15064
15372
|
function capabilityIssues(gateway, answer) {
|
|
@@ -15122,7 +15430,7 @@ async function onlineGatewayRules(gateway, projectId, api, derivedCors = []) {
|
|
|
15122
15430
|
const specFile = gateway.specs[0];
|
|
15123
15431
|
if (specFile) {
|
|
15124
15432
|
try {
|
|
15125
|
-
const text = await
|
|
15433
|
+
const text = await readFile13(specFile, "utf8");
|
|
15126
15434
|
if (text.length <= SPEC_SEND_LIMIT) spec = text;
|
|
15127
15435
|
} catch {
|
|
15128
15436
|
}
|
|
@@ -15150,7 +15458,7 @@ async function onlineGatewayRules(gateway, projectId, api, derivedCors = []) {
|
|
|
15150
15458
|
|
|
15151
15459
|
// src/cli/deploy/plans-file.ts
|
|
15152
15460
|
init_invariant();
|
|
15153
|
-
import { readFile as
|
|
15461
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
15154
15462
|
import { z as z14 } from "zod";
|
|
15155
15463
|
var PlansFileError = class extends ApitogoError {
|
|
15156
15464
|
constructor(code, message, developerHint) {
|
|
@@ -15194,7 +15502,7 @@ var issuesOf = (error) => error.issues.map(
|
|
|
15194
15502
|
async function readPlansFile(file) {
|
|
15195
15503
|
let text;
|
|
15196
15504
|
try {
|
|
15197
|
-
text = await
|
|
15505
|
+
text = await readFile14(file, "utf8");
|
|
15198
15506
|
} catch {
|
|
15199
15507
|
throw new PlansFileError(
|
|
15200
15508
|
"PLANS_FILE_UNREADABLE",
|
|
@@ -15385,7 +15693,7 @@ async function onlinePlansRules(resolved, projectId, api) {
|
|
|
15385
15693
|
async function isConfigV2(dir) {
|
|
15386
15694
|
try {
|
|
15387
15695
|
return /"version"\s*:\s*2\b/.test(
|
|
15388
|
-
await
|
|
15696
|
+
await readFile15(path49.join(dir, APITOGO_CONFIG_FILENAME), "utf8")
|
|
15389
15697
|
);
|
|
15390
15698
|
} catch {
|
|
15391
15699
|
return false;
|
|
@@ -15534,10 +15842,8 @@ async function validatePortalBuild(dir) {
|
|
|
15534
15842
|
async function runCheckV2(dir, args = {}, deps = {}) {
|
|
15535
15843
|
const configPath = path49.join(dir, APITOGO_CONFIG_FILENAME);
|
|
15536
15844
|
let report;
|
|
15537
|
-
let layout;
|
|
15538
15845
|
try {
|
|
15539
15846
|
const { config: config2, resolved } = loadConfigV2(dir);
|
|
15540
|
-
layout = resolved.layout;
|
|
15541
15847
|
const online = await fetchSecretNames(
|
|
15542
15848
|
config2,
|
|
15543
15849
|
resolved.project.id,
|
|
@@ -15584,7 +15890,6 @@ async function runCheckV2(dir, args = {}, deps = {}) {
|
|
|
15584
15890
|
status: report.errors.length > 0 ? "failed" : "ok",
|
|
15585
15891
|
configVersion: 2,
|
|
15586
15892
|
configPath,
|
|
15587
|
-
layout,
|
|
15588
15893
|
...report
|
|
15589
15894
|
};
|
|
15590
15895
|
}
|
|
@@ -15757,15 +16062,18 @@ import { existsSync as existsSync6 } from "node:fs";
|
|
|
15757
16062
|
import path51 from "node:path";
|
|
15758
16063
|
var USER_PANEL_PACKAGE = "@lukoweb/apitogo-module-user-panel";
|
|
15759
16064
|
var BILLING_PLUGIN_PACKAGE = "@lukoweb/apitogo-plugin-dev-portal-billing";
|
|
16065
|
+
var LANDING_PACKAGE = "@lukoweb/apitogo-module-landing";
|
|
15760
16066
|
function packagesForConfig(config2) {
|
|
15761
16067
|
const portal = config2.portal;
|
|
15762
|
-
if (!portal || portal.enabled === false
|
|
15763
|
-
|
|
16068
|
+
if (!portal || portal.enabled === false) return [];
|
|
16069
|
+
const landing = portal.landing ? [LANDING_PACKAGE] : [];
|
|
16070
|
+
if (portal.console?.enabled === false) return landing;
|
|
15764
16071
|
const identity = config2.identity && config2.identity.enabled !== false;
|
|
15765
16072
|
const plans = config2.plans && config2.plans.enabled !== false;
|
|
15766
16073
|
return [
|
|
15767
16074
|
...identity || plans ? [USER_PANEL_PACKAGE] : [],
|
|
15768
|
-
...plans ? [BILLING_PLUGIN_PACKAGE] : []
|
|
16075
|
+
...plans ? [BILLING_PLUGIN_PACKAGE] : [],
|
|
16076
|
+
...landing
|
|
15769
16077
|
];
|
|
15770
16078
|
}
|
|
15771
16079
|
function isInstalled(dir, name) {
|
|
@@ -15783,10 +16091,20 @@ function detectPackageManager(dir) {
|
|
|
15783
16091
|
if (existsSync6(path51.join(dir, "yarn.lock"))) return "yarn";
|
|
15784
16092
|
return "npm";
|
|
15785
16093
|
}
|
|
15786
|
-
function
|
|
15787
|
-
|
|
15788
|
-
|
|
16094
|
+
function installSpecsCommand(manager, specs) {
|
|
16095
|
+
return manager === "npm" ? {
|
|
16096
|
+
command: "npm",
|
|
16097
|
+
// Only errors: the audit summary and engine warnings of the dependencies are not about this project.
|
|
16098
|
+
args: [
|
|
16099
|
+
"install",
|
|
16100
|
+
"--no-audit",
|
|
16101
|
+
"--no-fund",
|
|
16102
|
+
"--loglevel=error",
|
|
16103
|
+
...specs
|
|
16104
|
+
]
|
|
16105
|
+
} : { command: manager, args: ["add", ...specs] };
|
|
15789
16106
|
}
|
|
16107
|
+
var specName = (spec) => spec.slice(0, spec.lastIndexOf("@"));
|
|
15790
16108
|
var runInstaller = (dir, { command: command2, args }) => new Promise((resolve, reject) => {
|
|
15791
16109
|
const child = spawn(command2, args, {
|
|
15792
16110
|
cwd: dir,
|
|
@@ -15803,10 +16121,24 @@ var runInstaller = (dir, { command: command2, args }) => new Promise((resolve, r
|
|
|
15803
16121
|
)
|
|
15804
16122
|
);
|
|
15805
16123
|
});
|
|
16124
|
+
function packagesToInstall(dir, config2) {
|
|
16125
|
+
const portal = config2.portal && config2.portal.enabled !== false;
|
|
16126
|
+
return {
|
|
16127
|
+
core: portal ? missingCorePackages(dir) : [],
|
|
16128
|
+
plugins: missingPackages(dir, packagesForConfig(config2))
|
|
16129
|
+
};
|
|
16130
|
+
}
|
|
15806
16131
|
async function installMissingPackages(dir, config2, version, options) {
|
|
15807
|
-
const
|
|
16132
|
+
const { core, plugins } = packagesToInstall(dir, config2);
|
|
16133
|
+
const missing = [...core, ...plugins];
|
|
15808
16134
|
if (missing.length === 0) return void 0;
|
|
15809
|
-
const
|
|
16135
|
+
const specs = [
|
|
16136
|
+
...portalPackageSpecs(version).filter(
|
|
16137
|
+
(spec) => core.includes(specName(spec))
|
|
16138
|
+
),
|
|
16139
|
+
...plugins.map((name) => `${name}@${version}`)
|
|
16140
|
+
];
|
|
16141
|
+
const command2 = installSpecsCommand(detectPackageManager(dir), specs);
|
|
15810
16142
|
const shown = `${command2.command} ${command2.args.join(" ")}`;
|
|
15811
16143
|
if (!options.install)
|
|
15812
16144
|
return {
|
|
@@ -15815,13 +16147,7 @@ async function installMissingPackages(dir, config2, version, options) {
|
|
|
15815
16147
|
reason: "--no-install was given",
|
|
15816
16148
|
command: shown
|
|
15817
16149
|
};
|
|
15818
|
-
|
|
15819
|
-
return {
|
|
15820
|
-
status: "skipped",
|
|
15821
|
-
packages: missing,
|
|
15822
|
-
reason: "there is no package.json here",
|
|
15823
|
-
command: shown
|
|
15824
|
-
};
|
|
16150
|
+
await ensurePackageJson(dir, config2.project?.name ?? path51.basename(dir));
|
|
15825
16151
|
await (options.installer ?? runInstaller)(dir, command2);
|
|
15826
16152
|
return { status: "installed", packages: missing, command: shown };
|
|
15827
16153
|
}
|
|
@@ -15983,7 +16309,7 @@ async function run(action, argv, plan2, deps = {}) {
|
|
|
15983
16309
|
}
|
|
15984
16310
|
}
|
|
15985
16311
|
}
|
|
15986
|
-
const needed = action === "add" ?
|
|
16312
|
+
const needed = action === "add" ? Object.values(packagesToInstall(dir, edit.config)).flat() : [];
|
|
15987
16313
|
if (!json) {
|
|
15988
16314
|
for (const line2 of formatServiceEdit(edit))
|
|
15989
16315
|
printDiagnosticsToConsole(line2);
|
|
@@ -16446,7 +16772,7 @@ function formatDevPortalBackendResponse(projectId, backend) {
|
|
|
16446
16772
|
// src/cli/platform/helpers/dev-portal-manifest.ts
|
|
16447
16773
|
init_gateway_limits();
|
|
16448
16774
|
import { existsSync as existsSync7 } from "node:fs";
|
|
16449
|
-
import { access as access5, mkdir as mkdir13, readFile as
|
|
16775
|
+
import { access as access5, mkdir as mkdir13, readFile as readFile16, writeFile as writeFile16 } from "node:fs/promises";
|
|
16450
16776
|
import path56 from "node:path";
|
|
16451
16777
|
|
|
16452
16778
|
// src/cli/make-config/json-config.ts
|
|
@@ -17682,7 +18008,7 @@ function applyOpenApiToSiteConfig(config2, options) {
|
|
|
17682
18008
|
async function readManifest2(siteDirectory) {
|
|
17683
18009
|
const filePath = manifestFilePath(siteDirectory);
|
|
17684
18010
|
if (await pathExists(filePath)) {
|
|
17685
|
-
const raw = await
|
|
18011
|
+
const raw = await readFile16(filePath, "utf8");
|
|
17686
18012
|
const config2 = JSON.parse(raw);
|
|
17687
18013
|
return extractManifestFromConfig(config2);
|
|
17688
18014
|
}
|
|
@@ -17693,7 +18019,7 @@ async function writeManifest2(siteDirectory, manifest) {
|
|
|
17693
18019
|
await mkdir13(path56.dirname(filePath), { recursive: true });
|
|
17694
18020
|
let config2 = {};
|
|
17695
18021
|
if (await pathExists(filePath)) {
|
|
17696
|
-
const raw = await
|
|
18022
|
+
const raw = await readFile16(filePath, "utf8");
|
|
17697
18023
|
config2 = JSON.parse(raw);
|
|
17698
18024
|
}
|
|
17699
18025
|
const {
|
|
@@ -17702,7 +18028,7 @@ async function writeManifest2(siteDirectory, manifest) {
|
|
|
17702
18028
|
...safeManifest
|
|
17703
18029
|
} = manifest;
|
|
17704
18030
|
config2 = applyManifestToConfig(config2, safeManifest);
|
|
17705
|
-
await
|
|
18031
|
+
await writeFile16(filePath, `${JSON.stringify(config2, null, 2)}
|
|
17706
18032
|
`, "utf8");
|
|
17707
18033
|
return filePath;
|
|
17708
18034
|
}
|
|
@@ -17712,9 +18038,9 @@ import {
|
|
|
17712
18038
|
access as access6,
|
|
17713
18039
|
appendFile,
|
|
17714
18040
|
mkdir as mkdir14,
|
|
17715
|
-
readFile as
|
|
18041
|
+
readFile as readFile17,
|
|
17716
18042
|
unlink,
|
|
17717
|
-
writeFile as
|
|
18043
|
+
writeFile as writeFile17
|
|
17718
18044
|
} from "node:fs/promises";
|
|
17719
18045
|
import path57 from "node:path";
|
|
17720
18046
|
var ENV_FILENAME = ".env";
|
|
@@ -17818,7 +18144,7 @@ function applyEnvOverrides2(config2, env) {
|
|
|
17818
18144
|
}
|
|
17819
18145
|
async function readEnvFile(filePath) {
|
|
17820
18146
|
try {
|
|
17821
|
-
const raw = await
|
|
18147
|
+
const raw = await readFile17(filePath, "utf8");
|
|
17822
18148
|
const env = {};
|
|
17823
18149
|
for (const line2 of raw.split("\n")) {
|
|
17824
18150
|
const trimmed = line2.trim();
|
|
@@ -17951,7 +18277,7 @@ var SECRETS_COLLECTION_INSTRUCTION = "For go-live, plans must already be in apit
|
|
|
17951
18277
|
|
|
17952
18278
|
// src/cli/platform/helpers/dev-portal-site-validation.ts
|
|
17953
18279
|
init_gateway_limits();
|
|
17954
|
-
import { readFile as
|
|
18280
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
17955
18281
|
import { createRequire as createRequire2 } from "node:module";
|
|
17956
18282
|
import path58 from "node:path";
|
|
17957
18283
|
var cachedFallbackTs;
|
|
@@ -18129,7 +18455,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
|
|
|
18129
18455
|
let parsed;
|
|
18130
18456
|
let parseFailed = false;
|
|
18131
18457
|
try {
|
|
18132
|
-
parsed = JSON.parse(await
|
|
18458
|
+
parsed = JSON.parse(await readFile18(jsonPath, "utf8"));
|
|
18133
18459
|
} catch (error) {
|
|
18134
18460
|
parseFailed = true;
|
|
18135
18461
|
issues.push({
|
|
@@ -18168,7 +18494,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
|
|
|
18168
18494
|
} else {
|
|
18169
18495
|
for (const relativeFile of existingTsxFiles) {
|
|
18170
18496
|
checkedFiles.push(relativeFile);
|
|
18171
|
-
const content = await
|
|
18497
|
+
const content = await readFile18(
|
|
18172
18498
|
path58.join(resolvedDir, relativeFile),
|
|
18173
18499
|
"utf8"
|
|
18174
18500
|
);
|
|
@@ -18191,7 +18517,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
|
|
|
18191
18517
|
|
|
18192
18518
|
// src/cli/platform/helpers/npm-build.ts
|
|
18193
18519
|
import { spawn as spawn2 } from "node:child_process";
|
|
18194
|
-
import { access as access7, readFile as
|
|
18520
|
+
import { access as access7, readFile as readFile19 } from "node:fs/promises";
|
|
18195
18521
|
import path59 from "node:path";
|
|
18196
18522
|
var BUILD_LOG_TAIL_BYTES = 8192;
|
|
18197
18523
|
var MIN_NODE_MAJOR = 20;
|
|
@@ -18828,7 +19154,7 @@ var check_default = {
|
|
|
18828
19154
|
|
|
18829
19155
|
// src/cli/make-config/handler.ts
|
|
18830
19156
|
import { constants as constants2 } from "node:fs";
|
|
18831
|
-
import { access as access11, mkdir as mkdir16, readFile as
|
|
19157
|
+
import { access as access11, mkdir as mkdir16, readFile as readFile23, writeFile as writeFile21 } from "node:fs/promises";
|
|
18832
19158
|
import path69 from "node:path";
|
|
18833
19159
|
import { stdin as input2 } from "node:process";
|
|
18834
19160
|
import { glob as glob8 } from "glob";
|
|
@@ -18968,12 +19294,12 @@ function withDefaultIncludedActionKeys(plan2, actionKeys) {
|
|
|
18968
19294
|
}
|
|
18969
19295
|
|
|
18970
19296
|
// src/cli/scaffold/dev-portal-config-pages.ts
|
|
18971
|
-
import { access as access8, readFile as
|
|
19297
|
+
import { access as access8, readFile as readFile20, rename as rename7, rm as rm9, writeFile as writeFile18 } from "node:fs/promises";
|
|
18972
19298
|
import path63 from "node:path";
|
|
18973
19299
|
async function writeConfigAtomically(filePath, content) {
|
|
18974
19300
|
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
18975
19301
|
try {
|
|
18976
|
-
await
|
|
19302
|
+
await writeFile18(temporaryPath, content, "utf8");
|
|
18977
19303
|
await rename7(temporaryPath, filePath);
|
|
18978
19304
|
} catch (error) {
|
|
18979
19305
|
await rm9(temporaryPath, { force: true });
|
|
@@ -19733,7 +20059,7 @@ async function wireNewDevPortalPageInConfig(sourceDirectory, slug, label2) {
|
|
|
19733
20059
|
if (!configPath) {
|
|
19734
20060
|
return { updated: false };
|
|
19735
20061
|
}
|
|
19736
|
-
const original = await
|
|
20062
|
+
const original = await readFile20(configPath, "utf8");
|
|
19737
20063
|
const patched = patchApitogoConfigForNewPage(original, slug, label2, {
|
|
19738
20064
|
typescript: configPath.endsWith(".tsx")
|
|
19739
20065
|
});
|
|
@@ -19745,7 +20071,7 @@ async function wireNewDevPortalPageInConfig(sourceDirectory, slug, label2) {
|
|
|
19745
20071
|
}
|
|
19746
20072
|
|
|
19747
20073
|
// src/cli/scaffold/wire-openapi.ts
|
|
19748
|
-
import { access as access9, readFile as
|
|
20074
|
+
import { access as access9, readFile as readFile21, writeFile as writeFile19 } from "node:fs/promises";
|
|
19749
20075
|
import path66 from "node:path";
|
|
19750
20076
|
|
|
19751
20077
|
// src/cli/platform/helpers/openapi.ts
|
|
@@ -20004,13 +20330,13 @@ async function wireDevPortalOpenApi(sourceDirectory, openApiPath) {
|
|
|
20004
20330
|
}
|
|
20005
20331
|
const jsonConfigPath = manifestFilePath(resolvedSource);
|
|
20006
20332
|
if (await pathExists3(jsonConfigPath)) {
|
|
20007
|
-
const raw = await
|
|
20333
|
+
const raw = await readFile21(jsonConfigPath, "utf8");
|
|
20008
20334
|
const siteConfig = JSON.parse(raw);
|
|
20009
20335
|
const updated = applyOpenApiToSiteConfig(siteConfig, {
|
|
20010
20336
|
input: input5,
|
|
20011
20337
|
apiPath: "api"
|
|
20012
20338
|
});
|
|
20013
|
-
await
|
|
20339
|
+
await writeFile19(
|
|
20014
20340
|
jsonConfigPath,
|
|
20015
20341
|
`${JSON.stringify(updated, null, 2)}
|
|
20016
20342
|
`,
|
|
@@ -20018,10 +20344,10 @@ async function wireDevPortalOpenApi(sourceDirectory, openApiPath) {
|
|
|
20018
20344
|
);
|
|
20019
20345
|
} else {
|
|
20020
20346
|
const tsxConfigPath = path66.join(resolvedSource, "apitogo.config.tsx");
|
|
20021
|
-
const configContent = await
|
|
20347
|
+
const configContent = await readFile21(tsxConfigPath, "utf8");
|
|
20022
20348
|
try {
|
|
20023
20349
|
const patched = patchApitogoConfigOpenApiToFile(configContent, input5);
|
|
20024
|
-
await
|
|
20350
|
+
await writeFile19(tsxConfigPath, patched);
|
|
20025
20351
|
} catch (error) {
|
|
20026
20352
|
return {
|
|
20027
20353
|
status: "failed",
|
|
@@ -20374,7 +20700,7 @@ export default function ${componentName}() {
|
|
|
20374
20700
|
// src/cli/make-config/scaffold-project.ts
|
|
20375
20701
|
init_package_json();
|
|
20376
20702
|
import { constants } from "node:fs";
|
|
20377
|
-
import { access as access10, mkdir as mkdir15, readFile as
|
|
20703
|
+
import { access as access10, mkdir as mkdir15, readFile as readFile22, writeFile as writeFile20 } from "node:fs/promises";
|
|
20378
20704
|
import path68 from "node:path";
|
|
20379
20705
|
import { glob as glob7 } from "glob";
|
|
20380
20706
|
|
|
@@ -20416,7 +20742,7 @@ async function findOpenApiSpecPath(dir) {
|
|
|
20416
20742
|
});
|
|
20417
20743
|
return extra[0] ? toPosix2(extra[0]) : null;
|
|
20418
20744
|
}
|
|
20419
|
-
async function
|
|
20745
|
+
async function ensurePackageJson2(dir) {
|
|
20420
20746
|
const pkgPath = path68.join(dir, "package.json");
|
|
20421
20747
|
const apitogoVer = apitogoVersionRange();
|
|
20422
20748
|
const baseDeps = {
|
|
@@ -20452,11 +20778,11 @@ async function ensurePackageJson(dir) {
|
|
|
20452
20778
|
typescript: "^5"
|
|
20453
20779
|
}
|
|
20454
20780
|
};
|
|
20455
|
-
await
|
|
20781
|
+
await writeFile20(pkgPath, `${JSON.stringify(pkg2, null, 2)}
|
|
20456
20782
|
`, "utf8");
|
|
20457
20783
|
return true;
|
|
20458
20784
|
}
|
|
20459
|
-
const raw = await
|
|
20785
|
+
const raw = await readFile22(pkgPath, "utf8");
|
|
20460
20786
|
let pkg;
|
|
20461
20787
|
try {
|
|
20462
20788
|
pkg = JSON.parse(raw);
|
|
@@ -20479,7 +20805,7 @@ async function ensurePackageJson(dir) {
|
|
|
20479
20805
|
for (const [k, v] of Object.entries(baseDeps)) {
|
|
20480
20806
|
if (!deps[k]) deps[k] = v;
|
|
20481
20807
|
}
|
|
20482
|
-
await
|
|
20808
|
+
await writeFile20(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
20483
20809
|
`, "utf8");
|
|
20484
20810
|
return false;
|
|
20485
20811
|
}
|
|
@@ -20514,7 +20840,7 @@ async function ensureTsconfigJson(dir) {
|
|
|
20514
20840
|
"user-panel"
|
|
20515
20841
|
]
|
|
20516
20842
|
};
|
|
20517
|
-
await
|
|
20843
|
+
await writeFile20(tsPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
20518
20844
|
`, "utf8");
|
|
20519
20845
|
return true;
|
|
20520
20846
|
}
|
|
@@ -20536,17 +20862,17 @@ async function ensurePortalStubFiles(dir) {
|
|
|
20536
20862
|
const landingPath = path68.join(dir, "landing", "LandingPage.tsx");
|
|
20537
20863
|
if (!await fileExists2(landingPath)) {
|
|
20538
20864
|
await mkdir15(path68.join(dir, "landing"), { recursive: true });
|
|
20539
|
-
await
|
|
20865
|
+
await writeFile20(landingPath, LANDING_PAGE_SOURCE, "utf8");
|
|
20540
20866
|
}
|
|
20541
20867
|
const plansPath = path68.join(dir, "user-panel", "AccountPlansPage.tsx");
|
|
20542
20868
|
if (!await fileExists2(plansPath)) {
|
|
20543
20869
|
await mkdir15(path68.join(dir, "user-panel"), { recursive: true });
|
|
20544
|
-
await
|
|
20870
|
+
await writeFile20(plansPath, ACCOUNT_PLANS_PAGE_SOURCE, "utf8");
|
|
20545
20871
|
}
|
|
20546
20872
|
const dashboardPath = path68.join(dir, "user-panel", "DashboardPage.tsx");
|
|
20547
20873
|
if (!await fileExists2(dashboardPath)) {
|
|
20548
20874
|
await mkdir15(path68.join(dir, "user-panel"), { recursive: true });
|
|
20549
|
-
await
|
|
20875
|
+
await writeFile20(dashboardPath, DASHBOARD_PAGE_SOURCE, "utf8");
|
|
20550
20876
|
}
|
|
20551
20877
|
await ensurePublicBrandingFiles(dir);
|
|
20552
20878
|
}
|
|
@@ -20558,11 +20884,11 @@ async function ensurePublicBrandingFiles(dir) {
|
|
|
20558
20884
|
if (await fileExists2(dest)) {
|
|
20559
20885
|
continue;
|
|
20560
20886
|
}
|
|
20561
|
-
await
|
|
20887
|
+
await writeFile20(dest, content, "utf8");
|
|
20562
20888
|
}
|
|
20563
20889
|
const faviconDest = path68.join(destDir, "Icon.png");
|
|
20564
20890
|
if (!await fileExists2(faviconDest)) {
|
|
20565
|
-
await
|
|
20891
|
+
await writeFile20(faviconDest, Buffer.from(FAVICON_PNG_BASE64, "base64"));
|
|
20566
20892
|
}
|
|
20567
20893
|
}
|
|
20568
20894
|
async function ensureThinConfigTsx(dir) {
|
|
@@ -20570,7 +20896,7 @@ async function ensureThinConfigTsx(dir) {
|
|
|
20570
20896
|
if (await fileExists2(tsxPath)) {
|
|
20571
20897
|
return false;
|
|
20572
20898
|
}
|
|
20573
|
-
await
|
|
20899
|
+
await writeFile20(tsxPath, THIN_WRAPPER_SOURCE, "utf8");
|
|
20574
20900
|
return true;
|
|
20575
20901
|
}
|
|
20576
20902
|
|
|
@@ -20585,7 +20911,7 @@ var fileExists3 = async (filePath) => {
|
|
|
20585
20911
|
}
|
|
20586
20912
|
};
|
|
20587
20913
|
var readJsonConfig = async (filePath) => {
|
|
20588
|
-
const raw = await
|
|
20914
|
+
const raw = await readFile23(filePath, "utf8");
|
|
20589
20915
|
const parsed = JSON.parse(raw);
|
|
20590
20916
|
if (!isPlainObject3(parsed)) {
|
|
20591
20917
|
throw new Error(`${APITOGO_JSON_FILENAME} must be a JSON object.`);
|
|
@@ -20631,13 +20957,13 @@ async function persistConfig(config2, ctx) {
|
|
|
20631
20957
|
force: ctx.force
|
|
20632
20958
|
})
|
|
20633
20959
|
};
|
|
20634
|
-
await
|
|
20960
|
+
await writeFile21(ctx.jsonPath, `${JSON.stringify(next, null, 2)}
|
|
20635
20961
|
`, "utf8");
|
|
20636
20962
|
const tsxPath = path69.join(ctx.dir, "apitogo.config.tsx");
|
|
20637
20963
|
if (await fileExists3(tsxPath) && tsxRoutes.length > 0) {
|
|
20638
|
-
const tsxSource = await
|
|
20964
|
+
const tsxSource = await readFile23(tsxPath, "utf8");
|
|
20639
20965
|
if (isManagedConfigWrapper(tsxSource)) {
|
|
20640
|
-
await
|
|
20966
|
+
await writeFile21(
|
|
20641
20967
|
tsxPath,
|
|
20642
20968
|
buildThinWrapperWithCustomPages(tsxRoutes),
|
|
20643
20969
|
"utf8"
|
|
@@ -20669,7 +20995,7 @@ async function makeConfig(argv) {
|
|
|
20669
20995
|
const changes = [];
|
|
20670
20996
|
try {
|
|
20671
20997
|
const discoveredOpenApi = await findOpenApiSpecPath(dir);
|
|
20672
|
-
const createdPackageJson = await
|
|
20998
|
+
const createdPackageJson = await ensurePackageJson2(dir);
|
|
20673
20999
|
const createdTsconfig = await ensureTsconfigJson(dir);
|
|
20674
21000
|
if (createdPackageJson && !json) {
|
|
20675
21001
|
printSuccessToConsole(
|
|
@@ -20741,7 +21067,7 @@ async function makeConfig(argv) {
|
|
|
20741
21067
|
if (action2.kind === "page") {
|
|
20742
21068
|
const pagePath = path69.join(dir, action2.page.relativePath);
|
|
20743
21069
|
await mkdir16(path69.dirname(pagePath), { recursive: true });
|
|
20744
|
-
await
|
|
21070
|
+
await writeFile21(
|
|
20745
21071
|
pagePath,
|
|
20746
21072
|
buildStarterDocsPageTsx(action2.page.slug, action2.page.title),
|
|
20747
21073
|
"utf8"
|
|
@@ -20850,7 +21176,7 @@ async function makeConfig(argv) {
|
|
|
20850
21176
|
argv.pageContent,
|
|
20851
21177
|
pageTemplate
|
|
20852
21178
|
);
|
|
20853
|
-
await
|
|
21179
|
+
await writeFile21(filePath, tsx, "utf8");
|
|
20854
21180
|
pageResult = {
|
|
20855
21181
|
action: "updated",
|
|
20856
21182
|
slug,
|
|
@@ -20870,7 +21196,7 @@ async function makeConfig(argv) {
|
|
|
20870
21196
|
pageTemplate
|
|
20871
21197
|
);
|
|
20872
21198
|
await mkdir16(path69.dirname(filePath), { recursive: true });
|
|
20873
|
-
await
|
|
21199
|
+
await writeFile21(filePath, tsx, "utf8");
|
|
20874
21200
|
const configResult = await wireNewDevPortalPageInConfig(
|
|
20875
21201
|
dir,
|
|
20876
21202
|
slug,
|
|
@@ -21009,11 +21335,21 @@ var config_default = {
|
|
|
21009
21335
|
// src/cli/create/handler.ts
|
|
21010
21336
|
init_apitogo_config_core();
|
|
21011
21337
|
import { execFile } from "node:child_process";
|
|
21012
|
-
import { readFile as
|
|
21338
|
+
import { readFile as readFile24 } from "node:fs/promises";
|
|
21013
21339
|
import path70 from "node:path";
|
|
21014
21340
|
import { promisify } from "node:util";
|
|
21015
21341
|
var execFileAsync = promisify(execFile);
|
|
21342
|
+
function nextStepHint(dir) {
|
|
21343
|
+
const linked = loadConfigV2(dir).resolved.project.id;
|
|
21344
|
+
return linked ? "Next: apitogo deploy" : "Next: apitogo project create, then apitogo deploy";
|
|
21345
|
+
}
|
|
21016
21346
|
var DONE = "done";
|
|
21347
|
+
var STARTER_SERVICES = [
|
|
21348
|
+
"gateway",
|
|
21349
|
+
"identity",
|
|
21350
|
+
"plans",
|
|
21351
|
+
"portal"
|
|
21352
|
+
];
|
|
21017
21353
|
async function gitHasUncommittedChanges(dir) {
|
|
21018
21354
|
try {
|
|
21019
21355
|
const { stdout } = await execFileAsync("git", ["status", "--porcelain"], {
|
|
@@ -21027,7 +21363,7 @@ async function gitHasUncommittedChanges(dir) {
|
|
|
21027
21363
|
async function defaultName(dir) {
|
|
21028
21364
|
try {
|
|
21029
21365
|
const parsed = JSON.parse(
|
|
21030
|
-
await
|
|
21366
|
+
await readFile24(path70.join(dir, "package.json"), "utf8")
|
|
21031
21367
|
);
|
|
21032
21368
|
if (typeof parsed.name === "string" && parsed.name.trim())
|
|
21033
21369
|
return parsed.name.replace(/^@[^/]+\//, "");
|
|
@@ -21060,7 +21396,8 @@ async function createProject(argv, deps = {}) {
|
|
|
21060
21396
|
};
|
|
21061
21397
|
try {
|
|
21062
21398
|
const dir = path70.resolve(process.cwd(), argv.dir ?? ".");
|
|
21063
|
-
|
|
21399
|
+
let requested = parseServicesFlag(argv.services);
|
|
21400
|
+
let template = argv.template;
|
|
21064
21401
|
if (!asker && requested.length === 0) {
|
|
21065
21402
|
throw new InitError(
|
|
21066
21403
|
"create needs a terminal to ask its questions, and none is available.",
|
|
@@ -21098,10 +21435,23 @@ async function createProject(argv, deps = {}) {
|
|
|
21098
21435
|
} else {
|
|
21099
21436
|
say(`${APITOGO_CONFIG_FILENAME} exists: continuing from it.`);
|
|
21100
21437
|
}
|
|
21438
|
+
if (!template && requested.length === 0 && asker)
|
|
21439
|
+
template = await asker.choose(
|
|
21440
|
+
"What do you want to start from?",
|
|
21441
|
+
[
|
|
21442
|
+
{
|
|
21443
|
+
value: "starter",
|
|
21444
|
+
label: "Starter: every service with examples (home page, sample plans, API reference)"
|
|
21445
|
+
},
|
|
21446
|
+
{ value: "blank", label: "Blank: choose the services one by one" }
|
|
21447
|
+
]
|
|
21448
|
+
);
|
|
21449
|
+
if (template === "starter" && requested.length === 0)
|
|
21450
|
+
requested = STARTER_SERVICES;
|
|
21101
21451
|
const stepArgs = (service) => ({
|
|
21102
21452
|
service,
|
|
21103
21453
|
dir: argv.dir,
|
|
21104
|
-
template
|
|
21454
|
+
template,
|
|
21105
21455
|
dryRun: argv.dryRun,
|
|
21106
21456
|
install: argv.install,
|
|
21107
21457
|
json,
|
|
@@ -21153,9 +21503,8 @@ async function createProject(argv, deps = {}) {
|
|
|
21153
21503
|
);
|
|
21154
21504
|
return;
|
|
21155
21505
|
}
|
|
21156
|
-
printResultToConsole("Setup finished.
|
|
21157
|
-
|
|
21158
|
-
printHintToConsole("Next: apitogo deploy");
|
|
21506
|
+
printResultToConsole("Setup finished.");
|
|
21507
|
+
printHintToConsole(nextStepHint(dir));
|
|
21159
21508
|
} catch (error) {
|
|
21160
21509
|
const message = error instanceof Error ? error.message : String(error);
|
|
21161
21510
|
const hint = error instanceof InitError ? error.developerHint : void 0;
|
|
@@ -21345,6 +21694,16 @@ function buildProgressSummary(options) {
|
|
|
21345
21694
|
terminalStatus: options.terminalStatus
|
|
21346
21695
|
};
|
|
21347
21696
|
}
|
|
21697
|
+
var MODULE_DISPLAY_NAMES = {
|
|
21698
|
+
"dev-portal": "Developer website",
|
|
21699
|
+
apihub: "API hosting"
|
|
21700
|
+
};
|
|
21701
|
+
function friendlyModuleNames(text) {
|
|
21702
|
+
return text.replace(
|
|
21703
|
+
/(?:Module|module) (dev-portal|apihub)(?![\w-])/g,
|
|
21704
|
+
(_match, id) => MODULE_DISPLAY_NAMES[id] ?? id
|
|
21705
|
+
);
|
|
21706
|
+
}
|
|
21348
21707
|
function createConsoleProgressReporter(stream = process.stderr) {
|
|
21349
21708
|
const shared = { spinner: void 0 };
|
|
21350
21709
|
return {
|
|
@@ -21364,7 +21723,9 @@ function createScopedConsoleProgressReporter(stream, stepLabel, shared) {
|
|
|
21364
21723
|
const reporter = {
|
|
21365
21724
|
async onStep(step2, detail) {
|
|
21366
21725
|
const message = (!animated ? detail?.message : void 0) ?? (detail?.platformStatus ? `${step2}: ${detail.platformStatus}` : step2);
|
|
21367
|
-
const line2 =
|
|
21726
|
+
const line2 = friendlyModuleNames(
|
|
21727
|
+
stepLabel ? `[${stepLabel}] ${message}` : message
|
|
21728
|
+
);
|
|
21368
21729
|
if (line2 === lastLine) return;
|
|
21369
21730
|
lastLine = line2;
|
|
21370
21731
|
if (!animated) {
|
|
@@ -21383,7 +21744,9 @@ function createScopedConsoleProgressReporter(stream, stepLabel, shared) {
|
|
|
21383
21744
|
async onNewMessages(messages) {
|
|
21384
21745
|
for (const entry of messages) {
|
|
21385
21746
|
writeLine2(
|
|
21386
|
-
|
|
21747
|
+
friendlyModuleNames(
|
|
21748
|
+
stepLabel ? `[${stepLabel}] ${entry.message}` : entry.message
|
|
21749
|
+
)
|
|
21387
21750
|
);
|
|
21388
21751
|
}
|
|
21389
21752
|
},
|
|
@@ -22247,7 +22610,7 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
|
|
|
22247
22610
|
}
|
|
22248
22611
|
|
|
22249
22612
|
// src/cli/platform/helpers/openapi-gateway-servers.ts
|
|
22250
|
-
import { mkdir as mkdir17, readFile as
|
|
22613
|
+
import { mkdir as mkdir17, readFile as readFile25, writeFile as writeFile22 } from "node:fs/promises";
|
|
22251
22614
|
import path73 from "node:path";
|
|
22252
22615
|
import YAML3 from "yaml";
|
|
22253
22616
|
var GATEWAY_URL_ENV_KEY = "APITOGO_GATEWAY_URL";
|
|
@@ -22279,7 +22642,7 @@ async function resolveProjectGatewayUrl(client, projectId) {
|
|
|
22279
22642
|
return null;
|
|
22280
22643
|
}
|
|
22281
22644
|
async function readOpenApiDocument(filePath) {
|
|
22282
|
-
const content = await
|
|
22645
|
+
const content = await readFile25(filePath, "utf8");
|
|
22283
22646
|
const ext = path73.extname(filePath).toLowerCase();
|
|
22284
22647
|
const parsed = ext === ".json" ? JSON.parse(content) : YAML3.parse(content);
|
|
22285
22648
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -22295,7 +22658,7 @@ async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApi
|
|
|
22295
22658
|
const doc = await readOpenApiDocument(source);
|
|
22296
22659
|
const rewritten = withGatewayServers(doc, gatewayUrl);
|
|
22297
22660
|
await mkdir17(path73.dirname(outputPath), { recursive: true });
|
|
22298
|
-
await
|
|
22661
|
+
await writeFile22(
|
|
22299
22662
|
outputPath,
|
|
22300
22663
|
`${JSON.stringify(rewritten, null, 2)}
|
|
22301
22664
|
`,
|
|
@@ -22308,7 +22671,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
|
|
|
22308
22671
|
path73.resolve(siteDirectory),
|
|
22309
22672
|
APITOGO_CONFIG_FILENAME2
|
|
22310
22673
|
);
|
|
22311
|
-
const raw = await
|
|
22674
|
+
const raw = await readFile25(configPath, "utf8");
|
|
22312
22675
|
const config2 = JSON.parse(raw);
|
|
22313
22676
|
const previousApis = config2.apis;
|
|
22314
22677
|
const previousBackend = config2.backend;
|
|
@@ -22321,11 +22684,11 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
|
|
|
22321
22684
|
const backend = config2.backend && typeof config2.backend === "object" ? { ...config2.backend } : {};
|
|
22322
22685
|
backend.openApiPath = relativeInput;
|
|
22323
22686
|
config2.backend = backend;
|
|
22324
|
-
await
|
|
22687
|
+
await writeFile22(configPath, `${JSON.stringify(config2, null, 2)}
|
|
22325
22688
|
`, "utf8");
|
|
22326
22689
|
return {
|
|
22327
22690
|
restore: async () => {
|
|
22328
|
-
const currentRaw = await
|
|
22691
|
+
const currentRaw = await readFile25(configPath, "utf8");
|
|
22329
22692
|
const current = JSON.parse(currentRaw);
|
|
22330
22693
|
if (previousApis === void 0) {
|
|
22331
22694
|
delete current.apis;
|
|
@@ -22337,7 +22700,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
|
|
|
22337
22700
|
} else {
|
|
22338
22701
|
current.backend = previousBackend;
|
|
22339
22702
|
}
|
|
22340
|
-
await
|
|
22703
|
+
await writeFile22(
|
|
22341
22704
|
configPath,
|
|
22342
22705
|
`${JSON.stringify(current, null, 2)}
|
|
22343
22706
|
`,
|
|
@@ -22360,7 +22723,7 @@ async function prepareOpenApiServersForGatewayBuild(options) {
|
|
|
22360
22723
|
);
|
|
22361
22724
|
if (!openApiPath) {
|
|
22362
22725
|
try {
|
|
22363
|
-
const raw = await
|
|
22726
|
+
const raw = await readFile25(
|
|
22364
22727
|
path73.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
|
|
22365
22728
|
"utf8"
|
|
22366
22729
|
);
|
|
@@ -22434,7 +22797,7 @@ function collectManifestPublicActionKeys(gateway) {
|
|
|
22434
22797
|
}
|
|
22435
22798
|
|
|
22436
22799
|
// src/cli/platform/helpers/publish-ledger.ts
|
|
22437
|
-
import { mkdir as mkdir18, readFile as
|
|
22800
|
+
import { mkdir as mkdir18, readFile as readFile26, writeFile as writeFile23 } from "node:fs/promises";
|
|
22438
22801
|
import path74 from "node:path";
|
|
22439
22802
|
var PUBLISH_LEDGER_RELATIVE_PATH = ".apitogo/publish-state.json";
|
|
22440
22803
|
function publishLedgerPath(siteDirectory) {
|
|
@@ -22443,7 +22806,7 @@ function publishLedgerPath(siteDirectory) {
|
|
|
22443
22806
|
async function readPublishLedger(siteDirectory) {
|
|
22444
22807
|
const filePath = publishLedgerPath(siteDirectory);
|
|
22445
22808
|
try {
|
|
22446
|
-
const raw = await
|
|
22809
|
+
const raw = await readFile26(filePath, "utf8");
|
|
22447
22810
|
const parsed = JSON.parse(raw);
|
|
22448
22811
|
if (parsed?.version !== 1 || !Array.isArray(parsed.steps)) {
|
|
22449
22812
|
return null;
|
|
@@ -22461,7 +22824,7 @@ async function writePublishLedger(siteDirectory, ledger) {
|
|
|
22461
22824
|
version: 1,
|
|
22462
22825
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22463
22826
|
};
|
|
22464
|
-
await
|
|
22827
|
+
await writeFile23(filePath, `${JSON.stringify(next, null, 2)}
|
|
22465
22828
|
`, "utf8");
|
|
22466
22829
|
return filePath;
|
|
22467
22830
|
}
|
|
@@ -22975,14 +23338,14 @@ async function createProjectWithWarmPoolRetry(client, body, options) {
|
|
|
22975
23338
|
|
|
22976
23339
|
// src/cli/platform/helpers/backend-packaging.ts
|
|
22977
23340
|
import { spawn as spawn3 } from "node:child_process";
|
|
22978
|
-
import { mkdtemp, readdir as
|
|
23341
|
+
import { mkdtemp, readdir as readdir4, rm as rm10 } from "node:fs/promises";
|
|
22979
23342
|
import { tmpdir } from "node:os";
|
|
22980
23343
|
import path75 from "node:path";
|
|
22981
23344
|
async function directoryHasRuntimeConfig(dir) {
|
|
22982
23345
|
if (!await pathExists(dir)) {
|
|
22983
23346
|
return false;
|
|
22984
23347
|
}
|
|
22985
|
-
const entries = await
|
|
23348
|
+
const entries = await readdir4(dir);
|
|
22986
23349
|
return entries.some((name) => name.endsWith(".runtimeconfig.json"));
|
|
22987
23350
|
}
|
|
22988
23351
|
function runDotnetPublish(projectDir, outputDir) {
|
|
@@ -23018,7 +23381,7 @@ function runDotnetPublish(projectDir, outputDir) {
|
|
|
23018
23381
|
});
|
|
23019
23382
|
}
|
|
23020
23383
|
async function findCsproj(projectDir) {
|
|
23021
|
-
const entries = await
|
|
23384
|
+
const entries = await readdir4(projectDir);
|
|
23022
23385
|
const csproj = entries.find((name) => name.endsWith(".csproj"));
|
|
23023
23386
|
return csproj ? path75.join(projectDir, csproj) : null;
|
|
23024
23387
|
}
|
|
@@ -24134,6 +24497,33 @@ async function handleDeleteProject(client, config2, args, progress) {
|
|
|
24134
24497
|
}
|
|
24135
24498
|
|
|
24136
24499
|
// src/cli/platform/tools/lifecycle.ts
|
|
24500
|
+
function shouldRetryActivation(infrastructureStatus, context) {
|
|
24501
|
+
const status2 = infrastructureStatus?.toLowerCase() ?? "";
|
|
24502
|
+
if (status2 === "failed") return true;
|
|
24503
|
+
if (!context.resumed) return false;
|
|
24504
|
+
return status2 === "none" || status2 === "" || context.timedOut;
|
|
24505
|
+
}
|
|
24506
|
+
async function retryActivation(client, projectId, steps, progress) {
|
|
24507
|
+
await progress?.onStep("Retrying hosting activation");
|
|
24508
|
+
try {
|
|
24509
|
+
const started = await client.retryAzureInfrastructure(projectId);
|
|
24510
|
+
steps.push({
|
|
24511
|
+
step: "retry_activation",
|
|
24512
|
+
status: "pending",
|
|
24513
|
+
detail: started
|
|
24514
|
+
});
|
|
24515
|
+
return true;
|
|
24516
|
+
} catch (error) {
|
|
24517
|
+
steps.push({
|
|
24518
|
+
step: "retry_activation",
|
|
24519
|
+
status: "skipped",
|
|
24520
|
+
detail: {
|
|
24521
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
24522
|
+
}
|
|
24523
|
+
});
|
|
24524
|
+
return false;
|
|
24525
|
+
}
|
|
24526
|
+
}
|
|
24137
24527
|
async function findExistingProjectByName(client, name) {
|
|
24138
24528
|
const projects = await client.listProjects();
|
|
24139
24529
|
return findProjectByNormalizedName(projects, name);
|
|
@@ -24238,11 +24628,27 @@ async function handleBootstrapProject(client, config2, args, progress) {
|
|
|
24238
24628
|
const projectBeforeModules = await client.getProject(projectId);
|
|
24239
24629
|
const planSelected = !isPlatformPlanUnselected(projectBeforeModules.planTier);
|
|
24240
24630
|
if (wait && planSelected) {
|
|
24241
|
-
|
|
24631
|
+
if (resumed && shouldRetryActivation(projectBeforeModules.infrastructureStatus, {
|
|
24632
|
+
resumed,
|
|
24633
|
+
timedOut: false
|
|
24634
|
+
})) {
|
|
24635
|
+
await retryActivation(client, projectId, steps, bootstrapProgress);
|
|
24636
|
+
}
|
|
24637
|
+
let binding = await waitForWarmPoolBinding(client, projectId, {
|
|
24242
24638
|
timeoutMs: args.timeoutMs,
|
|
24243
24639
|
pollIntervalMs: args.pollIntervalMs,
|
|
24244
24640
|
progress: bootstrapProgress?.child("Waiting for warm pool binding")
|
|
24245
24641
|
});
|
|
24642
|
+
if (shouldRetryActivation(binding.project.infrastructureStatus, {
|
|
24643
|
+
resumed,
|
|
24644
|
+
timedOut: binding.timedOut
|
|
24645
|
+
}) && await retryActivation(client, projectId, steps, bootstrapProgress)) {
|
|
24646
|
+
binding = await waitForWarmPoolBinding(client, projectId, {
|
|
24647
|
+
timeoutMs: args.timeoutMs,
|
|
24648
|
+
pollIntervalMs: args.pollIntervalMs,
|
|
24649
|
+
progress: bootstrapProgress?.child("Waiting for warm pool binding")
|
|
24650
|
+
});
|
|
24651
|
+
}
|
|
24246
24652
|
steps.push({
|
|
24247
24653
|
step: "wait_warm_pool_binding",
|
|
24248
24654
|
status: binding.timedOut ? "running" : binding.status,
|
|
@@ -24622,7 +25028,7 @@ async function resolveProjectIdForPublish(client, config2, args, wait, progress,
|
|
|
24622
25028
|
import path80 from "node:path";
|
|
24623
25029
|
|
|
24624
25030
|
// src/cli/platform/helpers/dev-portal-site-api-url.ts
|
|
24625
|
-
import { readdir as
|
|
25031
|
+
import { readdir as readdir5, readFile as readFile27, writeFile as writeFile24 } from "node:fs/promises";
|
|
24626
25032
|
import path77 from "node:path";
|
|
24627
25033
|
var SITE_API_BASE_URL_ENV_KEY = "VITE_APITOGO_AUTHENTICATION_API_BASE_URL";
|
|
24628
25034
|
var PLACEHOLDER_HOST_MARKERS = [
|
|
@@ -24647,7 +25053,7 @@ async function readSiteApiBaseUrl(sourceDirectory) {
|
|
|
24647
25053
|
const resolved = path77.resolve(sourceDirectory);
|
|
24648
25054
|
for (const envFile of [".env", ".env.production"]) {
|
|
24649
25055
|
try {
|
|
24650
|
-
const content = await
|
|
25056
|
+
const content = await readFile27(path77.join(resolved, envFile), "utf8");
|
|
24651
25057
|
const lines = [...content.matchAll(ENV_LINE_GLOBAL_RE)];
|
|
24652
25058
|
if (lines.length === 0) {
|
|
24653
25059
|
continue;
|
|
@@ -24678,19 +25084,19 @@ async function patchSiteApiBaseUrl(sourceDirectory, apiBaseUrl) {
|
|
|
24678
25084
|
for (const envFile of [".env.production", ".env"]) {
|
|
24679
25085
|
const envPath = path77.join(resolved, envFile);
|
|
24680
25086
|
try {
|
|
24681
|
-
const content = await
|
|
25087
|
+
const content = await readFile27(envPath, "utf8");
|
|
24682
25088
|
const withoutAssignments = content.replace(ENV_LINE_GLOBAL_RE, "").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
24683
25089
|
const patched = withoutAssignments ? `${withoutAssignments}
|
|
24684
25090
|
${line2}
|
|
24685
25091
|
` : `${line2}
|
|
24686
25092
|
`;
|
|
24687
|
-
await
|
|
25093
|
+
await writeFile24(envPath, patched);
|
|
24688
25094
|
patchedExisting = true;
|
|
24689
25095
|
} catch {
|
|
24690
25096
|
}
|
|
24691
25097
|
}
|
|
24692
25098
|
if (!patchedExisting) {
|
|
24693
|
-
await
|
|
25099
|
+
await writeFile24(path77.join(resolved, ".env"), `${line2}
|
|
24694
25100
|
`);
|
|
24695
25101
|
}
|
|
24696
25102
|
}
|
|
@@ -24729,7 +25135,7 @@ async function ensureLiveSiteApiBaseUrlForBuild(_client, config2, args, resolveF
|
|
|
24729
25135
|
async function* walkFiles(root) {
|
|
24730
25136
|
let entries;
|
|
24731
25137
|
try {
|
|
24732
|
-
entries = await
|
|
25138
|
+
entries = await readdir5(root, { withFileTypes: true });
|
|
24733
25139
|
} catch {
|
|
24734
25140
|
return;
|
|
24735
25141
|
}
|
|
@@ -24758,7 +25164,7 @@ async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiB
|
|
|
24758
25164
|
let found = false;
|
|
24759
25165
|
let foundPlaceholder = false;
|
|
24760
25166
|
for await (const file of walkFiles(dist)) {
|
|
24761
|
-
const content = await
|
|
25167
|
+
const content = await readFile27(file, "utf8");
|
|
24762
25168
|
if (content.includes(needle)) {
|
|
24763
25169
|
found = true;
|
|
24764
25170
|
}
|
|
@@ -26525,7 +26931,12 @@ function printPlanCatalog(payload) {
|
|
|
26525
26931
|
if (typeof payload.freeProjectsQuotaHint === "string") {
|
|
26526
26932
|
printDiagnosticsToConsole(payload.freeProjectsQuotaHint);
|
|
26527
26933
|
}
|
|
26528
|
-
|
|
26934
|
+
printPlanOptions(
|
|
26935
|
+
Array.isArray(payload.platformPlanCatalog) ? payload.platformPlanCatalog : []
|
|
26936
|
+
);
|
|
26937
|
+
printDiagnosticsToConsole("");
|
|
26938
|
+
}
|
|
26939
|
+
function printPlanOptions(catalog) {
|
|
26529
26940
|
for (const entry of catalog) {
|
|
26530
26941
|
if (!entry || typeof entry !== "object") continue;
|
|
26531
26942
|
const plan2 = entry;
|
|
@@ -26543,7 +26954,12 @@ function printPlanCatalog(payload) {
|
|
|
26543
26954
|
printHintToConsole(` - ${limitation}`);
|
|
26544
26955
|
}
|
|
26545
26956
|
}
|
|
26957
|
+
}
|
|
26958
|
+
async function pickPlatformPlan() {
|
|
26959
|
+
printDiagnosticsToConsole("Which API ToGo plan should this project use?");
|
|
26960
|
+
printPlanOptions(PLATFORM_PLAN_CATALOG);
|
|
26546
26961
|
printDiagnosticsToConsole("");
|
|
26962
|
+
return askPlanChoice();
|
|
26547
26963
|
}
|
|
26548
26964
|
async function askPlanChoice() {
|
|
26549
26965
|
const rl = createInterface4({ input: input3, output: output2 });
|
|
@@ -26897,7 +27313,7 @@ import path82 from "node:path";
|
|
|
26897
27313
|
|
|
26898
27314
|
// src/cli/deploy/gateway.ts
|
|
26899
27315
|
import { createHash as createHash4 } from "node:crypto";
|
|
26900
|
-
import { readFile as
|
|
27316
|
+
import { readFile as readFile28 } from "node:fs/promises";
|
|
26901
27317
|
init_invariant();
|
|
26902
27318
|
var GatewayDeployError = class extends ApitogoError {
|
|
26903
27319
|
constructor(code, message, developerHint) {
|
|
@@ -26906,6 +27322,12 @@ var GatewayDeployError = class extends ApitogoError {
|
|
|
26906
27322
|
this.name = "GatewayDeployError";
|
|
26907
27323
|
}
|
|
26908
27324
|
};
|
|
27325
|
+
var API_HOSTING_MODULE = "apihub";
|
|
27326
|
+
function isHostingNotActivated(error) {
|
|
27327
|
+
return error instanceof PlatformApiError && /module is not activated/i.test(
|
|
27328
|
+
`${error.message} ${error.problem?.detail ?? ""}`
|
|
27329
|
+
);
|
|
27330
|
+
}
|
|
26909
27331
|
var sha256 = (text) => createHash4("sha256").update(text).digest("hex");
|
|
26910
27332
|
function gatewayNotDeployableReason(gateway) {
|
|
26911
27333
|
if (gateway.specs.length > 1)
|
|
@@ -26929,7 +27351,7 @@ async function readSpec(gateway) {
|
|
|
26929
27351
|
"Add one to gateway.specs in the config."
|
|
26930
27352
|
);
|
|
26931
27353
|
try {
|
|
26932
|
-
return await
|
|
27354
|
+
return await readFile28(file);
|
|
26933
27355
|
} catch {
|
|
26934
27356
|
throw new GatewayDeployError(
|
|
26935
27357
|
"GATEWAY_SPEC_UNREADABLE",
|
|
@@ -27044,6 +27466,32 @@ async function deployGateway(resolved, projectId, deps, options = {}) {
|
|
|
27044
27466
|
const timeoutMs = deps.reconcileTimeoutMs ?? 42e4;
|
|
27045
27467
|
const backendTimeoutMs = deps.backendTimeoutMs ?? 20 * 6e4;
|
|
27046
27468
|
const intervalMs = deps.pollIntervalMs ?? 2e3;
|
|
27469
|
+
const claimApiHosting = async () => {
|
|
27470
|
+
await client.activateModule?.(projectId, API_HOSTING_MODULE);
|
|
27471
|
+
const deadline = now() + backendTimeoutMs;
|
|
27472
|
+
for (; ; ) {
|
|
27473
|
+
const state = await client.getInfrastructureStatus?.(
|
|
27474
|
+
projectId,
|
|
27475
|
+
API_HOSTING_MODULE
|
|
27476
|
+
);
|
|
27477
|
+
if (!state || isTerminalInfrastructureStatus(state.status)) {
|
|
27478
|
+
if (state?.status.toLowerCase() === "failed")
|
|
27479
|
+
throw new GatewayDeployError(
|
|
27480
|
+
"GATEWAY_HOSTING_FAILED",
|
|
27481
|
+
`The platform could not prepare the API hosting: ${state.error ?? "no reason given"}.`,
|
|
27482
|
+
"Run `apitogo project create` again, or `apitogo deploy` once it is fixed."
|
|
27483
|
+
);
|
|
27484
|
+
return;
|
|
27485
|
+
}
|
|
27486
|
+
if (now() >= deadline)
|
|
27487
|
+
throw new GatewayDeployError(
|
|
27488
|
+
"GATEWAY_HOSTING_TIMEOUT",
|
|
27489
|
+
`The API hosting was not ready within ${Math.round(backendTimeoutMs / 6e4)} minutes.`,
|
|
27490
|
+
"Run deploy again to keep waiting."
|
|
27491
|
+
);
|
|
27492
|
+
await sleep5(intervalMs);
|
|
27493
|
+
}
|
|
27494
|
+
};
|
|
27047
27495
|
const backendStep = {
|
|
27048
27496
|
name: "backend",
|
|
27049
27497
|
hash: () => backend?.hash ?? "",
|
|
@@ -27061,14 +27509,28 @@ async function deployGateway(resolved, projectId, deps, options = {}) {
|
|
|
27061
27509
|
);
|
|
27062
27510
|
zipPath = packaged.zipPath;
|
|
27063
27511
|
let started;
|
|
27512
|
+
const upload = () => client.deployBackend(projectId, {
|
|
27513
|
+
archivePath: packaged.zipPath,
|
|
27514
|
+
runtime: backend.runtime,
|
|
27515
|
+
runtimeVersion: backend.runtimeVersion,
|
|
27516
|
+
healthCheckPath: backend.healthPath
|
|
27517
|
+
});
|
|
27064
27518
|
try {
|
|
27065
|
-
|
|
27066
|
-
|
|
27067
|
-
|
|
27068
|
-
|
|
27069
|
-
|
|
27070
|
-
|
|
27519
|
+
try {
|
|
27520
|
+
started = await upload();
|
|
27521
|
+
} catch (error) {
|
|
27522
|
+
if (!isHostingNotActivated(error) || !client.activateModule)
|
|
27523
|
+
throw error;
|
|
27524
|
+
await claimApiHosting();
|
|
27525
|
+
started = await upload();
|
|
27526
|
+
}
|
|
27071
27527
|
} catch (error) {
|
|
27528
|
+
if (isHostingNotActivated(error))
|
|
27529
|
+
throw new GatewayDeployError(
|
|
27530
|
+
"GATEWAY_HOSTING_NOT_ACTIVATED",
|
|
27531
|
+
"The project has no API hosting yet, so there is nowhere to run your backend.",
|
|
27532
|
+
"Run `apitogo project create` again: it finishes the same project and claims the hosting, then run `apitogo deploy`."
|
|
27533
|
+
);
|
|
27072
27534
|
return rejected("backend upload", error);
|
|
27073
27535
|
}
|
|
27074
27536
|
if (!started.reused) {
|
|
@@ -28747,7 +29209,7 @@ function formatStatus(output4) {
|
|
|
28747
29209
|
}
|
|
28748
29210
|
|
|
28749
29211
|
// src/cli/deploy/static-web-config.ts
|
|
28750
|
-
import { access as access13, writeFile as
|
|
29212
|
+
import { access as access13, writeFile as writeFile25 } from "node:fs/promises";
|
|
28751
29213
|
import path83 from "node:path";
|
|
28752
29214
|
var STATIC_WEB_CONFIG_FILE = "staticwebapp.config.json";
|
|
28753
29215
|
var STATIC_WEB_CONFIG_VERSION = 3;
|
|
@@ -28771,7 +29233,7 @@ async function ensureStaticWebConfig(buildDir) {
|
|
|
28771
29233
|
await access13(file);
|
|
28772
29234
|
return false;
|
|
28773
29235
|
} catch {
|
|
28774
|
-
await
|
|
29236
|
+
await writeFile25(
|
|
28775
29237
|
file,
|
|
28776
29238
|
`${JSON.stringify(DEFAULT_STATIC_WEB_CONFIG, null, 2)}
|
|
28777
29239
|
`,
|
|
@@ -29117,6 +29579,12 @@ function formatDeploy(output4) {
|
|
|
29117
29579
|
// src/cli/deploy/command.ts
|
|
29118
29580
|
async function deployProject(argv) {
|
|
29119
29581
|
const json = argv.json === true;
|
|
29582
|
+
const quiet = json || argv.verbose !== true;
|
|
29583
|
+
if (quiet) applyJsonOnlyDeployQuietMode();
|
|
29584
|
+
if (!json && quiet)
|
|
29585
|
+
printDiagnosticsToConsole(
|
|
29586
|
+
"Deploying. This can take a few minutes; add --verbose to see the build log."
|
|
29587
|
+
);
|
|
29120
29588
|
try {
|
|
29121
29589
|
const config2 = loadConfig();
|
|
29122
29590
|
const output4 = await deployV2(
|
|
@@ -29127,7 +29595,7 @@ async function deployProject(argv) {
|
|
|
29127
29595
|
env: argv.env,
|
|
29128
29596
|
force: argv.force === true,
|
|
29129
29597
|
yes: argv.yes === true,
|
|
29130
|
-
jsonOnly:
|
|
29598
|
+
jsonOnly: quiet
|
|
29131
29599
|
},
|
|
29132
29600
|
{
|
|
29133
29601
|
client: new PlatformApiClient(config2),
|
|
@@ -29201,6 +29669,10 @@ var deploy_default = {
|
|
|
29201
29669
|
type: "boolean",
|
|
29202
29670
|
describe: "Wait for long-running jobs",
|
|
29203
29671
|
default: true
|
|
29672
|
+
}).option("verbose", {
|
|
29673
|
+
type: "boolean",
|
|
29674
|
+
describe: "Config version 2: show the full build log (by default only the result of each service is printed)",
|
|
29675
|
+
default: false
|
|
29204
29676
|
}).option("json", {
|
|
29205
29677
|
type: "boolean",
|
|
29206
29678
|
describe: "Print raw JSON result only",
|
|
@@ -29217,7 +29689,7 @@ var deploy_default = {
|
|
|
29217
29689
|
};
|
|
29218
29690
|
|
|
29219
29691
|
// src/cli/export/handler.ts
|
|
29220
|
-
import { mkdir as mkdir19, writeFile as
|
|
29692
|
+
import { mkdir as mkdir19, writeFile as writeFile26 } from "node:fs/promises";
|
|
29221
29693
|
import path86 from "node:path";
|
|
29222
29694
|
init_invariant();
|
|
29223
29695
|
var ExportError = class extends ApitogoError {
|
|
@@ -29259,7 +29731,7 @@ async function exportOpenApi(args, deps) {
|
|
|
29259
29731
|
if (!args.out) return { text };
|
|
29260
29732
|
const file = path86.resolve(process.cwd(), args.out);
|
|
29261
29733
|
await mkdir19(path86.dirname(file), { recursive: true });
|
|
29262
|
-
await
|
|
29734
|
+
await writeFile26(file, text);
|
|
29263
29735
|
return { text, file };
|
|
29264
29736
|
}
|
|
29265
29737
|
async function exportCommand(args) {
|
|
@@ -29680,9 +30152,9 @@ var plan_default = {
|
|
|
29680
30152
|
};
|
|
29681
30153
|
|
|
29682
30154
|
// src/cli/project/create-handler.ts
|
|
29683
|
-
init_invariant();
|
|
29684
30155
|
import path88 from "node:path";
|
|
29685
30156
|
import open3 from "open";
|
|
30157
|
+
init_invariant();
|
|
29686
30158
|
|
|
29687
30159
|
// src/cli/project/v2.ts
|
|
29688
30160
|
init_invariant();
|
|
@@ -29893,6 +30365,17 @@ async function createLinkedProject(argv, dir) {
|
|
|
29893
30365
|
const json = argv.json === true;
|
|
29894
30366
|
try {
|
|
29895
30367
|
assertConfigChecksOut(await runCheckV2(dir));
|
|
30368
|
+
let plan2 = argv.plan;
|
|
30369
|
+
if (!plan2 && !json && process.stdin.isTTY && process.stdout.isTTY && !loadConfigV2(dir).resolved.project.id) {
|
|
30370
|
+
const picked = await pickPlatformPlan();
|
|
30371
|
+
if (!picked) {
|
|
30372
|
+
printResultToConsole(
|
|
30373
|
+
"No plan selected. Run `apitogo project create --plan free|starter|professional` when you are ready."
|
|
30374
|
+
);
|
|
30375
|
+
return;
|
|
30376
|
+
}
|
|
30377
|
+
plan2 = picked;
|
|
30378
|
+
}
|
|
29896
30379
|
const config2 = loadConfig();
|
|
29897
30380
|
const client = new PlatformApiClient(config2);
|
|
29898
30381
|
const progress = createConsoleProgressReporter();
|
|
@@ -29902,7 +30385,7 @@ async function createLinkedProject(argv, dir) {
|
|
|
29902
30385
|
name: argv.name,
|
|
29903
30386
|
description: argv.description,
|
|
29904
30387
|
wait: argv.wait !== false,
|
|
29905
|
-
plan:
|
|
30388
|
+
plan: plan2 ?? "free",
|
|
29906
30389
|
skipPayment: argv.skipPayment === true,
|
|
29907
30390
|
open: argv.open !== false,
|
|
29908
30391
|
// In JSON mode the link is returned and a later run continues; otherwise the command waits for the payment.
|
|
@@ -29946,7 +30429,7 @@ async function createLinkedProject(argv, dir) {
|
|
|
29946
30429
|
return;
|
|
29947
30430
|
}
|
|
29948
30431
|
printDiagnosticsToConsole(
|
|
29949
|
-
`Project ${result.projectName} (${result.projectId}) is ready on the ${result.plan ?? "free"} plan${result.modules.length > 0 ? ` with: ${result.modules.join(", ")}` : " (the gateway is created by `apitogo deploy`)"}.`
|
|
30432
|
+
`Project ${result.projectName} (${result.projectId}) is ready on the ${result.plan ?? "free"} plan${result.modules.length > 0 ? ` with: ${result.modules.map((id) => friendlyModuleNames(`module ${id}`)).join(", ")}` : " (the gateway is created by `apitogo deploy`)"}.`
|
|
29950
30433
|
);
|
|
29951
30434
|
printResultToConsole(
|
|
29952
30435
|
"The folder is linked. Run `apitogo deploy` to publish."
|