Skip to content

Decorators

Andrew R. edited this page Jul 24, 2026 · 1 revision

Core Decorators

Shamoo has exactly 37 core decorators. The canonical import for every decorator on this page is @shamoo/decorators; focused packages such as @shamoo/lifecycle, @shamoo/commands, @shamoo/scheduler, @shamoo/guards, and @shamoo/validation re-export their relevant decorators.

import { Plugin, OnEnable } from '@shamoo/decorators';

shamooc resolves imported symbols rather than trusting local function names. It stores decorator arguments as deterministic JSON-compatible values and does not use emitDecoratorMetadata. The decorator factories are intentionally permissive at the TypeScript surface; the tables describe the arguments consumed by the compiler or expected by framework contracts.

Class declarations (7)

Decorator and import Target Arguments Example
Plugin from @shamoo/decorators Class Optional declaration object; commonly { name } @Plugin({ name: 'welcome' }) class Welcome {}
Module from @shamoo/decorators Class { imports?, providers?, exports? }; arrays contain declaration values; forwardRef() is supported for intentional import cycles @Module({ providers: [Greeter], exports: [Greeter] }) class AppModule {}
Component from @shamoo/decorators Class Optional declaration object @Component() class JoinListener {}
Injectable from @shamoo/decorators Class Optional declaration object; compiler kind is the same component kind as Component @Injectable() class Formatter {}
Service from @shamoo/decorators Class Optional { scope?: Scope, token?: ServiceIdentifier } @Service({ scope: Scope.PLUGIN }) class Economy {}
Global from @shamoo/decorators Class None; meaningful with a module declaration @Module() @Global() class SharedModule {}
Primary from @shamoo/decorators Class None; marks the preferred provider declaration @Service() @Primary() class MainEconomy {}

Only one of Plugin, Module, Component, Injectable, and Service may appear on a class. Global and Primary are modifiers, not substitutes for a declaration.

Current runtime limit: the compiler emits class/module/provider metadata, and @shamoo/di can execute explicit provider metadata. The bundled deployment adapter does not build that DI/module graph. It locates exported constructors by name and only constructs components with zero constructor dependencies. Global, Primary, service scope/token options, module visibility, and constructor injection therefore have no automatic deployed effect through that adapter.

Injection and context binding (8)

All eight target a constructor/method parameter or property.

Decorator and import Target Arguments Example
Inject from @shamoo/decorators Parameter/property Required ServiceIdentifier: class, token, string, or symbol constructor(@Inject(LOGGER) logger: Logger) {}
Optional from @shamoo/decorators Parameter/property None; modifies dependency resolution constructor(@Optional() logger?: Logger) {}
InjectAll from @shamoo/decorators Parameter/property None; requests all visible providers constructor(@InjectAll() hooks: Hook[]) {}
Lazy from @shamoo/decorators Parameter/property None; defers resolution constructor(@Lazy() cache: Cache) {}
Named from @shamoo/decorators Parameter/property Name string/expression constructor(@Named('audit') logger: Logger) {}
Qualifier from @shamoo/decorators Parameter/property Qualifier string/expression constructor(@Qualifier('primary') store: Store) {}
ConfigValue from @shamoo/decorators Parameter/property Configuration key public start(@ConfigValue('motd') motd: string): void {}
Context from @shamoo/decorators Parameter/property Optional invocation-context key; no argument is recorded as the internal value key public run(@Context('event') event: unknown): void {}

Optional, InjectAll, Lazy, Named, and Qualifier may modify the same dependency. Inject, ConfigValue, Argument, Option, Sender, and Context are competing binding sources and cannot be combined on one target. Interfaces, aliases, primitives, and type-only imports have no runtime identity and require @Inject(createToken<T>(...)).

Current runtime limit: @shamoo/di and loadRuntimeMetadata() support explicit optional, multi, lazy, named, qualified, and context dependencies. The bundled adapter does not resolve constructor/property injection or method parameter metadata. It passes the host callback's raw data argument directly. @shamoo/config defines source/decoder contracts but no file loader, reload manager, or automatic ConfigValue source.

Lifecycle methods (6)

All are imported from @shamoo/decorators or @shamoo/lifecycle, target methods, and take no required arguments.

Decorator Target Arguments Example
OnLoad from @shamoo/decorators or @shamoo/lifecycle Method None @OnLoad() load(): void {}
OnEnable from @shamoo/decorators or @shamoo/lifecycle Method None @OnEnable() enable(): void {}
OnReady from @shamoo/decorators or @shamoo/lifecycle Method None @OnReady() ready(): void {}
OnDrain from @shamoo/decorators or @shamoo/lifecycle Method None @OnDrain() drain(): void {}
OnDisable from @shamoo/decorators or @shamoo/lifecycle Method None @OnDisable() disable(): void {}
OnUnload from @shamoo/decorators or @shamoo/lifecycle Method None @OnUnload() unload(): void {}

Startup order is load, enable, ready. Shutdown/replacement order is drain, disable, unload. Only one lifecycle decorator may appear on a method.

Current runtime limit: lifecycle methods are called by the bundled adapter, in metadata component order, with no injected arguments. The standalone lifecycle runtime supports deterministic ordering, DI parameters, timeout signals, idempotence, and aggregate cleanup errors, but those richer parameter semantics are not wired by the bundled adapter. There is no OnReload; development changes replace a complete generation.

Events and commands (3)

Decorator and import Target Arguments Example
EventHandler from @shamoo/decorators or @shamoo/events Method Compiler records all arguments; the bundled adapter recognizes a first string as the event ID, otherwise uses the method name @EventHandler('org.bukkit.event.server.ServerLoadEvent') onLoadEvent(data: unknown) {}
Command from @shamoo/decorators or @shamoo/commands Method First string is the command name; otherwise method name @Command('status') status(context: PaperCommandContext) {}
Subcommand from @shamoo/decorators or @shamoo/commands Method First string is currently treated as a command name @Subcommand('admin') admin(): boolean { return true; }

These conflict with task declarations on the same method. Platform-generated event decorators are covered in Paper Event Decorators and Velocity Event Decorators.

Current runtime limit: direct event and command registration works. Paper event callbacks receive { type, asynchronous }; Velocity callbacks receive { type }, not generated live JVM objects. Generic event options such as priority and cancellation are not interpreted by the bundled adapter: Paper uses NORMAL and does not request cancelled events; Velocity uses order 0. Command aliases and subcommand trees are not derived from decorator metadata. Paper commands do receive the supported data-only PaperCommandContext; Velocity command payloads remain the basic copied host values.

Command parameters (3)

All are imported from @shamoo/decorators or @shamoo/commands and target parameters or properties.

Decorator Target Arguments Example
Argument from @shamoo/decorators or @shamoo/commands Parameter/property Argument name/index value greet(@Argument('name') name: string): void {}
Option from @shamoo/decorators or @shamoo/commands Parameter/property Option name value list(@Option('verbose') verbose: boolean): void {}
Sender from @shamoo/decorators or @shamoo/commands Parameter/property No required argument; compiler maps it to context key sender run(@Sender() sender: unknown): void {}

Current runtime limit: these bindings are compiler metadata and are supported by the standalone invocation runtime when a host supplies a context. The bundled adapter does not perform parameter binding. Use the direct PaperCommandContext argument for currently deployed Paper commands.

Scheduled tasks (3)

All are imported from @shamoo/decorators or @shamoo/scheduler and target methods.

Decorator Target Arguments Example
Scheduled from @shamoo/decorators or @shamoo/scheduler Method Conventionally a schedule object { delay, unit, repeat? }; repeatable @Scheduled({ delay: 20, unit: 'ticks' }) heartbeat() {}
Interval from @shamoo/decorators or @shamoo/scheduler Method Interval value/options recorded as metadata @Interval({ delay: 5, unit: 'seconds' }) poll() {}
Timeout from @shamoo/decorators or @shamoo/scheduler Method One-shot delay value/options recorded as metadata @Timeout({ delay: 1, unit: 'minutes' }) expire() {}

Supported TimeUnit values in @shamoo/scheduler are milliseconds, seconds, minutes, hours, and ticks.

Current runtime limit: the bundled adapter registers every task as immediate/global work. Paper uses the global scheduler callback; Velocity schedules with delay 0. It does not interpret delay, repeat, interval, timeout, region, or entity ownership metadata. Use explicit host scheduler integration when timing and Folia ownership matter.

Conditions and invocation pipeline (6)

Decorator and import Target Arguments Example
Requires from @shamoo/decorators or @shamoo/conditions Class/method One or more condition values/types; repeatable @Requires(PaperOnly) class Listener {}
RequiresExpression from @shamoo/decorators or @shamoo/conditions Class/method One expression string @RequiresExpression("platform == 'paper'") class Listener {}
UseInterceptors from @shamoo/decorators or @shamoo/interceptors Class/method Interceptor types, in declaration order; repeatable @UseInterceptors(TimingInterceptor) run() {}
UseGuards from @shamoo/decorators or @shamoo/guards Class/method Guard types, in declaration order; repeatable @UseGuards(AdminGuard) run() {}
UsePipes from @shamoo/decorators or @shamoo/pipes Class/method Pipe types, in declaration order; repeatable @UsePipes(TrimPipe) run(value: string) {}
Catch from @shamoo/decorators or @shamoo/filters Class/method Exception filter types; repeatable @Catch(LogFilter) run() {}

The standalone invocation order is parameter resolution, pipes and validators, guards, interceptors, handler/result transformation, error transformation and filters, then scope disposal. Interceptor proceed() is single-use.

Current runtime limit: the compiler records these declarations, but the bundled adapter neither evaluates conditions nor resolves/runs pipeline classes. It invokes handlers directly. These decorators are not an authorization or validation boundary in a normal deployed bundle today. See Custom Decorators for supported extension interfaces and wiring details.

Validation (1)

Decorator and import Target Arguments Example
Validate from @shamoo/decorators or @shamoo/validation Parameter/property One or more validator/rule values; repeatable run(@Argument('value') @Validate(NonEmptyValidator) value: string): void {}

Validator<T>.validate(value) returns validation issues and may be asynchronous. The helper aggregates issues into ValidationError.

Current runtime limit: validation helpers execute when explicitly composed with InvocationRuntime; the bundled adapter does not resolve validators from metadata or call them.

General rules

  • Non-repeatable duplicate decorators are errors. Repeatable decorators are Requires, Validate, Scheduled, UseInterceptors, UseGuards, UsePipes, and Catch.
  • Lifecycle decorators conflict with one another on a method.
  • EventHandler, Command, Subcommand, Scheduled, Interval, and Timeout conflict on one method.
  • Parameter decorators need experimentalDecorators; class/method/property-only projects may use standard decorators.
  • Runtime reflection is explicit: getRuntimeDeclarations(target) from @shamoo/reflection returns recorded declarations. It does not infer types.

Source: all 37 decorator implementations and compiler discovery.

Clone this wiki locally