Skip to content

Commit cb4e7b7

Browse files
committed
Enhance documentation and metadata handling: add detailed descriptions to decorators, improve metadata retrieval functions, and update the package.json to include the src directory for better package management.
1 parent a8b8d9a commit cb4e7b7

6 files changed

Lines changed: 700 additions & 49 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
}
2222
},
2323
"files": [
24+
"src",
2425
"dist",
2526
"README.md",
2627
"LICENSE"

src/index.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
11
/**
2-
* biome-ignore-all lint/performance/noBarrelFile: barrel file
2+
* @packageDocumentation
3+
*
4+
* A TypeScript decorator factory library with metadata storage and reflection capabilities.
5+
*
6+
* This package provides:
7+
* - **Decorator Factories**: {@link createClassDecorator}, {@link createMethodDecorator},
8+
* {@link createPropertyDecorator}, {@link createParameterDecorator} - Create type-safe
9+
* decorators that automatically store metadata for later reflection.
10+
* - **Interceptors**: {@link createMethodInterceptor}, {@link createPropertyInterceptor} -
11+
* Create decorators that wrap method calls or property access for cross-cutting concerns.
12+
* - **Metadata Utilities**: {@link getMetadata}, {@link defineMetadata}, {@link appendMetadata} -
13+
* Low-level functions for reading and writing metadata on targets.
14+
* - **Reflection**: {@link Reflector}, {@link reflect}, {@link createScopedReflector} -
15+
* Retrieve stored metadata from decorated classes and their members.
16+
*
17+
* @see {@link createClassDecorator} - Entry point for creating class decorators
18+
* @see {@link Reflector} - Primary API for reflecting on decorated classes
319
*/
20+
21+
/** biome-ignore-all lint/performance/noBarrelFile: main index file */
422
import "reflect-metadata";
523

624
export type {

src/lib/factories.ts

Lines changed: 181 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,41 @@ function toAccessor(target: object, key: string | symbol): PropertyDescriptor {
8080
/**
8181
* Creates a class decorator factory that stores metadata on the class.
8282
*
83+
* This factory generates decorators for marking classes with typed metadata.
84+
* The returned factory function creates decorators that can be applied to
85+
* class declarations. Metadata is stored using `reflect-metadata` and can
86+
* be retrieved via the attached reflection methods or the global {@link Reflector}.
87+
*
88+
* @typeParam TMeta - The type of metadata stored by the decorator
89+
* @typeParam TArgs - The argument types accepted by the decorator factory (defaults to `[TMeta]`)
90+
*
91+
* @param composeFn - Optional function to compose decorator arguments into metadata.
92+
* If not provided, the first argument is used as the metadata value directly.
93+
*
94+
* @returns A {@link DecoratedClassFactory} that creates class decorators and provides
95+
* reflection methods (`reflect`, `class`, `key`) for querying decorated classes.
96+
*
97+
* @see {@link DecoratedClassFactory}
98+
* @see {@link ScopedReflector}
99+
*
83100
* @example
84-
* // Simple - direct metadata
101+
* ```typescript
102+
* // Simple - direct metadata (first argument becomes metadata)
85103
* const Tag = createClassDecorator<string>();
86104
*
87-
* // Compose - inferred types from function args
88-
* const Role = createClassDecorator((name: string, level: number) => ({ name, level }));
89-
*
90105
* @Tag("admin")
91106
* class AdminController {}
92107
*
93-
* // Reflection
108+
* // Compose - transform multiple arguments into structured metadata
109+
* const Role = createClassDecorator((name: string, level: number) => ({ name, level }));
110+
*
111+
* @Role("moderator", 5)
112+
* class ModeratorController {}
113+
*
114+
* // Reflection - query decorated classes
94115
* const tags = Tag.class(AdminController);
116+
* // => [{ kind: "class", name: "AdminController", metadata: ["admin"], target: AdminController }]
117+
* ```
95118
*/
96119
export function createClassDecorator<TMeta, TArgs extends unknown[] = [TMeta]>(
97120
composeFn?: (...args: TArgs) => TMeta,
@@ -114,20 +137,45 @@ export function createClassDecorator<TMeta, TArgs extends unknown[] = [TMeta]>(
114137
/**
115138
* Creates a method decorator factory that stores metadata per method.
116139
*
140+
* This factory generates decorators for annotating methods with typed metadata.
141+
* The returned factory function creates decorators that can be applied to both
142+
* instance and static methods. Metadata is stored using `reflect-metadata` and
143+
* can be retrieved via the attached reflection methods or the global {@link Reflector}.
144+
*
145+
* @typeParam TMeta - The type of metadata stored by the decorator
146+
* @typeParam TArgs - The argument types accepted by the decorator factory (defaults to `[TMeta]`)
147+
*
148+
* @param composeFn - Optional function to compose decorator arguments into metadata.
149+
* If not provided, the first argument is used as the metadata value directly.
150+
*
151+
* @returns A {@link DecoratedMethodFactory} that creates method decorators and provides
152+
* reflection methods (`reflect`, `methods`, `key`) for querying decorated methods.
153+
*
154+
* @see {@link DecoratedMethodFactory}
155+
* @see {@link ScopedReflector}
156+
*
117157
* @example
118-
* // Simple - direct metadata
158+
* ```typescript
159+
* // Simple - direct metadata (first argument becomes metadata)
119160
* const Route = createMethodDecorator<string>();
120161
*
121-
* // Compose - inferred types from function args
162+
* class Api {
163+
* @Route("/users")
164+
* getUsers() {}
165+
* }
166+
*
167+
* // Compose - transform multiple arguments into structured metadata
122168
* const Route = createMethodDecorator((path: string, method: "GET" | "POST") => ({ path, method }));
123169
*
124170
* class Api {
125171
* @Route("/users", "GET")
126172
* getUsers() {}
127173
* }
128174
*
129-
* // Reflection
175+
* // Reflection - query decorated methods
130176
* const routes = Route.methods(Api);
177+
* // => [{ kind: "method", name: "getUsers", metadata: [{ path: "/users", method: "GET" }], target: fn }]
178+
* ```
131179
*/
132180
export function createMethodDecorator<TMeta, TArgs extends unknown[] = [TMeta]>(
133181
composeFn?: (...args: TArgs) => TMeta,
@@ -150,20 +198,46 @@ export function createMethodDecorator<TMeta, TArgs extends unknown[] = [TMeta]>(
150198
/**
151199
* Creates a property decorator factory that stores metadata on fields.
152200
*
201+
* This factory generates decorators for annotating class properties with typed metadata.
202+
* The returned factory function creates decorators that can be applied to both instance
203+
* and static properties. Properties are ensured to exist on the prototype for reflection
204+
* discovery. Metadata is stored using `reflect-metadata` and can be retrieved via the
205+
* attached reflection methods or the global {@link Reflector}.
206+
*
207+
* @typeParam TMeta - The type of metadata stored by the decorator
208+
* @typeParam TArgs - The argument types accepted by the decorator factory (defaults to `[TMeta]`)
209+
*
210+
* @param composeFn - Optional function to compose decorator arguments into metadata.
211+
* If not provided, the first argument is used as the metadata value directly.
212+
*
213+
* @returns A {@link DecoratedPropertyFactory} that creates property decorators and provides
214+
* reflection methods (`reflect`, `properties`, `key`) for querying decorated properties.
215+
*
216+
* @see {@link DecoratedPropertyFactory}
217+
* @see {@link ScopedReflector}
218+
*
153219
* @example
154-
* // Simple - direct metadata
220+
* ```typescript
221+
* // Simple - direct metadata (first argument becomes metadata)
155222
* const Column = createPropertyDecorator<string>();
156223
*
157-
* // Compose - inferred types from function args
224+
* class User {
225+
* @Column("varchar")
226+
* name!: string;
227+
* }
228+
*
229+
* // Compose - transform multiple arguments into structured metadata
158230
* const Column = createPropertyDecorator((type: string, nullable: boolean) => ({ type, nullable }));
159231
*
160232
* class User {
161233
* @Column("varchar", false)
162234
* name!: string;
163235
* }
164236
*
165-
* // Reflection
237+
* // Reflection - query decorated properties
166238
* const columns = Column.properties(User);
239+
* // => [{ kind: "property", name: "name", metadata: [{ type: "varchar", nullable: false }] }]
240+
* ```
167241
*/
168242
export function createPropertyDecorator<TMeta, TArgs extends unknown[] = [TMeta]>(
169243
composeFn?: (...args: TArgs) => TMeta,
@@ -187,19 +261,45 @@ export function createPropertyDecorator<TMeta, TArgs extends unknown[] = [TMeta]
187261
/**
188262
* Creates a parameter decorator factory that stores metadata per parameter.
189263
*
264+
* This factory generates decorators for annotating constructor and method parameters
265+
* with typed metadata. The returned factory function creates decorators that track
266+
* the parameter index and store metadata keyed by that index. Multiple decorators
267+
* on the same parameter accumulate their metadata in application order. Metadata
268+
* is stored using `reflect-metadata` and can be retrieved via the attached reflection
269+
* methods or the global {@link Reflector}.
270+
*
271+
* @typeParam TMeta - The type of metadata stored by the decorator
272+
* @typeParam TArgs - The argument types accepted by the decorator factory (defaults to `[TMeta]`)
273+
*
274+
* @param composeFn - Optional function to compose decorator arguments into metadata.
275+
* If not provided, the first argument is used as the metadata value directly.
276+
*
277+
* @returns A {@link DecoratedParameterFactory} that creates parameter decorators and provides
278+
* reflection methods (`reflect`, `parameters`, `key`) for querying decorated parameters.
279+
*
280+
* @see {@link DecoratedParameterFactory}
281+
* @see {@link ScopedReflector}
282+
*
190283
* @example
191-
* // Simple - direct metadata
284+
* ```typescript
285+
* // Simple - direct metadata (first argument becomes metadata)
192286
* const Inject = createParameterDecorator<string>();
193287
*
194-
* // Compose - inferred types from function args
288+
* class Service {
289+
* constructor(@Inject("db") db: Database) {}
290+
* }
291+
*
292+
* // Compose - transform multiple arguments into structured metadata
195293
* const Inject = createParameterDecorator((token: string, optional: boolean) => ({ token, optional }));
196294
*
197295
* class Service {
198296
* constructor(@Inject("db", false) db: Database) {}
199297
* }
200298
*
201-
* // Reflection
299+
* // Reflection - query decorated parameters
202300
* const params = Inject.parameters(Service);
301+
* // => [{ kind: "parameter", name: "constructor", parameterIndex: 0, metadata: [{ token: "db", optional: false }] }]
302+
* ```
203303
*/
204304
export function createParameterDecorator<TMeta, TArgs extends unknown[] = [TMeta]>(
205305
composeFn?: (...args: TArgs) => TMeta,
@@ -224,20 +324,50 @@ export function createParameterDecorator<TMeta, TArgs extends unknown[] = [TMeta
224324
}
225325

226326
/**
227-
* Creates a method decorator that wraps the original method.
327+
* Creates a method decorator that wraps the original method with an interceptor.
328+
*
329+
* This factory generates decorators that wrap method implementations, enabling
330+
* cross-cutting concerns like logging, timing, caching, or validation. The
331+
* interceptor receives the original method, accumulated metadata, and context
332+
* about the decoration target. Unlike {@link createMethodDecorator}, this factory
333+
* actively modifies method behavior at decoration time.
334+
*
335+
* @typeParam TMeta - The type of metadata stored by the decorator
336+
* @typeParam TArgs - The argument types accepted by the decorator factory (defaults to `[TMeta]`)
337+
*
338+
* @param options - Configuration for the method interceptor, including:
339+
* - `compose`: Optional function to transform decorator arguments into metadata
340+
* - `interceptor`: Function that receives the original method and returns a wrapped version
341+
*
342+
* @returns A {@link DecoratedMethodFactory} that creates method decorators with interception
343+
* and provides reflection methods (`reflect`, `methods`, `key`) for querying decorated methods.
344+
*
345+
* @see {@link MethodInterceptorOptions}
346+
* @see {@link DecoratedMethodFactory}
228347
*
229348
* @example
349+
* ```typescript
350+
* // Create a timing decorator
230351
* const Timed = createMethodInterceptor<string>({
231352
* interceptor: (original, meta, ctx) => function(...args) {
232353
* const start = Date.now();
233354
* const result = original.apply(this, args);
234-
* console.log(`${ctx.propertyKey} took ${Date.now() - start}ms`);
355+
* console.log(`${String(ctx.propertyKey)} took ${Date.now() - start}ms`);
235356
* return result;
236357
* }
237358
* });
238359
*
239-
* // Reflection
360+
* class Service {
361+
* @Timed("operation")
362+
* expensiveOperation() {
363+
* // ... slow work
364+
* }
365+
* }
366+
*
367+
* // Reflection - query decorated methods
240368
* const timed = Timed.methods(Service);
369+
* // => [{ kind: "method", name: "expensiveOperation", metadata: ["operation"], target: fn }]
370+
* ```
241371
*/
242372
export function createMethodInterceptor<TMeta, TArgs extends unknown[] = [TMeta]>(
243373
options: MethodInterceptorOptions<TMeta, TArgs>,
@@ -279,16 +409,48 @@ export function createMethodInterceptor<TMeta, TArgs extends unknown[] = [TMeta]
279409
/**
280410
* Creates a property decorator that intercepts get/set operations.
281411
*
412+
* This factory generates decorators that wrap property access, enabling
413+
* features like lazy initialization, validation, change tracking, or
414+
* computed properties. The interceptors receive the original getter/setter,
415+
* accumulated metadata, and context about the decoration target. Properties
416+
* are converted to accessor descriptors (get/set) if they aren't already.
417+
*
418+
* @typeParam TMeta - The type of metadata stored by the decorator
419+
* @typeParam TArgs - The argument types accepted by the decorator factory (defaults to `[TMeta]`)
420+
*
421+
* @param options - Configuration for the property interceptor, including:
422+
* - `compose`: Optional function to transform decorator arguments into metadata
423+
* - `onGet`: Optional interceptor for property reads
424+
* - `onSet`: Optional interceptor for property writes
425+
*
426+
* @returns A {@link DecoratedPropertyFactory} that creates property decorators with interception
427+
* and provides reflection methods (`reflect`, `properties`, `key`) for querying decorated properties.
428+
*
429+
* @see {@link PropertyInterceptorOptions}
430+
* @see {@link DecoratedPropertyFactory}
431+
*
282432
* @example
433+
* ```typescript
434+
* // Create an observable property decorator
283435
* const Observable = createPropertyInterceptor<string>({
284436
* onSet: (original, meta, ctx) => function(value) {
285-
* console.log(`${ctx.propertyKey} = ${value}`);
437+
* console.log(`${String(ctx.propertyKey)} changed to ${value}`);
286438
* original.call(this, value);
287439
* }
288440
* });
289441
*
290-
* // Reflection
442+
* class Store {
443+
* @Observable("count")
444+
* count = 0;
445+
* }
446+
*
447+
* const store = new Store();
448+
* store.count = 5; // logs: "count changed to 5"
449+
*
450+
* // Reflection - query decorated properties
291451
* const observed = Observable.properties(Store);
452+
* // => [{ kind: "property", name: "count", metadata: ["count"] }]
453+
* ```
292454
*/
293455
export function createPropertyInterceptor<TMeta, TArgs extends unknown[] = [TMeta]>(
294456
options: PropertyInterceptorOptions<TMeta, TArgs>,

0 commit comments

Comments
 (0)