Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commite8beb5604c

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, ... }"