@pithyjs/router 0.1.0-beta.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.
Files changed (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +621 -0
  3. package/dist/index.d.ts +29 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +49 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/router/core/config.d.ts +22 -0
  8. package/dist/router/core/config.d.ts.map +1 -0
  9. package/dist/router/core/config.js +54 -0
  10. package/dist/router/core/config.js.map +1 -0
  11. package/dist/router/core/constants.d.ts +89 -0
  12. package/dist/router/core/constants.d.ts.map +1 -0
  13. package/dist/router/core/constants.js +95 -0
  14. package/dist/router/core/constants.js.map +1 -0
  15. package/dist/router/core/history.d.ts +54 -0
  16. package/dist/router/core/history.d.ts.map +1 -0
  17. package/dist/router/core/history.js +89 -0
  18. package/dist/router/core/history.js.map +1 -0
  19. package/dist/router/core/matcher.d.ts +43 -0
  20. package/dist/router/core/matcher.d.ts.map +1 -0
  21. package/dist/router/core/matcher.js +242 -0
  22. package/dist/router/core/matcher.js.map +1 -0
  23. package/dist/router/core/params.d.ts +88 -0
  24. package/dist/router/core/params.d.ts.map +1 -0
  25. package/dist/router/core/params.js +150 -0
  26. package/dist/router/core/params.js.map +1 -0
  27. package/dist/router/core/pipeline.d.ts +90 -0
  28. package/dist/router/core/pipeline.d.ts.map +1 -0
  29. package/dist/router/core/pipeline.js +323 -0
  30. package/dist/router/core/pipeline.js.map +1 -0
  31. package/dist/router/core/route-tree.d.ts +76 -0
  32. package/dist/router/core/route-tree.d.ts.map +1 -0
  33. package/dist/router/core/route-tree.js +143 -0
  34. package/dist/router/core/route-tree.js.map +1 -0
  35. package/dist/router/core/router.d.ts +229 -0
  36. package/dist/router/core/router.d.ts.map +1 -0
  37. package/dist/router/core/router.js +762 -0
  38. package/dist/router/core/router.js.map +1 -0
  39. package/dist/router/core/types.d.ts +387 -0
  40. package/dist/router/core/types.d.ts.map +1 -0
  41. package/dist/router/core/types.js +22 -0
  42. package/dist/router/core/types.js.map +1 -0
  43. package/dist/router/core/utils.d.ts +66 -0
  44. package/dist/router/core/utils.d.ts.map +1 -0
  45. package/dist/router/core/utils.js +152 -0
  46. package/dist/router/core/utils.js.map +1 -0
  47. package/dist/router/features/coordinator.d.ts +75 -0
  48. package/dist/router/features/coordinator.d.ts.map +1 -0
  49. package/dist/router/features/coordinator.js +203 -0
  50. package/dist/router/features/coordinator.js.map +1 -0
  51. package/dist/router/features/errors/error-codes.d.ts +107 -0
  52. package/dist/router/features/errors/error-codes.d.ts.map +1 -0
  53. package/dist/router/features/errors/error-codes.js +247 -0
  54. package/dist/router/features/errors/error-codes.js.map +1 -0
  55. package/dist/router/features/errors/error-resolution.d.ts +31 -0
  56. package/dist/router/features/errors/error-resolution.d.ts.map +1 -0
  57. package/dist/router/features/errors/error-resolution.js +47 -0
  58. package/dist/router/features/errors/error-resolution.js.map +1 -0
  59. package/dist/router/features/errors/error-types.d.ts +60 -0
  60. package/dist/router/features/errors/error-types.d.ts.map +1 -0
  61. package/dist/router/features/errors/error-types.js +57 -0
  62. package/dist/router/features/errors/error-types.js.map +1 -0
  63. package/dist/router/features/errors/global-error-fallback.d.ts +33 -0
  64. package/dist/router/features/errors/global-error-fallback.d.ts.map +1 -0
  65. package/dist/router/features/errors/global-error-fallback.js +182 -0
  66. package/dist/router/features/errors/global-error-fallback.js.map +1 -0
  67. package/dist/router/features/errors/index.d.ts +15 -0
  68. package/dist/router/features/errors/index.d.ts.map +1 -0
  69. package/dist/router/features/errors/index.js +18 -0
  70. package/dist/router/features/errors/index.js.map +1 -0
  71. package/dist/router/features/errors/router-errors.d.ts +44 -0
  72. package/dist/router/features/errors/router-errors.d.ts.map +1 -0
  73. package/dist/router/features/errors/router-errors.js +53 -0
  74. package/dist/router/features/errors/router-errors.js.map +1 -0
  75. package/dist/router/features/guards/engine.d.ts +40 -0
  76. package/dist/router/features/guards/engine.d.ts.map +1 -0
  77. package/dist/router/features/guards/engine.js +67 -0
  78. package/dist/router/features/guards/engine.js.map +1 -0
  79. package/dist/router/features/guards/index.d.ts +52 -0
  80. package/dist/router/features/guards/index.d.ts.map +1 -0
  81. package/dist/router/features/guards/index.js +95 -0
  82. package/dist/router/features/guards/index.js.map +1 -0
  83. package/dist/router/features/loaders/cache.d.ts +60 -0
  84. package/dist/router/features/loaders/cache.d.ts.map +1 -0
  85. package/dist/router/features/loaders/cache.js +80 -0
  86. package/dist/router/features/loaders/cache.js.map +1 -0
  87. package/dist/router/features/loaders/engine.d.ts +17 -0
  88. package/dist/router/features/loaders/engine.d.ts.map +1 -0
  89. package/dist/router/features/loaders/engine.js +27 -0
  90. package/dist/router/features/loaders/engine.js.map +1 -0
  91. package/dist/router/features/loaders/index.d.ts +49 -0
  92. package/dist/router/features/loaders/index.d.ts.map +1 -0
  93. package/dist/router/features/loaders/index.js +112 -0
  94. package/dist/router/features/loaders/index.js.map +1 -0
  95. package/dist/router/features/loading-ui-manager.d.ts +69 -0
  96. package/dist/router/features/loading-ui-manager.d.ts.map +1 -0
  97. package/dist/router/features/loading-ui-manager.js +209 -0
  98. package/dist/router/features/loading-ui-manager.js.map +1 -0
  99. package/dist/router/features/module-loader.d.ts +50 -0
  100. package/dist/router/features/module-loader.d.ts.map +1 -0
  101. package/dist/router/features/module-loader.js +121 -0
  102. package/dist/router/features/module-loader.js.map +1 -0
  103. package/dist/router/features/outlet/errors.d.ts +24 -0
  104. package/dist/router/features/outlet/errors.d.ts.map +1 -0
  105. package/dist/router/features/outlet/errors.js +96 -0
  106. package/dist/router/features/outlet/errors.js.map +1 -0
  107. package/dist/router/features/outlet/index.d.ts +45 -0
  108. package/dist/router/features/outlet/index.d.ts.map +1 -0
  109. package/dist/router/features/outlet/index.js +95 -0
  110. package/dist/router/features/outlet/index.js.map +1 -0
  111. package/dist/router/features/outlet/mount.d.ts +38 -0
  112. package/dist/router/features/outlet/mount.d.ts.map +1 -0
  113. package/dist/router/features/outlet/mount.js +301 -0
  114. package/dist/router/features/outlet/mount.js.map +1 -0
  115. package/dist/router/features/outlet/segment-chain.d.ts +16 -0
  116. package/dist/router/features/outlet/segment-chain.d.ts.map +1 -0
  117. package/dist/router/features/outlet/segment-chain.js +62 -0
  118. package/dist/router/features/outlet/segment-chain.js.map +1 -0
  119. package/dist/router/features/prefetch/index.d.ts +97 -0
  120. package/dist/router/features/prefetch/index.d.ts.map +1 -0
  121. package/dist/router/features/prefetch/index.js +345 -0
  122. package/dist/router/features/prefetch/index.js.map +1 -0
  123. package/dist/router/features/ux/focus.d.ts +62 -0
  124. package/dist/router/features/ux/focus.d.ts.map +1 -0
  125. package/dist/router/features/ux/focus.js +136 -0
  126. package/dist/router/features/ux/focus.js.map +1 -0
  127. package/dist/router/features/ux/live-region.d.ts +81 -0
  128. package/dist/router/features/ux/live-region.d.ts.map +1 -0
  129. package/dist/router/features/ux/live-region.js +166 -0
  130. package/dist/router/features/ux/live-region.js.map +1 -0
  131. package/dist/router/features/ux/scroll-focus.d.ts +105 -0
  132. package/dist/router/features/ux/scroll-focus.d.ts.map +1 -0
  133. package/dist/router/features/ux/scroll-focus.js +111 -0
  134. package/dist/router/features/ux/scroll-focus.js.map +1 -0
  135. package/dist/router/features/ux/scroll.d.ts +71 -0
  136. package/dist/router/features/ux/scroll.d.ts.map +1 -0
  137. package/dist/router/features/ux/scroll.js +137 -0
  138. package/dist/router/features/ux/scroll.js.map +1 -0
  139. package/dist/router/features/ux/transitions.d.ts +120 -0
  140. package/dist/router/features/ux/transitions.d.ts.map +1 -0
  141. package/dist/router/features/ux/transitions.js +235 -0
  142. package/dist/router/features/ux/transitions.js.map +1 -0
  143. package/dist/router/integration/directive-binder.d.ts +20 -0
  144. package/dist/router/integration/directive-binder.d.ts.map +1 -0
  145. package/dist/router/integration/directive-binder.js +139 -0
  146. package/dist/router/integration/directive-binder.js.map +1 -0
  147. package/dist/router/integration/directives.d.ts +42 -0
  148. package/dist/router/integration/directives.d.ts.map +1 -0
  149. package/dist/router/integration/directives.js +109 -0
  150. package/dist/router/integration/directives.js.map +1 -0
  151. package/dist/router/integration/history-adapter.d.ts +76 -0
  152. package/dist/router/integration/history-adapter.d.ts.map +1 -0
  153. package/dist/router/integration/history-adapter.js +157 -0
  154. package/dist/router/integration/history-adapter.js.map +1 -0
  155. package/dist/router/integration/hmr.d.ts +83 -0
  156. package/dist/router/integration/hmr.d.ts.map +1 -0
  157. package/dist/router/integration/hmr.js +160 -0
  158. package/dist/router/integration/hmr.js.map +1 -0
  159. package/dist/router/integration/link-interceptor.d.ts +49 -0
  160. package/dist/router/integration/link-interceptor.d.ts.map +1 -0
  161. package/dist/router/integration/link-interceptor.js +94 -0
  162. package/dist/router/integration/link-interceptor.js.map +1 -0
  163. package/package.json +47 -0
@@ -0,0 +1,95 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.router.outlet",
4
+ "title": "Router Outlet Mounting System",
5
+ "category": "runtime"
6
+ }
7
+ */
8
+ import { buildSegmentsOffDOM, commitSegmentsToDOM, } from './mount';
9
+ /** @codexApi {"parent":"pithy.router.outlet","name":"OutletManager","stability":"experimental","signature":"class OutletManager"} */
10
+ /**
11
+ * Enhanced outlet mounting system
12
+ * Implements segment diff mounting with robust outlet resolution
13
+ * Fixed memory leak with proper two-phase mounting and cleanup
14
+ */
15
+ export class OutletManager {
16
+ constructor() {
17
+ this.mountedComponents = new Map();
18
+ this.loadingStates = new Map();
19
+ this.outletElements = new Map();
20
+ this.router = null;
21
+ this.disposers = new Map();
22
+ }
23
+ /** @codexApi {"parent":"pithy.router.outlet","name":"mountSegments","stability":"experimental","signature":"(segmentChain: RouteSegmentChain[], navId: NavigationId, rootElement: HTMLElement) => Promise<boolean>"} */
24
+ /**
25
+ * Mount route segments with two-phase mounting and rollback
26
+ * Phase 1: Build everything off-DOM, Phase 2: Atomic commit
27
+ * Only remounts changed segments, preserves parent layouts
28
+ */
29
+ async mountSegments(segmentChain, navId, rootElement) {
30
+ try {
31
+ // Find the first changed segment to determine mount point
32
+ const firstChangedIndex = segmentChain.findIndex(segment => segment.hasChanged);
33
+ if (firstChangedIndex === -1) {
34
+ // No changes needed
35
+ return true;
36
+ }
37
+ // Phase 1: Build everything off-DOM first
38
+ const buildResult = await buildSegmentsOffDOM(segmentChain, firstChangedIndex, navId, this.router);
39
+ if (!buildResult.success) {
40
+ // Run disposers to clean up any partial work
41
+ buildResult.disposers.forEach(fn => fn());
42
+ return false;
43
+ }
44
+ // Phase 2: Atomic commit (replaces old nodes in one shot)
45
+ const commitResult = commitSegmentsToDOM(buildResult.fragment, buildResult.firstChangedIndex, segmentChain, rootElement, this.outletElements);
46
+ if (!commitResult.success) {
47
+ // Run disposers to clean up
48
+ buildResult.disposers.forEach(fn => fn());
49
+ return false;
50
+ }
51
+ // Register disposers on the outlet for future cleanup
52
+ const outletKey = this.getOutletKey(rootElement);
53
+ this.disposers.set(outletKey, buildResult.disposers);
54
+ return true;
55
+ }
56
+ catch (error) {
57
+ console.error('[Router] Failed to mount segments:', error);
58
+ return false;
59
+ }
60
+ }
61
+ /** @codexApi {"parent":"pithy.router.outlet","name":"setRouter","stability":"experimental","signature":"(router: RouterInstance) => void"} */
62
+ /**
63
+ * Set router instance for navigation checking
64
+ */
65
+ setRouter(router) {
66
+ this.router = router;
67
+ }
68
+ /** @codexApi {"parent":"pithy.router.outlet","name":"dispose","stability":"experimental","signature":"() => void"} */
69
+ /**
70
+ * Clean up all mounted components and state
71
+ */
72
+ dispose() {
73
+ // Clean up all mounted components
74
+ for (const component of this.mountedComponents.values()) {
75
+ if (component.cleanup) {
76
+ component.cleanup();
77
+ }
78
+ }
79
+ // Clean up all disposers
80
+ for (const disposers of this.disposers.values()) {
81
+ disposers.forEach(fn => fn());
82
+ }
83
+ this.mountedComponents.clear();
84
+ this.loadingStates.clear();
85
+ this.outletElements.clear();
86
+ this.disposers.clear();
87
+ }
88
+ /**
89
+ * Generate unique key for outlet element
90
+ */
91
+ getOutletKey(element) {
92
+ return element.id || element.className || element.tagName;
93
+ }
94
+ }
95
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/router/features/outlet/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.router.outlet.mount",
4
+ "title": "Outlet Component Mounting System",
5
+ "category": "runtime"
6
+ }
7
+ */
8
+ import type { RouteSegmentChain, NavigationId, ResourceState } from '../../core/types';
9
+ /**
10
+ * Router interface for navigation checking
11
+ */
12
+ export interface RouterInstance {
13
+ getActiveNavId(): NavigationId;
14
+ isNavActive(navId: NavigationId): boolean;
15
+ loaderData(): () => ResourceState | null;
16
+ }
17
+ /** @codexApi {"parent":"pithy.router.outlet.mount","name":"buildSegmentsOffDOM","stability":"experimental","signature":"(segmentChain: RouteSegmentChain[], firstChangedIndex: number, navId: NavigationId, router: RouterInstance | null) => Promise<BuildResult>"} */
18
+ /**
19
+ * Build segments off-DOM first (Phase 1 of two-phase mounting)
20
+ * Returns a document fragment with all segments built, along with disposers for cleanup
21
+ */
22
+ export declare function buildSegmentsOffDOM(segmentChain: RouteSegmentChain[], firstChangedIndex: number, navId: NavigationId, router: RouterInstance | null): Promise<{
23
+ success: boolean;
24
+ reason?: string | undefined;
25
+ fragment: DocumentFragment;
26
+ firstChangedIndex: number;
27
+ disposers: Array<() => void>;
28
+ }>;
29
+ /** @codexApi {"parent":"pithy.router.outlet.mount","name":"commitSegmentsToDOM","stability":"experimental","signature":"(fragment: DocumentFragment, firstChangedIndex: number, segmentChain: RouteSegmentChain[], rootElement: HTMLElement, outletElements: Map<string, HTMLElement>) => CommitResult"} */
30
+ /**
31
+ * Commit segments to DOM atomically (Phase 2 of two-phase mounting)
32
+ * Replaces the container's children with the new fragment in one operation
33
+ */
34
+ export declare function commitSegmentsToDOM(fragment: DocumentFragment, firstChangedIndex: number, segmentChain: RouteSegmentChain[], rootElement: HTMLElement, outletElements: Map<string, HTMLElement>): {
35
+ success: boolean;
36
+ reason?: string | undefined;
37
+ };
38
+ //# sourceMappingURL=mount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../../src/router/features/outlet/mount.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,OAAO,KAAK,EACV,iBAAiB,EAEjB,YAAY,EACZ,aAAa,EACd,MAAM,kBAAkB,CAAC;AAe1B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,IAAI,YAAY,CAAC;IAC/B,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC;IAC1C,UAAU,IAAI,MAAM,aAAa,GAAG,IAAI,CAAC;CAC1C;AAED,wQAAwQ;AACxQ;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,YAAY,EAAE,iBAAiB,EAAE,EACjC,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,cAAc,GAAG,IAAI,GAC5B,OAAO,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;CAC9B,CAAC,CAuDD;AAgRD,4SAA4S;AAC5S;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,gBAAgB,EAC1B,iBAAiB,EAAE,MAAM,EACzB,YAAY,EAAE,iBAAiB,EAAE,EACjC,WAAW,EAAE,WAAW,EACxB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GACvC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CA0BnD"}
@@ -0,0 +1,301 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.router.outlet.mount",
4
+ "title": "Outlet Component Mounting System",
5
+ "category": "runtime"
6
+ }
7
+ */
8
+ import { ROUTER_CONFIG } from '../../core/constants';
9
+ import { routeErrors } from '../errors/router-errors';
10
+ import { buildErrorUI, createRetryHandler } from './errors';
11
+ import { loadRouteModule, getRouteModuleKey } from '../module-loader';
12
+ /** @codexApi {"parent":"pithy.router.outlet.mount","name":"buildSegmentsOffDOM","stability":"experimental","signature":"(segmentChain: RouteSegmentChain[], firstChangedIndex: number, navId: NavigationId, router: RouterInstance | null) => Promise<BuildResult>"} */
13
+ /**
14
+ * Build segments off-DOM first (Phase 1 of two-phase mounting)
15
+ * Returns a document fragment with all segments built, along with disposers for cleanup
16
+ */
17
+ export async function buildSegmentsOffDOM(segmentChain, firstChangedIndex, navId, router) {
18
+ const fragment = document.createDocumentFragment();
19
+ const disposers = [];
20
+ for (let i = firstChangedIndex; i < segmentChain.length; i++) {
21
+ const segment = segmentChain[i];
22
+ if (!segment)
23
+ continue;
24
+ // Check if navigation is still active
25
+ if (!isNavigationActive(navId, router)) {
26
+ return {
27
+ success: false,
28
+ reason: 'superseded',
29
+ fragment,
30
+ firstChangedIndex,
31
+ disposers,
32
+ };
33
+ }
34
+ // Build this segment (pass null for outlet since we're building off-DOM)
35
+ const buildResult = await buildSegmentOffDOM(segment, navId, router);
36
+ if (!buildResult.success) {
37
+ const result = {
38
+ success: false,
39
+ fragment,
40
+ firstChangedIndex,
41
+ disposers,
42
+ };
43
+ if (buildResult.reason) {
44
+ result.reason = buildResult.reason;
45
+ }
46
+ return result;
47
+ }
48
+ // Add to fragment
49
+ fragment.appendChild(buildResult.element);
50
+ // Collect disposers
51
+ if (buildResult.dispose) {
52
+ disposers.push(buildResult.dispose);
53
+ }
54
+ }
55
+ return {
56
+ success: true,
57
+ fragment,
58
+ firstChangedIndex,
59
+ disposers,
60
+ };
61
+ }
62
+ /**
63
+ * Build a single segment off-DOM
64
+ * Handles both layout + page composition and standalone page components
65
+ */
66
+ async function buildSegmentOffDOM(segment, navId, router) {
67
+ try {
68
+ const { node } = segment;
69
+ const cleanups = [];
70
+ // Check if route has a layout
71
+ if (node.imports.layout) {
72
+ // Load layout component through shared module cache
73
+ const layoutModule = await loadRouteModule(getRouteModuleKey(node.pattern, 'layout'), node.imports.layout);
74
+ const layoutCandidate = layoutModule.default || layoutModule;
75
+ if (typeof layoutCandidate !== 'function') {
76
+ return {
77
+ success: false,
78
+ reason: 'layout load failed',
79
+ element: document.createElement('div'),
80
+ };
81
+ }
82
+ const layoutFactory = layoutCandidate;
83
+ const layoutInstance = layoutFactory();
84
+ if (!layoutInstance || !layoutInstance.el) {
85
+ return {
86
+ success: false,
87
+ reason: 'layout load failed',
88
+ element: document.createElement('div'),
89
+ };
90
+ }
91
+ cleanups.push(layoutInstance.destroy);
92
+ // Find outlet in layout
93
+ const outlet = layoutInstance.el.querySelector('[data-router-outlet]');
94
+ // Load page component
95
+ const pageComponent = await loadComponent(segment, navId, outlet, router);
96
+ if (!pageComponent) {
97
+ return {
98
+ success: false,
99
+ reason: 'page load failed',
100
+ element: document.createElement('div'),
101
+ dispose: () => cleanups.forEach(fn => fn?.()),
102
+ };
103
+ }
104
+ cleanups.push(pageComponent.cleanup);
105
+ // Insert page into layout's outlet
106
+ if (outlet) {
107
+ outlet.appendChild(pageComponent.element);
108
+ }
109
+ // Return the layout element (which now contains the page)
110
+ return {
111
+ success: true,
112
+ element: layoutInstance.el,
113
+ dispose: () => cleanups.forEach(fn => fn?.()),
114
+ };
115
+ }
116
+ // No layout - just load the page component
117
+ const component = await loadComponent(segment, navId, null, router);
118
+ if (!component) {
119
+ return {
120
+ success: false,
121
+ reason: 'component load failed',
122
+ element: document.createElement('div'),
123
+ };
124
+ }
125
+ // Check if this is an error placeholder
126
+ if (component.element instanceof HTMLElement &&
127
+ component.element.classList.contains('router-error-placeholder')) {
128
+ return {
129
+ success: true,
130
+ element: component.element,
131
+ dispose: component.cleanup,
132
+ };
133
+ }
134
+ return {
135
+ success: true,
136
+ element: component.element,
137
+ dispose: component.cleanup,
138
+ };
139
+ }
140
+ catch (error) {
141
+ console.error('[Router] Failed to build segment:', error);
142
+ return {
143
+ success: false,
144
+ reason: 'build failed',
145
+ element: document.createElement('div'),
146
+ };
147
+ }
148
+ }
149
+ /**
150
+ * Load component with error handling
151
+ * Handles route errors, dynamic imports, component instantiation, and error UI
152
+ */
153
+ async function loadComponent(segment, navId, outletElement, router) {
154
+ const { node } = segment;
155
+ if (!node.imports.page) {
156
+ return null;
157
+ }
158
+ // Check for route errors first
159
+ const errors = routeErrors();
160
+ const routeError = errors.get(node.id);
161
+ if (routeError) {
162
+ // Show error UI
163
+ const errorElement = await buildErrorUI(segment, navId, routeError, createRetryHandler());
164
+ return {
165
+ element: errorElement,
166
+ cleanup: undefined,
167
+ navId,
168
+ route: node,
169
+ };
170
+ }
171
+ try {
172
+ // Dynamic import through shared module cache
173
+ const componentModule = await loadRouteModule(getRouteModuleKey(node.pattern, 'page'), node.imports.page);
174
+ // Check if navigation is still active
175
+ if (!isNavigationActive(navId, router)) {
176
+ return null;
177
+ }
178
+ // Create component instance with error handling
179
+ const componentCandidate = componentModule.default || componentModule;
180
+ if (typeof componentCandidate !== 'function') {
181
+ throw new Error('Component factory did not return valid instance');
182
+ }
183
+ const componentFactory = componentCandidate;
184
+ // Get loader data for this route
185
+ const loaderDataGetter = router?.loaderData();
186
+ const loaderState = loaderDataGetter ? loaderDataGetter() : null;
187
+ const loaderData = loaderState?.data;
188
+ // Build component props with both params and loader data
189
+ const componentProps = {
190
+ ...segment.params,
191
+ loaderData,
192
+ };
193
+ let componentInstance;
194
+ try {
195
+ componentInstance = componentFactory(componentProps);
196
+ }
197
+ catch (componentError) {
198
+ console.error('[Router] Component instantiation failed:', componentError);
199
+ // Set route error for component instantiation failures
200
+ const { RouterError, RouterErrorCode } = await import('../errors');
201
+ const { setRouteError } = await import('../errors/router-errors');
202
+ const err = new RouterError(`Component error: ${componentError instanceof Error ? componentError.message : String(componentError)}`, {
203
+ navId: String(navId),
204
+ routeId: node.id,
205
+ parentRouteId: undefined,
206
+ routePath: node.pattern,
207
+ ts: Date.now(),
208
+ }, componentError, RouterErrorCode.COMPONENT_RENDER_ERROR);
209
+ setRouteError(node.id, err);
210
+ // Show error UI instead of throwing
211
+ const errorElement = await buildErrorUI(segment, navId, err, createRetryHandler());
212
+ return {
213
+ element: errorElement,
214
+ cleanup: undefined,
215
+ navId,
216
+ route: node,
217
+ };
218
+ }
219
+ if (!componentInstance || !componentInstance.el) {
220
+ throw new Error('Component factory did not return valid instance');
221
+ }
222
+ return {
223
+ element: componentInstance.el,
224
+ cleanup: componentInstance.destroy,
225
+ navId,
226
+ route: node,
227
+ };
228
+ }
229
+ catch (error) {
230
+ console.error('[Router] Failed to load component:', error);
231
+ // For other types of errors (import errors, validation errors, etc.)
232
+ // Set route error and show error UI
233
+ const { RouterError, RouterErrorCode } = await import('../errors');
234
+ const { setRouteError } = await import('../errors/router-errors');
235
+ const err = new RouterError(`Component load error: ${error instanceof Error ? error.message : String(error)}`, {
236
+ navId: String(navId),
237
+ routeId: node.id,
238
+ parentRouteId: undefined,
239
+ routePath: node.pattern,
240
+ ts: Date.now(),
241
+ }, error, RouterErrorCode.COMPONENT_LOAD_ERROR);
242
+ setRouteError(node.id, err);
243
+ // Show error UI instead of throwing
244
+ const errorElement = await buildErrorUI(segment, navId, err, createRetryHandler());
245
+ return {
246
+ element: errorElement,
247
+ cleanup: undefined,
248
+ navId,
249
+ route: node,
250
+ };
251
+ }
252
+ }
253
+ /** @codexApi {"parent":"pithy.router.outlet.mount","name":"commitSegmentsToDOM","stability":"experimental","signature":"(fragment: DocumentFragment, firstChangedIndex: number, segmentChain: RouteSegmentChain[], rootElement: HTMLElement, outletElements: Map<string, HTMLElement>) => CommitResult"} */
254
+ /**
255
+ * Commit segments to DOM atomically (Phase 2 of two-phase mounting)
256
+ * Replaces the container's children with the new fragment in one operation
257
+ */
258
+ export function commitSegmentsToDOM(fragment, firstChangedIndex, segmentChain, rootElement, outletElements) {
259
+ try {
260
+ // Find the container to replace (the first changed segment's container)
261
+ let containerToReplace = rootElement;
262
+ // If we have previous segments, find the container of the first changed segment
263
+ if (firstChangedIndex > 0) {
264
+ const previousSegment = segmentChain[firstChangedIndex - 1];
265
+ if (previousSegment) {
266
+ const previousSegmentKey = getSegmentKey(previousSegment);
267
+ const previousOutlet = outletElements.get(previousSegmentKey);
268
+ if (previousOutlet) {
269
+ containerToReplace = previousOutlet;
270
+ }
271
+ }
272
+ }
273
+ // Atomic replace: clear and append fragment in one operation
274
+ containerToReplace.replaceChildren(fragment);
275
+ return { success: true };
276
+ }
277
+ catch (error) {
278
+ console.error('[Router] Atomic commit failed:', error);
279
+ return { success: false, reason: 'commit failed' };
280
+ }
281
+ }
282
+ /**
283
+ * Check if navigation is still active
284
+ * This is now integrated with the router's navigation transaction system
285
+ */
286
+ function isNavigationActive(navId, router) {
287
+ if (navId === ROUTER_CONFIG.INITIAL_NAV_ID)
288
+ return true; // [OutletManager] initial mount has no transaction
289
+ if (router) {
290
+ return router.isNavActive(navId);
291
+ }
292
+ // Fallback: assume navigation is active if no router is set
293
+ return true;
294
+ }
295
+ /**
296
+ * Generate unique key for route segment
297
+ */
298
+ function getSegmentKey(segment) {
299
+ return `${segment.node.id}-${JSON.stringify(segment.params)}`;
300
+ }
301
+ //# sourceMappingURL=mount.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mount.js","sourceRoot":"","sources":["../../../../src/router/features/outlet/mount.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.router.outlet.segment-chain",
4
+ "title": "Route Segment Chain Builder",
5
+ "category": "runtime"
6
+ }
7
+ */
8
+ import type { RouteNode, RouteSegmentChain, RouteParams } from '../../core/types';
9
+ /** @codexApi {"parent":"pithy.router.outlet.segment-chain","name":"buildSegmentChain","stability":"experimental","signature":"(routes: RouteNode[], matchedPath: string, params: RouteParams, previousChain?: RouteSegmentChain[]) => RouteSegmentChain[]"} */
10
+ /**
11
+ * Build route segment chain from route match
12
+ * Supports hierarchical nested routes with layouts
13
+ * Determines which segments have changed for efficient diff mounting
14
+ */
15
+ export declare function buildSegmentChain(routes: RouteNode[], matchedPath: string, params: RouteParams, previousChain?: RouteSegmentChain[]): RouteSegmentChain[];
16
+ //# sourceMappingURL=segment-chain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segment-chain.d.ts","sourceRoot":"","sources":["../../../../src/router/features/outlet/segment-chain.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,OAAO,KAAK,EACV,SAAS,EACT,iBAAiB,EACjB,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAU1B,+PAA+P;AAC/P;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EAAE,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,EACnB,aAAa,CAAC,EAAE,iBAAiB,EAAE,GAClC,iBAAiB,EAAE,CA6DrB"}
@@ -0,0 +1,62 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.router.outlet.segment-chain",
4
+ "title": "Route Segment Chain Builder",
5
+ "category": "runtime"
6
+ }
7
+ */
8
+ import { flattenRouteTree, buildRouteMap, getAncestorChain, extractAncestorParams, mergeAncestorParams, findMatchingEntry, } from '../../core/route-tree';
9
+ /** @codexApi {"parent":"pithy.router.outlet.segment-chain","name":"buildSegmentChain","stability":"experimental","signature":"(routes: RouteNode[], matchedPath: string, params: RouteParams, previousChain?: RouteSegmentChain[]) => RouteSegmentChain[]"} */
10
+ /**
11
+ * Build route segment chain from route match
12
+ * Supports hierarchical nested routes with layouts
13
+ * Determines which segments have changed for efficient diff mounting
14
+ */
15
+ export function buildSegmentChain(routes, matchedPath, params, previousChain) {
16
+ // Flatten the route tree to enable ancestor lookup
17
+ const flatRoutes = flattenRouteTree(routes);
18
+ const routeMap = buildRouteMap(flatRoutes);
19
+ // Find the exact matching route from the flat route list
20
+ const matchingEntry = findMatchingEntry(matchedPath, flatRoutes);
21
+ if (!matchingEntry) {
22
+ return [];
23
+ }
24
+ // Get full ancestor chain (includes layouts)
25
+ const ancestorChain = getAncestorChain(matchingEntry.node.id, routeMap);
26
+ // Extract params for each ancestor level
27
+ const paramsByLevel = extractAncestorParams(matchedPath, ancestorChain);
28
+ // Merge params cumulatively (child params inherit + override parent params)
29
+ const mergedParams = mergeAncestorParams(paramsByLevel);
30
+ // Build segment chain from ancestors
31
+ const segments = [];
32
+ let previousSegment;
33
+ for (let i = 0; i < ancestorChain.length; i++) {
34
+ const routeNode = ancestorChain[i];
35
+ // Skip if routeNode is undefined (should not happen, but TypeScript safety)
36
+ if (!routeNode) {
37
+ continue;
38
+ }
39
+ const segmentParams = mergedParams[i] || {};
40
+ // Check if this segment has changed compared to previous navigation
41
+ const prevChainSegment = previousChain?.[i];
42
+ const hasChanged = !prevChainSegment ||
43
+ prevChainSegment.node.id !== routeNode.id ||
44
+ JSON.stringify(prevChainSegment.params) !== JSON.stringify(segmentParams);
45
+ // Create segment chain entry
46
+ const segment = {
47
+ node: routeNode,
48
+ params: segmentParams,
49
+ hasChanged,
50
+ parent: previousSegment,
51
+ children: [],
52
+ };
53
+ // Link parent-child relationship
54
+ if (previousSegment) {
55
+ previousSegment.children.push(segment);
56
+ }
57
+ segments.push(segment);
58
+ previousSegment = segment;
59
+ }
60
+ return segments;
61
+ }
62
+ //# sourceMappingURL=segment-chain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segment-chain.js","sourceRoot":"","sources":["../../../../src/router/features/outlet/segment-chain.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAOF,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAE/B,+PAA+P;AAC/P;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAmB,EACnB,WAAmB,EACnB,MAAmB,EACnB,aAAmC;IAEnC,mDAAmD;IACnD,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAE3C,yDAAyD;IACzD,MAAM,aAAa,GAAG,iBAAiB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAEjE,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,6CAA6C;IAC7C,MAAM,aAAa,GAAG,gBAAgB,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IAExE,yCAAyC;IACzC,MAAM,aAAa,GAAG,qBAAqB,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAExE,4EAA4E;IAC5E,MAAM,YAAY,GAAG,mBAAmB,CAAC,aAAa,CAAC,CAAC;IAExD,qCAAqC;IACrC,MAAM,QAAQ,GAAwB,EAAE,CAAC;IACzC,IAAI,eAA8C,CAAC;IAEnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,MAAM,SAAS,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QAEnC,4EAA4E;QAC5E,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,SAAS;QACX,CAAC;QAED,MAAM,aAAa,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAE5C,oEAAoE;QACpE,MAAM,gBAAgB,GAAG,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,UAAU,GACd,CAAC,gBAAgB;YACjB,gBAAgB,CAAC,IAAI,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE;YACzC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QAE5E,6BAA6B;QAC7B,MAAM,OAAO,GAAsB;YACjC,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,aAAa;YACrB,UAAU;YACV,MAAM,EAAE,eAAe;YACvB,QAAQ,EAAE,EAAE;SACb,CAAC;QAEF,iCAAiC;QACjC,IAAI,eAAe,EAAE,CAAC;YACpB,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzC,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,eAAe,GAAG,OAAO,CAAC;IAC5B,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,97 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.router.prefetch",
4
+ "title": "Enhanced Prefetch System with Intelligent Heuristics",
5
+ "category": "runtime"
6
+ }
7
+ */
8
+ /** @codexApi {"parent":"pithy.router.prefetch","name":"PrefetchStrategy","stability":"stable","signature":"type PrefetchStrategy","description":"Strategy options controlling when routes are prefetched"} */
9
+ /**
10
+ * Prefetch strategy options for intelligent resource loading
11
+ * Controls when and how routes are prefetched before user navigation
12
+ */
13
+ export type PrefetchStrategy = 'none' | 'hover' | 'visible' | 'intent';
14
+ /** @codexApi {"parent":"pithy.router.prefetch","name":"PREFETCH_STRATEGIES","stability":"stable","signature":"readonly PrefetchStrategy[]","description":"Runtime list of valid PrefetchStrategy values for enum validation"} */
15
+ export declare const PREFETCH_STRATEGIES: readonly PrefetchStrategy[];
16
+ /** @codexApi {"parent":"pithy.router.prefetch","name":"PrefetchConfig","stability":"stable","signature":"interface PrefetchConfig","description":"Fine-tune prefetch behavior with delays, thresholds, and caching"} */
17
+ /**
18
+ * Prefetch configuration options
19
+ * Fine-tune prefetch behavior with delays, thresholds, and caching settings
20
+ */
21
+ export interface PrefetchConfig {
22
+ /** Hover intent delay in milliseconds */
23
+ hoverDelay?: number;
24
+ /** Intersection observer margin for visible strategy */
25
+ visibleMargin?: string;
26
+ /** Cache TTL for prefetched resources */
27
+ cacheTTL?: number;
28
+ /** Pointer velocity threshold for intent detection */
29
+ intentVelocityThreshold?: number;
30
+ /** Pointer direction consistency required for intent */
31
+ intentDirectionSamples?: number;
32
+ }
33
+ /** @codexApi {"parent":"pithy.router.prefetch","name":"PrefetchManager","stability":"stable","signature":"class PrefetchManager","description":"Intelligent resource prefetching with enhanced heuristics"} */
34
+ /**
35
+ * Prefetch manager for intelligent resource prefetching
36
+ * Implements enhanced prefetch policy & cache layer from architectural improvements
37
+ */
38
+ export declare class PrefetchManager {
39
+ private config;
40
+ private activeRequests;
41
+ private hoverTimers;
42
+ private intentTracking;
43
+ /** Track when each route was last prefetched with its TTL to avoid repeated prefetches */
44
+ private prefetchedAt;
45
+ constructor(config?: PrefetchConfig);
46
+ /** @codexApi {"parent":"pithy.router.prefetch","name":"registerLink","stability":"stable","signature":"(element: HTMLElement, strategy: PrefetchStrategy) => void","description":"Register a link element for prefetching with a given strategy"} */
47
+ /**
48
+ * Register a link for prefetching with specified strategy
49
+ */
50
+ registerLink(element: HTMLElement, strategy: PrefetchStrategy): void;
51
+ /** @codexApi {"parent":"pithy.router.prefetch","name":"unregisterLink","stability":"stable","signature":"(element: HTMLElement) => void","description":"Unregister a link and clean up its prefetch listeners"} */
52
+ /**
53
+ * Unregister a link from prefetching
54
+ */
55
+ unregisterLink(element: HTMLElement): void;
56
+ /**
57
+ * Setup hover-based prefetching with intent delay
58
+ */
59
+ private setupHoverPrefetch;
60
+ /**
61
+ * Setup visibility-based prefetching using shared observer pool from @pithyjs/shared
62
+ */
63
+ private setupVisiblePrefetch;
64
+ /**
65
+ * Setup intent-based prefetching with pointer tracking
66
+ */
67
+ private setupIntentPrefetch;
68
+ /** @codexApi {"parent":"pithy.router.prefetch","name":"prefetchRoute","stability":"stable","signature":"(href: string, strategy: PrefetchStrategy) => Promise<void>","description":"Prefetch a route with priority handling and deduplication"} */
69
+ /**
70
+ * Prefetch a route with priority handling
71
+ */
72
+ prefetchRoute(href: string, _strategy: PrefetchStrategy): Promise<void>;
73
+ /** @codexApi {"parent":"pithy.router.prefetch","name":"cancelPrefetch","stability":"stable","signature":"(href: string) => void","description":"Cancel an in-flight prefetch for a specific route"} */
74
+ /**
75
+ * Cancel prefetch for a specific route
76
+ */
77
+ cancelPrefetch(href: string): void;
78
+ /**
79
+ * Prefetch module code (dynamic imports)
80
+ */
81
+ private prefetchModuleCode;
82
+ /**
83
+ * Prefetch loader data
84
+ */
85
+ private prefetchLoaderData;
86
+ /** @codexApi {"parent":"pithy.router.prefetch","name":"dispose","stability":"stable","signature":"() => void","description":"Clean up all prefetch resources, requests, and timers"} */
87
+ /**
88
+ * Cleanup all prefetch resources
89
+ */
90
+ dispose(): void;
91
+ }
92
+ /** @codexApi {"parent":"pithy.router.prefetch","name":"getPrefetchManager","stability":"stable","signature":"() => PrefetchManager","description":"Get the lazy-initialized global prefetch manager"} */
93
+ /**
94
+ * Get the global prefetch manager
95
+ */
96
+ export declare function getPrefetchManager(): PrefetchManager;
97
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/router/features/prefetch/index.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAiBF,8MAA8M;AAC9M;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEvE,iOAAiO;AACjO,eAAO,MAAM,mBAAmB,EAAE,SAAS,gBAAgB,EAKjD,CAAC;AAEX,wNAAwN;AACxN;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,wDAAwD;IACxD,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,+MAA+M;AAC/M;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,WAAW,CAAyD;IAC5E,OAAO,CAAC,cAAc,CAA0C;IAChE,0FAA0F;IAC1F,OAAO,CAAC,YAAY,CAAyD;gBAEjE,MAAM,GAAE,cAAmB;IAcvC,qPAAqP;IACrP;;OAEG;IACI,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAmB3E,mNAAmN;IACnN;;OAEG;IACI,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAyBjD;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA+B1B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAe5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAkD3B,mPAAmP;IACnP;;OAEG;IACU,aAAa,CACxB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,gBAAgB,GAC1B,OAAO,CAAC,IAAI,CAAC;IA0DhB,uMAAuM;IACvM;;OAEG;IACI,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQzC;;OAEG;YACW,kBAAkB;IAmBhC;;OAEG;YACW,kBAAkB;IAmBhC,wLAAwL;IACxL;;OAEG;IACI,OAAO,IAAI,IAAI;CAsBvB;AAyED,yMAAyM;AACzM;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAKpD"}