Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/express-cargo/src/analysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ function collectNestedClasses(classMeta: CargoClassMetadata): ClassConstructor[]
if (isUserDefinedClass(typeFn)) {
nested.push(typeFn)
} else if (typeFn.length === 0) {
// @Type(() => Foo) — Thunk form. Resolver form (`(data) => Foo`) can't be
// evaluated statically and will throw on the no-arg call; we catch and skip.
// @Type(() => Foo) — Thunk form. A Resolver declares a parameter, so the arity
// check already excludes it; a zero-arg function may still read runtime data.
try {
const result = (typeFn as TypeThunk)()
if (isUserDefinedClass(result)) nested.push(result)
} catch {
// Resolver-shaped functions need runtime data; ignore.
// Needs runtime data; resolved during binding instead.
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/express-cargo/src/decorators/missingHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CargoClassMetadata } from '../metadata'

/**
* Marks a property as optional.
* If the property is missing in the request, it will be ignored during validation.
* When the property is missing from the request it is set to `null` and its validators are skipped.
*/
export function Optional(): PropertyDecorator {
return (target: any, propertyKey: string | symbol) => {
Expand Down
5 changes: 2 additions & 3 deletions packages/express-cargo/src/decorators/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ export function Request<T>(transformer: (req: Request) => T): TypedPropertyDecor
/**
* Defines a virtual property that is calculated based on other properties of the instance.
* @remarks
* **Important:** This property must be declared **after** all the properties it depends on.
* Since decorators are evaluated in the order they are defined, accessing a property
* declared below the virtual property will result in `undefined`.
* Every source field is bound before any virtual field runs, so a virtual property may read them regardless of declaration order.
* Virtual fields are computed in declaration order, however, so one that reads **another virtual field** must be declared after it.
* @param transformer - A function that receives the current object instance.
* @example
* ```ts
Expand Down
2 changes: 1 addition & 1 deletion packages/express-cargo/src/decorators/typeHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function List(elementType: ArrayElementType): TypedPropertyDecorator<Arra

/**
* Decorator to define the target class type for a property.
* * Supports three strategies:
* Supports three strategies:
* 1. **Thunk**: `() => Class` (Static types & circular refs)
* 2. **Resolver**: `(data) => Class` (Dynamic polymorphism)
* 3. **Discriminator**: Structural mapping via `options.discriminator`.
Expand Down
4 changes: 2 additions & 2 deletions packages/express-cargo/src/decorators/validators/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { addValidator } from './addValidator'
* @param message - Optional custom error message.
*/
export function ListContains(values: any[], comparator?: ArrayComparator, message?: CargoErrorMessage): TypedPropertyDecorator<any[]> {
// Pre-split only when using default comparison (Set + deepEqual optimization)
// Pre-split only when using default comparison (Set + isDeepEqual optimization)
const expectedPrimitives = !comparator ? values.filter(v => v === null || typeof v !== 'object') : []
const expectedObjects = !comparator ? values.filter(v => v !== null && typeof v === 'object') : []

Expand Down Expand Up @@ -73,7 +73,7 @@ export function ListContains(values: any[], comparator?: ArrayComparator, messag
* @param message - Optional custom error message.
*/
export function ListNotContains(values: any[], comparator?: ArrayComparator, message?: CargoErrorMessage): TypedPropertyDecorator<any[]> {
// Pre-split only when using default comparison (Set + deepEqual optimization)
// Pre-split only when using default comparison (Set + isDeepEqual optimization)
const excludedPrimitives = !comparator ? values.filter(v => v === null || typeof v !== 'object') : []
const excludedObjects = !comparator ? values.filter(v => v !== null && typeof v === 'object') : []

Expand Down
2 changes: 1 addition & 1 deletion packages/express-cargo/src/rules/errors.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ClassConstructor } from '../types'
import type { RuleViolation } from './types'

/** Thrown by `validateCargoSchema()` when one or more rule violations are detected. */
/** Thrown by `bindingCargo()` at route registration when the cargo class breaks one or more schema rules. */
export class CargoSchemaError extends Error {
name: string
violations: RuleViolation[]
Expand Down
7 changes: 1 addition & 6 deletions packages/express-cargo/src/rules/ruleChecker.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import type { CargoFieldMetadata } from '../metadata'
import type { FieldRuleFn, FieldState, RuleChecker, RuleViolation } from './types'

/**
* Contract for a single rule checker.
*
* Inspects one class (`ctx.cargoClass`) and returns every violation it finds.
* Nested-DTO traversal is handled by `validateCargoSchema`, so checkers don't recurse.
*/
/** Pre-computes the per-field view a {@link FieldRuleFn} receives. */
function buildFieldState(propertyKey: string | symbol, fieldMeta: CargoFieldMetadata, siblingFields: ReadonlySet<string | symbol>): FieldState {
const appliedSelf = fieldMeta.getAppliedDecorators('self')
const appliedEach = fieldMeta.getAppliedDecorators('each')
Expand Down
5 changes: 5 additions & 0 deletions packages/express-cargo/src/rules/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ export interface RuleViolation {
message: string
}

/**
* Contract for a single rule checker: inspects one class (`ctx.cargoClass`) and returns
* every violation it finds. Nested-DTO traversal is handled by `validateAnalysis`, so
* checkers don't recurse.
*/
export type RuleChecker = (ctx: RuleContext) => RuleViolation[]
2 changes: 2 additions & 0 deletions packages/express-cargo/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import type { CargoClassMetadata } from './metadata'
* - `params`: req.params
* - `header`: req.headers
* - `session`: req.session
* - `file`: one uploaded file, via the configured file locator
* - `files`: every uploaded file sharing the field name
*/
export type Source = 'body' | 'query' | 'params' | 'header' | 'session' | 'file' | 'files'

Expand Down
Loading