@moku-labs/core 0.1.0-alpha.4 → 0.1.0-alpha.5

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/dist/index.cjs CHANGED
@@ -742,6 +742,32 @@ function assertValidApi(frameworkId, pluginName, api) {
742
742
  function assertValidCreateState(frameworkId, pluginName, createState) {
743
743
  if (createState !== void 0 && typeof createState !== "function") throw new TypeError(`[${frameworkId}] Plugin "${pluginName}" has invalid createState: expected a function.\n Provide a function like: createState: ctx => ({ key: initialValue })`);
744
744
  }
745
+ /** PluginInstance field names that cannot be used as helper names. */
746
+ const PLUGIN_INSTANCE_RESERVED_KEYS = new Set([
747
+ "name",
748
+ "spec",
749
+ "_phantom"
750
+ ]);
751
+ /**
752
+ * Validates that `helpers` is a plain object of functions if provided.
753
+ * Also rejects helper names that collide with PluginInstance properties.
754
+ *
755
+ * @param frameworkId - Framework identifier used in error messages.
756
+ * @param pluginName - Validated plugin name.
757
+ * @param helpers - Candidate helpers value from plugin spec.
758
+ * @example
759
+ * ```ts
760
+ * assertValidHelpers("my-app", "router", { route: () => ({}) });
761
+ * ```
762
+ */
763
+ function assertValidHelpers(frameworkId, pluginName, helpers) {
764
+ if (helpers === void 0) return;
765
+ if (!isRecord(helpers)) throw new TypeError(`[${frameworkId}] Plugin "${pluginName}" has invalid helpers: expected an object.\n Provide an object of functions: helpers: { myHelper: (...args) => result }`);
766
+ for (const [key, value] of Object.entries(helpers)) {
767
+ if (typeof value !== "function") throw new TypeError(`[${frameworkId}] Plugin "${pluginName}" has invalid helper "${key}": expected a function.\n Each helper must be a function.`);
768
+ if (PLUGIN_INSTANCE_RESERVED_KEYS.has(key)) throw new TypeError(`[${frameworkId}] Plugin "${pluginName}" helper "${key}" conflicts with a PluginInstance property.\n Choose a different helper name.`);
769
+ }
770
+ }
745
771
  /**
746
772
  * Creates a bound `createPlugin` function that captures framework generics.
747
773
  *
@@ -780,10 +806,12 @@ function createPluginFactory(frameworkId) {
780
806
  assertValidHooks(frameworkId, name, spec.hooks);
781
807
  assertValidApi(frameworkId, name, spec.api);
782
808
  assertValidCreateState(frameworkId, name, spec.createState);
809
+ assertValidHelpers(frameworkId, name, spec.helpers);
783
810
  return {
784
811
  name,
785
812
  spec,
786
- _phantom: {}
813
+ _phantom: {},
814
+ ...isRecord(spec.helpers) ? spec.helpers : {}
787
815
  };
788
816
  };
789
817
  return createPlugin;
package/dist/index.d.cts CHANGED
@@ -781,7 +781,7 @@ type RegisterFunction = {
781
781
  * >;
782
782
  * ```
783
783
  */
784
- type CreatePluginSpec<GlobalConfig extends FrameworkConfig, GlobalEventMap extends FrameworkEventMap, PluginEventMap extends Record<string, unknown>, PluginConfig extends Record<string, unknown>, PluginState, PluginApi extends Record<string, unknown>, DependencyPlugins extends DependencyPluginTuple, CoreApis extends Record<string, unknown> = {}> = {
784
+ type CreatePluginSpec<GlobalConfig extends FrameworkConfig, GlobalEventMap extends FrameworkEventMap, PluginEventMap extends Record<string, unknown>, PluginConfig extends Record<string, unknown>, PluginState, PluginApi extends Record<string, unknown>, DependencyPlugins extends DependencyPluginTuple, CoreApis extends Record<string, unknown> = {}, Helpers extends Record<string, (...arguments_: any[]) => any> = Record<never, never>> = {
785
785
  /**
786
786
  * Declare plugin-specific events via a register callback.
787
787
  * Used for compile-time type inference only — the kernel does not call this at runtime.
@@ -893,6 +893,20 @@ type CreatePluginSpec<GlobalConfig extends FrameworkConfig, GlobalEventMap exten
893
893
  * ```
894
894
  */
895
895
  hooks?: (context: PluginExecutionContext<GlobalConfig, MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>, PluginConfig, PluginState, CoreApis>) => { [EventName in string & keyof MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>]?: (payload: MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>[EventName]) => void | Promise<void> };
896
+ /**
897
+ * Static helper functions exposed directly on the plugin instance.
898
+ * Helpers run before createApp — they receive no context.
899
+ * Use for typed factories and builders that produce config values.
900
+ *
901
+ * @example
902
+ * ```ts
903
+ * helpers: {
904
+ * route: (path: string, component: string) => ({ path, component }),
905
+ * redirect: (from: string, to: string) => ({ from, to, type: 'redirect' as const }),
906
+ * }
907
+ * ```
908
+ */
909
+ helpers?: Helpers;
896
910
  };
897
911
  /**
898
912
  * Bound createPlugin function type, parameterized by the framework's Config and Events.
@@ -907,9 +921,10 @@ type CreatePluginSpec<GlobalConfig extends FrameworkConfig, GlobalEventMap exten
907
921
  * ```
908
922
  */
909
923
  type BoundCreatePluginFunction<GlobalConfig extends FrameworkConfig, GlobalEventMap extends FrameworkEventMap, CoreApis extends Record<string, unknown> = {}> = {
910
- <const PluginName extends string = string, PluginConfig extends Record<string, unknown> = Record<string, never>, PluginState = Record<string, never>, PluginApi extends Record<string, unknown> = Record<string, never>, DependencyPlugins extends DependencyPluginTuple = readonly [], PluginEventMap extends Record<string, unknown> = EmptyPluginEventMap, HookHandlerMap extends Record<string, any> = Record<never, never>>(name: PluginName, spec: Omit<CreatePluginSpec<GlobalConfig, GlobalEventMap, PluginEventMap, PluginConfig, PluginState, PluginApi, DependencyPlugins, CoreApis>, "hooks"> & {
924
+ <const PluginName extends string = string, PluginConfig extends Record<string, unknown> = Record<string, never>, PluginState = Record<string, never>, PluginApi extends Record<string, unknown> = Record<string, never>, DependencyPlugins extends DependencyPluginTuple = readonly [], PluginEventMap extends Record<string, unknown> = EmptyPluginEventMap, HookHandlerMap extends Record<string, any> = Record<never, never>, Helpers extends Record<string, (...arguments_: any[]) => any> = Record<never, never>>(name: PluginName, spec: Omit<CreatePluginSpec<GlobalConfig, GlobalEventMap, PluginEventMap, PluginConfig, PluginState, PluginApi, DependencyPlugins, CoreApis>, "hooks" | "helpers"> & {
911
925
  hooks?: (context: PluginExecutionContext<GlobalConfig, MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>, PluginConfig, PluginState, CoreApis>) => { [K in keyof HookHandlerMap]: K extends string & keyof MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins> ? (payload: MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>[K & keyof MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>]) => void | Promise<void> : never };
912
- }): PluginInstance<PluginName, PluginConfig, PluginState, PluginApi, PluginEventMap>;
926
+ helpers?: Helpers;
927
+ }): PluginInstance<PluginName, PluginConfig, PluginState, PluginApi, PluginEventMap> & Helpers;
913
928
  };
914
929
  /**
915
930
  * Creates a bound `createPlugin` function that captures framework generics.
package/dist/index.d.mts CHANGED
@@ -781,7 +781,7 @@ type RegisterFunction = {
781
781
  * >;
782
782
  * ```
783
783
  */
784
- type CreatePluginSpec<GlobalConfig extends FrameworkConfig, GlobalEventMap extends FrameworkEventMap, PluginEventMap extends Record<string, unknown>, PluginConfig extends Record<string, unknown>, PluginState, PluginApi extends Record<string, unknown>, DependencyPlugins extends DependencyPluginTuple, CoreApis extends Record<string, unknown> = {}> = {
784
+ type CreatePluginSpec<GlobalConfig extends FrameworkConfig, GlobalEventMap extends FrameworkEventMap, PluginEventMap extends Record<string, unknown>, PluginConfig extends Record<string, unknown>, PluginState, PluginApi extends Record<string, unknown>, DependencyPlugins extends DependencyPluginTuple, CoreApis extends Record<string, unknown> = {}, Helpers extends Record<string, (...arguments_: any[]) => any> = Record<never, never>> = {
785
785
  /**
786
786
  * Declare plugin-specific events via a register callback.
787
787
  * Used for compile-time type inference only — the kernel does not call this at runtime.
@@ -893,6 +893,20 @@ type CreatePluginSpec<GlobalConfig extends FrameworkConfig, GlobalEventMap exten
893
893
  * ```
894
894
  */
895
895
  hooks?: (context: PluginExecutionContext<GlobalConfig, MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>, PluginConfig, PluginState, CoreApis>) => { [EventName in string & keyof MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>]?: (payload: MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>[EventName]) => void | Promise<void> };
896
+ /**
897
+ * Static helper functions exposed directly on the plugin instance.
898
+ * Helpers run before createApp — they receive no context.
899
+ * Use for typed factories and builders that produce config values.
900
+ *
901
+ * @example
902
+ * ```ts
903
+ * helpers: {
904
+ * route: (path: string, component: string) => ({ path, component }),
905
+ * redirect: (from: string, to: string) => ({ from, to, type: 'redirect' as const }),
906
+ * }
907
+ * ```
908
+ */
909
+ helpers?: Helpers;
896
910
  };
897
911
  /**
898
912
  * Bound createPlugin function type, parameterized by the framework's Config and Events.
@@ -907,9 +921,10 @@ type CreatePluginSpec<GlobalConfig extends FrameworkConfig, GlobalEventMap exten
907
921
  * ```
908
922
  */
909
923
  type BoundCreatePluginFunction<GlobalConfig extends FrameworkConfig, GlobalEventMap extends FrameworkEventMap, CoreApis extends Record<string, unknown> = {}> = {
910
- <const PluginName extends string = string, PluginConfig extends Record<string, unknown> = Record<string, never>, PluginState = Record<string, never>, PluginApi extends Record<string, unknown> = Record<string, never>, DependencyPlugins extends DependencyPluginTuple = readonly [], PluginEventMap extends Record<string, unknown> = EmptyPluginEventMap, HookHandlerMap extends Record<string, any> = Record<never, never>>(name: PluginName, spec: Omit<CreatePluginSpec<GlobalConfig, GlobalEventMap, PluginEventMap, PluginConfig, PluginState, PluginApi, DependencyPlugins, CoreApis>, "hooks"> & {
924
+ <const PluginName extends string = string, PluginConfig extends Record<string, unknown> = Record<string, never>, PluginState = Record<string, never>, PluginApi extends Record<string, unknown> = Record<string, never>, DependencyPlugins extends DependencyPluginTuple = readonly [], PluginEventMap extends Record<string, unknown> = EmptyPluginEventMap, HookHandlerMap extends Record<string, any> = Record<never, never>, Helpers extends Record<string, (...arguments_: any[]) => any> = Record<never, never>>(name: PluginName, spec: Omit<CreatePluginSpec<GlobalConfig, GlobalEventMap, PluginEventMap, PluginConfig, PluginState, PluginApi, DependencyPlugins, CoreApis>, "hooks" | "helpers"> & {
911
925
  hooks?: (context: PluginExecutionContext<GlobalConfig, MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>, PluginConfig, PluginState, CoreApis>) => { [K in keyof HookHandlerMap]: K extends string & keyof MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins> ? (payload: MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>[K & keyof MergedPluginEvents<GlobalEventMap, PluginEventMap, DependencyPlugins>]) => void | Promise<void> : never };
912
- }): PluginInstance<PluginName, PluginConfig, PluginState, PluginApi, PluginEventMap>;
926
+ helpers?: Helpers;
927
+ }): PluginInstance<PluginName, PluginConfig, PluginState, PluginApi, PluginEventMap> & Helpers;
913
928
  };
914
929
  /**
915
930
  * Creates a bound `createPlugin` function that captures framework generics.
package/dist/index.mjs CHANGED
@@ -740,6 +740,32 @@ function assertValidApi(frameworkId, pluginName, api) {
740
740
  function assertValidCreateState(frameworkId, pluginName, createState) {
741
741
  if (createState !== void 0 && typeof createState !== "function") throw new TypeError(`[${frameworkId}] Plugin "${pluginName}" has invalid createState: expected a function.\n Provide a function like: createState: ctx => ({ key: initialValue })`);
742
742
  }
743
+ /** PluginInstance field names that cannot be used as helper names. */
744
+ const PLUGIN_INSTANCE_RESERVED_KEYS = new Set([
745
+ "name",
746
+ "spec",
747
+ "_phantom"
748
+ ]);
749
+ /**
750
+ * Validates that `helpers` is a plain object of functions if provided.
751
+ * Also rejects helper names that collide with PluginInstance properties.
752
+ *
753
+ * @param frameworkId - Framework identifier used in error messages.
754
+ * @param pluginName - Validated plugin name.
755
+ * @param helpers - Candidate helpers value from plugin spec.
756
+ * @example
757
+ * ```ts
758
+ * assertValidHelpers("my-app", "router", { route: () => ({}) });
759
+ * ```
760
+ */
761
+ function assertValidHelpers(frameworkId, pluginName, helpers) {
762
+ if (helpers === void 0) return;
763
+ if (!isRecord(helpers)) throw new TypeError(`[${frameworkId}] Plugin "${pluginName}" has invalid helpers: expected an object.\n Provide an object of functions: helpers: { myHelper: (...args) => result }`);
764
+ for (const [key, value] of Object.entries(helpers)) {
765
+ if (typeof value !== "function") throw new TypeError(`[${frameworkId}] Plugin "${pluginName}" has invalid helper "${key}": expected a function.\n Each helper must be a function.`);
766
+ if (PLUGIN_INSTANCE_RESERVED_KEYS.has(key)) throw new TypeError(`[${frameworkId}] Plugin "${pluginName}" helper "${key}" conflicts with a PluginInstance property.\n Choose a different helper name.`);
767
+ }
768
+ }
743
769
  /**
744
770
  * Creates a bound `createPlugin` function that captures framework generics.
745
771
  *
@@ -778,10 +804,12 @@ function createPluginFactory(frameworkId) {
778
804
  assertValidHooks(frameworkId, name, spec.hooks);
779
805
  assertValidApi(frameworkId, name, spec.api);
780
806
  assertValidCreateState(frameworkId, name, spec.createState);
807
+ assertValidHelpers(frameworkId, name, spec.helpers);
781
808
  return {
782
809
  name,
783
810
  spec,
784
- _phantom: {}
811
+ _phantom: {},
812
+ ...isRecord(spec.helpers) ? spec.helpers : {}
785
813
  };
786
814
  };
787
815
  return createPlugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/core",
3
- "version": "0.1.0-alpha.4",
3
+ "version": "0.1.0-alpha.5",
4
4
  "author": "Alex Kucherenko",
5
5
  "repository": {
6
6
  "type": "git",