@whanext/core 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +46 -44
- package/dist/index.d.ts +33 -19
- package/dist/index.js +89 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
## 0.11.0 - Command discovery
|
|
5
|
+
|
|
6
|
+
- `CommandRouter.load()` carrega diretórios de comandos diretamente pelo router.
|
|
7
|
+
- `loadCommands()` agora aceita `URL` além de caminhos em string.
|
|
8
|
+
- Autoload reconhece `.ts`, `.mts` e `.cts` por padrão, além de JavaScript.
|
|
9
|
+
- `CommandContext` agora expõe `ctx.commands` (catálogo somente-leitura) e `ctx.prefix`.
|
|
10
|
+
- Menus e comandos de ajuda podem consultar o catálogo sem factory ou referência global ao app.
|
|
11
|
+
- Mantém compatibilidade com `defineCommand`, `defineCommands`, `defineSubcommand` e `defineCommandGroup`.
|
|
12
|
+
|
|
3
13
|
Todas as mudanças relevantes do projeto serão registradas neste arquivo.
|
|
4
14
|
|
|
5
15
|
## 0.10.0
|
package/README.md
CHANGED
|
@@ -566,81 +566,83 @@ Restrições disponíveis:
|
|
|
566
566
|
|
|
567
567
|
`ArgsParser` possui `string`, `number`, `boolean`, `enum`, `user`, `duration`, `peek`, `skip` e `rest`. `args.user()` retorna `User`, nunca uma string crua.
|
|
568
568
|
|
|
569
|
-
###
|
|
569
|
+
### Descoberta automática de comandos
|
|
570
570
|
|
|
571
|
-
|
|
571
|
+
A forma recomendada agora é deixar cada arquivo de comando autossuficiente e pedir ao próprio router para descobrir a árvore inteira. Não é necessário manter `index.ts` por pasta nem um arquivo central de imports:
|
|
572
572
|
|
|
573
573
|
```ts
|
|
574
|
-
|
|
574
|
+
const app = await create({ prefix: '&' });
|
|
575
575
|
|
|
576
|
-
await
|
|
576
|
+
await app.commands.load(new URL('./commands/', import.meta.url));
|
|
577
577
|
```
|
|
578
578
|
|
|
579
|
-
|
|
579
|
+
O mesmo código funciona em desenvolvimento TypeScript (com um runtime/loader como `tsx`) e depois do build: o loader reconhece `.ts`, `.mts`, `.cts`, `.js`, `.mjs` e `.cjs` por padrão. O diretório é percorrido recursivamente.
|
|
580
580
|
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
581
|
+
```text
|
|
582
|
+
src/commands/
|
|
583
|
+
├── admin/
|
|
584
|
+
│ ├── ban.ts
|
|
585
|
+
│ ├── mute.ts
|
|
586
|
+
│ └── warn.ts
|
|
587
|
+
├── group/
|
|
588
|
+
│ ├── access.ts
|
|
589
|
+
│ └── pin.ts
|
|
590
|
+
└── general/
|
|
591
|
+
├── menu.ts
|
|
592
|
+
└── profile.ts
|
|
590
593
|
```
|
|
591
594
|
|
|
592
|
-
|
|
595
|
+
Um arquivo pode exportar um comando:
|
|
593
596
|
|
|
594
597
|
```ts
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
description: 'Silencia um membro.',
|
|
601
|
-
async execute(message, args) {
|
|
602
|
-
// ...
|
|
603
|
-
},
|
|
604
|
-
});
|
|
605
|
-
|
|
606
|
-
const unmute = defineCommand({
|
|
607
|
-
name: 'unmute',
|
|
608
|
-
description: 'Remove o silêncio de um membro.',
|
|
609
|
-
async execute(message, args) {
|
|
610
|
-
// ...
|
|
598
|
+
export default defineCommand({
|
|
599
|
+
name: 'ping',
|
|
600
|
+
description: 'Responde pong.',
|
|
601
|
+
async execute(ctx) {
|
|
602
|
+
await ctx.reply('pong');
|
|
611
603
|
},
|
|
612
604
|
});
|
|
613
|
-
|
|
614
|
-
export default defineCommands(mute, unmute);
|
|
615
605
|
```
|
|
616
606
|
|
|
617
|
-
|
|
607
|
+
Ou vários comandos relacionados no mesmo módulo:
|
|
618
608
|
|
|
619
609
|
```ts
|
|
620
610
|
export const mute = defineCommand({ /* ... */ });
|
|
621
611
|
export const unmute = defineCommand({ /* ... */ });
|
|
622
612
|
```
|
|
623
613
|
|
|
624
|
-
|
|
614
|
+
Também é possível exportar uma coleção com `defineCommands(...)`. Exports auxiliares são ignorados quando o módulo contém pelo menos um comando válido, e o mesmo objeto não é registrado duas vezes.
|
|
625
615
|
|
|
626
|
-
|
|
616
|
+
Para menus dinâmicos, `CommandContext` expõe uma visão somente-leitura do catálogo e o prefixo atual:
|
|
627
617
|
|
|
628
618
|
```ts
|
|
629
|
-
|
|
619
|
+
export default defineCommand({
|
|
620
|
+
name: 'menu',
|
|
621
|
+
description: 'Mostra os comandos.',
|
|
622
|
+
async execute(ctx) {
|
|
623
|
+
const commands = ctx.commands.catalog({ category: 'administração' });
|
|
624
|
+
const lines = commands.map((command) =>
|
|
625
|
+
`${ctx.prefix}${command.path.join(' ')} — ${command.definition.description}`,
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
await ctx.reply(lines.join('\n'));
|
|
629
|
+
},
|
|
630
|
+
});
|
|
630
631
|
```
|
|
631
632
|
|
|
632
|
-
`
|
|
633
|
+
`ctx.commands` fornece `catalog()`, `categories()`, `find()`, `has()`, `size` e `prefix`; ele não expõe detalhes internos do provider.
|
|
633
634
|
|
|
634
|
-
|
|
635
|
+
Se for necessário controlar extensões ou recursão:
|
|
635
636
|
|
|
636
637
|
```ts
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
console.log(result.commands); // { name, filePath }[]
|
|
638
|
+
await app.commands.load(new URL('./commands/', import.meta.url), {
|
|
639
|
+
recursive: true,
|
|
640
|
+
extensions: ['.ts'],
|
|
641
|
+
});
|
|
642
642
|
```
|
|
643
643
|
|
|
644
|
+
`loadCommands(registrar, directory, options)` continua disponível como API de baixo nível para registradores customizados. O retorno contém arquivos carregados/ignorados e os comandos descobertos.
|
|
645
|
+
|
|
644
646
|
## Cache externo
|
|
645
647
|
|
|
646
648
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -438,6 +438,17 @@ declare class MessageService {
|
|
|
438
438
|
text(chatId: string, text: string, mentions?: MentionTarget[]): Promise<SentMessage>;
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
+
interface CommandCatalogView {
|
|
442
|
+
readonly prefix: string;
|
|
443
|
+
readonly size: number;
|
|
444
|
+
catalog(options?: {
|
|
445
|
+
category?: string;
|
|
446
|
+
includeHidden?: boolean;
|
|
447
|
+
}): readonly RegisteredCommand[];
|
|
448
|
+
categories(): readonly string[];
|
|
449
|
+
has(path: string): boolean;
|
|
450
|
+
find(path: string): RegisteredCommand | undefined;
|
|
451
|
+
}
|
|
441
452
|
interface CommandRuntimeServices {
|
|
442
453
|
messages: MessageService;
|
|
443
454
|
media: MediaService;
|
|
@@ -466,6 +477,8 @@ interface CommandContext<Schema extends CommandOptionSchema = CommandOptionSchem
|
|
|
466
477
|
readonly chat: CommandChatContext;
|
|
467
478
|
readonly group: CommandGroupContext | undefined;
|
|
468
479
|
readonly command: RegisteredCommand;
|
|
480
|
+
readonly commands: CommandCatalogView;
|
|
481
|
+
readonly prefix: string;
|
|
469
482
|
readonly options: ParsedCommandOptions<Schema>;
|
|
470
483
|
readonly args: ArgsParser;
|
|
471
484
|
readonly locale: string | undefined;
|
|
@@ -584,6 +597,24 @@ declare function defineCommandGroup<const Group extends CommandGroupDefinition>(
|
|
|
584
597
|
declare function defineCommands<const Commands extends readonly CommandDefinition[]>(...commands: Commands): Commands;
|
|
585
598
|
declare function isCommandGroup(definition: CommandDefinition): definition is CommandGroupDefinition;
|
|
586
599
|
|
|
600
|
+
interface CommandRegistrar {
|
|
601
|
+
command(definition: CommandDefinition): unknown;
|
|
602
|
+
}
|
|
603
|
+
interface LoadCommandsOptions {
|
|
604
|
+
extensions?: readonly string[];
|
|
605
|
+
recursive?: boolean;
|
|
606
|
+
}
|
|
607
|
+
interface LoadCommandsResult {
|
|
608
|
+
loaded: readonly string[];
|
|
609
|
+
skipped: readonly string[];
|
|
610
|
+
commands: readonly LoadedCommand[];
|
|
611
|
+
}
|
|
612
|
+
interface LoadedCommand {
|
|
613
|
+
name: string;
|
|
614
|
+
filePath: string;
|
|
615
|
+
}
|
|
616
|
+
declare function loadCommands(registrar: CommandRegistrar, dirPath: string | URL, options?: LoadCommandsOptions): Promise<LoadCommandsResult>;
|
|
617
|
+
|
|
587
618
|
interface RouterOptions {
|
|
588
619
|
prefix?: string;
|
|
589
620
|
onError?: (error: WhaNextError, message: Message) => void | Promise<void>;
|
|
@@ -606,6 +637,7 @@ declare class CommandRouter {
|
|
|
606
637
|
get prefix(): string;
|
|
607
638
|
get size(): number;
|
|
608
639
|
command(definition: CommandDefinition): this;
|
|
640
|
+
load(dirPath: string | URL, options?: LoadCommandsOptions): Promise<LoadCommandsResult>;
|
|
609
641
|
use(middleware: CommandMiddleware): this;
|
|
610
642
|
onError(handler: CommandErrorHandler): () => void;
|
|
611
643
|
catalog(options?: CommandCatalogOptions): readonly RegisteredCommand[];
|
|
@@ -749,24 +781,6 @@ declare class MemoryCache implements CacheStore {
|
|
|
749
781
|
stats(): Readonly<MemoryCacheStats>;
|
|
750
782
|
}
|
|
751
783
|
|
|
752
|
-
interface CommandRegistrar {
|
|
753
|
-
command(definition: CommandDefinition): unknown;
|
|
754
|
-
}
|
|
755
|
-
interface LoadCommandsOptions {
|
|
756
|
-
extensions?: readonly string[];
|
|
757
|
-
recursive?: boolean;
|
|
758
|
-
}
|
|
759
|
-
interface LoadCommandsResult {
|
|
760
|
-
loaded: readonly string[];
|
|
761
|
-
skipped: readonly string[];
|
|
762
|
-
commands: readonly LoadedCommand[];
|
|
763
|
-
}
|
|
764
|
-
interface LoadedCommand {
|
|
765
|
-
name: string;
|
|
766
|
-
filePath: string;
|
|
767
|
-
}
|
|
768
|
-
declare function loadCommands(registrar: CommandRegistrar, dirPath: string, options?: LoadCommandsOptions): Promise<LoadCommandsResult>;
|
|
769
|
-
|
|
770
784
|
declare class SqliteMuteStore implements MuteStore {
|
|
771
785
|
#private;
|
|
772
786
|
constructor(path?: string);
|
|
@@ -777,4 +791,4 @@ declare class SqliteMuteStore implements MuteStore {
|
|
|
777
791
|
close(): void;
|
|
778
792
|
}
|
|
779
793
|
|
|
780
|
-
export { type AddMuteOptions, type AddMuteResult, type AppEvents, type AppHealth, type AppHealthStatus, ArgsParser, type AudioContent, type BooleanOption, Browser, type CacheOptions, type CacheStore, type CallEvent, type CallStatus, type ChangeResult, type CommandCatalogOptions, type CommandChatContext, type CommandConcurrency, type CommandContext, type CommandCooldown, type CommandDefinition, type CommandErrorHandler, type CommandGroupContext, type CommandGroupDefinition, type CommandGuard, type CommandHelpOptions, type CommandHooks, type CommandLocalization, type CommandMetadata, type CommandMiddleware, type CommandOptionDefinition, type CommandOptionSchema, type CommandOptionValue, type CommandOptionValues, type CommandRegistrar, CommandRouter, type CommandRuntimeServices, type CommandScope, type ConcurrencyStrategy, type ConnectionState, type ConnectionUpdate, type CreateOptions, DeferredReply, type DownloadedMedia, type DurationOption, type EnumOption, type ExecutableCommandDefinition, type GroupAccess, type GroupAddressingMode, type GroupParticipant, type GroupParticipantAction, type GroupParticipantsChanged, type GroupRole, type GroupSnapshot, type GuardResult, type ImageContent, type InviteResult, type LoadCommandsOptions, type LoadCommandsResult, type LoadedCommand, type LogContext, type LogEntry, type LogFormat, type LogLevel, type LogWriter, Logger, type LoggerConfig, type LoggerOptions, type LoginOptions, type MediaKind, type MediaSource, type MemberActionState, MemoryCache, type MemoryCacheStats, type MentionTarget, type Message, type MessageContent, type MessageKey, type MessageMedia, type MuteChangeResult, type MuteEnforcement, type MuteOptions, type MuteRecord, MuteService, type MuteStore, type NumberOption, ParsedCommandOptions, type ParticipantUpdateResult, type PresenceState, type QuotedMessage, type ReconnectOptions, type RegisteredCommand, type RemoveMuteResult, type ReplyOptions, type RouterOptions, type SentMessage, SqliteMuteStore, type StickerContent, type StoredMute, type StringOption, type TextContent, User, type UserData, type UserOption, type VideoContent, WhaNextApp, WhaNextError, type WhaNextErrorCode, type WhatsAppProvider, create, defineCommand, defineCommandGroup, defineCommands, defineSubcommand, guards, isCommandGroup, loadCommands, option, toWhaNextError };
|
|
794
|
+
export { type AddMuteOptions, type AddMuteResult, type AppEvents, type AppHealth, type AppHealthStatus, ArgsParser, type AudioContent, type BooleanOption, Browser, type CacheOptions, type CacheStore, type CallEvent, type CallStatus, type ChangeResult, type CommandCatalogOptions, type CommandCatalogView, type CommandChatContext, type CommandConcurrency, type CommandContext, type CommandCooldown, type CommandDefinition, type CommandErrorHandler, type CommandGroupContext, type CommandGroupDefinition, type CommandGuard, type CommandHelpOptions, type CommandHooks, type CommandLocalization, type CommandMetadata, type CommandMiddleware, type CommandOptionDefinition, type CommandOptionSchema, type CommandOptionValue, type CommandOptionValues, type CommandRegistrar, CommandRouter, type CommandRuntimeServices, type CommandScope, type ConcurrencyStrategy, type ConnectionState, type ConnectionUpdate, type CreateOptions, DeferredReply, type DownloadedMedia, type DurationOption, type EnumOption, type ExecutableCommandDefinition, type GroupAccess, type GroupAddressingMode, type GroupParticipant, type GroupParticipantAction, type GroupParticipantsChanged, type GroupRole, type GroupSnapshot, type GuardResult, type ImageContent, type InviteResult, type LoadCommandsOptions, type LoadCommandsResult, type LoadedCommand, type LogContext, type LogEntry, type LogFormat, type LogLevel, type LogWriter, Logger, type LoggerConfig, type LoggerOptions, type LoginOptions, type MediaKind, type MediaSource, type MemberActionState, MemoryCache, type MemoryCacheStats, type MentionTarget, type Message, type MessageContent, type MessageKey, type MessageMedia, type MuteChangeResult, type MuteEnforcement, type MuteOptions, type MuteRecord, MuteService, type MuteStore, type NumberOption, ParsedCommandOptions, type ParticipantUpdateResult, type PresenceState, type QuotedMessage, type ReconnectOptions, type RegisteredCommand, type RemoveMuteResult, type ReplyOptions, type RouterOptions, type SentMessage, SqliteMuteStore, type StickerContent, type StoredMute, type StringOption, type TextContent, User, type UserData, type UserOption, type VideoContent, WhaNextApp, WhaNextError, type WhaNextErrorCode, type WhatsAppProvider, create, defineCommand, defineCommandGroup, defineCommands, defineSubcommand, guards, isCommandGroup, loadCommands, option, toWhaNextError };
|
package/dist/index.js
CHANGED
|
@@ -395,6 +395,8 @@ var CommandContextImplementation = class {
|
|
|
395
395
|
chat;
|
|
396
396
|
group;
|
|
397
397
|
command;
|
|
398
|
+
commands;
|
|
399
|
+
prefix;
|
|
398
400
|
options;
|
|
399
401
|
args;
|
|
400
402
|
locale;
|
|
@@ -413,6 +415,8 @@ var CommandContextImplementation = class {
|
|
|
413
415
|
this.user = options.message.sender;
|
|
414
416
|
this.chat = { id: options.message.chatId, isGroup: options.message.isGroup };
|
|
415
417
|
this.command = options.command;
|
|
418
|
+
this.commands = options.commands;
|
|
419
|
+
this.prefix = options.commands.prefix;
|
|
416
420
|
this.options = options.options;
|
|
417
421
|
this.args = options.args;
|
|
418
422
|
this.locale = options.locale;
|
|
@@ -510,6 +514,85 @@ function normalizeContent(content) {
|
|
|
510
514
|
return typeof content === "string" ? { text: content } : content;
|
|
511
515
|
}
|
|
512
516
|
|
|
517
|
+
// src/commands/load-commands.ts
|
|
518
|
+
import { readdir } from "fs/promises";
|
|
519
|
+
import path from "path";
|
|
520
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
521
|
+
var DEFAULT_EXTENSIONS = [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"];
|
|
522
|
+
async function loadCommands(registrar, dirPath, options = {}) {
|
|
523
|
+
const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
|
|
524
|
+
const recursive = options.recursive ?? true;
|
|
525
|
+
const resolvedDirPath = dirPath instanceof URL ? fileURLToPath(dirPath) : dirPath;
|
|
526
|
+
const entries = await readEntries(resolvedDirPath, recursive);
|
|
527
|
+
const loaded = [];
|
|
528
|
+
const skipped = [];
|
|
529
|
+
const commands = [];
|
|
530
|
+
for (const filePath of entries) {
|
|
531
|
+
if (isDeclarationFile(filePath) || !extensions.includes(path.extname(filePath))) {
|
|
532
|
+
skipped.push(filePath);
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
const definitions = await importCommands(filePath);
|
|
536
|
+
for (const definition of definitions) {
|
|
537
|
+
registrar.command(definition);
|
|
538
|
+
commands.push({ name: definition.name, filePath });
|
|
539
|
+
}
|
|
540
|
+
loaded.push(filePath);
|
|
541
|
+
}
|
|
542
|
+
return { loaded, skipped, commands };
|
|
543
|
+
}
|
|
544
|
+
async function readEntries(dirPath, recursive) {
|
|
545
|
+
let dirents;
|
|
546
|
+
try {
|
|
547
|
+
dirents = await readdir(dirPath, { recursive, withFileTypes: true });
|
|
548
|
+
} catch (error) {
|
|
549
|
+
throw new WhaNextError("COMMAND_LOAD_FAILED", `Could not read the commands directory "${dirPath}".`, {
|
|
550
|
+
cause: error,
|
|
551
|
+
context: { dirPath }
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
return dirents.filter((dirent) => dirent.isFile()).map((dirent) => path.join(dirent.parentPath, dirent.name)).sort((left, right) => left.localeCompare(right));
|
|
555
|
+
}
|
|
556
|
+
async function importCommands(filePath) {
|
|
557
|
+
let module;
|
|
558
|
+
try {
|
|
559
|
+
module = await import(pathToFileURL(filePath).href);
|
|
560
|
+
} catch (error) {
|
|
561
|
+
throw new WhaNextError("COMMAND_LOAD_FAILED", `Could not import the command file "${filePath}".`, {
|
|
562
|
+
cause: error,
|
|
563
|
+
context: { filePath }
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
const definitions = [];
|
|
567
|
+
const seen = /* @__PURE__ */ new Set();
|
|
568
|
+
const exports = [
|
|
569
|
+
module.default,
|
|
570
|
+
...Object.entries(module).filter(([name]) => name !== "default").map(([, value]) => value)
|
|
571
|
+
];
|
|
572
|
+
for (const value of exports) {
|
|
573
|
+
const candidates = Array.isArray(value) ? value : [value];
|
|
574
|
+
for (const candidate of candidates) {
|
|
575
|
+
if (isCommandDefinition(candidate) && !seen.has(candidate)) {
|
|
576
|
+
seen.add(candidate);
|
|
577
|
+
definitions.push(candidate);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (definitions.length === 0) {
|
|
582
|
+
throw new WhaNextError("COMMAND_LOAD_FAILED", `The file "${filePath}" does not export any valid commands.`, {
|
|
583
|
+
context: { filePath }
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
return definitions;
|
|
587
|
+
}
|
|
588
|
+
function isCommandDefinition(value) {
|
|
589
|
+
const candidate = value;
|
|
590
|
+
return typeof value === "object" && value !== null && typeof candidate.name === "string" && (typeof candidate.execute === "function" || Array.isArray(candidate.subcommands));
|
|
591
|
+
}
|
|
592
|
+
function isDeclarationFile(filePath) {
|
|
593
|
+
return /\.d\.(?:ts|mts|cts)$/.test(filePath);
|
|
594
|
+
}
|
|
595
|
+
|
|
513
596
|
// src/commands/options.ts
|
|
514
597
|
var option = {
|
|
515
598
|
string(definition) {
|
|
@@ -705,6 +788,9 @@ var CommandRouter = class {
|
|
|
705
788
|
this.#definitions.add(definition);
|
|
706
789
|
return this;
|
|
707
790
|
}
|
|
791
|
+
load(dirPath, options = {}) {
|
|
792
|
+
return loadCommands(this, dirPath, options);
|
|
793
|
+
}
|
|
708
794
|
use(middleware) {
|
|
709
795
|
this.#globalMiddleware.push(middleware);
|
|
710
796
|
return this;
|
|
@@ -778,6 +864,7 @@ _Nenhum comando dispon\xEDvel._`;
|
|
|
778
864
|
options: new ParsedCommandOptions({}),
|
|
779
865
|
args: new ArgsParser(tokens),
|
|
780
866
|
services: this.#services,
|
|
867
|
+
commands: this,
|
|
781
868
|
signal: new AbortController().signal,
|
|
782
869
|
...root.locale ? { locale: root.locale } : {}
|
|
783
870
|
});
|
|
@@ -820,6 +907,7 @@ _Nenhum comando dispon\xEDvel._`;
|
|
|
820
907
|
options: parsedOptions,
|
|
821
908
|
args: legacyArgs,
|
|
822
909
|
services: this.#services,
|
|
910
|
+
commands: this,
|
|
823
911
|
signal,
|
|
824
912
|
...resolved.locale ? { locale: resolved.locale } : {}
|
|
825
913
|
});
|
|
@@ -840,6 +928,7 @@ _Nenhum comando dispon\xEDvel._`;
|
|
|
840
928
|
options: new ParsedCommandOptions({}),
|
|
841
929
|
args: legacyArgs,
|
|
842
930
|
services: this.#services,
|
|
931
|
+
commands: this,
|
|
843
932
|
signal: new AbortController().signal,
|
|
844
933
|
...resolved.locale ? { locale: resolved.locale } : {}
|
|
845
934
|
});
|
|
@@ -2890,81 +2979,6 @@ var guards = {
|
|
|
2890
2979
|
return guard;
|
|
2891
2980
|
}
|
|
2892
2981
|
};
|
|
2893
|
-
|
|
2894
|
-
// src/commands/load-commands.ts
|
|
2895
|
-
import { readdir } from "fs/promises";
|
|
2896
|
-
import path from "path";
|
|
2897
|
-
import { pathToFileURL } from "url";
|
|
2898
|
-
var DEFAULT_EXTENSIONS = [".js", ".mjs", ".cjs"];
|
|
2899
|
-
async function loadCommands(registrar, dirPath, options = {}) {
|
|
2900
|
-
const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
|
|
2901
|
-
const recursive = options.recursive ?? true;
|
|
2902
|
-
const entries = await readEntries(dirPath, recursive);
|
|
2903
|
-
const loaded = [];
|
|
2904
|
-
const skipped = [];
|
|
2905
|
-
const commands = [];
|
|
2906
|
-
for (const filePath of entries) {
|
|
2907
|
-
if (!extensions.includes(path.extname(filePath))) {
|
|
2908
|
-
skipped.push(filePath);
|
|
2909
|
-
continue;
|
|
2910
|
-
}
|
|
2911
|
-
const definitions = await importCommands(filePath);
|
|
2912
|
-
for (const definition of definitions) {
|
|
2913
|
-
registrar.command(definition);
|
|
2914
|
-
commands.push({ name: definition.name, filePath });
|
|
2915
|
-
}
|
|
2916
|
-
loaded.push(filePath);
|
|
2917
|
-
}
|
|
2918
|
-
return { loaded, skipped, commands };
|
|
2919
|
-
}
|
|
2920
|
-
async function readEntries(dirPath, recursive) {
|
|
2921
|
-
let dirents;
|
|
2922
|
-
try {
|
|
2923
|
-
dirents = await readdir(dirPath, { recursive, withFileTypes: true });
|
|
2924
|
-
} catch (error) {
|
|
2925
|
-
throw new WhaNextError("COMMAND_LOAD_FAILED", `Could not read the commands directory "${dirPath}".`, {
|
|
2926
|
-
cause: error,
|
|
2927
|
-
context: { dirPath }
|
|
2928
|
-
});
|
|
2929
|
-
}
|
|
2930
|
-
return dirents.filter((dirent) => dirent.isFile()).map((dirent) => path.join(dirent.parentPath, dirent.name)).sort((left, right) => left.localeCompare(right));
|
|
2931
|
-
}
|
|
2932
|
-
async function importCommands(filePath) {
|
|
2933
|
-
let module;
|
|
2934
|
-
try {
|
|
2935
|
-
module = await import(pathToFileURL(filePath).href);
|
|
2936
|
-
} catch (error) {
|
|
2937
|
-
throw new WhaNextError("COMMAND_LOAD_FAILED", `Could not import the command file "${filePath}".`, {
|
|
2938
|
-
cause: error,
|
|
2939
|
-
context: { filePath }
|
|
2940
|
-
});
|
|
2941
|
-
}
|
|
2942
|
-
const definitions = [];
|
|
2943
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2944
|
-
const exports = [
|
|
2945
|
-
module.default,
|
|
2946
|
-
...Object.entries(module).filter(([name]) => name !== "default").map(([, value]) => value)
|
|
2947
|
-
];
|
|
2948
|
-
for (const value of exports) {
|
|
2949
|
-
const candidates = Array.isArray(value) ? value : [value];
|
|
2950
|
-
for (const candidate of candidates) {
|
|
2951
|
-
if (isCommandDefinition(candidate) && !seen.has(candidate)) {
|
|
2952
|
-
seen.add(candidate);
|
|
2953
|
-
definitions.push(candidate);
|
|
2954
|
-
}
|
|
2955
|
-
}
|
|
2956
|
-
}
|
|
2957
|
-
if (definitions.length === 0) {
|
|
2958
|
-
throw new WhaNextError("COMMAND_LOAD_FAILED", `The file "${filePath}" does not export any valid commands.`, {
|
|
2959
|
-
context: { filePath }
|
|
2960
|
-
});
|
|
2961
|
-
}
|
|
2962
|
-
return definitions;
|
|
2963
|
-
}
|
|
2964
|
-
function isCommandDefinition(value) {
|
|
2965
|
-
const candidate = value;
|
|
2966
|
-
return typeof value === "object" && value !== null && typeof candidate.name === "string" && (typeof candidate.execute === "function" || Array.isArray(candidate.subcommands));
|
|
2967
|
-
}
|
|
2968
2982
|
export {
|
|
2969
2983
|
ArgsParser,
|
|
2970
2984
|
Browser,
|