@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.
Files changed (62) hide show
  1. package/dist/client/index.cjs +5 -2
  2. package/dist/client/index.cjs.map +1 -1
  3. package/dist/client/index.js +5 -2
  4. package/dist/client/index.js.map +1 -1
  5. package/dist/controller/controller_lifecycle.cjs +4 -0
  6. package/dist/controller/controller_lifecycle.cjs.map +1 -1
  7. package/dist/controller/controller_lifecycle.d.cts +3 -1
  8. package/dist/controller/controller_lifecycle.d.ts +3 -1
  9. package/dist/controller/controller_lifecycle.js +4 -0
  10. package/dist/controller/controller_lifecycle.js.map +1 -1
  11. package/dist/index.cjs +2 -0
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +2 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/main/index.cjs +42 -17
  18. package/dist/main/index.cjs.map +1 -1
  19. package/dist/main/index.d.cts +2 -0
  20. package/dist/main/index.d.ts +2 -0
  21. package/dist/main/index.js +42 -17
  22. package/dist/main/index.js.map +1 -1
  23. package/dist/navigation/navigation_identity.cjs +43 -0
  24. package/dist/navigation/navigation_identity.cjs.map +1 -0
  25. package/dist/navigation/navigation_identity.d.cts +5 -0
  26. package/dist/navigation/navigation_identity.d.ts +5 -0
  27. package/dist/navigation/navigation_identity.js +18 -0
  28. package/dist/navigation/navigation_identity.js.map +1 -0
  29. package/dist/runtime/runtime.cjs +11 -4
  30. package/dist/runtime/runtime.cjs.map +1 -1
  31. package/dist/runtime/runtime.d.cts +2 -0
  32. package/dist/runtime/runtime.d.ts +2 -0
  33. package/dist/runtime/runtime.js +12 -5
  34. package/dist/runtime/runtime.js.map +1 -1
  35. package/dist/testing/create_controller_harness.cjs +1 -0
  36. package/dist/testing/create_controller_harness.cjs.map +1 -1
  37. package/dist/testing/create_controller_harness.js +1 -0
  38. package/dist/testing/create_controller_harness.js.map +1 -1
  39. package/dist/tracing/index.cjs +2 -0
  40. package/dist/tracing/index.cjs.map +1 -1
  41. package/dist/tracing/index.d.cts +2 -2
  42. package/dist/tracing/index.d.ts +2 -2
  43. package/dist/tracing/index.js +2 -0
  44. package/dist/tracing/index.js.map +1 -1
  45. package/dist/tracing/trace_hub.cjs +20 -5
  46. package/dist/tracing/trace_hub.cjs.map +1 -1
  47. package/dist/tracing/trace_hub.d.cts +2 -1
  48. package/dist/tracing/trace_hub.d.ts +2 -1
  49. package/dist/tracing/trace_hub.js +19 -5
  50. package/dist/tracing/trace_hub.js.map +1 -1
  51. package/dist/tracing/trace_provider.cjs +63 -6
  52. package/dist/tracing/trace_provider.cjs.map +1 -1
  53. package/dist/tracing/trace_provider.d.cts +10 -2
  54. package/dist/tracing/trace_provider.d.ts +10 -2
  55. package/dist/tracing/trace_provider.js +64 -6
  56. package/dist/tracing/trace_provider.js.map +1 -1
  57. package/package.json +18 -16
  58. package/src/client/controller_contract.ts +18 -0
  59. package/src/components/NavigationShell.svelte +18 -2
  60. package/src/controller/controller_lifecycle.ts +24 -0
  61. package/src/navigation/navigation_identity.ts +23 -0
  62. 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
- if (newKey === activeEntry.key) {
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 activeEntry.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}