> ## Documentation Index
> Fetch the complete documentation index at: https://stars-components.js.org/llms.txt
> Use this file to discover all available pages before exploring further.

# @wolfstar/decorators

> TypeScript decorators for @wolfstar/http-framework: ApplyOptions, guild and DM context guards, permission preconditions, Enumerable, and decorator factories.

```bash npm theme={"system"}
npm install @wolfstar/decorators @wolfstar/http-framework
```

```bash pnpm theme={"system"}
pnpm add @wolfstar/decorators @wolfstar/http-framework
```

```bash yarn theme={"system"}
yarn add @wolfstar/decorators @wolfstar/http-framework
```

```bash bun theme={"system"}
bun add @wolfstar/decorators @wolfstar/http-framework
```

> [card] [npm](https://npmx.dev/package/@wolfstar/decorators)

> [card] [Source](https://github.com/wolfstar-project/stars-components/tree/main/packages/decorators)

## Description

A set of decorators for configuring [`@wolfstar/http-framework`](https://stars-components.js.org/documentation/packages/http-framework) pieces and
gating their methods, modelled after [`@sapphire/decorators`](https://www.npmjs.com/package/@sapphire/decorators). They
shipped with the framework itself until `@wolfstar/http-framework` 6.0.0. The framework now only keeps the `Register*`
decorators and `RestrictGuildIds`, which are part of command registration.

Use this package when you want to set piece options without writing a constructor, restrict a method to guild or DM
interactions, or check user and application permissions before a method runs.

## Installation

`@wolfstar/http-framework` is a peer dependency, so install both packages. The decorators use the legacy
(`experimentalDecorators`) decorator semantics, like the `Register*` decorators of the framework.

`@wolfstar/decorators` is one of the default `imports.presets`. If your project uses the `stars` CLI's auto imports, you
get every export of `@wolfstar/decorators` without an explicit `import` once the package is installed. See the
[CLI guide](https://stars-components.js.org/documentation/packages/cli.md#auto-imports).

## Usage

### `ApplyOptions`

Sets the options of any `Piece` (`Command`, `Listener`, or `InteractionHandler`) without writing a constructor. The
decorator merges its values on top of the options the piece is constructed with, so they win on conflicting keys.

```typescript theme={"system"}
import { ApplyOptions } from '@wolfstar/decorators';
import { Command, RegisterCommand } from '@wolfstar/http-framework';

@ApplyOptions<Command.Options>({ name: 'ping', enabled: true })
@RegisterCommand({ name: 'ping', description: 'A simple ping pong command' })
export class UserCommand extends Command {
	public override chatInputRun(interaction: Command.ChatInputInteraction) {
		return interaction.reply({ content: 'Pong!' });
	}
}
```

It also accepts a function, which receives the loader context:

```typescript theme={"system"}
@ApplyOptions<Command.Options>(({ name }) => ({ name: name.toLowerCase() }))
export class UserCommand extends Command {}
```

> **Note**: `ApplyOptions` returns a `Proxy` wrapping the class. Apply it above (outside of) any other class decorator
> that keys metadata by class identity, such as `RegisterCommand`, as in the example above. A decorator applied above
> `ApplyOptions` would run after it and register against the proxy. Instances constructed from the exported class still
> resolve `.constructor` to the original, unproxied class, so the framework could never find that metadata.
> The `wolfstar/apply-options-decorator-order` rule of `@wolfstar/eslint-plugin-http-framework` enforces this.

### `RequiresGuildContext` / `RequiresDMContext`

Restrict a method to interactions received from a guild, or to interactions received outside of one (DMs and
user-installed app contexts). Both take an optional fallback that receives the same arguments as the decorated method.
Without a fallback, the decorator silently skips the method, which resolves to `undefined`.

```typescript theme={"system"}
import { RequiresGuildContext } from '@wolfstar/decorators';
import { Command, RegisterCommand } from '@wolfstar/http-framework';

@RegisterCommand({ name: 'kick', description: 'Kicks a member' })
export class UserCommand extends Command {
	@RequiresGuildContext((interaction: Command.ChatInputInteraction) => interaction.reply({ content: 'This command can only be used in a server.' }))
	public override chatInputRun(interaction: Command.ChatInputInteraction) {
		return interaction.reply({ content: `Hello from ${interaction.guildId}!` });
	}
}
```

### `RequiresUserPermissions` / `RequiresClientPermissions`

Check the permissions of the invoking user (`member.permissions`) or of the application (`app_permissions`) in the
channel the interaction was sent from. Permissions are given as `PermissionFlagsBits` values, as flag names, or as any
nested array of both.

```typescript theme={"system"}
import { RequiresClientPermissions, RequiresUserPermissions } from '@wolfstar/decorators';
import { Command, RegisterCommand } from '@wolfstar/http-framework';
import { PermissionFlagsBits } from 'discord-api-types/v10';

@RegisterCommand({ name: 'purge', description: 'Deletes messages' })
export class UserCommand extends Command {
	@RequiresUserPermissions('ManageMessages')
	@RequiresClientPermissions(PermissionFlagsBits.ManageMessages, 'ReadMessageHistory')
	public override chatInputRun(interaction: Command.ChatInputInteraction) {
		return interaction.reply({ content: 'Purging!' });
	}
}
```

When the check fails, the decorator throws a `PreconditionError`. Its identifier is
`Identifiers.PreconditionUserPermissions` or `Identifiers.PreconditionClientPermissions`, and its
`context: { missing, missingNames }` describes the missing permissions. A `PreconditionError` is a `UserError`, so the
framework emits it as `chatInputCommandDenied` or `contextMenuCommandDenied` (and as `interactionHandlerDenied` for
interaction handlers) instead of `commandError`. Listen to these events to turn the errors into a user-facing reply.
`commandError` only receives unexpected errors:

```typescript theme={"system"}
import { ApplyOptions } from '@wolfstar/decorators';
import { Identifiers, Listener, PreconditionError, type ClientEventCommandContext, type UserError } from '@wolfstar/http-framework';

@ApplyOptions<Listener.Options>({ emitter: 'client', event: 'chatInputCommandDenied' })
export class UserListener extends Listener {
	public run(error: UserError, context: ClientEventCommandContext) {
		if (
			error instanceof PreconditionError &&
			(error.identifier === Identifiers.PreconditionUserPermissions || error.identifier === Identifiers.PreconditionClientPermissions)
		) {
			const { missingNames } = error.context as { missingNames: string[] };
			this.container.logger.warn(`${context.command.name}: missing ${error.precondition}: ${missingNames.join(', ')}`);
		}
	}
}
```

Notes on the semantics:

* Before `@wolfstar/http-framework` 6.3.0, this error reached `commandError`. If a listener on that event handles
  `PreconditionError`, move it to the `*Denied` events. See
  [Expected errors](https://stars-components.js.org/documentation/packages/http-framework.md#expected-errors-denied-events).
* Members with `Administrator` implicitly satisfy every check.
* `RequiresUserPermissions` passes for interactions received outside of a guild, since there are no guild permissions to
  check. Combine it with `RequiresGuildContext` when the method must be guild-only.
* `RequiresClientPermissions` passes when `app_permissions` is absent from the payload, since there is nothing to check
  against.

### `Enumerable` / `EnumerableMethod`

Control whether a field or a method shows up in `Object.keys`, `JSON.stringify`, and console output.

```typescript theme={"system"}
import { Enumerable } from '@wolfstar/decorators';
import { Command } from '@wolfstar/http-framework';

export class UserCommand extends Command {
	@Enumerable(false)
	declare public cache: Map<string, string>;

	public constructor(context: Command.LoaderContext, options: Command.Options) {
		super(context, options);
		this.cache = new Map();
	}
}
```

> **Note**: `Enumerable` installs a setter on the prototype. `useDefineForClassFields` (enabled by `@sapphire/ts-config`,
> and the default from `ES2022` onwards) emits an `Object.defineProperty` call for class fields, which bypasses that
> setter. Mark the field as `declare` so no field definition is emitted, and assign it in the constructor.

### Building your own decorators

`createClassDecorator`, `createMethodDecorator`, `createProxy`, and `createFunctionPrecondition` are the primitives the
decorators above are built on, and are exported so you can build your own.

```typescript theme={"system"}
import { createFunctionPrecondition } from '@wolfstar/decorators';
import type { Command } from '@wolfstar/http-framework';

export const RequiresOwner = createFunctionPrecondition(
	(interaction: Command.ChatInputInteraction) => interaction.user.id === process.env.OWNER_ID,
	(interaction: Command.ChatInputInteraction) => interaction.reply({ content: 'Owner only.' })
);
```

> **Note**: `createFunctionPrecondition` replaces the decorated method with an `async` one, so a decorated method always
> returns a `Promise`, even when both the precondition and the method are synchronous.

## Migrating from `@wolfstar/http-framework` 5.x

Install `@wolfstar/decorators` and move the imports of the utility decorators over. Nothing else changes:

```diff theme={"system"}
-import { ApplyOptions, Command, RegisterCommand, RequiresGuildContext } from '@wolfstar/http-framework';
+import { ApplyOptions, RequiresGuildContext } from '@wolfstar/decorators';
+import { Command, RegisterCommand } from '@wolfstar/http-framework';
```

The `PieceConstructor` and `ContextFallback` types moved too. See the [migration guide](https://stars-components.js.org/documentation/guide/migration.md#v6)
for the full checklist.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.