@openelement/app 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zhi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # @openelement/app
2
+
3
+ JSX-first application authoring API for openElement.
4
+
5
+ > v0.39 surface: Framework product. Use this package for first-run pages,
6
+ > layouts, islands, route metadata, and the Vite facade.
7
+
8
+ Use the package root in route, island, and component modules:
9
+
10
+ ```tsx
11
+ import { definePage } from '@openelement/app';
12
+
13
+ export default definePage({
14
+ route: { path: '/' },
15
+ head: { title: 'Home' },
16
+ render() {
17
+ return <main>Hello openElement</main>;
18
+ },
19
+ });
20
+ ```
21
+
22
+ Use the `/vite` subpath in `vite.config.ts`:
23
+
24
+ ```ts
25
+ import { openElement } from '@openelement/app/vite';
26
+ import { defineConfig } from 'vite';
27
+
28
+ export default defineConfig({
29
+ plugins: [
30
+ openElement({
31
+ routesDir: 'app/routes',
32
+ islandsDir: 'app/islands',
33
+ packageIslands: ['@openelement/ui'],
34
+ content: { blog: { contentDir: 'content/blog' } },
35
+ i18n: { locales: ['en', 'zh'], defaultLocale: 'en' },
36
+ }),
37
+ ],
38
+ });
39
+ ```
40
+
41
+ ## Authoring API
42
+
43
+ ```tsx
44
+ import { defineElement, defineIsland, defineLayout, definePage } from '@openelement/app';
45
+ ```
46
+
47
+ - `definePage({ route, head, renderIntent, load, render, error })` creates a file-route page from a canonical object descriptor.
48
+ - `defineIslandConfig({ ssr, dsd, hydrate })` defines static island metadata for adapter scanning.
49
+ - `defineIsland(tagName, render, { hydrate, dsd, ssr })` creates a browser-upgraded island.
50
+ - `defineElement(tagName, render)` creates a DSD component.
51
+ - `defineLayout(tagName, render)` is the layout-specific form of `defineElement()`.
52
+
53
+ `DsdElement` remains the runtime primitive in `@openelement/core`, but application
54
+ authors should start from this package.
55
+
56
+ ## Install
57
+
58
+ ```bash
59
+ npm install @openelement/app
60
+ ```
61
+
62
+ ## License
63
+
64
+ MIT
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@openelement/app",
3
+ "version": "0.41.0-alpha.1",
4
+ "type": "module",
5
+ "main": "./src/index.js",
6
+ "types": "./src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./src/index.d.ts",
10
+ "import": "./src/index.js",
11
+ "default": "./src/index.js"
12
+ },
13
+ "./i18n": {
14
+ "types": "./src/i18n.d.ts",
15
+ "import": "./src/i18n.js",
16
+ "default": "./src/i18n.js"
17
+ },
18
+ "./i18n-plugin": {
19
+ "types": "./src/i18n-plugin.d.ts",
20
+ "import": "./src/i18n-plugin.js",
21
+ "default": "./src/i18n-plugin.js"
22
+ },
23
+ "./preact": {
24
+ "types": "./src/preact.d.ts",
25
+ "import": "./src/preact.js",
26
+ "default": "./src/preact.js"
27
+ },
28
+ "./vite": {
29
+ "types": "./src/vite.d.ts",
30
+ "import": "./src/vite.js",
31
+ "default": "./src/vite.js"
32
+ }
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/open-element/openelement.git"
37
+ },
38
+ "keywords": [
39
+ "openelement",
40
+ "web-components",
41
+ "ssg",
42
+ "framework",
43
+ "deno"
44
+ ],
45
+ "dependencies": {
46
+ "preact": "^10.28.0",
47
+ "preact-render-to-string": "^6.5.0",
48
+ "vite": "^8.0.10",
49
+ "@openelement/protocol": "^0.41.0-alpha.1",
50
+ "@openelement/core": "^0.41.0-alpha.1",
51
+ "@openelement/element": "^0.41.0-alpha.1",
52
+ "@openelement/router": "^0.41.0-alpha.1",
53
+ "@openelement/content": "^0.41.0-alpha.1",
54
+ "@openelement/adapter-vite": "^0.41.0-alpha.1"
55
+ }
56
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @openelement/app - JSX-first application authoring API.
3
+ *
4
+ * This file is intentionally free of Vite/build imports. Route modules can
5
+ * import from @openelement/app without pulling adapter-vite into the runtime
6
+ * graph.
7
+ */ import { type ElementDefinition, OpenElement, type VNode } from '@openelement/element';
8
+ import type { HydrationStrategy } from '@openelement/protocol/framework';
9
+ export type PageRenderingMode = 'auto' | 'static' | 'dynamic';
10
+ export type PageStreamingMode = 'auto' | 'force' | false;
11
+ export type PageRevalidate = false | number | `${number}s` | `${number}m` | `${number}h`;
12
+ export type PageMeta = Record<string, unknown>;
13
+ export interface PageRouteIntent {
14
+ path?: string;
15
+ id?: string;
16
+ params?: readonly string[];
17
+ }
18
+ export interface PageRenderIntent {
19
+ mode?: PageRenderingMode;
20
+ streaming?: PageStreamingMode;
21
+ revalidate?: PageRevalidate;
22
+ }
23
+ export interface NormalizedPageRenderIntent {
24
+ mode: PageRenderingMode;
25
+ streaming: PageStreamingMode;
26
+ revalidate: PageRevalidate;
27
+ }
28
+ export interface PageRouteContext {
29
+ path?: string;
30
+ filePath?: string;
31
+ }
32
+ export declare class OpenElementRedirect extends Error {
33
+ readonly location: string;
34
+ readonly status: number;
35
+ constructor(location: string | URL, status?: number);
36
+ }
37
+ export declare class OpenElementNotFound extends Error {
38
+ readonly status: 404;
39
+ constructor(message?: string);
40
+ }
41
+ export declare function redirect(location: string | URL, status?: number): never;
42
+ export declare function notFound(message?: string): never;
43
+ export declare function isOpenElementRedirect(error: unknown): error is OpenElementRedirect;
44
+ export declare function isOpenElementNotFound(error: unknown): error is OpenElementNotFound;
45
+ export interface PageHead {
46
+ title?: string;
47
+ description?: string;
48
+ meta?: Array<Record<string, string | number | boolean>>;
49
+ dangerouslyHeadFragments?: string[];
50
+ }
51
+ export interface PageRenderContext<Data = unknown, Params extends Record<string, string> = Record<string, string>> {
52
+ data: Data;
53
+ params: Params;
54
+ request?: Request;
55
+ route: PageRouteContext;
56
+ meta: PageMeta;
57
+ props: Record<string, unknown>;
58
+ }
59
+ export interface PageErrorContext<Data = unknown, Params extends Record<string, string> = Record<string, string>> extends PageRenderContext<Data, Params> {
60
+ error: unknown;
61
+ }
62
+ export type PageRenderFunction<Data = unknown, Params extends Record<string, string> = Record<string, string>> = (context: PageRenderContext<Data, Params>) => VNode | null;
63
+ export type PageErrorFunction<Data = unknown, Params extends Record<string, string> = Record<string, string>> = (context: PageErrorContext<Data, Params>) => VNode | null;
64
+ export interface PageDefinition<Data = unknown, Params extends Record<string, string> = Record<string, string>> {
65
+ route?: PageRouteIntent;
66
+ head?: PageHead;
67
+ renderIntent?: PageRenderIntent;
68
+ render: PageRenderFunction<Data, Params>;
69
+ error?: PageErrorFunction<Data, Params>;
70
+ }
71
+ export interface OpenElementPageDescriptor<Data = unknown, Params extends Record<string, string> = Record<string, string>> extends Omit<PageDefinition<Data, Params>, 'render' | 'renderIntent'> {
72
+ kind: 'page';
73
+ renderIntent: NormalizedPageRenderIntent;
74
+ render: PageRenderFunction<Data, Params>;
75
+ }
76
+ type PageConstructor<Data = unknown, Params extends Record<string, string> = Record<string, string>> = typeof OpenElement & {
77
+ openElementPage: OpenElementPageDescriptor<Data, Params>;
78
+ };
79
+ /**
80
+ * Define a file-route page.
81
+ *
82
+ * The returned class is a DsdElement-compatible custom element constructor, so
83
+ * the existing renderer pipeline remains unchanged while app authors write JSX
84
+ * functions instead of class components.
85
+ */ export declare function definePage<Data = unknown, Params extends Record<string, string> = Record<string, string>>(input: PageDefinition<Data, Params>): PageConstructor<Data, Params>;
86
+ export interface IslandConfig {
87
+ ssr?: boolean;
88
+ dsd?: boolean;
89
+ hydrate?: HydrationStrategy;
90
+ }
91
+ export type AppIslandOptions = IslandConfig;
92
+ export declare function defineIslandConfig(config: IslandConfig): IslandConfig;
93
+ export declare function defineIsland<Props extends Record<string, unknown> = Record<string, unknown>>(tagName: string, input: ((props: Props) => VNode | null) | ElementDefinition<Props> | CustomElementConstructor, options?: AppIslandOptions): CustomElementConstructor;
@@ -0,0 +1,161 @@
1
+ import { ERROR_PREFIX } from '@openelement/core';
2
+ /**
3
+ * @openelement/app - JSX-first application authoring API.
4
+ *
5
+ * This file is intentionally free of Vite/build imports. Route modules can
6
+ * import from @openelement/app without pulling adapter-vite into the runtime
7
+ * graph.
8
+ */ import { defineElement, OpenElement } from '@openelement/element';
9
+ import { defineIsland as defineRuntimeIsland } from '@openelement/core';
10
+ import { __internal_setActionData, __internal_setLoaderData } from '@openelement/router';
11
+ export class OpenElementRedirect extends Error {
12
+ location;
13
+ status;
14
+ constructor(location, status = 302){
15
+ super(`Redirect to ${String(location)}`);
16
+ this.name = 'OpenElementRedirect';
17
+ this.location = String(location);
18
+ this.status = status;
19
+ }
20
+ }
21
+ export class OpenElementNotFound extends Error {
22
+ status = 404;
23
+ constructor(message = 'Not Found'){
24
+ super(message);
25
+ this.name = 'OpenElementNotFound';
26
+ }
27
+ }
28
+ export function redirect(location, status = 302) {
29
+ throw new OpenElementRedirect(location, status);
30
+ }
31
+ export function notFound(message = 'Not Found') {
32
+ throw new OpenElementNotFound(message);
33
+ }
34
+ export function isOpenElementRedirect(error) {
35
+ return error instanceof OpenElementRedirect || typeof error === 'object' && error !== null && error.name === 'OpenElementRedirect' && typeof error.location === 'string' && typeof error.status === 'number';
36
+ }
37
+ export function isOpenElementNotFound(error) {
38
+ return error instanceof OpenElementNotFound || typeof error === 'object' && error !== null && error.name === 'OpenElementNotFound' && error.status === 404;
39
+ }
40
+ class ApplicationElement extends OpenElement {
41
+ }
42
+ class ApplicationPageElement extends ApplicationElement {
43
+ __openElementParams;
44
+ data;
45
+ __openElementActionData;
46
+ __openElementRequest;
47
+ __openElementRoute;
48
+ __openElementMeta;
49
+ __openElementError;
50
+ }
51
+ function collectPublicProps(host) {
52
+ const props = {};
53
+ for (const key of Object.keys(host)){
54
+ if (key.startsWith('__openElement')) continue;
55
+ props[key] = host[key];
56
+ }
57
+ return props;
58
+ }
59
+ const PAGE_DESCRIPTOR_FIELDS = new Set([
60
+ 'route',
61
+ 'head',
62
+ 'renderIntent',
63
+ 'render',
64
+ 'error'
65
+ ]);
66
+ const ISLAND_CONFIG_FIELDS = new Set([
67
+ 'ssr',
68
+ 'dsd',
69
+ 'hydrate'
70
+ ]);
71
+ const HYDRATION_STRATEGIES = new Set([
72
+ 'load',
73
+ 'idle',
74
+ 'visible',
75
+ 'only'
76
+ ]);
77
+ function assertCanonicalPageDefinition(input) {
78
+ if (typeof input === 'function') {
79
+ throw new Error(`${ERROR_PREFIX} definePage() requires a canonical object descriptor. ` + 'Use definePage({ route, head, renderIntent, render, error }).');
80
+ }
81
+ if (typeof input !== 'object' || input === null) {
82
+ throw new Error(`${ERROR_PREFIX} definePage() requires an object descriptor.`);
83
+ }
84
+ for (const key of Object.keys(input)){
85
+ if (PAGE_DESCRIPTOR_FIELDS.has(key)) continue;
86
+ throw new Error(`${ERROR_PREFIX} definePage() does not accept top-level "${key}". ` + 'Use only route, head, renderIntent, render, and error.');
87
+ }
88
+ if (typeof input.render !== 'function') {
89
+ throw new Error(`${ERROR_PREFIX} definePage() descriptor requires a render() function.`);
90
+ }
91
+ }
92
+ /**
93
+ * Define a file-route page.
94
+ *
95
+ * The returned class is a DsdElement-compatible custom element constructor, so
96
+ * the existing renderer pipeline remains unchanged while app authors write JSX
97
+ * functions instead of class components.
98
+ */ export function definePage(input) {
99
+ assertCanonicalPageDefinition(input);
100
+ const definition = input;
101
+ const pageDescriptor = {
102
+ kind: 'page',
103
+ ...definition,
104
+ renderIntent: {
105
+ mode: definition.renderIntent?.mode ?? 'auto',
106
+ streaming: definition.renderIntent?.streaming ?? 'auto',
107
+ revalidate: definition.renderIntent?.revalidate ?? false
108
+ }
109
+ };
110
+ class OpenElementPage extends ApplicationPageElement {
111
+ static openElementPage = pageDescriptor;
112
+ render() {
113
+ // Provide loader/action data to hooks (useLoaderData / useActionData)
114
+ __internal_setLoaderData(this.data);
115
+ __internal_setActionData(this.__openElementActionData);
116
+ const params = this.__openElementParams ?? this.params ?? {};
117
+ const data = this.data;
118
+ const context = {
119
+ data,
120
+ params,
121
+ request: this.__openElementRequest,
122
+ route: this.__openElementRoute ?? {},
123
+ meta: this.__openElementMeta ?? {},
124
+ props: collectPublicProps(this)
125
+ };
126
+ if (this.__openElementError !== undefined && definition.error) {
127
+ return definition.error({
128
+ ...context,
129
+ error: this.__openElementError
130
+ });
131
+ }
132
+ return definition.render(context);
133
+ }
134
+ }
135
+ return OpenElementPage;
136
+ }
137
+ export function defineIslandConfig(config) {
138
+ if (typeof config !== 'object' || config === null || Array.isArray(config)) {
139
+ throw new Error(`${ERROR_PREFIX} defineIslandConfig() requires an object descriptor.`);
140
+ }
141
+ for (const key of Object.keys(config)){
142
+ if (!ISLAND_CONFIG_FIELDS.has(key)) {
143
+ throw new Error(`${ERROR_PREFIX} defineIslandConfig() does not accept "${key}". ` + 'Use only ssr, dsd, and hydrate.');
144
+ }
145
+ }
146
+ if (config.hydrate !== undefined && !HYDRATION_STRATEGIES.has(config.hydrate)) {
147
+ throw new Error(`${ERROR_PREFIX} Invalid island hydrate strategy "${String(config.hydrate)}". ` + 'Use one of: load, idle, visible, only.');
148
+ }
149
+ return {
150
+ ...config
151
+ };
152
+ }
153
+ export function defineIsland(tagName, input, options = {}) {
154
+ const componentClass = typeof input === 'function' && input.prototype?.render ? input : defineElement(tagName, input);
155
+ return defineRuntimeIsland(tagName, componentClass, {
156
+ strategy: options.hydrate,
157
+ dsd: options.dsd,
158
+ ssr: options.ssr
159
+ });
160
+ }
161
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hcHAvc3JjL2F1dGhvcmluZy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBFUlJPUl9QUkVGSVggfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZSc7XG4vKipcbiAqIEBvcGVuZWxlbWVudC9hcHAgLSBKU1gtZmlyc3QgYXBwbGljYXRpb24gYXV0aG9yaW5nIEFQSS5cbiAqXG4gKiBUaGlzIGZpbGUgaXMgaW50ZW50aW9uYWxseSBmcmVlIG9mIFZpdGUvYnVpbGQgaW1wb3J0cy4gUm91dGUgbW9kdWxlcyBjYW5cbiAqIGltcG9ydCBmcm9tIEBvcGVuZWxlbWVudC9hcHAgd2l0aG91dCBwdWxsaW5nIGFkYXB0ZXItdml0ZSBpbnRvIHRoZSBydW50aW1lXG4gKiBncmFwaC5cbiAqL1xuXG5pbXBvcnQge1xuICBkZWZpbmVFbGVtZW50LFxuICB0eXBlIEVsZW1lbnREZWZpbml0aW9uLFxuICBPcGVuRWxlbWVudCxcbiAgdHlwZSBWTm9kZSxcbn0gZnJvbSAnQG9wZW5lbGVtZW50L2VsZW1lbnQnO1xuaW1wb3J0IHsgZGVmaW5lSXNsYW5kIGFzIGRlZmluZVJ1bnRpbWVJc2xhbmQgfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZSc7XG5pbXBvcnQgeyBfX2ludGVybmFsX3NldEFjdGlvbkRhdGEsIF9faW50ZXJuYWxfc2V0TG9hZGVyRGF0YSB9IGZyb20gJ0BvcGVuZWxlbWVudC9yb3V0ZXInO1xuaW1wb3J0IHR5cGUgeyBIeWRyYXRpb25TdHJhdGVneSB9IGZyb20gJ0BvcGVuZWxlbWVudC9wcm90b2NvbC9mcmFtZXdvcmsnO1xuXG5leHBvcnQgdHlwZSBQYWdlUmVuZGVyaW5nTW9kZSA9ICdhdXRvJyB8ICdzdGF0aWMnIHwgJ2R5bmFtaWMnO1xuZXhwb3J0IHR5cGUgUGFnZVN0cmVhbWluZ01vZGUgPSAnYXV0bycgfCAnZm9yY2UnIHwgZmFsc2U7XG5leHBvcnQgdHlwZSBQYWdlUmV2YWxpZGF0ZSA9IGZhbHNlIHwgbnVtYmVyIHwgYCR7bnVtYmVyfXNgIHwgYCR7bnVtYmVyfW1gIHwgYCR7bnVtYmVyfWhgO1xuZXhwb3J0IHR5cGUgUGFnZU1ldGEgPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcblxuZXhwb3J0IGludGVyZmFjZSBQYWdlUm91dGVJbnRlbnQge1xuICBwYXRoPzogc3RyaW5nO1xuICBpZD86IHN0cmluZztcbiAgcGFyYW1zPzogcmVhZG9ubHkgc3RyaW5nW107XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGFnZVJlbmRlckludGVudCB7XG4gIG1vZGU/OiBQYWdlUmVuZGVyaW5nTW9kZTtcbiAgc3RyZWFtaW5nPzogUGFnZVN0cmVhbWluZ01vZGU7XG4gIHJldmFsaWRhdGU/OiBQYWdlUmV2YWxpZGF0ZTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOb3JtYWxpemVkUGFnZVJlbmRlckludGVudCB7XG4gIG1vZGU6IFBhZ2VSZW5kZXJpbmdNb2RlO1xuICBzdHJlYW1pbmc6IFBhZ2VTdHJlYW1pbmdNb2RlO1xuICByZXZhbGlkYXRlOiBQYWdlUmV2YWxpZGF0ZTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQYWdlUm91dGVDb250ZXh0IHtcbiAgcGF0aD86IHN0cmluZztcbiAgZmlsZVBhdGg/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBjbGFzcyBPcGVuRWxlbWVudFJlZGlyZWN0IGV4dGVuZHMgRXJyb3Ige1xuICByZWFkb25seSBsb2NhdGlvbjogc3RyaW5nO1xuICByZWFkb25seSBzdGF0dXM6IG51bWJlcjtcblxuICBjb25zdHJ1Y3Rvcihsb2NhdGlvbjogc3RyaW5nIHwgVVJMLCBzdGF0dXMgPSAzMDIpIHtcbiAgICBzdXBlcihgUmVkaXJlY3QgdG8gJHtTdHJpbmcobG9jYXRpb24pfWApO1xuICAgIHRoaXMubmFtZSA9ICdPcGVuRWxlbWVudFJlZGlyZWN0JztcbiAgICB0aGlzLmxvY2F0aW9uID0gU3RyaW5nKGxvY2F0aW9uKTtcbiAgICB0aGlzLnN0YXR1cyA9IHN0YXR1cztcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgT3BlbkVsZW1lbnROb3RGb3VuZCBleHRlbmRzIEVycm9yIHtcbiAgcmVhZG9ubHkgc3RhdHVzID0gNDA0O1xuXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UgPSAnTm90IEZvdW5kJykge1xuICAgIHN1cGVyKG1lc3NhZ2UpO1xuICAgIHRoaXMubmFtZSA9ICdPcGVuRWxlbWVudE5vdEZvdW5kJztcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVkaXJlY3QobG9jYXRpb246IHN0cmluZyB8IFVSTCwgc3RhdHVzID0gMzAyKTogbmV2ZXIge1xuICB0aHJvdyBuZXcgT3BlbkVsZW1lbnRSZWRpcmVjdChsb2NhdGlvbiwgc3RhdHVzKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG5vdEZvdW5kKG1lc3NhZ2UgPSAnTm90IEZvdW5kJyk6IG5ldmVyIHtcbiAgdGhyb3cgbmV3IE9wZW5FbGVtZW50Tm90Rm91bmQobWVzc2FnZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc09wZW5FbGVtZW50UmVkaXJlY3QoZXJyb3I6IHVua25vd24pOiBlcnJvciBpcyBPcGVuRWxlbWVudFJlZGlyZWN0IHtcbiAgcmV0dXJuIGVycm9yIGluc3RhbmNlb2YgT3BlbkVsZW1lbnRSZWRpcmVjdCB8fFxuICAgIChcbiAgICAgIHR5cGVvZiBlcnJvciA9PT0gJ29iamVjdCcgJiZcbiAgICAgIGVycm9yICE9PSBudWxsICYmXG4gICAgICAoZXJyb3IgYXMgeyBuYW1lPzogdW5rbm93biB9KS5uYW1lID09PSAnT3BlbkVsZW1lbnRSZWRpcmVjdCcgJiZcbiAgICAgIHR5cGVvZiAoZXJyb3IgYXMgeyBsb2NhdGlvbj86IHVua25vd24gfSkubG9jYXRpb24gPT09ICdzdHJpbmcnICYmXG4gICAgICB0eXBlb2YgKGVycm9yIGFzIHsgc3RhdHVzPzogdW5rbm93biB9KS5zdGF0dXMgPT09ICdudW1iZXInXG4gICAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzT3BlbkVsZW1lbnROb3RGb3VuZChlcnJvcjogdW5rbm93bik6IGVycm9yIGlzIE9wZW5FbGVtZW50Tm90Rm91bmQge1xuICByZXR1cm4gZXJyb3IgaW5zdGFuY2VvZiBPcGVuRWxlbWVudE5vdEZvdW5kIHx8XG4gICAgKFxuICAgICAgdHlwZW9mIGVycm9yID09PSAnb2JqZWN0JyAmJlxuICAgICAgZXJyb3IgIT09IG51bGwgJiZcbiAgICAgIChlcnJvciBhcyB7IG5hbWU/OiB1bmtub3duIH0pLm5hbWUgPT09ICdPcGVuRWxlbWVudE5vdEZvdW5kJyAmJlxuICAgICAgKGVycm9yIGFzIHsgc3RhdHVzPzogdW5rbm93biB9KS5zdGF0dXMgPT09IDQwNFxuICAgICk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGFnZUhlYWQge1xuICB0aXRsZT86IHN0cmluZztcbiAgZGVzY3JpcHRpb24/OiBzdHJpbmc7XG4gIG1ldGE/OiBBcnJheTxSZWNvcmQ8c3RyaW5nLCBzdHJpbmcgfCBudW1iZXIgfCBib29sZWFuPj47XG4gIGRhbmdlcm91c2x5SGVhZEZyYWdtZW50cz86IHN0cmluZ1tdO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFBhZ2VSZW5kZXJDb250ZXh0PFxuICBEYXRhID0gdW5rbm93bixcbiAgUGFyYW1zIGV4dGVuZHMgUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IFJlY29yZDxzdHJpbmcsIHN0cmluZz4sXG4+IHtcbiAgZGF0YTogRGF0YTtcbiAgcGFyYW1zOiBQYXJhbXM7XG4gIHJlcXVlc3Q/OiBSZXF1ZXN0O1xuICByb3V0ZTogUGFnZVJvdXRlQ29udGV4dDtcbiAgbWV0YTogUGFnZU1ldGE7XG4gIHByb3BzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQYWdlRXJyb3JDb250ZXh0PFxuICBEYXRhID0gdW5rbm93bixcbiAgUGFyYW1zIGV4dGVuZHMgUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IFJlY29yZDxzdHJpbmcsIHN0cmluZz4sXG4+IGV4dGVuZHMgUGFnZVJlbmRlckNvbnRleHQ8RGF0YSwgUGFyYW1zPiB7XG4gIGVycm9yOiB1bmtub3duO1xufVxuXG5leHBvcnQgdHlwZSBQYWdlUmVuZGVyRnVuY3Rpb248XG4gIERhdGEgPSB1bmtub3duLFxuICBQYXJhbXMgZXh0ZW5kcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0gUmVjb3JkPHN0cmluZywgc3RyaW5nPixcbj4gPSAoY29udGV4dDogUGFnZVJlbmRlckNvbnRleHQ8RGF0YSwgUGFyYW1zPikgPT4gVk5vZGUgfCBudWxsO1xuXG5leHBvcnQgdHlwZSBQYWdlRXJyb3JGdW5jdGlvbjxcbiAgRGF0YSA9IHVua25vd24sXG4gIFBhcmFtcyBleHRlbmRzIFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+LFxuPiA9IChjb250ZXh0OiBQYWdlRXJyb3JDb250ZXh0PERhdGEsIFBhcmFtcz4pID0+IFZOb2RlIHwgbnVsbDtcblxuZXhwb3J0IGludGVyZmFjZSBQYWdlRGVmaW5pdGlvbjxcbiAgRGF0YSA9IHVua25vd24sXG4gIFBhcmFtcyBleHRlbmRzIFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+LFxuPiB7XG4gIHJvdXRlPzogUGFnZVJvdXRlSW50ZW50O1xuICBoZWFkPzogUGFnZUhlYWQ7XG4gIHJlbmRlckludGVudD86IFBhZ2VSZW5kZXJJbnRlbnQ7XG4gIHJlbmRlcjogUGFnZVJlbmRlckZ1bmN0aW9uPERhdGEsIFBhcmFtcz47XG4gIGVycm9yPzogUGFnZUVycm9yRnVuY3Rpb248RGF0YSwgUGFyYW1zPjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBPcGVuRWxlbWVudFBhZ2VEZXNjcmlwdG9yPFxuICBEYXRhID0gdW5rbm93bixcbiAgUGFyYW1zIGV4dGVuZHMgUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IFJlY29yZDxzdHJpbmcsIHN0cmluZz4sXG4+IGV4dGVuZHMgT21pdDxQYWdlRGVmaW5pdGlvbjxEYXRhLCBQYXJhbXM+LCAncmVuZGVyJyB8ICdyZW5kZXJJbnRlbnQnPiB7XG4gIGtpbmQ6ICdwYWdlJztcbiAgcmVuZGVySW50ZW50OiBOb3JtYWxpemVkUGFnZVJlbmRlckludGVudDtcbiAgcmVuZGVyOiBQYWdlUmVuZGVyRnVuY3Rpb248RGF0YSwgUGFyYW1zPjtcbn1cblxudHlwZSBQYWdlSG9zdFByb3BzID0ge1xuICBfX29wZW5FbGVtZW50UGFyYW1zPzogUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbiAgZGF0YT86IHVua25vd247XG4gIF9fb3BlbkVsZW1lbnRBY3Rpb25EYXRhPzogdW5rbm93bjtcbiAgX19vcGVuRWxlbWVudFJlcXVlc3Q/OiBSZXF1ZXN0O1xuICBfX29wZW5FbGVtZW50Um91dGU/OiBQYWdlUm91dGVDb250ZXh0O1xuICBfX29wZW5FbGVtZW50TWV0YT86IFBhZ2VNZXRhO1xuICBfX29wZW5FbGVtZW50RXJyb3I/OiB1bmtub3duO1xufTtcblxuYWJzdHJhY3QgY2xhc3MgQXBwbGljYXRpb25FbGVtZW50IGV4dGVuZHMgT3BlbkVsZW1lbnQge1xuICBba2V5OiBzdHJpbmddOiB1bmtub3duO1xufVxuXG5hYnN0cmFjdCBjbGFzcyBBcHBsaWNhdGlvblBhZ2VFbGVtZW50IGV4dGVuZHMgQXBwbGljYXRpb25FbGVtZW50IGltcGxlbWVudHMgUGFnZUhvc3RQcm9wcyB7XG4gIF9fb3BlbkVsZW1lbnRQYXJhbXM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICBkYXRhPzogdW5rbm93bjtcbiAgX19vcGVuRWxlbWVudEFjdGlvbkRhdGE/OiB1bmtub3duO1xuICBfX29wZW5FbGVtZW50UmVxdWVzdD86IFJlcXVlc3Q7XG4gIF9fb3BlbkVsZW1lbnRSb3V0ZT86IFBhZ2VSb3V0ZUNvbnRleHQ7XG4gIF9fb3BlbkVsZW1lbnRNZXRhPzogUGFnZU1ldGE7XG4gIF9fb3BlbkVsZW1lbnRFcnJvcj86IHVua25vd247XG59XG5cbnR5cGUgUGFnZUNvbnN0cnVjdG9yPFxuICBEYXRhID0gdW5rbm93bixcbiAgUGFyYW1zIGV4dGVuZHMgUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IFJlY29yZDxzdHJpbmcsIHN0cmluZz4sXG4+ID0gdHlwZW9mIE9wZW5FbGVtZW50ICYge1xuICBvcGVuRWxlbWVudFBhZ2U6IE9wZW5FbGVtZW50UGFnZURlc2NyaXB0b3I8RGF0YSwgUGFyYW1zPjtcbn07XG5cbmZ1bmN0aW9uIGNvbGxlY3RQdWJsaWNQcm9wcyhob3N0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgY29uc3QgcHJvcHM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG4gIGZvciAoY29uc3Qga2V5IG9mIE9iamVjdC5rZXlzKGhvc3QpKSB7XG4gICAgaWYgKGtleS5zdGFydHNXaXRoKCdfX29wZW5FbGVtZW50JykpIGNvbnRpbnVlO1xuICAgIHByb3BzW2tleV0gPSBob3N0W2tleV07XG4gIH1cbiAgcmV0dXJuIHByb3BzO1xufVxuXG5jb25zdCBQQUdFX0RFU0NSSVBUT1JfRklFTERTID0gbmV3IFNldChbXG4gICdyb3V0ZScsXG4gICdoZWFkJyxcbiAgJ3JlbmRlckludGVudCcsXG4gICdyZW5kZXInLFxuICAnZXJyb3InLFxuXSk7XG5cbmNvbnN0IElTTEFORF9DT05GSUdfRklFTERTID0gbmV3IFNldChbJ3NzcicsICdkc2QnLCAnaHlkcmF0ZSddKTtcbmNvbnN0IEhZRFJBVElPTl9TVFJBVEVHSUVTID0gbmV3IFNldChbJ2xvYWQnLCAnaWRsZScsICd2aXNpYmxlJywgJ29ubHknXSk7XG5cbmZ1bmN0aW9uIGFzc2VydENhbm9uaWNhbFBhZ2VEZWZpbml0aW9uKGlucHV0OiB1bmtub3duKTogYXNzZXJ0cyBpbnB1dCBpcyBQYWdlRGVmaW5pdGlvbiB7XG4gIGlmICh0eXBlb2YgaW5wdXQgPT09ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgJHtFUlJPUl9QUkVGSVh9IGRlZmluZVBhZ2UoKSByZXF1aXJlcyBhIGNhbm9uaWNhbCBvYmplY3QgZGVzY3JpcHRvci4gYCArXG4gICAgICAgICdVc2UgZGVmaW5lUGFnZSh7IHJvdXRlLCBoZWFkLCByZW5kZXJJbnRlbnQsIHJlbmRlciwgZXJyb3IgfSkuJyxcbiAgICApO1xuICB9XG4gIGlmICh0eXBlb2YgaW5wdXQgIT09ICdvYmplY3QnIHx8IGlucHV0ID09PSBudWxsKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGAke0VSUk9SX1BSRUZJWH0gZGVmaW5lUGFnZSgpIHJlcXVpcmVzIGFuIG9iamVjdCBkZXNjcmlwdG9yLmApO1xuICB9XG4gIGZvciAoY29uc3Qga2V5IG9mIE9iamVjdC5rZXlzKGlucHV0KSkge1xuICAgIGlmIChQQUdFX0RFU0NSSVBUT1JfRklFTERTLmhhcyhrZXkpKSBjb250aW51ZTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgJHtFUlJPUl9QUkVGSVh9IGRlZmluZVBhZ2UoKSBkb2VzIG5vdCBhY2NlcHQgdG9wLWxldmVsIFwiJHtrZXl9XCIuIGAgK1xuICAgICAgICAnVXNlIG9ubHkgcm91dGUsIGhlYWQsIHJlbmRlckludGVudCwgcmVuZGVyLCBhbmQgZXJyb3IuJyxcbiAgICApO1xuICB9XG4gIGlmICh0eXBlb2YgKGlucHV0IGFzIHsgcmVuZGVyPzogdW5rbm93biB9KS5yZW5kZXIgIT09ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYCR7RVJST1JfUFJFRklYfSBkZWZpbmVQYWdlKCkgZGVzY3JpcHRvciByZXF1aXJlcyBhIHJlbmRlcigpIGZ1bmN0aW9uLmApO1xuICB9XG59XG5cbi8qKlxuICogRGVmaW5lIGEgZmlsZS1yb3V0ZSBwYWdlLlxuICpcbiAqIFRoZSByZXR1cm5lZCBjbGFzcyBpcyBhIERzZEVsZW1lbnQtY29tcGF0aWJsZSBjdXN0b20gZWxlbWVudCBjb25zdHJ1Y3Rvciwgc29cbiAqIHRoZSBleGlzdGluZyByZW5kZXJlciBwaXBlbGluZSByZW1haW5zIHVuY2hhbmdlZCB3aGlsZSBhcHAgYXV0aG9ycyB3cml0ZSBKU1hcbiAqIGZ1bmN0aW9ucyBpbnN0ZWFkIG9mIGNsYXNzIGNvbXBvbmVudHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZWZpbmVQYWdlPFxuICBEYXRhID0gdW5rbm93bixcbiAgUGFyYW1zIGV4dGVuZHMgUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IFJlY29yZDxzdHJpbmcsIHN0cmluZz4sXG4+KFxuICBpbnB1dDogUGFnZURlZmluaXRpb248RGF0YSwgUGFyYW1zPixcbik6IFBhZ2VDb25zdHJ1Y3RvcjxEYXRhLCBQYXJhbXM+IHtcbiAgYXNzZXJ0Q2Fub25pY2FsUGFnZURlZmluaXRpb24oaW5wdXQpO1xuICBjb25zdCBkZWZpbml0aW9uID0gaW5wdXQ7XG4gIGNvbnN0IHBhZ2VEZXNjcmlwdG9yID0ge1xuICAgIGtpbmQ6ICdwYWdlJyxcbiAgICAuLi5kZWZpbml0aW9uLFxuICAgIHJlbmRlckludGVudDoge1xuICAgICAgbW9kZTogZGVmaW5pdGlvbi5yZW5kZXJJbnRlbnQ/Lm1vZGUgPz8gJ2F1dG8nLFxuICAgICAgc3RyZWFtaW5nOiBkZWZpbml0aW9uLnJlbmRlckludGVudD8uc3RyZWFtaW5nID8/ICdhdXRvJyxcbiAgICAgIHJldmFsaWRhdGU6IGRlZmluaXRpb24ucmVuZGVySW50ZW50Py5yZXZhbGlkYXRlID8/IGZhbHNlLFxuICAgIH0sXG4gIH0gYXMgT3BlbkVsZW1lbnRQYWdlRGVzY3JpcHRvcjxEYXRhLCBQYXJhbXM+O1xuXG4gIGNsYXNzIE9wZW5FbGVtZW50UGFnZSBleHRlbmRzIEFwcGxpY2F0aW9uUGFnZUVsZW1lbnQge1xuICAgIHN0YXRpYyBvcGVuRWxlbWVudFBhZ2UgPSBwYWdlRGVzY3JpcHRvcjtcblxuICAgIG92ZXJyaWRlIHJlbmRlcigpOiBWTm9kZSB8IG51bGwge1xuICAgICAgLy8gUHJvdmlkZSBsb2FkZXIvYWN0aW9uIGRhdGEgdG8gaG9va3MgKHVzZUxvYWRlckRhdGEgLyB1c2VBY3Rpb25EYXRhKVxuICAgICAgX19pbnRlcm5hbF9zZXRMb2FkZXJEYXRhKHRoaXMuZGF0YSk7XG4gICAgICBfX2ludGVybmFsX3NldEFjdGlvbkRhdGEodGhpcy5fX29wZW5FbGVtZW50QWN0aW9uRGF0YSk7XG5cbiAgICAgIGNvbnN0IHBhcmFtcyA9ICh0aGlzLl9fb3BlbkVsZW1lbnRQYXJhbXMgPz8gdGhpcy5wYXJhbXMgPz8ge30pIGFzIFBhcmFtcztcbiAgICAgIGNvbnN0IGRhdGEgPSB0aGlzLmRhdGEgYXMgRGF0YTtcbiAgICAgIGNvbnN0IGNvbnRleHQgPSB7XG4gICAgICAgIGRhdGEsXG4gICAgICAgIHBhcmFtcyxcbiAgICAgICAgcmVxdWVzdDogdGhpcy5fX29wZW5FbGVtZW50UmVxdWVzdCxcbiAgICAgICAgcm91dGU6IHRoaXMuX19vcGVuRWxlbWVudFJvdXRlID8/IHt9LFxuICAgICAgICBtZXRhOiB0aGlzLl9fb3BlbkVsZW1lbnRNZXRhID8/IHt9LFxuICAgICAgICBwcm9wczogY29sbGVjdFB1YmxpY1Byb3BzKHRoaXMpLFxuICAgICAgfTtcblxuICAgICAgaWYgKHRoaXMuX19vcGVuRWxlbWVudEVycm9yICE9PSB1bmRlZmluZWQgJiYgZGVmaW5pdGlvbi5lcnJvcikge1xuICAgICAgICByZXR1cm4gZGVmaW5pdGlvbi5lcnJvcih7IC4uLmNvbnRleHQsIGVycm9yOiB0aGlzLl9fb3BlbkVsZW1lbnRFcnJvciB9KTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIGRlZmluaXRpb24ucmVuZGVyKGNvbnRleHQpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBPcGVuRWxlbWVudFBhZ2U7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSXNsYW5kQ29uZmlnIHtcbiAgc3NyPzogYm9vbGVhbjtcbiAgZHNkPzogYm9vbGVhbjtcbiAgaHlkcmF0ZT86IEh5ZHJhdGlvblN0cmF0ZWd5O1xufVxuXG5leHBvcnQgdHlwZSBBcHBJc2xhbmRPcHRpb25zID0gSXNsYW5kQ29uZmlnO1xuXG5leHBvcnQgdHlwZSBJc2xhbmRDb25maWdUeXBlID0gSXNsYW5kQ29uZmlnO1xuXG5leHBvcnQgZnVuY3Rpb24gZGVmaW5lSXNsYW5kQ29uZmlnKGNvbmZpZzogSXNsYW5kQ29uZmlnKTogSXNsYW5kQ29uZmlnIHtcbiAgaWYgKHR5cGVvZiBjb25maWcgIT09ICdvYmplY3QnIHx8IGNvbmZpZyA9PT0gbnVsbCB8fCBBcnJheS5pc0FycmF5KGNvbmZpZykpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYCR7RVJST1JfUFJFRklYfSBkZWZpbmVJc2xhbmRDb25maWcoKSByZXF1aXJlcyBhbiBvYmplY3QgZGVzY3JpcHRvci5gKTtcbiAgfVxuICBmb3IgKGNvbnN0IGtleSBvZiBPYmplY3Qua2V5cyhjb25maWcpKSB7XG4gICAgaWYgKCFJU0xBTkRfQ09ORklHX0ZJRUxEUy5oYXMoa2V5KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgJHtFUlJPUl9QUkVGSVh9IGRlZmluZUlzbGFuZENvbmZpZygpIGRvZXMgbm90IGFjY2VwdCBcIiR7a2V5fVwiLiBgICtcbiAgICAgICAgICAnVXNlIG9ubHkgc3NyLCBkc2QsIGFuZCBoeWRyYXRlLicsXG4gICAgICApO1xuICAgIH1cbiAgfVxuICBpZiAoY29uZmlnLmh5ZHJhdGUgIT09IHVuZGVmaW5lZCAmJiAhSFlEUkFUSU9OX1NUUkFURUdJRVMuaGFzKGNvbmZpZy5oeWRyYXRlKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGAke0VSUk9SX1BSRUZJWH0gSW52YWxpZCBpc2xhbmQgaHlkcmF0ZSBzdHJhdGVneSBcIiR7U3RyaW5nKGNvbmZpZy5oeWRyYXRlKX1cIi4gYCArXG4gICAgICAgICdVc2Ugb25lIG9mOiBsb2FkLCBpZGxlLCB2aXNpYmxlLCBvbmx5LicsXG4gICAgKTtcbiAgfVxuICByZXR1cm4geyAuLi5jb25maWcgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlZmluZUlzbGFuZDxQcm9wcyBleHRlbmRzIFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0gUmVjb3JkPHN0cmluZywgdW5rbm93bj4+KFxuICB0YWdOYW1lOiBzdHJpbmcsXG4gIGlucHV0OiAoKHByb3BzOiBQcm9wcykgPT4gVk5vZGUgfCBudWxsKSB8IEVsZW1lbnREZWZpbml0aW9uPFByb3BzPiB8IEN1c3RvbUVsZW1lbnRDb25zdHJ1Y3RvcixcbiAgb3B0aW9uczogQXBwSXNsYW5kT3B0aW9ucyA9IHt9LFxuKTogQ3VzdG9tRWxlbWVudENvbnN0cnVjdG9yIHtcbiAgY29uc3QgY29tcG9uZW50Q2xhc3MgPSB0eXBlb2YgaW5wdXQgPT09ICdmdW5jdGlvbicgJiYgaW5wdXQucHJvdG90eXBlPy5yZW5kZXJcbiAgICA/IGlucHV0IGFzIEN1c3RvbUVsZW1lbnRDb25zdHJ1Y3RvclxuICAgIDogZGVmaW5lRWxlbWVudCh0YWdOYW1lLCBpbnB1dCBhcyAoKHByb3BzOiBQcm9wcykgPT4gVk5vZGUgfCBudWxsKSB8IEVsZW1lbnREZWZpbml0aW9uPFByb3BzPik7XG4gIHJldHVybiBkZWZpbmVSdW50aW1lSXNsYW5kKHRhZ05hbWUsIGNvbXBvbmVudENsYXNzLCB7XG4gICAgc3RyYXRlZ3k6IG9wdGlvbnMuaHlkcmF0ZSxcbiAgICBkc2Q6IG9wdGlvbnMuZHNkLFxuICAgIHNzcjogb3B0aW9ucy5zc3IsXG4gIH0pO1xufVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFNBQVMsWUFBWSxRQUFRLG9CQUFvQjtBQUNqRDs7Ozs7O0NBTUMsR0FFRCxTQUNFLGFBQWEsRUFFYixXQUFXLFFBRU4sdUJBQXVCO0FBQzlCLFNBQVMsZ0JBQWdCLG1CQUFtQixRQUFRLG9CQUFvQjtBQUN4RSxTQUFTLHdCQUF3QixFQUFFLHdCQUF3QixRQUFRLHNCQUFzQjtBQStCekYsT0FBTyxNQUFNLDRCQUE0QjtFQUM5QixTQUFpQjtFQUNqQixPQUFlO0VBRXhCLFlBQVksUUFBc0IsRUFBRSxTQUFTLEdBQUcsQ0FBRTtJQUNoRCxLQUFLLENBQUMsQ0FBQyxZQUFZLEVBQUUsT0FBTyxX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
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @openelement/app/i18n-plugin - Node-only Vite plugin for i18n
3
+ *
4
+ * deno-api-free:ignore — build-time Vite plugin, allowed to use node:*.
5
+ *
6
+ * This module uses node:process, node:path, node:fs and MUST NOT be imported
7
+ * from the main @openelement/app entry to avoid pulling Node built-ins into
8
+ * client island bundles.
9
+ *
10
+ * Standalone usage requires explicit ctx parameter:
11
+ * ```ts
12
+ * openI18n({ locales: ['en', 'zh'], defaultLocale: 'en', ctx }); // ctx must be explicitly passed
13
+ * ```
14
+ */ import type { Plugin } from 'vite';
15
+ import type { OpenElementBuildContextLike } from '@openelement/protocol/framework';
16
+ import type { OpenElementI18nOptions } from "./i18n-runtime.js";
17
+ /**
18
+ * Generate TypeScript module source code for i18n configuration.
19
+ * The output is a self-contained .ts file that exports locales,
20
+ * defaultLocale, and helper functions.
21
+ *
22
+ * SOP-001: Virtual Modules Removal — replaces @openelement/generated/i18n.
23
+ *
24
+ * @param locales - Array of locale code strings (e.g. ['en', 'zh'])
25
+ * @param defaultLocale - Default locale code (e.g. 'en')
26
+ * @returns TypeScript module source string
27
+ */ export declare function writeI18nDataModule(locales: string[], defaultLocale: string): string;
28
+ /**
29
+ * openElement i18n Vite plugin.
30
+ * Configures locale options for route-level i18n helpers.
31
+ */ export declare function openI18n(options: OpenElementI18nOptions & {
32
+ ctx?: OpenElementBuildContextLike;
33
+ }): Plugin;
34
+ export default openI18n;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @openelement/app/i18n-plugin - Node-only Vite plugin for i18n
3
+ *
4
+ * deno-api-free:ignore — build-time Vite plugin, allowed to use node:*.
5
+ *
6
+ * This module uses node:process, node:path, node:fs and MUST NOT be imported
7
+ * from the main @openelement/app entry to avoid pulling Node built-ins into
8
+ * client island bundles.
9
+ *
10
+ * Standalone usage requires explicit ctx parameter:
11
+ * ```ts
12
+ * openI18n({ locales: ['en', 'zh'], defaultLocale: 'en', ctx }); // ctx must be explicitly passed
13
+ * ```
14
+ */ import { createLogger } from '@openelement/core/logger';
15
+ import { formatError } from '@openelement/core/errors';
16
+ // ponytail: build-time Vite plugin, deno-api-free:ignore applies
17
+ import process from 'node:process';
18
+ import { join } from 'node:path';
19
+ import { mkdirSync, writeFileSync } from 'node:fs';
20
+ import { loadI18nData } from './i18n-runtime.js';
21
+ const log = createLogger('i18n');
22
+ // ─── Data utilities (node-only) ───────────────────────────────────
23
+ /**
24
+ * Generate TypeScript module source code for i18n configuration.
25
+ * The output is a self-contained .ts file that exports locales,
26
+ * defaultLocale, and helper functions.
27
+ *
28
+ * SOP-001: Virtual Modules Removal — replaces @openelement/generated/i18n.
29
+ *
30
+ * @param locales - Array of locale code strings (e.g. ['en', 'zh'])
31
+ * @param defaultLocale - Default locale code (e.g. 'en')
32
+ * @returns TypeScript module source string
33
+ */ export function writeI18nDataModule(locales, defaultLocale) {
34
+ return [
35
+ '// Auto-generated by @openelement/app/i18n — do not edit',
36
+ `export const locales = ${JSON.stringify(locales)};`,
37
+ `export const defaultLocale = "${defaultLocale}";`,
38
+ '',
39
+ 'export function getDefaultLocale(): string { return defaultLocale; }',
40
+ 'export function getI18nOptions() { return { locales, defaultLocale }; }'
41
+ ].join('\n') + '\n';
42
+ }
43
+ // ─── Vite plugin ─────────────────────────────────────────────────
44
+ /**
45
+ * openElement i18n Vite plugin.
46
+ * Configures locale options for route-level i18n helpers.
47
+ */ export function openI18n(options) {
48
+ // ctx must be explicitly provided (via openElement() umbrella or direct param)
49
+ const ctx = options.ctx;
50
+ return {
51
+ name: 'open:i18n',
52
+ buildStart () {
53
+ // ADR 0018: Use loadI18nData() pure function - zero module state
54
+ const i18nData = loadI18nData(options);
55
+ // Write i18n options to ctx (shared build context)
56
+ // The @openelement/generated/i18n plugin reads ctx.plugins.i18nOptions in its load() hook
57
+ if (ctx) {
58
+ ctx.plugins.i18nOptions = {
59
+ locales: i18nData.locales,
60
+ defaultLocale: i18nData.defaultLocale
61
+ };
62
+ }
63
+ // SOP-001: Write generated i18n data module to disk
64
+ try {
65
+ const dataDir = join(process.cwd(), 'app', 'data');
66
+ mkdirSync(dataDir, {
67
+ recursive: true
68
+ });
69
+ const i18nModule = writeI18nDataModule(i18nData.locales, i18nData.defaultLocale);
70
+ writeFileSync(join(dataDir, '_generated-i18n-data.ts'), i18nModule, 'utf-8');
71
+ log.info(`I18n: wrote _generated-i18n-data.ts (${i18nData.locales.join(', ')})`);
72
+ } catch (err) {
73
+ log.warn(`Failed to write _generated-i18n-data.ts: ${formatError(err)}`);
74
+ }
75
+ log.info(`${options.locales.join(', ')} (default: ${options.defaultLocale})`);
76
+ }
77
+ };
78
+ }
79
+ // Use @openelement/app/i18n for runtime helpers: i18nStaticPaths, loadI18nData, switchLocale, normalizeLocalePath
80
+ export default openI18n;
81
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hcHAvc3JjL2kxOG4tcGx1Z2luLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQG9wZW5lbGVtZW50L2FwcC9pMThuLXBsdWdpbiAtIE5vZGUtb25seSBWaXRlIHBsdWdpbiBmb3IgaTE4blxuICpcbiAqIGRlbm8tYXBpLWZyZWU6aWdub3JlIOKAlCBidWlsZC10aW1lIFZpdGUgcGx1Z2luLCBhbGxvd2VkIHRvIHVzZSBub2RlOiouXG4gKlxuICogVGhpcyBtb2R1bGUgdXNlcyBub2RlOnByb2Nlc3MsIG5vZGU6cGF0aCwgbm9kZTpmcyBhbmQgTVVTVCBOT1QgYmUgaW1wb3J0ZWRcbiAqIGZyb20gdGhlIG1haW4gQG9wZW5lbGVtZW50L2FwcCBlbnRyeSB0byBhdm9pZCBwdWxsaW5nIE5vZGUgYnVpbHQtaW5zIGludG9cbiAqIGNsaWVudCBpc2xhbmQgYnVuZGxlcy5cbiAqXG4gKiBTdGFuZGFsb25lIHVzYWdlIHJlcXVpcmVzIGV4cGxpY2l0IGN0eCBwYXJhbWV0ZXI6XG4gKiBgYGB0c1xuICogb3BlbkkxOG4oeyBsb2NhbGVzOiBbJ2VuJywgJ3poJ10sIGRlZmF1bHRMb2NhbGU6ICdlbicsIGN0eCB9KTsgIC8vIGN0eCBtdXN0IGJlIGV4cGxpY2l0bHkgcGFzc2VkXG4gKiBgYGBcbiAqL1xuXG5pbXBvcnQgdHlwZSB7IFBsdWdpbiB9IGZyb20gJ3ZpdGUnO1xuaW1wb3J0IHR5cGUgeyBPcGVuRWxlbWVudEJ1aWxkQ29udGV4dExpa2UgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvZnJhbWV3b3JrJztcbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciB9IGZyb20gJ0BvcGVuZWxlbWVudC9jb3JlL2xvZ2dlcic7XG5pbXBvcnQgeyBmb3JtYXRFcnJvciB9IGZyb20gJ0BvcGVuZWxlbWVudC9jb3JlL2Vycm9ycyc7XG4vLyBwb255dGFpbDogYnVpbGQtdGltZSBWaXRlIHBsdWdpbiwgZGVuby1hcGktZnJlZTppZ25vcmUgYXBwbGllc1xuaW1wb3J0IHByb2Nlc3MgZnJvbSAnbm9kZTpwcm9jZXNzJztcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHsgbWtkaXJTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnbm9kZTpmcyc7XG5pbXBvcnQgeyBsb2FkSTE4bkRhdGEgfSBmcm9tICcuL2kxOG4tcnVudGltZS5qcyc7XG5pbXBvcnQgdHlwZSB7IE9wZW5FbGVtZW50STE4bk9wdGlvbnMgfSBmcm9tICcuL2kxOG4tcnVudGltZS5qcyc7XG5cbmNvbnN0IGxvZyA9IGNyZWF0ZUxvZ2dlcignaTE4bicpO1xuXG4vLyDilIDilIDilIAgRGF0YSB1dGlsaXRpZXMgKG5vZGUtb25seSkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbi8qKlxuICogR2VuZXJhdGUgVHlwZVNjcmlwdCBtb2R1bGUgc291cmNlIGNvZGUgZm9yIGkxOG4gY29uZmlndXJhdGlvbi5cbiAqIFRoZSBvdXRwdXQgaXMgYSBzZWxmLWNvbnRhaW5lZCAudHMgZmlsZSB0aGF0IGV4cG9ydHMgbG9jYWxlcyxcbiAqIGRlZmF1bHRMb2NhbGUsIGFuZCBoZWxwZXIgZnVuY3Rpb25zLlxuICpcbiAqIFNPUC0wMDE6IFZpcnR1YWwgTW9kdWxlcyBSZW1vdmFsIOKAlCByZXBsYWNlcyBAb3BlbmVsZW1lbnQvZ2VuZXJhdGVkL2kxOG4uXG4gKlxuICogQHBhcmFtIGxvY2FsZXMgLSBBcnJheSBvZiBsb2NhbGUgY29kZSBzdHJpbmdzIChlLmcuIFsnZW4nLCAnemgnXSlcbiAqIEBwYXJhbSBkZWZhdWx0TG9jYWxlIC0gRGVmYXVsdCBsb2NhbGUgY29kZSAoZS5nLiAnZW4nKVxuICogQHJldHVybnMgVHlwZVNjcmlwdCBtb2R1bGUgc291cmNlIHN0cmluZ1xuICovXG5leHBvcnQgZnVuY3Rpb24gd3JpdGVJMThuRGF0YU1vZHVsZShsb2NhbGVzOiBzdHJpbmdbXSwgZGVmYXVsdExvY2FsZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIFtcbiAgICAnLy8gQXV0by1nZW5lcmF0ZWQgYnkgQG9wZW5lbGVtZW50L2FwcC9pMThuIOKAlCBkbyBub3QgZWRpdCcsXG4gICAgYGV4cG9ydCBjb25zdCBsb2NhbGVzID0gJHtKU09OLnN0cmluZ2lmeShsb2NhbGVzKX07YCxcbiAgICBgZXhwb3J0IGNvbnN0IGRlZmF1bHRMb2NhbGUgPSBcIiR7ZGVmYXVsdExvY2FsZX1cIjtgLFxuICAgICcnLFxuICAgICdleHBvcnQgZnVuY3Rpb24gZ2V0RGVmYXVsdExvY2FsZSgpOiBzdHJpbmcgeyByZXR1cm4gZGVmYXVsdExvY2FsZTsgfScsXG4gICAgJ2V4cG9ydCBmdW5jdGlvbiBnZXRJMThuT3B0aW9ucygpIHsgcmV0dXJuIHsgbG9jYWxlcywgZGVmYXVsdExvY2FsZSB9OyB9JyxcbiAgXS5qb2luKCdcXG4nKSArICdcXG4nO1xufVxuXG4vLyDilIDilIDilIAgVml0ZSBwbHVnaW4g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbi8qKlxuICogb3BlbkVsZW1lbnQgaTE4biBWaXRlIHBsdWdpbi5cbiAqIENvbmZpZ3VyZXMgbG9jYWxlIG9wdGlvbnMgZm9yIHJvdXRlLWxldmVsIGkxOG4gaGVscGVycy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG9wZW5JMThuKFxuICBvcHRpb25zOiBPcGVuRWxlbWVudEkxOG5PcHRpb25zICYgeyBjdHg/OiBPcGVuRWxlbWVudEJ1aWxkQ29udGV4dExpa2UgfSxcbik6IFBsdWdpbiB7XG4gIC8vIGN0eCBtdXN0IGJlIGV4cGxpY2l0bHkgcHJvdmlkZWQgKHZpYSBvcGVuRWxlbWVudCgpIHVtYnJlbGxhIG9yIGRpcmVjdCBwYXJhbSlcbiAgY29uc3QgY3R4ID0gb3B0aW9ucy5jdHg7XG5cbiAgcmV0dXJuIHtcbiAgICBuYW1lOiAnb3BlbjppMThuJyxcblxuICAgIGJ1aWxkU3RhcnQoKSB7XG4gICAgICAvLyBBRFIgMDAxODogVXNlIGxvYWRJMThuRGF0YSgpIHB1cmUgZnVuY3Rpb24gLSB6ZXJvIG1vZHVsZSBzdGF0ZVxuICAgICAgY29uc3QgaTE4bkRhdGEgPSBsb2FkSTE4bkRhdGEob3B0aW9ucyk7XG5cbiAgICAgIC8vIFdyaXRlIGkxOG4gb3B0aW9ucyB0byBjdHggKHNoYXJlZCBidWlsZCBjb250ZXh0KVxuICAgICAgLy8gVGhlIEBvcGVuZWxlbWVudC9nZW5lcmF0ZWQvaTE4biBwbHVnaW4gcmVhZHMgY3R4LnBsdWdpbnMuaTE4bk9wdGlvbnMgaW4gaXRzIGxvYWQoKSBob29rXG4gICAgICBpZiAoY3R4KSB7XG4gICAgICAgIGN0eC5wbHVnaW5zLmkxOG5PcHRpb25zID0ge1xuICAgICAgICAgIGxvY2FsZXM6IGkxOG5EYXRhLmxvY2FsZXMsXG4gICAgICAgICAgZGVmYXVsdExvY2FsZTogaTE4bkRhdGEuZGVmYXVsdExvY2FsZSxcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgLy8gU09QLTAwMTogV3JpdGUgZ2VuZXJhdGVkIGkxOG4gZGF0YSBtb2R1bGUgdG8gZGlza1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgZGF0YURpciA9IGpvaW4ocHJvY2Vzcy5jd2QoKSwgJ2FwcCcsICdkYXRhJyk7XG4gICAgICAgIG1rZGlyU3luYyhkYXRhRGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICAgICAgY29uc3QgaTE4bk1vZHVsZSA9IHdyaXRlSTE4bkRhdGFNb2R1bGUoaTE4bkRhdGEubG9jYWxlcywgaTE4bkRhdGEuZGVmYXVsdExvY2FsZSk7XG4gICAgICAgIHdyaXRlRmlsZVN5bmMoam9pbihkYXRhRGlyLCAnX2dlbmVyYXRlZC1pMThuLWRhdGEudHMnKSwgaTE4bk1vZHVsZSwgJ3V0Zi04Jyk7XG4gICAgICAgIGxvZy5pbmZvKGBJMThuOiB3cm90ZSBfZ2VuZXJhdGVkLWkxOG4tZGF0YS50cyAoJHtpMThuRGF0YS5sb2NhbGVzLmpvaW4oJywgJyl9KWApO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGxvZy53YXJuKFxuICAgICAgICAgIGBGYWlsZWQgdG8gd3JpdGUgX2dlbmVyYXRlZC1pMThuLWRhdGEudHM6ICR7Zm9ybWF0RXJyb3IoZXJyKX1gLFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICBsb2cuaW5mbyhgJHtvcHRpb25zLmxvY2FsZXMuam9pbignLCAnKX0gKGRlZmF1bHQ6ICR7b3B0aW9ucy5kZWZhdWx0TG9jYWxlfSlgKTtcbiAgICB9LFxuICB9O1xufVxuXG4vLyBVc2UgQG9wZW5lbGVtZW50L2FwcC9pMThuIGZvciBydW50aW1lIGhlbHBlcnM6IGkxOG5TdGF0aWNQYXRocywgbG9hZEkxOG5EYXRhLCBzd2l0Y2hMb2NhbGUsIG5vcm1hbGl6ZUxvY2FsZVBhdGhcblxuZXhwb3J0IGRlZmF1bHQgb3BlbkkxOG47XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Q0FhQyxHQUlELFNBQVMsWUFBWSxRQUFRLDJCQUEyQjtBQUN4RCxTQUFTLFdBQVcsUUFBUSwyQkFBMkI7QUFDdkQsaUVBQWlFO0FBQ2pFLE9BQU8sYUFBYSxlQUFlO0FBQ25DLFNBQVMsSUFBSSxRQUFRLFlBQVk7QUFDakMsU0FBUyxTQUFTLEVBQUUsYUFBYSxRQUFRLFVBQVU7QUFDbkQsU0FBUyxZQUFZLFFBQVEsb0JBQW9CO0FBR2pELE1BQU0sTUFBTSxhQUFhO0FBRXpCLHFFQUFxRTtBQUVyRTs7Ozs7Ozs7OztDQVVDLEdBQ0QsT0FBTyxTQUFTLG9CQUFvQixPQUFpQixFQUFFLGFBQXFCO0VBQzFFLE9BQU87SUFDTDtJQUNBLENBQUMsdUJBQXVCLEVBQUUsS0FBSyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDcEQsQ0FBQyw4QkFBOEIsRUFBRSxjQUFjLEVBQUUsQ0FBQztJQUNsRDtJQUNBO0lBQ0E7R0FDRCxDQUFDLElBQUksQ0FBQyxRQUFRO0FBQ2pCO0FBRUEsb0VBQW9FO0FBRXBFOzs7Q0FHQyxHQUNELE9BQU8sU0FBUyxTQUNkLE9BQXVFO0VBRXZFLCtFQUErRTtFQUMvRSxNQUFNLE1BQU0sUUFBUSxHQUFHO0VBRXZCLE9BQU87SUFDTCxNQUFNO0lBRU47TUFDRSxpRUFBaUU7TUFDakUsTUFBTSxXQUFXLGFBQWE7TUFFOUIsbURBQW1EO01BQ25ELDBGQUEwRjtNQUMxRixJQUFJLEtBQUs7UUFDUCxJQUFJLE9BQU8sQ0FBQyxXQUFXLEdBQUc7VUFDeEIsU0FBUyxTQUFTLE9BQU87VUFDekIsZUFBZSxTQUFTLGFBQWE7UUFDdkM7TUFDRjtNQUVBLG9EQUFvRDtNQUNwRCxJQUFJO1FBQ0YsTUFBTSxVQUFVLEtBQUssUUFBUSxHQUFHLElBQUksT0FBTztRQUMzQyxVQUFVLFNBQVM7VUFBRSxXQUFXO1FBQUs7UUFDckMsTUFBTSxhQUFhLG9CQUFvQixTQUFTLE9BQU8sRUFBRSxTQUFTLGFBQWE7UUFDL0UsY0FBYyxLQUFLLFNBQVMsNEJBQTRCLFlBQVk7UUFDcEUsSUFBSSxJQUFJLENBQUMsQ0FBQyxxQ0FBcUMsRUFBRSxTQUFTLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7TUFDakYsRUFBRSxPQUFPLEtBQUs7UUFDWixJQUFJLElBQUksQ0FDTixDQUFDLHlDQUF5QyxFQUFFLFlBQVksTUFBTTtNQUVsRTtNQUVBLElBQUksSUFBSSxDQUFDLEdBQUcsUUFBUSxPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sV0FBVyxFQUFFLFFBQVEsYUFBYSxDQUFDLENBQUMsQ0FBQztJQUM5RTtFQUNGO0FBQ0Y7QUFFQSxrSEFBa0g7QUFFbEgsZUFBZSxTQUFTIn0=
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @openelement/app/i18n-runtime - Runtime-safe i18n helpers (no node:* modules)
3
+ *
4
+ * Separated from i18n-plugin.ts to prevent node:process/node:path/node:fs from
5
+ * being pulled into client island bundles via @openelement/app main re-exports.
6
+ *
7
+ * Route-level helpers:
8
+ * ```ts
9
+ * import { i18nStaticPaths, switchLocale } from '@openelement/app/i18n';
10
+ * ```
11
+ */ export interface OpenElementI18nOptions {
12
+ /** Available locale codes, e.g. ['en', 'zh'] */ locales: string[];
13
+ /** Default locale, e.g. 'en' */ defaultLocale: string;
14
+ }
15
+ /**
16
+ * Pure function: load i18n configuration.
17
+ * No module-level state. No side effects.
18
+ *
19
+ * This replaces the stateful initI18nData() + getI18nOptions() pattern.
20
+ * For virtual module consumers, use @openelement/generated/i18n instead.
21
+ */ export declare function loadI18nData(options: OpenElementI18nOptions): OpenElementI18nOptions;
22
+ /**
23
+ * Generate getStaticPaths() return for locale-aware routes.
24
+ *
25
+ * ADR 0018: locales parameter is now REQUIRED (no module-level state fallback).
26
+ * Import locales from @openelement/generated/i18n in route components.
27
+ *
28
+ * Usage in route file:
29
+ * ```ts
30
+ * import { i18nStaticPaths } from '@openelement/app/i18n';
31
+ * import { locales } from '@openelement/generated/i18n';
32
+ *
33
+ * export function getStaticPaths() {
34
+ * return i18nStaticPaths(locales); // -> [{ locale: 'en' }, { locale: 'zh' }]
35
+ * }
36
+ * ```
37
+ */ export declare function i18nStaticPaths(locales: string[]): Array<Record<string, string>>;
38
+ /**
39
+ * Switch a URL path to a different locale.
40
+ * e.g. switchLocale('/en/guide/overview', 'zh') -> '/zh/guide/overview'
41
+ * switchLocale('/guide/overview', 'zh', ['en', 'zh']) -> '/zh/guide/overview'
42
+ */ export declare function switchLocale(currentPath: string, targetLocale: string, locales: string[]): string;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @openelement/app/i18n-runtime - Runtime-safe i18n helpers (no node:* modules)
3
+ *
4
+ * Separated from i18n-plugin.ts to prevent node:process/node:path/node:fs from
5
+ * being pulled into client island bundles via @openelement/app main re-exports.
6
+ *
7
+ * Route-level helpers:
8
+ * ```ts
9
+ * import { i18nStaticPaths, switchLocale } from '@openelement/app/i18n';
10
+ * ```
11
+ */ // ─── Types ────────────────────────────────────────────────────────
12
+ // ─── Data utilities ───────────────────────────────────────────────
13
+ /**
14
+ * Pure function: load i18n configuration.
15
+ * No module-level state. No side effects.
16
+ *
17
+ * This replaces the stateful initI18nData() + getI18nOptions() pattern.
18
+ * For virtual module consumers, use @openelement/generated/i18n instead.
19
+ */ export function loadI18nData(options) {
20
+ return {
21
+ ...options,
22
+ locales: [
23
+ ...options.locales
24
+ ]
25
+ };
26
+ }
27
+ // ─── Route helpers ────────────────────────────────────────────────
28
+ /**
29
+ * Generate getStaticPaths() return for locale-aware routes.
30
+ *
31
+ * ADR 0018: locales parameter is now REQUIRED (no module-level state fallback).
32
+ * Import locales from @openelement/generated/i18n in route components.
33
+ *
34
+ * Usage in route file:
35
+ * ```ts
36
+ * import { i18nStaticPaths } from '@openelement/app/i18n';
37
+ * import { locales } from '@openelement/generated/i18n';
38
+ *
39
+ * export function getStaticPaths() {
40
+ * return i18nStaticPaths(locales); // -> [{ locale: 'en' }, { locale: 'zh' }]
41
+ * }
42
+ * ```
43
+ */ export function i18nStaticPaths(locales) {
44
+ return locales.map((locale)=>({
45
+ locale
46
+ }));
47
+ }
48
+ /**
49
+ * Switch a URL path to a different locale.
50
+ * e.g. switchLocale('/en/guide/overview', 'zh') -> '/zh/guide/overview'
51
+ * switchLocale('/guide/overview', 'zh', ['en', 'zh']) -> '/zh/guide/overview'
52
+ */ export function switchLocale(currentPath, targetLocale, locales) {
53
+ // Strip any existing locale prefix
54
+ let stripped = currentPath;
55
+ for (const loc of locales){
56
+ if (stripped === `/${loc}` || stripped.startsWith(`/${loc}/`)) {
57
+ stripped = stripped.slice(loc.length + 1) || '/';
58
+ break;
59
+ }
60
+ }
61
+ // Prepend target locale
62
+ return `/${targetLocale}${stripped}`;
63
+ }
64
+ export { normalizeLocalePath } from '@openelement/router/i18n';
65
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hcHAvc3JjL2kxOG4tcnVudGltZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBvcGVuZWxlbWVudC9hcHAvaTE4bi1ydW50aW1lIC0gUnVudGltZS1zYWZlIGkxOG4gaGVscGVycyAobm8gbm9kZToqIG1vZHVsZXMpXG4gKlxuICogU2VwYXJhdGVkIGZyb20gaTE4bi1wbHVnaW4udHMgdG8gcHJldmVudCBub2RlOnByb2Nlc3Mvbm9kZTpwYXRoL25vZGU6ZnMgZnJvbVxuICogYmVpbmcgcHVsbGVkIGludG8gY2xpZW50IGlzbGFuZCBidW5kbGVzIHZpYSBAb3BlbmVsZW1lbnQvYXBwIG1haW4gcmUtZXhwb3J0cy5cbiAqXG4gKiBSb3V0ZS1sZXZlbCBoZWxwZXJzOlxuICogYGBgdHNcbiAqIGltcG9ydCB7IGkxOG5TdGF0aWNQYXRocywgc3dpdGNoTG9jYWxlIH0gZnJvbSAnQG9wZW5lbGVtZW50L2FwcC9pMThuJztcbiAqIGBgYFxuICovXG5cbi8vIOKUgOKUgOKUgCBUeXBlcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuZXhwb3J0IGludGVyZmFjZSBPcGVuRWxlbWVudEkxOG5PcHRpb25zIHtcbiAgLyoqIEF2YWlsYWJsZSBsb2NhbGUgY29kZXMsIGUuZy4gWydlbicsICd6aCddICovXG4gIGxvY2FsZXM6IHN0cmluZ1tdO1xuICAvKiogRGVmYXVsdCBsb2NhbGUsIGUuZy4gJ2VuJyAqL1xuICBkZWZhdWx0TG9jYWxlOiBzdHJpbmc7XG59XG5cbi8vIOKUgOKUgOKUgCBEYXRhIHV0aWxpdGllcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuLyoqXG4gKiBQdXJlIGZ1bmN0aW9uOiBsb2FkIGkxOG4gY29uZmlndXJhdGlvbi5cbiAqIE5vIG1vZHVsZS1sZXZlbCBzdGF0ZS4gTm8gc2lkZSBlZmZlY3RzLlxuICpcbiAqIFRoaXMgcmVwbGFjZXMgdGhlIHN0YXRlZnVsIGluaXRJMThuRGF0YSgpICsgZ2V0STE4bk9wdGlvbnMoKSBwYXR0ZXJuLlxuICogRm9yIHZpcnR1YWwgbW9kdWxlIGNvbnN1bWVycywgdXNlIEBvcGVuZWxlbWVudC9nZW5lcmF0ZWQvaTE4biBpbnN0ZWFkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbG9hZEkxOG5EYXRhKG9wdGlvbnM6IE9wZW5FbGVtZW50STE4bk9wdGlvbnMpOiBPcGVuRWxlbWVudEkxOG5PcHRpb25zIHtcbiAgcmV0dXJuIHtcbiAgICAuLi5vcHRpb25zLFxuICAgIGxvY2FsZXM6IFsuLi5vcHRpb25zLmxvY2FsZXNdLFxuICB9O1xufVxuXG4vLyDilIDilIDilIAgUm91dGUgaGVscGVycyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuLyoqXG4gKiBHZW5lcmF0ZSBnZXRTdGF0aWNQYXRocygpIHJldHVybiBmb3IgbG9jYWxlLWF3YXJlIHJvdXRlcy5cbiAqXG4gKiBBRFIgMDAxODogbG9jYWxlcyBwYXJhbWV0ZXIgaXMgbm93IFJFUVVJUkVEIChubyBtb2R1bGUtbGV2ZWwgc3RhdGUgZmFsbGJhY2spLlxuICogSW1wb3J0IGxvY2FsZXMgZnJvbSBAb3BlbmVsZW1lbnQvZ2VuZXJhdGVkL2kxOG4gaW4gcm91dGUgY29tcG9uZW50cy5cbiAqXG4gKiBVc2FnZSBpbiByb3V0ZSBmaWxlOlxuICogYGBgdHNcbiAqIGltcG9ydCB7IGkxOG5TdGF0aWNQYXRocyB9IGZyb20gJ0BvcGVuZWxlbWVudC9hcHAvaTE4bic7XG4gKiBpbXBvcnQgeyBsb2NhbGVzIH0gZnJvbSAnQG9wZW5lbGVtZW50L2dlbmVyYXRlZC9pMThuJztcbiAqXG4gKiBleHBvcnQgZnVuY3Rpb24gZ2V0U3RhdGljUGF0aHMoKSB7XG4gKiAgIHJldHVybiBpMThuU3RhdGljUGF0aHMobG9jYWxlcyk7IC8vIC0+IFt7IGxvY2FsZTogJ2VuJyB9LCB7IGxvY2FsZTogJ3poJyB9XVxuICogfVxuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpMThuU3RhdGljUGF0aHMoXG4gIGxvY2FsZXM6IHN0cmluZ1tdLFxuKTogQXJyYXk8UmVjb3JkPHN0cmluZywgc3RyaW5nPj4ge1xuICByZXR1cm4gbG9jYWxlcy5tYXAoKGxvY2FsZSkgPT4gKHsgbG9jYWxlIH0pKTtcbn1cblxuLyoqXG4gKiBTd2l0Y2ggYSBVUkwgcGF0aCB0byBhIGRpZmZlcmVudCBsb2NhbGUuXG4gKiBlLmcuIHN3aXRjaExvY2FsZSgnL2VuL2d1aWRlL292ZXJ2aWV3JywgJ3poJykgLT4gJy96aC9ndWlkZS9vdmVydmlldydcbiAqICAgICAgc3dpdGNoTG9jYWxlKCcvZ3VpZGUvb3ZlcnZpZXcnLCAnemgnLCBbJ2VuJywgJ3poJ10pIC0+ICcvemgvZ3VpZGUvb3ZlcnZpZXcnXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzd2l0Y2hMb2NhbGUoXG4gIGN1cnJlbnRQYXRoOiBzdHJpbmcsXG4gIHRhcmdldExvY2FsZTogc3RyaW5nLFxuICBsb2NhbGVzOiBzdHJpbmdbXSxcbik6IHN0cmluZyB7XG4gIC8vIFN0cmlwIGFueSBleGlzdGluZyBsb2NhbGUgcHJlZml4XG4gIGxldCBzdHJpcHBlZCA9IGN1cnJlbnRQYXRoO1xuICBmb3IgKGNvbnN0IGxvYyBvZiBsb2NhbGVzKSB7XG4gICAgaWYgKHN0cmlwcGVkID09PSBgLyR7bG9jfWAgfHwgc3RyaXBwZWQuc3RhcnRzV2l0aChgLyR7bG9jfS9gKSkge1xuICAgICAgc3RyaXBwZWQgPSBzdHJpcHBlZC5zbGljZShsb2MubGVuZ3RoICsgMSkgfHwgJy8nO1xuICAgICAgYnJlYWs7XG4gICAgfVxuICB9XG4gIC8vIFByZXBlbmQgdGFyZ2V0IGxvY2FsZVxuICByZXR1cm4gYC8ke3RhcmdldExvY2FsZX0ke3N0cmlwcGVkfWA7XG59XG5cbmV4cG9ydCB0eXBlIHsgTG9jYWxlUGF0aCB9IGZyb20gJ0BvcGVuZWxlbWVudC9yb3V0ZXIvaTE4bic7XG5leHBvcnQgeyBub3JtYWxpemVMb2NhbGVQYXRoIH0gZnJvbSAnQG9wZW5lbGVtZW50L3JvdXRlci9pMThuJztcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7OztDQVVDLEdBRUQscUVBQXFFO0FBU3JFLHFFQUFxRTtBQUVyRTs7Ozs7O0NBTUMsR0FDRCxPQUFPLFNBQVMsYUFBYSxPQUErQjtFQUMxRCxPQUFPO0lBQ0wsR0FBRyxPQUFPO0lBQ1YsU0FBUztTQUFJLFFBQVEsT0FBTztLQUFDO0VBQy9CO0FBQ0Y7QUFFQSxxRUFBcUU7QUFFckU7Ozs7Ozs7Ozs7Ozs7OztDQWVDLEdBQ0QsT0FBTyxTQUFTLGdCQUNkLE9BQWlCO0VBRWpCLE9BQU8sUUFBUSxHQUFHLENBQUMsQ0FBQyxTQUFXLENBQUM7TUFBRTtJQUFPLENBQUM7QUFDNUM7QUFFQTs7OztDQUlDLEdBQ0QsT0FBTyxTQUFTLGFBQ2QsV0FBbUIsRUFDbkIsWUFBb0IsRUFDcEIsT0FBaUI7RUFFakIsbUNBQW1DO0VBQ25DLElBQUksV0FBVztFQUNmLEtBQUssTUFBTSxPQUFPLFFBQVM7SUFDekIsSUFBSSxhQUFhLENBQUMsQ0FBQyxFQUFFLEtBQUssSUFBSSxTQUFTLFVBQVUsQ0FBQyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxHQUFHO01BQzdELFdBQVcsU0FBUyxLQUFLLENBQUMsSUFBSSxNQUFNLEdBQUcsTUFBTTtNQUM3QztJQUNGO0VBQ0Y7RUFDQSx3QkFBd0I7RUFDeEIsT0FBTyxDQUFDLENBQUMsRUFBRSxlQUFlLFVBQVU7QUFDdEM7QUFHQSxTQUFTLG1CQUFtQixRQUFRLDJCQUEyQiJ9
package/src/i18n.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @openelement/app/i18n - Runtime-safe i18n helpers (no node:* modules)
3
+ *
4
+ * Thin re-export barrel from i18n-runtime.ts.
5
+ * The node-only Vite plugin is in i18n-plugin.ts and must NOT be re-exported here,
6
+ * to prevent node:process/node:path/node:fs from being pulled into client
7
+ * island bundles via @openelement/app main re-exports.
8
+ *
9
+ * Route-level helpers:
10
+ * ```ts
11
+ * import { i18nStaticPaths, switchLocale } from '@openelement/app/i18n';
12
+ * ```
13
+ */ export type { OpenElementI18nOptions } from "./i18n-runtime.js";
14
+ export { i18nStaticPaths, loadI18nData, switchLocale } from "./i18n-runtime.js";
15
+ export type { LocalePath } from '@openelement/router/i18n';
16
+ export { normalizeLocalePath } from '@openelement/router/i18n';
package/src/i18n.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @openelement/app/i18n - Runtime-safe i18n helpers (no node:* modules)
3
+ *
4
+ * Thin re-export barrel from i18n-runtime.ts.
5
+ * The node-only Vite plugin is in i18n-plugin.ts and must NOT be re-exported here,
6
+ * to prevent node:process/node:path/node:fs from being pulled into client
7
+ * island bundles via @openelement/app main re-exports.
8
+ *
9
+ * Route-level helpers:
10
+ * ```ts
11
+ * import { i18nStaticPaths, switchLocale } from '@openelement/app/i18n';
12
+ * ```
13
+ */ export { i18nStaticPaths, loadI18nData, switchLocale } from './i18n-runtime.js';
14
+ export { normalizeLocalePath } from '@openelement/router/i18n';
15
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hcHAvc3JjL2kxOG4udHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAb3BlbmVsZW1lbnQvYXBwL2kxOG4gLSBSdW50aW1lLXNhZmUgaTE4biBoZWxwZXJzIChubyBub2RlOiogbW9kdWxlcylcbiAqXG4gKiBUaGluIHJlLWV4cG9ydCBiYXJyZWwgZnJvbSBpMThuLXJ1bnRpbWUudHMuXG4gKiBUaGUgbm9kZS1vbmx5IFZpdGUgcGx1Z2luIGlzIGluIGkxOG4tcGx1Z2luLnRzIGFuZCBtdXN0IE5PVCBiZSByZS1leHBvcnRlZCBoZXJlLFxuICogdG8gcHJldmVudCBub2RlOnByb2Nlc3Mvbm9kZTpwYXRoL25vZGU6ZnMgZnJvbSBiZWluZyBwdWxsZWQgaW50byBjbGllbnRcbiAqIGlzbGFuZCBidW5kbGVzIHZpYSBAb3BlbmVsZW1lbnQvYXBwIG1haW4gcmUtZXhwb3J0cy5cbiAqXG4gKiBSb3V0ZS1sZXZlbCBoZWxwZXJzOlxuICogYGBgdHNcbiAqIGltcG9ydCB7IGkxOG5TdGF0aWNQYXRocywgc3dpdGNoTG9jYWxlIH0gZnJvbSAnQG9wZW5lbGVtZW50L2FwcC9pMThuJztcbiAqIGBgYFxuICovXG5cbmV4cG9ydCB0eXBlIHsgT3BlbkVsZW1lbnRJMThuT3B0aW9ucyB9IGZyb20gJy4vaTE4bi1ydW50aW1lLmpzJztcbmV4cG9ydCB7IGkxOG5TdGF0aWNQYXRocywgbG9hZEkxOG5EYXRhLCBzd2l0Y2hMb2NhbGUgfSBmcm9tICcuL2kxOG4tcnVudGltZS5qcyc7XG5leHBvcnQgdHlwZSB7IExvY2FsZVBhdGggfSBmcm9tICdAb3BlbmVsZW1lbnQvcm91dGVyL2kxOG4nO1xuZXhwb3J0IHsgbm9ybWFsaXplTG9jYWxlUGF0aCB9IGZyb20gJ0BvcGVuZWxlbWVudC9yb3V0ZXIvaTE4bic7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7OztDQVlDLEdBR0QsU0FBUyxlQUFlLEVBQUUsWUFBWSxFQUFFLFlBQVksUUFBUSxvQkFBb0I7QUFFaEYsU0FBUyxtQkFBbUIsUUFBUSwyQkFBMkIifQ==
package/src/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { defineIsland, defineIslandConfig, definePage, isOpenElementNotFound, isOpenElementRedirect, notFound, OpenElementNotFound, OpenElementRedirect, redirect } from "./authoring.js";
2
+ export type { AppIslandOptions, IslandConfig, NormalizedPageRenderIntent, OpenElementPageDescriptor, PageDefinition, PageErrorContext, PageErrorFunction, PageHead, PageMeta, PageRenderContext, PageRenderFunction, PageRenderingMode, PageRenderIntent, PageRevalidate, PageRouteContext, PageRouteIntent, PageStreamingMode } from "./authoring.js";
3
+ export type { Action, ActionContext, Loader, LoaderContext } from '@openelement/protocol/data';
4
+ export { defineElement, defineLayout } from '@openelement/element';
5
+ export type { ElementDefinition } from '@openelement/element';
package/src/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { defineIsland, defineIslandConfig, definePage, isOpenElementNotFound, isOpenElementRedirect, notFound, OpenElementNotFound, OpenElementRedirect, redirect } from './authoring.js';
2
+ // Re-export from @openelement/element for convenience
3
+ export { defineElement, defineLayout } from '@openelement/element';
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hcHAvc3JjL2luZGV4LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7XG4gIGRlZmluZUlzbGFuZCxcbiAgZGVmaW5lSXNsYW5kQ29uZmlnLFxuICBkZWZpbmVQYWdlLFxuICBpc09wZW5FbGVtZW50Tm90Rm91bmQsXG4gIGlzT3BlbkVsZW1lbnRSZWRpcmVjdCxcbiAgbm90Rm91bmQsXG4gIE9wZW5FbGVtZW50Tm90Rm91bmQsXG4gIE9wZW5FbGVtZW50UmVkaXJlY3QsXG4gIHJlZGlyZWN0LFxufSBmcm9tICcuL2F1dGhvcmluZy5qcyc7XG5leHBvcnQgdHlwZSB7XG4gIEFwcElzbGFuZE9wdGlvbnMsXG4gIElzbGFuZENvbmZpZyxcbiAgTm9ybWFsaXplZFBhZ2VSZW5kZXJJbnRlbnQsXG4gIE9wZW5FbGVtZW50UGFnZURlc2NyaXB0b3IsXG4gIFBhZ2VEZWZpbml0aW9uLFxuICBQYWdlRXJyb3JDb250ZXh0LFxuICBQYWdlRXJyb3JGdW5jdGlvbixcbiAgUGFnZUhlYWQsXG4gIFBhZ2VNZXRhLFxuICBQYWdlUmVuZGVyQ29udGV4dCxcbiAgUGFnZVJlbmRlckZ1bmN0aW9uLFxuICBQYWdlUmVuZGVyaW5nTW9kZSxcbiAgUGFnZVJlbmRlckludGVudCxcbiAgUGFnZVJldmFsaWRhdGUsXG4gIFBhZ2VSb3V0ZUNvbnRleHQsXG4gIFBhZ2VSb3V0ZUludGVudCxcbiAgUGFnZVN0cmVhbWluZ01vZGUsXG59IGZyb20gJy4vYXV0aG9yaW5nLmpzJztcblxuLy8gUmUtZXhwb3J0IHJvdXRlIGRhdGEgdHlwZXMgZnJvbSBwcm90b2NvbCBmb3IgY29udmVuaWVuY2VcbmV4cG9ydCB0eXBlIHsgQWN0aW9uLCBBY3Rpb25Db250ZXh0LCBMb2FkZXIsIExvYWRlckNvbnRleHQgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvZGF0YSc7XG5cbi8vIFJlLWV4cG9ydCBmcm9tIEBvcGVuZWxlbWVudC9lbGVtZW50IGZvciBjb252ZW5pZW5jZVxuZXhwb3J0IHsgZGVmaW5lRWxlbWVudCwgZGVmaW5lTGF5b3V0IH0gZnJvbSAnQG9wZW5lbGVtZW50L2VsZW1lbnQnO1xuZXhwb3J0IHR5cGUgeyBFbGVtZW50RGVmaW5pdGlvbiB9IGZyb20gJ0BvcGVuZWxlbWVudC9lbGVtZW50JztcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxTQUNFLFlBQVksRUFDWixrQkFBa0IsRUFDbEIsVUFBVSxFQUNWLHFCQUFxQixFQUNyQixxQkFBcUIsRUFDckIsUUFBUSxFQUNSLG1CQUFtQixFQUNuQixtQkFBbUIsRUFDbkIsUUFBUSxRQUNILGlCQUFpQjtBQXdCeEIsc0RBQXNEO0FBQ3RELFNBQVMsYUFBYSxFQUFFLFlBQVksUUFBUSx1QkFBdUIifQ==
@@ -0,0 +1,8 @@
1
+ import { type ComponentChild } from 'preact';
2
+ import type { IslandConfig } from "./authoring.js";
3
+ export type PreactIslandProps = Record<string, unknown>;
4
+ export type PreactIslandComponent<Props extends PreactIslandProps = PreactIslandProps> = (props: Props) => ComponentChild;
5
+ export interface PreactIslandOptions extends IslandConfig {
6
+ props?: PreactIslandProps;
7
+ }
8
+ export declare function definePreactIsland<Props extends PreactIslandProps = PreactIslandProps>(tagName: string, Component: PreactIslandComponent<Props>, options?: PreactIslandOptions): CustomElementConstructor;
package/src/preact.js ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @openelement/app/preact — Preact island support.
3
+ *
4
+ * Creates custom elements that extend OpenElement and render Preact
5
+ * components through the DSD SSR pipeline. On the server, the Preact
6
+ * component is rendered to a string using preact-render-to-string and
7
+ * wrapped as trusted HTML in OpenElement's render(). On the client,
8
+ * preactHydrate or preactRender takes over in the clientActivate() hook.
9
+ *
10
+ * @module @openelement/app/preact
11
+ */ import { OpenElement, trustedHtml } from '@openelement/element';
12
+ import { getSsrProps } from '@openelement/core';
13
+ import { h, hydrate as preactHydrate, render as preactRender } from 'preact';
14
+ import { renderToString } from 'preact-render-to-string';
15
+ function assertCustomElementTag(tagName) {
16
+ if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(tagName)) {
17
+ throw new Error(`openElement: "${tagName}" is not a valid custom element name. ` + 'Use lowercase ASCII letters, digits, and at least one hyphen.');
18
+ }
19
+ }
20
+ function collectAttributes(host) {
21
+ const props = {};
22
+ const attrs = host.attributes;
23
+ if (!attrs) return props;
24
+ for (const attr of Array.from(attrs)){
25
+ if (attr.name === 'data-ssr-props') continue;
26
+ props[attr.name] = attr.value;
27
+ }
28
+ return props;
29
+ }
30
+ function resolveProps(host, baseProps) {
31
+ return {
32
+ ...baseProps,
33
+ ...collectAttributes(host),
34
+ ...getSsrProps(host) ?? {}
35
+ };
36
+ }
37
+ export function definePreactIsland(tagName, Component, options = {}) {
38
+ assertCustomElementTag(tagName);
39
+ const baseProps = options.props ?? {};
40
+ class OpenElementPreactIsland extends OpenElement {
41
+ render() {
42
+ if (typeof document === 'undefined') {
43
+ // SSR path: render Preact component to string, return as trusted HTML
44
+ const html = renderToString(h(Component, resolveProps(this, baseProps)));
45
+ return trustedHtml(html);
46
+ }
47
+ // Client: let clientActivate() handle Preact hydration/render
48
+ return null;
49
+ }
50
+ clientActivate() {
51
+ const root = this.shadowRoot ?? this.attachShadow({
52
+ mode: 'open'
53
+ });
54
+ const vnode = h(Component, resolveProps(this, baseProps));
55
+ if (options.ssr !== false) {
56
+ // DSD hydration: the shadow DOM already has SSR-rendered content
57
+ preactHydrate(vnode, root);
58
+ } else {
59
+ // CSR: full render from scratch
60
+ preactRender(vnode, root);
61
+ }
62
+ }
63
+ }
64
+ if (typeof customElements !== 'undefined' && !customElements.get(tagName)) {
65
+ customElements.define(tagName, OpenElementPreactIsland);
66
+ }
67
+ return OpenElementPreactIsland;
68
+ }
69
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hcHAvc3JjL3ByZWFjdC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBvcGVuZWxlbWVudC9hcHAvcHJlYWN0IOKAlCBQcmVhY3QgaXNsYW5kIHN1cHBvcnQuXG4gKlxuICogQ3JlYXRlcyBjdXN0b20gZWxlbWVudHMgdGhhdCBleHRlbmQgT3BlbkVsZW1lbnQgYW5kIHJlbmRlciBQcmVhY3RcbiAqIGNvbXBvbmVudHMgdGhyb3VnaCB0aGUgRFNEIFNTUiBwaXBlbGluZS4gT24gdGhlIHNlcnZlciwgdGhlIFByZWFjdFxuICogY29tcG9uZW50IGlzIHJlbmRlcmVkIHRvIGEgc3RyaW5nIHVzaW5nIHByZWFjdC1yZW5kZXItdG8tc3RyaW5nIGFuZFxuICogd3JhcHBlZCBhcyB0cnVzdGVkIEhUTUwgaW4gT3BlbkVsZW1lbnQncyByZW5kZXIoKS4gT24gdGhlIGNsaWVudCxcbiAqIHByZWFjdEh5ZHJhdGUgb3IgcHJlYWN0UmVuZGVyIHRha2VzIG92ZXIgaW4gdGhlIGNsaWVudEFjdGl2YXRlKCkgaG9vay5cbiAqXG4gKiBAbW9kdWxlIEBvcGVuZWxlbWVudC9hcHAvcHJlYWN0XG4gKi9cblxuaW1wb3J0IHsgT3BlbkVsZW1lbnQsIHRydXN0ZWRIdG1sLCB0eXBlIFZOb2RlIH0gZnJvbSAnQG9wZW5lbGVtZW50L2VsZW1lbnQnO1xuaW1wb3J0IHsgZ2V0U3NyUHJvcHMgfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZSc7XG5pbXBvcnQgeyB0eXBlIENvbXBvbmVudENoaWxkLCBoLCBoeWRyYXRlIGFzIHByZWFjdEh5ZHJhdGUsIHJlbmRlciBhcyBwcmVhY3RSZW5kZXIgfSBmcm9tICdwcmVhY3QnO1xuaW1wb3J0IHsgcmVuZGVyVG9TdHJpbmcgfSBmcm9tICdwcmVhY3QtcmVuZGVyLXRvLXN0cmluZyc7XG5pbXBvcnQgdHlwZSB7IElzbGFuZENvbmZpZyB9IGZyb20gJy4vYXV0aG9yaW5nLmpzJztcblxuZXhwb3J0IHR5cGUgUHJlYWN0SXNsYW5kUHJvcHMgPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcblxuZXhwb3J0IHR5cGUgUHJlYWN0SXNsYW5kQ29tcG9uZW50PFByb3BzIGV4dGVuZHMgUHJlYWN0SXNsYW5kUHJvcHMgPSBQcmVhY3RJc2xhbmRQcm9wcz4gPSAoXG4gIHByb3BzOiBQcm9wcyxcbikgPT4gQ29tcG9uZW50Q2hpbGQ7XG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJlYWN0SXNsYW5kT3B0aW9ucyBleHRlbmRzIElzbGFuZENvbmZpZyB7XG4gIHByb3BzPzogUHJlYWN0SXNsYW5kUHJvcHM7XG59XG5cbmZ1bmN0aW9uIGFzc2VydEN1c3RvbUVsZW1lbnRUYWcodGFnTmFtZTogc3RyaW5nKTogdm9pZCB7XG4gIGlmICghL15bYS16XVthLXowLTldKigtW2EtejAtOV0rKSokLy50ZXN0KHRhZ05hbWUpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYG9wZW5FbGVtZW50OiBcIiR7dGFnTmFtZX1cIiBpcyBub3QgYSB2YWxpZCBjdXN0b20gZWxlbWVudCBuYW1lLiBgICtcbiAgICAgICAgJ1VzZSBsb3dlcmNhc2UgQVNDSUkgbGV0dGVycywgZGlnaXRzLCBhbmQgYXQgbGVhc3Qgb25lIGh5cGhlbi4nLFxuICAgICk7XG4gIH1cbn1cblxuZnVuY3Rpb24gY29sbGVjdEF0dHJpYnV0ZXMoaG9zdDogSFRNTEVsZW1lbnQpOiBQcmVhY3RJc2xhbmRQcm9wcyB7XG4gIGNvbnN0IHByb3BzOiBQcmVhY3RJc2xhbmRQcm9wcyA9IHt9O1xuICBjb25zdCBhdHRycyA9IGhvc3QuYXR0cmlidXRlcztcbiAgaWYgKCFhdHRycykgcmV0dXJuIHByb3BzO1xuICBmb3IgKGNvbnN0IGF0dHIgb2YgQXJyYXkuZnJvbShhdHRycykpIHtcbiAgICBpZiAoYXR0ci5uYW1lID09PSAnZGF0YS1zc3ItcHJvcHMnKSBjb250aW51ZTtcbiAgICBwcm9wc1thdHRyLm5hbWVdID0gYXR0ci52YWx1ZTtcbiAgfVxuICByZXR1cm4gcHJvcHM7XG59XG5cbmZ1bmN0aW9uIHJlc29sdmVQcm9wcyhob3N0OiBIVE1MRWxlbWVudCwgYmFzZVByb3BzOiBQcmVhY3RJc2xhbmRQcm9wcyk6IFByZWFjdElzbGFuZFByb3BzIHtcbiAgcmV0dXJuIHtcbiAgICAuLi5iYXNlUHJvcHMsXG4gICAgLi4uY29sbGVjdEF0dHJpYnV0ZXMoaG9zdCksXG4gICAgLi4uKGdldFNzclByb3BzKGhvc3QpID8/IHt9KSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlZmluZVByZWFjdElzbGFuZDxQcm9wcyBleHRlbmRzIFByZWFjdElzbGFuZFByb3BzID0gUHJlYWN0SXNsYW5kUHJvcHM+KFxuICB0YWdOYW1lOiBzdHJpbmcsXG4gIENvbXBvbmVudDogUHJlYWN0SXNsYW5kQ29tcG9uZW50PFByb3BzPixcbiAgb3B0aW9uczogUHJlYWN0SXNsYW5kT3B0aW9ucyA9IHt9LFxuKTogQ3VzdG9tRWxlbWVudENvbnN0cnVjdG9yIHtcbiAgYXNzZXJ0Q3VzdG9tRWxlbWVudFRhZyh0YWdOYW1lKTtcbiAgY29uc3QgYmFzZVByb3BzID0gb3B0aW9ucy5wcm9wcyA/PyB7fTtcblxuICBjbGFzcyBPcGVuRWxlbWVudFByZWFjdElzbGFuZCBleHRlbmRzIE9wZW5FbGVtZW50IHtcbiAgICBvdmVycmlkZSByZW5kZXIoKTogVk5vZGUgfCBudWxsIHtcbiAgICAgIGlmICh0eXBlb2YgZG9jdW1lbnQgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgIC8vIFNTUiBwYXRoOiByZW5kZXIgUHJlYWN0IGNvbXBvbmVudCB0byBzdHJpbmcsIHJldHVybiBhcyB0cnVzdGVkIEhUTUxcbiAgICAgICAgY29uc3QgaHRtbCA9IHJlbmRlclRvU3RyaW5nKFxuICAgICAgICAgIGgoQ29tcG9uZW50LCByZXNvbHZlUHJvcHModGhpcywgYmFzZVByb3BzKSBhcyBQcm9wcyksXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiB0cnVzdGVkSHRtbChodG1sKTtcbiAgICAgIH1cbiAgICAgIC8vIENsaWVudDogbGV0IGNsaWVudEFjdGl2YXRlKCkgaGFuZGxlIFByZWFjdCBoeWRyYXRpb24vcmVuZGVyXG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgb3ZlcnJpZGUgY2xpZW50QWN0aXZhdGUoKTogdm9pZCB7XG4gICAgICBjb25zdCByb290ID0gdGhpcy5zaGFkb3dSb290ID8/IHRoaXMuYXR0YWNoU2hhZG93KHsgbW9kZTogJ29wZW4nIH0pO1xuICAgICAgY29uc3Qgdm5vZGUgPSBoKENvbXBvbmVudCwgcmVzb2x2ZVByb3BzKHRoaXMsIGJhc2VQcm9wcykgYXMgUHJvcHMpO1xuICAgICAgaWYgKG9wdGlvbnMuc3NyICE9PSBmYWxzZSkge1xuICAgICAgICAvLyBEU0QgaHlkcmF0aW9uOiB0aGUgc2hhZG93IERPTSBhbHJlYWR5IGhhcyBTU1ItcmVuZGVyZWQgY29udGVudFxuICAgICAgICBwcmVhY3RIeWRyYXRlKHZub2RlLCByb290KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIENTUjogZnVsbCByZW5kZXIgZnJvbSBzY3JhdGNoXG4gICAgICAgIHByZWFjdFJlbmRlcih2bm9kZSwgcm9vdCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgaWYgKHR5cGVvZiBjdXN0b21FbGVtZW50cyAhPT0gJ3VuZGVmaW5lZCcgJiYgIWN1c3RvbUVsZW1lbnRzLmdldCh0YWdOYW1lKSkge1xuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSh0YWdOYW1lLCBPcGVuRWxlbWVudFByZWFjdElzbGFuZCk7XG4gIH1cblxuICByZXR1cm4gT3BlbkVsZW1lbnRQcmVhY3RJc2xhbmQ7XG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Q0FVQyxHQUVELFNBQVMsV0FBVyxFQUFFLFdBQVcsUUFBb0IsdUJBQXVCO0FBQzVFLFNBQVMsV0FBVyxRQUFRLG9CQUFvQjtBQUNoRCxTQUE4QixDQUFDLEVBQUUsV0FBVyxhQUFhLEVBQUUsVUFBVSxZQUFZLFFBQVEsU0FBUztBQUNsRyxTQUFTLGNBQWMsUUFBUSwwQkFBMEI7QUFhekQsU0FBUyx1QkFBdUIsT0FBZTtFQUM3QyxJQUFJLENBQUMsZ0NBQWdDLElBQUksQ0FBQyxVQUFVO0lBQ2xELE1BQU0sSUFBSSxNQUNSLENBQUMsY0FBYyxFQUFFLFFBQVEsc0NBQXNDLENBQUMsR0FDOUQ7RUFFTjtBQUNGO0FBRUEsU0FBUyxrQkFBa0IsSUFBaUI7RUFDMUMsTUFBTSxRQUEyQixDQUFDO0VBQ2xDLE1BQU0sUUFBUSxLQUFLLFVBQVU7RUFDN0IsSUFBSSxDQUFDLE9BQU8sT0FBTztFQUNuQixLQUFLLE1BQU0sUUFBUSxNQUFNLElBQUksQ0FBQyxPQUFRO0lBQ3BDLElBQUksS0FBSyxJQUFJLEtBQUssa0JBQWtCO0lBQ3BDLEtBQUssQ0FBQyxLQUFLLElBQUksQ0FBQyxHQUFHLEtBQUssS0FBSztFQUMvQjtFQUNBLE9BQU87QUFDVDtBQUVBLFNBQVMsYUFBYSxJQUFpQixFQUFFLFNBQTRCO0VBQ25FLE9BQU87SUFDTCxHQUFHLFNBQVM7SUFDWixHQUFHLGtCQUFrQixLQUFLO0lBQzFCLEdBQUksWUFBWSxTQUFTLENBQUMsQ0FBQztFQUM3QjtBQUNGO0FBRUEsT0FBTyxTQUFTLG1CQUNkLE9BQWUsRUFDZixTQUF1QyxFQUN2QyxVQUErQixDQUFDLENBQUM7RUFFakMsdUJBQXVCO0VBQ3ZCLE1BQU0sWUFBWSxRQUFRLEtBQUssSUFBSSxDQUFDO0VBRXBDLE1BQU0sZ0NBQWdDO0lBQzNCLFNBQXVCO01BQzlCLElBQUksT0FBTyxhQUFhLGFBQWE7UUFDbkMsc0VBQXNFO1FBQ3RFLE1BQU0sT0FBTyxlQUNYLEVBQUUsV0FBVyxhQUFhLElBQUksRUFBRTtRQUVsQyxPQUFPLFlBQVk7TUFDckI7TUFDQSw4REFBOEQ7TUFDOUQsT0FBTztJQUNUO0lBRW1CLGlCQUF1QjtNQUN4QyxNQUFNLE9BQU8sSUFBSSxDQUFDLFVBQVUsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDO1FBQUUsTUFBTTtNQUFPO01BQ2pFLE1BQU0sUUFBUSxFQUFFLFdBQVcsYUFBYSxJQUFJLEVBQUU7TUFDOUMsSUFBSSxRQUFRLEdBQUcsS0FBSyxPQUFPO1FBQ3pCLGlFQUFpRTtRQUNqRSxjQUFjLE9BQU87TUFDdkIsT0FBTztRQUNMLGdDQUFnQztRQUNoQyxhQUFhLE9BQU87TUFDdEI7SUFDRjtFQUNGO0VBRUEsSUFBSSxPQUFPLG1CQUFtQixlQUFlLENBQUMsZUFBZSxHQUFHLENBQUMsVUFBVTtJQUN6RSxlQUFlLE1BQU0sQ0FBQyxTQUFTO0VBQ2pDO0VBRUEsT0FBTztBQUNUIn0=
package/src/vite.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @openelement/app/vite - Unified openElement Vite plugin entry.
3
+ *
4
+ * Kept separate from the route authoring API so application routes can import
5
+ * @openelement/app without loading Vite/build orchestration.
6
+ */ import type { Plugin } from 'vite';
7
+ import type { FrameworkOptions } from '@openelement/protocol/framework';
8
+ import type { OpenElementContentOptions } from '@openelement/content';
9
+ import type { OpenElementI18nOptions } from "./i18n-runtime.js";
10
+ /** Options for the openElement() unified Vite entry. */ export interface OpenElementOptions extends FrameworkOptions {
11
+ /** Content module options (blog + nav + sitemap). Omit to disable. */ content?: OpenElementContentOptions;
12
+ /** i18n module options. Omit to disable. */ i18n?: OpenElementI18nOptions;
13
+ }
14
+ export declare function openElement(options?: OpenElementOptions): Plugin[];
15
+ export default openElement;
package/src/vite.js ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @openelement/app/vite - Unified openElement Vite plugin entry.
3
+ *
4
+ * Kept separate from the route authoring API so application routes can import
5
+ * @openelement/app without loading Vite/build orchestration.
6
+ */ import { OpenElementBuildContext } from '@openelement/adapter-vite';
7
+ import { createOpenPlugin } from '@openelement/adapter-vite/plugin';
8
+ import { openContent } from '@openelement/content';
9
+ import { openI18n } from './i18n-plugin.js';
10
+ import { createLogger } from '@openelement/core/logger';
11
+ const log = createLogger('app');
12
+ export function openElement(options = {}) {
13
+ const { content: contentOpts, i18n: i18nOpts, ...coreOpts } = options;
14
+ const ctx = new OpenElementBuildContext({
15
+ ...coreOpts,
16
+ routesDir: coreOpts.routesDir || 'app/routes',
17
+ islandsDir: coreOpts.islandsDir || 'app/islands',
18
+ componentsDir: coreOpts.componentsDir || 'app/components'
19
+ });
20
+ const plugins = [
21
+ ...createOpenPlugin(coreOpts, ctx)
22
+ ];
23
+ if (i18nOpts) {
24
+ plugins.push(openI18n({
25
+ ...i18nOpts,
26
+ ctx
27
+ }));
28
+ log.info('i18n plugin loaded');
29
+ }
30
+ if (contentOpts) {
31
+ plugins.push(...openContent({
32
+ ...contentOpts,
33
+ ctx
34
+ }));
35
+ log.info('Content plugin loaded');
36
+ }
37
+ return plugins;
38
+ }
39
+ export default openElement;
40
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hcHAvc3JjL3ZpdGUudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAb3BlbmVsZW1lbnQvYXBwL3ZpdGUgLSBVbmlmaWVkIG9wZW5FbGVtZW50IFZpdGUgcGx1Z2luIGVudHJ5LlxuICpcbiAqIEtlcHQgc2VwYXJhdGUgZnJvbSB0aGUgcm91dGUgYXV0aG9yaW5nIEFQSSBzbyBhcHBsaWNhdGlvbiByb3V0ZXMgY2FuIGltcG9ydFxuICogQG9wZW5lbGVtZW50L2FwcCB3aXRob3V0IGxvYWRpbmcgVml0ZS9idWlsZCBvcmNoZXN0cmF0aW9uLlxuICovXG5cbmltcG9ydCB0eXBlIHsgUGx1Z2luIH0gZnJvbSAndml0ZSc7XG5pbXBvcnQgdHlwZSB7IEZyYW1ld29ya09wdGlvbnMgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvZnJhbWV3b3JrJztcbmltcG9ydCB0eXBlIHsgT3BlbkVsZW1lbnRDb250ZW50T3B0aW9ucyB9IGZyb20gJ0BvcGVuZWxlbWVudC9jb250ZW50JztcbmltcG9ydCB0eXBlIHsgT3BlbkVsZW1lbnRJMThuT3B0aW9ucyB9IGZyb20gJy4vaTE4bi1ydW50aW1lLmpzJztcblxuaW1wb3J0IHsgT3BlbkVsZW1lbnRCdWlsZENvbnRleHQgfSBmcm9tICdAb3BlbmVsZW1lbnQvYWRhcHRlci12aXRlJztcbmltcG9ydCB7IGNyZWF0ZU9wZW5QbHVnaW4gfSBmcm9tICdAb3BlbmVsZW1lbnQvYWRhcHRlci12aXRlL3BsdWdpbic7XG5pbXBvcnQgeyBvcGVuQ29udGVudCB9IGZyb20gJ0BvcGVuZWxlbWVudC9jb250ZW50JztcbmltcG9ydCB7IG9wZW5JMThuIH0gZnJvbSAnLi9pMThuLXBsdWdpbi5qcyc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZS9sb2dnZXInO1xuXG5jb25zdCBsb2cgPSBjcmVhdGVMb2dnZXIoJ2FwcCcpO1xuXG4vKiogT3B0aW9ucyBmb3IgdGhlIG9wZW5FbGVtZW50KCkgdW5pZmllZCBWaXRlIGVudHJ5LiAqL1xuZXhwb3J0IGludGVyZmFjZSBPcGVuRWxlbWVudE9wdGlvbnMgZXh0ZW5kcyBGcmFtZXdvcmtPcHRpb25zIHtcbiAgLyoqIENvbnRlbnQgbW9kdWxlIG9wdGlvbnMgKGJsb2cgKyBuYXYgKyBzaXRlbWFwKS4gT21pdCB0byBkaXNhYmxlLiAqL1xuICBjb250ZW50PzogT3BlbkVsZW1lbnRDb250ZW50T3B0aW9ucztcbiAgLyoqIGkxOG4gbW9kdWxlIG9wdGlvbnMuIE9taXQgdG8gZGlzYWJsZS4gKi9cbiAgaTE4bj86IE9wZW5FbGVtZW50STE4bk9wdGlvbnM7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBvcGVuRWxlbWVudChvcHRpb25zOiBPcGVuRWxlbWVudE9wdGlvbnMgPSB7fSk6IFBsdWdpbltdIHtcbiAgY29uc3QgeyBjb250ZW50OiBjb250ZW50T3B0cywgaTE4bjogaTE4bk9wdHMsIC4uLmNvcmVPcHRzIH0gPSBvcHRpb25zO1xuICBjb25zdCBjdHggPSBuZXcgT3BlbkVsZW1lbnRCdWlsZENvbnRleHQoe1xuICAgIC4uLmNvcmVPcHRzLFxuICAgIHJvdXRlc0RpcjogY29yZU9wdHMucm91dGVzRGlyIHx8ICdhcHAvcm91dGVzJyxcbiAgICBpc2xhbmRzRGlyOiBjb3JlT3B0cy5pc2xhbmRzRGlyIHx8ICdhcHAvaXNsYW5kcycsXG4gICAgY29tcG9uZW50c0RpcjogY29yZU9wdHMuY29tcG9uZW50c0RpciB8fCAnYXBwL2NvbXBvbmVudHMnLFxuICB9KTtcblxuICBjb25zdCBwbHVnaW5zOiBQbHVnaW5bXSA9IFsuLi5jcmVhdGVPcGVuUGx1Z2luKGNvcmVPcHRzLCBjdHgpXTtcblxuICBpZiAoaTE4bk9wdHMpIHtcbiAgICBwbHVnaW5zLnB1c2gob3BlbkkxOG4oeyAuLi5pMThuT3B0cywgY3R4IH0pKTtcbiAgICBsb2cuaW5mbygnaTE4biBwbHVnaW4gbG9hZGVkJyk7XG4gIH1cblxuICBpZiAoY29udGVudE9wdHMpIHtcbiAgICBwbHVnaW5zLnB1c2goLi4ub3BlbkNvbnRlbnQoeyAuLi5jb250ZW50T3B0cywgY3R4IH0pKTtcbiAgICBsb2cuaW5mbygnQ29udGVudCBwbHVnaW4gbG9hZGVkJyk7XG4gIH1cblxuICByZXR1cm4gcGx1Z2lucztcbn1cblxuZXhwb3J0IGRlZmF1bHQgb3BlbkVsZW1lbnQ7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0NBS0MsR0FPRCxTQUFTLHVCQUF1QixRQUFRLDRCQUE0QjtBQUNwRSxTQUFTLGdCQUFnQixRQUFRLG1DQUFtQztBQUNwRSxTQUFTLFdBQVcsUUFBUSx1QkFBdUI7QUFDbkQsU0FBUyxRQUFRLFFBQVEsbUJBQW1CO0FBQzVDLFNBQVMsWUFBWSxRQUFRLDJCQUEyQjtBQUV4RCxNQUFNLE1BQU0sYUFBYTtBQVV6QixPQUFPLFNBQVMsWUFBWSxVQUE4QixDQUFDLENBQUM7RUFDMUQsTUFBTSxFQUFFLFNBQVMsV0FBVyxFQUFFLE1BQU0sUUFBUSxFQUFFLEdBQUcsVUFBVSxHQUFHO0VBQzlELE1BQU0sTUFBTSxJQUFJLHdCQUF3QjtJQUN0QyxHQUFHLFFBQVE7SUFDWCxXQUFXLFNBQVMsU0FBUyxJQUFJO0lBQ2pDLFlBQVksU0FBUyxVQUFVLElBQUk7SUFDbkMsZUFBZSxTQUFTLGFBQWEsSUFBSTtFQUMzQztFQUVBLE1BQU0sVUFBb0I7T0FBSSxpQkFBaUIsVUFBVTtHQUFLO0VBRTlELElBQUksVUFBVTtJQUNaLFFBQVEsSUFBSSxDQUFDLFNBQVM7TUFBRSxHQUFHLFFBQVE7TUFBRTtJQUFJO0lBQ3pDLElBQUksSUFBSSxDQUFDO0VBQ1g7RUFFQSxJQUFJLGFBQWE7SUFDZixRQUFRLElBQUksSUFBSSxZQUFZO01BQUUsR0FBRyxXQUFXO01BQUU7SUFBSTtJQUNsRCxJQUFJLElBQUksQ0FBQztFQUNYO0VBRUEsT0FBTztBQUNUO0FBRUEsZUFBZSxZQUFZIn0=