seedcord 0.17.2 → 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 CHANGED
@@ -1,10 +1,11 @@
1
- import { n as SEEDCORD_CONFIG_FILENAMES, t as version } from "./src-QLkp6MNS.mjs";
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 tsconfig = config.hmr?.tsconfig ? resolve(root, config.hmr.tsconfig) : void 0;
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 (tsconfig) this.logger.trace(`Resolved dev tsconfig: ${tsconfig}`);
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
- tsconfig,
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
- }, "↕ resize to see filters"), /* @__PURE__ */ React.createElement(Heading, {
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({ interval: BLINK_MS });
2190
- return /* @__PURE__ */ React.createElement(Text, { color: ui.bad }, frame % 2 === 0 ? "●" : " ");
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, null) : /* @__PURE__ */ React.createElement(Text, null, meta.icon);
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, { phase: state.phase }), state.status ? /* @__PURE__ */ React.createElement(Text, null, state.status) : null, uptimeMs === null ? null : /* @__PURE__ */ React.createElement(Meta, {
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, null), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " live")) : /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "○ idle")));
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 = "↕ resize for full ui";
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
- async start(onReady) {
2882
- const cwd = dirname(this.config.configFile);
2883
- this.tscRunner = new TscRunner(this.config.tsconfig, cwd);
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: ["**/node_modules/**", "**/dist/**"]
3230
+ ignored: [
3231
+ "**/node_modules/**",
3232
+ "**/dist/**",
3233
+ "**/.git/**"
3234
+ ]
3087
3235
  }
3088
3236
  },
3089
3237
  build: {
@@ -3099,6 +3247,7 @@ var vite_config_default = defineConfig({
3099
3247
  ssr: {
3100
3248
  target: "node",
3101
3249
  external: ["@seedcord/logger", "@seedcord/logger/node"],
3250
+ noExternal: [/^@seedcord\//],
3102
3251
  resolve: {
3103
3252
  conditions: ["node", "import"],
3104
3253
  externalConditions: ["node"]
@@ -3145,6 +3294,7 @@ var ViteDevRuntime = class {
3145
3294
  this.hmrPlugin = hmrPlugin;
3146
3295
  const config = mergeConfig(vite_config_default, {
3147
3296
  root: projectRoot,
3297
+ server: { watch: { ignored: [logsIgnore(projectRoot)] } },
3148
3298
  plugins: [hmrPlugin.plugin]
3149
3299
  });
3150
3300
  this.viteServer = await createServer(config);
@@ -3222,8 +3372,8 @@ const BINARY = "cloudflared";
3222
3372
  const DOWNLOADS = "https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/";
3223
3373
  function systemLookup() {
3224
3374
  return {
3225
- pathVar: process.env.PATH,
3226
- pathExt: process.env.PATHEXT,
3375
+ pathVar: Envapter.get("PATH"),
3376
+ pathExt: Envapter.get("PATHEXT"),
3227
3377
  platform: process.platform,
3228
3378
  delimiter,
3229
3379
  exists: existsSync
@@ -3620,6 +3770,8 @@ var DevRunner = class DevRunner {
3620
3770
  this.deps.store.setPhase("starting");
3621
3771
  this.deps.store.setBusy(true);
3622
3772
  const config = await this.loadConfig();
3773
+ profileMark("config");
3774
+ this.deps.store.setIdleAnimation(config.idleAnimation);
3623
3775
  const runtime = new ViteDevRuntime();
3624
3776
  this.currentSession = new DevSession(config, runtime, this.deps.store, (event) => {
3625
3777
  this.deps.tunnel.route(config.tunnel, event);
@@ -3698,6 +3850,7 @@ var DevCommand = class extends BaseCommand {
3698
3850
  }
3699
3851
  register(program) {
3700
3852
  program.command(this.name).description(this.description).action(async () => {
3853
+ profileStdout();
3701
3854
  const store = new DevStore();
3702
3855
  const runner = DevRunner.create(this.logger, store);
3703
3856
  const onSignal = () => {
@@ -3714,12 +3867,21 @@ var DevCommand = class extends BaseCommand {
3714
3867
  process.off("SIGINT", onSignal);
3715
3868
  process.off("SIGTERM", onSignal);
3716
3869
  }
3717
- this.printLogLocation();
3870
+ await this.printLogLocation();
3718
3871
  process.exit();
3719
3872
  });
3720
3873
  }
3721
- printLogLocation() {
3722
- process.stdout.write("seedcord dev stopped. logs: logs/\n");
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`);
3723
3885
  }
3724
3886
  async runDevApp(store, runner) {
3725
3887
  let failure;
@@ -3740,7 +3902,10 @@ var DevCommand = class extends BaseCommand {
3740
3902
  }
3741
3903
  }), {
3742
3904
  exitOnCtrlC: false,
3743
- alternateScreen: true
3905
+ alternateScreen: true,
3906
+ onRender: ({ renderTime }) => {
3907
+ profileFrame(renderTime);
3908
+ }
3744
3909
  });
3745
3910
  await waitUntilExit();
3746
3911
  await runResult;
@@ -3751,7 +3916,7 @@ var DevCommand = class extends BaseCommand {
3751
3916
  //#endregion
3752
3917
  //#region src/cli.ts
3753
3918
  async function main() {
3754
- if (!process.env.ENV && !process.env.ENVIRONMENT && !process.env.NODE_ENV) process.env.NODE_ENV = "development";
3919
+ if (!process.env.ENV && !process.env.ENVIRONMENT && !process.env.NODE_ENV) Envapter.environment = Environment.Development;
3755
3920
  installNodeDefaults();
3756
3921
  const program = new Command().name("seedcord").description("seedcord CLI").version(version);
3757
3922
  new DevCommand().register(program);