feat: v1.0.0-alpha.0 — Stage-3 decorators rework#20
Closed
Zendrex wants to merge 13 commits into
Closed
Conversation
- tsconfig: enable Stage-3 (`useDefineForClassFields`, `experimentalDecorators` off), drop legacy emit - Drop `reflect-metadata` peer dep; rely on host `Symbol.metadata` with cross-runtime fallback in `src/runtime/symbol-metadata.ts` - Add `src/shim.ts` side-effect entry exposing `Symbol.metadata` for hosts without it; expose `./shim` subpath export with `sideEffects` declared - Add `src/runtime/prepare.ts` (deferred-metadata flush) with WeakSet sentinel short-circuit and degraded-path guard - Add `src/runtime/prototype-chain.ts` walker shared by reader helpers - Add `scripts/check-helper-imports.ts` build guard against helper-package leaks in dist/ - Cover runtime + shim with unit tests and `shim-harness` fixture
- Replace single `store.ts` with focused modules: `class-meta-store` (one value per class+key, dup-detected) and `member-meta-store` (per-class WeakMap of member buckets with static flag folded into entries) - Introduce `MetadataKey<T,C>` brand and `cardinality-registry` so reads/writes share one source of truth for unique vs list keys - Add `metadata-deferred-queue` for instance-member metadata buffered until first `new`, drained by `runtime/prepare` - Add `metadata-ctor-correlation` (bidirectional ctor↔metadata-bag WeakMaps) for ancestor-walking reads - Add `prepared-sentinel` (WeakSet) so `prepare(ctor)` is idempotent - Extract `append-guards` (`requireCardinality`, `assertNotDuplicate`) and `store-walk` (`readValues<T>` is the single trust boundary for `unknown[]`→`readonly T[]` cast) to share between class and member stores - Add `has-any-meta` (single-pass chain probe) and `get-or-create` Map/WeakMap upsert helper - Tighten `types.ts`: `MetadataKey` brand, `MemberEntry`, `MemberBucket`, `Deferred` shapes - Full unit coverage for every new module
- Collapse five subclass bodies behind a single `defineAnnotateError` factory; each subclass now declares only its name + context shape - Centralize slot rendering in `formatSlot` (handles symbol, anonymous, static-vs-instance, list-cardinality + label) - New subclasses: `ValidationError`, `InvalidDecorationTargetError`, `MissingMetadataError`, `UnregisteredClassError`; `DuplicateMetadataError` carries cardinality + label, looks up cardinality dynamically - `ValidationError` extracts message-aware reason from non-Error throws - Tightened context-shape naming and the constructor's no-narration policy - Full unit suite covers every subclass and the slot formatter
- Rewrite all four decorator/interceptor factories for TC39
Stage-3 (`createClass/Method/Property/AccessorDecorator`,
`createMethod/AccessorInterceptor`) with constraint generics:
`TInstance`, `TMethod`, `TField`
- Auto-`prepare(ctor)` on `applied`/`appliedOwn`/`metadata`
reader paths so deferred instance-member metadata flushes on
first reflective read
- Mint metadata keys via `mintMetadataKey<T>` overloads
(unique vs list cardinality), removing the old `unique` option
and ad-hoc `generateKey`
- Add list-cardinality variants: `createList*Decorator/Interceptor`
(inner-first ordering preserved end-to-end)
- Public surface consolidates onto `decorate.{class,method,
property,accessor}[.list]` and
`intercept.{method,accessor}[.list]` namespaces
- Extract `validator-chain` (composes per-decorator validators
with `requireInstanceOf`/typed validator chain) and
`validator-types` aliases
- `shared`: hoist `prepareFactoryShell`, `commitDecoration`
envelope, label/compose plumbing; `compose` overloads for
identity-tuple vs configured-mapper paths
- `types`: factories carry `FactoryGenerics`
(`MetadataOf`/`ArgsOf`/`ThisOf`/`CardinalityOf`); `derive`
drops redundant generics
- Drop `createParameterDecorator` (Stage-3 has no parameter
primitive) and the standalone `property-interceptor`
(subsumed by accessor-interceptor)
- Full unit suite per factory + cardinality variant + derive +
validator-chain + shared
- Rewrite `Reflector` for WeakMap-backed reads (no host `Symbol.metadata` walking required for own-bag semantics) - Brand-driven typing: `DecoratedMethod/Property/Class` split into `Unique` and `List` flavors that infer from `MetadataKey<T,C>` cardinality; `methods()`/`properties()`/ `class()` and `all()` overloads narrow on brand - Specialize factory readers; drop scalar reader methods + `Scalar` types now subsumed by `Unique` - Add `ScopedReflector` overloads (cardinality-narrowing) and `createScopedReflector` brand inference - Cache `ReflectorImpl` per ctor (perf) with covered tests - Single-pass member snapshot (T2.3); document cardinality fallthrough; remove `DecoratedBase` - `class-name` helper extracted for consistent slot rendering - `resolve-instance` aligned to new reader path - Unit suite: brand narrowing, reflect-cache, cross-cardinality rejection, scoped-reflector, name helper
- `src/index.ts`: Stage-3 public API barrel — `decorate.*` / `intercept.*` namespaces, `mintUniqueKey` / `mintListKey`, `prepare`, free `reflect`, `createScopedReflector`, branded `MetadataKey<T,C>`, all `Decorated*Unique` / `*List` types, full error class set - README: rewrite for Stage-3 v1 — constraint generics, lazy prepare semantics, accessor-vs-property interception, list cardinality + Tag example, validator/`requireInstanceOf` fire-on-first-`new` semantics - Integration suite: cross-class isolation, interceptor timing (decoration-order independence), introspection-semantics contract post-removal of `ensureProperty`, materialization + subclass regression, mixed unique+list cardinality through one reflector, reflect-instance rewrite for Stage-3 fixtures - `tests/unit/types.test-d.ts`: consolidated type-level assertions covering factory generics, metadata key brand, reflector brand narrowing, scoped-reflector inference - `.changeset/pre.json`: enter pre-release `alpha` mode, reset changesets array - `.changeset/stage3-alpha.md`: single consolidated alpha.0 changeset (supersedes the staged stage3-decorators / alpha-1-api-rework / cardinality-typed-keys entries)
Contributor
|
Preview published — install with: bun bun add https://pkg.pr.new/@zendrex/annotate@1295c8cnpm npm install https://pkg.pr.new/@zendrex/annotate@1295c8cBuilt from 1295c8c |
Closed
Moves the per-ctor WeakMap next to the class it backs, matching file-private cache placement in the class-design guideline. Behavior unchanged.
…lacement Stage-3 field decorators that return a value-replacement initializer closure are unsafe under Bun 1.3: the transformer emits a module-scope `var _init = __decoratorStart(...)` per class, so multiple decorated classes in one module shadow the binding and share addInit slots — only the last-registered initializer survives. intercept.field sidesteps this by resolving all per-decoration state from `this.constructor` and a module-global factory registry inside an addInitializer body. Field decorators never return a replacement closure, so the Bun shadowing bug cannot bite. Companion intercept.field.list ships for list-cardinality metadata. `InterceptorContext.kind` gains `"field"`. New exported type `FieldInterceptorOptions<TMeta, TArgs, TField>`.
Replace local makeInstanceReader/makeStaticReader with createMemberMetadataReader from ./shared (already used by accessor and method factories). Inline the trivial registerFieldInterceptor Map.set wrapper. Drop the Bun-bug rationale duplication on FieldInterceptorOptions; canonical explanation lives on createFieldInterceptor.
…nstruction recovery Added the applyFieldInterceptors function to enable idempotent recovery of field values after class construction. This function leverages a per-constructor cache to optimize performance and ensure correct behavior under Bun 1.3's addInit shadowing issue. Updated the intercept.field API to include this new functionality, enhancing the field decorator's capabilities. Also, added integration tests to validate the new behavior across multiple classes.
Owner
Author
|
Superseded by the dev branch. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First v1 alpha. Hard replace of the v0.x experimental-decorator surface — TC39 Stage-3 throughout, no
reflect-metadata, branded cardinality-typed metadata keys, consolidateddecorate.*/intercept.*namespaces, brand-driven reflector.Squashed from 118 working commits on
feat/stage3-decoratorsinto 6 thematic commits + version bump:build:Stage-3 tsconfig + Symbol.metadata runtime + shim + helper-leak guardfeat(metadata):WeakMap stores, branded keys, cardinality registryfeat(errors):unified hierarchy viadefineAnnotateError+formatSlotfeat(factories):Stage-3decorate.*/intercept.*with unique + list cardinalityfeat(reflector):WeakMap-backed brand-drivenReflector+ScopedReflectorfeat:publicindex.ts, integration tests, README rewrite, consolidated changesetchore: release v1.0.0-alpha.0(changeset version bump)Full breaking-change list and migration table in
CHANGELOG.md/.changeset/stage3-alpha.md.Test plan
bun run check-typesclean (tsc -b)bun test— 235/235 pass (37 files)bunx ultracite checkclean (86 files)bun run build(tsdown) producesdist/index.{mjs,cjs,d.mts,d.cts}+dist/shim.{mjs,cjs,...}bun run check:dist-helpersconfirms no helper-package leaks indist/decorate.method+reflect()round-tripexperimentalDecorators: falseprojectimport "@zendrex/annotate/shim"