@wolfstar/cli 2.3.1 → 2.6.0-next-20261007120201
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/README.md +83 -13
- package/dist/{_shared-BIQK0Bhm.js → _shared-CyzTojMA.js} +95 -20
- package/dist/_shared-CyzTojMA.js.map +1 -0
- package/dist/{bridge-BegCt_n2.js → bridge-Cbm0jX9h.js} +3 -236
- package/dist/bridge-Cbm0jX9h.js.map +1 -0
- package/dist/{build-DJcc95Yq.js → build-BbIEbOu3.js} +5 -5
- package/dist/{build-DJcc95Yq.js.map → build-BbIEbOu3.js.map} +1 -1
- package/dist/cli.js +1 -1
- package/dist/codegen-C2PY7RXA.js +262 -0
- package/dist/codegen-C2PY7RXA.js.map +1 -0
- package/dist/commands-3Tgwy6cj.js +16 -0
- package/dist/commands-3Tgwy6cj.js.map +1 -0
- package/dist/commands-3qXbxhXx.js +3 -0
- package/dist/{commands-Bh0K9iWX.js → commands-C-i3qYOz.js} +5 -93
- package/dist/commands-C-i3qYOz.js.map +1 -0
- package/dist/{completions-DMPlZ0S1.js → completions-Cs6YbuXO.js} +3 -3
- package/dist/{completions-DMPlZ0S1.js.map → completions-Cs6YbuXO.js.map} +1 -1
- package/dist/{dev-Owu3e3AO.js → dev-9YvzhtkU.js} +22 -215
- package/dist/dev-9YvzhtkU.js.map +1 -0
- package/dist/{diagnostics-BysbNnPD.js → diagnostics-Dn3CXa8J.js} +11 -2
- package/dist/diagnostics-Dn3CXa8J.js.map +1 -0
- package/dist/{doctor-DEtAAknK.js → doctor-CMGfbXS5.js} +15 -7
- package/dist/doctor-CMGfbXS5.js.map +1 -0
- package/dist/{hooks-Cy6F2bhH.js → hooks-D_RVS0ba.js} +13 -5
- package/dist/hooks-D_RVS0ba.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{info-CKJVMWIT.js → info-eXsx1AfE.js} +5 -4
- package/dist/info-eXsx1AfE.js.map +1 -0
- package/dist/local-commands-CGBjc8KJ.js +95 -0
- package/dist/local-commands-CGBjc8KJ.js.map +1 -0
- package/dist/{locales-BJAP4HYf.js → locales-BzyEMQnX.js} +5 -5
- package/dist/{locales-BJAP4HYf.js.map → locales-BzyEMQnX.js.map} +1 -1
- package/dist/{nitro-B8u1iVgf.js → nitro-DufOgx8s.js} +2 -2
- package/dist/{nitro-B8u1iVgf.js.map → nitro-DufOgx8s.js.map} +1 -1
- package/dist/{prepare-BM93i3Rm.js → prepare-DKH0UIYA.js} +12 -8
- package/dist/prepare-DKH0UIYA.js.map +1 -0
- package/dist/{project-zlQStwsf.js → project-DBqQMFCk.js} +14 -3
- package/dist/project-DBqQMFCk.js.map +1 -0
- package/dist/{run-CHqwvZXf.js → run-DlNY1QM1.js} +2 -2
- package/dist/{run-CHqwvZXf.js.map → run-DlNY1QM1.js.map} +1 -1
- package/dist/tsc-9yHyx0L6.js +3 -0
- package/dist/{tsc-puVAcwmo.js → tsc-BArammxe.js} +3 -3
- package/dist/{tsc-puVAcwmo.js.map → tsc-BArammxe.js.map} +1 -1
- package/dist/{tsdown-Dwt3JhPw.js → tsdown-2i5h88vT.js} +2 -2
- package/dist/{tsdown-Dwt3JhPw.js.map → tsdown-2i5h88vT.js.map} +1 -1
- package/dist/{tui-nldGSD4c.js → tui-CB_qk2Z1.js} +2 -2
- package/dist/{tui-nldGSD4c.js.map → tui-CB_qk2Z1.js.map} +1 -1
- package/dist/tunnel-BNV5cuJz.js +236 -0
- package/dist/tunnel-BNV5cuJz.js.map +1 -0
- package/dist/typecheck-p_2rj9lL.js +66 -0
- package/dist/typecheck-p_2rj9lL.js.map +1 -0
- package/dist/typechecker-C3UOI2LF.js +213 -0
- package/dist/typechecker-C3UOI2LF.js.map +1 -0
- package/dist/{vite-YZUtv2Hk.js → vite-C5CN3xUu.js} +2 -2
- package/dist/{vite-YZUtv2Hk.js.map → vite-C5CN3xUu.js.map} +1 -1
- package/package.json +9 -9
- package/dist/_shared-BIQK0Bhm.js.map +0 -1
- package/dist/bridge-BegCt_n2.js.map +0 -1
- package/dist/codegen-aJSyPz-J.js +0 -124
- package/dist/codegen-aJSyPz-J.js.map +0 -1
- package/dist/commands-Bh0K9iWX.js.map +0 -1
- package/dist/commands-BlDTDvFO.js +0 -3
- package/dist/commands-CSB-pjgs.js +0 -15
- package/dist/commands-CSB-pjgs.js.map +0 -1
- package/dist/dev-Owu3e3AO.js.map +0 -1
- package/dist/diagnostics-BysbNnPD.js.map +0 -1
- package/dist/doctor-DEtAAknK.js.map +0 -1
- package/dist/hooks-Cy6F2bhH.js.map +0 -1
- package/dist/info-CKJVMWIT.js.map +0 -1
- package/dist/prepare-BM93i3Rm.js.map +0 -1
- package/dist/project-zlQStwsf.js.map +0 -1
- package/dist/tsc-C5l5TQGC.js +0 -3
package/README.md
CHANGED
|
@@ -24,8 +24,9 @@ package has no install-time dependency on `@wolfstar/http-framework` in return,
|
|
|
24
24
|
- `stars dev` builds the project, starts the bot, restarts it (or leaves the change to the bot's hot reload) and shows what is happening in a full-screen dashboard: status, log channels and levels you can filter, and a prompt before changed commands are redeployed (or plain logs).
|
|
25
25
|
- `stars build` runs the configured build tool once.
|
|
26
26
|
- `stars info` prints the resolved configuration and environment (`--json` for scripts).
|
|
27
|
-
- `stars codegen` runs the configured code generators (`--check` for CI).
|
|
28
|
-
- `stars prepare` generates `.stars
|
|
27
|
+
- `stars codegen` runs the configured code generators: i18next types and the typed options of your commands (`--check` for CI).
|
|
28
|
+
- `stars prepare` generates the TypeScript configuration in `.stars/` (`tsconfig.app.json` and `tsconfig.node.json`) and the auto imports declaration file (`--check` for CI).
|
|
29
|
+
- `stars typecheck` regenerates `.stars/` and type-checks every project once, the way `nuxt typecheck` does.
|
|
29
30
|
- `stars commands` inspects, compares, deploys and cleans the application commands Discord has deployed.
|
|
30
31
|
- `stars doctor` checks that the project is ready: runtime, framework, credentials, interactions endpoint, generated files.
|
|
31
32
|
- `stars completions` prints the shell completion script for bash, zsh or fish.
|
|
@@ -66,14 +67,17 @@ const config = await loadStarsConfig({ cwd: process.cwd() });
|
|
|
66
67
|
console.log(config.entry, config.build.output);
|
|
67
68
|
```
|
|
68
69
|
|
|
70
|
+
`loadStarsConfig` only validates what the schema owns. A top-level key that is not built in is kept as `config.moduleOptions` (it may be the options of a module, `meta.configKey`), not rejected, so a misspelled key is reported by the `stars` commands once the modules are set up, not by `loadStarsConfig`. A tool that loads the configuration on its own and wants that check calls `assertModuleOptionsClaimed(config, claimedKeys)` from `@wolfstar/schema` (also exported by `@wolfstar/http-framework/config`) with the `meta.configKey`s of the modules it installed, or `[]` when it installs none.
|
|
71
|
+
|
|
69
72
|
## Commands
|
|
70
73
|
|
|
71
74
|
```sh
|
|
72
|
-
stars dev [--no-tui] [--layout <auto|dashboard|panel>] [--channel <name>] [--level <level>] [--theme <name>] [--config <file>] [--cwd <dir>]
|
|
75
|
+
stars dev [--no-tui] [--tunnel|--no-tunnel] [--layout <auto|dashboard|panel>] [--channel <name>] [--level <level>] [--theme <name>] [--config <file>] [--cwd <dir>]
|
|
73
76
|
stars build [--config <file>] [--cwd <dir>]
|
|
74
77
|
stars info [--json] [--config <file>] [--cwd <dir>]
|
|
75
78
|
stars codegen [--check] [--json] [--config <file>] [--cwd <dir>]
|
|
76
79
|
stars prepare [--check] [--json] [--config <file>] [--cwd <dir>]
|
|
80
|
+
stars typecheck [--config <file>] [--cwd <dir>]
|
|
77
81
|
stars commands [list|clean|diff|deploy] [--guild <id>] [--name <name>] [--check] [--yes] [--json]
|
|
78
82
|
stars doctor [--online] [--json] [--config <file>] [--cwd <dir>]
|
|
79
83
|
stars completions <bash|zsh|fish>
|
|
@@ -275,6 +279,48 @@ fills in on its own (`id`, `version`, defaults such as `nsfw: false`) are ignore
|
|
|
275
279
|
overwrite: a deployed command the project no longer defines is deleted, which is why it asks first and refuses to
|
|
276
280
|
run without `--yes` outside a terminal, or with `--json`.
|
|
277
281
|
|
|
282
|
+
### `stars codegen`
|
|
283
|
+
|
|
284
|
+
Runs the code generators `codegen` in `stars.config` enables, writing their files (`--check` fails with
|
|
285
|
+
`CODEGEN_OUTDATED` instead when one is stale, `--json` prints `{ check, results }`):
|
|
286
|
+
|
|
287
|
+
- `codegen.i18n` types the i18next resources with `@wolfstar/i18next-type-generator`. It is on by default when
|
|
288
|
+
`src/locales/en-US` exists.
|
|
289
|
+
- `codegen.commands` types the options of every command from its builder. It is off by default: `true` writes
|
|
290
|
+
`src/@types/commands.d.ts`, `{ output }` picks another file.
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
export default defineConfig({ codegen: { commands: true } });
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The generated file augments `CommandOptionsRegistry` with one entry per command path (`'ping'`, `'math add'`,
|
|
297
|
+
`'subscriptions twitch add'`), and `Command.OptionsOf<'math add'>` reads it, so the handler needs no hand-written
|
|
298
|
+
`interface Options` that can drift from the builder:
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
public add(interaction: Command.ChatInputInteraction, { left, right }: Command.OptionsOf<'math add'>) {}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Every option has the shape the framework resolves it to at runtime (`user` is `TransformedArguments.User`, `role` is
|
|
305
|
+
`APIRole`, ...), a `required` option is not optional, `choices` are a literal union and `channel_types` narrow a
|
|
306
|
+
channel. A localized command is keyed by its default name, the one the handler receives. Context menu commands have no
|
|
307
|
+
options and are skipped. For an autocomplete, `Command.AutocompleteArguments<Command.OptionsOf<'math add'>>` types
|
|
308
|
+
`focused` from the same entry.
|
|
309
|
+
|
|
310
|
+
The types come from what the bot registers, so loops and factories need no special care: `stars codegen` reads the
|
|
311
|
+
commands from the built bot the way `stars commands diff` does (run `stars build` first), by starting it in a mode
|
|
312
|
+
that loads its pieces, reports its registry and exits before it listens or talks to Discord. It does not call
|
|
313
|
+
`client.login`, but the bot still has to get through its own setup: values it reads while starting (`DISCORD_CLIENT_ID`,
|
|
314
|
+
`DISCORD_PUBLIC_KEY`, ...) have to be set, with placeholders in CI where the real secrets are not available:
|
|
315
|
+
|
|
316
|
+
```sh
|
|
317
|
+
DISCORD_CLIENT_ID=1 DISCORD_TOKEN=x DISCORD_PUBLIC_KEY=0 stars codegen --check
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
It is not part of `stars dev` or `stars prepare` (which has no build to read from yet): run `stars codegen` after
|
|
321
|
+
changing an option, and `stars codegen --check` in CI to keep the file up to date. A path registered more than once
|
|
322
|
+
(a command restricted to several guilds) keeps its first definition.
|
|
323
|
+
|
|
278
324
|
### `stars doctor`
|
|
279
325
|
|
|
280
326
|
Checks what a project needs before `stars dev` can do its job, and says what to do about each problem:
|
|
@@ -291,7 +337,7 @@ stars doctor v2.3.0
|
|
|
291
337
|
→ Stop it, or set another HTTP_PORT, unless it is this bot running.
|
|
292
338
|
ℹ tunnel No tunnel: Discord cannot reach a bot on localhost
|
|
293
339
|
→ Set `dev.tunnel: true` for a cloudflared quick tunnel, or press t in `stars dev`.
|
|
294
|
-
⚠ prepare Out of date: .stars/tsconfig.json
|
|
340
|
+
⚠ prepare Out of date: .stars/tsconfig.app.json, .stars/tsconfig.node.json
|
|
295
341
|
→ Run `stars prepare`.
|
|
296
342
|
|
|
297
343
|
1 error(s), 2 warning(s)
|
|
@@ -325,7 +371,8 @@ Three `dev` options round out the dev loop (all documented in
|
|
|
325
371
|
mode), `tsz` (the tsc-compatible checker, re-run after every build since it has no watch mode), or `auto` — the
|
|
326
372
|
default, which uses `golar` when the project depends on it and `tsc` otherwise.
|
|
327
373
|
- Pressing `t` opens and closes a `cloudflared` quick tunnel without configuration. `dev.tunnel: true` opens it at startup so Discord can reach the bot's interactions endpoint from the
|
|
328
|
-
internet
|
|
374
|
+
internet, and so does `stars dev --tunnel` for one session, whatever `dev.tunnel` says (`--no-tunnel` keeps it closed at
|
|
375
|
+
start; `t` still opens it). With `--no-tui` the URL is printed on the `tunnel` channel; a string is an https URL you already serve, which the CLI only probes. `dev.tunnel.updateEndpoint` writes
|
|
329
376
|
the URL to the Discord application, and is opt-in because it edits a live application.
|
|
330
377
|
- `dev.logFile` (default `.stars/dev.log`) mirrors the session's logs to disk, so a run can be read back after the
|
|
331
378
|
terminal UI is gone. Set it to `false` to disable it. It is truncated on every run; `dev.logs.dir` (for example
|
|
@@ -368,7 +415,8 @@ and which options the block sets.
|
|
|
368
415
|
| ------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
369
416
|
| `3` (EOL) | Legacy behaviour: a `tsdown.config.*` drives the build, auto imports off unless asked for; prints `COMPATIBILITY_VERSION_EOL` |
|
|
370
417
|
| `4` | `tsdown` configured from `stars.config` alone, auto imports on and wired in, `'auto'` picks `tsdown` for TypeScript |
|
|
371
|
-
| `5`
|
|
418
|
+
| `5` | Everything in `4`, plus `env` registered automatically when the project depends on `@wolfstar/env-utilities` |
|
|
419
|
+
| `6` (default) | Everything in `5`, plus the generated tsconfig split into `.stars/tsconfig.app.json` and `.stars/tsconfig.node.json` |
|
|
372
420
|
|
|
373
421
|
### Environment and hooks
|
|
374
422
|
|
|
@@ -435,7 +483,30 @@ package.json imports, and ESNext/DOM libraries. Use `import type` and `export ty
|
|
|
435
483
|
These options apply to tsdown and Vite; tsc keeps its emit-compatible settings. Sapphire's decorator options and
|
|
436
484
|
the ES2022 target remain in effect. This does not enable Stars' experimental Nitro runtime integration.
|
|
437
485
|
|
|
438
|
-
|
|
486
|
+
From compatibility version 6, `stars prepare` writes two files, the way a Nuxt 4 project has them:
|
|
487
|
+
|
|
488
|
+
- `.stars/tsconfig.app.json` is the bot: everything under the entry's directory plus the auto imports declaration,
|
|
489
|
+
with the `~`/`@`/`~~`/`@@` paths and the options above. `tsdown` builds with it.
|
|
490
|
+
- `.stars/tsconfig.node.json` is what Node runs without a bundler: `stars.config.*`, `vitest.config.*`,
|
|
491
|
+
`tsdown.config.*`, `vite.config.*` and `scripts/**`. It uses `NodeNext` resolution, `types: ["node"]`, no DOM
|
|
492
|
+
library, no aliases or auto imports and no decorators, and leaves the entry's directory to the app project.
|
|
493
|
+
|
|
494
|
+
Your own `tsconfig.json` only references them:
|
|
495
|
+
|
|
496
|
+
```json
|
|
497
|
+
{
|
|
498
|
+
"files": [],
|
|
499
|
+
"references": [{ "path": "./.stars/tsconfig.app.json" }, { "path": "./.stars/tsconfig.node.json" }]
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
`stars typecheck` regenerates `.stars/` and runs your checker (`dev.typecheck.checker`: `golar` or `tsc`; bare `tsc -p`
|
|
504
|
+
on that root would check nothing) once per project. `stars dev`'s `dev.typecheck` watches the app project. A root
|
|
505
|
+
`tsconfig.json` that still extends `./.stars/tsconfig.json` gets a `TSCONFIG_LEGACY_EXTENDS` warning from
|
|
506
|
+
`stars prepare` and `stars doctor`: update it once, or keep `future.compatibilityVersion` at `5`.
|
|
507
|
+
|
|
508
|
+
On `5` and `4` nothing changes: `stars prepare` writes the single `.stars/tsconfig.json`, which you extend from your
|
|
509
|
+
project's `tsconfig.json`:
|
|
439
510
|
|
|
440
511
|
```json
|
|
441
512
|
{
|
|
@@ -443,18 +514,17 @@ Run `stars prepare` and extend the generated config from your project's `tsconfi
|
|
|
443
514
|
}
|
|
444
515
|
```
|
|
445
516
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
For tsdown builds, `@/` and `~/` resolve to the entry file's directory (normally `src/`), while `@@/` and `~~/`
|
|
517
|
+
Keep your existing compiler options alongside `extends`. `stars dev` and `stars build` also regenerate the generated
|
|
518
|
+
files. For tsdown builds, `@/` and `~/` resolve to the entry file's directory (normally `src/`), while `@@/` and `~~/`
|
|
449
519
|
resolve to the project root. Filesystem aliases in `stars.config.ts`'s `tsdown.alias` are included too, with custom
|
|
450
520
|
values taking precedence. Legacy builds using a separate tsdown config only include aliases declared in
|
|
451
521
|
`stars.config.ts`. Other build tools do not get tsdown aliases, since TypeScript alone does not rewrite imports.
|
|
452
522
|
|
|
453
|
-
The generated config includes source files and the auto imports declaration, including a custom `imports.dts`
|
|
523
|
+
The generated app config includes source files and the auto imports declaration, including a custom `imports.dts`
|
|
454
524
|
location. Explicit `include` or `compilerOptions.paths` in your own tsconfig replace the inherited values;
|
|
455
525
|
remove manually duplicated paths to use the generated aliases. Generation works with `imports: false` too.
|
|
456
|
-
Use `stars prepare --check` to check
|
|
457
|
-
|
|
526
|
+
Use `stars prepare --check` to check the generated files without writing them. Do not edit anything in `.stars/` by
|
|
527
|
+
hand; keep `.stars/` ignored by Git and run `stars prepare` after installing dependencies on a fresh checkout.
|
|
458
528
|
|
|
459
529
|
## Server integrations
|
|
460
530
|
|
|
@@ -1,20 +1,69 @@
|
|
|
1
1
|
import { r as formatError } from "./errors-COFWKKKw.js";
|
|
2
|
+
import { t as cliDiagnostics } from "./diagnostics-Dn3CXa8J.js";
|
|
2
3
|
import { t as loadAutoImportsModule } from "./framework-auto-imports-Gu_PXSyr.js";
|
|
3
|
-
import { a as modulesPreloadWarning, o as prepareModulesPreload } from "./hooks-
|
|
4
|
+
import { a as modulesPreloadWarning, o as prepareModulesPreload } from "./hooks-D_RVS0ba.js";
|
|
4
5
|
import { createRequire } from "node:module";
|
|
5
6
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
7
|
+
import { SPLIT_TSCONFIG_VERSION } from "@wolfstar/schema";
|
|
6
8
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
7
9
|
|
|
8
10
|
//#region src/utils/tsconfig.ts
|
|
9
11
|
const require = createRequire(import.meta.url);
|
|
10
|
-
const
|
|
12
|
+
const presetOptions = (ids) => Object.assign({}, ...ids.map((id) => require(id).compilerOptions));
|
|
13
|
+
const strictOptions = presetOptions(["@sapphire/ts-config", "@sapphire/ts-config/extra-strict"]);
|
|
14
|
+
const sapphireOptions = presetOptions([
|
|
11
15
|
"@sapphire/ts-config",
|
|
12
16
|
"@sapphire/ts-config/extra-strict",
|
|
13
17
|
"@sapphire/ts-config/decorators"
|
|
14
|
-
]
|
|
15
|
-
/**
|
|
18
|
+
]);
|
|
19
|
+
/** The root config files that run in Node and never go through the bundler. */
|
|
20
|
+
const NODE_PROJECT_FILES = [
|
|
21
|
+
"stars.config.*",
|
|
22
|
+
"vitest.config.*",
|
|
23
|
+
"tsdown.config.*",
|
|
24
|
+
"vite.config.*",
|
|
25
|
+
"scripts/**/*"
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Generates the TypeScript configuration without changing the project's own tsconfig: one extendable
|
|
29
|
+
* `.stars/tsconfig.json` below compatibility version 6, and from 6 the `.stars/tsconfig.app.json` (the bot sources) and
|
|
30
|
+
* `.stars/tsconfig.node.json` (the root files that run in Node) a solution-style `tsconfig.json` references.
|
|
31
|
+
* Returns one status per file.
|
|
32
|
+
*/
|
|
16
33
|
async function prepareTsconfig(config, check = false) {
|
|
17
|
-
|
|
34
|
+
if (config.future.compatibilityVersion < SPLIT_TSCONFIG_VERSION) return [await emit(config, "tsconfig", appTsconfig(config, "tsconfig"), check)];
|
|
35
|
+
return [await emit(config, "app", appTsconfig(config, "app"), check), await emit(config, "node", nodeTsconfig(config), check)];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Reports a root `tsconfig.json` that still extends the single `.stars/tsconfig.json` of compatibility versions below 6,
|
|
39
|
+
* a file `stars prepare` no longer writes from version 6 on. A project moving to 6 updates it once.
|
|
40
|
+
*/
|
|
41
|
+
async function findLegacyRootTsconfig(config) {
|
|
42
|
+
if (config.future.compatibilityVersion < SPLIT_TSCONFIG_VERSION) return null;
|
|
43
|
+
const path = join(config.root, "tsconfig.json");
|
|
44
|
+
const content = await readFile(path, "utf-8").catch(() => null);
|
|
45
|
+
if (content === null || !/["']extends["']\s*:\s*(?:\[[^\]]*)?["']\.\/\.stars\/tsconfig\.json["']/.test(content)) return null;
|
|
46
|
+
return cliDiagnostics.TSCONFIG_LEGACY_EXTENDS({ file: relative(config.root, path) || "tsconfig.json" });
|
|
47
|
+
}
|
|
48
|
+
async function emit(config, name, compilerConfig, check) {
|
|
49
|
+
const path = join(config.root, ".stars", name === "tsconfig" ? "tsconfig.json" : `tsconfig.${name}.json`);
|
|
50
|
+
const content = `${JSON.stringify(compilerConfig, null, 2)}\n`;
|
|
51
|
+
if (check) return {
|
|
52
|
+
name,
|
|
53
|
+
path,
|
|
54
|
+
status: await readFile(path, "utf-8").catch(() => null) === content ? "up-to-date" : "outdated"
|
|
55
|
+
};
|
|
56
|
+
await mkdir(dirname(path), { recursive: true });
|
|
57
|
+
await writeFile(path, content);
|
|
58
|
+
return {
|
|
59
|
+
name,
|
|
60
|
+
path,
|
|
61
|
+
status: "written"
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** What `.stars/tsconfig.json` has always been: the bot sources, with the aliases and the bundler's compiler options. */
|
|
65
|
+
function appTsconfig(config, name) {
|
|
66
|
+
const path = join(config.root, ".stars", name === "tsconfig" ? "tsconfig.json" : `tsconfig.${name}.json`);
|
|
18
67
|
const fromGenerated = (target) => `./${relative(dirname(path), target).replaceAll("\\", "/")}`;
|
|
19
68
|
const paths = {};
|
|
20
69
|
if (config.build.tool === "tsdown") {
|
|
@@ -48,7 +97,7 @@ async function prepareTsconfig(config, check = false) {
|
|
|
48
97
|
paths[`${alias}/*`] = [`${resolved}/*`];
|
|
49
98
|
}
|
|
50
99
|
}
|
|
51
|
-
|
|
100
|
+
return {
|
|
52
101
|
compilerOptions: {
|
|
53
102
|
...sapphireOptions,
|
|
54
103
|
...config.build.tool === "tsdown" || config.build.tool === "vite" ? {
|
|
@@ -66,21 +115,44 @@ async function prepareTsconfig(config, check = false) {
|
|
|
66
115
|
target: "ES2022",
|
|
67
116
|
forceConsistentCasingInFileNames: true,
|
|
68
117
|
skipLibCheck: true,
|
|
69
|
-
tsBuildInfoFile: "./tsconfig.tsbuildinfo"
|
|
118
|
+
tsBuildInfoFile: name === "tsconfig" ? "./tsconfig.tsbuildinfo" : `./tsconfig.${name}.tsbuildinfo`,
|
|
70
119
|
paths
|
|
71
120
|
},
|
|
72
121
|
include: [`${fromGenerated(dirname(config.entry))}/**/*`, ...config.imports.enabled ? [fromGenerated(config.imports.dts)] : []],
|
|
73
122
|
exclude: [fromGenerated(join(config.root, "node_modules")), fromGenerated(config.build.outDir)]
|
|
74
|
-
}, null, 2)}\n`;
|
|
75
|
-
if (check) return {
|
|
76
|
-
path,
|
|
77
|
-
status: await readFile(path, "utf-8").catch(() => null) === content ? "up-to-date" : "outdated"
|
|
78
123
|
};
|
|
79
|
-
|
|
80
|
-
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The root files that run in Node: `stars.config.*`, `vitest.config.*`, `scripts/**` and the like. No DOM, no
|
|
127
|
+
* bundler resolution, no aliases or auto imports (none of it applies to a file Node loads itself), and no decorators.
|
|
128
|
+
*/
|
|
129
|
+
function nodeTsconfig(config) {
|
|
130
|
+
const path = join(config.root, ".stars", "tsconfig.node.json");
|
|
131
|
+
const fromGenerated = (target) => `./${relative(dirname(path), target).replaceAll("\\", "/")}`;
|
|
132
|
+
const source = dirname(config.entry);
|
|
133
|
+
const include = NODE_PROJECT_FILES.map((pattern) => fromGenerated(join(config.root, pattern)));
|
|
134
|
+
if (config.configFile !== null) include.push(fromGenerated(config.configFile));
|
|
81
135
|
return {
|
|
82
|
-
|
|
83
|
-
|
|
136
|
+
compilerOptions: {
|
|
137
|
+
...strictOptions,
|
|
138
|
+
target: "ES2022",
|
|
139
|
+
module: "NodeNext",
|
|
140
|
+
moduleResolution: "NodeNext",
|
|
141
|
+
moduleDetection: "force",
|
|
142
|
+
lib: ["ESNext"],
|
|
143
|
+
types: ["node"],
|
|
144
|
+
allowJs: true,
|
|
145
|
+
noEmit: true,
|
|
146
|
+
forceConsistentCasingInFileNames: true,
|
|
147
|
+
skipLibCheck: true,
|
|
148
|
+
tsBuildInfoFile: "./tsconfig.node.tsbuildinfo"
|
|
149
|
+
},
|
|
150
|
+
include: [...new Set(include)],
|
|
151
|
+
exclude: [
|
|
152
|
+
fromGenerated(join(config.root, "node_modules")),
|
|
153
|
+
fromGenerated(config.build.outDir),
|
|
154
|
+
...source === config.root ? [] : [fromGenerated(source)]
|
|
155
|
+
]
|
|
84
156
|
};
|
|
85
157
|
}
|
|
86
158
|
|
|
@@ -120,10 +192,11 @@ async function prepareAutoImports(config, check = false) {
|
|
|
120
192
|
/** Prepares TypeScript configuration and auto import declarations before building. */
|
|
121
193
|
async function prepareProject(config, hooks, check = false) {
|
|
122
194
|
await hooks?.callHook("prepare:before", config);
|
|
123
|
-
const
|
|
195
|
+
const tsconfigs = await prepareTsconfig(config, check);
|
|
124
196
|
const result = {
|
|
125
197
|
...await prepareAutoImports(config, check),
|
|
126
|
-
tsconfig,
|
|
198
|
+
tsconfig: tsconfigs[0],
|
|
199
|
+
tsconfigs,
|
|
127
200
|
modules: await prepareModulesPreload(config, check)
|
|
128
201
|
};
|
|
129
202
|
await hooks?.callHook("prepare:done", config, {
|
|
@@ -133,15 +206,17 @@ async function prepareProject(config, hooks, check = false) {
|
|
|
133
206
|
return result;
|
|
134
207
|
}
|
|
135
208
|
/**
|
|
136
|
-
* Prints the non-fatal configuration diagnostics (e.g. an end-of-life compatibility version), and, unless `production`
|
|
209
|
+
* Prints the non-fatal configuration diagnostics (e.g. an end-of-life compatibility version, or a root `tsconfig.json` still extending `.stars/tsconfig.json`), and, unless `production`
|
|
137
210
|
* is `false` (`stars dev`, which does that itself), what a production start of the project still needs to do.
|
|
138
211
|
*/
|
|
139
212
|
async function reportWarnings(config, write, options = {}) {
|
|
140
213
|
for (const warning of config.warnings) write(await formatError(warning));
|
|
214
|
+
const legacyTsconfig = await findLegacyRootTsconfig(config);
|
|
215
|
+
if (legacyTsconfig) write(await formatError(legacyTsconfig));
|
|
141
216
|
const preload = options.production === false ? null : modulesPreloadWarning(config);
|
|
142
217
|
if (preload) write(await formatError(preload));
|
|
143
218
|
}
|
|
144
219
|
|
|
145
220
|
//#endregion
|
|
146
|
-
export { reportWarnings as n, prepareProject as t };
|
|
147
|
-
//# sourceMappingURL=_shared-
|
|
221
|
+
export { reportWarnings as n, findLegacyRootTsconfig as r, prepareProject as t };
|
|
222
|
+
//# sourceMappingURL=_shared-CyzTojMA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_shared-CyzTojMA.js","names":[],"sources":["../src/utils/tsconfig.ts","../src/commands/_shared.ts"],"sourcesContent":["import { SPLIT_TSCONFIG_VERSION, type ResolvedStarsConfig } from '@wolfstar/schema';\nimport { createRequire } from 'node:module';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport type { Diagnostic } from 'nostics';\nimport { cliDiagnostics } from './diagnostics.js';\n\nconst require = createRequire(import.meta.url);\nconst presetOptions = (ids: string[]) =>\n\tObject.assign({}, ...ids.map((id) => (require(id) as { compilerOptions: Record<string, unknown> }).compilerOptions)) as Record<string, unknown>;\n// Strictness belongs to every project; the decorator options only to the code the bundler compiles.\nconst strictOptions = presetOptions(['@sapphire/ts-config', '@sapphire/ts-config/extra-strict']);\nconst sapphireOptions = presetOptions(['@sapphire/ts-config', '@sapphire/ts-config/extra-strict', '@sapphire/ts-config/decorators']);\n\nexport type TsconfigName = 'tsconfig' | 'app' | 'node';\n\nexport interface TsconfigStatus {\n\t/** `tsconfig` is the single file of compatibility versions below 6, `app` and `node` the split ones. */\n\tname: TsconfigName;\n\tpath: string;\n\tstatus: 'written' | 'up-to-date' | 'outdated';\n}\n\n/** The root config files that run in Node and never go through the bundler. */\nconst NODE_PROJECT_FILES = ['stars.config.*', 'vitest.config.*', 'tsdown.config.*', 'vite.config.*', 'scripts/**/*'] as const;\n\n/**\n * Generates the TypeScript configuration without changing the project's own tsconfig: one extendable\n * `.stars/tsconfig.json` below compatibility version 6, and from 6 the `.stars/tsconfig.app.json` (the bot sources) and\n * `.stars/tsconfig.node.json` (the root files that run in Node) a solution-style `tsconfig.json` references.\n * Returns one status per file.\n */\nexport async function prepareTsconfig(config: ResolvedStarsConfig, check = false): Promise<TsconfigStatus[]> {\n\tif (config.future.compatibilityVersion < SPLIT_TSCONFIG_VERSION) {\n\t\treturn [await emit(config, 'tsconfig', appTsconfig(config, 'tsconfig'), check)];\n\t}\n\n\t// Written for every build tool: the node project is about files Node runs whatever builds the bot. A `tsc` or `none`\n\t// project keeps its own tsconfig for the bot sources, so for it the app file is generated but nothing references it.\n\treturn [await emit(config, 'app', appTsconfig(config, 'app'), check), await emit(config, 'node', nodeTsconfig(config), check)];\n}\n\n/**\n * Reports a root `tsconfig.json` that still extends the single `.stars/tsconfig.json` of compatibility versions below 6,\n * a file `stars prepare` no longer writes from version 6 on. A project moving to 6 updates it once.\n */\nexport async function findLegacyRootTsconfig(config: ResolvedStarsConfig): Promise<Diagnostic | null> {\n\tif (config.future.compatibilityVersion < SPLIT_TSCONFIG_VERSION) return null;\n\n\tconst path = join(config.root, 'tsconfig.json');\n\tconst content = await readFile(path, 'utf-8').catch(() => null);\n\tif (content === null || !/[\"']extends[\"']\\s*:\\s*(?:\\[[^\\]]*)?[\"']\\.\\/\\.stars\\/tsconfig\\.json[\"']/.test(content)) return null;\n\n\treturn cliDiagnostics.TSCONFIG_LEGACY_EXTENDS({ file: relative(config.root, path) || 'tsconfig.json' });\n}\n\nasync function emit(config: ResolvedStarsConfig, name: TsconfigName, compilerConfig: object, check: boolean): Promise<TsconfigStatus> {\n\tconst path = join(config.root, '.stars', name === 'tsconfig' ? 'tsconfig.json' : `tsconfig.${name}.json`);\n\tconst content = `${JSON.stringify(compilerConfig, null, 2)}\\n`;\n\tif (check) {\n\t\tconst existing = await readFile(path, 'utf-8').catch(() => null);\n\t\treturn { name, path, status: existing === content ? 'up-to-date' : 'outdated' };\n\t}\n\tawait mkdir(dirname(path), { recursive: true });\n\tawait writeFile(path, content);\n\treturn { name, path, status: 'written' };\n}\n\n/** What `.stars/tsconfig.json` has always been: the bot sources, with the aliases and the bundler's compiler options. */\nfunction appTsconfig(config: ResolvedStarsConfig, name: TsconfigName) {\n\tconst path = join(config.root, '.stars', name === 'tsconfig' ? 'tsconfig.json' : `tsconfig.${name}.json`);\n\tconst fromGenerated = (target: string) => `./${relative(dirname(path), target).replaceAll('\\\\', '/')}`;\n\tconst paths: Record<string, string[]> = {};\n\tif (config.build.tool === 'tsdown') {\n\t\tconst source = dirname(config.entry);\n\t\tconst defaults = config.build.configFile === null ? { '~': source, '@': source, '~~': config.root, '@@': config.root } : {};\n\t\tconst aliases = { ...defaults, ...(config.tsdown.alias as Record<string, unknown> | undefined) };\n\t\tfor (const [alias, target] of Object.entries(aliases)) {\n\t\t\t// Module redirects belong to the bundler; only filesystem targets become TypeScript paths.\n\t\t\tif (typeof target !== 'string' || (!isAbsolute(target) && !/^\\.\\.?[/\\\\]/.test(target))) continue;\n\t\t\tconst resolved = fromGenerated(resolve(config.root, target));\n\t\t\tpaths[alias] = [resolved];\n\t\t\tpaths[`${alias}/*`] = [`${resolved}/*`];\n\t\t}\n\t} else if (config.experimental.enableNitro) {\n\t\t// `NitroBuilder` turns on Vite's native `resolve.tsconfigPaths` (see https://nitro.build/examples/import-alias),\n\t\t// which reads these `paths` straight out of the generated tsconfig — no `tsdown.alias` equivalent to merge\n\t\t// with, `vite`/Nitro projects only have this file's own `compilerOptions.paths` to extend or replace.\n\t\tconst source = dirname(config.entry);\n\t\tconst defaults = { '~': source, '@': source, '~~': config.root, '@@': config.root };\n\t\tfor (const [alias, target] of Object.entries(defaults)) {\n\t\t\tconst resolved = fromGenerated(resolve(config.root, target));\n\t\t\tpaths[alias] = [resolved];\n\t\t\tpaths[`${alias}/*`] = [`${resolved}/*`];\n\t\t}\n\t}\n\treturn {\n\t\tcompilerOptions: {\n\t\t\t...sapphireOptions,\n\t\t\t// Bundlers emit the application; TypeScript only checks it. Keep Node16 emit for tsc.\n\t\t\t...(config.build.tool === 'tsdown' || config.build.tool === 'vite'\n\t\t\t\t? {\n\t\t\t\t\t\tmodule: 'ESNext',\n\t\t\t\t\t\tmoduleResolution: 'Bundler',\n\t\t\t\t\t\tmoduleDetection: 'force',\n\t\t\t\t\t\tisolatedModules: true,\n\t\t\t\t\t\tverbatimModuleSyntax: true,\n\t\t\t\t\t\tallowJs: true,\n\t\t\t\t\t\tallowImportingTsExtensions: true,\n\t\t\t\t\t\tresolvePackageJsonImports: true,\n\t\t\t\t\t\tlib: ['ESNext', 'DOM'],\n\t\t\t\t\t\tnoEmit: true\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\ttarget: 'ES2022',\n\t\t\tforceConsistentCasingInFileNames: true,\n\t\t\tskipLibCheck: true,\n\t\t\ttsBuildInfoFile: name === 'tsconfig' ? './tsconfig.tsbuildinfo' : `./tsconfig.${name}.tsbuildinfo`,\n\t\t\tpaths\n\t\t},\n\t\tinclude: [`${fromGenerated(dirname(config.entry))}/**/*`, ...(config.imports.enabled ? [fromGenerated(config.imports.dts)] : [])],\n\t\texclude: [fromGenerated(join(config.root, 'node_modules')), fromGenerated(config.build.outDir)]\n\t};\n}\n\n/**\n * The root files that run in Node: `stars.config.*`, `vitest.config.*`, `scripts/**` and the like. No DOM, no\n * bundler resolution, no aliases or auto imports (none of it applies to a file Node loads itself), and no decorators.\n */\nfunction nodeTsconfig(config: ResolvedStarsConfig) {\n\tconst path = join(config.root, '.stars', 'tsconfig.node.json');\n\tconst fromGenerated = (target: string) => `./${relative(dirname(path), target).replaceAll('\\\\', '/')}`;\n\tconst source = dirname(config.entry);\n\tconst include = NODE_PROJECT_FILES.map((pattern) => fromGenerated(join(config.root, pattern)));\n\t// A custom `--config` file name is not covered by the globs.\n\tif (config.configFile !== null) include.push(fromGenerated(config.configFile));\n\n\treturn {\n\t\tcompilerOptions: {\n\t\t\t...strictOptions,\n\t\t\ttarget: 'ES2022',\n\t\t\tmodule: 'NodeNext',\n\t\t\tmoduleResolution: 'NodeNext',\n\t\t\tmoduleDetection: 'force',\n\t\t\tlib: ['ESNext'],\n\t\t\ttypes: ['node'],\n\t\t\tallowJs: true,\n\t\t\tnoEmit: true,\n\t\t\tforceConsistentCasingInFileNames: true,\n\t\t\tskipLibCheck: true,\n\t\t\ttsBuildInfoFile: './tsconfig.node.tsbuildinfo'\n\t\t},\n\t\tinclude: [...new Set(include)],\n\t\t// The two projects must not overlap: the bot sources belong to the app one. An entry in the project root has\n\t\t// no directory of its own to leave out.\n\t\texclude: [\n\t\t\tfromGenerated(join(config.root, 'node_modules')),\n\t\t\tfromGenerated(config.build.outDir),\n\t\t\t...(source === config.root ? [] : [fromGenerated(source)])\n\t\t]\n\t};\n}\n","import type { ResolvedStarsConfig } from '@wolfstar/schema';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { loadAutoImportsModule } from '../utils/framework-auto-imports.js';\nimport { formatError } from '../utils/errors.js';\nimport type { StarsHookable } from '../utils/hooks.js';\nimport { modulesPreloadWarning, prepareModulesPreload } from '../utils/modules.js';\nimport { findLegacyRootTsconfig, prepareTsconfig } from '../utils/tsconfig.js';\n\nexport type PrepareResult =\n\t| { enabled: false; dts: null; status: null }\n\t| { enabled: true; dts: string; status: 'written' | 'up-to-date' | 'outdated' };\n\n/**\n * Regenerates `imports.dts` unconditionally, used by `stars dev`/`stars build` before the first build so the\n * declaration file exists (and is current) even when the user never ran `stars prepare` themselves.\n */\nexport async function prepareAutoImports(config: ResolvedStarsConfig, check = false): Promise<PrepareResult> {\n\tif (!config.imports.enabled) return { enabled: false, dts: null, status: null };\n\n\tconst { dirs, presets, exclude, dts } = config.imports;\n\tconst { generateAutoImportsDts } = await loadAutoImportsModule(config.root);\n\tconst content = await generateAutoImportsDts({ root: config.root, dirs, presets, exclude });\n\n\tif (check) {\n\t\tconst existing = await readFile(dts, 'utf-8').catch(() => null);\n\t\treturn { enabled: true, dts, status: existing === content ? 'up-to-date' : 'outdated' };\n\t}\n\n\tawait mkdir(dirname(dts), { recursive: true });\n\tawait writeFile(dts, content);\n\treturn { enabled: true, dts, status: 'written' };\n}\n\n/** Prepares TypeScript configuration and auto import declarations before building. */\nexport async function prepareProject(config: ResolvedStarsConfig, hooks?: StarsHookable, check = false) {\n\tawait hooks?.callHook('prepare:before', config);\n\tconst tsconfigs = await prepareTsconfig(config, check);\n\tconst result = {\n\t\t...(await prepareAutoImports(config, check)),\n\t\t// The app config from version 6, the single file below it; `tsconfigs` has every generated file.\n\t\ttsconfig: tsconfigs[0]!,\n\t\ttsconfigs,\n\t\tmodules: await prepareModulesPreload(config, check)\n\t};\n\tawait hooks?.callHook('prepare:done', config, { dts: result.dts, status: result.status });\n\treturn result;\n}\n\n/**\n * Prints the non-fatal configuration diagnostics (e.g. an end-of-life compatibility version, or a root `tsconfig.json` still extending `.stars/tsconfig.json`), and, unless `production`\n * is `false` (`stars dev`, which does that itself), what a production start of the project still needs to do.\n */\nexport async function reportWarnings(\n\tconfig: ResolvedStarsConfig,\n\twrite: (text: string) => void,\n\toptions: { production?: boolean } = {}\n): Promise<void> {\n\tfor (const warning of config.warnings) write(await formatError(warning));\n\n\tconst legacyTsconfig = await findLegacyRootTsconfig(config);\n\tif (legacyTsconfig) write(await formatError(legacyTsconfig));\n\n\tconst preload = options.production === false ? null : modulesPreloadWarning(config);\n\tif (preload) write(await formatError(preload));\n}\n"],"mappings":"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"}
|
|
@@ -1,240 +1,7 @@
|
|
|
1
|
-
import { t as findInstalledVersion } from "./project-
|
|
2
|
-
import { i as readProjectEnv } from "./hooks-Cy6F2bhH.js";
|
|
1
|
+
import { t as findInstalledVersion } from "./project-DBqQMFCk.js";
|
|
3
2
|
import { pathToFileURL } from "node:url";
|
|
4
|
-
import { EventEmitter } from "node:events";
|
|
5
3
|
import { resolveSync } from "mlly";
|
|
6
|
-
import { startTunnel } from "untun";
|
|
7
4
|
|
|
8
|
-
//#region src/dev/tunnel.ts
|
|
9
|
-
const SIGNALS = [
|
|
10
|
-
"SIGINT",
|
|
11
|
-
"SIGTERM",
|
|
12
|
-
"SIGHUP"
|
|
13
|
-
];
|
|
14
|
-
function isCloudflaredExit(reason) {
|
|
15
|
-
return reason instanceof Error && reason.message.startsWith("cloudflared exited (");
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Exposes the bot's interactions endpoint publicly while `stars dev` runs.
|
|
19
|
-
*
|
|
20
|
-
* `dev.tunnel: true` opens a `cloudflared` quick tunnel via `untun` (it changes on every run); a configured https
|
|
21
|
-
* URL is only probed, since the user already serves it. Writing the URL to the Discord application is opt-in
|
|
22
|
-
* through `dev.tunnel.updateEndpoint`, because it edits a live application.
|
|
23
|
-
*/
|
|
24
|
-
var Tunnel = class extends EventEmitter {
|
|
25
|
-
config;
|
|
26
|
-
#startTunnel;
|
|
27
|
-
#tunnel = null;
|
|
28
|
-
#state = "off";
|
|
29
|
-
#url = null;
|
|
30
|
-
#wanted = false;
|
|
31
|
-
#rejectionGuard = null;
|
|
32
|
-
constructor(config, options = {}) {
|
|
33
|
-
super();
|
|
34
|
-
this.config = config;
|
|
35
|
-
this.#startTunnel = options.startTunnel ?? startTunnel;
|
|
36
|
-
}
|
|
37
|
-
get state() {
|
|
38
|
-
return this.#state;
|
|
39
|
-
}
|
|
40
|
-
/** The public URL of the interactions endpoint, `null` until the tunnel is up. */
|
|
41
|
-
get url() {
|
|
42
|
-
return this.#url;
|
|
43
|
-
}
|
|
44
|
-
async start(forceQuick = false) {
|
|
45
|
-
if (this.#state === "starting" || this.#state === "up") return;
|
|
46
|
-
const configured = this.config.dev.tunnel;
|
|
47
|
-
const tunnel = forceQuick && configured.mode === "off" ? {
|
|
48
|
-
mode: "quick",
|
|
49
|
-
path: "/",
|
|
50
|
-
updateEndpoint: false
|
|
51
|
-
} : configured;
|
|
52
|
-
if (tunnel.mode === "off") return;
|
|
53
|
-
this.#wanted = true;
|
|
54
|
-
this.#setState("starting", null);
|
|
55
|
-
const url = tunnel.mode === "url" ? await this.#useConfiguredUrl(tunnel) : await this.#openQuickTunnel();
|
|
56
|
-
if (!url || !this.#wanted) return;
|
|
57
|
-
this.#setState("up", endpointUrl(url, tunnel.path));
|
|
58
|
-
this.emit("log", "success", `Tunnel ready at ${this.#url}`);
|
|
59
|
-
if (tunnel.updateEndpoint) await this.#updateInteractionsEndpoint(this.#url);
|
|
60
|
-
}
|
|
61
|
-
/** Toggles the configured tunnel, opening a quick tunnel when the project did not configure one. */
|
|
62
|
-
toggle() {
|
|
63
|
-
return this.#state === "starting" || this.#state === "up" ? this.close() : this.start(true);
|
|
64
|
-
}
|
|
65
|
-
async close() {
|
|
66
|
-
this.#wanted = false;
|
|
67
|
-
const tunnel = this.#tunnel;
|
|
68
|
-
this.#tunnel = null;
|
|
69
|
-
this.#setState("off", null);
|
|
70
|
-
if (!tunnel) return;
|
|
71
|
-
try {
|
|
72
|
-
await tunnel.close();
|
|
73
|
-
} finally {
|
|
74
|
-
this.#releaseRejectionGuard();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
async #useConfiguredUrl(tunnel) {
|
|
78
|
-
if (!await probe(endpointUrl(tunnel.url, tunnel.path))) this.emit("log", "warn", `${tunnel.url} did not answer; make sure it forwards to ${this.config.dev.url ?? "the bot"}`);
|
|
79
|
-
return tunnel.url;
|
|
80
|
-
}
|
|
81
|
-
async #openQuickTunnel() {
|
|
82
|
-
const target = this.config.dev.url;
|
|
83
|
-
if (!target) {
|
|
84
|
-
this.emit("log", "error", "A quick tunnel needs `dev.url` to know what to forward to");
|
|
85
|
-
this.#setState("failed", null);
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
this.emit("log", "info", "Opening a cloudflared quick tunnel…");
|
|
89
|
-
this.#installRejectionGuard();
|
|
90
|
-
try {
|
|
91
|
-
const tunnel = await this.#startWithoutSignalHandlers(target);
|
|
92
|
-
if (!this.#wanted) {
|
|
93
|
-
await tunnel?.close();
|
|
94
|
-
this.#releaseRejectionGuard();
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
if (!tunnel) {
|
|
98
|
-
this.emit("log", "error", "cloudflared setup was cancelled");
|
|
99
|
-
this.#setState("failed", null);
|
|
100
|
-
this.#releaseRejectionGuard();
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
this.#tunnel = tunnel;
|
|
104
|
-
return await tunnel.getURL();
|
|
105
|
-
} catch (error) {
|
|
106
|
-
this.#releaseRejectionGuard();
|
|
107
|
-
if (!this.#wanted) return null;
|
|
108
|
-
this.emit("log", "error", `cloudflared failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
109
|
-
this.#setState("failed", null);
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* `untun` adds its own `SIGINT`/`SIGTERM`/`SIGHUP` listeners that call `process.exit()` once cloudflared is closed,
|
|
115
|
-
* which races `stars dev`'s graceful shutdown (it closes the tunnel through {@link close} anyway), so they are removed.
|
|
116
|
-
*/
|
|
117
|
-
async #startWithoutSignalHandlers(target) {
|
|
118
|
-
const before = SIGNALS.map((signal) => new Set(process.listeners(signal)));
|
|
119
|
-
try {
|
|
120
|
-
return await this.#startTunnel({
|
|
121
|
-
url: target,
|
|
122
|
-
acceptCloudflareNotice: true
|
|
123
|
-
});
|
|
124
|
-
} finally {
|
|
125
|
-
SIGNALS.forEach((signal, index) => {
|
|
126
|
-
for (const listener of process.listeners(signal)) if (!before[index].has(listener)) process.off(signal, listener);
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* `untun` rejects a promise per connection when cloudflared exits and never handles it (and its location pattern
|
|
132
|
-
* does not match lowercase codes such as `mxp03`, so that promise is never settled before), which Node reports as an
|
|
133
|
-
* unhandled rejection that ends the process, whenever the tunnel closes. The guard swallows that one error for as
|
|
134
|
-
* long as this tunnel exists; anything else it sees is thrown again, as if it was not installed.
|
|
135
|
-
*/
|
|
136
|
-
#installRejectionGuard() {
|
|
137
|
-
if (this.#rejectionGuard) return;
|
|
138
|
-
const guard = (reason) => {
|
|
139
|
-
if (!isCloudflaredExit(reason)) {
|
|
140
|
-
if (process.listeners("unhandledRejection").length === 1) throw reason;
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (this.#state !== "up" || !this.#wanted) return;
|
|
144
|
-
this.#tunnel = null;
|
|
145
|
-
this.emit("log", "error", `cloudflared stopped unexpectedly: ${reason.message}`);
|
|
146
|
-
this.#setState("failed", null);
|
|
147
|
-
this.#releaseRejectionGuard();
|
|
148
|
-
};
|
|
149
|
-
this.#rejectionGuard = guard;
|
|
150
|
-
process.on("unhandledRejection", guard);
|
|
151
|
-
}
|
|
152
|
-
/** Node reports an unhandled rejection after the microtasks that follow it, so the guard has to outlive them. */
|
|
153
|
-
#releaseRejectionGuard() {
|
|
154
|
-
const guard = this.#rejectionGuard;
|
|
155
|
-
this.#rejectionGuard = null;
|
|
156
|
-
if (guard) setImmediate(() => process.off("unhandledRejection", guard));
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Points the Discord application's interactions endpoint at the tunnel. Discord validates the endpoint with a
|
|
160
|
-
* signed ping before accepting it, so the bot has to be up already — this runs after the first successful build.
|
|
161
|
-
*/
|
|
162
|
-
async #updateInteractionsEndpoint(url) {
|
|
163
|
-
const credentials = readDiscordCredentials(this.config);
|
|
164
|
-
if (!credentials) {
|
|
165
|
-
this.emit("log", "warn", "Skipped the interactions endpoint update: DISCORD_TOKEN is not set");
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
try {
|
|
169
|
-
const response = await fetch("https://discord.com/api/v10/applications/@me", {
|
|
170
|
-
method: "PATCH",
|
|
171
|
-
headers: {
|
|
172
|
-
authorization: `Bot ${credentials.token}`,
|
|
173
|
-
"content-type": "application/json"
|
|
174
|
-
},
|
|
175
|
-
body: JSON.stringify({ interactions_endpoint_url: url }),
|
|
176
|
-
signal: AbortSignal.timeout(1e4)
|
|
177
|
-
});
|
|
178
|
-
if (response.ok) this.emit("log", "success", `Interactions endpoint set to ${url}`);
|
|
179
|
-
else this.emit("log", "error", `Discord rejected the interactions endpoint (${response.status}): ${(await response.text()).slice(0, 300)}`);
|
|
180
|
-
} catch (error) {
|
|
181
|
-
this.emit("log", "error", `Failed to update the interactions endpoint: ${error instanceof Error ? error.message : String(error)}`);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
#setState(state, url) {
|
|
185
|
-
this.#state = state;
|
|
186
|
-
this.#url = url;
|
|
187
|
-
this.emit("state", state, url);
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
/**
|
|
191
|
-
* Joins the tunnel's origin with the path the interactions endpoint is served on.
|
|
192
|
-
*/
|
|
193
|
-
function endpointUrl(origin, path) {
|
|
194
|
-
return path === "/" ? origin : new URL(path, origin.endsWith("/") ? origin : `${origin}/`).href;
|
|
195
|
-
}
|
|
196
|
-
async function probe(url) {
|
|
197
|
-
try {
|
|
198
|
-
return (await fetch(url, {
|
|
199
|
-
method: "HEAD",
|
|
200
|
-
signal: AbortSignal.timeout(5e3)
|
|
201
|
-
})).status < 500;
|
|
202
|
-
} catch {
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Reads the Discord credentials the CLI needs from the environment, falling back to the project's `.env` files the
|
|
208
|
-
* way the bot itself does once it starts.
|
|
209
|
-
*/
|
|
210
|
-
function readDiscordCredentials(config, env = process.env) {
|
|
211
|
-
const fromFiles = readProjectEnvFilesCached(config);
|
|
212
|
-
const read = (...keys) => {
|
|
213
|
-
for (const key of keys) {
|
|
214
|
-
const value = config.dev.env[key] ?? env[key] ?? fromFiles[key];
|
|
215
|
-
if (value) return value;
|
|
216
|
-
}
|
|
217
|
-
return null;
|
|
218
|
-
};
|
|
219
|
-
const token = read("DISCORD_TOKEN", "TOKEN");
|
|
220
|
-
if (!token) return null;
|
|
221
|
-
return {
|
|
222
|
-
token,
|
|
223
|
-
applicationId: read("DISCORD_APPLICATION_ID", "APPLICATION_ID", "DISCORD_CLIENT_ID", "CLIENT_ID")
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
let envFileCache = null;
|
|
227
|
-
/** The variables the bot loads (see {@link readProjectEnv}), once per project and `env` options. */
|
|
228
|
-
function readProjectEnvFilesCached(config) {
|
|
229
|
-
const key = JSON.stringify([config.root, config.env.options]);
|
|
230
|
-
if (envFileCache?.key !== key) envFileCache = {
|
|
231
|
-
key,
|
|
232
|
-
values: readProjectEnv(config)
|
|
233
|
-
};
|
|
234
|
-
return envFileCache.values;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
//#endregion
|
|
238
5
|
//#region src/dev/bridge.ts
|
|
239
6
|
/** Marks a message of the bridge, so a bot using the IPC channel for something else is left alone. */
|
|
240
7
|
const BRIDGE_SOURCE = "stars:bridge";
|
|
@@ -467,5 +234,5 @@ function isAtLeast(version, minimum) {
|
|
|
467
234
|
}
|
|
468
235
|
|
|
469
236
|
//#endregion
|
|
470
|
-
export { isAtLeast as a,
|
|
471
|
-
//# sourceMappingURL=bridge-
|
|
237
|
+
export { isAtLeast as a, bridgeImportArgs as i, CLI_SOURCE as n, parseBridgeMessage as o, DUMP_ENV as r, BRIDGE_FRAMEWORK_VERSION as t };
|
|
238
|
+
//# sourceMappingURL=bridge-Cbm0jX9h.js.map
|