chief/core
The core of Chief, a strictly and fully typed Luau module framework for Roblox. It loads modules, runs the Init/Start boot barrier, and fires hooks for extensions — nothing else. Roblox event lifecycles, dependency injection, and everything domain-specific live in extensions.
chief/core
The core of Chief, a strictly and fully typed Luau module framework for Roblox. It loads modules, runs the Init/Start boot barrier, and fires hooks for extensions — nothing else. Roblox event lifecycles, dependency injection, and everything domain-specific live in extensions.
Installation
pesde add chief/core
Usage
--!strict
local Chief = require('path/to/core')
Chief.new()
:AddModules(script.Modules:GetChildren())
:Start()
A module is a plain table. Init, Start, and Priority are all optional:
--!strict
local DataModule = {}
DataModule.Priority = 10 -- Higher initializes first (default 0).
function DataModule.Init (self: Self)
-- Runs synchronously, in order, before any module's Start.
-- Yielding here errors (NoYield) -- it would break the barrier for every module after.
end
function DataModule.Start (self: Self)
-- Spawned concurrently after every module's Init has completed.
-- Safe to yield here.
end
type Self = typeof(DataModule)
return DataModule
Boot sequence
Chief:Start() runs the following, in order:
PreLoadphase- Every added ModuleScript is required (
Loadedfires per module) - Entries are stably sorted by
Priority(higher first; ties break by insertion order) PostLoadphase — extensions may load extra modules here; loading closes afterwardPreInitphase — extensions may reorder entries here- Every module's
Initruns synchronously, in order (Initializedfires per module) PostInitphasePreStartphase- Every module's
Startis spawned concurrently (Startedfires per module) PostStartphase
API
Chief.new(options: Options?): Chief
Creates a new Chief instance. options.Verbose (default true) controls the boot summary print, e.g. Chief (Server) started 12 modules in 3ms.
Chief:AddModule(moduleScript: ModuleScript): Chief
Adds a module. Chainable. Adding the same ModuleScript twice is a no-op. Errors (BadCall) after Start.
Chief:AddModules(moduleScripts: { ModuleScript }): Chief
Adds an array of modules. Chainable.
Chief:AddExtension(extension: Extension): Chief
Registers an extension. Chainable. Errors (BadCall) after Start.
Chief:AddExtensions(extensions: { Extension }): Chief
Registers an array of extensions. Chainable.
Chief:LoadModule(moduleScript: ModuleScript): Entry
Loads a single module immediately and fires its Loaded event; returns the existing entry if already loaded. Primarily for extension authors — only callable during the Load/PostLoad window. An entry loaded during PostLoad is appended in load order (its Priority is not re-applied); reorder entries in an onPhase hook when ordering matters.
Chief:Start(): Chief
Runs the boot sequence. Calling twice errors (BadCall) rather than double-initializing. Chainable.
Writing an extension
An extension is a table with a Name and up to two hooks:
export type Extension = {
Name: string,
onPhase: ((chief: Chief.Self, phase: Phase, entries: { Entry }) -> ())?,
onModule: ((chief: Chief.Self, event: ModuleEvent, entry: Entry) -> ())?,
}
onPhase(chief, phase, entries)— fired at each boot phase (PreLoad,PostLoad,PreInit,PostInit,PreStart,PostStart) with every entry loaded so far.onModule(chief, event, entry)— fired per module as it isLoaded,Initialized, andStarted.
Rules of the road:
- During
PostLoad, an extension may callchief:LoadModuleto pull in additional modules. Loading closes oncePostLoadfinishes. - During
PostLoadorPreInit, an extension may reorderentriesin place to controlInitorder. OncePreInitfinishes, the order is final.
local function makeLoggerExtension (): Chief.Extension
return {
Name = 'Logger',
onModule = function (_chief, event, entry)
if event == 'Initialized' then
print(`{entry.Name} initialized`)
end
end,
}
end
See chief/lifecycles and chief/dependencies for full extension examples.
Types
| Type | Description |
|---|---|
Module | { Init: ((Module) -> ())?, Start: ((Module) -> ())?, Priority: number?, [any]: any } |
Entry | { Name: string, ModuleScript: ModuleScript, Module: Module } |
Phase | 'PreLoad' | 'PostLoad' | 'PreInit' | 'PostInit' | 'PreStart' | 'PostStart' |
ModuleEvent | 'Loaded' | 'Initialized' | 'Started' |
Extension | { Name: string, onPhase: (...)?, onModule: (...)? } |
Options | { Verbose: boolean? } |
Errors
All thrown errors are prefixed with a category tag:
BadArgument:— a method received a value of the wrong type or shape.BadCall:— a method was called at the wrong time (e.g.AddModuleafterStart,LoadModuleoutside the load window).NoYield:— a module yielded inside a synchronous step such asInit.
License
MIT