@askrjs/askr 0.2.4 → 0.3.1
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/CHANGELOG.md +30 -0
- package/dist/access-CEA2pOry.js +1046 -0
- package/dist/actions/index.d.ts +16 -2
- package/dist/actions/index.js +5 -5
- package/dist/{activity-FirwPXA_.js → activity-oy2YFTFF.js} +10 -16
- package/dist/benchmark.js +8 -9
- package/dist/boot/index.d.ts +19 -9
- package/dist/boot/index.js +1 -1
- package/dist/{boot-tWyF6121.js → boot-DLplW9h4.js} +700 -441
- package/dist/capabilities-Bbf0mlp9.js +42 -0
- package/dist/components/index.d.ts +11 -8
- package/dist/components/index.js +1 -1
- package/dist/{compose-ref-Bfaf4kUC.js → compose-ref-v0qGlHoN.js} +7 -1
- package/dist/control/index.d.ts +20 -2
- package/dist/control/index.js +1 -1
- package/dist/{control-C-wSaq9V.js → control-BR_mrX__.js} +4 -4
- package/dist/core.d.ts +163 -0
- package/dist/csp-nonce-BfFWcd0w.js +173 -0
- package/dist/data/index.d.ts +74 -2
- package/dist/data/index.js +3 -3
- package/dist/{data-ghKopAbp.js → data-2edW0jhF.js} +108 -122
- package/dist/{data-runtime-7t_Y0iv3.js → data-runtime-CxoY98lX.js} +22 -15
- package/dist/{deferred-CxjWCkIn.js → deferred-CbgNhf67.js} +5 -5
- package/dist/dom-renderer.d.ts +77 -0
- package/dist/domains/actions.d.ts +70 -0
- package/dist/domains/component.d.ts +119 -0
- package/dist/domains/context.d.ts +73 -0
- package/dist/domains/control.d.ts +328 -0
- package/dist/domains/data.d.ts +461 -0
- package/dist/domains/diagnostics.d.ts +21 -0
- package/dist/domains/lifecycle.d.ts +112 -0
- package/dist/domains/refs.d.ts +18 -0
- package/dist/domains/renderer.d.ts +127 -0
- package/dist/domains/routing.d.ts +501 -0
- package/dist/domains/scheduler.d.ts +79 -0
- package/dist/domains/server.d.ts +112 -0
- package/dist/domains/state.d.ts +129 -0
- package/dist/domains/telemetry.d.ts +29 -0
- package/dist/eager-control.d.ts +8 -0
- package/dist/{effect-CNwstBBD.js → effect-C3Zsc1k6.js} +14 -41
- package/dist/{types-DWVrKn9N.d.ts → elements.d.ts} +79 -42
- package/dist/{for-internal-DyBHfKJA.js → for-state-BDykeOJL.js} +598 -494
- package/dist/foundations/icon/index.d.ts +67 -16
- package/dist/foundations/icon/index.js +1 -1
- package/dist/foundations/index.d.ts +26 -3
- package/dist/foundations/index.js +1 -1
- package/dist/foundations/interactions/index.d.ts +73 -34
- package/dist/foundations/interactions/index.js +1 -1
- package/dist/foundations/state/index.d.ts +12 -5
- package/dist/foundations/state/index.js +1 -1
- package/dist/foundations/structures/index.d.ts +44 -3
- package/dist/foundations/structures/index.js +1 -1
- package/dist/foundations/utilities/index.d.ts +34 -3
- package/dist/foundations/utilities/index.js +1 -1
- package/dist/fx/index.d.ts +72 -25
- package/dist/fx/index.js +8 -8
- package/dist/index.d.ts +121 -4
- package/dist/index.js +316 -47
- package/dist/{component-internal-C4hKKQBF.js → instance-VAFYVJOa.js} +495 -283
- package/dist/{interactions-D102JxBR.js → interactions-CTeACdT_.js} +1 -1
- package/dist/jsx-dev-runtime.d.ts +51 -11
- package/dist/jsx-globals.d.ts +2 -0
- package/dist/jsx-runtime-CPJXAOBF.js +43 -0
- package/dist/jsx-runtime.d.ts +17 -3
- package/dist/jsx-runtime.js +2 -42
- package/dist/jsx.d.ts +108 -0
- package/dist/{manifest-D6rfj_jC.js → manifest-D1P3UNWV.js} +4 -9
- package/dist/{navigate-ZZu1IV0B.js → navigate-prC5V8XV.js} +125 -472
- package/dist/{navigate-F4sHgWbw.d.ts → navigation.d.ts} +8 -8
- package/dist/{readable-Di1fzSwm.js → notify-DxxuDPeY.js} +96 -34
- package/dist/{query-registry-Bua3cDyb.js → query-registry-LrYpQsDJ.js} +1 -1
- package/dist/{compose-ref-DOJHoW5X.d.ts → refs.d.ts} +11 -7
- package/dist/{ssr-Doog-AfJ.js → render-resolved-BC0W7_5b.js} +165 -558
- package/dist/{renderer-Cl23O0hV.js → renderer-CT0HeY58.js} +6825 -5692
- package/dist/{resolution-ClD-K7Lz.js → resolution-3DskUkjG.js} +189 -180
- package/dist/{resource-operation-Vz1S5h2k.js → resource-18-sFRl9.js} +98 -58
- package/dist/resources/index.d.ts +64 -12
- package/dist/resources/index.js +214 -8
- package/dist/route-activity.d.ts +21 -0
- package/dist/{route-matching-Bo6UqG-5.js → route-matching-CUzZ31gT.js} +68 -82
- package/dist/router/index.d.ts +378 -79
- package/dist/router/index.js +9 -9
- package/dist/router-internal-7nuNEHfG.js +36 -0
- package/dist/scope-Cq_iYbTR.js +403 -0
- package/dist/{selector-CsKuomw0.js → selector-BSPVkjKP.js} +26 -27
- package/dist/{shared-CYBH3F8I.js → shared-kU8yvryp.js} +3 -6
- package/dist/{snapshot-source-BB42xgwb.js → snapshot-source-An4iKajG.js} +1 -1
- package/dist/ssg/index.d.ts +51 -15
- package/dist/ssg/index.js +269 -209
- package/dist/ssr/index.d.ts +121 -68
- package/dist/ssr/index.js +2 -1
- package/dist/ssr-CiNm6Vzl.js +393 -0
- package/dist/{ssr-DSHd_Q9K.js → ssr-EPjwJH5R.js} +5 -1
- package/dist/{state-BGJgYSyQ.js → state-BXJfudq1.js} +10 -15
- package/dist/{structures-DFyK5AdQ.js → structures-CjEzfj4h.js} +37 -57
- package/dist/{index-CTpMmzgN.d.ts → structures.d.ts} +57 -36
- package/dist/testing/index.d.ts +123 -24
- package/dist/testing/index.js +24 -61
- package/dist/timer-C7uqnJVt.js +85 -0
- package/dist/{index-B1gu0_m0.d.ts → utilities.d.ts} +24 -13
- package/dist/{verify-hydration-BZPJ_kYa.js → verify-hydration-ikaxotf7.js} +8 -5
- package/package.json +12 -14
- package/dist/access-BucjYZcX.js +0 -509
- package/dist/activity-DooIuxQO.d.ts +0 -14
- package/dist/benchmark.d.ts +0 -26
- package/dist/cleanup-TXSsApEl.js +0 -535
- package/dist/component-cleanup-jdbgjkT1.js +0 -143
- package/dist/component-scope-4I3yEMkb.js +0 -195
- package/dist/control-Cxag1yqV.d.ts +0 -8
- package/dist/csp-nonce-9NFUb7hJ.js +0 -72
- package/dist/css-BuPaTOfe.js +0 -70
- package/dist/fastlane-BKZduiBQ.js +0 -166
- package/dist/index-Dgjbo2gU.d.ts +0 -1456
- package/dist/jsx-runtime-Cg7GqY1S.d.ts +0 -2
- package/dist/jsx-runtime-gVIaTPw0.d.ts +0 -38
- package/dist/lifecycle-batch-63aXFf2S.js +0 -253
- package/dist/lifecycle-operations-Q0k3EFnG.js +0 -287
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { JSXElementType, JSXElement, Props } from '../elements.js';
|
|
2
|
+
import '../jsx-globals.js';
|
|
3
|
+
import { AuthContext, AuthRequirement } from '@askrjs/auth';
|
|
4
|
+
import { InferSchema, ObjectSchema } from '@askrjs/schema';
|
|
5
|
+
import { VNode, ContextFrame } from './context.js';
|
|
6
|
+
import { ComponentInstance, ReadableSource } from './component.js';
|
|
7
|
+
import { Scheduler } from './scheduler.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Internal DOM range shape shared by runtime ownership records and the
|
|
11
|
+
* renderer. A singleton range uses the node itself for both anchors; a
|
|
12
|
+
* multi-node or empty range uses deterministic comment anchors.
|
|
13
|
+
*/
|
|
14
|
+
interface DOMRange {
|
|
15
|
+
start: Node;
|
|
16
|
+
end: Node;
|
|
17
|
+
single: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ChildScope {
|
|
21
|
+
key: string | number;
|
|
22
|
+
componentInstance: ComponentInstance;
|
|
23
|
+
previousVnode: VNode | undefined;
|
|
24
|
+
vnode: VNode | undefined;
|
|
25
|
+
dom?: Node;
|
|
26
|
+
/** @internal Fast singleton node plus an anchor-backed multi-node range. */
|
|
27
|
+
range?: DOMRange;
|
|
28
|
+
needsDomUpdate: boolean;
|
|
29
|
+
hydrationPending: boolean;
|
|
30
|
+
/** @internal Stable owner for validated intrinsic blueprints in list items. */
|
|
31
|
+
blueprintOwner?: object;
|
|
32
|
+
render(renderFn: () => VNode): VNode;
|
|
33
|
+
markDirty(): void;
|
|
34
|
+
dispose(): void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ChildScopeOwnership {
|
|
38
|
+
add(scope: ChildScope): void;
|
|
39
|
+
delete(scope: ChildScope): void;
|
|
40
|
+
bulkDispose(run: () => void): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** @internal Snapshot used to restore a child scope after a failed commit. */
|
|
44
|
+
interface ChildScopeTransactionSnapshot {
|
|
45
|
+
previousVnode: VNode | undefined;
|
|
46
|
+
vnode: VNode | undefined;
|
|
47
|
+
dom: Node | undefined;
|
|
48
|
+
range: DOMRange | undefined;
|
|
49
|
+
domTextData: string | undefined;
|
|
50
|
+
needsDomUpdate: boolean;
|
|
51
|
+
hydrationPending: boolean;
|
|
52
|
+
renderFn: (() => VNode) | undefined;
|
|
53
|
+
renderedOwnerFrame: ContextFrame | null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Diagnostic breakdown of a keyed-list reorder decision, returned by {@link RuntimeRendererHost.isKeyedReorderFastPathEligible}. */
|
|
57
|
+
interface RuntimeKeyedReorderDecision {
|
|
58
|
+
useFastPath: boolean;
|
|
59
|
+
totalKeyed: number;
|
|
60
|
+
totalChildren: number;
|
|
61
|
+
currentKeyCount: number;
|
|
62
|
+
moveCount: number;
|
|
63
|
+
lisLen: number;
|
|
64
|
+
hasPropChanges: boolean;
|
|
65
|
+
isWholeKeyedList: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The renderer implementation an {@link AskrRuntime} delegates DOM evaluation and cleanup to. */
|
|
69
|
+
interface RuntimeRendererHost {
|
|
70
|
+
evaluate(
|
|
71
|
+
node: unknown,
|
|
72
|
+
target: Element | null,
|
|
73
|
+
context?: object,
|
|
74
|
+
retainedOwner?: ComponentInstance
|
|
75
|
+
): void;
|
|
76
|
+
cleanupInstancesUnder(node: Node): void;
|
|
77
|
+
replaceComponentRange(
|
|
78
|
+
instance: ComponentInstance,
|
|
79
|
+
result: unknown,
|
|
80
|
+
host: Element | Comment
|
|
81
|
+
): Node | null;
|
|
82
|
+
resolveChildScopeRange?(scope: ChildScope): DOMRange | null;
|
|
83
|
+
teardownNodeSubtree(root: Node): void;
|
|
84
|
+
populateKeyMapForElement(parent: Element): void;
|
|
85
|
+
getKeyMapForElement(
|
|
86
|
+
parent: Element
|
|
87
|
+
): Map<string | number, Element> | undefined;
|
|
88
|
+
isKeyedReorderFastPathEligible(
|
|
89
|
+
parent: Element,
|
|
90
|
+
children: unknown[],
|
|
91
|
+
oldKeyMap: Map<string | number, Element> | undefined
|
|
92
|
+
): RuntimeKeyedReorderDecision;
|
|
93
|
+
markReactivePropsDirtySource(source: ReadableSource<unknown>): void;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Options for {@link createRuntime}. */
|
|
97
|
+
interface AskrRuntimeOptions {
|
|
98
|
+
scheduler?: Scheduler;
|
|
99
|
+
renderer?: RuntimeRendererHost;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Construction-only scheduler and renderer wiring. Mounting uses the default runtime. */
|
|
103
|
+
declare class AskrRuntime {
|
|
104
|
+
readonly scheduler: Scheduler;
|
|
105
|
+
private rendererHost;
|
|
106
|
+
constructor(options?: AskrRuntimeOptions);
|
|
107
|
+
get renderer(): RuntimeRendererHost;
|
|
108
|
+
configureRenderer(renderer: RuntimeRendererHost): void;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Create construction-only runtime wiring. Omitted schedulers share the default scheduler; mounting uses the default runtime. */
|
|
112
|
+
declare function createRuntime(options?: AskrRuntimeOptions): AskrRuntime;
|
|
113
|
+
|
|
114
|
+
/** Get the process-wide default {@link AskrRuntime}. */
|
|
115
|
+
declare function getDefaultRuntime(): AskrRuntime;
|
|
116
|
+
export {
|
|
117
|
+
DOMRange,
|
|
118
|
+
ChildScope,
|
|
119
|
+
ChildScopeOwnership,
|
|
120
|
+
ChildScopeTransactionSnapshot,
|
|
121
|
+
RuntimeKeyedReorderDecision,
|
|
122
|
+
RuntimeRendererHost,
|
|
123
|
+
AskrRuntimeOptions,
|
|
124
|
+
AskrRuntime,
|
|
125
|
+
createRuntime,
|
|
126
|
+
getDefaultRuntime,
|
|
127
|
+
};
|
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
import { JSXElementType, JSXElement, Props } from '../elements.js';
|
|
2
|
+
import '../jsx-globals.js';
|
|
3
|
+
import { AuthContext, AuthRequirement } from '@askrjs/auth';
|
|
4
|
+
import { InferSchema, ObjectSchema } from '@askrjs/schema';
|
|
5
|
+
import { state, selector } from './state.js';
|
|
6
|
+
import { RenderableChild } from './context.js';
|
|
7
|
+
import { QueryPrefetchContext } from './data.js';
|
|
8
|
+
import { CoreTelemetry } from './telemetry.js';
|
|
9
|
+
import { on, capture } from './lifecycle.js';
|
|
10
|
+
import { For } from './control.js';
|
|
11
|
+
import { ActionDescriptor } from './actions.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Common call contracts: Router types
|
|
15
|
+
*/
|
|
16
|
+
/** Path parameters captured for a matched route, keyed by parameter name. */
|
|
17
|
+
type RouteParams = Record<string, string>;
|
|
18
|
+
|
|
19
|
+
type StripRoutePathSuffix<Path extends string> =
|
|
20
|
+
Path extends `${infer Base}?${string}`
|
|
21
|
+
? StripRoutePathSuffix<Base>
|
|
22
|
+
: Path extends `${infer Base}#${string}`
|
|
23
|
+
? StripRoutePathSuffix<Base>
|
|
24
|
+
: Path;
|
|
25
|
+
|
|
26
|
+
type TrimRoutePathSlashes<Path extends string> = Path extends `/${infer Rest}`
|
|
27
|
+
? TrimRoutePathSlashes<Rest>
|
|
28
|
+
: Path extends `${infer Rest}/`
|
|
29
|
+
? TrimRoutePathSlashes<Rest>
|
|
30
|
+
: Path;
|
|
31
|
+
|
|
32
|
+
type TrimRoutePathWhitespace<Path extends string> =
|
|
33
|
+
Path extends `${' ' | '\n' | '\t' | '\r'}${infer Rest}`
|
|
34
|
+
? TrimRoutePathWhitespace<Rest>
|
|
35
|
+
: Path extends `${infer Rest}${' ' | '\n' | '\t' | '\r'}`
|
|
36
|
+
? TrimRoutePathWhitespace<Rest>
|
|
37
|
+
: Path;
|
|
38
|
+
|
|
39
|
+
type ExtractRouteSegmentParam<Segment extends string> =
|
|
40
|
+
Segment extends `{${infer Param}}`
|
|
41
|
+
? TrimRoutePathWhitespace<Param> extends `*${infer SplatParam}`
|
|
42
|
+
? TrimRoutePathWhitespace<SplatParam> extends ''
|
|
43
|
+
? never
|
|
44
|
+
: TrimRoutePathWhitespace<SplatParam> extends '*'
|
|
45
|
+
? never
|
|
46
|
+
: TrimRoutePathWhitespace<SplatParam>
|
|
47
|
+
: TrimRoutePathWhitespace<Param> extends ''
|
|
48
|
+
? never
|
|
49
|
+
: TrimRoutePathWhitespace<Param>
|
|
50
|
+
: Segment extends '*'
|
|
51
|
+
? '*'
|
|
52
|
+
: never;
|
|
53
|
+
|
|
54
|
+
type ExtractRoutePathParamNames<Path extends string> =
|
|
55
|
+
TrimRoutePathSlashes<
|
|
56
|
+
StripRoutePathSuffix<Path>
|
|
57
|
+
> extends `${infer Segment}/${infer Rest}`
|
|
58
|
+
? ExtractRouteSegmentParam<Segment> | ExtractRoutePathParamNames<Rest>
|
|
59
|
+
: ExtractRouteSegmentParam<
|
|
60
|
+
TrimRoutePathSlashes<StripRoutePathSuffix<Path>>
|
|
61
|
+
>;
|
|
62
|
+
|
|
63
|
+
/** Statically infers the param record shape from a route path string literal, e.g. `/posts/{id}`. */
|
|
64
|
+
type RoutePathParams<Path extends string> = [
|
|
65
|
+
ExtractRoutePathParamNames<Path>,
|
|
66
|
+
] extends [never]
|
|
67
|
+
? Record<never, string>
|
|
68
|
+
: { [Key in ExtractRoutePathParamNames<Path>]: string };
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* A route page component: a regular component that receives route params as
|
|
72
|
+
* props derived from the URL pattern.
|
|
73
|
+
*
|
|
74
|
+
* Components may accept no params at all — zero-argument components are still
|
|
75
|
+
* assignable.
|
|
76
|
+
*/
|
|
77
|
+
type RouteComponent<TParams extends RouteParams = RouteParams> = (
|
|
78
|
+
props: TParams
|
|
79
|
+
) => RenderableChild;
|
|
80
|
+
|
|
81
|
+
/** The rendering mode a route is currently being evaluated under. */
|
|
82
|
+
type RouteMode = 'spa' | 'ssr' | 'ssg';
|
|
83
|
+
|
|
84
|
+
type AccessRedirectStatus = 301 | 302 | 303 | 307 | 308;
|
|
85
|
+
|
|
86
|
+
type AccessDenyStatus = 401 | 403 | 404;
|
|
87
|
+
|
|
88
|
+
interface AccessAllowDecision {
|
|
89
|
+
kind: 'allow';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Policy decision produced by {@link redirect}: sends the visitor to another URL. */
|
|
93
|
+
interface AccessRedirectDecision {
|
|
94
|
+
kind: 'redirect';
|
|
95
|
+
to: string;
|
|
96
|
+
status?: AccessRedirectStatus;
|
|
97
|
+
replace?: boolean;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Policy decision produced by {@link deny}/{@link unauthorized}/{@link forbidden}/{@link notFound}. */
|
|
101
|
+
interface AccessDenyDecision {
|
|
102
|
+
kind: 'deny';
|
|
103
|
+
status: AccessDenyStatus;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Outcome of a {@link RoutePolicy} evaluation: allow, redirect, or deny. */
|
|
107
|
+
type AccessDecision =
|
|
108
|
+
| AccessAllowDecision
|
|
109
|
+
| AccessRedirectDecision
|
|
110
|
+
| AccessDenyDecision;
|
|
111
|
+
|
|
112
|
+
/** Context passed to route policies, auth resolvers, and loaders. */
|
|
113
|
+
interface RouteContext<TParams extends RouteParams = RouteParams> {
|
|
114
|
+
mode: RouteMode;
|
|
115
|
+
params: TParams;
|
|
116
|
+
pathname: string;
|
|
117
|
+
search: string;
|
|
118
|
+
hash: string;
|
|
119
|
+
href: string;
|
|
120
|
+
auth: AuthContext;
|
|
121
|
+
signal: AbortSignal;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** A route access-control check, evaluated against {@link RouteContext} to produce an {@link AccessDecision}. */
|
|
125
|
+
type RoutePolicy = (
|
|
126
|
+
context: RouteContext
|
|
127
|
+
) => AccessDecision | PromiseLike<AccessDecision>;
|
|
128
|
+
|
|
129
|
+
/** Resolves the {@link AuthContext} for a route request. */
|
|
130
|
+
type RouteAuthResolver = (
|
|
131
|
+
context: Omit<RouteContext, 'auth'>
|
|
132
|
+
) => AuthContext | PromiseLike<AuthContext>;
|
|
133
|
+
|
|
134
|
+
/** Auth configuration shared across a route registry or a single route. */
|
|
135
|
+
interface RouteAuthOptions {
|
|
136
|
+
resolve: RouteAuthResolver;
|
|
137
|
+
loginPath?:
|
|
138
|
+
| string
|
|
139
|
+
| ((context: RouteContext) => string | PromiseLike<string>);
|
|
140
|
+
authenticatedRedirectTo?:
|
|
141
|
+
| string
|
|
142
|
+
| ((context: RouteContext) => string | PromiseLike<string>);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
interface CommonAccessOptions {
|
|
146
|
+
auth?: AuthRequirement;
|
|
147
|
+
policies?: readonly RoutePolicy[];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** A route's metadata, or a function computing it from the resolved context. */
|
|
151
|
+
type RouteMetaSource<TParams extends RouteParams = RouteParams> =
|
|
152
|
+
| RouteMeta
|
|
153
|
+
| ((context: RouteContext<TParams>) => RouteMeta | PromiseLike<RouteMeta>);
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Options for `route()` declarations.
|
|
157
|
+
*
|
|
158
|
+
* - `loader`: server data loader called before render, result passed as SSR data
|
|
159
|
+
* - `entries`: SSG entry generator — returns one param map per static page
|
|
160
|
+
* - `title`: page title hint used by SSG and document-meta integrations
|
|
161
|
+
* - `namespace`: MFE namespace key for grouped route management
|
|
162
|
+
*/
|
|
163
|
+
interface RouteOptions<
|
|
164
|
+
TParams extends RouteParams = RouteParams,
|
|
165
|
+
TSearchSchema extends ObjectSchema<RouteSearch> | undefined =
|
|
166
|
+
| ObjectSchema<RouteSearch>
|
|
167
|
+
| undefined,
|
|
168
|
+
TLoaderData = unknown,
|
|
169
|
+
TDehydratedData = TLoaderData,
|
|
170
|
+
> extends CommonAccessOptions {
|
|
171
|
+
loader?: (
|
|
172
|
+
context: RouteContext<TParams> & {
|
|
173
|
+
request?: Request;
|
|
174
|
+
}
|
|
175
|
+
) => TLoaderData | PromiseLike<TLoaderData>;
|
|
176
|
+
/**
|
|
177
|
+
* Select the loader data transported to the browser for initial hydration.
|
|
178
|
+
*
|
|
179
|
+
* Server rendering still receives the complete loader value. The selector
|
|
180
|
+
* must be synchronous; client navigations rerun the loader and receive its
|
|
181
|
+
* complete result.
|
|
182
|
+
*/
|
|
183
|
+
dehydrate?: (
|
|
184
|
+
data: TLoaderData,
|
|
185
|
+
context: RouteContext<TParams> & {
|
|
186
|
+
request?: Request;
|
|
187
|
+
}
|
|
188
|
+
) => TDehydratedData extends PromiseLike<unknown> ? never : TDehydratedData;
|
|
189
|
+
preload?: (
|
|
190
|
+
context: RouteContext<TParams> & {
|
|
191
|
+
request?: Request;
|
|
192
|
+
data: QueryPrefetchContext;
|
|
193
|
+
}
|
|
194
|
+
) => unknown;
|
|
195
|
+
entries?: () => Array<TParams> | Promise<Array<TParams>>;
|
|
196
|
+
/** Optional invalidation keys used by incremental SSG generation. */
|
|
197
|
+
invalidationKeys?: readonly string[];
|
|
198
|
+
title?: string;
|
|
199
|
+
namespace?: string;
|
|
200
|
+
search?: TSearchSchema;
|
|
201
|
+
meta?: RouteMetaSource<TParams>;
|
|
202
|
+
actions?: readonly ActionDescriptor[];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
interface RouteMeta {
|
|
206
|
+
title?: string;
|
|
207
|
+
description?: string;
|
|
208
|
+
canonical?: string;
|
|
209
|
+
robots?: string;
|
|
210
|
+
openGraph?: Record<string, string>;
|
|
211
|
+
links?: readonly {
|
|
212
|
+
rel: string;
|
|
213
|
+
href: string;
|
|
214
|
+
[key: string]: string;
|
|
215
|
+
}[];
|
|
216
|
+
jsonLd?: unknown | readonly unknown[];
|
|
217
|
+
html?: {
|
|
218
|
+
lang?: string;
|
|
219
|
+
dir?: 'ltr' | 'rtl' | 'auto';
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** A stable, typed reference returned by route() for destination construction. */
|
|
224
|
+
type RouteSearchValue =
|
|
225
|
+
| string
|
|
226
|
+
| number
|
|
227
|
+
| boolean
|
|
228
|
+
| null
|
|
229
|
+
| undefined
|
|
230
|
+
| readonly (string | number | boolean | null)[];
|
|
231
|
+
|
|
232
|
+
/** A route's query-string parameters, keyed by name. */
|
|
233
|
+
type RouteSearch = Record<string, RouteSearchValue>;
|
|
234
|
+
|
|
235
|
+
/** Stable, typed reference to a route returned by `route()`, used to build destinations. */
|
|
236
|
+
interface RouteRef<
|
|
237
|
+
TParams extends RouteParams = RouteParams,
|
|
238
|
+
TSearch = RouteSearch,
|
|
239
|
+
> {
|
|
240
|
+
readonly path: string;
|
|
241
|
+
/** @internal Executable schema retained for destination validation. */
|
|
242
|
+
readonly searchSchema?: ObjectSchema<TSearch & RouteSearch>;
|
|
243
|
+
/** @internal Public mount point captured by createRouteRegistry(). */
|
|
244
|
+
readonly basePath?: string;
|
|
245
|
+
readonly __params?: TParams;
|
|
246
|
+
readonly __search?: TSearch;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
type RouteRefSearch<TSchema extends ObjectSchema<RouteSearch> | undefined> =
|
|
250
|
+
TSchema extends ObjectSchema<RouteSearch>
|
|
251
|
+
? InferSchema<TSchema>
|
|
252
|
+
: RouteSearch;
|
|
253
|
+
|
|
254
|
+
/** A resolved navigation target with a computed `href`, produced by {@link to}. */
|
|
255
|
+
interface RouteDestination {
|
|
256
|
+
readonly href: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Options accepted by the `page()` route-declaration helper. */
|
|
260
|
+
interface PageHelperOptions extends CommonAccessOptions {
|
|
261
|
+
preload?: (
|
|
262
|
+
context: RouteContext & {
|
|
263
|
+
request?: Request;
|
|
264
|
+
data: QueryPrefetchContext;
|
|
265
|
+
}
|
|
266
|
+
) => unknown;
|
|
267
|
+
meta?: RouteMetaSource;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* A single parsed segment from a route path.
|
|
272
|
+
*
|
|
273
|
+
* - `static`: a literal path segment, e.g. `"users"` in `/users/{id}`
|
|
274
|
+
* - `param`: a `{name}` capture group — `value` holds the param name
|
|
275
|
+
* - `wildcard`: a bare `*` segment that captures exactly one segment
|
|
276
|
+
* - `splat`: a `{*name}` capture group that captures the remaining path
|
|
277
|
+
* - `catchall`: the `/*` catch-all that matches any depth
|
|
278
|
+
*/
|
|
279
|
+
interface ParsedSegment {
|
|
280
|
+
kind: 'static' | 'param' | 'wildcard' | 'splat' | 'catchall';
|
|
281
|
+
/** For static/wildcard/catchall: the literal text; for param: the param name. */
|
|
282
|
+
value: string;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Resolved layout component as stored in a route record's layout chain. */
|
|
286
|
+
interface LayoutScopeRecord {
|
|
287
|
+
component: (props: { children?: RenderableChild }) => RenderableChild;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Resolved page host component as stored in a route record's page chain. */
|
|
291
|
+
interface PageScopeRecord {
|
|
292
|
+
component: RouteComponent;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** Options for {@link createRouteRegistry}. */
|
|
296
|
+
interface RouteRegistryOptions {
|
|
297
|
+
auth?: RouteAuthOptions;
|
|
298
|
+
/** Public pathname prefix for applications mounted below the origin root. */
|
|
299
|
+
basePath?: string;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** A callback that declares routes via `route()`/`page()`/`group()`, passed to {@link createRouteRegistry}. */
|
|
303
|
+
type RouteDefinition = () => void;
|
|
304
|
+
|
|
305
|
+
/** Options for resolving a route request (used internally by `createSPA`/`hydrateSPA`/SSR). */
|
|
306
|
+
interface RouteRequestOptions {
|
|
307
|
+
/** Explicit route source shared by the application renderers. */
|
|
308
|
+
registry: RouteRegistry;
|
|
309
|
+
mode?: RouteMode;
|
|
310
|
+
/** @internal Hydration adopts server loader data instead of rerunning it. */
|
|
311
|
+
load?: boolean;
|
|
312
|
+
auth?: RouteAuthOptions;
|
|
313
|
+
authContext?: AuthContext;
|
|
314
|
+
signal?: AbortSignal;
|
|
315
|
+
request?: Request;
|
|
316
|
+
telemetry?: CoreTelemetry;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** A resolved route request that should render `handler` with `params`. */
|
|
320
|
+
interface RouteRenderResult<TParams extends RouteParams = RouteParams> {
|
|
321
|
+
kind: 'render';
|
|
322
|
+
handler: RouteHandler<TParams>;
|
|
323
|
+
params: TParams;
|
|
324
|
+
record?: RouteRecord;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Outcome of resolving a route request: render, redirect, deny, or no match. */
|
|
328
|
+
type RouteRequestResult<TParams extends RouteParams = RouteParams> =
|
|
329
|
+
| RouteRenderResult<TParams>
|
|
330
|
+
| AccessRedirectDecision
|
|
331
|
+
| AccessDenyDecision
|
|
332
|
+
| null;
|
|
333
|
+
|
|
334
|
+
/** Options accepted by the `group()` route-declaration helper. */
|
|
335
|
+
interface GroupHelperOptions extends CommonAccessOptions {
|
|
336
|
+
layout?: (props: { children?: RenderableChild }) => RenderableChild;
|
|
337
|
+
meta?: RouteMetaSource;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* A fully normalized route record produced by `route(path, Component, options?)`.
|
|
342
|
+
*
|
|
343
|
+
* This is the canonical representation shared by:
|
|
344
|
+
* - SPA matching and navigation
|
|
345
|
+
* - SSR request resolution
|
|
346
|
+
* - SSG manifest expansion
|
|
347
|
+
*/
|
|
348
|
+
interface RouteRecord {
|
|
349
|
+
/** Canonical normalized absolute path, e.g. `/posts/{slug}` */
|
|
350
|
+
path: string;
|
|
351
|
+
/** The page component to render when this route is active */
|
|
352
|
+
component: RouteComponent;
|
|
353
|
+
/** Pre-parsed segment list for fast matching and typed param extraction */
|
|
354
|
+
segments: ParsedSegment[];
|
|
355
|
+
/** Pre-computed specificity rank (higher = more specific) */
|
|
356
|
+
rank: number;
|
|
357
|
+
/** Layout chain from outermost to innermost, applied automatically on render */
|
|
358
|
+
layoutChain: LayoutScopeRecord[];
|
|
359
|
+
/** Page chain from outermost to innermost, composed through Outlet before layouts apply */
|
|
360
|
+
pageChain: PageScopeRecord[];
|
|
361
|
+
/** Route metadata: loader, entries, policies, title, namespace */
|
|
362
|
+
options: RouteOptions;
|
|
363
|
+
/** Metadata sources ordered from outermost group/page to the route leaf. */
|
|
364
|
+
metaChain?: readonly RouteMetaSource[];
|
|
365
|
+
/** True when this is the `/*` catch-all fallback route */
|
|
366
|
+
isFallback: boolean;
|
|
367
|
+
/**
|
|
368
|
+
* Runtime-ready handler with layout composition baked in.
|
|
369
|
+
* Compatible with the low-level `RouteHandler` signature so that navigation
|
|
370
|
+
* and SSR rendering do not need to know about layout chains.
|
|
371
|
+
*/
|
|
372
|
+
handler: RouteHandler;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* The normalized route manifest produced by registered route definitions.
|
|
377
|
+
* declarations. Pass it to `createSPA`, `hydrateSPA`, or `renderToString`
|
|
378
|
+
* instead of assembling plain `Route[]` arrays.
|
|
379
|
+
*
|
|
380
|
+
* ```ts
|
|
381
|
+
* import { createRouteRegistry } from '@askrjs/askr/router';
|
|
382
|
+
* const registry = createRouteRegistry(() => { ... });
|
|
383
|
+
* await createSPA({ root: '#app', registry });
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
interface RouteManifest {
|
|
387
|
+
records: RouteRecord[];
|
|
388
|
+
auth?: RouteAuthOptions;
|
|
389
|
+
/** Normalized public pathname prefix. Empty and root mounts omit it. */
|
|
390
|
+
basePath?: string;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
declare const routeRegistryBrand: unique symbol;
|
|
394
|
+
|
|
395
|
+
/** A function rendering a matched route's page content, with layouts already composed. */
|
|
396
|
+
interface RouteHandler<TParams extends RouteParams = RouteParams> {
|
|
397
|
+
(
|
|
398
|
+
params: TParams,
|
|
399
|
+
context?: {
|
|
400
|
+
signal: AbortSignal;
|
|
401
|
+
}
|
|
402
|
+
): RenderableChild;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** A single path-to-handler binding as seen by low-level navigation code. */
|
|
406
|
+
interface Route<TParams extends RouteParams = RouteParams> {
|
|
407
|
+
path: string;
|
|
408
|
+
handler: RouteHandler<TParams>;
|
|
409
|
+
namespace?: string;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** Opaque handle produced by {@link createRouteRegistry}, required by `createSPA`/`hydrateSPA`. */
|
|
413
|
+
interface RouteRegistry {
|
|
414
|
+
/** Internal brand: registries must come from createRouteRegistry(). */
|
|
415
|
+
readonly [routeRegistryBrand]: true;
|
|
416
|
+
manifest: RouteManifest;
|
|
417
|
+
routes: readonly Route[];
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/** A single matched route, as reported by {@link currentRoute} and activity predicates. */
|
|
421
|
+
interface RouteMatch<TParams extends RouteParams = RouteParams> {
|
|
422
|
+
path: string;
|
|
423
|
+
params: Readonly<TParams>;
|
|
424
|
+
name?: string;
|
|
425
|
+
namespace?: string;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** Read-only accessor for the current route's query-string parameters. */
|
|
429
|
+
interface RouteQuery {
|
|
430
|
+
get(key: string): string | null;
|
|
431
|
+
getAll(key: string): string[];
|
|
432
|
+
has(key: string): boolean;
|
|
433
|
+
toJSON(): Record<string, string | string[]>;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** Full description of the currently active route, returned by {@link currentRoute}. */
|
|
437
|
+
interface RouteSnapshot<
|
|
438
|
+
TParams extends RouteParams = RouteParams,
|
|
439
|
+
TState = unknown,
|
|
440
|
+
> {
|
|
441
|
+
path: string;
|
|
442
|
+
params: Readonly<TParams>;
|
|
443
|
+
query: Readonly<RouteQuery>;
|
|
444
|
+
hash: string | null;
|
|
445
|
+
/** Whether the current browser history entry was given explicit location state. */
|
|
446
|
+
hasState: boolean;
|
|
447
|
+
/** Entry-local state supplied to navigate(); absent during SSR and when no state was supplied. */
|
|
448
|
+
state: TState | undefined;
|
|
449
|
+
name?: string;
|
|
450
|
+
namespace?: string;
|
|
451
|
+
matches: readonly RouteMatch<TParams>[];
|
|
452
|
+
}
|
|
453
|
+
export {
|
|
454
|
+
RouteParams,
|
|
455
|
+
StripRoutePathSuffix,
|
|
456
|
+
TrimRoutePathSlashes,
|
|
457
|
+
TrimRoutePathWhitespace,
|
|
458
|
+
ExtractRouteSegmentParam,
|
|
459
|
+
ExtractRoutePathParamNames,
|
|
460
|
+
RoutePathParams,
|
|
461
|
+
RouteComponent,
|
|
462
|
+
RouteMode,
|
|
463
|
+
AccessRedirectStatus,
|
|
464
|
+
AccessDenyStatus,
|
|
465
|
+
AccessAllowDecision,
|
|
466
|
+
AccessRedirectDecision,
|
|
467
|
+
AccessDenyDecision,
|
|
468
|
+
AccessDecision,
|
|
469
|
+
RouteContext,
|
|
470
|
+
RoutePolicy,
|
|
471
|
+
RouteAuthResolver,
|
|
472
|
+
RouteAuthOptions,
|
|
473
|
+
CommonAccessOptions,
|
|
474
|
+
RouteMetaSource,
|
|
475
|
+
RouteOptions,
|
|
476
|
+
RouteMeta,
|
|
477
|
+
RouteSearchValue,
|
|
478
|
+
RouteSearch,
|
|
479
|
+
RouteRef,
|
|
480
|
+
RouteRefSearch,
|
|
481
|
+
RouteDestination,
|
|
482
|
+
PageHelperOptions,
|
|
483
|
+
ParsedSegment,
|
|
484
|
+
LayoutScopeRecord,
|
|
485
|
+
PageScopeRecord,
|
|
486
|
+
RouteRegistryOptions,
|
|
487
|
+
RouteDefinition,
|
|
488
|
+
RouteRequestOptions,
|
|
489
|
+
RouteRenderResult,
|
|
490
|
+
RouteRequestResult,
|
|
491
|
+
GroupHelperOptions,
|
|
492
|
+
RouteRecord,
|
|
493
|
+
RouteManifest,
|
|
494
|
+
routeRegistryBrand,
|
|
495
|
+
RouteHandler,
|
|
496
|
+
Route,
|
|
497
|
+
RouteRegistry,
|
|
498
|
+
RouteMatch,
|
|
499
|
+
RouteQuery,
|
|
500
|
+
RouteSnapshot,
|
|
501
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { JSXElementType, JSXElement, Props } from '../elements.js';
|
|
2
|
+
import '../jsx-globals.js';
|
|
3
|
+
import { AuthContext, AuthRequirement } from '@askrjs/auth';
|
|
4
|
+
import { InferSchema, ObjectSchema } from '@askrjs/schema';
|
|
5
|
+
import { task } from './lifecycle.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Serialized update scheduler — safer design (no inline execution, explicit flush)
|
|
9
|
+
*
|
|
10
|
+
* Key ideas:
|
|
11
|
+
* - Never execute a task inline from `enqueue`.
|
|
12
|
+
* - `flush()` is explicit and non-reentrant.
|
|
13
|
+
* - `runWithSyncProgress()` allows enqueues temporarily but does not run tasks
|
|
14
|
+
* inline; it runs `fn` and then does an explicit `flush()`.
|
|
15
|
+
* - `waitForFlush()` is race-free with a monotonic `flushVersion`.
|
|
16
|
+
*/
|
|
17
|
+
type Task = () => void;
|
|
18
|
+
|
|
19
|
+
type SchedulerLane = 'derived' | 'component' | 'reactive' | 'post';
|
|
20
|
+
|
|
21
|
+
type SchedulerBulkCommitProbe = () => boolean;
|
|
22
|
+
|
|
23
|
+
declare class Scheduler {
|
|
24
|
+
private bulkCommitProbe;
|
|
25
|
+
private lanes;
|
|
26
|
+
private running;
|
|
27
|
+
private inHandler;
|
|
28
|
+
private depth;
|
|
29
|
+
private executionDepth;
|
|
30
|
+
private flushVersion;
|
|
31
|
+
private kickScheduled;
|
|
32
|
+
private allowSyncProgress;
|
|
33
|
+
private waiters;
|
|
34
|
+
private taskCount;
|
|
35
|
+
setBulkCommitProbe(probe: SchedulerBulkCommitProbe): void;
|
|
36
|
+
private isBulkCommitActive;
|
|
37
|
+
private hasPendingTasks;
|
|
38
|
+
private getPendingTaskCount;
|
|
39
|
+
private compactLane;
|
|
40
|
+
private scheduleFlushKick;
|
|
41
|
+
enqueue(task: Task): void;
|
|
42
|
+
enqueueInLane(lane: SchedulerLane, task: Task): void;
|
|
43
|
+
flush(): void;
|
|
44
|
+
runWithSyncProgress<T>(fn: () => T): T;
|
|
45
|
+
waitForFlush(targetVersion?: number, timeoutMs?: number): Promise<void>;
|
|
46
|
+
getState(): {
|
|
47
|
+
queueLength: number;
|
|
48
|
+
running: boolean;
|
|
49
|
+
depth: number;
|
|
50
|
+
executionDepth: number;
|
|
51
|
+
taskCount: number;
|
|
52
|
+
flushVersion: number;
|
|
53
|
+
laneQueues: {
|
|
54
|
+
derived: number;
|
|
55
|
+
component: number;
|
|
56
|
+
reactive: number;
|
|
57
|
+
post: number;
|
|
58
|
+
};
|
|
59
|
+
inHandler: boolean;
|
|
60
|
+
allowSyncProgress: boolean;
|
|
61
|
+
};
|
|
62
|
+
getFlushVersion(): number;
|
|
63
|
+
flushIfQueued(): void;
|
|
64
|
+
runInHandlerScope<T>(fn: () => T, flushMode?: 'defer' | 'sync'): T;
|
|
65
|
+
setInHandler(v: boolean): void;
|
|
66
|
+
isInHandler(): boolean;
|
|
67
|
+
isExecuting(): boolean;
|
|
68
|
+
clearPendingSyncTasks(): number;
|
|
69
|
+
private resolveWaiters;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare function scheduleEventHandler(handler: EventListener): EventListener;
|
|
73
|
+
export {
|
|
74
|
+
Task,
|
|
75
|
+
SchedulerLane,
|
|
76
|
+
SchedulerBulkCommitProbe,
|
|
77
|
+
Scheduler,
|
|
78
|
+
scheduleEventHandler,
|
|
79
|
+
};
|