Skip to content

refactor(interface-data-api): build on interface-core 0.4's typed decorator targets - #84

Merged
Upd4ting merged 6 commits into
campaign/integrationfrom
refactor/decorator-targets
Nov 2, 2026
Merged

Upd4ting merged 6 commits into
campaign/integrationfrom
refactor/decorator-targets

Conversation

@Upd4ting

@Upd4ting Upd4ting commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

Why

Antony's goal: delete every AntelopeJS guardrails.ratchet.json through verified PRs on campaign/integration, each contract change proven by tarball against every consumer, with no fallback or back-compat. This PR belongs to W7, the last contract wave: interface-core 0.4 types decorator targets as DecoratorTarget, defaults Class<T> to object and types log arguments as unknown[] (interface-core PR, tarball 6a8b526). The field decorators handed interface-core's any target to onField: 12 no-unsafe-argument rows in metadata.ts.

What changed

  • onField(target: DecoratorTarget, …) reads the decorated class through the existing isClass guard before GetMetadata(owner, DataAPIMeta) (throws A field decorator applies to a class. otherwise; a member decorator's target always has its class as constructor, so no real caller reaches it).
  • RegisterDataController passes the class prototype read through the existing prototypeOf guard to the parameter and route decorators, and builds the route location from the controller class (target.location), since ControllerMeta.location may now be undefined. It still reads ControllerMeta.computed_props as before.
  • Filter returns interface-core's PropertyDecorator (the type of the factory it wraps) instead of (target: T, propertyKey: string | symbol) => void, so a data type that applies a filter to the target it was handed (dms decorateColumn) needs no stand-in row type.
  • Pins interface-core 6a8b526, interface-api 2d1cc37, interface-database-decorators 1234214 and core 68d5ce0, and drops the @antelopejs/api>@antelopejs/interface-core: 0.2.7 override, so the tree holds one interface-core.

Rows (ratchet:update)

  • typescript/no-unsafe-argument: 12 -> 0
  • typescript/no-unsafe-assignment: 6 -> 0
  • typescript/no-unsafe-return: 4 -> 2

Measured rows 15 -> 3. The assignment and return ceilings were above their counts on integration; the 12 argument rows are this wave's.

Proofs

  • pnpm run checks (CI state, network namespace): Checks: 9 passed, 0 failed.; ajs module test .: 190 passing.
  • Published surface (base built in its own clone): only Filter's return type changes; runtime export keys identical (4 entries). api-diff suppressions: 0 to 0, @ts-expect-error 5 to 5, SAFETY: 4 to 4.
  • Tarball: antelopejs-interface-data-api-7a8719d.tgz, TGZ_VERSION=0.6.0 KEEP=1 at 7a8719d, sha256 05044cb0607b154f1efc4780fa783548e6101c134da56ca0e27ce86b8a55b247.

Attention

  • Filter(fn) no longer checks at the decoration site that the decorated class matches fn's row type T; T still types fn.
  • src/metadata.ts is 19994 bytes, 6 under the size cap.

Round 2 pins (supersede the pin and tarball lines above)

Head 1c2d010. interface-api#67 raised its interface-core peer floor to >=0.4.0 <1.0.0, so interface-api was repacked and interface-core#62 and antelopejs#356 landed; integration is merged in.

  • interface-api 015a93a (0.3.0, sha256 8e017528574748687b246df07affa6cbcfbe931cc6086fcb5dd2c7a28c1f0d8e, interface-core peer >=0.4.0 <1.0.0), interface-core f7df541 (0.4.0, 30391718e65481815db5b5bb10e7eabb1feac13165005ee46e54b592f3676c69), core 184eb90 (1.20.0, 0a51c9c2da58c2586bfa5095b1da92a7b74130acf1e64820bfd5f98eaa4200f9)
  • Own tarball antelopejs-interface-data-api-1c2d010.tgz (TGZ_VERSION=0.6.0), sha256 583046f873e66224df3ca03174642c2ac3646b48950f16f056cb64da03003d90. Its dist equals 7a8719d; only README and devDependency pin text differ.
  • Proof: pnpm run checks and tgz-proof PASS, Checks: 9 passed, 0 failed.; CI green.

Verifier correction (w7-b, 28ffb6e)

  • Rows against current integration (eccde48), ratchet-mode oxlint: 15 -> 3, typescript/no-unsafe-argument 12 -> 0 only. The no-unsafe-assignment 6 -> 0 and no-unsafe-return 4 -> 2 lines compare with the older merge-base 169448e; integration had already lowered those ceilings to 0 and 2.
  • onField's isClass check could not fail for target.constructor (typed Function): isClass was typeof value === "function". With the check replaced by if (false) in the built output, ajs module test . still passed 190 of 190.

Round 3 (bca1ca6)

  • isClass now requires a function with an object prototype (arrow functions, methods and bound functions fail it). A generator function has a prototype too, a language limit shared with every prototype test. prototypes.ts is its other caller and passes a prototype object's constructor, a class.
  • New test src/tests/index/field_decorator_target.test.ts: a target whose constructor is an arrow function must throw A field decorator applies to a class., and a class target declares its field. No as, no directive.
  • Mutation, built output, ajs module test .: baseline 193 passing. if (!isClass(owner)) replaced by if (false): 191 passing, 2 failing. isClass reduced to isFunction(value) (the old check): 191 passing, 2 failing.
  • pnpm run checks (no dist, GITHUB_BASE_REF=campaign/integration): 9 passed, 0 failed. Integration was 0 commits ahead.
  • Tarball: interface-data-api 0.6.0 packed from bca1ca6, sha256 b179ddc85718e17fae2b90125afa9aa88df348785cd20631a69e80a7a2d6467f (supersedes 1c2d010, sha256 583046f873e66224df3ca03174642c2ac3646b48950f16f056cb64da03003d90). tgz-proof against the core 184eb90, interface-core f7df541, interface-api 015a93a and decorators 1234214 tarballs: PASS, 9 passed, 0 failed.

Verifier correction (w7-r3, bca1ca6)

  • Test counts at bca1ca6: baseline 192 passing (not 193). Each mutation, onField's check set to if (false) and isClass reduced to isFunction, gives 191 passing, 1 failing (not 2): the 193/2 run included a "constructor is a method" test that is not in this head.
  • RegisterDataController now reads the prototype through prototypeOf, whose refusal (on integration since 1c5030c) no test reaches: with it set to if (false), 192 of 192 still pass. A bound-class test can cover it.
  • isClass lets generator and async generator functions through (they have an object prototype but are not constructible) and refuses bound classes (constructible, no prototype).

…orator targets

The field decorators get a DecoratorTarget and read the decorated class
through the isClass guard before they look up its DataAPIMeta.
RegisterDataController hands the parameter and route decorators the
class prototype read through prototypeOf, and builds the route location
from the controller class, since ControllerMeta.location may now be
undefined.

Pins interface-core 6a8b526 (0.4.0), interface-api 2d1cc37,
interface-database-decorators ca0851d and core 68d5ce0 (1.20.0), and
drops the api-scoped interface-core 0.2.7 override so the tree holds one
interface-core.
…ertyDecorator

Filter returned a decorator whose target was the filter function's
row type, while the factory it wraps takes a DecoratorTarget. It now
returns that PropertyDecorator, so a data type that applies a filter to
the target it was handed needs no stand-in row type.
# Conflicts:
#	guardrails.ratchet.json
…tor/decorator-targets

# Conflicts:
#	.campaign/tgz/SHA256SUMS
#	README.md
#	package.json
#	pnpm-lock.yaml
#	pnpm-workspace.yaml
@Upd4ting
Upd4ting marked this pull request as ready for review November 2, 2026 11:54
@Upd4ting
Upd4ting merged commit d20fad6 into campaign/integration Nov 2, 2026
7 checks passed
@Upd4ting
Upd4ting deleted the refactor/decorator-targets branch November 2, 2026 11:55
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.

1 participant