@moku-labs/core 0.1.0-alpha.2 → 0.1.0-alpha.4
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 +4 -4
- package/dist/index.cjs +247 -11
- package/dist/index.d.cts +272 -124
- package/dist/index.d.mts +272 -124
- package/dist/index.mjs +246 -11
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -30,6 +30,8 @@ const RESERVED_NAMES = new Set([
|
|
|
30
30
|
"require",
|
|
31
31
|
"has",
|
|
32
32
|
"config",
|
|
33
|
+
"global",
|
|
34
|
+
"state",
|
|
33
35
|
"__proto__",
|
|
34
36
|
"constructor",
|
|
35
37
|
"prototype"
|
|
@@ -106,6 +108,37 @@ function validatePlugins(id, plugins) {
|
|
|
106
108
|
checkDuplicateNames(id, names);
|
|
107
109
|
checkDependencyOrder(id, plugins, names);
|
|
108
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Validate core plugins: no reserved names, no duplicates.
|
|
113
|
+
*
|
|
114
|
+
* @param id - Framework identifier for error messages.
|
|
115
|
+
* @param corePlugins - The core plugin list to validate.
|
|
116
|
+
* @throws {TypeError} If validation fails.
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* validateCorePlugins("my-site", [logPlugin, envPlugin]); // throws if invalid
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
function validateCorePlugins(id, corePlugins) {
|
|
123
|
+
const names = corePlugins.map((p) => p.name);
|
|
124
|
+
checkReservedNames(id, names);
|
|
125
|
+
checkDuplicateNames(id, names);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Validate that no regular plugin name conflicts with a core plugin name.
|
|
129
|
+
*
|
|
130
|
+
* @param id - Framework identifier for error messages.
|
|
131
|
+
* @param plugins - The regular plugin list.
|
|
132
|
+
* @param corePluginNames - Set of core plugin names.
|
|
133
|
+
* @throws {TypeError} If a name conflict is found.
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* checkCorePluginConflicts("my-site", [routerPlugin], new Set(["log", "env"]));
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
function checkCorePluginConflicts(id, plugins, corePluginNames) {
|
|
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
|
+
}
|
|
109
142
|
|
|
110
143
|
//#endregion
|
|
111
144
|
//#region src/app.ts
|
|
@@ -300,6 +333,7 @@ function registerPluginHooks(flatPlugins, buildPluginContext, registerHook) {
|
|
|
300
333
|
* @param runtime - The kernel runtime with shared state.
|
|
301
334
|
* @param resolvedConfigs - The resolved per-plugin config map.
|
|
302
335
|
* @param states - The plugin state map.
|
|
336
|
+
* @param coreApis - Core plugin APIs to spread onto every plugin context.
|
|
303
337
|
* @returns A factory function that produces a PluginContext for any plugin.
|
|
304
338
|
* @example
|
|
305
339
|
* ```ts
|
|
@@ -307,7 +341,7 @@ function registerPluginHooks(flatPlugins, buildPluginContext, registerHook) {
|
|
|
307
341
|
* const ctx = factory(routerPlugin);
|
|
308
342
|
* ```
|
|
309
343
|
*/
|
|
310
|
-
function createContextFactory(runtime, resolvedConfigs, states) {
|
|
344
|
+
function createContextFactory(runtime, resolvedConfigs, states, coreApis) {
|
|
311
345
|
const has = createHas(runtime);
|
|
312
346
|
return (plugin) => ({
|
|
313
347
|
global: runtime.globalConfig,
|
|
@@ -315,7 +349,8 @@ function createContextFactory(runtime, resolvedConfigs, states) {
|
|
|
315
349
|
state: states.get(plugin.name),
|
|
316
350
|
emit: runtime.emit,
|
|
317
351
|
require: createRequire(runtime, (name) => `[${runtime.id}] Plugin "${plugin.name}" requires "${name}", but "${name}" is not registered.\n Add "${name}" to your plugin list.`),
|
|
318
|
-
has
|
|
352
|
+
has,
|
|
353
|
+
...coreApis
|
|
319
354
|
});
|
|
320
355
|
}
|
|
321
356
|
/**
|
|
@@ -362,6 +397,10 @@ async function executeStop(flatPlugins, globalConfig) {
|
|
|
362
397
|
* @param runtime - The kernel runtime with shared state and APIs.
|
|
363
398
|
* @param flatPlugins - The flattened plugin list.
|
|
364
399
|
* @param buildPluginContext - Factory that builds context for a plugin.
|
|
400
|
+
* @param corePluginData - Core plugin instances, resolved configs, and states.
|
|
401
|
+
* @param corePluginData.plugins - The core plugin instances.
|
|
402
|
+
* @param corePluginData.configs - Resolved core plugin config map.
|
|
403
|
+
* @param corePluginData.states - Core plugin state map.
|
|
365
404
|
* @param consumer - Optional consumer lifecycle callbacks.
|
|
366
405
|
* @returns A frozen app object with lifecycle methods and plugin APIs.
|
|
367
406
|
* @example
|
|
@@ -370,13 +409,17 @@ async function executeStop(flatPlugins, globalConfig) {
|
|
|
370
409
|
* await app.start();
|
|
371
410
|
* ```
|
|
372
411
|
*/
|
|
373
|
-
function buildApp(runtime, flatPlugins, buildPluginContext, consumer) {
|
|
412
|
+
function buildApp(runtime, flatPlugins, buildPluginContext, corePluginData, consumer) {
|
|
374
413
|
let started = false;
|
|
375
414
|
const appRequire = createRequire(runtime, (name) => `[${runtime.id}] app.require("${name}") failed: "${name}" is not registered.\n Check your plugin list.`);
|
|
376
415
|
const appHas = createHas(runtime);
|
|
377
416
|
const app = {
|
|
378
417
|
start: async () => {
|
|
379
418
|
if (started) throw new Error(`[${runtime.id}] App already started.\n start() can only be called once.`);
|
|
419
|
+
for (const plugin of corePluginData.plugins) if (plugin.spec.onStart) await plugin.spec.onStart({
|
|
420
|
+
config: corePluginData.configs.get(plugin.name) ?? {},
|
|
421
|
+
state: corePluginData.states.get(plugin.name)
|
|
422
|
+
});
|
|
380
423
|
for (const plugin of flatPlugins) if (plugin.spec.onStart) await plugin.spec.onStart(buildPluginContext(plugin));
|
|
381
424
|
if (consumer?.onStart) await consumer.onStart(buildCallbackContext(runtime));
|
|
382
425
|
started = true;
|
|
@@ -384,6 +427,10 @@ function buildApp(runtime, flatPlugins, buildPluginContext, consumer) {
|
|
|
384
427
|
stop: async () => {
|
|
385
428
|
if (!started) throw new Error(`[${runtime.id}] App not started.\n Call start() before stop().`);
|
|
386
429
|
await executeStop(flatPlugins, runtime.globalConfig);
|
|
430
|
+
for (const plugin of corePluginData.plugins.toReversed()) if (plugin.spec.onStop) await plugin.spec.onStop({
|
|
431
|
+
config: corePluginData.configs.get(plugin.name) ?? {},
|
|
432
|
+
state: corePluginData.states.get(plugin.name)
|
|
433
|
+
});
|
|
387
434
|
if (consumer?.onStop) await consumer.onStop(buildCallbackContext(runtime));
|
|
388
435
|
},
|
|
389
436
|
emit: (eventName, payload) => {
|
|
@@ -396,6 +443,59 @@ function buildApp(runtime, flatPlugins, buildPluginContext, consumer) {
|
|
|
396
443
|
return Object.freeze(app);
|
|
397
444
|
}
|
|
398
445
|
/**
|
|
446
|
+
* Initialize core plugins: resolve configs, create states, build APIs, run onInit.
|
|
447
|
+
*
|
|
448
|
+
* @param corePlugins - Core plugin instances.
|
|
449
|
+
* @param corePluginConfigs - Config overrides from createCoreConfig.
|
|
450
|
+
* @param frameworkPluginConfigs - Config overrides from createCore.
|
|
451
|
+
* @param consumerPluginConfigs - Config overrides from createApp.
|
|
452
|
+
* @returns Resolved configs, states, core APIs map, and shared apis Map.
|
|
453
|
+
* @example
|
|
454
|
+
* ```ts
|
|
455
|
+
* const { coreResolvedConfigs, coreStates, coreApis, apis } = initCorePlugins(
|
|
456
|
+
* corePlugins, corePluginConfigs, frameworkPluginConfigs, consumerPluginConfigs
|
|
457
|
+
* );
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
function initCorePlugins(corePlugins, corePluginConfigs, frameworkPluginConfigs, consumerPluginConfigs) {
|
|
461
|
+
const coreResolvedConfigs = /* @__PURE__ */ new Map();
|
|
462
|
+
for (const plugin of corePlugins) {
|
|
463
|
+
const merged = Object.freeze({
|
|
464
|
+
...plugin.spec.config,
|
|
465
|
+
...asRecord(corePluginConfigs[plugin.name]),
|
|
466
|
+
...asRecord(frameworkPluginConfigs[plugin.name]),
|
|
467
|
+
...asRecord(consumerPluginConfigs[plugin.name])
|
|
468
|
+
});
|
|
469
|
+
coreResolvedConfigs.set(plugin.name, merged);
|
|
470
|
+
}
|
|
471
|
+
const coreStates = /* @__PURE__ */ new Map();
|
|
472
|
+
for (const plugin of corePlugins) if (plugin.spec.createState) {
|
|
473
|
+
const pluginConfig = coreResolvedConfigs.get(plugin.name) ?? {};
|
|
474
|
+
coreStates.set(plugin.name, plugin.spec.createState({ config: pluginConfig }));
|
|
475
|
+
} else coreStates.set(plugin.name, {});
|
|
476
|
+
const coreApis = {};
|
|
477
|
+
const apis = /* @__PURE__ */ new Map();
|
|
478
|
+
for (const plugin of corePlugins) if (plugin.spec.api) {
|
|
479
|
+
const context = {
|
|
480
|
+
config: coreResolvedConfigs.get(plugin.name) ?? {},
|
|
481
|
+
state: coreStates.get(plugin.name)
|
|
482
|
+
};
|
|
483
|
+
const api = plugin.spec.api(context);
|
|
484
|
+
coreApis[plugin.name] = api;
|
|
485
|
+
apis.set(plugin.name, api);
|
|
486
|
+
}
|
|
487
|
+
for (const plugin of corePlugins) if (plugin.spec.onInit) plugin.spec.onInit({
|
|
488
|
+
config: coreResolvedConfigs.get(plugin.name) ?? {},
|
|
489
|
+
state: coreStates.get(plugin.name)
|
|
490
|
+
});
|
|
491
|
+
return {
|
|
492
|
+
coreResolvedConfigs,
|
|
493
|
+
coreStates,
|
|
494
|
+
coreApis,
|
|
495
|
+
apis
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
399
499
|
* The kernel — creates and initializes the application.
|
|
400
500
|
*
|
|
401
501
|
* Receives pre-flattened, pre-validated plugins and all captured context from
|
|
@@ -410,15 +510,15 @@ function buildApp(runtime, flatPlugins, buildPluginContext, consumer) {
|
|
|
410
510
|
* ```
|
|
411
511
|
*/
|
|
412
512
|
function kernel(parameters) {
|
|
413
|
-
const { id, configDefaults, frameworkPluginConfigs, flatPlugins, configOverrides, consumerPluginConfigs, onReady, onError, consumer } = parameters;
|
|
414
|
-
const pluginNameSet = new Set(flatPlugins.map((plugin) => plugin.name));
|
|
513
|
+
const { id, configDefaults, frameworkPluginConfigs, flatPlugins, corePlugins, corePluginConfigs, configOverrides, consumerPluginConfigs, onReady, onError, consumer } = parameters;
|
|
514
|
+
const pluginNameSet = new Set([...corePlugins.map((plugin) => plugin.name), ...flatPlugins.map((plugin) => plugin.name)]);
|
|
415
515
|
const globalConfig = Object.freeze({
|
|
416
516
|
...configDefaults,
|
|
417
517
|
...configOverrides
|
|
418
518
|
});
|
|
519
|
+
const { coreResolvedConfigs, coreStates, coreApis, apis } = initCorePlugins(corePlugins, corePluginConfigs, frameworkPluginConfigs, consumerPluginConfigs);
|
|
419
520
|
const resolvedConfigs = resolvePluginConfigs(flatPlugins, frameworkPluginConfigs, consumerPluginConfigs);
|
|
420
521
|
const states = createPluginStates(flatPlugins, globalConfig, resolvedConfigs);
|
|
421
|
-
const apis = /* @__PURE__ */ new Map();
|
|
422
522
|
const { emit, registerHook } = buildEventBus(onError || consumer?.onError ? (error) => {
|
|
423
523
|
if (onError) onError(error);
|
|
424
524
|
if (consumer?.onError) consumer.onError(error, buildCallbackContext(runtime));
|
|
@@ -430,13 +530,17 @@ function kernel(parameters) {
|
|
|
430
530
|
apis,
|
|
431
531
|
pluginNameSet
|
|
432
532
|
};
|
|
433
|
-
const buildPluginContext = createContextFactory(runtime, resolvedConfigs, states);
|
|
533
|
+
const buildPluginContext = createContextFactory(runtime, resolvedConfigs, states, coreApis);
|
|
434
534
|
registerPluginHooks(flatPlugins, buildPluginContext, registerHook);
|
|
435
535
|
for (const plugin of flatPlugins) if (plugin.spec.api) apis.set(plugin.name, plugin.spec.api(buildPluginContext(plugin)));
|
|
436
536
|
for (const plugin of flatPlugins) if (plugin.spec.onInit) plugin.spec.onInit(buildPluginContext(plugin));
|
|
437
537
|
if (onReady) onReady({ config: globalConfig });
|
|
438
538
|
if (consumer?.onReady) consumer.onReady(buildCallbackContext(runtime));
|
|
439
|
-
return buildApp(runtime, flatPlugins, buildPluginContext,
|
|
539
|
+
return buildApp(runtime, flatPlugins, buildPluginContext, {
|
|
540
|
+
plugins: corePlugins,
|
|
541
|
+
configs: coreResolvedConfigs,
|
|
542
|
+
states: coreStates
|
|
543
|
+
}, consumer);
|
|
440
544
|
}
|
|
441
545
|
|
|
442
546
|
//#endregion
|
|
@@ -451,6 +555,8 @@ function kernel(parameters) {
|
|
|
451
555
|
* @param frameworkId - The framework identifier for error messages.
|
|
452
556
|
* @param configDefaults - Default config values captured from Step 1.
|
|
453
557
|
* @param createPlugin - Bound createPlugin function from Step 1.
|
|
558
|
+
* @param corePlugins - Core plugin instances from createCoreConfig.
|
|
559
|
+
* @param corePluginConfigs - Core plugin config overrides from createCoreConfig.
|
|
454
560
|
* @returns A createCore function bound to the framework's Config and Events types.
|
|
455
561
|
* @example
|
|
456
562
|
* ```ts
|
|
@@ -459,7 +565,7 @@ function kernel(parameters) {
|
|
|
459
565
|
* );
|
|
460
566
|
* ```
|
|
461
567
|
*/
|
|
462
|
-
function createCoreFactory(frameworkId, configDefaults, createPlugin) {
|
|
568
|
+
function createCoreFactory(frameworkId, configDefaults, createPlugin, corePlugins, corePluginConfigs) {
|
|
463
569
|
/**
|
|
464
570
|
* Step 2: Captures framework default plugins and returns createApp.
|
|
465
571
|
*
|
|
@@ -493,11 +599,14 @@ function createCoreFactory(frameworkId, configDefaults, createPlugin) {
|
|
|
493
599
|
const appOptions = consumerOptions ?? {};
|
|
494
600
|
const allPlugins = [...defaultPlugins, ...appOptions.plugins ?? []];
|
|
495
601
|
validatePlugins(frameworkId, allPlugins);
|
|
602
|
+
checkCorePluginConflicts(frameworkId, allPlugins, new Set(corePlugins.map((p) => p.name)));
|
|
496
603
|
return kernel({
|
|
497
604
|
id: frameworkId,
|
|
498
605
|
configDefaults,
|
|
499
606
|
frameworkPluginConfigs,
|
|
500
607
|
flatPlugins: allPlugins,
|
|
608
|
+
corePlugins,
|
|
609
|
+
corePluginConfigs,
|
|
501
610
|
configOverrides: appOptions.config ?? {},
|
|
502
611
|
consumerPluginConfigs: appOptions.pluginConfigs ?? {},
|
|
503
612
|
onReady,
|
|
@@ -690,6 +799,8 @@ function createPluginFactory(frameworkId) {
|
|
|
690
799
|
* @param id - Framework identifier used in error messages.
|
|
691
800
|
* @param options - Configuration options containing the default config values.
|
|
692
801
|
* @param options.config - Default configuration values for the framework.
|
|
802
|
+
* @param options.plugins - Optional core plugin instances to register.
|
|
803
|
+
* @param options.pluginConfigs - Optional config overrides for core plugins.
|
|
693
804
|
* @returns An object with createPlugin (bound to Config/Events) and createCore.
|
|
694
805
|
* @example
|
|
695
806
|
* ```ts
|
|
@@ -702,12 +813,136 @@ function createPluginFactory(frameworkId) {
|
|
|
702
813
|
function createCoreConfig(id, options) {
|
|
703
814
|
const configDefaults = options.config;
|
|
704
815
|
const frameworkId = id;
|
|
816
|
+
const corePlugins = options.plugins ?? [];
|
|
817
|
+
const corePluginConfigs = options.pluginConfigs ?? {};
|
|
818
|
+
validateCorePlugins(frameworkId, corePlugins);
|
|
705
819
|
const createPlugin = createPluginFactory(frameworkId);
|
|
706
820
|
return {
|
|
707
821
|
createPlugin,
|
|
708
|
-
createCore: createCoreFactory(frameworkId, configDefaults, createPlugin)
|
|
822
|
+
createCore: createCoreFactory(frameworkId, configDefaults, createPlugin, corePlugins, corePluginConfigs)
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
//#endregion
|
|
827
|
+
//#region src/core-plugin.ts
|
|
828
|
+
/**
|
|
829
|
+
* Reserved names that cannot be used for core plugins.
|
|
830
|
+
* Includes regular reserved names plus context property names that would collide.
|
|
831
|
+
*/
|
|
832
|
+
const CORE_PLUGIN_RESERVED_NAMES = new Set([
|
|
833
|
+
"start",
|
|
834
|
+
"stop",
|
|
835
|
+
"emit",
|
|
836
|
+
"require",
|
|
837
|
+
"has",
|
|
838
|
+
"config",
|
|
839
|
+
"global",
|
|
840
|
+
"state",
|
|
841
|
+
"__proto__",
|
|
842
|
+
"constructor",
|
|
843
|
+
"prototype"
|
|
844
|
+
]);
|
|
845
|
+
/** Fields that core plugins must not have. */
|
|
846
|
+
const CORE_PLUGIN_FORBIDDEN_FIELDS = [
|
|
847
|
+
"depends",
|
|
848
|
+
"events",
|
|
849
|
+
"hooks"
|
|
850
|
+
];
|
|
851
|
+
/**
|
|
852
|
+
* Asserts that a core plugin name is a non-empty string and not reserved.
|
|
853
|
+
*
|
|
854
|
+
* @param name - Candidate core plugin name.
|
|
855
|
+
* @example
|
|
856
|
+
* ```ts
|
|
857
|
+
* assertValidCorePluginName("log"); // ok
|
|
858
|
+
* assertValidCorePluginName("config"); // throws
|
|
859
|
+
* ```
|
|
860
|
+
*/
|
|
861
|
+
function assertValidCorePluginName(name) {
|
|
862
|
+
if (typeof name !== "string" || name.length === 0) throw new TypeError("Core plugin name must be a non-empty string.\n Pass a non-empty string as the first argument to createCorePlugin.");
|
|
863
|
+
if (CORE_PLUGIN_RESERVED_NAMES.has(name)) throw new TypeError(`Core plugin name "${name}" conflicts with a reserved name.\n Choose a different core plugin name.`);
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Asserts that the core plugin spec is a non-null object.
|
|
867
|
+
*
|
|
868
|
+
* @param name - Validated core plugin name.
|
|
869
|
+
* @param spec - Candidate core plugin spec.
|
|
870
|
+
* @example
|
|
871
|
+
* ```ts
|
|
872
|
+
* assertValidCorePluginSpec("log", { api: () => ({}) }); // ok
|
|
873
|
+
* ```
|
|
874
|
+
*/
|
|
875
|
+
function assertValidCorePluginSpec(name, spec) {
|
|
876
|
+
if (isRecord(spec)) return;
|
|
877
|
+
throw new TypeError(`Core plugin "${name}" has invalid spec: expected an object.\n Provide a plugin specification object as the second argument.`);
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Asserts that the core plugin spec does not contain forbidden fields.
|
|
881
|
+
*
|
|
882
|
+
* @param name - Validated core plugin name.
|
|
883
|
+
* @param spec - Validated core plugin spec.
|
|
884
|
+
* @example
|
|
885
|
+
* ```ts
|
|
886
|
+
* assertNoCorePluginForbiddenFields("log", { api: () => ({}) }); // ok
|
|
887
|
+
* assertNoCorePluginForbiddenFields("log", { depends: [] }); // throws
|
|
888
|
+
* ```
|
|
889
|
+
*/
|
|
890
|
+
function assertNoCorePluginForbiddenFields(name, spec) {
|
|
891
|
+
for (const field of CORE_PLUGIN_FORBIDDEN_FIELDS) if (field in spec) throw new TypeError(`Core plugin "${name}" cannot have "${field}".\n Core plugins are self-contained — remove the forbidden field.`);
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Validates lifecycle handlers and factories on a core plugin spec.
|
|
895
|
+
*
|
|
896
|
+
* @param name - Validated core plugin name.
|
|
897
|
+
* @param spec - Validated core plugin spec.
|
|
898
|
+
* @example
|
|
899
|
+
* ```ts
|
|
900
|
+
* assertValidCorePluginCallbacks("log", { api: () => ({}) }); // ok
|
|
901
|
+
* ```
|
|
902
|
+
*/
|
|
903
|
+
function assertValidCorePluginCallbacks(name, spec) {
|
|
904
|
+
for (const field of [
|
|
905
|
+
"api",
|
|
906
|
+
"createState",
|
|
907
|
+
"onInit",
|
|
908
|
+
"onStart",
|
|
909
|
+
"onStop"
|
|
910
|
+
]) {
|
|
911
|
+
const value = spec[field];
|
|
912
|
+
if (value !== void 0 && typeof value !== "function") throw new TypeError(`Core plugin "${name}" has invalid ${field}: expected a function.\n Provide a function for ${field} or remove it from the spec.`);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Creates a core plugin instance. Core plugins are standalone, self-contained
|
|
917
|
+
* infrastructure plugins (log, storage, env) whose APIs are injected directly
|
|
918
|
+
* onto every regular plugin's context.
|
|
919
|
+
*
|
|
920
|
+
* @param name - Unique core plugin name (inferred as literal string type).
|
|
921
|
+
* @param spec - Core plugin specification: config, createState, api, lifecycle.
|
|
922
|
+
* @returns A CorePluginInstance carrying phantom types for compile-time inference.
|
|
923
|
+
* @example
|
|
924
|
+
* ```ts
|
|
925
|
+
* const logPlugin = createCorePlugin("log", {
|
|
926
|
+
* config: { level: "info" },
|
|
927
|
+
* createState: () => ({ entries: [] as string[] }),
|
|
928
|
+
* api: ctx => ({
|
|
929
|
+
* info: (msg: string) => { ctx.state.entries.push(msg); console.log(msg); },
|
|
930
|
+
* }),
|
|
931
|
+
* });
|
|
932
|
+
* ```
|
|
933
|
+
*/
|
|
934
|
+
function createCorePlugin(name, spec) {
|
|
935
|
+
assertValidCorePluginName(name);
|
|
936
|
+
assertValidCorePluginSpec(name, spec);
|
|
937
|
+
assertNoCorePluginForbiddenFields(name, spec);
|
|
938
|
+
assertValidCorePluginCallbacks(name, spec);
|
|
939
|
+
return {
|
|
940
|
+
name,
|
|
941
|
+
spec,
|
|
942
|
+
_corePlugin: true,
|
|
943
|
+
_phantom: {}
|
|
709
944
|
};
|
|
710
945
|
}
|
|
711
946
|
|
|
712
947
|
//#endregion
|
|
713
|
-
export { createCoreConfig };
|
|
948
|
+
export { createCoreConfig, createCorePlugin };
|