fix(native): require a runtime provider for native identity - #2274
Merged
Conversation
Claim issues #2200 and #2238 together because both residuals cross shared metadata identity helpers and require one combined provenance, spelling, composition, and consumer matrix. Constraint: Campaign pull requests cannot change versions, tags, releases, or publication state. Rejected: Separate helper edits | independent fixes at one shared classifier would duplicate integration risk and miss cross-effects. Confidence: high Scope-risk: broad Directive: Preserve issue invariants while treating every proposed metadata classification mechanism as a hypothesis until the complete matrices execute. Tested: Independent real-transform reproductions, expanded consequence matrices, and issue critic reviews passed on ce88a88. Not-tested: Product implementation and integration gates are intentionally pending. Related: #2200 #2238
`metadata_symbol_is_global_type` decided native identity with
`checker.ResolveName(name, nil, SymbolFlagsType, false) == symbol`, so a
project whose only `File`/`Blob` declaration is its own `declare global`
block won that lookup. Global-table resolution proves a declaration is
global, not that a runtime provides the constructor.
The predicate now requires a runtime provider: a default-library
declaration, a declaration inside an ambient `node:buffer`/`buffer`
module, or a constructor binding that resolves into either. The third
path is load-bearing because @types/node 25 declares the bare global as
`interface Blob extends _Blob {}` plus `var Blob: typeof buffer.Blob` in
an ordinary package `.d.ts`, so only the value binding carries provenance.
The same predicate gates `Map`/`Set`, `WeakMap`/`WeakSet` through
`iterate_metadata_native.go`, and all four intersection identity sites,
so one predicate owns the whole consequence surface.
`metadata_symbol_from_default_lib` answers default-library provenance
from the `lib.*.d.ts` base name, which `libReplacement` defeats: real lib
files resolve to `node_modules/@typescript/lib-es2022/index.d.ts`. A gate
built on it would demote genuine `Date`/`Uint8Array`/`Map` to structural
in any project using `@typescript/lib-*`. The new checker-keyed
`MetadataDefaultLibrary` registry, modelled on the existing
`MetadataDependency` registry and registered from both program-loading
entrypoints, answers it from `prog.TSProgram.IsLibFile` instead, the same
lesson `cmd/ttsc-typia/transform.go` already learned in #2108. The old
helper and its other callers are left untouched.
NOT COMPILED OR EXECUTED: written under a maintainer stop on all local
builds and tests. CI is the first thing to compile or run any of it.
Neither correction changes what the regression proves; both were making
it unable to prove anything.
`typia.llm.schema<File, "chatgpt">()` is malformed regardless of this
issue: the signature is `schema<T, Config = {}>($defs)`, so the model
string landed in `Config`, which must be a literal object type. The
transform rejected the whole fixture with a diagnostic, so every
assertion after it was unreachable.
The runner's expected case count was 26 while its table contains 28
assertions: 11 for the is/assert/assertGuard/validate/equals/random
matrix, 15 for the createIs forms across direct, alias, re-export,
branded intersection, union, and nested positions, and 2 near-miss name
controls. The count is asserted rather than the exit code precisely so a
row that stops running cannot pass as a row that ran and agreed, so a
wrong count defeats the guard it exists to be.
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.
Purpose
Closes #2200, which was removed from the consolidated campaign wave (#2266) because no in-repo fixture could express its premise. #2273 fixed that, and the work is now verifiable.
Problem
metadata_symbol_is_global_typedecided native identity withchecker.ResolveName(name, nil, SymbolFlagsType, false) == symbol. Winning the checker's global type table proves a declaration is global, not that a runtime provides the constructor, so a project whose onlyFileorBlobdeclaration is its owndeclare globalblock won that same lookup. Its members were erased,input instanceof Filereplaced them, and the check throwsReferenceErrorwherever no runtime supplies that constructor.One predicate owns the whole surface: it also gates
Map/Set,WeakMap/WeakSetthroughiterate_metadata_native.go, and all four intersection identity sites.Approach
Native identity now requires a runtime provider — a default-library declaration, a declaration inside the ambient
node:buffer/buffermodule, or a constructor binding that resolves into either. The third path is load-bearing rather than defensive: @types/node 25 declares the bare global asinterface Blob extends _Blob {}plusvar Blob: … typeof buffer.Blobin an ordinary package.d.ts, so nothing but the value binding carries its provenance.Answering "is this a default library file" from the
lib.*.d.tsbase name would have made this worse than the bug it closes: underlibReplacementreal lib files resolve tonode_modules/@typescript/lib-es2022/index.d.ts, so genuineDate,Uint8Array, andMapwould be demoted to structural in any project using@typescript/lib-*. A checker-keyedMetadataDefaultLibraryregistry — modelled on the existingMetadataDependencyregistry in the same package, registered from both program-loading entrypoints — answers it fromprog.TSProgram.IsLibFileinstead, the lessoncmd/ttsc-typia/transform.goalready learned in #2108.IsLibFilewas verified to be set membership (p.libFiles[sourceFile.Path()]), not a name test, so it is a genuinely stronger oracle.metadata_symbol_from_default_liband its other callers are untouched.What master already fixed
The original name-only classification was closed by
edd86583f2, refined by #22395473622897and #1568c7f7939d2b. This closes the reopened residual — global-table resolution treated as proof of a runtime provider — not the original report.Verification
Three regressions, all executing real transforms and then running the emitted validators in Node:
TestUserGlobalNativeIdentityTransformis/assert/assertGuard/validate/equals/random, direct and factory forms, alias, re-export, branded intersection, union, and nested positions, plus the json/protobuf/llm schema surfaces a native classification removes entirely — while a third project where @types/node provides the bridge keeps everyinstanceof. 28 runtime cases.TestDefaultLibrarySpoofNativeIdentityTransformlib.*.d.ts-named package declaration is refused.TestLibReplacementNativeIdentityTransformlibReplacement.The whole
packages/typia/native/...tree passes locally.Fail-before is documented rather than assumed: all three failed before #2273, and the instrumented evidence that the product code was already correct while the fixtures could not express their premise is on the #2200 and #2268 threads.
Two fixture premises corrected
Neither changes what the regression proves; both were stopping it from proving anything.
typia.llm.schema<File, "chatgpt">()is malformed regardless: the signature isschema<T, Config = {}>($defs), so the model string landed inConfig, which must be a literal object type. The transform rejected the whole fixture, making every later assertion unreachable.createIsforms, 2 near-miss name controls. That count is asserted instead of the exit code precisely so a row that stops running cannot pass as a row that ran and agreed, so a wrong count defeats the guard it exists to be.Known residual, filed separately
metadata_symbol_from_default_libstill answers default-library provenance from thelib.*.d.tsbase name for its remaining callers, includingmetadata_is_global_function_interface. This pull request routes only its own new predicate around that; the rest is untouched and left to its own change.