@noego/wood 0.1.0 → 0.1.2
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 +5 -2
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.js +5 -2
- package/dist/client/index.js.map +1 -1
- package/dist/controller/controller_lifecycle.cjs +4 -0
- package/dist/controller/controller_lifecycle.cjs.map +1 -1
- package/dist/controller/controller_lifecycle.d.cts +3 -1
- package/dist/controller/controller_lifecycle.d.ts +3 -1
- package/dist/controller/controller_lifecycle.js +4 -0
- package/dist/controller/controller_lifecycle.js.map +1 -1
- package/dist/index.cjs +2 -0
- 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 +2 -0
- package/dist/index.js.map +1 -1
- package/dist/main/index.cjs +42 -17
- package/dist/main/index.cjs.map +1 -1
- package/dist/main/index.d.cts +2 -0
- package/dist/main/index.d.ts +2 -0
- package/dist/main/index.js +42 -17
- package/dist/main/index.js.map +1 -1
- package/dist/navigation/navigation_identity.cjs +43 -0
- package/dist/navigation/navigation_identity.cjs.map +1 -0
- package/dist/navigation/navigation_identity.d.cts +5 -0
- package/dist/navigation/navigation_identity.d.ts +5 -0
- package/dist/navigation/navigation_identity.js +18 -0
- package/dist/navigation/navigation_identity.js.map +1 -0
- package/dist/runtime/runtime.cjs +11 -4
- package/dist/runtime/runtime.cjs.map +1 -1
- package/dist/runtime/runtime.d.cts +2 -0
- package/dist/runtime/runtime.d.ts +2 -0
- package/dist/runtime/runtime.js +12 -5
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/testing/create_controller_harness.cjs +1 -0
- package/dist/testing/create_controller_harness.cjs.map +1 -1
- package/dist/testing/create_controller_harness.js +1 -0
- package/dist/testing/create_controller_harness.js.map +1 -1
- package/dist/tracing/index.cjs +2 -0
- package/dist/tracing/index.cjs.map +1 -1
- package/dist/tracing/index.d.cts +2 -2
- package/dist/tracing/index.d.ts +2 -2
- package/dist/tracing/index.js +2 -0
- package/dist/tracing/index.js.map +1 -1
- package/dist/tracing/trace_hub.cjs +20 -5
- 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 +19 -5
- package/dist/tracing/trace_hub.js.map +1 -1
- package/dist/tracing/trace_provider.cjs +63 -6
- package/dist/tracing/trace_provider.cjs.map +1 -1
- package/dist/tracing/trace_provider.d.cts +10 -2
- package/dist/tracing/trace_provider.d.ts +10 -2
- package/dist/tracing/trace_provider.js +64 -6
- package/dist/tracing/trace_provider.js.map +1 -1
- package/package.json +18 -16
- package/src/client/controller_contract.ts +18 -0
- package/src/components/NavigationShell.svelte +18 -2
- package/src/controller/controller_lifecycle.ts +24 -0
- package/src/navigation/navigation_identity.ts +23 -0
- package/src/stick/WoodRecursiveRender.svelte +0 -74
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ControllerLifecycle } from '../controller/controller_lifecycle';
|
|
2
|
+
export type { ControllerLifecycle } from '../controller/controller_lifecycle';
|
|
3
|
+
|
|
4
|
+
export interface ControllerState {}
|
|
5
|
+
|
|
6
|
+
export interface ControllerInput {}
|
|
7
|
+
|
|
8
|
+
export interface ControllerEvents {}
|
|
9
|
+
|
|
10
|
+
export interface PageController<
|
|
11
|
+
S extends ControllerState = ControllerState,
|
|
12
|
+
I extends ControllerInput = ControllerInput,
|
|
13
|
+
E extends ControllerEvents = ControllerEvents,
|
|
14
|
+
> extends ControllerLifecycle {
|
|
15
|
+
readonly data?: S;
|
|
16
|
+
readonly input?: I;
|
|
17
|
+
readonly events?: E;
|
|
18
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { setContext, onDestroy } from 'svelte';
|
|
3
3
|
import { getNavigation } from '@noego/wood/navigation';
|
|
4
|
+
import { disposeControllerLifecycle } from '../controller/controller_lifecycle';
|
|
5
|
+
import { createNavigationMountKey } from '../navigation/navigation_identity';
|
|
4
6
|
import WoodRecursiveRender from './WoodRecursiveRender.svelte';
|
|
5
7
|
|
|
6
8
|
type ViewManifestEntry = {
|
|
@@ -48,6 +50,7 @@
|
|
|
48
50
|
let activeLayoutControllers: Array<unknown> = $state(initialLayoutControllers);
|
|
49
51
|
let activeParams: Record<string, unknown> = $state({});
|
|
50
52
|
let activeQuery: Record<string, unknown> = $state({});
|
|
53
|
+
let activeMountKey = $state(createNavigationMountKey(initialEntry.key, {}));
|
|
51
54
|
|
|
52
55
|
let activePage = $derived({
|
|
53
56
|
key: activeEntry.key,
|
|
@@ -106,6 +109,13 @@
|
|
|
106
109
|
}
|
|
107
110
|
}
|
|
108
111
|
|
|
112
|
+
async function disposeActiveControllers(): Promise<void> {
|
|
113
|
+
await disposeControllerLifecycle(activeController);
|
|
114
|
+
for (const layoutController of activeLayoutControllers) {
|
|
115
|
+
await disposeControllerLifecycle(layoutController);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
109
119
|
setContext('wood:navigate', (
|
|
110
120
|
viewKey: string,
|
|
111
121
|
params?: Record<string, unknown>,
|
|
@@ -138,6 +148,7 @@
|
|
|
138
148
|
if (navEntry) {
|
|
139
149
|
activeParams = { ...navEntry.params };
|
|
140
150
|
activeQuery = { ...navEntry.query };
|
|
151
|
+
activeMountKey = createNavigationMountKey(navEntry.page, navEntry.params);
|
|
141
152
|
}
|
|
142
153
|
return;
|
|
143
154
|
}
|
|
@@ -148,12 +159,15 @@
|
|
|
148
159
|
const entry = getViewManifestEntryFn(newKey);
|
|
149
160
|
if (!entry) return;
|
|
150
161
|
|
|
151
|
-
|
|
162
|
+
const nextMountKey = createNavigationMountKey(newKey, navEntry.params);
|
|
163
|
+
|
|
164
|
+
if (nextMountKey === activeMountKey) {
|
|
152
165
|
activeParams = { ...navEntry.params };
|
|
153
166
|
activeQuery = { ...navEntry.query };
|
|
154
167
|
return;
|
|
155
168
|
}
|
|
156
169
|
|
|
170
|
+
await disposeActiveControllers();
|
|
157
171
|
const [controller, layoutControllers] = await Promise.all([
|
|
158
172
|
createViewControllerFn(entry),
|
|
159
173
|
createLayoutControllersFn(entry),
|
|
@@ -164,6 +178,7 @@
|
|
|
164
178
|
activeLayoutControllers = layoutControllers;
|
|
165
179
|
activeParams = { ...navEntry.params };
|
|
166
180
|
activeQuery = { ...navEntry.query };
|
|
181
|
+
activeMountKey = nextMountKey;
|
|
167
182
|
});
|
|
168
183
|
|
|
169
184
|
const bridge = getWoodBridge();
|
|
@@ -188,10 +203,11 @@
|
|
|
188
203
|
onDestroy(() => {
|
|
189
204
|
unsubscribe();
|
|
190
205
|
unsubscribeWindowNavigate?.();
|
|
206
|
+
void disposeActiveControllers();
|
|
191
207
|
});
|
|
192
208
|
</script>
|
|
193
209
|
|
|
194
|
-
{#key
|
|
210
|
+
{#key activeMountKey}
|
|
195
211
|
<WoodRecursiveRender
|
|
196
212
|
layouts={activeEntry.layouts}
|
|
197
213
|
layoutControllers={activeLayoutControllers}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ControllerLifecycle {
|
|
2
|
+
dispose?(): void | Promise<void>;
|
|
3
|
+
destroy?(): void | Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Dispose a controller or controller-like instance if it exposes a lifecycle
|
|
8
|
+
* hook. `dispose` is the preferred Wood lifecycle name; `destroy` is supported
|
|
9
|
+
* for existing page controllers.
|
|
10
|
+
*/
|
|
11
|
+
export async function disposeControllerLifecycle(target: unknown): Promise<void> {
|
|
12
|
+
if (!target || (typeof target !== 'object' && typeof target !== 'function')) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const lifecycle = target as ControllerLifecycle;
|
|
17
|
+
if (typeof lifecycle.dispose === 'function') {
|
|
18
|
+
await lifecycle.dispose();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (typeof lifecycle.destroy === 'function') {
|
|
22
|
+
await lifecycle.destroy();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type NavigationParams = Record<string, unknown>;
|
|
2
|
+
|
|
3
|
+
function normalizeParamValue(value: unknown): string {
|
|
4
|
+
if (value === null || value === undefined) {
|
|
5
|
+
return '';
|
|
6
|
+
}
|
|
7
|
+
return String(value);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function hashNavigationParams(params: NavigationParams = {}): string {
|
|
11
|
+
const normalizedEntries = Object.entries(params)
|
|
12
|
+
.map(([key, value]) => [String(key), normalizeParamValue(value)] as const)
|
|
13
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
14
|
+
|
|
15
|
+
return JSON.stringify(normalizedEntries);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createNavigationMountKey(
|
|
19
|
+
page: string,
|
|
20
|
+
params: NavigationParams = {},
|
|
21
|
+
): string {
|
|
22
|
+
return `${page}:${hashNavigationParams(params)}`;
|
|
23
|
+
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
// SSR-only recursive layout renderer for stick (wood's visual preview CLI).
|
|
3
|
-
// Simplified version of Forge's RecursiveRender — no stores, navigation, or error boundaries.
|
|
4
|
-
let {
|
|
5
|
-
layouts = [],
|
|
6
|
-
view = null,
|
|
7
|
-
data = { layout: [], view: {} },
|
|
8
|
-
wood = undefined,
|
|
9
|
-
} = $props<{
|
|
10
|
-
layouts?: any[];
|
|
11
|
-
view?: any;
|
|
12
|
-
data?: { layout?: any[]; view?: any };
|
|
13
|
-
wood?: any;
|
|
14
|
-
}>();
|
|
15
|
-
|
|
16
|
-
let topLayout = $derived(layouts[0]);
|
|
17
|
-
let childLayouts = $derived(layouts.slice(1));
|
|
18
|
-
let topData = $derived((data.layout || [])[0]);
|
|
19
|
-
let otherData = $derived((data.layout || []).slice(1));
|
|
20
|
-
let viewData = $derived(data.view);
|
|
21
|
-
let recursiveChildData = $derived({
|
|
22
|
-
layout: otherData,
|
|
23
|
-
view: data.view,
|
|
24
|
-
});
|
|
25
|
-
</script>
|
|
26
|
-
|
|
27
|
-
{#if childLayouts.length > 0}
|
|
28
|
-
{#snippet nested_child()}
|
|
29
|
-
<svelte:self
|
|
30
|
-
layouts={childLayouts}
|
|
31
|
-
view={view}
|
|
32
|
-
data={recursiveChildData}
|
|
33
|
-
{wood}
|
|
34
|
-
/>
|
|
35
|
-
{/snippet}
|
|
36
|
-
<svelte:component
|
|
37
|
-
this={topLayout}
|
|
38
|
-
{...(topData || {})}
|
|
39
|
-
data={topData?.controller?.data}
|
|
40
|
-
input={topData?.controller?.input}
|
|
41
|
-
events={topData?.controller?.events}
|
|
42
|
-
{wood}
|
|
43
|
-
children={nested_child}
|
|
44
|
-
/>
|
|
45
|
-
{:else if topLayout}
|
|
46
|
-
{#snippet view_child()}
|
|
47
|
-
<svelte:component
|
|
48
|
-
this={view}
|
|
49
|
-
{...(viewData || {})}
|
|
50
|
-
data={viewData?.controller?.data}
|
|
51
|
-
input={viewData?.controller?.input}
|
|
52
|
-
events={viewData?.controller?.events}
|
|
53
|
-
{wood}
|
|
54
|
-
/>
|
|
55
|
-
{/snippet}
|
|
56
|
-
<svelte:component
|
|
57
|
-
this={topLayout}
|
|
58
|
-
{...(topData || {})}
|
|
59
|
-
data={topData?.controller?.data}
|
|
60
|
-
input={topData?.controller?.input}
|
|
61
|
-
events={topData?.controller?.events}
|
|
62
|
-
{wood}
|
|
63
|
-
children={view_child}
|
|
64
|
-
/>
|
|
65
|
-
{:else}
|
|
66
|
-
<svelte:component
|
|
67
|
-
this={view}
|
|
68
|
-
{...(viewData || {})}
|
|
69
|
-
data={viewData?.controller?.data}
|
|
70
|
-
input={viewData?.controller?.input}
|
|
71
|
-
events={viewData?.controller?.events}
|
|
72
|
-
{wood}
|
|
73
|
-
/>
|
|
74
|
-
{/if}
|