@moku-labs/core 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -5
- package/dist/index.cjs +16 -5
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +16 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Micro-kernel plugin framework for TypeScript. Three layers of isolation. Built for LLM-scale development.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Two runtime exports. Bundle < 8KB gzipped. Zero dependencies. The type system does the heavy lifting.
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
bun add @moku-labs/core
|
|
@@ -33,7 +33,7 @@ Moku enforces a 3-layer architecture where each layer physically constrains the
|
|
|
33
33
|
│ Cannot: modify the kernel │
|
|
34
34
|
├─────────────────────────────────────────────────────────┤
|
|
35
35
|
│ Layer 1: @moku-labs/core │
|
|
36
|
-
│
|
|
36
|
+
│ Two functions. Zero domain knowledge. Pure machinery. │
|
|
37
37
|
└─────────────────────────────────────────────────────────┘
|
|
38
38
|
```
|
|
39
39
|
|
|
@@ -122,7 +122,7 @@ app.blog.listPosts(); // fully typed
|
|
|
122
122
|
await app.stop();
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
**That's the entire
|
|
125
|
+
**That's the entire factory chain.** `createCoreConfig` → `createCore` → `createApp`. Three functions, three layers. (A second runtime export, `createCorePlugin`, builds self-contained infrastructure plugins — see the API reference below.)
|
|
126
126
|
|
|
127
127
|
---
|
|
128
128
|
|
|
@@ -171,7 +171,7 @@ Moku is designed for a world where LLMs write plugins and CI enforces quality. T
|
|
|
171
171
|
|
|
172
172
|
- **Strict emit** — only known event names compile. Wrong payloads are type errors. No `any`, no escape hatch.
|
|
173
173
|
- **Phantom types** — plugin APIs, configs, and events flow through the type system without runtime cost.
|
|
174
|
-
- **
|
|
174
|
+
- **Typed plugin configs** — `pluginConfigs` overrides are checked against each plugin's declared config shape. Wrong keys or value types don't compile. Overrides are optional — plugin defaults fill anything you omit.
|
|
175
175
|
- **Context tiers** — `createState` can't call `emit` (other plugins don't exist yet). `onStop` can't access other plugins (they may already be stopped). The type system prevents temporal bugs.
|
|
176
176
|
|
|
177
177
|
### Runtime guarantees
|
|
@@ -248,7 +248,7 @@ Every field is optional. A plugin with only `api` works. A plugin with only `hoo
|
|
|
248
248
|
|
|
249
249
|
```typescript
|
|
250
250
|
// Runtime
|
|
251
|
-
import { createCoreConfig } from '@moku-labs/core';
|
|
251
|
+
import { createCoreConfig, createCorePlugin } from '@moku-labs/core';
|
|
252
252
|
|
|
253
253
|
// Type utilities for plugin authors
|
|
254
254
|
import type { PluginCtx, EmitFn } from '@moku-labs/core';
|
|
@@ -258,6 +258,10 @@ import type { PluginCtx, EmitFn } from '@moku-labs/core';
|
|
|
258
258
|
|
|
259
259
|
Creates a bound factory chain for a framework. Returns `{ createPlugin, createCore }`, both locked to `Config` and `Events`.
|
|
260
260
|
|
|
261
|
+
### createCorePlugin(name, spec)
|
|
262
|
+
|
|
263
|
+
Creates a self-contained infrastructure plugin (logging, env, storage). Core plugins are passed to `createCoreConfig` via `plugins`, and their APIs are injected onto every regular plugin's context (`ctx.log.info(...)`). No `depends`, no `events`, no `hooks` — a minimal `{ config, state }` context only.
|
|
264
|
+
|
|
261
265
|
### createCore(coreConfig, options)
|
|
262
266
|
|
|
263
267
|
Captures framework defaults: plugins, plugin configs, `onReady`, `onError`. Returns `{ createApp, createPlugin }`.
|
package/dist/index.cjs
CHANGED
|
@@ -156,10 +156,16 @@ function checkCorePluginConflicts(id, plugins, corePluginNames) {
|
|
|
156
156
|
function asRecord(value) {
|
|
157
157
|
return isRecord(value) ? value : {};
|
|
158
158
|
}
|
|
159
|
+
/** Shared frozen API for registered plugins that declare no api(). */
|
|
160
|
+
const EMPTY_API = Object.freeze({});
|
|
159
161
|
/**
|
|
160
162
|
* Create a require function that looks up a plugin API by instance reference.
|
|
161
163
|
*
|
|
162
|
-
*
|
|
164
|
+
* Registered plugins without an api() resolve to a frozen empty object —
|
|
165
|
+
* matching the type contract (ExtractApi = Record<string, never>) and
|
|
166
|
+
* agreeing with has(). Only genuinely unregistered plugins throw.
|
|
167
|
+
*
|
|
168
|
+
* @param runtime - The kernel runtime containing the API map and name set.
|
|
163
169
|
* @param formatError - Formats the error message using the plugin instance name.
|
|
164
170
|
* @returns A function that returns the API for a given plugin instance or throws.
|
|
165
171
|
* @example
|
|
@@ -171,8 +177,9 @@ function asRecord(value) {
|
|
|
171
177
|
function createRequire(runtime, formatError) {
|
|
172
178
|
return (instance) => {
|
|
173
179
|
const api = runtime.apis.get(instance.name);
|
|
174
|
-
if (
|
|
175
|
-
return
|
|
180
|
+
if (api) return api;
|
|
181
|
+
if (runtime.pluginNameSet.has(instance.name)) return EMPTY_API;
|
|
182
|
+
throw new Error(formatError(instance.name));
|
|
176
183
|
};
|
|
177
184
|
}
|
|
178
185
|
/**
|
|
@@ -268,7 +275,9 @@ function buildEventBus(onError) {
|
|
|
268
275
|
for (const handler of handlers) try {
|
|
269
276
|
await handler(payload);
|
|
270
277
|
} catch (error) {
|
|
271
|
-
|
|
278
|
+
try {
|
|
279
|
+
if (onError) onError(error);
|
|
280
|
+
} catch {}
|
|
272
281
|
}
|
|
273
282
|
}
|
|
274
283
|
/**
|
|
@@ -566,7 +575,9 @@ function kernel(parameters) {
|
|
|
566
575
|
const resolvedConfigs = resolvePluginConfigs(flatPlugins, frameworkPluginConfigs, consumerPluginConfigs);
|
|
567
576
|
const states = createPluginStates(flatPlugins, globalConfig, resolvedConfigs);
|
|
568
577
|
const { emit, registerHook } = buildEventBus(onError || consumer?.onError ? (error) => {
|
|
569
|
-
|
|
578
|
+
try {
|
|
579
|
+
if (onError) onError(error);
|
|
580
|
+
} catch {}
|
|
570
581
|
if (consumer?.onError) consumer.onError(error, buildCallbackContext(runtime));
|
|
571
582
|
} : void 0);
|
|
572
583
|
const runtime = {
|
package/dist/index.d.cts
CHANGED
|
@@ -953,14 +953,14 @@ type BoundCreatePluginFunction<GlobalConfig extends FrameworkConfig, GlobalEvent
|
|
|
953
953
|
*/
|
|
954
954
|
interface CreateCoreOptions<Config> {
|
|
955
955
|
/** Framework default plugins. */
|
|
956
|
-
readonly plugins: AnyPluginInstance[];
|
|
956
|
+
readonly plugins: readonly AnyPluginInstance[];
|
|
957
957
|
/** Framework-level plugin config overrides keyed by plugin name. */
|
|
958
958
|
readonly pluginConfigs?: Record<string, unknown>;
|
|
959
959
|
/** Called after all plugins are initialized. */
|
|
960
960
|
readonly onReady?: (context: {
|
|
961
961
|
config: Readonly<Config>;
|
|
962
962
|
}) => void;
|
|
963
|
-
/** Error handler for hook dispatch
|
|
963
|
+
/** Error handler for hook dispatch failures. Lifecycle errors from start()/stop() propagate to the caller instead. */
|
|
964
964
|
readonly onError?: (error: Error) => void;
|
|
965
965
|
}
|
|
966
966
|
/**
|
|
@@ -996,7 +996,7 @@ interface CreateCoreResult<Config extends Record<string, unknown>, Events extend
|
|
|
996
996
|
type BoundCreateCoreFunction<Config extends Record<string, unknown>, Events extends Record<string, unknown>, CorePlugins extends readonly AnyCorePluginInstance[] = readonly []> = <const Plugins extends readonly AnyPluginInstance[]>(coreConfig: {
|
|
997
997
|
readonly createPlugin: BoundCreatePluginFunction<Config, Events, CoreApisFromTuple<CorePlugins>>;
|
|
998
998
|
}, options: CreateCoreOptions<Config> & {
|
|
999
|
-
readonly plugins: [...Plugins];
|
|
999
|
+
readonly plugins: readonly [...Plugins];
|
|
1000
1000
|
}) => CreateCoreResult<Config, Events, Plugins, CorePlugins>;
|
|
1001
1001
|
/**
|
|
1002
1002
|
* Creates a bound `createCore` function that captures framework context.
|
|
@@ -1055,7 +1055,7 @@ interface CoreConfigResult<Config extends Record<string, unknown>, Events extend
|
|
|
1055
1055
|
* const { createPlugin, createCore } = coreConfig;
|
|
1056
1056
|
* ```
|
|
1057
1057
|
*/
|
|
1058
|
-
declare function createCoreConfig<Config extends Record<string, unknown>, Events extends Record<string, unknown> =
|
|
1058
|
+
declare function createCoreConfig<Config extends Record<string, unknown>, Events extends Record<string, unknown> = EmptyPluginEventMap, const CorePlugins extends readonly AnyCorePluginInstance[] = readonly []>(id: string, options: {
|
|
1059
1059
|
config: Config;
|
|
1060
1060
|
plugins?: [...CorePlugins];
|
|
1061
1061
|
pluginConfigs?: { [K in CorePlugins[number] as ExtractCoreConfig<K> extends Record<string, never> ? never : IsLiteralString<ExtractCoreName<K>> extends true ? ExtractCoreName<K> : never]?: Partial<ExtractCoreConfig<K>> };
|
package/dist/index.d.mts
CHANGED
|
@@ -953,14 +953,14 @@ type BoundCreatePluginFunction<GlobalConfig extends FrameworkConfig, GlobalEvent
|
|
|
953
953
|
*/
|
|
954
954
|
interface CreateCoreOptions<Config> {
|
|
955
955
|
/** Framework default plugins. */
|
|
956
|
-
readonly plugins: AnyPluginInstance[];
|
|
956
|
+
readonly plugins: readonly AnyPluginInstance[];
|
|
957
957
|
/** Framework-level plugin config overrides keyed by plugin name. */
|
|
958
958
|
readonly pluginConfigs?: Record<string, unknown>;
|
|
959
959
|
/** Called after all plugins are initialized. */
|
|
960
960
|
readonly onReady?: (context: {
|
|
961
961
|
config: Readonly<Config>;
|
|
962
962
|
}) => void;
|
|
963
|
-
/** Error handler for hook dispatch
|
|
963
|
+
/** Error handler for hook dispatch failures. Lifecycle errors from start()/stop() propagate to the caller instead. */
|
|
964
964
|
readonly onError?: (error: Error) => void;
|
|
965
965
|
}
|
|
966
966
|
/**
|
|
@@ -996,7 +996,7 @@ interface CreateCoreResult<Config extends Record<string, unknown>, Events extend
|
|
|
996
996
|
type BoundCreateCoreFunction<Config extends Record<string, unknown>, Events extends Record<string, unknown>, CorePlugins extends readonly AnyCorePluginInstance[] = readonly []> = <const Plugins extends readonly AnyPluginInstance[]>(coreConfig: {
|
|
997
997
|
readonly createPlugin: BoundCreatePluginFunction<Config, Events, CoreApisFromTuple<CorePlugins>>;
|
|
998
998
|
}, options: CreateCoreOptions<Config> & {
|
|
999
|
-
readonly plugins: [...Plugins];
|
|
999
|
+
readonly plugins: readonly [...Plugins];
|
|
1000
1000
|
}) => CreateCoreResult<Config, Events, Plugins, CorePlugins>;
|
|
1001
1001
|
/**
|
|
1002
1002
|
* Creates a bound `createCore` function that captures framework context.
|
|
@@ -1055,7 +1055,7 @@ interface CoreConfigResult<Config extends Record<string, unknown>, Events extend
|
|
|
1055
1055
|
* const { createPlugin, createCore } = coreConfig;
|
|
1056
1056
|
* ```
|
|
1057
1057
|
*/
|
|
1058
|
-
declare function createCoreConfig<Config extends Record<string, unknown>, Events extends Record<string, unknown> =
|
|
1058
|
+
declare function createCoreConfig<Config extends Record<string, unknown>, Events extends Record<string, unknown> = EmptyPluginEventMap, const CorePlugins extends readonly AnyCorePluginInstance[] = readonly []>(id: string, options: {
|
|
1059
1059
|
config: Config;
|
|
1060
1060
|
plugins?: [...CorePlugins];
|
|
1061
1061
|
pluginConfigs?: { [K in CorePlugins[number] as ExtractCoreConfig<K> extends Record<string, never> ? never : IsLiteralString<ExtractCoreName<K>> extends true ? ExtractCoreName<K> : never]?: Partial<ExtractCoreConfig<K>> };
|
package/dist/index.mjs
CHANGED
|
@@ -155,10 +155,16 @@ function checkCorePluginConflicts(id, plugins, corePluginNames) {
|
|
|
155
155
|
function asRecord(value) {
|
|
156
156
|
return isRecord(value) ? value : {};
|
|
157
157
|
}
|
|
158
|
+
/** Shared frozen API for registered plugins that declare no api(). */
|
|
159
|
+
const EMPTY_API = Object.freeze({});
|
|
158
160
|
/**
|
|
159
161
|
* Create a require function that looks up a plugin API by instance reference.
|
|
160
162
|
*
|
|
161
|
-
*
|
|
163
|
+
* Registered plugins without an api() resolve to a frozen empty object —
|
|
164
|
+
* matching the type contract (ExtractApi = Record<string, never>) and
|
|
165
|
+
* agreeing with has(). Only genuinely unregistered plugins throw.
|
|
166
|
+
*
|
|
167
|
+
* @param runtime - The kernel runtime containing the API map and name set.
|
|
162
168
|
* @param formatError - Formats the error message using the plugin instance name.
|
|
163
169
|
* @returns A function that returns the API for a given plugin instance or throws.
|
|
164
170
|
* @example
|
|
@@ -170,8 +176,9 @@ function asRecord(value) {
|
|
|
170
176
|
function createRequire(runtime, formatError) {
|
|
171
177
|
return (instance) => {
|
|
172
178
|
const api = runtime.apis.get(instance.name);
|
|
173
|
-
if (
|
|
174
|
-
return
|
|
179
|
+
if (api) return api;
|
|
180
|
+
if (runtime.pluginNameSet.has(instance.name)) return EMPTY_API;
|
|
181
|
+
throw new Error(formatError(instance.name));
|
|
175
182
|
};
|
|
176
183
|
}
|
|
177
184
|
/**
|
|
@@ -267,7 +274,9 @@ function buildEventBus(onError) {
|
|
|
267
274
|
for (const handler of handlers) try {
|
|
268
275
|
await handler(payload);
|
|
269
276
|
} catch (error) {
|
|
270
|
-
|
|
277
|
+
try {
|
|
278
|
+
if (onError) onError(error);
|
|
279
|
+
} catch {}
|
|
271
280
|
}
|
|
272
281
|
}
|
|
273
282
|
/**
|
|
@@ -565,7 +574,9 @@ function kernel(parameters) {
|
|
|
565
574
|
const resolvedConfigs = resolvePluginConfigs(flatPlugins, frameworkPluginConfigs, consumerPluginConfigs);
|
|
566
575
|
const states = createPluginStates(flatPlugins, globalConfig, resolvedConfigs);
|
|
567
576
|
const { emit, registerHook } = buildEventBus(onError || consumer?.onError ? (error) => {
|
|
568
|
-
|
|
577
|
+
try {
|
|
578
|
+
if (onError) onError(error);
|
|
579
|
+
} catch {}
|
|
569
580
|
if (consumer?.onError) consumer.onError(error, buildCallbackContext(runtime));
|
|
570
581
|
} : void 0);
|
|
571
582
|
const runtime = {
|