@noego/wood 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/bin/wood.js +2 -1
- package/dist/client/controller_contract.cjs.map +1 -1
- package/dist/client/controller_contract.d.cts +5 -3
- package/dist/client/controller_contract.d.ts +5 -3
- package/dist/client/index.cjs +10 -0
- 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 +11 -1
- 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 +37 -0
- package/dist/controller/controller_lifecycle.cjs.map +1 -0
- package/dist/controller/controller_lifecycle.d.cts +10 -0
- package/dist/controller/controller_lifecycle.d.ts +10 -0
- package/dist/controller/controller_lifecycle.js +13 -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 +4 -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 +5 -2
- package/dist/index.js.map +1 -1
- package/dist/main/index.cjs +20 -0
- package/dist/main/index.cjs.map +1 -1
- package/dist/main/index.d.cts +1 -0
- package/dist/main/index.d.ts +1 -0
- package/dist/main/index.js +21 -0
- package/dist/main/index.js.map +1 -1
- 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/testing/create_controller_harness.cjs +3 -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 +3 -4
- package/dist/testing/create_controller_harness.js.map +1 -1
- package/dist/tracing/index.cjs +4 -2
- package/dist/tracing/index.cjs.map +1 -1
- package/dist/tracing/index.d.cts +1 -1
- package/dist/tracing/index.d.ts +1 -1
- package/dist/tracing/index.js +3 -1
- package/dist/tracing/index.js.map +1 -1
- package/dist/tracing/trace_hub.cjs +18 -6
- package/dist/tracing/trace_hub.cjs.map +1 -1
- package/dist/tracing/trace_hub.d.cts +2 -1
- package/dist/tracing/trace_hub.d.ts +2 -1
- package/dist/tracing/trace_hub.js +16 -5
- package/dist/tracing/trace_hub.js.map +1 -1
- package/package.json +4 -4
- package/src/components/WoodRecursiveRender.svelte +6 -6
- package/src/stick/WoodRecursiveRender.svelte +4 -4
package/README.md
CHANGED
|
@@ -74,6 +74,15 @@ const source = generateRuntime({
|
|
|
74
74
|
});
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
### Controller lifecycle
|
|
78
|
+
|
|
79
|
+
Controllers may optionally implement `dispose(): void | Promise<void>`.
|
|
80
|
+
|
|
81
|
+
`WoodRouter` resolves a controller for each dispatch and calls `dispose()` in a
|
|
82
|
+
`finally` block after the action completes or throws. Use `dispose()` to
|
|
83
|
+
unsubscribe from streams, unregister listeners, or release other controller-owned
|
|
84
|
+
resources.
|
|
85
|
+
|
|
77
86
|
## CLI
|
|
78
87
|
|
|
79
88
|
This package includes two executables:
|
package/bin/wood.js
CHANGED
|
@@ -243,6 +243,7 @@ function generateMainAppCode() {
|
|
|
243
243
|
const controllersDir = String(mainSection.controllers).replace(/\\/g, '/').replace(/'/g, "\\'");
|
|
244
244
|
const middlewareDir = String(mainSection.middleware).replace(/\\/g, '/').replace(/'/g, "\\'");
|
|
245
245
|
const outDirValue = String(config.outDir || 'out').replace(/\\/g, '/').replace(/'/g, "\\'");
|
|
246
|
+
const traceTransport = mainSection.traceTransport === 'messageport' ? 'messageport' : null;
|
|
246
247
|
|
|
247
248
|
const code = `// AUTO-GENERATED by @noego/wood -- do not edit
|
|
248
249
|
import type { StartWoodMainOptions, WoodMainAppBootResult } from '@noego/wood/main';
|
|
@@ -292,7 +293,7 @@ export const app: StartWoodMainOptions = {
|
|
|
292
293
|
controllersDir: '${controllersDir}',
|
|
293
294
|
middlewareDir: '${middlewareDir}',
|
|
294
295
|
appBoot: runAppBoot,
|
|
295
|
-
mainBoot: runMainBoot
|
|
296
|
+
mainBoot: runMainBoot,${traceTransport ? `\n traceTransport: '${traceTransport}',` : ''}
|
|
296
297
|
};
|
|
297
298
|
`;
|
|
298
299
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/controller_contract.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../src/client/controller_contract.ts"],"sourcesContent":["import type { ControllerLifecycle } from '../controller/controller_lifecycle.cjs';\nexport type { ControllerLifecycle } from '../controller/controller_lifecycle.cjs';\n\nexport interface ControllerState {}\n\nexport interface ControllerInput {}\n\nexport interface ControllerEvents {}\n\nexport interface PageController<\n S extends ControllerState = ControllerState,\n I extends ControllerInput = ControllerInput,\n E extends ControllerEvents = ControllerEvents,\n> extends ControllerLifecycle {\n readonly data?: S;\n readonly input?: I;\n readonly events?: E;\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { ControllerLifecycle } from '../controller/controller_lifecycle.cjs';
|
|
2
|
+
|
|
1
3
|
interface ControllerState {
|
|
2
4
|
}
|
|
3
5
|
interface ControllerInput {
|
|
4
6
|
}
|
|
5
7
|
interface ControllerEvents {
|
|
6
8
|
}
|
|
7
|
-
interface PageController<S extends ControllerState = ControllerState, I extends ControllerInput = ControllerInput, E extends ControllerEvents = ControllerEvents> {
|
|
8
|
-
readonly
|
|
9
|
+
interface PageController<S extends ControllerState = ControllerState, I extends ControllerInput = ControllerInput, E extends ControllerEvents = ControllerEvents> extends ControllerLifecycle {
|
|
10
|
+
readonly data?: S;
|
|
9
11
|
readonly input?: I;
|
|
10
12
|
readonly events?: E;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
export type
|
|
15
|
+
export { type ControllerEvents, type ControllerInput, ControllerLifecycle, type ControllerState, type PageController };
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { ControllerLifecycle } from '../controller/controller_lifecycle.js';
|
|
2
|
+
|
|
1
3
|
interface ControllerState {
|
|
2
4
|
}
|
|
3
5
|
interface ControllerInput {
|
|
4
6
|
}
|
|
5
7
|
interface ControllerEvents {
|
|
6
8
|
}
|
|
7
|
-
interface PageController<S extends ControllerState = ControllerState, I extends ControllerInput = ControllerInput, E extends ControllerEvents = ControllerEvents> {
|
|
8
|
-
readonly
|
|
9
|
+
interface PageController<S extends ControllerState = ControllerState, I extends ControllerInput = ControllerInput, E extends ControllerEvents = ControllerEvents> extends ControllerLifecycle {
|
|
10
|
+
readonly data?: S;
|
|
9
11
|
readonly input?: I;
|
|
10
12
|
readonly events?: E;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
export type
|
|
15
|
+
export { type ControllerEvents, type ControllerInput, ControllerLifecycle, type ControllerState, type PageController };
|
package/dist/client/index.cjs
CHANGED
|
@@ -38,6 +38,7 @@ __export(client_exports, {
|
|
|
38
38
|
getIpc: () => import_ipc.getIpc,
|
|
39
39
|
getNavigation: () => import_navigation.getNavigation,
|
|
40
40
|
getWindowControls: () => getWindowControls,
|
|
41
|
+
setRendererTraceTransport: () => import_tracing.setRendererTraceTransport,
|
|
41
42
|
startWood: () => startWood
|
|
42
43
|
});
|
|
43
44
|
module.exports = __toCommonJS(client_exports);
|
|
@@ -405,6 +406,14 @@ function resolveTarget(target) {
|
|
|
405
406
|
async function startWood(mountRoot, target) {
|
|
406
407
|
const container = getContainer();
|
|
407
408
|
(0, import_navigation.getNavigation)();
|
|
409
|
+
try {
|
|
410
|
+
const traceGlobal = globalThis.__NOEGO_TRACE__;
|
|
411
|
+
if (traceGlobal && typeof traceGlobal.send === "function") {
|
|
412
|
+
const sender = traceGlobal.send.bind(traceGlobal);
|
|
413
|
+
(0, import_tracing.setRendererTraceTransport)((payload) => sender(payload));
|
|
414
|
+
}
|
|
415
|
+
} catch {
|
|
416
|
+
}
|
|
408
417
|
try {
|
|
409
418
|
const bridge = resolveWoodBridge();
|
|
410
419
|
if (typeof bridge.__woodConfig === "function") {
|
|
@@ -444,6 +453,7 @@ function createSvelteBoot(mountRoot) {
|
|
|
444
453
|
getIpc,
|
|
445
454
|
getNavigation,
|
|
446
455
|
getWindowControls,
|
|
456
|
+
setRendererTraceTransport,
|
|
447
457
|
startWood
|
|
448
458
|
});
|
|
449
459
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["import './global_polyfill.cjs';\nimport 'reflect-metadata';\nimport { Container } from '@noego/ioc';\nimport {\n Navigation,\n getNavigation,\n clearNavigationForTests as clearNavigationSingletonForTests,\n} from '../navigation';\nimport { Ipc, getIpc } from '../ipc/index.js';\nimport {\n TraceProviderConfiguration,\n clearTraceHubForTests,\n emitRendererTrace,\n} from '../tracing/index.js';\nexport type {\n ControllerEvents,\n ControllerInput,\n ControllerState,\n PageController,\n} from './controller_contract';\n\nconst CLIENT_CONTAINER_SYMBOL = Symbol.for('@noego/wood.client.container');\nconst CLIENT_APP_SYMBOL = Symbol.for('@noego/wood.client.app');\nconst CLIENT_WINDOW_CONTROLS_SYMBOL = Symbol.for('@noego/wood.client.window-controls');\nconst CLIENT_LOGGER_REGISTRY_SYMBOL = Symbol.for('@noego/wood.client.logger-registry');\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\nexport const WOOD_INTERNAL_BRIDGE_KEY = INTERNAL_BRIDGE_GLOBAL_KEY;\n\nexport interface WoodRpcManifestEntry {\n channel: string;\n controller: string;\n action: string;\n path: string[];\n hasInput: boolean;\n}\n\ntype WoodBridgeRpcFunction = (data?: unknown) => Promise<unknown>;\ntype WoodBridgeLike = Record<string, unknown> & {\n __rpcManifest?: WoodRpcManifestEntry[];\n __window?: {\n current?: () => Promise<unknown>;\n open?: (windowId: string, options?: Record<string, unknown>) => Promise<unknown>;\n close?: (windowId?: string) => Promise<unknown>;\n focus?: (windowId: string) => Promise<unknown>;\n minimize?: (windowId?: string) => Promise<unknown>;\n maximize?: (windowId?: string) => Promise<unknown>;\n };\n __log?: (\n level: string,\n logger: string,\n message: string,\n context?: unknown,\n ) => void;\n __woodConfig?: () => Promise<unknown>;\n __trace?: (event: unknown) => void;\n};\n\ntype GlobalWithClientContainer = typeof globalThis & {\n [CLIENT_CONTAINER_SYMBOL]?: any;\n};\n\ntype GlobalWithClientApp = typeof globalThis & {\n [CLIENT_APP_SYMBOL]?: App;\n};\n\ntype GlobalWithWindowControls = typeof globalThis & {\n [CLIENT_WINDOW_CONTROLS_SYMBOL]?: WindowControls;\n};\n\ntype GlobalWithLoggerRegistry = typeof globalThis & {\n [CLIENT_LOGGER_REGISTRY_SYMBOL]?: Map<string, RendererLogger>;\n};\n\ninterface WoodContainerRegistration {\n registerFunction?(\n token: any,\n resolver: (...args: any[]) => unknown,\n options?: Record<string, unknown>,\n ): void;\n}\n\nexport class App {\n [controllerName: string]: unknown;\n}\n\nfunction resolveWoodBridge(): WoodBridgeLike {\n const globalObj = globalThis as typeof globalThis & {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n window?: {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n };\n };\n\n const bridge = globalObj[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.window?.[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n throw new Error(\n `Wood bridge is not available on globalThis.${INTERNAL_BRIDGE_GLOBAL_KEY}.`,\n );\n }\n\n return bridge as WoodBridgeLike;\n}\n\nfunction resolveRpcManifest(bridge: WoodBridgeLike): WoodRpcManifestEntry[] {\n const manifest = bridge.__rpcManifest;\n if (!Array.isArray(manifest)) {\n throw new Error(\n 'Wood bridge is missing \"__rpcManifest\". Regenerate preload with the latest @noego/wood.',\n );\n }\n\n return manifest;\n}\n\nfunction resolveBridgeFunction(\n bridge: WoodBridgeLike,\n pathSegments: string[],\n operationKey: string,\n): WoodBridgeRpcFunction {\n let current: unknown = bridge;\n\n for (const segment of pathSegments) {\n if (!current || typeof current !== 'object') {\n throw new Error(\n `Wood bridge operation \"${operationKey}\" is missing path \"${pathSegments.join('.')}\".`,\n );\n }\n current = (current as Record<string, unknown>)[segment];\n }\n\n if (typeof current !== 'function') {\n throw new Error(\n `Wood bridge path \"${pathSegments.join('.')}\" for \"${operationKey}\" is not callable.`,\n );\n }\n\n return current as WoodBridgeRpcFunction;\n}\n\nfunction assertSerializable(value: unknown, operationKey: string): void {\n if (typeof structuredClone !== 'function') {\n return;\n }\n\n try {\n structuredClone(value);\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(\n `App RPC \"${operationKey}\" payload is not serializable: ${reason}`,\n );\n }\n}\n\nfunction extractTraceConversationId(value: unknown): number | string | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n const record = value as Record<string, unknown>;\n if (typeof record.conversationId === 'number' || typeof record.conversationId === 'string') {\n return record.conversationId;\n }\n if (typeof record.threadId === 'number' || typeof record.threadId === 'string') {\n return record.threadId;\n }\n\n return undefined;\n}\n\nfunction validateManifestEntry(entry: WoodRpcManifestEntry, index: number): void {\n if (!entry.channel || !entry.controller || !entry.action) {\n throw new Error(`Invalid Wood RPC manifest entry at index ${index}.`);\n }\n if (!Array.isArray(entry.path) || entry.path.length === 0) {\n throw new Error(\n `Invalid Wood RPC manifest path for \"${entry.controller}.${entry.action}\".`,\n );\n }\n}\n\nfunction createAppInstance(): App {\n const bridge = resolveWoodBridge();\n const manifest = resolveRpcManifest(bridge);\n const app: Record<string, Record<string, (...args: unknown[]) => Promise<unknown>>> = {};\n\n manifest.forEach((entry, index) => {\n validateManifestEntry(entry, index);\n\n const operationKey = `${entry.controller}.${entry.action}`;\n const invoke = resolveBridgeFunction(bridge, entry.path, operationKey);\n\n if (!app[entry.controller]) {\n app[entry.controller] = {};\n }\n\n if (app[entry.controller][entry.action]) {\n throw new Error(\n `Duplicate RPC action \"${operationKey}\" in Wood manifest. Actions must be unique per controller.`,\n );\n }\n\n app[entry.controller][entry.action] = (...args: unknown[]): Promise<unknown> => {\n if (entry.hasInput) {\n if (args.length !== 1) {\n throw new Error(\n `App RPC \"${operationKey}\" expects exactly one payload argument.`,\n );\n }\n assertSerializable(args[0], operationKey);\n const conversationId = extractTraceConversationId(args[0]);\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke(args[0])\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n }\n\n if (args.length !== 0) {\n throw new Error(\n `App RPC \"${operationKey}\" expects no arguments.`,\n );\n }\n\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke()\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n };\n });\n\n return app as unknown as App;\n}\n\nexport function getApp(): App {\n const globalObj = globalThis as GlobalWithClientApp;\n if (!globalObj[CLIENT_APP_SYMBOL]) {\n globalObj[CLIENT_APP_SYMBOL] = createAppInstance();\n }\n return globalObj[CLIENT_APP_SYMBOL] as App;\n}\n\n/** Test-only helper to clear the client App singleton. */\nexport function clearClientAppForTests(): void {\n const globalObj = globalThis as GlobalWithClientApp;\n delete globalObj[CLIENT_APP_SYMBOL];\n}\n\nfunction resolveWindowControlsBridgeMethod<T>(\n method: keyof NonNullable<WoodBridgeLike['__window']>,\n): T {\n const bridge = resolveWoodBridge();\n const fn = bridge.__window?.[method];\n if (typeof fn !== 'function') {\n throw new Error(`Wood bridge does not expose \"__window.${String(method)}\".`);\n }\n return fn as unknown as T;\n}\n\nexport class WindowControls {\n async open(windowId: string, options?: Record<string, unknown>): Promise<void> {\n const open = resolveWindowControlsBridgeMethod<\n (id: string, opts?: Record<string, unknown>) => Promise<unknown>\n >('open');\n await open(windowId, options);\n }\n\n async close(windowId?: string): Promise<void> {\n const close = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('close');\n await close(windowId);\n }\n\n async focus(windowId: string): Promise<void> {\n const focus = resolveWindowControlsBridgeMethod<\n (id: string) => Promise<unknown>\n >('focus');\n await focus(windowId);\n }\n\n async minimize(windowId?: string): Promise<void> {\n const minimize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('minimize');\n await minimize(windowId);\n }\n\n async maximize(windowId?: string): Promise<void> {\n const maximize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('maximize');\n await maximize(windowId);\n }\n}\n\nexport function getWindowControls(): WindowControls {\n const globalObj = globalThis as GlobalWithWindowControls;\n if (!globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL]) {\n globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] = new WindowControls();\n }\n return globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] as WindowControls;\n}\n\n/** Test-only helper to clear the client WindowControls singleton. */\nexport function clearClientWindowControlsForTests(): void {\n const globalObj = globalThis as GlobalWithWindowControls;\n delete globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL];\n}\n\ntype RendererLogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nfunction stringifyLogMessage(message: unknown): string {\n if (typeof message === 'string') return message;\n if (message instanceof Error) return message.message;\n return String(message);\n}\n\nfunction writeConsole(level: RendererLogLevel, logger: string, message: string, context?: unknown): void {\n const prefix = `[${logger}] ${message}`;\n switch (level) {\n case 'debug':\n console.debug(prefix, context);\n return;\n case 'warn':\n console.warn(prefix, context);\n return;\n case 'error':\n console.error(prefix, context);\n return;\n default:\n console.info(prefix, context);\n }\n}\n\nclass RendererLogger {\n constructor(private readonly loggerName: string) {}\n\n debug(message: unknown, context?: unknown): void {\n this.log('debug', message, context);\n }\n\n info(message: unknown, context?: unknown): void {\n this.log('info', message, context);\n }\n\n warn(message: unknown, context?: unknown): void {\n this.log('warn', message, context);\n }\n\n error(message: unknown, context?: unknown): void {\n this.log('error', message, context);\n }\n\n private log(level: RendererLogLevel, rawMessage: unknown, context?: unknown): void {\n const message = stringifyLogMessage(rawMessage);\n\n let bridge: WoodBridgeLike;\n try {\n bridge = resolveWoodBridge();\n } catch {\n writeConsole(level, this.loggerName, message, context);\n return;\n }\n\n if (typeof bridge.__log !== 'function') {\n writeConsole(level, this.loggerName, message, context);\n return;\n }\n\n try {\n bridge.__log(level, this.loggerName, message, context);\n } catch {\n writeConsole(level, this.loggerName, message, context);\n }\n }\n}\n\ntype RendererLoggerShape = {\n debug(message: unknown, context?: unknown): void;\n info(message: unknown, context?: unknown): void;\n warn(message: unknown, context?: unknown): void;\n error(message: unknown, context?: unknown): void;\n};\n\nexport function createLogger(name: string): RendererLoggerShape {\n const loggerName = name.trim() || 'renderer';\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n if (!globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]) {\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL] = new Map<string, RendererLogger>();\n }\n\n const existing = globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.get(loggerName);\n if (existing) return existing;\n\n const logger = new RendererLogger(loggerName);\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.set(loggerName, logger);\n return logger;\n}\n\n/** Test-only helper to clear client logger singletons. */\nexport function clearClientLoggersForTests(): void {\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n delete globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL];\n}\n\nfunction registerClientBuiltins(container: WoodContainerRegistration): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\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\nfunction createClientContainerInstance(): any {\n const container = new Container();\n registerClientBuiltins(container as WoodContainerRegistration);\n return container;\n}\n\n/**\n * Return a process-global client container instance.\n *\n * - First call creates the container and stores it on a symbol.\n * - Later calls return the same container instance.\n */\nexport function getContainer(): any {\n const globalObj = globalThis as GlobalWithClientContainer;\n if (!globalObj[CLIENT_CONTAINER_SYMBOL]) {\n globalObj[CLIENT_CONTAINER_SYMBOL] = createClientContainerInstance();\n }\n return globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client container singleton. */\nexport function clearClientContainerForTests(): void {\n const globalObj = globalThis as GlobalWithClientContainer;\n delete globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client navigation singleton. */\nexport function clearClientNavigationForTests(): void {\n clearNavigationSingletonForTests();\n}\n\n/** Clamp a menu position so it stays within viewport bounds. */\nexport function clampMenuPosition(\n x: number,\n y: number,\n menuWidth: number,\n menuHeight: number,\n): { x: number; y: number } {\n const vw = typeof window !== 'undefined' ? window.innerWidth : Infinity;\n const vh = typeof window !== 'undefined' ? window.innerHeight : Infinity;\n return {\n x: Math.min(x, Math.max(0, vw - menuWidth)),\n y: Math.min(y, Math.max(0, vh - menuHeight)),\n };\n}\n\nfunction resolveTarget(target: Element | null): Element {\n if (!target) {\n throw new Error('Missing renderer mount target');\n }\n return target;\n}\n\n/**\n * Start a Wood renderer app by mounting it to a target element.\n * Usage:\n * import { app } from './generated/app.cjs';\n * startWood(app, document.getElementById('app'));\n */\nexport async function startWood<T>(\n mountRoot: (target: Element) => T,\n target: Element | null,\n): Promise<T> {\n // Ensure container singleton exists and registers built-in client tokens.\n const container = getContainer();\n // Ensure navigation singleton exists at first client boot.\n getNavigation();\n\n // Fetch framework config from the main process and apply trace min level\n // before the first render so no sub-threshold events cross IPC.\n try {\n const bridge = resolveWoodBridge();\n if (typeof bridge.__woodConfig === 'function') {\n const config = await bridge.__woodConfig() as { traceMinLevel?: string } | null;\n const level = config?.traceMinLevel;\n if (level === 'debug' || level === 'info' || level === 'warn' || level === 'error') {\n const traceConfiguration = container.instance\n ? await container.instance(TraceProviderConfiguration)\n : null;\n traceConfiguration?.configureMinLevel(level);\n }\n }\n } catch {\n // Config fetch is best-effort; fall back to defaults.\n }\n\n return mountRoot(resolveTarget(target));\n}\n\n/**\n * Backward-compatible helper that returns a target-aware boot function.\n * Usage:\n * const boot = createSvelteBoot(app);\n * const mounted = boot(document.getElementById('app'));\n */\nexport function createSvelteBoot<T>(\n mountRoot: (target: Element) => T,\n) {\n return (target: Element | null): T => mountRoot(resolveTarget(target));\n}\n\nexport { getNavigation };\nexport { Ipc, getIpc };\nexport { clearTraceHubForTests };\nexport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nexport type {\n FrameworkTracer,\n TraceCaptureOptions,\n TraceEvent,\n TraceInput,\n TraceLevel,\n TraceProcess,\n TraceProviderMode,\n TraceRuntime,\n TraceWaitOptions,\n} from '../tracing/index.js';\n"],"mappings":"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","names":["import_tracing","clearNavigationSingletonForTests"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["import './global_polyfill.cjs';\nimport 'reflect-metadata';\nimport { Container } from '@noego/ioc';\nimport {\n Navigation,\n getNavigation,\n clearNavigationForTests as clearNavigationSingletonForTests,\n} from '../navigation';\nimport { Ipc, getIpc } from '../ipc/index.js';\nimport {\n TraceProviderConfiguration,\n clearTraceHubForTests,\n emitRendererTrace,\n setRendererTraceTransport,\n} from '../tracing/index.js';\nexport type {\n ControllerEvents,\n ControllerInput,\n ControllerLifecycle,\n ControllerState,\n PageController,\n} from './controller_contract';\n\nconst CLIENT_CONTAINER_SYMBOL = Symbol.for('@noego/wood.client.container');\nconst CLIENT_APP_SYMBOL = Symbol.for('@noego/wood.client.app');\nconst CLIENT_WINDOW_CONTROLS_SYMBOL = Symbol.for('@noego/wood.client.window-controls');\nconst CLIENT_LOGGER_REGISTRY_SYMBOL = Symbol.for('@noego/wood.client.logger-registry');\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\nexport const WOOD_INTERNAL_BRIDGE_KEY = INTERNAL_BRIDGE_GLOBAL_KEY;\n\nexport interface WoodRpcManifestEntry {\n channel: string;\n controller: string;\n action: string;\n path: string[];\n hasInput: boolean;\n}\n\ntype WoodBridgeRpcFunction = (data?: unknown) => Promise<unknown>;\ntype WoodBridgeLike = Record<string, unknown> & {\n __rpcManifest?: WoodRpcManifestEntry[];\n __window?: {\n current?: () => Promise<unknown>;\n open?: (windowId: string, options?: Record<string, unknown>) => Promise<unknown>;\n close?: (windowId?: string) => Promise<unknown>;\n focus?: (windowId: string) => Promise<unknown>;\n minimize?: (windowId?: string) => Promise<unknown>;\n maximize?: (windowId?: string) => Promise<unknown>;\n };\n __log?: (\n level: string,\n logger: string,\n message: string,\n context?: unknown,\n ) => void;\n __woodConfig?: () => Promise<unknown>;\n __trace?: (event: unknown) => void;\n};\n\ntype GlobalWithClientContainer = typeof globalThis & {\n [CLIENT_CONTAINER_SYMBOL]?: any;\n};\n\ntype GlobalWithClientApp = typeof globalThis & {\n [CLIENT_APP_SYMBOL]?: App;\n};\n\ntype GlobalWithWindowControls = typeof globalThis & {\n [CLIENT_WINDOW_CONTROLS_SYMBOL]?: WindowControls;\n};\n\ntype GlobalWithLoggerRegistry = typeof globalThis & {\n [CLIENT_LOGGER_REGISTRY_SYMBOL]?: Map<string, RendererLogger>;\n};\n\ninterface WoodContainerRegistration {\n registerFunction?(\n token: any,\n resolver: (...args: any[]) => unknown,\n options?: Record<string, unknown>,\n ): void;\n}\n\nexport class App {\n [controllerName: string]: unknown;\n}\n\nfunction resolveWoodBridge(): WoodBridgeLike {\n const globalObj = globalThis as typeof globalThis & {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n window?: {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n };\n };\n\n const bridge = globalObj[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.window?.[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n throw new Error(\n `Wood bridge is not available on globalThis.${INTERNAL_BRIDGE_GLOBAL_KEY}.`,\n );\n }\n\n return bridge as WoodBridgeLike;\n}\n\nfunction resolveRpcManifest(bridge: WoodBridgeLike): WoodRpcManifestEntry[] {\n const manifest = bridge.__rpcManifest;\n if (!Array.isArray(manifest)) {\n throw new Error(\n 'Wood bridge is missing \"__rpcManifest\". Regenerate preload with the latest @noego/wood.',\n );\n }\n\n return manifest;\n}\n\nfunction resolveBridgeFunction(\n bridge: WoodBridgeLike,\n pathSegments: string[],\n operationKey: string,\n): WoodBridgeRpcFunction {\n let current: unknown = bridge;\n\n for (const segment of pathSegments) {\n if (!current || typeof current !== 'object') {\n throw new Error(\n `Wood bridge operation \"${operationKey}\" is missing path \"${pathSegments.join('.')}\".`,\n );\n }\n current = (current as Record<string, unknown>)[segment];\n }\n\n if (typeof current !== 'function') {\n throw new Error(\n `Wood bridge path \"${pathSegments.join('.')}\" for \"${operationKey}\" is not callable.`,\n );\n }\n\n return current as WoodBridgeRpcFunction;\n}\n\nfunction assertSerializable(value: unknown, operationKey: string): void {\n if (typeof structuredClone !== 'function') {\n return;\n }\n\n try {\n structuredClone(value);\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(\n `App RPC \"${operationKey}\" payload is not serializable: ${reason}`,\n );\n }\n}\n\nfunction extractTraceConversationId(value: unknown): number | string | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n const record = value as Record<string, unknown>;\n if (typeof record.conversationId === 'number' || typeof record.conversationId === 'string') {\n return record.conversationId;\n }\n if (typeof record.threadId === 'number' || typeof record.threadId === 'string') {\n return record.threadId;\n }\n\n return undefined;\n}\n\nfunction validateManifestEntry(entry: WoodRpcManifestEntry, index: number): void {\n if (!entry.channel || !entry.controller || !entry.action) {\n throw new Error(`Invalid Wood RPC manifest entry at index ${index}.`);\n }\n if (!Array.isArray(entry.path) || entry.path.length === 0) {\n throw new Error(\n `Invalid Wood RPC manifest path for \"${entry.controller}.${entry.action}\".`,\n );\n }\n}\n\nfunction createAppInstance(): App {\n const bridge = resolveWoodBridge();\n const manifest = resolveRpcManifest(bridge);\n const app: Record<string, Record<string, (...args: unknown[]) => Promise<unknown>>> = {};\n\n manifest.forEach((entry, index) => {\n validateManifestEntry(entry, index);\n\n const operationKey = `${entry.controller}.${entry.action}`;\n const invoke = resolveBridgeFunction(bridge, entry.path, operationKey);\n\n if (!app[entry.controller]) {\n app[entry.controller] = {};\n }\n\n if (app[entry.controller][entry.action]) {\n throw new Error(\n `Duplicate RPC action \"${operationKey}\" in Wood manifest. Actions must be unique per controller.`,\n );\n }\n\n app[entry.controller][entry.action] = (...args: unknown[]): Promise<unknown> => {\n if (entry.hasInput) {\n if (args.length !== 1) {\n throw new Error(\n `App RPC \"${operationKey}\" expects exactly one payload argument.`,\n );\n }\n assertSerializable(args[0], operationKey);\n const conversationId = extractTraceConversationId(args[0]);\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke(args[0])\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n }\n\n if (args.length !== 0) {\n throw new Error(\n `App RPC \"${operationKey}\" expects no arguments.`,\n );\n }\n\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke()\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n };\n });\n\n return app as unknown as App;\n}\n\nexport function getApp(): App {\n const globalObj = globalThis as GlobalWithClientApp;\n if (!globalObj[CLIENT_APP_SYMBOL]) {\n globalObj[CLIENT_APP_SYMBOL] = createAppInstance();\n }\n return globalObj[CLIENT_APP_SYMBOL] as App;\n}\n\n/** Test-only helper to clear the client App singleton. */\nexport function clearClientAppForTests(): void {\n const globalObj = globalThis as GlobalWithClientApp;\n delete globalObj[CLIENT_APP_SYMBOL];\n}\n\nfunction resolveWindowControlsBridgeMethod<T>(\n method: keyof NonNullable<WoodBridgeLike['__window']>,\n): T {\n const bridge = resolveWoodBridge();\n const fn = bridge.__window?.[method];\n if (typeof fn !== 'function') {\n throw new Error(`Wood bridge does not expose \"__window.${String(method)}\".`);\n }\n return fn as unknown as T;\n}\n\nexport class WindowControls {\n async open(windowId: string, options?: Record<string, unknown>): Promise<void> {\n const open = resolveWindowControlsBridgeMethod<\n (id: string, opts?: Record<string, unknown>) => Promise<unknown>\n >('open');\n await open(windowId, options);\n }\n\n async close(windowId?: string): Promise<void> {\n const close = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('close');\n await close(windowId);\n }\n\n async focus(windowId: string): Promise<void> {\n const focus = resolveWindowControlsBridgeMethod<\n (id: string) => Promise<unknown>\n >('focus');\n await focus(windowId);\n }\n\n async minimize(windowId?: string): Promise<void> {\n const minimize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('minimize');\n await minimize(windowId);\n }\n\n async maximize(windowId?: string): Promise<void> {\n const maximize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('maximize');\n await maximize(windowId);\n }\n}\n\nexport function getWindowControls(): WindowControls {\n const globalObj = globalThis as GlobalWithWindowControls;\n if (!globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL]) {\n globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] = new WindowControls();\n }\n return globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] as WindowControls;\n}\n\n/** Test-only helper to clear the client WindowControls singleton. */\nexport function clearClientWindowControlsForTests(): void {\n const globalObj = globalThis as GlobalWithWindowControls;\n delete globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL];\n}\n\ntype RendererLogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nfunction stringifyLogMessage(message: unknown): string {\n if (typeof message === 'string') return message;\n if (message instanceof Error) return message.message;\n return String(message);\n}\n\nfunction writeConsole(level: RendererLogLevel, logger: string, message: string, context?: unknown): void {\n const prefix = `[${logger}] ${message}`;\n switch (level) {\n case 'debug':\n console.debug(prefix, context);\n return;\n case 'warn':\n console.warn(prefix, context);\n return;\n case 'error':\n console.error(prefix, context);\n return;\n default:\n console.info(prefix, context);\n }\n}\n\nclass RendererLogger {\n constructor(private readonly loggerName: string) {}\n\n debug(message: unknown, context?: unknown): void {\n this.log('debug', message, context);\n }\n\n info(message: unknown, context?: unknown): void {\n this.log('info', message, context);\n }\n\n warn(message: unknown, context?: unknown): void {\n this.log('warn', message, context);\n }\n\n error(message: unknown, context?: unknown): void {\n this.log('error', message, context);\n }\n\n private log(level: RendererLogLevel, rawMessage: unknown, context?: unknown): void {\n const message = stringifyLogMessage(rawMessage);\n\n let bridge: WoodBridgeLike;\n try {\n bridge = resolveWoodBridge();\n } catch {\n writeConsole(level, this.loggerName, message, context);\n return;\n }\n\n if (typeof bridge.__log !== 'function') {\n writeConsole(level, this.loggerName, message, context);\n return;\n }\n\n try {\n bridge.__log(level, this.loggerName, message, context);\n } catch {\n writeConsole(level, this.loggerName, message, context);\n }\n }\n}\n\ntype RendererLoggerShape = {\n debug(message: unknown, context?: unknown): void;\n info(message: unknown, context?: unknown): void;\n warn(message: unknown, context?: unknown): void;\n error(message: unknown, context?: unknown): void;\n};\n\nexport function createLogger(name: string): RendererLoggerShape {\n const loggerName = name.trim() || 'renderer';\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n if (!globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]) {\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL] = new Map<string, RendererLogger>();\n }\n\n const existing = globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.get(loggerName);\n if (existing) return existing;\n\n const logger = new RendererLogger(loggerName);\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.set(loggerName, logger);\n return logger;\n}\n\n/** Test-only helper to clear client logger singletons. */\nexport function clearClientLoggersForTests(): void {\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n delete globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL];\n}\n\nfunction registerClientBuiltins(container: WoodContainerRegistration): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\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\nfunction createClientContainerInstance(): any {\n const container = new Container();\n registerClientBuiltins(container as WoodContainerRegistration);\n return container;\n}\n\n/**\n * Return a process-global client container instance.\n *\n * - First call creates the container and stores it on a symbol.\n * - Later calls return the same container instance.\n */\nexport function getContainer(): any {\n const globalObj = globalThis as GlobalWithClientContainer;\n if (!globalObj[CLIENT_CONTAINER_SYMBOL]) {\n globalObj[CLIENT_CONTAINER_SYMBOL] = createClientContainerInstance();\n }\n return globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client container singleton. */\nexport function clearClientContainerForTests(): void {\n const globalObj = globalThis as GlobalWithClientContainer;\n delete globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client navigation singleton. */\nexport function clearClientNavigationForTests(): void {\n clearNavigationSingletonForTests();\n}\n\n/** Clamp a menu position so it stays within viewport bounds. */\nexport function clampMenuPosition(\n x: number,\n y: number,\n menuWidth: number,\n menuHeight: number,\n): { x: number; y: number } {\n const vw = typeof window !== 'undefined' ? window.innerWidth : Infinity;\n const vh = typeof window !== 'undefined' ? window.innerHeight : Infinity;\n return {\n x: Math.min(x, Math.max(0, vw - menuWidth)),\n y: Math.min(y, Math.max(0, vh - menuHeight)),\n };\n}\n\nfunction resolveTarget(target: Element | null): Element {\n if (!target) {\n throw new Error('Missing renderer mount target');\n }\n return target;\n}\n\n/**\n * Start a Wood renderer app by mounting it to a target element.\n * Usage:\n * import { app } from './generated/app.cjs';\n * startWood(app, document.getElementById('app'));\n */\nexport async function startWood<T>(\n mountRoot: (target: Element) => T,\n target: Element | null,\n): Promise<T> {\n // Ensure container singleton exists and registers built-in client tokens.\n const container = getContainer();\n // Ensure navigation singleton exists at first client boot.\n getNavigation();\n\n // Auto-detect MessagePort-based trace transport\n try {\n const traceGlobal = (globalThis as Record<string, unknown>).__NOEGO_TRACE__ as\n | { send?: (data: unknown) => void }\n | undefined;\n if (traceGlobal && typeof traceGlobal.send === 'function') {\n const sender = traceGlobal.send.bind(traceGlobal);\n setRendererTraceTransport((payload: unknown) => sender(payload));\n }\n } catch {\n // MessagePort trace transport not available; IPC fallback remains active.\n }\n\n // Fetch framework config from the main process and apply trace min level\n // before the first render so no sub-threshold events cross IPC.\n try {\n const bridge = resolveWoodBridge();\n if (typeof bridge.__woodConfig === 'function') {\n const config = await bridge.__woodConfig() as { traceMinLevel?: string } | null;\n const level = config?.traceMinLevel;\n if (level === 'debug' || level === 'info' || level === 'warn' || level === 'error') {\n const traceConfiguration = container.instance\n ? await container.instance(TraceProviderConfiguration)\n : null;\n traceConfiguration?.configureMinLevel(level);\n }\n }\n } catch {\n // Config fetch is best-effort; fall back to defaults.\n }\n\n return mountRoot(resolveTarget(target));\n}\n\n/**\n * Backward-compatible helper that returns a target-aware boot function.\n * Usage:\n * const boot = createSvelteBoot(app);\n * const mounted = boot(document.getElementById('app'));\n */\nexport function createSvelteBoot<T>(\n mountRoot: (target: Element) => T,\n) {\n return (target: Element | null): T => mountRoot(resolveTarget(target));\n}\n\nexport { getNavigation };\nexport { Ipc, getIpc };\nexport { clearTraceHubForTests };\nexport { setRendererTraceTransport };\nexport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nexport type {\n FrameworkTracer,\n TraceCaptureOptions,\n TraceEvent,\n TraceInput,\n TraceLevel,\n TraceProcess,\n TraceProviderMode,\n TraceRuntime,\n TraceWaitOptions,\n} from '../tracing/index.js';\n"],"mappings":"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","names":["import_tracing","clearNavigationSingletonForTests"]}
|
package/dist/client/index.d.cts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { getNavigation } from '../navigation/index.cjs';
|
|
2
2
|
export { Ipc, getIpc } from '../ipc/index.cjs';
|
|
3
|
-
export { TraceCaptureOptions, TraceEvent, TraceInput, TraceLevel, TraceProcess, TraceWaitOptions, clearTraceHubForTests } from '../tracing/trace_hub.cjs';
|
|
3
|
+
export { TraceCaptureOptions, TraceEvent, TraceInput, TraceLevel, TraceProcess, TraceWaitOptions, clearTraceHubForTests, setRendererTraceTransport } from '../tracing/trace_hub.cjs';
|
|
4
4
|
export { FrameworkTracer, TraceProvider, TraceProviderConfiguration, TraceProviderMode, TraceRuntime } from '../tracing/trace_provider.cjs';
|
|
5
5
|
export { ControllerEvents, ControllerInput, ControllerState, PageController } from './controller_contract.cjs';
|
|
6
|
+
export { ControllerLifecycle } from '../controller/controller_lifecycle.cjs';
|
|
6
7
|
import '../ipc/shared.cjs';
|
|
7
8
|
import 'rxjs';
|
|
8
9
|
import '@noego/ioc';
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { getNavigation } from '../navigation/index.js';
|
|
2
2
|
export { Ipc, getIpc } from '../ipc/index.js';
|
|
3
|
-
export { TraceCaptureOptions, TraceEvent, TraceInput, TraceLevel, TraceProcess, TraceWaitOptions, clearTraceHubForTests } from '../tracing/trace_hub.js';
|
|
3
|
+
export { TraceCaptureOptions, TraceEvent, TraceInput, TraceLevel, TraceProcess, TraceWaitOptions, clearTraceHubForTests, setRendererTraceTransport } from '../tracing/trace_hub.js';
|
|
4
4
|
export { FrameworkTracer, TraceProvider, TraceProviderConfiguration, TraceProviderMode, TraceRuntime } from '../tracing/trace_provider.js';
|
|
5
5
|
export { ControllerEvents, ControllerInput, ControllerState, PageController } from './controller_contract.js';
|
|
6
|
+
export { ControllerLifecycle } from '../controller/controller_lifecycle.js';
|
|
6
7
|
import '../ipc/shared.js';
|
|
7
8
|
import 'rxjs';
|
|
8
9
|
import '@noego/ioc';
|
package/dist/client/index.js
CHANGED
|
@@ -10,7 +10,8 @@ import { Ipc, getIpc } from "../ipc/index.js";
|
|
|
10
10
|
import {
|
|
11
11
|
TraceProviderConfiguration,
|
|
12
12
|
clearTraceHubForTests,
|
|
13
|
-
emitRendererTrace
|
|
13
|
+
emitRendererTrace,
|
|
14
|
+
setRendererTraceTransport
|
|
14
15
|
} from "../tracing/index.js";
|
|
15
16
|
const CLIENT_CONTAINER_SYMBOL = /* @__PURE__ */ Symbol.for("@noego/wood.client.container");
|
|
16
17
|
const CLIENT_APP_SYMBOL = /* @__PURE__ */ Symbol.for("@noego/wood.client.app");
|
|
@@ -369,6 +370,14 @@ function resolveTarget(target) {
|
|
|
369
370
|
async function startWood(mountRoot, target) {
|
|
370
371
|
const container = getContainer();
|
|
371
372
|
getNavigation();
|
|
373
|
+
try {
|
|
374
|
+
const traceGlobal = globalThis.__NOEGO_TRACE__;
|
|
375
|
+
if (traceGlobal && typeof traceGlobal.send === "function") {
|
|
376
|
+
const sender = traceGlobal.send.bind(traceGlobal);
|
|
377
|
+
setRendererTraceTransport((payload) => sender(payload));
|
|
378
|
+
}
|
|
379
|
+
} catch {
|
|
380
|
+
}
|
|
372
381
|
try {
|
|
373
382
|
const bridge = resolveWoodBridge();
|
|
374
383
|
if (typeof bridge.__woodConfig === "function") {
|
|
@@ -408,6 +417,7 @@ export {
|
|
|
408
417
|
getIpc,
|
|
409
418
|
getNavigation,
|
|
410
419
|
getWindowControls,
|
|
420
|
+
setRendererTraceTransport,
|
|
411
421
|
startWood
|
|
412
422
|
};
|
|
413
423
|
//# sourceMappingURL=index.js.map
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["import './global_polyfill.js';\nimport 'reflect-metadata';\nimport { Container } from '@noego/ioc';\nimport {\n Navigation,\n getNavigation,\n clearNavigationForTests as clearNavigationSingletonForTests,\n} from '../navigation';\nimport { Ipc, getIpc } from '../ipc/index.js';\nimport {\n TraceProviderConfiguration,\n clearTraceHubForTests,\n emitRendererTrace,\n} from '../tracing/index.js';\nexport type {\n ControllerEvents,\n ControllerInput,\n ControllerState,\n PageController,\n} from './controller_contract';\n\nconst CLIENT_CONTAINER_SYMBOL = Symbol.for('@noego/wood.client.container');\nconst CLIENT_APP_SYMBOL = Symbol.for('@noego/wood.client.app');\nconst CLIENT_WINDOW_CONTROLS_SYMBOL = Symbol.for('@noego/wood.client.window-controls');\nconst CLIENT_LOGGER_REGISTRY_SYMBOL = Symbol.for('@noego/wood.client.logger-registry');\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\nexport const WOOD_INTERNAL_BRIDGE_KEY = INTERNAL_BRIDGE_GLOBAL_KEY;\n\nexport interface WoodRpcManifestEntry {\n channel: string;\n controller: string;\n action: string;\n path: string[];\n hasInput: boolean;\n}\n\ntype WoodBridgeRpcFunction = (data?: unknown) => Promise<unknown>;\ntype WoodBridgeLike = Record<string, unknown> & {\n __rpcManifest?: WoodRpcManifestEntry[];\n __window?: {\n current?: () => Promise<unknown>;\n open?: (windowId: string, options?: Record<string, unknown>) => Promise<unknown>;\n close?: (windowId?: string) => Promise<unknown>;\n focus?: (windowId: string) => Promise<unknown>;\n minimize?: (windowId?: string) => Promise<unknown>;\n maximize?: (windowId?: string) => Promise<unknown>;\n };\n __log?: (\n level: string,\n logger: string,\n message: string,\n context?: unknown,\n ) => void;\n __woodConfig?: () => Promise<unknown>;\n __trace?: (event: unknown) => void;\n};\n\ntype GlobalWithClientContainer = typeof globalThis & {\n [CLIENT_CONTAINER_SYMBOL]?: any;\n};\n\ntype GlobalWithClientApp = typeof globalThis & {\n [CLIENT_APP_SYMBOL]?: App;\n};\n\ntype GlobalWithWindowControls = typeof globalThis & {\n [CLIENT_WINDOW_CONTROLS_SYMBOL]?: WindowControls;\n};\n\ntype GlobalWithLoggerRegistry = typeof globalThis & {\n [CLIENT_LOGGER_REGISTRY_SYMBOL]?: Map<string, RendererLogger>;\n};\n\ninterface WoodContainerRegistration {\n registerFunction?(\n token: any,\n resolver: (...args: any[]) => unknown,\n options?: Record<string, unknown>,\n ): void;\n}\n\nexport class App {\n [controllerName: string]: unknown;\n}\n\nfunction resolveWoodBridge(): WoodBridgeLike {\n const globalObj = globalThis as typeof globalThis & {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n window?: {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n };\n };\n\n const bridge = globalObj[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.window?.[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n throw new Error(\n `Wood bridge is not available on globalThis.${INTERNAL_BRIDGE_GLOBAL_KEY}.`,\n );\n }\n\n return bridge as WoodBridgeLike;\n}\n\nfunction resolveRpcManifest(bridge: WoodBridgeLike): WoodRpcManifestEntry[] {\n const manifest = bridge.__rpcManifest;\n if (!Array.isArray(manifest)) {\n throw new Error(\n 'Wood bridge is missing \"__rpcManifest\". Regenerate preload with the latest @noego/wood.',\n );\n }\n\n return manifest;\n}\n\nfunction resolveBridgeFunction(\n bridge: WoodBridgeLike,\n pathSegments: string[],\n operationKey: string,\n): WoodBridgeRpcFunction {\n let current: unknown = bridge;\n\n for (const segment of pathSegments) {\n if (!current || typeof current !== 'object') {\n throw new Error(\n `Wood bridge operation \"${operationKey}\" is missing path \"${pathSegments.join('.')}\".`,\n );\n }\n current = (current as Record<string, unknown>)[segment];\n }\n\n if (typeof current !== 'function') {\n throw new Error(\n `Wood bridge path \"${pathSegments.join('.')}\" for \"${operationKey}\" is not callable.`,\n );\n }\n\n return current as WoodBridgeRpcFunction;\n}\n\nfunction assertSerializable(value: unknown, operationKey: string): void {\n if (typeof structuredClone !== 'function') {\n return;\n }\n\n try {\n structuredClone(value);\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(\n `App RPC \"${operationKey}\" payload is not serializable: ${reason}`,\n );\n }\n}\n\nfunction extractTraceConversationId(value: unknown): number | string | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n const record = value as Record<string, unknown>;\n if (typeof record.conversationId === 'number' || typeof record.conversationId === 'string') {\n return record.conversationId;\n }\n if (typeof record.threadId === 'number' || typeof record.threadId === 'string') {\n return record.threadId;\n }\n\n return undefined;\n}\n\nfunction validateManifestEntry(entry: WoodRpcManifestEntry, index: number): void {\n if (!entry.channel || !entry.controller || !entry.action) {\n throw new Error(`Invalid Wood RPC manifest entry at index ${index}.`);\n }\n if (!Array.isArray(entry.path) || entry.path.length === 0) {\n throw new Error(\n `Invalid Wood RPC manifest path for \"${entry.controller}.${entry.action}\".`,\n );\n }\n}\n\nfunction createAppInstance(): App {\n const bridge = resolveWoodBridge();\n const manifest = resolveRpcManifest(bridge);\n const app: Record<string, Record<string, (...args: unknown[]) => Promise<unknown>>> = {};\n\n manifest.forEach((entry, index) => {\n validateManifestEntry(entry, index);\n\n const operationKey = `${entry.controller}.${entry.action}`;\n const invoke = resolveBridgeFunction(bridge, entry.path, operationKey);\n\n if (!app[entry.controller]) {\n app[entry.controller] = {};\n }\n\n if (app[entry.controller][entry.action]) {\n throw new Error(\n `Duplicate RPC action \"${operationKey}\" in Wood manifest. Actions must be unique per controller.`,\n );\n }\n\n app[entry.controller][entry.action] = (...args: unknown[]): Promise<unknown> => {\n if (entry.hasInput) {\n if (args.length !== 1) {\n throw new Error(\n `App RPC \"${operationKey}\" expects exactly one payload argument.`,\n );\n }\n assertSerializable(args[0], operationKey);\n const conversationId = extractTraceConversationId(args[0]);\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke(args[0])\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n }\n\n if (args.length !== 0) {\n throw new Error(\n `App RPC \"${operationKey}\" expects no arguments.`,\n );\n }\n\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke()\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n };\n });\n\n return app as unknown as App;\n}\n\nexport function getApp(): App {\n const globalObj = globalThis as GlobalWithClientApp;\n if (!globalObj[CLIENT_APP_SYMBOL]) {\n globalObj[CLIENT_APP_SYMBOL] = createAppInstance();\n }\n return globalObj[CLIENT_APP_SYMBOL] as App;\n}\n\n/** Test-only helper to clear the client App singleton. */\nexport function clearClientAppForTests(): void {\n const globalObj = globalThis as GlobalWithClientApp;\n delete globalObj[CLIENT_APP_SYMBOL];\n}\n\nfunction resolveWindowControlsBridgeMethod<T>(\n method: keyof NonNullable<WoodBridgeLike['__window']>,\n): T {\n const bridge = resolveWoodBridge();\n const fn = bridge.__window?.[method];\n if (typeof fn !== 'function') {\n throw new Error(`Wood bridge does not expose \"__window.${String(method)}\".`);\n }\n return fn as unknown as T;\n}\n\nexport class WindowControls {\n async open(windowId: string, options?: Record<string, unknown>): Promise<void> {\n const open = resolveWindowControlsBridgeMethod<\n (id: string, opts?: Record<string, unknown>) => Promise<unknown>\n >('open');\n await open(windowId, options);\n }\n\n async close(windowId?: string): Promise<void> {\n const close = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('close');\n await close(windowId);\n }\n\n async focus(windowId: string): Promise<void> {\n const focus = resolveWindowControlsBridgeMethod<\n (id: string) => Promise<unknown>\n >('focus');\n await focus(windowId);\n }\n\n async minimize(windowId?: string): Promise<void> {\n const minimize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('minimize');\n await minimize(windowId);\n }\n\n async maximize(windowId?: string): Promise<void> {\n const maximize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('maximize');\n await maximize(windowId);\n }\n}\n\nexport function getWindowControls(): WindowControls {\n const globalObj = globalThis as GlobalWithWindowControls;\n if (!globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL]) {\n globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] = new WindowControls();\n }\n return globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] as WindowControls;\n}\n\n/** Test-only helper to clear the client WindowControls singleton. */\nexport function clearClientWindowControlsForTests(): void {\n const globalObj = globalThis as GlobalWithWindowControls;\n delete globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL];\n}\n\ntype RendererLogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nfunction stringifyLogMessage(message: unknown): string {\n if (typeof message === 'string') return message;\n if (message instanceof Error) return message.message;\n return String(message);\n}\n\nfunction writeConsole(level: RendererLogLevel, logger: string, message: string, context?: unknown): void {\n const prefix = `[${logger}] ${message}`;\n switch (level) {\n case 'debug':\n console.debug(prefix, context);\n return;\n case 'warn':\n console.warn(prefix, context);\n return;\n case 'error':\n console.error(prefix, context);\n return;\n default:\n console.info(prefix, context);\n }\n}\n\nclass RendererLogger {\n constructor(private readonly loggerName: string) {}\n\n debug(message: unknown, context?: unknown): void {\n this.log('debug', message, context);\n }\n\n info(message: unknown, context?: unknown): void {\n this.log('info', message, context);\n }\n\n warn(message: unknown, context?: unknown): void {\n this.log('warn', message, context);\n }\n\n error(message: unknown, context?: unknown): void {\n this.log('error', message, context);\n }\n\n private log(level: RendererLogLevel, rawMessage: unknown, context?: unknown): void {\n const message = stringifyLogMessage(rawMessage);\n\n let bridge: WoodBridgeLike;\n try {\n bridge = resolveWoodBridge();\n } catch {\n writeConsole(level, this.loggerName, message, context);\n return;\n }\n\n if (typeof bridge.__log !== 'function') {\n writeConsole(level, this.loggerName, message, context);\n return;\n }\n\n try {\n bridge.__log(level, this.loggerName, message, context);\n } catch {\n writeConsole(level, this.loggerName, message, context);\n }\n }\n}\n\ntype RendererLoggerShape = {\n debug(message: unknown, context?: unknown): void;\n info(message: unknown, context?: unknown): void;\n warn(message: unknown, context?: unknown): void;\n error(message: unknown, context?: unknown): void;\n};\n\nexport function createLogger(name: string): RendererLoggerShape {\n const loggerName = name.trim() || 'renderer';\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n if (!globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]) {\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL] = new Map<string, RendererLogger>();\n }\n\n const existing = globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.get(loggerName);\n if (existing) return existing;\n\n const logger = new RendererLogger(loggerName);\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.set(loggerName, logger);\n return logger;\n}\n\n/** Test-only helper to clear client logger singletons. */\nexport function clearClientLoggersForTests(): void {\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n delete globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL];\n}\n\nfunction registerClientBuiltins(container: WoodContainerRegistration): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\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\nfunction createClientContainerInstance(): any {\n const container = new Container();\n registerClientBuiltins(container as WoodContainerRegistration);\n return container;\n}\n\n/**\n * Return a process-global client container instance.\n *\n * - First call creates the container and stores it on a symbol.\n * - Later calls return the same container instance.\n */\nexport function getContainer(): any {\n const globalObj = globalThis as GlobalWithClientContainer;\n if (!globalObj[CLIENT_CONTAINER_SYMBOL]) {\n globalObj[CLIENT_CONTAINER_SYMBOL] = createClientContainerInstance();\n }\n return globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client container singleton. */\nexport function clearClientContainerForTests(): void {\n const globalObj = globalThis as GlobalWithClientContainer;\n delete globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client navigation singleton. */\nexport function clearClientNavigationForTests(): void {\n clearNavigationSingletonForTests();\n}\n\n/** Clamp a menu position so it stays within viewport bounds. */\nexport function clampMenuPosition(\n x: number,\n y: number,\n menuWidth: number,\n menuHeight: number,\n): { x: number; y: number } {\n const vw = typeof window !== 'undefined' ? window.innerWidth : Infinity;\n const vh = typeof window !== 'undefined' ? window.innerHeight : Infinity;\n return {\n x: Math.min(x, Math.max(0, vw - menuWidth)),\n y: Math.min(y, Math.max(0, vh - menuHeight)),\n };\n}\n\nfunction resolveTarget(target: Element | null): Element {\n if (!target) {\n throw new Error('Missing renderer mount target');\n }\n return target;\n}\n\n/**\n * Start a Wood renderer app by mounting it to a target element.\n * Usage:\n * import { app } from './generated/app.js';\n * startWood(app, document.getElementById('app'));\n */\nexport async function startWood<T>(\n mountRoot: (target: Element) => T,\n target: Element | null,\n): Promise<T> {\n // Ensure container singleton exists and registers built-in client tokens.\n const container = getContainer();\n // Ensure navigation singleton exists at first client boot.\n getNavigation();\n\n // Fetch framework config from the main process and apply trace min level\n // before the first render so no sub-threshold events cross IPC.\n try {\n const bridge = resolveWoodBridge();\n if (typeof bridge.__woodConfig === 'function') {\n const config = await bridge.__woodConfig() as { traceMinLevel?: string } | null;\n const level = config?.traceMinLevel;\n if (level === 'debug' || level === 'info' || level === 'warn' || level === 'error') {\n const traceConfiguration = container.instance\n ? await container.instance(TraceProviderConfiguration)\n : null;\n traceConfiguration?.configureMinLevel(level);\n }\n }\n } catch {\n // Config fetch is best-effort; fall back to defaults.\n }\n\n return mountRoot(resolveTarget(target));\n}\n\n/**\n * Backward-compatible helper that returns a target-aware boot function.\n * Usage:\n * const boot = createSvelteBoot(app);\n * const mounted = boot(document.getElementById('app'));\n */\nexport function createSvelteBoot<T>(\n mountRoot: (target: Element) => T,\n) {\n return (target: Element | null): T => mountRoot(resolveTarget(target));\n}\n\nexport { getNavigation };\nexport { Ipc, getIpc };\nexport { clearTraceHubForTests };\nexport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nexport type {\n FrameworkTracer,\n TraceCaptureOptions,\n TraceEvent,\n TraceInput,\n TraceLevel,\n TraceProcess,\n TraceProviderMode,\n TraceRuntime,\n TraceWaitOptions,\n} from '../tracing/index.js';\n"],"mappings":"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","names":["TraceProviderConfiguration"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["import './global_polyfill.js';\nimport 'reflect-metadata';\nimport { Container } from '@noego/ioc';\nimport {\n Navigation,\n getNavigation,\n clearNavigationForTests as clearNavigationSingletonForTests,\n} from '../navigation';\nimport { Ipc, getIpc } from '../ipc/index.js';\nimport {\n TraceProviderConfiguration,\n clearTraceHubForTests,\n emitRendererTrace,\n setRendererTraceTransport,\n} from '../tracing/index.js';\nexport type {\n ControllerEvents,\n ControllerInput,\n ControllerLifecycle,\n ControllerState,\n PageController,\n} from './controller_contract';\n\nconst CLIENT_CONTAINER_SYMBOL = Symbol.for('@noego/wood.client.container');\nconst CLIENT_APP_SYMBOL = Symbol.for('@noego/wood.client.app');\nconst CLIENT_WINDOW_CONTROLS_SYMBOL = Symbol.for('@noego/wood.client.window-controls');\nconst CLIENT_LOGGER_REGISTRY_SYMBOL = Symbol.for('@noego/wood.client.logger-registry');\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\nexport const WOOD_INTERNAL_BRIDGE_KEY = INTERNAL_BRIDGE_GLOBAL_KEY;\n\nexport interface WoodRpcManifestEntry {\n channel: string;\n controller: string;\n action: string;\n path: string[];\n hasInput: boolean;\n}\n\ntype WoodBridgeRpcFunction = (data?: unknown) => Promise<unknown>;\ntype WoodBridgeLike = Record<string, unknown> & {\n __rpcManifest?: WoodRpcManifestEntry[];\n __window?: {\n current?: () => Promise<unknown>;\n open?: (windowId: string, options?: Record<string, unknown>) => Promise<unknown>;\n close?: (windowId?: string) => Promise<unknown>;\n focus?: (windowId: string) => Promise<unknown>;\n minimize?: (windowId?: string) => Promise<unknown>;\n maximize?: (windowId?: string) => Promise<unknown>;\n };\n __log?: (\n level: string,\n logger: string,\n message: string,\n context?: unknown,\n ) => void;\n __woodConfig?: () => Promise<unknown>;\n __trace?: (event: unknown) => void;\n};\n\ntype GlobalWithClientContainer = typeof globalThis & {\n [CLIENT_CONTAINER_SYMBOL]?: any;\n};\n\ntype GlobalWithClientApp = typeof globalThis & {\n [CLIENT_APP_SYMBOL]?: App;\n};\n\ntype GlobalWithWindowControls = typeof globalThis & {\n [CLIENT_WINDOW_CONTROLS_SYMBOL]?: WindowControls;\n};\n\ntype GlobalWithLoggerRegistry = typeof globalThis & {\n [CLIENT_LOGGER_REGISTRY_SYMBOL]?: Map<string, RendererLogger>;\n};\n\ninterface WoodContainerRegistration {\n registerFunction?(\n token: any,\n resolver: (...args: any[]) => unknown,\n options?: Record<string, unknown>,\n ): void;\n}\n\nexport class App {\n [controllerName: string]: unknown;\n}\n\nfunction resolveWoodBridge(): WoodBridgeLike {\n const globalObj = globalThis as typeof globalThis & {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n window?: {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n };\n };\n\n const bridge = globalObj[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.window?.[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n throw new Error(\n `Wood bridge is not available on globalThis.${INTERNAL_BRIDGE_GLOBAL_KEY}.`,\n );\n }\n\n return bridge as WoodBridgeLike;\n}\n\nfunction resolveRpcManifest(bridge: WoodBridgeLike): WoodRpcManifestEntry[] {\n const manifest = bridge.__rpcManifest;\n if (!Array.isArray(manifest)) {\n throw new Error(\n 'Wood bridge is missing \"__rpcManifest\". Regenerate preload with the latest @noego/wood.',\n );\n }\n\n return manifest;\n}\n\nfunction resolveBridgeFunction(\n bridge: WoodBridgeLike,\n pathSegments: string[],\n operationKey: string,\n): WoodBridgeRpcFunction {\n let current: unknown = bridge;\n\n for (const segment of pathSegments) {\n if (!current || typeof current !== 'object') {\n throw new Error(\n `Wood bridge operation \"${operationKey}\" is missing path \"${pathSegments.join('.')}\".`,\n );\n }\n current = (current as Record<string, unknown>)[segment];\n }\n\n if (typeof current !== 'function') {\n throw new Error(\n `Wood bridge path \"${pathSegments.join('.')}\" for \"${operationKey}\" is not callable.`,\n );\n }\n\n return current as WoodBridgeRpcFunction;\n}\n\nfunction assertSerializable(value: unknown, operationKey: string): void {\n if (typeof structuredClone !== 'function') {\n return;\n }\n\n try {\n structuredClone(value);\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(\n `App RPC \"${operationKey}\" payload is not serializable: ${reason}`,\n );\n }\n}\n\nfunction extractTraceConversationId(value: unknown): number | string | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n const record = value as Record<string, unknown>;\n if (typeof record.conversationId === 'number' || typeof record.conversationId === 'string') {\n return record.conversationId;\n }\n if (typeof record.threadId === 'number' || typeof record.threadId === 'string') {\n return record.threadId;\n }\n\n return undefined;\n}\n\nfunction validateManifestEntry(entry: WoodRpcManifestEntry, index: number): void {\n if (!entry.channel || !entry.controller || !entry.action) {\n throw new Error(`Invalid Wood RPC manifest entry at index ${index}.`);\n }\n if (!Array.isArray(entry.path) || entry.path.length === 0) {\n throw new Error(\n `Invalid Wood RPC manifest path for \"${entry.controller}.${entry.action}\".`,\n );\n }\n}\n\nfunction createAppInstance(): App {\n const bridge = resolveWoodBridge();\n const manifest = resolveRpcManifest(bridge);\n const app: Record<string, Record<string, (...args: unknown[]) => Promise<unknown>>> = {};\n\n manifest.forEach((entry, index) => {\n validateManifestEntry(entry, index);\n\n const operationKey = `${entry.controller}.${entry.action}`;\n const invoke = resolveBridgeFunction(bridge, entry.path, operationKey);\n\n if (!app[entry.controller]) {\n app[entry.controller] = {};\n }\n\n if (app[entry.controller][entry.action]) {\n throw new Error(\n `Duplicate RPC action \"${operationKey}\" in Wood manifest. Actions must be unique per controller.`,\n );\n }\n\n app[entry.controller][entry.action] = (...args: unknown[]): Promise<unknown> => {\n if (entry.hasInput) {\n if (args.length !== 1) {\n throw new Error(\n `App RPC \"${operationKey}\" expects exactly one payload argument.`,\n );\n }\n assertSerializable(args[0], operationKey);\n const conversationId = extractTraceConversationId(args[0]);\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke(args[0])\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n }\n\n if (args.length !== 0) {\n throw new Error(\n `App RPC \"${operationKey}\" expects no arguments.`,\n );\n }\n\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke()\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n };\n });\n\n return app as unknown as App;\n}\n\nexport function getApp(): App {\n const globalObj = globalThis as GlobalWithClientApp;\n if (!globalObj[CLIENT_APP_SYMBOL]) {\n globalObj[CLIENT_APP_SYMBOL] = createAppInstance();\n }\n return globalObj[CLIENT_APP_SYMBOL] as App;\n}\n\n/** Test-only helper to clear the client App singleton. */\nexport function clearClientAppForTests(): void {\n const globalObj = globalThis as GlobalWithClientApp;\n delete globalObj[CLIENT_APP_SYMBOL];\n}\n\nfunction resolveWindowControlsBridgeMethod<T>(\n method: keyof NonNullable<WoodBridgeLike['__window']>,\n): T {\n const bridge = resolveWoodBridge();\n const fn = bridge.__window?.[method];\n if (typeof fn !== 'function') {\n throw new Error(`Wood bridge does not expose \"__window.${String(method)}\".`);\n }\n return fn as unknown as T;\n}\n\nexport class WindowControls {\n async open(windowId: string, options?: Record<string, unknown>): Promise<void> {\n const open = resolveWindowControlsBridgeMethod<\n (id: string, opts?: Record<string, unknown>) => Promise<unknown>\n >('open');\n await open(windowId, options);\n }\n\n async close(windowId?: string): Promise<void> {\n const close = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('close');\n await close(windowId);\n }\n\n async focus(windowId: string): Promise<void> {\n const focus = resolveWindowControlsBridgeMethod<\n (id: string) => Promise<unknown>\n >('focus');\n await focus(windowId);\n }\n\n async minimize(windowId?: string): Promise<void> {\n const minimize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('minimize');\n await minimize(windowId);\n }\n\n async maximize(windowId?: string): Promise<void> {\n const maximize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('maximize');\n await maximize(windowId);\n }\n}\n\nexport function getWindowControls(): WindowControls {\n const globalObj = globalThis as GlobalWithWindowControls;\n if (!globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL]) {\n globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] = new WindowControls();\n }\n return globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] as WindowControls;\n}\n\n/** Test-only helper to clear the client WindowControls singleton. */\nexport function clearClientWindowControlsForTests(): void {\n const globalObj = globalThis as GlobalWithWindowControls;\n delete globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL];\n}\n\ntype RendererLogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nfunction stringifyLogMessage(message: unknown): string {\n if (typeof message === 'string') return message;\n if (message instanceof Error) return message.message;\n return String(message);\n}\n\nfunction writeConsole(level: RendererLogLevel, logger: string, message: string, context?: unknown): void {\n const prefix = `[${logger}] ${message}`;\n switch (level) {\n case 'debug':\n console.debug(prefix, context);\n return;\n case 'warn':\n console.warn(prefix, context);\n return;\n case 'error':\n console.error(prefix, context);\n return;\n default:\n console.info(prefix, context);\n }\n}\n\nclass RendererLogger {\n constructor(private readonly loggerName: string) {}\n\n debug(message: unknown, context?: unknown): void {\n this.log('debug', message, context);\n }\n\n info(message: unknown, context?: unknown): void {\n this.log('info', message, context);\n }\n\n warn(message: unknown, context?: unknown): void {\n this.log('warn', message, context);\n }\n\n error(message: unknown, context?: unknown): void {\n this.log('error', message, context);\n }\n\n private log(level: RendererLogLevel, rawMessage: unknown, context?: unknown): void {\n const message = stringifyLogMessage(rawMessage);\n\n let bridge: WoodBridgeLike;\n try {\n bridge = resolveWoodBridge();\n } catch {\n writeConsole(level, this.loggerName, message, context);\n return;\n }\n\n if (typeof bridge.__log !== 'function') {\n writeConsole(level, this.loggerName, message, context);\n return;\n }\n\n try {\n bridge.__log(level, this.loggerName, message, context);\n } catch {\n writeConsole(level, this.loggerName, message, context);\n }\n }\n}\n\ntype RendererLoggerShape = {\n debug(message: unknown, context?: unknown): void;\n info(message: unknown, context?: unknown): void;\n warn(message: unknown, context?: unknown): void;\n error(message: unknown, context?: unknown): void;\n};\n\nexport function createLogger(name: string): RendererLoggerShape {\n const loggerName = name.trim() || 'renderer';\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n if (!globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]) {\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL] = new Map<string, RendererLogger>();\n }\n\n const existing = globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.get(loggerName);\n if (existing) return existing;\n\n const logger = new RendererLogger(loggerName);\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.set(loggerName, logger);\n return logger;\n}\n\n/** Test-only helper to clear client logger singletons. */\nexport function clearClientLoggersForTests(): void {\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n delete globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL];\n}\n\nfunction registerClientBuiltins(container: WoodContainerRegistration): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\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\nfunction createClientContainerInstance(): any {\n const container = new Container();\n registerClientBuiltins(container as WoodContainerRegistration);\n return container;\n}\n\n/**\n * Return a process-global client container instance.\n *\n * - First call creates the container and stores it on a symbol.\n * - Later calls return the same container instance.\n */\nexport function getContainer(): any {\n const globalObj = globalThis as GlobalWithClientContainer;\n if (!globalObj[CLIENT_CONTAINER_SYMBOL]) {\n globalObj[CLIENT_CONTAINER_SYMBOL] = createClientContainerInstance();\n }\n return globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client container singleton. */\nexport function clearClientContainerForTests(): void {\n const globalObj = globalThis as GlobalWithClientContainer;\n delete globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client navigation singleton. */\nexport function clearClientNavigationForTests(): void {\n clearNavigationSingletonForTests();\n}\n\n/** Clamp a menu position so it stays within viewport bounds. */\nexport function clampMenuPosition(\n x: number,\n y: number,\n menuWidth: number,\n menuHeight: number,\n): { x: number; y: number } {\n const vw = typeof window !== 'undefined' ? window.innerWidth : Infinity;\n const vh = typeof window !== 'undefined' ? window.innerHeight : Infinity;\n return {\n x: Math.min(x, Math.max(0, vw - menuWidth)),\n y: Math.min(y, Math.max(0, vh - menuHeight)),\n };\n}\n\nfunction resolveTarget(target: Element | null): Element {\n if (!target) {\n throw new Error('Missing renderer mount target');\n }\n return target;\n}\n\n/**\n * Start a Wood renderer app by mounting it to a target element.\n * Usage:\n * import { app } from './generated/app.js';\n * startWood(app, document.getElementById('app'));\n */\nexport async function startWood<T>(\n mountRoot: (target: Element) => T,\n target: Element | null,\n): Promise<T> {\n // Ensure container singleton exists and registers built-in client tokens.\n const container = getContainer();\n // Ensure navigation singleton exists at first client boot.\n getNavigation();\n\n // Auto-detect MessagePort-based trace transport\n try {\n const traceGlobal = (globalThis as Record<string, unknown>).__NOEGO_TRACE__ as\n | { send?: (data: unknown) => void }\n | undefined;\n if (traceGlobal && typeof traceGlobal.send === 'function') {\n const sender = traceGlobal.send.bind(traceGlobal);\n setRendererTraceTransport((payload: unknown) => sender(payload));\n }\n } catch {\n // MessagePort trace transport not available; IPC fallback remains active.\n }\n\n // Fetch framework config from the main process and apply trace min level\n // before the first render so no sub-threshold events cross IPC.\n try {\n const bridge = resolveWoodBridge();\n if (typeof bridge.__woodConfig === 'function') {\n const config = await bridge.__woodConfig() as { traceMinLevel?: string } | null;\n const level = config?.traceMinLevel;\n if (level === 'debug' || level === 'info' || level === 'warn' || level === 'error') {\n const traceConfiguration = container.instance\n ? await container.instance(TraceProviderConfiguration)\n : null;\n traceConfiguration?.configureMinLevel(level);\n }\n }\n } catch {\n // Config fetch is best-effort; fall back to defaults.\n }\n\n return mountRoot(resolveTarget(target));\n}\n\n/**\n * Backward-compatible helper that returns a target-aware boot function.\n * Usage:\n * const boot = createSvelteBoot(app);\n * const mounted = boot(document.getElementById('app'));\n */\nexport function createSvelteBoot<T>(\n mountRoot: (target: Element) => T,\n) {\n return (target: Element | null): T => mountRoot(resolveTarget(target));\n}\n\nexport { getNavigation };\nexport { Ipc, getIpc };\nexport { clearTraceHubForTests };\nexport { setRendererTraceTransport };\nexport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nexport type {\n FrameworkTracer,\n TraceCaptureOptions,\n TraceEvent,\n TraceInput,\n TraceLevel,\n TraceProcess,\n TraceProviderMode,\n TraceRuntime,\n TraceWaitOptions,\n} from '../tracing/index.js';\n"],"mappings":"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","names":["TraceProviderConfiguration"]}
|
|
@@ -148,6 +148,32 @@ ${operationsCode}
|
|
|
148
148
|
ipcRenderer.send('__log', { level, logger, message, context });
|
|
149
149
|
},
|
|
150
150
|
});
|
|
151
|
+
|
|
152
|
+
// Trace transport via MessagePort (activated when main process sends a port)
|
|
153
|
+
let __tracePort: MessagePort | null = null;
|
|
154
|
+
const __traceBuffer: unknown[] = [];
|
|
155
|
+
|
|
156
|
+
function __traceFlush(): void {
|
|
157
|
+
while (__traceBuffer.length > 0 && __tracePort) {
|
|
158
|
+
__tracePort.postMessage(__traceBuffer.shift());
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
ipcRenderer.on('__trace-port', (event) => {
|
|
163
|
+
__tracePort = event.ports[0];
|
|
164
|
+
__tracePort.start();
|
|
165
|
+
__traceFlush();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
contextBridge.exposeInMainWorld('__NOEGO_TRACE__', {
|
|
169
|
+
send: (data: unknown) => {
|
|
170
|
+
if (__tracePort) {
|
|
171
|
+
__tracePort.postMessage(data);
|
|
172
|
+
} else {
|
|
173
|
+
__traceBuffer.push(data);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
});
|
|
151
177
|
`;
|
|
152
178
|
}
|
|
153
179
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/codegen/preload_generator.ts"],"sourcesContent":["import type { OperationDef } from '../types/index.cjs';\n\nfunction operationHasInput(op: OperationDef): boolean {\n return Boolean(\n op.inputSchema &&\n (op.inputSchema as Record<string, unknown>).type === 'object' &&\n Object.keys((op.inputSchema as Record<string, Record<string, unknown>>).properties ?? {}).length > 0,\n );\n}\n\n/**\n * Build a nested object tree from dot-path operation channels.\n * \"chat.message.send\" -> { chat: { message: { send: ... } } }\n */\nfunction buildNestedTree(operations: Map<string, OperationDef>): Record<string, unknown> {\n const tree: Record<string, unknown> = {};\n\n for (const [channel, op] of operations) {\n const parts = channel.split('.');\n let current: Record<string, unknown> = tree;\n\n for (let i = 0; i < parts.length - 1; i++) {\n if (!current[parts[i]]) current[parts[i]] = {};\n current = current[parts[i]] as Record<string, unknown>;\n }\n\n const leaf = parts[parts.length - 1];\n current[leaf] = { __operation: op };\n }\n\n return tree;\n}\n\n/**\n * Generate TypeScript code for a nested tree node.\n */\nfunction generateTreeCode(node: Record<string, unknown>, indent: string = ' '): string {\n const lines: string[] = [];\n\n for (const [key, value] of Object.entries(node)) {\n const entry = value as Record<string, unknown>;\n if (entry.__operation) {\n const op = entry.__operation as OperationDef;\n const hasInput = operationHasInput(op);\n\n if (hasInput) {\n lines.push(`${indent}${key}: (data: unknown) =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}', data),`);\n } else {\n lines.push(`${indent}${key}: () =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}'),`);\n }\n } else {\n lines.push(`${indent}${key}: {`);\n lines.push(generateTreeCode(entry, indent + ' '));\n lines.push(`${indent}},`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction generateRpcManifestCode(operations: Map<string, OperationDef>): string {\n const entries = Array.from(operations.values())\n .sort((a, b) => a.channel.localeCompare(b.channel))\n .map((op) => {\n const payload = {\n channel: op.channel,\n controller: op.controller,\n action: op.action,\n path: op.channel.split('.'),\n hasInput: operationHasInput(op),\n };\n return ` ${JSON.stringify(payload)},`;\n });\n\n return `const __WOOD_RPC_MANIFEST = [\n${entries.join('\\n')}\n];`;\n}\n\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\n/**\n * Generate a complete preload.ts file from operations.\n */\nexport function generatePreload(operations: Map<string, OperationDef>): string {\n const tree = buildNestedTree(operations);\n const operationsCode = generateTreeCode(tree);\n const rpcManifestCode = generateRpcManifestCode(operations);\n\n return `// AUTO-GENERATED by @noego/wood -- do not edit\nimport { contextBridge, ipcRenderer } from 'electron';\n\n${rpcManifestCode}\n\nconst __WOOD_INTERNAL_BRIDGE_KEY = '${INTERNAL_BRIDGE_GLOBAL_KEY}';\n\ncontextBridge.exposeInMainWorld(__WOOD_INTERNAL_BRIDGE_KEY, {\n${operationsCode}\n\n // Internal: backend controller/action RPC map for @Inject(App)\n __rpcManifest: __WOOD_RPC_MANIFEST,\n\n // Internal: loader data requests\n __load: (route: string, params: Record<string, string>) =>\n ipcRenderer.invoke('__load', { route, params }),\n\n // Internal: cross-window navigation\n __navigate: (windowId: string, route: string) =>\n ipcRenderer.invoke('__navigate', { windowId, route }),\n\n // Internal: window management\n __window: {\n current: () =>\n ipcRenderer.invoke('__window', { action: 'current' }),\n open: (windowId: string, options?: Record<string, unknown>) =>\n ipcRenderer.invoke('__window', { action: 'open', windowId, options }),\n close: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'close', windowId }),\n focus: (windowId: string) =>\n ipcRenderer.invoke('__window', { action: 'focus', windowId }),\n minimize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'minimize', windowId }),\n maximize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'maximize', windowId }),\n },\n\n // Internal: framework config (trace min level, etc)\n __woodConfig: () =>\n ipcRenderer.invoke('__woodConfig'),\n\n // Internal: debug metadata\n __debug: {\n current: () =>\n ipcRenderer.invoke('__debug', { action: 'current' }),\n },\n\n // Internal: context menu\n __contextMenu: {\n show: (data: unknown) =>\n ipcRenderer.invoke('__contextMenu', { action: 'show', ...data as Record<string, unknown> }),\n },\n\n // Push events from main process\n on: (channel: string, callback: (...args: unknown[]) => void) => {\n const subscription = (_event: unknown, ...args: unknown[]) => callback(...args);\n ipcRenderer.on(\\`__event:\\${channel}\\`, subscription);\n return () => ipcRenderer.removeListener(\\`__event:\\${channel}\\`, subscription);\n },\n __trace: (event: unknown) => {\n ipcRenderer.send('__trace', event);\n },\n __log: (level: string, logger: string, message: string, context?: unknown) => {\n ipcRenderer.send('__log', { level, logger, message, context });\n },\n});\n`;\n}\n"],"mappings":"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
|
|
1
|
+
{"version":3,"sources":["../../src/codegen/preload_generator.ts"],"sourcesContent":["import type { OperationDef } from '../types/index.cjs';\n\nfunction operationHasInput(op: OperationDef): boolean {\n return Boolean(\n op.inputSchema &&\n (op.inputSchema as Record<string, unknown>).type === 'object' &&\n Object.keys((op.inputSchema as Record<string, Record<string, unknown>>).properties ?? {}).length > 0,\n );\n}\n\n/**\n * Build a nested object tree from dot-path operation channels.\n * \"chat.message.send\" -> { chat: { message: { send: ... } } }\n */\nfunction buildNestedTree(operations: Map<string, OperationDef>): Record<string, unknown> {\n const tree: Record<string, unknown> = {};\n\n for (const [channel, op] of operations) {\n const parts = channel.split('.');\n let current: Record<string, unknown> = tree;\n\n for (let i = 0; i < parts.length - 1; i++) {\n if (!current[parts[i]]) current[parts[i]] = {};\n current = current[parts[i]] as Record<string, unknown>;\n }\n\n const leaf = parts[parts.length - 1];\n current[leaf] = { __operation: op };\n }\n\n return tree;\n}\n\n/**\n * Generate TypeScript code for a nested tree node.\n */\nfunction generateTreeCode(node: Record<string, unknown>, indent: string = ' '): string {\n const lines: string[] = [];\n\n for (const [key, value] of Object.entries(node)) {\n const entry = value as Record<string, unknown>;\n if (entry.__operation) {\n const op = entry.__operation as OperationDef;\n const hasInput = operationHasInput(op);\n\n if (hasInput) {\n lines.push(`${indent}${key}: (data: unknown) =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}', data),`);\n } else {\n lines.push(`${indent}${key}: () =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}'),`);\n }\n } else {\n lines.push(`${indent}${key}: {`);\n lines.push(generateTreeCode(entry, indent + ' '));\n lines.push(`${indent}},`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction generateRpcManifestCode(operations: Map<string, OperationDef>): string {\n const entries = Array.from(operations.values())\n .sort((a, b) => a.channel.localeCompare(b.channel))\n .map((op) => {\n const payload = {\n channel: op.channel,\n controller: op.controller,\n action: op.action,\n path: op.channel.split('.'),\n hasInput: operationHasInput(op),\n };\n return ` ${JSON.stringify(payload)},`;\n });\n\n return `const __WOOD_RPC_MANIFEST = [\n${entries.join('\\n')}\n];`;\n}\n\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\n/**\n * Generate a complete preload.ts file from operations.\n */\nexport function generatePreload(operations: Map<string, OperationDef>): string {\n const tree = buildNestedTree(operations);\n const operationsCode = generateTreeCode(tree);\n const rpcManifestCode = generateRpcManifestCode(operations);\n\n return `// AUTO-GENERATED by @noego/wood -- do not edit\nimport { contextBridge, ipcRenderer } from 'electron';\n\n${rpcManifestCode}\n\nconst __WOOD_INTERNAL_BRIDGE_KEY = '${INTERNAL_BRIDGE_GLOBAL_KEY}';\n\ncontextBridge.exposeInMainWorld(__WOOD_INTERNAL_BRIDGE_KEY, {\n${operationsCode}\n\n // Internal: backend controller/action RPC map for @Inject(App)\n __rpcManifest: __WOOD_RPC_MANIFEST,\n\n // Internal: loader data requests\n __load: (route: string, params: Record<string, string>) =>\n ipcRenderer.invoke('__load', { route, params }),\n\n // Internal: cross-window navigation\n __navigate: (windowId: string, route: string) =>\n ipcRenderer.invoke('__navigate', { windowId, route }),\n\n // Internal: window management\n __window: {\n current: () =>\n ipcRenderer.invoke('__window', { action: 'current' }),\n open: (windowId: string, options?: Record<string, unknown>) =>\n ipcRenderer.invoke('__window', { action: 'open', windowId, options }),\n close: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'close', windowId }),\n focus: (windowId: string) =>\n ipcRenderer.invoke('__window', { action: 'focus', windowId }),\n minimize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'minimize', windowId }),\n maximize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'maximize', windowId }),\n },\n\n // Internal: framework config (trace min level, etc)\n __woodConfig: () =>\n ipcRenderer.invoke('__woodConfig'),\n\n // Internal: debug metadata\n __debug: {\n current: () =>\n ipcRenderer.invoke('__debug', { action: 'current' }),\n },\n\n // Internal: context menu\n __contextMenu: {\n show: (data: unknown) =>\n ipcRenderer.invoke('__contextMenu', { action: 'show', ...data as Record<string, unknown> }),\n },\n\n // Push events from main process\n on: (channel: string, callback: (...args: unknown[]) => void) => {\n const subscription = (_event: unknown, ...args: unknown[]) => callback(...args);\n ipcRenderer.on(\\`__event:\\${channel}\\`, subscription);\n return () => ipcRenderer.removeListener(\\`__event:\\${channel}\\`, subscription);\n },\n __trace: (event: unknown) => {\n ipcRenderer.send('__trace', event);\n },\n __log: (level: string, logger: string, message: string, context?: unknown) => {\n ipcRenderer.send('__log', { level, logger, message, context });\n },\n});\n\n// Trace transport via MessagePort (activated when main process sends a port)\nlet __tracePort: MessagePort | null = null;\nconst __traceBuffer: unknown[] = [];\n\nfunction __traceFlush(): void {\n while (__traceBuffer.length > 0 && __tracePort) {\n __tracePort.postMessage(__traceBuffer.shift());\n }\n}\n\nipcRenderer.on('__trace-port', (event) => {\n __tracePort = event.ports[0];\n __tracePort.start();\n __traceFlush();\n});\n\ncontextBridge.exposeInMainWorld('__NOEGO_TRACE__', {\n send: (data: unknown) => {\n if (__tracePort) {\n __tracePort.postMessage(data);\n } else {\n __traceBuffer.push(data);\n }\n },\n});\n`;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -125,6 +125,32 @@ ${operationsCode}
|
|
|
125
125
|
ipcRenderer.send('__log', { level, logger, message, context });
|
|
126
126
|
},
|
|
127
127
|
});
|
|
128
|
+
|
|
129
|
+
// Trace transport via MessagePort (activated when main process sends a port)
|
|
130
|
+
let __tracePort: MessagePort | null = null;
|
|
131
|
+
const __traceBuffer: unknown[] = [];
|
|
132
|
+
|
|
133
|
+
function __traceFlush(): void {
|
|
134
|
+
while (__traceBuffer.length > 0 && __tracePort) {
|
|
135
|
+
__tracePort.postMessage(__traceBuffer.shift());
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
ipcRenderer.on('__trace-port', (event) => {
|
|
140
|
+
__tracePort = event.ports[0];
|
|
141
|
+
__tracePort.start();
|
|
142
|
+
__traceFlush();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
contextBridge.exposeInMainWorld('__NOEGO_TRACE__', {
|
|
146
|
+
send: (data: unknown) => {
|
|
147
|
+
if (__tracePort) {
|
|
148
|
+
__tracePort.postMessage(data);
|
|
149
|
+
} else {
|
|
150
|
+
__traceBuffer.push(data);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
});
|
|
128
154
|
`;
|
|
129
155
|
}
|
|
130
156
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/codegen/preload_generator.ts"],"sourcesContent":["import type { OperationDef } from '../types/index.js';\n\nfunction operationHasInput(op: OperationDef): boolean {\n return Boolean(\n op.inputSchema &&\n (op.inputSchema as Record<string, unknown>).type === 'object' &&\n Object.keys((op.inputSchema as Record<string, Record<string, unknown>>).properties ?? {}).length > 0,\n );\n}\n\n/**\n * Build a nested object tree from dot-path operation channels.\n * \"chat.message.send\" -> { chat: { message: { send: ... } } }\n */\nfunction buildNestedTree(operations: Map<string, OperationDef>): Record<string, unknown> {\n const tree: Record<string, unknown> = {};\n\n for (const [channel, op] of operations) {\n const parts = channel.split('.');\n let current: Record<string, unknown> = tree;\n\n for (let i = 0; i < parts.length - 1; i++) {\n if (!current[parts[i]]) current[parts[i]] = {};\n current = current[parts[i]] as Record<string, unknown>;\n }\n\n const leaf = parts[parts.length - 1];\n current[leaf] = { __operation: op };\n }\n\n return tree;\n}\n\n/**\n * Generate TypeScript code for a nested tree node.\n */\nfunction generateTreeCode(node: Record<string, unknown>, indent: string = ' '): string {\n const lines: string[] = [];\n\n for (const [key, value] of Object.entries(node)) {\n const entry = value as Record<string, unknown>;\n if (entry.__operation) {\n const op = entry.__operation as OperationDef;\n const hasInput = operationHasInput(op);\n\n if (hasInput) {\n lines.push(`${indent}${key}: (data: unknown) =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}', data),`);\n } else {\n lines.push(`${indent}${key}: () =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}'),`);\n }\n } else {\n lines.push(`${indent}${key}: {`);\n lines.push(generateTreeCode(entry, indent + ' '));\n lines.push(`${indent}},`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction generateRpcManifestCode(operations: Map<string, OperationDef>): string {\n const entries = Array.from(operations.values())\n .sort((a, b) => a.channel.localeCompare(b.channel))\n .map((op) => {\n const payload = {\n channel: op.channel,\n controller: op.controller,\n action: op.action,\n path: op.channel.split('.'),\n hasInput: operationHasInput(op),\n };\n return ` ${JSON.stringify(payload)},`;\n });\n\n return `const __WOOD_RPC_MANIFEST = [\n${entries.join('\\n')}\n];`;\n}\n\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\n/**\n * Generate a complete preload.ts file from operations.\n */\nexport function generatePreload(operations: Map<string, OperationDef>): string {\n const tree = buildNestedTree(operations);\n const operationsCode = generateTreeCode(tree);\n const rpcManifestCode = generateRpcManifestCode(operations);\n\n return `// AUTO-GENERATED by @noego/wood -- do not edit\nimport { contextBridge, ipcRenderer } from 'electron';\n\n${rpcManifestCode}\n\nconst __WOOD_INTERNAL_BRIDGE_KEY = '${INTERNAL_BRIDGE_GLOBAL_KEY}';\n\ncontextBridge.exposeInMainWorld(__WOOD_INTERNAL_BRIDGE_KEY, {\n${operationsCode}\n\n // Internal: backend controller/action RPC map for @Inject(App)\n __rpcManifest: __WOOD_RPC_MANIFEST,\n\n // Internal: loader data requests\n __load: (route: string, params: Record<string, string>) =>\n ipcRenderer.invoke('__load', { route, params }),\n\n // Internal: cross-window navigation\n __navigate: (windowId: string, route: string) =>\n ipcRenderer.invoke('__navigate', { windowId, route }),\n\n // Internal: window management\n __window: {\n current: () =>\n ipcRenderer.invoke('__window', { action: 'current' }),\n open: (windowId: string, options?: Record<string, unknown>) =>\n ipcRenderer.invoke('__window', { action: 'open', windowId, options }),\n close: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'close', windowId }),\n focus: (windowId: string) =>\n ipcRenderer.invoke('__window', { action: 'focus', windowId }),\n minimize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'minimize', windowId }),\n maximize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'maximize', windowId }),\n },\n\n // Internal: framework config (trace min level, etc)\n __woodConfig: () =>\n ipcRenderer.invoke('__woodConfig'),\n\n // Internal: debug metadata\n __debug: {\n current: () =>\n ipcRenderer.invoke('__debug', { action: 'current' }),\n },\n\n // Internal: context menu\n __contextMenu: {\n show: (data: unknown) =>\n ipcRenderer.invoke('__contextMenu', { action: 'show', ...data as Record<string, unknown> }),\n },\n\n // Push events from main process\n on: (channel: string, callback: (...args: unknown[]) => void) => {\n const subscription = (_event: unknown, ...args: unknown[]) => callback(...args);\n ipcRenderer.on(\\`__event:\\${channel}\\`, subscription);\n return () => ipcRenderer.removeListener(\\`__event:\\${channel}\\`, subscription);\n },\n __trace: (event: unknown) => {\n ipcRenderer.send('__trace', event);\n },\n __log: (level: string, logger: string, message: string, context?: unknown) => {\n ipcRenderer.send('__log', { level, logger, message, context });\n },\n});\n`;\n}\n"],"mappings":"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
|
|
1
|
+
{"version":3,"sources":["../../src/codegen/preload_generator.ts"],"sourcesContent":["import type { OperationDef } from '../types/index.js';\n\nfunction operationHasInput(op: OperationDef): boolean {\n return Boolean(\n op.inputSchema &&\n (op.inputSchema as Record<string, unknown>).type === 'object' &&\n Object.keys((op.inputSchema as Record<string, Record<string, unknown>>).properties ?? {}).length > 0,\n );\n}\n\n/**\n * Build a nested object tree from dot-path operation channels.\n * \"chat.message.send\" -> { chat: { message: { send: ... } } }\n */\nfunction buildNestedTree(operations: Map<string, OperationDef>): Record<string, unknown> {\n const tree: Record<string, unknown> = {};\n\n for (const [channel, op] of operations) {\n const parts = channel.split('.');\n let current: Record<string, unknown> = tree;\n\n for (let i = 0; i < parts.length - 1; i++) {\n if (!current[parts[i]]) current[parts[i]] = {};\n current = current[parts[i]] as Record<string, unknown>;\n }\n\n const leaf = parts[parts.length - 1];\n current[leaf] = { __operation: op };\n }\n\n return tree;\n}\n\n/**\n * Generate TypeScript code for a nested tree node.\n */\nfunction generateTreeCode(node: Record<string, unknown>, indent: string = ' '): string {\n const lines: string[] = [];\n\n for (const [key, value] of Object.entries(node)) {\n const entry = value as Record<string, unknown>;\n if (entry.__operation) {\n const op = entry.__operation as OperationDef;\n const hasInput = operationHasInput(op);\n\n if (hasInput) {\n lines.push(`${indent}${key}: (data: unknown) =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}', data),`);\n } else {\n lines.push(`${indent}${key}: () =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}'),`);\n }\n } else {\n lines.push(`${indent}${key}: {`);\n lines.push(generateTreeCode(entry, indent + ' '));\n lines.push(`${indent}},`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction generateRpcManifestCode(operations: Map<string, OperationDef>): string {\n const entries = Array.from(operations.values())\n .sort((a, b) => a.channel.localeCompare(b.channel))\n .map((op) => {\n const payload = {\n channel: op.channel,\n controller: op.controller,\n action: op.action,\n path: op.channel.split('.'),\n hasInput: operationHasInput(op),\n };\n return ` ${JSON.stringify(payload)},`;\n });\n\n return `const __WOOD_RPC_MANIFEST = [\n${entries.join('\\n')}\n];`;\n}\n\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\n/**\n * Generate a complete preload.ts file from operations.\n */\nexport function generatePreload(operations: Map<string, OperationDef>): string {\n const tree = buildNestedTree(operations);\n const operationsCode = generateTreeCode(tree);\n const rpcManifestCode = generateRpcManifestCode(operations);\n\n return `// AUTO-GENERATED by @noego/wood -- do not edit\nimport { contextBridge, ipcRenderer } from 'electron';\n\n${rpcManifestCode}\n\nconst __WOOD_INTERNAL_BRIDGE_KEY = '${INTERNAL_BRIDGE_GLOBAL_KEY}';\n\ncontextBridge.exposeInMainWorld(__WOOD_INTERNAL_BRIDGE_KEY, {\n${operationsCode}\n\n // Internal: backend controller/action RPC map for @Inject(App)\n __rpcManifest: __WOOD_RPC_MANIFEST,\n\n // Internal: loader data requests\n __load: (route: string, params: Record<string, string>) =>\n ipcRenderer.invoke('__load', { route, params }),\n\n // Internal: cross-window navigation\n __navigate: (windowId: string, route: string) =>\n ipcRenderer.invoke('__navigate', { windowId, route }),\n\n // Internal: window management\n __window: {\n current: () =>\n ipcRenderer.invoke('__window', { action: 'current' }),\n open: (windowId: string, options?: Record<string, unknown>) =>\n ipcRenderer.invoke('__window', { action: 'open', windowId, options }),\n close: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'close', windowId }),\n focus: (windowId: string) =>\n ipcRenderer.invoke('__window', { action: 'focus', windowId }),\n minimize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'minimize', windowId }),\n maximize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'maximize', windowId }),\n },\n\n // Internal: framework config (trace min level, etc)\n __woodConfig: () =>\n ipcRenderer.invoke('__woodConfig'),\n\n // Internal: debug metadata\n __debug: {\n current: () =>\n ipcRenderer.invoke('__debug', { action: 'current' }),\n },\n\n // Internal: context menu\n __contextMenu: {\n show: (data: unknown) =>\n ipcRenderer.invoke('__contextMenu', { action: 'show', ...data as Record<string, unknown> }),\n },\n\n // Push events from main process\n on: (channel: string, callback: (...args: unknown[]) => void) => {\n const subscription = (_event: unknown, ...args: unknown[]) => callback(...args);\n ipcRenderer.on(\\`__event:\\${channel}\\`, subscription);\n return () => ipcRenderer.removeListener(\\`__event:\\${channel}\\`, subscription);\n },\n __trace: (event: unknown) => {\n ipcRenderer.send('__trace', event);\n },\n __log: (level: string, logger: string, message: string, context?: unknown) => {\n ipcRenderer.send('__log', { level, logger, message, context });\n },\n});\n\n// Trace transport via MessagePort (activated when main process sends a port)\nlet __tracePort: MessagePort | null = null;\nconst __traceBuffer: unknown[] = [];\n\nfunction __traceFlush(): void {\n while (__traceBuffer.length > 0 && __tracePort) {\n __tracePort.postMessage(__traceBuffer.shift());\n }\n}\n\nipcRenderer.on('__trace-port', (event) => {\n __tracePort = event.ports[0];\n __tracePort.start();\n __traceFlush();\n});\n\ncontextBridge.exposeInMainWorld('__NOEGO_TRACE__', {\n send: (data: unknown) => {\n if (__tracePort) {\n __tracePort.postMessage(data);\n } else {\n __traceBuffer.push(data);\n }\n },\n});\n`;\n}\n"],"mappings":"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","names":[]}
|