@openelement/ssg 0.41.0-alpha.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/LICENSE +21 -0
- package/README.md +21 -0
- package/package.json +30 -0
- package/src/build-postprocess.d.ts +24 -0
- package/src/build-postprocess.js +74 -0
- package/src/cem-compat.js +226 -0
- package/src/entry-generators.d.ts +3 -0
- package/src/entry-generators.js +126 -0
- package/src/entry-render-helpers.js +307 -0
- package/src/entry-render-runtime.js +94 -0
- package/src/entry-render-ssg.js +169 -0
- package/src/entry-renderer.d.ts +87 -0
- package/src/entry-renderer.js +555 -0
- package/src/external-resolver.d.ts +62 -0
- package/src/external-resolver.js +285 -0
- package/src/index.d.ts +31 -0
- package/src/index.js +28 -0
- package/src/island-manifest.d.ts +27 -0
- package/src/island-manifest.js +78 -0
- package/src/postprocess.d.ts +73 -0
- package/src/postprocess.js +376 -0
- package/src/route-scanner-fs.js +29 -0
- package/src/route-scanner.d.ts +132 -0
- package/src/route-scanner.js +497 -0
- package/src/route-type-generator.d.ts +29 -0
- package/src/route-type-generator.js +99 -0
- package/src/ssg-dynamic.js +66 -0
- package/src/ssg-helpers.d.ts +8 -0
- package/src/ssg-helpers.js +96 -0
- package/src/ssg-i18n.js +78 -0
- package/src/ssg-render.d.ts +3 -0
- package/src/ssg-render.js +162 -0
- package/src/ssg-report.js +172 -0
- package/src/ssr-polyfills.d.ts +15 -0
- package/src/ssr-polyfills.js +26 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @openelement/adapter-vite - Route Type Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates `.openElement/routes.d.ts` with type-safe route parameter definitions
|
|
5
|
+
* for the `virtual:open-routes` module. Only routes that have dynamic [param]
|
|
6
|
+
* segments are included in the output.
|
|
7
|
+
*
|
|
8
|
+
* v0.25.0: Initial implementation for build-time route type code generation.
|
|
9
|
+
*/ /**
|
|
10
|
+
* Convert a file path to a route path, preserving [param] bracket syntax.
|
|
11
|
+
* Unlike `filePathToRoutePath()` in route-scanner (which converts to `:param`
|
|
12
|
+
* for Hono/URLPattern), this preserves `[param]` to match the file-system
|
|
13
|
+
* convention that developers see in their route files.
|
|
14
|
+
*
|
|
15
|
+
* e.g., 'blog/[slug].ts' → '/blog/[slug]'
|
|
16
|
+
* e.g., 'index.ts' → '/'
|
|
17
|
+
*/ function filePathToBracketRoute(filePath) {
|
|
18
|
+
let p = filePath.replace(/\.[^.]+$/, '');
|
|
19
|
+
if (p === 'index') return '/';
|
|
20
|
+
if (p.endsWith('/index')) {
|
|
21
|
+
p = p.slice(0, -6);
|
|
22
|
+
if (p === '') return '/';
|
|
23
|
+
}
|
|
24
|
+
if (!p.startsWith('/')) p = '/' + p;
|
|
25
|
+
return p;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generate TypeScript type literal for route params.
|
|
29
|
+
* Converts param names to a Record<string, string> type.
|
|
30
|
+
*
|
|
31
|
+
* Example: ['slug'] → `{ slug: string }`
|
|
32
|
+
* Example: ['package', 'component'] → `{ package: string; component: string }`
|
|
33
|
+
*/ function generateParamsType(params) {
|
|
34
|
+
if (params.length === 0) {
|
|
35
|
+
return 'Record<string, never>';
|
|
36
|
+
}
|
|
37
|
+
const fields = params.map((p)=>`${p}: string`);
|
|
38
|
+
return `{ ${fields.join('; ')} }`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Generate the `.openElement/routes.d.ts` declaration file content.
|
|
42
|
+
*
|
|
43
|
+
* Only routes that have dynamic parameters (non-empty `params` array)
|
|
44
|
+
* are included in the generated output. Static routes with no params
|
|
45
|
+
* are excluded since they don't need type-safe parameter access.
|
|
46
|
+
*
|
|
47
|
+
* Example output:
|
|
48
|
+
* ```typescript
|
|
49
|
+
* declare module 'virtual:open-routes' {
|
|
50
|
+
* interface RouteParams {
|
|
51
|
+
* '/blog/[slug]': { slug: string };
|
|
52
|
+
* '/registry/[package]/[component]': { package: string; component: string };
|
|
53
|
+
* }
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @param routes - Array of route entries from the route scanner
|
|
58
|
+
* @returns TypeScript declaration file content as a string
|
|
59
|
+
*/ export function generateRouteTypes(routes) {
|
|
60
|
+
// Filter for page routes that have dynamic params
|
|
61
|
+
const paramRoutes = routes.filter((r)=>r.type === 'page' && r.params && r.params.length > 0);
|
|
62
|
+
if (paramRoutes.length === 0) {
|
|
63
|
+
// No routes with params - generate a placeholder interface
|
|
64
|
+
return [
|
|
65
|
+
'/**',
|
|
66
|
+
' * Auto-generated by @openelement/adapter-vite (v0.25.0)',
|
|
67
|
+
' * DO NOT EDIT MANUALLY',
|
|
68
|
+
' */',
|
|
69
|
+
'',
|
|
70
|
+
"declare module 'virtual:open-routes' {",
|
|
71
|
+
' // eslint-disable-next-line @typescript-eslint/no-empty-interface',
|
|
72
|
+
' export interface RouteParams {',
|
|
73
|
+
' }',
|
|
74
|
+
'}',
|
|
75
|
+
''
|
|
76
|
+
].join('\n');
|
|
77
|
+
}
|
|
78
|
+
// Generate entries for each route with params
|
|
79
|
+
const paramEntries = paramRoutes.map((r)=>{
|
|
80
|
+
const bracketPath = filePathToBracketRoute(r.filePath);
|
|
81
|
+
const path = JSON.stringify(bracketPath);
|
|
82
|
+
const type = generateParamsType(r.params);
|
|
83
|
+
return ` ${path}: ${type};`;
|
|
84
|
+
});
|
|
85
|
+
return [
|
|
86
|
+
'/**',
|
|
87
|
+
' * Auto-generated by @openelement/adapter-vite (v0.25.0)',
|
|
88
|
+
' * DO NOT EDIT MANUALLY',
|
|
89
|
+
' */',
|
|
90
|
+
'',
|
|
91
|
+
"declare module 'virtual:open-routes' {",
|
|
92
|
+
' export interface RouteParams {',
|
|
93
|
+
...paramEntries,
|
|
94
|
+
' }',
|
|
95
|
+
'}',
|
|
96
|
+
''
|
|
97
|
+
].join('\n');
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9zc2cvc3JjL3JvdXRlLXR5cGUtZ2VuZXJhdG9yLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQG9wZW5lbGVtZW50L2FkYXB0ZXItdml0ZSAtIFJvdXRlIFR5cGUgR2VuZXJhdG9yXG4gKlxuICogR2VuZXJhdGVzIGAub3BlbkVsZW1lbnQvcm91dGVzLmQudHNgIHdpdGggdHlwZS1zYWZlIHJvdXRlIHBhcmFtZXRlciBkZWZpbml0aW9uc1xuICogZm9yIHRoZSBgdmlydHVhbDpvcGVuLXJvdXRlc2AgbW9kdWxlLiBPbmx5IHJvdXRlcyB0aGF0IGhhdmUgZHluYW1pYyBbcGFyYW1dXG4gKiBzZWdtZW50cyBhcmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dC5cbiAqXG4gKiB2MC4yNS4wOiBJbml0aWFsIGltcGxlbWVudGF0aW9uIGZvciBidWlsZC10aW1lIHJvdXRlIHR5cGUgY29kZSBnZW5lcmF0aW9uLlxuICovXG5cbmltcG9ydCB0eXBlIHsgUm91dGVFbnRyeSB9IGZyb20gJ0BvcGVuZWxlbWVudC9wcm90b2NvbC9mcmFtZXdvcmsnO1xuXG4vKipcbiAqIENvbnZlcnQgYSBmaWxlIHBhdGggdG8gYSByb3V0ZSBwYXRoLCBwcmVzZXJ2aW5nIFtwYXJhbV0gYnJhY2tldCBzeW50YXguXG4gKiBVbmxpa2UgYGZpbGVQYXRoVG9Sb3V0ZVBhdGgoKWAgaW4gcm91dGUtc2Nhbm5lciAod2hpY2ggY29udmVydHMgdG8gYDpwYXJhbWBcbiAqIGZvciBIb25vL1VSTFBhdHRlcm4pLCB0aGlzIHByZXNlcnZlcyBgW3BhcmFtXWAgdG8gbWF0Y2ggdGhlIGZpbGUtc3lzdGVtXG4gKiBjb252ZW50aW9uIHRoYXQgZGV2ZWxvcGVycyBzZWUgaW4gdGhlaXIgcm91dGUgZmlsZXMuXG4gKlxuICogZS5nLiwgJ2Jsb2cvW3NsdWddLnRzJyDihpIgJy9ibG9nL1tzbHVnXSdcbiAqIGUuZy4sICdpbmRleC50cycg4oaSICcvJ1xuICovXG5mdW5jdGlvbiBmaWxlUGF0aFRvQnJhY2tldFJvdXRlKGZpbGVQYXRoOiBzdHJpbmcpOiBzdHJpbmcge1xuICBsZXQgcCA9IGZpbGVQYXRoLnJlcGxhY2UoL1xcLlteLl0rJC8sICcnKTtcblxuICBpZiAocCA9PT0gJ2luZGV4JykgcmV0dXJuICcvJztcbiAgaWYgKHAuZW5kc1dpdGgoJy9pbmRleCcpKSB7XG4gICAgcCA9IHAuc2xpY2UoMCwgLTYpO1xuICAgIGlmIChwID09PSAnJykgcmV0dXJuICcvJztcbiAgfVxuICBpZiAoIXAuc3RhcnRzV2l0aCgnLycpKSBwID0gJy8nICsgcDtcblxuICByZXR1cm4gcDtcbn1cblxuLyoqXG4gKiBHZW5lcmF0ZSBUeXBlU2NyaXB0IHR5cGUgbGl0ZXJhbCBmb3Igcm91dGUgcGFyYW1zLlxuICogQ29udmVydHMgcGFyYW0gbmFtZXMgdG8gYSBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IHR5cGUuXG4gKlxuICogRXhhbXBsZTogWydzbHVnJ10g4oaSIGB7IHNsdWc6IHN0cmluZyB9YFxuICogRXhhbXBsZTogWydwYWNrYWdlJywgJ2NvbXBvbmVudCddIOKGkiBgeyBwYWNrYWdlOiBzdHJpbmc7IGNvbXBvbmVudDogc3RyaW5nIH1gXG4gKi9cbmZ1bmN0aW9uIGdlbmVyYXRlUGFyYW1zVHlwZShwYXJhbXM6IHN0cmluZ1tdKTogc3RyaW5nIHtcbiAgaWYgKHBhcmFtcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gJ1JlY29yZDxzdHJpbmcsIG5ldmVyPic7XG4gIH1cbiAgY29uc3QgZmllbGRzID0gcGFyYW1zLm1hcCgocCkgPT4gYCR7cH06IHN0cmluZ2ApO1xuICByZXR1cm4gYHsgJHtmaWVsZHMuam9pbignOyAnKX0gfWA7XG59XG5cbi8qKlxuICogR2VuZXJhdGUgdGhlIGAub3BlbkVsZW1lbnQvcm91dGVzLmQudHNgIGRlY2xhcmF0aW9uIGZpbGUgY29udGVudC5cbiAqXG4gKiBPbmx5IHJvdXRlcyB0aGF0IGhhdmUgZHluYW1pYyBwYXJhbWV0ZXJzIChub24tZW1wdHkgYHBhcmFtc2AgYXJyYXkpXG4gKiBhcmUgaW5jbHVkZWQgaW4gdGhlIGdlbmVyYXRlZCBvdXRwdXQuIFN0YXRpYyByb3V0ZXMgd2l0aCBubyBwYXJhbXNcbiAqIGFyZSBleGNsdWRlZCBzaW5jZSB0aGV5IGRvbid0IG5lZWQgdHlwZS1zYWZlIHBhcmFtZXRlciBhY2Nlc3MuXG4gKlxuICogRXhhbXBsZSBvdXRwdXQ6XG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBkZWNsYXJlIG1vZHVsZSAndmlydHVhbDpvcGVuLXJvdXRlcycge1xuICogICBpbnRlcmZhY2UgUm91dGVQYXJhbXMge1xuICogICAgICcvYmxvZy9bc2x1Z10nOiB7IHNsdWc6IHN0cmluZyB9O1xuICogICAgICcvcmVnaXN0cnkvW3BhY2thZ2VdL1tjb21wb25lbnRdJzogeyBwYWNrYWdlOiBzdHJpbmc7IGNvbXBvbmVudDogc3RyaW5nIH07XG4gKiAgIH1cbiAqIH1cbiAqIGBgYFxuICpcbiAqIEBwYXJhbSByb3V0ZXMgLSBBcnJheSBvZiByb3V0ZSBlbnRyaWVzIGZyb20gdGhlIHJvdXRlIHNjYW5uZXJcbiAqIEByZXR1cm5zIFR5cGVTY3JpcHQgZGVjbGFyYXRpb24gZmlsZSBjb250ZW50IGFzIGEgc3RyaW5nXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZVJvdXRlVHlwZXMocm91dGVzOiBSb3V0ZUVudHJ5W10pOiBzdHJpbmcge1xuICAvLyBGaWx0ZXIgZm9yIHBhZ2Ugcm91dGVzIHRoYXQgaGF2ZSBkeW5hbWljIHBhcmFtc1xuICBjb25zdCBwYXJhbVJvdXRlcyA9IHJvdXRlcy5maWx0ZXIoXG4gICAgKHIpID0+IHIudHlwZSA9PT0gJ3BhZ2UnICYmIHIucGFyYW1zICYmIHIucGFyYW1zLmxlbmd0aCA+IDAsXG4gICk7XG5cbiAgaWYgKHBhcmFtUm91dGVzLmxlbmd0aCA9PT0gMCkge1xuICAgIC8vIE5vIHJvdXRlcyB3aXRoIHBhcmFtcyAtIGdlbmVyYXRlIGEgcGxhY2Vob2xkZXIgaW50ZXJmYWNlXG4gICAgcmV0dXJuIFtcbiAgICAgICcvKionLFxuICAgICAgJyAqIEF1dG8tZ2VuZXJhdGVkIGJ5IEBvcGVuZWxlbWVudC9hZGFwdGVyLXZpdGUgKHYwLjI1LjApJyxcbiAgICAgICcgKiBETyBOT1QgRURJVCBNQU5VQUxMWScsXG4gICAgICAnICovJyxcbiAgICAgICcnLFxuICAgICAgXCJkZWNsYXJlIG1vZHVsZSAndmlydHVhbDpvcGVuLXJvdXRlcycge1wiLFxuICAgICAgJyAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1lbXB0eS1pbnRlcmZhY2UnLFxuICAgICAgJyAgZXhwb3J0IGludGVyZmFjZSBSb3V0ZVBhcmFtcyB7JyxcbiAgICAgICcgIH0nLFxuICAgICAgJ30nLFxuICAgICAgJycsXG4gICAgXS5qb2luKCdcXG4nKTtcbiAgfVxuXG4gIC8vIEdlbmVyYXRlIGVudHJpZXMgZm9yIGVhY2ggcm91dGUgd2l0aCBwYXJhbXNcbiAgY29uc3QgcGFyYW1FbnRyaWVzID0gcGFyYW1Sb3V0ZXMubWFwKChyKSA9PiB7XG4gICAgY29uc3QgYnJhY2tldFBhdGggPSBmaWxlUGF0aFRvQnJhY2tldFJvdXRlKHIuZmlsZVBhdGgpO1xuICAgIGNvbnN0IHBhdGggPSBKU09OLnN0cmluZ2lmeShicmFja2V0UGF0aCk7XG4gICAgY29uc3QgdHlwZSA9IGdlbmVyYXRlUGFyYW1zVHlwZShyLnBhcmFtcyEpO1xuICAgIHJldHVybiBgICAgICR7cGF0aH06ICR7dHlwZX07YDtcbiAgfSk7XG5cbiAgcmV0dXJuIFtcbiAgICAnLyoqJyxcbiAgICAnICogQXV0by1nZW5lcmF0ZWQgYnkgQG9wZW5lbGVtZW50L2FkYXB0ZXItdml0ZSAodjAuMjUuMCknLFxuICAgICcgKiBETyBOT1QgRURJVCBNQU5VQUxMWScsXG4gICAgJyAqLycsXG4gICAgJycsXG4gICAgXCJkZWNsYXJlIG1vZHVsZSAndmlydHVhbDpvcGVuLXJvdXRlcycge1wiLFxuICAgICcgIGV4cG9ydCBpbnRlcmZhY2UgUm91dGVQYXJhbXMgeycsXG4gICAgLi4ucGFyYW1FbnRyaWVzLFxuICAgICcgIH0nLFxuICAgICd9JyxcbiAgICAnJyxcbiAgXS5qb2luKCdcXG4nKTtcbn1cbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Q0FRQyxHQUlEOzs7Ozs7OztDQVFDLEdBQ0QsU0FBUyx1QkFBdUIsUUFBZ0I7RUFDOUMsSUFBSSxJQUFJLFNBQVMsT0FBTyxDQUFDLFlBQVk7RUFFckMsSUFBSSxNQUFNLFNBQVMsT0FBTztFQUMxQixJQUFJLEVBQUUsUUFBUSxDQUFDLFdBQVc7SUFDeEIsSUFBSSxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUM7SUFDaEIsSUFBSSxNQUFNLElBQUksT0FBTztFQUN2QjtFQUNBLElBQUksQ0FBQyxFQUFFLFVBQVUsQ0FBQyxNQUFNLElBQUksTUFBTTtFQUVsQyxPQUFPO0FBQ1Q7QUFFQTs7Ozs7O0NBTUMsR0FDRCxTQUFTLG1CQUFtQixNQUFnQjtFQUMxQyxJQUFJLE9BQU8sTUFBTSxLQUFLLEdBQUc7SUFDdkIsT0FBTztFQUNUO0VBQ0EsTUFBTSxTQUFTLE9BQU8sR0FBRyxDQUFDLENBQUMsSUFBTSxHQUFHLEVBQUUsUUFBUSxDQUFDO0VBQy9DLE9BQU8sQ0FBQyxFQUFFLEVBQUUsT0FBTyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7QUFDbkM7QUFFQTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztDQW1CQyxHQUNELE9BQU8sU0FBUyxtQkFBbUIsTUFBb0I7RUFDckQsa0RBQWtEO0VBQ2xELE1BQU0sY0FBYyxPQUFPLE1BQU0sQ0FDL0IsQ0FBQyxJQUFNLEVBQUUsSUFBSSxLQUFLLFVBQVUsRUFBRSxNQUFNLElBQUksRUFBRSxNQUFNLENBQUMsTUFBTSxHQUFHO0VBRzVELElBQUksWUFBWSxNQUFNLEtBQUssR0FBRztJQUM1QiwyREFBMkQ7SUFDM0QsT0FBTztNQUNMO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7S0FDRCxDQUFDLElBQUksQ0FBQztFQUNUO0VBRUEsOENBQThDO0VBQzlDLE1BQU0sZUFBZSxZQUFZLEdBQUcsQ0FBQyxDQUFDO0lBQ3BDLE1BQU0sY0FBYyx1QkFBdUIsRUFBRSxRQUFRO0lBQ3JELE1BQU0sT0FBTyxLQUFLLFNBQVMsQ0FBQztJQUM1QixNQUFNLE9BQU8sbUJBQW1CLEVBQUUsTUFBTTtJQUN4QyxPQUFPLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO0VBQ2hDO0VBRUEsT0FBTztJQUNMO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO09BQ0c7SUFDSDtJQUNBO0lBQ0E7R0FDRCxDQUFDLElBQUksQ0FBQztBQUNUIn0=
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @openelement/ssg - Dynamic route expansion
|
|
3
|
+
*
|
|
4
|
+
* Handles dynamic route rendering using getStaticPaths() + renderRoute()
|
|
5
|
+
* from the SSR bundle.
|
|
6
|
+
*/ import { join } from 'node:path';
|
|
7
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { createLogger } from '@openelement/core/logger';
|
|
9
|
+
import { formatError } from '@openelement/core/errors';
|
|
10
|
+
import { collectPageOutput, resolveDynamicRoutePath } from './ssg-helpers.js';
|
|
11
|
+
const log = createLogger('ssg');
|
|
12
|
+
/**
|
|
13
|
+
* Expand dynamic routes by calling getStaticPaths() and renderRoute()
|
|
14
|
+
* for each parameter set.
|
|
15
|
+
*
|
|
16
|
+
* Returns a map of static path params keyed by route path, which is
|
|
17
|
+
* consumed later when building the ISR manifest.
|
|
18
|
+
*/ export async function expandDynamicRoutes(dynamicRoutes, renderRoute, getStaticPaths, options, root, outDir, pageDiagnostics) {
|
|
19
|
+
const staticPathParamsByRoute = new Map();
|
|
20
|
+
if (dynamicRoutes.length > 0 && renderRoute && getStaticPaths) {
|
|
21
|
+
for (const route of dynamicRoutes){
|
|
22
|
+
const paramNames = route.paramNames;
|
|
23
|
+
let paramsList;
|
|
24
|
+
try {
|
|
25
|
+
paramsList = await getStaticPaths(route.path);
|
|
26
|
+
} catch (e) {
|
|
27
|
+
log.warn(`Failed to get static paths for ${route.path}: ${formatError(e)}`);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
staticPathParamsByRoute.set(route.path, paramsList);
|
|
31
|
+
if (paramsList.length === 0) {
|
|
32
|
+
log.info(`Dynamic route ${route.path} has no static paths - skipping`);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
for (const params of paramsList){
|
|
36
|
+
let resolvedPath;
|
|
37
|
+
try {
|
|
38
|
+
resolvedPath = resolveDynamicRoutePath(route.path, paramNames, params);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
log.warn(`Skipping unsafe dynamic route ${route.path}: ${formatError(e)}`);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const output = await renderRoute(route.path, {
|
|
45
|
+
params,
|
|
46
|
+
title: options.html?.title,
|
|
47
|
+
lang: options.html?.lang,
|
|
48
|
+
headExtras: options.headExtras
|
|
49
|
+
});
|
|
50
|
+
const html = collectPageOutput(resolvedPath, output, pageDiagnostics);
|
|
51
|
+
const outputDir = join(root, outDir);
|
|
52
|
+
const pageDir = join(outputDir, resolvedPath);
|
|
53
|
+
mkdirSync(pageDir, {
|
|
54
|
+
recursive: true
|
|
55
|
+
});
|
|
56
|
+
writeFileSync(join(pageDir, 'index.html'), html, 'utf-8');
|
|
57
|
+
log.info(`Dynamic route: ${resolvedPath} -> ${resolvedPath}/index.html`);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
log.warn(`Failed to render dynamic route ${resolvedPath}: ${formatError(e)}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return staticPathParamsByRoute;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9zc2cvc3JjL3NzZy1keW5hbWljLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQG9wZW5lbGVtZW50L3NzZyAtIER5bmFtaWMgcm91dGUgZXhwYW5zaW9uXG4gKlxuICogSGFuZGxlcyBkeW5hbWljIHJvdXRlIHJlbmRlcmluZyB1c2luZyBnZXRTdGF0aWNQYXRocygpICsgcmVuZGVyUm91dGUoKVxuICogZnJvbSB0aGUgU1NSIGJ1bmRsZS5cbiAqL1xuXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IG1rZGlyU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ25vZGU6ZnMnO1xuaW1wb3J0IHR5cGUgeyBTc2dQYWdlT3V0cHV0LCBTc2dSZW5kZXJPcHRpb25zIH0gZnJvbSAnQG9wZW5lbGVtZW50L3Byb3RvY29sL3NzZyc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZS9sb2dnZXInO1xuaW1wb3J0IHsgZm9ybWF0RXJyb3IgfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZS9lcnJvcnMnO1xuaW1wb3J0IHsgY29sbGVjdFBhZ2VPdXRwdXQsIHR5cGUgUGFnZURpYWdub3N0aWMsIHJlc29sdmVEeW5hbWljUm91dGVQYXRoIH0gZnJvbSAnLi9zc2ctaGVscGVycy5qcyc7XG5cbmNvbnN0IGxvZyA9IGNyZWF0ZUxvZ2dlcignc3NnJyk7XG5cbmludGVyZmFjZSBSb3V0ZUluZm9JdGVtIHtcbiAgcGF0aDogc3RyaW5nO1xuICB0YWdOYW1lOiBzdHJpbmc7XG4gIGlzRHluYW1pYzogYm9vbGVhbjtcbiAgcGFyYW1OYW1lczogc3RyaW5nW107XG4gIHJldmFsaWRhdGU/OiBudW1iZXI7XG4gIHBhcmFtcz86IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG59XG5cbi8qKlxuICogRXhwYW5kIGR5bmFtaWMgcm91dGVzIGJ5IGNhbGxpbmcgZ2V0U3RhdGljUGF0aHMoKSBhbmQgcmVuZGVyUm91dGUoKVxuICogZm9yIGVhY2ggcGFyYW1ldGVyIHNldC5cbiAqXG4gKiBSZXR1cm5zIGEgbWFwIG9mIHN0YXRpYyBwYXRoIHBhcmFtcyBrZXllZCBieSByb3V0ZSBwYXRoLCB3aGljaCBpc1xuICogY29uc3VtZWQgbGF0ZXIgd2hlbiBidWlsZGluZyB0aGUgSVNSIG1hbmlmZXN0LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhwYW5kRHluYW1pY1JvdXRlcyhcbiAgZHluYW1pY1JvdXRlczogUm91dGVJbmZvSXRlbVtdLFxuICByZW5kZXJSb3V0ZTpcbiAgICB8ICgocGF0aDogc3RyaW5nLCBvcHRzPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pID0+IFByb21pc2U8U3NnUGFnZU91dHB1dD4pXG4gICAgfCB1bmRlZmluZWQsXG4gIGdldFN0YXRpY1BhdGhzOlxuICAgIHwgKChwYXRoOiBzdHJpbmcpID0+IFByb21pc2U8QXJyYXk8UmVjb3JkPHN0cmluZywgc3RyaW5nPj4+KVxuICAgIHwgdW5kZWZpbmVkLFxuICBvcHRpb25zOiBTc2dSZW5kZXJPcHRpb25zLFxuICByb290OiBzdHJpbmcsXG4gIG91dERpcjogc3RyaW5nLFxuICBwYWdlRGlhZ25vc3RpY3M6IFBhZ2VEaWFnbm9zdGljW10sXG4pOiBQcm9taXNlPE1hcDxzdHJpbmcsIEFycmF5PFJlY29yZDxzdHJpbmcsIHN0cmluZz4+Pj4ge1xuICBjb25zdCBzdGF0aWNQYXRoUGFyYW1zQnlSb3V0ZSA9IG5ldyBNYXA8c3RyaW5nLCBBcnJheTxSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+Pj4oKTtcblxuICBpZiAoZHluYW1pY1JvdXRlcy5sZW5ndGggPiAwICYmIHJlbmRlclJvdXRlICYmIGdldFN0YXRpY1BhdGhzKSB7XG4gICAgZm9yIChjb25zdCByb3V0ZSBvZiBkeW5hbWljUm91dGVzKSB7XG4gICAgICBjb25zdCBwYXJhbU5hbWVzID0gcm91dGUucGFyYW1OYW1lcztcbiAgICAgIGxldCBwYXJhbXNMaXN0OiBBcnJheTxSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+PjtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgcGFyYW1zTGlzdCA9IGF3YWl0IGdldFN0YXRpY1BhdGhzKHJvdXRlLnBhdGgpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBsb2cud2FybihcbiAgICAgICAgICBgRmFpbGVkIHRvIGdldCBzdGF0aWMgcGF0aHMgZm9yICR7cm91dGUucGF0aH06ICR7Zm9ybWF0RXJyb3IoZSl9YCxcbiAgICAgICAgKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBzdGF0aWNQYXRoUGFyYW1zQnlSb3V0ZS5zZXQocm91dGUucGF0aCwgcGFyYW1zTGlzdCk7XG5cbiAgICAgIGlmIChwYXJhbXNMaXN0Lmxlbmd0aCA9PT0gMCkge1xuICAgICAgICBsb2cuaW5mbyhgRHluYW1pYyByb3V0ZSAke3JvdXRlLnBhdGh9IGhhcyBubyBzdGF0aWMgcGF0aHMgLSBza2lwcGluZ2ApO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgZm9yIChjb25zdCBwYXJhbXMgb2YgcGFyYW1zTGlzdCkge1xuICAgICAgICBsZXQgcmVzb2x2ZWRQYXRoOiBzdHJpbmc7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgcmVzb2x2ZWRQYXRoID0gcmVzb2x2ZUR5bmFtaWNSb3V0ZVBhdGgoXG4gICAgICAgICAgICByb3V0ZS5wYXRoLFxuICAgICAgICAgICAgcGFyYW1OYW1lcyxcbiAgICAgICAgICAgIHBhcmFtcyxcbiAgICAgICAgICApO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgbG9nLndhcm4oXG4gICAgICAgICAgICBgU2tpcHBpbmcgdW5zYWZlIGR5bmFtaWMgcm91dGUgJHtyb3V0ZS5wYXRofTogJHtmb3JtYXRFcnJvcihlKX1gLFxuICAgICAgICAgICk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IG91dHB1dCA9IGF3YWl0IHJlbmRlclJvdXRlKHJvdXRlLnBhdGgsIHtcbiAgICAgICAgICAgIHBhcmFtcyxcbiAgICAgICAgICAgIHRpdGxlOiBvcHRpb25zLmh0bWw/LnRpdGxlLFxuICAgICAgICAgICAgbGFuZzogb3B0aW9ucy5odG1sPy5sYW5nLFxuICAgICAgICAgICAgaGVhZEV4dHJhczogb3B0aW9ucy5oZWFkRXh0cmFzLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGNvbnN0IGh0bWwgPSBjb2xsZWN0UGFnZU91dHB1dChyZXNvbHZlZFBhdGgsIG91dHB1dCwgcGFnZURpYWdub3N0aWNzKTtcblxuICAgICAgICAgIGNvbnN0IG91dHB1dERpciA9IGpvaW4ocm9vdCwgb3V0RGlyKTtcbiAgICAgICAgICBjb25zdCBwYWdlRGlyID0gam9pbihvdXRwdXREaXIsIHJlc29sdmVkUGF0aCk7XG4gICAgICAgICAgbWtkaXJTeW5jKHBhZ2VEaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgICAgICAgIHdyaXRlRmlsZVN5bmMoam9pbihwYWdlRGlyLCAnaW5kZXguaHRtbCcpLCBodG1sLCAndXRmLTgnKTtcbiAgICAgICAgICBsb2cuaW5mbyhcbiAgICAgICAgICAgIGBEeW5hbWljIHJvdXRlOiAke3Jlc29sdmVkUGF0aH0gLT4gJHtyZXNvbHZlZFBhdGh9L2luZGV4Lmh0bWxgLFxuICAgICAgICAgICk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICBsb2cud2FybihcbiAgICAgICAgICAgIGBGYWlsZWQgdG8gcmVuZGVyIGR5bmFtaWMgcm91dGUgJHtyZXNvbHZlZFBhdGh9OiAke2Zvcm1hdEVycm9yKGUpfWAsXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBzdGF0aWNQYXRoUGFyYW1zQnlSb3V0ZTtcbn1cbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Q0FLQyxHQUVELFNBQVMsSUFBSSxRQUFRLFlBQVk7QUFDakMsU0FBUyxTQUFTLEVBQUUsYUFBYSxRQUFRLFVBQVU7QUFFbkQsU0FBUyxZQUFZLFFBQVEsMkJBQTJCO0FBQ3hELFNBQVMsV0FBVyxRQUFRLDJCQUEyQjtBQUN2RCxTQUFTLGlCQUFpQixFQUF1Qix1QkFBdUIsUUFBUSxtQkFBbUI7QUFFbkcsTUFBTSxNQUFNLGFBQWE7QUFXekI7Ozs7OztDQU1DLEdBQ0QsT0FBTyxlQUFlLG9CQUNwQixhQUE4QixFQUM5QixXQUVhLEVBQ2IsY0FFYSxFQUNiLE9BQXlCLEVBQ3pCLElBQVksRUFDWixNQUFjLEVBQ2QsZUFBaUM7RUFFakMsTUFBTSwwQkFBMEIsSUFBSTtFQUVwQyxJQUFJLGNBQWMsTUFBTSxHQUFHLEtBQUssZUFBZSxnQkFBZ0I7SUFDN0QsS0FBSyxNQUFNLFNBQVMsY0FBZTtNQUNqQyxNQUFNLGFBQWEsTUFBTSxVQUFVO01BQ25DLElBQUk7TUFFSixJQUFJO1FBQ0YsYUFBYSxNQUFNLGVBQWUsTUFBTSxJQUFJO01BQzlDLEVBQUUsT0FBTyxHQUFHO1FBQ1YsSUFBSSxJQUFJLENBQ04sQ0FBQywrQkFBK0IsRUFBRSxNQUFNLElBQUksQ0FBQyxFQUFFLEVBQUUsWUFBWSxJQUFJO1FBRW5FO01BQ0Y7TUFDQSx3QkFBd0IsR0FBRyxDQUFDLE1BQU0sSUFBSSxFQUFFO01BRXhDLElBQUksV0FBVyxNQUFNLEtBQUssR0FBRztRQUMzQixJQUFJLElBQUksQ0FBQyxDQUFDLGNBQWMsRUFBRSxNQUFNLElBQUksQ0FBQywrQkFBK0IsQ0FBQztRQUNyRTtNQUNGO01BRUEsS0FBSyxNQUFNLFVBQVUsV0FBWTtRQUMvQixJQUFJO1FBQ0osSUFBSTtVQUNGLGVBQWUsd0JBQ2IsTUFBTSxJQUFJLEVBQ1YsWUFDQTtRQUVKLEVBQUUsT0FBTyxHQUFHO1VBQ1YsSUFBSSxJQUFJLENBQ04sQ0FBQyw4QkFBOEIsRUFBRSxNQUFNLElBQUksQ0FBQyxFQUFFLEVBQUUsWUFBWSxJQUFJO1VBRWxFO1FBQ0Y7UUFFQSxJQUFJO1VBQ0YsTUFBTSxTQUFTLE1BQU0sWUFBWSxNQUFNLElBQUksRUFBRTtZQUMzQztZQUNBLE9BQU8sUUFBUSxJQUFJLEVBQUU7WUFDckIsTUFBTSxRQUFRLElBQUksRUFBRTtZQUNwQixZQUFZLFFBQVEsVUFBVTtVQUNoQztVQUNBLE1BQU0sT0FBTyxrQkFBa0IsY0FBYyxRQUFRO1VBRXJELE1BQU0sWUFBWSxLQUFLLE1BQU07VUFDN0IsTUFBTSxVQUFVLEtBQUssV0FBVztVQUNoQyxVQUFVLFNBQVM7WUFBRSxXQUFXO1VBQUs7VUFDckMsY0FBYyxLQUFLLFNBQVMsZUFBZSxNQUFNO1VBQ2pELElBQUksSUFBSSxDQUNOLENBQUMsZUFBZSxFQUFFLGFBQWEsSUFBSSxFQUFFLGFBQWEsV0FBVyxDQUFDO1FBRWxFLEVBQUUsT0FBTyxHQUFHO1VBQ1YsSUFBSSxJQUFJLENBQ04sQ0FBQywrQkFBK0IsRUFBRSxhQUFhLEVBQUUsRUFBRSxZQUFZLElBQUk7UUFFdkU7TUFDRjtJQUNGO0VBQ0Y7RUFFQSxPQUFPO0FBQ1QifQ==
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a dynamic route path by substituting param values.
|
|
3
|
+
* Validates param values to prevent path traversal and control characters.
|
|
4
|
+
*/ export declare function resolveDynamicRoutePath(routePath: string, paramNames: string[], params: Record<string, string>): string;
|
|
5
|
+
/**
|
|
6
|
+
* Stable SHA-256 hash for SSG-generated asset names.
|
|
7
|
+
* Returns a deterministic lowercase hex string.
|
|
8
|
+
*/ export declare function stableHash(str: string): Promise<string>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @openelement/ssg - SSG helper utilities
|
|
3
|
+
*
|
|
4
|
+
* Pure utility functions used by the SSG render pipeline.
|
|
5
|
+
* This module sits at the bottom of the dependency graph.
|
|
6
|
+
*/ import { join } from 'node:path';
|
|
7
|
+
import { readdirSync } from 'node:fs';
|
|
8
|
+
import { createIsrCacheKey } from '@openelement/core/isr';
|
|
9
|
+
// ─── Path / URL helpers ────────────────────────────────────────
|
|
10
|
+
/** Recursively find all .html files under a directory. */ export function findHtmlFiles(dir) {
|
|
11
|
+
const results = [];
|
|
12
|
+
try {
|
|
13
|
+
const entries = readdirSync(dir, {
|
|
14
|
+
withFileTypes: true
|
|
15
|
+
});
|
|
16
|
+
for (const entry of entries){
|
|
17
|
+
const fullPath = join(dir, entry.name);
|
|
18
|
+
if (entry.isDirectory()) {
|
|
19
|
+
results.push(...findHtmlFiles(fullPath));
|
|
20
|
+
} else if (entry.name.endsWith('.html')) {
|
|
21
|
+
results.push(fullPath);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// Directory may not exist yet
|
|
26
|
+
}
|
|
27
|
+
return results;
|
|
28
|
+
}
|
|
29
|
+
// ─── Route helpers ─────────────────────────────────────────────
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a dynamic route path by substituting param values.
|
|
32
|
+
* Validates param values to prevent path traversal and control characters.
|
|
33
|
+
*/ export function resolveDynamicRoutePath(routePath, paramNames, params) {
|
|
34
|
+
let resolvedPath = routePath;
|
|
35
|
+
for (const name of paramNames){
|
|
36
|
+
const raw = params[name];
|
|
37
|
+
if (raw === undefined || raw === null || raw === '') {
|
|
38
|
+
throw new Error(`Missing value for route parameter "${name}" in ${routePath}`);
|
|
39
|
+
}
|
|
40
|
+
const value = String(raw);
|
|
41
|
+
if (value === '.' || value === '..' || /[\\/]/.test(value)) {
|
|
42
|
+
throw new Error(`Unsafe value for route parameter "${name}" in ${routePath}: ${value}`);
|
|
43
|
+
}
|
|
44
|
+
// Encode spaces and other URL-unsafe chars, but preserve @ for scoped packages.
|
|
45
|
+
// Full encodeURIComponent would encode @ -> %40, breaking file-to-URL matching.
|
|
46
|
+
const safeValue = value.replace(/ /g, '%20');
|
|
47
|
+
resolvedPath = resolvedPath.replace(`:${name}`, safeValue);
|
|
48
|
+
}
|
|
49
|
+
return resolvedPath;
|
|
50
|
+
}
|
|
51
|
+
// ─── Hash helpers ──────────────────────────────────────────────
|
|
52
|
+
/**
|
|
53
|
+
* Stable SHA-256 hash for SSG-generated asset names.
|
|
54
|
+
* Returns a deterministic lowercase hex string.
|
|
55
|
+
*/ export async function stableHash(str) {
|
|
56
|
+
const encoder = new TextEncoder();
|
|
57
|
+
const digest = await crypto.subtle.digest('SHA-256', encoder.encode(str));
|
|
58
|
+
return Array.from(new Uint8Array(digest)).map((b)=>b.toString(16).padStart(2, '0')).join('');
|
|
59
|
+
}
|
|
60
|
+
// ─── ISR manifest builder ──────────────────────────────────────
|
|
61
|
+
export function buildIsrManifestEntries(routeInfo, staticPathParamsByRoute) {
|
|
62
|
+
const entries = [];
|
|
63
|
+
for (const route of routeInfo){
|
|
64
|
+
const revalidate = typeof route.revalidate === 'number' && route.revalidate > 0 ? route.revalidate : undefined;
|
|
65
|
+
if (!revalidate) continue;
|
|
66
|
+
const paramsList = route.isDynamic ? staticPathParamsByRoute.get(route.path) ?? [] : [
|
|
67
|
+
route.params ?? {}
|
|
68
|
+
];
|
|
69
|
+
for (const params of paramsList){
|
|
70
|
+
entries.push({
|
|
71
|
+
path: route.path,
|
|
72
|
+
revalidate,
|
|
73
|
+
cacheKey: createIsrCacheKey(route.path, params),
|
|
74
|
+
params
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return entries;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Collect per-page render diagnostics (backward-compat with string output).
|
|
82
|
+
* Returns the rendered HTML string.
|
|
83
|
+
*/ export function collectPageOutput(routePath, output, pageDiagnostics) {
|
|
84
|
+
const html = typeof output === 'string' ? output : output.html;
|
|
85
|
+
if (typeof output !== 'string') {
|
|
86
|
+
pageDiagnostics.push({
|
|
87
|
+
path: routePath,
|
|
88
|
+
errors: output.errors,
|
|
89
|
+
hydrationHints: output.hydrationHints,
|
|
90
|
+
componentCount: output.componentCount,
|
|
91
|
+
renderTimeMs: output.renderTimeMs
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return html;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9zc2cvc3JjL3NzZy1oZWxwZXJzLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQG9wZW5lbGVtZW50L3NzZyAtIFNTRyBoZWxwZXIgdXRpbGl0aWVzXG4gKlxuICogUHVyZSB1dGlsaXR5IGZ1bmN0aW9ucyB1c2VkIGJ5IHRoZSBTU0cgcmVuZGVyIHBpcGVsaW5lLlxuICogVGhpcyBtb2R1bGUgc2l0cyBhdCB0aGUgYm90dG9tIG9mIHRoZSBkZXBlbmRlbmN5IGdyYXBoLlxuICovXG5cbmltcG9ydCB7IGpvaW4gfSBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHsgcmVhZGRpclN5bmMgfSBmcm9tICdub2RlOmZzJztcbmltcG9ydCB0eXBlIHsgSHlkcmF0aW9uSGludCwgUmVuZGVyRXJyb3IgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvcmVuZGVyJztcbmltcG9ydCB0eXBlIHsgSXNyTWFuaWZlc3RFbnRyeSB9IGZyb20gJ0BvcGVuZWxlbWVudC9wcm90b2NvbC9mcmFtZXdvcmsnO1xuaW1wb3J0IHsgY3JlYXRlSXNyQ2FjaGVLZXkgfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZS9pc3InO1xuXG4vLyDilIDilIDilIAgUGF0aCAvIFVSTCBoZWxwZXJzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4vKiogUmVjdXJzaXZlbHkgZmluZCBhbGwgLmh0bWwgZmlsZXMgdW5kZXIgYSBkaXJlY3RvcnkuICovXG5leHBvcnQgZnVuY3Rpb24gZmluZEh0bWxGaWxlcyhkaXI6IHN0cmluZyk6IHN0cmluZ1tdIHtcbiAgY29uc3QgcmVzdWx0czogc3RyaW5nW10gPSBbXTtcbiAgdHJ5IHtcbiAgICBjb25zdCBlbnRyaWVzID0gcmVhZGRpclN5bmMoZGlyLCB7IHdpdGhGaWxlVHlwZXM6IHRydWUgfSk7XG4gICAgZm9yIChjb25zdCBlbnRyeSBvZiBlbnRyaWVzKSB7XG4gICAgICBjb25zdCBmdWxsUGF0aCA9IGpvaW4oZGlyLCBlbnRyeS5uYW1lKTtcbiAgICAgIGlmIChlbnRyeS5pc0RpcmVjdG9yeSgpKSB7XG4gICAgICAgIHJlc3VsdHMucHVzaCguLi5maW5kSHRtbEZpbGVzKGZ1bGxQYXRoKSk7XG4gICAgICB9IGVsc2UgaWYgKGVudHJ5Lm5hbWUuZW5kc1dpdGgoJy5odG1sJykpIHtcbiAgICAgICAgcmVzdWx0cy5wdXNoKGZ1bGxQYXRoKTtcbiAgICAgIH1cbiAgICB9XG4gIH0gY2F0Y2gge1xuICAgIC8vIERpcmVjdG9yeSBtYXkgbm90IGV4aXN0IHlldFxuICB9XG4gIHJldHVybiByZXN1bHRzO1xufVxuXG4vLyDilIDilIDilIAgUm91dGUgaGVscGVycyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuLyoqXG4gKiBSZXNvbHZlIGEgZHluYW1pYyByb3V0ZSBwYXRoIGJ5IHN1YnN0aXR1dGluZyBwYXJhbSB2YWx1ZXMuXG4gKiBWYWxpZGF0ZXMgcGFyYW0gdmFsdWVzIHRvIHByZXZlbnQgcGF0aCB0cmF2ZXJzYWwgYW5kIGNvbnRyb2wgY2hhcmFjdGVycy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVEeW5hbWljUm91dGVQYXRoKFxuICByb3V0ZVBhdGg6IHN0cmluZyxcbiAgcGFyYW1OYW1lczogc3RyaW5nW10sXG4gIHBhcmFtczogUmVjb3JkPHN0cmluZywgc3RyaW5nPixcbik6IHN0cmluZyB7XG4gIGxldCByZXNvbHZlZFBhdGggPSByb3V0ZVBhdGg7XG4gIGZvciAoY29uc3QgbmFtZSBvZiBwYXJhbU5hbWVzKSB7XG4gICAgY29uc3QgcmF3ID0gcGFyYW1zW25hbWVdO1xuICAgIGlmIChyYXcgPT09IHVuZGVmaW5lZCB8fCByYXcgPT09IG51bGwgfHwgcmF3ID09PSAnJykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgTWlzc2luZyB2YWx1ZSBmb3Igcm91dGUgcGFyYW1ldGVyIFwiJHtuYW1lfVwiIGluICR7cm91dGVQYXRofWAsXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IHZhbHVlID0gU3RyaW5nKHJhdyk7XG4gICAgaWYgKFxuICAgICAgdmFsdWUgPT09ICcuJyB8fFxuICAgICAgdmFsdWUgPT09ICcuLicgfHxcbiAgICAgIC9bXFxcXC9dLy50ZXN0KHZhbHVlKVxuICAgICkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgVW5zYWZlIHZhbHVlIGZvciByb3V0ZSBwYXJhbWV0ZXIgXCIke25hbWV9XCIgaW4gJHtyb3V0ZVBhdGh9OiAke3ZhbHVlfWAsXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIEVuY29kZSBzcGFjZXMgYW5kIG90aGVyIFVSTC11bnNhZmUgY2hhcnMsIGJ1dCBwcmVzZXJ2ZSBAIGZvciBzY29wZWQgcGFja2FnZXMuXG4gICAgLy8gRnVsbCBlbmNvZGVVUklDb21wb25lbnQgd291bGQgZW5jb2RlIEAgLT4gJTQwLCBicmVha2luZyBmaWxlLXRvLVVSTCBtYXRjaGluZy5cbiAgICBjb25zdCBzYWZlVmFsdWUgPSB2YWx1ZS5yZXBsYWNlKC8gL2csICclMjAnKTtcbiAgICByZXNvbHZlZFBhdGggPSByZXNvbHZlZFBhdGgucmVwbGFjZShgOiR7bmFtZX1gLCBzYWZlVmFsdWUpO1xuICB9XG4gIHJldHVybiByZXNvbHZlZFBhdGg7XG59XG5cbi8vIOKUgOKUgOKUgCBIYXNoIGhlbHBlcnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbi8qKlxuICogU3RhYmxlIFNIQS0yNTYgaGFzaCBmb3IgU1NHLWdlbmVyYXRlZCBhc3NldCBuYW1lcy5cbiAqIFJldHVybnMgYSBkZXRlcm1pbmlzdGljIGxvd2VyY2FzZSBoZXggc3RyaW5nLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc3RhYmxlSGFzaChzdHI6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IGVuY29kZXIgPSBuZXcgVGV4dEVuY29kZXIoKTtcbiAgY29uc3QgZGlnZXN0ID0gYXdhaXQgY3J5cHRvLnN1YnRsZS5kaWdlc3QoJ1NIQS0yNTYnLCBlbmNvZGVyLmVuY29kZShzdHIpKTtcbiAgcmV0dXJuIEFycmF5LmZyb20obmV3IFVpbnQ4QXJyYXkoZGlnZXN0KSlcbiAgICAubWFwKChiKSA9PiBiLnRvU3RyaW5nKDE2KS5wYWRTdGFydCgyLCAnMCcpKVxuICAgIC5qb2luKCcnKTtcbn1cblxuLy8g4pSA4pSA4pSAIElTUiBtYW5pZmVzdCBidWlsZGVyIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRJc3JNYW5pZmVzdEVudHJpZXMoXG4gIHJvdXRlSW5mbzogQXJyYXk8e1xuICAgIHBhdGg6IHN0cmluZztcbiAgICBpc0R5bmFtaWM6IGJvb2xlYW47XG4gICAgcmV2YWxpZGF0ZT86IG51bWJlcjtcbiAgICBwYXJhbXM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICB9PixcbiAgc3RhdGljUGF0aFBhcmFtc0J5Um91dGU6IE1hcDxzdHJpbmcsIEFycmF5PFJlY29yZDxzdHJpbmcsIHN0cmluZz4+Pixcbik6IElzck1hbmlmZXN0RW50cnlbXSB7XG4gIGNvbnN0IGVudHJpZXM6IElzck1hbmlmZXN0RW50cnlbXSA9IFtdO1xuICBmb3IgKGNvbnN0IHJvdXRlIG9mIHJvdXRlSW5mbykge1xuICAgIGNvbnN0IHJldmFsaWRhdGUgPSB0eXBlb2Ygcm91dGUucmV2YWxpZGF0ZSA9PT0gJ251bWJlcicgJiYgcm91dGUucmV2YWxpZGF0ZSA+IDBcbiAgICAgID8gcm91dGUucmV2YWxpZGF0ZVxuICAgICAgOiB1bmRlZmluZWQ7XG4gICAgaWYgKCFyZXZhbGlkYXRlKSBjb250aW51ZTtcblxuICAgIGNvbnN0IHBhcmFtc0xpc3QgPSByb3V0ZS5pc0R5bmFtaWNcbiAgICAgID8gc3RhdGljUGF0aFBhcmFtc0J5Um91dGUuZ2V0KHJvdXRlLnBhdGgpID8/IFtdXG4gICAgICA6IFtyb3V0ZS5wYXJhbXMgPz8ge31dO1xuXG4gICAgZm9yIChjb25zdCBwYXJhbXMgb2YgcGFyYW1zTGlzdCkge1xuICAgICAgZW50cmllcy5wdXNoKHtcbiAgICAgICAgcGF0aDogcm91dGUucGF0aCxcbiAgICAgICAgcmV2YWxpZGF0ZSxcbiAgICAgICAgY2FjaGVLZXk6IGNyZWF0ZUlzckNhY2hlS2V5KHJvdXRlLnBhdGgsIHBhcmFtcyksXG4gICAgICAgIHBhcmFtcyxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICByZXR1cm4gZW50cmllcztcbn1cblxuLy8g4pSA4pSA4pSAIFBlci1wYWdlIGRpYWdub3N0aWMgY29sbGVjdG9yIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4vKiogUGFnZS1sZXZlbCByZW5kZXIgZGlhZ25vc3RpYyBlbnRyeSB1c2VkIGR1cmluZyBTU0cgcmVuZGVyLiAqL1xuZXhwb3J0IGludGVyZmFjZSBQYWdlRGlhZ25vc3RpYyB7XG4gIHBhdGg6IHN0cmluZztcbiAgZXJyb3JzOiBSZW5kZXJFcnJvcltdO1xuICBoeWRyYXRpb25IaW50czogSHlkcmF0aW9uSGludFtdO1xuICBjb21wb25lbnRDb3VudDogbnVtYmVyO1xuICByZW5kZXJUaW1lTXM6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBDb2xsZWN0IHBlci1wYWdlIHJlbmRlciBkaWFnbm9zdGljcyAoYmFja3dhcmQtY29tcGF0IHdpdGggc3RyaW5nIG91dHB1dCkuXG4gKiBSZXR1cm5zIHRoZSByZW5kZXJlZCBIVE1MIHN0cmluZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbGxlY3RQYWdlT3V0cHV0KFxuICByb3V0ZVBhdGg6IHN0cmluZyxcbiAgb3V0cHV0OiB7XG4gICAgaHRtbDogc3RyaW5nO1xuICAgIGVycm9yczogUmVuZGVyRXJyb3JbXTtcbiAgICBoeWRyYXRpb25IaW50czogSHlkcmF0aW9uSGludFtdO1xuICAgIGNvbXBvbmVudENvdW50OiBudW1iZXI7XG4gICAgcmVuZGVyVGltZU1zOiBudW1iZXI7XG4gIH0gfCBzdHJpbmcsXG4gIHBhZ2VEaWFnbm9zdGljczogUGFnZURpYWdub3N0aWNbXSxcbik6IHN0cmluZyB7XG4gIGNvbnN0IGh0bWwgPSB0eXBlb2Ygb3V0cHV0ID09PSAnc3RyaW5nJyA/IG91dHB1dCA6IG91dHB1dC5odG1sO1xuICBpZiAodHlwZW9mIG91dHB1dCAhPT0gJ3N0cmluZycpIHtcbiAgICBwYWdlRGlhZ25vc3RpY3MucHVzaCh7XG4gICAgICBwYXRoOiByb3V0ZVBhdGgsXG4gICAgICBlcnJvcnM6IG91dHB1dC5lcnJvcnMsXG4gICAgICBoeWRyYXRpb25IaW50czogb3V0cHV0Lmh5ZHJhdGlvbkhpbnRzLFxuICAgICAgY29tcG9uZW50Q291bnQ6IG91dHB1dC5jb21wb25lbnRDb3VudCxcbiAgICAgIHJlbmRlclRpbWVNczogb3V0cHV0LnJlbmRlclRpbWVNcyxcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gaHRtbDtcbn1cbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFB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
|
package/src/ssg-i18n.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @openelement/ssg - i18n locale expansion
|
|
3
|
+
*
|
|
4
|
+
* Expands SSG output for each locale when i18n options are provided.
|
|
5
|
+
* Renders each route for every locale and writes locale-prefixed output.
|
|
6
|
+
*/ import { join } from 'node:path';
|
|
7
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { createLogger } from '@openelement/core/logger';
|
|
9
|
+
import { formatError } from '@openelement/core/errors';
|
|
10
|
+
import { collectPageOutput, resolveDynamicRoutePath } from './ssg-helpers.js';
|
|
11
|
+
const log = createLogger('ssg');
|
|
12
|
+
/**
|
|
13
|
+
* Expand rendered pages for each locale when i18n is configured.
|
|
14
|
+
*
|
|
15
|
+
* For each locale and each route, re-renders the route with the locale
|
|
16
|
+
* parameter and writes output under /{locale}/{path}/index.html.
|
|
17
|
+
*/ export async function expandI18nLocales(evidence, renderRoute, routeInfo, getStaticPaths, options, root, outDir, pageDiagnostics) {
|
|
18
|
+
const i18nOpts = evidence.i18nOptions || null;
|
|
19
|
+
if (!i18nOpts || !renderRoute) return;
|
|
20
|
+
const locales = i18nOpts.locales || [];
|
|
21
|
+
if (locales.length <= 1) return;
|
|
22
|
+
log.info(`i18n: expanding for locales: ${locales.join(', ')}`);
|
|
23
|
+
for (const locale of locales){
|
|
24
|
+
for (const route of routeInfo){
|
|
25
|
+
let paramsList;
|
|
26
|
+
if (!route.isDynamic) {
|
|
27
|
+
paramsList = [
|
|
28
|
+
{}
|
|
29
|
+
];
|
|
30
|
+
} else if (getStaticPaths) {
|
|
31
|
+
try {
|
|
32
|
+
paramsList = await getStaticPaths(route.path);
|
|
33
|
+
} catch {
|
|
34
|
+
log.warn(`i18n: getStaticPaths failed for ${route.path}, skipping`);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (paramsList.length === 0) continue;
|
|
41
|
+
const paramNames = route.paramNames;
|
|
42
|
+
for (const params of paramsList){
|
|
43
|
+
let resolvedPath;
|
|
44
|
+
try {
|
|
45
|
+
resolvedPath = resolveDynamicRoutePath(route.path, paramNames, params);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
log.warn(`i18n: skipping unsafe dynamic route ${route.path}: ${formatError(e)}`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const pathSegment = resolvedPath.split('/')[1] || '';
|
|
51
|
+
if (locales.includes(pathSegment)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const localePath = '/' + locale + '/' + resolvedPath.replace(/^\//, '');
|
|
55
|
+
try {
|
|
56
|
+
const output = await renderRoute(route.path, {
|
|
57
|
+
params,
|
|
58
|
+
locale,
|
|
59
|
+
title: options.html?.title,
|
|
60
|
+
lang: locale,
|
|
61
|
+
headExtras: options.headExtras
|
|
62
|
+
});
|
|
63
|
+
const html = collectPageOutput(localePath, output, pageDiagnostics);
|
|
64
|
+
const outputDir = join(root, outDir);
|
|
65
|
+
const pageDir = join(outputDir, localePath);
|
|
66
|
+
mkdirSync(pageDir, {
|
|
67
|
+
recursive: true
|
|
68
|
+
});
|
|
69
|
+
writeFileSync(join(pageDir, 'index.html'), html, 'utf-8');
|
|
70
|
+
log.info(`i18n: ${localePath}/index.html`);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
log.warn(`i18n: failed for locale ${locale} on ${resolvedPath}: ${formatError(e)}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9zc2cvc3JjL3NzZy1pMThuLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQG9wZW5lbGVtZW50L3NzZyAtIGkxOG4gbG9jYWxlIGV4cGFuc2lvblxuICpcbiAqIEV4cGFuZHMgU1NHIG91dHB1dCBmb3IgZWFjaCBsb2NhbGUgd2hlbiBpMThuIG9wdGlvbnMgYXJlIHByb3ZpZGVkLlxuICogUmVuZGVycyBlYWNoIHJvdXRlIGZvciBldmVyeSBsb2NhbGUgYW5kIHdyaXRlcyBsb2NhbGUtcHJlZml4ZWQgb3V0cHV0LlxuICovXG5cbmltcG9ydCB7IGpvaW4gfSBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHsgbWtkaXJTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnbm9kZTpmcyc7XG5pbXBvcnQgdHlwZSB7IFNzZ1BhZ2VPdXRwdXQsIFNzZ1JlbmRlckV2aWRlbmNlLCBTc2dSZW5kZXJPcHRpb25zIH0gZnJvbSAnQG9wZW5lbGVtZW50L3Byb3RvY29sL3NzZyc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZS9sb2dnZXInO1xuaW1wb3J0IHsgZm9ybWF0RXJyb3IgfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZS9lcnJvcnMnO1xuaW1wb3J0IHsgY29sbGVjdFBhZ2VPdXRwdXQsIHR5cGUgUGFnZURpYWdub3N0aWMsIHJlc29sdmVEeW5hbWljUm91dGVQYXRoIH0gZnJvbSAnLi9zc2ctaGVscGVycy5qcyc7XG5cbmNvbnN0IGxvZyA9IGNyZWF0ZUxvZ2dlcignc3NnJyk7XG5cbmludGVyZmFjZSBSb3V0ZUluZm9JdGVtIHtcbiAgcGF0aDogc3RyaW5nO1xuICB0YWdOYW1lOiBzdHJpbmc7XG4gIGlzRHluYW1pYzogYm9vbGVhbjtcbiAgcGFyYW1OYW1lczogc3RyaW5nW107XG4gIHJldmFsaWRhdGU/OiBudW1iZXI7XG59XG5cbi8qKlxuICogRXhwYW5kIHJlbmRlcmVkIHBhZ2VzIGZvciBlYWNoIGxvY2FsZSB3aGVuIGkxOG4gaXMgY29uZmlndXJlZC5cbiAqXG4gKiBGb3IgZWFjaCBsb2NhbGUgYW5kIGVhY2ggcm91dGUsIHJlLXJlbmRlcnMgdGhlIHJvdXRlIHdpdGggdGhlIGxvY2FsZVxuICogcGFyYW1ldGVyIGFuZCB3cml0ZXMgb3V0cHV0IHVuZGVyIC97bG9jYWxlfS97cGF0aH0vaW5kZXguaHRtbC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGV4cGFuZEkxOG5Mb2NhbGVzKFxuICBldmlkZW5jZTogU3NnUmVuZGVyRXZpZGVuY2UsXG4gIHJlbmRlclJvdXRlOlxuICAgIHwgKChwYXRoOiBzdHJpbmcsIG9wdHM/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgPT4gUHJvbWlzZTxTc2dQYWdlT3V0cHV0PilcbiAgICB8IHVuZGVmaW5lZCxcbiAgcm91dGVJbmZvOiBSb3V0ZUluZm9JdGVtW10sXG4gIGdldFN0YXRpY1BhdGhzOlxuICAgIHwgKChwYXRoOiBzdHJpbmcpID0+IFByb21pc2U8QXJyYXk8UmVjb3JkPHN0cmluZywgc3RyaW5nPj4+KVxuICAgIHwgdW5kZWZpbmVkLFxuICBvcHRpb25zOiBTc2dSZW5kZXJPcHRpb25zLFxuICByb290OiBzdHJpbmcsXG4gIG91dERpcjogc3RyaW5nLFxuICBwYWdlRGlhZ25vc3RpY3M6IFBhZ2VEaWFnbm9zdGljW10sXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgaTE4bk9wdHMgPSBldmlkZW5jZS5pMThuT3B0aW9ucyB8fCBudWxsO1xuICBpZiAoIWkxOG5PcHRzIHx8ICFyZW5kZXJSb3V0ZSkgcmV0dXJuO1xuXG4gIGNvbnN0IGxvY2FsZXM6IHN0cmluZ1tdID0gaTE4bk9wdHMubG9jYWxlcyB8fCBbXTtcbiAgaWYgKGxvY2FsZXMubGVuZ3RoIDw9IDEpIHJldHVybjtcblxuICBsb2cuaW5mbyhgaTE4bjogZXhwYW5kaW5nIGZvciBsb2NhbGVzOiAke2xvY2FsZXMuam9pbignLCAnKX1gKTtcbiAgZm9yIChjb25zdCBsb2NhbGUgb2YgbG9jYWxlcykge1xuICAgIGZvciAoY29uc3Qgcm91dGUgb2Ygcm91dGVJbmZvKSB7XG4gICAgICBsZXQgcGFyYW1zTGlzdDogQXJyYXk8UmVjb3JkPHN0cmluZywgc3RyaW5nPj47XG4gICAgICBpZiAoIXJvdXRlLmlzRHluYW1pYykge1xuICAgICAgICBwYXJhbXNMaXN0ID0gW3t9XTtcbiAgICAgIH0gZWxzZSBpZiAoZ2V0U3RhdGljUGF0aHMpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBwYXJhbXNMaXN0ID0gYXdhaXQgZ2V0U3RhdGljUGF0aHMocm91dGUucGF0aCk7XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIGxvZy53YXJuKFxuICAgICAgICAgICAgYGkxOG46IGdldFN0YXRpY1BhdGhzIGZhaWxlZCBmb3IgJHtyb3V0ZS5wYXRofSwgc2tpcHBpbmdgLFxuICAgICAgICAgICk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKHBhcmFtc0xpc3QubGVuZ3RoID09PSAwKSBjb250aW51ZTtcblxuICAgICAgY29uc3QgcGFyYW1OYW1lcyA9IHJvdXRlLnBhcmFtTmFtZXM7XG4gICAgICBmb3IgKGNvbnN0IHBhcmFtcyBvZiBwYXJhbXNMaXN0KSB7XG4gICAgICAgIGxldCByZXNvbHZlZFBhdGg6IHN0cmluZztcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICByZXNvbHZlZFBhdGggPSByZXNvbHZlRHluYW1pY1JvdXRlUGF0aChcbiAgICAgICAgICAgIHJvdXRlLnBhdGgsXG4gICAgICAgICAgICBwYXJhbU5hbWVzLFxuICAgICAgICAgICAgcGFyYW1zLFxuICAgICAgICAgICk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICBsb2cud2FybihcbiAgICAgICAgICAgIGBpMThuOiBza2lwcGluZyB1bnNhZmUgZHluYW1pYyByb3V0ZSAke3JvdXRlLnBhdGh9OiAke2Zvcm1hdEVycm9yKGUpfWAsXG4gICAgICAgICAgKTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBwYXRoU2VnbWVudCA9IHJlc29sdmVkUGF0aC5zcGxpdCgnLycpWzFdIHx8ICcnO1xuICAgICAgICBpZiAobG9jYWxlcy5pbmNsdWRlcyhwYXRoU2VnbWVudCkpIHtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBsb2NhbGVQYXRoID0gJy8nICsgbG9jYWxlICsgJy8nICsgcmVzb2x2ZWRQYXRoLnJlcGxhY2UoL15cXC8vLCAnJyk7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3Qgb3V0cHV0ID0gYXdhaXQgcmVuZGVyUm91dGUocm91dGUucGF0aCwge1xuICAgICAgICAgICAgcGFyYW1zLFxuICAgICAgICAgICAgbG9jYWxlLFxuICAgICAgICAgICAgdGl0bGU6IG9wdGlvbnMuaHRtbD8udGl0bGUsXG4gICAgICAgICAgICBsYW5nOiBsb2NhbGUsXG4gICAgICAgICAgICBoZWFkRXh0cmFzOiBvcHRpb25zLmhlYWRFeHRyYXMsXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgY29uc3QgaHRtbCA9IGNvbGxlY3RQYWdlT3V0cHV0KGxvY2FsZVBhdGgsIG91dHB1dCwgcGFnZURpYWdub3N0aWNzKTtcbiAgICAgICAgICBjb25zdCBvdXRwdXREaXIgPSBqb2luKHJvb3QsIG91dERpcik7XG4gICAgICAgICAgY29uc3QgcGFnZURpciA9IGpvaW4ob3V0cHV0RGlyLCBsb2NhbGVQYXRoKTtcbiAgICAgICAgICBta2RpclN5bmMocGFnZURpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgICAgICAgd3JpdGVGaWxlU3luYyhqb2luKHBhZ2VEaXIsICdpbmRleC5odG1sJyksIGh0bWwsICd1dGYtOCcpO1xuICAgICAgICAgIGxvZy5pbmZvKGBpMThuOiAke2xvY2FsZVBhdGh9L2luZGV4Lmh0bWxgKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgIGxvZy53YXJuKFxuICAgICAgICAgICAgYGkxOG46IGZhaWxlZCBmb3IgbG9jYWxlICR7bG9jYWxlfSBvbiAke3Jlc29sdmVkUGF0aH06ICR7Zm9ybWF0RXJyb3IoZSl9YCxcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0NBS0MsR0FFRCxTQUFTLElBQUksUUFBUSxZQUFZO0FBQ2pDLFNBQVMsU0FBUyxFQUFFLGFBQWEsUUFBUSxVQUFVO0FBRW5ELFNBQVMsWUFBWSxRQUFRLDJCQUEyQjtBQUN4RCxTQUFTLFdBQVcsUUFBUSwyQkFBMkI7QUFDdkQsU0FBUyxpQkFBaUIsRUFBdUIsdUJBQXVCLFFBQVEsbUJBQW1CO0FBRW5HLE1BQU0sTUFBTSxhQUFhO0FBVXpCOzs7OztDQUtDLEdBQ0QsT0FBTyxlQUFlLGtCQUNwQixRQUEyQixFQUMzQixXQUVhLEVBQ2IsU0FBMEIsRUFDMUIsY0FFYSxFQUNiLE9BQXlCLEVBQ3pCLElBQVksRUFDWixNQUFjLEVBQ2QsZUFBaUM7RUFFakMsTUFBTSxXQUFXLFNBQVMsV0FBVyxJQUFJO0VBQ3pDLElBQUksQ0FBQyxZQUFZLENBQUMsYUFBYTtFQUUvQixNQUFNLFVBQW9CLFNBQVMsT0FBTyxJQUFJLEVBQUU7RUFDaEQsSUFBSSxRQUFRLE1BQU0sSUFBSSxHQUFHO0VBRXpCLElBQUksSUFBSSxDQUFDLENBQUMsNkJBQTZCLEVBQUUsUUFBUSxJQUFJLENBQUMsT0FBTztFQUM3RCxLQUFLLE1BQU0sVUFBVSxRQUFTO0lBQzVCLEtBQUssTUFBTSxTQUFTLFVBQVc7TUFDN0IsSUFBSTtNQUNKLElBQUksQ0FBQyxNQUFNLFNBQVMsRUFBRTtRQUNwQixhQUFhO1VBQUMsQ0FBQztTQUFFO01BQ25CLE9BQU8sSUFBSSxnQkFBZ0I7UUFDekIsSUFBSTtVQUNGLGFBQWEsTUFBTSxlQUFlLE1BQU0sSUFBSTtRQUM5QyxFQUFFLE9BQU07VUFDTixJQUFJLElBQUksQ0FDTixDQUFDLGdDQUFnQyxFQUFFLE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQztVQUUzRDtRQUNGO01BQ0YsT0FBTztRQUNMO01BQ0Y7TUFDQSxJQUFJLFdBQVcsTUFBTSxLQUFLLEdBQUc7TUFFN0IsTUFBTSxhQUFhLE1BQU0sVUFBVTtNQUNuQyxLQUFLLE1BQU0sVUFBVSxXQUFZO1FBQy9CLElBQUk7UUFDSixJQUFJO1VBQ0YsZUFBZSx3QkFDYixNQUFNLElBQUksRUFDVixZQUNBO1FBRUosRUFBRSxPQUFPLEdBQUc7VUFDVixJQUFJLElBQUksQ0FDTixDQUFDLG9DQUFvQyxFQUFFLE1BQU0sSUFBSSxDQUFDLEVBQUUsRUFBRSxZQUFZLElBQUk7VUFFeEU7UUFDRjtRQUNBLE1BQU0sY0FBYyxhQUFhLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJO1FBQ2xELElBQUksUUFBUSxRQUFRLENBQUMsY0FBYztVQUNqQztRQUNGO1FBQ0EsTUFBTSxhQUFhLE1BQU0sU0FBUyxNQUFNLGFBQWEsT0FBTyxDQUFDLE9BQU87UUFDcEUsSUFBSTtVQUNGLE1BQU0sU0FBUyxNQUFNLFlBQVksTUFBTSxJQUFJLEVBQUU7WUFDM0M7WUFDQTtZQUNBLE9BQU8sUUFBUSxJQUFJLEVBQUU7WUFDckIsTUFBTTtZQUNOLFlBQVksUUFBUSxVQUFVO1VBQ2hDO1VBQ0EsTUFBTSxPQUFPLGtCQUFrQixZQUFZLFFBQVE7VUFDbkQsTUFBTSxZQUFZLEtBQUssTUFBTTtVQUM3QixNQUFNLFVBQVUsS0FBSyxXQUFXO1VBQ2hDLFVBQVUsU0FBUztZQUFFLFdBQVc7VUFBSztVQUNyQyxjQUFjLEtBQUssU0FBUyxlQUFlLE1BQU07VUFDakQsSUFBSSxJQUFJLENBQUMsQ0FBQyxNQUFNLEVBQUUsV0FBVyxXQUFXLENBQUM7UUFDM0MsRUFBRSxPQUFPLEdBQUc7VUFDVixJQUFJLElBQUksQ0FDTixDQUFDLHdCQUF3QixFQUFFLE9BQU8sSUFBSSxFQUFFLGFBQWEsRUFBRSxFQUFFLFlBQVksSUFBSTtRQUU3RTtNQUNGO0lBQ0Y7RUFDRjtBQUNGIn0=
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SsgRenderEvidence, SsgRenderOptions, SsrBundle } from '@openelement/protocol/ssg';
|
|
2
|
+
export declare function ssgRender(module: SsrBundle, options: SsgRenderOptions, evidence?: SsgRenderEvidence): Promise<void>;
|
|
3
|
+
export { resolveDynamicRoutePath } from "./ssg-helpers.js";
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapter-vite internal SSG render pipeline (ADR 0022).
|
|
3
|
+
*
|
|
4
|
+
* Shared SSG rendering logic used by both:
|
|
5
|
+
* - build-ssg.ts (Vite inline mode, called from closeBundle)
|
|
6
|
+
* - ssg.ts (standalone CLI, loads SSR bundle via importmap)
|
|
7
|
+
*
|
|
8
|
+
* This module has zero Vite dependency - it only needs the SSR bundle module.
|
|
9
|
+
*
|
|
10
|
+
* Thin orchestrator that imports focused sub-modules for:
|
|
11
|
+
* - Dynamic route expansion (ssg-dynamic.ts)
|
|
12
|
+
* - i18n locale expansion (ssg-i18n.ts)
|
|
13
|
+
* - DSD report assembly (ssg-report.ts)
|
|
14
|
+
* - Utility helpers (ssg-helpers.ts)
|
|
15
|
+
*/ import { join } from 'node:path';
|
|
16
|
+
import process from 'node:process';
|
|
17
|
+
import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
18
|
+
import { createLogger } from '@openelement/core/logger';
|
|
19
|
+
import { expandDynamicRoutes } from './ssg-dynamic.js';
|
|
20
|
+
import { expandI18nLocales } from './ssg-i18n.js';
|
|
21
|
+
import { assembleDsdReport, writeDsdReport } from './ssg-report.js';
|
|
22
|
+
import { buildIsrManifestEntries, findHtmlFiles } from './ssg-helpers.js';
|
|
23
|
+
import { writeJson } from '@openelement/content/write-json';
|
|
24
|
+
const log = createLogger('ssg');
|
|
25
|
+
// ─── Core render pipeline ──────────────────────────────────────
|
|
26
|
+
export async function ssgRender(module, options, evidence = {}) {
|
|
27
|
+
const root = options.root || process.cwd();
|
|
28
|
+
const outDir = options.outDir || 'dist';
|
|
29
|
+
const basePath = options.base || '/';
|
|
30
|
+
// ── Report collection (v0.15.3: dsd-report.json) ──────────────
|
|
31
|
+
const pageDiagnostics = [];
|
|
32
|
+
// ── Dynamic route expansion via bundle.getStaticPaths() ──────
|
|
33
|
+
const routeInfo = module.routeInfo ?? [];
|
|
34
|
+
const renderRoute = module.renderRoute;
|
|
35
|
+
const getStaticPaths = module.getStaticPaths;
|
|
36
|
+
const dynamicRoutes = routeInfo.filter((r)=>r.isDynamic);
|
|
37
|
+
log.info(`Routes: ${routeInfo.length} total` + (dynamicRoutes.length > 0 ? ` (${dynamicRoutes.length} dynamic: ${dynamicRoutes.map((r)=>r.path).join(', ')})` : ''));
|
|
38
|
+
const staticPathParamsByRoute = await expandDynamicRoutes(dynamicRoutes, renderRoute, getStaticPaths, options, root, outDir, pageDiagnostics);
|
|
39
|
+
// ── Main SSG via Hono's toSSG() ────────────────────────────
|
|
40
|
+
const { toSSG } = await import('hono/ssg');
|
|
41
|
+
const nodeFs = await import('node:fs/promises');
|
|
42
|
+
const nodePath = await import('node:path');
|
|
43
|
+
const fsModule = {
|
|
44
|
+
writeFile: async (path, data)=>{
|
|
45
|
+
const dir = nodePath.dirname(path);
|
|
46
|
+
await nodeFs.mkdir(dir, {
|
|
47
|
+
recursive: true
|
|
48
|
+
}).catch(()=>{});
|
|
49
|
+
await nodeFs.writeFile(path, data);
|
|
50
|
+
},
|
|
51
|
+
mkdir: async (path)=>{
|
|
52
|
+
await nodeFs.mkdir(path, {
|
|
53
|
+
recursive: true
|
|
54
|
+
}).catch(()=>{});
|
|
55
|
+
},
|
|
56
|
+
isDirectory: async (path)=>{
|
|
57
|
+
try {
|
|
58
|
+
return (await nodeFs.stat(path)).isDirectory();
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const outputDir = join(root, outDir);
|
|
65
|
+
const app = module.default;
|
|
66
|
+
if (!app) {
|
|
67
|
+
throw new Error('SSR bundle loaded but no Hono app found (no default export)');
|
|
68
|
+
}
|
|
69
|
+
const result = await toSSG(app, fsModule, {
|
|
70
|
+
dir: outputDir
|
|
71
|
+
});
|
|
72
|
+
if (!result.success) throw result.error;
|
|
73
|
+
const isrRoutes = buildIsrManifestEntries(routeInfo, staticPathParamsByRoute);
|
|
74
|
+
if (isrRoutes.length > 0) {
|
|
75
|
+
writeFileSync(join(outputDir, 'isr-manifest.json'), writeJson(isrRoutes), 'utf-8');
|
|
76
|
+
log.info(`ISR manifest -> ${join(outputDir, 'isr-manifest.json')} (${isrRoutes.length} route(s))`);
|
|
77
|
+
}
|
|
78
|
+
// ── Post-processing ─────────────────────────────────────────
|
|
79
|
+
// Rename 404/index.html -> 404.html for GitHub Pages
|
|
80
|
+
const _404Dir = join(outputDir, '404');
|
|
81
|
+
const _404Html = join(outputDir, '404.html');
|
|
82
|
+
const _404Index = join(_404Dir, 'index.html');
|
|
83
|
+
if (existsSync(_404Index)) {
|
|
84
|
+
if (existsSync(_404Html)) {
|
|
85
|
+
log.warn('404.html already exists in output dir - removing before rename');
|
|
86
|
+
rmSync(_404Html, {
|
|
87
|
+
force: true
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
renameSync(_404Index, _404Html);
|
|
91
|
+
if (existsSync(_404Dir)) {
|
|
92
|
+
rmSync(_404Dir, {
|
|
93
|
+
recursive: true,
|
|
94
|
+
force: true
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
log.info('404 page -> dist/404.html (GitHub Pages)');
|
|
98
|
+
}
|
|
99
|
+
// Convert flat HTML files to clean URLs: about.html -> about/index.html
|
|
100
|
+
const allHtmlFiles = findHtmlFiles(outputDir);
|
|
101
|
+
for (const filePath of allHtmlFiles){
|
|
102
|
+
const rel = nodePath.relative(outputDir, filePath);
|
|
103
|
+
if (rel.endsWith('index.html') || rel === '404.html') continue;
|
|
104
|
+
const baseName = rel.replace(/\.html$/, '');
|
|
105
|
+
const urlBaseName = baseName.replace(/\\/g, '/');
|
|
106
|
+
const dirPath = join(outputDir, baseName);
|
|
107
|
+
const indexPath = join(dirPath, 'index.html');
|
|
108
|
+
if (existsSync(dirPath)) continue;
|
|
109
|
+
mkdirSync(dirPath, {
|
|
110
|
+
recursive: true
|
|
111
|
+
});
|
|
112
|
+
renameSync(filePath, indexPath);
|
|
113
|
+
log.info(`Clean URL: /${urlBaseName} -> ${urlBaseName}/index.html`);
|
|
114
|
+
}
|
|
115
|
+
log.info(`Static site generated -> ${outputDir}`);
|
|
116
|
+
// ── i18n locale expansion (if ctx available) ────────────────
|
|
117
|
+
await expandI18nLocales(evidence, renderRoute, routeInfo, getStaticPaths, options, root, outDir, pageDiagnostics);
|
|
118
|
+
// ── Post-processing modules ─────────────────────────────────
|
|
119
|
+
const { buildIslandChunkMap, injectCspMeta, injectDsdPolyfill, injectViewTransitionMeta, injectSpeculationRules, buildSpeculationRulesJson } = await import('./postprocess.js');
|
|
120
|
+
const islandTagNames = options.islandTagNames || [];
|
|
121
|
+
const _islandChunkMap = buildIslandChunkMap(root, outDir, islandTagNames, basePath);
|
|
122
|
+
if (options.viewTransition !== false) {
|
|
123
|
+
injectViewTransitionMeta(outputDir);
|
|
124
|
+
log.info('View Transitions meta tag injected');
|
|
125
|
+
}
|
|
126
|
+
if (options.speculation) {
|
|
127
|
+
const specOpts = typeof options.speculation === 'boolean' ? {} : options.speculation;
|
|
128
|
+
const rulesJson = buildSpeculationRulesJson(specOpts, routeInfo.map((r)=>({
|
|
129
|
+
path: r.path,
|
|
130
|
+
type: 'page'
|
|
131
|
+
})));
|
|
132
|
+
if (rulesJson) {
|
|
133
|
+
injectSpeculationRules(outputDir, rulesJson);
|
|
134
|
+
log.info('Speculation Rules injected');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const cspPolicy = options.middleware?.csp?.policy;
|
|
138
|
+
if (cspPolicy) {
|
|
139
|
+
injectCspMeta(outputDir, cspPolicy, options.middleware?.csp?.reportOnly || false, options.middleware?.csp?.nonce || false);
|
|
140
|
+
log.info('CSP meta tag injected');
|
|
141
|
+
}
|
|
142
|
+
injectDsdPolyfill(outputDir);
|
|
143
|
+
log.info('DSD polyfill injected');
|
|
144
|
+
// ── Sitemap (via ctx) ──────────────────────────────────────
|
|
145
|
+
await evidence.onPrintBuildManifest?.({
|
|
146
|
+
root,
|
|
147
|
+
outDir,
|
|
148
|
+
phase: 3,
|
|
149
|
+
headExtras: options.headExtras
|
|
150
|
+
});
|
|
151
|
+
try {
|
|
152
|
+
await evidence.onGenerateSitemap?.(join(root, outDir));
|
|
153
|
+
} catch {
|
|
154
|
+
log.debug('Sitemap generation skipped or failed');
|
|
155
|
+
}
|
|
156
|
+
// ── dsd-report.json (v0.15.3) ──────────────────────────────────
|
|
157
|
+
const report = assembleDsdReport(pageDiagnostics, evidence);
|
|
158
|
+
writeDsdReport(outputDir, report);
|
|
159
|
+
}
|
|
160
|
+
// Re-export resolveDynamicRoutePath for consumers who import from ssg-render.ts
|
|
161
|
+
export { resolveDynamicRoutePath } from './ssg-helpers.js';
|
|
162
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9zc2cvc3JjL3NzZy1yZW5kZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBhZGFwdGVyLXZpdGUgaW50ZXJuYWwgU1NHIHJlbmRlciBwaXBlbGluZSAoQURSIDAwMjIpLlxuICpcbiAqIFNoYXJlZCBTU0cgcmVuZGVyaW5nIGxvZ2ljIHVzZWQgYnkgYm90aDpcbiAqICAgLSBidWlsZC1zc2cudHMgKFZpdGUgaW5saW5lIG1vZGUsIGNhbGxlZCBmcm9tIGNsb3NlQnVuZGxlKVxuICogICAtIHNzZy50cyAoc3RhbmRhbG9uZSBDTEksIGxvYWRzIFNTUiBidW5kbGUgdmlhIGltcG9ydG1hcClcbiAqXG4gKiBUaGlzIG1vZHVsZSBoYXMgemVybyBWaXRlIGRlcGVuZGVuY3kgLSBpdCBvbmx5IG5lZWRzIHRoZSBTU1IgYnVuZGxlIG1vZHVsZS5cbiAqXG4gKiBUaGluIG9yY2hlc3RyYXRvciB0aGF0IGltcG9ydHMgZm9jdXNlZCBzdWItbW9kdWxlcyBmb3I6XG4gKiAgIC0gRHluYW1pYyByb3V0ZSBleHBhbnNpb24gKHNzZy1keW5hbWljLnRzKVxuICogICAtIGkxOG4gbG9jYWxlIGV4cGFuc2lvbiAoc3NnLWkxOG4udHMpXG4gKiAgIC0gRFNEIHJlcG9ydCBhc3NlbWJseSAoc3NnLXJlcG9ydC50cylcbiAqICAgLSBVdGlsaXR5IGhlbHBlcnMgKHNzZy1oZWxwZXJzLnRzKVxuICovXG5cbmltcG9ydCB7IGpvaW4gfSBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHByb2Nlc3MgZnJvbSAnbm9kZTpwcm9jZXNzJztcbmltcG9ydCB7IGV4aXN0c1N5bmMsIG1rZGlyU3luYywgcmVuYW1lU3luYywgcm1TeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnbm9kZTpmcyc7XG5pbXBvcnQgdHlwZSB7XG4gIFNzZ1BhZ2VPdXRwdXQsXG4gIFNzZ1JlbmRlckV2aWRlbmNlLFxuICBTc2dSZW5kZXJPcHRpb25zLFxuICBTc3JCdW5kbGUsXG59IGZyb20gJ0BvcGVuZWxlbWVudC9wcm90b2NvbC9zc2cnO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnQG9wZW5lbGVtZW50L2NvcmUvbG9nZ2VyJztcbmltcG9ydCB7IGV4cGFuZER5bmFtaWNSb3V0ZXMgfSBmcm9tICcuL3NzZy1keW5hbWljLmpzJztcbmltcG9ydCB7IGV4cGFuZEkxOG5Mb2NhbGVzIH0gZnJvbSAnLi9zc2ctaTE4bi5qcyc7XG5pbXBvcnQgeyBhc3NlbWJsZURzZFJlcG9ydCwgd3JpdGVEc2RSZXBvcnQgfSBmcm9tICcuL3NzZy1yZXBvcnQuanMnO1xuaW1wb3J0IHsgYnVpbGRJc3JNYW5pZmVzdEVudHJpZXMsIGZpbmRIdG1sRmlsZXMsIHR5cGUgUGFnZURpYWdub3N0aWMgfSBmcm9tICcuL3NzZy1oZWxwZXJzLmpzJztcbmltcG9ydCB7IHdyaXRlSnNvbiB9IGZyb20gJ0BvcGVuZWxlbWVudC9jb250ZW50L3dyaXRlLWpzb24nO1xuXG5jb25zdCBsb2cgPSBjcmVhdGVMb2dnZXIoJ3NzZycpO1xuXG4vLyDilIDilIDilIAgQ29yZSByZW5kZXIgcGlwZWxpbmUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzc2dSZW5kZXIoXG4gIG1vZHVsZTogU3NyQnVuZGxlLFxuICBvcHRpb25zOiBTc2dSZW5kZXJPcHRpb25zLFxuICBldmlkZW5jZTogU3NnUmVuZGVyRXZpZGVuY2UgPSB7fSxcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCByb290ID0gb3B0aW9ucy5yb290IHx8IHByb2Nlc3MuY3dkKCk7XG4gIGNvbnN0IG91dERpciA9IG9wdGlvbnMub3V0RGlyIHx8ICdkaXN0JztcbiAgY29uc3QgYmFzZVBhdGggPSBvcHRpb25zLmJhc2UgfHwgJy8nO1xuXG4gIC8vIOKUgOKUgCBSZXBvcnQgY29sbGVjdGlvbiAodjAuMTUuMzogZHNkLXJlcG9ydC5qc29uKSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgY29uc3QgcGFnZURpYWdub3N0aWNzOiBQYWdlRGlhZ25vc3RpY1tdID0gW107XG5cbiAgLy8g4pSA4pSAIER5bmFtaWMgcm91dGUgZXhwYW5zaW9uIHZpYSBidW5kbGUuZ2V0U3RhdGljUGF0aHMoKSDilIDilIDilIDilIDilIDilIBcbiAgY29uc3Qgcm91dGVJbmZvID0gKG1vZHVsZS5yb3V0ZUluZm8gPz8gW10pIGFzIEFycmF5PHtcbiAgICBwYXRoOiBzdHJpbmc7XG4gICAgdGFnTmFtZTogc3RyaW5nO1xuICAgIGlzRHluYW1pYzogYm9vbGVhbjtcbiAgICBwYXJhbU5hbWVzOiBzdHJpbmdbXTtcbiAgICByZXZhbGlkYXRlPzogbnVtYmVyO1xuICB9PjtcbiAgY29uc3QgcmVuZGVyUm91dGUgPSBtb2R1bGUucmVuZGVyUm91dGUgYXNcbiAgICB8ICgocGF0aDogc3RyaW5nLCBvcHRzPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pID0+IFByb21pc2U8U3NnUGFnZU91dHB1dD4pXG4gICAgfCB1bmRlZmluZWQ7XG4gIGNvbnN0IGdldFN0YXRpY1BhdGhzID0gbW9kdWxlLmdldFN0YXRpY1BhdGhzIGFzXG4gICAgfCAoKHBhdGg6IHN0cmluZykgPT4gUHJvbWlzZTxBcnJheTxSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+Pj4pXG4gICAgfCB1bmRlZmluZWQ7XG5cbiAgY29uc3QgZHluYW1pY1JvdXRlcyA9IHJvdXRlSW5mby5maWx0ZXIoKHIpID0+IHIuaXNEeW5hbWljKTtcbiAgbG9nLmluZm8oXG4gICAgYFJvdXRlczogJHtyb3V0ZUluZm8ubGVuZ3RofSB0b3RhbGAgK1xuICAgICAgKGR5bmFtaWNSb3V0ZXMubGVuZ3RoID4gMFxuICAgICAgICA/IGAgKCR7ZHluYW1pY1JvdXRlcy5sZW5ndGh9IGR5bmFtaWM6ICR7ZHluYW1pY1JvdXRlcy5tYXAoKHIpID0+IHIucGF0aCkuam9pbignLCAnKX0pYFxuICAgICAgICA6ICcnKSxcbiAgKTtcblxuICBjb25zdCBzdGF0aWNQYXRoUGFyYW1zQnlSb3V0ZSA9IGF3YWl0IGV4cGFuZER5bmFtaWNSb3V0ZXMoXG4gICAgZHluYW1pY1JvdXRlcyxcbiAgICByZW5kZXJSb3V0ZSxcbiAgICBnZXRTdGF0aWNQYXRocyxcbiAgICBvcHRpb25zLFxuICAgIHJvb3QsXG4gICAgb3V0RGlyLFxuICAgIHBhZ2VEaWFnbm9zdGljcyxcbiAgKTtcblxuICAvLyDilIDilIAgTWFpbiBTU0cgdmlhIEhvbm8ncyB0b1NTRygpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICBjb25zdCB7IHRvU1NHIH0gPSBhd2FpdCBpbXBvcnQoJ2hvbm8vc3NnJyk7XG4gIGNvbnN0IG5vZGVGcyA9IGF3YWl0IGltcG9ydCgnbm9kZTpmcy9wcm9taXNlcycpO1xuICBjb25zdCBub2RlUGF0aCA9IGF3YWl0IGltcG9ydCgnbm9kZTpwYXRoJyk7XG5cbiAgY29uc3QgZnNNb2R1bGUgPSB7XG4gICAgd3JpdGVGaWxlOiBhc3luYyAocGF0aDogc3RyaW5nLCBkYXRhOiBzdHJpbmcgfCBVaW50OEFycmF5KSA9PiB7XG4gICAgICBjb25zdCBkaXIgPSBub2RlUGF0aC5kaXJuYW1lKHBhdGgpO1xuICAgICAgYXdhaXQgbm9kZUZzLm1rZGlyKGRpciwgeyByZWN1cnNpdmU6IHRydWUgfSkuY2F0Y2goKCkgPT4ge30pO1xuICAgICAgYXdhaXQgbm9kZUZzLndyaXRlRmlsZShwYXRoLCBkYXRhKTtcbiAgICB9LFxuICAgIG1rZGlyOiBhc3luYyAocGF0aDogc3RyaW5nKSA9PiB7XG4gICAgICBhd2FpdCBub2RlRnMubWtkaXIocGF0aCwgeyByZWN1cnNpdmU6IHRydWUgfSkuY2F0Y2goKCkgPT4ge30pO1xuICAgIH0sXG4gICAgaXNEaXJlY3Rvcnk6IGFzeW5jIChwYXRoOiBzdHJpbmcpID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiAoYXdhaXQgbm9kZUZzLnN0YXQocGF0aCkpLmlzRGlyZWN0b3J5KCk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgIH0sXG4gIH07XG5cbiAgY29uc3Qgb3V0cHV0RGlyID0gam9pbihyb290LCBvdXREaXIpO1xuICBjb25zdCBhcHAgPSBtb2R1bGUuZGVmYXVsdCBhc1xuICAgIHwgeyBmZXRjaDogKHJlcTogUmVxdWVzdCwgLi4uYXJnczogdW5rbm93bltdKSA9PiBQcm9taXNlPFJlc3BvbnNlPiB9XG4gICAgfCB1bmRlZmluZWQ7XG4gIGlmICghYXBwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgJ1NTUiBidW5kbGUgbG9hZGVkIGJ1dCBubyBIb25vIGFwcCBmb3VuZCAobm8gZGVmYXVsdCBleHBvcnQpJyxcbiAgICApO1xuICB9XG5cbiAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdG9TU0coYXBwIGFzIG5ldmVyLCBmc01vZHVsZSwgeyBkaXI6IG91dHB1dERpciB9KTtcblxuICBpZiAoIXJlc3VsdC5zdWNjZXNzKSB0aHJvdyByZXN1bHQuZXJyb3I7XG5cbiAgY29uc3QgaXNyUm91dGVzID0gYnVpbGRJc3JNYW5pZmVzdEVudHJpZXMocm91dGVJbmZvLCBzdGF0aWNQYXRoUGFyYW1zQnlSb3V0ZSk7XG4gIGlmIChpc3JSb3V0ZXMubGVuZ3RoID4gMCkge1xuICAgIHdyaXRlRmlsZVN5bmMoXG4gICAgICBqb2luKG91dHB1dERpciwgJ2lzci1tYW5pZmVzdC5qc29uJyksXG4gICAgICB3cml0ZUpzb24oaXNyUm91dGVzKSxcbiAgICAgICd1dGYtOCcsXG4gICAgKTtcbiAgICBsb2cuaW5mbyhcbiAgICAgIGBJU1IgbWFuaWZlc3QgLT4gJHtqb2luKG91dHB1dERpciwgJ2lzci1tYW5pZmVzdC5qc29uJyl9ICgke2lzclJvdXRlcy5sZW5ndGh9IHJvdXRlKHMpKWAsXG4gICAgKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBQb3N0LXByb2Nlc3Npbmcg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLy8gUmVuYW1lIDQwNC9pbmRleC5odG1sIC0+IDQwNC5odG1sIGZvciBHaXRIdWIgUGFnZXNcbiAgY29uc3QgXzQwNERpciA9IGpvaW4ob3V0cHV0RGlyLCAnNDA0Jyk7XG4gIGNvbnN0IF80MDRIdG1sID0gam9pbihvdXRwdXREaXIsICc0MDQuaHRtbCcpO1xuICBjb25zdCBfNDA0SW5kZXggPSBqb2luKF80MDREaXIsICdpbmRleC5odG1sJyk7XG4gIGlmIChleGlzdHNTeW5jKF80MDRJbmRleCkpIHtcbiAgICBpZiAoZXhpc3RzU3luYyhfNDA0SHRtbCkpIHtcbiAgICAgIGxvZy53YXJuKFxuICAgICAgICAnNDA0Lmh0bWwgYWxyZWFkeSBleGlzdHMgaW4gb3V0cHV0IGRpciAtIHJlbW92aW5nIGJlZm9yZSByZW5hbWUnLFxuICAgICAgKTtcbiAgICAgIHJtU3luYyhfNDA0SHRtbCwgeyBmb3JjZTogdHJ1ZSB9KTtcbiAgICB9XG4gICAgcmVuYW1lU3luYyhfNDA0SW5kZXgsIF80MDRIdG1sKTtcbiAgICBpZiAoZXhpc3RzU3luYyhfNDA0RGlyKSkge1xuICAgICAgcm1TeW5jKF80MDREaXIsIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgICB9XG4gICAgbG9nLmluZm8oJzQwNCBwYWdlIC0+IGRpc3QvNDA0Lmh0bWwgKEdpdEh1YiBQYWdlcyknKTtcbiAgfVxuXG4gIC8vIENvbnZlcnQgZmxhdCBIVE1MIGZpbGVzIHRvIGNsZWFuIFVSTHM6IGFib3V0Lmh0bWwgLT4gYWJvdXQvaW5kZXguaHRtbFxuICBjb25zdCBhbGxIdG1sRmlsZXMgPSBmaW5kSHRtbEZpbGVzKG91dHB1dERpcik7XG4gIGZvciAoY29uc3QgZmlsZVBhdGggb2YgYWxsSHRtbEZpbGVzKSB7XG4gICAgY29uc3QgcmVsID0gbm9kZVBhdGgucmVsYXRpdmUob3V0cHV0RGlyLCBmaWxlUGF0aCk7XG4gICAgaWYgKHJlbC5lbmRzV2l0aCgnaW5kZXguaHRtbCcpIHx8IHJlbCA9PT0gJzQwNC5odG1sJykgY29udGludWU7XG4gICAgY29uc3QgYmFzZU5hbWUgPSByZWwucmVwbGFjZSgvXFwuaHRtbCQvLCAnJyk7XG4gICAgY29uc3QgdXJsQmFzZU5hbWUgPSBiYXNlTmFtZS5yZXBsYWNlKC9cXFxcL2csICcvJyk7XG4gICAgY29uc3QgZGlyUGF0aCA9IGpvaW4ob3V0cHV0RGlyLCBiYXNlTmFtZSk7XG4gICAgY29uc3QgaW5kZXhQYXRoID0gam9pbihkaXJQYXRoLCAnaW5kZXguaHRtbCcpO1xuICAgIGlmIChleGlzdHNTeW5jKGRpclBhdGgpKSBjb250aW51ZTtcbiAgICBta2RpclN5bmMoZGlyUGF0aCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgcmVuYW1lU3luYyhmaWxlUGF0aCwgaW5kZXhQYXRoKTtcbiAgICBsb2cuaW5mbyhgQ2xlYW4gVVJMOiAvJHt1cmxCYXNlTmFtZX0gLT4gJHt1cmxCYXNlTmFtZX0vaW5kZXguaHRtbGApO1xuICB9XG5cbiAgbG9nLmluZm8oYFN0YXRpYyBzaXRlIGdlbmVyYXRlZCAtPiAke291dHB1dERpcn1gKTtcblxuICAvLyDilIDilIAgaTE4biBsb2NhbGUgZXhwYW5zaW9uIChpZiBjdHggYXZhaWxhYmxlKSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgYXdhaXQgZXhwYW5kSTE4bkxvY2FsZXMoXG4gICAgZXZpZGVuY2UsXG4gICAgcmVuZGVyUm91dGUsXG4gICAgcm91dGVJbmZvLFxuICAgIGdldFN0YXRpY1BhdGhzLFxuICAgIG9wdGlvbnMsXG4gICAgcm9vdCxcbiAgICBvdXREaXIsXG4gICAgcGFnZURpYWdub3N0aWNzLFxuICApO1xuXG4gIC8vIOKUgOKUgCBQb3N0LXByb2Nlc3NpbmcgbW9kdWxlcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgY29uc3Qge1xuICAgIGJ1aWxkSXNsYW5kQ2h1bmtNYXAsXG4gICAgaW5qZWN0Q3NwTWV0YSxcbiAgICBpbmplY3REc2RQb2x5ZmlsbCxcbiAgICBpbmplY3RWaWV3VHJhbnNpdGlvbk1ldGEsXG4gICAgaW5qZWN0U3BlY3VsYXRpb25SdWxlcyxcbiAgICBidWlsZFNwZWN1bGF0aW9uUnVsZXNKc29uLFxuICB9ID0gYXdhaXQgaW1wb3J0KCcuL3Bvc3Rwcm9jZXNzLmpzJyk7XG5cbiAgY29uc3QgaXNsYW5kVGFnTmFtZXMgPSBvcHRpb25zLmlzbGFuZFRhZ05hbWVzIHx8IFtdO1xuICBjb25zdCBfaXNsYW5kQ2h1bmtNYXAgPSBidWlsZElzbGFuZENodW5rTWFwKFxuICAgIHJvb3QsXG4gICAgb3V0RGlyLFxuICAgIGlzbGFuZFRhZ05hbWVzLFxuICAgIGJhc2VQYXRoLFxuICApO1xuXG4gIGlmIChvcHRpb25zLnZpZXdUcmFuc2l0aW9uICE9PSBmYWxzZSkge1xuICAgIGluamVjdFZpZXdUcmFuc2l0aW9uTWV0YShvdXRwdXREaXIpO1xuICAgIGxvZy5pbmZvKCdWaWV3IFRyYW5zaXRpb25zIG1ldGEgdGFnIGluamVjdGVkJyk7XG4gIH1cblxuICBpZiAob3B0aW9ucy5zcGVjdWxhdGlvbikge1xuICAgIGNvbnN0IHNwZWNPcHRzID0gdHlwZW9mIG9wdGlvbnMuc3BlY3VsYXRpb24gPT09ICdib29sZWFuJ1xuICAgICAgPyB7fVxuICAgICAgOiAob3B0aW9ucy5zcGVjdWxhdGlvbiBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik7XG4gICAgY29uc3QgcnVsZXNKc29uID0gYnVpbGRTcGVjdWxhdGlvblJ1bGVzSnNvbihcbiAgICAgIHNwZWNPcHRzLFxuICAgICAgcm91dGVJbmZvLm1hcCgocikgPT4gKHsgcGF0aDogci5wYXRoLCB0eXBlOiAncGFnZScgYXMgY29uc3QgfSkpLFxuICAgICk7XG4gICAgaWYgKHJ1bGVzSnNvbikge1xuICAgICAgaW5qZWN0U3BlY3VsYXRpb25SdWxlcyhvdXRwdXREaXIsIHJ1bGVzSnNvbik7XG4gICAgICBsb2cuaW5mbygnU3BlY3VsYXRpb24gUnVsZXMgaW5qZWN0ZWQnKTtcbiAgICB9XG4gIH1cblxuICBjb25zdCBjc3BQb2xpY3kgPSBvcHRpb25zLm1pZGRsZXdhcmU/LmNzcD8ucG9saWN5O1xuICBpZiAoY3NwUG9saWN5KSB7XG4gICAgaW5qZWN0Q3NwTWV0YShcbiAgICAgIG91dHB1dERpcixcbiAgICAgIGNzcFBvbGljeSxcbiAgICAgIG9wdGlvbnMubWlkZGxld2FyZT8uY3NwPy5yZXBvcnRPbmx5IHx8IGZhbHNlLFxuICAgICAgb3B0aW9ucy5taWRkbGV3YXJlPy5jc3A/Lm5vbmNlIHx8IGZhbHNlLFxuICAgICk7XG4gICAgbG9nLmluZm8oJ0NTUCBtZXRhIHRhZyBpbmplY3RlZCcpO1xuICB9XG5cbiAgaW5qZWN0RHNkUG9seWZpbGwob3V0cHV0RGlyKTtcbiAgbG9nLmluZm8oJ0RTRCBwb2x5ZmlsbCBpbmplY3RlZCcpO1xuXG4gIC8vIOKUgOKUgCBTaXRlbWFwICh2aWEgY3R4KSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgYXdhaXQgZXZpZGVuY2Uub25QcmludEJ1aWxkTWFuaWZlc3Q/Lih7XG4gICAgcm9vdCxcbiAgICBvdXREaXIsXG4gICAgcGhhc2U6IDMsXG4gICAgaGVhZEV4dHJhczogb3B0aW9ucy5oZWFkRXh0cmFzLFxuICB9KTtcblxuICB0cnkge1xuICAgIGF3YWl0IGV2aWRlbmNlLm9uR2VuZXJhdGVTaXRlbWFwPy4oam9pbihyb290LCBvdXREaXIpKTtcbiAgfSBjYXRjaCB7XG4gICAgbG9nLmRlYnVnKCdTaXRlbWFwIGdlbmVyYXRpb24gc2tpcHBlZCBvciBmYWlsZWQnKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBkc2QtcmVwb3J0Lmpzb24gKHYwLjE1LjMpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICBjb25zdCByZXBvcnQgPSBhc3NlbWJsZURzZFJlcG9ydChwYWdlRGlhZ25vc3RpY3MsIGV2aWRlbmNlKTtcbiAgd3JpdGVEc2RSZXBvcnQob3V0cHV0RGlyLCByZXBvcnQpO1xufVxuXG4vLyBSZS1leHBvcnQgcmVzb2x2ZUR5bmFtaWNSb3V0ZVBhdGggZm9yIGNvbnN1bWVycyB3aG8gaW1wb3J0IGZyb20gc3NnLXJlbmRlci50c1xuZXhwb3J0IHsgcmVzb2x2ZUR5bmFtaWNSb3V0ZVBhdGggfSBmcm9tICcuL3NzZy1oZWxwZXJzLmpzJztcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Q0FjQyxHQUVELFNBQVMsSUFBSSxRQUFRLFlBQVk7QUFDakMsT0FBTyxhQUFhLGVBQWU7QUFDbkMsU0FBUyxVQUFVLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsYUFBYSxRQUFRLFVBQVU7QUFPbkYsU0FBUyxZQUFZLFFBQVEsMkJBQTJCO0FBQ3hELFNBQVMsbUJBQW1CLFFBQVEsbUJBQW1CO0FBQ3ZELFNBQVMsaUJBQWlCLFFBQVEsZ0JBQWdCO0FBQ2xELFNBQVMsaUJBQWlCLEVBQUUsY0FBYyxRQUFRLGtCQUFrQjtBQUNwRSxTQUFTLHVCQUF1QixF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
|