mycelia-kernel-plugin 1.2.0 → 1.4.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.
- package/README.md +89 -6
- package/bin/cli.js +769 -3
- package/package.json +15 -5
- package/src/angular/builders.js +37 -0
- package/src/angular/helpers.js +102 -0
- package/src/angular/index.js +189 -0
- package/src/angular/services.js +32 -0
- package/src/builder/dependency-graph.js +65 -9
- package/src/builder/hook-processor.js +26 -4
- package/src/builder/utils.js +78 -22
- package/src/core/facet.js +16 -3
- package/src/index.js +18 -0
- package/src/manager/facet-manager.js +10 -2
- package/src/qwik/builders.js +39 -0
- package/src/qwik/index.js +178 -0
- package/src/qwik/listeners.js +96 -0
- package/src/qwik/queues.js +87 -0
- package/src/qwik/signals.js +32 -0
- package/src/react/README.md +3 -0
- package/src/solid/README.md +69 -0
- package/src/solid/index.js +387 -0
- package/src/svelte/builders.js +43 -0
- package/src/svelte/index.js +183 -0
- package/src/svelte/listeners.js +96 -0
- package/src/svelte/queues.js +114 -0
- package/src/svelte/stores.js +36 -0
- package/src/utils/instrumentation.js +204 -0
- package/src/utils/use-base.js +205 -30
- package/src/vue/builders.js +40 -0
- package/src/vue/composables.js +37 -0
- package/src/vue/index.js +252 -0
- package/src/vue/listeners.js +78 -0
- package/src/vue/queues.js +113 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mycelia Plugin System - Svelte Bindings
|
|
3
|
+
*
|
|
4
|
+
* Svelte utilities that make the Mycelia Plugin System feel natural
|
|
5
|
+
* inside Svelte applications using stores and context.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```svelte
|
|
9
|
+
* <script>
|
|
10
|
+
* import { setMyceliaSystem, useFacet, useListener } from 'mycelia-kernel-plugin/svelte';
|
|
11
|
+
* import { buildSystem } from './system.js';
|
|
12
|
+
*
|
|
13
|
+
* let system;
|
|
14
|
+
* buildSystem().then(s => {
|
|
15
|
+
* system = s;
|
|
16
|
+
* setMyceliaSystem(system);
|
|
17
|
+
* });
|
|
18
|
+
* </script>
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { writable, derived, get } from 'svelte/store';
|
|
23
|
+
import { setContext, getContext } from 'svelte';
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Core Bindings: Context + Basic Stores
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
const MYCELIA_KEY = Symbol('mycelia');
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* setMyceliaSystem - Provide Mycelia system to Svelte component tree
|
|
33
|
+
*
|
|
34
|
+
* @param {Object} system - The Mycelia system instance
|
|
35
|
+
* @returns {Object} Context object with system, loading, and error stores
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```svelte
|
|
39
|
+
* <script>
|
|
40
|
+
* import { onMount } from 'svelte';
|
|
41
|
+
* import { setMyceliaSystem } from 'mycelia-kernel-plugin/svelte';
|
|
42
|
+
* import { buildSystem } from './system.js';
|
|
43
|
+
*
|
|
44
|
+
* let system;
|
|
45
|
+
*
|
|
46
|
+
* onMount(async () => {
|
|
47
|
+
* system = await buildSystem();
|
|
48
|
+
* setMyceliaSystem(system);
|
|
49
|
+
* });
|
|
50
|
+
* </script>
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function setMyceliaSystem(system) {
|
|
54
|
+
const systemStore = writable(system);
|
|
55
|
+
const loadingStore = writable(false);
|
|
56
|
+
const errorStore = writable(null);
|
|
57
|
+
|
|
58
|
+
const context = {
|
|
59
|
+
system: systemStore,
|
|
60
|
+
loading: loadingStore,
|
|
61
|
+
error: errorStore
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
setContext(MYCELIA_KEY, context);
|
|
65
|
+
|
|
66
|
+
return context;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* getMyceliaSystem - Get Mycelia system store from context
|
|
71
|
+
*
|
|
72
|
+
* @returns {import('svelte/store').Writable} Writable store containing the system instance
|
|
73
|
+
* @throws {Error} If used outside setMyceliaSystem context
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```svelte
|
|
77
|
+
* <script>
|
|
78
|
+
* import { getMyceliaSystem } from 'mycelia-kernel-plugin/svelte';
|
|
79
|
+
*
|
|
80
|
+
* const systemStore = getMyceliaSystem();
|
|
81
|
+
* $: system = $systemStore;
|
|
82
|
+
* </script>
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export function getMyceliaSystem() {
|
|
86
|
+
const context = getContext(MYCELIA_KEY);
|
|
87
|
+
if (!context) {
|
|
88
|
+
throw new Error('getMyceliaSystem must be used within setMyceliaSystem context');
|
|
89
|
+
}
|
|
90
|
+
return context.system;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* getMyceliaContext - Get the full Mycelia context (system, loading, error stores)
|
|
95
|
+
*
|
|
96
|
+
* @returns {Object} Context object with system, loading, and error stores
|
|
97
|
+
* @throws {Error} If used outside setMyceliaSystem context
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```svelte
|
|
101
|
+
* <script>
|
|
102
|
+
* import { getMyceliaContext } from 'mycelia-kernel-plugin/svelte';
|
|
103
|
+
*
|
|
104
|
+
* const { system, loading, error } = getMyceliaContext();
|
|
105
|
+
* </script>
|
|
106
|
+
*
|
|
107
|
+
* {#if $loading}
|
|
108
|
+
* <div>Loading...</div>
|
|
109
|
+
* {:else if $error}
|
|
110
|
+
* <div>Error: {$error.message}</div>
|
|
111
|
+
* {:else}
|
|
112
|
+
* <div>System ready: {$system.name}</div>
|
|
113
|
+
* {/if}
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export function getMyceliaContext() {
|
|
117
|
+
const context = getContext(MYCELIA_KEY);
|
|
118
|
+
if (!context) {
|
|
119
|
+
throw new Error('getMyceliaContext must be used within setMyceliaSystem context');
|
|
120
|
+
}
|
|
121
|
+
return context;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* useMycelia - Get Mycelia system store (reactive)
|
|
126
|
+
*
|
|
127
|
+
* @returns {import('svelte/store').Writable} Writable store containing the system instance
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```svelte
|
|
131
|
+
* <script>
|
|
132
|
+
* import { useMycelia } from 'mycelia-kernel-plugin/svelte';
|
|
133
|
+
*
|
|
134
|
+
* const systemStore = useMycelia();
|
|
135
|
+
* $: system = $systemStore;
|
|
136
|
+
* $: db = system?.find('database');
|
|
137
|
+
* </script>
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export function useMycelia() {
|
|
141
|
+
return getMyceliaSystem();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* useFacet - Get a facet by kind from the system (reactive store)
|
|
146
|
+
*
|
|
147
|
+
* @param {string} kind - Facet kind identifier
|
|
148
|
+
* @returns {import('svelte/store').Readable} Readable store containing the facet instance, or null if not found
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```svelte
|
|
152
|
+
* <script>
|
|
153
|
+
* import { useFacet } from 'mycelia-kernel-plugin/svelte';
|
|
154
|
+
*
|
|
155
|
+
* const dbStore = useFacet('database');
|
|
156
|
+
* $: db = $dbStore;
|
|
157
|
+
* </script>
|
|
158
|
+
*
|
|
159
|
+
* {#if db}
|
|
160
|
+
* <div>Database ready</div>
|
|
161
|
+
* {/if}
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export function useFacet(kind) {
|
|
165
|
+
const systemStore = getMyceliaSystem();
|
|
166
|
+
|
|
167
|
+
return derived(systemStore, ($system) => {
|
|
168
|
+
return $system?.find?.(kind) ?? null;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Re-export listener helpers
|
|
173
|
+
export { useListener, useEventStream } from './listeners.js';
|
|
174
|
+
|
|
175
|
+
// Re-export queue helpers
|
|
176
|
+
export { useQueueStatus, useQueueDrain } from './queues.js';
|
|
177
|
+
|
|
178
|
+
// Re-export builder helpers
|
|
179
|
+
export { createSvelteSystemBuilder } from './builders.js';
|
|
180
|
+
|
|
181
|
+
// Re-export store generator
|
|
182
|
+
export { createFacetStore } from './stores.js';
|
|
183
|
+
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mycelia Plugin System - Svelte Listener Helpers
|
|
3
|
+
*
|
|
4
|
+
* Svelte utilities for event listener management.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { writable, get } from 'svelte/store';
|
|
8
|
+
import { onDestroy } from 'svelte';
|
|
9
|
+
import { getMyceliaSystem } from './index.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* useListener - Register an event listener with automatic cleanup
|
|
13
|
+
*
|
|
14
|
+
* @param {string} eventName - Event name/path to listen for
|
|
15
|
+
* @param {Function} handler - Handler function: (message) => void
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```svelte
|
|
19
|
+
* <script>
|
|
20
|
+
* import { useListener } from 'mycelia-kernel-plugin/svelte';
|
|
21
|
+
*
|
|
22
|
+
* useListener('user:created', (msg) => {
|
|
23
|
+
* console.log('User created:', msg.body);
|
|
24
|
+
* });
|
|
25
|
+
* </script>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function useListener(eventName, handler) {
|
|
29
|
+
const systemStore = getMyceliaSystem();
|
|
30
|
+
let listeners = null;
|
|
31
|
+
let unsubscribe = null;
|
|
32
|
+
|
|
33
|
+
const setup = () => {
|
|
34
|
+
const system = get(systemStore);
|
|
35
|
+
if (!system) return;
|
|
36
|
+
|
|
37
|
+
listeners = system?.listeners;
|
|
38
|
+
if (!listeners || !listeners.hasListeners?.()) return;
|
|
39
|
+
|
|
40
|
+
listeners.on(eventName, handler);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Subscribe to system store to set up listener when system is available
|
|
44
|
+
unsubscribe = systemStore.subscribe(() => {
|
|
45
|
+
setup();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Initial setup
|
|
49
|
+
setup();
|
|
50
|
+
|
|
51
|
+
onDestroy(() => {
|
|
52
|
+
if (listeners) {
|
|
53
|
+
listeners.off?.(eventName, handler);
|
|
54
|
+
}
|
|
55
|
+
if (unsubscribe) {
|
|
56
|
+
unsubscribe();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* useEventStream - Subscribe to events and keep them in a reactive store
|
|
63
|
+
*
|
|
64
|
+
* @param {string} eventName - Event name/path to listen for
|
|
65
|
+
* @param {Object} [options={}] - Options
|
|
66
|
+
* @param {boolean} [options.accumulate=false] - If true, accumulate events in array
|
|
67
|
+
* @returns {import('svelte/store').Writable} Writable store containing latest event value, array of events, or null
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```svelte
|
|
71
|
+
* <script>
|
|
72
|
+
* import { useEventStream } from 'mycelia-kernel-plugin/svelte';
|
|
73
|
+
*
|
|
74
|
+
* const events = useEventStream('todo:created', { accumulate: true });
|
|
75
|
+
* </script>
|
|
76
|
+
*
|
|
77
|
+
* {#each $events as event}
|
|
78
|
+
* <div>{event.text}</div>
|
|
79
|
+
* {/each}
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export function useEventStream(eventName, options = {}) {
|
|
83
|
+
const { accumulate = false } = options;
|
|
84
|
+
const store = writable(accumulate ? [] : null);
|
|
85
|
+
|
|
86
|
+
useListener(eventName, (msg) => {
|
|
87
|
+
if (accumulate) {
|
|
88
|
+
store.update(events => [...events, msg.body]);
|
|
89
|
+
} else {
|
|
90
|
+
store.set(msg.body);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return store;
|
|
95
|
+
}
|
|
96
|
+
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mycelia Plugin System - Svelte Queue Helpers
|
|
3
|
+
*
|
|
4
|
+
* Svelte utilities for queue management.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { writable, get } from 'svelte/store';
|
|
8
|
+
import { onMount, onDestroy } from 'svelte';
|
|
9
|
+
import { useFacet } from './index.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* useQueueStatus - Get queue status with reactive updates
|
|
13
|
+
*
|
|
14
|
+
* @returns {import('svelte/store').Writable} Writable store containing queue status object
|
|
15
|
+
* @returns {number} size - Current queue size
|
|
16
|
+
* @returns {number} capacity - Maximum queue capacity
|
|
17
|
+
* @returns {number} utilization - Utilization ratio (0-1)
|
|
18
|
+
* @returns {boolean} isFull - Whether queue is full
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```svelte
|
|
22
|
+
* <script>
|
|
23
|
+
* import { useQueueStatus } from 'mycelia-kernel-plugin/svelte';
|
|
24
|
+
*
|
|
25
|
+
* const status = useQueueStatus();
|
|
26
|
+
* </script>
|
|
27
|
+
*
|
|
28
|
+
* <div>Queue: {$status.size}/{$status.capacity}</div>
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function useQueueStatus() {
|
|
32
|
+
const queueStore = useFacet('queue');
|
|
33
|
+
const statusStore = writable({
|
|
34
|
+
size: 0,
|
|
35
|
+
capacity: 0,
|
|
36
|
+
utilization: 0,
|
|
37
|
+
isFull: false
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
let intervalId = null;
|
|
41
|
+
|
|
42
|
+
const updateStatus = () => {
|
|
43
|
+
const queue = get(queueStore);
|
|
44
|
+
if (!queue?.getQueueStatus) return;
|
|
45
|
+
|
|
46
|
+
const newStatus = queue.getQueueStatus();
|
|
47
|
+
statusStore.set({
|
|
48
|
+
size: newStatus.size || 0,
|
|
49
|
+
capacity: newStatus.maxSize || 0,
|
|
50
|
+
utilization: newStatus.utilization || 0,
|
|
51
|
+
isFull: newStatus.isFull || false
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
onMount(() => {
|
|
56
|
+
updateStatus();
|
|
57
|
+
intervalId = setInterval(updateStatus, 100); // Poll every 100ms
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
onDestroy(() => {
|
|
61
|
+
if (intervalId) {
|
|
62
|
+
clearInterval(intervalId);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return statusStore;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* useQueueDrain - Automatically drain queue on mount
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} [options={}] - Options
|
|
73
|
+
* @param {number} [options.interval=100] - Polling interval in ms
|
|
74
|
+
* @param {Function} [options.onMessage] - Callback for each message: (msg, options) => void
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```svelte
|
|
78
|
+
* <script>
|
|
79
|
+
* import { useQueueDrain } from 'mycelia-kernel-plugin/svelte';
|
|
80
|
+
*
|
|
81
|
+
* useQueueDrain({
|
|
82
|
+
* interval: 50,
|
|
83
|
+
* onMessage: (msg) => console.log('Processed:', msg)
|
|
84
|
+
* });
|
|
85
|
+
* </script>
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export function useQueueDrain(options = {}) {
|
|
89
|
+
const { interval = 100, onMessage } = options;
|
|
90
|
+
const queueStore = useFacet('queue');
|
|
91
|
+
let processInterval = null;
|
|
92
|
+
|
|
93
|
+
onMount(() => {
|
|
94
|
+
const queue = get(queueStore);
|
|
95
|
+
if (!queue || !queue.hasMessagesToProcess) return;
|
|
96
|
+
|
|
97
|
+
processInterval = setInterval(() => {
|
|
98
|
+
const currentQueue = get(queueStore);
|
|
99
|
+
if (currentQueue?.hasMessagesToProcess()) {
|
|
100
|
+
const next = currentQueue.selectNextMessage();
|
|
101
|
+
if (next && onMessage) {
|
|
102
|
+
onMessage(next.msg, next.options);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}, interval);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
onDestroy(() => {
|
|
109
|
+
if (processInterval) {
|
|
110
|
+
clearInterval(processInterval);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mycelia Plugin System - Svelte Store Generator
|
|
3
|
+
*
|
|
4
|
+
* Utility for generating custom stores for specific facets.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useFacet } from './index.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* createFacetStore - Generate a custom store for a specific facet kind
|
|
11
|
+
*
|
|
12
|
+
* @param {string} kind - Facet kind identifier
|
|
13
|
+
* @returns {Function} Custom store function: () => import('svelte/store').Readable
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```js
|
|
17
|
+
* // stores/todos.js
|
|
18
|
+
* import { createFacetStore } from 'mycelia-kernel-plugin/svelte';
|
|
19
|
+
*
|
|
20
|
+
* export const useTodos = createFacetStore('todos');
|
|
21
|
+
*
|
|
22
|
+
* // In component
|
|
23
|
+
* <script>
|
|
24
|
+
* import { useTodos } from './stores/todos.js';
|
|
25
|
+
*
|
|
26
|
+
* const todosStore = useTodos();
|
|
27
|
+
* $: todos = $todosStore;
|
|
28
|
+
* </script>
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function createFacetStore(kind) {
|
|
32
|
+
return function useNamedFacet() {
|
|
33
|
+
return useFacet(kind);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instrumentation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides timing instrumentation for debugging build, initialization, and disposal phases.
|
|
5
|
+
* Helps identify slow hooks and facets when debugging performance issues.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createSubsystemLogger } from './logger.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default thresholds for timing warnings (in milliseconds)
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_THRESHOLDS = {
|
|
14
|
+
hookExecution: 50, // Warn if hook execution takes > 50ms
|
|
15
|
+
facetInit: 100, // Warn if facet init takes > 100ms
|
|
16
|
+
facetDispose: 50, // Warn if facet dispose takes > 50ms
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if instrumentation is enabled
|
|
21
|
+
*
|
|
22
|
+
* @param {BaseSubsystem} subsystem - Subsystem instance
|
|
23
|
+
* @returns {boolean} True if instrumentation is enabled
|
|
24
|
+
*/
|
|
25
|
+
export function isInstrumentationEnabled(subsystem) {
|
|
26
|
+
// Enable if debug is on, or if instrumentation is explicitly enabled
|
|
27
|
+
return subsystem?.debug === true || subsystem?.ctx?.instrumentation === true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get timing thresholds from config or use defaults
|
|
32
|
+
*
|
|
33
|
+
* @param {BaseSubsystem} subsystem - Subsystem instance
|
|
34
|
+
* @returns {Object} Thresholds object
|
|
35
|
+
*/
|
|
36
|
+
function getThresholds(subsystem) {
|
|
37
|
+
const config = subsystem?.ctx?.config?.instrumentation || {};
|
|
38
|
+
return {
|
|
39
|
+
hookExecution: config.hookExecutionThreshold ?? DEFAULT_THRESHOLDS.hookExecution,
|
|
40
|
+
facetInit: config.facetInitThreshold ?? DEFAULT_THRESHOLDS.facetInit,
|
|
41
|
+
facetDispose: config.facetDisposeThreshold ?? DEFAULT_THRESHOLDS.facetDispose,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Time a hook execution and log if it exceeds threshold
|
|
47
|
+
*
|
|
48
|
+
* @param {Function} hook - Hook function to execute
|
|
49
|
+
* @param {Object} resolvedCtx - Resolved context
|
|
50
|
+
* @param {Object} api - Subsystem API
|
|
51
|
+
* @param {BaseSubsystem} subsystem - Subsystem instance
|
|
52
|
+
* @returns {*} Result of hook execution
|
|
53
|
+
*/
|
|
54
|
+
export function instrumentHookExecution(hook, resolvedCtx, api, subsystem) {
|
|
55
|
+
if (!isInstrumentationEnabled(subsystem)) {
|
|
56
|
+
// No instrumentation - just execute
|
|
57
|
+
return hook(resolvedCtx, api, subsystem);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const logger = createSubsystemLogger(subsystem);
|
|
61
|
+
const hookKind = hook.kind || '<unknown>';
|
|
62
|
+
const hookSource = hook.source || '<unknown>';
|
|
63
|
+
const thresholds = getThresholds(subsystem);
|
|
64
|
+
|
|
65
|
+
const start = performance.now();
|
|
66
|
+
let result;
|
|
67
|
+
try {
|
|
68
|
+
result = hook(resolvedCtx, api, subsystem);
|
|
69
|
+
} finally {
|
|
70
|
+
const duration = performance.now() - start;
|
|
71
|
+
|
|
72
|
+
if (duration > thresholds.hookExecution) {
|
|
73
|
+
logger.warn(
|
|
74
|
+
`⚠️ Slow hook execution: '${hookKind}' took ${duration.toFixed(2)}ms ` +
|
|
75
|
+
`(threshold: ${thresholds.hookExecution}ms) [${hookSource}]`
|
|
76
|
+
);
|
|
77
|
+
} else {
|
|
78
|
+
logger.log(`✓ Hook '${hookKind}' executed in ${duration.toFixed(2)}ms [${hookSource}]`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Time a facet initialization callback and log if it exceeds threshold
|
|
87
|
+
*
|
|
88
|
+
* @param {Facet} facet - Facet instance
|
|
89
|
+
* @param {Object} ctx - Context object
|
|
90
|
+
* @param {Object} api - Subsystem API
|
|
91
|
+
* @param {BaseSubsystem} subsystem - Subsystem instance
|
|
92
|
+
* @param {Function} initCallback - The init callback to execute
|
|
93
|
+
* @returns {Promise<void>}
|
|
94
|
+
*/
|
|
95
|
+
export async function instrumentFacetInit(facet, ctx, api, subsystem, initCallback) {
|
|
96
|
+
if (!initCallback) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!isInstrumentationEnabled(subsystem)) {
|
|
101
|
+
// No instrumentation - call callback directly
|
|
102
|
+
return initCallback({ ctx, api, subsystem, facet });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const logger = createSubsystemLogger(subsystem);
|
|
106
|
+
const facetKind = facet.getKind?.() || '<unknown>';
|
|
107
|
+
const facetSource = facet.getSource?.() || '<unknown>';
|
|
108
|
+
const thresholds = getThresholds(subsystem);
|
|
109
|
+
|
|
110
|
+
const start = performance.now();
|
|
111
|
+
try {
|
|
112
|
+
await initCallback({ ctx, api, subsystem, facet });
|
|
113
|
+
} finally {
|
|
114
|
+
const duration = performance.now() - start;
|
|
115
|
+
|
|
116
|
+
if (duration > thresholds.facetInit) {
|
|
117
|
+
logger.warn(
|
|
118
|
+
`⚠️ Slow facet initialization: '${facetKind}' took ${duration.toFixed(2)}ms ` +
|
|
119
|
+
`(threshold: ${thresholds.facetInit}ms) [${facetSource}]`
|
|
120
|
+
);
|
|
121
|
+
} else {
|
|
122
|
+
logger.log(`✓ Facet '${facetKind}' initialized in ${duration.toFixed(2)}ms [${facetSource}]`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Time a facet disposal callback and warn if it exceeds threshold
|
|
129
|
+
*
|
|
130
|
+
* @param {Facet} facet - Facet instance
|
|
131
|
+
* @param {BaseSubsystem} subsystem - Subsystem instance
|
|
132
|
+
* @param {Function} disposeCallback - The dispose callback to execute
|
|
133
|
+
* @returns {Promise<void>}
|
|
134
|
+
*/
|
|
135
|
+
export async function instrumentDisposeCallback(facet, subsystem, disposeCallback) {
|
|
136
|
+
if (!disposeCallback) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!isInstrumentationEnabled(subsystem)) {
|
|
141
|
+
// No instrumentation - call callback directly
|
|
142
|
+
return disposeCallback(facet);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const logger = createSubsystemLogger(subsystem);
|
|
146
|
+
const facetKind = facet.getKind?.() || '<unknown>';
|
|
147
|
+
const facetSource = facet.getSource?.() || '<unknown>';
|
|
148
|
+
const thresholds = getThresholds(subsystem);
|
|
149
|
+
|
|
150
|
+
const start = performance.now();
|
|
151
|
+
let errorOccurred = false;
|
|
152
|
+
try {
|
|
153
|
+
await disposeCallback(facet);
|
|
154
|
+
const duration = performance.now() - start;
|
|
155
|
+
|
|
156
|
+
if (duration > thresholds.facetDispose) {
|
|
157
|
+
logger.warn(
|
|
158
|
+
`⚠️ Slow facet disposal: '${facetKind}' took ${duration.toFixed(2)}ms ` +
|
|
159
|
+
`(threshold: ${thresholds.facetDispose}ms) [${facetSource}]`
|
|
160
|
+
);
|
|
161
|
+
} else {
|
|
162
|
+
logger.log(`✓ Facet '${facetKind}' disposed in ${duration.toFixed(2)}ms [${facetSource}]`);
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
errorOccurred = true;
|
|
166
|
+
const duration = performance.now() - start;
|
|
167
|
+
logger.warn(
|
|
168
|
+
`⚠️ Facet disposal error: '${facetKind}' failed after ${duration.toFixed(2)}ms ` +
|
|
169
|
+
`[${facetSource}]: ${error.message}`
|
|
170
|
+
);
|
|
171
|
+
// Re-throw so disposeAll can catch and handle it
|
|
172
|
+
throw error;
|
|
173
|
+
} finally {
|
|
174
|
+
// Ensure timing is logged even if error occurred
|
|
175
|
+
if (!errorOccurred) {
|
|
176
|
+
// Already logged in try block
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Time the entire build phase and log summary
|
|
183
|
+
*
|
|
184
|
+
* @param {BaseSubsystem} subsystem - Subsystem instance
|
|
185
|
+
* @param {Function} buildFn - Build function to execute
|
|
186
|
+
* @returns {Promise<void>}
|
|
187
|
+
*/
|
|
188
|
+
export async function instrumentBuildPhase(subsystem, buildFn) {
|
|
189
|
+
if (!isInstrumentationEnabled(subsystem)) {
|
|
190
|
+
// No instrumentation - just build
|
|
191
|
+
return buildFn();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const logger = createSubsystemLogger(subsystem);
|
|
195
|
+
const start = performance.now();
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
await buildFn();
|
|
199
|
+
} finally {
|
|
200
|
+
const duration = performance.now() - start;
|
|
201
|
+
logger.log(`📦 Build phase completed in ${duration.toFixed(2)}ms`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|