Skip to main content

Client

ArcClient extends discord.js Client. All discord.js options, properties, events, and REST methods are available on it.

Porting an existing discord.js bot? See Migrate from discord.js for side-by-side examples.

Constructor

import { ArcClient } from "arcscord";

const client = new ArcClient(token, options);
ParameterTypeDescription
tokenstringBot token from the Discord Developer Portal.
optionsArcClientOptionsClient configuration. Extends discord.js ClientOptions.

Options

intents (required)

Gateway intents to enable. Inherited from discord.js. At minimum "Guilds" is needed for slash commands.

const client = new ArcClient(process.env.DISCORD_TOKEN!, {
intents: ["Guilds"],
});

All discord.js ClientOptions (partials, rest, presence, etc.) are also accepted.


applicationId

Discord application ID. When provided, loadCommands (and loadHandlers) can register commands via the REST API before the clientReady event fires, without waiting for discord.js to hydrate client.application.

const client = new ArcClient(process.env.DISCORD_TOKEN!, {
intents: ["Guilds"],
applicationId: process.env.APPLICATION_ID!,
});

logger

Controls the built-in logger. All fields are optional.

OptionTypeDefaultDescription
level"trace" | "debug" | "info" | "warn" | "error" | "fatal""info"Minimum level to log. Use "debug" to see command/component execution logs during development.
format"pretty" | "json""pretty"Output format. Use "json" in production or containers.
loggerFunc(...data: unknown[]) => voidper-level console.log/console.errorCustom function to receive each log line. When omitted, warn/error/fatal go to console.error, everything else to console.log.
customLoggerLoggerConstructorArcLoggerReplace the built-in logger class entirely. Must satisfy LoggerInterface.
diagnostics{ loggerFunc, format? }Secondary output for full error diagnostics. Providing loggerFunc is what turns it on.
errorDetail"short" | "full""short" if diagnostics is set, "full" otherwiseHow much detail logError/fatalError print on the main sink.
const client = new ArcClient(process.env.DISCORD_TOKEN!, {
intents: ["Guilds"],
logger: {
level: process.env.NODE_ENV === "production" ? "info" : "debug",
format: process.env.NODE_ENV === "production" ? "json" : "pretty",
},
});

Defaults can also be set via environment variables: ARCSCORD_LOG_LEVEL, LOG_LEVEL, ARCSCORD_LOG_FORMAT, LOG_FORMAT.


enableInternalTrace

Enables verbose trace logs from framework internals — command dispatch, middleware steps, locale detection, etc. Useful for debugging framework behavior.

const client = new ArcClient(process.env.DISCORD_TOKEN!, {
intents: ["Guilds"],
enableInternalTrace: true,
});

Default: false.


waitReady

Defines the default readiness timeout and check interval for the client. This configuration is also used by internal framework calls, such as queued events and loadHandlers when command registration must wait for Discord.

const client = new ArcClient(process.env.DISCORD_TOKEN!, {
intents: ["Guilds"],
waitReady: {
timeout: 15_000,
checkInterval: 100,
},
});
OptionTypeDefaultDescription
timeoutnumber30000Maximum wait in milliseconds before rejecting.
checkIntervalnumber50Delay in milliseconds between readiness checks.

baseMessages

Overrides framework-generated messages sent to users. Currently supports one key:

baseMessages.error — the message sent when an unhandled error occurs during a command or component handler.

const client = new ArcClient(process.env.DISCORD_TOKEN!, {
intents: ["Guilds"],
baseMessages: {
error: (id, context) => ({
content: `An error occurred (id: \`${id}\`). Please contact support.`,
}),
},
});

The context argument contains:

  • context.locale — the detected i18next language for the interaction (when locale manager is enabled).
  • context.t — a fixed translation function for that locale.

managers

Per-manager configuration. All fields are optional.

FieldManagesDocumentation
managers.commandSlash, user, and message commands — execution handlers, dispatch diagnosticsExecution handlers
managers.componentButtons, select menus, modals — execution handlers, dispatch diagnosticsExecution handlers
managers.eventDiscord.js event listeners — intent checks, execution handlersExecution handlers
managers.localei18next integration — language map, detection, resourcesLocalization

Example with event intent check configuration:

const client = new ArcClient(process.env.DISCORD_TOKEN!, {
intents: ["Guilds"],
managers: {
event: {
intentCheck: {
missing: "warn", // warn when an event has no matching intent
partialCoverage: "off",
},
},
},
});

Manager properties

ArcClient exposes four manager instances:

PropertyDescription
client.commandManagerRegisters commands with Discord and dispatches interactions.
client.componentManagerRoutes component custom IDs and dispatches interactions.
client.eventManagerWraps discord.js event listeners with execution handling.
client.localeManageri18next wrapper used at registration time and per interaction.

Methods

waitReady(options?)

Waits for the Discord client to become ready. Per-call options override the defaults configured through ArcClientOptions.waitReady. The promise rejects with ArcClientReadyTimeoutError when the effective timeout is reached.

await client.waitReady({
timeout: 15_000,
checkInterval: 100,
});
OptionTypeFallbackDescription
timeoutnumber30000Maximum wait in milliseconds before rejecting.
checkIntervalnumber50Delay in milliseconds between readiness checks.

The previous numeric form remains supported: waitReady(100) sets checkInterval to 100 ms and keeps the globally configured timeout.

Loading comes in two tiers with deliberately different error handling:

  • The per-category loaders (loadCommands, loadComponents, loadEvents) are async and return an Arcscord Result[error, count]. On failure the first tuple item is an ArcscordError whose code identifies the problem. They never throw for expected failures such as a duplicate route or an unmet intent requirement — you inspect the outcome.
  • The convenience loadHandlers is a bootstrap helper that fails fast: it throws the first ArcscordError instead of returning it, so a broken startup crashes loudly rather than continuing in a half-wired state. Reach for the per-category loaders when you want to handle the failure yourself.

loadHandlers(handlers, logs?)

Convenience method. Loads events, then components, then commands in a single call. Throws the first ArcscordError on failure; returns a HandlersLoadReport ({ commands, components, events } load counts) on success.

// Fail-fast: an unhandled throw crashes the process at startup.
const report = await client.loadHandlers({
commands: [avatarCommand, pingCommand],
components: [simpleButton, profileModal],
events: [messageEvent],
});
// report -> { commands: 2, components: 2, events: 1 }

// Or handle it explicitly:
try {
await client.loadHandlers(handlers);
}
catch (err) {
client.logger.fatalError(err);
process.exit(1);
}

If applicationId is set, commands are registered immediately over REST without waiting for clientReady. Otherwise, loadHandlers waits for the client to be ready before pushing commands.


loadCommands(commands, group?, guild?)

Registers commands with Discord and loads them into the command manager. Returns Result<number, ArcscordError> (the count of loaded commands); inspect error.code to distinguish validation, application, and registration failures.

const [err] = await client.loadCommands([pingCommand, avatarCommand]);
if (err !== null) {
client.logger.fatalError(err);
}

// Guild-scoped registration
await client.loadCommands([adminCommand], "admin", process.env.GUILD_ID!);

The optional group parameter is an internal label for the command set (used by deleteUnloadedCommands). The optional guild parameter restricts registration to a specific guild.


loadComponents(components)

Loads component handlers into the component manager. Returns Result<number, ArcscordError> (the count of loaded components). Fails with COMPONENT_VALIDATION_FAILED, COMPONENT_ROUTE_DUPLICATE, or COMPONENT_ROUTE_INVALID.

const [err, count] = await client.loadComponents([simpleButton, profileModal, roleMenu]);

Use client.componentManager.unloadComponent(route) to remove a previously loaded component; it returns true when a component was removed.


loadEvents(events)

Registers event handlers and attaches discord.js listeners. Returns Result<number, ArcscordError> (the count of loaded events). Fails with EVENT_HANDLER_DUPLICATE or EVENT_INTENT_MISSING.

const [err, count] = await client.loadEvents([messageEvent, inviteEvent]);

Use client.eventManager.unloadEvent(name) to remove a previously loaded event.


createLogger(name)

Returns a new logger instance scoped to the given name, using the same output function and configuration as the client logger.

const log = client.createLogger("my-module");
log.info("started");

Full setup example

src/index.ts
import { ArcClient } from "arcscord";
import { avatarCommand, pingCommand } from "./commands";
import { simpleButton, profileModal } from "./components";
import { messageEvent } from "./events";

const client = new ArcClient(process.env.DISCORD_TOKEN!, {
intents: ["Guilds"],
applicationId: process.env.APPLICATION_ID,
logger: {
level: process.env.NODE_ENV === "production" ? "info" : "debug",
format: process.env.NODE_ENV === "production" ? "json" : "pretty",
},
managers: {
event: {
intentCheck: { missing: "warn" },
},
},
});

await client.loadHandlers({
commands: [avatarCommand, pingCommand],
components: [simpleButton, profileModal],
events: [messageEvent],
});

void client.login();