Skip to content

fix(decorators): let the application own the reflect-metadata polyfill - #74

Merged
msalvatti merged 1 commit into
mainfrom
fix/reflect-metadata-polyfill
Aug 4, 2026
Merged

fix(decorators): let the application own the reflect-metadata polyfill#74
msalvatti merged 1 commit into
mainfrom
fix/reflect-metadata-polyfill

Conversation

@msalvatti

Copy link
Copy Markdown
Member

What is different here

Four decorators carry import 'reflect-metadata'. None of the other eight
@bymax-one libraries does
— this package is the only one of nine that both declares
"sideEffects": false and imports something purely for its side effect.

sideEffects imports reflect-metadata
nest-queue false yes
nest-cache · nest-storage · nest-core · nest-config · nest-realtime · nest-logger · nest-ai-tokens · nest-notification false no

Why the application should own it

reflect-metadata is a global polyfill: it mutates Reflect, and the application loads
it once in main.ts. A library that loads it too is asserting a side effect its own
manifest denies.

And it is unnecessary — NestJS already pulls it in. Measured in a clean consumer:

$ node -e "console.log(typeof Reflect.defineMetadata)"          → undefined
$ node -e "await import('@nestjs/common');
           console.log(typeof Reflect.defineMetadata)"          → function

So any consumer of this library has the polyfill before a decorator ever runs, by way of
@nestjs/common. That is why the other eight work without importing it.

The cost it was imposing

With the import present, a bundler inlines the polyfill into the consumer's bundle. A
minimal esbuild bundle of this package goes from 53 KB to 95 KB — the difference is
reflect-metadata, shipped again to an application that already loaded it.

What about tree-shaking dropping it?

That was the first worry, and it turned out not to be the problem. sideEffects: false
on the importing package does not license dropping import 'x'; the decision is made
by x's own flag, and reflect-metadata@0.2.2 declares none, so bundlers keep it.
Verified by bundling and grepping the output. The contradiction is in the manifest, not
in the behaviour — which is why this is a correctness/consistency fix rather than a bug
fix.

Scope

Removed from the four production decorators. The specs keep their own import: a test
file is its own entry point, with no application to load the polyfill for it.

Verification

typecheck · lint · build · 276 tests / 20 suites, 100% coverage · the built
dist/server/index.mjs no longer references reflect-metadata · a real consumer applies
Processor('q')(C) successfully without importing the polyfill itself.

Four decorators carried `import 'reflect-metadata'`. None of the other eight
`@bymax-one` libraries does — the polyfill belongs to the application, which loads
it once in `main.ts`, and NestJS itself pulls it in: importing `@nestjs/common`
alone takes `Reflect.defineMetadata` from `undefined` to `function`.

Carrying it here also contradicted this package's own `"sideEffects": false`. That
flag says no module in the package has a side effect, and an import whose entire
purpose is its side effect is exactly one. Bundlers happen not to drop it — the
decision is made by the imported package's flag, and `reflect-metadata` declares
none — but a manifest should not assert something the code disproves.

The measurable cost was in a consumer's bundle: with the import present, esbuild
inlines the polyfill, taking a minimal bundle from 53 KB to 95 KB even when the
application already loaded it.

Specs keep their own `import 'reflect-metadata'`: a test file is its own entry
point and has no application to load it.
Copilot AI lite review requested due to automatic review settings August 4, 2026 10:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes import 'reflect-metadata' side-effect imports from the library’s production decorators so the consuming application (or NestJS) owns loading the global reflect-metadata polyfill, aligning runtime behavior with "sideEffects": false and avoiding bundling the polyfill into consumer bundles unnecessarily.

Changes:

  • Removed reflect-metadata side-effect imports from four server decorators.
  • Minor formatting cleanups in Process (ternary) and WorkerEventName (union type).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/server/decorators/processor.decorator.ts Drops reflect-metadata side-effect import from @Processor.
src/server/decorators/process.decorator.ts Drops reflect-metadata side-effect import from @Process; minor formatting adjustment.
src/server/decorators/on-worker-event.decorator.ts Drops reflect-metadata side-effect import from @OnWorkerEvent; union type formatting change.
src/server/decorators/on-queue-event.decorator.ts Drops reflect-metadata side-effect import from @OnQueueEvent.
Suppressed comments (1)

src/server/decorators/processor.decorator.ts:11

  • With the reflect-metadata side-effect import removed, these decorators now assume the polyfill has already been loaded. If a consumer applies @Processor before importing reflect-metadata (or otherwise ensuring Reflect.defineMetadata exists), they’ll get a confusing runtime TypeError. Consider adding a small runtime assertion near the Reflect.defineMetadata(...) call to throw an actionable error when the polyfill is missing.
import { DEFAULT_WORKER_CONCURRENCY } from '../constants/default-options'
import type { ProcessorMetadata } from '../interfaces/processor-metadata.interface'
import type { WorkerOptions } from '../interfaces/worker-options.interface'
import { PROCESSOR_METADATA_KEY } from './metadata-keys.constants'

Comment thread src/server/decorators/on-worker-event.decorator.ts
Comment thread src/server/decorators/process.decorator.ts
Comment thread src/server/decorators/on-queue-event.decorator.ts
@msalvatti
msalvatti merged commit 55fbe6c into main Aug 4, 2026
18 checks passed
@msalvatti
msalvatti deleted the fix/reflect-metadata-polyfill branch August 4, 2026 10:39
@msalvatti msalvatti mentioned this pull request Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants