@continuous-excellence/ze-great-dashboard-aws 0.24.2 → 0.25.0
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.js +3 -1
- package/dist/index.js +3 -1
- package/dist/lambda.mjs +123 -67
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -303,6 +303,8 @@ import { z as z3 } from "zod";
|
|
|
303
303
|
var clientEnvSchema = z3.object({
|
|
304
304
|
/** Where the immutable client assets live. The one variable that repoints the whole client. */
|
|
305
305
|
assetPath: z3.string().min(1),
|
|
306
|
+
/** Opaque SHA-256 fingerprint of the canonical asset path, for diagnostic correlation only. */
|
|
307
|
+
assetPathId: z3.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
306
308
|
/** Same-origin path the proxy is mounted at, so there is no CORS and no cookie problem. */
|
|
307
309
|
proxyPath: z3.string().min(1),
|
|
308
310
|
/** Which board this is, resolved server-side so the client parses no URLs. */
|
|
@@ -313,7 +315,7 @@ var clientEnvSchema = z3.object({
|
|
|
313
315
|
clientId: z3.string().min(1).optional()
|
|
314
316
|
}).optional()
|
|
315
317
|
});
|
|
316
|
-
var
|
|
318
|
+
var clientIdentityResponseSchema = clientEnvSchema.pick({ assetPath: true, assetPathId: true }).extend({ serverVersion: z3.string().min(1) });
|
|
317
319
|
|
|
318
320
|
// packages/shared/src/envelope.ts
|
|
319
321
|
import { z as z4 } from "zod";
|
package/dist/index.js
CHANGED
|
@@ -984,6 +984,8 @@ import { z as z3 } from "zod";
|
|
|
984
984
|
var clientEnvSchema = z3.object({
|
|
985
985
|
/** Where the immutable client assets live. The one variable that repoints the whole client. */
|
|
986
986
|
assetPath: z3.string().min(1),
|
|
987
|
+
/** Opaque SHA-256 fingerprint of the canonical asset path, for diagnostic correlation only. */
|
|
988
|
+
assetPathId: z3.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
987
989
|
/** Same-origin path the proxy is mounted at, so there is no CORS and no cookie problem. */
|
|
988
990
|
proxyPath: z3.string().min(1),
|
|
989
991
|
/** Which board this is, resolved server-side so the client parses no URLs. */
|
|
@@ -994,7 +996,7 @@ var clientEnvSchema = z3.object({
|
|
|
994
996
|
clientId: z3.string().min(1).optional()
|
|
995
997
|
}).optional()
|
|
996
998
|
});
|
|
997
|
-
var
|
|
999
|
+
var clientIdentityResponseSchema = clientEnvSchema.pick({ assetPath: true, assetPathId: true }).extend({ serverVersion: z3.string().min(1) });
|
|
998
1000
|
|
|
999
1001
|
// packages/shared/src/envelope.ts
|
|
1000
1002
|
import { z as z4 } from "zod";
|
package/dist/lambda.mjs
CHANGED
|
@@ -25202,7 +25202,7 @@ var require_dist_cjs11 = __commonJS({
|
|
|
25202
25202
|
var { setCredentialFeature: setCredentialFeature2 } = (init_client3(), __toCommonJS(client_exports2));
|
|
25203
25203
|
var { CredentialsProviderError: CredentialsProviderError2, parseKnownFiles: parseKnownFiles2, getProfileName: getProfileName2 } = (init_config2(), __toCommonJS(config_exports));
|
|
25204
25204
|
var { HttpRequest: HttpRequest2 } = (init_protocols(), __toCommonJS(protocols_exports));
|
|
25205
|
-
var { createHash:
|
|
25205
|
+
var { createHash: createHash6, createPrivateKey, createPublicKey, sign: sign2 } = __require("node:crypto");
|
|
25206
25206
|
var { promises } = __require("node:fs");
|
|
25207
25207
|
var { homedir: homedir2 } = __require("node:os");
|
|
25208
25208
|
var { dirname, join: join5 } = __require("node:path");
|
|
@@ -25371,7 +25371,7 @@ var require_dist_cjs11 = __commonJS({
|
|
|
25371
25371
|
getTokenFilePath() {
|
|
25372
25372
|
const directory = process.env.AWS_LOGIN_CACHE_DIRECTORY ?? join5(homedir2(), ".aws", "login", "cache");
|
|
25373
25373
|
const loginSessionBytes = Buffer.from(this.loginSession, "utf8");
|
|
25374
|
-
const loginSessionSha256 =
|
|
25374
|
+
const loginSessionSha256 = createHash6("sha256").update(loginSessionBytes).digest("hex");
|
|
25375
25375
|
return join5(directory, `${loginSessionSha256}.json`);
|
|
25376
25376
|
}
|
|
25377
25377
|
derToRawSignature(derSignature) {
|
|
@@ -68552,6 +68552,8 @@ function boardSchemaModeline(url2) {
|
|
|
68552
68552
|
var clientEnvSchema = external_exports.object({
|
|
68553
68553
|
/** Where the immutable client assets live. The one variable that repoints the whole client. */
|
|
68554
68554
|
assetPath: external_exports.string().min(1),
|
|
68555
|
+
/** Opaque SHA-256 fingerprint of the canonical asset path, for diagnostic correlation only. */
|
|
68556
|
+
assetPathId: external_exports.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
68555
68557
|
/** Same-origin path the proxy is mounted at, so there is no CORS and no cookie problem. */
|
|
68556
68558
|
proxyPath: external_exports.string().min(1),
|
|
68557
68559
|
/** Which board this is, resolved server-side so the client parses no URLs. */
|
|
@@ -68562,7 +68564,7 @@ var clientEnvSchema = external_exports.object({
|
|
|
68562
68564
|
clientId: external_exports.string().min(1).optional()
|
|
68563
68565
|
}).optional()
|
|
68564
68566
|
});
|
|
68565
|
-
var
|
|
68567
|
+
var clientIdentityResponseSchema = clientEnvSchema.pick({ assetPath: true, assetPathId: true }).extend({ serverVersion: external_exports.string().min(1) });
|
|
68566
68568
|
|
|
68567
68569
|
// packages/shared/src/envelope.ts
|
|
68568
68570
|
var errorKindSchema = external_exports.enum([
|
|
@@ -72032,6 +72034,68 @@ var Hono2 = class extends Hono {
|
|
|
72032
72034
|
// packages/server/src/app.ts
|
|
72033
72035
|
var import_yaml = __toESM(require_dist(), 1);
|
|
72034
72036
|
|
|
72037
|
+
// packages/server/src/config.ts
|
|
72038
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
72039
|
+
import { fileURLToPath } from "node:url";
|
|
72040
|
+
var DEFAULT_BOARD_CONFIG_URL = fileURLToPath(
|
|
72041
|
+
new URL("../../../boards/example.yaml", import.meta.url)
|
|
72042
|
+
);
|
|
72043
|
+
var configSchema = external_exports.object({
|
|
72044
|
+
/**
|
|
72045
|
+
* Where the built client lives. Points at a versioned CDN path in a deployment
|
|
72046
|
+
* (`https://assets.../dashboard/1.0.7`) or at the Vite dev server locally. Trailing slashes are
|
|
72047
|
+
* trimmed so `${assetPath}/index.html` never doubles up.
|
|
72048
|
+
*/
|
|
72049
|
+
assetPath: external_exports.string({
|
|
72050
|
+
error: "ASSET_PATH is required \u2014 the server has no client to serve without it."
|
|
72051
|
+
}).min(1, "ASSET_PATH is required \u2014 the server has no client to serve without it.").transform((value) => value.replace(/\/+$/, "")),
|
|
72052
|
+
proxyPath: external_exports.string().min(1).default("/api"),
|
|
72053
|
+
/** A URL or a local file path. Local development uses a path and needs no credential. */
|
|
72054
|
+
boardConfigUrl: external_exports.string().min(1).default(DEFAULT_BOARD_CONFIG_URL),
|
|
72055
|
+
/** Optional for single-board files; startup selects the only board automatically. */
|
|
72056
|
+
board: external_exports.string().min(1).optional(),
|
|
72057
|
+
/** Optional ARN of the one Secrets Manager JSON credential map for this Lambda. */
|
|
72058
|
+
secretReference: external_exports.string().min(1).optional(),
|
|
72059
|
+
/** Immutable server image identifier, supplied by the image build and emitted only in startup logs. */
|
|
72060
|
+
serverRelease: external_exports.string().min(1).default("development"),
|
|
72061
|
+
port: external_exports.coerce.number().int().min(1).max(65535).default(3e3),
|
|
72062
|
+
host: external_exports.string().min(1).default("localhost"),
|
|
72063
|
+
/**
|
|
72064
|
+
* How long to keep retrying an unreachable template before giving up, in milliseconds.
|
|
72065
|
+
*
|
|
72066
|
+
* Zero — the default, and what every deployment uses — means a bad ASSET_PATH fails on the first
|
|
72067
|
+
* attempt, which is the behavior a misconfiguration deserves. `npm run dev` sets a few seconds
|
|
72068
|
+
* because it starts the server and the Vite dev server at the same moment, and without this the
|
|
72069
|
+
* local loop is a race the developer loses about half the time.
|
|
72070
|
+
*/
|
|
72071
|
+
templateWaitMillis: external_exports.coerce.number().int().min(0).default(0)
|
|
72072
|
+
});
|
|
72073
|
+
function loadConfig2(env2 = process.env) {
|
|
72074
|
+
const result = configSchema.safeParse({
|
|
72075
|
+
assetPath: env2.ASSET_PATH,
|
|
72076
|
+
proxyPath: env2.PROXY_PATH,
|
|
72077
|
+
boardConfigUrl: env2.BOARD_CONFIG_URL,
|
|
72078
|
+
board: env2.BOARD,
|
|
72079
|
+
secretReference: env2.SECRET_REFERENCE,
|
|
72080
|
+
serverRelease: env2.SERVER_RELEASE,
|
|
72081
|
+
port: env2.PORT,
|
|
72082
|
+
host: env2.HOST,
|
|
72083
|
+
templateWaitMillis: env2.TEMPLATE_WAIT_MS
|
|
72084
|
+
});
|
|
72085
|
+
if (!result.success) {
|
|
72086
|
+
throw new Error(`Invalid server configuration:
|
|
72087
|
+
${external_exports.prettifyError(result.error)}`);
|
|
72088
|
+
}
|
|
72089
|
+
return result.data;
|
|
72090
|
+
}
|
|
72091
|
+
function assetPathId(assetPath) {
|
|
72092
|
+
return `sha256:${createHash5("sha256").update(assetPath).digest("hex")}`;
|
|
72093
|
+
}
|
|
72094
|
+
var LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
72095
|
+
function isLocalHost(host) {
|
|
72096
|
+
return LOCAL_HOSTS.has(host);
|
|
72097
|
+
}
|
|
72098
|
+
|
|
72035
72099
|
// packages/server/src/logger.ts
|
|
72036
72100
|
var consoleLogger = {
|
|
72037
72101
|
log(event) {
|
|
@@ -72049,6 +72113,29 @@ function destinationOrigin(value) {
|
|
|
72049
72113
|
return void 0;
|
|
72050
72114
|
}
|
|
72051
72115
|
}
|
|
72116
|
+
function serverDiagnosticContext(serverVersion, configuredAssetPathId) {
|
|
72117
|
+
return { serverVersion, configuredAssetPathId };
|
|
72118
|
+
}
|
|
72119
|
+
function clientDiagnosticClaims(headers, context) {
|
|
72120
|
+
const version2 = headers.get("x-dashboard-client-version");
|
|
72121
|
+
const origin = headers.get("x-dashboard-client-origin");
|
|
72122
|
+
const assetPathId2 = headers.get("x-dashboard-client-asset-id");
|
|
72123
|
+
const clientOrigin = safeOrigin(origin);
|
|
72124
|
+
return {
|
|
72125
|
+
...version2 && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(version2) ? { clientVersion: version2 } : {},
|
|
72126
|
+
...clientOrigin ? { clientOrigin } : {},
|
|
72127
|
+
...assetPathId2 && /^sha256:[a-f0-9]{64}$/.test(assetPathId2) ? { clientAssetPathMatchesConfigured: assetPathId2 === context.configuredAssetPathId } : {}
|
|
72128
|
+
};
|
|
72129
|
+
}
|
|
72130
|
+
function safeOrigin(value) {
|
|
72131
|
+
if (!value) return void 0;
|
|
72132
|
+
try {
|
|
72133
|
+
const origin = new URL(value).origin;
|
|
72134
|
+
return origin === "null" ? void 0 : origin;
|
|
72135
|
+
} catch {
|
|
72136
|
+
return void 0;
|
|
72137
|
+
}
|
|
72138
|
+
}
|
|
72052
72139
|
|
|
72053
72140
|
// packages/server/src/template.ts
|
|
72054
72141
|
var ASSET_PATH_SENTINEL = "/__ASSET_PATH__";
|
|
@@ -72134,6 +72221,10 @@ function createApp(deps) {
|
|
|
72134
72221
|
const credentials = deps.credentials ?? environmentCredentials();
|
|
72135
72222
|
const githubClient = createGithubClient(credentials);
|
|
72136
72223
|
const logger2 = deps.logger ?? consoleLogger;
|
|
72224
|
+
const diagnosticContext = serverDiagnosticContext(
|
|
72225
|
+
config2.serverRelease,
|
|
72226
|
+
assetPathId(config2.assetPath)
|
|
72227
|
+
);
|
|
72137
72228
|
const app = new Hono2();
|
|
72138
72229
|
app.use("/api/*", async (c5, next) => {
|
|
72139
72230
|
const id = requestId();
|
|
@@ -72145,8 +72236,10 @@ function createApp(deps) {
|
|
|
72145
72236
|
const id = c5.get("dashboardRequestId");
|
|
72146
72237
|
logger2.log({
|
|
72147
72238
|
event: "server.unhandled_exception",
|
|
72239
|
+
serverVersion: diagnosticContext.serverVersion,
|
|
72148
72240
|
...id ? { requestId: id } : {},
|
|
72149
|
-
operation: "route-handler"
|
|
72241
|
+
operation: "route-handler",
|
|
72242
|
+
...clientDiagnosticClaims(c5.req.raw.headers, diagnosticContext)
|
|
72150
72243
|
});
|
|
72151
72244
|
return c5.json(
|
|
72152
72245
|
{ error: "The dashboard could not complete this request." },
|
|
@@ -72156,7 +72249,11 @@ function createApp(deps) {
|
|
|
72156
72249
|
});
|
|
72157
72250
|
app.get("/health", (c5) => c5.json({ status: "ok" }));
|
|
72158
72251
|
app.get(`${config2.proxyPath}/client`, (c5) => {
|
|
72159
|
-
const identity = {
|
|
72252
|
+
const identity = {
|
|
72253
|
+
assetPath: config2.assetPath,
|
|
72254
|
+
assetPathId: diagnosticContext.configuredAssetPathId,
|
|
72255
|
+
serverVersion: diagnosticContext.serverVersion
|
|
72256
|
+
};
|
|
72160
72257
|
return c5.json(identity, 200, { "cache-control": "no-store" });
|
|
72161
72258
|
});
|
|
72162
72259
|
app.get("/", (c5) => renderEntrypoint(c5.req.raw, selectedBoard));
|
|
@@ -72386,10 +72483,12 @@ ${(0, import_yaml.stringify)(outputConfig, { sortMapEntries: true })}`,
|
|
|
72386
72483
|
function rejected(c5, context) {
|
|
72387
72484
|
logger2.log({
|
|
72388
72485
|
event: "api.operation_rejected",
|
|
72486
|
+
serverVersion: diagnosticContext.serverVersion,
|
|
72389
72487
|
requestId: c5.get("dashboardRequestId"),
|
|
72390
72488
|
boardId: context.boardId,
|
|
72391
72489
|
panelId: context.panelId,
|
|
72392
|
-
operation: context.operation
|
|
72490
|
+
operation: context.operation,
|
|
72491
|
+
...clientDiagnosticClaims(c5.req.raw.headers, diagnosticContext)
|
|
72393
72492
|
});
|
|
72394
72493
|
return c5.notFound();
|
|
72395
72494
|
}
|
|
@@ -72400,10 +72499,12 @@ ${(0, import_yaml.stringify)(outputConfig, { sortMapEntries: true })}`,
|
|
|
72400
72499
|
if (adapted.envelope?.state === "error") {
|
|
72401
72500
|
logger2.log({
|
|
72402
72501
|
event: "panel.observation_failed",
|
|
72502
|
+
serverVersion: diagnosticContext.serverVersion,
|
|
72403
72503
|
requestId: c5.get("dashboardRequestId"),
|
|
72404
72504
|
boardId: context.boardId,
|
|
72405
72505
|
panelId: context.panelId,
|
|
72406
72506
|
operation: context.operation,
|
|
72507
|
+
...clientDiagnosticClaims(c5.req.raw.headers, diagnosticContext),
|
|
72407
72508
|
errorKind: adapted.envelope.error.kind,
|
|
72408
72509
|
elapsedMs: Math.round(performance.now() - started),
|
|
72409
72510
|
...context.sourceName ? { sourceName: context.sourceName } : {},
|
|
@@ -72419,6 +72520,7 @@ ${(0, import_yaml.stringify)(outputConfig, { sortMapEntries: true })}`,
|
|
|
72419
72520
|
const template = await templates.get(config2.assetPath);
|
|
72420
72521
|
const env2 = {
|
|
72421
72522
|
assetPath: config2.assetPath,
|
|
72523
|
+
assetPathId: diagnosticContext.configuredAssetPathId,
|
|
72422
72524
|
proxyPath: config2.proxyPath,
|
|
72423
72525
|
board
|
|
72424
72526
|
};
|
|
@@ -72465,68 +72567,13 @@ async function fetchBoardConfig(url2, fetcher) {
|
|
|
72465
72567
|
return response.text();
|
|
72466
72568
|
}
|
|
72467
72569
|
|
|
72468
|
-
// packages/server/src/config.ts
|
|
72469
|
-
import { fileURLToPath } from "node:url";
|
|
72470
|
-
var DEFAULT_BOARD_CONFIG_URL = fileURLToPath(
|
|
72471
|
-
new URL("../../../boards/example.yaml", import.meta.url)
|
|
72472
|
-
);
|
|
72473
|
-
var configSchema = external_exports.object({
|
|
72474
|
-
/**
|
|
72475
|
-
* Where the built client lives. Points at a versioned CDN path in a deployment
|
|
72476
|
-
* (`https://assets.../dashboard/1.0.7`) or at the Vite dev server locally. Trailing slashes are
|
|
72477
|
-
* trimmed so `${assetPath}/index.html` never doubles up.
|
|
72478
|
-
*/
|
|
72479
|
-
assetPath: external_exports.string({
|
|
72480
|
-
error: "ASSET_PATH is required \u2014 the server has no client to serve without it."
|
|
72481
|
-
}).min(1, "ASSET_PATH is required \u2014 the server has no client to serve without it.").transform((value) => value.replace(/\/+$/, "")),
|
|
72482
|
-
proxyPath: external_exports.string().min(1).default("/api"),
|
|
72483
|
-
/** A URL or a local file path. Local development uses a path and needs no credential. */
|
|
72484
|
-
boardConfigUrl: external_exports.string().min(1).default(DEFAULT_BOARD_CONFIG_URL),
|
|
72485
|
-
/** Optional for single-board files; startup selects the only board automatically. */
|
|
72486
|
-
board: external_exports.string().min(1).optional(),
|
|
72487
|
-
/** Optional ARN of the one Secrets Manager JSON credential map for this Lambda. */
|
|
72488
|
-
secretReference: external_exports.string().min(1).optional(),
|
|
72489
|
-
/** Immutable server image identifier, supplied by the image build and emitted only in startup logs. */
|
|
72490
|
-
serverRelease: external_exports.string().min(1).default("development"),
|
|
72491
|
-
port: external_exports.coerce.number().int().min(1).max(65535).default(3e3),
|
|
72492
|
-
host: external_exports.string().min(1).default("localhost"),
|
|
72493
|
-
/**
|
|
72494
|
-
* How long to keep retrying an unreachable template before giving up, in milliseconds.
|
|
72495
|
-
*
|
|
72496
|
-
* Zero — the default, and what every deployment uses — means a bad ASSET_PATH fails on the first
|
|
72497
|
-
* attempt, which is the behavior a misconfiguration deserves. `npm run dev` sets a few seconds
|
|
72498
|
-
* because it starts the server and the Vite dev server at the same moment, and without this the
|
|
72499
|
-
* local loop is a race the developer loses about half the time.
|
|
72500
|
-
*/
|
|
72501
|
-
templateWaitMillis: external_exports.coerce.number().int().min(0).default(0)
|
|
72502
|
-
});
|
|
72503
|
-
function loadConfig2(env2 = process.env) {
|
|
72504
|
-
const result = configSchema.safeParse({
|
|
72505
|
-
assetPath: env2.ASSET_PATH,
|
|
72506
|
-
proxyPath: env2.PROXY_PATH,
|
|
72507
|
-
boardConfigUrl: env2.BOARD_CONFIG_URL,
|
|
72508
|
-
board: env2.BOARD,
|
|
72509
|
-
secretReference: env2.SECRET_REFERENCE,
|
|
72510
|
-
serverRelease: env2.SERVER_RELEASE,
|
|
72511
|
-
port: env2.PORT,
|
|
72512
|
-
host: env2.HOST,
|
|
72513
|
-
templateWaitMillis: env2.TEMPLATE_WAIT_MS
|
|
72514
|
-
});
|
|
72515
|
-
if (!result.success) {
|
|
72516
|
-
throw new Error(`Invalid server configuration:
|
|
72517
|
-
${external_exports.prettifyError(result.error)}`);
|
|
72518
|
-
}
|
|
72519
|
-
return result.data;
|
|
72520
|
-
}
|
|
72521
|
-
var LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
72522
|
-
function isLocalHost(host) {
|
|
72523
|
-
return LOCAL_HOSTS.has(host);
|
|
72524
|
-
}
|
|
72525
|
-
|
|
72526
72570
|
// packages/server/src/startup.ts
|
|
72527
72571
|
async function startup(options = {}) {
|
|
72528
72572
|
const logger2 = options.logger ?? consoleLogger;
|
|
72529
|
-
logger2.log({
|
|
72573
|
+
logger2.log({
|
|
72574
|
+
event: "server.starting",
|
|
72575
|
+
serverVersion: process.env.SERVER_RELEASE ?? "development"
|
|
72576
|
+
});
|
|
72530
72577
|
try {
|
|
72531
72578
|
const config2 = loadConfig2();
|
|
72532
72579
|
const fetcher = options.fetcher ?? globalThis.fetch;
|
|
@@ -72559,7 +72606,11 @@ async function startup(options = {}) {
|
|
|
72559
72606
|
config: resolvedConfig
|
|
72560
72607
|
};
|
|
72561
72608
|
} catch (error62) {
|
|
72562
|
-
logger2.log({
|
|
72609
|
+
logger2.log({
|
|
72610
|
+
event: "server.startup_failed",
|
|
72611
|
+
serverVersion: process.env.SERVER_RELEASE ?? "development",
|
|
72612
|
+
category: startupFailureCategory(error62)
|
|
72613
|
+
});
|
|
72563
72614
|
throw error62;
|
|
72564
72615
|
}
|
|
72565
72616
|
}
|
|
@@ -72598,7 +72649,12 @@ async function waitForTemplate(config2, fetcher) {
|
|
|
72598
72649
|
var RETRY_INTERVAL_MILLIS = 250;
|
|
72599
72650
|
function warnAboutMissingAuth(config2, logger2) {
|
|
72600
72651
|
if (isLocalHost(config2.host)) return;
|
|
72601
|
-
logger2.log({
|
|
72652
|
+
logger2.log({
|
|
72653
|
+
event: "server.no_auth_warning",
|
|
72654
|
+
serverVersion: config2.serverRelease,
|
|
72655
|
+
host: config2.host,
|
|
72656
|
+
port: config2.port
|
|
72657
|
+
});
|
|
72602
72658
|
}
|
|
72603
72659
|
function startupFailureCategory(error62) {
|
|
72604
72660
|
const message = error62 instanceof Error ? error62.message : "";
|
package/package.json
CHANGED