@moku-labs/core 0.1.0-alpha.6 → 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 +10 -6
- package/dist/index.cjs +64 -14
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +63 -12
- package/package.json +5 -4
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 }`.
|
|
@@ -308,7 +312,7 @@ The full specification lives in [`specification/`](specification/):
|
|
|
308
312
|
|
|
309
313
|
## Status
|
|
310
314
|
|
|
311
|
-
|
|
315
|
+
Early release (`0.1.x`). The public API — `createCoreConfig` → `createCore` → `createApp` — is stable; pre-1.0 means internals may still be refined. Published to npm: `bun add @moku-labs/core`.
|
|
312
316
|
|
|
313
317
|
## License
|
|
314
318
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value:
|
|
2
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
2
|
//#region src/utilities.ts
|
|
4
3
|
/**
|
|
5
4
|
* Checks whether a value is a non-null, non-array object record.
|
|
@@ -141,7 +140,6 @@ function validateCorePlugins(id, corePlugins) {
|
|
|
141
140
|
function checkCorePluginConflicts(id, plugins, corePluginNames) {
|
|
142
141
|
for (const plugin of plugins) if (corePluginNames.has(plugin.name)) throw new TypeError(`[${id}] Plugin name "${plugin.name}" conflicts with core plugin "${plugin.name}".\n Choose a different plugin name.`);
|
|
143
142
|
}
|
|
144
|
-
|
|
145
143
|
//#endregion
|
|
146
144
|
//#region src/app.ts
|
|
147
145
|
/**
|
|
@@ -158,10 +156,16 @@ function checkCorePluginConflicts(id, plugins, corePluginNames) {
|
|
|
158
156
|
function asRecord(value) {
|
|
159
157
|
return isRecord(value) ? value : {};
|
|
160
158
|
}
|
|
159
|
+
/** Shared frozen API for registered plugins that declare no api(). */
|
|
160
|
+
const EMPTY_API = Object.freeze({});
|
|
161
161
|
/**
|
|
162
162
|
* Create a require function that looks up a plugin API by instance reference.
|
|
163
163
|
*
|
|
164
|
-
*
|
|
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.
|
|
165
169
|
* @param formatError - Formats the error message using the plugin instance name.
|
|
166
170
|
* @returns A function that returns the API for a given plugin instance or throws.
|
|
167
171
|
* @example
|
|
@@ -173,8 +177,9 @@ function asRecord(value) {
|
|
|
173
177
|
function createRequire(runtime, formatError) {
|
|
174
178
|
return (instance) => {
|
|
175
179
|
const api = runtime.apis.get(instance.name);
|
|
176
|
-
if (
|
|
177
|
-
return
|
|
180
|
+
if (api) return api;
|
|
181
|
+
if (runtime.pluginNameSet.has(instance.name)) return EMPTY_API;
|
|
182
|
+
throw new Error(formatError(instance.name));
|
|
178
183
|
};
|
|
179
184
|
}
|
|
180
185
|
/**
|
|
@@ -270,7 +275,9 @@ function buildEventBus(onError) {
|
|
|
270
275
|
for (const handler of handlers) try {
|
|
271
276
|
await handler(payload);
|
|
272
277
|
} catch (error) {
|
|
273
|
-
|
|
278
|
+
try {
|
|
279
|
+
if (onError) onError(error);
|
|
280
|
+
} catch {}
|
|
274
281
|
}
|
|
275
282
|
}
|
|
276
283
|
/**
|
|
@@ -416,6 +423,14 @@ function buildApp(runtime, flatPlugins, buildPluginContext, corePluginData, cons
|
|
|
416
423
|
const appRequire = createRequire(runtime, (name) => `[${runtime.id}] app.require("${name}") failed: "${name}" is not registered.\n Check your plugin list.`);
|
|
417
424
|
const appHas = createHas(runtime);
|
|
418
425
|
const app = {
|
|
426
|
+
/**
|
|
427
|
+
* Run onStart for all plugins, then consumer onStart.
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* ```ts
|
|
431
|
+
* await app.start();
|
|
432
|
+
* ```
|
|
433
|
+
*/
|
|
419
434
|
start: async () => {
|
|
420
435
|
if (started) throw new Error(`[${runtime.id}] App already started.\n start() can only be called once.`);
|
|
421
436
|
for (const plugin of corePluginData.plugins) if (plugin.spec.onStart) await plugin.spec.onStart({
|
|
@@ -426,6 +441,14 @@ function buildApp(runtime, flatPlugins, buildPluginContext, corePluginData, cons
|
|
|
426
441
|
if (consumer?.onStart) await consumer.onStart(buildCallbackContext(runtime));
|
|
427
442
|
started = true;
|
|
428
443
|
},
|
|
444
|
+
/**
|
|
445
|
+
* Run onStop for all plugins in reverse, then consumer onStop.
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* ```ts
|
|
449
|
+
* await app.stop();
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
429
452
|
stop: async () => {
|
|
430
453
|
if (!started) throw new Error(`[${runtime.id}] App not started.\n Call start() before stop().`);
|
|
431
454
|
await executeStop(flatPlugins, runtime.globalConfig);
|
|
@@ -435,10 +458,40 @@ function buildApp(runtime, flatPlugins, buildPluginContext, corePluginData, cons
|
|
|
435
458
|
});
|
|
436
459
|
if (consumer?.onStop) await consumer.onStop(buildCallbackContext(runtime));
|
|
437
460
|
},
|
|
461
|
+
/**
|
|
462
|
+
* Emit an event with an optional payload.
|
|
463
|
+
*
|
|
464
|
+
* @param eventName - The event name to emit.
|
|
465
|
+
* @param payload - The optional event payload.
|
|
466
|
+
* @example
|
|
467
|
+
* ```ts
|
|
468
|
+
* app.emit("page:view", { path: "/" });
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
438
471
|
emit: (eventName, payload) => {
|
|
439
472
|
runtime.emit(eventName, payload);
|
|
440
473
|
},
|
|
474
|
+
/**
|
|
475
|
+
* Look up a plugin API by instance reference.
|
|
476
|
+
*
|
|
477
|
+
* @param instance - The plugin instance to look up.
|
|
478
|
+
* @returns The plugin's API object.
|
|
479
|
+
* @example
|
|
480
|
+
* ```ts
|
|
481
|
+
* const routerApi = app.require(routerPlugin);
|
|
482
|
+
* ```
|
|
483
|
+
*/
|
|
441
484
|
require: (instance) => appRequire(instance),
|
|
485
|
+
/**
|
|
486
|
+
* Check if a plugin name is registered.
|
|
487
|
+
*
|
|
488
|
+
* @param name - The plugin name to check.
|
|
489
|
+
* @returns True if the plugin is registered.
|
|
490
|
+
* @example
|
|
491
|
+
* ```ts
|
|
492
|
+
* app.has("router"); // => true or false
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
442
495
|
has: (name) => appHas(name)
|
|
443
496
|
};
|
|
444
497
|
for (const [name, api] of runtime.apis) app[name] = api;
|
|
@@ -522,7 +575,9 @@ function kernel(parameters) {
|
|
|
522
575
|
const resolvedConfigs = resolvePluginConfigs(flatPlugins, frameworkPluginConfigs, consumerPluginConfigs);
|
|
523
576
|
const states = createPluginStates(flatPlugins, globalConfig, resolvedConfigs);
|
|
524
577
|
const { emit, registerHook } = buildEventBus(onError || consumer?.onError ? (error) => {
|
|
525
|
-
|
|
578
|
+
try {
|
|
579
|
+
if (onError) onError(error);
|
|
580
|
+
} catch {}
|
|
526
581
|
if (consumer?.onError) consumer.onError(error, buildCallbackContext(runtime));
|
|
527
582
|
} : void 0);
|
|
528
583
|
const runtime = {
|
|
@@ -544,7 +599,6 @@ function kernel(parameters) {
|
|
|
544
599
|
states: coreStates
|
|
545
600
|
}, consumer);
|
|
546
601
|
}
|
|
547
|
-
|
|
548
602
|
//#endregion
|
|
549
603
|
//#region src/core.ts
|
|
550
604
|
/**
|
|
@@ -628,7 +682,6 @@ function createCoreFactory(frameworkId, configDefaults, createPlugin, corePlugin
|
|
|
628
682
|
};
|
|
629
683
|
return createCore;
|
|
630
684
|
}
|
|
631
|
-
|
|
632
685
|
//#endregion
|
|
633
686
|
//#region src/plugin.ts
|
|
634
687
|
/**
|
|
@@ -816,7 +869,6 @@ function createPluginFactory(frameworkId) {
|
|
|
816
869
|
};
|
|
817
870
|
return createPlugin;
|
|
818
871
|
}
|
|
819
|
-
|
|
820
872
|
//#endregion
|
|
821
873
|
//#region src/config.ts
|
|
822
874
|
/**
|
|
@@ -852,7 +904,6 @@ function createCoreConfig(id, options) {
|
|
|
852
904
|
createCore: createCoreFactory(frameworkId, configDefaults, createPlugin, corePlugins, corePluginConfigs)
|
|
853
905
|
};
|
|
854
906
|
}
|
|
855
|
-
|
|
856
907
|
//#endregion
|
|
857
908
|
//#region src/core-plugin.ts
|
|
858
909
|
/**
|
|
@@ -973,7 +1024,6 @@ function createCorePlugin(name, spec) {
|
|
|
973
1024
|
_phantom: {}
|
|
974
1025
|
};
|
|
975
1026
|
}
|
|
976
|
-
|
|
977
1027
|
//#endregion
|
|
978
1028
|
exports.createCoreConfig = createCoreConfig;
|
|
979
|
-
exports.createCorePlugin = createCorePlugin;
|
|
1029
|
+
exports.createCorePlugin = createCorePlugin;
|
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
|
@@ -139,7 +139,6 @@ function validateCorePlugins(id, corePlugins) {
|
|
|
139
139
|
function checkCorePluginConflicts(id, plugins, corePluginNames) {
|
|
140
140
|
for (const plugin of plugins) if (corePluginNames.has(plugin.name)) throw new TypeError(`[${id}] Plugin name "${plugin.name}" conflicts with core plugin "${plugin.name}".\n Choose a different plugin name.`);
|
|
141
141
|
}
|
|
142
|
-
|
|
143
142
|
//#endregion
|
|
144
143
|
//#region src/app.ts
|
|
145
144
|
/**
|
|
@@ -156,10 +155,16 @@ function checkCorePluginConflicts(id, plugins, corePluginNames) {
|
|
|
156
155
|
function asRecord(value) {
|
|
157
156
|
return isRecord(value) ? value : {};
|
|
158
157
|
}
|
|
158
|
+
/** Shared frozen API for registered plugins that declare no api(). */
|
|
159
|
+
const EMPTY_API = Object.freeze({});
|
|
159
160
|
/**
|
|
160
161
|
* Create a require function that looks up a plugin API by instance reference.
|
|
161
162
|
*
|
|
162
|
-
*
|
|
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.
|
|
163
168
|
* @param formatError - Formats the error message using the plugin instance name.
|
|
164
169
|
* @returns A function that returns the API for a given plugin instance or throws.
|
|
165
170
|
* @example
|
|
@@ -171,8 +176,9 @@ function asRecord(value) {
|
|
|
171
176
|
function createRequire(runtime, formatError) {
|
|
172
177
|
return (instance) => {
|
|
173
178
|
const api = runtime.apis.get(instance.name);
|
|
174
|
-
if (
|
|
175
|
-
return
|
|
179
|
+
if (api) return api;
|
|
180
|
+
if (runtime.pluginNameSet.has(instance.name)) return EMPTY_API;
|
|
181
|
+
throw new Error(formatError(instance.name));
|
|
176
182
|
};
|
|
177
183
|
}
|
|
178
184
|
/**
|
|
@@ -268,7 +274,9 @@ function buildEventBus(onError) {
|
|
|
268
274
|
for (const handler of handlers) try {
|
|
269
275
|
await handler(payload);
|
|
270
276
|
} catch (error) {
|
|
271
|
-
|
|
277
|
+
try {
|
|
278
|
+
if (onError) onError(error);
|
|
279
|
+
} catch {}
|
|
272
280
|
}
|
|
273
281
|
}
|
|
274
282
|
/**
|
|
@@ -414,6 +422,14 @@ function buildApp(runtime, flatPlugins, buildPluginContext, corePluginData, cons
|
|
|
414
422
|
const appRequire = createRequire(runtime, (name) => `[${runtime.id}] app.require("${name}") failed: "${name}" is not registered.\n Check your plugin list.`);
|
|
415
423
|
const appHas = createHas(runtime);
|
|
416
424
|
const app = {
|
|
425
|
+
/**
|
|
426
|
+
* Run onStart for all plugins, then consumer onStart.
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```ts
|
|
430
|
+
* await app.start();
|
|
431
|
+
* ```
|
|
432
|
+
*/
|
|
417
433
|
start: async () => {
|
|
418
434
|
if (started) throw new Error(`[${runtime.id}] App already started.\n start() can only be called once.`);
|
|
419
435
|
for (const plugin of corePluginData.plugins) if (plugin.spec.onStart) await plugin.spec.onStart({
|
|
@@ -424,6 +440,14 @@ function buildApp(runtime, flatPlugins, buildPluginContext, corePluginData, cons
|
|
|
424
440
|
if (consumer?.onStart) await consumer.onStart(buildCallbackContext(runtime));
|
|
425
441
|
started = true;
|
|
426
442
|
},
|
|
443
|
+
/**
|
|
444
|
+
* Run onStop for all plugins in reverse, then consumer onStop.
|
|
445
|
+
*
|
|
446
|
+
* @example
|
|
447
|
+
* ```ts
|
|
448
|
+
* await app.stop();
|
|
449
|
+
* ```
|
|
450
|
+
*/
|
|
427
451
|
stop: async () => {
|
|
428
452
|
if (!started) throw new Error(`[${runtime.id}] App not started.\n Call start() before stop().`);
|
|
429
453
|
await executeStop(flatPlugins, runtime.globalConfig);
|
|
@@ -433,10 +457,40 @@ function buildApp(runtime, flatPlugins, buildPluginContext, corePluginData, cons
|
|
|
433
457
|
});
|
|
434
458
|
if (consumer?.onStop) await consumer.onStop(buildCallbackContext(runtime));
|
|
435
459
|
},
|
|
460
|
+
/**
|
|
461
|
+
* Emit an event with an optional payload.
|
|
462
|
+
*
|
|
463
|
+
* @param eventName - The event name to emit.
|
|
464
|
+
* @param payload - The optional event payload.
|
|
465
|
+
* @example
|
|
466
|
+
* ```ts
|
|
467
|
+
* app.emit("page:view", { path: "/" });
|
|
468
|
+
* ```
|
|
469
|
+
*/
|
|
436
470
|
emit: (eventName, payload) => {
|
|
437
471
|
runtime.emit(eventName, payload);
|
|
438
472
|
},
|
|
473
|
+
/**
|
|
474
|
+
* Look up a plugin API by instance reference.
|
|
475
|
+
*
|
|
476
|
+
* @param instance - The plugin instance to look up.
|
|
477
|
+
* @returns The plugin's API object.
|
|
478
|
+
* @example
|
|
479
|
+
* ```ts
|
|
480
|
+
* const routerApi = app.require(routerPlugin);
|
|
481
|
+
* ```
|
|
482
|
+
*/
|
|
439
483
|
require: (instance) => appRequire(instance),
|
|
484
|
+
/**
|
|
485
|
+
* Check if a plugin name is registered.
|
|
486
|
+
*
|
|
487
|
+
* @param name - The plugin name to check.
|
|
488
|
+
* @returns True if the plugin is registered.
|
|
489
|
+
* @example
|
|
490
|
+
* ```ts
|
|
491
|
+
* app.has("router"); // => true or false
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
440
494
|
has: (name) => appHas(name)
|
|
441
495
|
};
|
|
442
496
|
for (const [name, api] of runtime.apis) app[name] = api;
|
|
@@ -520,7 +574,9 @@ function kernel(parameters) {
|
|
|
520
574
|
const resolvedConfigs = resolvePluginConfigs(flatPlugins, frameworkPluginConfigs, consumerPluginConfigs);
|
|
521
575
|
const states = createPluginStates(flatPlugins, globalConfig, resolvedConfigs);
|
|
522
576
|
const { emit, registerHook } = buildEventBus(onError || consumer?.onError ? (error) => {
|
|
523
|
-
|
|
577
|
+
try {
|
|
578
|
+
if (onError) onError(error);
|
|
579
|
+
} catch {}
|
|
524
580
|
if (consumer?.onError) consumer.onError(error, buildCallbackContext(runtime));
|
|
525
581
|
} : void 0);
|
|
526
582
|
const runtime = {
|
|
@@ -542,7 +598,6 @@ function kernel(parameters) {
|
|
|
542
598
|
states: coreStates
|
|
543
599
|
}, consumer);
|
|
544
600
|
}
|
|
545
|
-
|
|
546
601
|
//#endregion
|
|
547
602
|
//#region src/core.ts
|
|
548
603
|
/**
|
|
@@ -626,7 +681,6 @@ function createCoreFactory(frameworkId, configDefaults, createPlugin, corePlugin
|
|
|
626
681
|
};
|
|
627
682
|
return createCore;
|
|
628
683
|
}
|
|
629
|
-
|
|
630
684
|
//#endregion
|
|
631
685
|
//#region src/plugin.ts
|
|
632
686
|
/**
|
|
@@ -814,7 +868,6 @@ function createPluginFactory(frameworkId) {
|
|
|
814
868
|
};
|
|
815
869
|
return createPlugin;
|
|
816
870
|
}
|
|
817
|
-
|
|
818
871
|
//#endregion
|
|
819
872
|
//#region src/config.ts
|
|
820
873
|
/**
|
|
@@ -850,7 +903,6 @@ function createCoreConfig(id, options) {
|
|
|
850
903
|
createCore: createCoreFactory(frameworkId, configDefaults, createPlugin, corePlugins, corePluginConfigs)
|
|
851
904
|
};
|
|
852
905
|
}
|
|
853
|
-
|
|
854
906
|
//#endregion
|
|
855
907
|
//#region src/core-plugin.ts
|
|
856
908
|
/**
|
|
@@ -971,6 +1023,5 @@ function createCorePlugin(name, spec) {
|
|
|
971
1023
|
_phantom: {}
|
|
972
1024
|
};
|
|
973
1025
|
}
|
|
974
|
-
|
|
975
1026
|
//#endregion
|
|
976
|
-
export { createCoreConfig, createCorePlugin };
|
|
1027
|
+
export { createCoreConfig, createCorePlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moku-labs/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"author": "Alex Kucherenko",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"jiti": "2.6.1",
|
|
24
24
|
"lefthook": "2.1.1",
|
|
25
25
|
"publint": "0.3.17",
|
|
26
|
-
"tsdown": "0.
|
|
27
|
-
"typescript": "
|
|
28
|
-
"typescript-eslint": "8.
|
|
26
|
+
"tsdown": "0.22.1",
|
|
27
|
+
"typescript": "6.0.3",
|
|
28
|
+
"typescript-eslint": "8.58.0",
|
|
29
29
|
"vitest": "4.0.18"
|
|
30
30
|
},
|
|
31
31
|
"exports": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
|
+
"sideEffects": false,
|
|
43
44
|
"bugs": {
|
|
44
45
|
"url": "https://github.com/moku-labs/core/issues"
|
|
45
46
|
},
|