@noego/wood 0.2.0 → 0.3.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/dist/client/index.cjs +1 -1
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/codegen/renderer_app_generator.cjs +17 -1
- package/dist/codegen/renderer_app_generator.cjs.map +1 -1
- package/dist/codegen/renderer_app_generator.js +17 -1
- package/dist/codegen/renderer_app_generator.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/navigation/index.cjs +17 -12
- package/dist/navigation/index.cjs.map +1 -1
- package/dist/navigation/index.d.cts +8 -4
- package/dist/navigation/index.d.ts +8 -4
- package/dist/navigation/index.js +17 -12
- package/dist/navigation/index.js.map +1 -1
- package/dist/parser/views_parser.cjs +24 -3
- package/dist/parser/views_parser.cjs.map +1 -1
- package/dist/parser/views_parser.js +24 -3
- package/dist/parser/views_parser.js.map +1 -1
- package/dist/testing/create_controller_harness.cjs +2 -2
- package/dist/testing/create_controller_harness.cjs.map +1 -1
- package/dist/testing/create_test_router.cjs +1 -1
- package/dist/testing/create_test_router.cjs.map +1 -1
- package/dist/testing/index.cjs +6 -6
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.js +3 -3
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock_electron.cjs +59 -39
- package/dist/testing/mock_electron.cjs.map +1 -1
- package/dist/testing/mock_electron.d.cts +2 -2
- package/dist/testing/mock_electron.d.ts +2 -2
- package/dist/testing/mock_electron.js +59 -39
- package/dist/testing/mock_electron.js.map +1 -1
- package/dist/types/views.cjs.map +1 -1
- package/dist/types/views.d.cts +3 -0
- package/dist/types/views.d.ts +3 -0
- package/package.json +1 -1
- package/src/components/NavigationShell.svelte +154 -27
- package/src/components/WoodRecursiveRender.svelte +49 -36
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @noego/wood — Declarative Electron desktop apps using the noego ecosystem\n\n// Types\nexport type {\n WoodConfig,\n WindowDef,\n DevConfig,\n ElectronBuilderConfig,\n OperationDef,\n RawOperationsDocument,\n RawModuleDef,\n RawOperationEntry,\n RawModuleOperationEntry,\n ViewDef,\n RawViewsDocument,\n RawViewEntry,\n ParameterDef,\n IpcContext,\n WoodMiddlewareContext,\n WoodMiddlewareFn,\n RequestData,\n ControllerArgs,\n LoaderResult,\n BridgeMessage,\n BridgeRequest,\n BridgeResponse,\n BridgeReady,\n BridgePush,\n BridgeElectronApi,\n SerializedError,\n ContextMenuContext,\n ContextMenuItem,\n ContextMenuEvent,\n} from './types';\n\n// Errors\nexport { WoodAuthError, WoodValidationError, BackendRestartingError } from './types/index.cjs';\n\n// Config\nexport { parseConfig } from './config/index.cjs';\n\n// Parsers\nexport { parseOperations, parseOperationsFile } from './parser/index.cjs';\nexport { parseViews, parseViewsFile } from './parser/index.cjs';\n\n// Router\nexport { WoodRouter } from './router/index.cjs';\n\n// Validation\nexport { SchemaValidator } from './validation/index.cjs';\n\n// Middleware\nexport { MiddlewareResolver, buildMiddlewareContext, executeMiddleware } from './middleware/index.cjs';\n\n// Controller\nexport { ControllerResolver, buildControllerArgs, disposeControllerLifecycle } from './controller/index.cjs';\nexport type { ControllerLifecycle } from './controller/index.cjs';\n\n// Codegen\nexport { generatePreload } from './codegen/index.cjs';\nexport { generateTypes } from './codegen/index.cjs';\nexport { generateTestMock } from './codegen/index.cjs';\nexport { generateControllerRegistration } from './codegen/index.cjs';\nexport type { ControllerRegistrationOptions } from './codegen/index.cjs';\nexport { generateRuntime } from './codegen/index.cjs';\nexport type { RuntimeGeneratorOptions } from './codegen/index.cjs';\nexport { generateRendererViewManifest } from './codegen/index.cjs';\nexport type { RendererViewManifestGeneratorOptions } from './codegen/index.cjs';\nexport { generateRendererControllerRegistration } from './codegen/index.cjs';\nexport type { RendererControllerRegistrationOptions } from './codegen/index.cjs';\nexport { generateRendererApp } from './codegen/index.cjs';\nexport type { RendererAppGeneratorOptions } from './codegen/index.cjs';\n\n// Window\nexport { WindowManager, WindowStateStore } from './window/index.cjs';\nexport type { ScreenLike, WindowBounds } from './window/index.cjs';\n\n// Loader\nexport { LoaderRunner } from './loader/index.cjs';\n\n// Bridge\nexport { BackendBridge, startBackendLoop, pushEvent } from './bridge/index.cjs';\n\n// Dev\nexport {\n WoodDevServer,\n BackendWatcher,\n YamlWatcher,\n DEFAULT_WOOD_REMOTE_DEBUGGING_PORT,\n WOOD_DEV_DIRECTORY,\n WOOD_DEV_METADATA_BASENAME,\n resolveWoodRemoteDebuggingPort,\n resolveWoodDevDirectory,\n resolveWoodDevMetadataPath,\n writeWoodDevMetadata,\n readWoodDevMetadata,\n removeWoodDevMetadata,\n} from './dev/index.js';\nexport type { WoodDevMetadata } from './dev/index.js';\n\n// Runtime\nexport {\n getContainer,\n clearContainerRegistryForTests,\n getApplicationRoot,\n setApplicationRoot,\n clearApplicationRootForTests,\n runtime,\n} from './runtime/index.js';\nexport type {\n GetContainerOptions,\n RuntimeOptions,\n RuntimeResult,\n RegisterRuntimeRouter,\n} from './runtime';\n\n// Navigation\nexport { Navigation, getNavigation, go, goto, goback } from './navigation/index.cjs';\nexport type { NavigationEntry } from './navigation/index.cjs';\n\n// Isomorphic IPC API\nexport { Ipc, getIpc } from './ipc/index.cjs';\nexport type { IpcPushOptions } from './ipc/index.cjs';\n\n// Tracing\nexport {\n FrameworkTracer,\n MainTracer,\n RendererTracer,\n TestTracer,\n TraceProvider,\n TraceProviderConfiguration,\n TraceCapture,\n clearTraceHubForTests,\n createTraceCapture,\n getTraceEvents$,\n setRendererTraceEnabled,\n setRendererTraceTransport,\n} from './tracing/index.js';\nexport type {\n TraceCaptureOptions,\n TraceDispatchResult,\n TraceEvent,\n TraceInput,\n TraceLevel,\n TraceProcess,\n TraceProviderMode,\n TraceRuntime,\n TraceWaitOptions,\n} from './tracing/index.js';\n\n// Testing — available via '@noego/wood/testing' subpath export.\n// Not re-exported here to avoid pulling vitest into production builds.\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoCA,mBAA2E;AAG3E,oBAA4B;AAG5B,oBAAqD;AACrD,IAAAA,iBAA2C;AAG3C,oBAA2B;AAG3B,wBAAgC;AAGhC,wBAA8E;AAG9E,wBAAoF;AAIpF,qBAAgC;AAChC,IAAAC,kBAA8B;AAC9B,IAAAA,kBAAiC;AACjC,IAAAA,kBAA+C;AAE/C,IAAAA,kBAAgC;AAEhC,IAAAA,kBAA6C;AAE7C,IAAAA,kBAAuD;AAEvD,IAAAA,kBAAoC;AAIpC,oBAAgD;AAIhD,oBAA6B;AAG7B,oBAA2D;AAG3D,iBAaO;AAIP,qBAOO;AASP,wBAA4D;AAI5D,iBAA4B;AAI5B,qBAaO;","names":["import_parser","import_codegen"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @noego/wood — Declarative Electron desktop apps using the noego ecosystem\n\n// Types\nexport type {\n WoodConfig,\n WindowDef,\n DevConfig,\n ElectronBuilderConfig,\n OperationDef,\n RawOperationsDocument,\n RawModuleDef,\n RawOperationEntry,\n RawModuleOperationEntry,\n ViewDef,\n RawViewsDocument,\n RawViewEntry,\n ParameterDef,\n IpcContext,\n WoodMiddlewareContext,\n WoodMiddlewareFn,\n RequestData,\n ControllerArgs,\n LoaderResult,\n BridgeMessage,\n BridgeRequest,\n BridgeResponse,\n BridgeReady,\n BridgePush,\n BridgeElectronApi,\n SerializedError,\n ContextMenuContext,\n ContextMenuItem,\n ContextMenuEvent,\n} from './types';\n\n// Errors\nexport { WoodAuthError, WoodValidationError, BackendRestartingError } from './types/index.cjs';\n\n// Config\nexport { parseConfig } from './config/index.cjs';\n\n// Parsers\nexport { parseOperations, parseOperationsFile } from './parser/index.cjs';\nexport { parseViews, parseViewsFile } from './parser/index.cjs';\n\n// Router\nexport { WoodRouter } from './router/index.cjs';\n\n// Validation\nexport { SchemaValidator } from './validation/index.cjs';\n\n// Middleware\nexport { MiddlewareResolver, buildMiddlewareContext, executeMiddleware } from './middleware/index.cjs';\n\n// Controller\nexport { ControllerResolver, buildControllerArgs, disposeControllerLifecycle } from './controller/index.cjs';\nexport type { ControllerLifecycle } from './controller/index.cjs';\n\n// Codegen\nexport { generatePreload } from './codegen/index.cjs';\nexport { generateTypes } from './codegen/index.cjs';\nexport { generateTestMock } from './codegen/index.cjs';\nexport { generateControllerRegistration } from './codegen/index.cjs';\nexport type { ControllerRegistrationOptions } from './codegen/index.cjs';\nexport { generateRuntime } from './codegen/index.cjs';\nexport type { RuntimeGeneratorOptions } from './codegen/index.cjs';\nexport { generateRendererViewManifest } from './codegen/index.cjs';\nexport type { RendererViewManifestGeneratorOptions } from './codegen/index.cjs';\nexport { generateRendererControllerRegistration } from './codegen/index.cjs';\nexport type { RendererControllerRegistrationOptions } from './codegen/index.cjs';\nexport { generateRendererApp } from './codegen/index.cjs';\nexport type { RendererAppGeneratorOptions } from './codegen/index.cjs';\n\n// Window\nexport { WindowManager, WindowStateStore } from './window/index.cjs';\nexport type { ScreenLike, WindowBounds } from './window/index.cjs';\n\n// Loader\nexport { LoaderRunner } from './loader/index.cjs';\n\n// Bridge\nexport { BackendBridge, startBackendLoop, pushEvent } from './bridge/index.cjs';\n\n// Dev\nexport {\n WoodDevServer,\n BackendWatcher,\n YamlWatcher,\n DEFAULT_WOOD_REMOTE_DEBUGGING_PORT,\n WOOD_DEV_DIRECTORY,\n WOOD_DEV_METADATA_BASENAME,\n resolveWoodRemoteDebuggingPort,\n resolveWoodDevDirectory,\n resolveWoodDevMetadataPath,\n writeWoodDevMetadata,\n readWoodDevMetadata,\n removeWoodDevMetadata,\n} from './dev/index.js';\nexport type { WoodDevMetadata } from './dev/index.js';\n\n// Runtime\nexport {\n getContainer,\n clearContainerRegistryForTests,\n getApplicationRoot,\n setApplicationRoot,\n clearApplicationRootForTests,\n runtime,\n} from './runtime/index.js';\nexport type {\n GetContainerOptions,\n RuntimeOptions,\n RuntimeResult,\n RegisterRuntimeRouter,\n} from './runtime';\n\n// Navigation\nexport { Navigation, getNavigation, go, goto, goback } from './navigation/index.cjs';\nexport type { NavigationEntry, NavigationOptions } from './navigation/index.cjs';\n\n// Isomorphic IPC API\nexport { Ipc, getIpc } from './ipc/index.cjs';\nexport type { IpcPushOptions } from './ipc/index.cjs';\n\n// Tracing\nexport {\n FrameworkTracer,\n MainTracer,\n RendererTracer,\n TestTracer,\n TraceProvider,\n TraceProviderConfiguration,\n TraceCapture,\n clearTraceHubForTests,\n createTraceCapture,\n getTraceEvents$,\n setRendererTraceEnabled,\n setRendererTraceTransport,\n} from './tracing/index.js';\nexport type {\n TraceCaptureOptions,\n TraceDispatchResult,\n TraceEvent,\n TraceInput,\n TraceLevel,\n TraceProcess,\n TraceProviderMode,\n TraceRuntime,\n TraceWaitOptions,\n} from './tracing/index.js';\n\n// Testing — available via '@noego/wood/testing' subpath export.\n// Not re-exported here to avoid pulling vitest into production builds.\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoCA,mBAA2E;AAG3E,oBAA4B;AAG5B,oBAAqD;AACrD,IAAAA,iBAA2C;AAG3C,oBAA2B;AAG3B,wBAAgC;AAGhC,wBAA8E;AAG9E,wBAAoF;AAIpF,qBAAgC;AAChC,IAAAC,kBAA8B;AAC9B,IAAAA,kBAAiC;AACjC,IAAAA,kBAA+C;AAE/C,IAAAA,kBAAgC;AAEhC,IAAAA,kBAA6C;AAE7C,IAAAA,kBAAuD;AAEvD,IAAAA,kBAAoC;AAIpC,oBAAgD;AAIhD,oBAA6B;AAG7B,oBAA2D;AAG3D,iBAaO;AAIP,qBAOO;AASP,wBAA4D;AAI5D,iBAA4B;AAI5B,qBAaO;","names":["import_parser","import_codegen"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -35,7 +35,7 @@ export { clearApplicationRootForTests, getApplicationRoot, setApplicationRoot }
|
|
|
35
35
|
export { RegisterRuntimeRouter, RuntimeOptions, RuntimeResult, runtime } from './runtime/runtime.cjs';
|
|
36
36
|
export { TraceCapture, TraceCaptureOptions, TraceDispatchResult, TraceEvent, TraceInput, TraceLevel, TraceProcess, TraceWaitOptions, clearTraceHubForTests, createTraceCapture, getTraceEvents$, setRendererTraceEnabled, setRendererTraceTransport } from './tracing/trace_hub.cjs';
|
|
37
37
|
export { FrameworkTracer, MainTracer, RendererTracer, TestTracer, TraceProvider, TraceProviderConfiguration, TraceProviderMode, TraceRuntime } from './tracing/trace_provider.cjs';
|
|
38
|
-
export { Navigation, NavigationEntry, getNavigation, go, goback, goto } from './navigation/index.cjs';
|
|
38
|
+
export { Navigation, NavigationEntry, NavigationOptions, getNavigation, go, goback, goto } from './navigation/index.cjs';
|
|
39
39
|
export { Ipc, getIpc } from './ipc/index.cjs';
|
|
40
40
|
export { IpcPushOptions } from './ipc/shared.cjs';
|
|
41
41
|
import 'rxjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export { clearApplicationRootForTests, getApplicationRoot, setApplicationRoot }
|
|
|
35
35
|
export { RegisterRuntimeRouter, RuntimeOptions, RuntimeResult, runtime } from './runtime/runtime.js';
|
|
36
36
|
export { TraceCapture, TraceCaptureOptions, TraceDispatchResult, TraceEvent, TraceInput, TraceLevel, TraceProcess, TraceWaitOptions, clearTraceHubForTests, createTraceCapture, getTraceEvents$, setRendererTraceEnabled, setRendererTraceTransport } from './tracing/trace_hub.js';
|
|
37
37
|
export { FrameworkTracer, MainTracer, RendererTracer, TestTracer, TraceProvider, TraceProviderConfiguration, TraceProviderMode, TraceRuntime } from './tracing/trace_provider.js';
|
|
38
|
-
export { Navigation, NavigationEntry, getNavigation, go, goback, goto } from './navigation/index.js';
|
|
38
|
+
export { Navigation, NavigationEntry, NavigationOptions, getNavigation, go, goback, goto } from './navigation/index.js';
|
|
39
39
|
export { Ipc, getIpc } from './ipc/index.js';
|
|
40
40
|
export { IpcPushOptions } from './ipc/shared.js';
|
|
41
41
|
import 'rxjs';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @noego/wood — Declarative Electron desktop apps using the noego ecosystem\n\n// Types\nexport type {\n WoodConfig,\n WindowDef,\n DevConfig,\n ElectronBuilderConfig,\n OperationDef,\n RawOperationsDocument,\n RawModuleDef,\n RawOperationEntry,\n RawModuleOperationEntry,\n ViewDef,\n RawViewsDocument,\n RawViewEntry,\n ParameterDef,\n IpcContext,\n WoodMiddlewareContext,\n WoodMiddlewareFn,\n RequestData,\n ControllerArgs,\n LoaderResult,\n BridgeMessage,\n BridgeRequest,\n BridgeResponse,\n BridgeReady,\n BridgePush,\n BridgeElectronApi,\n SerializedError,\n ContextMenuContext,\n ContextMenuItem,\n ContextMenuEvent,\n} from './types';\n\n// Errors\nexport { WoodAuthError, WoodValidationError, BackendRestartingError } from './types/index.js';\n\n// Config\nexport { parseConfig } from './config/index.js';\n\n// Parsers\nexport { parseOperations, parseOperationsFile } from './parser/index.js';\nexport { parseViews, parseViewsFile } from './parser/index.js';\n\n// Router\nexport { WoodRouter } from './router/index.js';\n\n// Validation\nexport { SchemaValidator } from './validation/index.js';\n\n// Middleware\nexport { MiddlewareResolver, buildMiddlewareContext, executeMiddleware } from './middleware/index.js';\n\n// Controller\nexport { ControllerResolver, buildControllerArgs, disposeControllerLifecycle } from './controller/index.js';\nexport type { ControllerLifecycle } from './controller/index.js';\n\n// Codegen\nexport { generatePreload } from './codegen/index.js';\nexport { generateTypes } from './codegen/index.js';\nexport { generateTestMock } from './codegen/index.js';\nexport { generateControllerRegistration } from './codegen/index.js';\nexport type { ControllerRegistrationOptions } from './codegen/index.js';\nexport { generateRuntime } from './codegen/index.js';\nexport type { RuntimeGeneratorOptions } from './codegen/index.js';\nexport { generateRendererViewManifest } from './codegen/index.js';\nexport type { RendererViewManifestGeneratorOptions } from './codegen/index.js';\nexport { generateRendererControllerRegistration } from './codegen/index.js';\nexport type { RendererControllerRegistrationOptions } from './codegen/index.js';\nexport { generateRendererApp } from './codegen/index.js';\nexport type { RendererAppGeneratorOptions } from './codegen/index.js';\n\n// Window\nexport { WindowManager, WindowStateStore } from './window/index.js';\nexport type { ScreenLike, WindowBounds } from './window/index.js';\n\n// Loader\nexport { LoaderRunner } from './loader/index.js';\n\n// Bridge\nexport { BackendBridge, startBackendLoop, pushEvent } from './bridge/index.js';\n\n// Dev\nexport {\n WoodDevServer,\n BackendWatcher,\n YamlWatcher,\n DEFAULT_WOOD_REMOTE_DEBUGGING_PORT,\n WOOD_DEV_DIRECTORY,\n WOOD_DEV_METADATA_BASENAME,\n resolveWoodRemoteDebuggingPort,\n resolveWoodDevDirectory,\n resolveWoodDevMetadataPath,\n writeWoodDevMetadata,\n readWoodDevMetadata,\n removeWoodDevMetadata,\n} from './dev/index.js';\nexport type { WoodDevMetadata } from './dev/index.js';\n\n// Runtime\nexport {\n getContainer,\n clearContainerRegistryForTests,\n getApplicationRoot,\n setApplicationRoot,\n clearApplicationRootForTests,\n runtime,\n} from './runtime/index.js';\nexport type {\n GetContainerOptions,\n RuntimeOptions,\n RuntimeResult,\n RegisterRuntimeRouter,\n} from './runtime';\n\n// Navigation\nexport { Navigation, getNavigation, go, goto, goback } from './navigation/index.js';\nexport type { NavigationEntry } from './navigation/index.js';\n\n// Isomorphic IPC API\nexport { Ipc, getIpc } from './ipc/index.js';\nexport type { IpcPushOptions } from './ipc/index.js';\n\n// Tracing\nexport {\n FrameworkTracer,\n MainTracer,\n RendererTracer,\n TestTracer,\n TraceProvider,\n TraceProviderConfiguration,\n TraceCapture,\n clearTraceHubForTests,\n createTraceCapture,\n getTraceEvents$,\n setRendererTraceEnabled,\n setRendererTraceTransport,\n} from './tracing/index.js';\nexport type {\n TraceCaptureOptions,\n TraceDispatchResult,\n TraceEvent,\n TraceInput,\n TraceLevel,\n TraceProcess,\n TraceProviderMode,\n TraceRuntime,\n TraceWaitOptions,\n} from './tracing/index.js';\n\n// Testing — available via '@noego/wood/testing' subpath export.\n// Not re-exported here to avoid pulling vitest into production builds.\n"],"mappings":"AAoCA,SAAS,eAAe,qBAAqB,8BAA8B;AAG3E,SAAS,mBAAmB;AAG5B,SAAS,iBAAiB,2BAA2B;AACrD,SAAS,YAAY,sBAAsB;AAG3C,SAAS,kBAAkB;AAG3B,SAAS,uBAAuB;AAGhC,SAAS,oBAAoB,wBAAwB,yBAAyB;AAG9E,SAAS,oBAAoB,qBAAqB,kCAAkC;AAIpF,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,wBAAwB;AACjC,SAAS,sCAAsC;AAE/C,SAAS,uBAAuB;AAEhC,SAAS,oCAAoC;AAE7C,SAAS,8CAA8C;AAEvD,SAAS,2BAA2B;AAIpC,SAAS,eAAe,wBAAwB;AAIhD,SAAS,oBAAoB;AAG7B,SAAS,eAAe,kBAAkB,iBAAiB;AAG3D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AASP,SAAS,YAAY,eAAe,IAAI,MAAM,cAAc;AAI5D,SAAS,KAAK,cAAc;AAI5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @noego/wood — Declarative Electron desktop apps using the noego ecosystem\n\n// Types\nexport type {\n WoodConfig,\n WindowDef,\n DevConfig,\n ElectronBuilderConfig,\n OperationDef,\n RawOperationsDocument,\n RawModuleDef,\n RawOperationEntry,\n RawModuleOperationEntry,\n ViewDef,\n RawViewsDocument,\n RawViewEntry,\n ParameterDef,\n IpcContext,\n WoodMiddlewareContext,\n WoodMiddlewareFn,\n RequestData,\n ControllerArgs,\n LoaderResult,\n BridgeMessage,\n BridgeRequest,\n BridgeResponse,\n BridgeReady,\n BridgePush,\n BridgeElectronApi,\n SerializedError,\n ContextMenuContext,\n ContextMenuItem,\n ContextMenuEvent,\n} from './types';\n\n// Errors\nexport { WoodAuthError, WoodValidationError, BackendRestartingError } from './types/index.js';\n\n// Config\nexport { parseConfig } from './config/index.js';\n\n// Parsers\nexport { parseOperations, parseOperationsFile } from './parser/index.js';\nexport { parseViews, parseViewsFile } from './parser/index.js';\n\n// Router\nexport { WoodRouter } from './router/index.js';\n\n// Validation\nexport { SchemaValidator } from './validation/index.js';\n\n// Middleware\nexport { MiddlewareResolver, buildMiddlewareContext, executeMiddleware } from './middleware/index.js';\n\n// Controller\nexport { ControllerResolver, buildControllerArgs, disposeControllerLifecycle } from './controller/index.js';\nexport type { ControllerLifecycle } from './controller/index.js';\n\n// Codegen\nexport { generatePreload } from './codegen/index.js';\nexport { generateTypes } from './codegen/index.js';\nexport { generateTestMock } from './codegen/index.js';\nexport { generateControllerRegistration } from './codegen/index.js';\nexport type { ControllerRegistrationOptions } from './codegen/index.js';\nexport { generateRuntime } from './codegen/index.js';\nexport type { RuntimeGeneratorOptions } from './codegen/index.js';\nexport { generateRendererViewManifest } from './codegen/index.js';\nexport type { RendererViewManifestGeneratorOptions } from './codegen/index.js';\nexport { generateRendererControllerRegistration } from './codegen/index.js';\nexport type { RendererControllerRegistrationOptions } from './codegen/index.js';\nexport { generateRendererApp } from './codegen/index.js';\nexport type { RendererAppGeneratorOptions } from './codegen/index.js';\n\n// Window\nexport { WindowManager, WindowStateStore } from './window/index.js';\nexport type { ScreenLike, WindowBounds } from './window/index.js';\n\n// Loader\nexport { LoaderRunner } from './loader/index.js';\n\n// Bridge\nexport { BackendBridge, startBackendLoop, pushEvent } from './bridge/index.js';\n\n// Dev\nexport {\n WoodDevServer,\n BackendWatcher,\n YamlWatcher,\n DEFAULT_WOOD_REMOTE_DEBUGGING_PORT,\n WOOD_DEV_DIRECTORY,\n WOOD_DEV_METADATA_BASENAME,\n resolveWoodRemoteDebuggingPort,\n resolveWoodDevDirectory,\n resolveWoodDevMetadataPath,\n writeWoodDevMetadata,\n readWoodDevMetadata,\n removeWoodDevMetadata,\n} from './dev/index.js';\nexport type { WoodDevMetadata } from './dev/index.js';\n\n// Runtime\nexport {\n getContainer,\n clearContainerRegistryForTests,\n getApplicationRoot,\n setApplicationRoot,\n clearApplicationRootForTests,\n runtime,\n} from './runtime/index.js';\nexport type {\n GetContainerOptions,\n RuntimeOptions,\n RuntimeResult,\n RegisterRuntimeRouter,\n} from './runtime';\n\n// Navigation\nexport { Navigation, getNavigation, go, goto, goback } from './navigation/index.js';\nexport type { NavigationEntry, NavigationOptions } from './navigation/index.js';\n\n// Isomorphic IPC API\nexport { Ipc, getIpc } from './ipc/index.js';\nexport type { IpcPushOptions } from './ipc/index.js';\n\n// Tracing\nexport {\n FrameworkTracer,\n MainTracer,\n RendererTracer,\n TestTracer,\n TraceProvider,\n TraceProviderConfiguration,\n TraceCapture,\n clearTraceHubForTests,\n createTraceCapture,\n getTraceEvents$,\n setRendererTraceEnabled,\n setRendererTraceTransport,\n} from './tracing/index.js';\nexport type {\n TraceCaptureOptions,\n TraceDispatchResult,\n TraceEvent,\n TraceInput,\n TraceLevel,\n TraceProcess,\n TraceProviderMode,\n TraceRuntime,\n TraceWaitOptions,\n} from './tracing/index.js';\n\n// Testing — available via '@noego/wood/testing' subpath export.\n// Not re-exported here to avoid pulling vitest into production builds.\n"],"mappings":"AAoCA,SAAS,eAAe,qBAAqB,8BAA8B;AAG3E,SAAS,mBAAmB;AAG5B,SAAS,iBAAiB,2BAA2B;AACrD,SAAS,YAAY,sBAAsB;AAG3C,SAAS,kBAAkB;AAG3B,SAAS,uBAAuB;AAGhC,SAAS,oBAAoB,wBAAwB,yBAAyB;AAG9E,SAAS,oBAAoB,qBAAqB,kCAAkC;AAIpF,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,wBAAwB;AACjC,SAAS,sCAAsC;AAE/C,SAAS,uBAAuB;AAEhC,SAAS,oCAAoC;AAE7C,SAAS,8CAA8C;AAEvD,SAAS,2BAA2B;AAIpC,SAAS,eAAe,wBAAwB;AAIhD,SAAS,oBAAoB;AAG7B,SAAS,eAAe,kBAAkB,iBAAiB;AAG3D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AASP,SAAS,YAAY,eAAe,IAAI,MAAM,cAAc;AAI5D,SAAS,KAAK,cAAc;AAI5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":[]}
|
|
@@ -51,8 +51,8 @@ class Navigation {
|
|
|
51
51
|
}
|
|
52
52
|
return { ...current.params };
|
|
53
53
|
}
|
|
54
|
-
replace(page, params = {}, query = {}) {
|
|
55
|
-
const entry = this.createEntry(page, params, query);
|
|
54
|
+
replace(page, params = {}, query = {}, options = {}) {
|
|
55
|
+
const entry = this.createEntry(page, params, query, options);
|
|
56
56
|
if (this.index < 0) {
|
|
57
57
|
this.entries.push(entry);
|
|
58
58
|
this.index = 0;
|
|
@@ -67,13 +67,14 @@ class Navigation {
|
|
|
67
67
|
payload: {
|
|
68
68
|
page: entry.page,
|
|
69
69
|
params: entry.params,
|
|
70
|
-
query: entry.query
|
|
70
|
+
query: entry.query,
|
|
71
|
+
options: entry.options
|
|
71
72
|
}
|
|
72
73
|
});
|
|
73
74
|
return this.cloneEntry(entry);
|
|
74
75
|
}
|
|
75
|
-
go(page, params = {}, query = {}) {
|
|
76
|
-
const entry = this.createEntry(page, params, query);
|
|
76
|
+
go(page, params = {}, query = {}, options = {}) {
|
|
77
|
+
const entry = this.createEntry(page, params, query, options);
|
|
77
78
|
if (this.index < this.entries.length - 1) {
|
|
78
79
|
this.entries = this.entries.slice(0, this.index + 1);
|
|
79
80
|
}
|
|
@@ -87,7 +88,8 @@ class Navigation {
|
|
|
87
88
|
payload: {
|
|
88
89
|
page: entry.page,
|
|
89
90
|
params: entry.params,
|
|
90
|
-
query: entry.query
|
|
91
|
+
query: entry.query,
|
|
92
|
+
options: entry.options
|
|
91
93
|
}
|
|
92
94
|
});
|
|
93
95
|
return this.cloneEntry(entry);
|
|
@@ -136,18 +138,20 @@ class Navigation {
|
|
|
136
138
|
listener(current);
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
|
-
createEntry(page, params, query) {
|
|
141
|
+
createEntry(page, params, query, options) {
|
|
140
142
|
return {
|
|
141
143
|
page,
|
|
142
144
|
params: this.normalizeRecord(params),
|
|
143
|
-
query: this.normalizeRecord(query)
|
|
145
|
+
query: this.normalizeRecord(query),
|
|
146
|
+
options: { ...options }
|
|
144
147
|
};
|
|
145
148
|
}
|
|
146
149
|
cloneEntry(entry) {
|
|
147
150
|
return {
|
|
148
151
|
page: entry.page,
|
|
149
152
|
params: { ...entry.params },
|
|
150
|
-
query: { ...entry.query }
|
|
153
|
+
query: { ...entry.query },
|
|
154
|
+
options: { ...entry.options }
|
|
151
155
|
};
|
|
152
156
|
}
|
|
153
157
|
normalizeRecord(input) {
|
|
@@ -165,7 +169,8 @@ class Navigation {
|
|
|
165
169
|
[HISTORY_STATE_MARKER]: true,
|
|
166
170
|
page: entry.page,
|
|
167
171
|
params: { ...entry.params },
|
|
168
|
-
query: { ...entry.query }
|
|
172
|
+
query: { ...entry.query },
|
|
173
|
+
options: { ...entry.options }
|
|
169
174
|
};
|
|
170
175
|
window.history[mode](state, "");
|
|
171
176
|
}
|
|
@@ -183,8 +188,8 @@ function getNavigation() {
|
|
|
183
188
|
function goto(delta) {
|
|
184
189
|
return getNavigation().goto(delta);
|
|
185
190
|
}
|
|
186
|
-
function go(page, params = {}, query = {}) {
|
|
187
|
-
return getNavigation().go(page, params, query);
|
|
191
|
+
function go(page, params = {}, query = {}, options = {}) {
|
|
192
|
+
return getNavigation().go(page, params, query, options);
|
|
188
193
|
}
|
|
189
194
|
function goback() {
|
|
190
195
|
return goto(-1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/navigation/index.ts"],"sourcesContent":["import { emitRendererTrace } from '../tracing/index.js';\n\nconst NAVIGATION_SYMBOL = Symbol.for('@noego/wood.navigation');\nconst HISTORY_STATE_MARKER = '__wood_navigation_state__';\n\nexport interface NavigationEntry {\n page: string;\n params: Record<string, string>;\n query: Record<string, string>;\n}\n\ntype NavigationHistoryState = NavigationEntry & {\n [HISTORY_STATE_MARKER]: true;\n};\n\ntype NavigationListener = (entry: NavigationEntry | null) => void;\n\nexport class Navigation {\n private entries: NavigationEntry[] = [];\n private index = -1;\n private listeners = new Set<NavigationListener>();\n\n getCurrent(): NavigationEntry | null {\n if (this.index < 0 || this.index >= this.entries.length) {\n return null;\n }\n return this.cloneEntry(this.entries[this.index]);\n }\n\n getPages(): NavigationEntry[] {\n return this.entries.map((entry) => this.cloneEntry(entry));\n }\n\n getParams(): Record<string, string> {\n const current = this.getCurrent();\n if (!current) {\n return {};\n }\n return { ...current.params };\n }\n\n replace(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query);\n\n if (this.index < 0) {\n this.entries.push(entry);\n this.index = 0;\n } else {\n this.entries[this.index] = entry;\n }\n\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.replace',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n },\n });\n return this.cloneEntry(entry);\n }\n\n go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query);\n if (this.index < this.entries.length - 1) {\n this.entries = this.entries.slice(0, this.index + 1);\n }\n this.entries.push(entry);\n this.index = this.entries.length - 1;\n\n this.writeWindowHistory('pushState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.go',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n },\n });\n return this.cloneEntry(entry);\n }\n\n goto(delta: number): NavigationEntry | null {\n return this.moveCursor(delta);\n }\n\n goBack(): NavigationEntry | null {\n return this.goto(-1);\n }\n\n goForward(): NavigationEntry | null {\n return this.goto(1);\n }\n\n subscribe(listener: NavigationListener): () => void {\n this.listeners.add(listener);\n listener(this.getCurrent());\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private moveCursor(delta: number): NavigationEntry | null {\n if (delta === 0) {\n return this.getCurrent();\n }\n\n const nextIndex = this.index + delta;\n if (nextIndex < 0 || nextIndex >= this.entries.length) {\n return this.getCurrent();\n }\n\n this.index = nextIndex;\n const entry = this.entries[this.index];\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.goto',\n payload: {\n delta,\n page: entry.page,\n },\n });\n return this.cloneEntry(entry);\n }\n\n private notify(): void {\n const current = this.getCurrent();\n for (const listener of this.listeners) {\n listener(current);\n }\n }\n\n private createEntry(\n page: string,\n params: Record<string, string>,\n query: Record<string, string>,\n ): NavigationEntry {\n return {\n page,\n params: this.normalizeRecord(params),\n query: this.normalizeRecord(query),\n };\n }\n\n private cloneEntry(entry: NavigationEntry): NavigationEntry {\n return {\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n };\n }\n\n private normalizeRecord(input: Record<string, string>): Record<string, string> {\n const output: Record<string, string> = {};\n for (const [key, value] of Object.entries(input ?? {})) {\n output[String(key)] = String(value);\n }\n return output;\n }\n\n private writeWindowHistory(\n mode: 'pushState' | 'replaceState',\n entry: NavigationEntry,\n ): void {\n if (typeof window === 'undefined') {\n return;\n }\n\n const state: NavigationHistoryState = {\n [HISTORY_STATE_MARKER]: true,\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n };\n\n window.history[mode](state, '');\n }\n}\n\ntype GlobalWithNavigation = typeof globalThis & {\n [NAVIGATION_SYMBOL]?: Navigation;\n};\n\nfunction createNavigationInstance(): Navigation {\n return new Navigation();\n}\n\nexport function getNavigation(): Navigation {\n const globalObj = globalThis as GlobalWithNavigation;\n if (!globalObj[NAVIGATION_SYMBOL]) {\n globalObj[NAVIGATION_SYMBOL] = createNavigationInstance();\n }\n return globalObj[NAVIGATION_SYMBOL];\n}\n\nexport function goto(\n delta: number,\n): NavigationEntry | null {\n return getNavigation().goto(delta);\n}\n\nexport function go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n): NavigationEntry {\n return getNavigation().go(page, params, query);\n}\n\nexport function goback(): NavigationEntry | null {\n return goto(-1);\n}\n\n/** Test-only helper to clear the navigation singleton. */\nexport function clearNavigationForTests(): void {\n const globalObj = globalThis as GlobalWithNavigation;\n delete globalObj[NAVIGATION_SYMBOL];\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/navigation/index.ts"],"sourcesContent":["import { emitRendererTrace } from '../tracing/index.js';\n\nconst NAVIGATION_SYMBOL = Symbol.for('@noego/wood.navigation');\nconst HISTORY_STATE_MARKER = '__wood_navigation_state__';\n\nexport interface NavigationEntry {\n page: string;\n params: Record<string, string>;\n query: Record<string, string>;\n options: NavigationOptions;\n}\n\nexport interface NavigationOptions {\n preserveLayouts?: boolean;\n}\n\ntype NavigationHistoryState = NavigationEntry & {\n [HISTORY_STATE_MARKER]: true;\n};\n\ntype NavigationListener = (entry: NavigationEntry | null) => void;\n\nexport class Navigation {\n private entries: NavigationEntry[] = [];\n private index = -1;\n private listeners = new Set<NavigationListener>();\n\n getCurrent(): NavigationEntry | null {\n if (this.index < 0 || this.index >= this.entries.length) {\n return null;\n }\n return this.cloneEntry(this.entries[this.index]);\n }\n\n getPages(): NavigationEntry[] {\n return this.entries.map((entry) => this.cloneEntry(entry));\n }\n\n getParams(): Record<string, string> {\n const current = this.getCurrent();\n if (!current) {\n return {};\n }\n return { ...current.params };\n }\n\n replace(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query, options);\n\n if (this.index < 0) {\n this.entries.push(entry);\n this.index = 0;\n } else {\n this.entries[this.index] = entry;\n }\n\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.replace',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n options: entry.options,\n },\n });\n return this.cloneEntry(entry);\n }\n\n go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query, options);\n if (this.index < this.entries.length - 1) {\n this.entries = this.entries.slice(0, this.index + 1);\n }\n this.entries.push(entry);\n this.index = this.entries.length - 1;\n\n this.writeWindowHistory('pushState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.go',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n options: entry.options,\n },\n });\n return this.cloneEntry(entry);\n }\n\n goto(delta: number): NavigationEntry | null {\n return this.moveCursor(delta);\n }\n\n goBack(): NavigationEntry | null {\n return this.goto(-1);\n }\n\n goForward(): NavigationEntry | null {\n return this.goto(1);\n }\n\n subscribe(listener: NavigationListener): () => void {\n this.listeners.add(listener);\n listener(this.getCurrent());\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private moveCursor(delta: number): NavigationEntry | null {\n if (delta === 0) {\n return this.getCurrent();\n }\n\n const nextIndex = this.index + delta;\n if (nextIndex < 0 || nextIndex >= this.entries.length) {\n return this.getCurrent();\n }\n\n this.index = nextIndex;\n const entry = this.entries[this.index];\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.goto',\n payload: {\n delta,\n page: entry.page,\n },\n });\n return this.cloneEntry(entry);\n }\n\n private notify(): void {\n const current = this.getCurrent();\n for (const listener of this.listeners) {\n listener(current);\n }\n }\n\n private createEntry(\n page: string,\n params: Record<string, string>,\n query: Record<string, string>,\n options: NavigationOptions,\n ): NavigationEntry {\n return {\n page,\n params: this.normalizeRecord(params),\n query: this.normalizeRecord(query),\n options: { ...options },\n };\n }\n\n private cloneEntry(entry: NavigationEntry): NavigationEntry {\n return {\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n options: { ...entry.options },\n };\n }\n\n private normalizeRecord(input: Record<string, string>): Record<string, string> {\n const output: Record<string, string> = {};\n for (const [key, value] of Object.entries(input ?? {})) {\n output[String(key)] = String(value);\n }\n return output;\n }\n\n private writeWindowHistory(\n mode: 'pushState' | 'replaceState',\n entry: NavigationEntry,\n ): void {\n if (typeof window === 'undefined') {\n return;\n }\n\n const state: NavigationHistoryState = {\n [HISTORY_STATE_MARKER]: true,\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n options: { ...entry.options },\n };\n\n window.history[mode](state, '');\n }\n}\n\ntype GlobalWithNavigation = typeof globalThis & {\n [NAVIGATION_SYMBOL]?: Navigation;\n};\n\nfunction createNavigationInstance(): Navigation {\n return new Navigation();\n}\n\nexport function getNavigation(): Navigation {\n const globalObj = globalThis as GlobalWithNavigation;\n if (!globalObj[NAVIGATION_SYMBOL]) {\n globalObj[NAVIGATION_SYMBOL] = createNavigationInstance();\n }\n return globalObj[NAVIGATION_SYMBOL];\n}\n\nexport function goto(\n delta: number,\n): NavigationEntry | null {\n return getNavigation().goto(delta);\n}\n\nexport function go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n): NavigationEntry {\n return getNavigation().go(page, params, query, options);\n}\n\nexport function goback(): NavigationEntry | null {\n return goto(-1);\n}\n\n/** Test-only helper to clear the navigation singleton. */\nexport function clearNavigationForTests(): void {\n const globalObj = globalThis as GlobalWithNavigation;\n delete globalObj[NAVIGATION_SYMBOL];\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,6 +2,10 @@ interface NavigationEntry {
|
|
|
2
2
|
page: string;
|
|
3
3
|
params: Record<string, string>;
|
|
4
4
|
query: Record<string, string>;
|
|
5
|
+
options: NavigationOptions;
|
|
6
|
+
}
|
|
7
|
+
interface NavigationOptions {
|
|
8
|
+
preserveLayouts?: boolean;
|
|
5
9
|
}
|
|
6
10
|
type NavigationListener = (entry: NavigationEntry | null) => void;
|
|
7
11
|
declare class Navigation {
|
|
@@ -11,8 +15,8 @@ declare class Navigation {
|
|
|
11
15
|
getCurrent(): NavigationEntry | null;
|
|
12
16
|
getPages(): NavigationEntry[];
|
|
13
17
|
getParams(): Record<string, string>;
|
|
14
|
-
replace(page: string, params?: Record<string, string>, query?: Record<string, string
|
|
15
|
-
go(page: string, params?: Record<string, string>, query?: Record<string, string
|
|
18
|
+
replace(page: string, params?: Record<string, string>, query?: Record<string, string>, options?: NavigationOptions): NavigationEntry;
|
|
19
|
+
go(page: string, params?: Record<string, string>, query?: Record<string, string>, options?: NavigationOptions): NavigationEntry;
|
|
16
20
|
goto(delta: number): NavigationEntry | null;
|
|
17
21
|
goBack(): NavigationEntry | null;
|
|
18
22
|
goForward(): NavigationEntry | null;
|
|
@@ -26,9 +30,9 @@ declare class Navigation {
|
|
|
26
30
|
}
|
|
27
31
|
declare function getNavigation(): Navigation;
|
|
28
32
|
declare function goto(delta: number): NavigationEntry | null;
|
|
29
|
-
declare function go(page: string, params?: Record<string, string>, query?: Record<string, string
|
|
33
|
+
declare function go(page: string, params?: Record<string, string>, query?: Record<string, string>, options?: NavigationOptions): NavigationEntry;
|
|
30
34
|
declare function goback(): NavigationEntry | null;
|
|
31
35
|
/** Test-only helper to clear the navigation singleton. */
|
|
32
36
|
declare function clearNavigationForTests(): void;
|
|
33
37
|
|
|
34
|
-
export { Navigation, type NavigationEntry, clearNavigationForTests, getNavigation, go, goback, goto };
|
|
38
|
+
export { Navigation, type NavigationEntry, type NavigationOptions, clearNavigationForTests, getNavigation, go, goback, goto };
|
|
@@ -2,6 +2,10 @@ interface NavigationEntry {
|
|
|
2
2
|
page: string;
|
|
3
3
|
params: Record<string, string>;
|
|
4
4
|
query: Record<string, string>;
|
|
5
|
+
options: NavigationOptions;
|
|
6
|
+
}
|
|
7
|
+
interface NavigationOptions {
|
|
8
|
+
preserveLayouts?: boolean;
|
|
5
9
|
}
|
|
6
10
|
type NavigationListener = (entry: NavigationEntry | null) => void;
|
|
7
11
|
declare class Navigation {
|
|
@@ -11,8 +15,8 @@ declare class Navigation {
|
|
|
11
15
|
getCurrent(): NavigationEntry | null;
|
|
12
16
|
getPages(): NavigationEntry[];
|
|
13
17
|
getParams(): Record<string, string>;
|
|
14
|
-
replace(page: string, params?: Record<string, string>, query?: Record<string, string
|
|
15
|
-
go(page: string, params?: Record<string, string>, query?: Record<string, string
|
|
18
|
+
replace(page: string, params?: Record<string, string>, query?: Record<string, string>, options?: NavigationOptions): NavigationEntry;
|
|
19
|
+
go(page: string, params?: Record<string, string>, query?: Record<string, string>, options?: NavigationOptions): NavigationEntry;
|
|
16
20
|
goto(delta: number): NavigationEntry | null;
|
|
17
21
|
goBack(): NavigationEntry | null;
|
|
18
22
|
goForward(): NavigationEntry | null;
|
|
@@ -26,9 +30,9 @@ declare class Navigation {
|
|
|
26
30
|
}
|
|
27
31
|
declare function getNavigation(): Navigation;
|
|
28
32
|
declare function goto(delta: number): NavigationEntry | null;
|
|
29
|
-
declare function go(page: string, params?: Record<string, string>, query?: Record<string, string
|
|
33
|
+
declare function go(page: string, params?: Record<string, string>, query?: Record<string, string>, options?: NavigationOptions): NavigationEntry;
|
|
30
34
|
declare function goback(): NavigationEntry | null;
|
|
31
35
|
/** Test-only helper to clear the navigation singleton. */
|
|
32
36
|
declare function clearNavigationForTests(): void;
|
|
33
37
|
|
|
34
|
-
export { Navigation, type NavigationEntry, clearNavigationForTests, getNavigation, go, goback, goto };
|
|
38
|
+
export { Navigation, type NavigationEntry, type NavigationOptions, clearNavigationForTests, getNavigation, go, goback, goto };
|
package/dist/navigation/index.js
CHANGED
|
@@ -23,8 +23,8 @@ class Navigation {
|
|
|
23
23
|
}
|
|
24
24
|
return { ...current.params };
|
|
25
25
|
}
|
|
26
|
-
replace(page, params = {}, query = {}) {
|
|
27
|
-
const entry = this.createEntry(page, params, query);
|
|
26
|
+
replace(page, params = {}, query = {}, options = {}) {
|
|
27
|
+
const entry = this.createEntry(page, params, query, options);
|
|
28
28
|
if (this.index < 0) {
|
|
29
29
|
this.entries.push(entry);
|
|
30
30
|
this.index = 0;
|
|
@@ -39,13 +39,14 @@ class Navigation {
|
|
|
39
39
|
payload: {
|
|
40
40
|
page: entry.page,
|
|
41
41
|
params: entry.params,
|
|
42
|
-
query: entry.query
|
|
42
|
+
query: entry.query,
|
|
43
|
+
options: entry.options
|
|
43
44
|
}
|
|
44
45
|
});
|
|
45
46
|
return this.cloneEntry(entry);
|
|
46
47
|
}
|
|
47
|
-
go(page, params = {}, query = {}) {
|
|
48
|
-
const entry = this.createEntry(page, params, query);
|
|
48
|
+
go(page, params = {}, query = {}, options = {}) {
|
|
49
|
+
const entry = this.createEntry(page, params, query, options);
|
|
49
50
|
if (this.index < this.entries.length - 1) {
|
|
50
51
|
this.entries = this.entries.slice(0, this.index + 1);
|
|
51
52
|
}
|
|
@@ -59,7 +60,8 @@ class Navigation {
|
|
|
59
60
|
payload: {
|
|
60
61
|
page: entry.page,
|
|
61
62
|
params: entry.params,
|
|
62
|
-
query: entry.query
|
|
63
|
+
query: entry.query,
|
|
64
|
+
options: entry.options
|
|
63
65
|
}
|
|
64
66
|
});
|
|
65
67
|
return this.cloneEntry(entry);
|
|
@@ -108,18 +110,20 @@ class Navigation {
|
|
|
108
110
|
listener(current);
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
|
-
createEntry(page, params, query) {
|
|
113
|
+
createEntry(page, params, query, options) {
|
|
112
114
|
return {
|
|
113
115
|
page,
|
|
114
116
|
params: this.normalizeRecord(params),
|
|
115
|
-
query: this.normalizeRecord(query)
|
|
117
|
+
query: this.normalizeRecord(query),
|
|
118
|
+
options: { ...options }
|
|
116
119
|
};
|
|
117
120
|
}
|
|
118
121
|
cloneEntry(entry) {
|
|
119
122
|
return {
|
|
120
123
|
page: entry.page,
|
|
121
124
|
params: { ...entry.params },
|
|
122
|
-
query: { ...entry.query }
|
|
125
|
+
query: { ...entry.query },
|
|
126
|
+
options: { ...entry.options }
|
|
123
127
|
};
|
|
124
128
|
}
|
|
125
129
|
normalizeRecord(input) {
|
|
@@ -137,7 +141,8 @@ class Navigation {
|
|
|
137
141
|
[HISTORY_STATE_MARKER]: true,
|
|
138
142
|
page: entry.page,
|
|
139
143
|
params: { ...entry.params },
|
|
140
|
-
query: { ...entry.query }
|
|
144
|
+
query: { ...entry.query },
|
|
145
|
+
options: { ...entry.options }
|
|
141
146
|
};
|
|
142
147
|
window.history[mode](state, "");
|
|
143
148
|
}
|
|
@@ -155,8 +160,8 @@ function getNavigation() {
|
|
|
155
160
|
function goto(delta) {
|
|
156
161
|
return getNavigation().goto(delta);
|
|
157
162
|
}
|
|
158
|
-
function go(page, params = {}, query = {}) {
|
|
159
|
-
return getNavigation().go(page, params, query);
|
|
163
|
+
function go(page, params = {}, query = {}, options = {}) {
|
|
164
|
+
return getNavigation().go(page, params, query, options);
|
|
160
165
|
}
|
|
161
166
|
function goback() {
|
|
162
167
|
return goto(-1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/navigation/index.ts"],"sourcesContent":["import { emitRendererTrace } from '../tracing/index.js';\n\nconst NAVIGATION_SYMBOL = Symbol.for('@noego/wood.navigation');\nconst HISTORY_STATE_MARKER = '__wood_navigation_state__';\n\nexport interface NavigationEntry {\n page: string;\n params: Record<string, string>;\n query: Record<string, string>;\n}\n\ntype NavigationHistoryState = NavigationEntry & {\n [HISTORY_STATE_MARKER]: true;\n};\n\ntype NavigationListener = (entry: NavigationEntry | null) => void;\n\nexport class Navigation {\n private entries: NavigationEntry[] = [];\n private index = -1;\n private listeners = new Set<NavigationListener>();\n\n getCurrent(): NavigationEntry | null {\n if (this.index < 0 || this.index >= this.entries.length) {\n return null;\n }\n return this.cloneEntry(this.entries[this.index]);\n }\n\n getPages(): NavigationEntry[] {\n return this.entries.map((entry) => this.cloneEntry(entry));\n }\n\n getParams(): Record<string, string> {\n const current = this.getCurrent();\n if (!current) {\n return {};\n }\n return { ...current.params };\n }\n\n replace(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query);\n\n if (this.index < 0) {\n this.entries.push(entry);\n this.index = 0;\n } else {\n this.entries[this.index] = entry;\n }\n\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.replace',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n },\n });\n return this.cloneEntry(entry);\n }\n\n go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query);\n if (this.index < this.entries.length - 1) {\n this.entries = this.entries.slice(0, this.index + 1);\n }\n this.entries.push(entry);\n this.index = this.entries.length - 1;\n\n this.writeWindowHistory('pushState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.go',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n },\n });\n return this.cloneEntry(entry);\n }\n\n goto(delta: number): NavigationEntry | null {\n return this.moveCursor(delta);\n }\n\n goBack(): NavigationEntry | null {\n return this.goto(-1);\n }\n\n goForward(): NavigationEntry | null {\n return this.goto(1);\n }\n\n subscribe(listener: NavigationListener): () => void {\n this.listeners.add(listener);\n listener(this.getCurrent());\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private moveCursor(delta: number): NavigationEntry | null {\n if (delta === 0) {\n return this.getCurrent();\n }\n\n const nextIndex = this.index + delta;\n if (nextIndex < 0 || nextIndex >= this.entries.length) {\n return this.getCurrent();\n }\n\n this.index = nextIndex;\n const entry = this.entries[this.index];\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.goto',\n payload: {\n delta,\n page: entry.page,\n },\n });\n return this.cloneEntry(entry);\n }\n\n private notify(): void {\n const current = this.getCurrent();\n for (const listener of this.listeners) {\n listener(current);\n }\n }\n\n private createEntry(\n page: string,\n params: Record<string, string>,\n query: Record<string, string>,\n ): NavigationEntry {\n return {\n page,\n params: this.normalizeRecord(params),\n query: this.normalizeRecord(query),\n };\n }\n\n private cloneEntry(entry: NavigationEntry): NavigationEntry {\n return {\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n };\n }\n\n private normalizeRecord(input: Record<string, string>): Record<string, string> {\n const output: Record<string, string> = {};\n for (const [key, value] of Object.entries(input ?? {})) {\n output[String(key)] = String(value);\n }\n return output;\n }\n\n private writeWindowHistory(\n mode: 'pushState' | 'replaceState',\n entry: NavigationEntry,\n ): void {\n if (typeof window === 'undefined') {\n return;\n }\n\n const state: NavigationHistoryState = {\n [HISTORY_STATE_MARKER]: true,\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n };\n\n window.history[mode](state, '');\n }\n}\n\ntype GlobalWithNavigation = typeof globalThis & {\n [NAVIGATION_SYMBOL]?: Navigation;\n};\n\nfunction createNavigationInstance(): Navigation {\n return new Navigation();\n}\n\nexport function getNavigation(): Navigation {\n const globalObj = globalThis as GlobalWithNavigation;\n if (!globalObj[NAVIGATION_SYMBOL]) {\n globalObj[NAVIGATION_SYMBOL] = createNavigationInstance();\n }\n return globalObj[NAVIGATION_SYMBOL];\n}\n\nexport function goto(\n delta: number,\n): NavigationEntry | null {\n return getNavigation().goto(delta);\n}\n\nexport function go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n): NavigationEntry {\n return getNavigation().go(page, params, query);\n}\n\nexport function goback(): NavigationEntry | null {\n return goto(-1);\n}\n\n/** Test-only helper to clear the navigation singleton. */\nexport function clearNavigationForTests(): void {\n const globalObj = globalThis as GlobalWithNavigation;\n delete globalObj[NAVIGATION_SYMBOL];\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/navigation/index.ts"],"sourcesContent":["import { emitRendererTrace } from '../tracing/index.js';\n\nconst NAVIGATION_SYMBOL = Symbol.for('@noego/wood.navigation');\nconst HISTORY_STATE_MARKER = '__wood_navigation_state__';\n\nexport interface NavigationEntry {\n page: string;\n params: Record<string, string>;\n query: Record<string, string>;\n options: NavigationOptions;\n}\n\nexport interface NavigationOptions {\n preserveLayouts?: boolean;\n}\n\ntype NavigationHistoryState = NavigationEntry & {\n [HISTORY_STATE_MARKER]: true;\n};\n\ntype NavigationListener = (entry: NavigationEntry | null) => void;\n\nexport class Navigation {\n private entries: NavigationEntry[] = [];\n private index = -1;\n private listeners = new Set<NavigationListener>();\n\n getCurrent(): NavigationEntry | null {\n if (this.index < 0 || this.index >= this.entries.length) {\n return null;\n }\n return this.cloneEntry(this.entries[this.index]);\n }\n\n getPages(): NavigationEntry[] {\n return this.entries.map((entry) => this.cloneEntry(entry));\n }\n\n getParams(): Record<string, string> {\n const current = this.getCurrent();\n if (!current) {\n return {};\n }\n return { ...current.params };\n }\n\n replace(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query, options);\n\n if (this.index < 0) {\n this.entries.push(entry);\n this.index = 0;\n } else {\n this.entries[this.index] = entry;\n }\n\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.replace',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n options: entry.options,\n },\n });\n return this.cloneEntry(entry);\n }\n\n go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query, options);\n if (this.index < this.entries.length - 1) {\n this.entries = this.entries.slice(0, this.index + 1);\n }\n this.entries.push(entry);\n this.index = this.entries.length - 1;\n\n this.writeWindowHistory('pushState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.go',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n options: entry.options,\n },\n });\n return this.cloneEntry(entry);\n }\n\n goto(delta: number): NavigationEntry | null {\n return this.moveCursor(delta);\n }\n\n goBack(): NavigationEntry | null {\n return this.goto(-1);\n }\n\n goForward(): NavigationEntry | null {\n return this.goto(1);\n }\n\n subscribe(listener: NavigationListener): () => void {\n this.listeners.add(listener);\n listener(this.getCurrent());\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private moveCursor(delta: number): NavigationEntry | null {\n if (delta === 0) {\n return this.getCurrent();\n }\n\n const nextIndex = this.index + delta;\n if (nextIndex < 0 || nextIndex >= this.entries.length) {\n return this.getCurrent();\n }\n\n this.index = nextIndex;\n const entry = this.entries[this.index];\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.goto',\n payload: {\n delta,\n page: entry.page,\n },\n });\n return this.cloneEntry(entry);\n }\n\n private notify(): void {\n const current = this.getCurrent();\n for (const listener of this.listeners) {\n listener(current);\n }\n }\n\n private createEntry(\n page: string,\n params: Record<string, string>,\n query: Record<string, string>,\n options: NavigationOptions,\n ): NavigationEntry {\n return {\n page,\n params: this.normalizeRecord(params),\n query: this.normalizeRecord(query),\n options: { ...options },\n };\n }\n\n private cloneEntry(entry: NavigationEntry): NavigationEntry {\n return {\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n options: { ...entry.options },\n };\n }\n\n private normalizeRecord(input: Record<string, string>): Record<string, string> {\n const output: Record<string, string> = {};\n for (const [key, value] of Object.entries(input ?? {})) {\n output[String(key)] = String(value);\n }\n return output;\n }\n\n private writeWindowHistory(\n mode: 'pushState' | 'replaceState',\n entry: NavigationEntry,\n ): void {\n if (typeof window === 'undefined') {\n return;\n }\n\n const state: NavigationHistoryState = {\n [HISTORY_STATE_MARKER]: true,\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n options: { ...entry.options },\n };\n\n window.history[mode](state, '');\n }\n}\n\ntype GlobalWithNavigation = typeof globalThis & {\n [NAVIGATION_SYMBOL]?: Navigation;\n};\n\nfunction createNavigationInstance(): Navigation {\n return new Navigation();\n}\n\nexport function getNavigation(): Navigation {\n const globalObj = globalThis as GlobalWithNavigation;\n if (!globalObj[NAVIGATION_SYMBOL]) {\n globalObj[NAVIGATION_SYMBOL] = createNavigationInstance();\n }\n return globalObj[NAVIGATION_SYMBOL];\n}\n\nexport function goto(\n delta: number,\n): NavigationEntry | null {\n return getNavigation().goto(delta);\n}\n\nexport function go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n): NavigationEntry {\n return getNavigation().go(page, params, query, options);\n}\n\nexport function goback(): NavigationEntry | null {\n return goto(-1);\n}\n\n/** Test-only helper to clear the navigation singleton. */\nexport function clearNavigationForTests(): void {\n const globalObj = globalThis as GlobalWithNavigation;\n delete globalObj[NAVIGATION_SYMBOL];\n}\n"],"mappings":"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","names":[]}
|
|
@@ -41,13 +41,20 @@ function validateControllerValue(controller, contextLabel) {
|
|
|
41
41
|
}
|
|
42
42
|
return controller.trim();
|
|
43
43
|
}
|
|
44
|
+
function validatePreserveValue(preserve, contextLabel) {
|
|
45
|
+
if (preserve === void 0) return false;
|
|
46
|
+
if (typeof preserve !== "boolean") {
|
|
47
|
+
throw new Error(`${contextLabel} has invalid preserve. Expected a boolean.`);
|
|
48
|
+
}
|
|
49
|
+
return preserve;
|
|
50
|
+
}
|
|
44
51
|
function parseLayoutEntry(entry, viewKey, index) {
|
|
45
52
|
if (typeof entry === "string") {
|
|
46
53
|
const path = entry.trim();
|
|
47
54
|
if (!path) {
|
|
48
55
|
throw new Error(`View "${viewKey}" has invalid layout entry at index ${index}. Expected a non-empty path string.`);
|
|
49
56
|
}
|
|
50
|
-
return { path };
|
|
57
|
+
return { path, preserve: false };
|
|
51
58
|
}
|
|
52
59
|
const layoutObj = entry;
|
|
53
60
|
const layoutPath = layoutObj.path ?? layoutObj.view ?? layoutObj.layout;
|
|
@@ -58,7 +65,8 @@ function parseLayoutEntry(entry, viewKey, index) {
|
|
|
58
65
|
}
|
|
59
66
|
return {
|
|
60
67
|
path: layoutPath.trim(),
|
|
61
|
-
controller: validateControllerValue(layoutObj.controller, `View "${viewKey}" layout[${index}]`)
|
|
68
|
+
controller: validateControllerValue(layoutObj.controller, `View "${viewKey}" layout[${index}]`),
|
|
69
|
+
preserve: validatePreserveValue(layoutObj.preserve, `View "${viewKey}" layout[${index}]`)
|
|
62
70
|
};
|
|
63
71
|
}
|
|
64
72
|
function isLayoutGroupEntry(entry) {
|
|
@@ -85,7 +93,8 @@ function normalizeLayouts(rawLayout, viewKey) {
|
|
|
85
93
|
}
|
|
86
94
|
return {
|
|
87
95
|
entries: rawLayout.views,
|
|
88
|
-
inheritedController: validateControllerValue(rawLayout.controller, `View "${viewKey}" layout`)
|
|
96
|
+
inheritedController: validateControllerValue(rawLayout.controller, `View "${viewKey}" layout`),
|
|
97
|
+
inheritedPreserve: validatePreserveValue(rawLayout.preserve, `View "${viewKey}" layout`)
|
|
89
98
|
};
|
|
90
99
|
}
|
|
91
100
|
if (isLayoutObjectEntry(rawLayout)) {
|
|
@@ -122,8 +131,17 @@ function parseViews(yamlContent) {
|
|
|
122
131
|
}
|
|
123
132
|
(_a = parsedLayouts[0]).controller ?? (_a.controller = normalizedLayouts.inheritedController);
|
|
124
133
|
}
|
|
134
|
+
if (normalizedLayouts.inheritedPreserve) {
|
|
135
|
+
if (parsedLayouts.length === 0) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
`View "${dotPath}" sets layout preserve but has no layout views. Define at least one layout entry.`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
parsedLayouts[0].preserve = true;
|
|
141
|
+
}
|
|
125
142
|
const layoutPaths = parsedLayouts.map((layout) => layout.path);
|
|
126
143
|
const layoutControllers = parsedLayouts.map((layout) => layout.controller);
|
|
144
|
+
const layoutPreserve = parsedLayouts.map((layout) => layout.preserve);
|
|
127
145
|
const viewController = validateControllerValue(entry.controller, `View "${dotPath}"`);
|
|
128
146
|
const viewDef = {
|
|
129
147
|
windowName,
|
|
@@ -136,6 +154,9 @@ function parseViews(yamlContent) {
|
|
|
136
154
|
if (layoutControllers.some((value) => value !== void 0)) {
|
|
137
155
|
viewDef.layoutControllers = layoutControllers;
|
|
138
156
|
}
|
|
157
|
+
if (layoutPreserve.some((value) => value)) {
|
|
158
|
+
viewDef.layoutPreserve = layoutPreserve;
|
|
159
|
+
}
|
|
139
160
|
views.push(viewDef);
|
|
140
161
|
}
|
|
141
162
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/parser/views_parser.ts"],"sourcesContent":["import yaml from 'js-yaml';\nimport { readFileSync } from 'fs';\nimport type {\n ViewDef,\n RawViewsDocument,\n RawLayoutEntry,\n RawLayoutObjectEntry,\n RawLayoutConfig,\n RawLayoutGroupEntry,\n} from '../types';\n\nfunction validateControllerValue(controller: unknown, contextLabel: string): string | undefined {\n if (controller === undefined) return undefined;\n if (typeof controller !== 'string' || controller.trim().length === 0) {\n throw new Error(`${contextLabel} has invalid controller. Expected a non-empty string.`);\n }\n return controller.trim();\n}\n\nfunction parseLayoutEntry(entry: RawLayoutEntry, viewKey: string, index: number): { path: string; controller?: string } {\n if (typeof entry === 'string') {\n const path = entry.trim();\n if (!path) {\n throw new Error(`View \"${viewKey}\" has invalid layout entry at index ${index}. Expected a non-empty path string.`);\n }\n return { path };\n }\n\n const layoutObj = entry as RawLayoutObjectEntry;\n const layoutPath = layoutObj.path ?? layoutObj.view ?? layoutObj.layout;\n if (typeof layoutPath !== 'string' || layoutPath.trim().length === 0) {\n throw new Error(\n `View \"${viewKey}\" has invalid layout object at index ${index}. ` +\n 'Expected one of: path, view, or layout as a non-empty string.',\n );\n }\n\n return {\n path: layoutPath.trim(),\n controller: validateControllerValue(layoutObj.controller, `View \"${viewKey}\" layout[${index}]`),\n };\n}\n\nfunction isLayoutGroupEntry(entry: unknown): entry is RawLayoutGroupEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return Object.prototype.hasOwnProperty.call(entry, 'views');\n}\n\nfunction isLayoutObjectEntry(entry: unknown): entry is RawLayoutObjectEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return (\n Object.prototype.hasOwnProperty.call(entry, 'path') ||\n Object.prototype.hasOwnProperty.call(entry, 'view') ||\n Object.prototype.hasOwnProperty.call(entry, 'layout') ||\n Object.prototype.hasOwnProperty.call(entry, 'controller')\n );\n}\n\nfunction normalizeLayouts(rawLayout: RawLayoutConfig | undefined, viewKey: string): {\n entries: RawLayoutEntry[];\n inheritedController?: string;\n} {\n if (rawLayout === undefined) {\n return { entries: [] };\n }\n\n if (Array.isArray(rawLayout)) {\n return { entries: rawLayout };\n }\n\n if (typeof rawLayout === 'string') {\n return { entries: [rawLayout] };\n }\n\n if (isLayoutGroupEntry(rawLayout)) {\n if (!Array.isArray(rawLayout.views)) {\n throw new Error(`View \"${viewKey}\" has invalid layout object. \"views\" must be an array.`);\n }\n\n return {\n entries: rawLayout.views,\n inheritedController: validateControllerValue(rawLayout.controller, `View \"${viewKey}\" layout`),\n };\n }\n\n if (isLayoutObjectEntry(rawLayout)) {\n return { entries: [rawLayout] };\n }\n\n throw new Error(\n `View \"${viewKey}\" has invalid layout definition. Expected an array, a string path, or an object with \"views\".`,\n );\n}\n\n/**\n * Parse views YAML content into a flat array of ViewDef.\n * Supports Wood-native `view:` format (dot-path keys).\n */\nexport function parseViews(yamlContent: string): ViewDef[] {\n const doc = yaml.load(yamlContent) as RawViewsDocument;\n const views: ViewDef[] = [];\n\n // Wood-native `view:` format\n // First segment = window name, rest = page name\n if (doc.view) {\n for (const [dotPath, entry] of Object.entries(doc.view)) {\n const segments = dotPath.split('.');\n const windowName = segments[0];\n const pageName = segments.slice(1).join('.');\n\n const normalizedLayouts = normalizeLayouts(entry.layout, dotPath);\n const parsedLayouts = normalizedLayouts.entries.map((layoutEntry, index) =>\n parseLayoutEntry(layoutEntry, dotPath, index),\n );\n if (normalizedLayouts.inheritedController) {\n if (parsedLayouts.length === 0) {\n throw new Error(\n `View \"${dotPath}\" sets a layout controller but has no layout views. Define at least one layout entry.`,\n );\n }\n\n const firstLayoutController = parsedLayouts[0].controller;\n if (firstLayoutController && firstLayoutController !== normalizedLayouts.inheritedController) {\n throw new Error(\n `View \"${dotPath}\" has conflicting layout controllers. Use either layout.controller or layout.views[0].controller.`,\n );\n }\n parsedLayouts[0].controller ??= normalizedLayouts.inheritedController;\n }\n\n const layoutPaths = parsedLayouts.map((layout) => layout.path);\n const layoutControllers = parsedLayouts.map((layout) => layout.controller);\n\n const viewController = validateControllerValue(entry.controller, `View \"${dotPath}\"`);\n\n const viewDef: ViewDef = {\n windowName,\n pageName,\n viewPath: entry.view,\n layouts: layoutPaths,\n middleware: entry.middleware,\n controller: viewController,\n };\n\n if (layoutControllers.some((value) => value !== undefined)) {\n viewDef.layoutControllers = layoutControllers;\n }\n\n views.push(viewDef);\n }\n }\n\n return views;\n}\n\n/** Read a YAML file from disk and parse it into ViewDef[]. */\nexport function parseViewsFile(filePath: string): ViewDef[] {\n const content = readFileSync(filePath, 'utf-8');\n return parseViews(content);\n}\n"],"mappings":"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","names":["yaml"]}
|
|
1
|
+
{"version":3,"sources":["../../src/parser/views_parser.ts"],"sourcesContent":["import yaml from 'js-yaml';\nimport { readFileSync } from 'fs';\nimport type {\n ViewDef,\n RawViewsDocument,\n RawLayoutEntry,\n RawLayoutObjectEntry,\n RawLayoutConfig,\n RawLayoutGroupEntry,\n} from '../types';\n\nfunction validateControllerValue(controller: unknown, contextLabel: string): string | undefined {\n if (controller === undefined) return undefined;\n if (typeof controller !== 'string' || controller.trim().length === 0) {\n throw new Error(`${contextLabel} has invalid controller. Expected a non-empty string.`);\n }\n return controller.trim();\n}\n\nfunction validatePreserveValue(preserve: unknown, contextLabel: string): boolean {\n if (preserve === undefined) return false;\n if (typeof preserve !== 'boolean') {\n throw new Error(`${contextLabel} has invalid preserve. Expected a boolean.`);\n }\n return preserve;\n}\n\nfunction parseLayoutEntry(entry: RawLayoutEntry, viewKey: string, index: number): { path: string; controller?: string; preserve: boolean } {\n if (typeof entry === 'string') {\n const path = entry.trim();\n if (!path) {\n throw new Error(`View \"${viewKey}\" has invalid layout entry at index ${index}. Expected a non-empty path string.`);\n }\n return { path, preserve: false };\n }\n\n const layoutObj = entry as RawLayoutObjectEntry;\n const layoutPath = layoutObj.path ?? layoutObj.view ?? layoutObj.layout;\n if (typeof layoutPath !== 'string' || layoutPath.trim().length === 0) {\n throw new Error(\n `View \"${viewKey}\" has invalid layout object at index ${index}. ` +\n 'Expected one of: path, view, or layout as a non-empty string.',\n );\n }\n\n return {\n path: layoutPath.trim(),\n controller: validateControllerValue(layoutObj.controller, `View \"${viewKey}\" layout[${index}]`),\n preserve: validatePreserveValue(layoutObj.preserve, `View \"${viewKey}\" layout[${index}]`),\n };\n}\n\nfunction isLayoutGroupEntry(entry: unknown): entry is RawLayoutGroupEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return Object.prototype.hasOwnProperty.call(entry, 'views');\n}\n\nfunction isLayoutObjectEntry(entry: unknown): entry is RawLayoutObjectEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return (\n Object.prototype.hasOwnProperty.call(entry, 'path') ||\n Object.prototype.hasOwnProperty.call(entry, 'view') ||\n Object.prototype.hasOwnProperty.call(entry, 'layout') ||\n Object.prototype.hasOwnProperty.call(entry, 'controller')\n );\n}\n\nfunction normalizeLayouts(rawLayout: RawLayoutConfig | undefined, viewKey: string): {\n entries: RawLayoutEntry[];\n inheritedController?: string;\n inheritedPreserve?: boolean;\n} {\n if (rawLayout === undefined) {\n return { entries: [] };\n }\n\n if (Array.isArray(rawLayout)) {\n return { entries: rawLayout };\n }\n\n if (typeof rawLayout === 'string') {\n return { entries: [rawLayout] };\n }\n\n if (isLayoutGroupEntry(rawLayout)) {\n if (!Array.isArray(rawLayout.views)) {\n throw new Error(`View \"${viewKey}\" has invalid layout object. \"views\" must be an array.`);\n }\n\n return {\n entries: rawLayout.views,\n inheritedController: validateControllerValue(rawLayout.controller, `View \"${viewKey}\" layout`),\n inheritedPreserve: validatePreserveValue(rawLayout.preserve, `View \"${viewKey}\" layout`),\n };\n }\n\n if (isLayoutObjectEntry(rawLayout)) {\n return { entries: [rawLayout] };\n }\n\n throw new Error(\n `View \"${viewKey}\" has invalid layout definition. Expected an array, a string path, or an object with \"views\".`,\n );\n}\n\n/**\n * Parse views YAML content into a flat array of ViewDef.\n * Supports Wood-native `view:` format (dot-path keys).\n */\nexport function parseViews(yamlContent: string): ViewDef[] {\n const doc = yaml.load(yamlContent) as RawViewsDocument;\n const views: ViewDef[] = [];\n\n // Wood-native `view:` format\n // First segment = window name, rest = page name\n if (doc.view) {\n for (const [dotPath, entry] of Object.entries(doc.view)) {\n const segments = dotPath.split('.');\n const windowName = segments[0];\n const pageName = segments.slice(1).join('.');\n\n const normalizedLayouts = normalizeLayouts(entry.layout, dotPath);\n const parsedLayouts = normalizedLayouts.entries.map((layoutEntry, index) =>\n parseLayoutEntry(layoutEntry, dotPath, index),\n );\n if (normalizedLayouts.inheritedController) {\n if (parsedLayouts.length === 0) {\n throw new Error(\n `View \"${dotPath}\" sets a layout controller but has no layout views. Define at least one layout entry.`,\n );\n }\n\n const firstLayoutController = parsedLayouts[0].controller;\n if (firstLayoutController && firstLayoutController !== normalizedLayouts.inheritedController) {\n throw new Error(\n `View \"${dotPath}\" has conflicting layout controllers. Use either layout.controller or layout.views[0].controller.`,\n );\n }\n parsedLayouts[0].controller ??= normalizedLayouts.inheritedController;\n }\n if (normalizedLayouts.inheritedPreserve) {\n if (parsedLayouts.length === 0) {\n throw new Error(\n `View \"${dotPath}\" sets layout preserve but has no layout views. Define at least one layout entry.`,\n );\n }\n parsedLayouts[0].preserve = true;\n }\n\n const layoutPaths = parsedLayouts.map((layout) => layout.path);\n const layoutControllers = parsedLayouts.map((layout) => layout.controller);\n const layoutPreserve = parsedLayouts.map((layout) => layout.preserve);\n\n const viewController = validateControllerValue(entry.controller, `View \"${dotPath}\"`);\n\n const viewDef: ViewDef = {\n windowName,\n pageName,\n viewPath: entry.view,\n layouts: layoutPaths,\n middleware: entry.middleware,\n controller: viewController,\n };\n\n if (layoutControllers.some((value) => value !== undefined)) {\n viewDef.layoutControllers = layoutControllers;\n }\n if (layoutPreserve.some((value) => value)) {\n viewDef.layoutPreserve = layoutPreserve;\n }\n\n views.push(viewDef);\n }\n }\n\n return views;\n}\n\n/** Read a YAML file from disk and parse it into ViewDef[]. */\nexport function parseViewsFile(filePath: string): ViewDef[] {\n const content = readFileSync(filePath, 'utf-8');\n return parseViews(content);\n}\n"],"mappings":"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","names":["yaml"]}
|
|
@@ -7,13 +7,20 @@ function validateControllerValue(controller, contextLabel) {
|
|
|
7
7
|
}
|
|
8
8
|
return controller.trim();
|
|
9
9
|
}
|
|
10
|
+
function validatePreserveValue(preserve, contextLabel) {
|
|
11
|
+
if (preserve === void 0) return false;
|
|
12
|
+
if (typeof preserve !== "boolean") {
|
|
13
|
+
throw new Error(`${contextLabel} has invalid preserve. Expected a boolean.`);
|
|
14
|
+
}
|
|
15
|
+
return preserve;
|
|
16
|
+
}
|
|
10
17
|
function parseLayoutEntry(entry, viewKey, index) {
|
|
11
18
|
if (typeof entry === "string") {
|
|
12
19
|
const path = entry.trim();
|
|
13
20
|
if (!path) {
|
|
14
21
|
throw new Error(`View "${viewKey}" has invalid layout entry at index ${index}. Expected a non-empty path string.`);
|
|
15
22
|
}
|
|
16
|
-
return { path };
|
|
23
|
+
return { path, preserve: false };
|
|
17
24
|
}
|
|
18
25
|
const layoutObj = entry;
|
|
19
26
|
const layoutPath = layoutObj.path ?? layoutObj.view ?? layoutObj.layout;
|
|
@@ -24,7 +31,8 @@ function parseLayoutEntry(entry, viewKey, index) {
|
|
|
24
31
|
}
|
|
25
32
|
return {
|
|
26
33
|
path: layoutPath.trim(),
|
|
27
|
-
controller: validateControllerValue(layoutObj.controller, `View "${viewKey}" layout[${index}]`)
|
|
34
|
+
controller: validateControllerValue(layoutObj.controller, `View "${viewKey}" layout[${index}]`),
|
|
35
|
+
preserve: validatePreserveValue(layoutObj.preserve, `View "${viewKey}" layout[${index}]`)
|
|
28
36
|
};
|
|
29
37
|
}
|
|
30
38
|
function isLayoutGroupEntry(entry) {
|
|
@@ -51,7 +59,8 @@ function normalizeLayouts(rawLayout, viewKey) {
|
|
|
51
59
|
}
|
|
52
60
|
return {
|
|
53
61
|
entries: rawLayout.views,
|
|
54
|
-
inheritedController: validateControllerValue(rawLayout.controller, `View "${viewKey}" layout`)
|
|
62
|
+
inheritedController: validateControllerValue(rawLayout.controller, `View "${viewKey}" layout`),
|
|
63
|
+
inheritedPreserve: validatePreserveValue(rawLayout.preserve, `View "${viewKey}" layout`)
|
|
55
64
|
};
|
|
56
65
|
}
|
|
57
66
|
if (isLayoutObjectEntry(rawLayout)) {
|
|
@@ -88,8 +97,17 @@ function parseViews(yamlContent) {
|
|
|
88
97
|
}
|
|
89
98
|
(_a = parsedLayouts[0]).controller ?? (_a.controller = normalizedLayouts.inheritedController);
|
|
90
99
|
}
|
|
100
|
+
if (normalizedLayouts.inheritedPreserve) {
|
|
101
|
+
if (parsedLayouts.length === 0) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`View "${dotPath}" sets layout preserve but has no layout views. Define at least one layout entry.`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
parsedLayouts[0].preserve = true;
|
|
107
|
+
}
|
|
91
108
|
const layoutPaths = parsedLayouts.map((layout) => layout.path);
|
|
92
109
|
const layoutControllers = parsedLayouts.map((layout) => layout.controller);
|
|
110
|
+
const layoutPreserve = parsedLayouts.map((layout) => layout.preserve);
|
|
93
111
|
const viewController = validateControllerValue(entry.controller, `View "${dotPath}"`);
|
|
94
112
|
const viewDef = {
|
|
95
113
|
windowName,
|
|
@@ -102,6 +120,9 @@ function parseViews(yamlContent) {
|
|
|
102
120
|
if (layoutControllers.some((value) => value !== void 0)) {
|
|
103
121
|
viewDef.layoutControllers = layoutControllers;
|
|
104
122
|
}
|
|
123
|
+
if (layoutPreserve.some((value) => value)) {
|
|
124
|
+
viewDef.layoutPreserve = layoutPreserve;
|
|
125
|
+
}
|
|
105
126
|
views.push(viewDef);
|
|
106
127
|
}
|
|
107
128
|
}
|