Log inGet started

lsp

Updated October 16, 2026

Embedded Luau language server — check / search / inspect

require("@builtin::modules.api.engine.lsp") -- lsp (also available as global 'lsp')

The lsp namespace: 78 functions.

globals/lsp/analysisFlags

lsp.analysisFlags() -> AnalysisFlags

The feature-flag configuration upstream Luau's analysis library (the linter and .luaurc parser behind these checks) runs under, read back from Luau's flag registry. It is upstream's own: every Luau* flag the analysis sources declare is on unless upstream's experimental list names it, and every other flag they declare keeps the value Luau registers it at. The flags the analysis library reads from the parser and the VM are shared: they run at the value the engine's runtime gives them, and one that differs from upstream's carries its deviation. flags lists every flag those sources declare or read, sorted by name; atUpstream counts the linked ones whose value is upstream's. available is false, with reason, on a build that does not link the analysis library.

Returns AnalysisFlags — { available, reason?, declared, shared, linked, atUpstream, flags }.

globals/lsp/check

lsp.check(path: string, opts: CheckFileOpts?) -> DiagnosticsResult

Validate a single .luau file in the VFS and return its diagnostics. A path the check could not read comes back as one lsp-check-* error naming the path and the reason, and a file past the 2 MiB analysis bound as one source-too-large warning with checked = false, so checked with errors == 0 means a code body was analysed and is clean. opts.upstream (true, or the options of lsp.upstreamCheck) also checks the file with upstream Luau's type checker and sets upstream to what lsp.upstreamCheck returns for it, whose modules say how the check of each module it reached ended and how long it took; the diagnostics and counts stay the engine checker's.

Parameters

  • path string — VFS path.
  • opts CheckFileOpts (optional) — { severity?, limit?, context?, upstream? }.

Returns DiagnosticsResult — Array of diagnostic tables.

local diags = lsp.check("/zero/source/main.luau")

globals/lsp/checkAll

lsp.checkAll(opts: CheckAllOpts?) -> CheckAllResult

Validate the user's Luau scripts and return an aggregate summary plus diagnostic list. opts.scope = "user" (default) skips library mounts; "all" includes them. The sweep is time-budgeted (ZERO_EXECUTE_INLINE_BUDGET_MS, ~20s by default): if the budget elapses it returns the partial result gathered so far with budgetExceeded = true rather than blocking the engine. opts.budgetMs sets the budget for the call and opts.maxFiles caps the files it checks. opts.upstream (true, or the options of lsp.upstreamCheck) also checks each file with upstream Luau's type checker once the engine's own pass is done, within the same budget, and sets upstream to what that pass found: every module it reached, once, with how its check ended ("completed", "timedOut", "internal", or "notAnalysable" with the reason) and how long it took, reused marking a module checked before the pass reached it and unchanged since. The pass starts no file once the budget is spent, so it ends within the budget plus the check of the one file in progress.

Parameters

  • opts CheckAllOpts (optional) — { scope?, severity?, limit?, budgetMs?, maxFiles?, upstream? }.

Returns CheckAllResult — { filesChecked, errors, warnings, info, hints, budgetExceeded, diagnostics, upstream? }.

globals/lsp/checkCode

lsp.checkCode(source: string, opts: CheckOpts?) -> DiagnosticsResult

Validate inline Luau source without a backing file. Useful for checking code before writing it to disk. A source past the 2 MiB analysis bound comes back as one source-too-large warning with checked = false.

Parameters

  • source string — Luau source.
  • opts CheckOpts (optional) — { severity?, limit?, context? }.

Returns DiagnosticsResult — Array of diagnostic tables.

globals/lsp/checkDirty

lsp.checkDirty() -> DiagnosticsResult

Drain the dirty-file set populated by the hot-reload hook, validate each, and return the combined diagnostic list.

Returns DiagnosticsResult — Array of diagnostic tables.

globals/lsp/definitions

lsp.definitions(opts: DefinitionsOpts?) -> Definitions

The engine's API as a Luau definitions source, upstream Luau's .d.luau form (declare statements and export type aliases), generated from the live API registry: the documented members, the --!global-types aliases and the asset-category shapes lsp.check types against, bound under the same names. It declares each global the calling script's globals hold, so a user script, a trusted script and a worker each get the globals they have; a name upstream's builtin environment declares keeps upstream's type and is left out. lsp.upstreamCheck loads the same source into every module it checks, and an external editor running luau-lsp reads it as a definitions file (lsp.exportDefinitions writes it out). It is generated again whenever the registry changes, a library hot reload included. package (@zero) keys each global's documentation symbol (@zero/global/<name>, @zero/global/<name>.<member>). Each type string the registry holds is declared as written, or listed in unrepresented with the reason and declared any; a declaration upstream refuses to load (an alias that reaches itself through aliases alone) is listed with upstream's reason, so the rest of the source loads. opts.documentation = true also returns documentation, the JSON documentation file luau-lsp reads beside the definitions: each symbol's signature and description. available is false, with reason, on a build that does not link the analysis library.

Parameters

  • opts DefinitionsOpts (optional) — { documentation? }.

Returns Definitions — { available, reason?, package, source, bytes, globals, types, unrepresented, documentation? }.

local d = lsp.definitions()
print(d.bytes, #d.globals, #d.unrepresented)

globals/lsp/describe

lsp.describe(path: string, opts: DescribeOpts?) -> DocEntry?

Inspect a single documented entry. Returns the full doc table (signature, args, returns, examples, level), or nil. The path is resolved independently of which root the doc is registered under and of separator style, so the spelling that reads off the API surface (renderer.texture.create) finds the entry registered as globals/renderer/texture/create. A path naming a binding the engine registered internally answers with the entry a Luau module publishes over it where there is one, so the signature is the call content makes; opts.includeInternal answers with the internally registered entry itself. When a path does not resolve, lsp.describePaths says what the registry holds near it.

Parameters

  • path string — Doc path (e.g. "asset/resolve", "renderer.texture.create").
  • opts DescribeOpts (optional) — Optional { includeInternal? } — default prefers the published entry.

Returns DocEntry? — Full doc table or nil.

local doc = lsp.describe("renderer.texture.create")

globals/lsp/describePaths

lsp.describePaths(path: string) -> { string }

List the registered doc paths related to path. A path that names an entry returns every root it is registered under (the first is what lsp.describe resolves to); a path that names a namespace returns the entries registered under it. Empty when the registry holds nothing near the path — so a lookup that returns nil can always be turned into the list of what does exist.

Parameters

  • path string — Doc path in any spelling ("renderer.texture", "ecs/query").

Returns { string } — Array of registered doc paths, most canonical first.

for _, p in ipairs(lsp.describePaths("renderer.texture")) do print(p) end

globals/lsp/describeTool

lsp.describeTool(path: string) -> string?

Return the full documentation text for a code-mode tool.

Parameters

  • path string — Tool path (e.g. "scene/spawnLight").

Returns string? — Full tool docs or nil.

globals/lsp/docsByKind

lsp.docsByKind(kind: string) -> { MethodSummary }

List every doc whose registration kind matches kind. Valid: "binding", "runtime_tool", "module", "component", "library", "lua_export".

Parameters

  • kind string — Registration kind.

Returns { MethodSummary } — Array of doc summary tables.

globals/lsp/exportDefinitions

lsp.exportDefinitions(dir: string) -> ExportDefinitionsResult

Write the engine's API out for an external editor (VS Code, Neovim, or any editor running luau-lsp), so it types the engine's globals: <dir>/zero.d.luau (the definitions lsp.definitions returns), <dir>/zero.docs.json (the documentation file naming each global's and member's signature and description) and <dir>/.luaurc (nonstrict mode). Point luau-lsp at the first two (its types.definitionFiles and types.documentationFiles settings) and it types the engine's globals: entity.spawn("x") checks against its signature and entity.spwan("x") is an unknown member. The files hold the globals the calling script's globals hold; call it from the tier whose globals the editor should know. The files land in the VFS: userfile.save(path) (the Files panel's "Save to this device") puts each one on the machine of the person at the screen, and over HTTP the engine's GET /engine/vfs/<path> reads it. The engine's own checker reads a *.d.luau file the way upstream does, by loading it, so the files can stay in a world's content. Writing again replaces them. globals, types and unrepresented are the lists lsp.definitions returns; the guide topics/external-editor walks through the editor setup.

Parameters

  • dir string — VFS directory to write into, such as /zero/source/editor.

Returns ExportDefinitionsResult — { available, reason?, files, bytes, globals, types, unrepresented }.

local r = lsp.exportDefinitions("/zero/source/editor")
print(r.files[1], r.bytes, #r.globals)

globals/lsp/getStrictMode

lsp.getStrictMode() -> StrictMode

Return the current strict mode.

Returns StrictMode — "off" | "soft" | "strict".

globals/lsp/isStrict

lsp.isStrict() -> boolean

Is the pre-execute LSP gate fully strict? False when off or in soft mode.

Returns boolean — True when fully strict.

globals/lsp/lastCheckGen

lsp.lastCheckGen() -> number

Generation counter — bumped each time the cache is rebuilt. UI polls this to know when to redraw.

Returns number — Generation number.

globals/lsp/methods

lsp.methods(namespace: string, opts: MethodsOpts?) -> { MethodSummary } | { string }

List every documented method / entry under a namespace. A broad namespace (ui, renderer) returns a large dump by default, so two options narrow it: opts.filter keeps only methods whose name (or doc path) contains the substring, case-insensitively; opts.namesOnly returns a plain list of method-name strings instead of the full per-method summary tables, which is much smaller and leaves nothing to unwrap. A name, in either form (a summary's name field or a namesOnly string), is the member's path below the namespace, dot-joined (texture.create under renderer, proxy.component.get under entity), so each one names a single member and lsp.describe(namespace .. "." .. name) answers for it. The listing answers with the surface content calls: an entry registered internally is left out where its signature spells the __ binding or a Luau module publishes the same member, and opts.includeInternal lists every registered entry instead.

Parameters

  • namespace string — Namespace name (e.g. "entity", "modules/Transform").
  • opts MethodsOpts (optional) — Optional { filter?, namesOnly?, includeInternal? }.

Returns { MethodSummary } | { string } — Array of method summary tables, or plain name strings when namesOnly is set (empty when the namespace is unknown or nothing matches the filter).

for _, name in ipairs(lsp.methods("ui", { namesOnly = true })) do print(name) end
lsp.methods("renderer", { filter = "shadow" })

globals/lsp/modules

lsp.modules() -> { ModuleEntry }

List every Luau library module the engine currently knows about — discovered via --!module headers, library scans, and manually-recorded docs.

Returns { ModuleEntry } — Array of module summary tables.

globals/lsp/namespaces

lsp.namespaces(opts: NamespacesOpts?) -> { NamespaceEntry }

List the documentation namespaces reachable from Luau. By default only namespaces exposing at least one PUBLIC method are returned, so the list matches what you can actually call — internal FFI plumbing (e.g. pause, native_entity), whose public surface lives elsewhere (engine.paused, the entity proxy, …), is left out. Pass { includeInternal = true } to list every namespace, internal ones included.

Parameters

  • opts NamespacesOpts (optional) — Optional { includeInternal? } — default lists public only.

Returns { NamespaceEntry } — Array of namespace summary tables.

for _, ns in ipairs(lsp.namespaces()) do print(ns.name) end

globals/lsp/readDirectives

lsp.readDirectives(source: string) -> DirectiveBlock

Parse the leading --! directive block of a Luau source string. Used by UIs that audit which files have skip directives and what they suppress.

Parameters

  • source string — Luau source text.

Returns DirectiveBlock — { mode, codes? }.

lsp.search(query: string, opts: SearchOpts?) -> { MethodSummary }

Case-insensitive substring search across every registered doc's path, signature, and description. Hits answer with the surface content calls: an entry registered internally is left out where its signature spells the __ binding or a Luau module publishes the same member, and opts.includeInternal searches every registered entry.

Parameters

  • query string — Substring to search for.
  • opts SearchOpts (optional) — { limit? = 50, includeInternal? }.

Returns { MethodSummary } — Array of method summary tables.

globals/lsp/setStrict

lsp.setStrict(enabled: boolean) -> boolean

Toggle the pre-execute LSP gate. Returns true when the change was persisted to .world_settings, false when the play-mode write lock blocked the write.

Parameters

  • enabled boolean — True = strict, false = off.

Returns boolean — Persistence signal.

globals/lsp/setStrictMode

lsp.setStrictMode(mode: StrictMode) -> boolean

Set the pre-execute strict gate's mode. Returns true when the change was persisted to .world_settings, false when the play-mode write lock blocked the write.

Parameters

  • mode StrictMode — "off" | "soft" | "strict".

Returns boolean — Persistence signal.

globals/lsp/summary

lsp.summary() -> Summary

Counts only — does not re-run validation.

Returns Summary — Counts of cached diagnostics by severity.

globals/lsp/symbolAt

lsp.symbolAt(path: string, line: number, col: number, name: string, chain: string?) -> SymbolHit?

The identifier name where it stands in path at line:col (1-based, the file's own columns), with the static type it resolves to THERE — a local inside a function body, a parameter, a field, a global — and, for a name a statement in the file introduced, declaredAt = {line, col}. The answer a hover shows and a go-to-definition jumps to. nil when nothing at that position is a reference to name: a word in a comment or a string, a keyword.

Parameters

  • path string — VFS path of the file.
  • line number — 1-based line.
  • col number — 1-based column in the file's own text.
  • name string — The identifier at that position.
  • chain string (optional) — The access path the identifier ends — entity.component.add for a pointer on add — which is where its API page is filed.

Returns SymbolHit? — The type descriptor of the reference (kind, display, and for a function its params and returns), with name, symbolKind (local, parameter, function, field, global, namespace, module), declaredAt when the file introduced the name, members and memberCount for a table, modulePath and moduleFile for a required module, and doc — the comment block above the declaration (--!desc, --!arg, --!return, --!example), the required module's header, or the API page under chain. nil when nothing there is a reference to name.

local hit = lsp.symbolAt("/zero/source/main.luau", 12, 9, "add", "entity.component.add")

globals/lsp/tools

lsp.tools() -> { ToolEntry }

List every code-mode tool registered in the VFS under /zero/docs/tools/<category>/<tool>.

Returns { ToolEntry } — Array of tool summary tables.

globals/lsp/typeOf

lsp.typeOf(expr_source: string, context_path: string?) -> TypeDescriptor

Infer the static type of a Luau expression. When context_path is given, the file is loaded and walked so the inference env contains every local + alias in scope at its end. An expression past the 2 MiB analysis bound answers kind unknown, and a context file past it adds nothing to the env.

Parameters

  • expr_source string — Luau expression source (no surrounding chunk).
  • context_path string (optional) — VFS path whose scope should be visible.

Returns TypeDescriptor — Type descriptor table.

local td = lsp.typeOf("Transform.lookAt", "/zero/source/main.luau")

globals/lsp/upstreamCheck

lsp.upstreamCheck(path: string, opts: UpstreamCheckOpts?) -> UpstreamCheck

Type-check a .luau file with upstream Luau's own type checker (its Frontend), and every module the file requires, and return what upstream reports as data. What lsp.check, the execute gate and the play gate report is unchanged by it. Each require literal resolves to the source file lsp.check's require-unresolved pass holds it to, which is the file the runtime loads wherever the runtime loads one, in whichever form it is written (@builtin::a.b, @ns::, @root::, a bare dotted a.b, .x, ..x, ./x, ../x, ~.x, ~pkg.x, an absolute path, a folder asset), and requires lists each literal of the file with the file it reached (module) and the file the runtime's require loads for it from this file (runtimeModule), nil where either reaches none. findings carry upstream's error kind or lint rule, the module they are in and 1-based positions in that module as written; they cover the file, and with opts.includeRequired the type errors of every module it reaches come back as well. Upstream reads every module through its analysis view: the engine's typed declarations read as the plain Luau functions they declare, their generic, parameter and return annotations kept, a return type written after -> read after :, and every line and column as in the source; view lists where the view of the file differs from it as { line, col, written, reads }. A module whose findings come back that upstream cannot parse through its view or past the 2 MiB analysis bound is listed in notAnalysable with the reason, and when that is the file itself analysable is false. The checked modules are kept between calls: checkedModules names the ones this call type-checked, empty when nothing the file reaches changed, and a write to a source re-checks that module and the modules depending on it. The phases of a module's check whose work grows with the types they meet stop at opts.timeLimitMs, and modules says of each module the call reached how its check ended ("completed", "timedOut" or "internal") and how long it took, reused marking one an earlier call checked that is unchanged since. The modules held between calls are bounded: once a call returns their sources sum to at most held.limitBytes, the modules the least recent calls reached going first, and held and evicted report it. A failure inside the analysis library comes back as an "internal" finding with completed false, and the internal errors upstream reports for a module come back as one "internal" finding whose count says how many. opts.typeCheck = false reads the file and every module it reaches and resolves their requires without checking types. available is false, with reason, on a build that does not link the analysis library.

Parameters

  • path string — VFS path of a .luau file, a module identity, or a @scope::path.
  • opts UpstreamCheckOpts (optional) — { solver?, mode?, timeLimitMs?, includeRequired?, typeCheck? }.

Returns UpstreamCheck — { available, path, analysable, reason?, completed, solver, mode, elapsedMs, findings, notAnalysable, requires, view, checkedModules, timedOut, modules, held, evicted, errors, warnings }.

local r = lsp.upstreamCheck("@builtin::modules.boids", { mode = "strict" })

modules/lsp/analysisFlags

analysisFlags(): AnalysisFlags

The feature-flag configuration upstream Luau's analysis library (the linter and .luaurc parser behind these checks) runs under, read back from Luau's flag registry. It is upstream's own: every Luau* flag the analysis sources declare is on unless upstream's experimental list names it, and every other flag they declare keeps the value Luau registers it at. The flags the analysis library reads from the parser and the VM are shared: they run at the value the engine's runtime gives them, and one that differs from upstream's carries its deviation. flags lists every flag those sources declare or read, sorted by name; atUpstream counts the linked ones whose value is upstream's. available is false, with reason, on a build that does not link the analysis library.

modules/lsp/check

check(path: string, opts: CheckFileOpts?): DiagnosticsResult

Validate a single .luau file in the VFS and return its diagnostics. A path the check could not read comes back as one lsp-check-* error naming the path and the reason, and a file past the 2 MiB analysis bound as one source-too-large warning with checked = false, so checked with errors == 0 means a code body was analysed and is clean. opts.upstream (true, or the options of lsp.upstreamCheck) also checks the file with upstream Luau's type checker and sets upstream to what lsp.upstreamCheck returns for it, whose modules say how the check of each module it reached ended and how long it took; the diagnostics and counts stay the engine checker's.

Parameters

  • path string — VFS path.
  • opts CheckFileOpts? (optional) — { severity?, limit?, context?, upstream? }.
local diags = lsp.check("/zero/source/main.luau")

modules/lsp/checkAll

checkAll(opts: CheckAllOpts?): CheckAllResult

Validate the user's Luau scripts and return an aggregate summary plus diagnostic list. opts.scope = "user" (default) skips library mounts; "all" includes them. The sweep is time-budgeted (ZERO_EXECUTE_INLINE_BUDGET_MS, ~20s by default): if the budget elapses it returns the partial result gathered so far with budgetExceeded = true rather than blocking the engine. opts.budgetMs sets the budget for the call and opts.maxFiles caps the files it checks. opts.upstream (true, or the options of lsp.upstreamCheck) also checks each file with upstream Luau's type checker once the engine's own pass is done, within the same budget, and sets upstream to what that pass found: every module it reached, once, with how its check ended ("completed", "timedOut", "internal", or "notAnalysable" with the reason) and how long it took, reused marking a module checked before the pass reached it and unchanged since. The pass starts no file once the budget is spent, so it ends within the budget plus the check of the one file in progress.

Parameters

  • opts CheckAllOpts? (optional) — { scope?, severity?, limit?, budgetMs?, maxFiles?, upstream? }.

modules/lsp/checkCode

checkCode(source: string, opts: CheckOpts?): DiagnosticsResult

Validate inline Luau source without a backing file. Useful for checking code before writing it to disk. A source past the 2 MiB analysis bound comes back as one source-too-large warning with checked = false.

Parameters

  • source string — Luau source.
  • opts CheckOpts? (optional) — { severity?, limit?, context? }.

modules/lsp/checkDirty

checkDirty(): DiagnosticsResult

Drain the dirty-file set populated by the hot-reload hook, validate each, and return the combined diagnostic list.

modules/lsp/definitions

definitions(opts: DefinitionsOpts?): Definitions

The engine's API as a Luau definitions source, upstream Luau's .d.luau form (declare statements and export type aliases), generated from the live API registry: the documented members, the --!global-types aliases and the asset-category shapes lsp.check types against, bound under the same names. It declares each global the calling script's globals hold, so a user script, a trusted script and a worker each get the globals they have; a name upstream's builtin environment declares keeps upstream's type and is left out. lsp.upstreamCheck loads the same source into every module it checks, and an external editor running luau-lsp reads it as a definitions file (lsp.exportDefinitions writes it out). It is generated again whenever the registry changes, a library hot reload included. package (@zero) keys each global's documentation symbol (@zero/global/<name>, @zero/global/<name>.<member>). Each type string the registry holds is declared as written, or listed in unrepresented with the reason and declared any; a declaration upstream refuses to load (an alias that reaches itself through aliases alone) is listed with upstream's reason, so the rest of the source loads. opts.documentation = true also returns documentation, the JSON documentation file luau-lsp reads beside the definitions: each symbol's signature and description. available is false, with reason, on a build that does not link the analysis library.

Parameters

  • opts DefinitionsOpts? (optional) — { documentation? }.
local d = lsp.definitions()
print(d.bytes, #d.globals, #d.unrepresented)

modules/lsp/describe

describe(path: string, opts: DescribeOpts?): DocEntry?

Inspect a single documented entry. Returns the full doc table (signature, args, returns, examples, level), or nil. The path is resolved independently of which root the doc is registered under and of separator style, so the spelling that reads off the API surface (renderer.texture.create) finds the entry registered as globals/renderer/texture/create. A path naming a binding the engine registered internally answers with the entry a Luau module publishes over it where there is one, so the signature is the call content makes; opts.includeInternal answers with the internally registered entry itself. When a path does not resolve, lsp.describePaths says what the registry holds near it.

Parameters

  • path string — Doc path (e.g. "asset/resolve", "renderer.texture.create").
  • opts DescribeOpts? (optional) — Optional { includeInternal? } — default prefers the published entry.
local doc = lsp.describe("renderer.texture.create")

modules/lsp/describePaths

describePaths(path: string): { string }

List the registered doc paths related to path. A path that names an entry returns every root it is registered under (the first is what lsp.describe resolves to); a path that names a namespace returns the entries registered under it. Empty when the registry holds nothing near the path — so a lookup that returns nil can always be turned into the list of what does exist.

Parameters

  • path string — Doc path in any spelling ("renderer.texture", "ecs/query").
for _, p in ipairs(lsp.describePaths("renderer.texture")) do print(p) end

modules/lsp/describeTool

describeTool(path: string): string?

Return the full documentation text for a code-mode tool.

Parameters

  • path string — Tool path (e.g. "scene/spawnLight").

modules/lsp/docsByKind

docsByKind(kind: string): { MethodSummary }

List every doc whose registration kind matches kind. Valid: "binding", "runtime_tool", "module", "component", "library", "lua_export".

Parameters

  • kind string — Registration kind.

modules/lsp/exportDefinitions

exportDefinitions(dir: string): ExportDefinitionsResult

Write the engine's API out for an external editor (VS Code, Neovim, or any editor running luau-lsp), so it types the engine's globals: <dir>/zero.d.luau (the definitions lsp.definitions returns), <dir>/zero.docs.json (the documentation file naming each global's and member's signature and description) and <dir>/.luaurc (nonstrict mode). Point luau-lsp at the first two (its types.definitionFiles and types.documentationFiles settings) and it types the engine's globals: entity.spawn("x") checks against its signature and entity.spwan("x") is an unknown member. The files hold the globals the calling script's globals hold; call it from the tier whose globals the editor should know. The files land in the VFS: userfile.save(path) (the Files panel's "Save to this device") puts each one on the machine of the person at the screen, and over HTTP the engine's GET /engine/vfs/<path> reads it. The engine's own checker reads a *.d.luau file the way upstream does, by loading it, so the files can stay in a world's content. Writing again replaces them. globals, types and unrepresented are the lists lsp.definitions returns; the guide topics/external-editor walks through the editor setup.

Parameters

  • dir string — VFS directory to write into, such as /zero/source/editor.
local r = lsp.exportDefinitions("/zero/source/editor")
print(r.files[1], r.bytes, #r.globals)

modules/lsp/getStrictMode

getStrictMode(): StrictMode

Return the current strict mode.

modules/lsp/isStrict

isStrict(): boolean

Is the pre-execute LSP gate fully strict? False when off or in soft mode.

modules/lsp/lastCheckGen

lastCheckGen(): number

Generation counter — bumped each time the cache is rebuilt. UI polls this to know when to redraw.

modules/lsp/methods

methods(namespace: string, opts: MethodsOpts?): { MethodSummary } | { string }

List every documented method / entry under a namespace. A broad namespace (ui, renderer) returns a large dump by default, so two options narrow it: opts.filter keeps only methods whose name (or doc path) contains the substring, case-insensitively; opts.namesOnly returns a plain list of method-name strings instead of the full per-method summary tables, which is much smaller and leaves nothing to unwrap. A name, in either form (a summary's name field or a namesOnly string), is the member's path below the namespace, dot-joined (texture.create under renderer, proxy.component.get under entity), so each one names a single member and lsp.describe(namespace .. "." .. name) answers for it. The listing answers with the surface content calls: an entry registered internally is left out where its signature spells the __ binding or a Luau module publishes the same member, and opts.includeInternal lists every registered entry instead.

Parameters

  • namespace string — Namespace name (e.g. "entity", "modules/Transform").
  • opts MethodsOpts? (optional) — Optional { filter?, namesOnly?, includeInternal? }.
for _, name in ipairs(lsp.methods("ui", { namesOnly = true })) do print(name) end
lsp.methods("renderer", { filter = "shadow" })

modules/lsp/modules

modules(): { ModuleEntry }

List every Luau library module the engine currently knows about — discovered via --!module headers, library scans, and manually-recorded docs.

modules/lsp/namespaces

namespaces(opts: NamespacesOpts?): { NamespaceEntry }

List the documentation namespaces reachable from Luau. By default only namespaces exposing at least one PUBLIC method are returned, so the list matches what you can actually call — internal FFI plumbing (e.g. pause, native_entity), whose public surface lives elsewhere (engine.paused, the entity proxy, …), is left out. Pass { includeInternal = true } to list every namespace, internal ones included.

Parameters

  • opts NamespacesOpts? (optional) — Optional { includeInternal? } — default lists public only.
for _, ns in ipairs(lsp.namespaces()) do print(ns.name) end

modules/lsp/readDirectives

readDirectives(source: string): DirectiveBlock

Parse the leading --! directive block of a Luau source string. Used by UIs that audit which files have skip directives and what they suppress.

Parameters

  • source string — Luau source text.
search(query: string, opts: SearchOpts?): { MethodSummary }

Case-insensitive substring search across every registered doc's path, signature, and description. Hits answer with the surface content calls: an entry registered internally is left out where its signature spells the __ binding or a Luau module publishes the same member, and opts.includeInternal searches every registered entry.

Parameters

  • query string — Substring to search for.
  • opts SearchOpts? (optional) — { limit? = 50, includeInternal? }.

modules/lsp/setStrict

setStrict(enabled: boolean): boolean

Toggle the pre-execute LSP gate. Returns true when the change was persisted to .world_settings, false when the play-mode write lock blocked the write.

Parameters

  • enabled boolean — True = strict, false = off.

modules/lsp/setStrictMode

setStrictMode(mode: StrictMode): boolean

Set the pre-execute strict gate's mode. Returns true when the change was persisted to .world_settings, false when the play-mode write lock blocked the write.

Parameters

  • mode StrictMode — "off" | "soft" | "strict".

modules/lsp/summary

summary(): Summary

Counts only — does not re-run validation.

modules/lsp/symbolAt

symbolAt(path: string, line: number, col: number, name: string, chain: string?): SymbolHit?

The identifier name where it stands in path at line:col (1-based, the file's own columns), with the static type it resolves to THERE — a local inside a function body, a parameter, a field, a global — and, for a name a statement in the file introduced, declaredAt = {line, col}. The answer a hover shows and a go-to-definition jumps to. nil when nothing at that position is a reference to name: a word in a comment or a string, a keyword.

Parameters

  • path string — VFS path of the file.
  • line number — 1-based line.
  • col number — 1-based column in the file's own text.
  • name string — The identifier at that position.
  • chain string? (optional) — The access path the identifier ends — entity.component.add for a pointer on add — which is where its API page is filed.
local hit = lsp.symbolAt("/zero/source/main.luau", 12, 9, "add", "entity.component.add")

modules/lsp/tools

tools(): { ToolEntry }

List every code-mode tool registered in the VFS under /zero/docs/tools/<category>/<tool>.

modules/lsp/typeOf

typeOf(expr_source: string, context_path: string?): TypeDescriptor

Infer the static type of a Luau expression. When context_path is given, the file is loaded and walked so the inference env contains every local + alias in scope at its end. An expression past the 2 MiB analysis bound answers kind unknown, and a context file past it adds nothing to the env.

Parameters

  • expr_source string — Luau expression source (no surrounding chunk).
  • context_path string? (optional) — VFS path whose scope should be visible.
local td = lsp.typeOf("Transform.lookAt", "/zero/source/main.luau")

modules/lsp/upstreamCheck

upstreamCheck(path: string, opts: UpstreamCheckOpts?): UpstreamCheck

Type-check a .luau file with upstream Luau's own type checker (its Frontend), and every module the file requires, and return what upstream reports as data. What lsp.check, the execute gate and the play gate report is unchanged by it. Each require literal resolves to the source file lsp.check's require-unresolved pass holds it to, which is the file the runtime loads wherever the runtime loads one, in whichever form it is written (@builtin::a.b, @ns::, @root::, a bare dotted a.b, .x, ..x, ./x, ../x, ~.x, ~pkg.x, an absolute path, a folder asset), and requires lists each literal of the file with the file it reached (module) and the file the runtime's require loads for it from this file (runtimeModule), nil where either reaches none. findings carry upstream's error kind or lint rule, the module they are in and 1-based positions in that module as written; they cover the file, and with opts.includeRequired the type errors of every module it reaches come back as well. Upstream reads every module through its analysis view: the engine's typed declarations read as the plain Luau functions they declare, their generic, parameter and return annotations kept, a return type written after -> read after :, and every line and column as in the source; view lists where the view of the file differs from it as { line, col, written, reads }. A module whose findings come back that upstream cannot parse through its view or past the 2 MiB analysis bound is listed in notAnalysable with the reason, and when that is the file itself analysable is false. The checked modules are kept between calls: checkedModules names the ones this call type-checked, empty when nothing the file reaches changed, and a write to a source re-checks that module and the modules depending on it. The phases of a module's check whose work grows with the types they meet stop at opts.timeLimitMs, and modules says of each module the call reached how its check ended ("completed", "timedOut" or "internal") and how long it took, reused marking one an earlier call checked that is unchanged since. The modules held between calls are bounded: once a call returns their sources sum to at most held.limitBytes, the modules the least recent calls reached going first, and held and evicted report it. A failure inside the analysis library comes back as an "internal" finding with completed false, and the internal errors upstream reports for a module come back as one "internal" finding whose count says how many. opts.typeCheck = false reads the file and every module it reaches and resolves their requires without checking types. available is false, with reason, on a build that does not link the analysis library.

Parameters

  • path string — VFS path of a .luau file, a module identity, or a @scope::path.
  • opts UpstreamCheckOpts? (optional) — { solver?, mode?, timeLimitMs?, includeRequired?, typeCheck? }.
local r = lsp.upstreamCheck("@builtin::modules.boids", { mode = "strict" })

typed/builtin//modules/api/engine/lsp/lsp/analysisFlags

lsp.analysisFlags() -> AnalysisFlags

The feature-flag configuration upstream Luau's analysis library (the linter and .luaurc parser behind these checks) runs under, read back from Luau's flag registry. It is upstream's own: every Luau* flag the analysis sources declare is on unless upstream's experimental list names it, and every other flag they declare keeps the value Luau registers it at. The flags the analysis library reads from the parser and the VM are shared: they run at the value the engine's runtime gives them, and one that differs from upstream's carries its deviation. flags lists every flag those sources declare or read, sorted by name; atUpstream counts the linked ones whose value is upstream's. available is false, with reason, on a build that does not link the analysis library.

Returns AnalysisFlags — { available, reason?, declared, shared, linked, atUpstream, flags }.

typed/builtin//modules/api/engine/lsp/lsp/check

lsp.check(path: string, opts: CheckFileOpts?) -> DiagnosticsResult

Validate a single .luau file in the VFS and return its diagnostics. A path the check could not read comes back as one lsp-check-* error naming the path and the reason, and a file past the 2 MiB analysis bound as one source-too-large warning with checked = false, so checked with errors == 0 means a code body was analysed and is clean. opts.upstream (true, or the options of lsp.upstreamCheck) also checks the file with upstream Luau's type checker and sets upstream to what lsp.upstreamCheck returns for it, whose modules say how the check of each module it reached ended and how long it took; the diagnostics and counts stay the engine checker's.

Parameters

  • path string — VFS path.
  • opts CheckFileOpts (optional) — { severity?, limit?, context?, upstream? }.

Returns DiagnosticsResult — Array of diagnostic tables.

local diags = lsp.check("/zero/source/main.luau")

typed/builtin//modules/api/engine/lsp/lsp/checkAll

lsp.checkAll(opts: CheckAllOpts?) -> CheckAllResult

Validate the user's Luau scripts and return an aggregate summary plus diagnostic list. opts.scope = "user" (default) skips library mounts; "all" includes them. The sweep is time-budgeted (ZERO_EXECUTE_INLINE_BUDGET_MS, ~20s by default): if the budget elapses it returns the partial result gathered so far with budgetExceeded = true rather than blocking the engine. opts.budgetMs sets the budget for the call and opts.maxFiles caps the files it checks. opts.upstream (true, or the options of lsp.upstreamCheck) also checks each file with upstream Luau's type checker once the engine's own pass is done, within the same budget, and sets upstream to what that pass found: every module it reached, once, with how its check ended ("completed", "timedOut", "internal", or "notAnalysable" with the reason) and how long it took, reused marking a module checked before the pass reached it and unchanged since. The pass starts no file once the budget is spent, so it ends within the budget plus the check of the one file in progress.

Parameters

  • opts CheckAllOpts (optional) — { scope?, severity?, limit?, budgetMs?, maxFiles?, upstream? }.

Returns CheckAllResult — { filesChecked, errors, warnings, info, hints, budgetExceeded, diagnostics, upstream? }.

typed/builtin//modules/api/engine/lsp/lsp/checkCode

lsp.checkCode(source: string, opts: CheckOpts?) -> DiagnosticsResult

Validate inline Luau source without a backing file. Useful for checking code before writing it to disk. A source past the 2 MiB analysis bound comes back as one source-too-large warning with checked = false.

Parameters

  • source string — Luau source.
  • opts CheckOpts (optional) — { severity?, limit?, context? }.

Returns DiagnosticsResult — Array of diagnostic tables.

typed/builtin//modules/api/engine/lsp/lsp/checkDirty

lsp.checkDirty() -> DiagnosticsResult

Drain the dirty-file set populated by the hot-reload hook, validate each, and return the combined diagnostic list.

Returns DiagnosticsResult — Array of diagnostic tables.

typed/builtin//modules/api/engine/lsp/lsp/definitions

lsp.definitions(opts: DefinitionsOpts?) -> Definitions

The engine's API as a Luau definitions source, upstream Luau's .d.luau form (declare statements and export type aliases), generated from the live API registry: the documented members, the --!global-types aliases and the asset-category shapes lsp.check types against, bound under the same names. It declares each global the calling script's globals hold, so a user script, a trusted script and a worker each get the globals they have; a name upstream's builtin environment declares keeps upstream's type and is left out. lsp.upstreamCheck loads the same source into every module it checks, and an external editor running luau-lsp reads it as a definitions file (lsp.exportDefinitions writes it out). It is generated again whenever the registry changes, a library hot reload included. package (@zero) keys each global's documentation symbol (@zero/global/<name>, @zero/global/<name>.<member>). Each type string the registry holds is declared as written, or listed in unrepresented with the reason and declared any; a declaration upstream refuses to load (an alias that reaches itself through aliases alone) is listed with upstream's reason, so the rest of the source loads. opts.documentation = true also returns documentation, the JSON documentation file luau-lsp reads beside the definitions: each symbol's signature and description. available is false, with reason, on a build that does not link the analysis library.

Parameters

  • opts DefinitionsOpts (optional) — { documentation? }.

Returns Definitions — { available, reason?, package, source, bytes, globals, types, unrepresented, documentation? }.

local d = lsp.definitions()
print(d.bytes, #d.globals, #d.unrepresented)

typed/builtin//modules/api/engine/lsp/lsp/describe

lsp.describe(path: string, opts: DescribeOpts?) -> DocEntry?

Inspect a single documented entry. Returns the full doc table (signature, args, returns, examples, level), or nil. The path is resolved independently of which root the doc is registered under and of separator style, so the spelling that reads off the API surface (renderer.texture.create) finds the entry registered as globals/renderer/texture/create. A path naming a binding the engine registered internally answers with the entry a Luau module publishes over it where there is one, so the signature is the call content makes; opts.includeInternal answers with the internally registered entry itself. When a path does not resolve, lsp.describePaths says what the registry holds near it.

Parameters

  • path string — Doc path (e.g. "asset/resolve", "renderer.texture.create").
  • opts DescribeOpts (optional) — Optional { includeInternal? } — default prefers the published entry.

Returns DocEntry? — Full doc table or nil.

local doc = lsp.describe("renderer.texture.create")

typed/builtin//modules/api/engine/lsp/lsp/describePaths

lsp.describePaths(path: string) -> { string }

List the registered doc paths related to path. A path that names an entry returns every root it is registered under (the first is what lsp.describe resolves to); a path that names a namespace returns the entries registered under it. Empty when the registry holds nothing near the path — so a lookup that returns nil can always be turned into the list of what does exist.

Parameters

  • path string — Doc path in any spelling ("renderer.texture", "ecs/query").

Returns { string } — Array of registered doc paths, most canonical first.

for _, p in ipairs(lsp.describePaths("renderer.texture")) do print(p) end

typed/builtin//modules/api/engine/lsp/lsp/describeTool

lsp.describeTool(path: string) -> string?

Return the full documentation text for a code-mode tool.

Parameters

  • path string — Tool path (e.g. "scene/spawnLight").

Returns string? — Full tool docs or nil.

typed/builtin//modules/api/engine/lsp/lsp/docsByKind

lsp.docsByKind(kind: string) -> { MethodSummary }

List every doc whose registration kind matches kind. Valid: "binding", "runtime_tool", "module", "component", "library", "lua_export".

Parameters

  • kind string — Registration kind.

Returns { MethodSummary } — Array of doc summary tables.

typed/builtin//modules/api/engine/lsp/lsp/exportDefinitions

lsp.exportDefinitions(dir: string) -> ExportDefinitionsResult

Write the engine's API out for an external editor (VS Code, Neovim, or any editor running luau-lsp), so it types the engine's globals: <dir>/zero.d.luau (the definitions lsp.definitions returns), <dir>/zero.docs.json (the documentation file naming each global's and member's signature and description) and <dir>/.luaurc (nonstrict mode). Point luau-lsp at the first two (its types.definitionFiles and types.documentationFiles settings) and it types the engine's globals: entity.spawn("x") checks against its signature and entity.spwan("x") is an unknown member. The files hold the globals the calling script's globals hold; call it from the tier whose globals the editor should know. The files land in the VFS: userfile.save(path) (the Files panel's "Save to this device") puts each one on the machine of the person at the screen, and over HTTP the engine's GET /engine/vfs/<path> reads it. The engine's own checker reads a *.d.luau file the way upstream does, by loading it, so the files can stay in a world's content. Writing again replaces them. globals, types and unrepresented are the lists lsp.definitions returns; the guide topics/external-editor walks through the editor setup.

Parameters

  • dir string — VFS directory to write into, such as /zero/source/editor.

Returns ExportDefinitionsResult — { available, reason?, files, bytes, globals, types, unrepresented }.

local r = lsp.exportDefinitions("/zero/source/editor")
print(r.files[1], r.bytes, #r.globals)

typed/builtin//modules/api/engine/lsp/lsp/getStrictMode

lsp.getStrictMode() -> StrictMode

Return the current strict mode.

Returns StrictMode — "off" | "soft" | "strict".

typed/builtin//modules/api/engine/lsp/lsp/isStrict

lsp.isStrict() -> boolean

Is the pre-execute LSP gate fully strict? False when off or in soft mode.

Returns boolean — True when fully strict.

typed/builtin//modules/api/engine/lsp/lsp/lastCheckGen

lsp.lastCheckGen() -> number

Generation counter — bumped each time the cache is rebuilt. UI polls this to know when to redraw.

Returns number — Generation number.

typed/builtin//modules/api/engine/lsp/lsp/methods

lsp.methods(namespace: string, opts: MethodsOpts?) -> { MethodSummary } | { string }

List every documented method / entry under a namespace. A broad namespace (ui, renderer) returns a large dump by default, so two options narrow it: opts.filter keeps only methods whose name (or doc path) contains the substring, case-insensitively; opts.namesOnly returns a plain list of method-name strings instead of the full per-method summary tables, which is much smaller and leaves nothing to unwrap. A name, in either form (a summary's name field or a namesOnly string), is the member's path below the namespace, dot-joined (texture.create under renderer, proxy.component.get under entity), so each one names a single member and lsp.describe(namespace .. "." .. name) answers for it. The listing answers with the surface content calls: an entry registered internally is left out where its signature spells the __ binding or a Luau module publishes the same member, and opts.includeInternal lists every registered entry instead.

Parameters

  • namespace string — Namespace name (e.g. "entity", "modules/Transform").
  • opts MethodsOpts (optional) — Optional { filter?, namesOnly?, includeInternal? }.

Returns { MethodSummary } | { string } — Array of method summary tables, or plain name strings when namesOnly is set (empty when the namespace is unknown or nothing matches the filter).

for _, name in ipairs(lsp.methods("ui", { namesOnly = true })) do print(name) end
lsp.methods("renderer", { filter = "shadow" })

typed/builtin//modules/api/engine/lsp/lsp/modules

lsp.modules() -> { ModuleEntry }

List every Luau library module the engine currently knows about — discovered via --!module headers, library scans, and manually-recorded docs.

Returns { ModuleEntry } — Array of module summary tables.

typed/builtin//modules/api/engine/lsp/lsp/namespaces

lsp.namespaces(opts: NamespacesOpts?) -> { NamespaceEntry }

List the documentation namespaces reachable from Luau. By default only namespaces exposing at least one PUBLIC method are returned, so the list matches what you can actually call — internal FFI plumbing (e.g. pause, native_entity), whose public surface lives elsewhere (engine.paused, the entity proxy, …), is left out. Pass { includeInternal = true } to list every namespace, internal ones included.

Parameters

  • opts NamespacesOpts (optional) — Optional { includeInternal? } — default lists public only.

Returns { NamespaceEntry } — Array of namespace summary tables.

for _, ns in ipairs(lsp.namespaces()) do print(ns.name) end

typed/builtin//modules/api/engine/lsp/lsp/readDirectives

lsp.readDirectives(source: string) -> DirectiveBlock

Parse the leading --! directive block of a Luau source string. Used by UIs that audit which files have skip directives and what they suppress.

Parameters

  • source string — Luau source text.

Returns DirectiveBlock — { mode, codes? }.

lsp.search(query: string, opts: SearchOpts?) -> { MethodSummary }

Case-insensitive substring search across every registered doc's path, signature, and description. Hits answer with the surface content calls: an entry registered internally is left out where its signature spells the __ binding or a Luau module publishes the same member, and opts.includeInternal searches every registered entry.

Parameters

  • query string — Substring to search for.
  • opts SearchOpts (optional) — { limit? = 50, includeInternal? }.

Returns { MethodSummary } — Array of method summary tables.

typed/builtin//modules/api/engine/lsp/lsp/setStrict

lsp.setStrict(enabled: boolean) -> boolean

Toggle the pre-execute LSP gate. Returns true when the change was persisted to .world_settings, false when the play-mode write lock blocked the write.

Parameters

  • enabled boolean — True = strict, false = off.

Returns boolean — Persistence signal.

typed/builtin//modules/api/engine/lsp/lsp/setStrictMode

lsp.setStrictMode(mode: StrictMode) -> boolean

Set the pre-execute strict gate's mode. Returns true when the change was persisted to .world_settings, false when the play-mode write lock blocked the write.

Parameters

  • mode StrictMode — "off" | "soft" | "strict".

Returns boolean — Persistence signal.

typed/builtin//modules/api/engine/lsp/lsp/summary

lsp.summary() -> Summary

Counts only — does not re-run validation.

Returns Summary — Counts of cached diagnostics by severity.

typed/builtin//modules/api/engine/lsp/lsp/symbolAt

lsp.symbolAt(path: string, line: number, col: number, name: string, chain: string?) -> SymbolHit?

The identifier name where it stands in path at line:col (1-based, the file's own columns), with the static type it resolves to THERE — a local inside a function body, a parameter, a field, a global — and, for a name a statement in the file introduced, declaredAt = {line, col}. The answer a hover shows and a go-to-definition jumps to. nil when nothing at that position is a reference to name: a word in a comment or a string, a keyword.

Parameters

  • path string — VFS path of the file.
  • line number — 1-based line.
  • col number — 1-based column in the file's own text.
  • name string — The identifier at that position.
  • chain string (optional) — The access path the identifier ends — entity.component.add for a pointer on add — which is where its API page is filed.

Returns SymbolHit? — The type descriptor of the reference (kind, display, and for a function its params and returns), with name, symbolKind (local, parameter, function, field, global, namespace, module), declaredAt when the file introduced the name, members and memberCount for a table, modulePath and moduleFile for a required module, and doc — the comment block above the declaration (--!desc, --!arg, --!return, --!example), the required module's header, or the API page under chain. nil when nothing there is a reference to name.

local hit = lsp.symbolAt("/zero/source/main.luau", 12, 9, "add", "entity.component.add")

typed/builtin//modules/api/engine/lsp/lsp/tools

lsp.tools() -> { ToolEntry }

List every code-mode tool registered in the VFS under /zero/docs/tools/<category>/<tool>.

Returns { ToolEntry } — Array of tool summary tables.

typed/builtin//modules/api/engine/lsp/lsp/typeOf

lsp.typeOf(expr_source: string, context_path: string?) -> TypeDescriptor

Infer the static type of a Luau expression. When context_path is given, the file is loaded and walked so the inference env contains every local + alias in scope at its end. An expression past the 2 MiB analysis bound answers kind unknown, and a context file past it adds nothing to the env.

Parameters

  • expr_source string — Luau expression source (no surrounding chunk).
  • context_path string (optional) — VFS path whose scope should be visible.

Returns TypeDescriptor — Type descriptor table.

local td = lsp.typeOf("Transform.lookAt", "/zero/source/main.luau")

typed/builtin//modules/api/engine/lsp/lsp/upstreamCheck

lsp.upstreamCheck(path: string, opts: UpstreamCheckOpts?) -> UpstreamCheck

Type-check a .luau file with upstream Luau's own type checker (its Frontend), and every module the file requires, and return what upstream reports as data. What lsp.check, the execute gate and the play gate report is unchanged by it. Each require literal resolves to the source file lsp.check's require-unresolved pass holds it to, which is the file the runtime loads wherever the runtime loads one, in whichever form it is written (@builtin::a.b, @ns::, @root::, a bare dotted a.b, .x, ..x, ./x, ../x, ~.x, ~pkg.x, an absolute path, a folder asset), and requires lists each literal of the file with the file it reached (module) and the file the runtime's require loads for it from this file (runtimeModule), nil where either reaches none. findings carry upstream's error kind or lint rule, the module they are in and 1-based positions in that module as written; they cover the file, and with opts.includeRequired the type errors of every module it reaches come back as well. Upstream reads every module through its analysis view: the engine's typed declarations read as the plain Luau functions they declare, their generic, parameter and return annotations kept, a return type written after -> read after :, and every line and column as in the source; view lists where the view of the file differs from it as { line, col, written, reads }. A module whose findings come back that upstream cannot parse through its view or past the 2 MiB analysis bound is listed in notAnalysable with the reason, and when that is the file itself analysable is false. The checked modules are kept between calls: checkedModules names the ones this call type-checked, empty when nothing the file reaches changed, and a write to a source re-checks that module and the modules depending on it. The phases of a module's check whose work grows with the types they meet stop at opts.timeLimitMs, and modules says of each module the call reached how its check ended ("completed", "timedOut" or "internal") and how long it took, reused marking one an earlier call checked that is unchanged since. The modules held between calls are bounded: once a call returns their sources sum to at most held.limitBytes, the modules the least recent calls reached going first, and held and evicted report it. A failure inside the analysis library comes back as an "internal" finding with completed false, and the internal errors upstream reports for a module come back as one "internal" finding whose count says how many. opts.typeCheck = false reads the file and every module it reaches and resolves their requires without checking types. available is false, with reason, on a build that does not link the analysis library.

Parameters

  • path string — VFS path of a .luau file, a module identity, or a @scope::path.
  • opts UpstreamCheckOpts (optional) — { solver?, mode?, timeLimitMs?, includeRequired?, typeCheck? }.

Returns UpstreamCheck — { available, path, analysable, reason?, completed, solver, mode, elapsedMs, findings, notAnalysable, requires, view, checkedModules, timedOut, modules, held, evicted, errors, warnings }.

local r = lsp.upstreamCheck("@builtin::modules.boids", { mode = "strict" })
  • api
  • reference