Checks
An authoring-time check. It runs while a mod is being built, not while it is running.
import { function check<TName extends string>(checkValue: CheckInput<TName>): CheckDeclaration<TName>Declare a self-verification the host runs before it activates the mod. Checks are a gate, not a report. They run in order once everything else in the generation is staged. The first one to throw or fail an assert stops the run, the staged contributions roll back, and the generation is quarantined with the failure message as evidence. So a check that fails keeps a broken mod off the user's machine rather than letting it half-load. Use one to confirm the mod's own contributions really landed, for example that a command it depends on is registered. This is not the place for unit tests of your logic. It answers "did I wire myself up correctly here", which only the running host can tell you.check, function defineMod<const TMod extends ModDefinition>(definition: TMod & ValidateModHooks<TMod>): ModThe entry point of the SDK. A mod's module default-exports one `defineMod` call and nothing else. The returned value is data. It registers nothing, starts nothing, and touches no host state. The host folds it twice. At build time it runs the module in a throwaway sandbox and reads the value to derive the serializable manifest. At runtime it folds the same value again to wire your handlers into their lanes: ext-host for commands, tools, effects, and hooks; the sandboxed renderer for views and widgets. That double fold is why the module must be pure. It is re-executed to produce the manifest, so anything at the top level, a file read, a network call, a timer, a `console` line, runs during a build with no host attached. Put behavior inside handlers: `run`, `activate`, `handle`. The `ValidateModHooks` constraint makes the call fail to typecheck when a `hook.beforeAction` targets an action that is neither a known host action nor `command:` plus one of this mod's own command ids.defineMod } from '@modular/sdk'; export defaultdefineMod<{ readonly metadata: { readonly displayName: "Hello"; readonly description: "A first mod"; }; readonly checks: readonly [CheckDeclaration<string>]; }>(definition: { readonly metadata: { readonly displayName: "Hello"; readonly description: "A first mod"; }; readonly checks: readonly [CheckDeclaration<string>]; }): ModThe entry point of the SDK. A mod's module default-exports one `defineMod` call and nothing else. The returned value is data. It registers nothing, starts nothing, and touches no host state. The host folds it twice. At build time it runs the module in a throwaway sandbox and reads the value to derive the serializable manifest. At runtime it folds the same value again to wire your handlers into their lanes: ext-host for commands, tools, effects, and hooks; the sandboxed renderer for views and widgets. That double fold is why the module must be pure. It is re-executed to produce the manifest, so anything at the top level, a file read, a network call, a timer, a `console` line, runs during a build with no host attached. Put behavior inside handlers: `run`, `activate`, `handle`. The `ValidateModHooks` constraint makes the call fail to typecheck when a `hook.beforeAction` targets an action that is neither a known host action nor `command:` plus one of this mod's own command ids.defineMod({metadata: { readonly displayName: "Hello"; readonly description: "A first mod"; }metadata: { displayName: "Hello"displayName: 'Hello', description: "A first mod"description: 'A first mod' }, checks: readonly [CheckDeclaration<string>]checks: [ check<string>(checkValue: CheckInput<string>): CheckDeclaration<string>Declare a self-verification the host runs before it activates the mod. Checks are a gate, not a report. They run in order once everything else in the generation is staged. The first one to throw or fail an assert stops the run, the staged contributions roll back, and the generation is quarantined with the failure message as evidence. So a check that fails keeps a broken mod off the user's machine rather than letting it half-load. Use one to confirm the mod's own contributions really landed, for example that a command it depends on is registered. This is not the place for unit tests of your logic. It answers "did I wire myself up correctly here", which only the running host can tell you.check({ name: stringname: 'hello.commands-registered', run: (ctx: CheckContext) => Promise<void>run: async ctx: CheckContextctx => { const const actions: readonly ModularActionDescriptorDto[]actions = await ctx: CheckContextctx.CheckContext.listActions(): Promise<readonly ModularActionDescriptorDto[]>Every action the host currently offers, this mod's included.listActions(); ctx: CheckContextctx.CheckContext.assert(condition: boolean, message: string): voidFails the check with `message` when `condition` is false.assert(const actions: readonly ModularActionDescriptorDto[]actions.ReadonlyArray<T>.length: numberGets the length of the array. This is a number one higher than the highest element defined in an array.length > 0, 'the mod registered no actions'); }, }), ], });
Members
CheckDeclaration — name,
run, kind.
Three members. No id, no when, no title — a check is not addressable and
not user-facing, which is the difference between it and every other contribution
kind.
What it is for
A check encodes a rule about your own mod that TypeScript cannot express — a naming convention, a required pairing between two contributions, an invariant across the mod value.
It is not a lint rule for a person's code, and it is not a runtime guard. If the rule has to hold at runtime, it belongs in the handler.
The authoring entry point
@modular/sdk/authoring holds the authoring-time surface — 7 exports covering
lint and diagnostics. Browse them at
@modular/sdk/authoring.