@lukoweb/apitogo 0.2.2 → 0.2.10
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 +2059 -476
- package/dist/declarations/cli/platform/client/platform-api.d.ts +114 -0
- package/package.json +1 -1
- package/src/config/v2/gateway-request.ts +123 -0
- package/src/config/v2/index.ts +11 -0
- package/src/config/v2/ledger.ts +16 -0
- package/src/config/v2/manifest.ts +70 -0
- package/src/config/v2/resolve.ts +2 -0
- package/src/config/v2/rules.ts +64 -0
- package/src/config/v2/schema.ts +34 -0
- package/src/config/v2/services.ts +17 -0
package/dist/cli/cli.js
CHANGED
|
@@ -836,7 +836,7 @@ import { stat as stat4 } from "node:fs/promises";
|
|
|
836
836
|
var fileExists;
|
|
837
837
|
var init_file_exists = __esm({
|
|
838
838
|
"src/config/file-exists.ts"() {
|
|
839
|
-
fileExists = (
|
|
839
|
+
fileExists = (path100) => stat4(path100).then(() => true).catch(() => false);
|
|
840
840
|
}
|
|
841
841
|
});
|
|
842
842
|
|
|
@@ -5809,24 +5809,24 @@ init_package_json();
|
|
|
5809
5809
|
import yargs from "yargs/yargs";
|
|
5810
5810
|
|
|
5811
5811
|
// src/cli/services/handler.ts
|
|
5812
|
-
import
|
|
5812
|
+
import path49 from "node:path";
|
|
5813
5813
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
5814
5814
|
|
|
5815
5815
|
// src/config/v2/errors.ts
|
|
5816
5816
|
init_invariant();
|
|
5817
|
-
function formatConfigIssue(
|
|
5818
|
-
const hint =
|
|
5819
|
-
const rest =
|
|
5820
|
-
const startsWithPath =
|
|
5821
|
-
const text = startsWithPath ?
|
|
5822
|
-
return `${text}${hint} [${
|
|
5817
|
+
function formatConfigIssue(issue3) {
|
|
5818
|
+
const hint = issue3.hint ? ` ${issue3.hint}` : "";
|
|
5819
|
+
const rest = issue3.message.slice(issue3.path.length);
|
|
5820
|
+
const startsWithPath = issue3.path !== "" && issue3.message.startsWith(issue3.path) && (rest === "" || /^[\s:]/.test(rest));
|
|
5821
|
+
const text = startsWithPath ? issue3.message : `${issue3.path === "" ? "(root)" : issue3.path}: ${issue3.message}`;
|
|
5822
|
+
return `${text}${hint} [${issue3.code}]`;
|
|
5823
5823
|
}
|
|
5824
5824
|
var ConfigError = class extends ApitogoError {
|
|
5825
5825
|
issues;
|
|
5826
5826
|
constructor(file, issues) {
|
|
5827
5827
|
super(
|
|
5828
5828
|
`${file} has ${issues.length} problem${issues.length === 1 ? "" : "s"}:
|
|
5829
|
-
${issues.map((
|
|
5829
|
+
${issues.map((issue3) => ` - ${formatConfigIssue(issue3)}`).join("\n")}`,
|
|
5830
5830
|
{ title: "Invalid apitogo config" }
|
|
5831
5831
|
);
|
|
5832
5832
|
this.name = "ConfigError";
|
|
@@ -6055,13 +6055,24 @@ var GatewayOriginSchema = z.strictObject({
|
|
|
6055
6055
|
runtime: z.enum(["nodejs", "python", "dotnet"]).optional(),
|
|
6056
6056
|
runtimeVersion: nonEmpty.optional(),
|
|
6057
6057
|
exclude: pathList.optional(),
|
|
6058
|
-
url: httpsUrl.optional()
|
|
6058
|
+
url: httpsUrl.optional(),
|
|
6059
|
+
/** Path the platform probes to check the origin, for example `/health`. */
|
|
6060
|
+
healthPath: z.string().regex(/^\/(?!\/)/, { error: 'must start with a single "/"' }).optional(),
|
|
6061
|
+
timeoutSeconds: z.int().min(1).max(120).optional(),
|
|
6062
|
+
/** What the gateway sends to the origin as credential. The value is always a secret reference. */
|
|
6063
|
+
auth: z.strictObject({
|
|
6064
|
+
type: z.enum(["none", "header", "mtls"]),
|
|
6065
|
+
name: nonEmpty.optional(),
|
|
6066
|
+
value: secretRef.optional()
|
|
6067
|
+
}).optional()
|
|
6059
6068
|
});
|
|
6060
6069
|
var GatewaySchema = z.strictObject({
|
|
6061
6070
|
enabled,
|
|
6062
6071
|
provider: nonEmpty.optional(),
|
|
6063
6072
|
origin: GatewayOriginSchema,
|
|
6064
6073
|
specs: z.array(path4).min(1, { error: "needs at least one OpenAPI file" }),
|
|
6074
|
+
/** Who may read the OpenAPI file the platform publishes for this gateway. Default: public. */
|
|
6075
|
+
export: z.strictObject({ access: z.enum(["public", "authenticated"]).optional() }).optional(),
|
|
6065
6076
|
auth: z.strictObject({
|
|
6066
6077
|
mode: z.enum(["identity", "oidc"]),
|
|
6067
6078
|
metadataUrl: httpsUrl.optional(),
|
|
@@ -6069,15 +6080,27 @@ var GatewaySchema = z.strictObject({
|
|
|
6069
6080
|
audiences: z.array(nonEmpty).optional()
|
|
6070
6081
|
}).optional(),
|
|
6071
6082
|
policies: z.strictObject({
|
|
6083
|
+
access: z.strictObject({
|
|
6084
|
+
jwt: z.boolean().optional(),
|
|
6085
|
+
apiKey: z.boolean().optional(),
|
|
6086
|
+
apiKeyHeader: nonEmpty.optional(),
|
|
6087
|
+
bindKeyToToken: z.boolean().optional()
|
|
6088
|
+
}).optional(),
|
|
6072
6089
|
rateLimit: z.strictObject({
|
|
6073
6090
|
calls: z.int().positive(),
|
|
6074
|
-
periodSeconds: z.int().positive()
|
|
6091
|
+
periodSeconds: z.int().positive(),
|
|
6092
|
+
scope: z.enum(["key", "entitlement"]).optional()
|
|
6075
6093
|
}).optional(),
|
|
6076
6094
|
quota: z.strictObject({
|
|
6077
6095
|
calls: z.int().positive(),
|
|
6078
|
-
periodDays: z.int().min(1)
|
|
6096
|
+
periodDays: z.int().min(1),
|
|
6097
|
+
scope: z.enum(["key", "entitlement"]).optional()
|
|
6079
6098
|
}).optional(),
|
|
6080
|
-
cors: z.array(z.url()).optional()
|
|
6099
|
+
cors: z.array(z.url()).optional(),
|
|
6100
|
+
operations: z.strictObject({
|
|
6101
|
+
public: z.array(nonEmpty).optional(),
|
|
6102
|
+
deny: z.array(nonEmpty).optional()
|
|
6103
|
+
}).optional()
|
|
6081
6104
|
}).optional()
|
|
6082
6105
|
});
|
|
6083
6106
|
var connectionId = z.string().regex(/^[a-z0-9-]{1,32}$/, {
|
|
@@ -6166,8 +6189,8 @@ var ConfigV2Schema = z.strictObject({
|
|
|
6166
6189
|
var REMOVED_KEYS = {
|
|
6167
6190
|
"plans.provider": "plans.provider was removed. Only plans.billing.provider (stripe or none) is selectable."
|
|
6168
6191
|
};
|
|
6169
|
-
function pathToString(
|
|
6170
|
-
return
|
|
6192
|
+
function pathToString(path100) {
|
|
6193
|
+
return path100.reduce(
|
|
6171
6194
|
(acc, segment) => typeof segment === "number" ? `${acc}[${segment}]` : acc === "" ? String(segment) : `${acc}.${String(segment)}`,
|
|
6172
6195
|
""
|
|
6173
6196
|
);
|
|
@@ -6187,9 +6210,9 @@ function step(schema2, segment) {
|
|
|
6187
6210
|
}
|
|
6188
6211
|
return void 0;
|
|
6189
6212
|
}
|
|
6190
|
-
function knownKeysAt(
|
|
6213
|
+
function knownKeysAt(path100) {
|
|
6191
6214
|
let current = ConfigV2Schema;
|
|
6192
|
-
for (const segment of
|
|
6215
|
+
for (const segment of path100) {
|
|
6193
6216
|
if (!current) return [];
|
|
6194
6217
|
current = step(current, segment);
|
|
6195
6218
|
}
|
|
@@ -6224,37 +6247,37 @@ function unknownKeyHint(fullPath, key, known) {
|
|
|
6224
6247
|
if (closest) return `Did you mean "${closest.candidate}"?`;
|
|
6225
6248
|
return known.length > 0 ? `Allowed keys: ${known.join(", ")}.` : void 0;
|
|
6226
6249
|
}
|
|
6227
|
-
function mapIssue(
|
|
6228
|
-
const
|
|
6229
|
-
switch (
|
|
6250
|
+
function mapIssue(issue3) {
|
|
6251
|
+
const path100 = pathToString(issue3.path);
|
|
6252
|
+
switch (issue3.code) {
|
|
6230
6253
|
case "unrecognized_keys":
|
|
6231
|
-
return
|
|
6232
|
-
const fullPath = pathToString([...
|
|
6254
|
+
return issue3.keys.map((key) => {
|
|
6255
|
+
const fullPath = pathToString([...issue3.path, key]);
|
|
6233
6256
|
return {
|
|
6234
6257
|
code: "CONFIG_UNKNOWN_KEY",
|
|
6235
6258
|
path: fullPath,
|
|
6236
6259
|
message: `Unknown key "${key}".`,
|
|
6237
|
-
hint: unknownKeyHint(fullPath, key, knownKeysAt(
|
|
6260
|
+
hint: unknownKeyHint(fullPath, key, knownKeysAt(issue3.path))
|
|
6238
6261
|
};
|
|
6239
6262
|
});
|
|
6240
6263
|
case "invalid_value":
|
|
6241
6264
|
return [
|
|
6242
6265
|
{
|
|
6243
6266
|
code: "CONFIG_INVALID_VALUE",
|
|
6244
|
-
path:
|
|
6245
|
-
message: `must be one of: ${
|
|
6267
|
+
path: path100,
|
|
6268
|
+
message: `must be one of: ${issue3.values.map((v) => JSON.stringify(v)).join(", ")}.`
|
|
6246
6269
|
}
|
|
6247
6270
|
];
|
|
6248
6271
|
case "custom": {
|
|
6249
|
-
const code = typeof
|
|
6250
|
-
return [{ code, path:
|
|
6272
|
+
const code = typeof issue3.params?.code === "string" ? issue3.params.code : "CONFIG_INVALID_VALUE";
|
|
6273
|
+
return [{ code, path: path100, message: issue3.message ?? "is invalid." }];
|
|
6251
6274
|
}
|
|
6252
6275
|
case "invalid_type":
|
|
6253
6276
|
return [
|
|
6254
|
-
/received undefined/.test(
|
|
6277
|
+
/received undefined/.test(issue3.message) ? { code: "CONFIG_MISSING_FIELD", path: path100, message: "is required." } : { code: "CONFIG_INVALID_VALUE", path: path100, message: issue3.message }
|
|
6255
6278
|
];
|
|
6256
6279
|
default:
|
|
6257
|
-
return [{ code: "CONFIG_INVALID_VALUE", path:
|
|
6280
|
+
return [{ code: "CONFIG_INVALID_VALUE", path: path100, message: issue3.message }];
|
|
6258
6281
|
}
|
|
6259
6282
|
}
|
|
6260
6283
|
function isRecord(value) {
|
|
@@ -6564,6 +6587,7 @@ function resolveConfigV2(config2, configPath) {
|
|
|
6564
6587
|
},
|
|
6565
6588
|
specs: gateway.specs.map((spec) => path7.resolve(configDir, spec)),
|
|
6566
6589
|
auth: gateway.auth,
|
|
6590
|
+
export: gateway.export,
|
|
6567
6591
|
policies: gateway.policies ?? {}
|
|
6568
6592
|
};
|
|
6569
6593
|
}
|
|
@@ -6703,7 +6727,7 @@ function dependencyRules(config2, resolved, out) {
|
|
|
6703
6727
|
function checkDependencies(config2, resolved) {
|
|
6704
6728
|
const issues = [];
|
|
6705
6729
|
dependencyRules(config2, resolved, {
|
|
6706
|
-
error: (
|
|
6730
|
+
error: (issue3) => void issues.push(issue3),
|
|
6707
6731
|
warn: () => void 0
|
|
6708
6732
|
});
|
|
6709
6733
|
return issues;
|
|
@@ -6801,10 +6825,55 @@ async function docsContentRule(config2, patterns, resolved, out) {
|
|
|
6801
6825
|
function isIpLiteral(hostname) {
|
|
6802
6826
|
return /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname) || hostname.startsWith("[");
|
|
6803
6827
|
}
|
|
6828
|
+
function originAuthRules(origin, out) {
|
|
6829
|
+
const auth = origin.auth;
|
|
6830
|
+
if (!auth) return;
|
|
6831
|
+
const base = "gateway.origin.auth";
|
|
6832
|
+
if (auth.type === "header") {
|
|
6833
|
+
if (!auth.name)
|
|
6834
|
+
out.error({
|
|
6835
|
+
code: "GATEWAY_ORIGIN_INCOMPLETE",
|
|
6836
|
+
path: `${base}.name`,
|
|
6837
|
+
message: "origin.auth.type is header, so origin.auth.name is required.",
|
|
6838
|
+
hint: 'For example: "X-Origin-Key".'
|
|
6839
|
+
});
|
|
6840
|
+
if (!auth.value)
|
|
6841
|
+
out.error({
|
|
6842
|
+
code: "GATEWAY_ORIGIN_INCOMPLETE",
|
|
6843
|
+
path: `${base}.value`,
|
|
6844
|
+
message: "origin.auth.type is header, so origin.auth.value is required.",
|
|
6845
|
+
hint: 'A secret reference, for example "secret:ORIGIN_KEY". Store it with `apitogo secrets set ORIGIN_KEY`.'
|
|
6846
|
+
});
|
|
6847
|
+
} else if (auth.type === "none" && (auth.name || auth.value)) {
|
|
6848
|
+
out.error({
|
|
6849
|
+
code: "GATEWAY_ORIGIN_INVALID",
|
|
6850
|
+
path: base,
|
|
6851
|
+
message: "origin.auth.type is none, but a name or value is set.",
|
|
6852
|
+
hint: 'Remove them, or set the type to "header".'
|
|
6853
|
+
});
|
|
6854
|
+
} else if (auth.type === "mtls") {
|
|
6855
|
+
out.error({
|
|
6856
|
+
code: "GATEWAY_CAPABILITY_UNSUPPORTED",
|
|
6857
|
+
path: `${base}.type`,
|
|
6858
|
+
message: "Client certificates (mtls) for the origin are not available yet.",
|
|
6859
|
+
hint: 'Use type "header" with a secret.'
|
|
6860
|
+
});
|
|
6861
|
+
}
|
|
6862
|
+
}
|
|
6804
6863
|
async function gatewayRules(resolved, out) {
|
|
6805
6864
|
const gateway = resolved.gateway;
|
|
6806
6865
|
if (!gateway?.enabled) return;
|
|
6807
6866
|
const origin = gateway.origin;
|
|
6867
|
+
(gateway.policies.cors ?? []).forEach((site, index) => {
|
|
6868
|
+
const { pathname, search, hash: hash2 } = new URL(site);
|
|
6869
|
+
if (pathname === "/" && !search && !hash2 && !site.endsWith("/")) return;
|
|
6870
|
+
out.error({
|
|
6871
|
+
code: "GATEWAY_CORS_ORIGIN_INVALID",
|
|
6872
|
+
path: `gateway.policies.cors[${index}]`,
|
|
6873
|
+
message: `"${site}" is not a browser origin: it has a path, a query or a trailing slash.`,
|
|
6874
|
+
hint: `Use only scheme and host, for example "${new URL(site).origin}".`
|
|
6875
|
+
});
|
|
6876
|
+
});
|
|
6808
6877
|
if (origin.mode === "managed") {
|
|
6809
6878
|
for (const field of ["source", "runtime", "runtimeVersion"]) {
|
|
6810
6879
|
if (!origin[field]) {
|
|
@@ -6881,6 +6950,15 @@ async function gatewayRules(resolved, out) {
|
|
|
6881
6950
|
}
|
|
6882
6951
|
}
|
|
6883
6952
|
}
|
|
6953
|
+
originAuthRules(origin, out);
|
|
6954
|
+
if (gateway.specs.length > 1) {
|
|
6955
|
+
out.warn({
|
|
6956
|
+
code: "GATEWAY_SPEC_INVALID",
|
|
6957
|
+
path: "gateway.specs",
|
|
6958
|
+
message: "gateway.specs lists several files, but the platform takes one OpenAPI file per gateway, so deploy will refuse it.",
|
|
6959
|
+
hint: "Merge them into one file."
|
|
6960
|
+
});
|
|
6961
|
+
}
|
|
6884
6962
|
for (const [index, spec] of gateway.specs.entries())
|
|
6885
6963
|
await specRule(spec, index, resolved, out);
|
|
6886
6964
|
authRule(resolved, out);
|
|
@@ -7119,8 +7197,8 @@ async function checkConfigV2(config2, resolved, context = {}) {
|
|
|
7119
7197
|
"online rules were not run (provider capabilities, origin reachability, OIDC discovery, tier limits)"
|
|
7120
7198
|
];
|
|
7121
7199
|
const out = {
|
|
7122
|
-
error: (
|
|
7123
|
-
warn: (
|
|
7200
|
+
error: (issue3) => errors.push(issue3),
|
|
7201
|
+
warn: (issue3) => warnings.push(issue3)
|
|
7124
7202
|
};
|
|
7125
7203
|
dependencyRules(config2, resolved, out);
|
|
7126
7204
|
secretRules(config2, context, out, skipped);
|
|
@@ -7223,7 +7301,7 @@ var ServiceManifestSchema = z3.looseObject({
|
|
|
7223
7301
|
}).optional()
|
|
7224
7302
|
}).default({})
|
|
7225
7303
|
});
|
|
7226
|
-
var issue = (code,
|
|
7304
|
+
var issue = (code, path100, message, hint) => ({ code, path: path100, message, hint });
|
|
7227
7305
|
function parseManifest(raw, file = "services.json") {
|
|
7228
7306
|
const version = typeof raw === "object" && raw !== null && "schemaVersion" in raw ? raw.schemaVersion : void 0;
|
|
7229
7307
|
if (typeof version === "number" && version > SUPPORTED_MANIFEST_SCHEMA) {
|
|
@@ -7351,6 +7429,34 @@ function withPortalEntry(existing, info) {
|
|
|
7351
7429
|
}
|
|
7352
7430
|
};
|
|
7353
7431
|
}
|
|
7432
|
+
function withGatewayEntry(existing, info) {
|
|
7433
|
+
const base = existing ?? parseManifest({
|
|
7434
|
+
schemaVersion: SUPPORTED_MANIFEST_SCHEMA,
|
|
7435
|
+
project: { name: info.projectName }
|
|
7436
|
+
});
|
|
7437
|
+
const baseUrl = info.baseUrl.replace(/\/+$/, "");
|
|
7438
|
+
return {
|
|
7439
|
+
...base,
|
|
7440
|
+
schemaVersion: SUPPORTED_MANIFEST_SCHEMA,
|
|
7441
|
+
generatedAt: (info.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
7442
|
+
preview: void 0,
|
|
7443
|
+
project: { ...base.project, id: info.projectId, name: info.projectName },
|
|
7444
|
+
services: {
|
|
7445
|
+
...base.services,
|
|
7446
|
+
gateway: {
|
|
7447
|
+
...base.services.gateway,
|
|
7448
|
+
provider: info.provider,
|
|
7449
|
+
status: "ready",
|
|
7450
|
+
baseUrl,
|
|
7451
|
+
audience: baseUrl,
|
|
7452
|
+
capabilities: info.capabilities,
|
|
7453
|
+
...info.openapiUrl ? { openapiUrl: info.openapiUrl } : {},
|
|
7454
|
+
...info.openapiHash ? { openapiHash: info.openapiHash } : {},
|
|
7455
|
+
...info.keyHeaderName ? { keyHeaderName: info.keyHeaderName } : {}
|
|
7456
|
+
}
|
|
7457
|
+
}
|
|
7458
|
+
};
|
|
7459
|
+
}
|
|
7354
7460
|
async function writeManifest(state, manifest) {
|
|
7355
7461
|
const file = manifestPathFor(state);
|
|
7356
7462
|
await mkdir3(path10.dirname(file), { recursive: true });
|
|
@@ -7368,6 +7474,78 @@ async function writeManifest(state, manifest) {
|
|
|
7368
7474
|
throw error;
|
|
7369
7475
|
}
|
|
7370
7476
|
}
|
|
7477
|
+
async function removeServicesFromManifest(state, names) {
|
|
7478
|
+
const existing = await readManifest(state);
|
|
7479
|
+
if (!existing) return;
|
|
7480
|
+
const services = Object.fromEntries(
|
|
7481
|
+
Object.entries(existing.services).filter(
|
|
7482
|
+
([name]) => !names.includes(name)
|
|
7483
|
+
)
|
|
7484
|
+
);
|
|
7485
|
+
if (Object.keys(services).length === 0) {
|
|
7486
|
+
await rm(manifestPathFor(state), { force: true });
|
|
7487
|
+
return;
|
|
7488
|
+
}
|
|
7489
|
+
await writeManifest(state, { ...existing, services });
|
|
7490
|
+
}
|
|
7491
|
+
|
|
7492
|
+
// src/config/v2/gateway-request.ts
|
|
7493
|
+
function derivedCorsSites(sites) {
|
|
7494
|
+
const originOf = (url2) => {
|
|
7495
|
+
if (!url2) return void 0;
|
|
7496
|
+
try {
|
|
7497
|
+
const { protocol, origin } = new URL(url2);
|
|
7498
|
+
return protocol === "https:" ? origin : void 0;
|
|
7499
|
+
} catch {
|
|
7500
|
+
return void 0;
|
|
7501
|
+
}
|
|
7502
|
+
};
|
|
7503
|
+
const found = [
|
|
7504
|
+
{ origin: originOf(sites.docsUrl), source: "docs" },
|
|
7505
|
+
{ origin: originOf(sites.portalUrl), source: "portal" }
|
|
7506
|
+
].flatMap((site) => site.origin ? [{ ...site, origin: site.origin }] : []);
|
|
7507
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7508
|
+
return found.filter((site) => !seen.has(site.origin) && seen.add(site.origin)).sort((a, b) => a.origin < b.origin ? -1 : a.origin > b.origin ? 1 : 0);
|
|
7509
|
+
}
|
|
7510
|
+
var defined = (value) => Object.fromEntries(
|
|
7511
|
+
Object.entries(value).filter(([, item]) => item !== void 0)
|
|
7512
|
+
);
|
|
7513
|
+
function gatewayConfigBody(gateway, derivedCors = []) {
|
|
7514
|
+
const { origin, auth, policies } = gateway;
|
|
7515
|
+
const body = {
|
|
7516
|
+
provider: gateway.provider,
|
|
7517
|
+
origin: defined({
|
|
7518
|
+
mode: origin.mode,
|
|
7519
|
+
url: origin.url,
|
|
7520
|
+
healthPath: origin.healthPath,
|
|
7521
|
+
timeoutSeconds: origin.timeoutSeconds,
|
|
7522
|
+
auth: origin.auth ? defined({ ...origin.auth }) : void 0
|
|
7523
|
+
})
|
|
7524
|
+
};
|
|
7525
|
+
if (auth) body.auth = defined({ ...auth });
|
|
7526
|
+
if (gateway.export) body.export = defined({ ...gateway.export });
|
|
7527
|
+
if (derivedCors.length > 0) body.corsDerived = derivedCors;
|
|
7528
|
+
if (Object.keys(policies).length > 0) {
|
|
7529
|
+
body.policies = defined({
|
|
7530
|
+
access: policies.access ? defined({ ...policies.access }) : void 0,
|
|
7531
|
+
rateLimit: policies.rateLimit ? defined({ ...policies.rateLimit }) : void 0,
|
|
7532
|
+
quota: policies.quota ? defined({ ...policies.quota }) : void 0,
|
|
7533
|
+
cors: policies.cors,
|
|
7534
|
+
operations: policies.operations ? defined({ ...policies.operations }) : void 0
|
|
7535
|
+
});
|
|
7536
|
+
}
|
|
7537
|
+
return body;
|
|
7538
|
+
}
|
|
7539
|
+
function stableJson(value) {
|
|
7540
|
+
return JSON.stringify(
|
|
7541
|
+
value,
|
|
7542
|
+
(_key, item) => item && typeof item === "object" && !Array.isArray(item) ? Object.fromEntries(
|
|
7543
|
+
Object.entries(item).sort(
|
|
7544
|
+
([a], [b]) => a < b ? -1 : a > b ? 1 : 0
|
|
7545
|
+
)
|
|
7546
|
+
) : item
|
|
7547
|
+
);
|
|
7548
|
+
}
|
|
7371
7549
|
|
|
7372
7550
|
// src/config/v2/wrapper.ts
|
|
7373
7551
|
import { mkdir as mkdir4, readFile as readFile4, rename as rename2, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
|
|
@@ -8030,12 +8208,12 @@ async function planAddService(service, options) {
|
|
|
8030
8208
|
)
|
|
8031
8209
|
);
|
|
8032
8210
|
const added = dependencyProblems(next, resolved.configPath).filter(
|
|
8033
|
-
(
|
|
8211
|
+
(issue3) => !before.has(issue3.code + issue3.path)
|
|
8034
8212
|
);
|
|
8035
8213
|
if (added.length > 0) {
|
|
8036
8214
|
throw new ServiceEditError(
|
|
8037
|
-
added.map((
|
|
8038
|
-
added.map((
|
|
8215
|
+
added.map((issue3) => issue3.message).join(" "),
|
|
8216
|
+
added.map((issue3) => issue3.hint).filter(Boolean).join(" ") || "Add the missing service first."
|
|
8039
8217
|
);
|
|
8040
8218
|
}
|
|
8041
8219
|
}
|
|
@@ -8064,11 +8242,11 @@ async function planRemoveService(service, options) {
|
|
|
8064
8242
|
dependencyProblems(config2, resolved.configPath).map((i) => i.code + i.path)
|
|
8065
8243
|
);
|
|
8066
8244
|
const caused = dependencyProblems(next, resolved.configPath).filter(
|
|
8067
|
-
(
|
|
8245
|
+
(issue3) => !before.has(issue3.code + issue3.path)
|
|
8068
8246
|
);
|
|
8069
8247
|
if (caused.length > 0) {
|
|
8070
8248
|
throw new ServiceEditError(
|
|
8071
|
-
`${service} cannot be removed while something depends on it: ${caused.map((
|
|
8249
|
+
`${service} cannot be removed while something depends on it: ${caused.map((issue3) => issue3.message).join(" ")}`,
|
|
8072
8250
|
"Remove or change the dependent service first."
|
|
8073
8251
|
);
|
|
8074
8252
|
}
|
|
@@ -8139,6 +8317,18 @@ async function setProjectId(dir, projectId) {
|
|
|
8139
8317
|
await writeConfig(resolved.configPath, next);
|
|
8140
8318
|
return true;
|
|
8141
8319
|
}
|
|
8320
|
+
async function clearProjectId(dir, projectId) {
|
|
8321
|
+
const { config: config2, resolved } = loadConfigV2(dir);
|
|
8322
|
+
if (config2.project?.id !== projectId) return false;
|
|
8323
|
+
const { id: _id, ...project } = config2.project;
|
|
8324
|
+
const { project: _old, ...rest } = config2;
|
|
8325
|
+
const next = reparse(resolved.configPath, {
|
|
8326
|
+
...rest,
|
|
8327
|
+
...Object.keys(project).length > 0 ? { project } : {}
|
|
8328
|
+
});
|
|
8329
|
+
await writeConfig(resolved.configPath, next);
|
|
8330
|
+
return true;
|
|
8331
|
+
}
|
|
8142
8332
|
|
|
8143
8333
|
// src/config/v2/ledger.ts
|
|
8144
8334
|
init_invariant();
|
|
@@ -8273,13 +8463,24 @@ async function runPipeline(state, service, steps, options = {}) {
|
|
|
8273
8463
|
}
|
|
8274
8464
|
return outcomes;
|
|
8275
8465
|
}
|
|
8466
|
+
async function clearServices(state, services) {
|
|
8467
|
+
const ledger = await readLedger(state);
|
|
8468
|
+
const remaining = Object.fromEntries(
|
|
8469
|
+
Object.entries(ledger.services).filter(
|
|
8470
|
+
([name]) => !services.includes(name)
|
|
8471
|
+
)
|
|
8472
|
+
);
|
|
8473
|
+
if (Object.keys(remaining).length === Object.keys(ledger.services).length)
|
|
8474
|
+
return;
|
|
8475
|
+
await writeLedger(state, { ...ledger, services: remaining });
|
|
8476
|
+
}
|
|
8276
8477
|
|
|
8277
8478
|
// src/cli/services/handler.ts
|
|
8278
8479
|
init_invariant();
|
|
8279
8480
|
|
|
8280
8481
|
// src/cli/check/v2.ts
|
|
8281
8482
|
init_apitogo_config_core();
|
|
8282
|
-
import { readFile as
|
|
8483
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
8283
8484
|
import path46 from "node:path";
|
|
8284
8485
|
|
|
8285
8486
|
// src/cli/platform/client/platform-api.ts
|
|
@@ -8393,8 +8594,8 @@ var PlatformApiClient = class {
|
|
|
8393
8594
|
constructor(config2) {
|
|
8394
8595
|
this.config = config2;
|
|
8395
8596
|
}
|
|
8396
|
-
url(
|
|
8397
|
-
return `${this.config.apiUrl}${
|
|
8597
|
+
url(path100) {
|
|
8598
|
+
return `${this.config.apiUrl}${path100.startsWith("/") ? path100 : `/${path100}`}`;
|
|
8398
8599
|
}
|
|
8399
8600
|
headers(extra) {
|
|
8400
8601
|
return {
|
|
@@ -8404,9 +8605,9 @@ var PlatformApiClient = class {
|
|
|
8404
8605
|
...extra
|
|
8405
8606
|
};
|
|
8406
8607
|
}
|
|
8407
|
-
async request(method,
|
|
8608
|
+
async request(method, path100, init4) {
|
|
8408
8609
|
const response = await fetchWithRetry(
|
|
8409
|
-
this.url(
|
|
8610
|
+
this.url(path100),
|
|
8410
8611
|
{
|
|
8411
8612
|
method,
|
|
8412
8613
|
...init4,
|
|
@@ -8621,6 +8822,92 @@ var PlatformApiClient = class {
|
|
|
8621
8822
|
}
|
|
8622
8823
|
return await response.json();
|
|
8623
8824
|
}
|
|
8825
|
+
/** Uploads the OpenAPI file of the gateway. The same file twice changes nothing. */
|
|
8826
|
+
async putGatewaySpec(projectId, content) {
|
|
8827
|
+
return this.request(
|
|
8828
|
+
"PUT",
|
|
8829
|
+
`/api/v1/projects/${projectId}/gateway/spec`,
|
|
8830
|
+
{
|
|
8831
|
+
headers: { "Content-Type": "application/json" },
|
|
8832
|
+
body: content
|
|
8833
|
+
}
|
|
8834
|
+
);
|
|
8835
|
+
}
|
|
8836
|
+
/** Sends the wanted gateway state. The platform checks it all at once and answers with a ticket to poll. */
|
|
8837
|
+
putGatewayConfig(projectId, body) {
|
|
8838
|
+
return this.request(
|
|
8839
|
+
"PUT",
|
|
8840
|
+
`/api/v1/projects/${projectId}/gateway/config`,
|
|
8841
|
+
{
|
|
8842
|
+
headers: { "Content-Type": "application/json" },
|
|
8843
|
+
body: JSON.stringify(body)
|
|
8844
|
+
}
|
|
8845
|
+
);
|
|
8846
|
+
}
|
|
8847
|
+
getGatewayReconciliation(projectId, reconciliationId) {
|
|
8848
|
+
return this.request(
|
|
8849
|
+
"GET",
|
|
8850
|
+
`/api/v1/projects/${projectId}/gateway/reconciliations/${reconciliationId}`
|
|
8851
|
+
);
|
|
8852
|
+
}
|
|
8853
|
+
getGatewayStatus(projectId) {
|
|
8854
|
+
return this.request(
|
|
8855
|
+
"GET",
|
|
8856
|
+
`/api/v1/projects/${projectId}/gateway/status`
|
|
8857
|
+
);
|
|
8858
|
+
}
|
|
8859
|
+
/** The OpenAPI file as the gateway serves it (servers, security and denied operations already applied). */
|
|
8860
|
+
async getGatewayOpenApi(projectId) {
|
|
8861
|
+
const response = await fetchWithRetry(
|
|
8862
|
+
this.url(`/api/v1/projects/${projectId}/gateway/openapi.json`),
|
|
8863
|
+
{ method: "GET", headers: this.headers() },
|
|
8864
|
+
{
|
|
8865
|
+
method: "GET",
|
|
8866
|
+
retryMutatingOnServerError: false,
|
|
8867
|
+
maxRetries: MAX_RETRIES
|
|
8868
|
+
}
|
|
8869
|
+
);
|
|
8870
|
+
if (!response.ok) throw await parseErrorResponse(response);
|
|
8871
|
+
return response.text();
|
|
8872
|
+
}
|
|
8873
|
+
getGatewayCapabilities(projectId) {
|
|
8874
|
+
return this.request(
|
|
8875
|
+
"GET",
|
|
8876
|
+
`/api/v1/projects/${projectId}/gateway/capabilities`
|
|
8877
|
+
);
|
|
8878
|
+
}
|
|
8879
|
+
/**
|
|
8880
|
+
* The online rules of `check`: origin address, OIDC discovery, plan capabilities, secrets and the OpenAPI text.
|
|
8881
|
+
* Changes nothing on the platform and always answers with the list of problems.
|
|
8882
|
+
*/
|
|
8883
|
+
checkGateway(projectId, body) {
|
|
8884
|
+
return this.request(
|
|
8885
|
+
"POST",
|
|
8886
|
+
`/api/v1/projects/${projectId}/gateway/check`,
|
|
8887
|
+
{
|
|
8888
|
+
headers: { "Content-Type": "application/json" },
|
|
8889
|
+
body: JSON.stringify(body)
|
|
8890
|
+
}
|
|
8891
|
+
);
|
|
8892
|
+
}
|
|
8893
|
+
/** The dry run of a deploy: what the gateway would change. The platform changes nothing. */
|
|
8894
|
+
planGateway(projectId, body) {
|
|
8895
|
+
return this.request(
|
|
8896
|
+
"POST",
|
|
8897
|
+
`/api/v1/projects/${projectId}/gateway/plan`,
|
|
8898
|
+
{
|
|
8899
|
+
headers: { "Content-Type": "application/json" },
|
|
8900
|
+
body: JSON.stringify(body)
|
|
8901
|
+
}
|
|
8902
|
+
);
|
|
8903
|
+
}
|
|
8904
|
+
/** Asks the platform to remove the gateway. Answers with a ticket; the platform revokes keys and frees the unit. */
|
|
8905
|
+
deleteGateway(projectId) {
|
|
8906
|
+
return this.request(
|
|
8907
|
+
"DELETE",
|
|
8908
|
+
`/api/v1/projects/${projectId}/gateway`
|
|
8909
|
+
);
|
|
8910
|
+
}
|
|
8624
8911
|
getDevPortalBackend(projectId) {
|
|
8625
8912
|
return this.request(
|
|
8626
8913
|
"GET",
|
|
@@ -8683,15 +8970,15 @@ var PlatformApiClient = class {
|
|
|
8683
8970
|
}
|
|
8684
8971
|
);
|
|
8685
8972
|
}
|
|
8686
|
-
deleteDevPortalPage(projectId,
|
|
8687
|
-
const query = new URLSearchParams({ path:
|
|
8973
|
+
deleteDevPortalPage(projectId, path100) {
|
|
8974
|
+
const query = new URLSearchParams({ path: path100 });
|
|
8688
8975
|
return this.request(
|
|
8689
8976
|
"DELETE",
|
|
8690
8977
|
`/api/v1/projects/${projectId}/dev-portal/pages/files?${query.toString()}`
|
|
8691
8978
|
);
|
|
8692
8979
|
}
|
|
8693
|
-
renameDevPortalPage(projectId,
|
|
8694
|
-
const query = new URLSearchParams({ path:
|
|
8980
|
+
renameDevPortalPage(projectId, path100, newName) {
|
|
8981
|
+
const query = new URLSearchParams({ path: path100 });
|
|
8695
8982
|
return this.request(
|
|
8696
8983
|
"POST",
|
|
8697
8984
|
`/api/v1/projects/${projectId}/dev-portal/pages/files/rename?${query.toString()}`,
|
|
@@ -8807,8 +9094,8 @@ var PlatformApiClient = class {
|
|
|
8807
9094
|
* the project-less endpoint is used; `planTier` overrides the tier used for paid-plan checks.
|
|
8808
9095
|
*/
|
|
8809
9096
|
validateGatewayPlans(plans, options = {}) {
|
|
8810
|
-
const
|
|
8811
|
-
return this.request("POST",
|
|
9097
|
+
const path100 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
|
|
9098
|
+
return this.request("POST", path100, {
|
|
8812
9099
|
headers: { "Content-Type": "application/json" },
|
|
8813
9100
|
body: JSON.stringify({ plans, planTier: options.planTier })
|
|
8814
9101
|
});
|
|
@@ -8909,8 +9196,8 @@ var PlatformApiClient = class {
|
|
|
8909
9196
|
}
|
|
8910
9197
|
};
|
|
8911
9198
|
async function readFileBytes(filePath) {
|
|
8912
|
-
const { readFile:
|
|
8913
|
-
const buffer = await
|
|
9199
|
+
const { readFile: readFile29 } = await import("node:fs/promises");
|
|
9200
|
+
const buffer = await readFile29(filePath);
|
|
8914
9201
|
const arrayBuffer = new ArrayBuffer(buffer.byteLength);
|
|
8915
9202
|
new Uint8Array(arrayBuffer).set(buffer);
|
|
8916
9203
|
return arrayBuffer;
|
|
@@ -9164,7 +9451,7 @@ import {
|
|
|
9164
9451
|
// package.json
|
|
9165
9452
|
var package_default = {
|
|
9166
9453
|
name: "@lukoweb/apitogo",
|
|
9167
|
-
version: "0.2.
|
|
9454
|
+
version: "0.2.10",
|
|
9168
9455
|
type: "module",
|
|
9169
9456
|
sideEffects: [
|
|
9170
9457
|
"**/*.css",
|
|
@@ -9946,14 +10233,14 @@ var flattenAllOf = (schema2) => {
|
|
|
9946
10233
|
};
|
|
9947
10234
|
|
|
9948
10235
|
// src/lib/util/traverse.ts
|
|
9949
|
-
var traverse = (specification, transform,
|
|
9950
|
-
const transformed = transform(specification,
|
|
10236
|
+
var traverse = (specification, transform, path100 = []) => {
|
|
10237
|
+
const transformed = transform(specification, path100);
|
|
9951
10238
|
if (typeof transformed !== "object" || transformed === null) {
|
|
9952
10239
|
return transformed;
|
|
9953
10240
|
}
|
|
9954
10241
|
const result = Array.isArray(transformed) ? [] : {};
|
|
9955
10242
|
for (const [key, value] of Object.entries(transformed)) {
|
|
9956
|
-
const currentPath = [...
|
|
10243
|
+
const currentPath = [...path100, key];
|
|
9957
10244
|
if (Array.isArray(value)) {
|
|
9958
10245
|
result[key] = value.map(
|
|
9959
10246
|
(item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
|
|
@@ -9981,9 +10268,9 @@ var resolveLocalRef = (schema2, ref) => {
|
|
|
9981
10268
|
if (schemaCache?.has(ref)) {
|
|
9982
10269
|
return schemaCache.get(ref);
|
|
9983
10270
|
}
|
|
9984
|
-
const
|
|
10271
|
+
const path100 = ref.split("/").slice(1);
|
|
9985
10272
|
let current = schema2;
|
|
9986
|
-
for (const segment of
|
|
10273
|
+
for (const segment of path100) {
|
|
9987
10274
|
if (!current || typeof current !== "object") {
|
|
9988
10275
|
current = null;
|
|
9989
10276
|
}
|
|
@@ -10002,7 +10289,7 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
10002
10289
|
}
|
|
10003
10290
|
const cloned = structuredClone(schema2);
|
|
10004
10291
|
const visited = /* @__PURE__ */ new Set();
|
|
10005
|
-
const resolve = async (current,
|
|
10292
|
+
const resolve = async (current, path100) => {
|
|
10006
10293
|
if (isIndexableObject(current)) {
|
|
10007
10294
|
if (visited.has(current)) {
|
|
10008
10295
|
return CIRCULAR_REF;
|
|
@@ -10010,7 +10297,7 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
10010
10297
|
visited.add(current);
|
|
10011
10298
|
if (Array.isArray(current)) {
|
|
10012
10299
|
for (let index = 0; index < current.length; index++) {
|
|
10013
|
-
current[index] = await resolve(current[index], `${
|
|
10300
|
+
current[index] = await resolve(current[index], `${path100}/${index}`);
|
|
10014
10301
|
}
|
|
10015
10302
|
} else {
|
|
10016
10303
|
if ("$ref" in current && typeof current.$ref === "string") {
|
|
@@ -10021,13 +10308,13 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
10021
10308
|
for (const resolver of resolvers) {
|
|
10022
10309
|
const resolved = await resolver($ref);
|
|
10023
10310
|
if (resolved) {
|
|
10024
|
-
result2 = await resolve(resolved,
|
|
10311
|
+
result2 = await resolve(resolved, path100);
|
|
10025
10312
|
break;
|
|
10026
10313
|
}
|
|
10027
10314
|
}
|
|
10028
10315
|
if (result2 === void 0) {
|
|
10029
10316
|
const resolved = await resolveLocalRef(cloned, $ref);
|
|
10030
|
-
result2 = await resolve(resolved,
|
|
10317
|
+
result2 = await resolve(resolved, path100);
|
|
10031
10318
|
}
|
|
10032
10319
|
if (hasSiblings) {
|
|
10033
10320
|
if (result2 === CIRCULAR_REF) {
|
|
@@ -10040,7 +10327,7 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
10040
10327
|
return result2;
|
|
10041
10328
|
}
|
|
10042
10329
|
for (const key in current) {
|
|
10043
|
-
current[key] = await resolve(current[key], `${
|
|
10330
|
+
current[key] = await resolve(current[key], `${path100}/${key}`);
|
|
10044
10331
|
}
|
|
10045
10332
|
}
|
|
10046
10333
|
visited.delete(current);
|
|
@@ -10079,9 +10366,9 @@ var upgradeSchema = (schema2) => {
|
|
|
10079
10366
|
}
|
|
10080
10367
|
return sub;
|
|
10081
10368
|
});
|
|
10082
|
-
schema2 = traverse(schema2, (sub,
|
|
10369
|
+
schema2 = traverse(schema2, (sub, path100) => {
|
|
10083
10370
|
if (sub.example !== void 0) {
|
|
10084
|
-
if (isSchemaPath(
|
|
10371
|
+
if (isSchemaPath(path100 ?? [])) {
|
|
10085
10372
|
sub.examples = [sub.example];
|
|
10086
10373
|
} else {
|
|
10087
10374
|
sub.examples = {
|
|
@@ -10094,11 +10381,11 @@ var upgradeSchema = (schema2) => {
|
|
|
10094
10381
|
}
|
|
10095
10382
|
return sub;
|
|
10096
10383
|
});
|
|
10097
|
-
schema2 = traverse(schema2, (schema3,
|
|
10384
|
+
schema2 = traverse(schema2, (schema3, path100) => {
|
|
10098
10385
|
if (schema3.type === "object" && schema3.properties !== void 0) {
|
|
10099
|
-
const parentPath =
|
|
10386
|
+
const parentPath = path100?.slice(0, -1);
|
|
10100
10387
|
const isMultipart = parentPath?.some((segment, index) => {
|
|
10101
|
-
return segment === "content" &&
|
|
10388
|
+
return segment === "content" && path100?.[index + 1] === "multipart/form-data";
|
|
10102
10389
|
});
|
|
10103
10390
|
if (isMultipart) {
|
|
10104
10391
|
const entries = Object.entries(schema3.properties);
|
|
@@ -10112,8 +10399,8 @@ var upgradeSchema = (schema2) => {
|
|
|
10112
10399
|
}
|
|
10113
10400
|
return schema3;
|
|
10114
10401
|
});
|
|
10115
|
-
schema2 = traverse(schema2, (schema3,
|
|
10116
|
-
if (
|
|
10402
|
+
schema2 = traverse(schema2, (schema3, path100) => {
|
|
10403
|
+
if (path100?.includes("content") && path100.includes("application/octet-stream")) {
|
|
10117
10404
|
return {};
|
|
10118
10405
|
}
|
|
10119
10406
|
if (schema3.type === "string" && schema3.format === "binary") {
|
|
@@ -10139,11 +10426,11 @@ var upgradeSchema = (schema2) => {
|
|
|
10139
10426
|
}
|
|
10140
10427
|
return sub;
|
|
10141
10428
|
});
|
|
10142
|
-
schema2 = traverse(schema2, (schema3,
|
|
10429
|
+
schema2 = traverse(schema2, (schema3, path100) => {
|
|
10143
10430
|
if (schema3.type === "string" && schema3.format === "byte") {
|
|
10144
|
-
const parentPath =
|
|
10431
|
+
const parentPath = path100?.slice(0, -1);
|
|
10145
10432
|
const contentMediaType = parentPath?.find(
|
|
10146
|
-
(_, index) =>
|
|
10433
|
+
(_, index) => path100?.[index - 1] === "content"
|
|
10147
10434
|
);
|
|
10148
10435
|
return {
|
|
10149
10436
|
type: "string",
|
|
@@ -10155,7 +10442,7 @@ var upgradeSchema = (schema2) => {
|
|
|
10155
10442
|
});
|
|
10156
10443
|
return schema2;
|
|
10157
10444
|
};
|
|
10158
|
-
function isSchemaPath(
|
|
10445
|
+
function isSchemaPath(path100) {
|
|
10159
10446
|
const schemaLocations = [
|
|
10160
10447
|
["components", "schemas"],
|
|
10161
10448
|
"properties",
|
|
@@ -10168,10 +10455,10 @@ function isSchemaPath(path95) {
|
|
|
10168
10455
|
];
|
|
10169
10456
|
return schemaLocations.some((location) => {
|
|
10170
10457
|
if (Array.isArray(location)) {
|
|
10171
|
-
return location.every((segment, index) =>
|
|
10458
|
+
return location.every((segment, index) => path100[index] === segment);
|
|
10172
10459
|
}
|
|
10173
|
-
return
|
|
10174
|
-
}) ||
|
|
10460
|
+
return path100.includes(location);
|
|
10461
|
+
}) || path100.includes("schema") || path100.some((segment) => segment.endsWith("Schema"));
|
|
10175
10462
|
}
|
|
10176
10463
|
|
|
10177
10464
|
// src/lib/oas/parser/index.ts
|
|
@@ -10253,13 +10540,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
|
|
|
10253
10540
|
"anyOf",
|
|
10254
10541
|
"oneOf"
|
|
10255
10542
|
]);
|
|
10256
|
-
var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(),
|
|
10543
|
+
var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path100 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
|
|
10257
10544
|
if (obj === null || typeof obj !== "object") return obj;
|
|
10258
10545
|
const refPath = obj.__$ref;
|
|
10259
10546
|
const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
|
|
10260
10547
|
if (isCircular) {
|
|
10261
10548
|
if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
|
|
10262
|
-
const circularProp =
|
|
10549
|
+
const circularProp = path100.find((p) => !OPENAPI_PROPS.has(p)) || path100[0];
|
|
10263
10550
|
return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
|
|
10264
10551
|
}
|
|
10265
10552
|
if (refs.has(obj)) return refs.get(obj);
|
|
@@ -10269,7 +10556,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
|
|
|
10269
10556
|
value,
|
|
10270
10557
|
currentPath,
|
|
10271
10558
|
refs,
|
|
10272
|
-
[...
|
|
10559
|
+
[...path100, key],
|
|
10273
10560
|
currentRefPaths
|
|
10274
10561
|
);
|
|
10275
10562
|
const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
|
|
@@ -10304,7 +10591,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
|
|
|
10304
10591
|
var getAllTags = (schema2) => {
|
|
10305
10592
|
const rootTags = schema2.tags ?? [];
|
|
10306
10593
|
const operations = Object.values(schema2.paths ?? {}).flatMap(
|
|
10307
|
-
(
|
|
10594
|
+
(path100) => HttpMethods.map((k) => path100?.[k]).filter((op) => op != null)
|
|
10308
10595
|
);
|
|
10309
10596
|
const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
|
|
10310
10597
|
const hasUntaggedOperations = operations.some(
|
|
@@ -10359,7 +10646,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
|
|
|
10359
10646
|
var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
|
|
10360
10647
|
var getAllOperations = (paths) => {
|
|
10361
10648
|
const operations = Object.entries(paths ?? {}).flatMap(
|
|
10362
|
-
([
|
|
10649
|
+
([path100, value]) => HttpMethods.flatMap((method) => {
|
|
10363
10650
|
if (!value?.[method]) return [];
|
|
10364
10651
|
const operation = value[method];
|
|
10365
10652
|
const pathParameters = value.parameters ?? [];
|
|
@@ -10379,7 +10666,7 @@ var getAllOperations = (paths) => {
|
|
|
10379
10666
|
return {
|
|
10380
10667
|
...operation,
|
|
10381
10668
|
method,
|
|
10382
|
-
path:
|
|
10669
|
+
path: path100,
|
|
10383
10670
|
parameters,
|
|
10384
10671
|
servers,
|
|
10385
10672
|
tags: operation.tags ?? []
|
|
@@ -10737,8 +11024,8 @@ var Schema = builder.objectRef("Schema").implement({
|
|
|
10737
11024
|
}),
|
|
10738
11025
|
paths: t.field({
|
|
10739
11026
|
type: [PathItem],
|
|
10740
|
-
resolve: (root) => Object.entries(root.paths ?? {}).map(([
|
|
10741
|
-
path:
|
|
11027
|
+
resolve: (root) => Object.entries(root.paths ?? {}).map(([path100, value]) => ({
|
|
11028
|
+
path: path100,
|
|
10742
11029
|
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
|
|
10743
11030
|
methods: Object.keys(value)
|
|
10744
11031
|
}))
|
|
@@ -10903,7 +11190,7 @@ init_joinUrl();
|
|
|
10903
11190
|
|
|
10904
11191
|
// src/vite/api/schema-codegen.ts
|
|
10905
11192
|
var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
10906
|
-
var getSegmentsFromPath = (
|
|
11193
|
+
var getSegmentsFromPath = (path100) => path100.split("/").slice(1).map(unescapeJsonPointer);
|
|
10907
11194
|
var createLocalRefMap = (obj) => {
|
|
10908
11195
|
const refMap = /* @__PURE__ */ new Map();
|
|
10909
11196
|
const siblingsMap = /* @__PURE__ */ new Map();
|
|
@@ -10934,16 +11221,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
|
|
|
10934
11221
|
/"__refMap\+Siblings:(.*?)"/g,
|
|
10935
11222
|
(_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
|
|
10936
11223
|
);
|
|
10937
|
-
var lookup = (schema2,
|
|
10938
|
-
const parts = getSegmentsFromPath(
|
|
11224
|
+
var lookup = (schema2, path100, filePath) => {
|
|
11225
|
+
const parts = getSegmentsFromPath(path100);
|
|
10939
11226
|
let val = schema2;
|
|
10940
11227
|
for (const part of parts) {
|
|
10941
11228
|
while (val.$ref?.startsWith("#/")) {
|
|
10942
|
-
val = val.$ref ===
|
|
11229
|
+
val = val.$ref === path100 ? val : lookup(schema2, val.$ref, filePath);
|
|
10943
11230
|
}
|
|
10944
11231
|
if (val[part] === void 0) {
|
|
10945
11232
|
throw new Error(
|
|
10946
|
-
`Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${
|
|
11233
|
+
`Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path100}`
|
|
10947
11234
|
);
|
|
10948
11235
|
}
|
|
10949
11236
|
val = val[part];
|
|
@@ -11188,8 +11475,8 @@ var SchemaManager = class {
|
|
|
11188
11475
|
}
|
|
11189
11476
|
}
|
|
11190
11477
|
};
|
|
11191
|
-
getLatestSchema = (
|
|
11192
|
-
getSchemasForPath = (
|
|
11478
|
+
getLatestSchema = (path100) => this.processedSchemas[path100]?.at(0);
|
|
11479
|
+
getSchemasForPath = (path100) => this.processedSchemas[path100];
|
|
11193
11480
|
getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
|
|
11194
11481
|
getUrlToFilePathMap = () => {
|
|
11195
11482
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -13625,14 +13912,14 @@ async function generateSitemap({
|
|
|
13625
13912
|
|
|
13626
13913
|
// src/vite/prerender/utils.ts
|
|
13627
13914
|
init_joinUrl();
|
|
13628
|
-
var resolveRoutePath = (
|
|
13629
|
-
const segments =
|
|
13915
|
+
var resolveRoutePath = (path100) => {
|
|
13916
|
+
const segments = path100.split("/");
|
|
13630
13917
|
if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
|
|
13631
13918
|
return void 0;
|
|
13632
13919
|
}
|
|
13633
13920
|
return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
|
|
13634
13921
|
};
|
|
13635
|
-
var isSkipped = (
|
|
13922
|
+
var isSkipped = (path100) => path100.includes("*") || /^\d+$/.test(path100);
|
|
13636
13923
|
var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
|
|
13637
13924
|
if (route.path && isSkipped(route.path)) return [];
|
|
13638
13925
|
const routePath = route.path ? resolveRoutePath(route.path) : void 0;
|
|
@@ -14081,11 +14368,94 @@ async function buildV2(options) {
|
|
|
14081
14368
|
return { skipped, buildDir, hash: hash2 };
|
|
14082
14369
|
}
|
|
14083
14370
|
|
|
14371
|
+
// src/cli/check/gateway-online.ts
|
|
14372
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
14373
|
+
var SPEC_SEND_LIMIT = 5 * 1024 * 1024 + 64 * 1024;
|
|
14374
|
+
var issue2 = (code, path100, message, hint) => ({ code, path: path100, message, hint });
|
|
14375
|
+
function capabilityIssues(gateway, answer) {
|
|
14376
|
+
const can = answer.capabilities;
|
|
14377
|
+
const plan2 = `your ${answer.tier} plan`;
|
|
14378
|
+
const found = [];
|
|
14379
|
+
const unsupported = (path100, what, hint) => found.push(
|
|
14380
|
+
issue2(
|
|
14381
|
+
"GATEWAY_CAPABILITY_UNSUPPORTED",
|
|
14382
|
+
path100,
|
|
14383
|
+
`${what} is not available on ${plan2}.`,
|
|
14384
|
+
hint
|
|
14385
|
+
)
|
|
14386
|
+
);
|
|
14387
|
+
if (gateway.origin.mode === "managed" && can.managedOrigin !== true)
|
|
14388
|
+
unsupported(
|
|
14389
|
+
"gateway.origin.mode",
|
|
14390
|
+
"A managed origin",
|
|
14391
|
+
"Use a proxy origin, or change your plan."
|
|
14392
|
+
);
|
|
14393
|
+
if (gateway.origin.mode === "proxy" && can.proxyOrigin !== true)
|
|
14394
|
+
unsupported(
|
|
14395
|
+
"gateway.origin.mode",
|
|
14396
|
+
"A proxy origin",
|
|
14397
|
+
"Use a managed origin, or change your plan."
|
|
14398
|
+
);
|
|
14399
|
+
if (gateway.origin.auth?.type === "mtls" && can.originMtls !== true)
|
|
14400
|
+
unsupported(
|
|
14401
|
+
"gateway.origin.auth.type",
|
|
14402
|
+
"Client certificates (mtls) for the origin",
|
|
14403
|
+
'Use type "header" with a secret.'
|
|
14404
|
+
);
|
|
14405
|
+
if (gateway.policies.access?.bindKeyToToken === true && can.bindKeyToToken !== true)
|
|
14406
|
+
unsupported(
|
|
14407
|
+
"gateway.policies.access.bindKeyToToken",
|
|
14408
|
+
"Binding a key to a token",
|
|
14409
|
+
"Remove bindKeyToToken."
|
|
14410
|
+
);
|
|
14411
|
+
for (const [name, scope] of [
|
|
14412
|
+
["rateLimit", gateway.policies.rateLimit?.scope],
|
|
14413
|
+
["quota", gateway.policies.quota?.scope]
|
|
14414
|
+
])
|
|
14415
|
+
if (scope === "entitlement" && can.quotaPerEntitlement !== true)
|
|
14416
|
+
unsupported(
|
|
14417
|
+
`gateway.policies.${name}.scope`,
|
|
14418
|
+
"Counting per entitlement",
|
|
14419
|
+
'Use scope "key".'
|
|
14420
|
+
);
|
|
14421
|
+
return found;
|
|
14422
|
+
}
|
|
14423
|
+
async function onlineGatewayRules(gateway, projectId, api, derivedCors = []) {
|
|
14424
|
+
let spec;
|
|
14425
|
+
const specFile = gateway.specs[0];
|
|
14426
|
+
if (specFile) {
|
|
14427
|
+
try {
|
|
14428
|
+
const text = await readFile11(specFile, "utf8");
|
|
14429
|
+
if (text.length <= SPEC_SEND_LIMIT) spec = text;
|
|
14430
|
+
} catch {
|
|
14431
|
+
}
|
|
14432
|
+
}
|
|
14433
|
+
try {
|
|
14434
|
+
const capabilities = await api.capabilities(projectId);
|
|
14435
|
+
const result = await api.check(projectId, {
|
|
14436
|
+
config: gatewayConfigBody(gateway, derivedCors),
|
|
14437
|
+
...spec === void 0 ? {} : { spec }
|
|
14438
|
+
});
|
|
14439
|
+
const fromPlatform = result.issues.map(
|
|
14440
|
+
(item) => issue2(item.code, item.field, item.message, item.hint)
|
|
14441
|
+
);
|
|
14442
|
+
return {
|
|
14443
|
+
issues: [...capabilityIssues(gateway, capabilities), ...fromPlatform]
|
|
14444
|
+
};
|
|
14445
|
+
} catch (error) {
|
|
14446
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
14447
|
+
return {
|
|
14448
|
+
issues: [],
|
|
14449
|
+
skipped: `the gateway's online rules were not run: the platform could not be asked (${reason})`
|
|
14450
|
+
};
|
|
14451
|
+
}
|
|
14452
|
+
}
|
|
14453
|
+
|
|
14084
14454
|
// src/cli/check/v2.ts
|
|
14085
14455
|
async function isConfigV2(dir) {
|
|
14086
14456
|
try {
|
|
14087
14457
|
return /"version"\s*:\s*2\b/.test(
|
|
14088
|
-
await
|
|
14458
|
+
await readFile12(path46.join(dir, APITOGO_CONFIG_FILENAME), "utf8")
|
|
14089
14459
|
);
|
|
14090
14460
|
} catch {
|
|
14091
14461
|
return false;
|
|
@@ -14104,6 +14474,46 @@ async function fetchSecretNames(config2, configuredProjectId, args, deps) {
|
|
|
14104
14474
|
};
|
|
14105
14475
|
}
|
|
14106
14476
|
}
|
|
14477
|
+
async function addGatewayOnlineRules(report, resolved, args, deps) {
|
|
14478
|
+
const gateway = resolved.gateway;
|
|
14479
|
+
if (args.offline || !deps.gateway || !gateway?.enabled) return;
|
|
14480
|
+
const projectId = resolved.project.id || deps.envProjectId?.trim();
|
|
14481
|
+
if (!projectId) {
|
|
14482
|
+
report.skipped.push(
|
|
14483
|
+
"the gateway's online rules were not run: this folder is not linked to a project (run `apitogo project create`)"
|
|
14484
|
+
);
|
|
14485
|
+
return;
|
|
14486
|
+
}
|
|
14487
|
+
const manifest = await readManifest(resolved.state);
|
|
14488
|
+
const result = await onlineGatewayRules(
|
|
14489
|
+
gateway,
|
|
14490
|
+
projectId,
|
|
14491
|
+
deps.gateway,
|
|
14492
|
+
derivedCorsSites({
|
|
14493
|
+
portalUrl: manifest?.services.portal?.url,
|
|
14494
|
+
docsUrl: resolved.portal?.docs.provider === "external" ? resolved.portal.docs.url : void 0
|
|
14495
|
+
})
|
|
14496
|
+
);
|
|
14497
|
+
if (result.skipped) {
|
|
14498
|
+
report.skipped.push(result.skipped);
|
|
14499
|
+
return;
|
|
14500
|
+
}
|
|
14501
|
+
const seen = new Set(
|
|
14502
|
+
[...report.errors, ...report.warnings].map((i) => `${i.code}|${i.path}`)
|
|
14503
|
+
);
|
|
14504
|
+
for (const item of result.issues) {
|
|
14505
|
+
const key = `${item.code}|${item.path}`;
|
|
14506
|
+
if (seen.has(key)) continue;
|
|
14507
|
+
seen.add(key);
|
|
14508
|
+
report.errors.push(item);
|
|
14509
|
+
}
|
|
14510
|
+
report.skipped = report.skipped.filter(
|
|
14511
|
+
(note) => !note.startsWith("online rules were not run")
|
|
14512
|
+
);
|
|
14513
|
+
report.skipped.unshift(
|
|
14514
|
+
"online rules ran for the gateway only; the other services have none yet"
|
|
14515
|
+
);
|
|
14516
|
+
}
|
|
14107
14517
|
async function validatePortalBuild(dir) {
|
|
14108
14518
|
try {
|
|
14109
14519
|
await buildV2({ dir, force: true, jsonOnly: true });
|
|
@@ -14113,7 +14523,7 @@ async function validatePortalBuild(dir) {
|
|
|
14113
14523
|
return {
|
|
14114
14524
|
code: error.issues[0]?.code ?? "PORTAL_BUILD_FAILED",
|
|
14115
14525
|
path: error.issues[0]?.path ?? "portal",
|
|
14116
|
-
message: error.issues.map((
|
|
14526
|
+
message: error.issues.map((issue3) => issue3.message).join(" "),
|
|
14117
14527
|
hint: error.issues[0]?.hint
|
|
14118
14528
|
};
|
|
14119
14529
|
}
|
|
@@ -14142,6 +14552,7 @@ async function runCheckV2(dir, args = {}, deps = {}) {
|
|
|
14142
14552
|
knownSecrets: online.names
|
|
14143
14553
|
});
|
|
14144
14554
|
if (online.failure) report.skipped.push(online.failure);
|
|
14555
|
+
await addGatewayOnlineRules(report, resolved, args, deps);
|
|
14145
14556
|
} catch (error) {
|
|
14146
14557
|
if (!(error instanceof ConfigError)) throw error;
|
|
14147
14558
|
report = {
|
|
@@ -14168,11 +14579,11 @@ async function runCheckV2(dir, args = {}, deps = {}) {
|
|
|
14168
14579
|
...report
|
|
14169
14580
|
};
|
|
14170
14581
|
}
|
|
14171
|
-
var line = (label,
|
|
14582
|
+
var line = (label, issue3) => `${label}: ${formatConfigIssue(issue3)}`;
|
|
14172
14583
|
function formatCheckV2(output4) {
|
|
14173
14584
|
const lines = [
|
|
14174
|
-
...output4.errors.map((
|
|
14175
|
-
...output4.warnings.map((
|
|
14585
|
+
...output4.errors.map((issue3) => line("error", issue3)),
|
|
14586
|
+
...output4.warnings.map((issue3) => line("warning", issue3)),
|
|
14176
14587
|
...output4.skipped.map((note) => `not checked: ${note}`)
|
|
14177
14588
|
];
|
|
14178
14589
|
const errors = `${output4.errors.length} error${output4.errors.length === 1 ? "" : "s"}`;
|
|
@@ -14189,7 +14600,11 @@ function platformDependencies(args) {
|
|
|
14189
14600
|
const client = new PlatformApiClient(config2);
|
|
14190
14601
|
return {
|
|
14191
14602
|
envProjectId: config2.defaultProjectId,
|
|
14192
|
-
listSecrets: async (projectId) => (await client.listSecrets(projectId)).secrets.map((s) => s.name)
|
|
14603
|
+
listSecrets: async (projectId) => (await client.listSecrets(projectId)).secrets.map((s) => s.name),
|
|
14604
|
+
gateway: {
|
|
14605
|
+
check: (projectId, body) => client.checkGateway(projectId, body),
|
|
14606
|
+
capabilities: (projectId) => client.getGatewayCapabilities(projectId)
|
|
14607
|
+
}
|
|
14193
14608
|
};
|
|
14194
14609
|
} catch {
|
|
14195
14610
|
return {};
|
|
@@ -14205,6 +14620,9 @@ async function checkV2(dir, args, deps = platformDependencies(args)) {
|
|
|
14205
14620
|
if (output4.status === "failed") process.exitCode = 1;
|
|
14206
14621
|
}
|
|
14207
14622
|
|
|
14623
|
+
// src/cli/services/handler.ts
|
|
14624
|
+
init_package_json();
|
|
14625
|
+
|
|
14208
14626
|
// src/cli/init/v2-handler.ts
|
|
14209
14627
|
import path47 from "node:path";
|
|
14210
14628
|
import { createInterface } from "node:readline/promises";
|
|
@@ -14339,7 +14757,7 @@ async function initV2(argv) {
|
|
|
14339
14757
|
`Created ${path47.basename(plan2.configPath)} (config version 2, ${plan2.layout} layout) in ${dir}.`
|
|
14340
14758
|
);
|
|
14341
14759
|
printHintToConsole(
|
|
14342
|
-
"Config version 2 is a preview: check, run
|
|
14760
|
+
"Config version 2 is a preview: add, remove, check, run, build, plan, status, deploy and secrets read it."
|
|
14343
14761
|
);
|
|
14344
14762
|
} catch (error) {
|
|
14345
14763
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -14361,6 +14779,81 @@ async function initV2(argv) {
|
|
|
14361
14779
|
}
|
|
14362
14780
|
}
|
|
14363
14781
|
|
|
14782
|
+
// src/cli/services/packages.ts
|
|
14783
|
+
import { spawn } from "node:child_process";
|
|
14784
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
14785
|
+
import path48 from "node:path";
|
|
14786
|
+
var USER_PANEL_PACKAGE = "@lukoweb/apitogo-module-user-panel";
|
|
14787
|
+
var BILLING_PLUGIN_PACKAGE = "@lukoweb/apitogo-plugin-dev-portal-billing";
|
|
14788
|
+
function packagesForConfig(config2) {
|
|
14789
|
+
const portal = config2.portal;
|
|
14790
|
+
if (!portal || portal.enabled === false || portal.console?.enabled === false)
|
|
14791
|
+
return [];
|
|
14792
|
+
const identity = config2.identity && config2.identity.enabled !== false;
|
|
14793
|
+
const plans = config2.plans && config2.plans.enabled !== false;
|
|
14794
|
+
return [
|
|
14795
|
+
...identity || plans ? [USER_PANEL_PACKAGE] : [],
|
|
14796
|
+
...plans ? [BILLING_PLUGIN_PACKAGE] : []
|
|
14797
|
+
];
|
|
14798
|
+
}
|
|
14799
|
+
function isInstalled(dir, name) {
|
|
14800
|
+
for (let current = path48.resolve(dir); ; current = path48.dirname(current)) {
|
|
14801
|
+
if (existsSync5(path48.join(current, "node_modules", name, "package.json")))
|
|
14802
|
+
return true;
|
|
14803
|
+
if (path48.dirname(current) === current) return false;
|
|
14804
|
+
}
|
|
14805
|
+
}
|
|
14806
|
+
function missingPackages(dir, names) {
|
|
14807
|
+
return names.filter((name) => !isInstalled(dir, name));
|
|
14808
|
+
}
|
|
14809
|
+
function detectPackageManager(dir) {
|
|
14810
|
+
if (existsSync5(path48.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
14811
|
+
if (existsSync5(path48.join(dir, "yarn.lock"))) return "yarn";
|
|
14812
|
+
return "npm";
|
|
14813
|
+
}
|
|
14814
|
+
function installCommand(manager, names, version) {
|
|
14815
|
+
const specs = names.map((name) => `${name}@${version}`);
|
|
14816
|
+
return manager === "npm" ? { command: "npm", args: ["install", ...specs] } : { command: manager, args: ["add", ...specs] };
|
|
14817
|
+
}
|
|
14818
|
+
var runInstaller = (dir, { command: command2, args }) => new Promise((resolve, reject) => {
|
|
14819
|
+
const child = spawn(command2, args, {
|
|
14820
|
+
cwd: dir,
|
|
14821
|
+
stdio: "inherit",
|
|
14822
|
+
shell: process.platform === "win32"
|
|
14823
|
+
});
|
|
14824
|
+
child.on("error", reject);
|
|
14825
|
+
child.on(
|
|
14826
|
+
"exit",
|
|
14827
|
+
(code) => code === 0 ? resolve() : reject(
|
|
14828
|
+
new Error(
|
|
14829
|
+
`${command2} ${args.join(" ")} failed with exit code ${code}.`
|
|
14830
|
+
)
|
|
14831
|
+
)
|
|
14832
|
+
);
|
|
14833
|
+
});
|
|
14834
|
+
async function installMissingPackages(dir, config2, version, options) {
|
|
14835
|
+
const missing = missingPackages(dir, packagesForConfig(config2));
|
|
14836
|
+
if (missing.length === 0) return void 0;
|
|
14837
|
+
const command2 = installCommand(detectPackageManager(dir), missing, version);
|
|
14838
|
+
const shown = `${command2.command} ${command2.args.join(" ")}`;
|
|
14839
|
+
if (!options.install)
|
|
14840
|
+
return {
|
|
14841
|
+
status: "skipped",
|
|
14842
|
+
packages: missing,
|
|
14843
|
+
reason: "--no-install was given",
|
|
14844
|
+
command: shown
|
|
14845
|
+
};
|
|
14846
|
+
if (!existsSync5(path48.join(dir, "package.json")))
|
|
14847
|
+
return {
|
|
14848
|
+
status: "skipped",
|
|
14849
|
+
packages: missing,
|
|
14850
|
+
reason: "there is no package.json here",
|
|
14851
|
+
command: shown
|
|
14852
|
+
};
|
|
14853
|
+
await (options.installer ?? runInstaller)(dir, command2);
|
|
14854
|
+
return { status: "installed", packages: missing, command: shown };
|
|
14855
|
+
}
|
|
14856
|
+
|
|
14364
14857
|
// src/cli/services/handler.ts
|
|
14365
14858
|
var list = (value) => {
|
|
14366
14859
|
const items = (Array.isArray(value) ? value : value === void 0 ? [] : [value]).filter(
|
|
@@ -14369,7 +14862,7 @@ var list = (value) => {
|
|
|
14369
14862
|
return items.length > 0 ? items : void 0;
|
|
14370
14863
|
};
|
|
14371
14864
|
function addOptionsFromArguments(service, argv) {
|
|
14372
|
-
const dir =
|
|
14865
|
+
const dir = path49.resolve(process.cwd(), argv.dir ?? ".");
|
|
14373
14866
|
const issuers = list(argv.issuer);
|
|
14374
14867
|
return {
|
|
14375
14868
|
dir,
|
|
@@ -14428,12 +14921,12 @@ function describe(edit, dir) {
|
|
|
14428
14921
|
return {
|
|
14429
14922
|
service: edit.service,
|
|
14430
14923
|
action: edit.action,
|
|
14431
|
-
willCreate: edit.files.map((file) =>
|
|
14432
|
-
keep: edit.keep.map((file) =>
|
|
14924
|
+
willCreate: edit.files.map((file) => path49.relative(dir, file.path)),
|
|
14925
|
+
keep: edit.keep.map((file) => path49.relative(dir, file)),
|
|
14433
14926
|
notes: edit.notes
|
|
14434
14927
|
};
|
|
14435
14928
|
}
|
|
14436
|
-
async function run(action, argv, plan2) {
|
|
14929
|
+
async function run(action, argv, plan2, deps = {}) {
|
|
14437
14930
|
const json = argv.json === true;
|
|
14438
14931
|
try {
|
|
14439
14932
|
if (!isServiceName(argv.service)) {
|
|
@@ -14442,7 +14935,7 @@ async function run(action, argv, plan2) {
|
|
|
14442
14935
|
`Choose one of ${SERVICES.join(", ")}.`
|
|
14443
14936
|
);
|
|
14444
14937
|
}
|
|
14445
|
-
const dir =
|
|
14938
|
+
const dir = path49.resolve(process.cwd(), argv.dir ?? ".");
|
|
14446
14939
|
if (!await isConfigV2(dir)) {
|
|
14447
14940
|
throw new ServiceEditError(
|
|
14448
14941
|
`${action} works with a config version 2 project, and there is none in ${dir}.`,
|
|
@@ -14450,9 +14943,13 @@ async function run(action, argv, plan2) {
|
|
|
14450
14943
|
);
|
|
14451
14944
|
}
|
|
14452
14945
|
const edit = await plan2(argv.service);
|
|
14453
|
-
|
|
14946
|
+
const needed = action === "add" ? missingPackages(dir, packagesForConfig(edit.config)) : [];
|
|
14947
|
+
if (!json) {
|
|
14454
14948
|
for (const line2 of formatServiceEdit(edit))
|
|
14455
14949
|
printDiagnosticsToConsole(line2);
|
|
14950
|
+
if (needed.length > 0 && argv.install !== false)
|
|
14951
|
+
printDiagnosticsToConsole(`Will install: ${needed.join(", ")}`);
|
|
14952
|
+
}
|
|
14456
14953
|
if (argv.dryRun) {
|
|
14457
14954
|
if (json)
|
|
14458
14955
|
console.log(
|
|
@@ -14478,13 +14975,15 @@ async function run(action, argv, plan2) {
|
|
|
14478
14975
|
}
|
|
14479
14976
|
}
|
|
14480
14977
|
const { created } = await applyServiceEdit(edit);
|
|
14978
|
+
const install = action === "add" ? await installPackages(dir, edit, argv, deps, json) : void 0;
|
|
14481
14979
|
if (json) {
|
|
14482
14980
|
console.log(
|
|
14483
14981
|
JSON.stringify(
|
|
14484
14982
|
{
|
|
14485
14983
|
status: action === "add" ? "added" : "removed",
|
|
14486
14984
|
...describe(edit, dir),
|
|
14487
|
-
created: created.map((file) =>
|
|
14985
|
+
created: created.map((file) => path49.relative(dir, file)),
|
|
14986
|
+
...install ? { packages: install } : {}
|
|
14488
14987
|
},
|
|
14489
14988
|
null,
|
|
14490
14989
|
2
|
|
@@ -14493,7 +14992,7 @@ async function run(action, argv, plan2) {
|
|
|
14493
14992
|
return;
|
|
14494
14993
|
}
|
|
14495
14994
|
printResultToConsole(
|
|
14496
|
-
`${action === "add" ? "Added" : "Removed"} ${edit.service} in ${
|
|
14995
|
+
`${action === "add" ? "Added" : "Removed"} ${edit.service} in ${path49.basename(edit.configPath)}.`
|
|
14497
14996
|
);
|
|
14498
14997
|
} catch (error) {
|
|
14499
14998
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -14514,16 +15013,44 @@ async function run(action, argv, plan2) {
|
|
|
14514
15013
|
);
|
|
14515
15014
|
}
|
|
14516
15015
|
}
|
|
14517
|
-
|
|
15016
|
+
async function installPackages(dir, edit, argv, deps, json) {
|
|
15017
|
+
try {
|
|
15018
|
+
const outcome = await installMissingPackages(
|
|
15019
|
+
dir,
|
|
15020
|
+
edit.config,
|
|
15021
|
+
deps.version ?? getZudokuPackageJson().version,
|
|
15022
|
+
{ install: argv.install !== false, installer: deps.installer }
|
|
15023
|
+
);
|
|
15024
|
+
if (outcome?.status === "skipped" && !json)
|
|
15025
|
+
printDiagnosticsToConsole(
|
|
15026
|
+
`Not installed (${outcome.reason}). Run: ${outcome.command}`
|
|
15027
|
+
);
|
|
15028
|
+
if (outcome?.status === "installed" && !json)
|
|
15029
|
+
printDiagnosticsToConsole(`Installed: ${outcome.packages.join(", ")}`);
|
|
15030
|
+
return outcome;
|
|
15031
|
+
} catch (error) {
|
|
15032
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
15033
|
+
if (!json)
|
|
15034
|
+
printDiagnosticsToConsole(
|
|
15035
|
+
`The packages could not be installed (${reason}). The service was added; install them yourself before you build.`
|
|
15036
|
+
);
|
|
15037
|
+
return {
|
|
15038
|
+
status: "failed",
|
|
15039
|
+
reason
|
|
15040
|
+
};
|
|
15041
|
+
}
|
|
15042
|
+
}
|
|
15043
|
+
var addService = (argv, deps = {}) => run(
|
|
14518
15044
|
"add",
|
|
14519
15045
|
argv,
|
|
14520
|
-
(service) => planAddService(service, addOptionsFromArguments(service, argv))
|
|
15046
|
+
(service) => planAddService(service, addOptionsFromArguments(service, argv)),
|
|
15047
|
+
deps
|
|
14521
15048
|
);
|
|
14522
15049
|
var removeService = (argv) => run(
|
|
14523
15050
|
"remove",
|
|
14524
15051
|
argv,
|
|
14525
15052
|
(service) => planRemoveService(service, {
|
|
14526
|
-
dir:
|
|
15053
|
+
dir: path49.resolve(process.cwd(), argv.dir ?? ".")
|
|
14527
15054
|
})
|
|
14528
15055
|
);
|
|
14529
15056
|
|
|
@@ -14558,6 +15085,10 @@ var add_default = {
|
|
|
14558
15085
|
type: "boolean",
|
|
14559
15086
|
default: false,
|
|
14560
15087
|
describe: "Print a JSON result on stdout"
|
|
15088
|
+
}).option("install", {
|
|
15089
|
+
type: "boolean",
|
|
15090
|
+
default: true,
|
|
15091
|
+
describe: "Install the plugin packages the service needs (use --no-install to skip)"
|
|
14561
15092
|
}).option("name", { type: "string", describe: "portal: display name" }).option("docs", {
|
|
14562
15093
|
type: "string",
|
|
14563
15094
|
array: true,
|
|
@@ -14667,16 +15198,16 @@ var add_default = {
|
|
|
14667
15198
|
};
|
|
14668
15199
|
|
|
14669
15200
|
// src/cli/build/handler.ts
|
|
14670
|
-
import
|
|
15201
|
+
import path51 from "node:path";
|
|
14671
15202
|
init_logger();
|
|
14672
15203
|
init_package_json();
|
|
14673
15204
|
|
|
14674
15205
|
// src/cli/preview/handler.ts
|
|
14675
|
-
import
|
|
15206
|
+
import path50 from "node:path";
|
|
14676
15207
|
import { preview as vitePreview } from "vite";
|
|
14677
15208
|
var DEFAULT_PREVIEW_PORT = 4e3;
|
|
14678
15209
|
async function preview(argv) {
|
|
14679
|
-
const dir =
|
|
15210
|
+
const dir = path50.resolve(process.cwd(), argv.dir);
|
|
14680
15211
|
const viteConfig = await getViteConfig(dir, {
|
|
14681
15212
|
command: "serve",
|
|
14682
15213
|
mode: "production",
|
|
@@ -14719,7 +15250,7 @@ async function build(argv) {
|
|
|
14719
15250
|
printDiagnosticsToConsole("");
|
|
14720
15251
|
printDiagnosticsToConsole("");
|
|
14721
15252
|
}
|
|
14722
|
-
const dir =
|
|
15253
|
+
const dir = path51.resolve(process.cwd(), argv.dir);
|
|
14723
15254
|
try {
|
|
14724
15255
|
if (await isConfigV2(dir)) {
|
|
14725
15256
|
const result = await buildV2({ dir, jsonOnly });
|
|
@@ -14784,10 +15315,10 @@ var build_default = {
|
|
|
14784
15315
|
};
|
|
14785
15316
|
|
|
14786
15317
|
// src/cli/check/handler.ts
|
|
14787
|
-
import
|
|
15318
|
+
import path58 from "node:path";
|
|
14788
15319
|
|
|
14789
15320
|
// src/cli/platform/tools/check-publish-readiness.ts
|
|
14790
|
-
import
|
|
15321
|
+
import path57 from "node:path";
|
|
14791
15322
|
|
|
14792
15323
|
// src/cli/platform/helpers/dev-portal-backend.ts
|
|
14793
15324
|
function deriveProvisioningState(backend) {
|
|
@@ -14870,12 +15401,12 @@ function formatDevPortalBackendResponse(projectId, backend) {
|
|
|
14870
15401
|
|
|
14871
15402
|
// src/cli/platform/helpers/dev-portal-manifest.ts
|
|
14872
15403
|
init_gateway_limits();
|
|
14873
|
-
import { existsSync as
|
|
14874
|
-
import { access as access5, mkdir as mkdir13, readFile as
|
|
14875
|
-
import
|
|
15404
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
15405
|
+
import { access as access5, mkdir as mkdir13, readFile as readFile13, writeFile as writeFile14 } from "node:fs/promises";
|
|
15406
|
+
import path53 from "node:path";
|
|
14876
15407
|
|
|
14877
15408
|
// src/cli/make-config/json-config.ts
|
|
14878
|
-
import
|
|
15409
|
+
import path52 from "node:path";
|
|
14879
15410
|
|
|
14880
15411
|
// src/cli/make-config/branding-assets.ts
|
|
14881
15412
|
var LOGO_LIGHT = '<svg xmlns="http://www.w3.org/2000/svg" width="201.57" height="72.66" viewBox="-0.09 -46.99 201.57 72.66">\n <path d="M13.5 0.39Q8.03 0.39 4.76 -2.09Q1.48 -4.57 1.41 -9.61H8.03Q8.14 -7.48 9.6 -6.41Q11.05 -5.33 13.42 -5.33Q15.57 -5.33 16.8 -6.2Q18.03 -7.07 18.03 -8.46Q18.03 -9.71 16.87 -10.44Q15.7 -11.17 13.32 -11.72L10.23 -12.44Q6.46 -13.3 4.32 -15.25Q2.17 -17.21 2.17 -20.49Q2.17 -23.18 3.62 -25.21Q5.08 -27.23 7.64 -28.36Q10.2 -29.49 13.5 -29.49Q16.88 -29.49 19.36 -28.35Q21.84 -27.21 23.2 -25.17Q24.57 -23.12 24.61 -20.43H17.99Q17.85 -22.01 16.7 -22.89Q15.55 -23.77 13.48 -23.77Q11.41 -23.77 10.35 -22.97Q9.3 -22.17 9.3 -20.96Q9.3 -19.61 10.47 -18.88Q11.64 -18.14 13.59 -17.71L16.11 -17.13Q20.33 -16.19 22.67 -14.06Q25 -11.93 25 -8.44Q25 -4.3 21.95 -1.95Q18.91 0.39 13.5 0.39Z M32.97 0.39Q29.82 0.39 27.8 -1.21Q25.78 -2.81 25.78 -6.05Q25.78 -8.48 26.89 -9.9Q28 -11.33 29.85 -12.01Q31.7 -12.7 33.9 -12.89Q36.68 -13.12 37.88 -13.47Q39.08 -13.81 39.08 -14.82V-14.9Q39.08 -16.04 38.29 -16.65Q37.5 -17.27 36.11 -17.27Q34.68 -17.27 33.77 -16.66Q32.85 -16.05 32.58 -15.02L26.4 -15.82Q27.05 -18.61 29.56 -20.36Q32.07 -22.11 36.17 -22.11Q38.88 -22.11 41.08 -21.27Q43.28 -20.43 44.58 -18.8Q45.88 -17.17 45.88 -14.82V0H39.47V-3.07H39.29Q37.48 0.39 32.97 0.39ZM35.04 -4.12Q36.75 -4.12 37.95 -5.14Q39.14 -6.15 39.14 -7.77V-9.86Q38.55 -9.55 37.44 -9.34Q36.33 -9.12 35.37 -8.96Q32.22 -8.5 32.22 -6.37Q32.22 -5.27 33.01 -4.7Q33.81 -4.12 35.04 -4.12Z M48.59 0V-21.84H54.86L55.17 -17.5Q56.07 -19.96 57.78 -21.04Q59.49 -22.13 61.46 -22.13Q63.53 -22.13 64.91 -20.94Q66.28 -19.75 67.08 -17.07Q68.08 -19.77 70.02 -20.95Q71.97 -22.13 74.17 -22.13Q77.24 -22.13 79.2 -20.16Q81.17 -18.18 81.17 -14.73V0H74.37V-13.18Q74.37 -14.84 73.5 -15.7Q72.63 -16.56 71.28 -16.56Q69.84 -16.56 69 -15.62Q68.16 -14.67 68.16 -13.11V0H61.61V-13.28Q61.61 -14.79 60.77 -15.67Q59.92 -16.56 58.57 -16.56Q57.18 -16.56 56.29 -15.62Q55.4 -14.69 55.4 -12.97V0Z M83.88 8.16V-21.84H90.62V-18.09H90.85Q91.26 -19.06 92.04 -19.99Q92.82 -20.92 94.05 -21.51Q95.28 -22.11 97.02 -22.11Q99.33 -22.11 101.31 -20.9Q103.29 -19.69 104.51 -17.21Q105.73 -14.73 105.73 -10.9Q105.73 -7.19 104.55 -4.7Q103.37 -2.21 101.39 -0.95Q99.4 0.31 96.98 0.31Q94.48 0.31 92.99 -0.87Q91.49 -2.05 90.85 -3.54H90.69V8.16ZM94.66 -5Q96.63 -5 97.68 -6.62Q98.72 -8.24 98.72 -10.9Q98.72 -13.55 97.68 -15.17Q96.63 -16.78 94.66 -16.78Q92.71 -16.78 91.62 -15.2Q90.54 -13.61 90.54 -10.9Q90.54 -8.22 91.62 -6.61Q92.71 -5 94.66 -5Z M114.4 -29.1V0H107.59V-29.1Z M127.29 0.41Q122.15 0.41 119.21 -2.59Q116.27 -5.59 116.27 -10.84Q116.27 -14.2 117.59 -16.74Q118.91 -19.28 121.33 -20.69Q123.75 -22.11 127.04 -22.11Q130.1 -22.11 132.47 -20.83Q134.85 -19.55 136.21 -17.06Q137.56 -14.57 137.56 -10.98V-9.26H122.97Q123.07 -6.89 124.27 -5.71Q125.47 -4.53 127.41 -4.53Q128.77 -4.53 129.77 -5.11Q130.77 -5.68 131.18 -6.78L137.25 -5.78Q136.43 -2.95 133.84 -1.27Q131.25 0.41 127.29 0.41ZM123.01 -13.22H131.1Q130.9 -15.08 129.89 -16.12Q128.87 -17.17 127.09 -17.17Q125.3 -17.17 124.25 -16.1Q123.21 -15.04 123.01 -13.22Z" fill="#202633"/>\n <path d="M138.48 0 148.19 -29.1H157.43L167.43 0H159.69L157.72 -6.19H148.03L146.14 0ZM149.65 -11.46H156.04L155.65 -12.71Q154.89 -15.21 154.16 -17.95Q153.44 -20.68 152.74 -23.61Q152.07 -20.66 151.42 -17.93Q150.77 -15.2 150.04 -12.71Z M168.81 0V-29.1H180.72Q184 -29.1 186.39 -27.83Q188.77 -26.56 190.05 -24.3Q191.33 -22.03 191.33 -19.04Q191.33 -16.04 190.02 -13.8Q188.71 -11.56 186.29 -10.32Q183.87 -9.08 180.53 -9.08H175.7V0ZM175.7 -14.53H179.28Q181.7 -14.53 182.94 -15.77Q184.18 -17.01 184.18 -19.04Q184.18 -21.09 182.93 -22.3Q181.68 -23.52 179.26 -23.52H175.7Z M199.98 -29.1V0H193.08V-29.1Z" fill="#008A7D"/>\n</svg>\n';
|
|
@@ -15345,9 +15876,9 @@ function openapiInputForConfig(specRelativePosix) {
|
|
|
15345
15876
|
return `./${specRelativePosix}`;
|
|
15346
15877
|
}
|
|
15347
15878
|
function toRelativeOpenApiInput(projectDir, inputPath) {
|
|
15348
|
-
const resolved =
|
|
15349
|
-
const relative =
|
|
15350
|
-
const posix = relative.split(
|
|
15879
|
+
const resolved = path52.isAbsolute(inputPath) ? inputPath : path52.resolve(projectDir, inputPath);
|
|
15880
|
+
const relative = path52.relative(projectDir, resolved);
|
|
15881
|
+
const posix = relative.split(path52.sep).join(path52.posix.sep);
|
|
15351
15882
|
if (!posix) {
|
|
15352
15883
|
throw new Error("OpenAPI path must point to a file.");
|
|
15353
15884
|
}
|
|
@@ -15627,7 +16158,7 @@ function isPlanFree(plan2) {
|
|
|
15627
16158
|
return (plan2.priceAmount ?? 0) <= 0;
|
|
15628
16159
|
}
|
|
15629
16160
|
function manifestFilePath(siteDirectory) {
|
|
15630
|
-
return
|
|
16161
|
+
return path53.join(path53.resolve(siteDirectory), APITOGO_CONFIG_FILENAME2);
|
|
15631
16162
|
}
|
|
15632
16163
|
var STORED_MANIFEST_KEYS = [
|
|
15633
16164
|
"authentication",
|
|
@@ -16100,39 +16631,39 @@ function resolveBackendSourceDirectory(siteDirectory, sourceDirectory) {
|
|
|
16100
16631
|
if (!trimmed) {
|
|
16101
16632
|
return void 0;
|
|
16102
16633
|
}
|
|
16103
|
-
return
|
|
16634
|
+
return path53.resolve(siteDirectory, trimmed);
|
|
16104
16635
|
}
|
|
16105
16636
|
function resolveBackendOpenApiPath(openApiPath, options) {
|
|
16106
16637
|
const trimmed = openApiPath?.trim();
|
|
16107
16638
|
if (!trimmed) {
|
|
16108
16639
|
return void 0;
|
|
16109
16640
|
}
|
|
16110
|
-
if (
|
|
16641
|
+
if (path53.isAbsolute(trimmed)) {
|
|
16111
16642
|
return trimmed;
|
|
16112
16643
|
}
|
|
16113
|
-
const siteResolved =
|
|
16114
|
-
if (
|
|
16644
|
+
const siteResolved = path53.resolve(options.siteDirectory, trimmed);
|
|
16645
|
+
if (existsSync6(siteResolved)) {
|
|
16115
16646
|
return siteResolved;
|
|
16116
16647
|
}
|
|
16117
16648
|
const backendDir = options.backendSourceDirectory?.trim();
|
|
16118
16649
|
if (backendDir) {
|
|
16119
|
-
const backendResolved =
|
|
16120
|
-
if (
|
|
16650
|
+
const backendResolved = path53.resolve(backendDir, trimmed);
|
|
16651
|
+
if (existsSync6(backendResolved)) {
|
|
16121
16652
|
return backendResolved;
|
|
16122
16653
|
}
|
|
16123
16654
|
}
|
|
16124
16655
|
return siteResolved;
|
|
16125
16656
|
}
|
|
16126
16657
|
function toSiteRelativeOpenApiInput(siteDirectory, openApiAbsolutePath) {
|
|
16127
|
-
const relative =
|
|
16128
|
-
|
|
16129
|
-
|
|
16658
|
+
const relative = path53.relative(
|
|
16659
|
+
path53.resolve(siteDirectory),
|
|
16660
|
+
path53.resolve(openApiAbsolutePath)
|
|
16130
16661
|
);
|
|
16131
|
-
const posix = relative.split(
|
|
16662
|
+
const posix = relative.split(path53.sep).join("/");
|
|
16132
16663
|
if (!posix || posix === ".") {
|
|
16133
16664
|
return "./";
|
|
16134
16665
|
}
|
|
16135
|
-
if (
|
|
16666
|
+
if (path53.isAbsolute(posix) || posix.startsWith("..")) {
|
|
16136
16667
|
return posix;
|
|
16137
16668
|
}
|
|
16138
16669
|
return posix.startsWith("./") ? posix : `./${posix}`;
|
|
@@ -16171,7 +16702,7 @@ function applyOpenApiToSiteConfig(config2, options) {
|
|
|
16171
16702
|
async function readManifest2(siteDirectory) {
|
|
16172
16703
|
const filePath = manifestFilePath(siteDirectory);
|
|
16173
16704
|
if (await pathExists(filePath)) {
|
|
16174
|
-
const raw = await
|
|
16705
|
+
const raw = await readFile13(filePath, "utf8");
|
|
16175
16706
|
const config2 = JSON.parse(raw);
|
|
16176
16707
|
return extractManifestFromConfig(config2);
|
|
16177
16708
|
}
|
|
@@ -16179,10 +16710,10 @@ async function readManifest2(siteDirectory) {
|
|
|
16179
16710
|
}
|
|
16180
16711
|
async function writeManifest2(siteDirectory, manifest) {
|
|
16181
16712
|
const filePath = manifestFilePath(siteDirectory);
|
|
16182
|
-
await mkdir13(
|
|
16713
|
+
await mkdir13(path53.dirname(filePath), { recursive: true });
|
|
16183
16714
|
let config2 = {};
|
|
16184
16715
|
if (await pathExists(filePath)) {
|
|
16185
|
-
const raw = await
|
|
16716
|
+
const raw = await readFile13(filePath, "utf8");
|
|
16186
16717
|
config2 = JSON.parse(raw);
|
|
16187
16718
|
}
|
|
16188
16719
|
const {
|
|
@@ -16201,11 +16732,11 @@ import {
|
|
|
16201
16732
|
access as access6,
|
|
16202
16733
|
appendFile,
|
|
16203
16734
|
mkdir as mkdir14,
|
|
16204
|
-
readFile as
|
|
16735
|
+
readFile as readFile14,
|
|
16205
16736
|
unlink,
|
|
16206
16737
|
writeFile as writeFile15
|
|
16207
16738
|
} from "node:fs/promises";
|
|
16208
|
-
import
|
|
16739
|
+
import path54 from "node:path";
|
|
16209
16740
|
var ENV_FILENAME = ".env";
|
|
16210
16741
|
var COLLAPSED_UPPERCASE_ALIASES2 = {
|
|
16211
16742
|
SECRETKEY: "secretKey",
|
|
@@ -16307,7 +16838,7 @@ function applyEnvOverrides2(config2, env) {
|
|
|
16307
16838
|
}
|
|
16308
16839
|
async function readEnvFile(filePath) {
|
|
16309
16840
|
try {
|
|
16310
|
-
const raw = await
|
|
16841
|
+
const raw = await readFile14(filePath, "utf8");
|
|
16311
16842
|
const env = {};
|
|
16312
16843
|
for (const line2 of raw.split("\n")) {
|
|
16313
16844
|
const trimmed = line2.trim();
|
|
@@ -16324,12 +16855,12 @@ async function readEnvFile(filePath) {
|
|
|
16324
16855
|
}
|
|
16325
16856
|
}
|
|
16326
16857
|
async function loadSiteEnv(siteDirectory) {
|
|
16327
|
-
const resolvedDirectory =
|
|
16858
|
+
const resolvedDirectory = path54.resolve(siteDirectory);
|
|
16328
16859
|
const merged = { ...process.env };
|
|
16329
16860
|
for (const fileName of [ENV_FILENAME, ".env.local"]) {
|
|
16330
16861
|
Object.assign(
|
|
16331
16862
|
merged,
|
|
16332
|
-
await readEnvFile(
|
|
16863
|
+
await readEnvFile(path54.join(resolvedDirectory, fileName))
|
|
16333
16864
|
);
|
|
16334
16865
|
}
|
|
16335
16866
|
return merged;
|
|
@@ -16366,7 +16897,7 @@ function buildAuthSecretsJson(secrets2, options) {
|
|
|
16366
16897
|
return payload;
|
|
16367
16898
|
}
|
|
16368
16899
|
function envFilePath(siteDirectory) {
|
|
16369
|
-
return
|
|
16900
|
+
return path54.join(path54.resolve(siteDirectory), ENV_FILENAME);
|
|
16370
16901
|
}
|
|
16371
16902
|
function createEnvContent() {
|
|
16372
16903
|
return [
|
|
@@ -16430,7 +16961,7 @@ function validateSecretsForPublish(manifest, secrets2, emailOptions) {
|
|
|
16430
16961
|
}
|
|
16431
16962
|
async function siteEnvFileHasEntries(siteDirectory) {
|
|
16432
16963
|
for (const fileName of [ENV_FILENAME, ".env.local"]) {
|
|
16433
|
-
const loaded = await readEnvFile(
|
|
16964
|
+
const loaded = await readEnvFile(path54.join(siteDirectory, fileName));
|
|
16434
16965
|
if (Object.keys(loaded).length > 0) {
|
|
16435
16966
|
return true;
|
|
16436
16967
|
}
|
|
@@ -16438,7 +16969,7 @@ async function siteEnvFileHasEntries(siteDirectory) {
|
|
|
16438
16969
|
return false;
|
|
16439
16970
|
}
|
|
16440
16971
|
async function resolvePublishConfig(siteDirectory, emailOptions) {
|
|
16441
|
-
const resolvedDirectory =
|
|
16972
|
+
const resolvedDirectory = path54.resolve(siteDirectory);
|
|
16442
16973
|
const manifestRaw = await readManifest2(resolvedDirectory);
|
|
16443
16974
|
if (!manifestRaw) {
|
|
16444
16975
|
throw new Error("apitogo.config.json not found in sourceDirectory.");
|
|
@@ -16471,7 +17002,7 @@ function envFileHasActiveAssignments(content) {
|
|
|
16471
17002
|
});
|
|
16472
17003
|
}
|
|
16473
17004
|
async function writeScaffoldEnv(siteDirectory) {
|
|
16474
|
-
const resolvedDirectory =
|
|
17005
|
+
const resolvedDirectory = path54.resolve(siteDirectory);
|
|
16475
17006
|
const filesWritten = [];
|
|
16476
17007
|
const envPath = envFilePath(resolvedDirectory);
|
|
16477
17008
|
try {
|
|
@@ -16482,9 +17013,9 @@ async function writeScaffoldEnv(siteDirectory) {
|
|
|
16482
17013
|
filesWritten.push(ENV_FILENAME);
|
|
16483
17014
|
}
|
|
16484
17015
|
for (const legacyName of [".env.example"]) {
|
|
16485
|
-
const legacyPath =
|
|
17016
|
+
const legacyPath = path54.join(resolvedDirectory, legacyName);
|
|
16486
17017
|
try {
|
|
16487
|
-
const legacy = await
|
|
17018
|
+
const legacy = await readFile14(legacyPath, "utf8");
|
|
16488
17019
|
if (!envFileHasActiveAssignments(legacy)) {
|
|
16489
17020
|
await unlink(legacyPath);
|
|
16490
17021
|
filesWritten.push(`removed:${legacyName}`);
|
|
@@ -16492,10 +17023,10 @@ async function writeScaffoldEnv(siteDirectory) {
|
|
|
16492
17023
|
} catch {
|
|
16493
17024
|
}
|
|
16494
17025
|
}
|
|
16495
|
-
const gitignorePath =
|
|
17026
|
+
const gitignorePath = path54.join(resolvedDirectory, ".gitignore");
|
|
16496
17027
|
let gitignore = "";
|
|
16497
17028
|
try {
|
|
16498
|
-
gitignore = await
|
|
17029
|
+
gitignore = await readFile14(gitignorePath, "utf8");
|
|
16499
17030
|
} catch {
|
|
16500
17031
|
gitignore = "";
|
|
16501
17032
|
}
|
|
@@ -16527,13 +17058,13 @@ var SECRETS_COLLECTION_INSTRUCTION = "For go-live, plans must already be in apit
|
|
|
16527
17058
|
|
|
16528
17059
|
// src/cli/platform/helpers/dev-portal-site-validation.ts
|
|
16529
17060
|
init_gateway_limits();
|
|
16530
|
-
import { readFile as
|
|
17061
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
16531
17062
|
import { createRequire as createRequire3 } from "node:module";
|
|
16532
|
-
import
|
|
17063
|
+
import path55 from "node:path";
|
|
16533
17064
|
var cachedFallbackTs;
|
|
16534
17065
|
async function loadTypeScriptForSite(sourceDirectory) {
|
|
16535
17066
|
const require2 = createRequire3(
|
|
16536
|
-
|
|
17067
|
+
path55.join(path55.resolve(sourceDirectory), "package.json")
|
|
16537
17068
|
);
|
|
16538
17069
|
try {
|
|
16539
17070
|
return require2("typescript");
|
|
@@ -16580,7 +17111,7 @@ async function referencedFileExists(sourceDirectory, reference) {
|
|
|
16580
17111
|
return true;
|
|
16581
17112
|
}
|
|
16582
17113
|
return pathExists(
|
|
16583
|
-
|
|
17114
|
+
path55.resolve(sourceDirectory, reference.replace(/^\.\//, ""))
|
|
16584
17115
|
);
|
|
16585
17116
|
}
|
|
16586
17117
|
async function validateSiteConfigStructure(config2, sourceDirectory) {
|
|
@@ -16695,17 +17226,17 @@ var DEFAULT_TSX_CANDIDATES = [
|
|
|
16695
17226
|
"landing/LandingPage.tsx"
|
|
16696
17227
|
];
|
|
16697
17228
|
async function validateDevPortalSiteConfig(sourceDirectory, options) {
|
|
16698
|
-
const resolvedDir =
|
|
17229
|
+
const resolvedDir = path55.resolve(sourceDirectory);
|
|
16699
17230
|
const issues = [];
|
|
16700
17231
|
const checkedFiles = [];
|
|
16701
17232
|
const skippedChecks = [];
|
|
16702
|
-
const jsonPath =
|
|
17233
|
+
const jsonPath = path55.join(resolvedDir, APITOGO_CONFIG_FILENAME2);
|
|
16703
17234
|
if (await pathExists(jsonPath)) {
|
|
16704
17235
|
checkedFiles.push(APITOGO_CONFIG_FILENAME2);
|
|
16705
17236
|
let parsed;
|
|
16706
17237
|
let parseFailed = false;
|
|
16707
17238
|
try {
|
|
16708
|
-
parsed = JSON.parse(await
|
|
17239
|
+
parsed = JSON.parse(await readFile15(jsonPath, "utf8"));
|
|
16709
17240
|
} catch (error) {
|
|
16710
17241
|
parseFailed = true;
|
|
16711
17242
|
issues.push({
|
|
@@ -16731,7 +17262,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
|
|
|
16731
17262
|
);
|
|
16732
17263
|
const existingTsxFiles = [];
|
|
16733
17264
|
for (const relativeFile of tsxFiles) {
|
|
16734
|
-
if (await pathExists(
|
|
17265
|
+
if (await pathExists(path55.join(resolvedDir, relativeFile))) {
|
|
16735
17266
|
existingTsxFiles.push(relativeFile);
|
|
16736
17267
|
}
|
|
16737
17268
|
}
|
|
@@ -16744,8 +17275,8 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
|
|
|
16744
17275
|
} else {
|
|
16745
17276
|
for (const relativeFile of existingTsxFiles) {
|
|
16746
17277
|
checkedFiles.push(relativeFile);
|
|
16747
|
-
const content = await
|
|
16748
|
-
|
|
17278
|
+
const content = await readFile15(
|
|
17279
|
+
path55.join(resolvedDir, relativeFile),
|
|
16749
17280
|
"utf8"
|
|
16750
17281
|
);
|
|
16751
17282
|
issues.push(
|
|
@@ -16765,7 +17296,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
|
|
|
16765
17296
|
};
|
|
16766
17297
|
}
|
|
16767
17298
|
function formatSiteValidationErrors(result) {
|
|
16768
|
-
return result.issues.map((
|
|
17299
|
+
return result.issues.map((issue3) => `${issue3.file}: ${issue3.message}`).join("; ");
|
|
16769
17300
|
}
|
|
16770
17301
|
function buildSiteValidationResponseFields(result) {
|
|
16771
17302
|
if (result.valid) {
|
|
@@ -16781,9 +17312,9 @@ function buildSiteValidationResponseFields(result) {
|
|
|
16781
17312
|
}
|
|
16782
17313
|
|
|
16783
17314
|
// src/cli/platform/helpers/npm-build.ts
|
|
16784
|
-
import { spawn } from "node:child_process";
|
|
16785
|
-
import { access as access7, readFile as
|
|
16786
|
-
import
|
|
17315
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
17316
|
+
import { access as access7, readFile as readFile16 } from "node:fs/promises";
|
|
17317
|
+
import path56 from "node:path";
|
|
16787
17318
|
var BUILD_LOG_TAIL_BYTES = 8192;
|
|
16788
17319
|
var MIN_NODE_MAJOR = 20;
|
|
16789
17320
|
var NpmBuildError = class extends Error {
|
|
@@ -16832,7 +17363,7 @@ async function pathExists2(filePath) {
|
|
|
16832
17363
|
}
|
|
16833
17364
|
function runCommand(command2, args, cwd, env) {
|
|
16834
17365
|
return new Promise((resolve, reject) => {
|
|
16835
|
-
const child =
|
|
17366
|
+
const child = spawn2(command2, args, {
|
|
16836
17367
|
cwd,
|
|
16837
17368
|
env: env ? { ...process.env, ...env } : process.env,
|
|
16838
17369
|
shell: process.platform === "win32"
|
|
@@ -16850,13 +17381,13 @@ function runCommand(command2, args, cwd, env) {
|
|
|
16850
17381
|
}
|
|
16851
17382
|
async function runNpmInstall(sourceDirectory, options) {
|
|
16852
17383
|
assertNodeVersion(options?.nodeVersion);
|
|
16853
|
-
const cwd =
|
|
16854
|
-
const packageJsonPath =
|
|
17384
|
+
const cwd = path56.resolve(sourceDirectory);
|
|
17385
|
+
const packageJsonPath = path56.join(cwd, "package.json");
|
|
16855
17386
|
if (!await pathExists2(packageJsonPath)) {
|
|
16856
17387
|
throw new Error(`package.json not found in sourceDirectory: ${cwd}`);
|
|
16857
17388
|
}
|
|
16858
17389
|
const startedAt = Date.now();
|
|
16859
|
-
const lockPath =
|
|
17390
|
+
const lockPath = path56.join(cwd, "package-lock.json");
|
|
16860
17391
|
const installArgs = await pathExists2(lockPath) ? ["ci"] : ["install"];
|
|
16861
17392
|
const install = await runCommand("npm", installArgs, cwd);
|
|
16862
17393
|
const installLog = install.output;
|
|
@@ -16874,8 +17405,8 @@ async function runNpmInstall(sourceDirectory, options) {
|
|
|
16874
17405
|
}
|
|
16875
17406
|
async function runNpmBuild(sourceDirectory, options) {
|
|
16876
17407
|
assertNodeVersion(options?.nodeVersion);
|
|
16877
|
-
const cwd =
|
|
16878
|
-
const packageJsonPath =
|
|
17408
|
+
const cwd = path56.resolve(sourceDirectory);
|
|
17409
|
+
const packageJsonPath = path56.join(cwd, "package.json");
|
|
16879
17410
|
if (!await pathExists2(packageJsonPath)) {
|
|
16880
17411
|
throw new Error(`package.json not found in sourceDirectory: ${cwd}`);
|
|
16881
17412
|
}
|
|
@@ -16892,8 +17423,8 @@ async function runNpmBuild(sourceDirectory, options) {
|
|
|
16892
17423
|
build2.exitCode
|
|
16893
17424
|
);
|
|
16894
17425
|
}
|
|
16895
|
-
const distPath =
|
|
16896
|
-
const indexPath =
|
|
17426
|
+
const distPath = path56.join(cwd, "dist");
|
|
17427
|
+
const indexPath = path56.join(distPath, "index.html");
|
|
16897
17428
|
if (!await pathExists2(indexPath)) {
|
|
16898
17429
|
throw new NpmBuildError(
|
|
16899
17430
|
`Build completed but dist/index.html was not found at ${indexPath}`,
|
|
@@ -17277,7 +17808,7 @@ function validateManifestForPlatformPublish(manifest) {
|
|
|
17277
17808
|
|
|
17278
17809
|
// src/cli/platform/tools/check-publish-readiness.ts
|
|
17279
17810
|
async function checkPublishReadiness(client, _config, args) {
|
|
17280
|
-
const sourceDirectory =
|
|
17811
|
+
const sourceDirectory = path57.resolve(args.sourceDirectory);
|
|
17281
17812
|
const manifest = await readManifest2(sourceDirectory) ?? createScaffoldManifest();
|
|
17282
17813
|
const emailOptions = await resolveEmailOptionsForPublish(
|
|
17283
17814
|
client,
|
|
@@ -17297,7 +17828,7 @@ async function checkPublishReadiness(client, _config, args) {
|
|
|
17297
17828
|
const mergedPrerequisites = mergePublishPrerequisitesWithPlatformValidation(
|
|
17298
17829
|
publishPrerequisites,
|
|
17299
17830
|
platformValidation.issues.map(
|
|
17300
|
-
(
|
|
17831
|
+
(issue3) => `${issue3.field}: ${issue3.message}`
|
|
17301
17832
|
)
|
|
17302
17833
|
);
|
|
17303
17834
|
const siteValidation = await validateDevPortalSiteConfig(sourceDirectory);
|
|
@@ -17355,7 +17886,7 @@ async function checkPublishReadiness(client, _config, args) {
|
|
|
17355
17886
|
|
|
17356
17887
|
// src/cli/check/handler.ts
|
|
17357
17888
|
async function check(argv) {
|
|
17358
|
-
const sourceDirectory =
|
|
17889
|
+
const sourceDirectory = path58.resolve(process.cwd(), argv.dir);
|
|
17359
17890
|
if (await isConfigV2(sourceDirectory)) {
|
|
17360
17891
|
await checkV2(sourceDirectory, argv);
|
|
17361
17892
|
return;
|
|
@@ -17404,7 +17935,7 @@ var check_default = {
|
|
|
17404
17935
|
default: false
|
|
17405
17936
|
}).option("offline", {
|
|
17406
17937
|
type: "boolean",
|
|
17407
|
-
describe: "Config version 2: do not ask the platform anything (secret names are then not checked)",
|
|
17938
|
+
describe: "Config version 2: do not ask the platform anything (secret names and the gateway's online rules are then not checked)",
|
|
17408
17939
|
default: false
|
|
17409
17940
|
}).option("json", {
|
|
17410
17941
|
type: "boolean",
|
|
@@ -17419,14 +17950,14 @@ var check_default = {
|
|
|
17419
17950
|
|
|
17420
17951
|
// src/cli/make-config/handler.ts
|
|
17421
17952
|
import { constants as constants2 } from "node:fs";
|
|
17422
|
-
import { access as access12, mkdir as mkdir17, readFile as
|
|
17423
|
-
import
|
|
17953
|
+
import { access as access12, mkdir as mkdir17, readFile as readFile21, writeFile as writeFile20 } from "node:fs/promises";
|
|
17954
|
+
import path68 from "node:path";
|
|
17424
17955
|
import { stdin as input2 } from "node:process";
|
|
17425
17956
|
import { glob as glob7 } from "glob";
|
|
17426
17957
|
|
|
17427
17958
|
// src/cli/platform/helpers/openapi-action-keys.ts
|
|
17428
|
-
import { existsSync as
|
|
17429
|
-
import
|
|
17959
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4 } from "node:fs";
|
|
17960
|
+
import path59 from "node:path";
|
|
17430
17961
|
import YAML from "yaml";
|
|
17431
17962
|
var HTTP_METHODS = [
|
|
17432
17963
|
"get",
|
|
@@ -17495,7 +18026,7 @@ function extractGatewayActionKeysFromOpenApiDocument(document) {
|
|
|
17495
18026
|
function extractGatewayActionKeysFromOpenApiFile(filePath) {
|
|
17496
18027
|
try {
|
|
17497
18028
|
const raw = readFileSync4(filePath, "utf8");
|
|
17498
|
-
const ext =
|
|
18029
|
+
const ext = path59.extname(filePath).toLowerCase();
|
|
17499
18030
|
const parsed = ext === ".json" ? JSON.parse(raw) : YAML.parse(raw);
|
|
17500
18031
|
return extractGatewayActionKeysFromOpenApiDocument(parsed);
|
|
17501
18032
|
} catch {
|
|
@@ -17516,8 +18047,8 @@ function resolveConfigOpenApiFilePath(siteDirectory, config2) {
|
|
|
17516
18047
|
}
|
|
17517
18048
|
}
|
|
17518
18049
|
for (const candidate of candidates) {
|
|
17519
|
-
const resolved =
|
|
17520
|
-
if (
|
|
18050
|
+
const resolved = path59.isAbsolute(candidate) ? candidate : path59.resolve(siteDirectory, candidate);
|
|
18051
|
+
if (existsSync7(resolved)) {
|
|
17521
18052
|
return resolved;
|
|
17522
18053
|
}
|
|
17523
18054
|
}
|
|
@@ -17531,8 +18062,8 @@ function loadOpenApiActionKeysForSite(siteDirectory, config2) {
|
|
|
17531
18062
|
return extractGatewayActionKeysFromOpenApiFile(filePath);
|
|
17532
18063
|
}
|
|
17533
18064
|
function loadOpenApiActionKeysFromSiteDirectory(siteDirectory) {
|
|
17534
|
-
const configPath =
|
|
17535
|
-
if (!
|
|
18065
|
+
const configPath = path59.join(siteDirectory, "apitogo.config.json");
|
|
18066
|
+
if (!existsSync7(configPath)) {
|
|
17536
18067
|
return [];
|
|
17537
18068
|
}
|
|
17538
18069
|
try {
|
|
@@ -17559,8 +18090,8 @@ function withDefaultIncludedActionKeys(plan2, actionKeys) {
|
|
|
17559
18090
|
}
|
|
17560
18091
|
|
|
17561
18092
|
// src/cli/scaffold/dev-portal-config-pages.ts
|
|
17562
|
-
import { access as access8, readFile as
|
|
17563
|
-
import
|
|
18093
|
+
import { access as access8, readFile as readFile17, rename as rename7, rm as rm9, writeFile as writeFile16 } from "node:fs/promises";
|
|
18094
|
+
import path60 from "node:path";
|
|
17564
18095
|
async function writeConfigAtomically(filePath, content) {
|
|
17565
18096
|
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
17566
18097
|
try {
|
|
@@ -17573,7 +18104,7 @@ async function writeConfigAtomically(filePath, content) {
|
|
|
17573
18104
|
}
|
|
17574
18105
|
async function resolveApitogoConfigPath(sourceDirectory) {
|
|
17575
18106
|
for (const filename of ["apitogo.config.tsx", "apitogo.config.jsx"]) {
|
|
17576
|
-
const configPath =
|
|
18107
|
+
const configPath = path60.join(sourceDirectory, filename);
|
|
17577
18108
|
try {
|
|
17578
18109
|
await access8(configPath);
|
|
17579
18110
|
return configPath;
|
|
@@ -18324,7 +18855,7 @@ async function wireNewDevPortalPageInConfig(sourceDirectory, slug, label) {
|
|
|
18324
18855
|
if (!configPath) {
|
|
18325
18856
|
return { updated: false };
|
|
18326
18857
|
}
|
|
18327
|
-
const original = await
|
|
18858
|
+
const original = await readFile17(configPath, "utf8");
|
|
18328
18859
|
const patched = patchApitogoConfigForNewPage(original, slug, label, {
|
|
18329
18860
|
typescript: configPath.endsWith(".tsx")
|
|
18330
18861
|
});
|
|
@@ -18336,18 +18867,18 @@ async function wireNewDevPortalPageInConfig(sourceDirectory, slug, label) {
|
|
|
18336
18867
|
}
|
|
18337
18868
|
|
|
18338
18869
|
// src/cli/scaffold/wire-openapi.ts
|
|
18339
|
-
import { access as access10, readFile as
|
|
18340
|
-
import
|
|
18870
|
+
import { access as access10, readFile as readFile19, writeFile as writeFile18 } from "node:fs/promises";
|
|
18871
|
+
import path65 from "node:path";
|
|
18341
18872
|
|
|
18342
18873
|
// src/cli/platform/helpers/openapi.ts
|
|
18343
18874
|
import { promises as fs6 } from "node:fs";
|
|
18344
|
-
import
|
|
18875
|
+
import path62 from "node:path";
|
|
18345
18876
|
import SwaggerParser from "@apidevtools/swagger-parser";
|
|
18346
18877
|
import YAML2 from "yaml";
|
|
18347
18878
|
|
|
18348
18879
|
// src/cli/scaffold/discover-openapi-files.ts
|
|
18349
18880
|
import { promises as fs5 } from "node:fs";
|
|
18350
|
-
import
|
|
18881
|
+
import path61 from "node:path";
|
|
18351
18882
|
var OPENAPI_BASENAMES = [
|
|
18352
18883
|
"openapi.yaml",
|
|
18353
18884
|
"openapi.yml",
|
|
@@ -18369,13 +18900,13 @@ var SKIP_OPENAPI_SEARCH_DIRS = /* @__PURE__ */ new Set([
|
|
|
18369
18900
|
".apitogo"
|
|
18370
18901
|
]);
|
|
18371
18902
|
async function collectFromDir(dir, searchedDirectories, candidates) {
|
|
18372
|
-
const resolved =
|
|
18903
|
+
const resolved = path61.resolve(dir);
|
|
18373
18904
|
if (searchedDirectories.includes(resolved)) {
|
|
18374
18905
|
return;
|
|
18375
18906
|
}
|
|
18376
18907
|
searchedDirectories.push(resolved);
|
|
18377
18908
|
for (const name of OPENAPI_BASENAMES) {
|
|
18378
|
-
const candidate =
|
|
18909
|
+
const candidate = path61.join(resolved, name);
|
|
18379
18910
|
try {
|
|
18380
18911
|
await fs5.access(candidate);
|
|
18381
18912
|
candidates.push(candidate);
|
|
@@ -18390,8 +18921,8 @@ async function collectSiteOpenApiFiles(siteDirectory) {
|
|
|
18390
18921
|
return { candidates: [...new Set(candidates)], searchedDirectories };
|
|
18391
18922
|
}
|
|
18392
18923
|
async function collectNearbyOpenApiFiles(siteDirectory) {
|
|
18393
|
-
const site =
|
|
18394
|
-
const parent =
|
|
18924
|
+
const site = path61.resolve(siteDirectory);
|
|
18925
|
+
const parent = path61.dirname(site);
|
|
18395
18926
|
const searchedDirectories = [];
|
|
18396
18927
|
const candidates = [];
|
|
18397
18928
|
if (parent && parent !== site) {
|
|
@@ -18402,8 +18933,8 @@ async function collectNearbyOpenApiFiles(siteDirectory) {
|
|
|
18402
18933
|
if (!entry.isDirectory() || SKIP_OPENAPI_SEARCH_DIRS.has(entry.name)) {
|
|
18403
18934
|
continue;
|
|
18404
18935
|
}
|
|
18405
|
-
const sibling =
|
|
18406
|
-
if (
|
|
18936
|
+
const sibling = path61.join(parent, entry.name);
|
|
18937
|
+
if (path61.resolve(sibling) === site) {
|
|
18407
18938
|
continue;
|
|
18408
18939
|
}
|
|
18409
18940
|
await collectFromDir(sibling, searchedDirectories, candidates);
|
|
@@ -18431,7 +18962,7 @@ function parseOpenApiObject(doc) {
|
|
|
18431
18962
|
};
|
|
18432
18963
|
}
|
|
18433
18964
|
function liteParseOpenApi(content, filePath) {
|
|
18434
|
-
const ext =
|
|
18965
|
+
const ext = path61.extname(filePath).toLowerCase();
|
|
18435
18966
|
if (ext === ".json") {
|
|
18436
18967
|
try {
|
|
18437
18968
|
const parsed = JSON.parse(content);
|
|
@@ -18496,9 +19027,9 @@ async function discoverNearbyOpenApi(siteDirectory) {
|
|
|
18496
19027
|
};
|
|
18497
19028
|
}
|
|
18498
19029
|
async function validateOpenApiFile(filePath) {
|
|
18499
|
-
const resolved =
|
|
19030
|
+
const resolved = path62.resolve(filePath);
|
|
18500
19031
|
const content = await fs6.readFile(resolved, "utf8");
|
|
18501
|
-
const ext =
|
|
19032
|
+
const ext = path62.extname(resolved).toLowerCase();
|
|
18502
19033
|
let parsed;
|
|
18503
19034
|
try {
|
|
18504
19035
|
parsed = ext === ".json" ? JSON.parse(content) : YAML2.parse(content);
|
|
@@ -18540,8 +19071,8 @@ async function validateOpenApiFile(filePath) {
|
|
|
18540
19071
|
}
|
|
18541
19072
|
|
|
18542
19073
|
// src/cli/scaffold/scaffold-dev-portal-site.ts
|
|
18543
|
-
import { mkdir as mkdir15, readFile as
|
|
18544
|
-
import
|
|
19074
|
+
import { mkdir as mkdir15, readFile as readFile18, writeFile as writeFile17 } from "node:fs/promises";
|
|
19075
|
+
import path64 from "node:path";
|
|
18545
19076
|
|
|
18546
19077
|
// src/cli/scaffold/apitogo-npm-packages.ts
|
|
18547
19078
|
var DEFAULT_CREATE_APITOGO_PACKAGE2 = "@lukoweb/create-apitogo@latest";
|
|
@@ -18627,9 +19158,9 @@ var DOC_TOOL_LOCAL_PACKAGE_PATHS = {
|
|
|
18627
19158
|
};
|
|
18628
19159
|
|
|
18629
19160
|
// src/cli/scaffold/create-apitogo-cli.ts
|
|
18630
|
-
import { spawn as
|
|
19161
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
18631
19162
|
import { access as access9, rm as rm10 } from "node:fs/promises";
|
|
18632
|
-
import
|
|
19163
|
+
import path63 from "node:path";
|
|
18633
19164
|
var SKIP_NEARBY_DISCOVERY_ENV = "APITOGO_SKIP_NEARBY_DISCOVERY";
|
|
18634
19165
|
var CreateApitogoCliError = class extends Error {
|
|
18635
19166
|
exitCode;
|
|
@@ -18646,9 +19177,9 @@ function resolveDocToolPath(explicit) {
|
|
|
18646
19177
|
return value || void 0;
|
|
18647
19178
|
}
|
|
18648
19179
|
function splitOutputDirectory(outputDirectory) {
|
|
18649
|
-
const resolvedOutputDirectory =
|
|
18650
|
-
const projectName =
|
|
18651
|
-
const parentDirectory =
|
|
19180
|
+
const resolvedOutputDirectory = path63.resolve(outputDirectory);
|
|
19181
|
+
const projectName = path63.basename(resolvedOutputDirectory);
|
|
19182
|
+
const parentDirectory = path63.dirname(resolvedOutputDirectory);
|
|
18652
19183
|
if (!projectName || projectName === "." || projectName === "..") {
|
|
18653
19184
|
throw new Error(
|
|
18654
19185
|
"outputDirectory must be a named folder (e.g. ./dev-portal-site), not the current directory."
|
|
@@ -18674,7 +19205,7 @@ async function directoryHasEntries(dir) {
|
|
|
18674
19205
|
}
|
|
18675
19206
|
}
|
|
18676
19207
|
function resolveLocalCreateApitogoEntry(docToolPath) {
|
|
18677
|
-
return
|
|
19208
|
+
return path63.resolve(docToolPath, "packages/create-zudoku/dist/index.js");
|
|
18678
19209
|
}
|
|
18679
19210
|
async function resolveCreateApitogoInvocation(options) {
|
|
18680
19211
|
const docToolPath = resolveDocToolPath(options.docToolPath);
|
|
@@ -18701,7 +19232,7 @@ async function resolveCreateApitogoInvocation(options) {
|
|
|
18701
19232
|
}
|
|
18702
19233
|
function runCommand2(command2, args, cwd) {
|
|
18703
19234
|
return new Promise((resolve, reject) => {
|
|
18704
|
-
const child =
|
|
19235
|
+
const child = spawn3(command2, args, {
|
|
18705
19236
|
cwd,
|
|
18706
19237
|
env: { ...process.env, [SKIP_NEARBY_DISCOVERY_ENV]: "1" },
|
|
18707
19238
|
shell: process.platform === "win32"
|
|
@@ -18756,7 +19287,7 @@ async function runCreateApitogoDevPortal(options) {
|
|
|
18756
19287
|
result.exitCode
|
|
18757
19288
|
);
|
|
18758
19289
|
}
|
|
18759
|
-
if (!await pathExists3(
|
|
19290
|
+
if (!await pathExists3(path63.join(resolvedOutputDirectory, "package.json"))) {
|
|
18760
19291
|
throw new CreateApitogoCliError(
|
|
18761
19292
|
`create-apitogo completed but package.json was not found at ${resolvedOutputDirectory}`,
|
|
18762
19293
|
result.output,
|
|
@@ -18815,7 +19346,7 @@ function ensureRequiredApitogoModules(pkg, options) {
|
|
|
18815
19346
|
]
|
|
18816
19347
|
]) {
|
|
18817
19348
|
if (options.docToolPath) {
|
|
18818
|
-
deps[moduleName] = `file:${
|
|
19349
|
+
deps[moduleName] = `file:${path64.join(options.docToolPath, localPath)}`;
|
|
18819
19350
|
continue;
|
|
18820
19351
|
}
|
|
18821
19352
|
deps[moduleName] = `npm:${moduleName}@${moduleAliasVersion}`;
|
|
@@ -18846,7 +19377,7 @@ function rewritePackageJson(raw, options) {
|
|
|
18846
19377
|
DOC_TOOL_LOCAL_PACKAGE_PATHS
|
|
18847
19378
|
)) {
|
|
18848
19379
|
if (dep in deps) {
|
|
18849
|
-
deps[dep] = `file:${
|
|
19380
|
+
deps[dep] = `file:${path64.join(options.docToolPath, relativePath)}`;
|
|
18850
19381
|
linkedWorkspacePackage = true;
|
|
18851
19382
|
}
|
|
18852
19383
|
}
|
|
@@ -18946,23 +19477,23 @@ function patchDashboardPageConfig(content) {
|
|
|
18946
19477
|
);
|
|
18947
19478
|
}
|
|
18948
19479
|
async function ensureAccountPlansPage(outputDirectory) {
|
|
18949
|
-
const pagePath =
|
|
19480
|
+
const pagePath = path64.join(outputDirectory, ACCOUNT_PLANS_PAGE_PATH);
|
|
18950
19481
|
try {
|
|
18951
|
-
await
|
|
19482
|
+
await readFile18(pagePath, "utf8");
|
|
18952
19483
|
return void 0;
|
|
18953
19484
|
} catch {
|
|
18954
|
-
await mkdir15(
|
|
19485
|
+
await mkdir15(path64.dirname(pagePath), { recursive: true });
|
|
18955
19486
|
await writeFile17(pagePath, ACCOUNT_PLANS_PAGE);
|
|
18956
19487
|
return ACCOUNT_PLANS_PAGE_PATH;
|
|
18957
19488
|
}
|
|
18958
19489
|
}
|
|
18959
19490
|
async function ensureDashboardPage(outputDirectory) {
|
|
18960
|
-
const pagePath =
|
|
19491
|
+
const pagePath = path64.join(outputDirectory, DASHBOARD_PAGE_PATH);
|
|
18961
19492
|
try {
|
|
18962
|
-
await
|
|
19493
|
+
await readFile18(pagePath, "utf8");
|
|
18963
19494
|
return void 0;
|
|
18964
19495
|
} catch {
|
|
18965
|
-
await mkdir15(
|
|
19496
|
+
await mkdir15(path64.dirname(pagePath), { recursive: true });
|
|
18966
19497
|
await writeFile17(pagePath, DASHBOARD_PAGE);
|
|
18967
19498
|
return DASHBOARD_PAGE_PATH;
|
|
18968
19499
|
}
|
|
@@ -19019,9 +19550,9 @@ function patchApitogoConfig(content, options) {
|
|
|
19019
19550
|
async function patchScaffoldedSite(outputDirectory, options) {
|
|
19020
19551
|
const title = options.branding?.title ?? options.siteName;
|
|
19021
19552
|
const filesWritten = [];
|
|
19022
|
-
const packageJsonPath =
|
|
19553
|
+
const packageJsonPath = path64.join(outputDirectory, "package.json");
|
|
19023
19554
|
if (options.patchPackageJson !== false) {
|
|
19024
|
-
const packageJson3 = await
|
|
19555
|
+
const packageJson3 = await readFile18(packageJsonPath, "utf8");
|
|
19025
19556
|
await writeFile17(
|
|
19026
19557
|
packageJsonPath,
|
|
19027
19558
|
rewritePackageJson(packageJson3, {
|
|
@@ -19032,8 +19563,8 @@ async function patchScaffoldedSite(outputDirectory, options) {
|
|
|
19032
19563
|
);
|
|
19033
19564
|
filesWritten.push("package.json");
|
|
19034
19565
|
}
|
|
19035
|
-
const configPath =
|
|
19036
|
-
const configContent = await
|
|
19566
|
+
const configPath = path64.join(outputDirectory, "apitogo.config.tsx");
|
|
19567
|
+
const configContent = await readFile18(configPath, "utf8");
|
|
19037
19568
|
await writeFile17(
|
|
19038
19569
|
configPath,
|
|
19039
19570
|
patchDashboardPageConfig(
|
|
@@ -19127,8 +19658,8 @@ async function discoverOpenApiByMode(siteDirectory, mode) {
|
|
|
19127
19658
|
return discoverNearbyOpenApi(siteDirectory);
|
|
19128
19659
|
}
|
|
19129
19660
|
async function wireDevPortalOpenApi(sourceDirectory, openApiPath) {
|
|
19130
|
-
const resolvedSource =
|
|
19131
|
-
const resolvedOpenApi =
|
|
19661
|
+
const resolvedSource = path65.resolve(sourceDirectory);
|
|
19662
|
+
const resolvedOpenApi = path65.resolve(openApiPath);
|
|
19132
19663
|
const input5 = toSiteRelativeOpenApiInput(resolvedSource, resolvedOpenApi);
|
|
19133
19664
|
const validation = await validateOpenApiFile(resolvedOpenApi);
|
|
19134
19665
|
if (!validation.valid) {
|
|
@@ -19140,7 +19671,7 @@ async function wireDevPortalOpenApi(sourceDirectory, openApiPath) {
|
|
|
19140
19671
|
}
|
|
19141
19672
|
const jsonConfigPath = manifestFilePath(resolvedSource);
|
|
19142
19673
|
if (await pathExists4(jsonConfigPath)) {
|
|
19143
|
-
const raw = await
|
|
19674
|
+
const raw = await readFile19(jsonConfigPath, "utf8");
|
|
19144
19675
|
const siteConfig = JSON.parse(raw);
|
|
19145
19676
|
const updated = applyOpenApiToSiteConfig(siteConfig, {
|
|
19146
19677
|
input: input5,
|
|
@@ -19153,8 +19684,8 @@ async function wireDevPortalOpenApi(sourceDirectory, openApiPath) {
|
|
|
19153
19684
|
"utf8"
|
|
19154
19685
|
);
|
|
19155
19686
|
} else {
|
|
19156
|
-
const tsxConfigPath =
|
|
19157
|
-
const configContent = await
|
|
19687
|
+
const tsxConfigPath = path65.join(resolvedSource, "apitogo.config.tsx");
|
|
19688
|
+
const configContent = await readFile19(tsxConfigPath, "utf8");
|
|
19158
19689
|
try {
|
|
19159
19690
|
const patched = patchApitogoConfigOpenApiToFile(configContent, input5);
|
|
19160
19691
|
await writeFile18(tsxConfigPath, patched);
|
|
@@ -19239,7 +19770,7 @@ async function discoverAndWireOpenApi(siteDirectory, mode) {
|
|
|
19239
19770
|
}
|
|
19240
19771
|
|
|
19241
19772
|
// src/cli/make-config/interactive.ts
|
|
19242
|
-
import
|
|
19773
|
+
import path66 from "node:path";
|
|
19243
19774
|
import { stdin as input, stdout as output } from "node:process";
|
|
19244
19775
|
import { createInterface as createInterface3 } from "node:readline/promises";
|
|
19245
19776
|
|
|
@@ -19415,7 +19946,7 @@ async function promptMakeConfigInteractive(options) {
|
|
|
19415
19946
|
continue;
|
|
19416
19947
|
}
|
|
19417
19948
|
const routePath = `/${slug}`;
|
|
19418
|
-
const relativePath =
|
|
19949
|
+
const relativePath = path66.posix.join(options.pagesDir, `${slug}.tsx`);
|
|
19419
19950
|
const page = {
|
|
19420
19951
|
slug,
|
|
19421
19952
|
title: pageTitle.trim(),
|
|
@@ -19510,8 +20041,8 @@ export default function ${componentName}() {
|
|
|
19510
20041
|
// src/cli/make-config/scaffold-project.ts
|
|
19511
20042
|
init_package_json();
|
|
19512
20043
|
import { constants } from "node:fs";
|
|
19513
|
-
import { access as access11, mkdir as mkdir16, readFile as
|
|
19514
|
-
import
|
|
20044
|
+
import { access as access11, mkdir as mkdir16, readFile as readFile20, writeFile as writeFile19 } from "node:fs/promises";
|
|
20045
|
+
import path67 from "node:path";
|
|
19515
20046
|
import { glob as glob6 } from "glob";
|
|
19516
20047
|
|
|
19517
20048
|
// src/cli/make-config/favicon-png.ts
|
|
@@ -19526,9 +20057,9 @@ var OPENAPI_GLOBS = [
|
|
|
19526
20057
|
"openapi.yaml",
|
|
19527
20058
|
"openapi.yml"
|
|
19528
20059
|
];
|
|
19529
|
-
var toPosix2 = (p) => p.split(
|
|
20060
|
+
var toPosix2 = (p) => p.split(path67.sep).join("/");
|
|
19530
20061
|
var sanitizePackageName = (dir) => {
|
|
19531
|
-
const base =
|
|
20062
|
+
const base = path67.basename(path67.resolve(dir));
|
|
19532
20063
|
const s = base.toLowerCase().replace(/[^a-z0-9-_.]/g, "-").replace(/^-+|-+$/g, "");
|
|
19533
20064
|
return s || "apitogo-site";
|
|
19534
20065
|
};
|
|
@@ -19538,7 +20069,7 @@ var apitogoVersionRange = () => {
|
|
|
19538
20069
|
};
|
|
19539
20070
|
async function findOpenApiSpecPath(dir) {
|
|
19540
20071
|
for (const rel2 of OPENAPI_GLOBS) {
|
|
19541
|
-
const full =
|
|
20072
|
+
const full = path67.join(dir, rel2);
|
|
19542
20073
|
try {
|
|
19543
20074
|
await access11(full, constants.R_OK);
|
|
19544
20075
|
return toPosix2(rel2);
|
|
@@ -19553,7 +20084,7 @@ async function findOpenApiSpecPath(dir) {
|
|
|
19553
20084
|
return extra[0] ? toPosix2(extra[0]) : null;
|
|
19554
20085
|
}
|
|
19555
20086
|
async function ensurePackageJson(dir) {
|
|
19556
|
-
const pkgPath =
|
|
20087
|
+
const pkgPath = path67.join(dir, "package.json");
|
|
19557
20088
|
const apitogoVer = apitogoVersionRange();
|
|
19558
20089
|
const baseDeps = {
|
|
19559
20090
|
react: ">=19.0.0",
|
|
@@ -19592,7 +20123,7 @@ async function ensurePackageJson(dir) {
|
|
|
19592
20123
|
`, "utf8");
|
|
19593
20124
|
return true;
|
|
19594
20125
|
}
|
|
19595
|
-
const raw = await
|
|
20126
|
+
const raw = await readFile20(pkgPath, "utf8");
|
|
19596
20127
|
let pkg;
|
|
19597
20128
|
try {
|
|
19598
20129
|
pkg = JSON.parse(raw);
|
|
@@ -19620,7 +20151,7 @@ async function ensurePackageJson(dir) {
|
|
|
19620
20151
|
return false;
|
|
19621
20152
|
}
|
|
19622
20153
|
async function ensureTsconfigJson(dir) {
|
|
19623
|
-
const tsPath =
|
|
20154
|
+
const tsPath = path67.join(dir, "tsconfig.json");
|
|
19624
20155
|
try {
|
|
19625
20156
|
await access11(tsPath, constants.R_OK);
|
|
19626
20157
|
return false;
|
|
@@ -19669,40 +20200,40 @@ var fileExists2 = async (filePath) => {
|
|
|
19669
20200
|
}
|
|
19670
20201
|
};
|
|
19671
20202
|
async function ensurePortalStubFiles(dir) {
|
|
19672
|
-
const landingPath =
|
|
20203
|
+
const landingPath = path67.join(dir, "landing", "LandingPage.tsx");
|
|
19673
20204
|
if (!await fileExists2(landingPath)) {
|
|
19674
|
-
await mkdir16(
|
|
20205
|
+
await mkdir16(path67.join(dir, "landing"), { recursive: true });
|
|
19675
20206
|
await writeFile19(landingPath, LANDING_PAGE_SOURCE, "utf8");
|
|
19676
20207
|
}
|
|
19677
|
-
const plansPath =
|
|
20208
|
+
const plansPath = path67.join(dir, "user-panel", "AccountPlansPage.tsx");
|
|
19678
20209
|
if (!await fileExists2(plansPath)) {
|
|
19679
|
-
await mkdir16(
|
|
20210
|
+
await mkdir16(path67.join(dir, "user-panel"), { recursive: true });
|
|
19680
20211
|
await writeFile19(plansPath, ACCOUNT_PLANS_PAGE_SOURCE, "utf8");
|
|
19681
20212
|
}
|
|
19682
|
-
const dashboardPath =
|
|
20213
|
+
const dashboardPath = path67.join(dir, "user-panel", "DashboardPage.tsx");
|
|
19683
20214
|
if (!await fileExists2(dashboardPath)) {
|
|
19684
|
-
await mkdir16(
|
|
20215
|
+
await mkdir16(path67.join(dir, "user-panel"), { recursive: true });
|
|
19685
20216
|
await writeFile19(dashboardPath, DASHBOARD_PAGE_SOURCE, "utf8");
|
|
19686
20217
|
}
|
|
19687
20218
|
await ensurePublicBrandingFiles(dir);
|
|
19688
20219
|
}
|
|
19689
20220
|
async function ensurePublicBrandingFiles(dir) {
|
|
19690
|
-
const destDir =
|
|
20221
|
+
const destDir = path67.join(dir, "public");
|
|
19691
20222
|
await mkdir16(destDir, { recursive: true });
|
|
19692
20223
|
for (const { name, content } of BRANDING_ASSETS) {
|
|
19693
|
-
const dest =
|
|
20224
|
+
const dest = path67.join(destDir, name);
|
|
19694
20225
|
if (await fileExists2(dest)) {
|
|
19695
20226
|
continue;
|
|
19696
20227
|
}
|
|
19697
20228
|
await writeFile19(dest, content, "utf8");
|
|
19698
20229
|
}
|
|
19699
|
-
const faviconDest =
|
|
20230
|
+
const faviconDest = path67.join(destDir, "Icon.png");
|
|
19700
20231
|
if (!await fileExists2(faviconDest)) {
|
|
19701
20232
|
await writeFile19(faviconDest, Buffer.from(FAVICON_PNG_BASE64, "base64"));
|
|
19702
20233
|
}
|
|
19703
20234
|
}
|
|
19704
20235
|
async function ensureThinConfigTsx(dir) {
|
|
19705
|
-
const tsxPath =
|
|
20236
|
+
const tsxPath = path67.join(dir, "apitogo.config.tsx");
|
|
19706
20237
|
if (await fileExists2(tsxPath)) {
|
|
19707
20238
|
return false;
|
|
19708
20239
|
}
|
|
@@ -19711,7 +20242,7 @@ async function ensureThinConfigTsx(dir) {
|
|
|
19711
20242
|
}
|
|
19712
20243
|
|
|
19713
20244
|
// src/cli/make-config/handler.ts
|
|
19714
|
-
var toPosixPath2 = (value) => value.split(
|
|
20245
|
+
var toPosixPath2 = (value) => value.split(path68.sep).join(path68.posix.sep);
|
|
19715
20246
|
var fileExists3 = async (filePath) => {
|
|
19716
20247
|
try {
|
|
19717
20248
|
await access12(filePath, constants2.F_OK);
|
|
@@ -19721,7 +20252,7 @@ var fileExists3 = async (filePath) => {
|
|
|
19721
20252
|
}
|
|
19722
20253
|
};
|
|
19723
20254
|
var readJsonConfig = async (filePath) => {
|
|
19724
|
-
const raw = await
|
|
20255
|
+
const raw = await readFile21(filePath, "utf8");
|
|
19725
20256
|
const parsed = JSON.parse(raw);
|
|
19726
20257
|
if (!isPlainObject3(parsed)) {
|
|
19727
20258
|
throw new Error(`${APITOGO_JSON_FILENAME} must be a JSON object.`);
|
|
@@ -19769,9 +20300,9 @@ async function persistConfig(config2, ctx) {
|
|
|
19769
20300
|
};
|
|
19770
20301
|
await writeFile20(ctx.jsonPath, `${JSON.stringify(next, null, 2)}
|
|
19771
20302
|
`, "utf8");
|
|
19772
|
-
const tsxPath =
|
|
20303
|
+
const tsxPath = path68.join(ctx.dir, "apitogo.config.tsx");
|
|
19773
20304
|
if (await fileExists3(tsxPath) && tsxRoutes.length > 0) {
|
|
19774
|
-
const tsxSource = await
|
|
20305
|
+
const tsxSource = await readFile21(tsxPath, "utf8");
|
|
19775
20306
|
if (isManagedConfigWrapper(tsxSource)) {
|
|
19776
20307
|
await writeFile20(
|
|
19777
20308
|
tsxPath,
|
|
@@ -19796,7 +20327,7 @@ function mergeBackendFlag(config2, backend) {
|
|
|
19796
20327
|
return next;
|
|
19797
20328
|
}
|
|
19798
20329
|
async function makeConfig(argv) {
|
|
19799
|
-
const dir =
|
|
20330
|
+
const dir = path68.resolve(process.cwd(), argv.dir);
|
|
19800
20331
|
const pagesDir = toPosixPath2(
|
|
19801
20332
|
argv.pagesDir.replace(/^[./]+/, "").replace(/\/+$/, "")
|
|
19802
20333
|
);
|
|
@@ -19816,14 +20347,14 @@ async function makeConfig(argv) {
|
|
|
19816
20347
|
printSuccessToConsole("Created tsconfig.json.");
|
|
19817
20348
|
}
|
|
19818
20349
|
await ensurePortalStubFiles(dir);
|
|
19819
|
-
const jsonPath =
|
|
20350
|
+
const jsonPath = path68.join(dir, APITOGO_JSON_FILENAME);
|
|
19820
20351
|
let createdJson = false;
|
|
19821
20352
|
let config2;
|
|
19822
20353
|
if (await fileExists3(jsonPath)) {
|
|
19823
20354
|
config2 = await readJsonConfig(jsonPath);
|
|
19824
20355
|
} else {
|
|
19825
20356
|
config2 = createDefaultApitogoJsonConfig({
|
|
19826
|
-
title: titleFromDirectoryName(
|
|
20357
|
+
title: titleFromDirectoryName(path68.basename(dir)),
|
|
19827
20358
|
pagesDir
|
|
19828
20359
|
});
|
|
19829
20360
|
createdJson = true;
|
|
@@ -19875,8 +20406,8 @@ async function makeConfig(argv) {
|
|
|
19875
20406
|
return;
|
|
19876
20407
|
}
|
|
19877
20408
|
if (action2.kind === "page") {
|
|
19878
|
-
const pagePath =
|
|
19879
|
-
await mkdir17(
|
|
20409
|
+
const pagePath = path68.join(dir, action2.page.relativePath);
|
|
20410
|
+
await mkdir17(path68.dirname(pagePath), { recursive: true });
|
|
19880
20411
|
await writeFile20(
|
|
19881
20412
|
pagePath,
|
|
19882
20413
|
buildStarterDocsPageTsx(action2.page.slug, action2.page.title),
|
|
@@ -19943,7 +20474,7 @@ async function makeConfig(argv) {
|
|
|
19943
20474
|
if (argv.openapi) {
|
|
19944
20475
|
const wire = await wireDevPortalOpenApi(
|
|
19945
20476
|
dir,
|
|
19946
|
-
|
|
20477
|
+
path68.resolve(dir, argv.openapi)
|
|
19947
20478
|
);
|
|
19948
20479
|
if (wire.status !== "succeeded") {
|
|
19949
20480
|
throw new Error(
|
|
@@ -19972,7 +20503,7 @@ async function makeConfig(argv) {
|
|
|
19972
20503
|
const slug = normalizePageSlug2(argv.pageSlug);
|
|
19973
20504
|
const pageTemplate = normalizePageTemplate(argv.pageTemplate);
|
|
19974
20505
|
const relativePagePath = pageComponentRelativePath(slug);
|
|
19975
|
-
const filePath =
|
|
20506
|
+
const filePath = path68.join(dir, relativePagePath);
|
|
19976
20507
|
const isUpdate = argv.pageUpdate === true;
|
|
19977
20508
|
if (isUpdate) {
|
|
19978
20509
|
if (!await fileExists3(filePath)) {
|
|
@@ -20005,7 +20536,7 @@ async function makeConfig(argv) {
|
|
|
20005
20536
|
argv.pageContent,
|
|
20006
20537
|
pageTemplate
|
|
20007
20538
|
);
|
|
20008
|
-
await mkdir17(
|
|
20539
|
+
await mkdir17(path68.dirname(filePath), { recursive: true });
|
|
20009
20540
|
await writeFile20(filePath, tsx, "utf8");
|
|
20010
20541
|
const configResult = await wireNewDevPortalPageInConfig(
|
|
20011
20542
|
dir,
|
|
@@ -20018,7 +20549,7 @@ async function makeConfig(argv) {
|
|
|
20018
20549
|
pagePath: relativePagePath,
|
|
20019
20550
|
pageTemplate,
|
|
20020
20551
|
configUpdated: configResult.updated,
|
|
20021
|
-
configPath: configResult.configPath ?
|
|
20552
|
+
configPath: configResult.configPath ? path68.relative(dir, configResult.configPath) : null
|
|
20022
20553
|
};
|
|
20023
20554
|
changes.push("page_add");
|
|
20024
20555
|
}
|
|
@@ -20143,10 +20674,10 @@ var config_default = {
|
|
|
20143
20674
|
};
|
|
20144
20675
|
|
|
20145
20676
|
// src/cli/cmds/deploy.ts
|
|
20146
|
-
import
|
|
20677
|
+
import path82 from "node:path";
|
|
20147
20678
|
|
|
20148
20679
|
// src/cli/apply/handler.ts
|
|
20149
|
-
import
|
|
20680
|
+
import path79 from "node:path";
|
|
20150
20681
|
|
|
20151
20682
|
// src/cli/platform/helpers/gateway-plan-validation.ts
|
|
20152
20683
|
init_gateway_limits();
|
|
@@ -20167,15 +20698,15 @@ function buildGatewayPlansValidationRequest(plans) {
|
|
|
20167
20698
|
};
|
|
20168
20699
|
});
|
|
20169
20700
|
}
|
|
20170
|
-
function toConfigIssue(
|
|
20701
|
+
function toConfigIssue(issue3) {
|
|
20171
20702
|
return {
|
|
20172
|
-
...
|
|
20173
|
-
field:
|
|
20703
|
+
...issue3,
|
|
20704
|
+
field: issue3.field.replace(/\.limitsJson$/, ".limits")
|
|
20174
20705
|
};
|
|
20175
20706
|
}
|
|
20176
|
-
function formatGatewayPlanIssue(
|
|
20177
|
-
const sku =
|
|
20178
|
-
return `${
|
|
20707
|
+
function formatGatewayPlanIssue(issue3) {
|
|
20708
|
+
const sku = issue3.sku ? ` (${issue3.sku})` : "";
|
|
20709
|
+
return `${issue3.field}${sku}: ${issue3.message}`;
|
|
20179
20710
|
}
|
|
20180
20711
|
async function checkGatewayPlansOnPlatform(client, args) {
|
|
20181
20712
|
let result;
|
|
@@ -20198,10 +20729,10 @@ async function checkGatewayPlansOnPlatform(client, args) {
|
|
|
20198
20729
|
async function assertGatewayPlansValidForPublish(client, args) {
|
|
20199
20730
|
const localErrors = args.localErrors ?? [];
|
|
20200
20731
|
const check2 = await checkGatewayPlansOnPlatform(client, args);
|
|
20201
|
-
const localFields = new Set(localErrors.map((
|
|
20732
|
+
const localFields = new Set(localErrors.map((issue3) => issue3.field));
|
|
20202
20733
|
const errors = [
|
|
20203
20734
|
...localErrors,
|
|
20204
|
-
...(check2?.errors ?? []).filter((
|
|
20735
|
+
...(check2?.errors ?? []).filter((issue3) => !localFields.has(issue3.field))
|
|
20205
20736
|
];
|
|
20206
20737
|
if (errors.length === 0) {
|
|
20207
20738
|
return { blocked: false };
|
|
@@ -20341,11 +20872,11 @@ init_gateway_limits();
|
|
|
20341
20872
|
// src/cli/platform/helpers/deploy-artifact-hash.ts
|
|
20342
20873
|
import { createHash as createHash3 } from "node:crypto";
|
|
20343
20874
|
import { createReadStream, promises as fs8 } from "node:fs";
|
|
20344
|
-
import
|
|
20875
|
+
import path70 from "node:path";
|
|
20345
20876
|
|
|
20346
20877
|
// src/cli/platform/helpers/package-files.ts
|
|
20347
20878
|
import { promises as fs7 } from "node:fs";
|
|
20348
|
-
import
|
|
20879
|
+
import path69 from "node:path";
|
|
20349
20880
|
import ignore from "ignore";
|
|
20350
20881
|
var DEFAULT_EXCLUDES = /* @__PURE__ */ new Set([
|
|
20351
20882
|
"node_modules",
|
|
@@ -20358,7 +20889,7 @@ var DEFAULT_EXCLUDES = /* @__PURE__ */ new Set([
|
|
|
20358
20889
|
async function loadGitignore(rootDir) {
|
|
20359
20890
|
const ig = ignore();
|
|
20360
20891
|
ig.add(["node_modules/", ".git/", "dist/", "bin/", "obj/"]);
|
|
20361
|
-
const gitignorePath =
|
|
20892
|
+
const gitignorePath = path69.join(rootDir, ".gitignore");
|
|
20362
20893
|
try {
|
|
20363
20894
|
const content = await fs7.readFile(gitignorePath, "utf8");
|
|
20364
20895
|
ig.add(content);
|
|
@@ -20367,11 +20898,11 @@ async function loadGitignore(rootDir) {
|
|
|
20367
20898
|
return ig;
|
|
20368
20899
|
}
|
|
20369
20900
|
async function collectFiles(rootDir, relativeDir, ig, files) {
|
|
20370
|
-
const absDir =
|
|
20901
|
+
const absDir = path69.join(rootDir, relativeDir);
|
|
20371
20902
|
const entries = await fs7.readdir(absDir, { withFileTypes: true });
|
|
20372
20903
|
for (const entry of entries) {
|
|
20373
|
-
const rel2 = relativeDir ?
|
|
20374
|
-
const relPosix = rel2.split(
|
|
20904
|
+
const rel2 = relativeDir ? path69.join(relativeDir, entry.name) : entry.name;
|
|
20905
|
+
const relPosix = rel2.split(path69.sep).join("/");
|
|
20375
20906
|
if (DEFAULT_EXCLUDES.has(entry.name) && !relativeDir) {
|
|
20376
20907
|
continue;
|
|
20377
20908
|
}
|
|
@@ -20386,7 +20917,7 @@ async function collectFiles(rootDir, relativeDir, ig, files) {
|
|
|
20386
20917
|
}
|
|
20387
20918
|
}
|
|
20388
20919
|
async function collectPackageableFiles(sourceDirectory) {
|
|
20389
|
-
const resolved =
|
|
20920
|
+
const resolved = path69.resolve(sourceDirectory);
|
|
20390
20921
|
const stat7 = await fs7.stat(resolved).catch(() => null);
|
|
20391
20922
|
if (!stat7?.isDirectory()) {
|
|
20392
20923
|
throw new Error(`sourceDirectory is not a directory: ${resolved}`);
|
|
@@ -20412,7 +20943,7 @@ async function sha256File(filePath) {
|
|
|
20412
20943
|
return hash2.digest("hex");
|
|
20413
20944
|
}
|
|
20414
20945
|
async function hashDirectoryContents(rootDirectory) {
|
|
20415
|
-
const resolved =
|
|
20946
|
+
const resolved = path70.resolve(rootDirectory);
|
|
20416
20947
|
const files = (await collectPackageableFiles(resolved)).sort(
|
|
20417
20948
|
(a, b) => a.localeCompare(b)
|
|
20418
20949
|
);
|
|
@@ -20420,13 +20951,13 @@ async function hashDirectoryContents(rootDirectory) {
|
|
|
20420
20951
|
for (const rel2 of files) {
|
|
20421
20952
|
hash2.update(rel2);
|
|
20422
20953
|
hash2.update("\0");
|
|
20423
|
-
hash2.update(await fs8.readFile(
|
|
20954
|
+
hash2.update(await fs8.readFile(path70.join(resolved, rel2)));
|
|
20424
20955
|
hash2.update("\0");
|
|
20425
20956
|
}
|
|
20426
20957
|
return hash2.digest("hex");
|
|
20427
20958
|
}
|
|
20428
20959
|
async function hashDevPortalSiteSources(siteDirectory, options) {
|
|
20429
|
-
const resolved =
|
|
20960
|
+
const resolved = path70.resolve(siteDirectory);
|
|
20430
20961
|
const hash2 = createHash3("sha256");
|
|
20431
20962
|
const candidates = [
|
|
20432
20963
|
"apitogo.config.json",
|
|
@@ -20436,7 +20967,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
|
|
|
20436
20967
|
"zudoku.config.tsx"
|
|
20437
20968
|
];
|
|
20438
20969
|
for (const name of candidates) {
|
|
20439
|
-
const full =
|
|
20970
|
+
const full = path70.join(resolved, name);
|
|
20440
20971
|
try {
|
|
20441
20972
|
hash2.update(name);
|
|
20442
20973
|
hash2.update("\0");
|
|
@@ -20446,7 +20977,7 @@ async function hashDevPortalSiteSources(siteDirectory, options) {
|
|
|
20446
20977
|
}
|
|
20447
20978
|
}
|
|
20448
20979
|
for (const dir of ["pages", "landing", "src"]) {
|
|
20449
|
-
const full =
|
|
20980
|
+
const full = path70.join(resolved, dir);
|
|
20450
20981
|
try {
|
|
20451
20982
|
const st = await fs8.stat(full);
|
|
20452
20983
|
if (!st.isDirectory()) continue;
|
|
@@ -20675,8 +21206,8 @@ function normalizeActionKey(actionKey) {
|
|
|
20675
21206
|
const trimmed = actionKey.trim();
|
|
20676
21207
|
if (trimmed.includes(":")) {
|
|
20677
21208
|
const [method2, ...pathParts] = trimmed.split(":");
|
|
20678
|
-
const
|
|
20679
|
-
return `${method2?.toLowerCase()}:${
|
|
21209
|
+
const path101 = pathParts.join(":").trim();
|
|
21210
|
+
return `${method2?.toLowerCase()}:${path101.startsWith("/") ? path101 : `/${path101}`}`;
|
|
20680
21211
|
}
|
|
20681
21212
|
const spaceIndex = trimmed.indexOf(" ");
|
|
20682
21213
|
if (spaceIndex <= 0) {
|
|
@@ -20685,8 +21216,8 @@ function normalizeActionKey(actionKey) {
|
|
|
20685
21216
|
);
|
|
20686
21217
|
}
|
|
20687
21218
|
const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
|
|
20688
|
-
const
|
|
20689
|
-
return `${method}:${
|
|
21219
|
+
const path100 = trimmed.slice(spaceIndex + 1).trim();
|
|
21220
|
+
return `${method}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
|
|
20690
21221
|
}
|
|
20691
21222
|
async function applyGatewayPublicRoutes(client, projectId, routes, options) {
|
|
20692
21223
|
const applied = [];
|
|
@@ -21164,8 +21695,8 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
|
|
|
21164
21695
|
}
|
|
21165
21696
|
|
|
21166
21697
|
// src/cli/platform/helpers/openapi-gateway-servers.ts
|
|
21167
|
-
import { mkdir as mkdir18, readFile as
|
|
21168
|
-
import
|
|
21698
|
+
import { mkdir as mkdir18, readFile as readFile22, writeFile as writeFile21 } from "node:fs/promises";
|
|
21699
|
+
import path71 from "node:path";
|
|
21169
21700
|
import YAML3 from "yaml";
|
|
21170
21701
|
var GATEWAY_URL_ENV_KEY = "APITOGO_GATEWAY_URL";
|
|
21171
21702
|
var PUBLISH_OPENAPI_RELATIVE = ".apitogo/openapi-publish.json";
|
|
@@ -21196,8 +21727,8 @@ async function resolveProjectGatewayUrl(client, projectId) {
|
|
|
21196
21727
|
return null;
|
|
21197
21728
|
}
|
|
21198
21729
|
async function readOpenApiDocument(filePath) {
|
|
21199
|
-
const content = await
|
|
21200
|
-
const ext =
|
|
21730
|
+
const content = await readFile22(filePath, "utf8");
|
|
21731
|
+
const ext = path71.extname(filePath).toLowerCase();
|
|
21201
21732
|
const parsed = ext === ".json" ? JSON.parse(content) : YAML3.parse(content);
|
|
21202
21733
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
21203
21734
|
throw new Error(`OpenAPI document must be an object: ${filePath}`);
|
|
@@ -21205,13 +21736,13 @@ async function readOpenApiDocument(filePath) {
|
|
|
21205
21736
|
return parsed;
|
|
21206
21737
|
}
|
|
21207
21738
|
async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApiPath, gatewayUrl) {
|
|
21208
|
-
const site =
|
|
21209
|
-
const source =
|
|
21739
|
+
const site = path71.resolve(siteDirectory);
|
|
21740
|
+
const source = path71.resolve(sourceOpenApiPath);
|
|
21210
21741
|
const relativeInput = PUBLISH_OPENAPI_RELATIVE;
|
|
21211
|
-
const outputPath =
|
|
21742
|
+
const outputPath = path71.join(site, ...relativeInput.split("/"));
|
|
21212
21743
|
const doc = await readOpenApiDocument(source);
|
|
21213
21744
|
const rewritten = withGatewayServers(doc, gatewayUrl);
|
|
21214
|
-
await mkdir18(
|
|
21745
|
+
await mkdir18(path71.dirname(outputPath), { recursive: true });
|
|
21215
21746
|
await writeFile21(
|
|
21216
21747
|
outputPath,
|
|
21217
21748
|
`${JSON.stringify(rewritten, null, 2)}
|
|
@@ -21221,11 +21752,11 @@ async function materializePublishOpenApiWithGateway(siteDirectory, sourceOpenApi
|
|
|
21221
21752
|
return { outputPath, relativeInput };
|
|
21222
21753
|
}
|
|
21223
21754
|
async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
|
|
21224
|
-
const configPath =
|
|
21225
|
-
|
|
21755
|
+
const configPath = path71.join(
|
|
21756
|
+
path71.resolve(siteDirectory),
|
|
21226
21757
|
APITOGO_CONFIG_FILENAME2
|
|
21227
21758
|
);
|
|
21228
|
-
const raw = await
|
|
21759
|
+
const raw = await readFile22(configPath, "utf8");
|
|
21229
21760
|
const config2 = JSON.parse(raw);
|
|
21230
21761
|
const previousApis = config2.apis;
|
|
21231
21762
|
const previousBackend = config2.backend;
|
|
@@ -21242,7 +21773,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
|
|
|
21242
21773
|
`, "utf8");
|
|
21243
21774
|
return {
|
|
21244
21775
|
restore: async () => {
|
|
21245
|
-
const currentRaw = await
|
|
21776
|
+
const currentRaw = await readFile22(configPath, "utf8");
|
|
21246
21777
|
const current = JSON.parse(currentRaw);
|
|
21247
21778
|
if (previousApis === void 0) {
|
|
21248
21779
|
delete current.apis;
|
|
@@ -21264,7 +21795,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
|
|
|
21264
21795
|
};
|
|
21265
21796
|
}
|
|
21266
21797
|
async function prepareOpenApiServersForGatewayBuild(options) {
|
|
21267
|
-
const siteDirectory =
|
|
21798
|
+
const siteDirectory = path71.resolve(options.siteDirectory);
|
|
21268
21799
|
let openApiPath = resolveBackendOpenApiPath(
|
|
21269
21800
|
options.manifest?.backend?.openApiPath,
|
|
21270
21801
|
{
|
|
@@ -21277,8 +21808,8 @@ async function prepareOpenApiServersForGatewayBuild(options) {
|
|
|
21277
21808
|
);
|
|
21278
21809
|
if (!openApiPath) {
|
|
21279
21810
|
try {
|
|
21280
|
-
const raw = await
|
|
21281
|
-
|
|
21811
|
+
const raw = await readFile22(
|
|
21812
|
+
path71.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
|
|
21282
21813
|
"utf8"
|
|
21283
21814
|
);
|
|
21284
21815
|
const config2 = JSON.parse(raw);
|
|
@@ -21307,8 +21838,8 @@ function normalizeActionKeyFormat(actionKey) {
|
|
|
21307
21838
|
const trimmed = actionKey.trim();
|
|
21308
21839
|
if (trimmed.includes(":")) {
|
|
21309
21840
|
const [method2, ...pathParts] = trimmed.split(":");
|
|
21310
|
-
const
|
|
21311
|
-
return `${method2.toLowerCase()}:${
|
|
21841
|
+
const path101 = pathParts.join(":").trim();
|
|
21842
|
+
return `${method2.toLowerCase()}:${path101.startsWith("/") ? path101 : `/${path101}`}`;
|
|
21312
21843
|
}
|
|
21313
21844
|
const spaceIndex = trimmed.indexOf(" ");
|
|
21314
21845
|
if (spaceIndex <= 0) {
|
|
@@ -21317,8 +21848,8 @@ function normalizeActionKeyFormat(actionKey) {
|
|
|
21317
21848
|
);
|
|
21318
21849
|
}
|
|
21319
21850
|
const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
|
|
21320
|
-
const
|
|
21321
|
-
return `${method}:${
|
|
21851
|
+
const path100 = trimmed.slice(spaceIndex + 1).trim();
|
|
21852
|
+
return `${method}:${path100.startsWith("/") ? path100 : `/${path100}`}`;
|
|
21322
21853
|
}
|
|
21323
21854
|
function normalizePublicRoute(route) {
|
|
21324
21855
|
const trimmed = route.trim();
|
|
@@ -21351,16 +21882,16 @@ function collectManifestPublicActionKeys(gateway) {
|
|
|
21351
21882
|
}
|
|
21352
21883
|
|
|
21353
21884
|
// src/cli/platform/helpers/publish-ledger.ts
|
|
21354
|
-
import { mkdir as mkdir19, readFile as
|
|
21355
|
-
import
|
|
21885
|
+
import { mkdir as mkdir19, readFile as readFile23, writeFile as writeFile22 } from "node:fs/promises";
|
|
21886
|
+
import path72 from "node:path";
|
|
21356
21887
|
var PUBLISH_LEDGER_RELATIVE_PATH = ".apitogo/publish-state.json";
|
|
21357
21888
|
function publishLedgerPath(siteDirectory) {
|
|
21358
|
-
return
|
|
21889
|
+
return path72.join(path72.resolve(siteDirectory), PUBLISH_LEDGER_RELATIVE_PATH);
|
|
21359
21890
|
}
|
|
21360
21891
|
async function readPublishLedger(siteDirectory) {
|
|
21361
21892
|
const filePath = publishLedgerPath(siteDirectory);
|
|
21362
21893
|
try {
|
|
21363
|
-
const raw = await
|
|
21894
|
+
const raw = await readFile23(filePath, "utf8");
|
|
21364
21895
|
const parsed = JSON.parse(raw);
|
|
21365
21896
|
if (parsed?.version !== 1 || !Array.isArray(parsed.steps)) {
|
|
21366
21897
|
return null;
|
|
@@ -21372,7 +21903,7 @@ async function readPublishLedger(siteDirectory) {
|
|
|
21372
21903
|
}
|
|
21373
21904
|
async function writePublishLedger(siteDirectory, ledger) {
|
|
21374
21905
|
const filePath = publishLedgerPath(siteDirectory);
|
|
21375
|
-
await mkdir19(
|
|
21906
|
+
await mkdir19(path72.dirname(filePath), { recursive: true });
|
|
21376
21907
|
const next = {
|
|
21377
21908
|
...ledger,
|
|
21378
21909
|
version: 1,
|
|
@@ -21891,10 +22422,10 @@ async function createProjectWithWarmPoolRetry(client, body, options) {
|
|
|
21891
22422
|
}
|
|
21892
22423
|
|
|
21893
22424
|
// src/cli/platform/helpers/backend-packaging.ts
|
|
21894
|
-
import { spawn as
|
|
22425
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
21895
22426
|
import { mkdtemp, readdir as readdir2, rm as rm11 } from "node:fs/promises";
|
|
21896
22427
|
import { tmpdir } from "node:os";
|
|
21897
|
-
import
|
|
22428
|
+
import path73 from "node:path";
|
|
21898
22429
|
async function directoryHasRuntimeConfig(dir) {
|
|
21899
22430
|
if (!await pathExists(dir)) {
|
|
21900
22431
|
return false;
|
|
@@ -21904,7 +22435,7 @@ async function directoryHasRuntimeConfig(dir) {
|
|
|
21904
22435
|
}
|
|
21905
22436
|
function runDotnetPublish(projectDir, outputDir) {
|
|
21906
22437
|
return new Promise((resolve, reject) => {
|
|
21907
|
-
const child =
|
|
22438
|
+
const child = spawn4(
|
|
21908
22439
|
"dotnet",
|
|
21909
22440
|
[
|
|
21910
22441
|
"publish",
|
|
@@ -21937,20 +22468,20 @@ function runDotnetPublish(projectDir, outputDir) {
|
|
|
21937
22468
|
async function findCsproj(projectDir) {
|
|
21938
22469
|
const entries = await readdir2(projectDir);
|
|
21939
22470
|
const csproj = entries.find((name) => name.endsWith(".csproj"));
|
|
21940
|
-
return csproj ?
|
|
22471
|
+
return csproj ? path73.join(projectDir, csproj) : null;
|
|
21941
22472
|
}
|
|
21942
22473
|
async function prepareBackendPackagingDirectory(args) {
|
|
21943
|
-
const sourceDirectory =
|
|
22474
|
+
const sourceDirectory = path73.resolve(args.sourceDirectory);
|
|
21944
22475
|
if (args.publishDirectory?.trim()) {
|
|
21945
22476
|
return {
|
|
21946
|
-
directory:
|
|
22477
|
+
directory: path73.resolve(args.publishDirectory),
|
|
21947
22478
|
packagingMode: "published"
|
|
21948
22479
|
};
|
|
21949
22480
|
}
|
|
21950
22481
|
if (await directoryHasRuntimeConfig(sourceDirectory)) {
|
|
21951
22482
|
return { directory: sourceDirectory, packagingMode: "published" };
|
|
21952
22483
|
}
|
|
21953
|
-
const publishSubdir =
|
|
22484
|
+
const publishSubdir = path73.join(sourceDirectory, "publish");
|
|
21954
22485
|
if (await directoryHasRuntimeConfig(publishSubdir)) {
|
|
21955
22486
|
return { directory: publishSubdir, packagingMode: "published" };
|
|
21956
22487
|
}
|
|
@@ -21962,9 +22493,9 @@ async function prepareBackendPackagingDirectory(args) {
|
|
|
21962
22493
|
return { directory: sourceDirectory, packagingMode: "source" };
|
|
21963
22494
|
}
|
|
21964
22495
|
const tempRoot = await mkdtemp(
|
|
21965
|
-
|
|
22496
|
+
path73.join(tmpdir(), "apitogo-dotnet-publish-")
|
|
21966
22497
|
);
|
|
21967
|
-
const outputDir =
|
|
22498
|
+
const outputDir = path73.join(tempRoot, "publish");
|
|
21968
22499
|
const result = await runDotnetPublish(sourceDirectory, outputDir);
|
|
21969
22500
|
if (result.exitCode !== 0) {
|
|
21970
22501
|
await rm11(tempRoot, { recursive: true, force: true });
|
|
@@ -22010,12 +22541,12 @@ function formatJobStatusResponse(options) {
|
|
|
22010
22541
|
// src/cli/platform/helpers/zip.ts
|
|
22011
22542
|
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, promises as fs9 } from "node:fs";
|
|
22012
22543
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
22013
|
-
import
|
|
22544
|
+
import path74 from "node:path";
|
|
22014
22545
|
import { ZipArchive } from "archiver";
|
|
22015
22546
|
async function packageBackendDirectory(sourceDirectory) {
|
|
22016
|
-
const resolved =
|
|
22547
|
+
const resolved = path74.resolve(sourceDirectory);
|
|
22017
22548
|
const files = await collectPackageableFiles(resolved);
|
|
22018
|
-
const zipPath =
|
|
22549
|
+
const zipPath = path74.join(
|
|
22019
22550
|
tmpdir2(),
|
|
22020
22551
|
`apitogo-backend-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`
|
|
22021
22552
|
);
|
|
@@ -22027,8 +22558,8 @@ async function packageBackendDirectory(sourceDirectory) {
|
|
|
22027
22558
|
output4.on("error", reject);
|
|
22028
22559
|
archive.pipe(output4);
|
|
22029
22560
|
for (const rel2 of files) {
|
|
22030
|
-
archive.append(createReadStream2(
|
|
22031
|
-
name: rel2.split(
|
|
22561
|
+
archive.append(createReadStream2(path74.join(resolved, rel2)), {
|
|
22562
|
+
name: rel2.split(path74.sep).join("/")
|
|
22032
22563
|
});
|
|
22033
22564
|
}
|
|
22034
22565
|
void archive.finalize();
|
|
@@ -23529,11 +24060,11 @@ async function resolveProjectIdForPublish(client, config2, args, wait, progress,
|
|
|
23529
24060
|
}
|
|
23530
24061
|
|
|
23531
24062
|
// src/cli/platform/tools/dev-portal-site-build.ts
|
|
23532
|
-
import
|
|
24063
|
+
import path78 from "node:path";
|
|
23533
24064
|
|
|
23534
24065
|
// src/cli/platform/helpers/dev-portal-site-api-url.ts
|
|
23535
|
-
import { readdir as readdir3, readFile as
|
|
23536
|
-
import
|
|
24066
|
+
import { readdir as readdir3, readFile as readFile24, writeFile as writeFile23 } from "node:fs/promises";
|
|
24067
|
+
import path75 from "node:path";
|
|
23537
24068
|
var SITE_API_BASE_URL_ENV_KEY = "VITE_APITOGO_AUTHENTICATION_API_BASE_URL";
|
|
23538
24069
|
var PLACEHOLDER_HOST_MARKERS = [
|
|
23539
24070
|
"dp-example.azurecontainerapps.io",
|
|
@@ -23554,10 +24085,10 @@ function isPlaceholderDevPortalApiUrl(apiBaseUrl) {
|
|
|
23554
24085
|
return PLACEHOLDER_HOST_MARKERS.some((marker) => normalized.includes(marker));
|
|
23555
24086
|
}
|
|
23556
24087
|
async function readSiteApiBaseUrl(sourceDirectory) {
|
|
23557
|
-
const resolved =
|
|
24088
|
+
const resolved = path75.resolve(sourceDirectory);
|
|
23558
24089
|
for (const envFile of [".env", ".env.production"]) {
|
|
23559
24090
|
try {
|
|
23560
|
-
const content = await
|
|
24091
|
+
const content = await readFile24(path75.join(resolved, envFile), "utf8");
|
|
23561
24092
|
const lines = [...content.matchAll(ENV_LINE_GLOBAL_RE)];
|
|
23562
24093
|
if (lines.length === 0) {
|
|
23563
24094
|
continue;
|
|
@@ -23582,13 +24113,13 @@ async function readSiteApiBaseUrl(sourceDirectory) {
|
|
|
23582
24113
|
return null;
|
|
23583
24114
|
}
|
|
23584
24115
|
async function patchSiteApiBaseUrl(sourceDirectory, apiBaseUrl) {
|
|
23585
|
-
const resolved =
|
|
24116
|
+
const resolved = path75.resolve(sourceDirectory);
|
|
23586
24117
|
const line2 = `${SITE_API_BASE_URL_ENV_KEY}=${apiBaseUrl}`;
|
|
23587
24118
|
let patchedExisting = false;
|
|
23588
24119
|
for (const envFile of [".env.production", ".env"]) {
|
|
23589
|
-
const envPath =
|
|
24120
|
+
const envPath = path75.join(resolved, envFile);
|
|
23590
24121
|
try {
|
|
23591
|
-
const content = await
|
|
24122
|
+
const content = await readFile24(envPath, "utf8");
|
|
23592
24123
|
const withoutAssignments = content.replace(ENV_LINE_GLOBAL_RE, "").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
23593
24124
|
const patched = withoutAssignments ? `${withoutAssignments}
|
|
23594
24125
|
${line2}
|
|
@@ -23600,7 +24131,7 @@ ${line2}
|
|
|
23600
24131
|
}
|
|
23601
24132
|
}
|
|
23602
24133
|
if (!patchedExisting) {
|
|
23603
|
-
await writeFile23(
|
|
24134
|
+
await writeFile23(path75.join(resolved, ".env"), `${line2}
|
|
23604
24135
|
`);
|
|
23605
24136
|
}
|
|
23606
24137
|
}
|
|
@@ -23608,7 +24139,7 @@ function tryProjectId(explicit, manifestProjectId, config2) {
|
|
|
23608
24139
|
return explicit?.trim() || manifestProjectId?.trim() || config2.defaultProjectId?.trim() || void 0;
|
|
23609
24140
|
}
|
|
23610
24141
|
async function ensureLiveSiteApiBaseUrlForBuild(_client, config2, args, resolveFromProject) {
|
|
23611
|
-
const sourceDirectory =
|
|
24142
|
+
const sourceDirectory = path75.resolve(args.sourceDirectory);
|
|
23612
24143
|
const explicit = args.apiBaseUrl?.trim();
|
|
23613
24144
|
if (explicit && !isPlaceholderDevPortalApiUrl(explicit)) {
|
|
23614
24145
|
await patchSiteApiBaseUrl(sourceDirectory, explicit);
|
|
@@ -23644,7 +24175,7 @@ async function* walkFiles(root) {
|
|
|
23644
24175
|
return;
|
|
23645
24176
|
}
|
|
23646
24177
|
for (const entry of entries) {
|
|
23647
|
-
const full =
|
|
24178
|
+
const full = path75.join(root, entry.name);
|
|
23648
24179
|
if (entry.isDirectory()) {
|
|
23649
24180
|
if (entry.name === "node_modules" || entry.name === ".git") {
|
|
23650
24181
|
continue;
|
|
@@ -23656,8 +24187,8 @@ async function* walkFiles(root) {
|
|
|
23656
24187
|
}
|
|
23657
24188
|
}
|
|
23658
24189
|
async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiBaseUrl) {
|
|
23659
|
-
const dist =
|
|
23660
|
-
const siteRoot =
|
|
24190
|
+
const dist = path75.resolve(distDirectory);
|
|
24191
|
+
const siteRoot = path75.dirname(dist);
|
|
23661
24192
|
const fromEnv = expectedApiBaseUrl?.trim() || await readSiteApiBaseUrl(siteRoot) || await readSiteApiBaseUrl(dist);
|
|
23662
24193
|
if (!fromEnv || isPlaceholderDevPortalApiUrl(fromEnv)) {
|
|
23663
24194
|
throw new Error(
|
|
@@ -23668,7 +24199,7 @@ async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiB
|
|
|
23668
24199
|
let found = false;
|
|
23669
24200
|
let foundPlaceholder = false;
|
|
23670
24201
|
for await (const file of walkFiles(dist)) {
|
|
23671
|
-
const content = await
|
|
24202
|
+
const content = await readFile24(file, "utf8");
|
|
23672
24203
|
if (content.includes(needle)) {
|
|
23673
24204
|
found = true;
|
|
23674
24205
|
}
|
|
@@ -23693,7 +24224,7 @@ async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiB
|
|
|
23693
24224
|
|
|
23694
24225
|
// src/cli/platform/tools/dev-portal.ts
|
|
23695
24226
|
import { promises as fs11 } from "node:fs";
|
|
23696
|
-
import
|
|
24227
|
+
import path77 from "node:path";
|
|
23697
24228
|
|
|
23698
24229
|
// src/cli/platform/helpers/dev-portal-backend-job.ts
|
|
23699
24230
|
var DEV_PORTAL_BACKEND_UPDATE_RESOURCE_TYPE = "DevPortalBackendUpdate";
|
|
@@ -23803,7 +24334,7 @@ async function snapshotBackendUpdateJobIds(client, projectId) {
|
|
|
23803
24334
|
// src/cli/platform/helpers/tarball.ts
|
|
23804
24335
|
import { createReadStream as createReadStream3, createWriteStream as createWriteStream3, promises as fs10 } from "node:fs";
|
|
23805
24336
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
23806
|
-
import
|
|
24337
|
+
import path76 from "node:path";
|
|
23807
24338
|
import { TarArchive } from "archiver";
|
|
23808
24339
|
var DEV_PORTAL_DEPLOY_ARCHIVE_ROOT = "dist";
|
|
23809
24340
|
function normalizeArchiveRoot(archiveRoot) {
|
|
@@ -23814,10 +24345,10 @@ function normalizeArchiveRoot(archiveRoot) {
|
|
|
23814
24345
|
return `${trimmed}/`;
|
|
23815
24346
|
}
|
|
23816
24347
|
async function packageDirectoryAsTarGz(sourceDirectory, options) {
|
|
23817
|
-
const resolved =
|
|
24348
|
+
const resolved = path76.resolve(sourceDirectory);
|
|
23818
24349
|
const files = await collectPackageableFiles(resolved);
|
|
23819
24350
|
const entryPrefix = options?.archiveRoot ? normalizeArchiveRoot(options.archiveRoot) : "";
|
|
23820
|
-
const archivePath =
|
|
24351
|
+
const archivePath = path76.join(
|
|
23821
24352
|
tmpdir3(),
|
|
23822
24353
|
`apitogo-docs-${Date.now()}-${Math.random().toString(36).slice(2)}.tgz`
|
|
23823
24354
|
);
|
|
@@ -23829,8 +24360,8 @@ async function packageDirectoryAsTarGz(sourceDirectory, options) {
|
|
|
23829
24360
|
output4.on("error", reject);
|
|
23830
24361
|
archive.pipe(output4);
|
|
23831
24362
|
for (const rel2 of files) {
|
|
23832
|
-
const entryName = entryPrefix + rel2.split(
|
|
23833
|
-
archive.append(createReadStream3(
|
|
24363
|
+
const entryName = entryPrefix + rel2.split(path76.sep).join("/");
|
|
24364
|
+
archive.append(createReadStream3(path76.join(resolved, rel2)), {
|
|
23834
24365
|
name: entryName
|
|
23835
24366
|
});
|
|
23836
24367
|
}
|
|
@@ -24072,7 +24603,7 @@ function validateDeployArchiveInput(args) {
|
|
|
24072
24603
|
async function resolveDevPortalArchive(args) {
|
|
24073
24604
|
validateDeployArchiveInput(args);
|
|
24074
24605
|
if (args.archivePath?.trim()) {
|
|
24075
|
-
const archivePath =
|
|
24606
|
+
const archivePath = path77.resolve(args.archivePath.trim());
|
|
24076
24607
|
const stat7 = await fs11.stat(archivePath).catch(() => null);
|
|
24077
24608
|
if (!stat7?.isFile()) {
|
|
24078
24609
|
throw new Error(`archivePath is not a file: ${archivePath}`);
|
|
@@ -24137,7 +24668,7 @@ async function handleDeployDevPortal(client, config2, args, progress) {
|
|
|
24137
24668
|
// src/cli/platform/tools/dev-portal-site-build.ts
|
|
24138
24669
|
async function handleBuildDevPortalSite(client, config2, args, progress) {
|
|
24139
24670
|
const buildProgress = progress?.child("Build dev portal site");
|
|
24140
|
-
const sourceDirectory =
|
|
24671
|
+
const sourceDirectory = path78.resolve(args.sourceDirectory);
|
|
24141
24672
|
await buildProgress?.onStep("Ensuring live portal API URL");
|
|
24142
24673
|
const apiBaseUrl = await ensureLiveSiteApiBaseUrlForBuild(
|
|
24143
24674
|
client,
|
|
@@ -24353,7 +24884,7 @@ function throwOnManifestIssues(issues) {
|
|
|
24353
24884
|
return;
|
|
24354
24885
|
}
|
|
24355
24886
|
throw new Error(
|
|
24356
|
-
`apitogo.config.json is not platform-compatible: ${issues.map((
|
|
24887
|
+
`apitogo.config.json is not platform-compatible: ${issues.map((issue3) => `${issue3.field}: ${issue3.message}`).join("; ")}`
|
|
24357
24888
|
);
|
|
24358
24889
|
}
|
|
24359
24890
|
async function runApplyFromSiteDirectory(client, config2, args, progress) {
|
|
@@ -25371,14 +25902,14 @@ function formatBlockedOrFailedResultForHuman(parsed) {
|
|
|
25371
25902
|
const lines = [
|
|
25372
25903
|
typeof parsed.userMessage === "string" ? parsed.userMessage : "Deploy is blocked: apitogo.config.json has errors."
|
|
25373
25904
|
];
|
|
25374
|
-
for (const
|
|
25375
|
-
lines.push(` - ${formatGatewayPlanIssue(
|
|
25905
|
+
for (const issue3 of Array.isArray(parsed.errors) ? parsed.errors : []) {
|
|
25906
|
+
lines.push(` - ${formatGatewayPlanIssue(issue3)}`);
|
|
25376
25907
|
}
|
|
25377
25908
|
const warnings = Array.isArray(parsed.warnings) ? parsed.warnings : [];
|
|
25378
25909
|
if (warnings.length > 0) {
|
|
25379
25910
|
lines.push("", "Warnings:");
|
|
25380
|
-
for (const
|
|
25381
|
-
lines.push(` - ${formatGatewayPlanIssue(
|
|
25911
|
+
for (const issue3 of warnings) {
|
|
25912
|
+
lines.push(` - ${formatGatewayPlanIssue(issue3)}`);
|
|
25382
25913
|
}
|
|
25383
25914
|
}
|
|
25384
25915
|
lines.push(
|
|
@@ -25633,7 +26164,7 @@ function resultLooksFailed(payload) {
|
|
|
25633
26164
|
return parsed.status === "failed" || parsed.status === "blocked";
|
|
25634
26165
|
}
|
|
25635
26166
|
async function apply(argv) {
|
|
25636
|
-
const sourceDirectory =
|
|
26167
|
+
const sourceDirectory = path79.resolve(process.cwd(), argv.dir);
|
|
25637
26168
|
let config2;
|
|
25638
26169
|
try {
|
|
25639
26170
|
config2 = loadConfig();
|
|
@@ -25703,7 +26234,7 @@ async function apply(argv) {
|
|
|
25703
26234
|
if (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_not_found") {
|
|
25704
26235
|
const resolution = await resolveProjectNotFoundInteractively(
|
|
25705
26236
|
parsed,
|
|
25706
|
-
|
|
26237
|
+
path79.basename(sourceDirectory)
|
|
25707
26238
|
);
|
|
25708
26239
|
if (resolution) {
|
|
25709
26240
|
effectiveProjectId = void 0;
|
|
@@ -25723,7 +26254,7 @@ async function apply(argv) {
|
|
|
25723
26254
|
while (canPromptInteractively && parsed?.status === "blocked" && parsed.reason === "project_name_exists") {
|
|
25724
26255
|
const resolution = await resolveProjectNameExistsInteractively(
|
|
25725
26256
|
parsed,
|
|
25726
|
-
effectiveProjectName ??
|
|
26257
|
+
effectiveProjectName ?? path79.basename(sourceDirectory)
|
|
25727
26258
|
);
|
|
25728
26259
|
if (!resolution) {
|
|
25729
26260
|
break;
|
|
@@ -25795,7 +26326,7 @@ async function apply(argv) {
|
|
|
25795
26326
|
|
|
25796
26327
|
// src/cli/deploy/command.ts
|
|
25797
26328
|
init_invariant();
|
|
25798
|
-
import
|
|
26329
|
+
import path81 from "node:path";
|
|
25799
26330
|
|
|
25800
26331
|
// src/cli/deploy/v2.ts
|
|
25801
26332
|
import { rm as rm12 } from "node:fs/promises";
|
|
@@ -25803,7 +26334,245 @@ init_invariant();
|
|
|
25803
26334
|
|
|
25804
26335
|
// src/cli/portal/report.ts
|
|
25805
26336
|
import { access as access13 } from "node:fs/promises";
|
|
25806
|
-
import
|
|
26337
|
+
import path80 from "node:path";
|
|
26338
|
+
|
|
26339
|
+
// src/cli/deploy/gateway.ts
|
|
26340
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
26341
|
+
import { readFile as readFile25 } from "node:fs/promises";
|
|
26342
|
+
init_invariant();
|
|
26343
|
+
var GatewayDeployError = class extends ApitogoError {
|
|
26344
|
+
constructor(code, message, developerHint) {
|
|
26345
|
+
super(message, { developerHint, title: "Cannot deploy the gateway" });
|
|
26346
|
+
this.code = code;
|
|
26347
|
+
this.name = "GatewayDeployError";
|
|
26348
|
+
}
|
|
26349
|
+
};
|
|
26350
|
+
var sha256 = (text) => createHash4("sha256").update(text).digest("hex");
|
|
26351
|
+
function gatewayNotDeployableReason(gateway) {
|
|
26352
|
+
if (gateway.origin.mode === "managed")
|
|
26353
|
+
return "a managed origin cannot be deployed from this CLI version yet; use origin.mode proxy";
|
|
26354
|
+
if (gateway.specs.length > 1)
|
|
26355
|
+
return "the platform takes one OpenAPI file per gateway, but the config lists several";
|
|
26356
|
+
return void 0;
|
|
26357
|
+
}
|
|
26358
|
+
function assertDeployable(gateway) {
|
|
26359
|
+
if (gateway.origin.mode === "managed")
|
|
26360
|
+
throw new GatewayDeployError(
|
|
26361
|
+
"GATEWAY_MANAGED_ORIGIN_UNSUPPORTED",
|
|
26362
|
+
"A managed origin cannot be deployed from this CLI version yet.",
|
|
26363
|
+
'Set gateway.origin.mode to "proxy" and point gateway.origin.url at your API.'
|
|
26364
|
+
);
|
|
26365
|
+
if (gateway.specs.length > 1)
|
|
26366
|
+
throw new GatewayDeployError(
|
|
26367
|
+
"GATEWAY_MULTIPLE_SPECS",
|
|
26368
|
+
"The platform takes one OpenAPI file per gateway, but gateway.specs lists several.",
|
|
26369
|
+
"Merge them into one file, or list only one."
|
|
26370
|
+
);
|
|
26371
|
+
}
|
|
26372
|
+
async function readSpec(gateway) {
|
|
26373
|
+
const file = gateway.specs[0];
|
|
26374
|
+
if (!file)
|
|
26375
|
+
throw new GatewayDeployError(
|
|
26376
|
+
"GATEWAY_SPEC_UNREADABLE",
|
|
26377
|
+
"gateway.specs lists no OpenAPI file.",
|
|
26378
|
+
"Add one to gateway.specs in the config."
|
|
26379
|
+
);
|
|
26380
|
+
try {
|
|
26381
|
+
return await readFile25(file);
|
|
26382
|
+
} catch {
|
|
26383
|
+
throw new GatewayDeployError(
|
|
26384
|
+
"GATEWAY_SPEC_UNREADABLE",
|
|
26385
|
+
`Cannot read the OpenAPI file ${file}.`,
|
|
26386
|
+
"Check gateway.specs in the config."
|
|
26387
|
+
);
|
|
26388
|
+
}
|
|
26389
|
+
}
|
|
26390
|
+
async function gatewayDeployInputs(resolved, projectId) {
|
|
26391
|
+
const gateway = resolved.gateway;
|
|
26392
|
+
if (!gateway) throw new Error("gateway is not configured");
|
|
26393
|
+
const spec = await readSpec(gateway);
|
|
26394
|
+
const manifest = await readManifest(resolved.state);
|
|
26395
|
+
const body = gatewayConfigBody(
|
|
26396
|
+
gateway,
|
|
26397
|
+
derivedCorsSites({
|
|
26398
|
+
portalUrl: manifest?.services.portal?.url,
|
|
26399
|
+
docsUrl: resolved.portal?.docs.provider === "external" ? resolved.portal.docs.url : void 0
|
|
26400
|
+
})
|
|
26401
|
+
);
|
|
26402
|
+
return {
|
|
26403
|
+
gateway,
|
|
26404
|
+
spec,
|
|
26405
|
+
body,
|
|
26406
|
+
specHash: sha256(`${projectId}|${sha256(spec)}`),
|
|
26407
|
+
configHash: sha256(`${projectId}|${stableJson(body)}`)
|
|
26408
|
+
};
|
|
26409
|
+
}
|
|
26410
|
+
var defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
26411
|
+
function describePlatformProblem(error) {
|
|
26412
|
+
if (!(error instanceof PlatformApiError) || !error.problem) return [];
|
|
26413
|
+
const errors = error.problem.errors;
|
|
26414
|
+
if (Array.isArray(errors) && errors.length > 0) {
|
|
26415
|
+
return errors.map((item) => {
|
|
26416
|
+
const entry = item;
|
|
26417
|
+
return `${String(entry.field ?? "gateway")}: ${String(entry.message ?? "")} ${String(entry.hint ?? "")} [${String(entry.code ?? "")}]`.replace(
|
|
26418
|
+
/\s+/g,
|
|
26419
|
+
" "
|
|
26420
|
+
);
|
|
26421
|
+
});
|
|
26422
|
+
}
|
|
26423
|
+
const { code, field, hint, detail } = error.problem;
|
|
26424
|
+
return code ? [
|
|
26425
|
+
`${String(field ?? "gateway")}: ${String(detail ?? "")} ${String(hint ?? "")} [${String(code)}]`.replace(
|
|
26426
|
+
/\s+/g,
|
|
26427
|
+
" "
|
|
26428
|
+
)
|
|
26429
|
+
] : [];
|
|
26430
|
+
}
|
|
26431
|
+
function rejected(what, error) {
|
|
26432
|
+
if (error instanceof PlatformApiError && error.status === 400) {
|
|
26433
|
+
const lines = describePlatformProblem(error);
|
|
26434
|
+
throw new GatewayDeployError(
|
|
26435
|
+
"GATEWAY_REJECTED",
|
|
26436
|
+
`The platform refused the ${what}:
|
|
26437
|
+
${lines.join("\n ") || error.message}`,
|
|
26438
|
+
"Fix the listed problems in apitogo.config.json and run deploy again."
|
|
26439
|
+
);
|
|
26440
|
+
}
|
|
26441
|
+
throw error;
|
|
26442
|
+
}
|
|
26443
|
+
async function deployGateway(resolved, projectId, deps, options = {}) {
|
|
26444
|
+
const { gateway, spec, body, specHash, configHash } = await gatewayDeployInputs(resolved, projectId);
|
|
26445
|
+
assertDeployable(gateway);
|
|
26446
|
+
const client = deps.client;
|
|
26447
|
+
const sleep5 = deps.sleep ?? defaultSleep;
|
|
26448
|
+
const now = deps.now ?? Date.now;
|
|
26449
|
+
const timeoutMs = deps.reconcileTimeoutMs ?? 18e4;
|
|
26450
|
+
const intervalMs = deps.pollIntervalMs ?? 2e3;
|
|
26451
|
+
const specStep = {
|
|
26452
|
+
name: "spec",
|
|
26453
|
+
hash: () => specHash,
|
|
26454
|
+
run: async () => {
|
|
26455
|
+
try {
|
|
26456
|
+
const uploaded = await client.putGatewaySpec(projectId, spec);
|
|
26457
|
+
return {
|
|
26458
|
+
specSha256: uploaded.sha256,
|
|
26459
|
+
operationCount: String(uploaded.operationCount)
|
|
26460
|
+
};
|
|
26461
|
+
} catch (error) {
|
|
26462
|
+
return rejected("OpenAPI file", error);
|
|
26463
|
+
}
|
|
26464
|
+
}
|
|
26465
|
+
};
|
|
26466
|
+
const ticketOf = async () => stepOf(await readLedger(resolved.state), "gateway", "config")?.output?.reconciliationId;
|
|
26467
|
+
const configStep = {
|
|
26468
|
+
name: "config",
|
|
26469
|
+
hash: () => configHash,
|
|
26470
|
+
run: async () => {
|
|
26471
|
+
try {
|
|
26472
|
+
const accepted = await client.putGatewayConfig(projectId, body);
|
|
26473
|
+
return {
|
|
26474
|
+
reconciliationId: accepted.reconciliationId,
|
|
26475
|
+
desiredHash: accepted.desiredHash
|
|
26476
|
+
};
|
|
26477
|
+
} catch (error) {
|
|
26478
|
+
return rejected("gateway config", error);
|
|
26479
|
+
}
|
|
26480
|
+
},
|
|
26481
|
+
// A reconciliation the platform gave up on is not a result worth keeping.
|
|
26482
|
+
stillValid: async () => {
|
|
26483
|
+
const ticket = await ticketOf();
|
|
26484
|
+
if (!ticket) return false;
|
|
26485
|
+
try {
|
|
26486
|
+
const state = await client.getGatewayReconciliation(projectId, ticket);
|
|
26487
|
+
return state.state !== "Failed" && state.state !== "Superseded";
|
|
26488
|
+
} catch {
|
|
26489
|
+
return false;
|
|
26490
|
+
}
|
|
26491
|
+
}
|
|
26492
|
+
};
|
|
26493
|
+
const reconcileStep = {
|
|
26494
|
+
name: "reconcile",
|
|
26495
|
+
hash: () => configHash,
|
|
26496
|
+
run: async () => {
|
|
26497
|
+
const ticket = await ticketOf();
|
|
26498
|
+
if (!ticket)
|
|
26499
|
+
throw new GatewayDeployError(
|
|
26500
|
+
"GATEWAY_RECONCILE_FAILED",
|
|
26501
|
+
"There is no reconciliation to wait for.",
|
|
26502
|
+
"Run deploy again."
|
|
26503
|
+
);
|
|
26504
|
+
const deadline = now() + timeoutMs;
|
|
26505
|
+
for (; ; ) {
|
|
26506
|
+
const state = await client.getGatewayReconciliation(projectId, ticket);
|
|
26507
|
+
if (state.state === "Succeeded") break;
|
|
26508
|
+
if (state.state === "Failed" || state.state === "Superseded")
|
|
26509
|
+
throw new GatewayDeployError(
|
|
26510
|
+
"GATEWAY_RECONCILE_FAILED",
|
|
26511
|
+
`The platform could not apply the gateway config: ${[state.errorCode, state.errorMessage].filter(Boolean).join(" ") || state.state}.`,
|
|
26512
|
+
"Fix the cause and run deploy again; the config is sent again."
|
|
26513
|
+
);
|
|
26514
|
+
if (now() >= deadline)
|
|
26515
|
+
throw new GatewayDeployError(
|
|
26516
|
+
"GATEWAY_RECONCILE_TIMEOUT",
|
|
26517
|
+
`The platform did not finish within ${Math.round(timeoutMs / 1e3)} seconds (state: ${state.state}).`,
|
|
26518
|
+
"Run deploy again to keep waiting. If it stays Pending, the platform worker that applies gateway configs may not be running yet."
|
|
26519
|
+
);
|
|
26520
|
+
await sleep5(intervalMs);
|
|
26521
|
+
}
|
|
26522
|
+
const status2 = await client.getGatewayStatus(projectId);
|
|
26523
|
+
return {
|
|
26524
|
+
baseUrl: status2.baseUrl,
|
|
26525
|
+
operationCount: String(status2.operationCount),
|
|
26526
|
+
...status2.specSha256 ? { specSha256: status2.specSha256 } : {}
|
|
26527
|
+
};
|
|
26528
|
+
}
|
|
26529
|
+
};
|
|
26530
|
+
const outcomes = await runPipeline(
|
|
26531
|
+
resolved.state,
|
|
26532
|
+
"gateway",
|
|
26533
|
+
[specStep, configStep, reconcileStep],
|
|
26534
|
+
{ force: options.force }
|
|
26535
|
+
);
|
|
26536
|
+
const reconcile = outcomes.find((outcome) => outcome.step === "reconcile");
|
|
26537
|
+
const ran = reconcile !== void 0 && !reconcile.skipped;
|
|
26538
|
+
const known = (await readManifest(resolved.state))?.services.gateway;
|
|
26539
|
+
let baseUrl = reconcile?.output?.baseUrl ?? known?.baseUrl;
|
|
26540
|
+
if (ran || known === void 0) {
|
|
26541
|
+
const status2 = await client.getGatewayStatus(projectId);
|
|
26542
|
+
baseUrl = status2.baseUrl || baseUrl;
|
|
26543
|
+
if (baseUrl) {
|
|
26544
|
+
const capabilities = await client.getGatewayCapabilities(projectId);
|
|
26545
|
+
const previous = known?.baseUrl?.replace(/\/+$/, "");
|
|
26546
|
+
if (baseUrl.replace(/\/+$/, "") !== previous || ran) {
|
|
26547
|
+
await writeManifest(
|
|
26548
|
+
resolved.state,
|
|
26549
|
+
withGatewayEntry(await readManifest(resolved.state), {
|
|
26550
|
+
projectId,
|
|
26551
|
+
projectName: resolved.project.name,
|
|
26552
|
+
provider: status2.provider,
|
|
26553
|
+
baseUrl,
|
|
26554
|
+
capabilities: capabilities.capabilities,
|
|
26555
|
+
openapiUrl: status2.openApiPath ?? void 0,
|
|
26556
|
+
openapiHash: status2.openApiHash ?? status2.specSha256 ?? void 0
|
|
26557
|
+
})
|
|
26558
|
+
);
|
|
26559
|
+
}
|
|
26560
|
+
}
|
|
26561
|
+
}
|
|
26562
|
+
const config2 = outcomes.find((outcome) => outcome.step === "config");
|
|
26563
|
+
return {
|
|
26564
|
+
status: ran ? "deployed" : "unchanged",
|
|
26565
|
+
steps: outcomes.map((outcome) => ({
|
|
26566
|
+
step: outcome.step,
|
|
26567
|
+
skipped: outcome.skipped
|
|
26568
|
+
})),
|
|
26569
|
+
baseUrl,
|
|
26570
|
+
operationCount: reconcile?.output?.operationCount ? Number(reconcile.output.operationCount) : void 0,
|
|
26571
|
+
reconciliationId: config2?.output?.reconciliationId
|
|
26572
|
+
};
|
|
26573
|
+
}
|
|
26574
|
+
|
|
26575
|
+
// src/cli/portal/report.ts
|
|
25807
26576
|
var DEPLOY_ORDER = [
|
|
25808
26577
|
"identity",
|
|
25809
26578
|
"gateway",
|
|
@@ -25844,7 +26613,7 @@ async function planPortalService(resolved, ledger) {
|
|
|
25844
26613
|
const hash2 = await portalBuildHash(resolved, prepared);
|
|
25845
26614
|
const previous = stepOf(ledger, "portal", "build");
|
|
25846
26615
|
const built = await exists4(
|
|
25847
|
-
|
|
26616
|
+
path80.join(resolved.state.buildDir, "index.html")
|
|
25848
26617
|
);
|
|
25849
26618
|
if (previous?.status === "succeeded" && previous.hash === hash2 && built) {
|
|
25850
26619
|
return {
|
|
@@ -25874,7 +26643,148 @@ async function planPortalService(resolved, ledger) {
|
|
|
25874
26643
|
};
|
|
25875
26644
|
}
|
|
25876
26645
|
}
|
|
25877
|
-
|
|
26646
|
+
function describePolicies(gateway) {
|
|
26647
|
+
const policies = gateway.policies;
|
|
26648
|
+
return [
|
|
26649
|
+
policies.access ? `access: ${[
|
|
26650
|
+
policies.access.jwt === void 0 ? void 0 : `jwt ${policies.access.jwt ? "on" : "off"}`,
|
|
26651
|
+
policies.access.apiKey === void 0 ? void 0 : `api key ${policies.access.apiKey ? "on" : "off"}`
|
|
26652
|
+
].filter(Boolean).join(", ")}` : void 0,
|
|
26653
|
+
policies.rateLimit ? `rate limit: ${policies.rateLimit.calls} per ${policies.rateLimit.periodSeconds} s` : void 0,
|
|
26654
|
+
policies.quota ? `quota: ${policies.quota.calls} per ${policies.quota.periodDays} day(s)` : void 0,
|
|
26655
|
+
policies.cors ? `cors: ${policies.cors.length} origin(s)` : void 0,
|
|
26656
|
+
policies.operations?.public ? `public operations: ${policies.operations.public.length}` : void 0
|
|
26657
|
+
].filter((line2) => line2 !== void 0);
|
|
26658
|
+
}
|
|
26659
|
+
function dryRunLines(dryRun) {
|
|
26660
|
+
const changes = dryRun.changes;
|
|
26661
|
+
if (!changes) return [];
|
|
26662
|
+
const list2 = (label, items) => items.length > 0 ? [` ${label}: ${items.join(", ")}`] : [];
|
|
26663
|
+
if (changes.wouldCreate)
|
|
26664
|
+
return [
|
|
26665
|
+
"platform dry run: the gateway does not exist yet; everything would be created",
|
|
26666
|
+
...list2("operations", changes.operationsAdded),
|
|
26667
|
+
...list2("public", changes.nowPublic),
|
|
26668
|
+
...list2("denied", changes.nowDenied)
|
|
26669
|
+
];
|
|
26670
|
+
if (!changes.hasChanges)
|
|
26671
|
+
return ["platform dry run: the gateway already matches this config"];
|
|
26672
|
+
return [
|
|
26673
|
+
"platform dry run: the gateway would change",
|
|
26674
|
+
...list2("operations added", changes.operationsAdded),
|
|
26675
|
+
...list2("operations removed", changes.operationsRemoved),
|
|
26676
|
+
...list2("now public", changes.nowPublic),
|
|
26677
|
+
...list2("now protected", changes.nowProtected),
|
|
26678
|
+
...list2("now denied", changes.nowDenied),
|
|
26679
|
+
...changes.originChanged ? [" origin address changes"] : [],
|
|
26680
|
+
...changes.policyChanges.map((c) => ` ${c}`),
|
|
26681
|
+
...dryRun.activeKeys > 0 ? [
|
|
26682
|
+
` active keys: ${dryRun.activeKeys} (removed or denied operations stop working for them)`
|
|
26683
|
+
] : []
|
|
26684
|
+
];
|
|
26685
|
+
}
|
|
26686
|
+
async function planGatewayService(resolved, ledger, dryRun) {
|
|
26687
|
+
const base = { service: "gateway", issues: [] };
|
|
26688
|
+
const gateway = resolved.gateway;
|
|
26689
|
+
if (!gateway) throw new Error("gateway is not configured");
|
|
26690
|
+
const why = gatewayNotDeployableReason(gateway);
|
|
26691
|
+
if (why)
|
|
26692
|
+
return {
|
|
26693
|
+
...base,
|
|
26694
|
+
action: "not-deployable-yet",
|
|
26695
|
+
reason: `configured, but ${why}`,
|
|
26696
|
+
details: []
|
|
26697
|
+
};
|
|
26698
|
+
const details = [
|
|
26699
|
+
`origin: proxy ${gateway.origin.url ?? "(no url set)"}`,
|
|
26700
|
+
...gateway.auth ? [`auth: ${gateway.auth.mode}`] : ["auth: none"],
|
|
26701
|
+
...describePolicies(gateway)
|
|
26702
|
+
];
|
|
26703
|
+
const projectId = resolved.project.id;
|
|
26704
|
+
const notAsked = !dryRun ? "platform dry run: not asked, you are not logged in" : !projectId ? "platform dry run: not asked, this folder is not linked to a project" : void 0;
|
|
26705
|
+
let inputs;
|
|
26706
|
+
try {
|
|
26707
|
+
inputs = await gatewayDeployInputs(resolved, resolved.project.id ?? "");
|
|
26708
|
+
} catch (error) {
|
|
26709
|
+
return {
|
|
26710
|
+
...base,
|
|
26711
|
+
action: "blocked",
|
|
26712
|
+
reason: "cannot deploy until this is fixed",
|
|
26713
|
+
details,
|
|
26714
|
+
issues: [
|
|
26715
|
+
{
|
|
26716
|
+
code: "GATEWAY_SPEC_UNREADABLE",
|
|
26717
|
+
path: "gateway.specs",
|
|
26718
|
+
message: error instanceof Error ? error.message : String(error)
|
|
26719
|
+
}
|
|
26720
|
+
]
|
|
26721
|
+
};
|
|
26722
|
+
}
|
|
26723
|
+
const issues = [];
|
|
26724
|
+
if (notAsked) {
|
|
26725
|
+
details.push(notAsked);
|
|
26726
|
+
} else if (dryRun && projectId) {
|
|
26727
|
+
try {
|
|
26728
|
+
const answer = await dryRun(projectId, {
|
|
26729
|
+
config: inputs.body,
|
|
26730
|
+
spec: inputs.spec.toString("utf8")
|
|
26731
|
+
});
|
|
26732
|
+
if (!answer.ok)
|
|
26733
|
+
issues.push(
|
|
26734
|
+
...answer.issues.map((item) => ({
|
|
26735
|
+
code: item.code,
|
|
26736
|
+
path: item.field,
|
|
26737
|
+
message: item.message,
|
|
26738
|
+
hint: item.hint
|
|
26739
|
+
}))
|
|
26740
|
+
);
|
|
26741
|
+
else details.push(...dryRunLines(answer));
|
|
26742
|
+
if (answer.ok && !answer.comparedWithGateway)
|
|
26743
|
+
details.push(
|
|
26744
|
+
" (the platform has no gateway unit for this project yet, so nothing could be compared)"
|
|
26745
|
+
);
|
|
26746
|
+
} catch (error) {
|
|
26747
|
+
details.push(
|
|
26748
|
+
`platform dry run: not available (${error instanceof Error ? error.message : String(error)})`
|
|
26749
|
+
);
|
|
26750
|
+
}
|
|
26751
|
+
}
|
|
26752
|
+
const step2 = (name) => stepOf(ledger, "gateway", name);
|
|
26753
|
+
const hashes = {
|
|
26754
|
+
spec: inputs.specHash,
|
|
26755
|
+
config: inputs.configHash,
|
|
26756
|
+
reconcile: inputs.configHash
|
|
26757
|
+
};
|
|
26758
|
+
const state = (name) => {
|
|
26759
|
+
const record = step2(name);
|
|
26760
|
+
if (!record) return "not done yet";
|
|
26761
|
+
if (record.status === "failed")
|
|
26762
|
+
return `failed last time (${record.error ?? "no message"})`;
|
|
26763
|
+
if (record.status === "running") return "did not finish last time";
|
|
26764
|
+
return record.hash === hashes[name] ? "done, unchanged" : "inputs changed";
|
|
26765
|
+
};
|
|
26766
|
+
const lines = [
|
|
26767
|
+
`step spec (upload the OpenAPI file): ${state("spec")}`,
|
|
26768
|
+
`step config (send the wanted state): ${state("config")}`,
|
|
26769
|
+
`step reconcile (wait until the platform applied it): ${state("reconcile")}`
|
|
26770
|
+
];
|
|
26771
|
+
const allDone = step2("spec")?.status === "succeeded" && step2("spec")?.hash === hashes.spec && step2("config")?.status === "succeeded" && step2("config")?.hash === hashes.config && step2("reconcile")?.status === "succeeded" && step2("reconcile")?.hash === hashes.reconcile;
|
|
26772
|
+
if (issues.length > 0)
|
|
26773
|
+
return {
|
|
26774
|
+
...base,
|
|
26775
|
+
action: "blocked",
|
|
26776
|
+
reason: "the platform refuses this config; fix these first",
|
|
26777
|
+
details: [...details, ...lines],
|
|
26778
|
+
issues
|
|
26779
|
+
};
|
|
26780
|
+
return {
|
|
26781
|
+
...base,
|
|
26782
|
+
action: allDone ? "skip" : "deploy",
|
|
26783
|
+
reason: allDone ? "deploy skipped: nothing it depends on has changed" : "will deploy: the wanted state or the OpenAPI file is not applied yet",
|
|
26784
|
+
details: [...details, ...lines]
|
|
26785
|
+
};
|
|
26786
|
+
}
|
|
26787
|
+
async function planV2(dir, only, options = {}) {
|
|
25878
26788
|
const { resolved } = loadConfigV2(dir);
|
|
25879
26789
|
const ledger = await readLedger(resolved.state);
|
|
25880
26790
|
const services = [];
|
|
@@ -25898,6 +26808,10 @@ async function planV2(dir, only) {
|
|
|
25898
26808
|
});
|
|
25899
26809
|
} else if (service === "portal") {
|
|
25900
26810
|
services.push(await planPortalService(resolved, ledger));
|
|
26811
|
+
} else if (service === "gateway") {
|
|
26812
|
+
services.push(
|
|
26813
|
+
await planGatewayService(resolved, ledger, options.gatewayDryRun)
|
|
26814
|
+
);
|
|
25901
26815
|
} else {
|
|
25902
26816
|
services.push({
|
|
25903
26817
|
service,
|
|
@@ -25913,7 +26827,80 @@ async function planV2(dir, only) {
|
|
|
25913
26827
|
function entryStatus(manifest, service) {
|
|
25914
26828
|
return manifest?.services[service];
|
|
25915
26829
|
}
|
|
25916
|
-
|
|
26830
|
+
function originHealthLines(origin) {
|
|
26831
|
+
if (!origin.lastProbeAtUtc) return ["origin check: not checked yet"];
|
|
26832
|
+
return [
|
|
26833
|
+
`origin check: ${origin.lastProbeAtUtc}${origin.latencyMs != null ? `, ${origin.latencyMs} ms` : ""}`,
|
|
26834
|
+
...origin.lastErrorCode ? [`origin problem: ${origin.lastErrorCode}`] : [],
|
|
26835
|
+
...origin.healthChangedAtUtc ? [`origin health changed: ${origin.healthChangedAtUtc}`] : []
|
|
26836
|
+
];
|
|
26837
|
+
}
|
|
26838
|
+
function corsLines(platform) {
|
|
26839
|
+
const sites = platform.cors ?? [];
|
|
26840
|
+
return [
|
|
26841
|
+
...sites.length > 0 ? [
|
|
26842
|
+
"browser origins (CORS):",
|
|
26843
|
+
...sites.map(
|
|
26844
|
+
(site) => ` ${site.origin} (${site.source})${site.applied ? "" : ", not on the real gateway yet"}`
|
|
26845
|
+
)
|
|
26846
|
+
] : [],
|
|
26847
|
+
...platform.openApiPath ? [
|
|
26848
|
+
`openapi: ${platform.openApiPath} (${platform.exportAccess ?? "public"}${platform.openApiHash ? `, ${platform.openApiHash.slice(0, 12)}` : ""})`
|
|
26849
|
+
] : []
|
|
26850
|
+
];
|
|
26851
|
+
}
|
|
26852
|
+
async function gatewayStatus(resolved, ledger, manifest, remote) {
|
|
26853
|
+
const steps = ["spec", "config", "reconcile"].map((name) => ({
|
|
26854
|
+
name,
|
|
26855
|
+
record: stepOf(ledger, "gateway", name)
|
|
26856
|
+
}));
|
|
26857
|
+
const failed = steps.find((step2) => step2.record?.status === "failed");
|
|
26858
|
+
const local = [
|
|
26859
|
+
...steps.filter((step2) => step2.record).map((step2) => `step ${step2.name}: ${step2.record?.status}`),
|
|
26860
|
+
...failed ? [`error: ${failed.record?.error ?? "unknown"}`] : []
|
|
26861
|
+
];
|
|
26862
|
+
const entry = manifest?.services.gateway;
|
|
26863
|
+
const projectId = resolved.project.id ?? manifest?.project.id;
|
|
26864
|
+
let state = entry ? "ready" : "not-deployed";
|
|
26865
|
+
const details = [];
|
|
26866
|
+
if (entry?.baseUrl) details.push(`base url: ${entry.baseUrl}`);
|
|
26867
|
+
if (entry?.provider) details.push(`provider: ${entry.provider}`);
|
|
26868
|
+
if (remote && projectId) {
|
|
26869
|
+
try {
|
|
26870
|
+
const platform = await remote(projectId);
|
|
26871
|
+
details.push(
|
|
26872
|
+
`platform state: ${platform.status}`,
|
|
26873
|
+
platform.inSync ? "drift: none (the real gateway matches the config)" : `drift: the real gateway does not match the config yet (wanted ${platform.desiredHash?.slice(0, 12) ?? "none"}, real ${platform.observedHash?.slice(0, 12) ?? "none"})`,
|
|
26874
|
+
...platform.origin ? [
|
|
26875
|
+
`origin: ${platform.origin.mode}${platform.origin.url ? ` ${platform.origin.url}` : ""}, health ${platform.origin.health}`,
|
|
26876
|
+
...originHealthLines(platform.origin)
|
|
26877
|
+
] : [],
|
|
26878
|
+
`operations: ${platform.operationCount}`,
|
|
26879
|
+
...corsLines(platform)
|
|
26880
|
+
);
|
|
26881
|
+
const latest = platform.latestReconciliation;
|
|
26882
|
+
if (latest)
|
|
26883
|
+
details.push(
|
|
26884
|
+
`last reconciliation: ${latest.state}${latest.errorCode ? ` (${latest.errorCode})` : ""}`
|
|
26885
|
+
);
|
|
26886
|
+
if (platform.baseUrl && !entry?.baseUrl)
|
|
26887
|
+
details.push(`base url: ${platform.baseUrl}`);
|
|
26888
|
+
if (!platform.inSync && ["Active", "Reconciling", "Failed"].includes(platform.status))
|
|
26889
|
+
state = "drift";
|
|
26890
|
+
} catch (error) {
|
|
26891
|
+
details.push(
|
|
26892
|
+
error instanceof PlatformApiError && error.status === 404 ? "platform: no gateway for this project yet" : `platform: not reachable (${error instanceof Error ? error.message : String(error)}); showing local records only`
|
|
26893
|
+
);
|
|
26894
|
+
}
|
|
26895
|
+
} else if (!projectId) {
|
|
26896
|
+
details.push("platform: not asked, this folder is not linked to a project");
|
|
26897
|
+
} else if (!remote) {
|
|
26898
|
+
details.push("platform: not asked, you are not logged in");
|
|
26899
|
+
}
|
|
26900
|
+
if (failed) state = "deploy-failed";
|
|
26901
|
+
return { service: "gateway", state, details: [...details, ...local] };
|
|
26902
|
+
}
|
|
26903
|
+
async function statusV2(dir, only, options = {}) {
|
|
25917
26904
|
const { resolved } = loadConfigV2(dir);
|
|
25918
26905
|
const ledger = await readLedger(resolved.state);
|
|
25919
26906
|
const manifest = await readManifest(resolved.state);
|
|
@@ -25933,7 +26920,7 @@ async function statusV2(dir, only) {
|
|
|
25933
26920
|
if (service === "portal") {
|
|
25934
26921
|
const build2 = stepOf(ledger, "portal", "build");
|
|
25935
26922
|
const built = await exists4(
|
|
25936
|
-
|
|
26923
|
+
path80.join(resolved.state.buildDir, "index.html")
|
|
25937
26924
|
);
|
|
25938
26925
|
const state = build2?.status === "failed" ? "build-failed" : build2?.status === "succeeded" && built ? "built" : "not-built";
|
|
25939
26926
|
services.push({
|
|
@@ -25944,13 +26931,19 @@ async function statusV2(dir, only) {
|
|
|
25944
26931
|
...build2?.finishedAt ? [`last build: ${build2.finishedAt}`] : [],
|
|
25945
26932
|
...build2?.status === "failed" ? [`error: ${build2.error ?? "unknown"}`] : [],
|
|
25946
26933
|
...state === "built" ? [
|
|
25947
|
-
`output: ${
|
|
26934
|
+
`output: ${path80.relative(resolved.configDir, resolved.state.buildDir)}`
|
|
25948
26935
|
] : [],
|
|
25949
26936
|
...url2 ? [`url: ${url2}`] : ["url: not known (portal is not deployed yet)"]
|
|
25950
26937
|
]
|
|
25951
26938
|
});
|
|
25952
26939
|
continue;
|
|
25953
26940
|
}
|
|
26941
|
+
if (service === "gateway") {
|
|
26942
|
+
services.push(
|
|
26943
|
+
await gatewayStatus(resolved, ledger, manifest, options.gatewayRemote)
|
|
26944
|
+
);
|
|
26945
|
+
continue;
|
|
26946
|
+
}
|
|
25954
26947
|
services.push({
|
|
25955
26948
|
service,
|
|
25956
26949
|
state: entry ? "ready" : "not-deployed",
|
|
@@ -25975,7 +26968,7 @@ function formatPlan(output4) {
|
|
|
25975
26968
|
`${svc.service}: ${svc.reason}`,
|
|
25976
26969
|
...svc.details.map((line2) => ` ${line2}`),
|
|
25977
26970
|
...svc.issues.map(
|
|
25978
|
-
(
|
|
26971
|
+
(issue3) => ` problem: ${issue3.message}${issue3.hint ? ` ${issue3.hint}` : ""} [${issue3.code}]`
|
|
25979
26972
|
)
|
|
25980
26973
|
]);
|
|
25981
26974
|
}
|
|
@@ -26042,7 +27035,7 @@ async function deployV2(options, deps) {
|
|
|
26042
27035
|
throw new DeployError(
|
|
26043
27036
|
"PROJECT_REQUIRED",
|
|
26044
27037
|
"This folder is not linked to a project yet.",
|
|
26045
|
-
"
|
|
27038
|
+
"Run `apitogo project create` to make a platform project and link this folder. Or pass --project-id <id> (saved as project.id), or set APITOGO_DEFAULT_PROJECT_ID."
|
|
26046
27039
|
);
|
|
26047
27040
|
}
|
|
26048
27041
|
const results = [];
|
|
@@ -26068,12 +27061,16 @@ async function deployV2(options, deps) {
|
|
|
26068
27061
|
});
|
|
26069
27062
|
continue;
|
|
26070
27063
|
}
|
|
27064
|
+
if (service === "gateway") {
|
|
27065
|
+
results.push(await deployGatewayService(options, deps, projectId, named));
|
|
27066
|
+
continue;
|
|
27067
|
+
}
|
|
26071
27068
|
if (service !== "portal") {
|
|
26072
27069
|
if (named)
|
|
26073
27070
|
throw new DeployError(
|
|
26074
27071
|
"SERVICE_NOT_DEPLOYABLE",
|
|
26075
27072
|
`Deploying ${service} is not available in this CLI version yet.`,
|
|
26076
|
-
"Only the portal can be deployed for now."
|
|
27073
|
+
"Only the portal and the gateway can be deployed for now."
|
|
26077
27074
|
);
|
|
26078
27075
|
results.push({
|
|
26079
27076
|
service,
|
|
@@ -26094,6 +27091,36 @@ async function deployV2(options, deps) {
|
|
|
26094
27091
|
if (!resolved.project.id) await setProjectId(options.dir, projectId);
|
|
26095
27092
|
return { projectId, services: results };
|
|
26096
27093
|
}
|
|
27094
|
+
async function deployGatewayService(options, deps, projectId, named) {
|
|
27095
|
+
const { resolved } = loadConfigV2(options.dir);
|
|
27096
|
+
const gateway = resolved.gateway;
|
|
27097
|
+
const why = gateway ? gatewayNotDeployableReason(gateway) : void 0;
|
|
27098
|
+
if (why) {
|
|
27099
|
+
if (named)
|
|
27100
|
+
throw new DeployError(
|
|
27101
|
+
"SERVICE_NOT_DEPLOYABLE",
|
|
27102
|
+
`The gateway cannot be deployed: ${why}.`,
|
|
27103
|
+
"Change gateway.origin / gateway.specs in the config, or deploy another service."
|
|
27104
|
+
);
|
|
27105
|
+
return { service: "gateway", status: "skipped", reason: why, steps: [] };
|
|
27106
|
+
}
|
|
27107
|
+
const client = deps.client;
|
|
27108
|
+
if (!client.putGatewaySpec || !client.putGatewayConfig || !client.getGatewayReconciliation || !client.getGatewayStatus || !client.getGatewayCapabilities)
|
|
27109
|
+
throw new Error("The platform client has no gateway calls.");
|
|
27110
|
+
const result = await deployGateway(
|
|
27111
|
+
resolved,
|
|
27112
|
+
projectId,
|
|
27113
|
+
{ ...deps.gateway, client },
|
|
27114
|
+
{ force: options.force }
|
|
27115
|
+
);
|
|
27116
|
+
return {
|
|
27117
|
+
service: "gateway",
|
|
27118
|
+
status: result.status,
|
|
27119
|
+
steps: result.steps,
|
|
27120
|
+
url: result.baseUrl,
|
|
27121
|
+
operationCount: result.operationCount
|
|
27122
|
+
};
|
|
27123
|
+
}
|
|
26097
27124
|
async function deployPortal(options, deps, projectId) {
|
|
26098
27125
|
const { resolved, root, prepared } = await preparePortalWorkspace(
|
|
26099
27126
|
options.dir,
|
|
@@ -26154,7 +27181,8 @@ async function deployPortal(options, deps, projectId) {
|
|
|
26154
27181
|
function formatDeploy(output4) {
|
|
26155
27182
|
return output4.services.flatMap((result) => {
|
|
26156
27183
|
const url2 = result.url ? ` (${result.url})` : "";
|
|
26157
|
-
const
|
|
27184
|
+
const ops = result.operationCount !== void 0 ? `, ${result.operationCount} operation(s)` : "";
|
|
27185
|
+
const head = result.status === "deployed" ? `${result.service}: deployed${result.environment ? ` to ${result.environment}` : ""}${url2}${ops}` : result.status === "unchanged" ? `${result.service}: unchanged, nothing to publish${url2}` : `${result.service}: skipped, ${result.reason}`;
|
|
26158
27186
|
return [
|
|
26159
27187
|
head,
|
|
26160
27188
|
...result.steps.map(
|
|
@@ -26171,7 +27199,7 @@ async function deployProject(argv) {
|
|
|
26171
27199
|
const config2 = loadConfig();
|
|
26172
27200
|
const output4 = await deployV2(
|
|
26173
27201
|
{
|
|
26174
|
-
dir:
|
|
27202
|
+
dir: path81.resolve(process.cwd(), argv.dir),
|
|
26175
27203
|
only: Array.isArray(argv.only) ? argv.only.join(",") : argv.only,
|
|
26176
27204
|
projectId: argv.projectId,
|
|
26177
27205
|
env: argv.env,
|
|
@@ -26252,7 +27280,7 @@ var deploy_default = {
|
|
|
26252
27280
|
}),
|
|
26253
27281
|
handler: async (argv) => {
|
|
26254
27282
|
await captureEvent({ argv, event: "apitogo deploy" });
|
|
26255
|
-
if (await isConfigV2(
|
|
27283
|
+
if (await isConfigV2(path82.resolve(process.cwd(), argv.dir))) {
|
|
26256
27284
|
await deployProject(argv);
|
|
26257
27285
|
return;
|
|
26258
27286
|
}
|
|
@@ -26260,16 +27288,108 @@ var deploy_default = {
|
|
|
26260
27288
|
}
|
|
26261
27289
|
};
|
|
26262
27290
|
|
|
27291
|
+
// src/cli/export/handler.ts
|
|
27292
|
+
import { mkdir as mkdir20, writeFile as writeFile24 } from "node:fs/promises";
|
|
27293
|
+
import path83 from "node:path";
|
|
27294
|
+
init_invariant();
|
|
27295
|
+
var ExportError = class extends ApitogoError {
|
|
27296
|
+
constructor(message, developerHint) {
|
|
27297
|
+
super(message, { developerHint, title: "Cannot export OpenAPI" });
|
|
27298
|
+
this.name = "ExportError";
|
|
27299
|
+
}
|
|
27300
|
+
};
|
|
27301
|
+
async function exportOpenApi(args, deps) {
|
|
27302
|
+
if (args.target !== "openapi")
|
|
27303
|
+
throw new ExportError(
|
|
27304
|
+
`Cannot export "${args.target}".`,
|
|
27305
|
+
"Only `apitogo export openapi` exists."
|
|
27306
|
+
);
|
|
27307
|
+
const dir = path83.resolve(process.cwd(), args.dir ?? ".");
|
|
27308
|
+
if (!await isConfigV2(dir))
|
|
27309
|
+
throw new ExportError(
|
|
27310
|
+
"This command needs a config version 2 project.",
|
|
27311
|
+
"Run it in a folder with a version 2 apitogo.config.json."
|
|
27312
|
+
);
|
|
27313
|
+
const { resolved } = loadConfigV2(dir);
|
|
27314
|
+
const projectId = args.projectId?.trim() || resolved.project.id || deps.envProjectId?.trim();
|
|
27315
|
+
if (!projectId)
|
|
27316
|
+
throw new ExportError(
|
|
27317
|
+
"This folder is not linked to a project.",
|
|
27318
|
+
"Run `apitogo project create`, or pass --project-id."
|
|
27319
|
+
);
|
|
27320
|
+
let text;
|
|
27321
|
+
try {
|
|
27322
|
+
text = await deps.client.getGatewayOpenApi(projectId);
|
|
27323
|
+
} catch (error) {
|
|
27324
|
+
if (error instanceof PlatformApiError && error.status === 404)
|
|
27325
|
+
throw new ExportError(
|
|
27326
|
+
"The platform has no OpenAPI file for this project yet.",
|
|
27327
|
+
"Deploy the gateway first with `apitogo deploy --only gateway`."
|
|
27328
|
+
);
|
|
27329
|
+
throw error;
|
|
27330
|
+
}
|
|
27331
|
+
if (!args.out) return { text };
|
|
27332
|
+
const file = path83.resolve(process.cwd(), args.out);
|
|
27333
|
+
await mkdir20(path83.dirname(file), { recursive: true });
|
|
27334
|
+
await writeFile24(file, text);
|
|
27335
|
+
return { text, file };
|
|
27336
|
+
}
|
|
27337
|
+
async function exportCommand(args) {
|
|
27338
|
+
try {
|
|
27339
|
+
const config2 = loadConfig();
|
|
27340
|
+
const result = await exportOpenApi(args, {
|
|
27341
|
+
client: new PlatformApiClient(config2),
|
|
27342
|
+
envProjectId: config2.defaultProjectId
|
|
27343
|
+
});
|
|
27344
|
+
if (result.file) printDiagnosticsToConsole(`Wrote ${result.file}.`);
|
|
27345
|
+
else printResultToConsole(result.text);
|
|
27346
|
+
} catch (error) {
|
|
27347
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27348
|
+
const hint = error instanceof ApitogoError ? error.developerHint : void 0;
|
|
27349
|
+
await printCriticalFailureToConsoleAndExit(
|
|
27350
|
+
hint ? `${message} ${hint}` : message
|
|
27351
|
+
);
|
|
27352
|
+
}
|
|
27353
|
+
}
|
|
27354
|
+
|
|
27355
|
+
// src/cli/cmds/export.ts
|
|
27356
|
+
var export_default = {
|
|
27357
|
+
desc: "Print or save the OpenAPI file the platform publishes for your gateway (config version 2)",
|
|
27358
|
+
command: "export <target>",
|
|
27359
|
+
builder: (yargs2) => yargs2.positional("target", {
|
|
27360
|
+
type: "string",
|
|
27361
|
+
choices: ["openapi"],
|
|
27362
|
+
describe: "What to export"
|
|
27363
|
+
}).option("out", {
|
|
27364
|
+
type: "string",
|
|
27365
|
+
describe: "Write to this file instead of printing",
|
|
27366
|
+
normalize: true
|
|
27367
|
+
}).option("dir", {
|
|
27368
|
+
type: "string",
|
|
27369
|
+
describe: "Project directory",
|
|
27370
|
+
default: ".",
|
|
27371
|
+
normalize: true,
|
|
27372
|
+
hidden: true
|
|
27373
|
+
}).option("project-id", {
|
|
27374
|
+
type: "string",
|
|
27375
|
+
describe: "Project id (default: project.id in apitogo.config.json)"
|
|
27376
|
+
}),
|
|
27377
|
+
handler: async (argv) => {
|
|
27378
|
+
await captureEvent({ argv: { dir: argv.dir }, event: "apitogo export" });
|
|
27379
|
+
await exportCommand(argv);
|
|
27380
|
+
}
|
|
27381
|
+
};
|
|
27382
|
+
|
|
26263
27383
|
// src/cli/init/handler.ts
|
|
26264
|
-
import
|
|
27384
|
+
import path87 from "node:path";
|
|
26265
27385
|
|
|
26266
27386
|
// src/cli/scaffold/run-scaffold-discovery.ts
|
|
26267
|
-
import { access as access15, readFile as
|
|
26268
|
-
import
|
|
27387
|
+
import { access as access15, readFile as readFile28, writeFile as writeFile26 } from "node:fs/promises";
|
|
27388
|
+
import path86 from "node:path";
|
|
26269
27389
|
|
|
26270
27390
|
// src/cli/scaffold/detect-backend.ts
|
|
26271
|
-
import { readdir as readdir4, readFile as
|
|
26272
|
-
import
|
|
27391
|
+
import { readdir as readdir4, readFile as readFile26 } from "node:fs/promises";
|
|
27392
|
+
import path84 from "node:path";
|
|
26273
27393
|
var DEFAULT_RUNTIME_VERSION = {
|
|
26274
27394
|
dotnet: "DOTNET_10",
|
|
26275
27395
|
nodejs: "NODE_22_LTS",
|
|
@@ -26286,7 +27406,7 @@ async function listFiles(directory) {
|
|
|
26286
27406
|
}
|
|
26287
27407
|
async function readText(filePath) {
|
|
26288
27408
|
try {
|
|
26289
|
-
return await
|
|
27409
|
+
return await readFile26(filePath, "utf8");
|
|
26290
27410
|
} catch {
|
|
26291
27411
|
return void 0;
|
|
26292
27412
|
}
|
|
@@ -26315,15 +27435,15 @@ function isRunnableNodePackage(pkg) {
|
|
|
26315
27435
|
return typeof scripts.start === "string" || typeof pkg.main === "string";
|
|
26316
27436
|
}
|
|
26317
27437
|
async function detectParentBackend(siteDirectory) {
|
|
26318
|
-
const site =
|
|
26319
|
-
const directory =
|
|
27438
|
+
const site = path84.resolve(siteDirectory);
|
|
27439
|
+
const directory = path84.dirname(site);
|
|
26320
27440
|
if (directory === site) {
|
|
26321
27441
|
return void 0;
|
|
26322
27442
|
}
|
|
26323
27443
|
const files = await listFiles(directory);
|
|
26324
27444
|
const toResult = (runtime, runtimeVersion, marker) => ({
|
|
26325
27445
|
directory,
|
|
26326
|
-
sourceDirectory:
|
|
27446
|
+
sourceDirectory: path84.relative(site, directory).split(path84.sep).join("/") || ".",
|
|
26327
27447
|
runtime,
|
|
26328
27448
|
runtimeVersion,
|
|
26329
27449
|
marker
|
|
@@ -26331,7 +27451,7 @@ async function detectParentBackend(siteDirectory) {
|
|
|
26331
27451
|
const csproj = files.find((f) => f.endsWith(".csproj"));
|
|
26332
27452
|
const solution = files.find((f) => /\.slnx?$/.test(f));
|
|
26333
27453
|
if (csproj || solution) {
|
|
26334
|
-
const content = csproj ? await readText(
|
|
27454
|
+
const content = csproj ? await readText(path84.join(directory, csproj)) : void 0;
|
|
26335
27455
|
return toResult(
|
|
26336
27456
|
"dotnet",
|
|
26337
27457
|
dotnetVersionFromCsproj(content),
|
|
@@ -26339,7 +27459,7 @@ async function detectParentBackend(siteDirectory) {
|
|
|
26339
27459
|
);
|
|
26340
27460
|
}
|
|
26341
27461
|
if (files.includes("package.json")) {
|
|
26342
|
-
const raw = await readText(
|
|
27462
|
+
const raw = await readText(path84.join(directory, "package.json"));
|
|
26343
27463
|
try {
|
|
26344
27464
|
const pkg = JSON.parse(raw ?? "");
|
|
26345
27465
|
if (isRunnableNodePackage(pkg)) {
|
|
@@ -26354,7 +27474,7 @@ async function detectParentBackend(siteDirectory) {
|
|
|
26354
27474
|
}
|
|
26355
27475
|
const pythonMarker = PYTHON_MARKERS.find((m) => files.includes(m));
|
|
26356
27476
|
if (pythonMarker) {
|
|
26357
|
-
const versionSource = await readText(
|
|
27477
|
+
const versionSource = await readText(path84.join(directory, ".python-version")) ?? (pythonMarker === "pyproject.toml" ? (await readText(path84.join(directory, pythonMarker)))?.match(
|
|
26358
27478
|
/requires-python\s*=\s*"([^"]+)"/
|
|
26359
27479
|
)?.[1] : void 0);
|
|
26360
27480
|
return toResult(
|
|
@@ -26370,14 +27490,14 @@ async function detectParentBackend(siteDirectory) {
|
|
|
26370
27490
|
import {
|
|
26371
27491
|
access as access14,
|
|
26372
27492
|
lstat as lstat2,
|
|
26373
|
-
mkdir as
|
|
27493
|
+
mkdir as mkdir21,
|
|
26374
27494
|
readdir as readdir5,
|
|
26375
|
-
readFile as
|
|
27495
|
+
readFile as readFile27,
|
|
26376
27496
|
readlink,
|
|
26377
27497
|
symlink as symlink2,
|
|
26378
|
-
writeFile as
|
|
27498
|
+
writeFile as writeFile25
|
|
26379
27499
|
} from "node:fs/promises";
|
|
26380
|
-
import
|
|
27500
|
+
import path85 from "node:path";
|
|
26381
27501
|
var DOCS_DIR_WHITELIST = [
|
|
26382
27502
|
"docs",
|
|
26383
27503
|
"documents",
|
|
@@ -26414,14 +27534,14 @@ async function isDirectory2(filePath) {
|
|
|
26414
27534
|
}
|
|
26415
27535
|
}
|
|
26416
27536
|
function toPosix3(value) {
|
|
26417
|
-
return value.split(
|
|
27537
|
+
return value.split(path85.sep).join(path85.posix.sep);
|
|
26418
27538
|
}
|
|
26419
27539
|
function isWhitelistedDirName(name) {
|
|
26420
27540
|
return DOCS_DIR_WHITELIST.includes(name.toLowerCase());
|
|
26421
27541
|
}
|
|
26422
27542
|
function isInside2(parent, child) {
|
|
26423
|
-
const relative =
|
|
26424
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
27543
|
+
const relative = path85.relative(parent, child);
|
|
27544
|
+
return relative === "" || !relative.startsWith("..") && !path85.isAbsolute(relative);
|
|
26425
27545
|
}
|
|
26426
27546
|
async function collectMarkdownFiles(dir, excludeDir) {
|
|
26427
27547
|
const files = [];
|
|
@@ -26435,9 +27555,9 @@ async function collectMarkdownFiles(dir, excludeDir) {
|
|
|
26435
27555
|
if (entry.name.startsWith(".")) {
|
|
26436
27556
|
continue;
|
|
26437
27557
|
}
|
|
26438
|
-
const full =
|
|
27558
|
+
const full = path85.join(dir, entry.name);
|
|
26439
27559
|
if (entry.isDirectory()) {
|
|
26440
|
-
if (SKIP_SEARCH_DIRS.has(entry.name) || entry.isSymbolicLink() || excludeDir &&
|
|
27560
|
+
if (SKIP_SEARCH_DIRS.has(entry.name) || entry.isSymbolicLink() || excludeDir && path85.resolve(full) === excludeDir) {
|
|
26441
27561
|
continue;
|
|
26442
27562
|
}
|
|
26443
27563
|
files.push(...await collectMarkdownFiles(full, excludeDir));
|
|
@@ -26450,11 +27570,11 @@ async function collectMarkdownFiles(dir, excludeDir) {
|
|
|
26450
27570
|
return files;
|
|
26451
27571
|
}
|
|
26452
27572
|
async function collectSearchRoots(siteDirectory) {
|
|
26453
|
-
const site =
|
|
26454
|
-
const parent =
|
|
27573
|
+
const site = path85.resolve(siteDirectory);
|
|
27574
|
+
const parent = path85.dirname(site);
|
|
26455
27575
|
const roots = [];
|
|
26456
27576
|
const add = (dir) => {
|
|
26457
|
-
const resolved =
|
|
27577
|
+
const resolved = path85.resolve(dir);
|
|
26458
27578
|
if (!roots.includes(resolved)) {
|
|
26459
27579
|
roots.push(resolved);
|
|
26460
27580
|
}
|
|
@@ -26467,7 +27587,7 @@ async function collectSearchRoots(siteDirectory) {
|
|
|
26467
27587
|
if (!entry.isDirectory() || SKIP_SEARCH_DIRS.has(entry.name)) {
|
|
26468
27588
|
continue;
|
|
26469
27589
|
}
|
|
26470
|
-
add(
|
|
27590
|
+
add(path85.join(parent, entry.name));
|
|
26471
27591
|
}
|
|
26472
27592
|
} catch {
|
|
26473
27593
|
}
|
|
@@ -26476,13 +27596,13 @@ async function collectSearchRoots(siteDirectory) {
|
|
|
26476
27596
|
return roots;
|
|
26477
27597
|
}
|
|
26478
27598
|
async function discoverNearbyDocsDirectories(siteDirectory) {
|
|
26479
|
-
const site =
|
|
26480
|
-
const sitePages =
|
|
27599
|
+
const site = path85.resolve(siteDirectory);
|
|
27600
|
+
const sitePages = path85.join(site, PAGES_DIR);
|
|
26481
27601
|
const searchedDirectories = await collectSearchRoots(site);
|
|
26482
27602
|
const directories = [];
|
|
26483
27603
|
const seen = /* @__PURE__ */ new Set();
|
|
26484
27604
|
const consider = async (dir) => {
|
|
26485
|
-
const resolved =
|
|
27605
|
+
const resolved = path85.resolve(dir);
|
|
26486
27606
|
if (resolved === site || seen.has(resolved) || isInside2(sitePages, resolved)) {
|
|
26487
27607
|
return;
|
|
26488
27608
|
}
|
|
@@ -26499,7 +27619,7 @@ async function discoverNearbyDocsDirectories(siteDirectory) {
|
|
|
26499
27619
|
directories.push({ sourceDirectory: resolved, files });
|
|
26500
27620
|
};
|
|
26501
27621
|
for (const root of searchedDirectories) {
|
|
26502
|
-
if (isWhitelistedDirName(
|
|
27622
|
+
if (isWhitelistedDirName(path85.basename(root))) {
|
|
26503
27623
|
await consider(root);
|
|
26504
27624
|
}
|
|
26505
27625
|
try {
|
|
@@ -26511,7 +27631,7 @@ async function discoverNearbyDocsDirectories(siteDirectory) {
|
|
|
26511
27631
|
if (!isWhitelistedDirName(entry.name)) {
|
|
26512
27632
|
continue;
|
|
26513
27633
|
}
|
|
26514
|
-
await consider(
|
|
27634
|
+
await consider(path85.join(root, entry.name));
|
|
26515
27635
|
}
|
|
26516
27636
|
} catch {
|
|
26517
27637
|
}
|
|
@@ -26530,8 +27650,8 @@ function slugifyDirName(name) {
|
|
|
26530
27650
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "docs";
|
|
26531
27651
|
}
|
|
26532
27652
|
async function allocateLinkName(pagesDir, sourceDirectory, used) {
|
|
26533
|
-
const base = slugifyDirName(
|
|
26534
|
-
const parent = slugifyDirName(
|
|
27653
|
+
const base = slugifyDirName(path85.basename(sourceDirectory));
|
|
27654
|
+
const parent = slugifyDirName(path85.basename(path85.dirname(sourceDirectory)));
|
|
26535
27655
|
const candidates = [base, `${parent}-${base}`];
|
|
26536
27656
|
let index = 2;
|
|
26537
27657
|
while (true) {
|
|
@@ -26539,7 +27659,7 @@ async function allocateLinkName(pagesDir, sourceDirectory, used) {
|
|
|
26539
27659
|
if (used.has(name)) {
|
|
26540
27660
|
continue;
|
|
26541
27661
|
}
|
|
26542
|
-
const target =
|
|
27662
|
+
const target = path85.join(pagesDir, name);
|
|
26543
27663
|
if (await pathExists5(target)) {
|
|
26544
27664
|
const stat7 = await lstat2(target);
|
|
26545
27665
|
if (!stat7.isDirectory() || !await isManagedLinkDir(target)) {
|
|
@@ -26570,8 +27690,8 @@ async function alreadyLinked(linkPath, sourcePath) {
|
|
|
26570
27690
|
return false;
|
|
26571
27691
|
}
|
|
26572
27692
|
const current = await readlink(linkPath);
|
|
26573
|
-
const resolved =
|
|
26574
|
-
return resolved ===
|
|
27693
|
+
const resolved = path85.resolve(path85.dirname(linkPath), current);
|
|
27694
|
+
return resolved === path85.resolve(sourcePath);
|
|
26575
27695
|
} catch {
|
|
26576
27696
|
return false;
|
|
26577
27697
|
}
|
|
@@ -26583,8 +27703,8 @@ async function linkMarkdownFile(sourcePath, linkPath) {
|
|
|
26583
27703
|
if (await pathExists5(linkPath)) {
|
|
26584
27704
|
return "skipped";
|
|
26585
27705
|
}
|
|
26586
|
-
await
|
|
26587
|
-
const relativeTarget =
|
|
27706
|
+
await mkdir21(path85.dirname(linkPath), { recursive: true });
|
|
27707
|
+
const relativeTarget = path85.relative(path85.dirname(linkPath), sourcePath);
|
|
26588
27708
|
await symlink2(
|
|
26589
27709
|
relativeTarget,
|
|
26590
27710
|
linkPath,
|
|
@@ -26599,11 +27719,11 @@ async function applyDocsNavigation(siteDirectory, routes) {
|
|
|
26599
27719
|
if (routes.length === 0) {
|
|
26600
27720
|
return;
|
|
26601
27721
|
}
|
|
26602
|
-
const configPath =
|
|
27722
|
+
const configPath = path85.join(siteDirectory, CONFIG_FILENAME);
|
|
26603
27723
|
if (!await pathExists5(configPath)) {
|
|
26604
27724
|
return;
|
|
26605
27725
|
}
|
|
26606
|
-
const raw = await
|
|
27726
|
+
const raw = await readFile27(configPath, "utf8");
|
|
26607
27727
|
const parsed = JSON.parse(raw);
|
|
26608
27728
|
if (!isPlainObject5(parsed)) {
|
|
26609
27729
|
return;
|
|
@@ -26612,12 +27732,12 @@ async function applyDocsNavigation(siteDirectory, routes) {
|
|
|
26612
27732
|
force: false
|
|
26613
27733
|
});
|
|
26614
27734
|
const updated = ensureHeaderDocsNavigation(parsed, routes);
|
|
26615
|
-
await
|
|
27735
|
+
await writeFile25(configPath, `${JSON.stringify(updated, null, 2)}
|
|
26616
27736
|
`, "utf8");
|
|
26617
27737
|
}
|
|
26618
27738
|
async function discoverAndWireDocs(siteDirectory) {
|
|
26619
|
-
const site =
|
|
26620
|
-
const pagesDir =
|
|
27739
|
+
const site = path85.resolve(siteDirectory);
|
|
27740
|
+
const pagesDir = path85.join(site, PAGES_DIR);
|
|
26621
27741
|
const { directories, searchedDirectories } = await discoverNearbyDocsDirectories(site);
|
|
26622
27742
|
if (directories.length === 0) {
|
|
26623
27743
|
return {
|
|
@@ -26629,7 +27749,7 @@ async function discoverAndWireDocs(siteDirectory) {
|
|
|
26629
27749
|
skippedCount: 0
|
|
26630
27750
|
};
|
|
26631
27751
|
}
|
|
26632
|
-
await
|
|
27752
|
+
await mkdir21(pagesDir, { recursive: true });
|
|
26633
27753
|
const usedNames = /* @__PURE__ */ new Set();
|
|
26634
27754
|
const sources = [];
|
|
26635
27755
|
const routes = [];
|
|
@@ -26639,15 +27759,15 @@ async function discoverAndWireDocs(siteDirectory) {
|
|
|
26639
27759
|
directory.sourceDirectory,
|
|
26640
27760
|
usedNames
|
|
26641
27761
|
);
|
|
26642
|
-
const linkDirectory =
|
|
27762
|
+
const linkDirectory = path85.join(pagesDir, linkName);
|
|
26643
27763
|
const linked = [];
|
|
26644
27764
|
const skipped = [];
|
|
26645
27765
|
for (const sourcePath of directory.files) {
|
|
26646
27766
|
const relativeSource = toPosix3(
|
|
26647
|
-
|
|
27767
|
+
path85.relative(directory.sourceDirectory, sourcePath)
|
|
26648
27768
|
);
|
|
26649
|
-
const linkPath =
|
|
26650
|
-
const relativeLink = toPosix3(
|
|
27769
|
+
const linkPath = path85.join(linkDirectory, relativeSource);
|
|
27770
|
+
const relativeLink = toPosix3(path85.relative(site, linkPath));
|
|
26651
27771
|
const route = toRoutePath(relativeLink, PAGES_DIR);
|
|
26652
27772
|
if (routes.includes(route)) {
|
|
26653
27773
|
skipped.push({
|
|
@@ -26695,11 +27815,11 @@ async function discoverAndWireDocs(siteDirectory) {
|
|
|
26695
27815
|
};
|
|
26696
27816
|
}
|
|
26697
27817
|
function siteRelative(siteDirectory, filePath) {
|
|
26698
|
-
const relative = toPosix3(
|
|
27818
|
+
const relative = toPosix3(path85.relative(siteDirectory, filePath));
|
|
26699
27819
|
if (!relative) {
|
|
26700
27820
|
return ".";
|
|
26701
27821
|
}
|
|
26702
|
-
if (
|
|
27822
|
+
if (path85.isAbsolute(relative)) {
|
|
26703
27823
|
return toPosix3(filePath);
|
|
26704
27824
|
}
|
|
26705
27825
|
if (relative.startsWith("..")) {
|
|
@@ -26787,8 +27907,8 @@ async function pathExists6(filePath) {
|
|
|
26787
27907
|
}
|
|
26788
27908
|
async function wireOpenApiIntoJsonConfig(siteDirectory, openApiPath) {
|
|
26789
27909
|
const input5 = toSiteRelativeOpenApiInput(siteDirectory, openApiPath);
|
|
26790
|
-
const jsonConfigPath =
|
|
26791
|
-
const raw = await
|
|
27910
|
+
const jsonConfigPath = path86.join(siteDirectory, "apitogo.config.json");
|
|
27911
|
+
const raw = await readFile28(jsonConfigPath, "utf8");
|
|
26792
27912
|
const siteConfig = JSON.parse(raw);
|
|
26793
27913
|
const updated = applyOpenApiToSiteConfig(siteConfig, {
|
|
26794
27914
|
input: input5,
|
|
@@ -26796,7 +27916,7 @@ async function wireOpenApiIntoJsonConfig(siteDirectory, openApiPath) {
|
|
|
26796
27916
|
});
|
|
26797
27917
|
const backend = updated.backend && typeof updated.backend === "object" && !Array.isArray(updated.backend) ? { ...updated.backend } : {};
|
|
26798
27918
|
updated.backend = { ...backend, openApiPath: input5 };
|
|
26799
|
-
await
|
|
27919
|
+
await writeFile26(
|
|
26800
27920
|
jsonConfigPath,
|
|
26801
27921
|
`${JSON.stringify(updated, null, 2)}
|
|
26802
27922
|
`,
|
|
@@ -26815,7 +27935,7 @@ async function discoverAndWireOpenApiLite(siteDirectory, mode) {
|
|
|
26815
27935
|
const invalidCandidates = [];
|
|
26816
27936
|
let selected;
|
|
26817
27937
|
for (const candidate of search.candidates) {
|
|
26818
|
-
const content = await
|
|
27938
|
+
const content = await readFile28(candidate, "utf8");
|
|
26819
27939
|
const parsed = liteParseOpenApi(content, candidate);
|
|
26820
27940
|
if (parsed.valid) {
|
|
26821
27941
|
selected = {
|
|
@@ -26839,7 +27959,7 @@ async function discoverAndWireOpenApiLite(siteDirectory, mode) {
|
|
|
26839
27959
|
wired: false
|
|
26840
27960
|
};
|
|
26841
27961
|
}
|
|
26842
|
-
const jsonConfigPath =
|
|
27962
|
+
const jsonConfigPath = path86.join(siteDirectory, "apitogo.config.json");
|
|
26843
27963
|
if (!await pathExists6(jsonConfigPath)) {
|
|
26844
27964
|
return {
|
|
26845
27965
|
openApiDiscovery: {
|
|
@@ -26889,13 +28009,13 @@ async function discoverAndWireBackend(siteDirectory, mode) {
|
|
|
26889
28009
|
if (mode !== "nearby") {
|
|
26890
28010
|
return { status: "skipped", mode };
|
|
26891
28011
|
}
|
|
26892
|
-
const jsonConfigPath =
|
|
28012
|
+
const jsonConfigPath = path86.join(siteDirectory, "apitogo.config.json");
|
|
26893
28013
|
if (!await pathExists6(jsonConfigPath)) {
|
|
26894
28014
|
return { status: "not_found" };
|
|
26895
28015
|
}
|
|
26896
28016
|
try {
|
|
26897
28017
|
const siteConfig = JSON.parse(
|
|
26898
|
-
await
|
|
28018
|
+
await readFile28(jsonConfigPath, "utf8")
|
|
26899
28019
|
);
|
|
26900
28020
|
const existing = siteConfig.backend && typeof siteConfig.backend === "object" && !Array.isArray(siteConfig.backend) ? siteConfig.backend : {};
|
|
26901
28021
|
if (typeof existing.sourceDirectory === "string" && existing.sourceDirectory.trim()) {
|
|
@@ -26916,7 +28036,7 @@ async function discoverAndWireBackend(siteDirectory, mode) {
|
|
|
26916
28036
|
runtime: detected.runtime,
|
|
26917
28037
|
runtimeVersion: detected.runtimeVersion
|
|
26918
28038
|
};
|
|
26919
|
-
await
|
|
28039
|
+
await writeFile26(
|
|
26920
28040
|
jsonConfigPath,
|
|
26921
28041
|
`${JSON.stringify(siteConfig, null, 2)}
|
|
26922
28042
|
`,
|
|
@@ -26956,8 +28076,8 @@ function slugifyName2(name) {
|
|
|
26956
28076
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "dev-portal-site";
|
|
26957
28077
|
}
|
|
26958
28078
|
function resolveInitOutputDirectory(dirArg, name) {
|
|
26959
|
-
const resolved =
|
|
26960
|
-
const base =
|
|
28079
|
+
const resolved = path87.resolve(process.cwd(), dirArg);
|
|
28080
|
+
const base = path87.basename(resolved);
|
|
26961
28081
|
const isDot = dirArg === "." || dirArg === "./" || base === "." || resolved === process.cwd();
|
|
26962
28082
|
if (isDot) {
|
|
26963
28083
|
const fromName = name?.trim();
|
|
@@ -26967,7 +28087,7 @@ function resolveInitOutputDirectory(dirArg, name) {
|
|
|
26967
28087
|
);
|
|
26968
28088
|
}
|
|
26969
28089
|
return {
|
|
26970
|
-
outputDirectory:
|
|
28090
|
+
outputDirectory: path87.resolve(process.cwd(), slugifyName2(fromName)),
|
|
26971
28091
|
siteName: fromName
|
|
26972
28092
|
};
|
|
26973
28093
|
}
|
|
@@ -27111,7 +28231,7 @@ var init_default = {
|
|
|
27111
28231
|
}).option("v2", {
|
|
27112
28232
|
type: "boolean",
|
|
27113
28233
|
default: false,
|
|
27114
|
-
describe: "Preview: create a config version 2 project (standalone or embedded layout). check, run
|
|
28234
|
+
describe: "Preview: create a config version 2 project (standalone or embedded layout). add, remove, check, run, build, plan, status, deploy and secrets read it"
|
|
27115
28235
|
}).option("layout", {
|
|
27116
28236
|
type: "string",
|
|
27117
28237
|
choices: ["standalone", "embedded"],
|
|
@@ -27150,8 +28270,8 @@ import open2 from "open";
|
|
|
27150
28270
|
import colors14 from "picocolors";
|
|
27151
28271
|
|
|
27152
28272
|
// src/cli/platform/device-auth.ts
|
|
27153
|
-
function url(apiUrl,
|
|
27154
|
-
return `${apiUrl}${
|
|
28273
|
+
function url(apiUrl, path100) {
|
|
28274
|
+
return `${apiUrl}${path100.startsWith("/") ? path100 : `/${path100}`}`;
|
|
27155
28275
|
}
|
|
27156
28276
|
async function requestDeviceCode(apiUrl) {
|
|
27157
28277
|
const response = await fetchWithRetry(
|
|
@@ -27333,7 +28453,7 @@ var logout_default = {
|
|
|
27333
28453
|
};
|
|
27334
28454
|
|
|
27335
28455
|
// src/cli/portal/commands.ts
|
|
27336
|
-
import
|
|
28456
|
+
import path88 from "node:path";
|
|
27337
28457
|
init_invariant();
|
|
27338
28458
|
function parseOnly(value) {
|
|
27339
28459
|
if (value === void 0) return void 0;
|
|
@@ -27350,7 +28470,7 @@ function parseOnly(value) {
|
|
|
27350
28470
|
async function run2(command2, argv, compute, format) {
|
|
27351
28471
|
const json = argv.json === true;
|
|
27352
28472
|
try {
|
|
27353
|
-
const dir =
|
|
28473
|
+
const dir = path88.resolve(process.cwd(), argv.dir ?? ".");
|
|
27354
28474
|
if (!await isConfigV2(dir)) {
|
|
27355
28475
|
throw new ServiceEditError(
|
|
27356
28476
|
`${command2} works with a config version 2 project, and there is none in ${dir}.`,
|
|
@@ -27379,8 +28499,34 @@ async function run2(command2, argv, compute, format) {
|
|
|
27379
28499
|
);
|
|
27380
28500
|
}
|
|
27381
28501
|
}
|
|
27382
|
-
var plan = (argv) => run2(
|
|
27383
|
-
|
|
28502
|
+
var plan = (argv) => run2(
|
|
28503
|
+
"plan",
|
|
28504
|
+
argv,
|
|
28505
|
+
(dir, only) => planV2(dir, only, { gatewayDryRun: gatewayDryRun() }),
|
|
28506
|
+
formatPlan
|
|
28507
|
+
);
|
|
28508
|
+
function gatewayRemote() {
|
|
28509
|
+
try {
|
|
28510
|
+
const client = new PlatformApiClient(loadConfig());
|
|
28511
|
+
return (projectId) => client.getGatewayStatus(projectId);
|
|
28512
|
+
} catch {
|
|
28513
|
+
return void 0;
|
|
28514
|
+
}
|
|
28515
|
+
}
|
|
28516
|
+
function gatewayDryRun() {
|
|
28517
|
+
try {
|
|
28518
|
+
const client = new PlatformApiClient(loadConfig());
|
|
28519
|
+
return (projectId, body) => client.planGateway(projectId, body);
|
|
28520
|
+
} catch {
|
|
28521
|
+
return void 0;
|
|
28522
|
+
}
|
|
28523
|
+
}
|
|
28524
|
+
var statusOfProject = (argv) => run2(
|
|
28525
|
+
"status",
|
|
28526
|
+
argv,
|
|
28527
|
+
(dir, only) => statusV2(dir, only, { gatewayRemote: gatewayRemote() }),
|
|
28528
|
+
formatStatus
|
|
28529
|
+
);
|
|
27384
28530
|
|
|
27385
28531
|
// src/cli/cmds/plan.ts
|
|
27386
28532
|
var plan_default = {
|
|
@@ -27406,6 +28552,88 @@ var plan_default = {
|
|
|
27406
28552
|
}
|
|
27407
28553
|
};
|
|
27408
28554
|
|
|
28555
|
+
// src/cli/project/create-handler.ts
|
|
28556
|
+
init_invariant();
|
|
28557
|
+
import path89 from "node:path";
|
|
28558
|
+
|
|
28559
|
+
// src/cli/project/v2.ts
|
|
28560
|
+
init_invariant();
|
|
28561
|
+
var ProjectCreateError = class extends ApitogoError {
|
|
28562
|
+
constructor(code, message, developerHint) {
|
|
28563
|
+
super(message, { developerHint, title: "Cannot create the project" });
|
|
28564
|
+
this.code = code;
|
|
28565
|
+
this.name = "ProjectCreateError";
|
|
28566
|
+
}
|
|
28567
|
+
};
|
|
28568
|
+
function modulesForConfig(config2) {
|
|
28569
|
+
return [
|
|
28570
|
+
...config2.portal && config2.portal.enabled !== false ? ["dev-portal"] : [],
|
|
28571
|
+
...config2.gateway && config2.gateway.enabled !== false ? ["apihub"] : []
|
|
28572
|
+
];
|
|
28573
|
+
}
|
|
28574
|
+
async function createProjectV2(options, deps) {
|
|
28575
|
+
const { config: config2, resolved } = loadConfigV2(options.dir);
|
|
28576
|
+
if (resolved.project.id) {
|
|
28577
|
+
throw new ProjectCreateError(
|
|
28578
|
+
"ALREADY_LINKED",
|
|
28579
|
+
`This folder is already linked to project ${resolved.project.id}.`,
|
|
28580
|
+
"Run `apitogo deploy`. To start over, run `apitogo teardown` first."
|
|
28581
|
+
);
|
|
28582
|
+
}
|
|
28583
|
+
const modules = modulesForConfig(config2);
|
|
28584
|
+
if (modules.length === 0) {
|
|
28585
|
+
throw new ProjectCreateError(
|
|
28586
|
+
"NOTHING_TO_HOST",
|
|
28587
|
+
"No enabled service needs hosting.",
|
|
28588
|
+
"Enable the portal or the gateway first (`apitogo add portal`)."
|
|
28589
|
+
);
|
|
28590
|
+
}
|
|
28591
|
+
const name = options.name?.trim() || resolved.project.name;
|
|
28592
|
+
const raw = await deps.bootstrap({
|
|
28593
|
+
name,
|
|
28594
|
+
description: options.description,
|
|
28595
|
+
modules,
|
|
28596
|
+
wait: options.wait !== false
|
|
28597
|
+
});
|
|
28598
|
+
const outcome = parse3(raw);
|
|
28599
|
+
if (outcome.requiresPlatformPlanSelection) {
|
|
28600
|
+
throw new ProjectCreateError(
|
|
28601
|
+
"PLAN_NOT_SELECTED",
|
|
28602
|
+
`A project named "${name}" already exists and has no plan yet.`,
|
|
28603
|
+
"Pick another name with --name, or choose a plan for that project in the web app."
|
|
28604
|
+
);
|
|
28605
|
+
}
|
|
28606
|
+
if (outcome.status === "failed" || outcome.status === "blocked" || !outcome.projectId) {
|
|
28607
|
+
throw new ProjectCreateError(
|
|
28608
|
+
"CREATE_FAILED",
|
|
28609
|
+
`The platform could not create the project: ${outcome.message ?? outcome.status ?? "no answer"}.`,
|
|
28610
|
+
"Nothing was written to this folder. Try again, or check `apitogo status`."
|
|
28611
|
+
);
|
|
28612
|
+
}
|
|
28613
|
+
if (outcome.status === "running" || outcome.status === "pending") {
|
|
28614
|
+
await setProjectId(options.dir, outcome.projectId);
|
|
28615
|
+
throw new ProjectCreateError(
|
|
28616
|
+
"CREATE_FAILED",
|
|
28617
|
+
"The project was created but hosting is still being prepared.",
|
|
28618
|
+
`The folder is linked (project.id ${outcome.projectId}). Wait a few minutes, then run \`apitogo deploy\`.`
|
|
28619
|
+
);
|
|
28620
|
+
}
|
|
28621
|
+
await setProjectId(options.dir, outcome.projectId);
|
|
28622
|
+
return {
|
|
28623
|
+
projectId: outcome.projectId,
|
|
28624
|
+
projectName: name,
|
|
28625
|
+
modules,
|
|
28626
|
+
resumed: outcome.resumed === true
|
|
28627
|
+
};
|
|
28628
|
+
}
|
|
28629
|
+
function parse3(raw) {
|
|
28630
|
+
try {
|
|
28631
|
+
return JSON.parse(raw);
|
|
28632
|
+
} catch {
|
|
28633
|
+
return { status: "failed", message: "unreadable answer from the platform" };
|
|
28634
|
+
}
|
|
28635
|
+
}
|
|
28636
|
+
|
|
27409
28637
|
// src/cli/project/create-handler.ts
|
|
27410
28638
|
function resultLooksFailed2(payload) {
|
|
27411
28639
|
try {
|
|
@@ -27415,7 +28643,71 @@ function resultLooksFailed2(payload) {
|
|
|
27415
28643
|
return true;
|
|
27416
28644
|
}
|
|
27417
28645
|
}
|
|
28646
|
+
async function createLinkedProject(argv, dir) {
|
|
28647
|
+
const json = argv.json === true;
|
|
28648
|
+
try {
|
|
28649
|
+
const config2 = loadConfig();
|
|
28650
|
+
const client = new PlatformApiClient(config2);
|
|
28651
|
+
const progress = createConsoleProgressReporter();
|
|
28652
|
+
const result = await createProjectV2(
|
|
28653
|
+
{
|
|
28654
|
+
dir,
|
|
28655
|
+
name: argv.name,
|
|
28656
|
+
description: argv.description,
|
|
28657
|
+
wait: argv.wait !== false
|
|
28658
|
+
},
|
|
28659
|
+
{
|
|
28660
|
+
bootstrap: (args) => handleBootstrapProject(
|
|
28661
|
+
client,
|
|
28662
|
+
config2,
|
|
28663
|
+
{
|
|
28664
|
+
name: args.name,
|
|
28665
|
+
description: args.description,
|
|
28666
|
+
modules: args.modules,
|
|
28667
|
+
selectFreePlan: true,
|
|
28668
|
+
wait: args.wait,
|
|
28669
|
+
configureGatewayBaseline: false
|
|
28670
|
+
},
|
|
28671
|
+
json ? void 0 : progress
|
|
28672
|
+
)
|
|
28673
|
+
}
|
|
28674
|
+
);
|
|
28675
|
+
progress.stop();
|
|
28676
|
+
if (json) {
|
|
28677
|
+
console.log(JSON.stringify({ status: "succeeded", ...result }, null, 2));
|
|
28678
|
+
return;
|
|
28679
|
+
}
|
|
28680
|
+
printDiagnosticsToConsole(
|
|
28681
|
+
`Project ${result.projectName} (${result.projectId}) is ready on the free plan with: ${result.modules.join(", ")}.`
|
|
28682
|
+
);
|
|
28683
|
+
printResultToConsole(
|
|
28684
|
+
"The folder is linked. Run `apitogo deploy` to publish."
|
|
28685
|
+
);
|
|
28686
|
+
} catch (error) {
|
|
28687
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
28688
|
+
const hint = error instanceof ApitogoError ? error.developerHint : void 0;
|
|
28689
|
+
if (json) {
|
|
28690
|
+
console.log(
|
|
28691
|
+
JSON.stringify({
|
|
28692
|
+
status: "failed",
|
|
28693
|
+
error: message,
|
|
28694
|
+
hint: hint ?? null
|
|
28695
|
+
})
|
|
28696
|
+
);
|
|
28697
|
+
process.exitCode = 1;
|
|
28698
|
+
return;
|
|
28699
|
+
}
|
|
28700
|
+
await printCriticalFailureToConsoleAndExit(
|
|
28701
|
+
hint ? `${message} ${hint}` : message
|
|
28702
|
+
);
|
|
28703
|
+
}
|
|
28704
|
+
}
|
|
27418
28705
|
async function createProject(argv) {
|
|
28706
|
+
const dir = path89.resolve(process.cwd(), argv.dir ?? ".");
|
|
28707
|
+
if (await isConfigV2(dir)) {
|
|
28708
|
+
await createLinkedProject(argv, dir);
|
|
28709
|
+
return;
|
|
28710
|
+
}
|
|
27419
28711
|
const name = argv.name?.trim();
|
|
27420
28712
|
if (!name) {
|
|
27421
28713
|
await printCriticalFailureToConsoleAndExit(
|
|
@@ -27466,12 +28758,17 @@ async function createProject(argv) {
|
|
|
27466
28758
|
|
|
27467
28759
|
// src/cli/cmds/project-create.ts
|
|
27468
28760
|
var project_create_default = {
|
|
27469
|
-
desc: "Create an API ToGo platform project
|
|
28761
|
+
desc: "Create an API ToGo platform project. In a config version 2 folder: free plan, hosting for the enabled services, and project.id written into the config",
|
|
27470
28762
|
command: "project create",
|
|
27471
28763
|
builder: (yargs2) => yargs2.option("name", {
|
|
27472
28764
|
type: "string",
|
|
27473
|
-
describe: "Project display name"
|
|
27474
|
-
|
|
28765
|
+
describe: "Project display name (required for version 1; version 2 defaults to project.name)"
|
|
28766
|
+
}).option("dir", {
|
|
28767
|
+
type: "string",
|
|
28768
|
+
describe: "Project directory",
|
|
28769
|
+
default: ".",
|
|
28770
|
+
normalize: true,
|
|
28771
|
+
hidden: true
|
|
27475
28772
|
}).option("description", {
|
|
27476
28773
|
type: "string",
|
|
27477
28774
|
describe: "Optional project description"
|
|
@@ -27530,14 +28827,14 @@ var remove_default = {
|
|
|
27530
28827
|
|
|
27531
28828
|
// src/cli/dev/handler.ts
|
|
27532
28829
|
init_joinUrl();
|
|
27533
|
-
import
|
|
28830
|
+
import path92 from "node:path";
|
|
27534
28831
|
|
|
27535
28832
|
// src/vite/dev-server.ts
|
|
27536
28833
|
init_logger();
|
|
27537
28834
|
import fs12 from "node:fs/promises";
|
|
27538
28835
|
import http from "node:http";
|
|
27539
28836
|
import https from "node:https";
|
|
27540
|
-
import
|
|
28837
|
+
import path91 from "node:path";
|
|
27541
28838
|
import { createHttpTerminator } from "http-terminator";
|
|
27542
28839
|
import {
|
|
27543
28840
|
createServer as createViteServer,
|
|
@@ -27570,7 +28867,7 @@ init_loader();
|
|
|
27570
28867
|
// src/vite/pagefind-dev-index.ts
|
|
27571
28868
|
init_invariant();
|
|
27572
28869
|
init_joinUrl();
|
|
27573
|
-
import
|
|
28870
|
+
import path90 from "node:path";
|
|
27574
28871
|
import { createIndex as createIndex2 } from "pagefind";
|
|
27575
28872
|
import { isRunnableDevEnvironment } from "vite";
|
|
27576
28873
|
async function* buildPagefindDevIndex(vite, config2) {
|
|
@@ -27623,7 +28920,7 @@ async function* buildPagefindDevIndex(vite, config2) {
|
|
|
27623
28920
|
path: urlPath
|
|
27624
28921
|
};
|
|
27625
28922
|
}
|
|
27626
|
-
const outputPath =
|
|
28923
|
+
const outputPath = path90.join(vite.config.publicDir, "pagefind");
|
|
27627
28924
|
await pagefindIndex.writeFiles({ outputPath });
|
|
27628
28925
|
yield { type: "complete", success: true, indexed };
|
|
27629
28926
|
}
|
|
@@ -27642,9 +28939,9 @@ var DevServer = class {
|
|
|
27642
28939
|
this.protocol = "https";
|
|
27643
28940
|
const { dir } = this.options;
|
|
27644
28941
|
const [key, cert, ca] = await Promise.all([
|
|
27645
|
-
fs12.readFile(
|
|
27646
|
-
fs12.readFile(
|
|
27647
|
-
config2.https.ca ? fs12.readFile(
|
|
28942
|
+
fs12.readFile(path91.resolve(dir, config2.https.key)),
|
|
28943
|
+
fs12.readFile(path91.resolve(dir, config2.https.cert)),
|
|
28944
|
+
config2.https.ca ? fs12.readFile(path91.resolve(dir, config2.https.ca)) : void 0
|
|
27648
28945
|
]);
|
|
27649
28946
|
return https.createServer({ key, cert, ca });
|
|
27650
28947
|
}
|
|
@@ -27661,7 +28958,7 @@ var DevServer = class {
|
|
|
27661
28958
|
);
|
|
27662
28959
|
const server = await this.createNodeServer(config2);
|
|
27663
28960
|
if (config2.search?.type === "pagefind" && viteConfig.publicDir !== false) {
|
|
27664
|
-
const publicRoot =
|
|
28961
|
+
const publicRoot = path91.resolve(
|
|
27665
28962
|
this.options.dir,
|
|
27666
28963
|
typeof viteConfig.publicDir === "string" ? viteConfig.publicDir : "public"
|
|
27667
28964
|
);
|
|
@@ -27678,7 +28975,7 @@ var DevServer = class {
|
|
|
27678
28975
|
// built-in transform middleware which would treat the path as a static asset.
|
|
27679
28976
|
name: "apitogo:entry-client",
|
|
27680
28977
|
configureServer(server2) {
|
|
27681
|
-
const entryPath =
|
|
28978
|
+
const entryPath = path91.posix.join(
|
|
27682
28979
|
server2.config.base,
|
|
27683
28980
|
"/__z/entry.client.tsx"
|
|
27684
28981
|
);
|
|
@@ -27843,7 +29140,7 @@ init_package_json();
|
|
|
27843
29140
|
async function dev(argv) {
|
|
27844
29141
|
const packageJson3 = getZudokuPackageJson();
|
|
27845
29142
|
process.env.NODE_ENV = "development";
|
|
27846
|
-
let dir =
|
|
29143
|
+
let dir = path92.resolve(process.cwd(), argv.dir);
|
|
27847
29144
|
if (await isConfigV2(dir)) {
|
|
27848
29145
|
const { root, prepared } = await preparePortalWorkspace(dir, "development");
|
|
27849
29146
|
dir = root;
|
|
@@ -27942,7 +29239,7 @@ var run_default = {
|
|
|
27942
29239
|
};
|
|
27943
29240
|
|
|
27944
29241
|
// src/cli/secrets/handler.ts
|
|
27945
|
-
import
|
|
29242
|
+
import path93 from "node:path";
|
|
27946
29243
|
init_invariant();
|
|
27947
29244
|
|
|
27948
29245
|
// src/cli/secrets/input.ts
|
|
@@ -28007,7 +29304,7 @@ var SecretsError = class extends ApitogoError {
|
|
|
28007
29304
|
async function resolveProjectId2(argv, deps) {
|
|
28008
29305
|
const explicit = argv.projectId?.trim();
|
|
28009
29306
|
if (explicit) return explicit;
|
|
28010
|
-
const dir =
|
|
29307
|
+
const dir = path93.resolve(process.cwd(), argv.dir ?? ".");
|
|
28011
29308
|
if (await isConfigV2(dir)) {
|
|
28012
29309
|
const id = loadConfigV2(dir).resolved.project.id;
|
|
28013
29310
|
if (id) return id;
|
|
@@ -28167,12 +29464,12 @@ var secrets_default = {
|
|
|
28167
29464
|
};
|
|
28168
29465
|
|
|
28169
29466
|
// src/cli/cmds/status.ts
|
|
28170
|
-
import
|
|
29467
|
+
import path95 from "node:path";
|
|
28171
29468
|
|
|
28172
29469
|
// src/cli/status/handler.ts
|
|
28173
|
-
import
|
|
29470
|
+
import path94 from "node:path";
|
|
28174
29471
|
async function status(argv) {
|
|
28175
|
-
const sourceDirectory =
|
|
29472
|
+
const sourceDirectory = path94.resolve(process.cwd(), argv.dir);
|
|
28176
29473
|
let config2;
|
|
28177
29474
|
try {
|
|
28178
29475
|
config2 = loadConfig();
|
|
@@ -28270,7 +29567,7 @@ var status_default = {
|
|
|
28270
29567
|
}),
|
|
28271
29568
|
handler: async (argv) => {
|
|
28272
29569
|
await captureEvent({ argv, event: "apitogo status" });
|
|
28273
|
-
if (await isConfigV2(
|
|
29570
|
+
if (await isConfigV2(path95.resolve(process.cwd(), argv.dir))) {
|
|
28274
29571
|
await statusOfProject(argv);
|
|
28275
29572
|
return;
|
|
28276
29573
|
}
|
|
@@ -28278,8 +29575,287 @@ var status_default = {
|
|
|
28278
29575
|
}
|
|
28279
29576
|
};
|
|
28280
29577
|
|
|
29578
|
+
// src/cli/cmds/teardown.ts
|
|
29579
|
+
import path98 from "node:path";
|
|
29580
|
+
|
|
29581
|
+
// src/cli/teardown/command.ts
|
|
29582
|
+
init_invariant();
|
|
29583
|
+
import path96 from "node:path";
|
|
29584
|
+
import { createInterface as createInterface7 } from "node:readline/promises";
|
|
29585
|
+
|
|
29586
|
+
// src/cli/teardown/v2.ts
|
|
29587
|
+
init_invariant();
|
|
29588
|
+
var TeardownError = class extends ApitogoError {
|
|
29589
|
+
constructor(code, message, developerHint) {
|
|
29590
|
+
super(message, { developerHint, title: "Cannot tear down" });
|
|
29591
|
+
this.code = code;
|
|
29592
|
+
this.name = "TeardownError";
|
|
29593
|
+
}
|
|
29594
|
+
};
|
|
29595
|
+
async function teardownV2(options, deps) {
|
|
29596
|
+
if (options.only) {
|
|
29597
|
+
if (options.only.trim() === "gateway")
|
|
29598
|
+
return teardownGateway(options, deps);
|
|
29599
|
+
throw new TeardownError(
|
|
29600
|
+
"PARTIAL_TEARDOWN_UNSUPPORTED",
|
|
29601
|
+
"The platform cannot release the hosting of one service on its own: it is released by deleting the project.",
|
|
29602
|
+
"Run `apitogo teardown` without --only to delete the whole project, or `apitogo teardown --only gateway` to remove just the gateway. Your local files are kept."
|
|
29603
|
+
);
|
|
29604
|
+
}
|
|
29605
|
+
const { resolved } = loadConfigV2(options.dir);
|
|
29606
|
+
const projectId = options.projectId?.trim() || resolved.project.id || deps.envProjectId?.trim() || "";
|
|
29607
|
+
if (!projectId) {
|
|
29608
|
+
throw new TeardownError(
|
|
29609
|
+
"PROJECT_REQUIRED",
|
|
29610
|
+
"This folder is not linked to a project, so there is nothing to tear down.",
|
|
29611
|
+
"Pass --project-id <id> to name the project."
|
|
29612
|
+
);
|
|
29613
|
+
}
|
|
29614
|
+
const project = await deps.client.getProject(projectId);
|
|
29615
|
+
const name = project.name;
|
|
29616
|
+
if (options.yes !== true) {
|
|
29617
|
+
if (!deps.isTerminal) {
|
|
29618
|
+
throw new TeardownError(
|
|
29619
|
+
"CONFIRMATION_REQUIRED",
|
|
29620
|
+
"This is not an interactive terminal.",
|
|
29621
|
+
"Pass --yes to delete the project without asking."
|
|
29622
|
+
);
|
|
29623
|
+
}
|
|
29624
|
+
const typed = await deps.askName(
|
|
29625
|
+
`This deletes the project "${name}" (${projectId}) and its hosted site from the platform. Your local files are kept.
|
|
29626
|
+
Type the project name to confirm: `
|
|
29627
|
+
);
|
|
29628
|
+
if (typed.trim() !== name) {
|
|
29629
|
+
throw new TeardownError(
|
|
29630
|
+
"CANCELLED",
|
|
29631
|
+
"The name did not match. Nothing was deleted."
|
|
29632
|
+
);
|
|
29633
|
+
}
|
|
29634
|
+
}
|
|
29635
|
+
const raw = await deps.deleteProject(projectId, options.wait !== false);
|
|
29636
|
+
const outcome = parseOutcome(raw);
|
|
29637
|
+
if (outcome.status === "failed" || outcome.status === "blocked") {
|
|
29638
|
+
throw new TeardownError(
|
|
29639
|
+
"DELETE_FAILED",
|
|
29640
|
+
`The platform could not delete the project: ${outcome.message ?? outcome.status}.`,
|
|
29641
|
+
"Nothing local was changed. Try again, or contact support with the project id."
|
|
29642
|
+
);
|
|
29643
|
+
}
|
|
29644
|
+
if (outcome.status !== "succeeded") {
|
|
29645
|
+
return {
|
|
29646
|
+
projectId,
|
|
29647
|
+
projectName: name,
|
|
29648
|
+
status: "pending",
|
|
29649
|
+
cleared: [],
|
|
29650
|
+
message: "Deletion is queued and runs in the background. Local records are kept until it finishes: run `apitogo teardown` again later to clear them."
|
|
29651
|
+
};
|
|
29652
|
+
}
|
|
29653
|
+
const linked = await isLinkedTo(options.dir, projectId);
|
|
29654
|
+
const cleared = linked ? await clearLocalRecords(options.dir, projectId) : [];
|
|
29655
|
+
return {
|
|
29656
|
+
projectId,
|
|
29657
|
+
projectName: name,
|
|
29658
|
+
status: "removed",
|
|
29659
|
+
cleared,
|
|
29660
|
+
message: linked ? "The project was deleted. Your local files are kept." : "The project was deleted. This folder is linked to a different project, so its local records were left as they are."
|
|
29661
|
+
};
|
|
29662
|
+
}
|
|
29663
|
+
function parseOutcome(raw) {
|
|
29664
|
+
try {
|
|
29665
|
+
const parsed = JSON.parse(raw);
|
|
29666
|
+
return { status: parsed.status ?? "unknown", message: parsed.message };
|
|
29667
|
+
} catch {
|
|
29668
|
+
return { status: "failed", message: "unreadable answer from the platform" };
|
|
29669
|
+
}
|
|
29670
|
+
}
|
|
29671
|
+
async function isLinkedTo(dir, projectId) {
|
|
29672
|
+
const { resolved } = loadConfigV2(dir);
|
|
29673
|
+
if (resolved.project.id === projectId) return true;
|
|
29674
|
+
return (await readManifest(resolved.state))?.project.id === projectId;
|
|
29675
|
+
}
|
|
29676
|
+
async function clearLocalRecords(dir, projectId) {
|
|
29677
|
+
const { resolved } = loadConfigV2(dir);
|
|
29678
|
+
const cleared = [];
|
|
29679
|
+
const ledger = await readLedger(resolved.state);
|
|
29680
|
+
const recorded = DEPLOY_ORDER.filter((service) => ledger.services[service]);
|
|
29681
|
+
if (recorded.length > 0) {
|
|
29682
|
+
await clearServices(resolved.state, recorded);
|
|
29683
|
+
cleared.push("state.json entries");
|
|
29684
|
+
}
|
|
29685
|
+
await removeServicesFromManifest(resolved.state, [...DEPLOY_ORDER]);
|
|
29686
|
+
cleared.push("services.json");
|
|
29687
|
+
if (await clearProjectId(dir, projectId))
|
|
29688
|
+
cleared.push("project.id in the config");
|
|
29689
|
+
return cleared;
|
|
29690
|
+
}
|
|
29691
|
+
async function teardownGateway(options, deps) {
|
|
29692
|
+
const { resolved } = loadConfigV2(options.dir);
|
|
29693
|
+
const projectId = options.projectId?.trim() || resolved.project.id || deps.envProjectId?.trim() || "";
|
|
29694
|
+
if (!projectId) {
|
|
29695
|
+
throw new TeardownError(
|
|
29696
|
+
"PROJECT_REQUIRED",
|
|
29697
|
+
"This folder is not linked to a project, so there is no gateway to remove.",
|
|
29698
|
+
"Pass --project-id <id> to name the project."
|
|
29699
|
+
);
|
|
29700
|
+
}
|
|
29701
|
+
if (!deps.client.deleteGateway || !deps.client.getGatewayReconciliation)
|
|
29702
|
+
throw new Error("The platform client has no gateway calls.");
|
|
29703
|
+
const project = await deps.client.getProject(projectId);
|
|
29704
|
+
const name = project.name;
|
|
29705
|
+
if (options.yes !== true) {
|
|
29706
|
+
if (!deps.isTerminal) {
|
|
29707
|
+
throw new TeardownError(
|
|
29708
|
+
"CONFIRMATION_REQUIRED",
|
|
29709
|
+
"This is not an interactive terminal.",
|
|
29710
|
+
"Pass --yes to remove the gateway without asking."
|
|
29711
|
+
);
|
|
29712
|
+
}
|
|
29713
|
+
const typed = await deps.askName(
|
|
29714
|
+
`This removes the gateway of project "${name}" (${projectId}). Its API keys are revoked and stop working, and the gateway address is released. The project and the portal stay.
|
|
29715
|
+
Type the project name to confirm: `
|
|
29716
|
+
);
|
|
29717
|
+
if (typed.trim() !== name) {
|
|
29718
|
+
throw new TeardownError(
|
|
29719
|
+
"CANCELLED",
|
|
29720
|
+
"The name did not match. Nothing was removed."
|
|
29721
|
+
);
|
|
29722
|
+
}
|
|
29723
|
+
}
|
|
29724
|
+
let ticket;
|
|
29725
|
+
try {
|
|
29726
|
+
ticket = await deps.client.deleteGateway(projectId);
|
|
29727
|
+
} catch (error) {
|
|
29728
|
+
if (error instanceof PlatformApiError && error.status === 404)
|
|
29729
|
+
throw new TeardownError(
|
|
29730
|
+
"GATEWAY_NOT_DEPLOYED",
|
|
29731
|
+
"The platform has no gateway for this project.",
|
|
29732
|
+
"There is nothing to remove. Clear stale local records by deleting the gateway entries in the state folder."
|
|
29733
|
+
);
|
|
29734
|
+
throw error;
|
|
29735
|
+
}
|
|
29736
|
+
const sleep5 = deps.gateway?.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
29737
|
+
const deadline = Date.now() + (options.wait === false ? 0 : deps.gateway?.timeoutMs ?? 12e4);
|
|
29738
|
+
for (; ; ) {
|
|
29739
|
+
const state = await deps.client.getGatewayReconciliation(
|
|
29740
|
+
projectId,
|
|
29741
|
+
ticket.reconciliationId
|
|
29742
|
+
);
|
|
29743
|
+
if (state.state === "Succeeded") break;
|
|
29744
|
+
if (state.state === "Failed" || state.state === "Superseded") {
|
|
29745
|
+
throw new TeardownError(
|
|
29746
|
+
"DELETE_FAILED",
|
|
29747
|
+
`The platform could not remove the gateway: ${[state.errorCode, state.errorMessage].filter(Boolean).join(" ") || state.state}.`,
|
|
29748
|
+
"Nothing local was changed. Try again, or contact support with the project id."
|
|
29749
|
+
);
|
|
29750
|
+
}
|
|
29751
|
+
if (Date.now() >= deadline) {
|
|
29752
|
+
return {
|
|
29753
|
+
projectId,
|
|
29754
|
+
projectName: name,
|
|
29755
|
+
status: "pending",
|
|
29756
|
+
cleared: [],
|
|
29757
|
+
message: "The gateway is queued for removal and the platform finishes it in the background. Local records are kept until then: run `apitogo teardown --only gateway` again later to clear them."
|
|
29758
|
+
};
|
|
29759
|
+
}
|
|
29760
|
+
await sleep5(deps.gateway?.pollIntervalMs ?? 2e3);
|
|
29761
|
+
}
|
|
29762
|
+
const cleared = [];
|
|
29763
|
+
const ledger = await readLedger(resolved.state);
|
|
29764
|
+
if (ledger.services.gateway) {
|
|
29765
|
+
await clearServices(resolved.state, ["gateway"]);
|
|
29766
|
+
cleared.push("state.json entries of the gateway");
|
|
29767
|
+
}
|
|
29768
|
+
const manifest = await readManifest(resolved.state);
|
|
29769
|
+
if (manifest?.services.gateway) {
|
|
29770
|
+
await removeServicesFromManifest(resolved.state, ["gateway"]);
|
|
29771
|
+
cleared.push("the gateway entry of services.json");
|
|
29772
|
+
}
|
|
29773
|
+
return {
|
|
29774
|
+
projectId,
|
|
29775
|
+
projectName: name,
|
|
29776
|
+
status: "removed",
|
|
29777
|
+
cleared,
|
|
29778
|
+
message: "The gateway was removed. The project and your local files are kept."
|
|
29779
|
+
};
|
|
29780
|
+
}
|
|
29781
|
+
|
|
29782
|
+
// src/cli/teardown/command.ts
|
|
29783
|
+
async function ask3(prompt) {
|
|
29784
|
+
const rl = createInterface7({ input: process.stdin, output: process.stdout });
|
|
29785
|
+
try {
|
|
29786
|
+
return await rl.question(prompt);
|
|
29787
|
+
} finally {
|
|
29788
|
+
rl.close();
|
|
29789
|
+
}
|
|
29790
|
+
}
|
|
29791
|
+
async function teardownProject(argv) {
|
|
29792
|
+
const json = argv.json === true;
|
|
29793
|
+
try {
|
|
29794
|
+
const config2 = loadConfig();
|
|
29795
|
+
const client = new PlatformApiClient(config2);
|
|
29796
|
+
const progress = createConsoleProgressReporter();
|
|
29797
|
+
const result = await teardownV2(
|
|
29798
|
+
{
|
|
29799
|
+
dir: path96.resolve(process.cwd(), argv.dir),
|
|
29800
|
+
projectId: argv.projectId,
|
|
29801
|
+
only: Array.isArray(argv.only) ? argv.only.join(",") : argv.only,
|
|
29802
|
+
yes: argv.yes === true,
|
|
29803
|
+
wait: argv.wait !== false
|
|
29804
|
+
},
|
|
29805
|
+
{
|
|
29806
|
+
client,
|
|
29807
|
+
envProjectId: config2.defaultProjectId,
|
|
29808
|
+
isTerminal: process.stdin.isTTY === true && !json,
|
|
29809
|
+
askName: ask3,
|
|
29810
|
+
deleteProject: (projectId, wait) => handleDeleteProject(
|
|
29811
|
+
client,
|
|
29812
|
+
config2,
|
|
29813
|
+
{ projectId, wait },
|
|
29814
|
+
json ? void 0 : progress
|
|
29815
|
+
)
|
|
29816
|
+
}
|
|
29817
|
+
);
|
|
29818
|
+
progress.stop();
|
|
29819
|
+
if (json) {
|
|
29820
|
+
console.log(
|
|
29821
|
+
JSON.stringify(
|
|
29822
|
+
{ ...result, outcome: result.status, status: "succeeded" },
|
|
29823
|
+
null,
|
|
29824
|
+
2
|
|
29825
|
+
)
|
|
29826
|
+
);
|
|
29827
|
+
return;
|
|
29828
|
+
}
|
|
29829
|
+
printDiagnosticsToConsole(
|
|
29830
|
+
`${result.status === "removed" ? "Deleted" : "Queued deletion of"} project ${result.projectName} (${result.projectId}).`
|
|
29831
|
+
);
|
|
29832
|
+
if (result.cleared.length > 0)
|
|
29833
|
+
printDiagnosticsToConsole(
|
|
29834
|
+
`Cleared locally: ${result.cleared.join(", ")}.`
|
|
29835
|
+
);
|
|
29836
|
+
printResultToConsole(result.message);
|
|
29837
|
+
} catch (error) {
|
|
29838
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29839
|
+
const hint = error instanceof ApitogoError ? error.developerHint : void 0;
|
|
29840
|
+
if (json) {
|
|
29841
|
+
console.log(
|
|
29842
|
+
JSON.stringify({
|
|
29843
|
+
status: "failed",
|
|
29844
|
+
error: message,
|
|
29845
|
+
hint: hint ?? null
|
|
29846
|
+
})
|
|
29847
|
+
);
|
|
29848
|
+
process.exitCode = 1;
|
|
29849
|
+
return;
|
|
29850
|
+
}
|
|
29851
|
+
await printCriticalFailureToConsoleAndExit(
|
|
29852
|
+
hint ? `${message} ${hint}` : message
|
|
29853
|
+
);
|
|
29854
|
+
}
|
|
29855
|
+
}
|
|
29856
|
+
|
|
28281
29857
|
// src/cli/teardown/handler.ts
|
|
28282
|
-
import
|
|
29858
|
+
import path97 from "node:path";
|
|
28283
29859
|
function resultLooksFailed3(payload) {
|
|
28284
29860
|
try {
|
|
28285
29861
|
const parsed = JSON.parse(payload);
|
|
@@ -28295,7 +29871,7 @@ async function teardown(argv) {
|
|
|
28295
29871
|
);
|
|
28296
29872
|
return;
|
|
28297
29873
|
}
|
|
28298
|
-
const sourceDirectory =
|
|
29874
|
+
const sourceDirectory = path97.resolve(process.cwd(), argv.dir);
|
|
28299
29875
|
let config2;
|
|
28300
29876
|
try {
|
|
28301
29877
|
config2 = loadConfig();
|
|
@@ -28344,7 +29920,7 @@ async function teardown(argv) {
|
|
|
28344
29920
|
|
|
28345
29921
|
// src/cli/cmds/teardown.ts
|
|
28346
29922
|
var teardown_default = {
|
|
28347
|
-
desc: "Delete the linked API ToGo project",
|
|
29923
|
+
desc: "Delete the linked API ToGo project. Config version 2 asks you to type the project name; local files are kept",
|
|
28348
29924
|
command: "teardown",
|
|
28349
29925
|
builder: (yargs2) => yargs2.option("dir", {
|
|
28350
29926
|
type: "string",
|
|
@@ -28354,10 +29930,13 @@ var teardown_default = {
|
|
|
28354
29930
|
}).option("project-id", {
|
|
28355
29931
|
type: "string",
|
|
28356
29932
|
describe: "Override project GUID"
|
|
29933
|
+
}).option("only", {
|
|
29934
|
+
type: "string",
|
|
29935
|
+
describe: "Config version 2: only `gateway` is supported (removes the gateway, keeps the project); other hosting is released only by deleting the whole project"
|
|
28357
29936
|
}).option("yes", {
|
|
28358
29937
|
alias: "y",
|
|
28359
29938
|
type: "boolean",
|
|
28360
|
-
describe: "Confirm
|
|
29939
|
+
describe: "Confirm without asking (version 1: required)",
|
|
28361
29940
|
default: false
|
|
28362
29941
|
}).option("wait", {
|
|
28363
29942
|
type: "boolean",
|
|
@@ -28370,6 +29949,10 @@ var teardown_default = {
|
|
|
28370
29949
|
}),
|
|
28371
29950
|
handler: async (argv) => {
|
|
28372
29951
|
await captureEvent({ argv, event: "apitogo teardown" });
|
|
29952
|
+
if (await isConfigV2(path98.resolve(process.cwd(), argv.dir))) {
|
|
29953
|
+
await teardownProject(argv);
|
|
29954
|
+
return;
|
|
29955
|
+
}
|
|
28373
29956
|
await teardown(argv);
|
|
28374
29957
|
}
|
|
28375
29958
|
};
|
|
@@ -28438,42 +30021,42 @@ To fix, run:
|
|
|
28438
30021
|
var packageJson = getZudokuPackageJson();
|
|
28439
30022
|
var command = (module) => module;
|
|
28440
30023
|
function createApitogoCli(argv) {
|
|
28441
|
-
return yargs(argv).scriptName("apitogo").middleware(warnPackageVersionMismatch).command(command(init_default)).command(command(add_default)).command(command(remove_default)).command(command(config_default)).command(command(run_default)).command(command(build_default)).command(command(check_default)).command(command(plan_default)).command(command(secrets_default)).command(command(project_create_default)).command(command(deploy_default)).command(command(status_default)).command(command(teardown_default)).command(command(login_default)).command(command(logout_default)).demandCommand().strictCommands().version(packageJson?.version).fail(false).help();
|
|
30024
|
+
return yargs(argv).scriptName("apitogo").middleware(warnPackageVersionMismatch).command(command(init_default)).command(command(add_default)).command(command(remove_default)).command(command(config_default)).command(command(run_default)).command(command(build_default)).command(command(check_default)).command(command(plan_default)).command(command(secrets_default)).command(command(project_create_default)).command(command(deploy_default)).command(command(export_default)).command(command(status_default)).command(command(teardown_default)).command(command(login_default)).command(command(logout_default)).demandCommand().strictCommands().version(packageJson?.version).fail(false).help();
|
|
28442
30025
|
}
|
|
28443
30026
|
|
|
28444
30027
|
// src/cli/local-handoff.ts
|
|
28445
|
-
import { spawn as
|
|
28446
|
-
import { existsSync as
|
|
28447
|
-
import
|
|
30028
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
30029
|
+
import { existsSync as existsSync8, realpathSync } from "node:fs";
|
|
30030
|
+
import path99 from "node:path";
|
|
28448
30031
|
var LOCAL_HANDOFF_ENV = "APITOGO_LOCAL_CLI";
|
|
28449
30032
|
var PACKAGE_NAME = "@lukoweb/apitogo";
|
|
28450
30033
|
var resolveProjectDir = (argv, cwd) => {
|
|
28451
30034
|
for (let i = 0; i < argv.length; i++) {
|
|
28452
30035
|
const arg = argv[i];
|
|
28453
30036
|
const next = argv[i + 1];
|
|
28454
|
-
if (arg === "--dir" && next) return
|
|
30037
|
+
if (arg === "--dir" && next) return path99.resolve(cwd, next);
|
|
28455
30038
|
if (arg?.startsWith("--dir=")) {
|
|
28456
|
-
return
|
|
30039
|
+
return path99.resolve(cwd, arg.slice("--dir=".length));
|
|
28457
30040
|
}
|
|
28458
30041
|
}
|
|
28459
30042
|
return cwd;
|
|
28460
30043
|
};
|
|
28461
30044
|
var findLocalCli = (projectDir, runningRoot) => {
|
|
28462
|
-
let dir =
|
|
30045
|
+
let dir = path99.resolve(projectDir);
|
|
28463
30046
|
let localPackageRoot;
|
|
28464
30047
|
while (!localPackageRoot) {
|
|
28465
|
-
const candidate =
|
|
28466
|
-
if (
|
|
30048
|
+
const candidate = path99.join(dir, "node_modules", PACKAGE_NAME);
|
|
30049
|
+
if (existsSync8(path99.join(candidate, "package.json"))) {
|
|
28467
30050
|
localPackageRoot = candidate;
|
|
28468
|
-
} else if (dir ===
|
|
30051
|
+
} else if (dir === path99.dirname(dir)) {
|
|
28469
30052
|
return void 0;
|
|
28470
30053
|
} else {
|
|
28471
|
-
dir =
|
|
30054
|
+
dir = path99.dirname(dir);
|
|
28472
30055
|
}
|
|
28473
30056
|
}
|
|
28474
30057
|
const localRoot = realpathSync(localPackageRoot);
|
|
28475
30058
|
if (localRoot === realpathSync(runningRoot)) return void 0;
|
|
28476
|
-
return
|
|
30059
|
+
return path99.join(localRoot, "cli.js");
|
|
28477
30060
|
};
|
|
28478
30061
|
var handoffToLocalCli = async (argv, runningRoot) => {
|
|
28479
30062
|
if (process.env[LOCAL_HANDOFF_ENV] || process.env.ZUDOKU_ENV === "internal") {
|
|
@@ -28485,9 +30068,9 @@ var handoffToLocalCli = async (argv, runningRoot) => {
|
|
|
28485
30068
|
);
|
|
28486
30069
|
if (!localCli) return void 0;
|
|
28487
30070
|
printDiagnosticsToConsole(
|
|
28488
|
-
`Using the project's local ${PACKAGE_NAME} (${
|
|
30071
|
+
`Using the project's local ${PACKAGE_NAME} (${path99.dirname(localCli)})`
|
|
28489
30072
|
);
|
|
28490
|
-
const child =
|
|
30073
|
+
const child = spawn5(process.execPath, [localCli, ...argv], {
|
|
28491
30074
|
stdio: "inherit",
|
|
28492
30075
|
env: { ...process.env, [LOCAL_HANDOFF_ENV]: "1" }
|
|
28493
30076
|
});
|