@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.
- package/LICENSE +21 -0
- package/README.md +621 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/router/core/config.d.ts +22 -0
- package/dist/router/core/config.d.ts.map +1 -0
- package/dist/router/core/config.js +54 -0
- package/dist/router/core/config.js.map +1 -0
- package/dist/router/core/constants.d.ts +89 -0
- package/dist/router/core/constants.d.ts.map +1 -0
- package/dist/router/core/constants.js +95 -0
- package/dist/router/core/constants.js.map +1 -0
- package/dist/router/core/history.d.ts +54 -0
- package/dist/router/core/history.d.ts.map +1 -0
- package/dist/router/core/history.js +89 -0
- package/dist/router/core/history.js.map +1 -0
- package/dist/router/core/matcher.d.ts +43 -0
- package/dist/router/core/matcher.d.ts.map +1 -0
- package/dist/router/core/matcher.js +242 -0
- package/dist/router/core/matcher.js.map +1 -0
- package/dist/router/core/params.d.ts +88 -0
- package/dist/router/core/params.d.ts.map +1 -0
- package/dist/router/core/params.js +150 -0
- package/dist/router/core/params.js.map +1 -0
- package/dist/router/core/pipeline.d.ts +90 -0
- package/dist/router/core/pipeline.d.ts.map +1 -0
- package/dist/router/core/pipeline.js +323 -0
- package/dist/router/core/pipeline.js.map +1 -0
- package/dist/router/core/route-tree.d.ts +76 -0
- package/dist/router/core/route-tree.d.ts.map +1 -0
- package/dist/router/core/route-tree.js +143 -0
- package/dist/router/core/route-tree.js.map +1 -0
- package/dist/router/core/router.d.ts +229 -0
- package/dist/router/core/router.d.ts.map +1 -0
- package/dist/router/core/router.js +762 -0
- package/dist/router/core/router.js.map +1 -0
- package/dist/router/core/types.d.ts +387 -0
- package/dist/router/core/types.d.ts.map +1 -0
- package/dist/router/core/types.js +22 -0
- package/dist/router/core/types.js.map +1 -0
- package/dist/router/core/utils.d.ts +66 -0
- package/dist/router/core/utils.d.ts.map +1 -0
- package/dist/router/core/utils.js +152 -0
- package/dist/router/core/utils.js.map +1 -0
- package/dist/router/features/coordinator.d.ts +75 -0
- package/dist/router/features/coordinator.d.ts.map +1 -0
- package/dist/router/features/coordinator.js +203 -0
- package/dist/router/features/coordinator.js.map +1 -0
- package/dist/router/features/errors/error-codes.d.ts +107 -0
- package/dist/router/features/errors/error-codes.d.ts.map +1 -0
- package/dist/router/features/errors/error-codes.js +247 -0
- package/dist/router/features/errors/error-codes.js.map +1 -0
- package/dist/router/features/errors/error-resolution.d.ts +31 -0
- package/dist/router/features/errors/error-resolution.d.ts.map +1 -0
- package/dist/router/features/errors/error-resolution.js +47 -0
- package/dist/router/features/errors/error-resolution.js.map +1 -0
- package/dist/router/features/errors/error-types.d.ts +60 -0
- package/dist/router/features/errors/error-types.d.ts.map +1 -0
- package/dist/router/features/errors/error-types.js +57 -0
- package/dist/router/features/errors/error-types.js.map +1 -0
- package/dist/router/features/errors/global-error-fallback.d.ts +33 -0
- package/dist/router/features/errors/global-error-fallback.d.ts.map +1 -0
- package/dist/router/features/errors/global-error-fallback.js +182 -0
- package/dist/router/features/errors/global-error-fallback.js.map +1 -0
- package/dist/router/features/errors/index.d.ts +15 -0
- package/dist/router/features/errors/index.d.ts.map +1 -0
- package/dist/router/features/errors/index.js +18 -0
- package/dist/router/features/errors/index.js.map +1 -0
- package/dist/router/features/errors/router-errors.d.ts +44 -0
- package/dist/router/features/errors/router-errors.d.ts.map +1 -0
- package/dist/router/features/errors/router-errors.js +53 -0
- package/dist/router/features/errors/router-errors.js.map +1 -0
- package/dist/router/features/guards/engine.d.ts +40 -0
- package/dist/router/features/guards/engine.d.ts.map +1 -0
- package/dist/router/features/guards/engine.js +67 -0
- package/dist/router/features/guards/engine.js.map +1 -0
- package/dist/router/features/guards/index.d.ts +52 -0
- package/dist/router/features/guards/index.d.ts.map +1 -0
- package/dist/router/features/guards/index.js +95 -0
- package/dist/router/features/guards/index.js.map +1 -0
- package/dist/router/features/loaders/cache.d.ts +60 -0
- package/dist/router/features/loaders/cache.d.ts.map +1 -0
- package/dist/router/features/loaders/cache.js +80 -0
- package/dist/router/features/loaders/cache.js.map +1 -0
- package/dist/router/features/loaders/engine.d.ts +17 -0
- package/dist/router/features/loaders/engine.d.ts.map +1 -0
- package/dist/router/features/loaders/engine.js +27 -0
- package/dist/router/features/loaders/engine.js.map +1 -0
- package/dist/router/features/loaders/index.d.ts +49 -0
- package/dist/router/features/loaders/index.d.ts.map +1 -0
- package/dist/router/features/loaders/index.js +112 -0
- package/dist/router/features/loaders/index.js.map +1 -0
- package/dist/router/features/loading-ui-manager.d.ts +69 -0
- package/dist/router/features/loading-ui-manager.d.ts.map +1 -0
- package/dist/router/features/loading-ui-manager.js +209 -0
- package/dist/router/features/loading-ui-manager.js.map +1 -0
- package/dist/router/features/module-loader.d.ts +50 -0
- package/dist/router/features/module-loader.d.ts.map +1 -0
- package/dist/router/features/module-loader.js +121 -0
- package/dist/router/features/module-loader.js.map +1 -0
- package/dist/router/features/outlet/errors.d.ts +24 -0
- package/dist/router/features/outlet/errors.d.ts.map +1 -0
- package/dist/router/features/outlet/errors.js +96 -0
- package/dist/router/features/outlet/errors.js.map +1 -0
- package/dist/router/features/outlet/index.d.ts +45 -0
- package/dist/router/features/outlet/index.d.ts.map +1 -0
- package/dist/router/features/outlet/index.js +95 -0
- package/dist/router/features/outlet/index.js.map +1 -0
- package/dist/router/features/outlet/mount.d.ts +38 -0
- package/dist/router/features/outlet/mount.d.ts.map +1 -0
- package/dist/router/features/outlet/mount.js +301 -0
- package/dist/router/features/outlet/mount.js.map +1 -0
- package/dist/router/features/outlet/segment-chain.d.ts +16 -0
- package/dist/router/features/outlet/segment-chain.d.ts.map +1 -0
- package/dist/router/features/outlet/segment-chain.js +62 -0
- package/dist/router/features/outlet/segment-chain.js.map +1 -0
- package/dist/router/features/prefetch/index.d.ts +97 -0
- package/dist/router/features/prefetch/index.d.ts.map +1 -0
- package/dist/router/features/prefetch/index.js +345 -0
- package/dist/router/features/prefetch/index.js.map +1 -0
- package/dist/router/features/ux/focus.d.ts +62 -0
- package/dist/router/features/ux/focus.d.ts.map +1 -0
- package/dist/router/features/ux/focus.js +136 -0
- package/dist/router/features/ux/focus.js.map +1 -0
- package/dist/router/features/ux/live-region.d.ts +81 -0
- package/dist/router/features/ux/live-region.d.ts.map +1 -0
- package/dist/router/features/ux/live-region.js +166 -0
- package/dist/router/features/ux/live-region.js.map +1 -0
- package/dist/router/features/ux/scroll-focus.d.ts +105 -0
- package/dist/router/features/ux/scroll-focus.d.ts.map +1 -0
- package/dist/router/features/ux/scroll-focus.js +111 -0
- package/dist/router/features/ux/scroll-focus.js.map +1 -0
- package/dist/router/features/ux/scroll.d.ts +71 -0
- package/dist/router/features/ux/scroll.d.ts.map +1 -0
- package/dist/router/features/ux/scroll.js +137 -0
- package/dist/router/features/ux/scroll.js.map +1 -0
- package/dist/router/features/ux/transitions.d.ts +120 -0
- package/dist/router/features/ux/transitions.d.ts.map +1 -0
- package/dist/router/features/ux/transitions.js +235 -0
- package/dist/router/features/ux/transitions.js.map +1 -0
- package/dist/router/integration/directive-binder.d.ts +20 -0
- package/dist/router/integration/directive-binder.d.ts.map +1 -0
- package/dist/router/integration/directive-binder.js +139 -0
- package/dist/router/integration/directive-binder.js.map +1 -0
- package/dist/router/integration/directives.d.ts +42 -0
- package/dist/router/integration/directives.d.ts.map +1 -0
- package/dist/router/integration/directives.js +109 -0
- package/dist/router/integration/directives.js.map +1 -0
- package/dist/router/integration/history-adapter.d.ts +76 -0
- package/dist/router/integration/history-adapter.d.ts.map +1 -0
- package/dist/router/integration/history-adapter.js +157 -0
- package/dist/router/integration/history-adapter.js.map +1 -0
- package/dist/router/integration/hmr.d.ts +83 -0
- package/dist/router/integration/hmr.d.ts.map +1 -0
- package/dist/router/integration/hmr.js +160 -0
- package/dist/router/integration/hmr.js.map +1 -0
- package/dist/router/integration/link-interceptor.d.ts +49 -0
- package/dist/router/integration/link-interceptor.d.ts.map +1 -0
- package/dist/router/integration/link-interceptor.js +94 -0
- package/dist/router/integration/link-interceptor.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/** @codex
|
|
2
|
+
{
|
|
3
|
+
"id": "pithy.router.core.utils",
|
|
4
|
+
"title": "Router Utility Functions",
|
|
5
|
+
"category": "runtime"
|
|
6
|
+
}
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Extract HTMLElement from various value types
|
|
10
|
+
* Supports: HTMLElement, DocumentFragment, { el: ... }, HTML string
|
|
11
|
+
*
|
|
12
|
+
* @param value - Value that may contain an element
|
|
13
|
+
* @returns HTMLElement or null if extraction failed
|
|
14
|
+
*
|
|
15
|
+
* @codexApi {"parent":"pithy.router.core.utils","name":"extractElement","stability":"stable","signature":"function extractElement(value: any): HTMLElement | null"}
|
|
16
|
+
*/
|
|
17
|
+
export function extractElement(value) {
|
|
18
|
+
if (!value)
|
|
19
|
+
return null;
|
|
20
|
+
const wrapFragment = (frag) => {
|
|
21
|
+
const wrapper = document.createElement('div');
|
|
22
|
+
// clone so callers can reuse their fragment
|
|
23
|
+
wrapper.appendChild(frag.cloneNode(true));
|
|
24
|
+
return wrapper;
|
|
25
|
+
};
|
|
26
|
+
// Direct HTMLElement
|
|
27
|
+
if (value instanceof HTMLElement) {
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
// Direct DocumentFragment
|
|
31
|
+
if (value instanceof DocumentFragment) {
|
|
32
|
+
return wrapFragment(value);
|
|
33
|
+
}
|
|
34
|
+
// { el: ... } shape
|
|
35
|
+
if (typeof value === 'object' && 'el' in value && value.el) {
|
|
36
|
+
const el = value.el;
|
|
37
|
+
if (el instanceof HTMLElement)
|
|
38
|
+
return el;
|
|
39
|
+
if (el instanceof DocumentFragment)
|
|
40
|
+
return wrapFragment(el);
|
|
41
|
+
}
|
|
42
|
+
// HTML string
|
|
43
|
+
if (typeof value === 'string') {
|
|
44
|
+
const wrapper = document.createElement('div');
|
|
45
|
+
wrapper.innerHTML = value;
|
|
46
|
+
return wrapper;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Generate a CSS selector for an element
|
|
52
|
+
* Used for scroll position restoration
|
|
53
|
+
*
|
|
54
|
+
* Priority:
|
|
55
|
+
* 1. data-remember-scroll-id attribute
|
|
56
|
+
* 2. element ID (#id)
|
|
57
|
+
* 3. data-remember-scroll attribute value
|
|
58
|
+
* 4. fallback to [data-remember-scroll]
|
|
59
|
+
*
|
|
60
|
+
* @param el - The element to generate a selector for
|
|
61
|
+
* @returns CSS selector string
|
|
62
|
+
*
|
|
63
|
+
* @codexApi {"parent":"pithy.router.core.utils","name":"selectorFor","stability":"stable","signature":"function selectorFor(el: HTMLElement): string"}
|
|
64
|
+
*/
|
|
65
|
+
export function selectorFor(el) {
|
|
66
|
+
return (el.getAttribute('data-remember-scroll-id') ||
|
|
67
|
+
(el.id && `#${CSS.escape(el.id)}`) ||
|
|
68
|
+
(el.dataset.rememberScroll &&
|
|
69
|
+
`[data-remember-scroll="${el.dataset.rememberScroll}"]`) ||
|
|
70
|
+
'[data-remember-scroll]');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Combine multiple abort signals into one
|
|
74
|
+
* Returns a signal that aborts when any of the input signals abort
|
|
75
|
+
*
|
|
76
|
+
* @param signals - Array of AbortSignals to combine
|
|
77
|
+
* @returns Combined AbortSignal
|
|
78
|
+
*
|
|
79
|
+
* @codexApi {"parent":"pithy.router.core.utils","name":"combineAbortSignals","stability":"stable","signature":"function combineAbortSignals(signals: AbortSignal[]): AbortSignal"}
|
|
80
|
+
*/
|
|
81
|
+
export function combineAbortSignals(signals) {
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
for (const signal of signals) {
|
|
84
|
+
if (signal.aborted) {
|
|
85
|
+
controller.abort();
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
signal.addEventListener('abort', () => {
|
|
89
|
+
controller.abort();
|
|
90
|
+
}, { once: true });
|
|
91
|
+
}
|
|
92
|
+
return controller.signal;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if a mouse event should be handled for navigation
|
|
96
|
+
* Filters out modified clicks (ctrl, meta, shift, alt) and non-left clicks
|
|
97
|
+
*
|
|
98
|
+
* @param event - MouseEvent to check
|
|
99
|
+
* @returns true if the event should trigger navigation
|
|
100
|
+
*
|
|
101
|
+
* @codexApi {"parent":"pithy.router.core.utils","name":"isNavigationClick","stability":"stable","signature":"function isNavigationClick(event: MouseEvent): boolean"}
|
|
102
|
+
*/
|
|
103
|
+
export function isNavigationClick(event) {
|
|
104
|
+
// Only left clicks without modifier keys
|
|
105
|
+
return (event.button === 0 &&
|
|
106
|
+
!event.metaKey &&
|
|
107
|
+
!event.ctrlKey &&
|
|
108
|
+
!event.shiftKey &&
|
|
109
|
+
!event.altKey);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Check if an anchor element should be intercepted for client-side navigation
|
|
113
|
+
* Returns null if the link should not be intercepted (let browser handle)
|
|
114
|
+
* Returns the normalized href if it should be intercepted
|
|
115
|
+
*
|
|
116
|
+
* @param anchor - Anchor element to check
|
|
117
|
+
* @param basePath - Optional base path to strip from URLs
|
|
118
|
+
* @returns Normalized href string or null
|
|
119
|
+
*
|
|
120
|
+
* @codexApi {"parent":"pithy.router.core.utils","name":"shouldInterceptLink","stability":"stable","signature":"function shouldInterceptLink(anchor: HTMLAnchorElement, basePath?: string): string | null"}
|
|
121
|
+
*/
|
|
122
|
+
export function shouldInterceptLink(anchor, basePath) {
|
|
123
|
+
// Check target attribute
|
|
124
|
+
if (anchor.target && anchor.target !== '_self')
|
|
125
|
+
return null;
|
|
126
|
+
// Check download attribute
|
|
127
|
+
if (anchor.hasAttribute('download'))
|
|
128
|
+
return null;
|
|
129
|
+
// Get raw href
|
|
130
|
+
const rawHref = anchor.getAttribute('href');
|
|
131
|
+
if (!rawHref || rawHref.startsWith('#'))
|
|
132
|
+
return null;
|
|
133
|
+
// Parse and validate URL
|
|
134
|
+
let url;
|
|
135
|
+
try {
|
|
136
|
+
url = new URL(rawHref, window.location.origin);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return null; // Invalid URL, let browser handle
|
|
140
|
+
}
|
|
141
|
+
// Check if same-origin
|
|
142
|
+
if (url.origin !== window.location.origin)
|
|
143
|
+
return null;
|
|
144
|
+
// Normalize path by stripping base path if provided
|
|
145
|
+
let pathname = url.pathname;
|
|
146
|
+
if (basePath) {
|
|
147
|
+
pathname = pathname.replace(new RegExp(`^${basePath}`), '');
|
|
148
|
+
}
|
|
149
|
+
// Return full href including search and hash
|
|
150
|
+
return pathname + url.search + url.hash;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/router/core/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** @codex
|
|
2
|
+
{
|
|
3
|
+
"id": "pithy.router.features.coordinator",
|
|
4
|
+
"title": "Feature Coordinator - Orchestrates Guards, Loaders, Loading UI, and UX Features",
|
|
5
|
+
"category": "runtime"
|
|
6
|
+
}
|
|
7
|
+
*/
|
|
8
|
+
import { type RouteNode, type RouteMatch, type NavigationId, type RouterConfig, type GuardContext as BaseGuardContext, type LoaderContext as BaseLoaderContext, type GuardExecutionResult, type ResourceState } from '../core/types';
|
|
9
|
+
import { type GuardFunction } from './guards';
|
|
10
|
+
import { LoadingUIManager } from './loading-ui-manager';
|
|
11
|
+
export type GuardContext = BaseGuardContext;
|
|
12
|
+
/**
|
|
13
|
+
* Router state updates type
|
|
14
|
+
* Partial type to avoid circular dependency with RouterState from router.ts
|
|
15
|
+
*/
|
|
16
|
+
export interface RouterStateUpdates {
|
|
17
|
+
loaderData?: ResourceState;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Loader context for loader execution (extends base with router state update)
|
|
21
|
+
*/
|
|
22
|
+
export interface LoaderContext extends BaseLoaderContext {
|
|
23
|
+
updateRouterState?: (updates: RouterStateUpdates) => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Feature Coordinator
|
|
27
|
+
* Orchestrates guards, loaders, loading UI, scroll, focus, HMR, and history
|
|
28
|
+
*
|
|
29
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"FeatureCoordinator","stability":"stable","signature":"class FeatureCoordinator"}
|
|
30
|
+
*/
|
|
31
|
+
export declare class FeatureCoordinator {
|
|
32
|
+
private guardManager;
|
|
33
|
+
private loadingUI;
|
|
34
|
+
constructor(_config: RouterConfig);
|
|
35
|
+
/**
|
|
36
|
+
* Get the loading UI manager
|
|
37
|
+
* Allows external access for router to set mounted element
|
|
38
|
+
*/
|
|
39
|
+
getLoadingUI(): LoadingUIManager;
|
|
40
|
+
/**
|
|
41
|
+
* Load guards for a specific route pattern
|
|
42
|
+
* Dynamically imports the route config and extracts guards
|
|
43
|
+
*
|
|
44
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"loadGuards","stability":"stable","signature":"async loadGuards(pattern: string, routes: RouteNode[]): Promise<GuardFunction[]>"}
|
|
45
|
+
*/
|
|
46
|
+
loadGuards(pattern: string, routes: RouteNode[]): Promise<GuardFunction[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Execute guards for a route
|
|
49
|
+
* Returns guard result with allowed status and optional redirect
|
|
50
|
+
*
|
|
51
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"executeGuards","stability":"stable","signature":"async executeGuards(guards: GuardFunction[], context: GuardContext): Promise<GuardExecutionResult>"}
|
|
52
|
+
*/
|
|
53
|
+
executeGuards(guards: GuardFunction[], context: GuardContext): Promise<GuardExecutionResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Execute loaders for a route with caching, error handling, and state updates
|
|
56
|
+
* This is the COMPLETE implementation that handles everything
|
|
57
|
+
*
|
|
58
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"executeLoaders","stability":"stable","signature":"async executeLoaders(pattern: string, context: LoaderContext, routes: RouteNode[]): Promise<boolean>"}
|
|
59
|
+
*/
|
|
60
|
+
executeLoaders(pattern: string, context: LoaderContext, routes: RouteNode[]): Promise<boolean>;
|
|
61
|
+
/**
|
|
62
|
+
* Show loading UI based on policy and route configuration
|
|
63
|
+
* Encapsulates the loading policy logic (replace vs overlay) and checks if route has loader
|
|
64
|
+
*
|
|
65
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"showLoadingUI","stability":"stable","signature":"async showLoadingUI(route: RouteNode | undefined, match: RouteMatch, navId: NavigationId, mountedElement: HTMLElement | null, loadingPolicy: string, routes: RouteNode[]): Promise<void>"}
|
|
66
|
+
*/
|
|
67
|
+
showLoadingUI(route: RouteNode | undefined, match: RouteMatch, navId: NavigationId, mountedElement: HTMLElement | null, loadingPolicy: string, routes: RouteNode[]): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Clear all loading UI
|
|
70
|
+
*
|
|
71
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"clearLoadingUI","stability":"stable","signature":"clearLoadingUI(): void"}
|
|
72
|
+
*/
|
|
73
|
+
clearLoadingUI(): void;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=coordinator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinator.d.ts","sourceRoot":"","sources":["../../../src/router/features/coordinator.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,YAAY,EAEjB,KAAK,YAAY,IAAI,gBAAgB,EACrC,KAAK,aAAa,IAAI,iBAAiB,EACvC,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAM5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AASxD,MAAM,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAE5C;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,aAAa,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,iBAAiB;IACtD,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC3D;AAED;;;;;GAKG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,SAAS,CAAmB;gBAExB,OAAO,EAAE,YAAY;IAKjC;;;OAGG;IACH,YAAY,IAAI,gBAAgB;IAIhC;;;;;OAKG;IACG,UAAU,CACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,SAAS,EAAE,GAClB,OAAO,CAAC,aAAa,EAAE,CAAC;IA4B3B;;;;;OAKG;IACG,aAAa,CACjB,MAAM,EAAE,aAAa,EAAE,EACvB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,oBAAoB,CAAC;IAIhC;;;;;OAKG;IACG,cAAc,CAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,SAAS,EAAE,GAClB,OAAO,CAAC,OAAO,CAAC;IAqInB;;;;;OAKG;IACG,aAAa,CACjB,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,YAAY,EACnB,cAAc,EAAE,WAAW,GAAG,IAAI,EAClC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,SAAS,EAAE,GAClB,OAAO,CAAC,IAAI,CAAC;IAwBhB;;;;OAIG;IACH,cAAc,IAAI,IAAI;CAGvB"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/** @codex
|
|
2
|
+
{
|
|
3
|
+
"id": "pithy.router.features.coordinator",
|
|
4
|
+
"title": "Feature Coordinator - Orchestrates Guards, Loaders, Loading UI, and UX Features",
|
|
5
|
+
"category": "runtime"
|
|
6
|
+
}
|
|
7
|
+
*/
|
|
8
|
+
import { LOADER_FAILED, } from '../core/types';
|
|
9
|
+
import { LOADER_DEFAULTS } from '../core/constants';
|
|
10
|
+
import { GuardManager } from './guards';
|
|
11
|
+
import { executeRouteLoader, } from './loaders';
|
|
12
|
+
import { LoadingUIManager } from './loading-ui-manager';
|
|
13
|
+
import { RouterError, RouterErrorCode, setRouteError, clearRouteError, } from './errors';
|
|
14
|
+
/**
|
|
15
|
+
* Feature Coordinator
|
|
16
|
+
* Orchestrates guards, loaders, loading UI, scroll, focus, HMR, and history
|
|
17
|
+
*
|
|
18
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"FeatureCoordinator","stability":"stable","signature":"class FeatureCoordinator"}
|
|
19
|
+
*/
|
|
20
|
+
export class FeatureCoordinator {
|
|
21
|
+
constructor(_config) {
|
|
22
|
+
this.guardManager = new GuardManager();
|
|
23
|
+
this.loadingUI = new LoadingUIManager();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get the loading UI manager
|
|
27
|
+
* Allows external access for router to set mounted element
|
|
28
|
+
*/
|
|
29
|
+
getLoadingUI() {
|
|
30
|
+
return this.loadingUI;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Load guards for a specific route pattern
|
|
34
|
+
* Dynamically imports the route config and extracts guards
|
|
35
|
+
*
|
|
36
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"loadGuards","stability":"stable","signature":"async loadGuards(pattern: string, routes: RouteNode[]): Promise<GuardFunction[]>"}
|
|
37
|
+
*/
|
|
38
|
+
async loadGuards(pattern, routes) {
|
|
39
|
+
// Find the route node for this pattern
|
|
40
|
+
const route = routes.find(r => r.pattern === pattern);
|
|
41
|
+
if (!route || !route.imports.config) {
|
|
42
|
+
return []; // No config file, no guards
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
// Dynamically import the route config
|
|
46
|
+
const configModule = await route.imports.config();
|
|
47
|
+
// Extract guards from the config module
|
|
48
|
+
if (configModule &&
|
|
49
|
+
configModule.guards &&
|
|
50
|
+
Array.isArray(configModule.guards)) {
|
|
51
|
+
return configModule.guards;
|
|
52
|
+
}
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error(`[Router] Failed to load guards for ${pattern}:`, error);
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Execute guards for a route
|
|
62
|
+
* Returns guard result with allowed status and optional redirect
|
|
63
|
+
*
|
|
64
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"executeGuards","stability":"stable","signature":"async executeGuards(guards: GuardFunction[], context: GuardContext): Promise<GuardExecutionResult>"}
|
|
65
|
+
*/
|
|
66
|
+
async executeGuards(guards, context) {
|
|
67
|
+
return this.guardManager.executeGuards(guards, context);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Execute loaders for a route with caching, error handling, and state updates
|
|
71
|
+
* This is the COMPLETE implementation that handles everything
|
|
72
|
+
*
|
|
73
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"executeLoaders","stability":"stable","signature":"async executeLoaders(pattern: string, context: LoaderContext, routes: RouteNode[]): Promise<boolean>"}
|
|
74
|
+
*/
|
|
75
|
+
async executeLoaders(pattern, context, routes) {
|
|
76
|
+
const route = routes.find(r => r.pattern === pattern);
|
|
77
|
+
if (!route || !route.imports.loader) {
|
|
78
|
+
return true; // No loader file = success (nothing to fail)
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
// Dynamically import the loader module
|
|
82
|
+
const loaderModule = await route.imports.loader();
|
|
83
|
+
// Extract loader function from the module
|
|
84
|
+
const loaderExport = loaderModule.loader || loaderModule.default;
|
|
85
|
+
if (typeof loaderExport !== 'function') {
|
|
86
|
+
return true; // No valid loader = success (nothing to fail)
|
|
87
|
+
}
|
|
88
|
+
const loaderFunction = loaderExport;
|
|
89
|
+
// Load route config to get cache options
|
|
90
|
+
let cacheOptions = {
|
|
91
|
+
strategy: 'swr',
|
|
92
|
+
ttl: LOADER_DEFAULTS.CACHE_TTL,
|
|
93
|
+
};
|
|
94
|
+
if (route.imports.config) {
|
|
95
|
+
try {
|
|
96
|
+
const configModule = await route.imports.config();
|
|
97
|
+
const routeCache = configModule?.cache;
|
|
98
|
+
if (routeCache && typeof routeCache === 'object') {
|
|
99
|
+
const cache = routeCache;
|
|
100
|
+
// Merge route cache config with defaults
|
|
101
|
+
cacheOptions = {
|
|
102
|
+
strategy: cache.strategy || cacheOptions.strategy,
|
|
103
|
+
ttl: cache.ttl || cacheOptions.ttl,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Use defaults if config loading fails
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Execute the loader with caching and error handling
|
|
112
|
+
try {
|
|
113
|
+
const result = await executeRouteLoader(route.id, loaderFunction, {
|
|
114
|
+
params: context.params,
|
|
115
|
+
query: context.query,
|
|
116
|
+
signal: context.signal,
|
|
117
|
+
navId: context.navId,
|
|
118
|
+
}, cacheOptions);
|
|
119
|
+
// Don't treat abort as error
|
|
120
|
+
if (context.signal.aborted) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
// Check for LOADER_FAILED sentinel
|
|
124
|
+
if (result === LOADER_FAILED) {
|
|
125
|
+
const err = new RouterError('Loader failed', {
|
|
126
|
+
navId: String(context.navId),
|
|
127
|
+
routeId: route.id,
|
|
128
|
+
parentRouteId: undefined,
|
|
129
|
+
routePath: route.pattern,
|
|
130
|
+
ts: Date.now(),
|
|
131
|
+
}, undefined, RouterErrorCode.LOADER_FAILED_SENTINEL);
|
|
132
|
+
setRouteError(route.id, err);
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
// Success: clear any previous error
|
|
136
|
+
clearRouteError(route.id);
|
|
137
|
+
// Update router state with the loaded data (if callback provided)
|
|
138
|
+
if (context.updateRouterState) {
|
|
139
|
+
context.updateRouterState({
|
|
140
|
+
loaderData: {
|
|
141
|
+
status: 'ready',
|
|
142
|
+
data: result,
|
|
143
|
+
navId: context.navId,
|
|
144
|
+
loadedAt: Date.now(),
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
catch (cause) {
|
|
151
|
+
// Don't treat abort as error
|
|
152
|
+
if (context.signal.aborted) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
console.error('[Router] Loader failed:', cause);
|
|
156
|
+
// Create and set error
|
|
157
|
+
const err = cause instanceof RouterError
|
|
158
|
+
? cause
|
|
159
|
+
: new RouterError(cause?.message ?? 'Loader failed', {
|
|
160
|
+
navId: String(context.navId),
|
|
161
|
+
routeId: route.id,
|
|
162
|
+
parentRouteId: undefined,
|
|
163
|
+
routePath: route.pattern,
|
|
164
|
+
ts: Date.now(),
|
|
165
|
+
}, cause, RouterErrorCode.LOADER_EXECUTION_ERROR);
|
|
166
|
+
setRouteError(route.id, err);
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error('[Router] Failed to load loader module:', error);
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Show loading UI based on policy and route configuration
|
|
177
|
+
* Encapsulates the loading policy logic (replace vs overlay) and checks if route has loader
|
|
178
|
+
*
|
|
179
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"showLoadingUI","stability":"stable","signature":"async showLoadingUI(route: RouteNode | undefined, match: RouteMatch, navId: NavigationId, mountedElement: HTMLElement | null, loadingPolicy: string, routes: RouteNode[]): Promise<void>"}
|
|
180
|
+
*/
|
|
181
|
+
async showLoadingUI(route, match, navId, mountedElement, loadingPolicy, routes) {
|
|
182
|
+
if (!mountedElement)
|
|
183
|
+
return;
|
|
184
|
+
const hasLoader = !!route?.imports.loader;
|
|
185
|
+
if (hasLoader) {
|
|
186
|
+
if (loadingPolicy === 'replace') {
|
|
187
|
+
await this.loadingUI.showAtomic(match.pattern, navId, mountedElement, routes);
|
|
188
|
+
}
|
|
189
|
+
else if (loadingPolicy === 'overlay') {
|
|
190
|
+
await this.loadingUI.showCustomOverlay(match.pattern, navId, mountedElement, routes);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Clear all loading UI
|
|
196
|
+
*
|
|
197
|
+
* @codexApi {"parent":"pithy.router.features.coordinator","name":"clearLoadingUI","stability":"stable","signature":"clearLoadingUI(): void"}
|
|
198
|
+
*/
|
|
199
|
+
clearLoadingUI() {
|
|
200
|
+
this.loadingUI.clearAll();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=coordinator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinator.js","sourceRoot":"","sources":["../../../src/router/features/coordinator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/** @codex
|
|
2
|
+
{
|
|
3
|
+
"id": "pithy.router.error-codes",
|
|
4
|
+
"title": "Router Error Code Taxonomy",
|
|
5
|
+
"category": "types"
|
|
6
|
+
}
|
|
7
|
+
*/
|
|
8
|
+
/** @codexApi {"parent":"pithy.router.error-codes","name":"RouterErrorCode","stability":"stable","signature":"enum RouterErrorCode"} */
|
|
9
|
+
/**
|
|
10
|
+
* Canonical error codes for router errors
|
|
11
|
+
* Provides deterministic error categorization and handling
|
|
12
|
+
*/
|
|
13
|
+
export declare enum RouterErrorCode {
|
|
14
|
+
/** Loader function threw an error */
|
|
15
|
+
LOADER_EXECUTION_ERROR = "LOADER_EXECUTION_ERROR",
|
|
16
|
+
/** Loader timed out */
|
|
17
|
+
LOADER_TIMEOUT = "LOADER_TIMEOUT",
|
|
18
|
+
/** Loader was aborted/cancelled */
|
|
19
|
+
LOADER_ABORTED = "LOADER_ABORTED",
|
|
20
|
+
/** Loader returned LOADER_FAILED sentinel */
|
|
21
|
+
LOADER_FAILED_SENTINEL = "LOADER_FAILED_SENTINEL",
|
|
22
|
+
/** Guard function threw an error */
|
|
23
|
+
GUARD_EXECUTION_ERROR = "GUARD_EXECUTION_ERROR",
|
|
24
|
+
/** Guard timed out */
|
|
25
|
+
GUARD_TIMEOUT = "GUARD_TIMEOUT",
|
|
26
|
+
/** Guard was aborted/cancelled */
|
|
27
|
+
GUARD_ABORTED = "GUARD_ABORTED",
|
|
28
|
+
/** Guard denied navigation (returned false) */
|
|
29
|
+
GUARD_DENIED = "GUARD_DENIED",
|
|
30
|
+
/** Component factory function threw error */
|
|
31
|
+
COMPONENT_FACTORY_ERROR = "COMPONENT_FACTORY_ERROR",
|
|
32
|
+
/** Component render threw error */
|
|
33
|
+
COMPONENT_RENDER_ERROR = "COMPONENT_RENDER_ERROR",
|
|
34
|
+
/** Component module failed to load */
|
|
35
|
+
COMPONENT_LOAD_ERROR = "COMPONENT_LOAD_ERROR",
|
|
36
|
+
/** Component lifecycle hook threw error */
|
|
37
|
+
COMPONENT_LIFECYCLE_ERROR = "COMPONENT_LIFECYCLE_ERROR",
|
|
38
|
+
/** Route not found for path */
|
|
39
|
+
ROUTE_NOT_FOUND = "ROUTE_NOT_FOUND",
|
|
40
|
+
/** Navigation was aborted */
|
|
41
|
+
NAVIGATION_ABORTED = "NAVIGATION_ABORTED",
|
|
42
|
+
/** Navigation was superseded by another navigation */
|
|
43
|
+
NAVIGATION_SUPERSEDED = "NAVIGATION_SUPERSEDED",
|
|
44
|
+
/** Circular redirect detected */
|
|
45
|
+
CIRCULAR_REDIRECT = "CIRCULAR_REDIRECT",
|
|
46
|
+
/** Error boundary component failed to load */
|
|
47
|
+
ERROR_BOUNDARY_LOAD_ERROR = "ERROR_BOUNDARY_LOAD_ERROR",
|
|
48
|
+
/** Error boundary component threw error during render */
|
|
49
|
+
ERROR_BOUNDARY_RENDER_ERROR = "ERROR_BOUNDARY_RENDER_ERROR",
|
|
50
|
+
/** Unknown error with no specific categorization */
|
|
51
|
+
UNKNOWN_ERROR = "UNKNOWN_ERROR"
|
|
52
|
+
}
|
|
53
|
+
/** @codexApi {"parent":"pithy.router.error-codes","name":"ErrorSeverity","stability":"stable","signature":"enum ErrorSeverity"} */
|
|
54
|
+
/**
|
|
55
|
+
* Error severity levels for prioritization and handling
|
|
56
|
+
*/
|
|
57
|
+
export declare enum ErrorSeverity {
|
|
58
|
+
/** Critical error - requires immediate attention, blocks navigation */
|
|
59
|
+
CRITICAL = "critical",
|
|
60
|
+
/** Error - navigation failed but recoverable */
|
|
61
|
+
ERROR = "error",
|
|
62
|
+
/** Warning - operation completed with issues */
|
|
63
|
+
WARNING = "warning",
|
|
64
|
+
/** Info - informational message about navigation state */
|
|
65
|
+
INFO = "info"
|
|
66
|
+
}
|
|
67
|
+
/** @codexApi {"parent":"pithy.router.error-codes","name":"ErrorRecoveryStrategy","stability":"stable","signature":"enum ErrorRecoveryStrategy"} */
|
|
68
|
+
/**
|
|
69
|
+
* Error recovery strategies for different error types
|
|
70
|
+
*/
|
|
71
|
+
export declare enum ErrorRecoveryStrategy {
|
|
72
|
+
/** Show error UI with retry button */
|
|
73
|
+
SHOW_ERROR_UI = "show-error-ui",
|
|
74
|
+
/** Redirect to fallback route */
|
|
75
|
+
REDIRECT_FALLBACK = "redirect-fallback",
|
|
76
|
+
/** Silent fail - log error but don't show UI */
|
|
77
|
+
SILENT_FAIL = "silent-fail",
|
|
78
|
+
/** Retry operation automatically */
|
|
79
|
+
AUTO_RETRY = "auto-retry",
|
|
80
|
+
/** Throw to parent error boundary */
|
|
81
|
+
BUBBLE_UP = "bubble-up"
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Metadata for each error code including severity and recovery strategy
|
|
85
|
+
*/
|
|
86
|
+
interface ErrorCodeMetadata {
|
|
87
|
+
code: RouterErrorCode;
|
|
88
|
+
severity: ErrorSeverity;
|
|
89
|
+
defaultStrategy: ErrorRecoveryStrategy;
|
|
90
|
+
userMessage: string;
|
|
91
|
+
retryable: boolean;
|
|
92
|
+
}
|
|
93
|
+
/** @codexApi {"parent":"pithy.router.error-codes","name":"ERROR_TAXONOMY","stability":"stable","signature":"Record<RouterErrorCode, ErrorCodeMetadata>"} */
|
|
94
|
+
/**
|
|
95
|
+
* Complete error taxonomy with metadata for each error code
|
|
96
|
+
* Maps error codes to their handling characteristics
|
|
97
|
+
*/
|
|
98
|
+
export declare const ERROR_TAXONOMY: Record<RouterErrorCode, ErrorCodeMetadata>;
|
|
99
|
+
/** @codexApi {"parent":"pithy.router.error-codes","name":"getErrorMetadata","stability":"stable","signature":"(code: RouterErrorCode) => ErrorCodeMetadata"} */
|
|
100
|
+
/**
|
|
101
|
+
* Get error metadata for a specific error code
|
|
102
|
+
* Provides severity, recovery strategy, and user message
|
|
103
|
+
* Used internally by RouterError instance methods
|
|
104
|
+
*/
|
|
105
|
+
export declare function getErrorMetadata(code: RouterErrorCode): ErrorCodeMetadata;
|
|
106
|
+
export {};
|
|
107
|
+
//# sourceMappingURL=error-codes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-codes.d.ts","sourceRoot":"","sources":["../../../../src/router/features/errors/error-codes.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,uIAAuI;AACvI;;;GAGG;AACH,oBAAY,eAAe;IAEzB,qCAAqC;IACrC,sBAAsB,2BAA2B;IACjD,uBAAuB;IACvB,cAAc,mBAAmB;IACjC,mCAAmC;IACnC,cAAc,mBAAmB;IACjC,6CAA6C;IAC7C,sBAAsB,2BAA2B;IAGjD,oCAAoC;IACpC,qBAAqB,0BAA0B;IAC/C,sBAAsB;IACtB,aAAa,kBAAkB;IAC/B,kCAAkC;IAClC,aAAa,kBAAkB;IAC/B,+CAA+C;IAC/C,YAAY,iBAAiB;IAG7B,6CAA6C;IAC7C,uBAAuB,4BAA4B;IACnD,mCAAmC;IACnC,sBAAsB,2BAA2B;IACjD,sCAAsC;IACtC,oBAAoB,yBAAyB;IAC7C,2CAA2C;IAC3C,yBAAyB,8BAA8B;IAGvD,+BAA+B;IAC/B,eAAe,oBAAoB;IACnC,6BAA6B;IAC7B,kBAAkB,uBAAuB;IACzC,sDAAsD;IACtD,qBAAqB,0BAA0B;IAC/C,iCAAiC;IACjC,iBAAiB,sBAAsB;IAGvC,8CAA8C;IAC9C,yBAAyB,8BAA8B;IACvD,yDAAyD;IACzD,2BAA2B,gCAAgC;IAG3D,oDAAoD;IACpD,aAAa,kBAAkB;CAChC;AAED,mIAAmI;AACnI;;GAEG;AACH,oBAAY,aAAa;IACvB,uEAAuE;IACvE,QAAQ,aAAa;IACrB,gDAAgD;IAChD,KAAK,UAAU;IACf,gDAAgD;IAChD,OAAO,YAAY;IACnB,0DAA0D;IAC1D,IAAI,SAAS;CACd;AAED,mJAAmJ;AACnJ;;GAEG;AACH,oBAAY,qBAAqB;IAC/B,sCAAsC;IACtC,aAAa,kBAAkB;IAC/B,iCAAiC;IACjC,iBAAiB,sBAAsB;IACvC,gDAAgD;IAChD,WAAW,gBAAgB;IAC3B,oCAAoC;IACpC,UAAU,eAAe;IACzB,qCAAqC;IACrC,SAAS,cAAc;CACxB;AAED;;GAEG;AACH,UAAU,iBAAiB;IACzB,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,EAAE,aAAa,CAAC;IACxB,eAAe,EAAE,qBAAqB,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,4JAA4J;AAC5J;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,eAAe,EAAE,iBAAiB,CAiJrE,CAAC;AAEF,gKAAgK;AAChK;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAEzE"}
|