mycelia-kernel-plugin 1.0.0

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.
Files changed (53) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +248 -0
  3. package/bin/cli.js +433 -0
  4. package/package.json +63 -0
  5. package/src/builder/context-resolver.js +62 -0
  6. package/src/builder/dependency-graph-cache.js +105 -0
  7. package/src/builder/dependency-graph.js +141 -0
  8. package/src/builder/facet-validator.js +43 -0
  9. package/src/builder/hook-processor.js +271 -0
  10. package/src/builder/index.js +13 -0
  11. package/src/builder/subsystem-builder.js +104 -0
  12. package/src/builder/utils.js +165 -0
  13. package/src/contract/contracts/hierarchy.contract.js +60 -0
  14. package/src/contract/contracts/index.js +17 -0
  15. package/src/contract/contracts/listeners.contract.js +66 -0
  16. package/src/contract/contracts/processor.contract.js +47 -0
  17. package/src/contract/contracts/queue.contract.js +58 -0
  18. package/src/contract/contracts/router.contract.js +53 -0
  19. package/src/contract/contracts/scheduler.contract.js +65 -0
  20. package/src/contract/contracts/server.contract.js +88 -0
  21. package/src/contract/contracts/speak.contract.js +50 -0
  22. package/src/contract/contracts/storage.contract.js +107 -0
  23. package/src/contract/contracts/websocket.contract.js +90 -0
  24. package/src/contract/facet-contract-registry.js +155 -0
  25. package/src/contract/facet-contract.js +136 -0
  26. package/src/contract/index.js +63 -0
  27. package/src/core/create-hook.js +63 -0
  28. package/src/core/facet.js +189 -0
  29. package/src/core/index.js +3 -0
  30. package/src/hooks/listeners/handler-group-manager.js +88 -0
  31. package/src/hooks/listeners/listener-manager-policies.js +229 -0
  32. package/src/hooks/listeners/listener-manager.js +668 -0
  33. package/src/hooks/listeners/listener-registry.js +176 -0
  34. package/src/hooks/listeners/listener-statistics.js +106 -0
  35. package/src/hooks/listeners/pattern-matcher.js +283 -0
  36. package/src/hooks/listeners/use-listeners.js +164 -0
  37. package/src/hooks/queue/bounded-queue.js +341 -0
  38. package/src/hooks/queue/circular-buffer.js +231 -0
  39. package/src/hooks/queue/subsystem-queue-manager.js +198 -0
  40. package/src/hooks/queue/use-queue.js +96 -0
  41. package/src/hooks/speak/use-speak.js +79 -0
  42. package/src/index.js +49 -0
  43. package/src/manager/facet-manager-transaction.js +45 -0
  44. package/src/manager/facet-manager.js +570 -0
  45. package/src/manager/index.js +3 -0
  46. package/src/system/base-subsystem.js +416 -0
  47. package/src/system/base-subsystem.utils.js +106 -0
  48. package/src/system/index.js +4 -0
  49. package/src/system/standalone-plugin-system.js +70 -0
  50. package/src/utils/debug-flag.js +34 -0
  51. package/src/utils/find-facet.js +30 -0
  52. package/src/utils/logger.js +84 -0
  53. package/src/utils/semver.js +221 -0
@@ -0,0 +1,104 @@
1
+ import { verifySubsystemBuild, buildSubsystem as executeBuild, deepMerge } from './utils.js';
2
+
3
+ export class SubsystemBuilder {
4
+ #subsystem;
5
+ #ctx = {};
6
+ #plan = null;
7
+ #lastCtxHash = null;
8
+ #lastGraphCache = null;
9
+
10
+ constructor(subsystem) {
11
+ if (!subsystem) throw new Error('SubsystemBuilder: subsystem is required');
12
+ this.#subsystem = subsystem;
13
+ // Note: ctx is resolved in verifySubsystemBuild, not stored here
14
+ }
15
+
16
+ // Simple hash function for context comparison
17
+ #hashCtx(ctx) {
18
+ try {
19
+ return JSON.stringify(ctx);
20
+ } catch {
21
+ // If JSON.stringify fails, use a fallback
22
+ return String(ctx);
23
+ }
24
+ }
25
+
26
+ withCtx(ctx = {}) {
27
+ // Shallow merge for most properties
28
+ const merged = { ...this.#ctx, ...ctx };
29
+
30
+ // Deep merge config objects if both exist
31
+ if (this.#ctx.config && typeof this.#ctx.config === 'object' &&
32
+ ctx.config && typeof ctx.config === 'object' &&
33
+ !Array.isArray(this.#ctx.config) && !Array.isArray(ctx.config)) {
34
+ merged.config = deepMerge(this.#ctx.config, ctx.config);
35
+ }
36
+
37
+ this.#ctx = merged;
38
+ return this;
39
+ }
40
+
41
+ clearCtx() {
42
+ this.#ctx = {};
43
+ this.#lastCtxHash = null;
44
+ this.#plan = null; // Clear plan when context is cleared
45
+ return this;
46
+ }
47
+
48
+ plan(graphCache = null) {
49
+ // Determine which graphCache to use (priority: subsystem.ctx > parameter > cached)
50
+ const subsystemCache = this.#subsystem.ctx?.graphCache;
51
+
52
+ // Check if we have a cached plan and if the context hasn't changed
53
+ const currentCtxHash = this.#hashCtx(this.#ctx);
54
+ if (this.#plan && this.#lastCtxHash === currentCtxHash) {
55
+ // Update lastGraphCache if a new one is provided
56
+ const cacheToUse = subsystemCache || (graphCache !== null ? graphCache : this.#lastGraphCache);
57
+ if (cacheToUse !== this.#lastGraphCache) {
58
+ this.#lastGraphCache = cacheToUse;
59
+ }
60
+ // Return this for method chaining
61
+ return this;
62
+ }
63
+
64
+ // Use the determined cache (subsystem.ctx takes priority, then parameter, then null)
65
+ const cacheToUse = subsystemCache || graphCache;
66
+ const result = verifySubsystemBuild(this.#subsystem, this.#ctx, cacheToUse);
67
+ // Extract plan and updated graphCache from result
68
+ const { graphCache: updatedGraphCache, ...plan } = result;
69
+ this.#plan = plan;
70
+ this.#lastCtxHash = currentCtxHash;
71
+ this.#lastGraphCache = updatedGraphCache || cacheToUse;
72
+ // Return this for method chaining
73
+ return this;
74
+ }
75
+
76
+ dryRun(graphCache = null) { return this.plan(graphCache); }
77
+
78
+ getPlan() { return this.#plan; }
79
+
80
+ getGraphCache() { return this.#lastGraphCache; }
81
+
82
+ invalidate() {
83
+ this.#plan = null;
84
+ this.#lastCtxHash = null;
85
+ this.#lastGraphCache = null;
86
+ return this;
87
+ }
88
+
89
+ /** Executes verify → transactional facet add/init/attach → build children. No internal guards here. */
90
+ async build(graphCache = null) {
91
+ // Create or use cached plan
92
+ this.plan(graphCache);
93
+ const plan = this.#plan;
94
+ if (!plan) {
95
+ throw new Error('SubsystemBuilder.build: failed to create plan');
96
+ }
97
+ // graphCache is already included in plan.resolvedCtx by verifySubsystemBuild
98
+ // and will be set on subsystem.ctx by buildSubsystem
99
+
100
+ await executeBuild(this.#subsystem, plan);
101
+ return this.#subsystem;
102
+ }
103
+ }
104
+
@@ -0,0 +1,165 @@
1
+ import { buildChildren } from '../system/base-subsystem.utils.js';
2
+ import { defaultContractRegistry } from '../contract/index.js';
3
+ import { resolveCtx } from './context-resolver.js';
4
+ import { createCacheKey, buildDepGraph, topoSort } from './dependency-graph.js';
5
+ import { validateFacets } from './facet-validator.js';
6
+ import {
7
+ extractHookMetadata,
8
+ orderHooksByDependencies,
9
+ executeHooksAndCreateFacets,
10
+ validateHookDependencies,
11
+ } from './hook-processor.js';
12
+
13
+ // Re-export deepMerge for backward compatibility
14
+ export { deepMerge } from './context-resolver.js';
15
+
16
+ /**
17
+ * VERIFY (pure):
18
+ * - resolve ctx (pure)
19
+ * - collect hooks (defaults + user)
20
+ * - instantiate facets
21
+ * - validate + topo-sort (with caching)
22
+ */
23
+ export function verifySubsystemBuild(subsystem, ctx = {}, graphCache = null) {
24
+ const resolvedCtx = resolveCtx(subsystem, ctx);
25
+
26
+ // Check whether the defaults are defined as a DefaultHooks instance or an array of hooks.
27
+ // If it's a DefaultHooks instance, use the list() method to get the array of hooks.
28
+ const defaults = Array.isArray(subsystem.defaultHooks)
29
+ ? subsystem.defaultHooks
30
+ : (subsystem.defaultHooks?.list?.() || []);
31
+
32
+ const user = Array.isArray(subsystem.hooks) ? subsystem.hooks : [];
33
+ const hooks = [...defaults, ...user];
34
+
35
+ // Extract hook metadata
36
+ const hooksByKind = extractHookMetadata(hooks);
37
+
38
+ // Order hooks based on dependencies
39
+ const orderedHooks = orderHooksByDependencies(hooks);
40
+
41
+ // Execute hooks and create facets
42
+ const { facetsByKind } = executeHooksAndCreateFacets(orderedHooks, resolvedCtx, subsystem, hooksByKind);
43
+
44
+ // Validate facets against their contracts (before dependency graph building)
45
+ validateFacets(facetsByKind, resolvedCtx, subsystem, defaultContractRegistry);
46
+
47
+ // Validate hook.required dependencies exist
48
+ validateHookDependencies(hooksByKind, facetsByKind, subsystem);
49
+
50
+ // Create cache key from sorted facet kinds
51
+ const kinds = Object.keys(facetsByKind);
52
+ const cacheKey = graphCache ? createCacheKey(kinds) : null;
53
+
54
+ // Include graphCache in resolvedCtx so it persists through buildSubsystem
55
+ if (graphCache) {
56
+ resolvedCtx.graphCache = graphCache;
57
+ }
58
+
59
+ // Check cache before building graph
60
+ if (graphCache && cacheKey) {
61
+ const cached = graphCache.get(cacheKey);
62
+ if (cached) {
63
+ if (cached.valid) {
64
+ // Return cached result (skip graph building and sorting)
65
+ return { resolvedCtx, orderedKinds: cached.orderedKinds, facetsByKind, graphCache };
66
+ } else {
67
+ // Throw cached error
68
+ throw new Error(cached.error || 'Cached dependency graph error');
69
+ }
70
+ }
71
+ }
72
+
73
+ // Build graph and sort (will cache result in topoSort)
74
+ const graph = buildDepGraph(hooksByKind, facetsByKind, subsystem);
75
+ const orderedKinds = topoSort(graph, graphCache, cacheKey);
76
+
77
+ return { resolvedCtx, orderedKinds, facetsByKind, graphCache };
78
+ }
79
+
80
+ /**
81
+ * EXECUTE (transactional):
82
+ * - assign resolved ctx
83
+ * - add/init/attach facets via FacetManager.addMany
84
+ * - build children
85
+ */
86
+ export async function buildSubsystem(subsystem, plan) {
87
+ if (!plan) throw new Error('buildSubsystem: invalid plan');
88
+ const { resolvedCtx, orderedKinds, facetsByKind } = plan;
89
+ if (!Array.isArray(orderedKinds)) throw new Error('buildSubsystem: invalid plan');
90
+ if (!facetsByKind || typeof facetsByKind !== 'object' || Array.isArray(facetsByKind)) throw new Error('buildSubsystem: invalid plan');
91
+
92
+ // Validate consistency: if one is non-empty, the other must match
93
+ const hasOrderedKinds = orderedKinds.length > 0;
94
+ const hasFacetsByKind = Object.keys(facetsByKind).length > 0;
95
+
96
+ // If orderedKinds is empty but facetsByKind has items, that's invalid
97
+ if (!hasOrderedKinds && hasFacetsByKind) throw new Error('buildSubsystem: invalid plan');
98
+ // If facetsByKind is empty but orderedKinds has items, that's invalid
99
+ if (hasOrderedKinds && !hasFacetsByKind) throw new Error('buildSubsystem: invalid plan');
100
+ // Both empty is valid (no facets to add)
101
+
102
+ subsystem.ctx = resolvedCtx;
103
+
104
+ // Separate facets into new and overwrite
105
+ const facetsToAdd = {};
106
+ const kindsToAdd = [];
107
+ const facetsToOverwrite = {};
108
+ const kindsToOverwrite = [];
109
+
110
+ for (const kind of orderedKinds) {
111
+ const facet = facetsByKind[kind];
112
+ const existingFacet = subsystem.api.__facets.find(kind);
113
+
114
+ if (!existingFacet) {
115
+ // New facet - add normally
116
+ facetsToAdd[kind] = facet;
117
+ kindsToAdd.push(kind);
118
+ } else if (existingFacet === facet) {
119
+ // Same facet instance - this was added during verify phase for dependency lookups
120
+ // It needs to be properly initialized/attached, so add it
121
+ facetsToAdd[kind] = facet;
122
+ kindsToAdd.push(kind);
123
+ } else {
124
+ // Different facet instance - check if we can overwrite
125
+ const canOverwrite = facet.shouldOverwrite?.() === true;
126
+ if (canOverwrite) {
127
+ // Remove old facet first, then add new one
128
+ facetsToOverwrite[kind] = facet;
129
+ kindsToOverwrite.push(kind);
130
+ } else {
131
+ // Cannot overwrite - skip (keep existing)
132
+ continue;
133
+ }
134
+ }
135
+ }
136
+
137
+ // First, remove overwritten facets
138
+ for (const kind of kindsToOverwrite) {
139
+ subsystem.api.__facets.remove(kind);
140
+ // Also remove from subsystem property if it exists
141
+ if (kind in subsystem) {
142
+ try {
143
+ delete subsystem[kind];
144
+ } catch {
145
+ // Best-effort cleanup
146
+ }
147
+ }
148
+ }
149
+
150
+ // Then add all facets (new + overwritten)
151
+ const allFacets = { ...facetsToAdd, ...facetsToOverwrite };
152
+ const allKinds = [...kindsToAdd, ...kindsToOverwrite];
153
+
154
+ if (allKinds.length > 0) {
155
+ await subsystem.api.__facets.addMany(allKinds, allFacets, {
156
+ init: true,
157
+ attach: true,
158
+ ctx: resolvedCtx,
159
+ api: subsystem.api
160
+ });
161
+ }
162
+
163
+ await buildChildren(subsystem);
164
+ }
165
+
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Hierarchy Facet Contract
3
+ *
4
+ * Defines the contract that hierarchy facets must satisfy.
5
+ * Ensures all required hierarchy management methods are implemented and validates
6
+ * internal structure for compatibility with other hooks and BaseSubsystem.
7
+ *
8
+ * @example
9
+ * import { hierarchyContract } from './hierarchy.contract.mycelia.js';
10
+ *
11
+ * // Enforce contract on a hierarchy facet
12
+ * hierarchyContract.enforce(ctx, api, subsystem, hierarchyFacet);
13
+ */
14
+ import { createFacetContract } from '../facet-contract.js';
15
+
16
+ /**
17
+ * Hierarchy Facet Contract
18
+ *
19
+ * Required methods:
20
+ * - addChild: Register a child subsystem under the current subsystem
21
+ * - removeChild: Remove a registered child subsystem by reference or by name
22
+ * - getChild: Retrieve a specific child subsystem by name
23
+ * - listChildren: Return an array of all currently registered child subsystems
24
+ * - setParent: Set the parent subsystem
25
+ * - getParent: Get the parent subsystem
26
+ * - isRoot: Check if this subsystem is a root (has no parent)
27
+ * - getRoot: Get the root subsystem by traversing up the parent chain
28
+ * - getLineage: Return the full ancestor chain (from root to node)
29
+ *
30
+ * Required properties:
31
+ * - children: Getter for direct access to registry instance (returns an object)
32
+ *
33
+ * Custom validation:
34
+ * - Validates children getter returns an object (not null or primitive)
35
+ */
36
+ export const hierarchyContract = createFacetContract({
37
+ name: 'hierarchy',
38
+ requiredMethods: [
39
+ 'addChild',
40
+ 'removeChild',
41
+ 'getChild',
42
+ 'listChildren',
43
+ 'setParent',
44
+ 'getParent',
45
+ 'isRoot',
46
+ 'getRoot',
47
+ 'getLineage'
48
+ ],
49
+ requiredProperties: [
50
+ 'children'
51
+ ],
52
+ validate: (ctx, api, subsystem, facet) => {
53
+ // Validate children getter exists and returns an object
54
+ const registry = facet.children;
55
+ if (typeof registry !== 'object' || registry === null) {
56
+ throw new Error('Hierarchy facet children getter must return an object');
57
+ }
58
+ }
59
+ });
60
+
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Contract Exports
3
+ *
4
+ * Exports all contract implementations for easy importing.
5
+ */
6
+
7
+ export { hierarchyContract } from './hierarchy.contract.js';
8
+ export { listenersContract } from './listeners.contract.js';
9
+ export { processorContract } from './processor.contract.js';
10
+ export { queueContract } from './queue.contract.js';
11
+ export { routerContract } from './router.contract.js';
12
+ export { schedulerContract } from './scheduler.contract.js';
13
+ export { serverContract } from './server.contract.js';
14
+ export { speakContract } from './speak.contract.js';
15
+ export { storageContract } from './storage.contract.js';
16
+ export { websocketContract } from './websocket.contract.js';
17
+
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Listeners Facet Contract
3
+ *
4
+ * Defines the contract that listeners facets must satisfy.
5
+ * Ensures all required listener management methods are implemented and validates
6
+ * internal structure for compatibility with other hooks.
7
+ *
8
+ * @example
9
+ * import { listenersContract } from './listeners.contract.mycelia.js';
10
+ *
11
+ * // Enforce contract on a listeners facet
12
+ * listenersContract.enforce(ctx, api, subsystem, listenersFacet);
13
+ */
14
+ import { createFacetContract } from '../facet-contract.js';
15
+
16
+ /**
17
+ * Listeners Facet Contract
18
+ *
19
+ * Required methods:
20
+ * - on: Register a listener for a specific message path
21
+ * - off: Unregister a listener for a specific message path
22
+ * - hasListeners: Check if listeners are enabled
23
+ * - enableListeners: Enable listeners and initialize ListenerManager
24
+ * - disableListeners: Disable listeners (but keep manager instance)
25
+ *
26
+ * Required properties:
27
+ * - listeners: Getter for direct access to ListenerManager instance (can be null)
28
+ * - _listenerManager: Function accessor that returns ListenerManager or null
29
+ *
30
+ * Custom validation:
31
+ * - Validates _listenerManager is a function
32
+ * - Validates listeners property exists
33
+ * - Validates _listenerManager() returns object or null
34
+ */
35
+ export const listenersContract = createFacetContract({
36
+ name: 'listeners',
37
+ requiredMethods: [
38
+ 'on',
39
+ 'off',
40
+ 'hasListeners',
41
+ 'enableListeners',
42
+ 'disableListeners'
43
+ ],
44
+ requiredProperties: [
45
+ 'listeners',
46
+ '_listenerManager'
47
+ ],
48
+ validate: (ctx, api, subsystem, facet) => {
49
+ // Validate _listenerManager is a function
50
+ if (typeof facet._listenerManager !== 'function') {
51
+ throw new Error('Listeners facet _listenerManager must be a function');
52
+ }
53
+
54
+ // Validate listeners property exists (getter)
55
+ if (!('listeners' in facet)) {
56
+ throw new Error('Listeners facet must have listeners property');
57
+ }
58
+
59
+ // Validate _listenerManager() returns object or null
60
+ const manager = facet._listenerManager();
61
+ if (manager !== null && (typeof manager !== 'object' || manager === null)) {
62
+ throw new Error('Listeners facet _listenerManager() must return an object or null');
63
+ }
64
+ }
65
+ });
66
+
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Processor Facet Contract
3
+ *
4
+ * Defines the contract that processor facets must satisfy.
5
+ * Ensures all required message processing methods are implemented.
6
+ *
7
+ * @example
8
+ * import { processorContract } from './processor.contract.js';
9
+ *
10
+ * // Enforce contract on a processor facet
11
+ * processorContract.enforce(ctx, api, subsystem, processorFacet);
12
+ */
13
+ import { createFacetContract } from '../facet-contract.js';
14
+
15
+ /**
16
+ * Processor Facet Contract
17
+ *
18
+ * Required methods:
19
+ * - accept: Accept a message and place it on the queue (or process immediately for queries)
20
+ * - processMessage: Process a message through the complete processing pipeline
21
+ * - processTick: Process a single message from the queue (process one tick)
22
+ * - processImmediately: Process a message immediately without queuing
23
+ *
24
+ * Required properties:
25
+ * - None (processor doesn't expose internal properties)
26
+ *
27
+ * Custom validation:
28
+ * - None (methods are validated by requiredMethods check)
29
+ */
30
+ export const processorContract = createFacetContract({
31
+ name: 'processor',
32
+ requiredMethods: [
33
+ 'accept',
34
+ 'processMessage',
35
+ 'processTick',
36
+ 'processImmediately'
37
+ ],
38
+ requiredProperties: [],
39
+ validate: null
40
+ });
41
+
42
+
43
+
44
+
45
+
46
+
47
+
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Queue Facet Contract
3
+ *
4
+ * Defines the contract that queue facets must satisfy.
5
+ * Ensures all required queue methods are implemented and validates
6
+ * internal structure for compatibility with other hooks.
7
+ *
8
+ * @example
9
+ * import { queueContract } from './queue.contract.js';
10
+ *
11
+ * // Enforce contract on a queue facet
12
+ * queueContract.enforce(ctx, api, subsystem, queueFacet);
13
+ */
14
+ import { createFacetContract } from '../facet-contract.js';
15
+
16
+ /**
17
+ * Queue Facet Contract
18
+ *
19
+ * Required methods:
20
+ * - selectNextMessage: Dequeue and return the next message-options pair
21
+ * - hasMessagesToProcess: Check if queue has messages to process
22
+ * - getQueueStatus: Get queue status information (size, capacity, utilization, etc.)
23
+ *
24
+ * Required properties:
25
+ * - _queueManager: Internal queue manager instance (used by useMessageProcessor for enqueueing)
26
+ * - queue: Direct access to underlying BoundedQueue instance
27
+ *
28
+ * Custom validation:
29
+ * - Validates _queueManager is an object with enqueue method
30
+ * - Validates queue property is an object
31
+ */
32
+ export const queueContract = createFacetContract({
33
+ name: 'queue',
34
+ requiredMethods: [
35
+ 'selectNextMessage',
36
+ 'hasMessagesToProcess',
37
+ 'getQueueStatus'
38
+ ],
39
+ requiredProperties: [
40
+ '_queueManager',
41
+ 'queue'
42
+ ],
43
+ validate: (ctx, api, subsystem, facet) => {
44
+ // Validate that _queueManager is an object with enqueue method
45
+ if (typeof facet._queueManager !== 'object' || facet._queueManager === null) {
46
+ throw new Error('Queue facet _queueManager must be an object');
47
+ }
48
+ if (typeof facet._queueManager.enqueue !== 'function') {
49
+ throw new Error('Queue facet _queueManager must have enqueue method');
50
+ }
51
+
52
+ // Validate queue property is an object
53
+ if (typeof facet.queue !== 'object' || facet.queue === null) {
54
+ throw new Error('Queue facet queue property must be an object');
55
+ }
56
+ }
57
+ });
58
+
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Router Facet Contract
3
+ *
4
+ * Defines the contract that router facets must satisfy.
5
+ * Ensures all required routing methods are implemented and validates
6
+ * internal structure for compatibility with other hooks.
7
+ *
8
+ * @example
9
+ * import { routerContract } from './router.contract.js';
10
+ *
11
+ * // Enforce contract on a router facet
12
+ * routerContract.enforce(ctx, api, subsystem, routerFacet);
13
+ */
14
+ import { createFacetContract } from '../facet-contract.js';
15
+
16
+ /**
17
+ * Router Facet Contract
18
+ *
19
+ * Required methods:
20
+ * - registerRoute: Register a route pattern with a handler
21
+ * - match: Match a path against registered routes
22
+ * - route: Route a message by matching its path and executing the handler
23
+ * - unregisterRoute: Unregister a route pattern
24
+ * - hasRoute: Check if a route pattern is registered
25
+ * - getRoutes: Get all registered routes
26
+ *
27
+ * Required properties:
28
+ * - _routeRegistry: Internal router instance (used by useMessageProcessor)
29
+ *
30
+ * Custom validation:
31
+ * - Validates _routeRegistry is an object (not null or primitive)
32
+ */
33
+ export const routerContract = createFacetContract({
34
+ name: 'router',
35
+ requiredMethods: [
36
+ 'registerRoute',
37
+ 'match',
38
+ 'route',
39
+ 'unregisterRoute',
40
+ 'hasRoute',
41
+ 'getRoutes'
42
+ ],
43
+ requiredProperties: [
44
+ '_routeRegistry'
45
+ ],
46
+ validate: (ctx, api, subsystem, facet) => {
47
+ // Validate that _routeRegistry is an object (not null or primitive)
48
+ if (typeof facet._routeRegistry !== 'object' || facet._routeRegistry === null) {
49
+ throw new Error('Router facet _routeRegistry must be an object');
50
+ }
51
+ }
52
+ });
53
+
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Scheduler Facet Contract
3
+ *
4
+ * Defines the contract that scheduler facets must satisfy.
5
+ * Ensures all required scheduling methods are implemented and validates
6
+ * internal structure for compatibility with other hooks and BaseSubsystem.
7
+ *
8
+ * @example
9
+ * import { schedulerContract } from './scheduler.contract.js';
10
+ *
11
+ * // Enforce contract on a scheduler facet
12
+ * schedulerContract.enforce(ctx, api, subsystem, schedulerFacet);
13
+ */
14
+ import { createFacetContract } from '../facet-contract.js';
15
+
16
+ /**
17
+ * Scheduler Facet Contract
18
+ *
19
+ * Required methods:
20
+ * - process: Process messages during a time slice
21
+ * - pauseProcessing: Pause message processing
22
+ * - resumeProcessing: Resume message processing
23
+ * - isPaused: Check if processing is paused
24
+ * - isProcessing: Check if currently processing
25
+ * - getPriority: Get subsystem priority
26
+ * - setPriority: Set subsystem priority
27
+ * - configureScheduler: Configure scheduler options
28
+ * - getScheduler: Get scheduler instance
29
+ *
30
+ * Required properties:
31
+ * - _scheduler: Internal scheduler instance (used internally by other hooks)
32
+ *
33
+ * Custom validation:
34
+ * - Validates _scheduler is an object (not null or primitive)
35
+ */
36
+ export const schedulerContract = createFacetContract({
37
+ name: 'scheduler',
38
+ requiredMethods: [
39
+ 'process',
40
+ 'pauseProcessing',
41
+ 'resumeProcessing',
42
+ 'isPaused',
43
+ 'isProcessing',
44
+ 'getPriority',
45
+ 'setPriority',
46
+ 'configureScheduler',
47
+ 'getScheduler'
48
+ ],
49
+ requiredProperties: [
50
+ '_scheduler'
51
+ ],
52
+ validate: (ctx, api, subsystem, facet) => {
53
+ // Validate _scheduler is an object
54
+ if (typeof facet._scheduler !== 'object' || facet._scheduler === null) {
55
+ throw new Error('Scheduler facet _scheduler must be an object');
56
+ }
57
+ }
58
+ });
59
+
60
+
61
+
62
+
63
+
64
+
65
+