@lukoweb/apitogo 0.2.21 → 0.2.23
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 +1714 -408
- package/dist/declarations/cli/platform/client/platform-api.d.ts +22 -0
- package/dist/declarations/cli/platform/tools/lifecycle.d.ts +3 -0
- package/docs/configuration/gateway.md +221 -0
- package/package.json +1 -1
- package/src/config/v2/index.ts +2 -0
- package/src/config/v2/manifest.ts +89 -0
- package/src/config/v2/services.ts +4 -2
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 = (path104) => stat4(path104).then(() => true).catch(() => false);
|
|
840
840
|
}
|
|
841
841
|
});
|
|
842
842
|
|
|
@@ -5814,19 +5814,19 @@ 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(issue4) {
|
|
5818
|
+
const hint = issue4.hint ? ` ${issue4.hint}` : "";
|
|
5819
|
+
const rest = issue4.message.slice(issue4.path.length);
|
|
5820
|
+
const startsWithPath = issue4.path !== "" && issue4.message.startsWith(issue4.path) && (rest === "" || /^[\s:]/.test(rest));
|
|
5821
|
+
const text = startsWithPath ? issue4.message : `${issue4.path === "" ? "(root)" : issue4.path}: ${issue4.message}`;
|
|
5822
|
+
return `${text}${hint} [${issue4.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((issue4) => ` - ${formatConfigIssue(issue4)}`).join("\n")}`,
|
|
5830
5830
|
{ title: "Invalid apitogo config" }
|
|
5831
5831
|
);
|
|
5832
5832
|
this.name = "ConfigError";
|
|
@@ -6189,8 +6189,8 @@ var ConfigV2Schema = z.strictObject({
|
|
|
6189
6189
|
var REMOVED_KEYS = {
|
|
6190
6190
|
"plans.provider": "plans.provider was removed. Only plans.billing.provider (stripe or none) is selectable."
|
|
6191
6191
|
};
|
|
6192
|
-
function pathToString(
|
|
6193
|
-
return
|
|
6192
|
+
function pathToString(path104) {
|
|
6193
|
+
return path104.reduce(
|
|
6194
6194
|
(acc, segment) => typeof segment === "number" ? `${acc}[${segment}]` : acc === "" ? String(segment) : `${acc}.${String(segment)}`,
|
|
6195
6195
|
""
|
|
6196
6196
|
);
|
|
@@ -6210,9 +6210,9 @@ function step(schema2, segment) {
|
|
|
6210
6210
|
}
|
|
6211
6211
|
return void 0;
|
|
6212
6212
|
}
|
|
6213
|
-
function knownKeysAt(
|
|
6213
|
+
function knownKeysAt(path104) {
|
|
6214
6214
|
let current = ConfigV2Schema;
|
|
6215
|
-
for (const segment of
|
|
6215
|
+
for (const segment of path104) {
|
|
6216
6216
|
if (!current) return [];
|
|
6217
6217
|
current = step(current, segment);
|
|
6218
6218
|
}
|
|
@@ -6247,37 +6247,37 @@ function unknownKeyHint(fullPath, key, known) {
|
|
|
6247
6247
|
if (closest) return `Did you mean "${closest.candidate}"?`;
|
|
6248
6248
|
return known.length > 0 ? `Allowed keys: ${known.join(", ")}.` : void 0;
|
|
6249
6249
|
}
|
|
6250
|
-
function mapIssue(
|
|
6251
|
-
const
|
|
6252
|
-
switch (
|
|
6250
|
+
function mapIssue(issue4) {
|
|
6251
|
+
const path104 = pathToString(issue4.path);
|
|
6252
|
+
switch (issue4.code) {
|
|
6253
6253
|
case "unrecognized_keys":
|
|
6254
|
-
return
|
|
6255
|
-
const fullPath = pathToString([...
|
|
6254
|
+
return issue4.keys.map((key) => {
|
|
6255
|
+
const fullPath = pathToString([...issue4.path, key]);
|
|
6256
6256
|
return {
|
|
6257
6257
|
code: "CONFIG_UNKNOWN_KEY",
|
|
6258
6258
|
path: fullPath,
|
|
6259
6259
|
message: `Unknown key "${key}".`,
|
|
6260
|
-
hint: unknownKeyHint(fullPath, key, knownKeysAt(
|
|
6260
|
+
hint: unknownKeyHint(fullPath, key, knownKeysAt(issue4.path))
|
|
6261
6261
|
};
|
|
6262
6262
|
});
|
|
6263
6263
|
case "invalid_value":
|
|
6264
6264
|
return [
|
|
6265
6265
|
{
|
|
6266
6266
|
code: "CONFIG_INVALID_VALUE",
|
|
6267
|
-
path:
|
|
6268
|
-
message: `must be one of: ${
|
|
6267
|
+
path: path104,
|
|
6268
|
+
message: `must be one of: ${issue4.values.map((v) => JSON.stringify(v)).join(", ")}.`
|
|
6269
6269
|
}
|
|
6270
6270
|
];
|
|
6271
6271
|
case "custom": {
|
|
6272
|
-
const code = typeof
|
|
6273
|
-
return [{ code, path:
|
|
6272
|
+
const code = typeof issue4.params?.code === "string" ? issue4.params.code : "CONFIG_INVALID_VALUE";
|
|
6273
|
+
return [{ code, path: path104, message: issue4.message ?? "is invalid." }];
|
|
6274
6274
|
}
|
|
6275
6275
|
case "invalid_type":
|
|
6276
6276
|
return [
|
|
6277
|
-
/received undefined/.test(
|
|
6277
|
+
/received undefined/.test(issue4.message) ? { code: "CONFIG_MISSING_FIELD", path: path104, message: "is required." } : { code: "CONFIG_INVALID_VALUE", path: path104, message: issue4.message }
|
|
6278
6278
|
];
|
|
6279
6279
|
default:
|
|
6280
|
-
return [{ code: "CONFIG_INVALID_VALUE", path:
|
|
6280
|
+
return [{ code: "CONFIG_INVALID_VALUE", path: path104, message: issue4.message }];
|
|
6281
6281
|
}
|
|
6282
6282
|
}
|
|
6283
6283
|
function isRecord(value) {
|
|
@@ -6727,7 +6727,7 @@ function dependencyRules(config2, resolved, out) {
|
|
|
6727
6727
|
function checkDependencies(config2, resolved) {
|
|
6728
6728
|
const issues = [];
|
|
6729
6729
|
dependencyRules(config2, resolved, {
|
|
6730
|
-
error: (
|
|
6730
|
+
error: (issue4) => void issues.push(issue4),
|
|
6731
6731
|
warn: () => void 0
|
|
6732
6732
|
});
|
|
6733
6733
|
return issues;
|
|
@@ -7197,8 +7197,8 @@ async function checkConfigV2(config2, resolved, context = {}) {
|
|
|
7197
7197
|
"online rules were not run (provider capabilities, origin reachability, OIDC discovery, tier limits)"
|
|
7198
7198
|
];
|
|
7199
7199
|
const out = {
|
|
7200
|
-
error: (
|
|
7201
|
-
warn: (
|
|
7200
|
+
error: (issue4) => errors.push(issue4),
|
|
7201
|
+
warn: (issue4) => warnings.push(issue4)
|
|
7202
7202
|
};
|
|
7203
7203
|
dependencyRules(config2, resolved, out);
|
|
7204
7204
|
secretRules(config2, context, out, skipped);
|
|
@@ -7301,7 +7301,7 @@ var ServiceManifestSchema = z3.looseObject({
|
|
|
7301
7301
|
}).optional()
|
|
7302
7302
|
}).default({})
|
|
7303
7303
|
});
|
|
7304
|
-
var issue = (code,
|
|
7304
|
+
var issue = (code, path104, message, hint) => ({ code, path: path104, message, hint });
|
|
7305
7305
|
function parseManifest(raw, file = "services.json") {
|
|
7306
7306
|
const version = typeof raw === "object" && raw !== null && "schemaVersion" in raw ? raw.schemaVersion : void 0;
|
|
7307
7307
|
if (typeof version === "number" && version > SUPPORTED_MANIFEST_SCHEMA) {
|
|
@@ -7457,6 +7457,57 @@ function withGatewayEntry(existing, info) {
|
|
|
7457
7457
|
}
|
|
7458
7458
|
};
|
|
7459
7459
|
}
|
|
7460
|
+
function withIdentityEntry(existing, info) {
|
|
7461
|
+
const base = existing ?? parseManifest({
|
|
7462
|
+
schemaVersion: SUPPORTED_MANIFEST_SCHEMA,
|
|
7463
|
+
project: { name: info.projectName }
|
|
7464
|
+
});
|
|
7465
|
+
return {
|
|
7466
|
+
...base,
|
|
7467
|
+
schemaVersion: SUPPORTED_MANIFEST_SCHEMA,
|
|
7468
|
+
generatedAt: (info.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
7469
|
+
preview: void 0,
|
|
7470
|
+
project: { ...base.project, id: info.projectId, name: info.projectName },
|
|
7471
|
+
services: {
|
|
7472
|
+
...base.services,
|
|
7473
|
+
identity: {
|
|
7474
|
+
...base.services.identity,
|
|
7475
|
+
provider: info.provider ?? "apitogo",
|
|
7476
|
+
status: "ready",
|
|
7477
|
+
issuer: info.issuer.replace(/\/+$/, ""),
|
|
7478
|
+
clientId: info.clientId,
|
|
7479
|
+
scopes: ["openid", "profile", "email"],
|
|
7480
|
+
...info.audience ? { audience: info.audience } : {},
|
|
7481
|
+
capabilities: { nativeSignup: info.nativeSignup }
|
|
7482
|
+
}
|
|
7483
|
+
}
|
|
7484
|
+
};
|
|
7485
|
+
}
|
|
7486
|
+
function withPlansEntry(existing, info) {
|
|
7487
|
+
const base = existing ?? parseManifest({
|
|
7488
|
+
schemaVersion: SUPPORTED_MANIFEST_SCHEMA,
|
|
7489
|
+
project: { name: info.projectName }
|
|
7490
|
+
});
|
|
7491
|
+
return {
|
|
7492
|
+
...base,
|
|
7493
|
+
schemaVersion: SUPPORTED_MANIFEST_SCHEMA,
|
|
7494
|
+
generatedAt: (info.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
7495
|
+
preview: void 0,
|
|
7496
|
+
project: { ...base.project, id: info.projectId, name: info.projectName },
|
|
7497
|
+
services: {
|
|
7498
|
+
...base.services,
|
|
7499
|
+
plans: {
|
|
7500
|
+
...base.services.plans,
|
|
7501
|
+
provider: "apitogo",
|
|
7502
|
+
status: "ready",
|
|
7503
|
+
apiBaseUrl: info.apiBaseUrl.replace(/\/+$/, ""),
|
|
7504
|
+
billing: info.billing,
|
|
7505
|
+
catalogRevision: info.catalogRevision,
|
|
7506
|
+
capabilities: info.capabilities
|
|
7507
|
+
}
|
|
7508
|
+
}
|
|
7509
|
+
};
|
|
7510
|
+
}
|
|
7460
7511
|
async function writeManifest(state, manifest) {
|
|
7461
7512
|
const file = manifestPathFor(state);
|
|
7462
7513
|
await mkdir3(path10.dirname(file), { recursive: true });
|
|
@@ -7504,8 +7555,8 @@ var METHODS = [
|
|
|
7504
7555
|
"trace"
|
|
7505
7556
|
];
|
|
7506
7557
|
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7507
|
-
function actionKey(method,
|
|
7508
|
-
const trimmed =
|
|
7558
|
+
function actionKey(method, path104) {
|
|
7559
|
+
const trimmed = path104.trim();
|
|
7509
7560
|
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
7510
7561
|
const normalized = withSlash.length > 1 ? withSlash.replace(/\/+$/, "") || "/" : withSlash;
|
|
7511
7562
|
return `${method.trim().toLowerCase()}:${normalized}`;
|
|
@@ -7515,8 +7566,8 @@ function configActionKey(entry) {
|
|
|
7515
7566
|
return match?.[1] && match[2] ? actionKey(match[1], match[2]) : void 0;
|
|
7516
7567
|
}
|
|
7517
7568
|
function referenceOptions(gateway, gatewayBaseUrl, keyHeaderName) {
|
|
7518
|
-
const
|
|
7519
|
-
const jwt =
|
|
7569
|
+
const access17 = gateway.policies.access;
|
|
7570
|
+
const jwt = access17?.jwt ?? gateway.auth !== void 0;
|
|
7520
7571
|
const keys = (list2) => new Set(
|
|
7521
7572
|
(list2 ?? []).flatMap((entry) => {
|
|
7522
7573
|
const key = configActionKey(entry);
|
|
@@ -7528,8 +7579,8 @@ function referenceOptions(gateway, gatewayBaseUrl, keyHeaderName) {
|
|
|
7528
7579
|
originUrl: gateway.origin.url,
|
|
7529
7580
|
jwtRequired: jwt && gateway.auth !== void 0,
|
|
7530
7581
|
oidcMetadataUrl: jwt ? gateway.auth?.metadataUrl : void 0,
|
|
7531
|
-
apiKeyRequired:
|
|
7532
|
-
apiKeyHeader: keyHeaderName ??
|
|
7582
|
+
apiKeyRequired: access17?.apiKey ?? true,
|
|
7583
|
+
apiKeyHeader: keyHeaderName ?? access17?.apiKeyHeader ?? DEFAULT_KEY_HEADER,
|
|
7533
7584
|
publicOperations: keys(gateway.policies.operations?.public),
|
|
7534
7585
|
deniedOperations: keys(gateway.policies.operations?.deny)
|
|
7535
7586
|
};
|
|
@@ -7542,8 +7593,8 @@ var ReferenceSpecError = class extends Error {
|
|
|
7542
7593
|
};
|
|
7543
7594
|
function operationsOf(paths) {
|
|
7544
7595
|
return Object.entries(paths).flatMap(
|
|
7545
|
-
([
|
|
7546
|
-
(method) => [
|
|
7596
|
+
([path104, item]) => isObject(item) ? METHODS.filter((method) => isObject(item[method])).map(
|
|
7597
|
+
(method) => [path104, item, method]
|
|
7547
7598
|
) : []
|
|
7548
7599
|
);
|
|
7549
7600
|
}
|
|
@@ -7558,14 +7609,14 @@ function buildReferenceSpec(specText, options) {
|
|
|
7558
7609
|
throw new ReferenceSpecError("The OpenAPI file could not be read.");
|
|
7559
7610
|
const doc = structuredClone(root);
|
|
7560
7611
|
const paths = isObject(doc.paths) ? doc.paths : {};
|
|
7561
|
-
for (const [
|
|
7562
|
-
if (options.deniedOperations.has(actionKey(method,
|
|
7612
|
+
for (const [path104, item, method] of operationsOf(paths))
|
|
7613
|
+
if (options.deniedOperations.has(actionKey(method, path104)))
|
|
7563
7614
|
delete item[method];
|
|
7564
|
-
for (const [
|
|
7615
|
+
for (const [path104, item] of Object.entries(paths))
|
|
7565
7616
|
if (!isObject(item) || !METHODS.some((method) => method in item))
|
|
7566
|
-
delete paths[
|
|
7567
|
-
for (const [
|
|
7568
|
-
if (options.publicOperations.has(actionKey(method,
|
|
7617
|
+
delete paths[path104];
|
|
7618
|
+
for (const [path104, item, method] of operationsOf(paths))
|
|
7619
|
+
if (options.publicOperations.has(actionKey(method, path104)))
|
|
7569
7620
|
item[method].security = [];
|
|
7570
7621
|
doc.servers = [{ url: options.gatewayBaseUrl.replace(/\/+$/, "") }];
|
|
7571
7622
|
for (const item of Object.values(paths)) {
|
|
@@ -8266,7 +8317,8 @@ var PLAN_TEMPLATES = {
|
|
|
8266
8317
|
displayName: "Free",
|
|
8267
8318
|
default: true,
|
|
8268
8319
|
limits: {
|
|
8269
|
-
|
|
8320
|
+
// The platform's Free tier allows at most 3 calls per 60 seconds per plan; a higher number is refused.
|
|
8321
|
+
rateLimit: { calls: 3, periodSeconds: 60 },
|
|
8270
8322
|
quota: { calls: 1e3, periodDays: 30 }
|
|
8271
8323
|
}
|
|
8272
8324
|
}
|
|
@@ -8277,7 +8329,8 @@ var PLAN_TEMPLATES = {
|
|
|
8277
8329
|
displayName: "Free",
|
|
8278
8330
|
default: true,
|
|
8279
8331
|
limits: {
|
|
8280
|
-
|
|
8332
|
+
// The platform's Free tier allows at most 3 calls per 60 seconds per plan; a higher number is refused.
|
|
8333
|
+
rateLimit: { calls: 3, periodSeconds: 60 },
|
|
8281
8334
|
quota: { calls: 1e3, periodDays: 30 }
|
|
8282
8335
|
}
|
|
8283
8336
|
},
|
|
@@ -8564,12 +8617,12 @@ async function planAddService(service, options) {
|
|
|
8564
8617
|
)
|
|
8565
8618
|
);
|
|
8566
8619
|
const added = dependencyProblems(next, resolved.configPath).filter(
|
|
8567
|
-
(
|
|
8620
|
+
(issue4) => !before.has(issue4.code + issue4.path)
|
|
8568
8621
|
);
|
|
8569
8622
|
if (added.length > 0) {
|
|
8570
8623
|
throw new ServiceEditError(
|
|
8571
|
-
added.map((
|
|
8572
|
-
added.map((
|
|
8624
|
+
added.map((issue4) => issue4.message).join(" "),
|
|
8625
|
+
added.map((issue4) => issue4.hint).filter(Boolean).join(" ") || "Add the missing service first."
|
|
8573
8626
|
);
|
|
8574
8627
|
}
|
|
8575
8628
|
}
|
|
@@ -8598,11 +8651,11 @@ async function planRemoveService(service, options) {
|
|
|
8598
8651
|
dependencyProblems(config2, resolved.configPath).map((i) => i.code + i.path)
|
|
8599
8652
|
);
|
|
8600
8653
|
const caused = dependencyProblems(next, resolved.configPath).filter(
|
|
8601
|
-
(
|
|
8654
|
+
(issue4) => !before.has(issue4.code + issue4.path)
|
|
8602
8655
|
);
|
|
8603
8656
|
if (caused.length > 0) {
|
|
8604
8657
|
throw new ServiceEditError(
|
|
8605
|
-
`${service} cannot be removed while something depends on it: ${caused.map((
|
|
8658
|
+
`${service} cannot be removed while something depends on it: ${caused.map((issue4) => issue4.message).join(" ")}`,
|
|
8606
8659
|
"Remove or change the dependent service first."
|
|
8607
8660
|
);
|
|
8608
8661
|
}
|
|
@@ -8836,7 +8889,7 @@ init_invariant();
|
|
|
8836
8889
|
|
|
8837
8890
|
// src/cli/check/v2.ts
|
|
8838
8891
|
init_apitogo_config_core();
|
|
8839
|
-
import { readFile as
|
|
8892
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
8840
8893
|
import path49 from "node:path";
|
|
8841
8894
|
|
|
8842
8895
|
// src/cli/platform/client/platform-api.ts
|
|
@@ -8950,8 +9003,8 @@ var PlatformApiClient = class {
|
|
|
8950
9003
|
constructor(config2) {
|
|
8951
9004
|
this.config = config2;
|
|
8952
9005
|
}
|
|
8953
|
-
url(
|
|
8954
|
-
return `${this.config.apiUrl}${
|
|
9006
|
+
url(path104) {
|
|
9007
|
+
return `${this.config.apiUrl}${path104.startsWith("/") ? path104 : `/${path104}`}`;
|
|
8955
9008
|
}
|
|
8956
9009
|
headers(extra) {
|
|
8957
9010
|
return {
|
|
@@ -8961,9 +9014,9 @@ var PlatformApiClient = class {
|
|
|
8961
9014
|
...extra
|
|
8962
9015
|
};
|
|
8963
9016
|
}
|
|
8964
|
-
async request(method,
|
|
9017
|
+
async request(method, path104, init4) {
|
|
8965
9018
|
const response = await fetchWithRetry(
|
|
8966
|
-
this.url(
|
|
9019
|
+
this.url(path104),
|
|
8967
9020
|
{
|
|
8968
9021
|
method,
|
|
8969
9022
|
...init4,
|
|
@@ -9264,6 +9317,52 @@ var PlatformApiClient = class {
|
|
|
9264
9317
|
`/api/v1/projects/${projectId}/gateway`
|
|
9265
9318
|
);
|
|
9266
9319
|
}
|
|
9320
|
+
/**
|
|
9321
|
+
* Asks for the project's identity. The same request twice changes nothing; a different request for an identity that
|
|
9322
|
+
* exists is refused (409), because its audience and redirect addresses cannot change in place. Answers 200 when it is
|
|
9323
|
+
* already Active and 202 otherwise: read `status` and poll `getProjectIdentity`.
|
|
9324
|
+
*/
|
|
9325
|
+
putProjectIdentity(projectId, body) {
|
|
9326
|
+
return this.request(
|
|
9327
|
+
"PUT",
|
|
9328
|
+
`/api/v1/projects/${projectId}/identity`,
|
|
9329
|
+
{
|
|
9330
|
+
headers: { "Content-Type": "application/json" },
|
|
9331
|
+
body: JSON.stringify(body)
|
|
9332
|
+
}
|
|
9333
|
+
);
|
|
9334
|
+
}
|
|
9335
|
+
/** The state of the project's identity, or null when it has none. */
|
|
9336
|
+
async getProjectIdentity(projectId) {
|
|
9337
|
+
try {
|
|
9338
|
+
return await this.request(
|
|
9339
|
+
"GET",
|
|
9340
|
+
`/api/v1/projects/${projectId}/identity`
|
|
9341
|
+
);
|
|
9342
|
+
} catch (error) {
|
|
9343
|
+
if (error instanceof PlatformApiError && error.status === 404)
|
|
9344
|
+
return null;
|
|
9345
|
+
throw error;
|
|
9346
|
+
}
|
|
9347
|
+
}
|
|
9348
|
+
/**
|
|
9349
|
+
* Removes the project's identity and DELETES EVERY USER in it, for good. Always sends `confirm=true`: ask the person
|
|
9350
|
+
* before calling. Returns null when there was nothing to remove, otherwise the state to poll until Released.
|
|
9351
|
+
*/
|
|
9352
|
+
async deleteProjectIdentity(projectId) {
|
|
9353
|
+
const result = await this.request(
|
|
9354
|
+
"DELETE",
|
|
9355
|
+
`/api/v1/projects/${projectId}/identity?confirm=true`
|
|
9356
|
+
);
|
|
9357
|
+
return result ?? null;
|
|
9358
|
+
}
|
|
9359
|
+
/** Applies the project's identity to its plans backend as the sign-in provider. Queues a job. */
|
|
9360
|
+
linkDevPortalIdentity(projectId) {
|
|
9361
|
+
return this.request(
|
|
9362
|
+
"POST",
|
|
9363
|
+
`/api/v1/projects/${projectId}/dev-portal/identity-link`
|
|
9364
|
+
);
|
|
9365
|
+
}
|
|
9267
9366
|
getDevPortalBackend(projectId) {
|
|
9268
9367
|
return this.request(
|
|
9269
9368
|
"GET",
|
|
@@ -9326,15 +9425,15 @@ var PlatformApiClient = class {
|
|
|
9326
9425
|
}
|
|
9327
9426
|
);
|
|
9328
9427
|
}
|
|
9329
|
-
deleteDevPortalPage(projectId,
|
|
9330
|
-
const query = new URLSearchParams({ path:
|
|
9428
|
+
deleteDevPortalPage(projectId, path104) {
|
|
9429
|
+
const query = new URLSearchParams({ path: path104 });
|
|
9331
9430
|
return this.request(
|
|
9332
9431
|
"DELETE",
|
|
9333
9432
|
`/api/v1/projects/${projectId}/dev-portal/pages/files?${query.toString()}`
|
|
9334
9433
|
);
|
|
9335
9434
|
}
|
|
9336
|
-
renameDevPortalPage(projectId,
|
|
9337
|
-
const query = new URLSearchParams({ path:
|
|
9435
|
+
renameDevPortalPage(projectId, path104, newName) {
|
|
9436
|
+
const query = new URLSearchParams({ path: path104 });
|
|
9338
9437
|
return this.request(
|
|
9339
9438
|
"POST",
|
|
9340
9439
|
`/api/v1/projects/${projectId}/dev-portal/pages/files/rename?${query.toString()}`,
|
|
@@ -9450,8 +9549,8 @@ var PlatformApiClient = class {
|
|
|
9450
9549
|
* the project-less endpoint is used; `planTier` overrides the tier used for paid-plan checks.
|
|
9451
9550
|
*/
|
|
9452
9551
|
validateGatewayPlans(plans, options = {}) {
|
|
9453
|
-
const
|
|
9454
|
-
return this.request("POST",
|
|
9552
|
+
const path104 = options.projectId ? `/api/v1/projects/${options.projectId}/gateway/plans/validate` : "/api/v1/gateway/plans/validate";
|
|
9553
|
+
return this.request("POST", path104, {
|
|
9455
9554
|
headers: { "Content-Type": "application/json" },
|
|
9456
9555
|
body: JSON.stringify({ plans, planTier: options.planTier })
|
|
9457
9556
|
});
|
|
@@ -9552,8 +9651,8 @@ var PlatformApiClient = class {
|
|
|
9552
9651
|
}
|
|
9553
9652
|
};
|
|
9554
9653
|
async function readFileBytes(filePath) {
|
|
9555
|
-
const { readFile:
|
|
9556
|
-
const buffer = await
|
|
9654
|
+
const { readFile: readFile32 } = await import("node:fs/promises");
|
|
9655
|
+
const buffer = await readFile32(filePath);
|
|
9557
9656
|
const arrayBuffer = new ArrayBuffer(buffer.byteLength);
|
|
9558
9657
|
new Uint8Array(arrayBuffer).set(buffer);
|
|
9559
9658
|
return arrayBuffer;
|
|
@@ -9655,7 +9754,6 @@ import {
|
|
|
9655
9754
|
rm as rm8,
|
|
9656
9755
|
writeFile as writeFile14
|
|
9657
9756
|
} from "node:fs/promises";
|
|
9658
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
9659
9757
|
import path48 from "node:path";
|
|
9660
9758
|
|
|
9661
9759
|
// src/vite/build.ts
|
|
@@ -9807,7 +9905,7 @@ import {
|
|
|
9807
9905
|
// package.json
|
|
9808
9906
|
var package_default = {
|
|
9809
9907
|
name: "@lukoweb/apitogo",
|
|
9810
|
-
version: "0.2.
|
|
9908
|
+
version: "0.2.23",
|
|
9811
9909
|
type: "module",
|
|
9812
9910
|
sideEffects: [
|
|
9813
9911
|
"**/*.css",
|
|
@@ -10589,14 +10687,14 @@ var flattenAllOf = (schema2) => {
|
|
|
10589
10687
|
};
|
|
10590
10688
|
|
|
10591
10689
|
// src/lib/util/traverse.ts
|
|
10592
|
-
var traverse = (specification, transform,
|
|
10593
|
-
const transformed = transform(specification,
|
|
10690
|
+
var traverse = (specification, transform, path104 = []) => {
|
|
10691
|
+
const transformed = transform(specification, path104);
|
|
10594
10692
|
if (typeof transformed !== "object" || transformed === null) {
|
|
10595
10693
|
return transformed;
|
|
10596
10694
|
}
|
|
10597
10695
|
const result = Array.isArray(transformed) ? [] : {};
|
|
10598
10696
|
for (const [key, value] of Object.entries(transformed)) {
|
|
10599
|
-
const currentPath = [...
|
|
10697
|
+
const currentPath = [...path104, key];
|
|
10600
10698
|
if (Array.isArray(value)) {
|
|
10601
10699
|
result[key] = value.map(
|
|
10602
10700
|
(item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
|
|
@@ -10624,9 +10722,9 @@ var resolveLocalRef = (schema2, ref) => {
|
|
|
10624
10722
|
if (schemaCache?.has(ref)) {
|
|
10625
10723
|
return schemaCache.get(ref);
|
|
10626
10724
|
}
|
|
10627
|
-
const
|
|
10725
|
+
const path104 = ref.split("/").slice(1);
|
|
10628
10726
|
let current = schema2;
|
|
10629
|
-
for (const segment of
|
|
10727
|
+
for (const segment of path104) {
|
|
10630
10728
|
if (!current || typeof current !== "object") {
|
|
10631
10729
|
current = null;
|
|
10632
10730
|
}
|
|
@@ -10645,7 +10743,7 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
10645
10743
|
}
|
|
10646
10744
|
const cloned = structuredClone(schema2);
|
|
10647
10745
|
const visited = /* @__PURE__ */ new Set();
|
|
10648
|
-
const resolve = async (current,
|
|
10746
|
+
const resolve = async (current, path104) => {
|
|
10649
10747
|
if (isIndexableObject(current)) {
|
|
10650
10748
|
if (visited.has(current)) {
|
|
10651
10749
|
return CIRCULAR_REF;
|
|
@@ -10653,7 +10751,7 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
10653
10751
|
visited.add(current);
|
|
10654
10752
|
if (Array.isArray(current)) {
|
|
10655
10753
|
for (let index = 0; index < current.length; index++) {
|
|
10656
|
-
current[index] = await resolve(current[index], `${
|
|
10754
|
+
current[index] = await resolve(current[index], `${path104}/${index}`);
|
|
10657
10755
|
}
|
|
10658
10756
|
} else {
|
|
10659
10757
|
if ("$ref" in current && typeof current.$ref === "string") {
|
|
@@ -10664,13 +10762,13 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
10664
10762
|
for (const resolver of resolvers) {
|
|
10665
10763
|
const resolved = await resolver($ref);
|
|
10666
10764
|
if (resolved) {
|
|
10667
|
-
result2 = await resolve(resolved,
|
|
10765
|
+
result2 = await resolve(resolved, path104);
|
|
10668
10766
|
break;
|
|
10669
10767
|
}
|
|
10670
10768
|
}
|
|
10671
10769
|
if (result2 === void 0) {
|
|
10672
10770
|
const resolved = await resolveLocalRef(cloned, $ref);
|
|
10673
|
-
result2 = await resolve(resolved,
|
|
10771
|
+
result2 = await resolve(resolved, path104);
|
|
10674
10772
|
}
|
|
10675
10773
|
if (hasSiblings) {
|
|
10676
10774
|
if (result2 === CIRCULAR_REF) {
|
|
@@ -10683,7 +10781,7 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
10683
10781
|
return result2;
|
|
10684
10782
|
}
|
|
10685
10783
|
for (const key in current) {
|
|
10686
|
-
current[key] = await resolve(current[key], `${
|
|
10784
|
+
current[key] = await resolve(current[key], `${path104}/${key}`);
|
|
10687
10785
|
}
|
|
10688
10786
|
}
|
|
10689
10787
|
visited.delete(current);
|
|
@@ -10722,9 +10820,9 @@ var upgradeSchema = (schema2) => {
|
|
|
10722
10820
|
}
|
|
10723
10821
|
return sub;
|
|
10724
10822
|
});
|
|
10725
|
-
schema2 = traverse(schema2, (sub,
|
|
10823
|
+
schema2 = traverse(schema2, (sub, path104) => {
|
|
10726
10824
|
if (sub.example !== void 0) {
|
|
10727
|
-
if (isSchemaPath(
|
|
10825
|
+
if (isSchemaPath(path104 ?? [])) {
|
|
10728
10826
|
sub.examples = [sub.example];
|
|
10729
10827
|
} else {
|
|
10730
10828
|
sub.examples = {
|
|
@@ -10737,11 +10835,11 @@ var upgradeSchema = (schema2) => {
|
|
|
10737
10835
|
}
|
|
10738
10836
|
return sub;
|
|
10739
10837
|
});
|
|
10740
|
-
schema2 = traverse(schema2, (schema3,
|
|
10838
|
+
schema2 = traverse(schema2, (schema3, path104) => {
|
|
10741
10839
|
if (schema3.type === "object" && schema3.properties !== void 0) {
|
|
10742
|
-
const parentPath =
|
|
10840
|
+
const parentPath = path104?.slice(0, -1);
|
|
10743
10841
|
const isMultipart = parentPath?.some((segment, index) => {
|
|
10744
|
-
return segment === "content" &&
|
|
10842
|
+
return segment === "content" && path104?.[index + 1] === "multipart/form-data";
|
|
10745
10843
|
});
|
|
10746
10844
|
if (isMultipart) {
|
|
10747
10845
|
const entries = Object.entries(schema3.properties);
|
|
@@ -10755,8 +10853,8 @@ var upgradeSchema = (schema2) => {
|
|
|
10755
10853
|
}
|
|
10756
10854
|
return schema3;
|
|
10757
10855
|
});
|
|
10758
|
-
schema2 = traverse(schema2, (schema3,
|
|
10759
|
-
if (
|
|
10856
|
+
schema2 = traverse(schema2, (schema3, path104) => {
|
|
10857
|
+
if (path104?.includes("content") && path104.includes("application/octet-stream")) {
|
|
10760
10858
|
return {};
|
|
10761
10859
|
}
|
|
10762
10860
|
if (schema3.type === "string" && schema3.format === "binary") {
|
|
@@ -10782,11 +10880,11 @@ var upgradeSchema = (schema2) => {
|
|
|
10782
10880
|
}
|
|
10783
10881
|
return sub;
|
|
10784
10882
|
});
|
|
10785
|
-
schema2 = traverse(schema2, (schema3,
|
|
10883
|
+
schema2 = traverse(schema2, (schema3, path104) => {
|
|
10786
10884
|
if (schema3.type === "string" && schema3.format === "byte") {
|
|
10787
|
-
const parentPath =
|
|
10885
|
+
const parentPath = path104?.slice(0, -1);
|
|
10788
10886
|
const contentMediaType = parentPath?.find(
|
|
10789
|
-
(_, index) =>
|
|
10887
|
+
(_, index) => path104?.[index - 1] === "content"
|
|
10790
10888
|
);
|
|
10791
10889
|
return {
|
|
10792
10890
|
type: "string",
|
|
@@ -10798,7 +10896,7 @@ var upgradeSchema = (schema2) => {
|
|
|
10798
10896
|
});
|
|
10799
10897
|
return schema2;
|
|
10800
10898
|
};
|
|
10801
|
-
function isSchemaPath(
|
|
10899
|
+
function isSchemaPath(path104) {
|
|
10802
10900
|
const schemaLocations = [
|
|
10803
10901
|
["components", "schemas"],
|
|
10804
10902
|
"properties",
|
|
@@ -10811,10 +10909,10 @@ function isSchemaPath(path103) {
|
|
|
10811
10909
|
];
|
|
10812
10910
|
return schemaLocations.some((location) => {
|
|
10813
10911
|
if (Array.isArray(location)) {
|
|
10814
|
-
return location.every((segment, index) =>
|
|
10912
|
+
return location.every((segment, index) => path104[index] === segment);
|
|
10815
10913
|
}
|
|
10816
|
-
return
|
|
10817
|
-
}) ||
|
|
10914
|
+
return path104.includes(location);
|
|
10915
|
+
}) || path104.includes("schema") || path104.some((segment) => segment.endsWith("Schema"));
|
|
10818
10916
|
}
|
|
10819
10917
|
|
|
10820
10918
|
// src/lib/oas/parser/index.ts
|
|
@@ -10896,13 +10994,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
|
|
|
10896
10994
|
"anyOf",
|
|
10897
10995
|
"oneOf"
|
|
10898
10996
|
]);
|
|
10899
|
-
var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(),
|
|
10997
|
+
var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path104 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
|
|
10900
10998
|
if (obj === null || typeof obj !== "object") return obj;
|
|
10901
10999
|
const refPath = obj.__$ref;
|
|
10902
11000
|
const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
|
|
10903
11001
|
if (isCircular) {
|
|
10904
11002
|
if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
|
|
10905
|
-
const circularProp =
|
|
11003
|
+
const circularProp = path104.find((p) => !OPENAPI_PROPS.has(p)) || path104[0];
|
|
10906
11004
|
return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
|
|
10907
11005
|
}
|
|
10908
11006
|
if (refs.has(obj)) return refs.get(obj);
|
|
@@ -10912,7 +11010,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
|
|
|
10912
11010
|
value,
|
|
10913
11011
|
currentPath,
|
|
10914
11012
|
refs,
|
|
10915
|
-
[...
|
|
11013
|
+
[...path104, key],
|
|
10916
11014
|
currentRefPaths
|
|
10917
11015
|
);
|
|
10918
11016
|
const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
|
|
@@ -10947,7 +11045,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
|
|
|
10947
11045
|
var getAllTags = (schema2) => {
|
|
10948
11046
|
const rootTags = schema2.tags ?? [];
|
|
10949
11047
|
const operations = Object.values(schema2.paths ?? {}).flatMap(
|
|
10950
|
-
(
|
|
11048
|
+
(path104) => HttpMethods.map((k) => path104?.[k]).filter((op) => op != null)
|
|
10951
11049
|
);
|
|
10952
11050
|
const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
|
|
10953
11051
|
const hasUntaggedOperations = operations.some(
|
|
@@ -11002,7 +11100,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
|
|
|
11002
11100
|
var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
|
|
11003
11101
|
var getAllOperations = (paths) => {
|
|
11004
11102
|
const operations = Object.entries(paths ?? {}).flatMap(
|
|
11005
|
-
([
|
|
11103
|
+
([path104, value]) => HttpMethods.flatMap((method) => {
|
|
11006
11104
|
if (!value?.[method]) return [];
|
|
11007
11105
|
const operation = value[method];
|
|
11008
11106
|
const pathParameters = value.parameters ?? [];
|
|
@@ -11022,7 +11120,7 @@ var getAllOperations = (paths) => {
|
|
|
11022
11120
|
return {
|
|
11023
11121
|
...operation,
|
|
11024
11122
|
method,
|
|
11025
|
-
path:
|
|
11123
|
+
path: path104,
|
|
11026
11124
|
parameters,
|
|
11027
11125
|
servers,
|
|
11028
11126
|
tags: operation.tags ?? []
|
|
@@ -11380,8 +11478,8 @@ var Schema = builder.objectRef("Schema").implement({
|
|
|
11380
11478
|
}),
|
|
11381
11479
|
paths: t.field({
|
|
11382
11480
|
type: [PathItem],
|
|
11383
|
-
resolve: (root) => Object.entries(root.paths ?? {}).map(([
|
|
11384
|
-
path:
|
|
11481
|
+
resolve: (root) => Object.entries(root.paths ?? {}).map(([path104, value]) => ({
|
|
11482
|
+
path: path104,
|
|
11385
11483
|
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
|
|
11386
11484
|
methods: Object.keys(value)
|
|
11387
11485
|
}))
|
|
@@ -11546,7 +11644,7 @@ init_joinUrl();
|
|
|
11546
11644
|
|
|
11547
11645
|
// src/vite/api/schema-codegen.ts
|
|
11548
11646
|
var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
11549
|
-
var getSegmentsFromPath = (
|
|
11647
|
+
var getSegmentsFromPath = (path104) => path104.split("/").slice(1).map(unescapeJsonPointer);
|
|
11550
11648
|
var createLocalRefMap = (obj) => {
|
|
11551
11649
|
const refMap = /* @__PURE__ */ new Map();
|
|
11552
11650
|
const siblingsMap = /* @__PURE__ */ new Map();
|
|
@@ -11577,16 +11675,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
|
|
|
11577
11675
|
/"__refMap\+Siblings:(.*?)"/g,
|
|
11578
11676
|
(_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
|
|
11579
11677
|
);
|
|
11580
|
-
var lookup = (schema2,
|
|
11581
|
-
const parts = getSegmentsFromPath(
|
|
11678
|
+
var lookup = (schema2, path104, filePath) => {
|
|
11679
|
+
const parts = getSegmentsFromPath(path104);
|
|
11582
11680
|
let val = schema2;
|
|
11583
11681
|
for (const part of parts) {
|
|
11584
11682
|
while (val.$ref?.startsWith("#/")) {
|
|
11585
|
-
val = val.$ref ===
|
|
11683
|
+
val = val.$ref === path104 ? val : lookup(schema2, val.$ref, filePath);
|
|
11586
11684
|
}
|
|
11587
11685
|
if (val[part] === void 0) {
|
|
11588
11686
|
throw new Error(
|
|
11589
|
-
`Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${
|
|
11687
|
+
`Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path104}`
|
|
11590
11688
|
);
|
|
11591
11689
|
}
|
|
11592
11690
|
val = val[part];
|
|
@@ -11831,8 +11929,8 @@ var SchemaManager = class {
|
|
|
11831
11929
|
}
|
|
11832
11930
|
}
|
|
11833
11931
|
};
|
|
11834
|
-
getLatestSchema = (
|
|
11835
|
-
getSchemasForPath = (
|
|
11932
|
+
getLatestSchema = (path104) => this.processedSchemas[path104]?.at(0);
|
|
11933
|
+
getSchemasForPath = (path104) => this.processedSchemas[path104];
|
|
11836
11934
|
getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
|
|
11837
11935
|
getUrlToFilePathMap = () => {
|
|
11838
11936
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -14286,14 +14384,14 @@ async function generateSitemap({
|
|
|
14286
14384
|
|
|
14287
14385
|
// src/vite/prerender/utils.ts
|
|
14288
14386
|
init_joinUrl();
|
|
14289
|
-
var resolveRoutePath = (
|
|
14290
|
-
const segments =
|
|
14387
|
+
var resolveRoutePath = (path104) => {
|
|
14388
|
+
const segments = path104.split("/");
|
|
14291
14389
|
if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
|
|
14292
14390
|
return void 0;
|
|
14293
14391
|
}
|
|
14294
14392
|
return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
|
|
14295
14393
|
};
|
|
14296
|
-
var isSkipped = (
|
|
14394
|
+
var isSkipped = (path104) => path104.includes("*") || /^\d+$/.test(path104);
|
|
14297
14395
|
var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
|
|
14298
14396
|
if (route.path && isSkipped(route.path)) return [];
|
|
14299
14397
|
const routePath = route.path ? resolveRoutePath(route.path) : void 0;
|
|
@@ -14702,15 +14800,9 @@ var missingCorePackages = (dir) => CORE_PACKAGES.filter((name) => !isPackageInst
|
|
|
14702
14800
|
|
|
14703
14801
|
// src/cli/portal/v2.ts
|
|
14704
14802
|
function assertPackagesInstalled(resolved, prepared) {
|
|
14705
|
-
const
|
|
14706
|
-
|
|
14707
|
-
|
|
14708
|
-
require2.resolve(name);
|
|
14709
|
-
return false;
|
|
14710
|
-
} catch {
|
|
14711
|
-
return true;
|
|
14712
|
-
}
|
|
14713
|
-
});
|
|
14803
|
+
const missing = requiredPackages(prepared.plan).filter(
|
|
14804
|
+
(name) => !isPackageInstalled(resolved.configDir, name)
|
|
14805
|
+
);
|
|
14714
14806
|
const missingCore = missingCorePackages(resolved.configDir);
|
|
14715
14807
|
if (missing.length === 0 && missingCore.length === 0) return;
|
|
14716
14808
|
throw new ConfigError(resolved.configPath, [
|
|
@@ -14803,15 +14895,15 @@ async function buildV2(options) {
|
|
|
14803
14895
|
// src/cli/check/gateway-online.ts
|
|
14804
14896
|
import { readFile as readFile13 } from "node:fs/promises";
|
|
14805
14897
|
var SPEC_SEND_LIMIT = 5 * 1024 * 1024 + 64 * 1024;
|
|
14806
|
-
var issue2 = (code,
|
|
14898
|
+
var issue2 = (code, path104, message, hint) => ({ code, path: path104, message, hint });
|
|
14807
14899
|
function capabilityIssues(gateway, answer) {
|
|
14808
14900
|
const can = answer.capabilities;
|
|
14809
14901
|
const plan2 = `your ${answer.tier} plan`;
|
|
14810
14902
|
const found = [];
|
|
14811
|
-
const unsupported = (
|
|
14903
|
+
const unsupported = (path104, what, hint) => found.push(
|
|
14812
14904
|
issue2(
|
|
14813
14905
|
"GATEWAY_CAPABILITY_UNSUPPORTED",
|
|
14814
|
-
|
|
14906
|
+
path104,
|
|
14815
14907
|
`${what} is not available on ${plan2}.`,
|
|
14816
14908
|
hint
|
|
14817
14909
|
)
|
|
@@ -14883,11 +14975,240 @@ async function onlineGatewayRules(gateway, projectId, api, derivedCors = []) {
|
|
|
14883
14975
|
}
|
|
14884
14976
|
}
|
|
14885
14977
|
|
|
14978
|
+
// src/cli/deploy/plans-file.ts
|
|
14979
|
+
init_invariant();
|
|
14980
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
14981
|
+
import { z as z14 } from "zod";
|
|
14982
|
+
var PlansFileError = class extends ApitogoError {
|
|
14983
|
+
constructor(code, message, developerHint) {
|
|
14984
|
+
super(message, { developerHint, title: "Cannot read the plans file" });
|
|
14985
|
+
this.code = code;
|
|
14986
|
+
this.name = "PlansFileError";
|
|
14987
|
+
}
|
|
14988
|
+
};
|
|
14989
|
+
var Limits = z14.strictObject({
|
|
14990
|
+
rateLimit: z14.strictObject({
|
|
14991
|
+
calls: z14.int().positive(),
|
|
14992
|
+
periodSeconds: z14.int().positive()
|
|
14993
|
+
}).optional(),
|
|
14994
|
+
quota: z14.strictObject({
|
|
14995
|
+
calls: z14.int().positive(),
|
|
14996
|
+
periodDays: z14.int().positive()
|
|
14997
|
+
}).optional()
|
|
14998
|
+
});
|
|
14999
|
+
var Plan = z14.strictObject({
|
|
15000
|
+
sku: z14.string().trim().min(1),
|
|
15001
|
+
displayName: z14.string().trim().min(1),
|
|
15002
|
+
/** The plan the portal offers first. The platform has no such flag; the portal reads the file order. */
|
|
15003
|
+
default: z14.boolean().optional(),
|
|
15004
|
+
limits: Limits.optional(),
|
|
15005
|
+
priceAmount: z14.number().min(0).optional(),
|
|
15006
|
+
priceCurrency: z14.string().trim().min(1).optional(),
|
|
15007
|
+
billingInterval: z14.enum(["month", "year"]).optional(),
|
|
15008
|
+
pricingModel: z14.enum(["fixed", "custom"]).optional(),
|
|
15009
|
+
priceLabel: z14.string().optional(),
|
|
15010
|
+
sortOrder: z14.int().optional(),
|
|
15011
|
+
/** Action keys such as `get:/orders`. Left out, the plan includes every action of the gateway. */
|
|
15012
|
+
includedActions: z14.array(z14.string().trim().min(1)).optional()
|
|
15013
|
+
});
|
|
15014
|
+
var PlansFile = z14.strictObject({
|
|
15015
|
+
version: z14.literal(1),
|
|
15016
|
+
plans: z14.array(Plan)
|
|
15017
|
+
});
|
|
15018
|
+
var issuesOf = (error) => error.issues.map(
|
|
15019
|
+
(issue4) => `${issue4.path.join(".") || "(file)"}: ${issue4.message}`
|
|
15020
|
+
);
|
|
15021
|
+
async function readPlansFile(file) {
|
|
15022
|
+
let text;
|
|
15023
|
+
try {
|
|
15024
|
+
text = await readFile14(file, "utf8");
|
|
15025
|
+
} catch {
|
|
15026
|
+
throw new PlansFileError(
|
|
15027
|
+
"PLANS_FILE_UNREADABLE",
|
|
15028
|
+
`The plans file ${file} could not be read.`,
|
|
15029
|
+
"Create it with `apitogo add plans`, or set plans.file in the config."
|
|
15030
|
+
);
|
|
15031
|
+
}
|
|
15032
|
+
let raw;
|
|
15033
|
+
try {
|
|
15034
|
+
raw = JSON.parse(text);
|
|
15035
|
+
} catch (error) {
|
|
15036
|
+
throw new PlansFileError(
|
|
15037
|
+
"PLANS_FILE_INVALID",
|
|
15038
|
+
`The plans file ${file} is not valid JSON (${error.message}).`
|
|
15039
|
+
);
|
|
15040
|
+
}
|
|
15041
|
+
const parsed = PlansFile.safeParse(raw);
|
|
15042
|
+
if (!parsed.success)
|
|
15043
|
+
throw new PlansFileError(
|
|
15044
|
+
"PLANS_FILE_INVALID",
|
|
15045
|
+
`The plans file ${file} is not valid:
|
|
15046
|
+
${issuesOf(parsed.error).join("\n ")}`,
|
|
15047
|
+
'Expected { "version": 1, "plans": [ { "sku", "displayName", "limits": { "rateLimit": { "calls", "periodSeconds" }, "quota": { "calls", "periodDays" } } } ] }.'
|
|
15048
|
+
);
|
|
15049
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15050
|
+
for (const plan2 of parsed.data.plans) {
|
|
15051
|
+
const sku = plan2.sku.toLowerCase();
|
|
15052
|
+
if (seen.has(sku))
|
|
15053
|
+
throw new PlansFileError(
|
|
15054
|
+
"PLANS_FILE_INVALID",
|
|
15055
|
+
`The plans file ${file} names the sku "${plan2.sku}" twice.`,
|
|
15056
|
+
"Every plan needs its own sku."
|
|
15057
|
+
);
|
|
15058
|
+
seen.add(sku);
|
|
15059
|
+
}
|
|
15060
|
+
return parsed.data.plans;
|
|
15061
|
+
}
|
|
15062
|
+
var BILLING_PERIOD = { month: "monthly", year: "annual" };
|
|
15063
|
+
function toPlatformPlans(plans) {
|
|
15064
|
+
return plans.map((plan2, index) => {
|
|
15065
|
+
const paid = plan2.pricingModel !== "custom" && (plan2.priceAmount ?? 0) > 0;
|
|
15066
|
+
if (paid)
|
|
15067
|
+
throw new PlansFileError(
|
|
15068
|
+
"PLANS_PAID_UNSUPPORTED",
|
|
15069
|
+
`The plan "${plan2.sku}" has a price, and deploying paid plans is not available in this CLI version yet.`,
|
|
15070
|
+
"Remove the plan, or set priceAmount to 0 (or pricingModel to custom for a contact-us plan)."
|
|
15071
|
+
);
|
|
15072
|
+
const limits = plan2.limits ? {
|
|
15073
|
+
...plan2.limits.rateLimit ? {
|
|
15074
|
+
rateLimit: {
|
|
15075
|
+
calls: plan2.limits.rateLimit.calls,
|
|
15076
|
+
renewalPeriodSeconds: plan2.limits.rateLimit.periodSeconds
|
|
15077
|
+
}
|
|
15078
|
+
} : {},
|
|
15079
|
+
...plan2.limits.quota ? {
|
|
15080
|
+
quota: {
|
|
15081
|
+
calls: plan2.limits.quota.calls,
|
|
15082
|
+
renewalPeriodDays: plan2.limits.quota.periodDays
|
|
15083
|
+
}
|
|
15084
|
+
} : {}
|
|
15085
|
+
} : void 0;
|
|
15086
|
+
return {
|
|
15087
|
+
sku: plan2.sku.trim().toLowerCase(),
|
|
15088
|
+
displayName: plan2.displayName,
|
|
15089
|
+
...limits ? { limitsJson: JSON.stringify(limits) } : {},
|
|
15090
|
+
includedActionKeys: plan2.includedActions ?? null,
|
|
15091
|
+
...plan2.pricingModel ? { pricingModel: plan2.pricingModel } : {},
|
|
15092
|
+
...plan2.priceAmount !== void 0 ? { priceAmount: plan2.priceAmount } : {},
|
|
15093
|
+
...plan2.priceLabel !== void 0 ? { priceLabel: plan2.priceLabel } : {},
|
|
15094
|
+
...plan2.priceCurrency ? { priceCurrency: plan2.priceCurrency } : {},
|
|
15095
|
+
...plan2.billingInterval ? { billingPeriod: BILLING_PERIOD[plan2.billingInterval] } : {},
|
|
15096
|
+
sortOrder: plan2.sortOrder ?? index
|
|
15097
|
+
};
|
|
15098
|
+
});
|
|
15099
|
+
}
|
|
15100
|
+
|
|
15101
|
+
// src/cli/check/services-online.ts
|
|
15102
|
+
var issue3 = (code, path104, message, hint) => ({
|
|
15103
|
+
code,
|
|
15104
|
+
path: path104,
|
|
15105
|
+
message,
|
|
15106
|
+
hint
|
|
15107
|
+
});
|
|
15108
|
+
var trimSlash = (value) => value.replace(/\/+$/, "");
|
|
15109
|
+
async function onlineIdentityRules(resolved, manifest, api) {
|
|
15110
|
+
const identity = resolved.identity;
|
|
15111
|
+
if (!identity?.enabled) return { errors: [], warnings: [] };
|
|
15112
|
+
const issuer = manifest?.services.identity?.issuer;
|
|
15113
|
+
if (!issuer)
|
|
15114
|
+
return {
|
|
15115
|
+
errors: [],
|
|
15116
|
+
warnings: [],
|
|
15117
|
+
skipped: "the identity's online rules were not run: identity is not deployed yet (run `apitogo deploy --only identity`)"
|
|
15118
|
+
};
|
|
15119
|
+
let answer;
|
|
15120
|
+
try {
|
|
15121
|
+
answer = await api.discover(issuer);
|
|
15122
|
+
} catch (error) {
|
|
15123
|
+
return {
|
|
15124
|
+
errors: [],
|
|
15125
|
+
warnings: [],
|
|
15126
|
+
skipped: `the identity's online rules were not run: the issuer could not be reached (${error instanceof Error ? error.message : String(error)})`
|
|
15127
|
+
};
|
|
15128
|
+
}
|
|
15129
|
+
if ("status" in answer)
|
|
15130
|
+
return {
|
|
15131
|
+
errors: [
|
|
15132
|
+
issue3(
|
|
15133
|
+
"IDENTITY_ISSUER_NOT_ANSWERING",
|
|
15134
|
+
"identity",
|
|
15135
|
+
`the issuer ${issuer} answered ${answer.status} instead of its discovery document.`,
|
|
15136
|
+
"Run `apitogo status --only identity` to see what the platform says, then deploy identity again."
|
|
15137
|
+
)
|
|
15138
|
+
],
|
|
15139
|
+
warnings: []
|
|
15140
|
+
};
|
|
15141
|
+
if (!answer.document.issuer || trimSlash(answer.document.issuer) !== trimSlash(issuer))
|
|
15142
|
+
return {
|
|
15143
|
+
errors: [
|
|
15144
|
+
issue3(
|
|
15145
|
+
"IDENTITY_ISSUER_MISMATCH",
|
|
15146
|
+
"identity",
|
|
15147
|
+
`the discovery document of ${issuer} names a different issuer (${answer.document.issuer ?? "none"}).`,
|
|
15148
|
+
"Deploy identity again so services.json is rewritten."
|
|
15149
|
+
)
|
|
15150
|
+
],
|
|
15151
|
+
warnings: []
|
|
15152
|
+
};
|
|
15153
|
+
return { errors: [], warnings: [] };
|
|
15154
|
+
}
|
|
15155
|
+
async function plansFileProblem(resolved) {
|
|
15156
|
+
const plans = resolved.plans;
|
|
15157
|
+
if (!plans?.enabled) return { wanted: [] };
|
|
15158
|
+
try {
|
|
15159
|
+
return { wanted: toPlatformPlans(await readPlansFile(plans.file)) };
|
|
15160
|
+
} catch (error) {
|
|
15161
|
+
if (!(error instanceof PlansFileError)) throw error;
|
|
15162
|
+
return {
|
|
15163
|
+
wanted: [],
|
|
15164
|
+
issue: issue3(
|
|
15165
|
+
error.code,
|
|
15166
|
+
"plans.file",
|
|
15167
|
+
error.message,
|
|
15168
|
+
error.developerHint
|
|
15169
|
+
)
|
|
15170
|
+
};
|
|
15171
|
+
}
|
|
15172
|
+
}
|
|
15173
|
+
async function onlinePlansRules(resolved, projectId, api) {
|
|
15174
|
+
const plans = resolved.plans;
|
|
15175
|
+
if (!plans?.enabled) return { errors: [], warnings: [] };
|
|
15176
|
+
const file = await plansFileProblem(resolved);
|
|
15177
|
+
if (file.issue) return { errors: [file.issue], warnings: [] };
|
|
15178
|
+
const wanted = file.wanted;
|
|
15179
|
+
if (wanted.length === 0) return { errors: [], warnings: [] };
|
|
15180
|
+
try {
|
|
15181
|
+
const answer = await api.validate(
|
|
15182
|
+
projectId,
|
|
15183
|
+
// The platform's check needs the list even when it is empty: the actions are only known after the gateway deploy.
|
|
15184
|
+
wanted.map((plan2) => ({
|
|
15185
|
+
...plan2,
|
|
15186
|
+
includedActionKeys: plan2.includedActionKeys ?? []
|
|
15187
|
+
}))
|
|
15188
|
+
);
|
|
15189
|
+
const convert = (item) => issue3(
|
|
15190
|
+
"PLANS_PLAN_INVALID",
|
|
15191
|
+
`plans.file ${item.field}`,
|
|
15192
|
+
`${item.sku ? `plan ${item.sku}: ` : ""}${item.message}`
|
|
15193
|
+
);
|
|
15194
|
+
return {
|
|
15195
|
+
errors: answer.errors.map(convert),
|
|
15196
|
+
warnings: answer.warnings.map(convert)
|
|
15197
|
+
};
|
|
15198
|
+
} catch (error) {
|
|
15199
|
+
return {
|
|
15200
|
+
errors: [],
|
|
15201
|
+
warnings: [],
|
|
15202
|
+
skipped: `the plans' online rules were not run: the platform could not be asked (${error instanceof Error ? error.message : String(error)})`
|
|
15203
|
+
};
|
|
15204
|
+
}
|
|
15205
|
+
}
|
|
15206
|
+
|
|
14886
15207
|
// src/cli/check/v2.ts
|
|
14887
15208
|
async function isConfigV2(dir) {
|
|
14888
15209
|
try {
|
|
14889
15210
|
return /"version"\s*:\s*2\b/.test(
|
|
14890
|
-
await
|
|
15211
|
+
await readFile15(path49.join(dir, APITOGO_CONFIG_FILENAME), "utf8")
|
|
14891
15212
|
);
|
|
14892
15213
|
} catch {
|
|
14893
15214
|
return false;
|
|
@@ -14908,13 +15229,13 @@ async function fetchSecretNames(config2, configuredProjectId, args, deps) {
|
|
|
14908
15229
|
}
|
|
14909
15230
|
async function addGatewayOnlineRules(report, resolved, args, deps) {
|
|
14910
15231
|
const gateway = resolved.gateway;
|
|
14911
|
-
if (args.offline || !deps.gateway || !gateway?.enabled) return;
|
|
15232
|
+
if (args.offline || !deps.gateway || !gateway?.enabled) return false;
|
|
14912
15233
|
const projectId = resolved.project.id || deps.envProjectId?.trim();
|
|
14913
15234
|
if (!projectId) {
|
|
14914
15235
|
report.skipped.push(
|
|
14915
15236
|
"the gateway's online rules were not run: this folder is not linked to a project (run `apitogo project create`)"
|
|
14916
15237
|
);
|
|
14917
|
-
return;
|
|
15238
|
+
return false;
|
|
14918
15239
|
}
|
|
14919
15240
|
const manifest = await readManifest(resolved.state);
|
|
14920
15241
|
const result = await onlineGatewayRules(
|
|
@@ -14928,7 +15249,7 @@ async function addGatewayOnlineRules(report, resolved, args, deps) {
|
|
|
14928
15249
|
);
|
|
14929
15250
|
if (result.skipped) {
|
|
14930
15251
|
report.skipped.push(result.skipped);
|
|
14931
|
-
return;
|
|
15252
|
+
return false;
|
|
14932
15253
|
}
|
|
14933
15254
|
const seen = new Set(
|
|
14934
15255
|
[...report.errors, ...report.warnings].map((i) => `${i.code}|${i.path}`)
|
|
@@ -14939,12 +15260,60 @@ async function addGatewayOnlineRules(report, resolved, args, deps) {
|
|
|
14939
15260
|
seen.add(key);
|
|
14940
15261
|
report.errors.push(item);
|
|
14941
15262
|
}
|
|
14942
|
-
|
|
14943
|
-
|
|
14944
|
-
|
|
14945
|
-
|
|
14946
|
-
|
|
15263
|
+
return true;
|
|
15264
|
+
}
|
|
15265
|
+
async function addServiceOnlineRules(report, resolved, args, deps) {
|
|
15266
|
+
if (args.offline) return [];
|
|
15267
|
+
const ran = [];
|
|
15268
|
+
const seen = new Set(
|
|
15269
|
+
[...report.errors, ...report.warnings].map((i) => `${i.code}|${i.path}`)
|
|
14947
15270
|
);
|
|
15271
|
+
const take = (list2, into) => {
|
|
15272
|
+
for (const item of list2) {
|
|
15273
|
+
const key = `${item.code}|${item.path}`;
|
|
15274
|
+
if (seen.has(key)) continue;
|
|
15275
|
+
if (item.path === "plans.file" && report.errors.some((e) => e.path === "plans.file"))
|
|
15276
|
+
continue;
|
|
15277
|
+
seen.add(key);
|
|
15278
|
+
into.push(item);
|
|
15279
|
+
}
|
|
15280
|
+
};
|
|
15281
|
+
if (deps.identity && resolved.identity?.enabled) {
|
|
15282
|
+
const result = await onlineIdentityRules(
|
|
15283
|
+
resolved,
|
|
15284
|
+
await readManifest(resolved.state),
|
|
15285
|
+
deps.identity
|
|
15286
|
+
);
|
|
15287
|
+
if (result.skipped) report.skipped.push(result.skipped);
|
|
15288
|
+
else {
|
|
15289
|
+
take(result.errors, report.errors);
|
|
15290
|
+
take(result.warnings, report.warnings);
|
|
15291
|
+
ran.push("identity");
|
|
15292
|
+
}
|
|
15293
|
+
}
|
|
15294
|
+
if (deps.plans && resolved.plans?.enabled) {
|
|
15295
|
+
const projectId = resolved.project.id || deps.envProjectId?.trim();
|
|
15296
|
+
if (!projectId)
|
|
15297
|
+
report.skipped.push(
|
|
15298
|
+
"the plans' online rules were not run: this folder is not linked to a project (run `apitogo project create`)"
|
|
15299
|
+
);
|
|
15300
|
+
else {
|
|
15301
|
+
const result = await onlinePlansRules(resolved, projectId, deps.plans);
|
|
15302
|
+
if (result.skipped) report.skipped.push(result.skipped);
|
|
15303
|
+
else {
|
|
15304
|
+
take(result.errors, report.errors);
|
|
15305
|
+
take(result.warnings, report.warnings);
|
|
15306
|
+
ran.push("plans");
|
|
15307
|
+
}
|
|
15308
|
+
}
|
|
15309
|
+
}
|
|
15310
|
+
return ran;
|
|
15311
|
+
}
|
|
15312
|
+
function onlineRulesNote(ran) {
|
|
15313
|
+
if (ran.length === 1 && ran[0] === "gateway")
|
|
15314
|
+
return "online rules ran for the gateway only; the other services have none yet";
|
|
15315
|
+
const names = ran.length > 1 ? `${ran.slice(0, -1).join(", ")} and ${ran[ran.length - 1]}` : ran[0] ?? "";
|
|
15316
|
+
return `online rules ran for the ${names}`;
|
|
14948
15317
|
}
|
|
14949
15318
|
async function addApiReferenceWarning(report, resolved) {
|
|
14950
15319
|
const portal = resolved.portal;
|
|
@@ -14968,7 +15337,7 @@ async function validatePortalBuild(dir) {
|
|
|
14968
15337
|
return {
|
|
14969
15338
|
code: error.issues[0]?.code ?? "PORTAL_BUILD_FAILED",
|
|
14970
15339
|
path: error.issues[0]?.path ?? "portal",
|
|
14971
|
-
message: error.issues.map((
|
|
15340
|
+
message: error.issues.map((issue4) => issue4.message).join(" "),
|
|
14972
15341
|
hint: error.issues[0]?.hint
|
|
14973
15342
|
};
|
|
14974
15343
|
}
|
|
@@ -14997,7 +15366,19 @@ async function runCheckV2(dir, args = {}, deps = {}) {
|
|
|
14997
15366
|
knownSecrets: online.names
|
|
14998
15367
|
});
|
|
14999
15368
|
if (online.failure) report.skipped.push(online.failure);
|
|
15000
|
-
await
|
|
15369
|
+
const file = await plansFileProblem(resolved);
|
|
15370
|
+
if (file.issue && !report.errors.some((e) => e.path === file.issue.path))
|
|
15371
|
+
report.errors.push(file.issue);
|
|
15372
|
+
const ran = [];
|
|
15373
|
+
if (await addGatewayOnlineRules(report, resolved, args, deps))
|
|
15374
|
+
ran.push("gateway");
|
|
15375
|
+
ran.push(...await addServiceOnlineRules(report, resolved, args, deps));
|
|
15376
|
+
if (ran.length > 0) {
|
|
15377
|
+
report.skipped = report.skipped.filter(
|
|
15378
|
+
(note) => !note.startsWith("online rules were not run")
|
|
15379
|
+
);
|
|
15380
|
+
report.skipped.unshift(onlineRulesNote(ran));
|
|
15381
|
+
}
|
|
15001
15382
|
await addApiReferenceWarning(report, resolved);
|
|
15002
15383
|
} catch (error) {
|
|
15003
15384
|
if (!(error instanceof ConfigError)) throw error;
|
|
@@ -15025,11 +15406,11 @@ async function runCheckV2(dir, args = {}, deps = {}) {
|
|
|
15025
15406
|
...report
|
|
15026
15407
|
};
|
|
15027
15408
|
}
|
|
15028
|
-
var line = (label,
|
|
15409
|
+
var line = (label, issue4) => `${label}: ${formatConfigIssue(issue4)}`;
|
|
15029
15410
|
function formatCheckV2(output4) {
|
|
15030
15411
|
const lines = [
|
|
15031
|
-
...output4.errors.map((
|
|
15032
|
-
...output4.warnings.map((
|
|
15412
|
+
...output4.errors.map((issue4) => line("error", issue4)),
|
|
15413
|
+
...output4.warnings.map((issue4) => line("warning", issue4)),
|
|
15033
15414
|
...output4.skipped.map((note) => `not checked: ${note}`)
|
|
15034
15415
|
];
|
|
15035
15416
|
const errors = `${output4.errors.length} error${output4.errors.length === 1 ? "" : "s"}`;
|
|
@@ -15039,6 +15420,14 @@ function formatCheckV2(output4) {
|
|
|
15039
15420
|
);
|
|
15040
15421
|
return lines;
|
|
15041
15422
|
}
|
|
15423
|
+
async function discoverIssuer(issuer) {
|
|
15424
|
+
const response = await fetch(
|
|
15425
|
+
`${issuer.replace(/\/+$/, "")}/.well-known/openid-configuration`,
|
|
15426
|
+
{ signal: AbortSignal.timeout(1e4) }
|
|
15427
|
+
);
|
|
15428
|
+
if (!response.ok) return { status: response.status };
|
|
15429
|
+
return { document: await response.json() };
|
|
15430
|
+
}
|
|
15042
15431
|
function platformDependencies(args) {
|
|
15043
15432
|
if (args.offline) return {};
|
|
15044
15433
|
try {
|
|
@@ -15050,6 +15439,10 @@ function platformDependencies(args) {
|
|
|
15050
15439
|
gateway: {
|
|
15051
15440
|
check: (projectId, body) => client.checkGateway(projectId, body),
|
|
15052
15441
|
capabilities: (projectId) => client.getGatewayCapabilities(projectId)
|
|
15442
|
+
},
|
|
15443
|
+
identity: { discover: discoverIssuer },
|
|
15444
|
+
plans: {
|
|
15445
|
+
validate: (projectId, plans) => client.validateGatewayPlans(plans, { projectId })
|
|
15053
15446
|
}
|
|
15054
15447
|
};
|
|
15055
15448
|
} catch {
|
|
@@ -15076,7 +15469,7 @@ init_package_json();
|
|
|
15076
15469
|
|
|
15077
15470
|
// src/cli/platform/helpers/npm-build.ts
|
|
15078
15471
|
import { spawn } from "node:child_process";
|
|
15079
|
-
import { access as access5, readFile as
|
|
15472
|
+
import { access as access5, readFile as readFile16 } from "node:fs/promises";
|
|
15080
15473
|
import path50 from "node:path";
|
|
15081
15474
|
var BUILD_LOG_TAIL_BYTES = 8192;
|
|
15082
15475
|
var MIN_NODE_MAJOR = 20;
|
|
@@ -16028,7 +16421,7 @@ function formatDevPortalBackendResponse(projectId, backend) {
|
|
|
16028
16421
|
// src/cli/platform/helpers/dev-portal-manifest.ts
|
|
16029
16422
|
init_gateway_limits();
|
|
16030
16423
|
import { existsSync as existsSync7 } from "node:fs";
|
|
16031
|
-
import { access as access6, mkdir as mkdir13, readFile as
|
|
16424
|
+
import { access as access6, mkdir as mkdir13, readFile as readFile17, writeFile as writeFile15 } from "node:fs/promises";
|
|
16032
16425
|
import path57 from "node:path";
|
|
16033
16426
|
|
|
16034
16427
|
// src/cli/make-config/json-config.ts
|
|
@@ -17328,7 +17721,7 @@ function applyOpenApiToSiteConfig(config2, options) {
|
|
|
17328
17721
|
async function readManifest2(siteDirectory) {
|
|
17329
17722
|
const filePath = manifestFilePath(siteDirectory);
|
|
17330
17723
|
if (await pathExists2(filePath)) {
|
|
17331
|
-
const raw = await
|
|
17724
|
+
const raw = await readFile17(filePath, "utf8");
|
|
17332
17725
|
const config2 = JSON.parse(raw);
|
|
17333
17726
|
return extractManifestFromConfig(config2);
|
|
17334
17727
|
}
|
|
@@ -17339,7 +17732,7 @@ async function writeManifest2(siteDirectory, manifest) {
|
|
|
17339
17732
|
await mkdir13(path57.dirname(filePath), { recursive: true });
|
|
17340
17733
|
let config2 = {};
|
|
17341
17734
|
if (await pathExists2(filePath)) {
|
|
17342
|
-
const raw = await
|
|
17735
|
+
const raw = await readFile17(filePath, "utf8");
|
|
17343
17736
|
config2 = JSON.parse(raw);
|
|
17344
17737
|
}
|
|
17345
17738
|
const {
|
|
@@ -17358,7 +17751,7 @@ import {
|
|
|
17358
17751
|
access as access7,
|
|
17359
17752
|
appendFile,
|
|
17360
17753
|
mkdir as mkdir14,
|
|
17361
|
-
readFile as
|
|
17754
|
+
readFile as readFile18,
|
|
17362
17755
|
unlink,
|
|
17363
17756
|
writeFile as writeFile16
|
|
17364
17757
|
} from "node:fs/promises";
|
|
@@ -17464,7 +17857,7 @@ function applyEnvOverrides2(config2, env) {
|
|
|
17464
17857
|
}
|
|
17465
17858
|
async function readEnvFile(filePath) {
|
|
17466
17859
|
try {
|
|
17467
|
-
const raw = await
|
|
17860
|
+
const raw = await readFile18(filePath, "utf8");
|
|
17468
17861
|
const env = {};
|
|
17469
17862
|
for (const line2 of raw.split("\n")) {
|
|
17470
17863
|
const trimmed = line2.trim();
|
|
@@ -17641,7 +18034,7 @@ async function writeScaffoldEnv(siteDirectory) {
|
|
|
17641
18034
|
for (const legacyName of [".env.example"]) {
|
|
17642
18035
|
const legacyPath = path58.join(resolvedDirectory, legacyName);
|
|
17643
18036
|
try {
|
|
17644
|
-
const legacy = await
|
|
18037
|
+
const legacy = await readFile18(legacyPath, "utf8");
|
|
17645
18038
|
if (!envFileHasActiveAssignments(legacy)) {
|
|
17646
18039
|
await unlink(legacyPath);
|
|
17647
18040
|
filesWritten.push(`removed:${legacyName}`);
|
|
@@ -17652,7 +18045,7 @@ async function writeScaffoldEnv(siteDirectory) {
|
|
|
17652
18045
|
const gitignorePath = path58.join(resolvedDirectory, ".gitignore");
|
|
17653
18046
|
let gitignore = "";
|
|
17654
18047
|
try {
|
|
17655
|
-
gitignore = await
|
|
18048
|
+
gitignore = await readFile18(gitignorePath, "utf8");
|
|
17656
18049
|
} catch {
|
|
17657
18050
|
gitignore = "";
|
|
17658
18051
|
}
|
|
@@ -17684,12 +18077,12 @@ var SECRETS_COLLECTION_INSTRUCTION = "For go-live, plans must already be in apit
|
|
|
17684
18077
|
|
|
17685
18078
|
// src/cli/platform/helpers/dev-portal-site-validation.ts
|
|
17686
18079
|
init_gateway_limits();
|
|
17687
|
-
import { readFile as
|
|
17688
|
-
import { createRequire as
|
|
18080
|
+
import { readFile as readFile19 } from "node:fs/promises";
|
|
18081
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
17689
18082
|
import path59 from "node:path";
|
|
17690
18083
|
var cachedFallbackTs;
|
|
17691
18084
|
async function loadTypeScriptForSite(sourceDirectory) {
|
|
17692
|
-
const require2 =
|
|
18085
|
+
const require2 = createRequire2(
|
|
17693
18086
|
path59.join(path59.resolve(sourceDirectory), "package.json")
|
|
17694
18087
|
);
|
|
17695
18088
|
try {
|
|
@@ -17862,7 +18255,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
|
|
|
17862
18255
|
let parsed;
|
|
17863
18256
|
let parseFailed = false;
|
|
17864
18257
|
try {
|
|
17865
|
-
parsed = JSON.parse(await
|
|
18258
|
+
parsed = JSON.parse(await readFile19(jsonPath, "utf8"));
|
|
17866
18259
|
} catch (error) {
|
|
17867
18260
|
parseFailed = true;
|
|
17868
18261
|
issues.push({
|
|
@@ -17901,7 +18294,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
|
|
|
17901
18294
|
} else {
|
|
17902
18295
|
for (const relativeFile of existingTsxFiles) {
|
|
17903
18296
|
checkedFiles.push(relativeFile);
|
|
17904
|
-
const content = await
|
|
18297
|
+
const content = await readFile19(
|
|
17905
18298
|
path59.join(resolvedDir, relativeFile),
|
|
17906
18299
|
"utf8"
|
|
17907
18300
|
);
|
|
@@ -17922,7 +18315,7 @@ async function validateDevPortalSiteConfig(sourceDirectory, options) {
|
|
|
17922
18315
|
};
|
|
17923
18316
|
}
|
|
17924
18317
|
function formatSiteValidationErrors(result) {
|
|
17925
|
-
return result.issues.map((
|
|
18318
|
+
return result.issues.map((issue4) => `${issue4.file}: ${issue4.message}`).join("; ");
|
|
17926
18319
|
}
|
|
17927
18320
|
function buildSiteValidationResponseFields(result) {
|
|
17928
18321
|
if (result.valid) {
|
|
@@ -18326,7 +18719,7 @@ async function checkPublishReadiness(client, _config, args) {
|
|
|
18326
18719
|
const mergedPrerequisites = mergePublishPrerequisitesWithPlatformValidation(
|
|
18327
18720
|
publishPrerequisites,
|
|
18328
18721
|
platformValidation.issues.map(
|
|
18329
|
-
(
|
|
18722
|
+
(issue4) => `${issue4.field}: ${issue4.message}`
|
|
18330
18723
|
)
|
|
18331
18724
|
);
|
|
18332
18725
|
const siteValidation = await validateDevPortalSiteConfig(sourceDirectory);
|
|
@@ -18448,7 +18841,7 @@ var check_default = {
|
|
|
18448
18841
|
|
|
18449
18842
|
// src/cli/make-config/handler.ts
|
|
18450
18843
|
import { constants as constants2 } from "node:fs";
|
|
18451
|
-
import { access as access12, mkdir as mkdir17, readFile as
|
|
18844
|
+
import { access as access12, mkdir as mkdir17, readFile as readFile24, writeFile as writeFile21 } from "node:fs/promises";
|
|
18452
18845
|
import path71 from "node:path";
|
|
18453
18846
|
import { stdin as input2 } from "node:process";
|
|
18454
18847
|
import { glob as glob8 } from "glob";
|
|
@@ -18588,7 +18981,7 @@ function withDefaultIncludedActionKeys(plan2, actionKeys) {
|
|
|
18588
18981
|
}
|
|
18589
18982
|
|
|
18590
18983
|
// src/cli/scaffold/dev-portal-config-pages.ts
|
|
18591
|
-
import { access as access8, readFile as
|
|
18984
|
+
import { access as access8, readFile as readFile20, rename as rename7, rm as rm9, writeFile as writeFile17 } from "node:fs/promises";
|
|
18592
18985
|
import path63 from "node:path";
|
|
18593
18986
|
async function writeConfigAtomically(filePath, content) {
|
|
18594
18987
|
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
@@ -19353,7 +19746,7 @@ async function wireNewDevPortalPageInConfig(sourceDirectory, slug, label) {
|
|
|
19353
19746
|
if (!configPath) {
|
|
19354
19747
|
return { updated: false };
|
|
19355
19748
|
}
|
|
19356
|
-
const original = await
|
|
19749
|
+
const original = await readFile20(configPath, "utf8");
|
|
19357
19750
|
const patched = patchApitogoConfigForNewPage(original, slug, label, {
|
|
19358
19751
|
typescript: configPath.endsWith(".tsx")
|
|
19359
19752
|
});
|
|
@@ -19365,7 +19758,7 @@ async function wireNewDevPortalPageInConfig(sourceDirectory, slug, label) {
|
|
|
19365
19758
|
}
|
|
19366
19759
|
|
|
19367
19760
|
// src/cli/scaffold/wire-openapi.ts
|
|
19368
|
-
import { access as access10, readFile as
|
|
19761
|
+
import { access as access10, readFile as readFile22, writeFile as writeFile19 } from "node:fs/promises";
|
|
19369
19762
|
import path68 from "node:path";
|
|
19370
19763
|
|
|
19371
19764
|
// src/cli/platform/helpers/openapi.ts
|
|
@@ -19569,7 +19962,7 @@ async function validateOpenApiFile(filePath) {
|
|
|
19569
19962
|
}
|
|
19570
19963
|
|
|
19571
19964
|
// src/cli/scaffold/scaffold-dev-portal-site.ts
|
|
19572
|
-
import { mkdir as mkdir15, readFile as
|
|
19965
|
+
import { mkdir as mkdir15, readFile as readFile21, writeFile as writeFile18 } from "node:fs/promises";
|
|
19573
19966
|
import path67 from "node:path";
|
|
19574
19967
|
|
|
19575
19968
|
// src/cli/scaffold/apitogo-npm-packages.ts
|
|
@@ -19977,7 +20370,7 @@ function patchDashboardPageConfig(content) {
|
|
|
19977
20370
|
async function ensureAccountPlansPage(outputDirectory) {
|
|
19978
20371
|
const pagePath = path67.join(outputDirectory, ACCOUNT_PLANS_PAGE_PATH);
|
|
19979
20372
|
try {
|
|
19980
|
-
await
|
|
20373
|
+
await readFile21(pagePath, "utf8");
|
|
19981
20374
|
return void 0;
|
|
19982
20375
|
} catch {
|
|
19983
20376
|
await mkdir15(path67.dirname(pagePath), { recursive: true });
|
|
@@ -19988,7 +20381,7 @@ async function ensureAccountPlansPage(outputDirectory) {
|
|
|
19988
20381
|
async function ensureDashboardPage(outputDirectory) {
|
|
19989
20382
|
const pagePath = path67.join(outputDirectory, DASHBOARD_PAGE_PATH);
|
|
19990
20383
|
try {
|
|
19991
|
-
await
|
|
20384
|
+
await readFile21(pagePath, "utf8");
|
|
19992
20385
|
return void 0;
|
|
19993
20386
|
} catch {
|
|
19994
20387
|
await mkdir15(path67.dirname(pagePath), { recursive: true });
|
|
@@ -20050,7 +20443,7 @@ async function patchScaffoldedSite(outputDirectory, options) {
|
|
|
20050
20443
|
const filesWritten = [];
|
|
20051
20444
|
const packageJsonPath = path67.join(outputDirectory, "package.json");
|
|
20052
20445
|
if (options.patchPackageJson !== false) {
|
|
20053
|
-
const packageJson3 = await
|
|
20446
|
+
const packageJson3 = await readFile21(packageJsonPath, "utf8");
|
|
20054
20447
|
await writeFile18(
|
|
20055
20448
|
packageJsonPath,
|
|
20056
20449
|
rewritePackageJson(packageJson3, {
|
|
@@ -20062,7 +20455,7 @@ async function patchScaffoldedSite(outputDirectory, options) {
|
|
|
20062
20455
|
filesWritten.push("package.json");
|
|
20063
20456
|
}
|
|
20064
20457
|
const configPath = path67.join(outputDirectory, "apitogo.config.tsx");
|
|
20065
|
-
const configContent = await
|
|
20458
|
+
const configContent = await readFile21(configPath, "utf8");
|
|
20066
20459
|
await writeFile18(
|
|
20067
20460
|
configPath,
|
|
20068
20461
|
patchDashboardPageConfig(
|
|
@@ -20169,7 +20562,7 @@ async function wireDevPortalOpenApi(sourceDirectory, openApiPath) {
|
|
|
20169
20562
|
}
|
|
20170
20563
|
const jsonConfigPath = manifestFilePath(resolvedSource);
|
|
20171
20564
|
if (await pathExists4(jsonConfigPath)) {
|
|
20172
|
-
const raw = await
|
|
20565
|
+
const raw = await readFile22(jsonConfigPath, "utf8");
|
|
20173
20566
|
const siteConfig = JSON.parse(raw);
|
|
20174
20567
|
const updated = applyOpenApiToSiteConfig(siteConfig, {
|
|
20175
20568
|
input: input5,
|
|
@@ -20183,7 +20576,7 @@ async function wireDevPortalOpenApi(sourceDirectory, openApiPath) {
|
|
|
20183
20576
|
);
|
|
20184
20577
|
} else {
|
|
20185
20578
|
const tsxConfigPath = path68.join(resolvedSource, "apitogo.config.tsx");
|
|
20186
|
-
const configContent = await
|
|
20579
|
+
const configContent = await readFile22(tsxConfigPath, "utf8");
|
|
20187
20580
|
try {
|
|
20188
20581
|
const patched = patchApitogoConfigOpenApiToFile(configContent, input5);
|
|
20189
20582
|
await writeFile19(tsxConfigPath, patched);
|
|
@@ -20539,7 +20932,7 @@ export default function ${componentName}() {
|
|
|
20539
20932
|
// src/cli/make-config/scaffold-project.ts
|
|
20540
20933
|
init_package_json();
|
|
20541
20934
|
import { constants } from "node:fs";
|
|
20542
|
-
import { access as access11, mkdir as mkdir16, readFile as
|
|
20935
|
+
import { access as access11, mkdir as mkdir16, readFile as readFile23, writeFile as writeFile20 } from "node:fs/promises";
|
|
20543
20936
|
import path70 from "node:path";
|
|
20544
20937
|
import { glob as glob7 } from "glob";
|
|
20545
20938
|
|
|
@@ -20621,7 +21014,7 @@ async function ensurePackageJson2(dir) {
|
|
|
20621
21014
|
`, "utf8");
|
|
20622
21015
|
return true;
|
|
20623
21016
|
}
|
|
20624
|
-
const raw = await
|
|
21017
|
+
const raw = await readFile23(pkgPath, "utf8");
|
|
20625
21018
|
let pkg;
|
|
20626
21019
|
try {
|
|
20627
21020
|
pkg = JSON.parse(raw);
|
|
@@ -20750,7 +21143,7 @@ var fileExists3 = async (filePath) => {
|
|
|
20750
21143
|
}
|
|
20751
21144
|
};
|
|
20752
21145
|
var readJsonConfig = async (filePath) => {
|
|
20753
|
-
const raw = await
|
|
21146
|
+
const raw = await readFile24(filePath, "utf8");
|
|
20754
21147
|
const parsed = JSON.parse(raw);
|
|
20755
21148
|
if (!isPlainObject3(parsed)) {
|
|
20756
21149
|
throw new Error(`${APITOGO_JSON_FILENAME} must be a JSON object.`);
|
|
@@ -20800,7 +21193,7 @@ async function persistConfig(config2, ctx) {
|
|
|
20800
21193
|
`, "utf8");
|
|
20801
21194
|
const tsxPath = path71.join(ctx.dir, "apitogo.config.tsx");
|
|
20802
21195
|
if (await fileExists3(tsxPath) && tsxRoutes.length > 0) {
|
|
20803
|
-
const tsxSource = await
|
|
21196
|
+
const tsxSource = await readFile24(tsxPath, "utf8");
|
|
20804
21197
|
if (isManagedConfigWrapper(tsxSource)) {
|
|
20805
21198
|
await writeFile21(
|
|
20806
21199
|
tsxPath,
|
|
@@ -21172,7 +21565,7 @@ var config_default = {
|
|
|
21172
21565
|
};
|
|
21173
21566
|
|
|
21174
21567
|
// src/cli/cmds/deploy.ts
|
|
21175
|
-
import
|
|
21568
|
+
import path86 from "node:path";
|
|
21176
21569
|
|
|
21177
21570
|
// src/cli/apply/handler.ts
|
|
21178
21571
|
import path82 from "node:path";
|
|
@@ -21196,15 +21589,15 @@ function buildGatewayPlansValidationRequest(plans) {
|
|
|
21196
21589
|
};
|
|
21197
21590
|
});
|
|
21198
21591
|
}
|
|
21199
|
-
function toConfigIssue(
|
|
21592
|
+
function toConfigIssue(issue4) {
|
|
21200
21593
|
return {
|
|
21201
|
-
...
|
|
21202
|
-
field:
|
|
21594
|
+
...issue4,
|
|
21595
|
+
field: issue4.field.replace(/\.limitsJson$/, ".limits")
|
|
21203
21596
|
};
|
|
21204
21597
|
}
|
|
21205
|
-
function formatGatewayPlanIssue(
|
|
21206
|
-
const sku =
|
|
21207
|
-
return `${
|
|
21598
|
+
function formatGatewayPlanIssue(issue4) {
|
|
21599
|
+
const sku = issue4.sku ? ` (${issue4.sku})` : "";
|
|
21600
|
+
return `${issue4.field}${sku}: ${issue4.message}`;
|
|
21208
21601
|
}
|
|
21209
21602
|
async function checkGatewayPlansOnPlatform(client, args) {
|
|
21210
21603
|
let result;
|
|
@@ -21227,10 +21620,10 @@ async function checkGatewayPlansOnPlatform(client, args) {
|
|
|
21227
21620
|
async function assertGatewayPlansValidForPublish(client, args) {
|
|
21228
21621
|
const localErrors = args.localErrors ?? [];
|
|
21229
21622
|
const check2 = await checkGatewayPlansOnPlatform(client, args);
|
|
21230
|
-
const localFields = new Set(localErrors.map((
|
|
21623
|
+
const localFields = new Set(localErrors.map((issue4) => issue4.field));
|
|
21231
21624
|
const errors = [
|
|
21232
21625
|
...localErrors,
|
|
21233
|
-
...(check2?.errors ?? []).filter((
|
|
21626
|
+
...(check2?.errors ?? []).filter((issue4) => !localFields.has(issue4.field))
|
|
21234
21627
|
];
|
|
21235
21628
|
if (errors.length === 0) {
|
|
21236
21629
|
return { blocked: false };
|
|
@@ -21704,8 +22097,8 @@ function normalizeActionKey(actionKey2) {
|
|
|
21704
22097
|
const trimmed = actionKey2.trim();
|
|
21705
22098
|
if (trimmed.includes(":")) {
|
|
21706
22099
|
const [method2, ...pathParts] = trimmed.split(":");
|
|
21707
|
-
const
|
|
21708
|
-
return `${method2?.toLowerCase()}:${
|
|
22100
|
+
const path105 = pathParts.join(":").trim();
|
|
22101
|
+
return `${method2?.toLowerCase()}:${path105.startsWith("/") ? path105 : `/${path105}`}`;
|
|
21709
22102
|
}
|
|
21710
22103
|
const spaceIndex = trimmed.indexOf(" ");
|
|
21711
22104
|
if (spaceIndex <= 0) {
|
|
@@ -21714,8 +22107,8 @@ function normalizeActionKey(actionKey2) {
|
|
|
21714
22107
|
);
|
|
21715
22108
|
}
|
|
21716
22109
|
const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
|
|
21717
|
-
const
|
|
21718
|
-
return `${method}:${
|
|
22110
|
+
const path104 = trimmed.slice(spaceIndex + 1).trim();
|
|
22111
|
+
return `${method}:${path104.startsWith("/") ? path104 : `/${path104}`}`;
|
|
21719
22112
|
}
|
|
21720
22113
|
async function applyGatewayPublicRoutes(client, projectId, routes, options) {
|
|
21721
22114
|
const applied = [];
|
|
@@ -22193,7 +22586,7 @@ async function upsertGatewayPlansFromManifest(client, config2, projectId, plans,
|
|
|
22193
22586
|
}
|
|
22194
22587
|
|
|
22195
22588
|
// src/cli/platform/helpers/openapi-gateway-servers.ts
|
|
22196
|
-
import { mkdir as mkdir18, readFile as
|
|
22589
|
+
import { mkdir as mkdir18, readFile as readFile25, writeFile as writeFile22 } from "node:fs/promises";
|
|
22197
22590
|
import path74 from "node:path";
|
|
22198
22591
|
import YAML3 from "yaml";
|
|
22199
22592
|
var GATEWAY_URL_ENV_KEY = "APITOGO_GATEWAY_URL";
|
|
@@ -22225,7 +22618,7 @@ async function resolveProjectGatewayUrl(client, projectId) {
|
|
|
22225
22618
|
return null;
|
|
22226
22619
|
}
|
|
22227
22620
|
async function readOpenApiDocument(filePath) {
|
|
22228
|
-
const content = await
|
|
22621
|
+
const content = await readFile25(filePath, "utf8");
|
|
22229
22622
|
const ext = path74.extname(filePath).toLowerCase();
|
|
22230
22623
|
const parsed = ext === ".json" ? JSON.parse(content) : YAML3.parse(content);
|
|
22231
22624
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -22254,7 +22647,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
|
|
|
22254
22647
|
path74.resolve(siteDirectory),
|
|
22255
22648
|
APITOGO_CONFIG_FILENAME2
|
|
22256
22649
|
);
|
|
22257
|
-
const raw = await
|
|
22650
|
+
const raw = await readFile25(configPath, "utf8");
|
|
22258
22651
|
const config2 = JSON.parse(raw);
|
|
22259
22652
|
const previousApis = config2.apis;
|
|
22260
22653
|
const previousBackend = config2.backend;
|
|
@@ -22271,7 +22664,7 @@ async function pointSiteApisAtPublishOpenApi(siteDirectory, relativeInput) {
|
|
|
22271
22664
|
`, "utf8");
|
|
22272
22665
|
return {
|
|
22273
22666
|
restore: async () => {
|
|
22274
|
-
const currentRaw = await
|
|
22667
|
+
const currentRaw = await readFile25(configPath, "utf8");
|
|
22275
22668
|
const current = JSON.parse(currentRaw);
|
|
22276
22669
|
if (previousApis === void 0) {
|
|
22277
22670
|
delete current.apis;
|
|
@@ -22306,7 +22699,7 @@ async function prepareOpenApiServersForGatewayBuild(options) {
|
|
|
22306
22699
|
);
|
|
22307
22700
|
if (!openApiPath) {
|
|
22308
22701
|
try {
|
|
22309
|
-
const raw = await
|
|
22702
|
+
const raw = await readFile25(
|
|
22310
22703
|
path74.join(siteDirectory, APITOGO_CONFIG_FILENAME2),
|
|
22311
22704
|
"utf8"
|
|
22312
22705
|
);
|
|
@@ -22336,8 +22729,8 @@ function normalizeActionKeyFormat(actionKey2) {
|
|
|
22336
22729
|
const trimmed = actionKey2.trim();
|
|
22337
22730
|
if (trimmed.includes(":")) {
|
|
22338
22731
|
const [method2, ...pathParts] = trimmed.split(":");
|
|
22339
|
-
const
|
|
22340
|
-
return `${method2.toLowerCase()}:${
|
|
22732
|
+
const path105 = pathParts.join(":").trim();
|
|
22733
|
+
return `${method2.toLowerCase()}:${path105.startsWith("/") ? path105 : `/${path105}`}`;
|
|
22341
22734
|
}
|
|
22342
22735
|
const spaceIndex = trimmed.indexOf(" ");
|
|
22343
22736
|
if (spaceIndex <= 0) {
|
|
@@ -22346,8 +22739,8 @@ function normalizeActionKeyFormat(actionKey2) {
|
|
|
22346
22739
|
);
|
|
22347
22740
|
}
|
|
22348
22741
|
const method = trimmed.slice(0, spaceIndex).trim().toLowerCase();
|
|
22349
|
-
const
|
|
22350
|
-
return `${method}:${
|
|
22742
|
+
const path104 = trimmed.slice(spaceIndex + 1).trim();
|
|
22743
|
+
return `${method}:${path104.startsWith("/") ? path104 : `/${path104}`}`;
|
|
22351
22744
|
}
|
|
22352
22745
|
function normalizePublicRoute(route) {
|
|
22353
22746
|
const trimmed = route.trim();
|
|
@@ -22380,7 +22773,7 @@ function collectManifestPublicActionKeys(gateway) {
|
|
|
22380
22773
|
}
|
|
22381
22774
|
|
|
22382
22775
|
// src/cli/platform/helpers/publish-ledger.ts
|
|
22383
|
-
import { mkdir as mkdir19, readFile as
|
|
22776
|
+
import { mkdir as mkdir19, readFile as readFile26, writeFile as writeFile23 } from "node:fs/promises";
|
|
22384
22777
|
import path75 from "node:path";
|
|
22385
22778
|
var PUBLISH_LEDGER_RELATIVE_PATH = ".apitogo/publish-state.json";
|
|
22386
22779
|
function publishLedgerPath(siteDirectory) {
|
|
@@ -22389,7 +22782,7 @@ function publishLedgerPath(siteDirectory) {
|
|
|
22389
22782
|
async function readPublishLedger(siteDirectory) {
|
|
22390
22783
|
const filePath = publishLedgerPath(siteDirectory);
|
|
22391
22784
|
try {
|
|
22392
|
-
const raw = await
|
|
22785
|
+
const raw = await readFile26(filePath, "utf8");
|
|
22393
22786
|
const parsed = JSON.parse(raw);
|
|
22394
22787
|
if (parsed?.version !== 1 || !Array.isArray(parsed.steps)) {
|
|
22395
22788
|
return null;
|
|
@@ -24092,6 +24485,10 @@ function resumedCreateStep(projectId) {
|
|
|
24092
24485
|
};
|
|
24093
24486
|
}
|
|
24094
24487
|
async function resolveBootstrapProjectId(client, args, steps) {
|
|
24488
|
+
if (args.projectId) {
|
|
24489
|
+
steps.push(resumedCreateStep(args.projectId));
|
|
24490
|
+
return { projectId: args.projectId, resumed: true };
|
|
24491
|
+
}
|
|
24095
24492
|
const existing = await findExistingProjectByName(client, args.name);
|
|
24096
24493
|
if (existing) {
|
|
24097
24494
|
steps.push(resumedCreateStep(existing.id));
|
|
@@ -24151,11 +24548,14 @@ async function handleBootstrapProject(client, config2, args, progress) {
|
|
|
24151
24548
|
{
|
|
24152
24549
|
name: args.name,
|
|
24153
24550
|
description: args.description,
|
|
24154
|
-
modules
|
|
24551
|
+
modules,
|
|
24552
|
+
projectId: args.projectId
|
|
24155
24553
|
},
|
|
24156
24554
|
steps
|
|
24157
24555
|
);
|
|
24158
|
-
|
|
24556
|
+
await args.onProjectResolved?.(projectId);
|
|
24557
|
+
const continuingWithoutPlan = selectFreePlan && args.projectId !== void 0 && isPlatformPlanUnselected((await client.getProject(projectId)).planTier);
|
|
24558
|
+
if (selectFreePlan && (!resumed || continuingWithoutPlan)) {
|
|
24159
24559
|
await bootstrapProgress?.onStep("Selecting free plan");
|
|
24160
24560
|
const freeResult = await handleSelectFreePlan(
|
|
24161
24561
|
client,
|
|
@@ -24167,7 +24567,7 @@ async function handleBootstrapProject(client, config2, args, progress) {
|
|
|
24167
24567
|
return freeResult;
|
|
24168
24568
|
}
|
|
24169
24569
|
steps.push({ step: "select_free_plan", status: "succeeded" });
|
|
24170
|
-
} else if (selectFreePlan && resumed) {
|
|
24570
|
+
} else if (selectFreePlan && resumed && !continuingWithoutPlan) {
|
|
24171
24571
|
steps.push({
|
|
24172
24572
|
step: "select_free_plan",
|
|
24173
24573
|
status: "skipped",
|
|
@@ -24356,9 +24756,9 @@ async function handleTeardownProject(client, config2, args, progress) {
|
|
|
24356
24756
|
|
|
24357
24757
|
// src/cli/platform/tools/dev-portal-bootstrap-shared.ts
|
|
24358
24758
|
var BootstrapStepError = class extends Error {
|
|
24359
|
-
constructor(
|
|
24759
|
+
constructor(failedStep2, message, recoveryHint) {
|
|
24360
24760
|
super(message);
|
|
24361
|
-
this.failedStep =
|
|
24761
|
+
this.failedStep = failedStep2;
|
|
24362
24762
|
this.recoveryHint = recoveryHint;
|
|
24363
24763
|
this.name = "BootstrapStepError";
|
|
24364
24764
|
}
|
|
@@ -24561,7 +24961,7 @@ async function resolveProjectIdForPublish(client, config2, args, wait, progress,
|
|
|
24561
24961
|
import path81 from "node:path";
|
|
24562
24962
|
|
|
24563
24963
|
// src/cli/platform/helpers/dev-portal-site-api-url.ts
|
|
24564
|
-
import { readdir as readdir3, readFile as
|
|
24964
|
+
import { readdir as readdir3, readFile as readFile27, writeFile as writeFile24 } from "node:fs/promises";
|
|
24565
24965
|
import path78 from "node:path";
|
|
24566
24966
|
var SITE_API_BASE_URL_ENV_KEY = "VITE_APITOGO_AUTHENTICATION_API_BASE_URL";
|
|
24567
24967
|
var PLACEHOLDER_HOST_MARKERS = [
|
|
@@ -24586,7 +24986,7 @@ async function readSiteApiBaseUrl(sourceDirectory) {
|
|
|
24586
24986
|
const resolved = path78.resolve(sourceDirectory);
|
|
24587
24987
|
for (const envFile of [".env", ".env.production"]) {
|
|
24588
24988
|
try {
|
|
24589
|
-
const content = await
|
|
24989
|
+
const content = await readFile27(path78.join(resolved, envFile), "utf8");
|
|
24590
24990
|
const lines = [...content.matchAll(ENV_LINE_GLOBAL_RE)];
|
|
24591
24991
|
if (lines.length === 0) {
|
|
24592
24992
|
continue;
|
|
@@ -24617,7 +25017,7 @@ async function patchSiteApiBaseUrl(sourceDirectory, apiBaseUrl) {
|
|
|
24617
25017
|
for (const envFile of [".env.production", ".env"]) {
|
|
24618
25018
|
const envPath = path78.join(resolved, envFile);
|
|
24619
25019
|
try {
|
|
24620
|
-
const content = await
|
|
25020
|
+
const content = await readFile27(envPath, "utf8");
|
|
24621
25021
|
const withoutAssignments = content.replace(ENV_LINE_GLOBAL_RE, "").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
24622
25022
|
const patched = withoutAssignments ? `${withoutAssignments}
|
|
24623
25023
|
${line2}
|
|
@@ -24697,7 +25097,7 @@ async function assertDeployableDistHasLiveApiBaseUrl(distDirectory, expectedApiB
|
|
|
24697
25097
|
let found = false;
|
|
24698
25098
|
let foundPlaceholder = false;
|
|
24699
25099
|
for await (const file of walkFiles(dist)) {
|
|
24700
|
-
const content = await
|
|
25100
|
+
const content = await readFile27(file, "utf8");
|
|
24701
25101
|
if (content.includes(needle)) {
|
|
24702
25102
|
found = true;
|
|
24703
25103
|
}
|
|
@@ -25382,7 +25782,7 @@ function throwOnManifestIssues(issues) {
|
|
|
25382
25782
|
return;
|
|
25383
25783
|
}
|
|
25384
25784
|
throw new Error(
|
|
25385
|
-
`apitogo.config.json is not platform-compatible: ${issues.map((
|
|
25785
|
+
`apitogo.config.json is not platform-compatible: ${issues.map((issue4) => `${issue4.field}: ${issue4.message}`).join("; ")}`
|
|
25386
25786
|
);
|
|
25387
25787
|
}
|
|
25388
25788
|
async function runApplyFromSiteDirectory(client, config2, args, progress) {
|
|
@@ -26400,14 +26800,14 @@ function formatBlockedOrFailedResultForHuman(parsed) {
|
|
|
26400
26800
|
const lines = [
|
|
26401
26801
|
typeof parsed.userMessage === "string" ? parsed.userMessage : "Deploy is blocked: apitogo.config.json has errors."
|
|
26402
26802
|
];
|
|
26403
|
-
for (const
|
|
26404
|
-
lines.push(` - ${formatGatewayPlanIssue(
|
|
26803
|
+
for (const issue4 of Array.isArray(parsed.errors) ? parsed.errors : []) {
|
|
26804
|
+
lines.push(` - ${formatGatewayPlanIssue(issue4)}`);
|
|
26405
26805
|
}
|
|
26406
26806
|
const warnings = Array.isArray(parsed.warnings) ? parsed.warnings : [];
|
|
26407
26807
|
if (warnings.length > 0) {
|
|
26408
26808
|
lines.push("", "Warnings:");
|
|
26409
|
-
for (const
|
|
26410
|
-
lines.push(` - ${formatGatewayPlanIssue(
|
|
26809
|
+
for (const issue4 of warnings) {
|
|
26810
|
+
lines.push(` - ${formatGatewayPlanIssue(issue4)}`);
|
|
26411
26811
|
}
|
|
26412
26812
|
}
|
|
26413
26813
|
lines.push(
|
|
@@ -26824,7 +27224,7 @@ async function apply(argv) {
|
|
|
26824
27224
|
|
|
26825
27225
|
// src/cli/deploy/command.ts
|
|
26826
27226
|
init_invariant();
|
|
26827
|
-
import
|
|
27227
|
+
import path85 from "node:path";
|
|
26828
27228
|
|
|
26829
27229
|
// src/cli/deploy/v2.ts
|
|
26830
27230
|
import { rm as rm12 } from "node:fs/promises";
|
|
@@ -26836,7 +27236,7 @@ import path83 from "node:path";
|
|
|
26836
27236
|
|
|
26837
27237
|
// src/cli/deploy/gateway.ts
|
|
26838
27238
|
import { createHash as createHash4 } from "node:crypto";
|
|
26839
|
-
import { readFile as
|
|
27239
|
+
import { readFile as readFile28 } from "node:fs/promises";
|
|
26840
27240
|
init_invariant();
|
|
26841
27241
|
var GatewayDeployError = class extends ApitogoError {
|
|
26842
27242
|
constructor(code, message, developerHint) {
|
|
@@ -26876,7 +27276,7 @@ async function readSpec(gateway) {
|
|
|
26876
27276
|
"Add one to gateway.specs in the config."
|
|
26877
27277
|
);
|
|
26878
27278
|
try {
|
|
26879
|
-
return await
|
|
27279
|
+
return await readFile28(file);
|
|
26880
27280
|
} catch {
|
|
26881
27281
|
throw new GatewayDeployError(
|
|
26882
27282
|
"GATEWAY_SPEC_UNREADABLE",
|
|
@@ -27070,58 +27470,692 @@ async function deployGateway(resolved, projectId, deps, options = {}) {
|
|
|
27070
27470
|
};
|
|
27071
27471
|
}
|
|
27072
27472
|
|
|
27073
|
-
// src/cli/
|
|
27074
|
-
|
|
27075
|
-
|
|
27076
|
-
|
|
27077
|
-
|
|
27078
|
-
|
|
27079
|
-
|
|
27080
|
-
|
|
27081
|
-
|
|
27082
|
-
|
|
27083
|
-
|
|
27084
|
-
|
|
27085
|
-
|
|
27473
|
+
// src/cli/deploy/identity.ts
|
|
27474
|
+
init_invariant();
|
|
27475
|
+
var IdentityDeployError = class extends ApitogoError {
|
|
27476
|
+
constructor(code, message, developerHint) {
|
|
27477
|
+
super(message, { developerHint, title: "Cannot deploy identity" });
|
|
27478
|
+
this.code = code;
|
|
27479
|
+
this.name = "IdentityDeployError";
|
|
27480
|
+
}
|
|
27481
|
+
};
|
|
27482
|
+
var IDENTITY_STEPS = ["request", "activate"];
|
|
27483
|
+
var PORTAL_CALLBACK_PATH = "/oauth/callback";
|
|
27484
|
+
var defaultSleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
27485
|
+
function identityNotDeployableReason(resolved) {
|
|
27486
|
+
const identity = resolved.identity;
|
|
27487
|
+
if (!identity) return "identity is not in the config";
|
|
27488
|
+
if (identity.provider === "oidc" && (!identity.issuer || !identity.clientId))
|
|
27489
|
+
return 'identity.provider "oidc" needs issuer and clientId';
|
|
27490
|
+
return void 0;
|
|
27086
27491
|
}
|
|
27087
|
-
function
|
|
27088
|
-
return
|
|
27492
|
+
function identityBodyForRedirect(resolved, redirectUri) {
|
|
27493
|
+
return {
|
|
27494
|
+
portalRedirectUris: [redirectUri],
|
|
27495
|
+
nativeSignup: resolved.identity?.native ?? true
|
|
27496
|
+
};
|
|
27089
27497
|
}
|
|
27090
|
-
|
|
27091
|
-
|
|
27092
|
-
|
|
27093
|
-
|
|
27094
|
-
|
|
27095
|
-
|
|
27096
|
-
|
|
27097
|
-
|
|
27098
|
-
|
|
27099
|
-
|
|
27100
|
-
|
|
27101
|
-
|
|
27102
|
-
|
|
27103
|
-
|
|
27104
|
-
|
|
27105
|
-
|
|
27106
|
-
|
|
27107
|
-
plan2.apiReference ? "API reference" : void 0
|
|
27108
|
-
].filter((feature) => feature !== void 0).map((feature) => `will appear: ${feature}`),
|
|
27109
|
-
...plan2.skipped.map((s) => `left out: ${s.piece} (${s.reason})`)
|
|
27110
|
-
];
|
|
27111
|
-
const hash2 = await portalBuildHash(resolved, prepared);
|
|
27112
|
-
const previous = stepOf(ledger, "portal", "build");
|
|
27113
|
-
const built = await exists4(
|
|
27114
|
-
path83.join(resolved.state.buildDir, "index.html")
|
|
27498
|
+
function identityRequestBody(resolved, portalUrl) {
|
|
27499
|
+
return identityBodyForRedirect(
|
|
27500
|
+
resolved,
|
|
27501
|
+
`${portalUrl.replace(/\/+$/, "")}${PORTAL_CALLBACK_PATH}`
|
|
27502
|
+
);
|
|
27503
|
+
}
|
|
27504
|
+
function identityInputsHash(projectId, body) {
|
|
27505
|
+
return stableJson({ projectId, body });
|
|
27506
|
+
}
|
|
27507
|
+
async function portalUrlOf(resolved, projectId, client) {
|
|
27508
|
+
const known = (await readManifest(resolved.state))?.services.portal?.url;
|
|
27509
|
+
const url2 = known ?? resolveSwaUrl(await client.getProject(projectId));
|
|
27510
|
+
if (!url2)
|
|
27511
|
+
throw new IdentityDeployError(
|
|
27512
|
+
"IDENTITY_PORTAL_ADDRESS_UNKNOWN",
|
|
27513
|
+
"The portal has no public address yet, and the identity needs it for the sign-in redirect.",
|
|
27514
|
+
"Run `apitogo deploy --only portal` once so the platform gives the portal an address, then deploy identity."
|
|
27115
27515
|
);
|
|
27116
|
-
|
|
27117
|
-
|
|
27118
|
-
|
|
27119
|
-
|
|
27120
|
-
|
|
27121
|
-
|
|
27122
|
-
|
|
27123
|
-
|
|
27124
|
-
|
|
27516
|
+
return url2;
|
|
27517
|
+
}
|
|
27518
|
+
function rejected2(error) {
|
|
27519
|
+
if (error instanceof PlatformApiError && error.status === 409) {
|
|
27520
|
+
throw new IdentityDeployError(
|
|
27521
|
+
"IDENTITY_SETTINGS_CHANGED",
|
|
27522
|
+
"This project already has an identity with other settings, and its audience and portal address cannot be changed in place.",
|
|
27523
|
+
"Keep the portal address as it was, or remove the identity first (this deletes every user in it)."
|
|
27524
|
+
);
|
|
27525
|
+
}
|
|
27526
|
+
if (error instanceof PlatformApiError && error.status === 400) {
|
|
27527
|
+
const lines = describePlatformProblem(error);
|
|
27528
|
+
throw new IdentityDeployError(
|
|
27529
|
+
"IDENTITY_REJECTED",
|
|
27530
|
+
`The platform refused the identity request:
|
|
27531
|
+
${lines.join("\n ") || error.message}`,
|
|
27532
|
+
"Fix the cause in apitogo.config.json and run deploy again."
|
|
27533
|
+
);
|
|
27534
|
+
}
|
|
27535
|
+
throw error;
|
|
27536
|
+
}
|
|
27537
|
+
async function recordOwnProvider(resolved, projectId) {
|
|
27538
|
+
const identity = resolved.identity;
|
|
27539
|
+
const issuer = identity?.issuer?.replace(/\/+$/, "");
|
|
27540
|
+
const clientId = identity?.clientId;
|
|
27541
|
+
if (!issuer || !clientId)
|
|
27542
|
+
throw new Error("identity.issuer and identity.clientId are needed.");
|
|
27543
|
+
const known = (await readManifest(resolved.state))?.services.identity;
|
|
27544
|
+
const changed = known?.provider !== "oidc" || known.issuer !== issuer || known.clientId !== clientId;
|
|
27545
|
+
if (changed)
|
|
27546
|
+
await writeManifest(
|
|
27547
|
+
resolved.state,
|
|
27548
|
+
withIdentityEntry(await readManifest(resolved.state), {
|
|
27549
|
+
projectId,
|
|
27550
|
+
projectName: resolved.project.name,
|
|
27551
|
+
provider: "oidc",
|
|
27552
|
+
issuer,
|
|
27553
|
+
clientId,
|
|
27554
|
+
nativeSignup: false
|
|
27555
|
+
})
|
|
27556
|
+
);
|
|
27557
|
+
return {
|
|
27558
|
+
status: changed ? "deployed" : "unchanged",
|
|
27559
|
+
steps: [],
|
|
27560
|
+
issuer,
|
|
27561
|
+
clientId
|
|
27562
|
+
};
|
|
27563
|
+
}
|
|
27564
|
+
async function deployIdentity(resolved, projectId, deps, options = {}) {
|
|
27565
|
+
if (resolved.identity?.provider === "oidc")
|
|
27566
|
+
return recordOwnProvider(resolved, projectId);
|
|
27567
|
+
const client = deps.client;
|
|
27568
|
+
const sleep5 = deps.sleep ?? defaultSleep2;
|
|
27569
|
+
const now = deps.now ?? Date.now;
|
|
27570
|
+
const timeoutMs = deps.activateTimeoutMs ?? 12e4;
|
|
27571
|
+
const intervalMs = deps.pollIntervalMs ?? 2e3;
|
|
27572
|
+
const portalUrl = await portalUrlOf(resolved, projectId, client);
|
|
27573
|
+
const body = identityRequestBody(resolved, portalUrl);
|
|
27574
|
+
const inputsHash = identityInputsHash(projectId, body);
|
|
27575
|
+
const requestStep = {
|
|
27576
|
+
name: "request",
|
|
27577
|
+
hash: () => inputsHash,
|
|
27578
|
+
run: async () => {
|
|
27579
|
+
try {
|
|
27580
|
+
const answer = await client.putProjectIdentity(projectId, body);
|
|
27581
|
+
return {
|
|
27582
|
+
status: answer.status,
|
|
27583
|
+
redirectUri: body.portalRedirectUris[0] ?? ""
|
|
27584
|
+
};
|
|
27585
|
+
} catch (error) {
|
|
27586
|
+
return rejected2(error);
|
|
27587
|
+
}
|
|
27588
|
+
},
|
|
27589
|
+
// An identity that was removed, or that failed, is not a result worth keeping.
|
|
27590
|
+
stillValid: async () => {
|
|
27591
|
+
const found = await client.getProjectIdentity(projectId);
|
|
27592
|
+
return found !== null && found.status !== "Failed" && found.status !== "Released" && found.status !== "Releasing";
|
|
27593
|
+
}
|
|
27594
|
+
};
|
|
27595
|
+
const activateStep = {
|
|
27596
|
+
name: "activate",
|
|
27597
|
+
hash: () => inputsHash,
|
|
27598
|
+
run: async () => {
|
|
27599
|
+
const deadline = now() + timeoutMs;
|
|
27600
|
+
for (; ; ) {
|
|
27601
|
+
const found = await client.getProjectIdentity(projectId);
|
|
27602
|
+
if (found?.status === "Active")
|
|
27603
|
+
return {
|
|
27604
|
+
issuer: found.issuer,
|
|
27605
|
+
clientId: found.clientId,
|
|
27606
|
+
audience: found.audience,
|
|
27607
|
+
nativeSignup: String(found.nativeSignup)
|
|
27608
|
+
};
|
|
27609
|
+
if (found?.status === "Failed")
|
|
27610
|
+
throw new IdentityDeployError(
|
|
27611
|
+
"IDENTITY_FAILED",
|
|
27612
|
+
`The platform could not make the identity: ${[found.errorCode, found.errorMessage].filter(Boolean).join(" ") || "it failed"}.`,
|
|
27613
|
+
"Fix the cause and run deploy again; the request is sent again."
|
|
27614
|
+
);
|
|
27615
|
+
if (now() >= deadline)
|
|
27616
|
+
throw new IdentityDeployError(
|
|
27617
|
+
"IDENTITY_TIMEOUT",
|
|
27618
|
+
`The identity did not become active within ${Math.round(timeoutMs / 1e3)} seconds (state: ${found?.status ?? "not found"}).`,
|
|
27619
|
+
"Run deploy again to keep waiting."
|
|
27620
|
+
);
|
|
27621
|
+
await sleep5(intervalMs);
|
|
27622
|
+
}
|
|
27623
|
+
},
|
|
27624
|
+
stillValid: async () => (await client.getProjectIdentity(projectId))?.status === "Active"
|
|
27625
|
+
};
|
|
27626
|
+
const outcomes = await runPipeline(
|
|
27627
|
+
resolved.state,
|
|
27628
|
+
"identity",
|
|
27629
|
+
[requestStep, activateStep],
|
|
27630
|
+
{ force: options.force }
|
|
27631
|
+
);
|
|
27632
|
+
const activate = outcomes.find((outcome) => outcome.step === "activate");
|
|
27633
|
+
const ran = activate !== void 0 && !activate.skipped;
|
|
27634
|
+
const known = (await readManifest(resolved.state))?.services.identity;
|
|
27635
|
+
let learned;
|
|
27636
|
+
if (ran || known?.issuer === void 0) {
|
|
27637
|
+
learned = await client.getProjectIdentity(projectId) ?? void 0;
|
|
27638
|
+
if (learned?.status === "Active") {
|
|
27639
|
+
await writeManifest(
|
|
27640
|
+
resolved.state,
|
|
27641
|
+
withIdentityEntry(await readManifest(resolved.state), {
|
|
27642
|
+
projectId,
|
|
27643
|
+
projectName: resolved.project.name,
|
|
27644
|
+
issuer: learned.issuer,
|
|
27645
|
+
clientId: learned.clientId,
|
|
27646
|
+
audience: learned.audience,
|
|
27647
|
+
nativeSignup: learned.nativeSignup
|
|
27648
|
+
})
|
|
27649
|
+
);
|
|
27650
|
+
}
|
|
27651
|
+
}
|
|
27652
|
+
return {
|
|
27653
|
+
status: ran ? "deployed" : "unchanged",
|
|
27654
|
+
steps: outcomes.map((outcome) => ({
|
|
27655
|
+
step: outcome.step,
|
|
27656
|
+
skipped: outcome.skipped
|
|
27657
|
+
})),
|
|
27658
|
+
issuer: learned?.issuer ?? known?.issuer,
|
|
27659
|
+
clientId: learned?.clientId ?? known?.clientId
|
|
27660
|
+
};
|
|
27661
|
+
}
|
|
27662
|
+
|
|
27663
|
+
// src/cli/deploy/plans.ts
|
|
27664
|
+
init_invariant();
|
|
27665
|
+
var PlansDeployError = class extends ApitogoError {
|
|
27666
|
+
constructor(code, message, developerHint) {
|
|
27667
|
+
super(message, { developerHint, title: "Cannot deploy plans" });
|
|
27668
|
+
this.code = code;
|
|
27669
|
+
this.name = "PlansDeployError";
|
|
27670
|
+
}
|
|
27671
|
+
};
|
|
27672
|
+
var PLANS_STEPS = ["catalog", "backend", "identity-link"];
|
|
27673
|
+
var defaultSleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
27674
|
+
var BACKEND_MODULE = "dev-portal";
|
|
27675
|
+
var PLANS_CAPABILITIES = { catalog: true, keys: true, usage: true };
|
|
27676
|
+
async function plansInputs(resolved, projectId) {
|
|
27677
|
+
const plans = resolved.plans;
|
|
27678
|
+
if (!plans) throw new Error("plans is not in the config.");
|
|
27679
|
+
const wanted = toPlatformPlans(await readPlansFile(plans.file));
|
|
27680
|
+
const manifest = await readManifest(resolved.state);
|
|
27681
|
+
const identity = manifest?.services.identity;
|
|
27682
|
+
const ownIdentity = Boolean(
|
|
27683
|
+
identity?.provider === "apitogo" && identity.issuer
|
|
27684
|
+
);
|
|
27685
|
+
return {
|
|
27686
|
+
wanted,
|
|
27687
|
+
manifest,
|
|
27688
|
+
ownIdentity,
|
|
27689
|
+
catalogHash: stableJson({ projectId, plans: wanted }),
|
|
27690
|
+
backendHash: stableJson({ projectId }),
|
|
27691
|
+
linkHash: stableJson({
|
|
27692
|
+
projectId,
|
|
27693
|
+
issuer: identity?.issuer,
|
|
27694
|
+
clientId: identity?.clientId
|
|
27695
|
+
})
|
|
27696
|
+
};
|
|
27697
|
+
}
|
|
27698
|
+
function plansNotDeployableReason(resolved) {
|
|
27699
|
+
return resolved.plans ? void 0 : "plans is not in the config";
|
|
27700
|
+
}
|
|
27701
|
+
async function allActionKeys(client, projectId) {
|
|
27702
|
+
const keys = [];
|
|
27703
|
+
try {
|
|
27704
|
+
for (let page = 1; ; page++) {
|
|
27705
|
+
const result = await client.listGatewayActions(projectId, page, 100);
|
|
27706
|
+
keys.push(...result.items.map((item) => item.actionKey));
|
|
27707
|
+
if (result.items.length === 0 || page * result.pageSize >= result.totalCount)
|
|
27708
|
+
break;
|
|
27709
|
+
}
|
|
27710
|
+
} catch {
|
|
27711
|
+
return [];
|
|
27712
|
+
}
|
|
27713
|
+
return keys;
|
|
27714
|
+
}
|
|
27715
|
+
function refused(error) {
|
|
27716
|
+
if (error instanceof PlatformApiError && error.status === 400) {
|
|
27717
|
+
const lines = describePlatformProblem(error);
|
|
27718
|
+
throw new PlansDeployError(
|
|
27719
|
+
"PLANS_REJECTED",
|
|
27720
|
+
`The platform refused a plan:
|
|
27721
|
+
${lines.join("\n ") || error.message}`,
|
|
27722
|
+
"Fix the plan in the plans file and run deploy again."
|
|
27723
|
+
);
|
|
27724
|
+
}
|
|
27725
|
+
throw error;
|
|
27726
|
+
}
|
|
27727
|
+
async function deployPlans(resolved, projectId, deps, options = {}) {
|
|
27728
|
+
const plans = resolved.plans;
|
|
27729
|
+
if (!plans) throw new Error("plans is not in the config.");
|
|
27730
|
+
const client = deps.client;
|
|
27731
|
+
const sleep5 = deps.sleep ?? defaultSleep3;
|
|
27732
|
+
const now = deps.now ?? Date.now;
|
|
27733
|
+
const intervalMs = deps.pollIntervalMs ?? 2e3;
|
|
27734
|
+
const backendTimeoutMs = deps.backendTimeoutMs ?? 9e5;
|
|
27735
|
+
const linkTimeoutMs = deps.linkTimeoutMs ?? 6e5;
|
|
27736
|
+
const { wanted, manifest, ownIdentity, catalogHash, backendHash, linkHash } = await plansInputs(resolved, projectId);
|
|
27737
|
+
const catalogStep = {
|
|
27738
|
+
name: "catalog",
|
|
27739
|
+
hash: () => catalogHash,
|
|
27740
|
+
run: async () => {
|
|
27741
|
+
if (wanted.length === 0) return { created: "0", updated: "0" };
|
|
27742
|
+
const fallback = wanted.some((plan2) => plan2.includedActionKeys === null) ? await allActionKeys(client, projectId) : [];
|
|
27743
|
+
const bodies = wanted.map((plan2) => ({
|
|
27744
|
+
...plan2,
|
|
27745
|
+
includedActionKeys: plan2.includedActionKeys ?? fallback
|
|
27746
|
+
}));
|
|
27747
|
+
const checked = await client.validateGatewayPlans(bodies, { projectId });
|
|
27748
|
+
if (!checked.valid)
|
|
27749
|
+
throw new PlansDeployError(
|
|
27750
|
+
"PLANS_REJECTED",
|
|
27751
|
+
`The platform refused the plans:
|
|
27752
|
+
${checked.errors.map((issue4) => `${issue4.field}: ${issue4.message}`).join("\n ")}`,
|
|
27753
|
+
"Fix the listed problems in the plans file and run deploy again."
|
|
27754
|
+
);
|
|
27755
|
+
const existing = new Map(
|
|
27756
|
+
(await client.listGatewayPlans(projectId)).map((plan2) => [
|
|
27757
|
+
plan2.sku.toLowerCase(),
|
|
27758
|
+
plan2
|
|
27759
|
+
])
|
|
27760
|
+
);
|
|
27761
|
+
let created = 0;
|
|
27762
|
+
let updated = 0;
|
|
27763
|
+
for (const body of bodies) {
|
|
27764
|
+
const found = existing.get(body.sku);
|
|
27765
|
+
try {
|
|
27766
|
+
if (found) {
|
|
27767
|
+
await client.updateGatewayPlan(projectId, found.id, {
|
|
27768
|
+
displayName: body.displayName,
|
|
27769
|
+
isActive: true,
|
|
27770
|
+
limitsJson: body.limitsJson ?? null,
|
|
27771
|
+
includedActionKeys: body.includedActionKeys,
|
|
27772
|
+
priceAmount: body.priceAmount,
|
|
27773
|
+
pricingModel: body.pricingModel,
|
|
27774
|
+
priceLabel: body.priceLabel,
|
|
27775
|
+
priceCurrency: body.priceCurrency,
|
|
27776
|
+
billingPeriod: body.billingPeriod,
|
|
27777
|
+
sortOrder: body.sortOrder
|
|
27778
|
+
});
|
|
27779
|
+
updated++;
|
|
27780
|
+
} else {
|
|
27781
|
+
await client.createGatewayPlan(projectId, body);
|
|
27782
|
+
created++;
|
|
27783
|
+
}
|
|
27784
|
+
} catch (error) {
|
|
27785
|
+
refused(error);
|
|
27786
|
+
}
|
|
27787
|
+
}
|
|
27788
|
+
return { created: String(created), updated: String(updated) };
|
|
27789
|
+
},
|
|
27790
|
+
// A plan someone deleted on the platform is not a result worth keeping.
|
|
27791
|
+
stillValid: async () => {
|
|
27792
|
+
const present = new Set(
|
|
27793
|
+
(await client.listGatewayPlans(projectId)).filter((plan2) => plan2.isActive).map((plan2) => plan2.sku.toLowerCase())
|
|
27794
|
+
);
|
|
27795
|
+
return wanted.every((plan2) => present.has(plan2.sku));
|
|
27796
|
+
}
|
|
27797
|
+
};
|
|
27798
|
+
const backendOf = async () => (await client.getDevPortalBackend(projectId))?.fqdn;
|
|
27799
|
+
const backendStep = {
|
|
27800
|
+
name: "backend",
|
|
27801
|
+
hash: () => backendHash,
|
|
27802
|
+
run: async () => {
|
|
27803
|
+
let fqdn = await backendOf();
|
|
27804
|
+
if (!fqdn) {
|
|
27805
|
+
await client.activateModule(projectId, BACKEND_MODULE);
|
|
27806
|
+
const deadline = now() + backendTimeoutMs;
|
|
27807
|
+
for (; ; ) {
|
|
27808
|
+
fqdn = await backendOf();
|
|
27809
|
+
if (fqdn) break;
|
|
27810
|
+
if (now() >= deadline)
|
|
27811
|
+
throw new PlansDeployError(
|
|
27812
|
+
"PLANS_BACKEND_TIMEOUT",
|
|
27813
|
+
`The plans backend did not get an address within ${Math.round(backendTimeoutMs / 1e3)} seconds.`,
|
|
27814
|
+
"Run deploy again to keep waiting."
|
|
27815
|
+
);
|
|
27816
|
+
await sleep5(intervalMs);
|
|
27817
|
+
}
|
|
27818
|
+
}
|
|
27819
|
+
return { fqdn };
|
|
27820
|
+
},
|
|
27821
|
+
stillValid: async () => Boolean(await backendOf())
|
|
27822
|
+
};
|
|
27823
|
+
const linkStep = {
|
|
27824
|
+
name: "identity-link",
|
|
27825
|
+
hash: () => linkHash,
|
|
27826
|
+
run: async () => {
|
|
27827
|
+
const queued = await client.linkDevPortalIdentity(projectId);
|
|
27828
|
+
const deadline = now() + linkTimeoutMs;
|
|
27829
|
+
for (; ; ) {
|
|
27830
|
+
const job = (await client.listProvisioningJobs(projectId)).jobs.find(
|
|
27831
|
+
(item) => item.id === queued.jobId
|
|
27832
|
+
);
|
|
27833
|
+
const status2 = job?.status.toLowerCase();
|
|
27834
|
+
if (status2?.startsWith("completed")) return { jobId: queued.jobId };
|
|
27835
|
+
if (status2 === "failed")
|
|
27836
|
+
throw new PlansDeployError(
|
|
27837
|
+
"PLANS_LINK_FAILED",
|
|
27838
|
+
`Applying the identity to the plans backend failed: ${job?.errorMessage ?? "no reason given"}.`,
|
|
27839
|
+
"Run deploy again; the link is applied again."
|
|
27840
|
+
);
|
|
27841
|
+
if (now() >= deadline)
|
|
27842
|
+
throw new PlansDeployError(
|
|
27843
|
+
"PLANS_LINK_TIMEOUT",
|
|
27844
|
+
`Applying the identity did not finish within ${Math.round(linkTimeoutMs / 1e3)} seconds (state: ${status2 ?? "unknown"}).`,
|
|
27845
|
+
"Run deploy again to keep waiting."
|
|
27846
|
+
);
|
|
27847
|
+
await sleep5(intervalMs);
|
|
27848
|
+
}
|
|
27849
|
+
}
|
|
27850
|
+
};
|
|
27851
|
+
const steps = [catalogStep, backendStep, ...ownIdentity ? [linkStep] : []];
|
|
27852
|
+
const outcomes = await runPipeline(
|
|
27853
|
+
resolved.state,
|
|
27854
|
+
"plans",
|
|
27855
|
+
steps,
|
|
27856
|
+
{
|
|
27857
|
+
force: options.force
|
|
27858
|
+
}
|
|
27859
|
+
);
|
|
27860
|
+
const catalog = outcomes.find((outcome) => outcome.step === "catalog");
|
|
27861
|
+
const ran = outcomes.some((outcome) => !outcome.skipped);
|
|
27862
|
+
const known = manifest?.services.plans;
|
|
27863
|
+
let apiBaseUrl = known?.apiBaseUrl;
|
|
27864
|
+
if (ran || known?.apiBaseUrl === void 0) {
|
|
27865
|
+
apiBaseUrl = toApiBaseUrl(await backendOf()) ?? apiBaseUrl;
|
|
27866
|
+
if (apiBaseUrl) {
|
|
27867
|
+
const previous = known?.catalogRevision ?? 0;
|
|
27868
|
+
await writeManifest(
|
|
27869
|
+
resolved.state,
|
|
27870
|
+
withPlansEntry(await readManifest(resolved.state), {
|
|
27871
|
+
projectId,
|
|
27872
|
+
projectName: resolved.project.name,
|
|
27873
|
+
apiBaseUrl,
|
|
27874
|
+
billing: plans.billing.provider,
|
|
27875
|
+
catalogRevision: catalog && !catalog.skipped ? previous + 1 : Math.max(previous, 1),
|
|
27876
|
+
capabilities: PLANS_CAPABILITIES
|
|
27877
|
+
})
|
|
27878
|
+
);
|
|
27879
|
+
}
|
|
27880
|
+
}
|
|
27881
|
+
return {
|
|
27882
|
+
status: ran ? "deployed" : "unchanged",
|
|
27883
|
+
steps: outcomes.map((outcome) => ({
|
|
27884
|
+
step: outcome.step,
|
|
27885
|
+
skipped: outcome.skipped
|
|
27886
|
+
})),
|
|
27887
|
+
apiBaseUrl
|
|
27888
|
+
};
|
|
27889
|
+
}
|
|
27890
|
+
|
|
27891
|
+
// src/cli/portal/services-report.ts
|
|
27892
|
+
function stepState(record, hash2) {
|
|
27893
|
+
if (!record) return "not done yet";
|
|
27894
|
+
if (record.status === "failed")
|
|
27895
|
+
return `failed last time (${record.error ?? "no message"})`;
|
|
27896
|
+
if (record.status === "running") return "did not finish last time";
|
|
27897
|
+
if (hash2 === void 0)
|
|
27898
|
+
return "done (cannot compare: an input is not known locally)";
|
|
27899
|
+
return record.hash === hash2 ? "done, unchanged" : "inputs changed";
|
|
27900
|
+
}
|
|
27901
|
+
var isDone = (record, hash2) => record?.status === "succeeded" && hash2 !== void 0 && record.hash === hash2;
|
|
27902
|
+
async function planIdentityService(resolved, ledger, manifest) {
|
|
27903
|
+
const base = { service: "identity", issues: [] };
|
|
27904
|
+
const identity = resolved.identity;
|
|
27905
|
+
if (!identity) throw new Error("identity is not configured");
|
|
27906
|
+
const why = identityNotDeployableReason(resolved);
|
|
27907
|
+
if (why)
|
|
27908
|
+
return {
|
|
27909
|
+
...base,
|
|
27910
|
+
action: "not-deployable-yet",
|
|
27911
|
+
reason: `configured, but ${why}`,
|
|
27912
|
+
details: []
|
|
27913
|
+
};
|
|
27914
|
+
const known = manifest?.services.identity;
|
|
27915
|
+
if (identity.provider === "oidc") {
|
|
27916
|
+
const same = known?.provider === "oidc" && known.issuer === identity.issuer?.replace(/\/+$/, "") && known.clientId === identity.clientId;
|
|
27917
|
+
return {
|
|
27918
|
+
...base,
|
|
27919
|
+
action: same ? "skip" : "deploy",
|
|
27920
|
+
reason: same ? "deploy skipped: services.json already records your sign-in provider" : "will record your own sign-in provider in services.json (nothing is made on the platform)",
|
|
27921
|
+
details: [
|
|
27922
|
+
`provider: oidc ${identity.issuer}`,
|
|
27923
|
+
`client id: ${identity.clientId}`
|
|
27924
|
+
]
|
|
27925
|
+
};
|
|
27926
|
+
}
|
|
27927
|
+
const projectId = resolved.project.id;
|
|
27928
|
+
const portalUrl = manifest?.services.portal?.url;
|
|
27929
|
+
const recorded = stepOf(ledger, "identity", "request")?.output?.redirectUri;
|
|
27930
|
+
const body = portalUrl ? identityRequestBody(resolved, portalUrl) : recorded ? identityBodyForRedirect(resolved, recorded) : void 0;
|
|
27931
|
+
const hash2 = projectId && body ? identityInputsHash(projectId, body) : void 0;
|
|
27932
|
+
const details = [
|
|
27933
|
+
"provider: apitogo (one issuer for this project)",
|
|
27934
|
+
`sign-up with email and password: ${identity.native ? "on" : "off"}`,
|
|
27935
|
+
portalUrl ? `sign-in redirect: ${portalUrl.replace(/\/+$/, "")}${PORTAL_CALLBACK_PATH}` : recorded ? `sign-in redirect: ${recorded} (sent by the last deploy)` : "sign-in redirect: the portal address is not known locally; deploy takes it from the project",
|
|
27936
|
+
...known?.issuer ? [`issuer: ${known.issuer}`] : [],
|
|
27937
|
+
...IDENTITY_STEPS.map(
|
|
27938
|
+
(name) => `step ${name}: ${stepState(stepOf(ledger, "identity", name), hash2)}`
|
|
27939
|
+
)
|
|
27940
|
+
];
|
|
27941
|
+
const allDone = IDENTITY_STEPS.every(
|
|
27942
|
+
(name) => isDone(stepOf(ledger, "identity", name), hash2)
|
|
27943
|
+
);
|
|
27944
|
+
return {
|
|
27945
|
+
...base,
|
|
27946
|
+
action: allDone ? "skip" : "deploy",
|
|
27947
|
+
reason: allDone ? "deploy skipped: nothing it depends on has changed" : "will deploy: the identity is not made yet, or its settings changed",
|
|
27948
|
+
details
|
|
27949
|
+
};
|
|
27950
|
+
}
|
|
27951
|
+
async function planPlansService(resolved, ledger) {
|
|
27952
|
+
const base = { service: "plans", issues: [] };
|
|
27953
|
+
const why = plansNotDeployableReason(resolved);
|
|
27954
|
+
if (why)
|
|
27955
|
+
return {
|
|
27956
|
+
...base,
|
|
27957
|
+
action: "not-deployable-yet",
|
|
27958
|
+
reason: `configured, but ${why}`,
|
|
27959
|
+
details: []
|
|
27960
|
+
};
|
|
27961
|
+
const projectId = resolved.project.id ?? "";
|
|
27962
|
+
let inputs;
|
|
27963
|
+
try {
|
|
27964
|
+
inputs = await plansInputs(resolved, projectId);
|
|
27965
|
+
} catch (error) {
|
|
27966
|
+
if (!(error instanceof PlansFileError)) throw error;
|
|
27967
|
+
return {
|
|
27968
|
+
...base,
|
|
27969
|
+
action: "blocked",
|
|
27970
|
+
reason: "cannot deploy until this is fixed",
|
|
27971
|
+
details: [],
|
|
27972
|
+
issues: [
|
|
27973
|
+
{
|
|
27974
|
+
code: error.code,
|
|
27975
|
+
path: "plans.file",
|
|
27976
|
+
message: error.message,
|
|
27977
|
+
hint: error.developerHint
|
|
27978
|
+
}
|
|
27979
|
+
]
|
|
27980
|
+
};
|
|
27981
|
+
}
|
|
27982
|
+
const hashes = {
|
|
27983
|
+
catalog: projectId ? inputs.catalogHash : void 0,
|
|
27984
|
+
backend: projectId ? inputs.backendHash : void 0,
|
|
27985
|
+
"identity-link": projectId ? inputs.linkHash : void 0
|
|
27986
|
+
};
|
|
27987
|
+
const steps = PLANS_STEPS.filter(
|
|
27988
|
+
(name) => name !== "identity-link" || inputs.ownIdentity
|
|
27989
|
+
);
|
|
27990
|
+
const details = [
|
|
27991
|
+
...inputs.wanted.length === 0 ? ["plans: none in the file"] : inputs.wanted.map((plan2) => `plan ${plan2.sku}: ${plan2.displayName}`),
|
|
27992
|
+
inputs.ownIdentity ? "sign-in: the project's identity will be applied to the plans backend" : "sign-in: no identity of the platform deployed, so nothing is applied to the plans backend",
|
|
27993
|
+
...steps.map(
|
|
27994
|
+
(name) => `step ${name}: ${stepState(stepOf(ledger, "plans", name), hashes[name])}`
|
|
27995
|
+
)
|
|
27996
|
+
];
|
|
27997
|
+
const allDone = steps.every(
|
|
27998
|
+
(name) => isDone(stepOf(ledger, "plans", name), hashes[name])
|
|
27999
|
+
);
|
|
28000
|
+
return {
|
|
28001
|
+
...base,
|
|
28002
|
+
action: allDone ? "skip" : "deploy",
|
|
28003
|
+
reason: allDone ? "deploy skipped: nothing it depends on has changed" : "will deploy: the plans file, the backend or the identity is not applied yet",
|
|
28004
|
+
details
|
|
28005
|
+
};
|
|
28006
|
+
}
|
|
28007
|
+
var failedStep = (ledger, service, names) => names.map((name) => ({ name, record: stepOf(ledger, service, name) })).find((step2) => step2.record?.status === "failed");
|
|
28008
|
+
var unreachable = (error) => `platform: not reachable (${error instanceof Error ? error.message : String(error)}); showing local records only`;
|
|
28009
|
+
async function identityStatus(resolved, ledger, manifest, remote) {
|
|
28010
|
+
const entry = manifest?.services.identity;
|
|
28011
|
+
const failed = failedStep(ledger, "identity", IDENTITY_STEPS);
|
|
28012
|
+
const details = [];
|
|
28013
|
+
if (entry?.provider) details.push(`provider: ${entry.provider}`);
|
|
28014
|
+
if (entry?.issuer) details.push(`issuer: ${entry.issuer}`);
|
|
28015
|
+
if (entry?.clientId) details.push(`client id: ${entry.clientId}`);
|
|
28016
|
+
let state = entry ? "ready" : "not-deployed";
|
|
28017
|
+
const projectId = resolved.project.id ?? manifest?.project.id;
|
|
28018
|
+
if (resolved.identity?.provider === "oidc") {
|
|
28019
|
+
details.push("platform: nothing to ask (your own sign-in provider)");
|
|
28020
|
+
} else if (remote && projectId) {
|
|
28021
|
+
try {
|
|
28022
|
+
const platform = await remote(projectId);
|
|
28023
|
+
if (!platform) {
|
|
28024
|
+
details.push("platform: no identity for this project yet");
|
|
28025
|
+
if (entry) state = "drift";
|
|
28026
|
+
} else {
|
|
28027
|
+
details.push(
|
|
28028
|
+
`platform state: ${platform.status}`,
|
|
28029
|
+
`sign-up with email and password: ${platform.nativeSignup ? "on" : "off"}`,
|
|
28030
|
+
`sign-in redirect: ${platform.portalRedirectUris.join(", ") || "none"}`
|
|
28031
|
+
);
|
|
28032
|
+
if (platform.status === "Failed")
|
|
28033
|
+
details.push(
|
|
28034
|
+
`platform problem: ${[platform.errorCode, platform.errorMessage].filter(Boolean).join(" ") || "no message"}`
|
|
28035
|
+
);
|
|
28036
|
+
const same = entry?.issuer === platform.issuer.replace(/\/+$/, "");
|
|
28037
|
+
if (entry && (!same || platform.status !== "Active")) {
|
|
28038
|
+
details.push(
|
|
28039
|
+
"drift: services.json does not match the platform's identity"
|
|
28040
|
+
);
|
|
28041
|
+
state = "drift";
|
|
28042
|
+
}
|
|
28043
|
+
}
|
|
28044
|
+
} catch (error) {
|
|
28045
|
+
details.push(unreachable(error));
|
|
28046
|
+
}
|
|
28047
|
+
} else if (!projectId) {
|
|
28048
|
+
details.push("platform: not asked, this folder is not linked to a project");
|
|
28049
|
+
} else {
|
|
28050
|
+
details.push("platform: not asked, you are not logged in");
|
|
28051
|
+
}
|
|
28052
|
+
if (failed) {
|
|
28053
|
+
details.push(`error: ${failed.record?.error ?? "unknown"}`);
|
|
28054
|
+
state = "deploy-failed";
|
|
28055
|
+
}
|
|
28056
|
+
return { service: "identity", state, details };
|
|
28057
|
+
}
|
|
28058
|
+
async function plansStatus(resolved, ledger, manifest, remote, wantedSkus2) {
|
|
28059
|
+
const entry = manifest?.services.plans;
|
|
28060
|
+
const failed = failedStep(ledger, "plans", PLANS_STEPS);
|
|
28061
|
+
const details = [];
|
|
28062
|
+
if (entry?.apiBaseUrl) details.push(`plans backend: ${entry.apiBaseUrl}`);
|
|
28063
|
+
if (entry?.catalogRevision !== void 0)
|
|
28064
|
+
details.push(`catalog revision: ${entry.catalogRevision}`);
|
|
28065
|
+
let state = entry ? "ready" : "not-deployed";
|
|
28066
|
+
const projectId = resolved.project.id ?? manifest?.project.id;
|
|
28067
|
+
if (remote && projectId) {
|
|
28068
|
+
try {
|
|
28069
|
+
const platform = await remote(projectId);
|
|
28070
|
+
const active2 = platform.plans.filter((plan2) => plan2.isActive).map((plan2) => plan2.sku.toLowerCase());
|
|
28071
|
+
details.push(
|
|
28072
|
+
`plans on the platform: ${active2.length > 0 ? active2.join(", ") : "none"}`
|
|
28073
|
+
);
|
|
28074
|
+
const missing = (wantedSkus2 ?? []).filter((sku) => !active2.includes(sku));
|
|
28075
|
+
if (missing.length > 0) {
|
|
28076
|
+
details.push(
|
|
28077
|
+
`drift: plans in the file but not on the platform: ${missing.join(", ")}`
|
|
28078
|
+
);
|
|
28079
|
+
if (entry) state = "drift";
|
|
28080
|
+
}
|
|
28081
|
+
if (!platform.backendAddress) {
|
|
28082
|
+
details.push("platform: the plans backend has no address");
|
|
28083
|
+
if (entry) state = "drift";
|
|
28084
|
+
} else if (entry?.apiBaseUrl && entry.apiBaseUrl !== platform.backendAddress.replace(/\/+$/, "")) {
|
|
28085
|
+
details.push(
|
|
28086
|
+
`drift: the plans backend is at ${platform.backendAddress}, services.json says ${entry.apiBaseUrl}`
|
|
28087
|
+
);
|
|
28088
|
+
state = "drift";
|
|
28089
|
+
}
|
|
28090
|
+
} catch (error) {
|
|
28091
|
+
details.push(
|
|
28092
|
+
error instanceof PlatformApiError && error.status === 404 ? "platform: no plans for this project yet" : unreachable(error)
|
|
28093
|
+
);
|
|
28094
|
+
}
|
|
28095
|
+
} else if (!projectId) {
|
|
28096
|
+
details.push("platform: not asked, this folder is not linked to a project");
|
|
28097
|
+
} else {
|
|
28098
|
+
details.push("platform: not asked, you are not logged in");
|
|
28099
|
+
}
|
|
28100
|
+
if (failed) {
|
|
28101
|
+
details.push(`error: ${failed.record?.error ?? "unknown"}`);
|
|
28102
|
+
state = "deploy-failed";
|
|
28103
|
+
}
|
|
28104
|
+
return { service: "plans", state, details };
|
|
28105
|
+
}
|
|
28106
|
+
|
|
28107
|
+
// src/cli/portal/report.ts
|
|
28108
|
+
var DEPLOY_ORDER = [
|
|
28109
|
+
"identity",
|
|
28110
|
+
"gateway",
|
|
28111
|
+
"plans",
|
|
28112
|
+
"portal"
|
|
28113
|
+
];
|
|
28114
|
+
var exists4 = (file) => access13(file).then(
|
|
28115
|
+
() => true,
|
|
28116
|
+
() => false
|
|
28117
|
+
);
|
|
28118
|
+
function sectionOf(resolved, service) {
|
|
28119
|
+
return resolved[service];
|
|
28120
|
+
}
|
|
28121
|
+
function chooseServices(only) {
|
|
28122
|
+
return DEPLOY_ORDER.filter((name) => !only || only.includes(name));
|
|
28123
|
+
}
|
|
28124
|
+
async function planPortalService(resolved, ledger) {
|
|
28125
|
+
const base = { service: "portal", details: [] };
|
|
28126
|
+
try {
|
|
28127
|
+
const manifest = await readManifest(resolved.state);
|
|
28128
|
+
const prepared = await preparePortal(resolved, manifest, "production", {
|
|
28129
|
+
write: false
|
|
28130
|
+
});
|
|
28131
|
+
const { profile, plan: plan2 } = prepared;
|
|
28132
|
+
const details = [
|
|
28133
|
+
`docs: ${!profile.docs.enabled ? "off" : profile.docs.provider === "external" ? `external (${profile.docs.url})` : "built by apitogo"}`,
|
|
28134
|
+
`console: ${profile.console.enabled ? "on" : "off"}`,
|
|
28135
|
+
`manifest: ${prepared.manifestSource === "file" ? "services.json" : "not needed"}`,
|
|
28136
|
+
...[
|
|
28137
|
+
plan2.auth ? "sign-in" : void 0,
|
|
28138
|
+
plan2.signup ? "sign-up" : void 0,
|
|
28139
|
+
plan2.userPanel ? "account pages" : void 0,
|
|
28140
|
+
plan2.plansPlugin ? "pricing, keys and usage pages" : void 0,
|
|
28141
|
+
plan2.apiReference ? "API reference" : void 0
|
|
28142
|
+
].filter((feature) => feature !== void 0).map((feature) => `will appear: ${feature}`),
|
|
28143
|
+
...plan2.skipped.map((s) => `left out: ${s.piece} (${s.reason})`)
|
|
28144
|
+
];
|
|
28145
|
+
const hash2 = await portalBuildHash(resolved, prepared);
|
|
28146
|
+
const previous = stepOf(ledger, "portal", "build");
|
|
28147
|
+
const built = await exists4(
|
|
28148
|
+
path83.join(resolved.state.buildDir, "index.html")
|
|
28149
|
+
);
|
|
28150
|
+
if (previous?.status === "succeeded" && previous.hash === hash2 && built) {
|
|
28151
|
+
return {
|
|
28152
|
+
...base,
|
|
28153
|
+
action: "skip",
|
|
28154
|
+
reason: "build skipped: nothing it depends on has changed",
|
|
28155
|
+
details,
|
|
28156
|
+
issues: []
|
|
28157
|
+
};
|
|
28158
|
+
}
|
|
27125
28159
|
const reason = !previous ? "no earlier build" : previous.status === "failed" ? `the last build failed (${previous.error ?? "no message"})` : previous.status === "running" ? "the last build did not finish" : !built ? "the build folder is gone" : "its inputs changed since the last build";
|
|
27126
28160
|
return {
|
|
27127
28161
|
...base,
|
|
@@ -27310,21 +28344,20 @@ async function planV2(dir, only, options = {}) {
|
|
|
27310
28344
|
services.push(
|
|
27311
28345
|
await planGatewayService(resolved, ledger, options.gatewayDryRun)
|
|
27312
28346
|
);
|
|
28347
|
+
} else if (service === "identity") {
|
|
28348
|
+
services.push(
|
|
28349
|
+
await planIdentityService(
|
|
28350
|
+
resolved,
|
|
28351
|
+
ledger,
|
|
28352
|
+
await readManifest(resolved.state)
|
|
28353
|
+
)
|
|
28354
|
+
);
|
|
27313
28355
|
} else {
|
|
27314
|
-
services.push(
|
|
27315
|
-
service,
|
|
27316
|
-
action: "not-deployable-yet",
|
|
27317
|
-
reason: "configured; deploying it is not available in this CLI version yet",
|
|
27318
|
-
details: [],
|
|
27319
|
-
issues: []
|
|
27320
|
-
});
|
|
28356
|
+
services.push(await planPlansService(resolved, ledger));
|
|
27321
28357
|
}
|
|
27322
28358
|
}
|
|
27323
28359
|
return { configPath: resolved.configPath, services };
|
|
27324
28360
|
}
|
|
27325
|
-
function entryStatus(manifest, service) {
|
|
27326
|
-
return manifest?.services[service];
|
|
27327
|
-
}
|
|
27328
28361
|
function originHealthLines(origin) {
|
|
27329
28362
|
if (!origin.lastProbeAtUtc) return ["origin check: not checked yet"];
|
|
27330
28363
|
return [
|
|
@@ -27398,6 +28431,16 @@ async function gatewayStatus(resolved, ledger, manifest, remote) {
|
|
|
27398
28431
|
if (failed) state = "deploy-failed";
|
|
27399
28432
|
return { service: "gateway", state, details: [...details, ...local] };
|
|
27400
28433
|
}
|
|
28434
|
+
async function wantedSkus(resolved) {
|
|
28435
|
+
if (!resolved.plans) return void 0;
|
|
28436
|
+
try {
|
|
28437
|
+
return (await readPlansFile(resolved.plans.file)).map(
|
|
28438
|
+
(plan2) => plan2.sku.trim().toLowerCase()
|
|
28439
|
+
);
|
|
28440
|
+
} catch {
|
|
28441
|
+
return void 0;
|
|
28442
|
+
}
|
|
28443
|
+
}
|
|
27401
28444
|
async function statusV2(dir, only, options = {}) {
|
|
27402
28445
|
const { resolved } = loadConfigV2(dir);
|
|
27403
28446
|
const ledger = await readLedger(resolved.state);
|
|
@@ -27413,7 +28456,6 @@ async function statusV2(dir, only, options = {}) {
|
|
|
27413
28456
|
services.push({ service, state: "disabled", details: [] });
|
|
27414
28457
|
continue;
|
|
27415
28458
|
}
|
|
27416
|
-
const entry = entryStatus(manifest, service);
|
|
27417
28459
|
const url2 = service === "portal" ? manifest?.services.portal?.url : void 0;
|
|
27418
28460
|
if (service === "portal") {
|
|
27419
28461
|
const build2 = stepOf(ledger, "portal", "build");
|
|
@@ -27442,14 +28484,26 @@ async function statusV2(dir, only, options = {}) {
|
|
|
27442
28484
|
);
|
|
27443
28485
|
continue;
|
|
27444
28486
|
}
|
|
27445
|
-
|
|
27446
|
-
|
|
27447
|
-
|
|
27448
|
-
|
|
27449
|
-
|
|
27450
|
-
|
|
27451
|
-
|
|
27452
|
-
|
|
28487
|
+
if (service === "identity") {
|
|
28488
|
+
services.push(
|
|
28489
|
+
await identityStatus(
|
|
28490
|
+
resolved,
|
|
28491
|
+
ledger,
|
|
28492
|
+
manifest,
|
|
28493
|
+
options.identityRemote
|
|
28494
|
+
)
|
|
28495
|
+
);
|
|
28496
|
+
continue;
|
|
28497
|
+
}
|
|
28498
|
+
services.push(
|
|
28499
|
+
await plansStatus(
|
|
28500
|
+
resolved,
|
|
28501
|
+
ledger,
|
|
28502
|
+
manifest,
|
|
28503
|
+
options.plansRemote,
|
|
28504
|
+
await wantedSkus(resolved)
|
|
28505
|
+
)
|
|
28506
|
+
);
|
|
27453
28507
|
}
|
|
27454
28508
|
return {
|
|
27455
28509
|
configPath: resolved.configPath,
|
|
@@ -27466,7 +28520,7 @@ function formatPlan(output4) {
|
|
|
27466
28520
|
`${svc.service}: ${svc.reason}`,
|
|
27467
28521
|
...svc.details.map((line2) => ` ${line2}`),
|
|
27468
28522
|
...svc.issues.map(
|
|
27469
|
-
(
|
|
28523
|
+
(issue4) => ` problem: ${issue4.message}${issue4.hint ? ` ${issue4.hint}` : ""} [${issue4.code}]`
|
|
27470
28524
|
)
|
|
27471
28525
|
]);
|
|
27472
28526
|
}
|
|
@@ -27480,6 +28534,36 @@ function formatStatus(output4) {
|
|
|
27480
28534
|
];
|
|
27481
28535
|
}
|
|
27482
28536
|
|
|
28537
|
+
// src/cli/deploy/static-web-config.ts
|
|
28538
|
+
import { access as access14, writeFile as writeFile25 } from "node:fs/promises";
|
|
28539
|
+
import path84 from "node:path";
|
|
28540
|
+
var STATIC_WEB_CONFIG_FILE = "staticwebapp.config.json";
|
|
28541
|
+
var STATIC_WEB_CONFIG_VERSION = 1;
|
|
28542
|
+
var DEFAULT_STATIC_WEB_CONFIG = {
|
|
28543
|
+
navigationFallback: {
|
|
28544
|
+
rewrite: "/index.html",
|
|
28545
|
+
exclude: [
|
|
28546
|
+
"/assets/*",
|
|
28547
|
+
"/*.{png,jpg,jpeg,gif,svg,ico,css,js,mjs,json,txt,xml,webmanifest,woff,woff2,map}"
|
|
28548
|
+
]
|
|
28549
|
+
}
|
|
28550
|
+
};
|
|
28551
|
+
async function ensureStaticWebConfig(buildDir) {
|
|
28552
|
+
const file = path84.join(buildDir, STATIC_WEB_CONFIG_FILE);
|
|
28553
|
+
try {
|
|
28554
|
+
await access14(file);
|
|
28555
|
+
return false;
|
|
28556
|
+
} catch {
|
|
28557
|
+
await writeFile25(
|
|
28558
|
+
file,
|
|
28559
|
+
`${JSON.stringify(DEFAULT_STATIC_WEB_CONFIG, null, 2)}
|
|
28560
|
+
`,
|
|
28561
|
+
"utf8"
|
|
28562
|
+
);
|
|
28563
|
+
return true;
|
|
28564
|
+
}
|
|
28565
|
+
}
|
|
28566
|
+
|
|
27483
28567
|
// src/cli/deploy/v2.ts
|
|
27484
28568
|
var DeployError = class extends ApitogoError {
|
|
27485
28569
|
constructor(code, message, developerHint) {
|
|
@@ -27504,8 +28588,9 @@ function parseDeployOnly(value) {
|
|
|
27504
28588
|
function publishStep(deps, options) {
|
|
27505
28589
|
return {
|
|
27506
28590
|
name: "publish",
|
|
27507
|
-
hash: () => `${options.buildHash}|${options.projectId}|${options.env ?? ""}`,
|
|
28591
|
+
hash: () => `${options.buildHash}|${options.projectId}|${options.env ?? ""}|host-config-${STATIC_WEB_CONFIG_VERSION}`,
|
|
27508
28592
|
run: async () => {
|
|
28593
|
+
await ensureStaticWebConfig(options.buildDir);
|
|
27509
28594
|
const pack = deps.pack ?? packageDirectoryAsTarGz;
|
|
27510
28595
|
const archive = await pack(options.buildDir, { archiveRoot: "dist" });
|
|
27511
28596
|
try {
|
|
@@ -27563,19 +28648,14 @@ async function deployV2(options, deps) {
|
|
|
27563
28648
|
results.push(await deployGatewayService(options, deps, projectId, named));
|
|
27564
28649
|
continue;
|
|
27565
28650
|
}
|
|
27566
|
-
if (service
|
|
27567
|
-
|
|
27568
|
-
|
|
27569
|
-
|
|
27570
|
-
|
|
27571
|
-
|
|
27572
|
-
|
|
27573
|
-
results.push(
|
|
27574
|
-
service,
|
|
27575
|
-
status: "skipped",
|
|
27576
|
-
reason: "deploying it is not available in this CLI version yet",
|
|
27577
|
-
steps: []
|
|
27578
|
-
});
|
|
28651
|
+
if (service === "identity") {
|
|
28652
|
+
results.push(
|
|
28653
|
+
await deployIdentityService(options, deps, projectId, named)
|
|
28654
|
+
);
|
|
28655
|
+
continue;
|
|
28656
|
+
}
|
|
28657
|
+
if (service === "plans") {
|
|
28658
|
+
results.push(await deployPlansService(options, deps, projectId, named));
|
|
27579
28659
|
continue;
|
|
27580
28660
|
}
|
|
27581
28661
|
results.push(await deployPortal(options, deps, projectId));
|
|
@@ -27619,6 +28699,65 @@ async function deployGatewayService(options, deps, projectId, named) {
|
|
|
27619
28699
|
operationCount: result.operationCount
|
|
27620
28700
|
};
|
|
27621
28701
|
}
|
|
28702
|
+
async function deployIdentityService(options, deps, projectId, named) {
|
|
28703
|
+
const { resolved } = loadConfigV2(options.dir);
|
|
28704
|
+
const why = identityNotDeployableReason(resolved);
|
|
28705
|
+
if (why) {
|
|
28706
|
+
if (named)
|
|
28707
|
+
throw new DeployError(
|
|
28708
|
+
"SERVICE_NOT_DEPLOYABLE",
|
|
28709
|
+
`Identity cannot be deployed: ${why}.`,
|
|
28710
|
+
"Fix the identity section of the config, or deploy another service."
|
|
28711
|
+
);
|
|
28712
|
+
return { service: "identity", status: "skipped", reason: why, steps: [] };
|
|
28713
|
+
}
|
|
28714
|
+
const client = deps.client;
|
|
28715
|
+
if (resolved.identity?.provider !== "oidc" && (!client.putProjectIdentity || !client.getProjectIdentity || !client.getProject))
|
|
28716
|
+
throw new Error("The platform client has no identity calls.");
|
|
28717
|
+
const result = await deployIdentity(
|
|
28718
|
+
resolved,
|
|
28719
|
+
projectId,
|
|
28720
|
+
{
|
|
28721
|
+
...deps.identity,
|
|
28722
|
+
client
|
|
28723
|
+
},
|
|
28724
|
+
{ force: options.force }
|
|
28725
|
+
);
|
|
28726
|
+
return {
|
|
28727
|
+
service: "identity",
|
|
28728
|
+
status: result.status,
|
|
28729
|
+
steps: result.steps,
|
|
28730
|
+
url: result.issuer
|
|
28731
|
+
};
|
|
28732
|
+
}
|
|
28733
|
+
async function deployPlansService(options, deps, projectId, named) {
|
|
28734
|
+
const { resolved } = loadConfigV2(options.dir);
|
|
28735
|
+
const why = plansNotDeployableReason(resolved);
|
|
28736
|
+
if (why) {
|
|
28737
|
+
if (named)
|
|
28738
|
+
throw new DeployError(
|
|
28739
|
+
"SERVICE_NOT_DEPLOYABLE",
|
|
28740
|
+
`Plans cannot be deployed: ${why}.`,
|
|
28741
|
+
"Add them with `apitogo add plans`, or deploy another service."
|
|
28742
|
+
);
|
|
28743
|
+
return { service: "plans", status: "skipped", reason: why, steps: [] };
|
|
28744
|
+
}
|
|
28745
|
+
const client = deps.client;
|
|
28746
|
+
if (!client.listGatewayPlans || !client.createGatewayPlan || !client.updateGatewayPlan || !client.validateGatewayPlans || !client.listGatewayActions || !client.getDevPortalBackend || !client.activateModule || !client.linkDevPortalIdentity || !client.listProvisioningJobs)
|
|
28747
|
+
throw new Error("The platform client has no plans calls.");
|
|
28748
|
+
const result = await deployPlans(
|
|
28749
|
+
resolved,
|
|
28750
|
+
projectId,
|
|
28751
|
+
{ ...deps.plans, client },
|
|
28752
|
+
{ force: options.force }
|
|
28753
|
+
);
|
|
28754
|
+
return {
|
|
28755
|
+
service: "plans",
|
|
28756
|
+
status: result.status,
|
|
28757
|
+
steps: result.steps,
|
|
28758
|
+
url: result.apiBaseUrl
|
|
28759
|
+
};
|
|
28760
|
+
}
|
|
27622
28761
|
async function deployPortal(options, deps, projectId) {
|
|
27623
28762
|
const { resolved, root, prepared } = await preparePortalWorkspace(
|
|
27624
28763
|
options.dir,
|
|
@@ -27697,7 +28836,7 @@ async function deployProject(argv) {
|
|
|
27697
28836
|
const config2 = loadConfig();
|
|
27698
28837
|
const output4 = await deployV2(
|
|
27699
28838
|
{
|
|
27700
|
-
dir:
|
|
28839
|
+
dir: path85.resolve(process.cwd(), argv.dir),
|
|
27701
28840
|
only: Array.isArray(argv.only) ? argv.only.join(",") : argv.only,
|
|
27702
28841
|
projectId: argv.projectId,
|
|
27703
28842
|
env: argv.env,
|
|
@@ -27778,7 +28917,7 @@ var deploy_default = {
|
|
|
27778
28917
|
}),
|
|
27779
28918
|
handler: async (argv) => {
|
|
27780
28919
|
await captureEvent({ argv, event: "apitogo deploy" });
|
|
27781
|
-
if (await isConfigV2(
|
|
28920
|
+
if (await isConfigV2(path86.resolve(process.cwd(), argv.dir))) {
|
|
27782
28921
|
await deployProject(argv);
|
|
27783
28922
|
return;
|
|
27784
28923
|
}
|
|
@@ -27787,8 +28926,8 @@ var deploy_default = {
|
|
|
27787
28926
|
};
|
|
27788
28927
|
|
|
27789
28928
|
// src/cli/export/handler.ts
|
|
27790
|
-
import { mkdir as mkdir20, writeFile as
|
|
27791
|
-
import
|
|
28929
|
+
import { mkdir as mkdir20, writeFile as writeFile26 } from "node:fs/promises";
|
|
28930
|
+
import path87 from "node:path";
|
|
27792
28931
|
init_invariant();
|
|
27793
28932
|
var ExportError = class extends ApitogoError {
|
|
27794
28933
|
constructor(message, developerHint) {
|
|
@@ -27802,7 +28941,7 @@ async function exportOpenApi(args, deps) {
|
|
|
27802
28941
|
`Cannot export "${args.target}".`,
|
|
27803
28942
|
"Only `apitogo export openapi` exists."
|
|
27804
28943
|
);
|
|
27805
|
-
const dir =
|
|
28944
|
+
const dir = path87.resolve(process.cwd(), args.dir ?? ".");
|
|
27806
28945
|
if (!await isConfigV2(dir))
|
|
27807
28946
|
throw new ExportError(
|
|
27808
28947
|
"This command needs a config version 2 project.",
|
|
@@ -27827,9 +28966,9 @@ async function exportOpenApi(args, deps) {
|
|
|
27827
28966
|
throw error;
|
|
27828
28967
|
}
|
|
27829
28968
|
if (!args.out) return { text };
|
|
27830
|
-
const file =
|
|
27831
|
-
await mkdir20(
|
|
27832
|
-
await
|
|
28969
|
+
const file = path87.resolve(process.cwd(), args.out);
|
|
28970
|
+
await mkdir20(path87.dirname(file), { recursive: true });
|
|
28971
|
+
await writeFile26(file, text);
|
|
27833
28972
|
return { text, file };
|
|
27834
28973
|
}
|
|
27835
28974
|
async function exportCommand(args) {
|
|
@@ -27879,15 +29018,15 @@ var export_default = {
|
|
|
27879
29018
|
};
|
|
27880
29019
|
|
|
27881
29020
|
// src/cli/init/handler.ts
|
|
27882
|
-
import
|
|
29021
|
+
import path91 from "node:path";
|
|
27883
29022
|
|
|
27884
29023
|
// src/cli/scaffold/run-scaffold-discovery.ts
|
|
27885
|
-
import { access as
|
|
27886
|
-
import
|
|
29024
|
+
import { access as access16, readFile as readFile31, writeFile as writeFile28 } from "node:fs/promises";
|
|
29025
|
+
import path90 from "node:path";
|
|
27887
29026
|
|
|
27888
29027
|
// src/cli/scaffold/detect-backend.ts
|
|
27889
|
-
import { readdir as readdir4, readFile as
|
|
27890
|
-
import
|
|
29028
|
+
import { readdir as readdir4, readFile as readFile29 } from "node:fs/promises";
|
|
29029
|
+
import path88 from "node:path";
|
|
27891
29030
|
var DEFAULT_RUNTIME_VERSION = {
|
|
27892
29031
|
dotnet: "DOTNET_10",
|
|
27893
29032
|
nodejs: "NODE_22_LTS",
|
|
@@ -27904,7 +29043,7 @@ async function listFiles(directory) {
|
|
|
27904
29043
|
}
|
|
27905
29044
|
async function readText(filePath) {
|
|
27906
29045
|
try {
|
|
27907
|
-
return await
|
|
29046
|
+
return await readFile29(filePath, "utf8");
|
|
27908
29047
|
} catch {
|
|
27909
29048
|
return void 0;
|
|
27910
29049
|
}
|
|
@@ -27933,15 +29072,15 @@ function isRunnableNodePackage(pkg) {
|
|
|
27933
29072
|
return typeof scripts.start === "string" || typeof pkg.main === "string";
|
|
27934
29073
|
}
|
|
27935
29074
|
async function detectParentBackend(siteDirectory) {
|
|
27936
|
-
const site =
|
|
27937
|
-
const directory =
|
|
29075
|
+
const site = path88.resolve(siteDirectory);
|
|
29076
|
+
const directory = path88.dirname(site);
|
|
27938
29077
|
if (directory === site) {
|
|
27939
29078
|
return void 0;
|
|
27940
29079
|
}
|
|
27941
29080
|
const files = await listFiles(directory);
|
|
27942
29081
|
const toResult = (runtime, runtimeVersion, marker) => ({
|
|
27943
29082
|
directory,
|
|
27944
|
-
sourceDirectory:
|
|
29083
|
+
sourceDirectory: path88.relative(site, directory).split(path88.sep).join("/") || ".",
|
|
27945
29084
|
runtime,
|
|
27946
29085
|
runtimeVersion,
|
|
27947
29086
|
marker
|
|
@@ -27949,7 +29088,7 @@ async function detectParentBackend(siteDirectory) {
|
|
|
27949
29088
|
const csproj = files.find((f) => f.endsWith(".csproj"));
|
|
27950
29089
|
const solution = files.find((f) => /\.slnx?$/.test(f));
|
|
27951
29090
|
if (csproj || solution) {
|
|
27952
|
-
const content = csproj ? await readText(
|
|
29091
|
+
const content = csproj ? await readText(path88.join(directory, csproj)) : void 0;
|
|
27953
29092
|
return toResult(
|
|
27954
29093
|
"dotnet",
|
|
27955
29094
|
dotnetVersionFromCsproj(content),
|
|
@@ -27957,7 +29096,7 @@ async function detectParentBackend(siteDirectory) {
|
|
|
27957
29096
|
);
|
|
27958
29097
|
}
|
|
27959
29098
|
if (files.includes("package.json")) {
|
|
27960
|
-
const raw = await readText(
|
|
29099
|
+
const raw = await readText(path88.join(directory, "package.json"));
|
|
27961
29100
|
try {
|
|
27962
29101
|
const pkg = JSON.parse(raw ?? "");
|
|
27963
29102
|
if (isRunnableNodePackage(pkg)) {
|
|
@@ -27972,7 +29111,7 @@ async function detectParentBackend(siteDirectory) {
|
|
|
27972
29111
|
}
|
|
27973
29112
|
const pythonMarker = PYTHON_MARKERS.find((m) => files.includes(m));
|
|
27974
29113
|
if (pythonMarker) {
|
|
27975
|
-
const versionSource = await readText(
|
|
29114
|
+
const versionSource = await readText(path88.join(directory, ".python-version")) ?? (pythonMarker === "pyproject.toml" ? (await readText(path88.join(directory, pythonMarker)))?.match(
|
|
27976
29115
|
/requires-python\s*=\s*"([^"]+)"/
|
|
27977
29116
|
)?.[1] : void 0);
|
|
27978
29117
|
return toResult(
|
|
@@ -27986,16 +29125,16 @@ async function detectParentBackend(siteDirectory) {
|
|
|
27986
29125
|
|
|
27987
29126
|
// src/cli/scaffold/wire-docs.ts
|
|
27988
29127
|
import {
|
|
27989
|
-
access as
|
|
29128
|
+
access as access15,
|
|
27990
29129
|
lstat as lstat2,
|
|
27991
29130
|
mkdir as mkdir21,
|
|
27992
29131
|
readdir as readdir5,
|
|
27993
|
-
readFile as
|
|
29132
|
+
readFile as readFile30,
|
|
27994
29133
|
readlink,
|
|
27995
29134
|
symlink as symlink2,
|
|
27996
|
-
writeFile as
|
|
29135
|
+
writeFile as writeFile27
|
|
27997
29136
|
} from "node:fs/promises";
|
|
27998
|
-
import
|
|
29137
|
+
import path89 from "node:path";
|
|
27999
29138
|
var DOCS_DIR_WHITELIST = [
|
|
28000
29139
|
"docs",
|
|
28001
29140
|
"documents",
|
|
@@ -28017,7 +29156,7 @@ var PAGES_DIR = "pages";
|
|
|
28017
29156
|
var CONFIG_FILENAME = "apitogo.config.json";
|
|
28018
29157
|
async function pathExists5(filePath) {
|
|
28019
29158
|
try {
|
|
28020
|
-
await
|
|
29159
|
+
await access15(filePath);
|
|
28021
29160
|
return true;
|
|
28022
29161
|
} catch {
|
|
28023
29162
|
return false;
|
|
@@ -28032,14 +29171,14 @@ async function isDirectory2(filePath) {
|
|
|
28032
29171
|
}
|
|
28033
29172
|
}
|
|
28034
29173
|
function toPosix3(value) {
|
|
28035
|
-
return value.split(
|
|
29174
|
+
return value.split(path89.sep).join(path89.posix.sep);
|
|
28036
29175
|
}
|
|
28037
29176
|
function isWhitelistedDirName(name) {
|
|
28038
29177
|
return DOCS_DIR_WHITELIST.includes(name.toLowerCase());
|
|
28039
29178
|
}
|
|
28040
29179
|
function isInside2(parent, child) {
|
|
28041
|
-
const relative =
|
|
28042
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
29180
|
+
const relative = path89.relative(parent, child);
|
|
29181
|
+
return relative === "" || !relative.startsWith("..") && !path89.isAbsolute(relative);
|
|
28043
29182
|
}
|
|
28044
29183
|
async function collectMarkdownFiles(dir, excludeDir) {
|
|
28045
29184
|
const files = [];
|
|
@@ -28053,9 +29192,9 @@ async function collectMarkdownFiles(dir, excludeDir) {
|
|
|
28053
29192
|
if (entry.name.startsWith(".")) {
|
|
28054
29193
|
continue;
|
|
28055
29194
|
}
|
|
28056
|
-
const full =
|
|
29195
|
+
const full = path89.join(dir, entry.name);
|
|
28057
29196
|
if (entry.isDirectory()) {
|
|
28058
|
-
if (SKIP_SEARCH_DIRS.has(entry.name) || entry.isSymbolicLink() || excludeDir &&
|
|
29197
|
+
if (SKIP_SEARCH_DIRS.has(entry.name) || entry.isSymbolicLink() || excludeDir && path89.resolve(full) === excludeDir) {
|
|
28059
29198
|
continue;
|
|
28060
29199
|
}
|
|
28061
29200
|
files.push(...await collectMarkdownFiles(full, excludeDir));
|
|
@@ -28068,11 +29207,11 @@ async function collectMarkdownFiles(dir, excludeDir) {
|
|
|
28068
29207
|
return files;
|
|
28069
29208
|
}
|
|
28070
29209
|
async function collectSearchRoots(siteDirectory) {
|
|
28071
|
-
const site =
|
|
28072
|
-
const parent =
|
|
29210
|
+
const site = path89.resolve(siteDirectory);
|
|
29211
|
+
const parent = path89.dirname(site);
|
|
28073
29212
|
const roots = [];
|
|
28074
29213
|
const add = (dir) => {
|
|
28075
|
-
const resolved =
|
|
29214
|
+
const resolved = path89.resolve(dir);
|
|
28076
29215
|
if (!roots.includes(resolved)) {
|
|
28077
29216
|
roots.push(resolved);
|
|
28078
29217
|
}
|
|
@@ -28085,7 +29224,7 @@ async function collectSearchRoots(siteDirectory) {
|
|
|
28085
29224
|
if (!entry.isDirectory() || SKIP_SEARCH_DIRS.has(entry.name)) {
|
|
28086
29225
|
continue;
|
|
28087
29226
|
}
|
|
28088
|
-
add(
|
|
29227
|
+
add(path89.join(parent, entry.name));
|
|
28089
29228
|
}
|
|
28090
29229
|
} catch {
|
|
28091
29230
|
}
|
|
@@ -28094,13 +29233,13 @@ async function collectSearchRoots(siteDirectory) {
|
|
|
28094
29233
|
return roots;
|
|
28095
29234
|
}
|
|
28096
29235
|
async function discoverNearbyDocsDirectories(siteDirectory) {
|
|
28097
|
-
const site =
|
|
28098
|
-
const sitePages =
|
|
29236
|
+
const site = path89.resolve(siteDirectory);
|
|
29237
|
+
const sitePages = path89.join(site, PAGES_DIR);
|
|
28099
29238
|
const searchedDirectories = await collectSearchRoots(site);
|
|
28100
29239
|
const directories = [];
|
|
28101
29240
|
const seen = /* @__PURE__ */ new Set();
|
|
28102
29241
|
const consider = async (dir) => {
|
|
28103
|
-
const resolved =
|
|
29242
|
+
const resolved = path89.resolve(dir);
|
|
28104
29243
|
if (resolved === site || seen.has(resolved) || isInside2(sitePages, resolved)) {
|
|
28105
29244
|
return;
|
|
28106
29245
|
}
|
|
@@ -28117,7 +29256,7 @@ async function discoverNearbyDocsDirectories(siteDirectory) {
|
|
|
28117
29256
|
directories.push({ sourceDirectory: resolved, files });
|
|
28118
29257
|
};
|
|
28119
29258
|
for (const root of searchedDirectories) {
|
|
28120
|
-
if (isWhitelistedDirName(
|
|
29259
|
+
if (isWhitelistedDirName(path89.basename(root))) {
|
|
28121
29260
|
await consider(root);
|
|
28122
29261
|
}
|
|
28123
29262
|
try {
|
|
@@ -28129,7 +29268,7 @@ async function discoverNearbyDocsDirectories(siteDirectory) {
|
|
|
28129
29268
|
if (!isWhitelistedDirName(entry.name)) {
|
|
28130
29269
|
continue;
|
|
28131
29270
|
}
|
|
28132
|
-
await consider(
|
|
29271
|
+
await consider(path89.join(root, entry.name));
|
|
28133
29272
|
}
|
|
28134
29273
|
} catch {
|
|
28135
29274
|
}
|
|
@@ -28148,8 +29287,8 @@ function slugifyDirName(name) {
|
|
|
28148
29287
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "docs";
|
|
28149
29288
|
}
|
|
28150
29289
|
async function allocateLinkName(pagesDir, sourceDirectory, used) {
|
|
28151
|
-
const base = slugifyDirName(
|
|
28152
|
-
const parent = slugifyDirName(
|
|
29290
|
+
const base = slugifyDirName(path89.basename(sourceDirectory));
|
|
29291
|
+
const parent = slugifyDirName(path89.basename(path89.dirname(sourceDirectory)));
|
|
28153
29292
|
const candidates = [base, `${parent}-${base}`];
|
|
28154
29293
|
let index = 2;
|
|
28155
29294
|
while (true) {
|
|
@@ -28157,7 +29296,7 @@ async function allocateLinkName(pagesDir, sourceDirectory, used) {
|
|
|
28157
29296
|
if (used.has(name)) {
|
|
28158
29297
|
continue;
|
|
28159
29298
|
}
|
|
28160
|
-
const target =
|
|
29299
|
+
const target = path89.join(pagesDir, name);
|
|
28161
29300
|
if (await pathExists5(target)) {
|
|
28162
29301
|
const stat7 = await lstat2(target);
|
|
28163
29302
|
if (!stat7.isDirectory() || !await isManagedLinkDir(target)) {
|
|
@@ -28188,8 +29327,8 @@ async function alreadyLinked(linkPath, sourcePath) {
|
|
|
28188
29327
|
return false;
|
|
28189
29328
|
}
|
|
28190
29329
|
const current = await readlink(linkPath);
|
|
28191
|
-
const resolved =
|
|
28192
|
-
return resolved ===
|
|
29330
|
+
const resolved = path89.resolve(path89.dirname(linkPath), current);
|
|
29331
|
+
return resolved === path89.resolve(sourcePath);
|
|
28193
29332
|
} catch {
|
|
28194
29333
|
return false;
|
|
28195
29334
|
}
|
|
@@ -28201,8 +29340,8 @@ async function linkMarkdownFile(sourcePath, linkPath) {
|
|
|
28201
29340
|
if (await pathExists5(linkPath)) {
|
|
28202
29341
|
return "skipped";
|
|
28203
29342
|
}
|
|
28204
|
-
await mkdir21(
|
|
28205
|
-
const relativeTarget =
|
|
29343
|
+
await mkdir21(path89.dirname(linkPath), { recursive: true });
|
|
29344
|
+
const relativeTarget = path89.relative(path89.dirname(linkPath), sourcePath);
|
|
28206
29345
|
await symlink2(
|
|
28207
29346
|
relativeTarget,
|
|
28208
29347
|
linkPath,
|
|
@@ -28217,11 +29356,11 @@ async function applyDocsNavigation(siteDirectory, routes) {
|
|
|
28217
29356
|
if (routes.length === 0) {
|
|
28218
29357
|
return;
|
|
28219
29358
|
}
|
|
28220
|
-
const configPath =
|
|
29359
|
+
const configPath = path89.join(siteDirectory, CONFIG_FILENAME);
|
|
28221
29360
|
if (!await pathExists5(configPath)) {
|
|
28222
29361
|
return;
|
|
28223
29362
|
}
|
|
28224
|
-
const raw = await
|
|
29363
|
+
const raw = await readFile30(configPath, "utf8");
|
|
28225
29364
|
const parsed = JSON.parse(raw);
|
|
28226
29365
|
if (!isPlainObject5(parsed)) {
|
|
28227
29366
|
return;
|
|
@@ -28230,12 +29369,12 @@ async function applyDocsNavigation(siteDirectory, routes) {
|
|
|
28230
29369
|
force: false
|
|
28231
29370
|
});
|
|
28232
29371
|
const updated = ensureHeaderDocsNavigation(parsed, routes);
|
|
28233
|
-
await
|
|
29372
|
+
await writeFile27(configPath, `${JSON.stringify(updated, null, 2)}
|
|
28234
29373
|
`, "utf8");
|
|
28235
29374
|
}
|
|
28236
29375
|
async function discoverAndWireDocs(siteDirectory) {
|
|
28237
|
-
const site =
|
|
28238
|
-
const pagesDir =
|
|
29376
|
+
const site = path89.resolve(siteDirectory);
|
|
29377
|
+
const pagesDir = path89.join(site, PAGES_DIR);
|
|
28239
29378
|
const { directories, searchedDirectories } = await discoverNearbyDocsDirectories(site);
|
|
28240
29379
|
if (directories.length === 0) {
|
|
28241
29380
|
return {
|
|
@@ -28257,15 +29396,15 @@ async function discoverAndWireDocs(siteDirectory) {
|
|
|
28257
29396
|
directory.sourceDirectory,
|
|
28258
29397
|
usedNames
|
|
28259
29398
|
);
|
|
28260
|
-
const linkDirectory =
|
|
29399
|
+
const linkDirectory = path89.join(pagesDir, linkName);
|
|
28261
29400
|
const linked = [];
|
|
28262
29401
|
const skipped = [];
|
|
28263
29402
|
for (const sourcePath of directory.files) {
|
|
28264
29403
|
const relativeSource = toPosix3(
|
|
28265
|
-
|
|
29404
|
+
path89.relative(directory.sourceDirectory, sourcePath)
|
|
28266
29405
|
);
|
|
28267
|
-
const linkPath =
|
|
28268
|
-
const relativeLink = toPosix3(
|
|
29406
|
+
const linkPath = path89.join(linkDirectory, relativeSource);
|
|
29407
|
+
const relativeLink = toPosix3(path89.relative(site, linkPath));
|
|
28269
29408
|
const route = toRoutePath(relativeLink, PAGES_DIR);
|
|
28270
29409
|
if (routes.includes(route)) {
|
|
28271
29410
|
skipped.push({
|
|
@@ -28313,11 +29452,11 @@ async function discoverAndWireDocs(siteDirectory) {
|
|
|
28313
29452
|
};
|
|
28314
29453
|
}
|
|
28315
29454
|
function siteRelative(siteDirectory, filePath) {
|
|
28316
|
-
const relative = toPosix3(
|
|
29455
|
+
const relative = toPosix3(path89.relative(siteDirectory, filePath));
|
|
28317
29456
|
if (!relative) {
|
|
28318
29457
|
return ".";
|
|
28319
29458
|
}
|
|
28320
|
-
if (
|
|
29459
|
+
if (path89.isAbsolute(relative)) {
|
|
28321
29460
|
return toPosix3(filePath);
|
|
28322
29461
|
}
|
|
28323
29462
|
if (relative.startsWith("..")) {
|
|
@@ -28397,7 +29536,7 @@ function formatInitDiscoveryReport(siteDirectory, openApiDiscovery, docsDiscover
|
|
|
28397
29536
|
// src/cli/scaffold/run-scaffold-discovery.ts
|
|
28398
29537
|
async function pathExists6(filePath) {
|
|
28399
29538
|
try {
|
|
28400
|
-
await
|
|
29539
|
+
await access16(filePath);
|
|
28401
29540
|
return true;
|
|
28402
29541
|
} catch {
|
|
28403
29542
|
return false;
|
|
@@ -28405,8 +29544,8 @@ async function pathExists6(filePath) {
|
|
|
28405
29544
|
}
|
|
28406
29545
|
async function wireOpenApiIntoJsonConfig(siteDirectory, openApiPath) {
|
|
28407
29546
|
const input5 = toSiteRelativeOpenApiInput(siteDirectory, openApiPath);
|
|
28408
|
-
const jsonConfigPath =
|
|
28409
|
-
const raw = await
|
|
29547
|
+
const jsonConfigPath = path90.join(siteDirectory, "apitogo.config.json");
|
|
29548
|
+
const raw = await readFile31(jsonConfigPath, "utf8");
|
|
28410
29549
|
const siteConfig = JSON.parse(raw);
|
|
28411
29550
|
const updated = applyOpenApiToSiteConfig(siteConfig, {
|
|
28412
29551
|
input: input5,
|
|
@@ -28414,7 +29553,7 @@ async function wireOpenApiIntoJsonConfig(siteDirectory, openApiPath) {
|
|
|
28414
29553
|
});
|
|
28415
29554
|
const backend = updated.backend && typeof updated.backend === "object" && !Array.isArray(updated.backend) ? { ...updated.backend } : {};
|
|
28416
29555
|
updated.backend = { ...backend, openApiPath: input5 };
|
|
28417
|
-
await
|
|
29556
|
+
await writeFile28(
|
|
28418
29557
|
jsonConfigPath,
|
|
28419
29558
|
`${JSON.stringify(updated, null, 2)}
|
|
28420
29559
|
`,
|
|
@@ -28433,7 +29572,7 @@ async function discoverAndWireOpenApiLite(siteDirectory, mode) {
|
|
|
28433
29572
|
const invalidCandidates = [];
|
|
28434
29573
|
let selected;
|
|
28435
29574
|
for (const candidate of search.candidates) {
|
|
28436
|
-
const content = await
|
|
29575
|
+
const content = await readFile31(candidate, "utf8");
|
|
28437
29576
|
const parsed = liteParseOpenApi(content, candidate);
|
|
28438
29577
|
if (parsed.valid) {
|
|
28439
29578
|
selected = {
|
|
@@ -28457,7 +29596,7 @@ async function discoverAndWireOpenApiLite(siteDirectory, mode) {
|
|
|
28457
29596
|
wired: false
|
|
28458
29597
|
};
|
|
28459
29598
|
}
|
|
28460
|
-
const jsonConfigPath =
|
|
29599
|
+
const jsonConfigPath = path90.join(siteDirectory, "apitogo.config.json");
|
|
28461
29600
|
if (!await pathExists6(jsonConfigPath)) {
|
|
28462
29601
|
return {
|
|
28463
29602
|
openApiDiscovery: {
|
|
@@ -28507,13 +29646,13 @@ async function discoverAndWireBackend(siteDirectory, mode) {
|
|
|
28507
29646
|
if (mode !== "nearby") {
|
|
28508
29647
|
return { status: "skipped", mode };
|
|
28509
29648
|
}
|
|
28510
|
-
const jsonConfigPath =
|
|
29649
|
+
const jsonConfigPath = path90.join(siteDirectory, "apitogo.config.json");
|
|
28511
29650
|
if (!await pathExists6(jsonConfigPath)) {
|
|
28512
29651
|
return { status: "not_found" };
|
|
28513
29652
|
}
|
|
28514
29653
|
try {
|
|
28515
29654
|
const siteConfig = JSON.parse(
|
|
28516
|
-
await
|
|
29655
|
+
await readFile31(jsonConfigPath, "utf8")
|
|
28517
29656
|
);
|
|
28518
29657
|
const existing = siteConfig.backend && typeof siteConfig.backend === "object" && !Array.isArray(siteConfig.backend) ? siteConfig.backend : {};
|
|
28519
29658
|
if (typeof existing.sourceDirectory === "string" && existing.sourceDirectory.trim()) {
|
|
@@ -28534,7 +29673,7 @@ async function discoverAndWireBackend(siteDirectory, mode) {
|
|
|
28534
29673
|
runtime: detected.runtime,
|
|
28535
29674
|
runtimeVersion: detected.runtimeVersion
|
|
28536
29675
|
};
|
|
28537
|
-
await
|
|
29676
|
+
await writeFile28(
|
|
28538
29677
|
jsonConfigPath,
|
|
28539
29678
|
`${JSON.stringify(siteConfig, null, 2)}
|
|
28540
29679
|
`,
|
|
@@ -28574,8 +29713,8 @@ function slugifyName2(name) {
|
|
|
28574
29713
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "dev-portal-site";
|
|
28575
29714
|
}
|
|
28576
29715
|
function resolveInitOutputDirectory(dirArg, name) {
|
|
28577
|
-
const resolved =
|
|
28578
|
-
const base =
|
|
29716
|
+
const resolved = path91.resolve(process.cwd(), dirArg);
|
|
29717
|
+
const base = path91.basename(resolved);
|
|
28579
29718
|
const isDot = dirArg === "." || dirArg === "./" || base === "." || resolved === process.cwd();
|
|
28580
29719
|
if (isDot) {
|
|
28581
29720
|
const fromName = name?.trim();
|
|
@@ -28585,7 +29724,7 @@ function resolveInitOutputDirectory(dirArg, name) {
|
|
|
28585
29724
|
);
|
|
28586
29725
|
}
|
|
28587
29726
|
return {
|
|
28588
|
-
outputDirectory:
|
|
29727
|
+
outputDirectory: path91.resolve(process.cwd(), slugifyName2(fromName)),
|
|
28589
29728
|
siteName: fromName
|
|
28590
29729
|
};
|
|
28591
29730
|
}
|
|
@@ -28768,8 +29907,8 @@ import open2 from "open";
|
|
|
28768
29907
|
import colors14 from "picocolors";
|
|
28769
29908
|
|
|
28770
29909
|
// src/cli/platform/device-auth.ts
|
|
28771
|
-
function url(apiUrl,
|
|
28772
|
-
return `${apiUrl}${
|
|
29910
|
+
function url(apiUrl, path104) {
|
|
29911
|
+
return `${apiUrl}${path104.startsWith("/") ? path104 : `/${path104}`}`;
|
|
28773
29912
|
}
|
|
28774
29913
|
async function requestDeviceCode(apiUrl) {
|
|
28775
29914
|
const response = await fetchWithRetry(
|
|
@@ -28951,7 +30090,7 @@ var logout_default = {
|
|
|
28951
30090
|
};
|
|
28952
30091
|
|
|
28953
30092
|
// src/cli/portal/commands.ts
|
|
28954
|
-
import
|
|
30093
|
+
import path92 from "node:path";
|
|
28955
30094
|
init_invariant();
|
|
28956
30095
|
function parseOnly(value) {
|
|
28957
30096
|
if (value === void 0) return void 0;
|
|
@@ -28968,7 +30107,7 @@ function parseOnly(value) {
|
|
|
28968
30107
|
async function run2(command2, argv, compute, format) {
|
|
28969
30108
|
const json = argv.json === true;
|
|
28970
30109
|
try {
|
|
28971
|
-
const dir =
|
|
30110
|
+
const dir = path92.resolve(process.cwd(), argv.dir ?? ".");
|
|
28972
30111
|
if (!await isConfigV2(dir)) {
|
|
28973
30112
|
throw new ServiceEditError(
|
|
28974
30113
|
`${command2} works with a config version 2 project, and there is none in ${dir}.`,
|
|
@@ -29019,10 +30158,35 @@ function gatewayDryRun() {
|
|
|
29019
30158
|
return void 0;
|
|
29020
30159
|
}
|
|
29021
30160
|
}
|
|
30161
|
+
function identityRemote() {
|
|
30162
|
+
try {
|
|
30163
|
+
const client = new PlatformApiClient(loadConfig());
|
|
30164
|
+
return (projectId) => client.getProjectIdentity(projectId);
|
|
30165
|
+
} catch {
|
|
30166
|
+
return void 0;
|
|
30167
|
+
}
|
|
30168
|
+
}
|
|
30169
|
+
function plansRemote() {
|
|
30170
|
+
try {
|
|
30171
|
+
const client = new PlatformApiClient(loadConfig());
|
|
30172
|
+
return async (projectId) => ({
|
|
30173
|
+
plans: await client.listGatewayPlans(projectId),
|
|
30174
|
+
backendAddress: toApiBaseUrl(
|
|
30175
|
+
(await client.getDevPortalBackend(projectId))?.fqdn
|
|
30176
|
+
)
|
|
30177
|
+
});
|
|
30178
|
+
} catch {
|
|
30179
|
+
return void 0;
|
|
30180
|
+
}
|
|
30181
|
+
}
|
|
29022
30182
|
var statusOfProject = (argv) => run2(
|
|
29023
30183
|
"status",
|
|
29024
30184
|
argv,
|
|
29025
|
-
(dir, only) => statusV2(dir, only, {
|
|
30185
|
+
(dir, only) => statusV2(dir, only, {
|
|
30186
|
+
gatewayRemote: gatewayRemote(),
|
|
30187
|
+
identityRemote: identityRemote(),
|
|
30188
|
+
plansRemote: plansRemote()
|
|
30189
|
+
}),
|
|
29026
30190
|
formatStatus
|
|
29027
30191
|
);
|
|
29028
30192
|
|
|
@@ -29052,7 +30216,7 @@ var plan_default = {
|
|
|
29052
30216
|
|
|
29053
30217
|
// src/cli/project/create-handler.ts
|
|
29054
30218
|
init_invariant();
|
|
29055
|
-
import
|
|
30219
|
+
import path93 from "node:path";
|
|
29056
30220
|
|
|
29057
30221
|
// src/cli/project/v2.ts
|
|
29058
30222
|
init_invariant();
|
|
@@ -29069,13 +30233,7 @@ function modulesForConfig(config2) {
|
|
|
29069
30233
|
var hostsAnything = (config2) => config2.portal && config2.portal.enabled !== false || config2.gateway && config2.gateway.enabled !== false;
|
|
29070
30234
|
async function createProjectV2(options, deps) {
|
|
29071
30235
|
const { config: config2, resolved } = loadConfigV2(options.dir);
|
|
29072
|
-
|
|
29073
|
-
throw new ProjectCreateError(
|
|
29074
|
-
"ALREADY_LINKED",
|
|
29075
|
-
`This folder is already linked to project ${resolved.project.id}.`,
|
|
29076
|
-
"Run `apitogo deploy`. To start over, run `apitogo teardown` first."
|
|
29077
|
-
);
|
|
29078
|
-
}
|
|
30236
|
+
const linkedProjectId = resolved.project.id;
|
|
29079
30237
|
const modules = modulesForConfig(config2);
|
|
29080
30238
|
if (!hostsAnything(config2)) {
|
|
29081
30239
|
throw new ProjectCreateError(
|
|
@@ -29089,7 +30247,9 @@ async function createProjectV2(options, deps) {
|
|
|
29089
30247
|
name,
|
|
29090
30248
|
description: options.description,
|
|
29091
30249
|
modules,
|
|
29092
|
-
wait: options.wait !== false
|
|
30250
|
+
wait: options.wait !== false,
|
|
30251
|
+
projectId: linkedProjectId,
|
|
30252
|
+
onProjectResolved: (projectId) => setProjectId(options.dir, projectId)
|
|
29093
30253
|
});
|
|
29094
30254
|
const outcome = parse4(raw);
|
|
29095
30255
|
if (outcome.requiresPlatformPlanSelection) {
|
|
@@ -29162,7 +30322,9 @@ async function createLinkedProject(argv, dir) {
|
|
|
29162
30322
|
modules: args.modules,
|
|
29163
30323
|
selectFreePlan: true,
|
|
29164
30324
|
wait: args.wait,
|
|
29165
|
-
configureGatewayBaseline: false
|
|
30325
|
+
configureGatewayBaseline: false,
|
|
30326
|
+
projectId: args.projectId,
|
|
30327
|
+
onProjectResolved: args.onProjectResolved
|
|
29166
30328
|
},
|
|
29167
30329
|
json ? void 0 : progress
|
|
29168
30330
|
)
|
|
@@ -29199,7 +30361,7 @@ async function createLinkedProject(argv, dir) {
|
|
|
29199
30361
|
}
|
|
29200
30362
|
}
|
|
29201
30363
|
async function createProject(argv) {
|
|
29202
|
-
const dir =
|
|
30364
|
+
const dir = path93.resolve(process.cwd(), argv.dir ?? ".");
|
|
29203
30365
|
if (await isConfigV2(dir)) {
|
|
29204
30366
|
await createLinkedProject(argv, dir);
|
|
29205
30367
|
return;
|
|
@@ -29323,14 +30485,14 @@ var remove_default = {
|
|
|
29323
30485
|
|
|
29324
30486
|
// src/cli/dev/handler.ts
|
|
29325
30487
|
init_joinUrl();
|
|
29326
|
-
import
|
|
30488
|
+
import path96 from "node:path";
|
|
29327
30489
|
|
|
29328
30490
|
// src/vite/dev-server.ts
|
|
29329
30491
|
init_logger();
|
|
29330
30492
|
import fs12 from "node:fs/promises";
|
|
29331
30493
|
import http from "node:http";
|
|
29332
30494
|
import https from "node:https";
|
|
29333
|
-
import
|
|
30495
|
+
import path95 from "node:path";
|
|
29334
30496
|
import { createHttpTerminator } from "http-terminator";
|
|
29335
30497
|
import {
|
|
29336
30498
|
createServer as createViteServer,
|
|
@@ -29363,7 +30525,7 @@ init_loader();
|
|
|
29363
30525
|
// src/vite/pagefind-dev-index.ts
|
|
29364
30526
|
init_invariant();
|
|
29365
30527
|
init_joinUrl();
|
|
29366
|
-
import
|
|
30528
|
+
import path94 from "node:path";
|
|
29367
30529
|
import { createIndex as createIndex2 } from "pagefind";
|
|
29368
30530
|
import { isRunnableDevEnvironment } from "vite";
|
|
29369
30531
|
async function* buildPagefindDevIndex(vite, config2) {
|
|
@@ -29416,7 +30578,7 @@ async function* buildPagefindDevIndex(vite, config2) {
|
|
|
29416
30578
|
path: urlPath
|
|
29417
30579
|
};
|
|
29418
30580
|
}
|
|
29419
|
-
const outputPath =
|
|
30581
|
+
const outputPath = path94.join(vite.config.publicDir, "pagefind");
|
|
29420
30582
|
await pagefindIndex.writeFiles({ outputPath });
|
|
29421
30583
|
yield { type: "complete", success: true, indexed };
|
|
29422
30584
|
}
|
|
@@ -29435,9 +30597,9 @@ var DevServer = class {
|
|
|
29435
30597
|
this.protocol = "https";
|
|
29436
30598
|
const { dir } = this.options;
|
|
29437
30599
|
const [key, cert, ca] = await Promise.all([
|
|
29438
|
-
fs12.readFile(
|
|
29439
|
-
fs12.readFile(
|
|
29440
|
-
config2.https.ca ? fs12.readFile(
|
|
30600
|
+
fs12.readFile(path95.resolve(dir, config2.https.key)),
|
|
30601
|
+
fs12.readFile(path95.resolve(dir, config2.https.cert)),
|
|
30602
|
+
config2.https.ca ? fs12.readFile(path95.resolve(dir, config2.https.ca)) : void 0
|
|
29441
30603
|
]);
|
|
29442
30604
|
return https.createServer({ key, cert, ca });
|
|
29443
30605
|
}
|
|
@@ -29454,7 +30616,7 @@ var DevServer = class {
|
|
|
29454
30616
|
);
|
|
29455
30617
|
const server = await this.createNodeServer(config2);
|
|
29456
30618
|
if (config2.search?.type === "pagefind" && viteConfig.publicDir !== false) {
|
|
29457
|
-
const publicRoot =
|
|
30619
|
+
const publicRoot = path95.resolve(
|
|
29458
30620
|
this.options.dir,
|
|
29459
30621
|
typeof viteConfig.publicDir === "string" ? viteConfig.publicDir : "public"
|
|
29460
30622
|
);
|
|
@@ -29471,7 +30633,7 @@ var DevServer = class {
|
|
|
29471
30633
|
// built-in transform middleware which would treat the path as a static asset.
|
|
29472
30634
|
name: "apitogo:entry-client",
|
|
29473
30635
|
configureServer(server2) {
|
|
29474
|
-
const entryPath =
|
|
30636
|
+
const entryPath = path95.posix.join(
|
|
29475
30637
|
server2.config.base,
|
|
29476
30638
|
"/__z/entry.client.tsx"
|
|
29477
30639
|
);
|
|
@@ -29636,7 +30798,7 @@ init_package_json();
|
|
|
29636
30798
|
async function dev(argv) {
|
|
29637
30799
|
const packageJson3 = getZudokuPackageJson();
|
|
29638
30800
|
process.env.NODE_ENV = "development";
|
|
29639
|
-
let dir =
|
|
30801
|
+
let dir = path96.resolve(process.cwd(), argv.dir);
|
|
29640
30802
|
if (await isConfigV2(dir)) {
|
|
29641
30803
|
const { root, prepared } = await preparePortalWorkspace(dir, "development");
|
|
29642
30804
|
dir = root;
|
|
@@ -29735,7 +30897,7 @@ var run_default = {
|
|
|
29735
30897
|
};
|
|
29736
30898
|
|
|
29737
30899
|
// src/cli/secrets/handler.ts
|
|
29738
|
-
import
|
|
30900
|
+
import path97 from "node:path";
|
|
29739
30901
|
init_invariant();
|
|
29740
30902
|
|
|
29741
30903
|
// src/cli/secrets/input.ts
|
|
@@ -29800,7 +30962,7 @@ var SecretsError = class extends ApitogoError {
|
|
|
29800
30962
|
async function resolveProjectId2(argv, deps) {
|
|
29801
30963
|
const explicit = argv.projectId?.trim();
|
|
29802
30964
|
if (explicit) return explicit;
|
|
29803
|
-
const dir =
|
|
30965
|
+
const dir = path97.resolve(process.cwd(), argv.dir ?? ".");
|
|
29804
30966
|
if (await isConfigV2(dir)) {
|
|
29805
30967
|
const id = loadConfigV2(dir).resolved.project.id;
|
|
29806
30968
|
if (id) return id;
|
|
@@ -29960,12 +31122,12 @@ var secrets_default = {
|
|
|
29960
31122
|
};
|
|
29961
31123
|
|
|
29962
31124
|
// src/cli/cmds/status.ts
|
|
29963
|
-
import
|
|
31125
|
+
import path99 from "node:path";
|
|
29964
31126
|
|
|
29965
31127
|
// src/cli/status/handler.ts
|
|
29966
|
-
import
|
|
31128
|
+
import path98 from "node:path";
|
|
29967
31129
|
async function status(argv) {
|
|
29968
|
-
const sourceDirectory =
|
|
31130
|
+
const sourceDirectory = path98.resolve(process.cwd(), argv.dir);
|
|
29969
31131
|
let config2;
|
|
29970
31132
|
try {
|
|
29971
31133
|
config2 = loadConfig();
|
|
@@ -30063,7 +31225,7 @@ var status_default = {
|
|
|
30063
31225
|
}),
|
|
30064
31226
|
handler: async (argv) => {
|
|
30065
31227
|
await captureEvent({ argv, event: "apitogo status" });
|
|
30066
|
-
if (await isConfigV2(
|
|
31228
|
+
if (await isConfigV2(path99.resolve(process.cwd(), argv.dir))) {
|
|
30067
31229
|
await statusOfProject(argv);
|
|
30068
31230
|
return;
|
|
30069
31231
|
}
|
|
@@ -30072,11 +31234,11 @@ var status_default = {
|
|
|
30072
31234
|
};
|
|
30073
31235
|
|
|
30074
31236
|
// src/cli/cmds/teardown.ts
|
|
30075
|
-
import
|
|
31237
|
+
import path102 from "node:path";
|
|
30076
31238
|
|
|
30077
31239
|
// src/cli/teardown/command.ts
|
|
30078
31240
|
init_invariant();
|
|
30079
|
-
import
|
|
31241
|
+
import path100 from "node:path";
|
|
30080
31242
|
import { createInterface as createInterface7 } from "node:readline/promises";
|
|
30081
31243
|
|
|
30082
31244
|
// src/cli/teardown/v2.ts
|
|
@@ -30090,12 +31252,14 @@ var TeardownError = class extends ApitogoError {
|
|
|
30090
31252
|
};
|
|
30091
31253
|
async function teardownV2(options, deps) {
|
|
30092
31254
|
if (options.only) {
|
|
30093
|
-
|
|
30094
|
-
|
|
31255
|
+
const only = options.only.trim();
|
|
31256
|
+
if (only === "gateway") return teardownGateway(options, deps);
|
|
31257
|
+
if (only === "identity") return teardownIdentity(options, deps);
|
|
31258
|
+
if (only === "plans") return teardownPlans(options, deps);
|
|
30095
31259
|
throw new TeardownError(
|
|
30096
31260
|
"PARTIAL_TEARDOWN_UNSUPPORTED",
|
|
30097
31261
|
"The platform cannot release the hosting of one service on its own: it is released by deleting the project.",
|
|
30098
|
-
"Run `apitogo teardown` without --only to delete the whole project, or `apitogo teardown --only gateway` to remove just
|
|
31262
|
+
"Run `apitogo teardown` without --only to delete the whole project, or `apitogo teardown --only gateway`, `--only identity` or `--only plans` to remove just that service. Your local files are kept."
|
|
30099
31263
|
);
|
|
30100
31264
|
}
|
|
30101
31265
|
const { resolved } = loadConfigV2(options.dir);
|
|
@@ -30275,6 +31439,148 @@ Type the project name to confirm: `
|
|
|
30275
31439
|
message: "The gateway was removed. The project and your local files are kept."
|
|
30276
31440
|
};
|
|
30277
31441
|
}
|
|
31442
|
+
function projectIdOf(options, deps, resolved, what) {
|
|
31443
|
+
const projectId = options.projectId?.trim() || resolved.project.id || deps.envProjectId?.trim() || "";
|
|
31444
|
+
if (!projectId) {
|
|
31445
|
+
throw new TeardownError(
|
|
31446
|
+
"PROJECT_REQUIRED",
|
|
31447
|
+
`This folder is not linked to a project, so there is no ${what} to remove.`,
|
|
31448
|
+
"Pass --project-id <id> to name the project."
|
|
31449
|
+
);
|
|
31450
|
+
}
|
|
31451
|
+
return projectId;
|
|
31452
|
+
}
|
|
31453
|
+
async function confirmByName(options, deps, name, prompt, what) {
|
|
31454
|
+
if (options.yes === true) return;
|
|
31455
|
+
if (!deps.isTerminal) {
|
|
31456
|
+
throw new TeardownError(
|
|
31457
|
+
"CONFIRMATION_REQUIRED",
|
|
31458
|
+
"This is not an interactive terminal.",
|
|
31459
|
+
`Pass --yes to remove the ${what} without asking.`
|
|
31460
|
+
);
|
|
31461
|
+
}
|
|
31462
|
+
const typed = await deps.askName(prompt);
|
|
31463
|
+
if (typed.trim() !== name) {
|
|
31464
|
+
throw new TeardownError(
|
|
31465
|
+
"CANCELLED",
|
|
31466
|
+
"The name did not match. Nothing was removed."
|
|
31467
|
+
);
|
|
31468
|
+
}
|
|
31469
|
+
}
|
|
31470
|
+
async function clearService(resolved, service) {
|
|
31471
|
+
const cleared = [];
|
|
31472
|
+
const ledger = await readLedger(resolved.state);
|
|
31473
|
+
if (ledger.services[service]) {
|
|
31474
|
+
await clearServices(resolved.state, [service]);
|
|
31475
|
+
cleared.push(`state.json entries of ${service}`);
|
|
31476
|
+
}
|
|
31477
|
+
if ((await readManifest(resolved.state))?.services[service]) {
|
|
31478
|
+
await removeServicesFromManifest(resolved.state, [service]);
|
|
31479
|
+
cleared.push(`the ${service} entry of services.json`);
|
|
31480
|
+
}
|
|
31481
|
+
return cleared;
|
|
31482
|
+
}
|
|
31483
|
+
async function teardownIdentity(options, deps) {
|
|
31484
|
+
const { resolved } = loadConfigV2(options.dir);
|
|
31485
|
+
const projectId = projectIdOf(options, deps, resolved, "identity");
|
|
31486
|
+
const client = deps.client;
|
|
31487
|
+
if (!client.getProjectIdentity || !client.deleteProjectIdentity)
|
|
31488
|
+
throw new Error("The platform client has no identity calls.");
|
|
31489
|
+
const project = await client.getProject(projectId);
|
|
31490
|
+
const name = project.name;
|
|
31491
|
+
const current = await client.getProjectIdentity(projectId);
|
|
31492
|
+
if (!current || current.status === "Released")
|
|
31493
|
+
throw new TeardownError(
|
|
31494
|
+
"IDENTITY_NOT_DEPLOYED",
|
|
31495
|
+
"The platform has no identity for this project.",
|
|
31496
|
+
"There is nothing to remove. Clear stale local records by deleting the identity entries in the state folder."
|
|
31497
|
+
);
|
|
31498
|
+
await confirmByName(
|
|
31499
|
+
options,
|
|
31500
|
+
deps,
|
|
31501
|
+
name,
|
|
31502
|
+
`This removes the identity of project "${name}" (${projectId}) and DELETES EVERY USER in it, for good. Nobody can sign in to the portal or the API until a new identity is made. The project, the portal and the gateway stay.
|
|
31503
|
+
Type the project name to confirm: `,
|
|
31504
|
+
"identity"
|
|
31505
|
+
);
|
|
31506
|
+
const answer = await client.deleteProjectIdentity(projectId);
|
|
31507
|
+
if (answer !== null) {
|
|
31508
|
+
const sleep5 = deps.identity?.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
31509
|
+
const deadline = Date.now() + (options.wait === false ? 0 : deps.identity?.timeoutMs ?? 12e4);
|
|
31510
|
+
for (; ; ) {
|
|
31511
|
+
const state = await client.getProjectIdentity(projectId);
|
|
31512
|
+
if (state === null || state.status === "Released") break;
|
|
31513
|
+
if (state.status === "Failed")
|
|
31514
|
+
throw new TeardownError(
|
|
31515
|
+
"DELETE_FAILED",
|
|
31516
|
+
`The platform could not remove the identity: ${[state.errorCode, state.errorMessage].filter(Boolean).join(" ") || state.status}.`,
|
|
31517
|
+
"Nothing local was changed. Try again, or contact support with the project id."
|
|
31518
|
+
);
|
|
31519
|
+
if (Date.now() >= deadline) {
|
|
31520
|
+
return {
|
|
31521
|
+
projectId,
|
|
31522
|
+
projectName: name,
|
|
31523
|
+
status: "pending",
|
|
31524
|
+
cleared: [],
|
|
31525
|
+
message: "The identity is queued for removal and the platform finishes it in the background. Local records are kept until then: run `apitogo teardown --only identity` again later to clear them."
|
|
31526
|
+
};
|
|
31527
|
+
}
|
|
31528
|
+
await sleep5(deps.identity?.pollIntervalMs ?? 2e3);
|
|
31529
|
+
}
|
|
31530
|
+
}
|
|
31531
|
+
return {
|
|
31532
|
+
projectId,
|
|
31533
|
+
projectName: name,
|
|
31534
|
+
status: "removed",
|
|
31535
|
+
scope: "identity",
|
|
31536
|
+
cleared: await clearService(resolved, "identity"),
|
|
31537
|
+
message: "The identity and its users were removed. The project and your local files are kept."
|
|
31538
|
+
};
|
|
31539
|
+
}
|
|
31540
|
+
async function teardownPlans(options, deps) {
|
|
31541
|
+
const { resolved } = loadConfigV2(options.dir);
|
|
31542
|
+
const projectId = projectIdOf(options, deps, resolved, "plans");
|
|
31543
|
+
const client = deps.client;
|
|
31544
|
+
if (!client.listGatewayPlans || !client.deleteGatewayPlan)
|
|
31545
|
+
throw new Error("The platform client has no plans calls.");
|
|
31546
|
+
const project = await client.getProject(projectId);
|
|
31547
|
+
const name = project.name;
|
|
31548
|
+
const plans = await client.listGatewayPlans(projectId);
|
|
31549
|
+
if (plans.length === 0)
|
|
31550
|
+
throw new TeardownError(
|
|
31551
|
+
"PLANS_NOT_DEPLOYED",
|
|
31552
|
+
"The platform has no plans for this project.",
|
|
31553
|
+
"There is nothing to remove. Clear stale local records by deleting the plans entries in the state folder."
|
|
31554
|
+
);
|
|
31555
|
+
await confirmByName(
|
|
31556
|
+
options,
|
|
31557
|
+
deps,
|
|
31558
|
+
name,
|
|
31559
|
+
`This removes the plans (${plans.map((plan2) => plan2.sku).join(", ")}) of project "${name}" (${projectId}). Customers' subscriptions and API keys on them stop working. The plans backend stays: it is released with the project.
|
|
31560
|
+
Type the project name to confirm: `,
|
|
31561
|
+
"plans"
|
|
31562
|
+
);
|
|
31563
|
+
for (const plan2 of plans) {
|
|
31564
|
+
try {
|
|
31565
|
+
await client.deleteGatewayPlan(projectId, plan2.id);
|
|
31566
|
+
} catch (error) {
|
|
31567
|
+
if (error instanceof PlatformApiError && error.status === 404) continue;
|
|
31568
|
+
throw new TeardownError(
|
|
31569
|
+
"DELETE_FAILED",
|
|
31570
|
+
`The platform could not remove the plan "${plan2.sku}": ${error instanceof Error ? error.message : String(error)}.`,
|
|
31571
|
+
"Plans removed before it are gone. Nothing local was changed; run the command again after fixing the cause."
|
|
31572
|
+
);
|
|
31573
|
+
}
|
|
31574
|
+
}
|
|
31575
|
+
return {
|
|
31576
|
+
projectId,
|
|
31577
|
+
projectName: name,
|
|
31578
|
+
status: "removed",
|
|
31579
|
+
scope: "plans",
|
|
31580
|
+
cleared: await clearService(resolved, "plans"),
|
|
31581
|
+
message: "The plans were removed. The plans backend, the project and your local files are kept."
|
|
31582
|
+
};
|
|
31583
|
+
}
|
|
30278
31584
|
|
|
30279
31585
|
// src/cli/teardown/command.ts
|
|
30280
31586
|
async function ask3(prompt) {
|
|
@@ -30293,7 +31599,7 @@ async function teardownProject(argv) {
|
|
|
30293
31599
|
const progress = createConsoleProgressReporter();
|
|
30294
31600
|
const result = await teardownV2(
|
|
30295
31601
|
{
|
|
30296
|
-
dir:
|
|
31602
|
+
dir: path100.resolve(process.cwd(), argv.dir),
|
|
30297
31603
|
projectId: argv.projectId,
|
|
30298
31604
|
only: Array.isArray(argv.only) ? argv.only.join(",") : argv.only,
|
|
30299
31605
|
yes: argv.yes === true,
|
|
@@ -30324,7 +31630,7 @@ async function teardownProject(argv) {
|
|
|
30324
31630
|
return;
|
|
30325
31631
|
}
|
|
30326
31632
|
printDiagnosticsToConsole(
|
|
30327
|
-
result.scope === "gateway" ? `Removed the
|
|
31633
|
+
result.scope === "gateway" || result.scope === "identity" || result.scope === "plans" ? `Removed the ${result.scope} of project ${result.projectName} (${result.projectId}).` : `${result.status === "removed" ? "Deleted" : "Queued deletion of"} project ${result.projectName} (${result.projectId}).`
|
|
30328
31634
|
);
|
|
30329
31635
|
if (result.cleared.length > 0)
|
|
30330
31636
|
printDiagnosticsToConsole(
|
|
@@ -30352,7 +31658,7 @@ async function teardownProject(argv) {
|
|
|
30352
31658
|
}
|
|
30353
31659
|
|
|
30354
31660
|
// src/cli/teardown/handler.ts
|
|
30355
|
-
import
|
|
31661
|
+
import path101 from "node:path";
|
|
30356
31662
|
function resultLooksFailed3(payload) {
|
|
30357
31663
|
try {
|
|
30358
31664
|
const parsed = JSON.parse(payload);
|
|
@@ -30368,7 +31674,7 @@ async function teardown(argv) {
|
|
|
30368
31674
|
);
|
|
30369
31675
|
return;
|
|
30370
31676
|
}
|
|
30371
|
-
const sourceDirectory =
|
|
31677
|
+
const sourceDirectory = path101.resolve(process.cwd(), argv.dir);
|
|
30372
31678
|
let config2;
|
|
30373
31679
|
try {
|
|
30374
31680
|
config2 = loadConfig();
|
|
@@ -30429,7 +31735,7 @@ var teardown_default = {
|
|
|
30429
31735
|
describe: "Override project GUID"
|
|
30430
31736
|
}).option("only", {
|
|
30431
31737
|
type: "string",
|
|
30432
|
-
describe: "Config version 2:
|
|
31738
|
+
describe: "Config version 2: `gateway`, `identity` or `plans` removes just that service and keeps the project (`identity` deletes every user in it); other hosting is released only by deleting the whole project"
|
|
30433
31739
|
}).option("yes", {
|
|
30434
31740
|
alias: "y",
|
|
30435
31741
|
type: "boolean",
|
|
@@ -30446,7 +31752,7 @@ var teardown_default = {
|
|
|
30446
31752
|
}),
|
|
30447
31753
|
handler: async (argv) => {
|
|
30448
31754
|
await captureEvent({ argv, event: "apitogo teardown" });
|
|
30449
|
-
if (await isConfigV2(
|
|
31755
|
+
if (await isConfigV2(path102.resolve(process.cwd(), argv.dir))) {
|
|
30450
31756
|
await teardownProject(argv);
|
|
30451
31757
|
return;
|
|
30452
31758
|
}
|
|
@@ -30524,36 +31830,36 @@ function createApitogoCli(argv) {
|
|
|
30524
31830
|
// src/cli/local-handoff.ts
|
|
30525
31831
|
import { spawn as spawn5 } from "node:child_process";
|
|
30526
31832
|
import { existsSync as existsSync9, realpathSync as realpathSync2 } from "node:fs";
|
|
30527
|
-
import
|
|
31833
|
+
import path103 from "node:path";
|
|
30528
31834
|
var LOCAL_HANDOFF_ENV = "APITOGO_LOCAL_CLI";
|
|
30529
31835
|
var PACKAGE_NAME = "@lukoweb/apitogo";
|
|
30530
31836
|
var resolveProjectDir = (argv, cwd) => {
|
|
30531
31837
|
for (let i = 0; i < argv.length; i++) {
|
|
30532
31838
|
const arg = argv[i];
|
|
30533
31839
|
const next = argv[i + 1];
|
|
30534
|
-
if (arg === "--dir" && next) return
|
|
31840
|
+
if (arg === "--dir" && next) return path103.resolve(cwd, next);
|
|
30535
31841
|
if (arg?.startsWith("--dir=")) {
|
|
30536
|
-
return
|
|
31842
|
+
return path103.resolve(cwd, arg.slice("--dir=".length));
|
|
30537
31843
|
}
|
|
30538
31844
|
}
|
|
30539
31845
|
return cwd;
|
|
30540
31846
|
};
|
|
30541
31847
|
var findLocalCli = (projectDir, runningRoot) => {
|
|
30542
|
-
let dir =
|
|
31848
|
+
let dir = path103.resolve(projectDir);
|
|
30543
31849
|
let localPackageRoot;
|
|
30544
31850
|
while (!localPackageRoot) {
|
|
30545
|
-
const candidate =
|
|
30546
|
-
if (existsSync9(
|
|
31851
|
+
const candidate = path103.join(dir, "node_modules", PACKAGE_NAME);
|
|
31852
|
+
if (existsSync9(path103.join(candidate, "package.json"))) {
|
|
30547
31853
|
localPackageRoot = candidate;
|
|
30548
|
-
} else if (dir ===
|
|
31854
|
+
} else if (dir === path103.dirname(dir)) {
|
|
30549
31855
|
return void 0;
|
|
30550
31856
|
} else {
|
|
30551
|
-
dir =
|
|
31857
|
+
dir = path103.dirname(dir);
|
|
30552
31858
|
}
|
|
30553
31859
|
}
|
|
30554
31860
|
const localRoot = realpathSync2(localPackageRoot);
|
|
30555
31861
|
if (localRoot === realpathSync2(runningRoot)) return void 0;
|
|
30556
|
-
return
|
|
31862
|
+
return path103.join(localRoot, "cli.js");
|
|
30557
31863
|
};
|
|
30558
31864
|
var handoffToLocalCli = async (argv, runningRoot) => {
|
|
30559
31865
|
if (process.env[LOCAL_HANDOFF_ENV] || process.env.ZUDOKU_ENV === "internal") {
|
|
@@ -30565,7 +31871,7 @@ var handoffToLocalCli = async (argv, runningRoot) => {
|
|
|
30565
31871
|
);
|
|
30566
31872
|
if (!localCli) return void 0;
|
|
30567
31873
|
printDiagnosticsToConsole(
|
|
30568
|
-
`Using the project's local ${PACKAGE_NAME} (${
|
|
31874
|
+
`Using the project's local ${PACKAGE_NAME} (${path103.dirname(localCli)})`
|
|
30569
31875
|
);
|
|
30570
31876
|
const child = spawn5(process.execPath, [localCli, ...argv], {
|
|
30571
31877
|
stdio: "inherit",
|