kiru 0.53.0 → 0.54.0-preview.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/dist/globals.d.ts +1 -1
- package/dist/globals.d.ts.map +1 -1
- package/dist/globals.js.map +1 -1
- package/dist/router/client/index.d.ts +4 -2
- package/dist/router/client/index.d.ts.map +1 -1
- package/dist/router/client/index.js +49 -11
- package/dist/router/client/index.js.map +1 -1
- package/dist/router/context.d.ts +2 -0
- package/dist/router/context.d.ts.map +1 -1
- package/dist/router/context.js +5 -1
- package/dist/router/context.js.map +1 -1
- package/dist/router/fileRouterController.d.ts +2 -0
- package/dist/router/fileRouterController.d.ts.map +1 -1
- package/dist/router/fileRouterController.js +80 -9
- package/dist/router/fileRouterController.js.map +1 -1
- package/dist/router/globals.d.ts +3 -0
- package/dist/router/globals.d.ts.map +1 -1
- package/dist/router/globals.js +3 -0
- package/dist/router/globals.js.map +1 -1
- package/dist/router/guard.d.ts +17 -0
- package/dist/router/guard.d.ts.map +1 -0
- package/dist/router/guard.js +45 -0
- package/dist/router/guard.js.map +1 -0
- package/dist/router/head.d.ts.map +1 -1
- package/dist/router/head.js +5 -7
- package/dist/router/head.js.map +1 -1
- package/dist/router/index.d.ts +2 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +2 -1
- package/dist/router/index.js.map +1 -1
- package/dist/router/{server → ssg}/index.d.ts +4 -3
- package/dist/router/ssg/index.d.ts.map +1 -0
- package/dist/router/{server → ssg}/index.js +7 -4
- package/dist/router/ssg/index.js.map +1 -0
- package/dist/router/ssr/index.d.ts +20 -0
- package/dist/router/ssr/index.d.ts.map +1 -0
- package/dist/router/ssr/index.js +160 -0
- package/dist/router/ssr/index.js.map +1 -0
- package/dist/router/types.d.ts +37 -4
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/types.internal.d.ts +4 -0
- package/dist/router/types.internal.d.ts.map +1 -1
- package/dist/router/utils/index.d.ts +8 -3
- package/dist/router/utils/index.d.ts.map +1 -1
- package/dist/router/utils/index.js +38 -6
- package/dist/router/utils/index.js.map +1 -1
- package/dist/ssr/client.d.ts +1 -1
- package/dist/ssr/client.d.ts.map +1 -1
- package/dist/ssr/server.d.ts +1 -2
- package/dist/ssr/server.d.ts.map +1 -1
- package/dist/ssr/server.js +16 -19
- package/dist/ssr/server.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/format.d.ts +2 -1
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +4 -1
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/runtime.d.ts +1 -1
- package/dist/utils/runtime.js +1 -1
- package/package.json +8 -4
- package/src/globals.ts +1 -1
- package/src/router/client/index.ts +100 -14
- package/src/router/context.ts +7 -1
- package/src/router/fileRouterController.ts +137 -8
- package/src/router/globals.ts +4 -0
- package/src/router/guard.ts +72 -0
- package/src/router/head.ts +5 -7
- package/src/router/index.ts +12 -1
- package/src/router/{server → ssg}/index.ts +16 -9
- package/src/router/ssr/index.ts +247 -0
- package/src/router/types.internal.ts +5 -0
- package/src/router/types.ts +48 -4
- package/src/router/utils/index.ts +74 -8
- package/src/ssr/client.ts +1 -1
- package/src/ssr/server.ts +19 -21
- package/src/types.ts +3 -0
- package/src/utils/format.ts +5 -0
- package/src/utils/runtime.ts +1 -1
- package/dist/router/server/index.d.ts.map +0 -1
- package/dist/router/server/index.js.map +0 -1
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import path from "path"
|
|
2
|
+
import { createElement, Fragment } from "../../element.js"
|
|
3
|
+
import { __DEV__ } from "../../env.js"
|
|
4
|
+
import { renderToString } from "../../renderToString.js"
|
|
5
|
+
import { renderToReadableStream } from "../../ssr/server.js"
|
|
6
|
+
import { toArray } from "../../utils/format.js"
|
|
7
|
+
import {
|
|
8
|
+
matchModules,
|
|
9
|
+
matchRoute,
|
|
10
|
+
match404Route,
|
|
11
|
+
parseQuery,
|
|
12
|
+
wrapWithLayouts,
|
|
13
|
+
runBeforeEachGuards,
|
|
14
|
+
runAfterEachGuards,
|
|
15
|
+
runBeforeEnterHooks,
|
|
16
|
+
} from "../utils/index.js"
|
|
17
|
+
import { RouterContext, RequestContext } from "../context.js"
|
|
18
|
+
import type { PageConfig, PageProps, RouterState } from "../types.js"
|
|
19
|
+
import type {
|
|
20
|
+
FormattedViteImportMap,
|
|
21
|
+
GuardModule,
|
|
22
|
+
PageModule,
|
|
23
|
+
} from "../types.internal.js"
|
|
24
|
+
|
|
25
|
+
export interface SSRRenderContext {
|
|
26
|
+
pages: FormattedViteImportMap<PageModule>
|
|
27
|
+
layouts: FormattedViteImportMap
|
|
28
|
+
guards: FormattedViteImportMap<GuardModule>
|
|
29
|
+
Document: Kiru.FC
|
|
30
|
+
userContext: Kiru.RequestContext
|
|
31
|
+
registerModule: (moduleId: string) => void
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface SSRHttpResponse {
|
|
35
|
+
html: string
|
|
36
|
+
statusCode: number
|
|
37
|
+
headers: Array<[string, string]>
|
|
38
|
+
stream: ReadableStream | null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface SSRRenderResult {
|
|
42
|
+
httpResponse: SSRHttpResponse | null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function render(
|
|
46
|
+
url: string,
|
|
47
|
+
ctx: SSRRenderContext
|
|
48
|
+
): Promise<SSRRenderResult> {
|
|
49
|
+
const extName = path.extname(url)
|
|
50
|
+
if (extName && extName.length > 0) {
|
|
51
|
+
return {
|
|
52
|
+
httpResponse: null,
|
|
53
|
+
}
|
|
54
|
+
} else if (url.startsWith("/@")) {
|
|
55
|
+
return {
|
|
56
|
+
httpResponse: null,
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const u = new URL(url, "http://localhost")
|
|
60
|
+
const pathSegments = u.pathname.split("/").filter(Boolean)
|
|
61
|
+
let routeMatch = matchRoute(ctx.pages, pathSegments)
|
|
62
|
+
|
|
63
|
+
if (!routeMatch) {
|
|
64
|
+
// Try to find a 404 page in parent directories
|
|
65
|
+
const fourOhFourMatch = match404Route(ctx.pages, pathSegments)
|
|
66
|
+
if (fourOhFourMatch) {
|
|
67
|
+
routeMatch = fourOhFourMatch
|
|
68
|
+
} else {
|
|
69
|
+
// Fallback to root 404 or default fallback
|
|
70
|
+
if (url === "/404") {
|
|
71
|
+
if (__DEV__) {
|
|
72
|
+
console.warn(
|
|
73
|
+
"[kiru/router]: No 404 route defined. Using fallback 404 page."
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
httpResponse: {
|
|
78
|
+
statusCode: 404,
|
|
79
|
+
headers: [["Content-Type", "text/html"]],
|
|
80
|
+
html: "<!doctype html><html><head><title>Not Found</title></head><body><h1>404</h1></body></html>",
|
|
81
|
+
stream: null,
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Recursively render the 404 page
|
|
86
|
+
const notFoundResponse = await render("/404", ctx)
|
|
87
|
+
return {
|
|
88
|
+
httpResponse: {
|
|
89
|
+
html: notFoundResponse.httpResponse?.html ?? "",
|
|
90
|
+
headers: notFoundResponse.httpResponse?.headers ?? [
|
|
91
|
+
["Content-Type", "text/html"],
|
|
92
|
+
],
|
|
93
|
+
...notFoundResponse,
|
|
94
|
+
statusCode: 404,
|
|
95
|
+
stream: null,
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const { pageEntry, routeSegments, params } = routeMatch
|
|
101
|
+
const is404Route = routeMatch.routeSegments.includes("404")
|
|
102
|
+
|
|
103
|
+
const guardEntries = matchModules(ctx.guards, routeSegments)
|
|
104
|
+
const guardModules = await Promise.all(
|
|
105
|
+
guardEntries.map((entry) => entry.load() as unknown as Promise<GuardModule>)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
const redirectPath = await runBeforeEachGuards(
|
|
109
|
+
guardModules,
|
|
110
|
+
{ ...ctx.userContext },
|
|
111
|
+
u.pathname
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if (redirectPath !== null) {
|
|
115
|
+
return createRedirectResult(redirectPath)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const layoutEntries = matchModules(ctx.layouts, routeSegments)
|
|
119
|
+
|
|
120
|
+
// Register all modules for CSS collection
|
|
121
|
+
;[pageEntry, ...layoutEntries].forEach((e) => {
|
|
122
|
+
ctx.registerModule(e.filePath)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const [page, ...layouts] = await Promise.all([
|
|
126
|
+
pageEntry.load(),
|
|
127
|
+
...layoutEntries.map((layoutEntry) => layoutEntry.load()),
|
|
128
|
+
])
|
|
129
|
+
|
|
130
|
+
const onBeforeEnter = page.config?.hooks?.onBeforeEnter
|
|
131
|
+
if (onBeforeEnter) {
|
|
132
|
+
const redirectPath = await runBeforeEnterHooks(
|
|
133
|
+
toArray(onBeforeEnter),
|
|
134
|
+
{ ...ctx.userContext },
|
|
135
|
+
u.pathname
|
|
136
|
+
)
|
|
137
|
+
if (redirectPath) {
|
|
138
|
+
return createRedirectResult(redirectPath)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const query = parseQuery(u.search)
|
|
143
|
+
|
|
144
|
+
let props = {} as PageProps<PageConfig>
|
|
145
|
+
const config = page.config ?? {}
|
|
146
|
+
const abortController = new AbortController()
|
|
147
|
+
|
|
148
|
+
// PageConfig loaders don't run on the server
|
|
149
|
+
if (config.loader) {
|
|
150
|
+
props = {
|
|
151
|
+
data: null,
|
|
152
|
+
error: null,
|
|
153
|
+
loading: true,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let documentShell = renderToString(createElement(ctx.Document))
|
|
158
|
+
|
|
159
|
+
if (
|
|
160
|
+
documentShell.includes("</body>") ||
|
|
161
|
+
!documentShell.includes("<kiru-body-outlet>")
|
|
162
|
+
) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
"[kiru/router]: Document is expected to contain a <Body.Outlet> element. See https://kirujs.dev/docs/api/file-router#general-usage"
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const children = wrapWithLayouts(
|
|
169
|
+
layouts
|
|
170
|
+
.map((layout) => layout.default)
|
|
171
|
+
.filter((l) => typeof l === "function"),
|
|
172
|
+
page.default,
|
|
173
|
+
props
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
const routerContextValue = {
|
|
177
|
+
state: {
|
|
178
|
+
params,
|
|
179
|
+
query,
|
|
180
|
+
pathname: u.pathname,
|
|
181
|
+
hash: "",
|
|
182
|
+
signal: abortController.signal,
|
|
183
|
+
} satisfies RouterState,
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const app = createElement(RouterContext.Provider, {
|
|
187
|
+
children: createElement(RequestContext.Provider, {
|
|
188
|
+
children: Fragment({ children }),
|
|
189
|
+
value: ctx.userContext,
|
|
190
|
+
}),
|
|
191
|
+
value: routerContextValue,
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
let { immediate: pageOutletContent, stream } = renderToReadableStream(app)
|
|
195
|
+
const hasHeadContent = pageOutletContent.includes("<kiru-head-content>")
|
|
196
|
+
const hasHeadOutlet = documentShell.includes("<kiru-head-outlet>")
|
|
197
|
+
|
|
198
|
+
if (hasHeadOutlet && hasHeadContent) {
|
|
199
|
+
let [preHeadContent = "", headContentInner = "", postHeadContent = ""] =
|
|
200
|
+
pageOutletContent.split(/<kiru-head-content>|<\/kiru-head-content>/)
|
|
201
|
+
|
|
202
|
+
documentShell = documentShell.replace(
|
|
203
|
+
"<kiru-head-outlet>",
|
|
204
|
+
headContentInner
|
|
205
|
+
)
|
|
206
|
+
pageOutletContent = `${preHeadContent}${postHeadContent}`
|
|
207
|
+
} else if (hasHeadContent) {
|
|
208
|
+
// remove head content element and everything within it
|
|
209
|
+
pageOutletContent = pageOutletContent.replace(
|
|
210
|
+
/<kiru-head-content>(.*?)<\/kiru-head-content>/,
|
|
211
|
+
""
|
|
212
|
+
)
|
|
213
|
+
} else if (hasHeadOutlet) {
|
|
214
|
+
// remove head outlet element and everything within it
|
|
215
|
+
documentShell = documentShell.replaceAll("<kiru-head-outlet>", "")
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const [prePageOutlet, postPageOutlet] =
|
|
219
|
+
documentShell.split("<kiru-body-outlet>")
|
|
220
|
+
|
|
221
|
+
const html = `<!DOCTYPE html>${prePageOutlet}<body>${pageOutletContent}</body>${postPageOutlet}`
|
|
222
|
+
const statusCode = is404Route ? 404 : 200
|
|
223
|
+
|
|
224
|
+
queueMicrotask(() => {
|
|
225
|
+
runAfterEachGuards(guardModules, { ...ctx.userContext }, u.pathname)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
httpResponse: {
|
|
230
|
+
html,
|
|
231
|
+
statusCode,
|
|
232
|
+
headers: [["Content-Type", "text/html;charset=utf-8"]],
|
|
233
|
+
stream,
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function createRedirectResult(to: string): SSRRenderResult {
|
|
239
|
+
return {
|
|
240
|
+
httpResponse: {
|
|
241
|
+
statusCode: 302,
|
|
242
|
+
headers: [["Location", to]],
|
|
243
|
+
html: "",
|
|
244
|
+
stream: null,
|
|
245
|
+
},
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FileRouterContextType } from "./context"
|
|
2
2
|
import type { PageConfig } from "./types"
|
|
3
|
+
import type { NavGuardBuilder } from "./guard"
|
|
3
4
|
|
|
4
5
|
export interface CurrentPage {
|
|
5
6
|
component: Kiru.FC<any>
|
|
@@ -20,6 +21,10 @@ export interface PageModule {
|
|
|
20
21
|
>
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
export interface GuardModule {
|
|
25
|
+
guard: NavGuardBuilder
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
export interface ViteImportMap {
|
|
24
29
|
[fp: string]: () => Promise<DefaultComponentModule>
|
|
25
30
|
}
|
package/src/router/types.ts
CHANGED
|
@@ -3,12 +3,14 @@ import type { FileRouterDataLoadError } from "./errors"
|
|
|
3
3
|
import type {
|
|
4
4
|
DefaultComponentModule,
|
|
5
5
|
FormattedViteImportMap,
|
|
6
|
+
GuardModule,
|
|
6
7
|
PageModule,
|
|
7
8
|
} from "./types.internal"
|
|
8
9
|
|
|
9
10
|
export interface FileRouterPreloadConfig {
|
|
10
|
-
pages: FormattedViteImportMap
|
|
11
|
+
pages: FormattedViteImportMap<PageModule>
|
|
11
12
|
layouts: FormattedViteImportMap
|
|
13
|
+
guards?: FormattedViteImportMap<GuardModule>
|
|
12
14
|
page: PageModule
|
|
13
15
|
pageProps: Record<string, unknown>
|
|
14
16
|
pageLayouts: DefaultComponentModule[]
|
|
@@ -40,6 +42,14 @@ export interface FileRouterConfig {
|
|
|
40
42
|
* ```
|
|
41
43
|
*/
|
|
42
44
|
layouts: Record<string, unknown>
|
|
45
|
+
/**
|
|
46
|
+
* The import map to use for loading nav guards
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* <FileRouter config={{ guards: import.meta.glob("/∗∗/guard.{ts,js}"), ... }} />
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
guards?: Record<string, unknown>
|
|
43
53
|
|
|
44
54
|
/**
|
|
45
55
|
* The base url to use as a prefix for route matching
|
|
@@ -98,18 +108,34 @@ export interface RouterState {
|
|
|
98
108
|
signal: AbortSignal
|
|
99
109
|
}
|
|
100
110
|
|
|
101
|
-
type PageDataLoaderContext = RouterState & {}
|
|
102
|
-
|
|
103
111
|
export interface PageDataLoaderCacheConfig {
|
|
104
112
|
type: "memory" | "localStorage" | "sessionStorage"
|
|
105
113
|
ttl: number
|
|
106
114
|
}
|
|
107
115
|
|
|
116
|
+
interface LoaderContext extends RouterState {
|
|
117
|
+
/**
|
|
118
|
+
* The request context - in SSR, this is the data from the server
|
|
119
|
+
* that's passed to the `renderPage` function
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* // server.ts
|
|
123
|
+
* renderPage({ url, context: { test: 123 } })
|
|
124
|
+
*
|
|
125
|
+
* // page.tsx
|
|
126
|
+
* loader: {
|
|
127
|
+
* load: ({ context }) => context.test
|
|
128
|
+
* }
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
context: Kiru.RequestContext
|
|
132
|
+
}
|
|
133
|
+
|
|
108
134
|
export type PageDataLoaderConfig<T = unknown> = {
|
|
109
135
|
/**
|
|
110
136
|
* The function to load the page data
|
|
111
137
|
*/
|
|
112
|
-
load: (context:
|
|
138
|
+
load: (context: LoaderContext) => Promise<T>
|
|
113
139
|
} & (
|
|
114
140
|
| {
|
|
115
141
|
/**
|
|
@@ -149,6 +175,22 @@ export type PageDataLoaderConfig<T = unknown> = {
|
|
|
149
175
|
}
|
|
150
176
|
)
|
|
151
177
|
|
|
178
|
+
export type NavigationHook<T> = (
|
|
179
|
+
context: Kiru.RequestContext,
|
|
180
|
+
to: string,
|
|
181
|
+
from: string
|
|
182
|
+
) => T
|
|
183
|
+
|
|
184
|
+
export type OnBeforeEnterHook = NavigationHook<
|
|
185
|
+
string | void | Promise<string | void>
|
|
186
|
+
>
|
|
187
|
+
export type OnBeforeLeaveHook = NavigationHook<false | void>
|
|
188
|
+
|
|
189
|
+
interface PageContextHooks {
|
|
190
|
+
onBeforeEnter?: OnBeforeEnterHook | OnBeforeEnterHook[]
|
|
191
|
+
onBeforeLeave?: OnBeforeLeaveHook | OnBeforeLeaveHook[]
|
|
192
|
+
}
|
|
193
|
+
|
|
152
194
|
export interface PageConfig<T = unknown> {
|
|
153
195
|
/**
|
|
154
196
|
* The loader configuration for this page
|
|
@@ -159,6 +201,8 @@ export interface PageConfig<T = unknown> {
|
|
|
159
201
|
* returned, a page will be generated
|
|
160
202
|
*/
|
|
161
203
|
generateStaticParams?: () => RouteParams[] | Promise<RouteParams[]>
|
|
204
|
+
|
|
205
|
+
hooks?: PageContextHooks
|
|
162
206
|
}
|
|
163
207
|
|
|
164
208
|
export type PageProps<T extends PageConfig<any>> = T extends PageConfig<infer U>
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
import { createElement } from "../../element.js"
|
|
2
2
|
import { __DEV__ } from "../../env.js"
|
|
3
|
+
import { resolveNavguard } from "../guard.js"
|
|
3
4
|
import type {
|
|
5
|
+
DefaultComponentModule,
|
|
4
6
|
FormattedViteImportMap,
|
|
7
|
+
GuardModule,
|
|
5
8
|
RouteMatch,
|
|
6
9
|
ViteImportMap,
|
|
7
10
|
} from "../types.internal"
|
|
11
|
+
import { OnBeforeEnterHook, OnBeforeLeaveHook } from "../types.js"
|
|
8
12
|
|
|
9
13
|
export {
|
|
10
14
|
formatViteImportMap,
|
|
11
15
|
matchRoute,
|
|
12
16
|
match404Route,
|
|
13
|
-
|
|
17
|
+
matchModules,
|
|
14
18
|
normalizePrefixPath,
|
|
15
19
|
parseQuery,
|
|
16
20
|
wrapWithLayouts,
|
|
21
|
+
runBeforeLeaveHooks,
|
|
22
|
+
runBeforeEnterHooks,
|
|
23
|
+
runBeforeEachGuards,
|
|
24
|
+
runAfterEachGuards,
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
function formatViteImportMap(
|
|
@@ -177,20 +185,20 @@ function match404Route(
|
|
|
177
185
|
return null
|
|
178
186
|
}
|
|
179
187
|
|
|
180
|
-
function
|
|
181
|
-
|
|
188
|
+
function matchModules<T = DefaultComponentModule>(
|
|
189
|
+
modules: FormattedViteImportMap<T>,
|
|
182
190
|
routeSegments: string[]
|
|
183
191
|
) {
|
|
184
192
|
return ["/", ...routeSegments].reduce((acc, _, i) => {
|
|
185
|
-
const
|
|
186
|
-
const
|
|
193
|
+
const modulePath = "/" + routeSegments.slice(0, i).join("/")
|
|
194
|
+
const module = modules[modulePath]
|
|
187
195
|
|
|
188
|
-
if (!
|
|
196
|
+
if (!module) {
|
|
189
197
|
return acc
|
|
190
198
|
}
|
|
191
199
|
|
|
192
|
-
return [...acc,
|
|
193
|
-
}, [] as FormattedViteImportMap[string][])
|
|
200
|
+
return [...acc, module]
|
|
201
|
+
}, [] as FormattedViteImportMap<T>[string][])
|
|
194
202
|
}
|
|
195
203
|
|
|
196
204
|
function normalizePrefixPath(path: string) {
|
|
@@ -239,3 +247,61 @@ function wrapWithLayouts(
|
|
|
239
247
|
createElement(page, props)
|
|
240
248
|
)
|
|
241
249
|
}
|
|
250
|
+
|
|
251
|
+
function runBeforeLeaveHooks(
|
|
252
|
+
hooks: OnBeforeLeaveHook[],
|
|
253
|
+
context: Kiru.RequestContext,
|
|
254
|
+
to: string,
|
|
255
|
+
from: string = to
|
|
256
|
+
): false | void {
|
|
257
|
+
for (const hook of hooks) {
|
|
258
|
+
const res = hook(context, to, from)
|
|
259
|
+
if (res === false) {
|
|
260
|
+
return false
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function runBeforeEnterHooks(
|
|
266
|
+
hooks: OnBeforeEnterHook[],
|
|
267
|
+
context: Kiru.RequestContext,
|
|
268
|
+
to: string,
|
|
269
|
+
from: string = to
|
|
270
|
+
) {
|
|
271
|
+
for (const hook of hooks) {
|
|
272
|
+
const result = await hook(context, to, from)
|
|
273
|
+
if (typeof result === "string") {
|
|
274
|
+
return result
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return null
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function runBeforeEachGuards(
|
|
282
|
+
guardModules: GuardModule[],
|
|
283
|
+
context: Kiru.RequestContext,
|
|
284
|
+
to: string,
|
|
285
|
+
from: string = to
|
|
286
|
+
): Promise<string | null> {
|
|
287
|
+
const beforeHooks = guardModules
|
|
288
|
+
.map((guardModule) => resolveNavguard(guardModule)?.beforeEach)
|
|
289
|
+
.filter((x) => typeof x === "function")
|
|
290
|
+
|
|
291
|
+
return runBeforeEnterHooks(beforeHooks, context, to, from)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async function runAfterEachGuards(
|
|
295
|
+
guardModules: GuardModule[],
|
|
296
|
+
context: Kiru.RequestContext,
|
|
297
|
+
to: string,
|
|
298
|
+
from: string = to
|
|
299
|
+
): Promise<void> {
|
|
300
|
+
const afterHooks = guardModules
|
|
301
|
+
.map((guardModule) => resolveNavguard(guardModule)?.afterEach)
|
|
302
|
+
.filter((x) => typeof x === "function")
|
|
303
|
+
|
|
304
|
+
for (const hook of afterHooks) {
|
|
305
|
+
await hook(context, to, from)
|
|
306
|
+
}
|
|
307
|
+
}
|
package/src/ssr/client.ts
CHANGED
package/src/ssr/server.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Readable } from "node:stream"
|
|
2
1
|
import { Fragment } from "../element.js"
|
|
3
2
|
import { renderMode } from "../globals.js"
|
|
4
3
|
import { STREAMED_DATA_EVENT } from "../constants.js"
|
|
@@ -19,16 +18,15 @@ d.querySelectorAll("[k-data]").forEach((p) => {
|
|
|
19
18
|
});
|
|
20
19
|
d.currentScript.remove()
|
|
21
20
|
</script>
|
|
22
|
-
|
|
21
|
+
`.replace(/\s+/g, " ")
|
|
23
22
|
|
|
24
23
|
export function renderToReadableStream(element: JSX.Element): {
|
|
25
24
|
immediate: string
|
|
26
|
-
stream:
|
|
25
|
+
stream: ReadableStream | null
|
|
27
26
|
} {
|
|
28
|
-
const stream = new Readable({ read() {} })
|
|
29
|
-
const rootNode = Fragment({ children: element })
|
|
30
27
|
const streamPromises = new Set<Kiru.StatefulPromise<unknown>>()
|
|
31
|
-
const
|
|
28
|
+
const dataPromises: Promise<string>[] = []
|
|
29
|
+
let stream: ReadableStream | null = null
|
|
32
30
|
|
|
33
31
|
let immediate = ""
|
|
34
32
|
|
|
@@ -39,33 +37,33 @@ export function renderToReadableStream(element: JSX.Element): {
|
|
|
39
37
|
if (streamPromises.has(promise)) continue
|
|
40
38
|
streamPromises.add(promise)
|
|
41
39
|
|
|
42
|
-
const
|
|
40
|
+
const dataPromise = promise
|
|
43
41
|
.then(() => ({ data: promise.value }))
|
|
44
42
|
.catch(() => ({ error: promise.error?.message }))
|
|
45
|
-
.then(
|
|
46
|
-
|
|
47
|
-
stream.push(
|
|
43
|
+
.then(
|
|
44
|
+
(value, content = JSON.stringify(value)) =>
|
|
48
45
|
`<script id="${promise.id}" k-data type="application/json">${content}</script>`
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
pendingWritePromises.push(writePromise)
|
|
46
|
+
)
|
|
47
|
+
dataPromises.push(dataPromise)
|
|
53
48
|
}
|
|
54
49
|
},
|
|
55
50
|
}
|
|
56
51
|
|
|
57
52
|
const prev = renderMode.current
|
|
58
53
|
renderMode.current = "stream"
|
|
59
|
-
headlessRender(ctx,
|
|
54
|
+
headlessRender(ctx, Fragment({ children: element }), null, 0)
|
|
60
55
|
renderMode.current = prev
|
|
61
56
|
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
if (dataPromises.length > 0) {
|
|
58
|
+
stream = new ReadableStream({
|
|
59
|
+
async pull(controller) {
|
|
60
|
+
for await (const chunk of dataPromises) {
|
|
61
|
+
controller.enqueue(chunk)
|
|
62
|
+
}
|
|
63
|
+
controller.enqueue(STREAMED_DATA_SETUP)
|
|
64
|
+
controller.close()
|
|
65
|
+
},
|
|
66
66
|
})
|
|
67
|
-
} else {
|
|
68
|
-
stream.push(null)
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
return { immediate, stream }
|
package/src/types.ts
CHANGED
|
@@ -117,6 +117,8 @@ declare global {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
namespace Kiru {
|
|
120
|
+
interface RequestContext {}
|
|
121
|
+
|
|
120
122
|
interface CustomEvents {}
|
|
121
123
|
|
|
122
124
|
interface ProviderProps<T> {
|
|
@@ -173,6 +175,7 @@ declare global {
|
|
|
173
175
|
|
|
174
176
|
interface StatefulPromise<T> extends Promise<T>, PromiseState<T> {}
|
|
175
177
|
|
|
178
|
+
type HydrationMode = "static" | "dynamic"
|
|
176
179
|
type RenderMode = "dom" | "hydrate" | "string" | "stream"
|
|
177
180
|
|
|
178
181
|
type StateSetter<T> = T | ((prev: T) => T)
|
package/src/utils/format.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { unwrap } from "../signals/utils.js"
|
|
|
2
2
|
import { booleanAttributes, snakeCaseAttributes } from "../constants.js"
|
|
3
3
|
|
|
4
4
|
export {
|
|
5
|
+
toArray,
|
|
5
6
|
className,
|
|
6
7
|
encodeHtmlEntities,
|
|
7
8
|
propFilters,
|
|
@@ -20,6 +21,10 @@ const REGEX_DBLQT = /"/g
|
|
|
20
21
|
const REGEX_SLASH = /\//g
|
|
21
22
|
const REGEX_ALPHA_UPPER = /[A-Z]/g
|
|
22
23
|
|
|
24
|
+
function toArray<T>(value: T | T[]): T[] {
|
|
25
|
+
return Array.isArray(value) ? value : [value]
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
function className(...classes: (string | false | null | undefined)[]): string {
|
|
24
29
|
return classes.filter(Boolean).join(" ")
|
|
25
30
|
}
|
package/src/utils/runtime.ts
CHANGED
|
@@ -46,7 +46,7 @@ function setRef<T>(ref: Kiru.Ref<T>, value: T): void {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* Returns
|
|
49
|
+
* Returns true if called during 'dom' or 'hydrate' mode.
|
|
50
50
|
*/
|
|
51
51
|
function sideEffectsEnabled(): boolean {
|
|
52
52
|
return renderMode.current === "dom" || renderMode.current === "hydrate"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/router/server/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,sBAAsB,EAAc,MAAM,sBAAsB,CAAA;AAKzE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,sBAAsB,CAAA;IAC7B,OAAO,EAAE,sBAAsB,CAAA;IAC/B,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAA;IACjB,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,0BAA0B,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CACrE;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb;AAED,wBAAsB,MAAM,CAC1B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,aAAa,EAClB,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,YAAY,CAAC,CAuJvB;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,sBAAsB,mCAmCtE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/router/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EACL,YAAY,EACZ,UAAU,EACV,aAAa,EACb,UAAU,EACV,eAAe,GAChB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAG7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAexD,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,GAAW,EACX,GAAkB,EAClB,MAAqB;IAErB,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAA;IAC1C,MAAM,YAAY,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAC1D,IAAI,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAA;IAEpD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,+CAA+C;QAC/C,MAAM,eAAe,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAA;QAC9D,IAAI,eAAe,EAAE,CAAC;YACpB,UAAU,GAAG,eAAe,CAAA;QAC9B,CAAC;aAAM,CAAC;YACN,2CAA2C;YAC3C,IAAI,GAAG,KAAK,MAAM,IAAI,MAAM,EAAE,CAAC;gBAC7B,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,CAAC,IAAI,CACV,+DAA+D,CAChE,CAAA;gBACH,CAAC;gBACD,OAAO;oBACL,MAAM,EAAE,GAAG;oBACX,IAAI,EAAE,4FAA4F;iBACnG,CAAA;YACH,CAAC;YACD,OAAO,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE;gBACzB,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;gBACjB,IAAI,EAAE,EAAE;gBACR,MAAM,EAAE,GAAG;aACZ,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,UAAU,CAAA;IACvD,MAAM,UAAU,GAAG,UAAU,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3D,MAAM,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,aAAa,CAAC,CAE7D;IAAA,CAAC,SAAS,EAAE,GAAG,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QAC3C,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;IAChC,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC3C,SAAS,CAAC,IAAI,EAAoC;QAClD,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;KAC1D,CAAC,CAAA;IAEF,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;IAElC,IAAI,KAAK,GAAG,EAA2B,CAAA;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAA;IAChC,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAA;IAE7C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,EAAE,CAAC;YAC/C,KAAK,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;QACpD,CAAC;aAAM,CAAC;YACN,MAAM,WAAW,GAAgB;gBAC/B,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,IAAI,EAAE,EAAE;gBACR,MAAM;gBACN,KAAK;gBACL,MAAM,EAAE,eAAe,CAAC,MAAM;aAC/B,CAAA;YACD,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC9B,eAAe,CAAC,KAAK,CACnB,6DAA6D,CAC9D,CAAA;YACH,CAAC,EAAE,KAAK,CAAC,CAAA;YAET,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;gBAClD,KAAK,GAAG;oBACN,IAAI;oBACJ,KAAK,EAAE,IAAI;oBACX,OAAO,EAAE,KAAK;iBACf,CAAA;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,KAAK,GAAG;oBACN,KAAK,EAAE,IAAI,uBAAuB,CAAC,KAAK,CAAC;oBACzC,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,IAAI;iBACX,CAAA;YACH,CAAC;oBAAS,CAAC;gBACT,YAAY,CAAC,OAAO,CAAC,CAAA;gBACrB,GAAG,CAAC,0BAA0B,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA;YAC1E,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAC9B,OAAO;SACJ,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC;SAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,UAAU,CAAC,EACzC,IAAI,CAAC,OAAO,EACZ,KAAK,CACN,CAAA;IAED,IAAI,aAAa,GAAG,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAA;IAE/D,IACE,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC;QACjC,CAAC,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAC7C,CAAC;QACD,MAAM,IAAI,KAAK,CACb,mIAAmI,CACpI,CAAA;IACH,CAAC;IAED,MAAM,GAAG,GAAG,aAAa,CAAC,aAAa,CAAC,QAAQ,EAAE;QAChD,QAAQ,EAAE,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;QAChC,KAAK,EAAE;YACL,KAAK,EAAE;gBACL,MAAM;gBACN,KAAK;gBACL,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,qCAAqC;aACvD;SACjB;KACF,CAAC,CAAA;IAEF,IAAI,iBAAiB,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;IAC3C,MAAM,cAAc,GAAG,iBAAiB,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAA;IACxE,MAAM,aAAa,GAAG,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAA;IAElE,IAAI,aAAa,IAAI,cAAc,EAAE,CAAC;QACpC,IAAI,CAAC,cAAc,GAAG,EAAE,EAAE,gBAAgB,GAAG,EAAE,EAAE,eAAe,GAAG,EAAE,CAAC,GACpE,iBAAiB,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAA;QAEtE,aAAa,GAAG,aAAa,CAAC,OAAO,CACnC,oBAAoB,EACpB,gBAAgB,CACjB,CAAA;QACD,iBAAiB,GAAG,GAAG,cAAc,GAAG,eAAe,EAAE,CAAA;IAC3D,CAAC;SAAM,IAAI,cAAc,EAAE,CAAC;QAC1B,uDAAuD;QACvD,iBAAiB,GAAG,iBAAiB,CAAC,OAAO,CAC3C,+CAA+C,EAC/C,EAAE,CACH,CAAA;IACH,CAAC;SAAM,IAAI,aAAa,EAAE,CAAC;QACzB,sDAAsD;QACtD,aAAa,GAAG,aAAa,CAAC,UAAU,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAA;IACpE,CAAC;IAED,MAAM,CAAC,aAAa,EAAE,cAAc,CAAC,GACnC,aAAa,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAA;IAE3C,sCAAsC;IAEtC,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG;QAChD,IAAI,EAAE,kBAAkB,aAAa,SAAS,iBAAiB,UAAU,cAAc,EAAE;KAC1F,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,KAA6B;IACrE,MAAM,OAAO,GAA2B,EAAE,CAAA;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACpC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,kEAAkE;QAClE,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CACvC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAC/C,CAAA;QAED,MAAM,QAAQ,GAAG,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC5C,0CAA0C;QAE1C,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;QAC7D,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAA;YAC1D,SAAQ;QACV,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAe,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;YAC1C,MAAM,GAAG,GAAG,GAAG,EAAE,MAAM,EAAE,oBAAoB,CAAA;YAC7C,IAAI,CAAC,GAAG;gBAAE,SAAQ;YAClB,MAAM,UAAU,GAAG,MAAM,GAAG,EAAE,CAAA;YAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;gBAAE,SAAQ;YAExC,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;gBAChC,IAAI,CAAC,GAAG,QAAQ,CAAA;gBAChB,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;oBACzB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;oBACzB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,GAAG,EAAE,EAAE,KAAK,CAAC,CAAA;gBAC5D,CAAC;gBACD,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAA;YAC7B,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
|