seedcord 0.17.3 → 0.17.4
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.mjs +203 -39
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +21 -3
- package/dist/index.mjs +1 -1
- package/dist/{src-_7QZSCTR.mjs → src-i-qherg7.mjs} +2 -2
- package/dist/src-i-qherg7.mjs.map +1 -0
- package/package.json +10 -9
- package/dist/src-_7QZSCTR.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { n as SEEDCORD_CONFIG_FILENAMES, t as version } from "./src-
|
|
1
|
+
import { n as SEEDCORD_CONFIG_FILENAMES, t as version } from "./src-i-qherg7.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { Command } from "@commander-js/extra-typings";
|
|
4
4
|
import { LEVEL_COLOR, Logger, LoggerChannelRegistry, paint } from "@seedcord/logger";
|
|
5
5
|
import { formatBody, installNodeDefaults } from "@seedcord/logger/node";
|
|
6
|
+
import { Envapter, Environment } from "envapt";
|
|
6
7
|
import { SeedcordErrorCode, isSeedcordError } from "@seedcord/errors";
|
|
7
|
-
import { existsSync } from "node:fs";
|
|
8
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
8
9
|
import { BRAND, SeedcordError, validateDiscordToken } from "@seedcord/errors/internal";
|
|
9
10
|
import { delimiter, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
10
11
|
import { pathToFileURL } from "node:url";
|
|
@@ -16,7 +17,6 @@ import { SeedcordBrand } from "@seedcord/types/internal";
|
|
|
16
17
|
import { isTsOrJsFile } from "@seedcord/utils/node";
|
|
17
18
|
import { ApplicationCommandOptionType, ApplicationCommandType, RESTJSONErrorCodes, Routes } from "discord-api-types/v10";
|
|
18
19
|
import { routeLeavesOf } from "@seedcord/utils/internal";
|
|
19
|
-
import { Envapter } from "envapt";
|
|
20
20
|
import { DiscordAPIError, REST } from "@discordjs/rest";
|
|
21
21
|
import chalk from "chalk";
|
|
22
22
|
import { autocompleteMultiselect, cancel, confirm, intro, isCancel, log, multiselect, note, outro, select, spinner } from "@clack/prompts";
|
|
@@ -26,6 +26,8 @@ import React, { Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useR
|
|
|
26
26
|
import { TypedEventEmitter } from "@seedcord/event-emitter";
|
|
27
27
|
import wrapAnsi from "wrap-ansi";
|
|
28
28
|
import Spinner from "ink-spinner";
|
|
29
|
+
import { cpus, totalmem } from "node:os";
|
|
30
|
+
import process$1 from "node:process";
|
|
29
31
|
import { assertNever } from "@seedcord/utils";
|
|
30
32
|
import { settleWithin } from "@seedcord/core/node/internal";
|
|
31
33
|
import { createServer, createServerModuleRunner, defineConfig, mergeConfig } from "vite";
|
|
@@ -79,12 +81,17 @@ function validateBuild(value) {
|
|
|
79
81
|
if (!isOptionalString(value.tsconfig)) throw new SeedcordError(SeedcordErrorCode.CliConfigInvalidBuildTsconfig);
|
|
80
82
|
if (!isOptionalString(value.bootstrap)) throw new SeedcordError(SeedcordErrorCode.CliConfigInvalidBuildBootstrap);
|
|
81
83
|
}
|
|
84
|
+
function validateTypecheck(value) {
|
|
85
|
+
if (value === void 0 || typeof value === "boolean") return;
|
|
86
|
+
if (!isPlainObject(value)) throw new SeedcordError(SeedcordErrorCode.CliConfigInvalidHmrTypecheck);
|
|
87
|
+
if (!isOptionalString(value.tsconfig)) throw new SeedcordError(SeedcordErrorCode.CliConfigInvalidTsconfig);
|
|
88
|
+
}
|
|
82
89
|
function validateHmr(value) {
|
|
83
90
|
if (value === void 0) return;
|
|
84
91
|
if (!isPlainObject(value)) throw new SeedcordError(SeedcordErrorCode.CliConfigInvalidHmr);
|
|
85
|
-
if (!isOptionalString(value.tsconfig)) throw new SeedcordError(SeedcordErrorCode.CliConfigInvalidTsconfig);
|
|
86
92
|
if (value.restart !== void 0 && !isStringArray(value.restart)) throw new SeedcordError(SeedcordErrorCode.CliConfigInvalidHmrRestart);
|
|
87
93
|
if (value.rollback !== void 0 && typeof value.rollback !== "boolean") throw new SeedcordError(SeedcordErrorCode.CliConfigInvalidHmrRollback);
|
|
94
|
+
validateTypecheck(value.typecheck);
|
|
88
95
|
}
|
|
89
96
|
function validateTunnel(value) {
|
|
90
97
|
if (value === void 0 || typeof value === "boolean") return;
|
|
@@ -98,11 +105,20 @@ function resolveTunnel(value) {
|
|
|
98
105
|
};
|
|
99
106
|
return { mode: "quick" };
|
|
100
107
|
}
|
|
108
|
+
function resolveTypecheck(value, root) {
|
|
109
|
+
if (value === void 0 || value === false) return { enabled: false };
|
|
110
|
+
if (value === true || value.tsconfig === void 0) return { enabled: true };
|
|
111
|
+
return {
|
|
112
|
+
enabled: true,
|
|
113
|
+
tsconfig: resolve(root, value.tsconfig)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
101
116
|
function validateConfig(raw) {
|
|
102
117
|
if (!isPlainObject(raw)) throw new SeedcordError(SeedcordErrorCode.CliConfigInvalidExport);
|
|
103
118
|
if (typeof raw.instance !== "string" || raw.instance.length === 0) throw new SeedcordError(SeedcordErrorCode.CliConfigMissingInstance);
|
|
104
119
|
if (typeof raw.entry !== "string" || raw.entry.length === 0) throw new SeedcordError(SeedcordErrorCode.CliConfigMissingEntry);
|
|
105
120
|
if (!isOptionalString(raw.root)) throw new SeedcordError(SeedcordErrorCode.CliConfigInvalidRoot);
|
|
121
|
+
if (raw.idleAnimation !== void 0 && typeof raw.idleAnimation !== "boolean") throw new SeedcordError(SeedcordErrorCode.CliConfigInvalidIdleAnimation);
|
|
106
122
|
validateTunnel(raw.tunnel);
|
|
107
123
|
validateBuild(raw.build);
|
|
108
124
|
validateHmr(raw.hmr);
|
|
@@ -124,14 +140,14 @@ var ConfigLoader = class {
|
|
|
124
140
|
const entry = this.resolveWithinRoot(root, config.entry);
|
|
125
141
|
this.assertEntryWithinRoot(root, entry);
|
|
126
142
|
const build = this.resolveBuildOptions(configDir, config.build);
|
|
127
|
-
const
|
|
143
|
+
const typecheck = resolveTypecheck(config.hmr?.typecheck, root);
|
|
128
144
|
this.logger.debug(`Loaded configuration from ${configPath}`);
|
|
129
145
|
this.logger.trace(`Resolved root: ${root}`);
|
|
130
146
|
this.logger.trace(`Resolved instance: ${instance}`);
|
|
131
147
|
this.logger.trace(`Resolved entry: ${entry}`);
|
|
132
148
|
this.logger.trace(`Resolved build outDir: ${build.outDir}`);
|
|
133
149
|
if (build.tsconfig) this.logger.trace(`Resolved build tsconfig: ${build.tsconfig}`);
|
|
134
|
-
if (
|
|
150
|
+
if (typecheck.enabled) this.logger.trace(`Typecheck tsconfig: ${typecheck.tsconfig ?? "nearest"}`);
|
|
135
151
|
this.logger.trace(`Resolved bootstrap: ${build.bootstrap}`);
|
|
136
152
|
return {
|
|
137
153
|
instance,
|
|
@@ -139,7 +155,8 @@ var ConfigLoader = class {
|
|
|
139
155
|
configFile: configPath,
|
|
140
156
|
entry,
|
|
141
157
|
build,
|
|
142
|
-
|
|
158
|
+
typecheck,
|
|
159
|
+
idleAnimation: config.idleAnimation ?? true,
|
|
143
160
|
tunnel: resolveTunnel(config.tunnel),
|
|
144
161
|
hmr: config.hmr
|
|
145
162
|
};
|
|
@@ -1753,8 +1770,8 @@ const PHASE_META = {
|
|
|
1753
1770
|
//#endregion
|
|
1754
1771
|
//#region src/ui/hooks/useUptime.ts
|
|
1755
1772
|
const TICK_MS = 1e3;
|
|
1756
|
-
function useUptime(state) {
|
|
1757
|
-
const active = isSessionLive(state.phase);
|
|
1773
|
+
function useUptime(state, shown) {
|
|
1774
|
+
const active = isSessionLive(state.phase) && shown;
|
|
1758
1775
|
const { time } = useAnimation({
|
|
1759
1776
|
interval: TICK_MS,
|
|
1760
1777
|
isActive: active
|
|
@@ -1832,7 +1849,7 @@ function FilterChips({ channels, enabledChannels, enabledLevels, cursor, open })
|
|
|
1832
1849
|
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, open ? null : /* @__PURE__ */ React.createElement(Text, {
|
|
1833
1850
|
dimColor: true,
|
|
1834
1851
|
wrap: "truncate"
|
|
1835
|
-
}, "
|
|
1852
|
+
}, "resize to see filters"), /* @__PURE__ */ React.createElement(Heading, {
|
|
1836
1853
|
label: "channels",
|
|
1837
1854
|
open
|
|
1838
1855
|
}), open ? /* @__PURE__ */ React.createElement(Box, {
|
|
@@ -1987,14 +2004,14 @@ function NotificationStack({ notices }) {
|
|
|
1987
2004
|
|
|
1988
2005
|
//#endregion
|
|
1989
2006
|
//#region src/ui/format.ts
|
|
1990
|
-
const MS_PER_SECOND = 1e3;
|
|
2007
|
+
const MS_PER_SECOND$1 = 1e3;
|
|
1991
2008
|
const SECONDS_PER_MINUTE = 60;
|
|
1992
2009
|
const MINUTES_PER_HOUR = 60;
|
|
1993
2010
|
function pad(value) {
|
|
1994
2011
|
return value.toString().padStart(2, "0");
|
|
1995
2012
|
}
|
|
1996
2013
|
function formatUptime(ms) {
|
|
1997
|
-
const totalSeconds = Math.max(0, Math.floor(ms / MS_PER_SECOND));
|
|
2014
|
+
const totalSeconds = Math.max(0, Math.floor(ms / MS_PER_SECOND$1));
|
|
1998
2015
|
const seconds = totalSeconds % SECONDS_PER_MINUTE;
|
|
1999
2016
|
const totalMinutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE);
|
|
2000
2017
|
const minutes = totalMinutes % MINUTES_PER_HOUR;
|
|
@@ -2080,7 +2097,7 @@ function rowKey(row) {
|
|
|
2080
2097
|
//#region src/ui/components/primitives/ScrollableLogView.tsx
|
|
2081
2098
|
const MIN_LOG_LINES = 5;
|
|
2082
2099
|
const NO_ROOM = "Need more room for logs.";
|
|
2083
|
-
const DOT = "
|
|
2100
|
+
const DOT = "●";
|
|
2084
2101
|
const BAR = "▌";
|
|
2085
2102
|
const GUIDE = "│";
|
|
2086
2103
|
const ELLIPSIS = "…";
|
|
@@ -2185,34 +2202,39 @@ function Banner({ version }) {
|
|
|
2185
2202
|
//#endregion
|
|
2186
2203
|
//#region src/ui/components/primitives/BlinkDot.tsx
|
|
2187
2204
|
const BLINK_MS = 530;
|
|
2188
|
-
function BlinkDot() {
|
|
2189
|
-
const { frame } = useAnimation({
|
|
2190
|
-
|
|
2205
|
+
function BlinkDot({ animate = true }) {
|
|
2206
|
+
const { frame } = useAnimation({
|
|
2207
|
+
interval: BLINK_MS,
|
|
2208
|
+
isActive: animate
|
|
2209
|
+
});
|
|
2210
|
+
const lit = !animate || frame % 2 === 0;
|
|
2211
|
+
return /* @__PURE__ */ React.createElement(Text, { color: ui.bad }, lit ? "●" : " ");
|
|
2191
2212
|
}
|
|
2192
2213
|
|
|
2193
2214
|
//#endregion
|
|
2194
2215
|
//#region src/ui/components/StatusBadge.tsx
|
|
2195
|
-
function Glyph({ phase, glyph }) {
|
|
2216
|
+
function Glyph({ phase, glyph, animate }) {
|
|
2196
2217
|
const meta = PHASE_META[phase];
|
|
2197
|
-
if (glyph === "live") return isStreaming(phase) ? /* @__PURE__ */ React.createElement(BlinkDot,
|
|
2218
|
+
if (glyph === "live") return isStreaming(phase) ? /* @__PURE__ */ React.createElement(BlinkDot, { animate }) : /* @__PURE__ */ React.createElement(Text, null, meta.icon);
|
|
2198
2219
|
if (meta.kind === "spinner") return /* @__PURE__ */ React.createElement(Spinner, {
|
|
2199
2220
|
key: "balloon2",
|
|
2200
2221
|
type: "balloon2"
|
|
2201
2222
|
});
|
|
2202
|
-
if (meta.kind === "arc") return /* @__PURE__ */ React.createElement(Spinner, {
|
|
2223
|
+
if (meta.kind === "arc" && animate) return /* @__PURE__ */ React.createElement(Spinner, {
|
|
2203
2224
|
key: "toggle4",
|
|
2204
2225
|
type: "toggle4"
|
|
2205
2226
|
});
|
|
2206
2227
|
return /* @__PURE__ */ React.createElement(Text, null, meta.icon);
|
|
2207
2228
|
}
|
|
2208
|
-
function StatusBadge({ phase, glyph = "phase" }) {
|
|
2229
|
+
function StatusBadge({ phase, glyph = "phase", animate = true }) {
|
|
2209
2230
|
const meta = PHASE_META[phase];
|
|
2210
2231
|
return /* @__PURE__ */ React.createElement(Text, {
|
|
2211
2232
|
color: meta.color,
|
|
2212
2233
|
bold: true
|
|
2213
2234
|
}, /* @__PURE__ */ React.createElement(Glyph, {
|
|
2214
2235
|
phase,
|
|
2215
|
-
glyph
|
|
2236
|
+
glyph,
|
|
2237
|
+
animate
|
|
2216
2238
|
}), " ", meta.label);
|
|
2217
2239
|
}
|
|
2218
2240
|
|
|
@@ -2285,7 +2307,10 @@ function tunnelValue(status) {
|
|
|
2285
2307
|
return status === "live" || status === "lost" ? status : `${status}…`;
|
|
2286
2308
|
}
|
|
2287
2309
|
function StatusBlock({ state, uptimeMs }) {
|
|
2288
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(StatusBadge, {
|
|
2310
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(StatusBadge, {
|
|
2311
|
+
phase: state.phase,
|
|
2312
|
+
animate: state.idleAnimation
|
|
2313
|
+
}), state.status ? /* @__PURE__ */ React.createElement(Text, null, state.status) : null, uptimeMs === null ? null : /* @__PURE__ */ React.createElement(Meta, {
|
|
2289
2314
|
label: "up",
|
|
2290
2315
|
value: formatUptime(uptimeMs)
|
|
2291
2316
|
}), state.port === null ? null : /* @__PURE__ */ React.createElement(Meta, {
|
|
@@ -2338,12 +2363,12 @@ function Sidebar({ state, enabled, enabledLevels, uptimeMs, following, interacti
|
|
|
2338
2363
|
})), /* @__PURE__ */ React.createElement(Box, { flexGrow: 1 }), /* @__PURE__ */ React.createElement(Box, {
|
|
2339
2364
|
flexShrink: 0,
|
|
2340
2365
|
justifyContent: "flex-end"
|
|
2341
|
-
}, isStreaming(state.phase) ? /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(BlinkDot,
|
|
2366
|
+
}, isStreaming(state.phase) ? /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(BlinkDot, { animate: state.idleAnimation }), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " live")) : /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "○ idle")));
|
|
2342
2367
|
}
|
|
2343
2368
|
|
|
2344
2369
|
//#endregion
|
|
2345
2370
|
//#region src/ui/components/primitives/StatusLine.tsx
|
|
2346
|
-
const HINT = "
|
|
2371
|
+
const HINT = "resize for full ui";
|
|
2347
2372
|
const HINT_COLUMNS = 64;
|
|
2348
2373
|
/** The single row standing in for the rail once the terminal is too small to hold it. */
|
|
2349
2374
|
function StatusLine({ state, notices, columns }) {
|
|
@@ -2357,7 +2382,8 @@ function StatusLine({ state, notices, columns }) {
|
|
|
2357
2382
|
overflow: "hidden"
|
|
2358
2383
|
}, /* @__PURE__ */ React.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ React.createElement(Banner, { version: null }), " ", /* @__PURE__ */ React.createElement(StatusBadge, {
|
|
2359
2384
|
phase: state.phase,
|
|
2360
|
-
glyph: "live"
|
|
2385
|
+
glyph: "live",
|
|
2386
|
+
animate: state.idleAnimation
|
|
2361
2387
|
}), top?.summary ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " · ") : null, top?.summary ? /* @__PURE__ */ React.createElement(Text, { color: ui.warn }, top.summary) : null, rest.length > 0 ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " · +", rest.length) : null)), /* @__PURE__ */ React.createElement(Box, {
|
|
2362
2388
|
flexShrink: 0,
|
|
2363
2389
|
marginLeft: 2
|
|
@@ -2571,6 +2597,109 @@ function noticesOf(state) {
|
|
|
2571
2597
|
return notices;
|
|
2572
2598
|
}
|
|
2573
2599
|
|
|
2600
|
+
//#endregion
|
|
2601
|
+
//#region src/ui/profile.ts
|
|
2602
|
+
const enabled = Envapter.getBoolean("SEEDCORD_DEV_PROFILE", false);
|
|
2603
|
+
const MEDIAN = .5;
|
|
2604
|
+
const NEAR_WORST = .99;
|
|
2605
|
+
const BYTES_PER_MB = 1048576;
|
|
2606
|
+
const PERCENT = 100;
|
|
2607
|
+
const MS_PER_SECOND = 1e3;
|
|
2608
|
+
const PHASES = [
|
|
2609
|
+
"config",
|
|
2610
|
+
"vite",
|
|
2611
|
+
"entry",
|
|
2612
|
+
"ready"
|
|
2613
|
+
];
|
|
2614
|
+
const marks = /* @__PURE__ */ new Map();
|
|
2615
|
+
const renderTimes = [];
|
|
2616
|
+
let wrapCalls = 0;
|
|
2617
|
+
let wrapTotal = 0;
|
|
2618
|
+
let writes = 0;
|
|
2619
|
+
let written = 0;
|
|
2620
|
+
let writeTotal = 0;
|
|
2621
|
+
let framesAtReady = 0;
|
|
2622
|
+
function profileMark(phase) {
|
|
2623
|
+
if (!enabled) return;
|
|
2624
|
+
marks.set(phase, performance.now());
|
|
2625
|
+
if (phase === "ready") framesAtReady = renderTimes.length;
|
|
2626
|
+
}
|
|
2627
|
+
function profileFrame(renderTime) {
|
|
2628
|
+
if (enabled) renderTimes.push(renderTime);
|
|
2629
|
+
}
|
|
2630
|
+
function profileWrap(build) {
|
|
2631
|
+
if (!enabled) return build();
|
|
2632
|
+
const started = performance.now();
|
|
2633
|
+
const rows = build();
|
|
2634
|
+
wrapTotal += performance.now() - started;
|
|
2635
|
+
wrapCalls++;
|
|
2636
|
+
return rows;
|
|
2637
|
+
}
|
|
2638
|
+
function profileStdout() {
|
|
2639
|
+
if (!enabled) return;
|
|
2640
|
+
const original = process$1.stdout.write.bind(process$1.stdout);
|
|
2641
|
+
process$1.stdout.write = (chunk, ...rest) => {
|
|
2642
|
+
writes++;
|
|
2643
|
+
written += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.byteLength;
|
|
2644
|
+
const started = performance.now();
|
|
2645
|
+
const flushed = original(chunk, ...rest);
|
|
2646
|
+
writeTotal += performance.now() - started;
|
|
2647
|
+
return flushed;
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
function at(sorted, fraction) {
|
|
2651
|
+
return sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * fraction))] ?? 0;
|
|
2652
|
+
}
|
|
2653
|
+
function ms(value) {
|
|
2654
|
+
return `${value.toFixed(1)} ms`;
|
|
2655
|
+
}
|
|
2656
|
+
function machine() {
|
|
2657
|
+
const cores = cpus();
|
|
2658
|
+
const gb = (totalmem() / BYTES_PER_MB / 1024).toFixed(1);
|
|
2659
|
+
const terminal = process$1.env.WT_SESSION ? "windows terminal" : process$1.env.TERM_PROGRAM ?? "unknown";
|
|
2660
|
+
return [
|
|
2661
|
+
` node ${process$1.version} on ${process$1.platform} ${process$1.arch}`,
|
|
2662
|
+
` cpu ${cores[0]?.model ?? "unknown"} x${String(cores.length)}, ${gb} GB`,
|
|
2663
|
+
` terminal ${terminal}, TERM=${process$1.env.TERM ?? "unset"}, ${String(process$1.stdout.columns)}x${String(process$1.stdout.rows)}`
|
|
2664
|
+
];
|
|
2665
|
+
}
|
|
2666
|
+
function startup() {
|
|
2667
|
+
const ready = marks.get("ready");
|
|
2668
|
+
if (ready === void 0) return [" startup never reached ready"];
|
|
2669
|
+
const lines = [` startup ${ms(ready)}`];
|
|
2670
|
+
let previous = 0;
|
|
2671
|
+
for (const phase of PHASES) {
|
|
2672
|
+
const at = marks.get(phase);
|
|
2673
|
+
if (at === void 0) continue;
|
|
2674
|
+
lines.push(` ${phase.padEnd(10)} ${ms(at - previous)}`);
|
|
2675
|
+
previous = at;
|
|
2676
|
+
}
|
|
2677
|
+
return lines;
|
|
2678
|
+
}
|
|
2679
|
+
function profileReport() {
|
|
2680
|
+
if (!enabled) return null;
|
|
2681
|
+
const session = performance.now();
|
|
2682
|
+
const sorted = [...renderTimes].sort((a, b) => a - b);
|
|
2683
|
+
const layoutTotal = renderTimes.reduce((sum, time) => sum + time, 0);
|
|
2684
|
+
const mb = (written / BYTES_PER_MB).toFixed(2);
|
|
2685
|
+
const share = (part) => `${(part / session * PERCENT).toFixed(1)}% of session`;
|
|
2686
|
+
const ready = marks.get("ready") ?? session;
|
|
2687
|
+
const idleFrames = renderTimes.length - framesAtReady;
|
|
2688
|
+
const idleSeconds = (session - ready) / MS_PER_SECOND;
|
|
2689
|
+
const idleRate = idleSeconds > 0 ? (idleFrames / idleSeconds).toFixed(1) : "0.0";
|
|
2690
|
+
return [
|
|
2691
|
+
"seedcord dev profile",
|
|
2692
|
+
...machine(),
|
|
2693
|
+
` session ${ms(session)}`,
|
|
2694
|
+
...startup(),
|
|
2695
|
+
` frames ${String(renderTimes.length)}`,
|
|
2696
|
+
` idle ${String(idleFrames)} frames over ${idleSeconds.toFixed(1)}s = ${idleRate} fps`,
|
|
2697
|
+
` layout p50 ${ms(at(sorted, MEDIAN))}, p99 ${ms(at(sorted, NEAR_WORST))}, total ${ms(layoutTotal)}, ${share(layoutTotal)}`,
|
|
2698
|
+
` stdout ${mb} MB over ${String(writes)} writes, ${ms(writeTotal)}, ${share(writeTotal)}`,
|
|
2699
|
+
` log wrap ${ms(wrapTotal)} over ${String(wrapCalls)} builds, ${share(wrapTotal)}`
|
|
2700
|
+
].join("\n");
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2574
2703
|
//#endregion
|
|
2575
2704
|
//#region src/ui/tier.ts
|
|
2576
2705
|
const FULL_ROWS = 29;
|
|
@@ -2599,10 +2728,10 @@ function DevApp(props) {
|
|
|
2599
2728
|
const tier = tierFor(rows, columns);
|
|
2600
2729
|
const notices = noticesOf(state);
|
|
2601
2730
|
const logs = useLogs(enabled, enabledLevels);
|
|
2602
|
-
const logRows = useMemo(() => wrapRows(expandRows(logs), logBox.width, Math.max(1, LogStore.instance.getLabelWidth())), [logs, logBox.width]);
|
|
2731
|
+
const logRows = useMemo(() => profileWrap(() => wrapRows(expandRows(logs), logBox.width, Math.max(1, LogStore.instance.getLabelWidth()))), [logs, logBox.width]);
|
|
2603
2732
|
const viewportHeight = Math.max(1, logBox.height);
|
|
2604
2733
|
const scroll = useScroll(logRows, viewportHeight, rowKey);
|
|
2605
|
-
const uptimeMs = useUptime(state);
|
|
2734
|
+
const uptimeMs = useUptime(state, tier !== "logs");
|
|
2606
2735
|
const interactive = !state.isBusy || state.restartRequired;
|
|
2607
2736
|
useInput((input, key) => {
|
|
2608
2737
|
dispatchHotkey({
|
|
@@ -2672,7 +2801,8 @@ const INITIAL = {
|
|
|
2672
2801
|
commandUpdatePrompt: null,
|
|
2673
2802
|
port: null,
|
|
2674
2803
|
tunnel: null,
|
|
2675
|
-
quitArmed: false
|
|
2804
|
+
quitArmed: false,
|
|
2805
|
+
idleAnimation: true
|
|
2676
2806
|
};
|
|
2677
2807
|
var DevStore = class extends TypedEventEmitter {
|
|
2678
2808
|
state = INITIAL;
|
|
@@ -2700,6 +2830,9 @@ var DevStore = class extends TypedEventEmitter {
|
|
|
2700
2830
|
setQuitArmed(quitArmed) {
|
|
2701
2831
|
this.patch({ quitArmed });
|
|
2702
2832
|
}
|
|
2833
|
+
setIdleAnimation(idleAnimation) {
|
|
2834
|
+
this.patch({ idleAnimation });
|
|
2835
|
+
}
|
|
2703
2836
|
clearPrompt() {
|
|
2704
2837
|
this.patch({ commandUpdatePrompt: null });
|
|
2705
2838
|
}
|
|
@@ -2869,19 +3002,24 @@ var DevSession = class {
|
|
|
2869
3002
|
this.onEvent(event);
|
|
2870
3003
|
}
|
|
2871
3004
|
});
|
|
3005
|
+
profileMark("vite");
|
|
2872
3006
|
if (!existsSync(this.config.instance)) throw new SeedcordError(SeedcordErrorCode.CliEntryNotFound, [this.config.instance]);
|
|
2873
3007
|
try {
|
|
2874
3008
|
const { module } = await this.runtime.loadEntry();
|
|
3009
|
+
profileMark("entry");
|
|
2875
3010
|
return module;
|
|
2876
3011
|
} catch (error) {
|
|
2877
3012
|
const message = Error.isError(error) ? error.message : String(error);
|
|
2878
3013
|
throw new SeedcordError(SeedcordErrorCode.CliStartFailed, [this.config.instance, message]);
|
|
2879
3014
|
}
|
|
2880
3015
|
}
|
|
2881
|
-
|
|
2882
|
-
const
|
|
2883
|
-
|
|
3016
|
+
startTypecheck() {
|
|
3017
|
+
const { typecheck } = this.config;
|
|
3018
|
+
if (!typecheck.enabled) return;
|
|
3019
|
+
this.tscRunner = new TscRunner(typecheck.tsconfig, dirname(this.config.configFile));
|
|
2884
3020
|
this.tscRunner.start();
|
|
3021
|
+
}
|
|
3022
|
+
async start(onReady) {
|
|
2885
3023
|
const module = await this.loadInstanceModule();
|
|
2886
3024
|
const exported = resolveDefaultExport(module);
|
|
2887
3025
|
const instance = await Promise.resolve(exported);
|
|
@@ -2896,7 +3034,9 @@ var DevSession = class {
|
|
|
2896
3034
|
if (this.isStopped) return;
|
|
2897
3035
|
this.store.setPhase("running");
|
|
2898
3036
|
this.store.setStatus(`${chalk.bold(instance.username ?? "Bot")} is ready!`);
|
|
3037
|
+
profileMark("ready");
|
|
2899
3038
|
onReady?.();
|
|
3039
|
+
this.startTypecheck();
|
|
2900
3040
|
await new Promise((resolve) => {
|
|
2901
3041
|
this.stopResolve = resolve;
|
|
2902
3042
|
});
|
|
@@ -3077,13 +3217,21 @@ var HmrPlugin = class extends TypedEventEmitter {
|
|
|
3077
3217
|
|
|
3078
3218
|
//#endregion
|
|
3079
3219
|
//#region src/commands/dev/runtime/vite.config.ts
|
|
3220
|
+
function logsIgnore(root) {
|
|
3221
|
+
const logs = resolve(realpathSync(root), "logs");
|
|
3222
|
+
return (path) => path === logs || path.startsWith(logs + sep);
|
|
3223
|
+
}
|
|
3080
3224
|
var vite_config_default = defineConfig({
|
|
3081
3225
|
server: {
|
|
3082
3226
|
middlewareMode: true,
|
|
3083
3227
|
hmr: true,
|
|
3084
3228
|
watch: {
|
|
3085
3229
|
usePolling: false,
|
|
3086
|
-
ignored: [
|
|
3230
|
+
ignored: [
|
|
3231
|
+
"**/node_modules/**",
|
|
3232
|
+
"**/dist/**",
|
|
3233
|
+
"**/.git/**"
|
|
3234
|
+
]
|
|
3087
3235
|
}
|
|
3088
3236
|
},
|
|
3089
3237
|
build: {
|
|
@@ -3146,6 +3294,7 @@ var ViteDevRuntime = class {
|
|
|
3146
3294
|
this.hmrPlugin = hmrPlugin;
|
|
3147
3295
|
const config = mergeConfig(vite_config_default, {
|
|
3148
3296
|
root: projectRoot,
|
|
3297
|
+
server: { watch: { ignored: [logsIgnore(projectRoot)] } },
|
|
3149
3298
|
plugins: [hmrPlugin.plugin]
|
|
3150
3299
|
});
|
|
3151
3300
|
this.viteServer = await createServer(config);
|
|
@@ -3223,8 +3372,8 @@ const BINARY = "cloudflared";
|
|
|
3223
3372
|
const DOWNLOADS = "https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/";
|
|
3224
3373
|
function systemLookup() {
|
|
3225
3374
|
return {
|
|
3226
|
-
pathVar:
|
|
3227
|
-
pathExt:
|
|
3375
|
+
pathVar: Envapter.get("PATH"),
|
|
3376
|
+
pathExt: Envapter.get("PATHEXT"),
|
|
3228
3377
|
platform: process.platform,
|
|
3229
3378
|
delimiter,
|
|
3230
3379
|
exists: existsSync
|
|
@@ -3621,6 +3770,8 @@ var DevRunner = class DevRunner {
|
|
|
3621
3770
|
this.deps.store.setPhase("starting");
|
|
3622
3771
|
this.deps.store.setBusy(true);
|
|
3623
3772
|
const config = await this.loadConfig();
|
|
3773
|
+
profileMark("config");
|
|
3774
|
+
this.deps.store.setIdleAnimation(config.idleAnimation);
|
|
3624
3775
|
const runtime = new ViteDevRuntime();
|
|
3625
3776
|
this.currentSession = new DevSession(config, runtime, this.deps.store, (event) => {
|
|
3626
3777
|
this.deps.tunnel.route(config.tunnel, event);
|
|
@@ -3699,6 +3850,7 @@ var DevCommand = class extends BaseCommand {
|
|
|
3699
3850
|
}
|
|
3700
3851
|
register(program) {
|
|
3701
3852
|
program.command(this.name).description(this.description).action(async () => {
|
|
3853
|
+
profileStdout();
|
|
3702
3854
|
const store = new DevStore();
|
|
3703
3855
|
const runner = DevRunner.create(this.logger, store);
|
|
3704
3856
|
const onSignal = () => {
|
|
@@ -3715,12 +3867,21 @@ var DevCommand = class extends BaseCommand {
|
|
|
3715
3867
|
process.off("SIGINT", onSignal);
|
|
3716
3868
|
process.off("SIGTERM", onSignal);
|
|
3717
3869
|
}
|
|
3718
|
-
this.printLogLocation();
|
|
3870
|
+
await this.printLogLocation();
|
|
3719
3871
|
process.exit();
|
|
3720
3872
|
});
|
|
3721
3873
|
}
|
|
3722
|
-
|
|
3723
|
-
|
|
3874
|
+
async write(text) {
|
|
3875
|
+
return new Promise((resolve) => {
|
|
3876
|
+
process.stdout.write(text, () => {
|
|
3877
|
+
resolve();
|
|
3878
|
+
});
|
|
3879
|
+
});
|
|
3880
|
+
}
|
|
3881
|
+
async printLogLocation() {
|
|
3882
|
+
await this.write("seedcord dev stopped. logs: logs/\n");
|
|
3883
|
+
const report = profileReport();
|
|
3884
|
+
if (report !== null) await this.write(`${report}\n`);
|
|
3724
3885
|
}
|
|
3725
3886
|
async runDevApp(store, runner) {
|
|
3726
3887
|
let failure;
|
|
@@ -3741,7 +3902,10 @@ var DevCommand = class extends BaseCommand {
|
|
|
3741
3902
|
}
|
|
3742
3903
|
}), {
|
|
3743
3904
|
exitOnCtrlC: false,
|
|
3744
|
-
alternateScreen: true
|
|
3905
|
+
alternateScreen: true,
|
|
3906
|
+
onRender: ({ renderTime }) => {
|
|
3907
|
+
profileFrame(renderTime);
|
|
3908
|
+
}
|
|
3745
3909
|
});
|
|
3746
3910
|
await waitUntilExit();
|
|
3747
3911
|
await runResult;
|
|
@@ -3752,7 +3916,7 @@ var DevCommand = class extends BaseCommand {
|
|
|
3752
3916
|
//#endregion
|
|
3753
3917
|
//#region src/cli.ts
|
|
3754
3918
|
async function main() {
|
|
3755
|
-
if (!process.env.ENV && !process.env.ENVIRONMENT && !process.env.NODE_ENV)
|
|
3919
|
+
if (!process.env.ENV && !process.env.ENVIRONMENT && !process.env.NODE_ENV) Envapter.environment = Environment.Development;
|
|
3756
3920
|
installNodeDefaults();
|
|
3757
3921
|
const program = new Command().name("seedcord").description("seedcord CLI").version(version);
|
|
3758
3922
|
new DevCommand().register(program);
|