@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 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,21 @@
1
+ # @openelement/ssg
2
+
3
+ Adapter-agnostic static site generation engine for openElement.
4
+
5
+ This package owns the build-time SSG pipeline:
6
+
7
+ - Route scanning and island metadata extraction.
8
+ - Entry descriptor construction and Hono server-entry code generation.
9
+ - Parallel/sequential static page rendering.
10
+ - HTML post-processing, island manifest generation, and SSR admission planning.
11
+
12
+ `@openelement/ssg` depends on `@openelement/core`, `hono`, and `typescript` — it has no Vite dependency. Generated server entries may import optional packages such as `@openelement/content`, `@openelement/router`, and `@openelement/app` when the consuming project uses those features. Build adapters such as `@openelement/adapter-vite` delegate SSG orchestration to this engine and provide only adapter-specific glue.
13
+
14
+ Most users should not import this package directly; use the framework entry
15
+ `@openelement/app` or the adapter CLI instead.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ npm install @openelement/ssg
21
+ ```
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@openelement/ssg",
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
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/open-element/openelement.git"
17
+ },
18
+ "keywords": [
19
+ "openelement",
20
+ "web-components",
21
+ "ssg",
22
+ "framework",
23
+ "deno"
24
+ ],
25
+ "dependencies": {
26
+ "@openelement/protocol": "^0.41.0-alpha.1",
27
+ "@openelement/core": "^0.41.0-alpha.1",
28
+ "@openelement/content": "^0.41.0-alpha.1"
29
+ }
30
+ }
@@ -0,0 +1,24 @@
1
+ import type { HydrationStrategy } from '@openelement/protocol/framework';
2
+ import type { IslandDecl } from '@openelement/protocol/ssg';
3
+ /** Narrow view of OpenElementBuildContext used by the SSG post-processor. */ export interface BuildContextView {
4
+ phase3: {
5
+ root: string;
6
+ outDir: string;
7
+ base: string;
8
+ upgradeStrategy: HydrationStrategy;
9
+ };
10
+ phase1: {
11
+ islandTagNames: string[];
12
+ packageIslandDecls: IslandDecl[];
13
+ islandMeta: Record<string, Partial<IslandDecl>>;
14
+ };
15
+ }
16
+ /**
17
+ * Inject the island client script and generate per-page island manifests.
18
+ * Must only run after Phase 2 (client island build) has completed.
19
+ */ export declare function postProcessClientIslandBuild(ctx: BuildContextView, scriptSrc: string): Promise<void>;
20
+ /**
21
+ * Clean Phase 1 SSR artifacts from the public dist directory.
22
+ * The SSR virtual entry bundle and its source map are build-time only
23
+ * and must not be deployed to static hosting.
24
+ */ export declare function cleanSsrArtifacts(ctx: BuildContextView): Promise<void>;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @openelement/ssg - Adapter-agnostic SSG build post-processing.
3
+ *
4
+ * Orchestrates client-script injection, island chunk/strategy/layer map
5
+ * construction, and SSR artifact cleanup. This module has zero Vite
6
+ * dependency and only reads/writes files.
7
+ */ import { join } from 'node:path';
8
+ import process from 'node:process';
9
+ import { readdir, unlink } from 'node:fs/promises';
10
+ import { createLogger } from '@openelement/core/logger';
11
+ import { buildIslandChunkMap, injectClientScript } from './postprocess.js';
12
+ import { generateIslandManifests, writeIslandManifests } from './island-manifest.js';
13
+ const log = createLogger('core');
14
+ /**
15
+ * Inject the island client script and generate per-page island manifests.
16
+ * Must only run after Phase 2 (client island build) has completed.
17
+ */ export async function postProcessClientIslandBuild(ctx, scriptSrc) {
18
+ const root = ctx.phase3.root || process.cwd();
19
+ const outDir = ctx.phase3.outDir || 'dist';
20
+ const base = ctx.phase3.base || '/';
21
+ const outputDir = join(root, outDir);
22
+ injectClientScript(outputDir, scriptSrc);
23
+ const islandTagNames = [
24
+ ...ctx.phase1.islandTagNames || [],
25
+ ...(ctx.phase1.packageIslandDecls || []).map((island)=>island.tagName)
26
+ ];
27
+ const chunkMap = buildIslandChunkMap(root, outDir, islandTagNames, base);
28
+ const strategyMap = Object.fromEntries([
29
+ ...Object.entries(ctx.phase1.islandMeta || {}).map(([tag, meta])=>[
30
+ tag,
31
+ meta.hydrate || ctx.phase3.upgradeStrategy || 'idle'
32
+ ]),
33
+ ...(ctx.phase1.packageIslandDecls || []).map((island)=>[
34
+ island.tagName,
35
+ island.hydrate || ctx.phase3.upgradeStrategy || 'idle'
36
+ ])
37
+ ]);
38
+ const layerMap = Object.fromEntries([
39
+ ...Object.entries(ctx.phase1.islandMeta || {}).map(([tag, meta])=>[
40
+ tag,
41
+ meta.hydrate === 'only' || meta.ssr === false ? 'pure-island' : 'dsd-interactive'
42
+ ]),
43
+ ...(ctx.phase1.packageIslandDecls || []).map((island)=>[
44
+ island.tagName,
45
+ island.hydrate === 'only' || island.ssr === false ? 'pure-island' : 'dsd-interactive'
46
+ ])
47
+ ]);
48
+ const pageManifests = generateIslandManifests(outputDir, chunkMap, strategyMap, layerMap);
49
+ await writeIslandManifests(outputDir, pageManifests);
50
+ }
51
+ /**
52
+ * Clean Phase 1 SSR artifacts from the public dist directory.
53
+ * The SSR virtual entry bundle and its source map are build-time only
54
+ * and must not be deployed to static hosting.
55
+ */ export async function cleanSsrArtifacts(ctx) {
56
+ const root = ctx.phase3.root || process.cwd();
57
+ const outDir = ctx.phase3.outDir || 'dist';
58
+ try {
59
+ const assetsDir = join(root, outDir, 'assets');
60
+ const entries = await readdir(assetsDir).catch(()=>[]);
61
+ const toDelete = entries.filter((f)=>f.startsWith('_virtual_open-hono-entry') || f.startsWith('src-') && f.endsWith('.js') && !f.includes('client'));
62
+ for (const f of toDelete){
63
+ const p = join(assetsDir, f);
64
+ await unlink(p).catch(()=>{});
65
+ log.info(`Cleaned SSR artifact: ${f}`);
66
+ }
67
+ if (toDelete.length > 0) {
68
+ log.info(`Removed ${toDelete.length} unreferenced SSR artifact(s) from dist/assets/`);
69
+ }
70
+ } catch {
71
+ // Non-critical - assets dir may not exist in some configs
72
+ }
73
+ }
74
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9zc2cvc3JjL2J1aWxkLXBvc3Rwcm9jZXNzLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQG9wZW5lbGVtZW50L3NzZyAtIEFkYXB0ZXItYWdub3N0aWMgU1NHIGJ1aWxkIHBvc3QtcHJvY2Vzc2luZy5cbiAqXG4gKiBPcmNoZXN0cmF0ZXMgY2xpZW50LXNjcmlwdCBpbmplY3Rpb24sIGlzbGFuZCBjaHVuay9zdHJhdGVneS9sYXllciBtYXBcbiAqIGNvbnN0cnVjdGlvbiwgYW5kIFNTUiBhcnRpZmFjdCBjbGVhbnVwLiBUaGlzIG1vZHVsZSBoYXMgemVybyBWaXRlXG4gKiBkZXBlbmRlbmN5IGFuZCBvbmx5IHJlYWRzL3dyaXRlcyBmaWxlcy5cbiAqL1xuXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCBwcm9jZXNzIGZyb20gJ25vZGU6cHJvY2Vzcyc7XG5pbXBvcnQgeyByZWFkZGlyLCB1bmxpbmsgfSBmcm9tICdub2RlOmZzL3Byb21pc2VzJztcbmltcG9ydCB0eXBlIHsgQ29tcG9uZW50TGF5ZXIsIEh5ZHJhdGlvblN0cmF0ZWd5IH0gZnJvbSAnQG9wZW5lbGVtZW50L3Byb3RvY29sL2ZyYW1ld29yayc7XG5pbXBvcnQgdHlwZSB7IElzbGFuZERlY2wgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvc3NnJztcbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciB9IGZyb20gJ0BvcGVuZWxlbWVudC9jb3JlL2xvZ2dlcic7XG5pbXBvcnQgeyBidWlsZElzbGFuZENodW5rTWFwLCBpbmplY3RDbGllbnRTY3JpcHQgfSBmcm9tICcuL3Bvc3Rwcm9jZXNzLmpzJztcbmltcG9ydCB7IGdlbmVyYXRlSXNsYW5kTWFuaWZlc3RzLCB3cml0ZUlzbGFuZE1hbmlmZXN0cyB9IGZyb20gJy4vaXNsYW5kLW1hbmlmZXN0LmpzJztcblxuY29uc3QgbG9nID0gY3JlYXRlTG9nZ2VyKCdjb3JlJyk7XG5cbi8qKiBOYXJyb3cgdmlldyBvZiBPcGVuRWxlbWVudEJ1aWxkQ29udGV4dCB1c2VkIGJ5IHRoZSBTU0cgcG9zdC1wcm9jZXNzb3IuICovXG5leHBvcnQgaW50ZXJmYWNlIEJ1aWxkQ29udGV4dFZpZXcge1xuICBwaGFzZTM6IHtcbiAgICByb290OiBzdHJpbmc7XG4gICAgb3V0RGlyOiBzdHJpbmc7XG4gICAgYmFzZTogc3RyaW5nO1xuICAgIHVwZ3JhZGVTdHJhdGVneTogSHlkcmF0aW9uU3RyYXRlZ3k7XG4gIH07XG4gIHBoYXNlMToge1xuICAgIGlzbGFuZFRhZ05hbWVzOiBzdHJpbmdbXTtcbiAgICBwYWNrYWdlSXNsYW5kRGVjbHM6IElzbGFuZERlY2xbXTtcbiAgICBpc2xhbmRNZXRhOiBSZWNvcmQ8c3RyaW5nLCBQYXJ0aWFsPElzbGFuZERlY2w+PjtcbiAgfTtcbn1cblxuLyoqXG4gKiBJbmplY3QgdGhlIGlzbGFuZCBjbGllbnQgc2NyaXB0IGFuZCBnZW5lcmF0ZSBwZXItcGFnZSBpc2xhbmQgbWFuaWZlc3RzLlxuICogTXVzdCBvbmx5IHJ1biBhZnRlciBQaGFzZSAyIChjbGllbnQgaXNsYW5kIGJ1aWxkKSBoYXMgY29tcGxldGVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcG9zdFByb2Nlc3NDbGllbnRJc2xhbmRCdWlsZChcbiAgY3R4OiBCdWlsZENvbnRleHRWaWV3LFxuICBzY3JpcHRTcmM6IHN0cmluZyxcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCByb290ID0gY3R4LnBoYXNlMy5yb290IHx8IHByb2Nlc3MuY3dkKCk7XG4gIGNvbnN0IG91dERpciA9IGN0eC5waGFzZTMub3V0RGlyIHx8ICdkaXN0JztcbiAgY29uc3QgYmFzZSA9IGN0eC5waGFzZTMuYmFzZSB8fCAnLyc7XG4gIGNvbnN0IG91dHB1dERpciA9IGpvaW4ocm9vdCwgb3V0RGlyKTtcblxuICBpbmplY3RDbGllbnRTY3JpcHQob3V0cHV0RGlyLCBzY3JpcHRTcmMpO1xuXG4gIGNvbnN0IGlzbGFuZFRhZ05hbWVzID0gW1xuICAgIC4uLihjdHgucGhhc2UxLmlzbGFuZFRhZ05hbWVzIHx8IFtdKSxcbiAgICAuLi4oY3R4LnBoYXNlMS5wYWNrYWdlSXNsYW5kRGVjbHMgfHwgW10pLm1hcCgoaXNsYW5kKSA9PiBpc2xhbmQudGFnTmFtZSksXG4gIF07XG5cbiAgY29uc3QgY2h1bmtNYXAgPSBidWlsZElzbGFuZENodW5rTWFwKHJvb3QsIG91dERpciwgaXNsYW5kVGFnTmFtZXMsIGJhc2UpO1xuXG4gIGNvbnN0IHN0cmF0ZWd5TWFwID0gT2JqZWN0LmZyb21FbnRyaWVzKFtcbiAgICAuLi5PYmplY3QuZW50cmllcyhjdHgucGhhc2UxLmlzbGFuZE1ldGEgfHwge30pLm1hcCgoW3RhZywgbWV0YV0pID0+IFtcbiAgICAgIHRhZyxcbiAgICAgIG1ldGEuaHlkcmF0ZSB8fCBjdHgucGhhc2UzLnVwZ3JhZGVTdHJhdGVneSB8fCAnaWRsZScsXG4gICAgXSksXG4gICAgLi4uKGN0eC5waGFzZTEucGFja2FnZUlzbGFuZERlY2xzIHx8IFtdKS5tYXAoKGlzbGFuZCkgPT4gW1xuICAgICAgaXNsYW5kLnRhZ05hbWUsXG4gICAgICBpc2xhbmQuaHlkcmF0ZSB8fCBjdHgucGhhc2UzLnVwZ3JhZGVTdHJhdGVneSB8fCAnaWRsZScsXG4gICAgXSksXG4gIF0pIGFzIFJlY29yZDxzdHJpbmcsIEh5ZHJhdGlvblN0cmF0ZWd5PjtcblxuICBjb25zdCBsYXllck1hcCA9IE9iamVjdC5mcm9tRW50cmllcyhbXG4gICAgLi4uT2JqZWN0LmVudHJpZXMoY3R4LnBoYXNlMS5pc2xhbmRNZXRhIHx8IHt9KS5tYXAoKFt0YWcsIG1ldGFdKSA9PiBbXG4gICAgICB0YWcsXG4gICAgICBtZXRhLmh5ZHJhdGUgPT09ICdvbmx5JyB8fCBtZXRhLnNzciA9PT0gZmFsc2UgPyAncHVyZS1pc2xhbmQnIDogJ2RzZC1pbnRlcmFjdGl2ZScsXG4gICAgXSksXG4gICAgLi4uKGN0eC5waGFzZTEucGFja2FnZUlzbGFuZERlY2xzIHx8IFtdKS5tYXAoKGlzbGFuZCkgPT4gW1xuICAgICAgaXNsYW5kLnRhZ05hbWUsXG4gICAgICBpc2xhbmQuaHlkcmF0ZSA9PT0gJ29ubHknIHx8IGlzbGFuZC5zc3IgPT09IGZhbHNlID8gJ3B1cmUtaXNsYW5kJyA6ICdkc2QtaW50ZXJhY3RpdmUnLFxuICAgIF0pLFxuICBdKSBhcyBSZWNvcmQ8c3RyaW5nLCBDb21wb25lbnRMYXllcj47XG5cbiAgY29uc3QgcGFnZU1hbmlmZXN0cyA9IGdlbmVyYXRlSXNsYW5kTWFuaWZlc3RzKG91dHB1dERpciwgY2h1bmtNYXAsIHN0cmF0ZWd5TWFwLCBsYXllck1hcCk7XG4gIGF3YWl0IHdyaXRlSXNsYW5kTWFuaWZlc3RzKG91dHB1dERpciwgcGFnZU1hbmlmZXN0cyk7XG59XG5cbi8qKlxuICogQ2xlYW4gUGhhc2UgMSBTU1IgYXJ0aWZhY3RzIGZyb20gdGhlIHB1YmxpYyBkaXN0IGRpcmVjdG9yeS5cbiAqIFRoZSBTU1IgdmlydHVhbCBlbnRyeSBidW5kbGUgYW5kIGl0cyBzb3VyY2UgbWFwIGFyZSBidWlsZC10aW1lIG9ubHlcbiAqIGFuZCBtdXN0IG5vdCBiZSBkZXBsb3llZCB0byBzdGF0aWMgaG9zdGluZy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFuU3NyQXJ0aWZhY3RzKGN0eDogQnVpbGRDb250ZXh0Vmlldyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCByb290ID0gY3R4LnBoYXNlMy5yb290IHx8IHByb2Nlc3MuY3dkKCk7XG4gIGNvbnN0IG91dERpciA9IGN0eC5waGFzZTMub3V0RGlyIHx8ICdkaXN0JztcblxuICB0cnkge1xuICAgIGNvbnN0IGFzc2V0c0RpciA9IGpvaW4ocm9vdCwgb3V0RGlyLCAnYXNzZXRzJyk7XG4gICAgY29uc3QgZW50cmllcyA9IGF3YWl0IHJlYWRkaXIoYXNzZXRzRGlyKS5jYXRjaCgoKSA9PiBbXSBhcyBzdHJpbmdbXSk7XG4gICAgY29uc3QgdG9EZWxldGUgPSBlbnRyaWVzLmZpbHRlcihcbiAgICAgIChmKSA9PlxuICAgICAgICBmLnN0YXJ0c1dpdGgoJ192aXJ0dWFsX29wZW4taG9uby1lbnRyeScpIHx8XG4gICAgICAgIChmLnN0YXJ0c1dpdGgoJ3NyYy0nKSAmJiBmLmVuZHNXaXRoKCcuanMnKSAmJiAhZi5pbmNsdWRlcygnY2xpZW50JykpLFxuICAgICk7XG4gICAgZm9yIChjb25zdCBmIG9mIHRvRGVsZXRlKSB7XG4gICAgICBjb25zdCBwID0gam9pbihhc3NldHNEaXIsIGYpO1xuICAgICAgYXdhaXQgdW5saW5rKHApLmNhdGNoKCgpID0+IHt9KTtcbiAgICAgIGxvZy5pbmZvKGBDbGVhbmVkIFNTUiBhcnRpZmFjdDogJHtmfWApO1xuICAgIH1cbiAgICBpZiAodG9EZWxldGUubGVuZ3RoID4gMCkge1xuICAgICAgbG9nLmluZm8oYFJlbW92ZWQgJHt0b0RlbGV0ZS5sZW5ndGh9IHVucmVmZXJlbmNlZCBTU1IgYXJ0aWZhY3QocykgZnJvbSBkaXN0L2Fzc2V0cy9gKTtcbiAgICB9XG4gIH0gY2F0Y2gge1xuICAgIC8vIE5vbi1jcml0aWNhbCAtIGFzc2V0cyBkaXIgbWF5IG5vdCBleGlzdCBpbiBzb21lIGNvbmZpZ3NcbiAgfVxufVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Q0FNQyxHQUVELFNBQVMsSUFBSSxRQUFRLFlBQVk7QUFDakMsT0FBTyxhQUFhLGVBQWU7QUFDbkMsU0FBUyxPQUFPLEVBQUUsTUFBTSxRQUFRLG1CQUFtQjtBQUduRCxTQUFTLFlBQVksUUFBUSwyQkFBMkI7QUFDeEQsU0FBUyxtQkFBbUIsRUFBRSxrQkFBa0IsUUFBUSxtQkFBbUI7QUFDM0UsU0FBUyx1QkFBdUIsRUFBRSxvQkFBb0IsUUFBUSx1QkFBdUI7QUFFckYsTUFBTSxNQUFNLGFBQWE7QUFpQnpCOzs7Q0FHQyxHQUNELE9BQU8sZUFBZSw2QkFDcEIsR0FBcUIsRUFDckIsU0FBaUI7RUFFakIsTUFBTSxPQUFP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
@@ -0,0 +1,226 @@
1
+ import { isValidTagName } from '@openelement/core';
2
+ import { formatError } from '@openelement/core/errors';
3
+ function isCemCustomElement(declaration) {
4
+ return declaration.kind === 'custom-element';
5
+ }
6
+ function isRecord(value) {
7
+ return !!value && typeof value === 'object' && !Array.isArray(value);
8
+ }
9
+ function toCemBaseArray(value) {
10
+ if (!Array.isArray(value)) return undefined;
11
+ return value.filter(isRecord);
12
+ }
13
+ function toCemExports(value) {
14
+ if (!Array.isArray(value)) return undefined;
15
+ return value.filter(isRecord).map((entry)=>({
16
+ declaration: entry.declaration
17
+ }));
18
+ }
19
+ function toCemModule(value) {
20
+ if (!isRecord(value)) return undefined;
21
+ return {
22
+ kind: typeof value.kind === 'string' ? value.kind : undefined,
23
+ path: typeof value.path === 'string' ? value.path : '',
24
+ declarations: toCemBaseArray(value.declarations),
25
+ exports: toCemExports(value.exports)
26
+ };
27
+ }
28
+ export function parseCem(json) {
29
+ const errors = [];
30
+ const warnings = [];
31
+ let parsed;
32
+ try {
33
+ parsed = JSON.parse(json);
34
+ } catch (error) {
35
+ errors.push({
36
+ code: 'CEM_PARSE_ERROR',
37
+ message: `Invalid JSON: ${formatError(error)}`
38
+ });
39
+ return {
40
+ success: false,
41
+ errors,
42
+ warnings
43
+ };
44
+ }
45
+ if (!isRecord(parsed)) {
46
+ errors.push({
47
+ code: 'CEM_INVALID_ROOT',
48
+ message: 'Manifest root must be an object'
49
+ });
50
+ return {
51
+ success: false,
52
+ errors,
53
+ warnings
54
+ };
55
+ }
56
+ const root = parsed;
57
+ if (!root.schemaVersion) {
58
+ warnings.push({
59
+ code: 'CEM_NO_SCHEMA_VERSION',
60
+ message: 'Missing schemaVersion field, assuming legacy format'
61
+ });
62
+ }
63
+ if (!Array.isArray(root.modules)) {
64
+ errors.push({
65
+ code: 'CEM_NO_MODULES',
66
+ message: 'Manifest must have a modules array'
67
+ });
68
+ return {
69
+ success: false,
70
+ errors,
71
+ warnings
72
+ };
73
+ }
74
+ const manifest = {
75
+ ...root,
76
+ schemaVersion: typeof root.schemaVersion === 'string' ? root.schemaVersion : undefined,
77
+ packageName: typeof root.packageName === 'string' ? root.packageName : undefined,
78
+ version: typeof root.version === 'string' ? root.version : undefined,
79
+ modules: root.modules.map((module)=>toCemModule(module) ?? {
80
+ path: ''
81
+ })
82
+ };
83
+ const seenTagNames = new Set();
84
+ for(let i = 0; i < manifest.modules.length; i++){
85
+ const module = manifest.modules[i];
86
+ const modulePath = `modules[${i}]`;
87
+ if (!module.kind) {
88
+ warnings.push({
89
+ code: 'CEM_MODULE_NO_KIND',
90
+ message: `Module at index ${i} has no kind field`,
91
+ path: modulePath
92
+ });
93
+ }
94
+ if (!module.path) {
95
+ errors.push({
96
+ code: 'CEM_MODULE_NO_PATH',
97
+ message: `Module at index ${i} has no path field`,
98
+ path: modulePath
99
+ });
100
+ }
101
+ for (const [j, declaration] of (module.declarations ?? []).entries()){
102
+ if (!isCemCustomElement(declaration)) continue;
103
+ const declarationPath = `${modulePath}.declarations[${j}]`;
104
+ const tagName = declaration.tagName;
105
+ if (!tagName) {
106
+ errors.push({
107
+ code: 'CEM_CE_NO_TAG_NAME',
108
+ message: `Custom element at ${declarationPath} has no tagName`,
109
+ path: declarationPath
110
+ });
111
+ } else if (!isValidTagName(tagName)) {
112
+ errors.push({
113
+ code: 'CEM_CE_INVALID_TAG_NAME',
114
+ message: `Invalid tag name: ${tagName} (must contain a hyphen and match HTML spec)`,
115
+ path: declarationPath
116
+ });
117
+ } else if (seenTagNames.has(tagName)) {
118
+ errors.push({
119
+ code: 'CEM_CE_DUPLICATE_TAG',
120
+ message: `Duplicate tag name: ${tagName}`,
121
+ path: declarationPath
122
+ });
123
+ } else {
124
+ seenTagNames.add(tagName);
125
+ }
126
+ }
127
+ for (const [j, exported] of (module.exports ?? []).entries()){
128
+ if (!exported.declaration) {
129
+ errors.push({
130
+ code: 'CEM_EXPORT_NO_DECLARATION',
131
+ message: `Export at ${modulePath}.exports[${j}] has no declaration reference`,
132
+ path: `${modulePath}.exports[${j}]`
133
+ });
134
+ }
135
+ }
136
+ }
137
+ return {
138
+ success: errors.length === 0,
139
+ manifest: errors.length === 0 ? manifest : undefined,
140
+ errors,
141
+ warnings
142
+ };
143
+ }
144
+ export function classifyCemManifest(manifest) {
145
+ const classifications = [];
146
+ const seenTags = new Map();
147
+ for (const module of manifest.modules){
148
+ for (const declaration of module.declarations ?? []){
149
+ if (!isCemCustomElement(declaration) || !declaration.tagName) continue;
150
+ if (seenTags.has(declaration.tagName)) {
151
+ classifications.push({
152
+ tagName: declaration.tagName,
153
+ tier: 'rejected',
154
+ reason: `Duplicate tag name: ${declaration.tagName}`,
155
+ source: 'package',
156
+ modulePath: module.path,
157
+ ssr: false,
158
+ dsd: false,
159
+ hydrate: 'idle'
160
+ });
161
+ continue;
162
+ }
163
+ const classification = classifyCemDeclaration(manifest, module, declaration);
164
+ classifications.push(classification);
165
+ seenTags.set(declaration.tagName, classification);
166
+ }
167
+ }
168
+ const rejectedTags = [];
169
+ const ssrCapableTags = [];
170
+ const clientOnlyTags = [];
171
+ const experimentalDomTags = [];
172
+ // ponytail: bucket lookup replaces 4-case switch
173
+ const buckets = {
174
+ rejected: rejectedTags,
175
+ 'ssr-capable': ssrCapableTags,
176
+ 'client-only': clientOnlyTags,
177
+ 'experimental-dom': experimentalDomTags
178
+ };
179
+ for (const classification of classifications){
180
+ buckets[classification.tier]?.push(classification.tagName);
181
+ }
182
+ return {
183
+ classifications,
184
+ rejectedTags,
185
+ ssrCapableTags,
186
+ clientOnlyTags,
187
+ experimentalDomTags,
188
+ stats: {
189
+ totalComponents: classifications.length,
190
+ ssrCapableCount: ssrCapableTags.length,
191
+ clientOnlyCount: clientOnlyTags.length,
192
+ rejectedCount: rejectedTags.length,
193
+ experimentalDomCount: experimentalDomTags.length
194
+ }
195
+ };
196
+ }
197
+ function classifyCemDeclaration(manifest, module, declaration) {
198
+ const openElement = declaration.openElement;
199
+ const ssr = openElement?.ssr ?? false;
200
+ const dsd = openElement?.dsd ?? false;
201
+ const hydrate = openElement?.hydrate ?? 'idle';
202
+ let tier = 'client-only';
203
+ let reason = manifest.packageName ? `CEM-only package ${manifest.packageName} (no openElement SSR declaration)` : 'CEM-only package (no openElement SSR declaration)';
204
+ if (openElement?.ssr === true) {
205
+ if (openElement.layer) {
206
+ tier = 'ssr-capable';
207
+ reason = `ssr: true with layer: ${openElement.layer}`;
208
+ } else {
209
+ tier = 'client-only';
210
+ reason = 'ssr: true but no adapter/layer declared';
211
+ }
212
+ } else if (openElement?.ssr === false) {
213
+ reason = 'ssr: false (explicit client-only)';
214
+ }
215
+ return {
216
+ tagName: declaration.tagName,
217
+ tier,
218
+ reason,
219
+ source: 'package',
220
+ modulePath: module.path,
221
+ ssr,
222
+ dsd,
223
+ hydrate
224
+ };
225
+ }
226
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9zc2cvc3JjL2NlbS1jb21wYXQudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUge1xuICBDb21wYXRpYmlsaXR5Q2xhc3NpZmljYXRpb24sXG4gIENvbXBhdGliaWxpdHlUaWVyLFxuICBIeWRyYXRpb25TdHJhdGVneSxcbn0gZnJvbSAnQG9wZW5lbGVtZW50L3Byb3RvY29sL2ZyYW1ld29yayc7XG5pbXBvcnQgeyBpc1ZhbGlkVGFnTmFtZSB9IGZyb20gJ0BvcGVuZWxlbWVudC9jb3JlJztcbmltcG9ydCB7IGZvcm1hdEVycm9yIH0gZnJvbSAnQG9wZW5lbGVtZW50L2NvcmUvZXJyb3JzJztcblxuZXhwb3J0IGludGVyZmFjZSBPcGVuRWxlbWVudEV4dGVuc2lvbnMge1xuICBzc3I/OiBib29sZWFuO1xuICBkc2Q/OiBib29sZWFuO1xuICBsYXllcj86IHN0cmluZztcbiAgaHlkcmF0ZT86IEh5ZHJhdGlvblN0cmF0ZWd5O1xuICBtb2R1bGU/OiBzdHJpbmc7XG4gIGV4cG9ydD86IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIENlbUJhc2Uge1xuICBraW5kPzogc3RyaW5nO1xuICBba2V5OiBzdHJpbmddOiB1bmtub3duO1xufVxuXG5pbnRlcmZhY2UgQ2VtQ3VzdG9tRWxlbWVudCBleHRlbmRzIENlbUJhc2Uge1xuICBraW5kOiAnY3VzdG9tLWVsZW1lbnQnO1xuICB0YWdOYW1lPzogc3RyaW5nO1xuICBzdXBlckNsYXNzPzogeyBuYW1lPzogc3RyaW5nIH07XG4gIG9wZW5FbGVtZW50PzogT3BlbkVsZW1lbnRFeHRlbnNpb25zO1xufVxuXG5pbnRlcmZhY2UgQ2VtTW9kdWxlIHtcbiAga2luZD86IHN0cmluZztcbiAgcGF0aDogc3RyaW5nO1xuICBkZWNsYXJhdGlvbnM/OiBDZW1CYXNlW107XG4gIGV4cG9ydHM/OiB7IGRlY2xhcmF0aW9uPzogdW5rbm93biB9W107XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ3VzdG9tRWxlbWVudHNNYW5pZmVzdCB7XG4gIHNjaGVtYVZlcnNpb24/OiBzdHJpbmc7XG4gIHBhY2thZ2VOYW1lPzogc3RyaW5nO1xuICB2ZXJzaW9uPzogc3RyaW5nO1xuICBtb2R1bGVzOiBDZW1Nb2R1bGVbXTtcbiAgW2tleTogc3RyaW5nXTogdW5rbm93bjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDZW1QYXJzZUVycm9yIHtcbiAgY29kZTogc3RyaW5nO1xuICBtZXNzYWdlOiBzdHJpbmc7XG4gIHBhdGg/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ2VtUGFyc2VXYXJuaW5nIHtcbiAgY29kZTogc3RyaW5nO1xuICBtZXNzYWdlOiBzdHJpbmc7XG4gIHBhdGg/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ2VtUGFyc2VSZXN1bHQge1xuICBzdWNjZXNzOiBib29sZWFuO1xuICBtYW5pZmVzdD86IEN1c3RvbUVsZW1lbnRzTWFuaWZlc3Q7XG4gIGVycm9yczogQ2VtUGFyc2VFcnJvcltdO1xuICB3YXJuaW5nczogQ2VtUGFyc2VXYXJuaW5nW107XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ2VtQ2xhc3NpZmljYXRpb25SZXN1bHQge1xuICBjbGFzc2lmaWNhdGlvbnM6IENvbXBhdGliaWxpdHlDbGFzc2lmaWNhdGlvbltdO1xuICByZWplY3RlZFRhZ3M6IHN0cmluZ1tdO1xuICBzc3JDYXBhYmxlVGFnczogc3RyaW5nW107XG4gIGNsaWVudE9ubHlUYWdzOiBzdHJpbmdbXTtcbiAgZXhwZXJpbWVudGFsRG9tVGFnczogc3RyaW5nW107XG4gIHN0YXRzOiB7XG4gICAgdG90YWxDb21wb25lbnRzOiBudW1iZXI7XG4gICAgc3NyQ2FwYWJsZUNvdW50OiBudW1iZXI7XG4gICAgY2xpZW50T25seUNvdW50OiBudW1iZXI7XG4gICAgcmVqZWN0ZWRDb3VudDogbnVtYmVyO1xuICAgIGV4cGVyaW1lbnRhbERvbUNvdW50OiBudW1iZXI7XG4gIH07XG59XG5cbmZ1bmN0aW9uIGlzQ2VtQ3VzdG9tRWxlbWVudChkZWNsYXJhdGlvbjogQ2VtQmFzZSk6IGRlY2xhcmF0aW9uIGlzIENlbUN1c3RvbUVsZW1lbnQge1xuICByZXR1cm4gZGVjbGFyYXRpb24ua2luZCA9PT0gJ2N1c3RvbS1lbGVtZW50Jztcbn1cblxuZnVuY3Rpb24gaXNSZWNvcmQodmFsdWU6IHVua25vd24pOiB2YWx1ZSBpcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIHJldHVybiAhIXZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkodmFsdWUpO1xufVxuXG5mdW5jdGlvbiB0b0NlbUJhc2VBcnJheSh2YWx1ZTogdW5rbm93bik6IENlbUJhc2VbXSB8IHVuZGVmaW5lZCB7XG4gIGlmICghQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHJldHVybiB1bmRlZmluZWQ7XG4gIHJldHVybiB2YWx1ZS5maWx0ZXIoaXNSZWNvcmQpO1xufVxuXG5mdW5jdGlvbiB0b0NlbUV4cG9ydHModmFsdWU6IHVua25vd24pOiB7IGRlY2xhcmF0aW9uPzogdW5rbm93biB9W10gfCB1bmRlZmluZWQge1xuICBpZiAoIUFycmF5LmlzQXJyYXkodmFsdWUpKSByZXR1cm4gdW5kZWZpbmVkO1xuICByZXR1cm4gdmFsdWUuZmlsdGVyKGlzUmVjb3JkKS5tYXAoKGVudHJ5KSA9PiAoeyBkZWNsYXJhdGlvbjogZW50cnkuZGVjbGFyYXRpb24gfSkpO1xufVxuXG5mdW5jdGlvbiB0b0NlbU1vZHVsZSh2YWx1ZTogdW5rbm93bik6IENlbU1vZHVsZSB8IHVuZGVmaW5lZCB7XG4gIGlmICghaXNSZWNvcmQodmFsdWUpKSByZXR1cm4gdW5kZWZpbmVkO1xuICByZXR1cm4ge1xuICAgIGtpbmQ6IHR5cGVvZiB2YWx1ZS5raW5kID09PSAnc3RyaW5nJyA/IHZhbHVlLmtpbmQgOiB1bmRlZmluZWQsXG4gICAgcGF0aDogdHlwZW9mIHZhbHVlLnBhdGggPT09ICdzdHJpbmcnID8gdmFsdWUucGF0aCA6ICcnLFxuICAgIGRlY2xhcmF0aW9uczogdG9DZW1CYXNlQXJyYXkodmFsdWUuZGVjbGFyYXRpb25zKSxcbiAgICBleHBvcnRzOiB0b0NlbUV4cG9ydHModmFsdWUuZXhwb3J0cyksXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZUNlbShqc29uOiBzdHJpbmcpOiBDZW1QYXJzZVJlc3VsdCB7XG4gIGNvbnN0IGVycm9yczogQ2VtUGFyc2VFcnJvcltdID0gW107XG4gIGNvbnN0IHdhcm5pbmdzOiBDZW1QYXJzZVdhcm5pbmdbXSA9IFtdO1xuXG4gIGxldCBwYXJzZWQ6IHVua25vd247XG4gIHRyeSB7XG4gICAgcGFyc2VkID0gSlNPTi5wYXJzZShqc29uKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBlcnJvcnMucHVzaCh7XG4gICAgICBjb2RlOiAnQ0VNX1BBUlNFX0VSUk9SJyxcbiAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIEpTT046ICR7Zm9ybWF0RXJyb3IoZXJyb3IpfWAsXG4gICAgfSk7XG4gICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9ycywgd2FybmluZ3MgfTtcbiAgfVxuXG4gIGlmICghaXNSZWNvcmQocGFyc2VkKSkge1xuICAgIGVycm9ycy5wdXNoKHsgY29kZTogJ0NFTV9JTlZBTElEX1JPT1QnLCBtZXNzYWdlOiAnTWFuaWZlc3Qgcm9vdCBtdXN0IGJlIGFuIG9iamVjdCcgfSk7XG4gICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9ycywgd2FybmluZ3MgfTtcbiAgfVxuXG4gIGNvbnN0IHJvb3QgPSBwYXJzZWQ7XG4gIGlmICghcm9vdC5zY2hlbWFWZXJzaW9uKSB7XG4gICAgd2FybmluZ3MucHVzaCh7XG4gICAgICBjb2RlOiAnQ0VNX05PX1NDSEVNQV9WRVJTSU9OJyxcbiAgICAgIG1lc3NhZ2U6ICdNaXNzaW5nIHNjaGVtYVZlcnNpb24gZmllbGQsIGFzc3VtaW5nIGxlZ2FjeSBmb3JtYXQnLFxuICAgIH0pO1xuICB9XG4gIGlmICghQXJyYXkuaXNBcnJheShyb290Lm1vZHVsZXMpKSB7XG4gICAgZXJyb3JzLnB1c2goeyBjb2RlOiAnQ0VNX05PX01PRFVMRVMnLCBtZXNzYWdlOiAnTWFuaWZlc3QgbXVzdCBoYXZlIGEgbW9kdWxlcyBhcnJheScgfSk7XG4gICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9ycywgd2FybmluZ3MgfTtcbiAgfVxuXG4gIGNvbnN0IG1hbmlmZXN0OiBDdXN0b21FbGVtZW50c01hbmlmZXN0ID0ge1xuICAgIC4uLnJvb3QsXG4gICAgc2NoZW1hVmVyc2lvbjogdHlwZW9mIHJvb3Quc2NoZW1hVmVyc2lvbiA9PT0gJ3N0cmluZycgPyByb290LnNjaGVtYVZlcnNpb24gOiB1bmRlZmluZWQsXG4gICAgcGFja2FnZU5hbWU6IHR5cGVvZiByb290LnBhY2thZ2VOYW1lID09PSAnc3RyaW5nJyA/IHJvb3QucGFja2FnZU5hbWUgOiB1bmRlZmluZWQsXG4gICAgdmVyc2lvbjogdHlwZW9mIHJvb3QudmVyc2lvbiA9PT0gJ3N0cmluZycgPyByb290LnZlcnNpb24gOiB1bmRlZmluZWQsXG4gICAgbW9kdWxlczogcm9vdC5tb2R1bGVzLm1hcCgobW9kdWxlKSA9PlxuICAgICAgdG9DZW1Nb2R1bGUobW9kdWxlKSA/PyB7XG4gICAgICAgIHBhdGg6ICcnLFxuICAgICAgfVxuICAgICksXG4gIH07XG4gIGNvbnN0IHNlZW5UYWdOYW1lcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbWFuaWZlc3QubW9kdWxlcy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IG1vZHVsZSA9IG1hbmlmZXN0Lm1vZHVsZXNbaV07XG4gICAgY29uc3QgbW9kdWxlUGF0aCA9IGBtb2R1bGVzWyR7aX1dYDtcblxuICAgIGlmICghbW9kdWxlLmtpbmQpIHtcbiAgICAgIHdhcm5pbmdzLnB1c2goe1xuICAgICAgICBjb2RlOiAnQ0VNX01PRFVMRV9OT19LSU5EJyxcbiAgICAgICAgbWVzc2FnZTogYE1vZHVsZSBhdCBpbmRleCAke2l9IGhhcyBubyBraW5kIGZpZWxkYCxcbiAgICAgICAgcGF0aDogbW9kdWxlUGF0aCxcbiAgICAgIH0pO1xuICAgIH1cbiAgICBpZiAoIW1vZHVsZS5wYXRoKSB7XG4gICAgICBlcnJvcnMucHVzaCh7XG4gICAgICAgIGNvZGU6ICdDRU1fTU9EVUxFX05PX1BBVEgnLFxuICAgICAgICBtZXNzYWdlOiBgTW9kdWxlIGF0IGluZGV4ICR7aX0gaGFzIG5vIHBhdGggZmllbGRgLFxuICAgICAgICBwYXRoOiBtb2R1bGVQYXRoLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgZm9yIChjb25zdCBbaiwgZGVjbGFyYXRpb25dIG9mIChtb2R1bGUuZGVjbGFyYXRpb25zID8/IFtdKS5lbnRyaWVzKCkpIHtcbiAgICAgIGlmICghaXNDZW1DdXN0b21FbGVtZW50KGRlY2xhcmF0aW9uKSkgY29udGludWU7XG4gICAgICBjb25zdCBkZWNsYXJhdGlvblBhdGggPSBgJHttb2R1bGVQYXRofS5kZWNsYXJhdGlvbnNbJHtqfV1gO1xuICAgICAgY29uc3QgdGFnTmFtZSA9IGRlY2xhcmF0aW9uLnRhZ05hbWU7XG4gICAgICBpZiAoIXRhZ05hbWUpIHtcbiAgICAgICAgZXJyb3JzLnB1c2goe1xuICAgICAgICAgIGNvZGU6ICdDRU1fQ0VfTk9fVEFHX05BTUUnLFxuICAgICAgICAgIG1lc3NhZ2U6IGBDdXN0b20gZWxlbWVudCBhdCAke2RlY2xhcmF0aW9uUGF0aH0gaGFzIG5vIHRhZ05hbWVgLFxuICAgICAgICAgIHBhdGg6IGRlY2xhcmF0aW9uUGF0aCxcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2UgaWYgKCFpc1ZhbGlkVGFnTmFtZSh0YWdOYW1lKSkge1xuICAgICAgICBlcnJvcnMucHVzaCh7XG4gICAgICAgICAgY29kZTogJ0NFTV9DRV9JTlZBTElEX1RBR19OQU1FJyxcbiAgICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB0YWcgbmFtZTogJHt0YWdOYW1lfSAobXVzdCBjb250YWluIGEgaHlwaGVuIGFuZCBtYXRjaCBIVE1MIHNwZWMpYCxcbiAgICAgICAgICBwYXRoOiBkZWNsYXJhdGlvblBhdGgsXG4gICAgICAgIH0pO1xuICAgICAgfSBlbHNlIGlmIChzZWVuVGFnTmFtZXMuaGFzKHRhZ05hbWUpKSB7XG4gICAgICAgIGVycm9ycy5wdXNoKHtcbiAgICAgICAgICBjb2RlOiAnQ0VNX0NFX0RVUExJQ0FURV9UQUcnLFxuICAgICAgICAgIG1lc3NhZ2U6IGBEdXBsaWNhdGUgdGFnIG5hbWU6ICR7dGFnTmFtZX1gLFxuICAgICAgICAgIHBhdGg6IGRlY2xhcmF0aW9uUGF0aCxcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzZWVuVGFnTmFtZXMuYWRkKHRhZ05hbWUpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGZvciAoY29uc3QgW2osIGV4cG9ydGVkXSBvZiAobW9kdWxlLmV4cG9ydHMgPz8gW10pLmVudHJpZXMoKSkge1xuICAgICAgaWYgKCFleHBvcnRlZC5kZWNsYXJhdGlvbikge1xuICAgICAgICBlcnJvcnMucHVzaCh7XG4gICAgICAgICAgY29kZTogJ0NFTV9FWFBPUlRfTk9fREVDTEFSQVRJT04nLFxuICAgICAgICAgIG1lc3NhZ2U6IGBFeHBvcnQgYXQgJHttb2R1bGVQYXRofS5leHBvcnRzWyR7an1dIGhhcyBubyBkZWNsYXJhdGlvbiByZWZlcmVuY2VgLFxuICAgICAgICAgIHBhdGg6IGAke21vZHVsZVBhdGh9LmV4cG9ydHNbJHtqfV1gLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4ge1xuICAgIHN1Y2Nlc3M6IGVycm9ycy5sZW5ndGggPT09IDAsXG4gICAgbWFuaWZlc3Q6IGVycm9ycy5sZW5ndGggPT09IDAgPyBtYW5pZmVzdCA6IHVuZGVmaW5lZCxcbiAgICBlcnJvcnMsXG4gICAgd2FybmluZ3MsXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjbGFzc2lmeUNlbU1hbmlmZXN0KG1hbmlmZXN0OiBDdXN0b21FbGVtZW50c01hbmlmZXN0KTogQ2VtQ2xhc3NpZmljYXRpb25SZXN1bHQge1xuICBjb25zdCBjbGFzc2lmaWNhdGlvbnM6IENvbXBhdGliaWxpdHlDbGFzc2lmaWNhdGlvbltdID0gW107XG4gIGNvbnN0IHNlZW5UYWdzID0gbmV3IE1hcDxzdHJpbmcsIENvbXBhdGliaWxpdHlDbGFzc2lmaWNhdGlvbj4oKTtcblxuICBmb3IgKGNvbnN0IG1vZHVsZSBvZiBtYW5pZmVzdC5tb2R1bGVzKSB7XG4gICAgZm9yIChjb25zdCBkZWNsYXJhdGlvbiBvZiBtb2R1bGUuZGVjbGFyYXRpb25zID8/IFtdKSB7XG4gICAgICBpZiAoIWlzQ2VtQ3VzdG9tRWxlbWVudChkZWNsYXJhdGlvbikgfHwgIWRlY2xhcmF0aW9uLnRhZ05hbWUpIGNvbnRpbnVlO1xuXG4gICAgICBpZiAoc2VlblRhZ3MuaGFzKGRlY2xhcmF0aW9uLnRhZ05hbWUpKSB7XG4gICAgICAgIGNsYXNzaWZpY2F0aW9ucy5wdXNoKHtcbiAgICAgICAgICB0YWdOYW1lOiBkZWNsYXJhdGlvbi50YWdOYW1lLFxuICAgICAgICAgIHRpZXI6ICdyZWplY3RlZCcsXG4gICAgICAgICAgcmVhc29uOiBgRHVwbGljYXRlIHRhZyBuYW1lOiAke2RlY2xhcmF0aW9uLnRhZ05hbWV9YCxcbiAgICAgICAgICBzb3VyY2U6ICdwYWNrYWdlJyxcbiAgICAgICAgICBtb2R1bGVQYXRoOiBtb2R1bGUucGF0aCxcbiAgICAgICAgICBzc3I6IGZhbHNlLFxuICAgICAgICAgIGRzZDogZmFsc2UsXG4gICAgICAgICAgaHlkcmF0ZTogJ2lkbGUnLFxuICAgICAgICB9KTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGNsYXNzaWZpY2F0aW9uID0gY2xhc3NpZnlDZW1EZWNsYXJhdGlvbihtYW5pZmVzdCwgbW9kdWxlLCBkZWNsYXJhdGlvbik7XG4gICAgICBjbGFzc2lmaWNhdGlvbnMucHVzaChjbGFzc2lmaWNhdGlvbik7XG4gICAgICBzZWVuVGFncy5zZXQoZGVjbGFyYXRpb24udGFnTmFtZSwgY2xhc3NpZmljYXRpb24pO1xuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHJlamVjdGVkVGFnczogc3RyaW5nW10gPSBbXTtcbiAgY29uc3Qgc3NyQ2FwYWJsZVRhZ3M6IHN0cmluZ1tdID0gW107XG4gIGNvbnN0IGNsaWVudE9ubHlUYWdzOiBzdHJpbmdbXSA9IFtdO1xuICBjb25zdCBleHBlcmltZW50YWxEb21UYWdzOiBzdHJpbmdbXSA9IFtdO1xuXG4gIC8vIHBvbnl0YWlsOiBidWNrZXQgbG9va3VwIHJlcGxhY2VzIDQtY2FzZSBzd2l0Y2hcbiAgY29uc3QgYnVja2V0czogUmVjb3JkPHN0cmluZywgc3RyaW5nW10+ID0ge1xuICAgIHJlamVjdGVkOiByZWplY3RlZFRhZ3MsXG4gICAgJ3Nzci1jYXBhYmxlJzogc3NyQ2FwYWJsZVRhZ3MsXG4gICAgJ2NsaWVudC1vbmx5JzogY2xpZW50T25seVRhZ3MsXG4gICAgJ2V4cGVyaW1lbnRhbC1kb20nOiBleHBlcmltZW50YWxEb21UYWdzLFxuICB9O1xuXG4gIGZvciAoY29uc3QgY2xhc3NpZmljYXRpb24gb2YgY2xhc3NpZmljYXRpb25zKSB7XG4gICAgYnVja2V0c1tjbGFzc2lmaWNhdGlvbi50aWVyXT8ucHVzaChjbGFzc2lmaWNhdGlvbi50YWdOYW1lKTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgY2xhc3NpZmljYXRpb25zLFxuICAgIHJlamVjdGVkVGFncyxcbiAgICBzc3JDYXBhYmxlVGFncyxcbiAgICBjbGllbnRPbmx5VGFncyxcbiAgICBleHBlcmltZW50YWxEb21UYWdzLFxuICAgIHN0YXRzOiB7XG4gICAgICB0b3RhbENvbXBvbmVudHM6IGNsYXNzaWZpY2F0aW9ucy5sZW5ndGgsXG4gICAgICBzc3JDYXBhYmxlQ291bnQ6IHNzckNhcGFibGVUYWdzLmxlbmd0aCxcbiAgICAgIGNsaWVudE9ubHlDb3VudDogY2xpZW50T25seVRhZ3MubGVuZ3RoLFxuICAgICAgcmVqZWN0ZWRDb3VudDogcmVqZWN0ZWRUYWdzLmxlbmd0aCxcbiAgICAgIGV4cGVyaW1lbnRhbERvbUNvdW50OiBleHBlcmltZW50YWxEb21UYWdzLmxlbmd0aCxcbiAgICB9LFxuICB9O1xufVxuXG5mdW5jdGlvbiBjbGFzc2lmeUNlbURlY2xhcmF0aW9uKFxuICBtYW5pZmVzdDogQ3VzdG9tRWxlbWVudHNNYW5pZmVzdCxcbiAgbW9kdWxlOiBDZW1Nb2R1bGUsXG4gIGRlY2xhcmF0aW9uOiBDZW1DdXN0b21FbGVtZW50LFxuKTogQ29tcGF0aWJpbGl0eUNsYXNzaWZpY2F0aW9uIHtcbiAgY29uc3Qgb3BlbkVsZW1lbnQgPSBkZWNsYXJhdGlvbi5vcGVuRWxlbWVudDtcbiAgY29uc3Qgc3NyID0gb3BlbkVsZW1lbnQ/LnNzciA/PyBmYWxzZTtcbiAgY29uc3QgZHNkID0gb3BlbkVsZW1lbnQ/LmRzZCA/PyBmYWxzZTtcbiAgY29uc3QgaHlkcmF0ZSA9IG9wZW5FbGVtZW50Py5oeWRyYXRlID8/ICdpZGxlJztcblxuICBsZXQgdGllcjogQ29tcGF0aWJpbGl0eVRpZXIgPSAnY2xpZW50LW9ubHknO1xuICBsZXQgcmVhc29uID0gbWFuaWZlc3QucGFja2FnZU5hbWVcbiAgICA/IGBDRU0tb25seSBwYWNrYWdlICR7bWFuaWZlc3QucGFja2FnZU5hbWV9IChubyBvcGVuRWxlbWVudCBTU1IgZGVjbGFyYXRpb24pYFxuICAgIDogJ0NFTS1vbmx5IHBhY2thZ2UgKG5vIG9wZW5FbGVtZW50IFNTUiBkZWNsYXJhdGlvbiknO1xuXG4gIGlmIChvcGVuRWxlbWVudD8uc3NyID09PSB0cnVlKSB7XG4gICAgaWYgKG9wZW5FbGVtZW50LmxheWVyKSB7XG4gICAgICB0aWVyID0gJ3Nzci1jYXBhYmxlJztcbiAgICAgIHJlYXNvbiA9IGBzc3I6IHRydWUgd2l0aCBsYXllcjogJHtvcGVuRWxlbWVudC5sYXllcn1gO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aWVyID0gJ2NsaWVudC1vbmx5JztcbiAgICAgIHJlYXNvbiA9ICdzc3I6IHRydWUgYnV0IG5vIGFkYXB0ZXIvbGF5ZXIgZGVjbGFyZWQnO1xuICAgIH1cbiAgfSBlbHNlIGlmIChvcGVuRWxlbWVudD8uc3NyID09PSBmYWxzZSkge1xuICAgIHJlYXNvbiA9ICdzc3I6IGZhbHNlIChleHBsaWNpdCBjbGllbnQtb25seSknO1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICB0YWdOYW1lOiBkZWNsYXJhdGlvbi50YWdOYW1lISxcbiAgICB0aWVyLFxuICAgIHJlYXNvbixcbiAgICBzb3VyY2U6ICdwYWNrYWdlJyxcbiAgICBtb2R1bGVQYXRoOiBtb2R1bGUucGF0aCxcbiAgICBzc3IsXG4gICAgZHNkLFxuICAgIGh5ZHJhdGUsXG4gIH07XG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBS0EsU0FBUyxjQUFjLFFBQVEsb0JBQW9CO0FBQ25ELFNBQVMsV0FBVyxRQUFRLDJCQUEyQjtBQXdFdkQsU0FBUyxtQkFBbUIsV0FBb0I7RUFDOUMsT0FBTyxZQUFZLElBQUksS0FBSztBQUM5QjtBQUVBLFNBQVMsU0FBUyxLQUFjO0VBQzlCLE9BQU8sQ0FBQyxDQUFDLFNBQVMsT0FBTyxVQUFVLFlBQVksQ0FBQyxNQUFNLE9BQU8sQ0FBQztBQUNoRTtBQUVBLFNBQVMsZUFBZSxLQUFjO0VBQ3BDLElBQUksQ0FBQyxNQUFNLE9BQU8sQ0FBQyxRQUFRLE9BQU87RUFDbEMsT0FBTyxNQUFNLE1BQU0sQ0FBQztBQUN0QjtBQUVBLFNBQVMsYUFBYSxLQUFjO0VBQ2xDLElBQUksQ0FBQyxNQUFNLE9BQU8sQ0FBQyxRQUFRLE9BQU87RUFDbEMsT0FBTyxNQUFNLE1BQU0sQ0FBQyxVQUFVLEdBQUcsQ0FBQyxDQUFDLFFBQVUsQ0FBQztNQUFFLGFBQWEsTUFBTSxXQUFXO0lBQUMsQ0FBQztBQUNsRjtBQUVBLFNBQVMsWUFBWSxLQUFjO0VBQ2pDLElBQUksQ0FBQyxTQUFTLFFBQVEsT0FBTztFQUM3QixPQUFPO0lB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
@@ -0,0 +1,3 @@
1
+ import type { ClientIslandEntry } from '@openelement/protocol/ssg';
2
+ export declare function validateClientIslandEntry(entry: ClientIslandEntry): void;
3
+ export declare function generateClientEntry(islands: ClientIslandEntry[]): string;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @openelement/core - Entry Generators
3
+ *
4
+ * v0.21.0: manifest-driven hydration strategies.
5
+ * Zero DOM interaction - cannot interfere with DSD rendering.
6
+ */ const CUSTOM_ELEMENT_NAME_RE = /^[a-z][.0-9_a-z]*-[\-.0-9_a-z]*$/;
7
+ const UNSAFE_IMPORT_PROTOCOL_RE = /^(?:javascript|data|vbscript|node):/i;
8
+ const VALID_STRATEGIES = new Set([
9
+ 'load',
10
+ 'idle',
11
+ 'visible',
12
+ 'only'
13
+ ]);
14
+ function hasControlCharacter(value) {
15
+ for(let i = 0; i < value.length; i++){
16
+ const code = value.charCodeAt(i);
17
+ if (code <= 0x1f || code === 0x7f) return true;
18
+ }
19
+ return false;
20
+ }
21
+ export function validateClientIslandEntry(entry) {
22
+ if (!CUSTOM_ELEMENT_NAME_RE.test(entry.tagName)) {
23
+ throw new Error(`Invalid island tagName: ${entry.tagName}`);
24
+ }
25
+ if (!entry.modulePath || hasControlCharacter(entry.modulePath) || /[\r\n]/.test(entry.modulePath) || UNSAFE_IMPORT_PROTOCOL_RE.test(entry.modulePath)) {
26
+ throw new Error(`Invalid island modulePath for ${entry.tagName}: ${entry.modulePath}`);
27
+ }
28
+ if (!VALID_STRATEGIES.has(entry.strategy)) {
29
+ throw new Error(`Invalid island strategy for ${entry.tagName}: ${String(entry.strategy)}. ` + 'Use one of: load, idle, visible, only.');
30
+ }
31
+ }
32
+ export function generateClientEntry(islands) {
33
+ islands.forEach(validateClientIslandEntry);
34
+ if (islands.length === 0) {
35
+ return '// openElement Client Entry - No islands detected, zero client JS needed\n';
36
+ }
37
+ const islandMap = islands.map((i)=>` ${JSON.stringify(i.tagName)}: () => import(${JSON.stringify(i.modulePath)})`).join(',\n');
38
+ const tags = islands.map((i)=>JSON.stringify(i.tagName)).join(', ');
39
+ const loadTags = islands.filter((i)=>i.strategy === 'load').map((i)=>JSON.stringify(i.tagName)).join(', ');
40
+ const visibleTags = islands.filter((i)=>i.strategy === 'visible').map((i)=>JSON.stringify(i.tagName)).join(', ');
41
+ const idleTags = islands.filter((i)=>i.strategy === 'idle').map((i)=>JSON.stringify(i.tagName)).join(', ');
42
+ const onlyTags = islands.filter((i)=>i.strategy === 'only').map((i)=>JSON.stringify(i.tagName)).join(', ');
43
+ return `// openElement Client Entry (v0.21 - load/idle/visible/only)
44
+ // load islands import immediately.
45
+ // idle islands import during browser idle time.
46
+ // visible islands import when their host enters the viewport.
47
+ // only islands are client-only and import immediately (no DSD/SSR).
48
+ // Zero DOM interaction - safe with DSD rendering.
49
+
50
+ var log = {
51
+ warn: function() { var a = ['[openElement]']; a.push.apply(a, arguments); console.warn.apply(console, a); },
52
+ error: function() { var a = ['[openElement]']; a.push.apply(a, arguments); console.error.apply(console, a); },
53
+ };
54
+
55
+ var __map = {
56
+ ${islandMap}
57
+ };
58
+ var __tags = [${tags}];
59
+
60
+ function __load(tag) {
61
+ if (__map[tag]) {
62
+ __map[tag]().catch(function(e) { log.warn(tag, e); });
63
+ __map[tag] = null;
64
+ }
65
+ }
66
+
67
+ function __onReady(fn) {
68
+ if (document.readyState === 'loading') {
69
+ document.addEventListener('DOMContentLoaded', fn, { once: true });
70
+ } else {
71
+ fn();
72
+ }
73
+ }
74
+
75
+ function __dispatchReady(strategy, tags) {
76
+ document.dispatchEvent(new CustomEvent('open:ready', {
77
+ detail: { strategy: strategy, islands: tags }
78
+ }));
79
+ }
80
+
81
+ // client:load islands - import immediately
82
+ [${loadTags || ''}].filter(Boolean).forEach(__load);
83
+
84
+ // client:only islands - import immediately, no DSD/SSR expected
85
+ ${onlyTags ? `var __onlyTags = [${onlyTags || ''}];\n__onlyTags.forEach(__load);\n` : '// No client:only islands\n'}
86
+ // client:visible islands - load when their element enters viewport
87
+ ${visibleTags ? `var __visibleTags = [${visibleTags || ''}];
88
+ var __observedTags = [];
89
+ function __observeVisible() {
90
+ if (!('IntersectionObserver' in window)) {
91
+ __visibleTags.forEach(__load);
92
+ __dispatchReady('visible', __visibleTags);
93
+ return;
94
+ }
95
+ __visibleTags.forEach(function(tag) {
96
+ var els = document.querySelectorAll(tag);
97
+ if (els.length > 0 && __observedTags.indexOf(tag) === -1) {
98
+ __observedTags.push(tag);
99
+ els.forEach(function(el) {
100
+ var obs = new IntersectionObserver(function(entries) {
101
+ entries.forEach(function(entry) {
102
+ if (entry.isIntersecting) {
103
+ __load(tag);
104
+ __dispatchReady('visible', [tag]);
105
+ obs.disconnect();
106
+ }
107
+ });
108
+ }, { rootMargin: '200px' });
109
+ obs.observe(el);
110
+ });
111
+ }
112
+ });
113
+ }
114
+ __onReady(__observeVisible);` : '// No client:visible islands'}
115
+
116
+ // client:idle islands - defer to browser idle
117
+ ${idleTags ? `var __idleTags = [${idleTags || ''}];
118
+ var __deferred = function() {
119
+ __idleTags.forEach(__load);
120
+ __dispatchReady('idle', __idleTags);
121
+ };
122
+ var __schedule = window.requestIdleCallback || window.requestAnimationFrame || function(fn) { setTimeout(fn, 50); };
123
+ __schedule(__deferred);` : '// No client:idle islands'}
124
+ `;
125
+ }
126
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9zc2cvc3JjL2VudHJ5LWdlbmVyYXRvcnMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAb3BlbmVsZW1lbnQvY29yZSAtIEVudHJ5IEdlbmVyYXRvcnNcbiAqXG4gKiB2MC4yMS4wOiBtYW5pZmVzdC1kcml2ZW4gaHlkcmF0aW9uIHN0cmF0ZWdpZXMuXG4gKiBaZXJvIERPTSBpbnRlcmFjdGlvbiAtIGNhbm5vdCBpbnRlcmZlcmUgd2l0aCBEU0QgcmVuZGVyaW5nLlxuICovXG5cbmltcG9ydCB0eXBlIHsgSHlkcmF0aW9uU3RyYXRlZ3kgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvZnJhbWV3b3JrJztcbmltcG9ydCB0eXBlIHsgQ2xpZW50SXNsYW5kRW50cnkgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvc3NnJztcblxuY29uc3QgQ1VTVE9NX0VMRU1FTlRfTkFNRV9SRSA9IC9eW2Etel1bLjAtOV9hLXpdKi1bXFwtLjAtOV9hLXpdKiQvO1xuY29uc3QgVU5TQUZFX0lNUE9SVF9QUk9UT0NPTF9SRSA9IC9eKD86amF2YXNjcmlwdHxkYXRhfHZic2NyaXB0fG5vZGUpOi9pO1xuY29uc3QgVkFMSURfU1RSQVRFR0lFUyA9IG5ldyBTZXQ8SHlkcmF0aW9uU3RyYXRlZ3k+KFsnbG9hZCcsICdpZGxlJywgJ3Zpc2libGUnLCAnb25seSddKTtcblxuZnVuY3Rpb24gaGFzQ29udHJvbENoYXJhY3Rlcih2YWx1ZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWUubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBjb2RlID0gdmFsdWUuY2hhckNvZGVBdChpKTtcbiAgICBpZiAoY29kZSA8PSAweDFmIHx8IGNvZGUgPT09IDB4N2YpIHJldHVybiB0cnVlO1xuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlQ2xpZW50SXNsYW5kRW50cnkoZW50cnk6IENsaWVudElzbGFuZEVudHJ5KTogdm9pZCB7XG4gIGlmICghQ1VTVE9NX0VMRU1FTlRfTkFNRV9SRS50ZXN0KGVudHJ5LnRhZ05hbWUpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGlzbGFuZCB0YWdOYW1lOiAke2VudHJ5LnRhZ05hbWV9YCk7XG4gIH1cbiAgaWYgKFxuICAgICFlbnRyeS5tb2R1bGVQYXRoIHx8XG4gICAgaGFzQ29udHJvbENoYXJhY3RlcihlbnRyeS5tb2R1bGVQYXRoKSB8fFxuICAgIC9bXFxyXFxuXS8udGVzdChlbnRyeS5tb2R1bGVQYXRoKSB8fFxuICAgIFVOU0FGRV9JTVBPUlRfUFJPVE9DT0xfUkUudGVzdChlbnRyeS5tb2R1bGVQYXRoKVxuICApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgaXNsYW5kIG1vZHVsZVBhdGggZm9yICR7ZW50cnkudGFnTmFtZX06ICR7ZW50cnkubW9kdWxlUGF0aH1gKTtcbiAgfVxuICBpZiAoIVZBTElEX1NUUkFURUdJRVMuaGFzKGVudHJ5LnN0cmF0ZWd5KSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBJbnZhbGlkIGlzbGFuZCBzdHJhdGVneSBmb3IgJHtlbnRyeS50YWdOYW1lfTogJHtTdHJpbmcoZW50cnkuc3RyYXRlZ3kpfS4gYCArXG4gICAgICAgICdVc2Ugb25lIG9mOiBsb2FkLCBpZGxlLCB2aXNpYmxlLCBvbmx5LicsXG4gICAgKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVDbGllbnRFbnRyeShcbiAgaXNsYW5kczogQ2xpZW50SXNsYW5kRW50cnlbXSxcbik6IHN0cmluZyB7XG4gIGlzbGFuZHMuZm9yRWFjaCh2YWxpZGF0ZUNsaWVudElzbGFuZEVudHJ5KTtcblxuICBpZiAoaXNsYW5kcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gJy8vIG9wZW5FbGVtZW50IENsaWVudCBFbnRyeSAtIE5vIGlzbGFuZHMgZGV0ZWN0ZWQsIHplcm8gY2xpZW50IEpTIG5lZWRlZFxcbic7XG4gIH1cblxuICBjb25zdCBpc2xhbmRNYXAgPSBpc2xhbmRzXG4gICAgLm1hcCgoaSkgPT4gYCAgJHtKU09OLnN0cmluZ2lmeShpLnRhZ05hbWUpfTogKCkgPT4gaW1wb3J0KCR7SlNPTi5zdHJpbmdpZnkoaS5tb2R1bGVQYXRoKX0pYClcbiAgICAuam9pbignLFxcbicpO1xuXG4gIGNvbnN0IHRhZ3MgPSBpc2xhbmRzLm1hcCgoaSkgPT4gSlNPTi5zdHJpbmdpZnkoaS50YWdOYW1lKSkuam9pbignLCAnKTtcbiAgY29uc3QgbG9hZFRhZ3MgPSBpc2xhbmRzXG4gICAgLmZpbHRlcigoaSkgPT4gaS5zdHJhdGVneSA9PT0gJ2xvYWQnKVxuICAgIC5tYXAoKGkpID0+IEpTT04uc3RyaW5naWZ5KGkudGFnTmFtZSkpXG4gICAgLmpvaW4oJywgJyk7XG4gIGNvbnN0IHZpc2libGVUYWdzID0gaXNsYW5kc1xuICAgIC5maWx0ZXIoKGkpID0+IGkuc3RyYXRlZ3kgPT09ICd2aXNpYmxlJylcbiAgICAubWFwKChpKSA9PiBKU09OLnN0cmluZ2lmeShpLnRhZ05hbWUpKVxuICAgIC5qb2luKCcsICcpO1xuICBjb25zdCBpZGxlVGFncyA9IGlzbGFuZHNcbiAgICAuZmlsdGVyKChpKSA9PiBpLnN0cmF0ZWd5ID09PSAnaWRsZScpXG4gICAgLm1hcCgoaSkgPT4gSlNPTi5zdHJpbmdpZnkoaS50YWdOYW1lKSlcbiAgICAuam9pbignLCAnKTtcbiAgY29uc3Qgb25seVRhZ3MgPSBpc2xhbmRzXG4gICAgLmZpbHRlcigoaSkgPT4gaS5zdHJhdGVneSA9PT0gJ29ubHknKVxuICAgIC5tYXAoKGkpID0+IEpTT04uc3RyaW5naWZ5KGkudGFnTmFtZSkpXG4gICAgLmpvaW4oJywgJyk7XG5cbiAgcmV0dXJuIGAvLyBvcGVuRWxlbWVudCBDbGllbnQgRW50cnkgKHYwLjIxIC0gbG9hZC9pZGxlL3Zpc2libGUvb25seSlcbi8vIGxvYWQgaXNsYW5kcyBpbXBvcnQgaW1tZWRpYXRlbHkuXG4vLyBpZGxlIGlzbGFuZHMgaW1wb3J0IGR1cmluZyBicm93c2VyIGlkbGUgdGltZS5cbi8vIHZpc2libGUgaXNsYW5kcyBpbXBvcnQgd2hlbiB0aGVpciBob3N0IGVudGVycyB0aGUgdmlld3BvcnQuXG4vLyBvbmx5IGlzbGFuZHMgYXJlIGNsaWVudC1vbmx5IGFuZCBpbXBvcnQgaW1tZWRpYXRlbHkgKG5vIERTRC9TU1IpLlxuLy8gWmVybyBET00gaW50ZXJhY3Rpb24gLSBzYWZlIHdpdGggRFNEIHJlbmRlcmluZy5cblxudmFyIGxvZyA9IHtcbiAgd2FybjogZnVuY3Rpb24oKSB7IHZhciBhID0gWydbb3BlbkVsZW1lbnRdJ107IGEucHVzaC5hcHBseShhLCBhcmd1bWVudHMpOyBjb25zb2xlLndhcm4uYXBwbHkoY29uc29sZSwgYSk7IH0sXG4gIGVycm9yOiBmdW5jdGlvbigpIHsgdmFyIGEgPSBbJ1tvcGVuRWxlbWVudF0nXTsgYS5wdXNoLmFwcGx5KGEsIGFyZ3VtZW50cyk7IGNvbnNvbGUuZXJyb3IuYXBwbHkoY29uc29sZSwgYSk7IH0sXG59O1xuXG52YXIgX19tYXAgPSB7XG4ke2lzbGFuZE1hcH1cbn07XG52YXIgX190YWdzID0gWyR7dGFnc31dO1xuXG5mdW5jdGlvbiBfX2xvYWQodGFnKSB7XG4gIGlmIChfX21hcFt0YWddKSB7XG4gICAgX19tYXBbdGFnXSgpLmNhdGNoKGZ1bmN0aW9uKGUpIHsgbG9nLndhcm4odGFnLCBlKTsgfSk7XG4gICAgX19tYXBbdGFnXSA9IG51bGw7XG4gIH1cbn1cblxuZnVuY3Rpb24gX19vblJlYWR5KGZuKSB7XG4gIGlmIChkb2N1bWVudC5yZWFkeVN0YXRlID09PSAnbG9hZGluZycpIHtcbiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgZm4sIHsgb25jZTogdHJ1ZSB9KTtcbiAgfSBlbHNlIHtcbiAgICBmbigpO1xuICB9XG59XG5cbmZ1bmN0aW9uIF9fZGlzcGF0Y2hSZWFkeShzdHJhdGVneSwgdGFncykge1xuICBkb2N1bWVudC5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudCgnb3BlbjpyZWFkeScsIHtcbiAgICBkZXRhaWw6IHsgc3RyYXRlZ3k6IHN0cmF0ZWd5LCBpc2xhbmRzOiB0YWdzIH1cbiAgfSkpO1xufVxuXG4vLyBjbGllbnQ6bG9hZCBpc2xhbmRzIC0gaW1wb3J0IGltbWVkaWF0ZWx5XG5bJHtsb2FkVGFncyB8fCAnJ31dLmZpbHRlcihCb29sZWFuKS5mb3JFYWNoKF9fbG9hZCk7XG5cbi8vIGNsaWVudDpvbmx5IGlzbGFuZHMgLSBpbXBvcnQgaW1tZWRpYXRlbHksIG5vIERTRC9TU1IgZXhwZWN0ZWRcbiR7XG4gICAgb25seVRhZ3NcbiAgICAgID8gYHZhciBfX29ubHlUYWdzID0gWyR7b25seVRhZ3MgfHwgJyd9XTtcXG5fX29ubHlUYWdzLmZvckVhY2goX19sb2FkKTtcXG5gXG4gICAgICA6ICcvLyBObyBjbGllbnQ6b25seSBpc2xhbmRzXFxuJ1xuICB9XG4vLyBjbGllbnQ6dmlzaWJsZSBpc2xhbmRzIC0gbG9hZCB3aGVuIHRoZWlyIGVsZW1lbnQgZW50ZXJzIHZpZXdwb3J0XG4ke1xuICAgIHZpc2libGVUYWdzXG4gICAgICA/IGB2YXIgX192aXNpYmxlVGFncyA9IFske3Zpc2libGVUYWdzIHx8ICcnfV07XG52YXIgX19vYnNlcnZlZFRhZ3MgPSBbXTtcbmZ1bmN0aW9uIF9fb2JzZXJ2ZVZpc2libGUoKSB7XG4gIGlmICghKCdJbnRlcnNlY3Rpb25PYnNlcnZlcicgaW4gd2luZG93KSkge1xuICAgIF9fdmlzaWJsZVRhZ3MuZm9yRWFjaChfX2xvYWQpO1xuICAgIF9fZGlzcGF0Y2hSZWFkeSgndmlzaWJsZScsIF9fdmlzaWJsZVRhZ3MpO1xuICAgIHJldHVybjtcbiAgfVxuICBfX3Zpc2libGVUYWdzLmZvckVhY2goZnVuY3Rpb24odGFnKSB7XG4gICAgdmFyIGVscyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwodGFnKTtcbiAgICBpZiAoZWxzLmxlbmd0aCA+IDAgJiYgX19vYnNlcnZlZFRhZ3MuaW5kZXhPZih0YWcpID09PSAtMSkge1xuICAgICAgX19vYnNlcnZlZFRhZ3MucHVzaCh0YWcpO1xuICAgICAgZWxzLmZvckVhY2goZnVuY3Rpb24oZWwpIHtcbiAgICAgICAgdmFyIG9icyA9IG5ldyBJbnRlcnNlY3Rpb25PYnNlcnZlcihmdW5jdGlvbihlbnRyaWVzKSB7XG4gICAgICAgICAgZW50cmllcy5mb3JFYWNoKGZ1bmN0aW9uKGVudHJ5KSB7XG4gICAgICAgICAgICBpZiAoZW50cnkuaXNJbnRlcnNlY3RpbmcpIHtcbiAgICAgICAgICAgICAgX19sb2FkKHRhZyk7XG4gICAgICAgICAgICAgIF9fZGlzcGF0Y2hSZWFkeSgndmlzaWJsZScsIFt0YWddKTtcbiAgICAgICAgICAgICAgb2JzLmRpc2Nvbm5lY3QoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfSwgeyByb290TWFyZ2luOiAnMjAwcHgnIH0pO1xuICAgICAgICBvYnMub2JzZXJ2ZShlbCk7XG4gICAgICB9KTtcbiAgICB9XG4gIH0pO1xufVxuX19vblJlYWR5KF9fb2JzZXJ2ZVZpc2libGUpO2BcbiAgICAgIDogJy8vIE5vIGNsaWVudDp2aXNpYmxlIGlzbGFuZHMnXG4gIH1cblxuLy8gY2xpZW50OmlkbGUgaXNsYW5kcyAtIGRlZmVyIHRvIGJyb3dzZXIgaWRsZVxuJHtcbiAgICBpZGxlVGFnc1xuICAgICAgPyBgdmFyIF9faWRsZVRhZ3MgPSBbJHtpZGxlVGFncyB8fCAnJ31dO1xudmFyIF9fZGVmZXJyZWQgPSBmdW5jdGlvbigpIHtcbiAgX19pZGxlVGFncy5mb3JFYWNoKF9fbG9hZCk7XG4gIF9fZGlzcGF0Y2hSZWFkeSgnaWRsZScsIF9faWRsZVRhZ3MpO1xufTtcbnZhciBfX3NjaGVkdWxlID0gd2luZG93LnJlcXVlc3RJZGxlQ2FsbGJhY2sgfHwgd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSB8fCBmdW5jdGlvbihmbikgeyBzZXRUaW1lb3V0KGZuLCA1MCk7IH07XG5fX3NjaGVkdWxlKF9fZGVmZXJyZWQpO2BcbiAgICAgIDogJy8vIE5vIGNsaWVudDppZGxlIGlzbGFuZHMnXG4gIH1cbmA7XG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0NBS0MsR0FLRCxNQUFNLHlCQUF5QjtBQUMvQixNQUFNLDRCQUE0QjtBQUNsQyxNQUFNLG1CQUFtQixJQUFJLElBQXVCO0VBQUM7RUFBUTtFQUFRO0VBQVc7Q0FBTztBQUV2RixTQUFTLG9CQUFvQixLQUFhO0VBQ3hDLElBQUssSUFBSSxJQUFJLEdBQUcsSUFBSSxNQUFNLE1BQU0sRUFBRSxJQUFLO0lBQ3JDLE1BQU0sT0FBTyxNQUFNLFVBQVUsQ0FBQztJQUM5QixJQUFJLFFBQVEsUUFBUSxTQUFTLE1BQU0sT0FBTztFQUM1QztFQUNBLE9BQU87QUFDVDtBQUVBLE9BQU8sU0FBUywwQkFBMEIsS0FBd0I7RUFDaEUsSUFBSSxDQUFDLHVCQUF1QixJQUFJLENBQUMsTUFBTSxPQUFPLEdBQUc7SUFDL0MsTUFBTSxJQUFJLE1BQU0sQ0FBQyx3QkFBd0IsRUFBRSxNQUFNLE9BQU8sRUFBRTtFQUM1RDtFQUNBLElBQ0UsQ0FBQyxNQUFNLFVBQVUsSUFDakIsb0JBQW9CLE1BQU0sVUFBVSxLQUNwQyxTQUFTLElBQUksQ0FBQyxNQUFNLFVBQVUsS0FDOUIsMEJBQTBCLElBQUksQ0FBQyxNQUFNLFVBQVUsR0FDL0M7SUFDQSxNQUFNLElBQUksTUFBTSxDQUFDLDhCQUE4QixFQUFFLE1BQU0sT0FBTyxDQUFDLEVBQUUsRUFBRSxNQUFNLFVBQVUsRUFBRTtFQUN2RjtFQUNBLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxDQUFDLE1BQU0sUUFBUSxHQUFHO0lBQ3pDLE1BQU0sSUFBSSxNQUNSLENBQUMsNEJBQTRCLEVBQUUsTUFBTSxPQUFPLENBQUMsRUFBRSxFQUFFLE9BQU8sTUFBTSxRQUFRLEVBQUUsRUFBRSxDQUFDLEdBQ3pFO0VBRU47QUFDRjtBQUVBLE9BQU8sU0FBUyxvQkFDZCxPQUE0QjtFQUU1QixRQUFRLE9BQU8sQ0FBQztFQUVoQixJQUFJLFFBQVEsTUFBTSxLQUFLLEdBQUc7SUFDeEIsT0FBTztFQUNUO0VBRUEsTUFBTSxZQUFZLFFBQ2YsR0FBRyxDQUFDLENBQUMsSUFBTSxDQUFDLEVBQUUsRUFBRSxLQUFLLFNBQVMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxlQUFlLEVBQUUsS0FBSyxTQUFTLENBQUMsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDLEVBQzFGLElBQUksQ0FBQztFQUVSLE1BQU0sT0FBTyxRQUFRLEdBQUcsQ0FBQyxDQUFDLElBQU0sS0FBSyxTQUFTLENBQUMsRUFBRSxPQUFPLEdBQUcsSUFBSSxDQUFDO0VBQ2hFLE1BQU0sV0FBVyxRQUNkLE1BQU0sQ0FBQyxDQUFDLElBQU0sRUFBRSxRQUFRLEtBQUssUUFDN0IsR0FBRyxDQUFDLENBQUMsSUFBTSxLQUFLLFNBQVMsQ0FBQyxFQUFFLE9BQU8sR0FDbkMsSUFBSSxDQUFDO0VBQ1IsTUFBTSxjQUFjLFFBQ2pCLE1BQU0sQ0FBQyxDQUFDLElBQU0sRUFBRSxRQUFRLEtBQUssV0FDN0IsR0FBRyxDQUFDLENBQUMsSUFBTSxLQUFLLFNBQVMsQ0FBQyxFQUFFLE9BQU8sR0FDbkMsSUFBSSxDQUFDO0VBQ1IsTUFBTSxXQUFXLFFBQ2QsTUFBTSxDQUFDLENBQUMsSUFBTSxFQUFFLFFBQVEsS0FBSyxRQUM3QixHQUFHLENBQUMsQ0FBQyxJQUFNLEtBQUssU0FBUyxDQUFDLEVBQUUsT0FBTyxHQUNuQyxJQUFJLENBQUM7RUFDUixNQUFNLFdBQVcsUUFDZCxNQUFNLENBQUMsQ0FBQyxJQUFNLEVBQUUsUUFBUSxLQUFLLFFBQzdCLEdBQUcsQ0FBQyxDQUFDLElBQU0sS0FBSyxTQUFTLENBQUMsRUFBRSxPQUFPLEdBQ25DLElBQUksQ0FBQztFQUVSLE9BQU8sQ0FBQzs7Ozs7Ozs7Ozs7OztBQWFWLEVBQUUsVUFBVTs7Y0FFRSxFQUFFLEtBQUs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztDQXdCcEIsRUFBRSxZQUFZLEdBQUc7OztBQUdsQixFQUNJLFdBQ0ksQ0FBQyxrQkFBa0IsRUFBRSxZQUFZLEdBQUcsaUNBQWlDLENBQUMsR0FDdEUsOEJBQ0w7O0FBRUgsRUFDSSxjQUNJLENBQUMscUJBQXFCLEVBQUUsZUFBZSxHQUFHOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7NEJBMkJ0QixDQUFDLEdBQ3JCLCtCQUNMOzs7QUFHSCxFQUNJLFdBQ0ksQ0FBQyxrQkFBa0IsRUFBRSxZQUFZLEdBQUc7Ozs7Ozt1QkFNckIsQ0FBQyxHQUNoQiw0QkFDTDtBQUNILENBQUM7QUFDRCJ9