Reactor Core: signals, event bus, job scheduler, and coordinator kernel
Skrip ini tidak untuk dipasang secara langsung. Ini adalah pustaka skrip lain untuk disertakan dengan direktif meta // @require https://update.greasyfork.org/scripts/591444/1903232/Brazen%20Framework%20-%20Reactor.js
Cross-tab coordination for Brazen apps: one coordinator tab (Web Locks), one event bus (Commands in, ordered Patches out), a signal graph for shared UI state, and a typed job scheduler for download resolution and config sync. IndexedDB remains the source of truth; follower tabs hold replica atoms and send Commands instead of writing queue stores directly.
Greasy Fork: TBD (not published yet) · Requires: IndexedDB Storage (kernel write-through on repositories) · Load before: Download Manager when using the Reactor pipeline · Grant on this module: none
@requires Download Manager and needs cross-tab download queues, coordinator leadership, or config/commands that must not race across tabs.BrazenFramework and call APIs that delegate to sendCommand, isCoordinator(), or config-sync jobs.@require standalone for a trivial single-tab script with no Download Manager and no cross-tab shared state — the rest of the stack will not wire Reactor for you.localStorage locks or second BroadcastChannels; use the coordinator APIs below.Load order (after Configuration Manager and optional Tag Query Engine, before Download Manager):
// @require … Brazen Framework - Reactor.js
// @require … Brazen Framework - Download Manager.js
// @require … Brazen Framework - Framework.js
During local development, point Tampermonkey Resource override at base-scripts/BrazenReactor.js until the Greasy Fork listing exists.
Reactor bootstraps automatically when Download Manager initializes (_ensureReactor()). You normally do not construct BrazenKernel yourself unless building a new framework module that participates in the bus.
Check coordinator role from Framework or Download Manager:
if (this.isCoordinator()) {
// this tab runs scheduler jobs and IDB write-through
}
Send a custom Command (coordinator applies locally; followers publish to the bus):
await this.sendCommand({
type: 'custom',
payload: { name: 'my-feature', data: { ok: true } },
})
One tab per script prefix holds brazen-{scriptPrefix}-coordinator. That tab:
| API | Where | Role |
|---|---|---|
isCoordinator() |
Framework / Download Manager | Sync check |
requestCoordinatorRole({ steal: true }) |
Framework / Download Manager | Take leadership (replaces legacy “leader tab” steal) |
requestCoordinatorRole({ ifAvailable: true }) |
Framework / Download Manager | Claim only if lock is free |
On pagehide, the coordinator releases the lock and aborts in-flight work. Followers apply Patches only — they do not write download queue stores.
| Direction | Shape | Who |
|---|---|---|
| Command | { type, payload? } |
Any tab → bus → coordinator handleCommand |
| Patch | { path, version, value }[] with monotonic seq |
Coordinator → bus → all tabs |
Common Command types used by Download Manager and Configuration Manager:
type |
Purpose |
|---|---|
enqueue-download |
Add resolution queue row |
dequeue-download |
Remove item from pipeline |
toggle-paused |
Pause/resume download lane |
write-setting |
Cross-tab setting write |
config-save / config-sync |
Persist config + schedule sync job |
custom |
Extensibility (pipeline-pump, config-change notifications, …) |
Followers may apply optimistic local atom updates until an authoritative Patch with a higher seq arrives.
BrazenSignals)Path-keyed atoms (e.g. dm.state.paused, config.settings.{key}) drive dock UI without polling IDB.
const signals = globalThis.BrazenSignals
const paused = signals.atom('dm.state.paused', false)
paused.write(true)
signals.applyPatches([{ path: 'dm.state.paused', version: 42, value: false }])
Legacy helpers createAtom, createComputed, createEffect, and batch remain on globalThis and share the same graph.
BrazenEventBus)One BroadcastChannel per script prefix (brazen-{scriptPrefix}). Configuration Manager creates the instance; Download Manager reuses it.
Typical app code does not call BrazenEventBus.create() directly — use sendCommand on Framework/Download Manager.
BrazenScheduler + BrazenJobRegistry)Built-in job types: resolve, download (constants JOB_TYPE_RESOLVE, JOB_TYPE_DOWNLOAD).
Descriptors support coalesce keys, merge, dependency DAG, concurrency limits, and reassess gates. Download Manager registers pipeline job types during _ensureReactor().
All symbols are on globalThis after the script loads.
| Export | Kind |
|---|---|
BrazenSignals |
Namespace (atom, computed, effect, batch, applyPatches, …) |
BrazenEventBus |
Class (create, publish, subscribe, requestSnapshot, …) |
BrazenJobRegistry |
Class |
BrazenScheduler |
Class |
BrazenKernel |
Class |
coordinatorLockName(scriptPrefix) |
Function |
JOB_TYPE_RESOLVE, JOB_TYPE_DOWNLOAD |
String constants |
createAtom, createComputed, createEffect, batch, assertAcyclic, BrazenSignalCycleError, getPropagationGeneration |
Legacy signal helpers |
Framework/Download Manager delegates (preferred in apps):
| Method | Role |
|---|---|
sendCommand(command) |
Route Command to coordinator or bus |
isCoordinator() |
Coordinator probe |
requestCoordinatorRole(options) |
Claim / steal lock |
| Topic | Detail |
|---|---|
| Load order | Reactor → Download Manager → Framework core (match your app’s @require chain) |
| IDB writes | Only the coordinator tab writes queue/state stores via kernel write-through; followers use Commands |
| Patch vs job seq | Patch sequence comes from BrazenEventBus.nextSeq(); job ordering uses scheduler local seq unless overridden |
| Cold start | Followers call requestSnapshot before applying live Patches (handled internally when bus subscribes) |
| Testing | Run npm run smoke in the framework repo; uses jsdom + mock Web Locks |
| Publishing | One GF listing for this file (formerly five separate Reactor modules); pin version id in app @require after publish |
sendCommand)kernelWriteThrough / kernelHydrate bridgesendCommand / coordinator delegates, config-sync job