Builtin Library
README.md
asset_ref_shapes
Publishes what every AssetRef<category> answers to, so a member read on an
asset-typed value is checked against the category's own surface.
An asset category's per-instance surface is authored: a
<name>.assetType/behavior.luau declares M.ref = { ... }, and each category
declares its own. The members an AssetRef<inputMap> carries are therefore
knowable only to inputMap itself — no fixed set of types covers the ones a
world defines, and the checker has no way to guess them.
This module reads each registered category's M.ref table from its source
(never executing it), renders it as a Luau table type, and hands the set to
the engine. From there a ref:method(...) on an asset-typed value resolves
against the category's real surface: a name it does not carry is reported
along with the ones it does.
Results shaped by the asset
Some results are shaped by the asset rather than by its category —
inputMapRef:activate() answers one handle per binding THAT map declares, a
set that is authored and differs per map. A category states those by
declaring refShapes, and this module asks it once per instance and
publishes the answers keyed by asset identity. types/assetType documents
the authoring side.
When it publishes
Publishing is driven by use: every entry point that produces diagnostics
calls ensure, which is a no-op once the set is current. A world load, a
write inside a type definition, and a write inside any asset whose container
computes shapes each mark it stale, so the cost lands on the next check and
only if one comes.
The sweep is complete and replaces what was published before, so a category whose type is removed stops being published.
A check is answered in the turn it is asked in, so the sweep ensure runs
completes in that turn. What makes it cheap is what the module keeps: each
type's parsed surface until its behavior.luau changes, and each instance's
answer until a write lands inside it. prepare fills those across frames,
handing the frame back between types and instances once the frame's slice of
cooperative work (modules.frame_budget) is spent, for a caller that can
wait: the LSP's background drain runs it before the check it makes, so the
publish that check runs reads what was kept.
Reading what the checker believes
From a call site, a type that is correct and one that was never published are
the same absence of a diagnostic. published() tells them apart:
local shapes = require("@builtin::assetTypes.assetType.shared.refShapes").published()
shapes.categories.inputMap
--> "{ activate: () -> any, controls: () -> { any }, ... }"
shapes.returns["@builtin::inputMaps.default"].activate
--> "{ crouch: Handle, interact: Handle, jump: Handle, ... }"