@noego/wood 0.0.4 → 0.1.1
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 +9 -0
- package/bin/wood.js +2 -1
- package/dist/client/controller_contract.cjs.map +1 -1
- package/dist/client/controller_contract.d.cts +4 -2
- package/dist/client/controller_contract.d.ts +4 -2
- package/dist/client/index.cjs +15 -2
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +2 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.js +16 -3
- package/dist/client/index.js.map +1 -1
- package/dist/codegen/preload_generator.cjs +26 -0
- package/dist/codegen/preload_generator.cjs.map +1 -1
- package/dist/codegen/preload_generator.js +26 -0
- package/dist/codegen/preload_generator.js.map +1 -1
- package/dist/controller/controller_lifecycle.cjs +41 -0
- package/dist/controller/controller_lifecycle.cjs.map +1 -0
- package/dist/controller/controller_lifecycle.d.cts +12 -0
- package/dist/controller/controller_lifecycle.d.ts +12 -0
- package/dist/controller/controller_lifecycle.js +17 -0
- package/dist/controller/controller_lifecycle.js.map +1 -0
- package/dist/controller/index.cjs +5 -2
- package/dist/controller/index.cjs.map +1 -1
- package/dist/controller/index.d.cts +1 -0
- package/dist/controller/index.d.ts +1 -0
- package/dist/controller/index.js +3 -1
- package/dist/controller/index.js.map +1 -1
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/main/index.cjs +61 -16
- package/dist/main/index.cjs.map +1 -1
- package/dist/main/index.d.cts +3 -0
- package/dist/main/index.d.ts +3 -0
- package/dist/main/index.js +62 -16
- package/dist/main/index.js.map +1 -1
- package/dist/navigation/navigation_identity.cjs +43 -0
- package/dist/navigation/navigation_identity.cjs.map +1 -0
- package/dist/navigation/navigation_identity.d.cts +5 -0
- package/dist/navigation/navigation_identity.d.ts +5 -0
- package/dist/navigation/navigation_identity.js +18 -0
- package/dist/navigation/navigation_identity.js.map +1 -0
- package/dist/router/wood_router.cjs +36 -11
- package/dist/router/wood_router.cjs.map +1 -1
- package/dist/router/wood_router.js +36 -11
- package/dist/router/wood_router.js.map +1 -1
- package/dist/runtime/runtime.cjs +11 -4
- package/dist/runtime/runtime.cjs.map +1 -1
- package/dist/runtime/runtime.d.cts +2 -0
- package/dist/runtime/runtime.d.ts +2 -0
- package/dist/runtime/runtime.js +12 -5
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/testing/create_controller_harness.cjs +4 -4
- package/dist/testing/create_controller_harness.cjs.map +1 -1
- package/dist/testing/create_controller_harness.d.cts +1 -1
- package/dist/testing/create_controller_harness.d.ts +1 -1
- package/dist/testing/create_controller_harness.js +4 -4
- package/dist/testing/create_controller_harness.js.map +1 -1
- package/dist/tracing/index.cjs +6 -2
- package/dist/tracing/index.cjs.map +1 -1
- package/dist/tracing/index.d.cts +2 -2
- package/dist/tracing/index.d.ts +2 -2
- package/dist/tracing/index.js +5 -1
- package/dist/tracing/index.js.map +1 -1
- package/dist/tracing/trace_hub.cjs +38 -11
- package/dist/tracing/trace_hub.cjs.map +1 -1
- package/dist/tracing/trace_hub.d.cts +3 -1
- package/dist/tracing/trace_hub.d.ts +3 -1
- package/dist/tracing/trace_hub.js +35 -10
- package/dist/tracing/trace_hub.js.map +1 -1
- package/dist/tracing/trace_provider.cjs +63 -6
- package/dist/tracing/trace_provider.cjs.map +1 -1
- package/dist/tracing/trace_provider.d.cts +10 -2
- package/dist/tracing/trace_provider.d.ts +10 -2
- package/dist/tracing/trace_provider.js +64 -6
- package/dist/tracing/trace_provider.js.map +1 -1
- package/package.json +17 -17
- package/src/components/NavigationShell.svelte +18 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/testing/create_controller_harness.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport {\n App,\n clearClientAppForTests,\n clearClientContainerForTests,\n clearClientLoggersForTests,\n clearClientNavigationForTests,\n clearClientWindowControlsForTests,\n getApp,\n getWindowControls,\n Ipc,\n WOOD_INTERNAL_BRIDGE_KEY,\n} from '../client/index.js';\nimport { getIpc } from '../ipc/index.js';\nimport { Navigation, getNavigation } from '../navigation/index.js';\nimport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nimport { parseOperations } from '../parser/operations_parser.cjs';\nimport type { OperationDef } from '../types/operations.cjs';\nimport { createRouterBridge, type RouterBridgeResult } from './create_router_bridge.cjs';\nimport { createTestRouter } from './create_test_router.cjs';\n\nexport interface CreateControllerHarnessOptions<T> {\n controllerClass: new (...args: any[]) => T;\n container: any;\n operations: string | string[];\n controllersDir: string;\n middlewareDir?: string;\n configureContainer?: (container: any) => void | Promise<void>;\n configureRouter?: (router: ReturnType<typeof createTestRouter>['router']) => void | Promise<void>;\n prepareClient?: (context: {\n bridge: Record<string, any>;\n events: RouterBridgeResult['events'];\n navigation: Navigation;\n container: any;\n }) => void | Promise<void>;\n initialize?: boolean;\n}\n\nexport interface ControllerHarnessResult<T> extends RouterBridgeResult {\n controller: T;\n container: any;\n router: ReturnType<typeof createTestRouter>['router'];\n navigation: Navigation;\n trace: TraceProvider;\n destroy: () => void;\n}\n\ntype MutableGlobal = typeof globalThis & {\n [WOOD_INTERNAL_BRIDGE_KEY]?: unknown;\n wood?: unknown;\n};\n\nfunction readOperations(input: string): Map<string, OperationDef> {\n if (input.includes('\\n') || input.includes(':')) {\n return parseOperations(input);\n }\n\n return parseOperations(readFileSync(input, 'utf-8'));\n}\n\nfunction mergeOperations(inputs: string | string[]): Map<string, OperationDef> {\n const merged = new Map<string, OperationDef>();\n\n for (const input of Array.isArray(inputs) ? inputs : [inputs]) {\n for (const [channel, operation] of readOperations(input)) {\n merged.set(channel, operation);\n }\n }\n\n return merged;\n}\n\nfunction createRpcManifest(operations: Map<string, OperationDef>) {\n return Array.from(operations.values()).map((operation) => ({\n channel: operation.channel,\n controller: operation.controller,\n action: operation.action,\n path: operation.channel.split('.'),\n hasInput: !!operation.inputSchema,\n }));\n}\n\nfunction registerClientBuiltins(container: any): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\n traceConfiguration.configureMode('test');\n traceConfiguration.configureRuntime('renderer');\n\n container.registerFunction(Navigation, () => getNavigation());\n container.registerFunction(App, () => getApp());\n container.registerFunction(Ipc, () => getIpc());\n container.registerFunction(WindowControls, () => getWindowControls());\n container.registerFunction(TraceProviderConfiguration, () => traceConfiguration);\n}\n\nclass WindowControls {\n async open(_windowId: string, _options?: Record<string, unknown>): Promise<void> {}\n async close(_windowId?: string): Promise<void> {}\n async focus(_windowId: string): Promise<void> {}\n async minimize(_windowId?: string): Promise<void> {}\n async maximize(_windowId?: string): Promise<void> {}\n}\n\nexport async function createControllerHarness<T>(\n options: CreateControllerHarnessOptions<T>,\n): Promise<ControllerHarnessResult<T>> {\n await options.configureContainer?.(options.container);\n\n if (typeof options.container.instance === 'function') {\n const rootTraceConfiguration = await options.container.instance(TraceProviderConfiguration);\n rootTraceConfiguration.configureMode('test');\n rootTraceConfiguration.configureRuntime('main');\n }\n\n const operations = mergeOperations(options.operations);\n const { router } = createTestRouter('', {\n container: options.container,\n controllersDir: options.controllersDir,\n middlewareDir: options.middlewareDir,\n });\n router.loadOperations(operations);\n await options.configureRouter?.(router);\n\n const { bridge, events } = createRouterBridge(router);\n const harnessBridge = Object.assign(bridge, {\n __rpcManifest: createRpcManifest(operations),\n });\n\n const globalObj = globalThis as MutableGlobal;\n const previousInternalBridge = globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n const previousWoodBridge = globalObj.wood;\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = harnessBridge;\n globalObj.wood = harnessBridge;\n\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n const scopedContainer = options.container.extend();\n registerClientBuiltins(scopedContainer);\n if (typeof scopedContainer.registerClass === 'function') {\n scopedContainer.registerClass(options.controllerClass);\n }\n const navigation = getNavigation();\n await options.prepareClient?.({\n bridge: harnessBridge,\n events,\n navigation,\n container: scopedContainer,\n });\n\n const controller = await scopedContainer.instance(options.controllerClass);\n const trace = await scopedContainer.instance(TraceProvider);\n if (options.initialize && typeof (controller as { initialize?: () => void }).initialize === 'function') {\n await Promise.resolve(\n (controller as { initialize: () => void | Promise<void> }).initialize(),\n );\n }\n\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.created',\n payload: {\n controllerClass: options.controllerClass.name,\n },\n });\n\n return {\n controller,\n bridge: harnessBridge,\n events,\n router,\n container: scopedContainer,\n navigation,\n trace,\n destroy: () => {\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.destroyed',\n });\n if (typeof (controller as { destroy?: () => void }).destroy === 'function') {\n (controller as { destroy: () => void }).destroy();\n }\n events.clear();\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n if (previousInternalBridge === undefined) {\n delete globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n } else {\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = previousInternalBridge;\n }\n\n if (previousWoodBridge === undefined) {\n delete globalObj.wood;\n } else {\n globalObj.wood = previousWoodBridge;\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/testing/create_controller_harness.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport {\n App,\n clearClientAppForTests,\n clearClientContainerForTests,\n clearClientLoggersForTests,\n clearClientNavigationForTests,\n clearClientWindowControlsForTests,\n getApp,\n getWindowControls,\n Ipc,\n WOOD_INTERNAL_BRIDGE_KEY,\n} from '../client/index.js';\nimport { getIpc } from '../ipc/index.js';\nimport { Navigation, getNavigation } from '../navigation/index.js';\nimport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nimport { parseOperations } from '../parser/operations_parser.cjs';\nimport type { OperationDef } from '../types/operations.cjs';\nimport { disposeControllerLifecycle } from '../controller/controller_lifecycle.cjs';\nimport { createRouterBridge, type RouterBridgeResult } from './create_router_bridge.cjs';\nimport { createTestRouter } from './create_test_router.cjs';\n\nexport interface CreateControllerHarnessOptions<T> {\n controllerClass: new (...args: any[]) => T;\n container: any;\n operations: string | string[];\n controllersDir: string;\n middlewareDir?: string;\n configureContainer?: (container: any) => void | Promise<void>;\n configureRouter?: (router: ReturnType<typeof createTestRouter>['router']) => void | Promise<void>;\n prepareClient?: (context: {\n bridge: Record<string, any>;\n events: RouterBridgeResult['events'];\n navigation: Navigation;\n container: any;\n }) => void | Promise<void>;\n initialize?: boolean;\n}\n\nexport interface ControllerHarnessResult<T> extends RouterBridgeResult {\n controller: T;\n container: any;\n router: ReturnType<typeof createTestRouter>['router'];\n navigation: Navigation;\n trace: TraceProvider;\n destroy: () => void | Promise<void>;\n}\n\ntype MutableGlobal = typeof globalThis & {\n [WOOD_INTERNAL_BRIDGE_KEY]?: unknown;\n wood?: unknown;\n};\n\nfunction readOperations(input: string): Map<string, OperationDef> {\n if (input.includes('\\n') || input.includes(':')) {\n return parseOperations(input);\n }\n\n return parseOperations(readFileSync(input, 'utf-8'));\n}\n\nfunction mergeOperations(inputs: string | string[]): Map<string, OperationDef> {\n const merged = new Map<string, OperationDef>();\n\n for (const input of Array.isArray(inputs) ? inputs : [inputs]) {\n for (const [channel, operation] of readOperations(input)) {\n merged.set(channel, operation);\n }\n }\n\n return merged;\n}\n\nfunction createRpcManifest(operations: Map<string, OperationDef>) {\n return Array.from(operations.values()).map((operation) => ({\n channel: operation.channel,\n controller: operation.controller,\n action: operation.action,\n path: operation.channel.split('.'),\n hasInput: !!operation.inputSchema,\n }));\n}\n\nfunction registerClientBuiltins(container: any): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\n traceConfiguration.configureMode('test');\n traceConfiguration.configureRuntime('renderer');\n traceConfiguration.configureEnabled(true);\n\n container.registerFunction(Navigation, () => getNavigation());\n container.registerFunction(App, () => getApp());\n container.registerFunction(Ipc, () => getIpc());\n container.registerFunction(WindowControls, () => getWindowControls());\n container.registerFunction(TraceProviderConfiguration, () => traceConfiguration);\n}\n\nclass WindowControls {\n async open(_windowId: string, _options?: Record<string, unknown>): Promise<void> {}\n async close(_windowId?: string): Promise<void> {}\n async focus(_windowId: string): Promise<void> {}\n async minimize(_windowId?: string): Promise<void> {}\n async maximize(_windowId?: string): Promise<void> {}\n}\n\nexport async function createControllerHarness<T>(\n options: CreateControllerHarnessOptions<T>,\n): Promise<ControllerHarnessResult<T>> {\n await options.configureContainer?.(options.container);\n\n if (typeof options.container.instance === 'function') {\n const rootTraceConfiguration = await options.container.instance(TraceProviderConfiguration);\n rootTraceConfiguration.configureMode('test');\n rootTraceConfiguration.configureRuntime('main');\n }\n\n const operations = mergeOperations(options.operations);\n const { router } = createTestRouter('', {\n container: options.container,\n controllersDir: options.controllersDir,\n middlewareDir: options.middlewareDir,\n });\n router.loadOperations(operations);\n await options.configureRouter?.(router);\n\n const { bridge, events } = createRouterBridge(router);\n const harnessBridge = Object.assign(bridge, {\n __rpcManifest: createRpcManifest(operations),\n });\n\n const globalObj = globalThis as MutableGlobal;\n const previousInternalBridge = globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n const previousWoodBridge = globalObj.wood;\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = harnessBridge;\n globalObj.wood = harnessBridge;\n\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n const scopedContainer = options.container.extend();\n registerClientBuiltins(scopedContainer);\n if (typeof scopedContainer.registerClass === 'function') {\n scopedContainer.registerClass(options.controllerClass);\n }\n const navigation = getNavigation();\n await options.prepareClient?.({\n bridge: harnessBridge,\n events,\n navigation,\n container: scopedContainer,\n });\n\n const controller = await scopedContainer.instance(options.controllerClass);\n const trace = await scopedContainer.instance(TraceProvider);\n if (options.initialize && typeof (controller as { initialize?: () => void }).initialize === 'function') {\n await Promise.resolve(\n (controller as { initialize: () => void | Promise<void> }).initialize(),\n );\n }\n\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.created',\n payload: {\n controllerClass: options.controllerClass.name,\n },\n });\n\n return {\n controller,\n bridge: harnessBridge,\n events,\n router,\n container: scopedContainer,\n navigation,\n trace,\n destroy: async () => {\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.destroyed',\n });\n await disposeControllerLifecycle(controller);\n events.clear();\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n if (previousInternalBridge === undefined) {\n delete globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n } else {\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = previousInternalBridge;\n }\n\n if (previousWoodBridge === undefined) {\n delete globalObj.wood;\n } else {\n globalObj.wood = previousWoodBridge;\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -30,7 +30,7 @@ interface ControllerHarnessResult<T> extends RouterBridgeResult {
|
|
|
30
30
|
router: ReturnType<typeof createTestRouter>['router'];
|
|
31
31
|
navigation: Navigation;
|
|
32
32
|
trace: TraceProvider;
|
|
33
|
-
destroy: () => void
|
|
33
|
+
destroy: () => void | Promise<void>;
|
|
34
34
|
}
|
|
35
35
|
declare function createControllerHarness<T>(options: CreateControllerHarnessOptions<T>): Promise<ControllerHarnessResult<T>>;
|
|
36
36
|
|
|
@@ -30,7 +30,7 @@ interface ControllerHarnessResult<T> extends RouterBridgeResult {
|
|
|
30
30
|
router: ReturnType<typeof createTestRouter>['router'];
|
|
31
31
|
navigation: Navigation;
|
|
32
32
|
trace: TraceProvider;
|
|
33
|
-
destroy: () => void
|
|
33
|
+
destroy: () => void | Promise<void>;
|
|
34
34
|
}
|
|
35
35
|
declare function createControllerHarness<T>(options: CreateControllerHarnessOptions<T>): Promise<ControllerHarnessResult<T>>;
|
|
36
36
|
|
|
@@ -15,6 +15,7 @@ import { getIpc } from "../ipc/index.js";
|
|
|
15
15
|
import { Navigation, getNavigation } from "../navigation/index.js";
|
|
16
16
|
import { TraceProvider, TraceProviderConfiguration } from "../tracing/index.js";
|
|
17
17
|
import { parseOperations } from "../parser/operations_parser.js";
|
|
18
|
+
import { disposeControllerLifecycle } from "../controller/controller_lifecycle.js";
|
|
18
19
|
import { createRouterBridge } from "./create_router_bridge.js";
|
|
19
20
|
import { createTestRouter } from "./create_test_router.js";
|
|
20
21
|
function readOperations(input) {
|
|
@@ -48,6 +49,7 @@ function registerClientBuiltins(container) {
|
|
|
48
49
|
const traceConfiguration = new TraceProviderConfiguration();
|
|
49
50
|
traceConfiguration.configureMode("test");
|
|
50
51
|
traceConfiguration.configureRuntime("renderer");
|
|
52
|
+
traceConfiguration.configureEnabled(true);
|
|
51
53
|
container.registerFunction(Navigation, () => getNavigation());
|
|
52
54
|
container.registerFunction(App, () => getApp());
|
|
53
55
|
container.registerFunction(Ipc, () => getIpc());
|
|
@@ -129,14 +131,12 @@ async function createControllerHarness(options) {
|
|
|
129
131
|
container: scopedContainer,
|
|
130
132
|
navigation,
|
|
131
133
|
trace,
|
|
132
|
-
destroy: () => {
|
|
134
|
+
destroy: async () => {
|
|
133
135
|
trace.info({
|
|
134
136
|
source: "wood.testing.controller-harness",
|
|
135
137
|
type: "harness.destroyed"
|
|
136
138
|
});
|
|
137
|
-
|
|
138
|
-
controller.destroy();
|
|
139
|
-
}
|
|
139
|
+
await disposeControllerLifecycle(controller);
|
|
140
140
|
events.clear();
|
|
141
141
|
clearClientContainerForTests();
|
|
142
142
|
clearClientAppForTests();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/testing/create_controller_harness.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport {\n App,\n clearClientAppForTests,\n clearClientContainerForTests,\n clearClientLoggersForTests,\n clearClientNavigationForTests,\n clearClientWindowControlsForTests,\n getApp,\n getWindowControls,\n Ipc,\n WOOD_INTERNAL_BRIDGE_KEY,\n} from '../client/index.js';\nimport { getIpc } from '../ipc/index.js';\nimport { Navigation, getNavigation } from '../navigation/index.js';\nimport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nimport { parseOperations } from '../parser/operations_parser.js';\nimport type { OperationDef } from '../types/operations.js';\nimport { createRouterBridge, type RouterBridgeResult } from './create_router_bridge.js';\nimport { createTestRouter } from './create_test_router.js';\n\nexport interface CreateControllerHarnessOptions<T> {\n controllerClass: new (...args: any[]) => T;\n container: any;\n operations: string | string[];\n controllersDir: string;\n middlewareDir?: string;\n configureContainer?: (container: any) => void | Promise<void>;\n configureRouter?: (router: ReturnType<typeof createTestRouter>['router']) => void | Promise<void>;\n prepareClient?: (context: {\n bridge: Record<string, any>;\n events: RouterBridgeResult['events'];\n navigation: Navigation;\n container: any;\n }) => void | Promise<void>;\n initialize?: boolean;\n}\n\nexport interface ControllerHarnessResult<T> extends RouterBridgeResult {\n controller: T;\n container: any;\n router: ReturnType<typeof createTestRouter>['router'];\n navigation: Navigation;\n trace: TraceProvider;\n destroy: () => void;\n}\n\ntype MutableGlobal = typeof globalThis & {\n [WOOD_INTERNAL_BRIDGE_KEY]?: unknown;\n wood?: unknown;\n};\n\nfunction readOperations(input: string): Map<string, OperationDef> {\n if (input.includes('\\n') || input.includes(':')) {\n return parseOperations(input);\n }\n\n return parseOperations(readFileSync(input, 'utf-8'));\n}\n\nfunction mergeOperations(inputs: string | string[]): Map<string, OperationDef> {\n const merged = new Map<string, OperationDef>();\n\n for (const input of Array.isArray(inputs) ? inputs : [inputs]) {\n for (const [channel, operation] of readOperations(input)) {\n merged.set(channel, operation);\n }\n }\n\n return merged;\n}\n\nfunction createRpcManifest(operations: Map<string, OperationDef>) {\n return Array.from(operations.values()).map((operation) => ({\n channel: operation.channel,\n controller: operation.controller,\n action: operation.action,\n path: operation.channel.split('.'),\n hasInput: !!operation.inputSchema,\n }));\n}\n\nfunction registerClientBuiltins(container: any): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\n traceConfiguration.configureMode('test');\n traceConfiguration.configureRuntime('renderer');\n\n container.registerFunction(Navigation, () => getNavigation());\n container.registerFunction(App, () => getApp());\n container.registerFunction(Ipc, () => getIpc());\n container.registerFunction(WindowControls, () => getWindowControls());\n container.registerFunction(TraceProviderConfiguration, () => traceConfiguration);\n}\n\nclass WindowControls {\n async open(_windowId: string, _options?: Record<string, unknown>): Promise<void> {}\n async close(_windowId?: string): Promise<void> {}\n async focus(_windowId: string): Promise<void> {}\n async minimize(_windowId?: string): Promise<void> {}\n async maximize(_windowId?: string): Promise<void> {}\n}\n\nexport async function createControllerHarness<T>(\n options: CreateControllerHarnessOptions<T>,\n): Promise<ControllerHarnessResult<T>> {\n await options.configureContainer?.(options.container);\n\n if (typeof options.container.instance === 'function') {\n const rootTraceConfiguration = await options.container.instance(TraceProviderConfiguration);\n rootTraceConfiguration.configureMode('test');\n rootTraceConfiguration.configureRuntime('main');\n }\n\n const operations = mergeOperations(options.operations);\n const { router } = createTestRouter('', {\n container: options.container,\n controllersDir: options.controllersDir,\n middlewareDir: options.middlewareDir,\n });\n router.loadOperations(operations);\n await options.configureRouter?.(router);\n\n const { bridge, events } = createRouterBridge(router);\n const harnessBridge = Object.assign(bridge, {\n __rpcManifest: createRpcManifest(operations),\n });\n\n const globalObj = globalThis as MutableGlobal;\n const previousInternalBridge = globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n const previousWoodBridge = globalObj.wood;\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = harnessBridge;\n globalObj.wood = harnessBridge;\n\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n const scopedContainer = options.container.extend();\n registerClientBuiltins(scopedContainer);\n if (typeof scopedContainer.registerClass === 'function') {\n scopedContainer.registerClass(options.controllerClass);\n }\n const navigation = getNavigation();\n await options.prepareClient?.({\n bridge: harnessBridge,\n events,\n navigation,\n container: scopedContainer,\n });\n\n const controller = await scopedContainer.instance(options.controllerClass);\n const trace = await scopedContainer.instance(TraceProvider);\n if (options.initialize && typeof (controller as { initialize?: () => void }).initialize === 'function') {\n await Promise.resolve(\n (controller as { initialize: () => void | Promise<void> }).initialize(),\n );\n }\n\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.created',\n payload: {\n controllerClass: options.controllerClass.name,\n },\n });\n\n return {\n controller,\n bridge: harnessBridge,\n events,\n router,\n container: scopedContainer,\n navigation,\n trace,\n destroy: () => {\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.destroyed',\n });\n if (typeof (controller as { destroy?: () => void }).destroy === 'function') {\n (controller as { destroy: () => void }).destroy();\n }\n events.clear();\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n if (previousInternalBridge === undefined) {\n delete globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n } else {\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = previousInternalBridge;\n }\n\n if (previousWoodBridge === undefined) {\n delete globalObj.wood;\n } else {\n globalObj.wood = previousWoodBridge;\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/testing/create_controller_harness.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport {\n App,\n clearClientAppForTests,\n clearClientContainerForTests,\n clearClientLoggersForTests,\n clearClientNavigationForTests,\n clearClientWindowControlsForTests,\n getApp,\n getWindowControls,\n Ipc,\n WOOD_INTERNAL_BRIDGE_KEY,\n} from '../client/index.js';\nimport { getIpc } from '../ipc/index.js';\nimport { Navigation, getNavigation } from '../navigation/index.js';\nimport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nimport { parseOperations } from '../parser/operations_parser.js';\nimport type { OperationDef } from '../types/operations.js';\nimport { disposeControllerLifecycle } from '../controller/controller_lifecycle.js';\nimport { createRouterBridge, type RouterBridgeResult } from './create_router_bridge.js';\nimport { createTestRouter } from './create_test_router.js';\n\nexport interface CreateControllerHarnessOptions<T> {\n controllerClass: new (...args: any[]) => T;\n container: any;\n operations: string | string[];\n controllersDir: string;\n middlewareDir?: string;\n configureContainer?: (container: any) => void | Promise<void>;\n configureRouter?: (router: ReturnType<typeof createTestRouter>['router']) => void | Promise<void>;\n prepareClient?: (context: {\n bridge: Record<string, any>;\n events: RouterBridgeResult['events'];\n navigation: Navigation;\n container: any;\n }) => void | Promise<void>;\n initialize?: boolean;\n}\n\nexport interface ControllerHarnessResult<T> extends RouterBridgeResult {\n controller: T;\n container: any;\n router: ReturnType<typeof createTestRouter>['router'];\n navigation: Navigation;\n trace: TraceProvider;\n destroy: () => void | Promise<void>;\n}\n\ntype MutableGlobal = typeof globalThis & {\n [WOOD_INTERNAL_BRIDGE_KEY]?: unknown;\n wood?: unknown;\n};\n\nfunction readOperations(input: string): Map<string, OperationDef> {\n if (input.includes('\\n') || input.includes(':')) {\n return parseOperations(input);\n }\n\n return parseOperations(readFileSync(input, 'utf-8'));\n}\n\nfunction mergeOperations(inputs: string | string[]): Map<string, OperationDef> {\n const merged = new Map<string, OperationDef>();\n\n for (const input of Array.isArray(inputs) ? inputs : [inputs]) {\n for (const [channel, operation] of readOperations(input)) {\n merged.set(channel, operation);\n }\n }\n\n return merged;\n}\n\nfunction createRpcManifest(operations: Map<string, OperationDef>) {\n return Array.from(operations.values()).map((operation) => ({\n channel: operation.channel,\n controller: operation.controller,\n action: operation.action,\n path: operation.channel.split('.'),\n hasInput: !!operation.inputSchema,\n }));\n}\n\nfunction registerClientBuiltins(container: any): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\n traceConfiguration.configureMode('test');\n traceConfiguration.configureRuntime('renderer');\n traceConfiguration.configureEnabled(true);\n\n container.registerFunction(Navigation, () => getNavigation());\n container.registerFunction(App, () => getApp());\n container.registerFunction(Ipc, () => getIpc());\n container.registerFunction(WindowControls, () => getWindowControls());\n container.registerFunction(TraceProviderConfiguration, () => traceConfiguration);\n}\n\nclass WindowControls {\n async open(_windowId: string, _options?: Record<string, unknown>): Promise<void> {}\n async close(_windowId?: string): Promise<void> {}\n async focus(_windowId: string): Promise<void> {}\n async minimize(_windowId?: string): Promise<void> {}\n async maximize(_windowId?: string): Promise<void> {}\n}\n\nexport async function createControllerHarness<T>(\n options: CreateControllerHarnessOptions<T>,\n): Promise<ControllerHarnessResult<T>> {\n await options.configureContainer?.(options.container);\n\n if (typeof options.container.instance === 'function') {\n const rootTraceConfiguration = await options.container.instance(TraceProviderConfiguration);\n rootTraceConfiguration.configureMode('test');\n rootTraceConfiguration.configureRuntime('main');\n }\n\n const operations = mergeOperations(options.operations);\n const { router } = createTestRouter('', {\n container: options.container,\n controllersDir: options.controllersDir,\n middlewareDir: options.middlewareDir,\n });\n router.loadOperations(operations);\n await options.configureRouter?.(router);\n\n const { bridge, events } = createRouterBridge(router);\n const harnessBridge = Object.assign(bridge, {\n __rpcManifest: createRpcManifest(operations),\n });\n\n const globalObj = globalThis as MutableGlobal;\n const previousInternalBridge = globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n const previousWoodBridge = globalObj.wood;\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = harnessBridge;\n globalObj.wood = harnessBridge;\n\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n const scopedContainer = options.container.extend();\n registerClientBuiltins(scopedContainer);\n if (typeof scopedContainer.registerClass === 'function') {\n scopedContainer.registerClass(options.controllerClass);\n }\n const navigation = getNavigation();\n await options.prepareClient?.({\n bridge: harnessBridge,\n events,\n navigation,\n container: scopedContainer,\n });\n\n const controller = await scopedContainer.instance(options.controllerClass);\n const trace = await scopedContainer.instance(TraceProvider);\n if (options.initialize && typeof (controller as { initialize?: () => void }).initialize === 'function') {\n await Promise.resolve(\n (controller as { initialize: () => void | Promise<void> }).initialize(),\n );\n }\n\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.created',\n payload: {\n controllerClass: options.controllerClass.name,\n },\n });\n\n return {\n controller,\n bridge: harnessBridge,\n events,\n router,\n container: scopedContainer,\n navigation,\n trace,\n destroy: async () => {\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.destroyed',\n });\n await disposeControllerLifecycle(controller);\n events.clear();\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n if (previousInternalBridge === undefined) {\n delete globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n } else {\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = previousInternalBridge;\n }\n\n if (previousWoodBridge === undefined) {\n delete globalObj.wood;\n } else {\n globalObj.wood = previousWoodBridge;\n }\n },\n };\n}\n"],"mappings":"AAAA,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,cAAc;AACvB,SAAS,YAAY,qBAAqB;AAC1C,SAAS,eAAe,kCAAkC;AAC1D,SAAS,uBAAuB;AAEhC,SAAS,kCAAkC;AAC3C,SAAS,0BAAmD;AAC5D,SAAS,wBAAwB;AAiCjC,SAAS,eAAe,OAA0C;AAChE,MAAI,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,GAAG,GAAG;AAC/C,WAAO,gBAAgB,KAAK;AAAA,EAC9B;AAEA,SAAO,gBAAgB,aAAa,OAAO,OAAO,CAAC;AACrD;AAEA,SAAS,gBAAgB,QAAsD;AAC7E,QAAM,SAAS,oBAAI,IAA0B;AAE7C,aAAW,SAAS,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM,GAAG;AAC7D,eAAW,CAAC,SAAS,SAAS,KAAK,eAAe,KAAK,GAAG;AACxD,aAAO,IAAI,SAAS,SAAS;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,kBAAkB,YAAuC;AAChE,SAAO,MAAM,KAAK,WAAW,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe;AAAA,IACzD,SAAS,UAAU;AAAA,IACnB,YAAY,UAAU;AAAA,IACtB,QAAQ,UAAU;AAAA,IAClB,MAAM,UAAU,QAAQ,MAAM,GAAG;AAAA,IACjC,UAAU,CAAC,CAAC,UAAU;AAAA,EACxB,EAAE;AACJ;AAEA,SAAS,uBAAuB,WAAsB;AACpD,MAAI,OAAO,UAAU,qBAAqB,YAAY;AACpD;AAAA,EACF;AAEA,QAAM,qBAAqB,IAAI,2BAA2B;AAC1D,qBAAmB,cAAc,MAAM;AACvC,qBAAmB,iBAAiB,UAAU;AAC9C,qBAAmB,iBAAiB,IAAI;AAExC,YAAU,iBAAiB,YAAY,MAAM,cAAc,CAAC;AAC5D,YAAU,iBAAiB,KAAK,MAAM,OAAO,CAAC;AAC9C,YAAU,iBAAiB,KAAK,MAAM,OAAO,CAAC;AAC9C,YAAU,iBAAiB,gBAAgB,MAAM,kBAAkB,CAAC;AACpE,YAAU,iBAAiB,4BAA4B,MAAM,kBAAkB;AACjF;AAEA,MAAM,eAAe;AAAA,EACnB,MAAM,KAAK,WAAmB,UAAmD;AAAA,EAAC;AAAA,EAClF,MAAM,MAAM,WAAmC;AAAA,EAAC;AAAA,EAChD,MAAM,MAAM,WAAkC;AAAA,EAAC;AAAA,EAC/C,MAAM,SAAS,WAAmC;AAAA,EAAC;AAAA,EACnD,MAAM,SAAS,WAAmC;AAAA,EAAC;AACrD;AAEA,eAAsB,wBACpB,SACqC;AACrC,QAAM,QAAQ,qBAAqB,QAAQ,SAAS;AAEpD,MAAI,OAAO,QAAQ,UAAU,aAAa,YAAY;AACpD,UAAM,yBAAyB,MAAM,QAAQ,UAAU,SAAS,0BAA0B;AAC1F,2BAAuB,cAAc,MAAM;AAC3C,2BAAuB,iBAAiB,MAAM;AAAA,EAChD;AAEA,QAAM,aAAa,gBAAgB,QAAQ,UAAU;AACrD,QAAM,EAAE,OAAO,IAAI,iBAAiB,IAAI;AAAA,IACtC,WAAW,QAAQ;AAAA,IACnB,gBAAgB,QAAQ;AAAA,IACxB,eAAe,QAAQ;AAAA,EACzB,CAAC;AACD,SAAO,eAAe,UAAU;AAChC,QAAM,QAAQ,kBAAkB,MAAM;AAEtC,QAAM,EAAE,QAAQ,OAAO,IAAI,mBAAmB,MAAM;AACpD,QAAM,gBAAgB,OAAO,OAAO,QAAQ;AAAA,IAC1C,eAAe,kBAAkB,UAAU;AAAA,EAC7C,CAAC;AAED,QAAM,YAAY;AAClB,QAAM,yBAAyB,UAAU,wBAAwB;AACjE,QAAM,qBAAqB,UAAU;AACrC,YAAU,wBAAwB,IAAI;AACtC,YAAU,OAAO;AAEjB,+BAA6B;AAC7B,yBAAuB;AACvB,gCAA8B;AAC9B,oCAAkC;AAClC,6BAA2B;AAE3B,QAAM,kBAAkB,QAAQ,UAAU,OAAO;AACjD,yBAAuB,eAAe;AACtC,MAAI,OAAO,gBAAgB,kBAAkB,YAAY;AACvD,oBAAgB,cAAc,QAAQ,eAAe;AAAA,EACvD;AACA,QAAM,aAAa,cAAc;AACjC,QAAM,QAAQ,gBAAgB;AAAA,IAC5B,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,WAAW;AAAA,EACb,CAAC;AAED,QAAM,aAAa,MAAM,gBAAgB,SAAS,QAAQ,eAAe;AACzE,QAAM,QAAQ,MAAM,gBAAgB,SAAS,aAAa;AAC1D,MAAI,QAAQ,cAAc,OAAQ,WAA2C,eAAe,YAAY;AACtG,UAAM,QAAQ;AAAA,MACX,WAA0D,WAAW;AAAA,IACxE;AAAA,EACF;AAEA,QAAM,KAAK;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,MACP,iBAAiB,QAAQ,gBAAgB;AAAA,IAC3C;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA,SAAS,YAAY;AACnB,YAAM,KAAK;AAAA,QACT,QAAQ;AAAA,QACR,MAAM;AAAA,MACR,CAAC;AACD,YAAM,2BAA2B,UAAU;AAC3C,aAAO,MAAM;AACb,mCAA6B;AAC7B,6BAAuB;AACvB,oCAA8B;AAC9B,wCAAkC;AAClC,iCAA2B;AAE3B,UAAI,2BAA2B,QAAW;AACxC,eAAO,UAAU,wBAAwB;AAAA,MAC3C,OAAO;AACL,kBAAU,wBAAwB,IAAI;AAAA,MACxC;AAEA,UAAI,uBAAuB,QAAW;AACpC,eAAO,UAAU;AAAA,MACnB,OAAO;AACL,kBAAU,OAAO;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/dist/tracing/index.cjs
CHANGED
|
@@ -33,7 +33,9 @@ __export(tracing_exports, {
|
|
|
33
33
|
publishMainTrace: () => import_trace_hub.publishMainTrace,
|
|
34
34
|
publishTestTrace: () => import_trace_hub.publishTestTrace,
|
|
35
35
|
setMainTraceMinLevel: () => import_trace_hub.setMainTraceMinLevel,
|
|
36
|
-
|
|
36
|
+
setRendererTraceEnabled: () => import_trace_hub.setRendererTraceEnabled,
|
|
37
|
+
setRendererTraceMinLevel: () => import_trace_hub.setRendererTraceMinLevel,
|
|
38
|
+
setRendererTraceTransport: () => import_trace_hub.setRendererTraceTransport
|
|
37
39
|
});
|
|
38
40
|
module.exports = __toCommonJS(tracing_exports);
|
|
39
41
|
var import_trace_hub = require("./trace_hub.js");
|
|
@@ -55,6 +57,8 @@ var import_trace_provider = require("./trace_provider.js");
|
|
|
55
57
|
publishMainTrace,
|
|
56
58
|
publishTestTrace,
|
|
57
59
|
setMainTraceMinLevel,
|
|
58
|
-
|
|
60
|
+
setRendererTraceEnabled,
|
|
61
|
+
setRendererTraceMinLevel,
|
|
62
|
+
setRendererTraceTransport
|
|
59
63
|
});
|
|
60
64
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tracing/index.ts"],"sourcesContent":["export {\n clearTraceHubForTests,\n createTraceCapture,\n dispatchRendererTracePayload,\n emitRendererTrace,\n getTraceEvents$,\n publishMainTrace,\n publishTestTrace,\n setMainTraceMinLevel,\n setRendererTraceMinLevel,\n TraceCapture,\n type TraceCaptureOptions,\n type TraceDispatchResult,\n type TraceEvent,\n type TraceInput,\n type TraceLevel,\n type TraceProcess,\n type TraceWaitOptions,\n} from './trace_hub.js';\n\nexport {\n FrameworkTracer,\n MainTracer,\n RendererTracer,\n TestTracer,\n TraceProvider,\n TraceProviderConfiguration,\n type TraceProviderMode,\n type TraceRuntime,\n} from './trace_provider.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"sources":["../../src/tracing/index.ts"],"sourcesContent":["export {\n clearTraceHubForTests,\n createTraceCapture,\n dispatchRendererTracePayload,\n emitRendererTrace,\n getTraceEvents$,\n publishMainTrace,\n publishTestTrace,\n setMainTraceMinLevel,\n setRendererTraceEnabled,\n setRendererTraceMinLevel,\n setRendererTraceTransport,\n TraceCapture,\n type TraceCaptureOptions,\n type TraceDispatchResult,\n type TraceEvent,\n type TraceInput,\n type TraceLevel,\n type TraceProcess,\n type TraceWaitOptions,\n} from './trace_hub.js';\n\nexport {\n FrameworkTracer,\n MainTracer,\n RendererTracer,\n TestTracer,\n TraceProvider,\n TraceProviderConfiguration,\n type TraceContext,\n type TraceProviderMode,\n type TraceRuntime,\n} from './trace_provider.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAoBO;AAEP,4BAUO;","names":[]}
|
package/dist/tracing/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { TraceCapture, TraceCaptureOptions, TraceDispatchResult, TraceEvent, TraceInput, TraceLevel, TraceProcess, TraceWaitOptions, clearTraceHubForTests, createTraceCapture, dispatchRendererTracePayload, emitRendererTrace, getTraceEvents$, publishMainTrace, publishTestTrace, setMainTraceMinLevel, setRendererTraceMinLevel } from './trace_hub.cjs';
|
|
2
|
-
export { FrameworkTracer, MainTracer, RendererTracer, TestTracer, TraceProvider, TraceProviderConfiguration, TraceProviderMode, TraceRuntime } from './trace_provider.cjs';
|
|
1
|
+
export { TraceCapture, TraceCaptureOptions, TraceDispatchResult, TraceEvent, TraceInput, TraceLevel, TraceProcess, TraceWaitOptions, clearTraceHubForTests, createTraceCapture, dispatchRendererTracePayload, emitRendererTrace, getTraceEvents$, publishMainTrace, publishTestTrace, setMainTraceMinLevel, setRendererTraceEnabled, setRendererTraceMinLevel, setRendererTraceTransport } from './trace_hub.cjs';
|
|
2
|
+
export { FrameworkTracer, MainTracer, RendererTracer, TestTracer, TraceContext, TraceProvider, TraceProviderConfiguration, TraceProviderMode, TraceRuntime } from './trace_provider.cjs';
|
|
3
3
|
import 'rxjs';
|
|
4
4
|
import '@noego/ioc';
|
package/dist/tracing/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { TraceCapture, TraceCaptureOptions, TraceDispatchResult, TraceEvent, TraceInput, TraceLevel, TraceProcess, TraceWaitOptions, clearTraceHubForTests, createTraceCapture, dispatchRendererTracePayload, emitRendererTrace, getTraceEvents$, publishMainTrace, publishTestTrace, setMainTraceMinLevel, setRendererTraceMinLevel } from './trace_hub.js';
|
|
2
|
-
export { FrameworkTracer, MainTracer, RendererTracer, TestTracer, TraceProvider, TraceProviderConfiguration, TraceProviderMode, TraceRuntime } from './trace_provider.js';
|
|
1
|
+
export { TraceCapture, TraceCaptureOptions, TraceDispatchResult, TraceEvent, TraceInput, TraceLevel, TraceProcess, TraceWaitOptions, clearTraceHubForTests, createTraceCapture, dispatchRendererTracePayload, emitRendererTrace, getTraceEvents$, publishMainTrace, publishTestTrace, setMainTraceMinLevel, setRendererTraceEnabled, setRendererTraceMinLevel, setRendererTraceTransport } from './trace_hub.js';
|
|
2
|
+
export { FrameworkTracer, MainTracer, RendererTracer, TestTracer, TraceContext, TraceProvider, TraceProviderConfiguration, TraceProviderMode, TraceRuntime } from './trace_provider.js';
|
|
3
3
|
import 'rxjs';
|
|
4
4
|
import '@noego/ioc';
|
package/dist/tracing/index.js
CHANGED
|
@@ -7,7 +7,9 @@ import {
|
|
|
7
7
|
publishMainTrace,
|
|
8
8
|
publishTestTrace,
|
|
9
9
|
setMainTraceMinLevel,
|
|
10
|
+
setRendererTraceEnabled,
|
|
10
11
|
setRendererTraceMinLevel,
|
|
12
|
+
setRendererTraceTransport,
|
|
11
13
|
TraceCapture
|
|
12
14
|
} from "./trace_hub.js";
|
|
13
15
|
import {
|
|
@@ -34,6 +36,8 @@ export {
|
|
|
34
36
|
publishMainTrace,
|
|
35
37
|
publishTestTrace,
|
|
36
38
|
setMainTraceMinLevel,
|
|
37
|
-
|
|
39
|
+
setRendererTraceEnabled,
|
|
40
|
+
setRendererTraceMinLevel,
|
|
41
|
+
setRendererTraceTransport
|
|
38
42
|
};
|
|
39
43
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tracing/index.ts"],"sourcesContent":["export {\n clearTraceHubForTests,\n createTraceCapture,\n dispatchRendererTracePayload,\n emitRendererTrace,\n getTraceEvents$,\n publishMainTrace,\n publishTestTrace,\n setMainTraceMinLevel,\n setRendererTraceMinLevel,\n TraceCapture,\n type TraceCaptureOptions,\n type TraceDispatchResult,\n type TraceEvent,\n type TraceInput,\n type TraceLevel,\n type TraceProcess,\n type TraceWaitOptions,\n} from './trace_hub.js';\n\nexport {\n FrameworkTracer,\n MainTracer,\n RendererTracer,\n TestTracer,\n TraceProvider,\n TraceProviderConfiguration,\n type TraceProviderMode,\n type TraceRuntime,\n} from './trace_provider.js';\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAQK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,
|
|
1
|
+
{"version":3,"sources":["../../src/tracing/index.ts"],"sourcesContent":["export {\n clearTraceHubForTests,\n createTraceCapture,\n dispatchRendererTracePayload,\n emitRendererTrace,\n getTraceEvents$,\n publishMainTrace,\n publishTestTrace,\n setMainTraceMinLevel,\n setRendererTraceEnabled,\n setRendererTraceMinLevel,\n setRendererTraceTransport,\n TraceCapture,\n type TraceCaptureOptions,\n type TraceDispatchResult,\n type TraceEvent,\n type TraceInput,\n type TraceLevel,\n type TraceProcess,\n type TraceWaitOptions,\n} from './trace_hub.js';\n\nexport {\n FrameworkTracer,\n MainTracer,\n RendererTracer,\n TestTracer,\n TraceProvider,\n TraceProviderConfiguration,\n type TraceContext,\n type TraceProviderMode,\n type TraceRuntime,\n} from './trace_provider.js';\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAQK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;","names":[]}
|
|
@@ -37,7 +37,9 @@ __export(trace_hub_exports, {
|
|
|
37
37
|
publishMainTrace: () => publishMainTrace,
|
|
38
38
|
publishTestTrace: () => publishTestTrace,
|
|
39
39
|
setMainTraceMinLevel: () => setMainTraceMinLevel,
|
|
40
|
-
|
|
40
|
+
setRendererTraceEnabled: () => setRendererTraceEnabled,
|
|
41
|
+
setRendererTraceMinLevel: () => setRendererTraceMinLevel,
|
|
42
|
+
setRendererTraceTransport: () => setRendererTraceTransport
|
|
41
43
|
});
|
|
42
44
|
module.exports = __toCommonJS(trace_hub_exports);
|
|
43
45
|
var import_rxjs = require("rxjs");
|
|
@@ -50,6 +52,8 @@ const LEVEL_ORDER = {
|
|
|
50
52
|
error: 3
|
|
51
53
|
};
|
|
52
54
|
let rendererMinLevel = "debug";
|
|
55
|
+
let rendererEnabled = false;
|
|
56
|
+
let customRendererTraceTransport = null;
|
|
53
57
|
function normalizeTraceInput(raw) {
|
|
54
58
|
const input = raw ?? {};
|
|
55
59
|
const source = typeof input.source === "string" && input.source.trim().length > 0 ? input.source : "unknown";
|
|
@@ -145,13 +149,17 @@ class TraceHub {
|
|
|
145
149
|
this.subject = new import_rxjs.Subject();
|
|
146
150
|
this.shared$ = this.subject.asObservable().pipe((0, import_operators.share)());
|
|
147
151
|
this.nextTraceSeq = 1;
|
|
148
|
-
this.
|
|
152
|
+
this.minLevels = {
|
|
153
|
+
main: "debug",
|
|
154
|
+
renderer: "debug",
|
|
155
|
+
test: "debug"
|
|
156
|
+
};
|
|
149
157
|
}
|
|
150
158
|
get events$() {
|
|
151
159
|
return this.shared$;
|
|
152
160
|
}
|
|
153
|
-
setMinLevel(level) {
|
|
154
|
-
this.
|
|
161
|
+
setMinLevel(process, level) {
|
|
162
|
+
this.minLevels[process] = level;
|
|
155
163
|
}
|
|
156
164
|
publish(process, level, input) {
|
|
157
165
|
const normalized = normalizeTraceInput(input);
|
|
@@ -162,7 +170,7 @@ class TraceHub {
|
|
|
162
170
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
163
171
|
traceSeq: this.nextTraceSeq++
|
|
164
172
|
};
|
|
165
|
-
if (LEVEL_ORDER[level] >= LEVEL_ORDER[this.
|
|
173
|
+
if (LEVEL_ORDER[level] >= LEVEL_ORDER[this.minLevels[process]]) {
|
|
166
174
|
this.subject.next(event);
|
|
167
175
|
}
|
|
168
176
|
return event;
|
|
@@ -182,10 +190,14 @@ function getTraceEvents$() {
|
|
|
182
190
|
return getGlobalTraceHub().events$;
|
|
183
191
|
}
|
|
184
192
|
function setMainTraceMinLevel(level) {
|
|
185
|
-
getGlobalTraceHub().setMinLevel(level);
|
|
193
|
+
getGlobalTraceHub().setMinLevel("main", level);
|
|
186
194
|
}
|
|
187
195
|
function setRendererTraceMinLevel(level) {
|
|
188
196
|
rendererMinLevel = level;
|
|
197
|
+
getGlobalTraceHub().setMinLevel("renderer", level);
|
|
198
|
+
}
|
|
199
|
+
function setRendererTraceEnabled(enabled) {
|
|
200
|
+
rendererEnabled = enabled;
|
|
189
201
|
}
|
|
190
202
|
function publishMainTrace(level, input) {
|
|
191
203
|
return getGlobalTraceHub().publish("main", level, input);
|
|
@@ -202,19 +214,30 @@ function dispatchRendererTracePayload(rawPayload) {
|
|
|
202
214
|
forwarded: true
|
|
203
215
|
};
|
|
204
216
|
}
|
|
217
|
+
function setRendererTraceTransport(fn) {
|
|
218
|
+
customRendererTraceTransport = fn;
|
|
219
|
+
}
|
|
205
220
|
function emitRendererTrace(level, input) {
|
|
221
|
+
if (!rendererEnabled) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
206
224
|
if (LEVEL_ORDER[level] < LEVEL_ORDER[rendererMinLevel]) {
|
|
207
225
|
return;
|
|
208
226
|
}
|
|
227
|
+
const normalized = { ...normalizeTraceInput(input), level };
|
|
228
|
+
if (customRendererTraceTransport) {
|
|
229
|
+
try {
|
|
230
|
+
customRendererTraceTransport(normalized);
|
|
231
|
+
return;
|
|
232
|
+
} catch {
|
|
233
|
+
}
|
|
234
|
+
}
|
|
209
235
|
const bridge = resolveTraceBridge();
|
|
210
236
|
if (!bridge || typeof bridge.__trace !== "function") {
|
|
211
237
|
return;
|
|
212
238
|
}
|
|
213
239
|
try {
|
|
214
|
-
bridge.__trace(
|
|
215
|
-
...normalizeTraceInput(input),
|
|
216
|
-
level
|
|
217
|
-
});
|
|
240
|
+
bridge.__trace(normalized);
|
|
218
241
|
} catch {
|
|
219
242
|
}
|
|
220
243
|
}
|
|
@@ -225,6 +248,8 @@ function clearTraceHubForTests() {
|
|
|
225
248
|
const globalObj = globalThis;
|
|
226
249
|
delete globalObj[TRACE_HUB_SYMBOL];
|
|
227
250
|
rendererMinLevel = "debug";
|
|
251
|
+
rendererEnabled = false;
|
|
252
|
+
customRendererTraceTransport = null;
|
|
228
253
|
}
|
|
229
254
|
// Annotate the CommonJS export names for ESM import in node:
|
|
230
255
|
0 && (module.exports = {
|
|
@@ -237,6 +262,8 @@ function clearTraceHubForTests() {
|
|
|
237
262
|
publishMainTrace,
|
|
238
263
|
publishTestTrace,
|
|
239
264
|
setMainTraceMinLevel,
|
|
240
|
-
|
|
265
|
+
setRendererTraceEnabled,
|
|
266
|
+
setRendererTraceMinLevel,
|
|
267
|
+
setRendererTraceTransport
|
|
241
268
|
});
|
|
242
269
|
//# sourceMappingURL=trace_hub.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tracing/trace_hub.ts"],"sourcesContent":["import { Observable, Subject, Subscription } from 'rxjs';\nimport { share } from 'rxjs/operators';\n\nexport type TraceLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type TraceProcess = 'main' | 'renderer' | 'test';\n\nexport type TraceInput = {\n source: string;\n type: string;\n message?: string;\n conversationId?: number | string;\n payload?: Record<string, unknown>;\n};\n\nexport type TraceEvent = TraceInput & {\n level: TraceLevel;\n process: TraceProcess;\n timestamp: string;\n traceSeq: number;\n};\n\nexport type TraceDispatchResult = {\n ok: boolean;\n forwarded: boolean;\n};\n\nexport type TraceCaptureOptions = {\n maxEvents?: number;\n};\n\nexport type TraceWaitOptions = {\n timeoutMs?: number;\n};\n\nconst TRACE_HUB_SYMBOL = Symbol.for('@noego/wood.trace.hub');\n\nconst LEVEL_ORDER: Record<TraceLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nlet rendererMinLevel: TraceLevel = 'debug';\n\ntype PendingWaiter = {\n predicate: (event: TraceEvent) => boolean;\n resolve: (event: TraceEvent) => void;\n reject: (error: Error) => void;\n timeoutId?: ReturnType<typeof setTimeout>;\n};\n\ntype GlobalWithTraceHub = typeof globalThis & {\n [TRACE_HUB_SYMBOL]?: TraceHub;\n};\n\ntype TraceBridgeLike = Record<string, unknown> & {\n __trace?: (payload: unknown) => void;\n};\n\ntype GlobalWithTraceBridge = typeof globalThis & {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n window?: {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n };\n};\n\nfunction normalizeTraceInput(raw: unknown): TraceInput {\n const input = (raw ?? {}) as Partial<TraceInput> & Record<string, unknown>;\n const source = typeof input.source === 'string' && input.source.trim().length > 0\n ? input.source\n : 'unknown';\n const type = typeof input.type === 'string' && input.type.trim().length > 0\n ? input.type\n : 'unknown';\n const message = typeof input.message === 'string' ? input.message : undefined;\n const conversationId = typeof input.conversationId === 'number' || typeof input.conversationId === 'string'\n ? input.conversationId\n : undefined;\n const payload = isRecord(input.payload) ? { ...input.payload } : undefined;\n\n return {\n source,\n type,\n ...(message ? { message } : {}),\n ...(conversationId !== undefined ? { conversationId } : {}),\n ...(payload ? { payload } : {}),\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction resolveTraceBridge(): TraceBridgeLike | null {\n const globalObj = globalThis as GlobalWithTraceBridge;\n const bridge = globalObj.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.window?.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n return null;\n }\n return bridge as TraceBridgeLike;\n}\n\nexport class TraceCapture {\n private readonly buffer: TraceEvent[] = [];\n private readonly pending = new Set<PendingWaiter>();\n private readonly maxEvents: number;\n private subscription: Subscription;\n\n constructor(\n source$: Observable<TraceEvent>,\n options: TraceCaptureOptions = {},\n ) {\n this.maxEvents = Math.max(1, options.maxEvents ?? 1000);\n this.subscription = source$.subscribe((event) => {\n this.buffer.push(event);\n if (this.buffer.length > this.maxEvents) {\n this.buffer.splice(0, this.buffer.length - this.maxEvents);\n }\n for (const waiter of Array.from(this.pending)) {\n if (!waiter.predicate(event)) {\n continue;\n }\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n }\n this.pending.delete(waiter);\n waiter.resolve(event);\n }\n });\n }\n\n events(): TraceEvent[] {\n return [...this.buffer];\n }\n\n async waitFor(\n predicate: (event: TraceEvent) => boolean,\n options: TraceWaitOptions = {},\n ): Promise<TraceEvent> {\n const existing = this.buffer.find(predicate);\n if (existing) {\n return existing;\n }\n\n return new Promise<TraceEvent>((resolve, reject) => {\n const waiter: PendingWaiter = {\n predicate,\n resolve,\n reject,\n };\n const timeoutMs = options.timeoutMs ?? 5000;\n waiter.timeoutId = setTimeout(() => {\n this.pending.delete(waiter);\n reject(new Error(`Timed out waiting for trace event after ${timeoutMs}ms`));\n }, timeoutMs);\n this.pending.add(waiter);\n });\n }\n\n async writeArtifact(targetPath: string): Promise<void> {\n const [{ mkdir, writeFile }, pathModule] = await Promise.all([\n import('node:fs/promises'),\n import('node:path'),\n ]);\n await mkdir(pathModule.dirname(targetPath), { recursive: true });\n const lines = this.buffer.map((event) => JSON.stringify(event)).join('\\n');\n await writeFile(targetPath, `${lines}${lines.length > 0 ? '\\n' : ''}`, 'utf8');\n }\n\n stop(): void {\n for (const waiter of this.pending) {\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n }\n waiter.reject(new Error('Trace capture stopped before predicate matched'));\n }\n this.pending.clear();\n this.subscription.unsubscribe();\n }\n}\n\nclass TraceHub {\n private readonly subject = new Subject<TraceEvent>();\n private readonly shared$ = this.subject.asObservable().pipe(share());\n private nextTraceSeq = 1;\n private minLevel: TraceLevel = 'debug';\n\n get events$(): Observable<TraceEvent> {\n return this.shared$;\n }\n\n setMinLevel(level: TraceLevel): void {\n this.minLevel = level;\n }\n\n publish(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n const normalized = normalizeTraceInput(input);\n const event: TraceEvent = {\n ...normalized,\n level,\n process,\n timestamp: new Date().toISOString(),\n traceSeq: this.nextTraceSeq++,\n };\n if (LEVEL_ORDER[level] >= LEVEL_ORDER[this.minLevel]) {\n this.subject.next(event);\n }\n return event;\n }\n\n capture(options: TraceCaptureOptions = {}): TraceCapture {\n return new TraceCapture(this.events$, options);\n }\n}\n\nfunction getGlobalTraceHub(): TraceHub {\n const globalObj = globalThis as GlobalWithTraceHub;\n if (!globalObj[TRACE_HUB_SYMBOL]) {\n globalObj[TRACE_HUB_SYMBOL] = new TraceHub();\n }\n return globalObj[TRACE_HUB_SYMBOL] as TraceHub;\n}\n\nexport function getTraceEvents$(): Observable<TraceEvent> {\n return getGlobalTraceHub().events$;\n}\n\nexport function setMainTraceMinLevel(level: TraceLevel): void {\n getGlobalTraceHub().setMinLevel(level);\n}\n\nexport function setRendererTraceMinLevel(level: TraceLevel): void {\n rendererMinLevel = level;\n}\n\nexport function publishMainTrace(level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish('main', level, input);\n}\n\nexport function publishTestTrace(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish(process, level, input);\n}\n\nexport function dispatchRendererTracePayload(rawPayload: unknown): TraceDispatchResult {\n const payload = (rawPayload ?? {}) as Partial<TraceInput> & { level?: TraceLevel };\n const level = payload.level === 'debug' || payload.level === 'info' || payload.level === 'warn' || payload.level === 'error'\n ? payload.level\n : 'info';\n getGlobalTraceHub().publish('renderer', level, payload);\n return {\n ok: true,\n forwarded: true,\n };\n}\n\nexport function emitRendererTrace(level: TraceLevel, input: TraceInput): void {\n if (LEVEL_ORDER[level] < LEVEL_ORDER[rendererMinLevel]) {\n return;\n }\n const bridge = resolveTraceBridge();\n if (!bridge || typeof bridge.__trace !== 'function') {\n return;\n }\n try {\n bridge.__trace({\n ...normalizeTraceInput(input),\n level,\n });\n } catch {\n // Tracing is observational and must never break the caller.\n }\n}\n\nexport function createTraceCapture(options: TraceCaptureOptions = {}): TraceCapture {\n return getGlobalTraceHub().capture(options);\n}\n\nexport function clearTraceHubForTests(): void {\n const globalObj = globalThis as GlobalWithTraceHub;\n delete globalObj[TRACE_HUB_SYMBOL];\n rendererMinLevel = 'debug';\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/tracing/trace_hub.ts"],"sourcesContent":["import { Observable, Subject, Subscription } from 'rxjs';\nimport { share } from 'rxjs/operators';\n\nexport type TraceLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type TraceProcess = 'main' | 'renderer' | 'test';\n\nexport type TraceInput = {\n source: string;\n type: string;\n message?: string;\n conversationId?: number | string;\n payload?: Record<string, unknown>;\n};\n\nexport type TraceEvent = TraceInput & {\n level: TraceLevel;\n process: TraceProcess;\n timestamp: string;\n traceSeq: number;\n};\n\nexport type TraceDispatchResult = {\n ok: boolean;\n forwarded: boolean;\n};\n\nexport type TraceCaptureOptions = {\n maxEvents?: number;\n};\n\nexport type TraceWaitOptions = {\n timeoutMs?: number;\n};\n\nconst TRACE_HUB_SYMBOL = Symbol.for('@noego/wood.trace.hub');\n\nconst LEVEL_ORDER: Record<TraceLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nlet rendererMinLevel: TraceLevel = 'debug';\nlet rendererEnabled = false;\nlet customRendererTraceTransport: ((payload: unknown) => void) | null = null;\n\ntype PendingWaiter = {\n predicate: (event: TraceEvent) => boolean;\n resolve: (event: TraceEvent) => void;\n reject: (error: Error) => void;\n timeoutId?: ReturnType<typeof setTimeout>;\n};\n\ntype GlobalWithTraceHub = typeof globalThis & {\n [TRACE_HUB_SYMBOL]?: TraceHub;\n};\n\ntype TraceBridgeLike = Record<string, unknown> & {\n __trace?: (payload: unknown) => void;\n};\n\ntype GlobalWithTraceBridge = typeof globalThis & {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n window?: {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n };\n};\n\nfunction normalizeTraceInput(raw: unknown): TraceInput {\n const input = (raw ?? {}) as Partial<TraceInput> & Record<string, unknown>;\n const source = typeof input.source === 'string' && input.source.trim().length > 0\n ? input.source\n : 'unknown';\n const type = typeof input.type === 'string' && input.type.trim().length > 0\n ? input.type\n : 'unknown';\n const message = typeof input.message === 'string' ? input.message : undefined;\n const conversationId = typeof input.conversationId === 'number' || typeof input.conversationId === 'string'\n ? input.conversationId\n : undefined;\n const payload = isRecord(input.payload) ? { ...input.payload } : undefined;\n\n return {\n source,\n type,\n ...(message ? { message } : {}),\n ...(conversationId !== undefined ? { conversationId } : {}),\n ...(payload ? { payload } : {}),\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction resolveTraceBridge(): TraceBridgeLike | null {\n const globalObj = globalThis as GlobalWithTraceBridge;\n const bridge = globalObj.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.window?.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n return null;\n }\n return bridge as TraceBridgeLike;\n}\n\nexport class TraceCapture {\n private readonly buffer: TraceEvent[] = [];\n private readonly pending = new Set<PendingWaiter>();\n private readonly maxEvents: number;\n private subscription: Subscription;\n\n constructor(\n source$: Observable<TraceEvent>,\n options: TraceCaptureOptions = {},\n ) {\n this.maxEvents = Math.max(1, options.maxEvents ?? 1000);\n this.subscription = source$.subscribe((event) => {\n this.buffer.push(event);\n if (this.buffer.length > this.maxEvents) {\n this.buffer.splice(0, this.buffer.length - this.maxEvents);\n }\n for (const waiter of Array.from(this.pending)) {\n if (!waiter.predicate(event)) {\n continue;\n }\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n }\n this.pending.delete(waiter);\n waiter.resolve(event);\n }\n });\n }\n\n events(): TraceEvent[] {\n return [...this.buffer];\n }\n\n async waitFor(\n predicate: (event: TraceEvent) => boolean,\n options: TraceWaitOptions = {},\n ): Promise<TraceEvent> {\n const existing = this.buffer.find(predicate);\n if (existing) {\n return existing;\n }\n\n return new Promise<TraceEvent>((resolve, reject) => {\n const waiter: PendingWaiter = {\n predicate,\n resolve,\n reject,\n };\n const timeoutMs = options.timeoutMs ?? 5000;\n waiter.timeoutId = setTimeout(() => {\n this.pending.delete(waiter);\n reject(new Error(`Timed out waiting for trace event after ${timeoutMs}ms`));\n }, timeoutMs);\n this.pending.add(waiter);\n });\n }\n\n async writeArtifact(targetPath: string): Promise<void> {\n const [{ mkdir, writeFile }, pathModule] = await Promise.all([\n import('node:fs/promises'),\n import('node:path'),\n ]);\n await mkdir(pathModule.dirname(targetPath), { recursive: true });\n const lines = this.buffer.map((event) => JSON.stringify(event)).join('\\n');\n await writeFile(targetPath, `${lines}${lines.length > 0 ? '\\n' : ''}`, 'utf8');\n }\n\n stop(): void {\n for (const waiter of this.pending) {\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n }\n waiter.reject(new Error('Trace capture stopped before predicate matched'));\n }\n this.pending.clear();\n this.subscription.unsubscribe();\n }\n}\n\nclass TraceHub {\n private readonly subject = new Subject<TraceEvent>();\n private readonly shared$ = this.subject.asObservable().pipe(share());\n private nextTraceSeq = 1;\n private minLevels: Record<TraceProcess, TraceLevel> = {\n main: 'debug',\n renderer: 'debug',\n test: 'debug',\n };\n\n get events$(): Observable<TraceEvent> {\n return this.shared$;\n }\n\n setMinLevel(process: TraceProcess, level: TraceLevel): void {\n this.minLevels[process] = level;\n }\n\n publish(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n const normalized = normalizeTraceInput(input);\n const event: TraceEvent = {\n ...normalized,\n level,\n process,\n timestamp: new Date().toISOString(),\n traceSeq: this.nextTraceSeq++,\n };\n if (LEVEL_ORDER[level] >= LEVEL_ORDER[this.minLevels[process]]) {\n this.subject.next(event);\n }\n return event;\n }\n\n capture(options: TraceCaptureOptions = {}): TraceCapture {\n return new TraceCapture(this.events$, options);\n }\n}\n\nfunction getGlobalTraceHub(): TraceHub {\n const globalObj = globalThis as GlobalWithTraceHub;\n if (!globalObj[TRACE_HUB_SYMBOL]) {\n globalObj[TRACE_HUB_SYMBOL] = new TraceHub();\n }\n return globalObj[TRACE_HUB_SYMBOL] as TraceHub;\n}\n\nexport function getTraceEvents$(): Observable<TraceEvent> {\n return getGlobalTraceHub().events$;\n}\n\nexport function setMainTraceMinLevel(level: TraceLevel): void {\n getGlobalTraceHub().setMinLevel('main', level);\n}\n\nexport function setRendererTraceMinLevel(level: TraceLevel): void {\n rendererMinLevel = level;\n getGlobalTraceHub().setMinLevel('renderer', level);\n}\n\nexport function setRendererTraceEnabled(enabled: boolean): void {\n rendererEnabled = enabled;\n}\n\nexport function publishMainTrace(level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish('main', level, input);\n}\n\nexport function publishTestTrace(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish(process, level, input);\n}\n\nexport function dispatchRendererTracePayload(rawPayload: unknown): TraceDispatchResult {\n const payload = (rawPayload ?? {}) as Partial<TraceInput> & { level?: TraceLevel };\n const level = payload.level === 'debug' || payload.level === 'info' || payload.level === 'warn' || payload.level === 'error'\n ? payload.level\n : 'info';\n getGlobalTraceHub().publish('renderer', level, payload);\n return {\n ok: true,\n forwarded: true,\n };\n}\n\nexport function setRendererTraceTransport(fn: ((payload: unknown) => void) | null): void {\n customRendererTraceTransport = fn;\n}\n\nexport function emitRendererTrace(level: TraceLevel, input: TraceInput): void {\n if (!rendererEnabled) {\n return;\n }\n if (LEVEL_ORDER[level] < LEVEL_ORDER[rendererMinLevel]) {\n return;\n }\n const normalized = { ...normalizeTraceInput(input), level };\n\n if (customRendererTraceTransport) {\n try {\n customRendererTraceTransport(normalized);\n return;\n } catch {\n // Fall through to bridge.__trace\n }\n }\n\n const bridge = resolveTraceBridge();\n if (!bridge || typeof bridge.__trace !== 'function') {\n return;\n }\n try {\n bridge.__trace(normalized);\n } catch {\n // Tracing is observational and must never break the caller.\n }\n}\n\nexport function createTraceCapture(options: TraceCaptureOptions = {}): TraceCapture {\n return getGlobalTraceHub().capture(options);\n}\n\nexport function clearTraceHubForTests(): void {\n const globalObj = globalThis as GlobalWithTraceHub;\n delete globalObj[TRACE_HUB_SYMBOL];\n rendererMinLevel = 'debug';\n rendererEnabled = false;\n customRendererTraceTransport = null;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -39,11 +39,13 @@ declare class TraceCapture {
|
|
|
39
39
|
declare function getTraceEvents$(): Observable<TraceEvent>;
|
|
40
40
|
declare function setMainTraceMinLevel(level: TraceLevel): void;
|
|
41
41
|
declare function setRendererTraceMinLevel(level: TraceLevel): void;
|
|
42
|
+
declare function setRendererTraceEnabled(enabled: boolean): void;
|
|
42
43
|
declare function publishMainTrace(level: TraceLevel, input: unknown): TraceEvent;
|
|
43
44
|
declare function publishTestTrace(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent;
|
|
44
45
|
declare function dispatchRendererTracePayload(rawPayload: unknown): TraceDispatchResult;
|
|
46
|
+
declare function setRendererTraceTransport(fn: ((payload: unknown) => void) | null): void;
|
|
45
47
|
declare function emitRendererTrace(level: TraceLevel, input: TraceInput): void;
|
|
46
48
|
declare function createTraceCapture(options?: TraceCaptureOptions): TraceCapture;
|
|
47
49
|
declare function clearTraceHubForTests(): void;
|
|
48
50
|
|
|
49
|
-
export { TraceCapture, type TraceCaptureOptions, type TraceDispatchResult, type TraceEvent, type TraceInput, type TraceLevel, type TraceProcess, type TraceWaitOptions, clearTraceHubForTests, createTraceCapture, dispatchRendererTracePayload, emitRendererTrace, getTraceEvents$, publishMainTrace, publishTestTrace, setMainTraceMinLevel, setRendererTraceMinLevel };
|
|
51
|
+
export { TraceCapture, type TraceCaptureOptions, type TraceDispatchResult, type TraceEvent, type TraceInput, type TraceLevel, type TraceProcess, type TraceWaitOptions, clearTraceHubForTests, createTraceCapture, dispatchRendererTracePayload, emitRendererTrace, getTraceEvents$, publishMainTrace, publishTestTrace, setMainTraceMinLevel, setRendererTraceEnabled, setRendererTraceMinLevel, setRendererTraceTransport };
|
|
@@ -39,11 +39,13 @@ declare class TraceCapture {
|
|
|
39
39
|
declare function getTraceEvents$(): Observable<TraceEvent>;
|
|
40
40
|
declare function setMainTraceMinLevel(level: TraceLevel): void;
|
|
41
41
|
declare function setRendererTraceMinLevel(level: TraceLevel): void;
|
|
42
|
+
declare function setRendererTraceEnabled(enabled: boolean): void;
|
|
42
43
|
declare function publishMainTrace(level: TraceLevel, input: unknown): TraceEvent;
|
|
43
44
|
declare function publishTestTrace(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent;
|
|
44
45
|
declare function dispatchRendererTracePayload(rawPayload: unknown): TraceDispatchResult;
|
|
46
|
+
declare function setRendererTraceTransport(fn: ((payload: unknown) => void) | null): void;
|
|
45
47
|
declare function emitRendererTrace(level: TraceLevel, input: TraceInput): void;
|
|
46
48
|
declare function createTraceCapture(options?: TraceCaptureOptions): TraceCapture;
|
|
47
49
|
declare function clearTraceHubForTests(): void;
|
|
48
50
|
|
|
49
|
-
export { TraceCapture, type TraceCaptureOptions, type TraceDispatchResult, type TraceEvent, type TraceInput, type TraceLevel, type TraceProcess, type TraceWaitOptions, clearTraceHubForTests, createTraceCapture, dispatchRendererTracePayload, emitRendererTrace, getTraceEvents$, publishMainTrace, publishTestTrace, setMainTraceMinLevel, setRendererTraceMinLevel };
|
|
51
|
+
export { TraceCapture, type TraceCaptureOptions, type TraceDispatchResult, type TraceEvent, type TraceInput, type TraceLevel, type TraceProcess, type TraceWaitOptions, clearTraceHubForTests, createTraceCapture, dispatchRendererTracePayload, emitRendererTrace, getTraceEvents$, publishMainTrace, publishTestTrace, setMainTraceMinLevel, setRendererTraceEnabled, setRendererTraceMinLevel, setRendererTraceTransport };
|
|
@@ -8,6 +8,8 @@ const LEVEL_ORDER = {
|
|
|
8
8
|
error: 3
|
|
9
9
|
};
|
|
10
10
|
let rendererMinLevel = "debug";
|
|
11
|
+
let rendererEnabled = false;
|
|
12
|
+
let customRendererTraceTransport = null;
|
|
11
13
|
function normalizeTraceInput(raw) {
|
|
12
14
|
const input = raw ?? {};
|
|
13
15
|
const source = typeof input.source === "string" && input.source.trim().length > 0 ? input.source : "unknown";
|
|
@@ -103,13 +105,17 @@ class TraceHub {
|
|
|
103
105
|
this.subject = new Subject();
|
|
104
106
|
this.shared$ = this.subject.asObservable().pipe(share());
|
|
105
107
|
this.nextTraceSeq = 1;
|
|
106
|
-
this.
|
|
108
|
+
this.minLevels = {
|
|
109
|
+
main: "debug",
|
|
110
|
+
renderer: "debug",
|
|
111
|
+
test: "debug"
|
|
112
|
+
};
|
|
107
113
|
}
|
|
108
114
|
get events$() {
|
|
109
115
|
return this.shared$;
|
|
110
116
|
}
|
|
111
|
-
setMinLevel(level) {
|
|
112
|
-
this.
|
|
117
|
+
setMinLevel(process, level) {
|
|
118
|
+
this.minLevels[process] = level;
|
|
113
119
|
}
|
|
114
120
|
publish(process, level, input) {
|
|
115
121
|
const normalized = normalizeTraceInput(input);
|
|
@@ -120,7 +126,7 @@ class TraceHub {
|
|
|
120
126
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
121
127
|
traceSeq: this.nextTraceSeq++
|
|
122
128
|
};
|
|
123
|
-
if (LEVEL_ORDER[level] >= LEVEL_ORDER[this.
|
|
129
|
+
if (LEVEL_ORDER[level] >= LEVEL_ORDER[this.minLevels[process]]) {
|
|
124
130
|
this.subject.next(event);
|
|
125
131
|
}
|
|
126
132
|
return event;
|
|
@@ -140,10 +146,14 @@ function getTraceEvents$() {
|
|
|
140
146
|
return getGlobalTraceHub().events$;
|
|
141
147
|
}
|
|
142
148
|
function setMainTraceMinLevel(level) {
|
|
143
|
-
getGlobalTraceHub().setMinLevel(level);
|
|
149
|
+
getGlobalTraceHub().setMinLevel("main", level);
|
|
144
150
|
}
|
|
145
151
|
function setRendererTraceMinLevel(level) {
|
|
146
152
|
rendererMinLevel = level;
|
|
153
|
+
getGlobalTraceHub().setMinLevel("renderer", level);
|
|
154
|
+
}
|
|
155
|
+
function setRendererTraceEnabled(enabled) {
|
|
156
|
+
rendererEnabled = enabled;
|
|
147
157
|
}
|
|
148
158
|
function publishMainTrace(level, input) {
|
|
149
159
|
return getGlobalTraceHub().publish("main", level, input);
|
|
@@ -160,19 +170,30 @@ function dispatchRendererTracePayload(rawPayload) {
|
|
|
160
170
|
forwarded: true
|
|
161
171
|
};
|
|
162
172
|
}
|
|
173
|
+
function setRendererTraceTransport(fn) {
|
|
174
|
+
customRendererTraceTransport = fn;
|
|
175
|
+
}
|
|
163
176
|
function emitRendererTrace(level, input) {
|
|
177
|
+
if (!rendererEnabled) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
164
180
|
if (LEVEL_ORDER[level] < LEVEL_ORDER[rendererMinLevel]) {
|
|
165
181
|
return;
|
|
166
182
|
}
|
|
183
|
+
const normalized = { ...normalizeTraceInput(input), level };
|
|
184
|
+
if (customRendererTraceTransport) {
|
|
185
|
+
try {
|
|
186
|
+
customRendererTraceTransport(normalized);
|
|
187
|
+
return;
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
190
|
+
}
|
|
167
191
|
const bridge = resolveTraceBridge();
|
|
168
192
|
if (!bridge || typeof bridge.__trace !== "function") {
|
|
169
193
|
return;
|
|
170
194
|
}
|
|
171
195
|
try {
|
|
172
|
-
bridge.__trace(
|
|
173
|
-
...normalizeTraceInput(input),
|
|
174
|
-
level
|
|
175
|
-
});
|
|
196
|
+
bridge.__trace(normalized);
|
|
176
197
|
} catch {
|
|
177
198
|
}
|
|
178
199
|
}
|
|
@@ -183,6 +204,8 @@ function clearTraceHubForTests() {
|
|
|
183
204
|
const globalObj = globalThis;
|
|
184
205
|
delete globalObj[TRACE_HUB_SYMBOL];
|
|
185
206
|
rendererMinLevel = "debug";
|
|
207
|
+
rendererEnabled = false;
|
|
208
|
+
customRendererTraceTransport = null;
|
|
186
209
|
}
|
|
187
210
|
export {
|
|
188
211
|
TraceCapture,
|
|
@@ -194,6 +217,8 @@ export {
|
|
|
194
217
|
publishMainTrace,
|
|
195
218
|
publishTestTrace,
|
|
196
219
|
setMainTraceMinLevel,
|
|
197
|
-
|
|
220
|
+
setRendererTraceEnabled,
|
|
221
|
+
setRendererTraceMinLevel,
|
|
222
|
+
setRendererTraceTransport
|
|
198
223
|
};
|
|
199
224
|
//# sourceMappingURL=trace_hub.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tracing/trace_hub.ts"],"sourcesContent":["import { Observable, Subject, Subscription } from 'rxjs';\nimport { share } from 'rxjs/operators';\n\nexport type TraceLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type TraceProcess = 'main' | 'renderer' | 'test';\n\nexport type TraceInput = {\n source: string;\n type: string;\n message?: string;\n conversationId?: number | string;\n payload?: Record<string, unknown>;\n};\n\nexport type TraceEvent = TraceInput & {\n level: TraceLevel;\n process: TraceProcess;\n timestamp: string;\n traceSeq: number;\n};\n\nexport type TraceDispatchResult = {\n ok: boolean;\n forwarded: boolean;\n};\n\nexport type TraceCaptureOptions = {\n maxEvents?: number;\n};\n\nexport type TraceWaitOptions = {\n timeoutMs?: number;\n};\n\nconst TRACE_HUB_SYMBOL = Symbol.for('@noego/wood.trace.hub');\n\nconst LEVEL_ORDER: Record<TraceLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nlet rendererMinLevel: TraceLevel = 'debug';\n\ntype PendingWaiter = {\n predicate: (event: TraceEvent) => boolean;\n resolve: (event: TraceEvent) => void;\n reject: (error: Error) => void;\n timeoutId?: ReturnType<typeof setTimeout>;\n};\n\ntype GlobalWithTraceHub = typeof globalThis & {\n [TRACE_HUB_SYMBOL]?: TraceHub;\n};\n\ntype TraceBridgeLike = Record<string, unknown> & {\n __trace?: (payload: unknown) => void;\n};\n\ntype GlobalWithTraceBridge = typeof globalThis & {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n window?: {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n };\n};\n\nfunction normalizeTraceInput(raw: unknown): TraceInput {\n const input = (raw ?? {}) as Partial<TraceInput> & Record<string, unknown>;\n const source = typeof input.source === 'string' && input.source.trim().length > 0\n ? input.source\n : 'unknown';\n const type = typeof input.type === 'string' && input.type.trim().length > 0\n ? input.type\n : 'unknown';\n const message = typeof input.message === 'string' ? input.message : undefined;\n const conversationId = typeof input.conversationId === 'number' || typeof input.conversationId === 'string'\n ? input.conversationId\n : undefined;\n const payload = isRecord(input.payload) ? { ...input.payload } : undefined;\n\n return {\n source,\n type,\n ...(message ? { message } : {}),\n ...(conversationId !== undefined ? { conversationId } : {}),\n ...(payload ? { payload } : {}),\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction resolveTraceBridge(): TraceBridgeLike | null {\n const globalObj = globalThis as GlobalWithTraceBridge;\n const bridge = globalObj.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.window?.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n return null;\n }\n return bridge as TraceBridgeLike;\n}\n\nexport class TraceCapture {\n private readonly buffer: TraceEvent[] = [];\n private readonly pending = new Set<PendingWaiter>();\n private readonly maxEvents: number;\n private subscription: Subscription;\n\n constructor(\n source$: Observable<TraceEvent>,\n options: TraceCaptureOptions = {},\n ) {\n this.maxEvents = Math.max(1, options.maxEvents ?? 1000);\n this.subscription = source$.subscribe((event) => {\n this.buffer.push(event);\n if (this.buffer.length > this.maxEvents) {\n this.buffer.splice(0, this.buffer.length - this.maxEvents);\n }\n for (const waiter of Array.from(this.pending)) {\n if (!waiter.predicate(event)) {\n continue;\n }\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n }\n this.pending.delete(waiter);\n waiter.resolve(event);\n }\n });\n }\n\n events(): TraceEvent[] {\n return [...this.buffer];\n }\n\n async waitFor(\n predicate: (event: TraceEvent) => boolean,\n options: TraceWaitOptions = {},\n ): Promise<TraceEvent> {\n const existing = this.buffer.find(predicate);\n if (existing) {\n return existing;\n }\n\n return new Promise<TraceEvent>((resolve, reject) => {\n const waiter: PendingWaiter = {\n predicate,\n resolve,\n reject,\n };\n const timeoutMs = options.timeoutMs ?? 5000;\n waiter.timeoutId = setTimeout(() => {\n this.pending.delete(waiter);\n reject(new Error(`Timed out waiting for trace event after ${timeoutMs}ms`));\n }, timeoutMs);\n this.pending.add(waiter);\n });\n }\n\n async writeArtifact(targetPath: string): Promise<void> {\n const [{ mkdir, writeFile }, pathModule] = await Promise.all([\n import('node:fs/promises'),\n import('node:path'),\n ]);\n await mkdir(pathModule.dirname(targetPath), { recursive: true });\n const lines = this.buffer.map((event) => JSON.stringify(event)).join('\\n');\n await writeFile(targetPath, `${lines}${lines.length > 0 ? '\\n' : ''}`, 'utf8');\n }\n\n stop(): void {\n for (const waiter of this.pending) {\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n }\n waiter.reject(new Error('Trace capture stopped before predicate matched'));\n }\n this.pending.clear();\n this.subscription.unsubscribe();\n }\n}\n\nclass TraceHub {\n private readonly subject = new Subject<TraceEvent>();\n private readonly shared$ = this.subject.asObservable().pipe(share());\n private nextTraceSeq = 1;\n private minLevel: TraceLevel = 'debug';\n\n get events$(): Observable<TraceEvent> {\n return this.shared$;\n }\n\n setMinLevel(level: TraceLevel): void {\n this.minLevel = level;\n }\n\n publish(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n const normalized = normalizeTraceInput(input);\n const event: TraceEvent = {\n ...normalized,\n level,\n process,\n timestamp: new Date().toISOString(),\n traceSeq: this.nextTraceSeq++,\n };\n if (LEVEL_ORDER[level] >= LEVEL_ORDER[this.minLevel]) {\n this.subject.next(event);\n }\n return event;\n }\n\n capture(options: TraceCaptureOptions = {}): TraceCapture {\n return new TraceCapture(this.events$, options);\n }\n}\n\nfunction getGlobalTraceHub(): TraceHub {\n const globalObj = globalThis as GlobalWithTraceHub;\n if (!globalObj[TRACE_HUB_SYMBOL]) {\n globalObj[TRACE_HUB_SYMBOL] = new TraceHub();\n }\n return globalObj[TRACE_HUB_SYMBOL] as TraceHub;\n}\n\nexport function getTraceEvents$(): Observable<TraceEvent> {\n return getGlobalTraceHub().events$;\n}\n\nexport function setMainTraceMinLevel(level: TraceLevel): void {\n getGlobalTraceHub().setMinLevel(level);\n}\n\nexport function setRendererTraceMinLevel(level: TraceLevel): void {\n rendererMinLevel = level;\n}\n\nexport function publishMainTrace(level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish('main', level, input);\n}\n\nexport function publishTestTrace(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish(process, level, input);\n}\n\nexport function dispatchRendererTracePayload(rawPayload: unknown): TraceDispatchResult {\n const payload = (rawPayload ?? {}) as Partial<TraceInput> & { level?: TraceLevel };\n const level = payload.level === 'debug' || payload.level === 'info' || payload.level === 'warn' || payload.level === 'error'\n ? payload.level\n : 'info';\n getGlobalTraceHub().publish('renderer', level, payload);\n return {\n ok: true,\n forwarded: true,\n };\n}\n\nexport function emitRendererTrace(level: TraceLevel, input: TraceInput): void {\n if (LEVEL_ORDER[level] < LEVEL_ORDER[rendererMinLevel]) {\n return;\n }\n const bridge = resolveTraceBridge();\n if (!bridge || typeof bridge.__trace !== 'function') {\n return;\n }\n try {\n bridge.__trace({\n ...normalizeTraceInput(input),\n level,\n });\n } catch {\n // Tracing is observational and must never break the caller.\n }\n}\n\nexport function createTraceCapture(options: TraceCaptureOptions = {}): TraceCapture {\n return getGlobalTraceHub().capture(options);\n}\n\nexport function clearTraceHubForTests(): void {\n const globalObj = globalThis as GlobalWithTraceHub;\n delete globalObj[TRACE_HUB_SYMBOL];\n rendererMinLevel = 'debug';\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/tracing/trace_hub.ts"],"sourcesContent":["import { Observable, Subject, Subscription } from 'rxjs';\nimport { share } from 'rxjs/operators';\n\nexport type TraceLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type TraceProcess = 'main' | 'renderer' | 'test';\n\nexport type TraceInput = {\n source: string;\n type: string;\n message?: string;\n conversationId?: number | string;\n payload?: Record<string, unknown>;\n};\n\nexport type TraceEvent = TraceInput & {\n level: TraceLevel;\n process: TraceProcess;\n timestamp: string;\n traceSeq: number;\n};\n\nexport type TraceDispatchResult = {\n ok: boolean;\n forwarded: boolean;\n};\n\nexport type TraceCaptureOptions = {\n maxEvents?: number;\n};\n\nexport type TraceWaitOptions = {\n timeoutMs?: number;\n};\n\nconst TRACE_HUB_SYMBOL = Symbol.for('@noego/wood.trace.hub');\n\nconst LEVEL_ORDER: Record<TraceLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nlet rendererMinLevel: TraceLevel = 'debug';\nlet rendererEnabled = false;\nlet customRendererTraceTransport: ((payload: unknown) => void) | null = null;\n\ntype PendingWaiter = {\n predicate: (event: TraceEvent) => boolean;\n resolve: (event: TraceEvent) => void;\n reject: (error: Error) => void;\n timeoutId?: ReturnType<typeof setTimeout>;\n};\n\ntype GlobalWithTraceHub = typeof globalThis & {\n [TRACE_HUB_SYMBOL]?: TraceHub;\n};\n\ntype TraceBridgeLike = Record<string, unknown> & {\n __trace?: (payload: unknown) => void;\n};\n\ntype GlobalWithTraceBridge = typeof globalThis & {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n window?: {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n };\n};\n\nfunction normalizeTraceInput(raw: unknown): TraceInput {\n const input = (raw ?? {}) as Partial<TraceInput> & Record<string, unknown>;\n const source = typeof input.source === 'string' && input.source.trim().length > 0\n ? input.source\n : 'unknown';\n const type = typeof input.type === 'string' && input.type.trim().length > 0\n ? input.type\n : 'unknown';\n const message = typeof input.message === 'string' ? input.message : undefined;\n const conversationId = typeof input.conversationId === 'number' || typeof input.conversationId === 'string'\n ? input.conversationId\n : undefined;\n const payload = isRecord(input.payload) ? { ...input.payload } : undefined;\n\n return {\n source,\n type,\n ...(message ? { message } : {}),\n ...(conversationId !== undefined ? { conversationId } : {}),\n ...(payload ? { payload } : {}),\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction resolveTraceBridge(): TraceBridgeLike | null {\n const globalObj = globalThis as GlobalWithTraceBridge;\n const bridge = globalObj.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.window?.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n return null;\n }\n return bridge as TraceBridgeLike;\n}\n\nexport class TraceCapture {\n private readonly buffer: TraceEvent[] = [];\n private readonly pending = new Set<PendingWaiter>();\n private readonly maxEvents: number;\n private subscription: Subscription;\n\n constructor(\n source$: Observable<TraceEvent>,\n options: TraceCaptureOptions = {},\n ) {\n this.maxEvents = Math.max(1, options.maxEvents ?? 1000);\n this.subscription = source$.subscribe((event) => {\n this.buffer.push(event);\n if (this.buffer.length > this.maxEvents) {\n this.buffer.splice(0, this.buffer.length - this.maxEvents);\n }\n for (const waiter of Array.from(this.pending)) {\n if (!waiter.predicate(event)) {\n continue;\n }\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n }\n this.pending.delete(waiter);\n waiter.resolve(event);\n }\n });\n }\n\n events(): TraceEvent[] {\n return [...this.buffer];\n }\n\n async waitFor(\n predicate: (event: TraceEvent) => boolean,\n options: TraceWaitOptions = {},\n ): Promise<TraceEvent> {\n const existing = this.buffer.find(predicate);\n if (existing) {\n return existing;\n }\n\n return new Promise<TraceEvent>((resolve, reject) => {\n const waiter: PendingWaiter = {\n predicate,\n resolve,\n reject,\n };\n const timeoutMs = options.timeoutMs ?? 5000;\n waiter.timeoutId = setTimeout(() => {\n this.pending.delete(waiter);\n reject(new Error(`Timed out waiting for trace event after ${timeoutMs}ms`));\n }, timeoutMs);\n this.pending.add(waiter);\n });\n }\n\n async writeArtifact(targetPath: string): Promise<void> {\n const [{ mkdir, writeFile }, pathModule] = await Promise.all([\n import('node:fs/promises'),\n import('node:path'),\n ]);\n await mkdir(pathModule.dirname(targetPath), { recursive: true });\n const lines = this.buffer.map((event) => JSON.stringify(event)).join('\\n');\n await writeFile(targetPath, `${lines}${lines.length > 0 ? '\\n' : ''}`, 'utf8');\n }\n\n stop(): void {\n for (const waiter of this.pending) {\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n }\n waiter.reject(new Error('Trace capture stopped before predicate matched'));\n }\n this.pending.clear();\n this.subscription.unsubscribe();\n }\n}\n\nclass TraceHub {\n private readonly subject = new Subject<TraceEvent>();\n private readonly shared$ = this.subject.asObservable().pipe(share());\n private nextTraceSeq = 1;\n private minLevels: Record<TraceProcess, TraceLevel> = {\n main: 'debug',\n renderer: 'debug',\n test: 'debug',\n };\n\n get events$(): Observable<TraceEvent> {\n return this.shared$;\n }\n\n setMinLevel(process: TraceProcess, level: TraceLevel): void {\n this.minLevels[process] = level;\n }\n\n publish(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n const normalized = normalizeTraceInput(input);\n const event: TraceEvent = {\n ...normalized,\n level,\n process,\n timestamp: new Date().toISOString(),\n traceSeq: this.nextTraceSeq++,\n };\n if (LEVEL_ORDER[level] >= LEVEL_ORDER[this.minLevels[process]]) {\n this.subject.next(event);\n }\n return event;\n }\n\n capture(options: TraceCaptureOptions = {}): TraceCapture {\n return new TraceCapture(this.events$, options);\n }\n}\n\nfunction getGlobalTraceHub(): TraceHub {\n const globalObj = globalThis as GlobalWithTraceHub;\n if (!globalObj[TRACE_HUB_SYMBOL]) {\n globalObj[TRACE_HUB_SYMBOL] = new TraceHub();\n }\n return globalObj[TRACE_HUB_SYMBOL] as TraceHub;\n}\n\nexport function getTraceEvents$(): Observable<TraceEvent> {\n return getGlobalTraceHub().events$;\n}\n\nexport function setMainTraceMinLevel(level: TraceLevel): void {\n getGlobalTraceHub().setMinLevel('main', level);\n}\n\nexport function setRendererTraceMinLevel(level: TraceLevel): void {\n rendererMinLevel = level;\n getGlobalTraceHub().setMinLevel('renderer', level);\n}\n\nexport function setRendererTraceEnabled(enabled: boolean): void {\n rendererEnabled = enabled;\n}\n\nexport function publishMainTrace(level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish('main', level, input);\n}\n\nexport function publishTestTrace(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish(process, level, input);\n}\n\nexport function dispatchRendererTracePayload(rawPayload: unknown): TraceDispatchResult {\n const payload = (rawPayload ?? {}) as Partial<TraceInput> & { level?: TraceLevel };\n const level = payload.level === 'debug' || payload.level === 'info' || payload.level === 'warn' || payload.level === 'error'\n ? payload.level\n : 'info';\n getGlobalTraceHub().publish('renderer', level, payload);\n return {\n ok: true,\n forwarded: true,\n };\n}\n\nexport function setRendererTraceTransport(fn: ((payload: unknown) => void) | null): void {\n customRendererTraceTransport = fn;\n}\n\nexport function emitRendererTrace(level: TraceLevel, input: TraceInput): void {\n if (!rendererEnabled) {\n return;\n }\n if (LEVEL_ORDER[level] < LEVEL_ORDER[rendererMinLevel]) {\n return;\n }\n const normalized = { ...normalizeTraceInput(input), level };\n\n if (customRendererTraceTransport) {\n try {\n customRendererTraceTransport(normalized);\n return;\n } catch {\n // Fall through to bridge.__trace\n }\n }\n\n const bridge = resolveTraceBridge();\n if (!bridge || typeof bridge.__trace !== 'function') {\n return;\n }\n try {\n bridge.__trace(normalized);\n } catch {\n // Tracing is observational and must never break the caller.\n }\n}\n\nexport function createTraceCapture(options: TraceCaptureOptions = {}): TraceCapture {\n return getGlobalTraceHub().capture(options);\n}\n\nexport function clearTraceHubForTests(): void {\n const globalObj = globalThis as GlobalWithTraceHub;\n delete globalObj[TRACE_HUB_SYMBOL];\n rendererMinLevel = 'debug';\n rendererEnabled = false;\n customRendererTraceTransport = null;\n}\n"],"mappings":"AAAA,SAAqB,eAA6B;AAClD,SAAS,aAAa;AAiCtB,MAAM,mBAAmB,uBAAO,IAAI,uBAAuB;AAE3D,MAAM,cAA0C;AAAA,EAC9C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEA,IAAI,mBAA+B;AACnC,IAAI,kBAAkB;AACtB,IAAI,+BAAoE;AA0BxE,SAAS,oBAAoB,KAA0B;AACrD,QAAM,QAAS,OAAO,CAAC;AACvB,QAAM,SAAS,OAAO,MAAM,WAAW,YAAY,MAAM,OAAO,KAAK,EAAE,SAAS,IAC5E,MAAM,SACN;AACJ,QAAM,OAAO,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,KAAK,EAAE,SAAS,IACtE,MAAM,OACN;AACJ,QAAM,UAAU,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU;AACpE,QAAM,iBAAiB,OAAO,MAAM,mBAAmB,YAAY,OAAO,MAAM,mBAAmB,WAC/F,MAAM,iBACN;AACJ,QAAM,UAAU,SAAS,MAAM,OAAO,IAAI,EAAE,GAAG,MAAM,QAAQ,IAAI;AAEjE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7B,GAAI,mBAAmB,SAAY,EAAE,eAAe,IAAI,CAAC;AAAA,IACzD,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC/B;AACF;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,qBAA6C;AACpD,QAAM,YAAY;AAClB,QAAM,SAAS,UAAU,yBACpB,UAAU,QAAQ,yBAClB,UAAU,QACV,UAAU,QAAQ;AACvB,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,MAAM,aAAa;AAAA,EAMxB,YACE,SACA,UAA+B,CAAC,GAChC;AARF,SAAiB,SAAuB,CAAC;AACzC,SAAiB,UAAU,oBAAI,IAAmB;AAQhD,SAAK,YAAY,KAAK,IAAI,GAAG,QAAQ,aAAa,GAAI;AACtD,SAAK,eAAe,QAAQ,UAAU,CAAC,UAAU;AAC/C,WAAK,OAAO,KAAK,KAAK;AACtB,UAAI,KAAK,OAAO,SAAS,KAAK,WAAW;AACvC,aAAK,OAAO,OAAO,GAAG,KAAK,OAAO,SAAS,KAAK,SAAS;AAAA,MAC3D;AACA,iBAAW,UAAU,MAAM,KAAK,KAAK,OAAO,GAAG;AAC7C,YAAI,CAAC,OAAO,UAAU,KAAK,GAAG;AAC5B;AAAA,QACF;AACA,YAAI,OAAO,WAAW;AACpB,uBAAa,OAAO,SAAS;AAAA,QAC/B;AACA,aAAK,QAAQ,OAAO,MAAM;AAC1B,eAAO,QAAQ,KAAK;AAAA,MACtB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,SAAuB;AACrB,WAAO,CAAC,GAAG,KAAK,MAAM;AAAA,EACxB;AAAA,EAEA,MAAM,QACJ,WACA,UAA4B,CAAC,GACR;AACrB,UAAM,WAAW,KAAK,OAAO,KAAK,SAAS;AAC3C,QAAI,UAAU;AACZ,aAAO;AAAA,IACT;AAEA,WAAO,IAAI,QAAoB,CAAC,SAAS,WAAW;AAClD,YAAM,SAAwB;AAAA,QAC5B;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,YAAM,YAAY,QAAQ,aAAa;AACvC,aAAO,YAAY,WAAW,MAAM;AAClC,aAAK,QAAQ,OAAO,MAAM;AAC1B,eAAO,IAAI,MAAM,2CAA2C,SAAS,IAAI,CAAC;AAAA,MAC5E,GAAG,SAAS;AACZ,WAAK,QAAQ,IAAI,MAAM;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,cAAc,YAAmC;AACrD,UAAM,CAAC,EAAE,OAAO,UAAU,GAAG,UAAU,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC3D,OAAO,kBAAkB;AAAA,MACzB,OAAO,WAAW;AAAA,IACpB,CAAC;AACD,UAAM,MAAM,WAAW,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC/D,UAAM,QAAQ,KAAK,OAAO,IAAI,CAAC,UAAU,KAAK,UAAU,KAAK,CAAC,EAAE,KAAK,IAAI;AACzE,UAAM,UAAU,YAAY,GAAG,KAAK,GAAG,MAAM,SAAS,IAAI,OAAO,EAAE,IAAI,MAAM;AAAA,EAC/E;AAAA,EAEA,OAAa;AACX,eAAW,UAAU,KAAK,SAAS;AACjC,UAAI,OAAO,WAAW;AACpB,qBAAa,OAAO,SAAS;AAAA,MAC/B;AACA,aAAO,OAAO,IAAI,MAAM,gDAAgD,CAAC;AAAA,IAC3E;AACA,SAAK,QAAQ,MAAM;AACnB,SAAK,aAAa,YAAY;AAAA,EAChC;AACF;AAEA,MAAM,SAAS;AAAA,EAAf;AACE,SAAiB,UAAU,IAAI,QAAoB;AACnD,SAAiB,UAAU,KAAK,QAAQ,aAAa,EAAE,KAAK,MAAM,CAAC;AACnE,SAAQ,eAAe;AACvB,SAAQ,YAA8C;AAAA,MACpD,MAAM;AAAA,MACN,UAAU;AAAA,MACV,MAAM;AAAA,IACR;AAAA;AAAA,EAEA,IAAI,UAAkC;AACpC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAAY,SAAuB,OAAyB;AAC1D,SAAK,UAAU,OAAO,IAAI;AAAA,EAC5B;AAAA,EAEA,QAAQ,SAAuB,OAAmB,OAA4B;AAC5E,UAAM,aAAa,oBAAoB,KAAK;AAC5C,UAAM,QAAoB;AAAA,MACxB,GAAG;AAAA,MACH;AAAA,MACA;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,UAAU,KAAK;AAAA,IACjB;AACA,QAAI,YAAY,KAAK,KAAK,YAAY,KAAK,UAAU,OAAO,CAAC,GAAG;AAC9D,WAAK,QAAQ,KAAK,KAAK;AAAA,IACzB;AACA,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,UAA+B,CAAC,GAAiB;AACvD,WAAO,IAAI,aAAa,KAAK,SAAS,OAAO;AAAA,EAC/C;AACF;AAEA,SAAS,oBAA8B;AACrC,QAAM,YAAY;AAClB,MAAI,CAAC,UAAU,gBAAgB,GAAG;AAChC,cAAU,gBAAgB,IAAI,IAAI,SAAS;AAAA,EAC7C;AACA,SAAO,UAAU,gBAAgB;AACnC;AAEO,SAAS,kBAA0C;AACxD,SAAO,kBAAkB,EAAE;AAC7B;AAEO,SAAS,qBAAqB,OAAyB;AAC5D,oBAAkB,EAAE,YAAY,QAAQ,KAAK;AAC/C;AAEO,SAAS,yBAAyB,OAAyB;AAChE,qBAAmB;AACnB,oBAAkB,EAAE,YAAY,YAAY,KAAK;AACnD;AAEO,SAAS,wBAAwB,SAAwB;AAC9D,oBAAkB;AACpB;AAEO,SAAS,iBAAiB,OAAmB,OAA4B;AAC9E,SAAO,kBAAkB,EAAE,QAAQ,QAAQ,OAAO,KAAK;AACzD;AAEO,SAAS,iBAAiB,SAAuB,OAAmB,OAA4B;AACrG,SAAO,kBAAkB,EAAE,QAAQ,SAAS,OAAO,KAAK;AAC1D;AAEO,SAAS,6BAA6B,YAA0C;AACrF,QAAM,UAAW,cAAc,CAAC;AAChC,QAAM,QAAQ,QAAQ,UAAU,WAAW,QAAQ,UAAU,UAAU,QAAQ,UAAU,UAAU,QAAQ,UAAU,UACjH,QAAQ,QACR;AACJ,oBAAkB,EAAE,QAAQ,YAAY,OAAO,OAAO;AACtD,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,WAAW;AAAA,EACb;AACF;AAEO,SAAS,0BAA0B,IAA+C;AACvF,iCAA+B;AACjC;AAEO,SAAS,kBAAkB,OAAmB,OAAyB;AAC5E,MAAI,CAAC,iBAAiB;AACpB;AAAA,EACF;AACA,MAAI,YAAY,KAAK,IAAI,YAAY,gBAAgB,GAAG;AACtD;AAAA,EACF;AACA,QAAM,aAAa,EAAE,GAAG,oBAAoB,KAAK,GAAG,MAAM;AAE1D,MAAI,8BAA8B;AAChC,QAAI;AACF,mCAA6B,UAAU;AACvC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,SAAS,mBAAmB;AAClC,MAAI,CAAC,UAAU,OAAO,OAAO,YAAY,YAAY;AACnD;AAAA,EACF;AACA,MAAI;AACF,WAAO,QAAQ,UAAU;AAAA,EAC3B,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,mBAAmB,UAA+B,CAAC,GAAiB;AAClF,SAAO,kBAAkB,EAAE,QAAQ,OAAO;AAC5C;AAEO,SAAS,wBAA8B;AAC5C,QAAM,YAAY;AAClB,SAAO,UAAU,gBAAgB;AACjC,qBAAmB;AACnB,oBAAkB;AAClB,iCAA+B;AACjC;","names":[]}
|