Skip to content

Releases: moku-labs/core

v0.1.4

11 Jun 23:10
dd723ce

Choose a tag to compare

What's Changed

Full Changelog: v0.1.3...v0.1.4

v0.1.3

10 Jun 07:29
d928159

Choose a tag to compare

What changed

Maintenance release: Node 24 toolchain and a spec docs-truth pass.

Node 24 (#9)

Both workflows now use Node 24-ready, SHA-pinned actions (checkout v6.0.2, setup-node v6.4.0, setup-bun v2.2.0, cache v5.0.5), ahead of GitHub's June 16 force-switch of JS actions off Node 20.

⚠️ Consumer-facing: engines.node is now >=24.0.0 (was >=22.0.0), aligning with the rest of the moku family. Consumers on Node 22/23 with strict engines enforcement will be blocked from installing this version — stay on 0.1.2 or upgrade Node.

Spec docs-truth fixes (#10, #11)

Finishes what v0.1.2's #7 started — the specification no longer contradicts itself or the implementation:

  • 11-INVARIANTS.md §1.4 rewritten ("Config Shape Checking"): every pluginConfigs entry is optional; overrides are shape-checked as Partial<C>; genuinely consumer-required values use the sentinel-default + runtime-check pattern. The matching stale rule in 12-PLUGIN-PATTERNS.md's cheat-sheet was fixed too.
  • Stale claims that createApp is async removed; 01-ARCHITECTURE.md required-config claims aligned with the optional Partial<C> semantics.

No runtime code changes; npm package behavior is identical to 0.1.2.

Full Changelog: v0.1.2...v0.1.3

v0.1.2

09 Jun 23:24
9d02b96

Choose a tag to compare

What changed

Hardening release: four runtime/type fixes, the sandbox suite wired into CI, and a docs-truth pass — every item found by a multi-agent audit of the moku family and shipped as an individually reviewed PR.

Event bus survives throwing error handlers (#3)

A throwing onError handler no longer aborts hook dispatch or leaks an unhandled rejection from the fire-and-forget emit (which crashed the process on Node >= 22). Remaining hooks always run, and the consumer onError is still invoked when the framework handler throws. Specs 07/13 updated to document the guarded semantics.

Omitted Events generic keeps hook names strict (#6)

createCoreConfig's Events default was Record<string, never>, whose keyof is string — so frameworks that omitted global events silently lost hook-name checking and typo'd hooks compiled but never fired. The default is now Record<never, never>: typo'd dependency-event hooks are a compile error, matching the behavior with an explicit Events map.

createCore accepts readonly / as const plugin tuples (#4)

CreateCoreOptions.plugins is now readonly AnyPluginInstance[], consistent with the generic constraint. Note: widening the exported option to readonly is technically a breaking type change for code that read the property as a mutable array.

require() honors its type for api-less plugins (#5)

Requiring a registered plugin that exposes no api returned a misleading "x" is not registered error while has("x") was true. It now returns a frozen empty object — exactly what the ExtractApi type promises — at all three call sites (plugin ctx, callback ctx, app.require).

Sandbox suite runs in CI (#2)

The sandbox vitest project (500+ runtime tests over the exemplar plugins) now runs in CI and the pre-commit hook; previously it ran nowhere automated.

Docs truth pass (#7)

README/spec/CLAUDE.md/llms corrections: two runtime exports (createCoreConfig + createCorePlugin), real bundle size, reserved-name list now includes global/state (matching what the kernel actually rejects), removed the unimplemented "required configs are a compile-time guarantee" claim, and fixed the onError JSDoc that promised teardown-failure handling.

Full Changelog: v0.1.0-alpha.6...v0.1.2

v0.1.0-alpha.6

11 Mar 07:37

Choose a tag to compare

What changed

Export CorePluginContext and RegisterFunction from public API

Two types were defined internally but missing from the public API, forcing framework-layer packages to duplicate them locally:

CorePluginContext<C, S> — the minimal context shape for core plugin authors. Contains only config and state — no emit, require, or has. Core plugins are self-contained infrastructure (log, storage, env) that get injected as APIs onto regular plugin contexts.

RegisterFunction — the typed callback passed to the events factory in plugin specs. Supports two modes:

  • Individual: register<PayloadType>(description?)
  • Bulk: register.map<EventMap>(descriptions?)

Both types are now exported directly from @moku-labs/core so framework authors can import them instead of maintaining local copies.

Why

Framework packages building on @moku-labs/core (e.g. a spa framework layer) need these types to write correctly typed core plugins and event declaration files. Without them being exported, each downstream package had to copy-paste the definitions locally — a maintenance hazard and a spec deviation.

v0.1.0-alpha.5

08 Mar 20:07

Choose a tag to compare

What's New

Plugin Helpers — static factory/builder functions that live on the plugin instance and are available before createApp. Consumers get typed, autocomplete-friendly APIs for building config values.

import { createPlugin, createApp } from "my-framework";

type Route = { path: string; component: string };

// Framework author defines helpers on the plugin spec:
export const routerPlugin = createPlugin("router", {
  config: { routes: [] as Route[] },
  api: ctx => ({
    navigate: (path: string) => { /* ... */ },
  }),
  helpers: {
    route: (path: string, component: string): Route => ({ path, component }),
  },
});

// Consumer uses helpers BEFORE createApp — fully typed:
const home = routerPlugin.route("/home", "HomePage");
const about = routerPlugin.route("/about", "AboutPage");

const app = createApp({
  pluginConfigs: { router: { routes: [home, about] } },
});

// Destructuring also works — types preserved:
const { route } = routerPlugin;
const contact = route("/contact", "ContactPage");

Features

  • helpers field on plugin spec — plain object of functions, each value must be a function. Optional field, defaults to empty.
  • Intersection return typecreatePlugin returns PluginInstance<...> & Helpers. No 6th generic added to PluginInstance — the intersection widens away naturally in constraint positions (AnyPluginInstance, depends, plugins arrays).
  • Zero breaking changes — existing plugins without helpers work identically. & Record<never, never> is the identity intersection.
  • Runtime validation — helpers must be a plain object of functions. Helper names must not collide with PluginInstance fields (name, spec, _phantom). Clear error messages on violation.
  • Full type inference — helper parameter types and return types are inferred from the spec. Destructured helpers preserve their signatures.
  • Static pure functions — helpers have no access to ctx, no lifecycle, no side effects. They run before the kernel exists.

Tests

  • 722 tests passing (169 unit, 41 integration, 512 sandbox)
  • New unit tests for helpers validation (valid helpers, null/non-object rejection, non-function values, name collisions, empty helpers)
  • New type-level tests for helpers inference, intersection behavior, destructuring, and constraint assignability

Spec Updates

Updated specifications: 03 (Plugin System), 12 (Plugin Patterns), 13 (Kernel Pseudocode), 15 (Plugin Structure), README index, and CLAUDE.md.

v0.1.0-alpha.4

07 Mar 12:12

Choose a tag to compare

What's New

Core Plugins — a new plugin tier for self-contained infrastructure services (logging, storage, environment) whose APIs are injected directly onto every regular plugin's context.

import { createCoreConfig, createCorePlugin } from "@moku-labs/core";

const logPlugin = createCorePlugin("log", {
  config: { level: "info" },
  createState: () => ({ entries: [] as string[] }),
  api: ctx => ({
    info: (msg: string) => { ctx.state.entries.push(msg); },
    error: (msg: string) => { ctx.state.entries.push(`[ERROR] ${msg}`); },
  }),
});

const { createPlugin, createCore } = createCoreConfig("my-app", {
  config: { siteName: "My Site" },
  plugins: [logPlugin],
  pluginConfigs: { log: { level: "debug" } },
});

// Every regular plugin gets typed core APIs on context — no require() needed
const router = createPlugin("router", {
  api: ctx => ({
    navigate: (path: string) => {
      ctx.log.info(`navigating to ${path}`);  // fully typed
    }
  }),
});

Features

  • createCorePlugin(name, spec) — new factory for core plugin instances. Supports config, createState, api, onInit, onStart, and onStop.
  • Context injection — core plugin APIs are spread directly onto every regular plugin's context (ctx.log, ctx.env, etc.) with full type inference.
  • 4-level config cascade — core plugin configs merge through all factory chain levels: spec defaults → createCoreConfigcreateCorecreateApp.
  • Lifecycle ordering — core plugins initialize and start before regular plugins; stop after regular plugins.
  • Self-contained by design — core plugins have no depends, events, or hooks. Their context is limited to { config, state }. Attempting to use forbidden fields is a compile error and a runtime error.
  • pluginConfigs on createCoreConfig — framework authors can override core plugin defaults at registration time.

Type System

  • New exported types: CorePluginInstance, AnyCorePluginInstance, CoreApisFromTuple
  • CoreApis generic parameter threaded through PluginContext, App, AppCallbackContext, CreateAppOptions, and BoundCreatePluginFunction (defaults to {} — fully backward compatible)
  • Phantom types on CorePluginInstance carry config, state, and api shapes at compile time

Constraints

Core plugins are intentionally limited:

Feature Core Plugin Regular Plugin
config / createState / api
onInit / onStart / onStop
depends
events
hooks
ctx.global / ctx.emit / ctx.require / ctx.has

Breaking Changes

None. The CoreApis = {} default is the identity element for type intersection — all existing code compiles and behaves identically.

v0.1.0-alpha.3

05 Mar 23:15

Choose a tag to compare

Add Very Complex plugin spec and integration tests. Auto-detect npm dist-tag for pre-releases.

v0.1.0-alpha.2

03 Mar 23:15

Choose a tag to compare

What's Changed

  • Export framework-facing types for declaration emit — Added 8 type-only re-exports (CoreConfigResult, BoundCreateCoreFunction, CreateCoreOptions, CreateCoreResult, BoundCreatePluginFunction, PluginInstance, AnyPluginInstance, App, CreateAppOptions) required by downstream framework packages to generate .d.ts files without TS4023.

These types appear in return-type positions of createCoreConfig, createCore, and createApp. Without them exported, downstream bun run build (tsdown with dts: true) fails because TypeScript cannot reference unnamed types in generated declaration files.

Type-only exports — zero runtime/bundle cost.

v0.1.0-alpha.1

02 Mar 21:03

Choose a tag to compare

First published alpha release

v1-alpha

02 Mar 07:01

Choose a tag to compare