@openelement/ssg 0.41.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/package.json +30 -0
- package/src/build-postprocess.d.ts +24 -0
- package/src/build-postprocess.js +74 -0
- package/src/cem-compat.js +226 -0
- package/src/entry-generators.d.ts +3 -0
- package/src/entry-generators.js +126 -0
- package/src/entry-render-helpers.js +307 -0
- package/src/entry-render-runtime.js +94 -0
- package/src/entry-render-ssg.js +169 -0
- package/src/entry-renderer.d.ts +87 -0
- package/src/entry-renderer.js +555 -0
- package/src/external-resolver.d.ts +62 -0
- package/src/external-resolver.js +285 -0
- package/src/index.d.ts +31 -0
- package/src/index.js +28 -0
- package/src/island-manifest.d.ts +27 -0
- package/src/island-manifest.js +78 -0
- package/src/postprocess.d.ts +73 -0
- package/src/postprocess.js +376 -0
- package/src/route-scanner-fs.js +29 -0
- package/src/route-scanner.d.ts +132 -0
- package/src/route-scanner.js +497 -0
- package/src/route-type-generator.d.ts +29 -0
- package/src/route-type-generator.js +99 -0
- package/src/ssg-dynamic.js +66 -0
- package/src/ssg-helpers.d.ts +8 -0
- package/src/ssg-helpers.js +96 -0
- package/src/ssg-i18n.js +78 -0
- package/src/ssg-render.d.ts +3 -0
- package/src/ssg-render.js +162 -0
- package/src/ssg-report.js +172 -0
- package/src/ssr-polyfills.d.ts +15 -0
- package/src/ssr-polyfills.js +26 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @openelement/core - Route scanner
|
|
3
|
+
* Scans the routes directory and generates a route map.
|
|
4
|
+
* Produces the virtual:routes module.
|
|
5
|
+
*
|
|
6
|
+
* Phase 1 enhancement: support for _renderer.ts (layout) and
|
|
7
|
+
* _middleware.ts (Hono middleware) special files.
|
|
8
|
+
*
|
|
9
|
+
* Phase 2 enhancement: support for package islands auto-detection.
|
|
10
|
+
* Packages can export an `islands` array in their main entry.
|
|
11
|
+
*
|
|
12
|
+
* Convention (minimal augmentation):
|
|
13
|
+
* - _renderer.ts: exports a renderer that wraps route VNodes
|
|
14
|
+
* - _middleware.ts: exports a Hono middleware function applied before the route
|
|
15
|
+
* - Files starting with _ are not route handlers but are loaded by the framework
|
|
16
|
+
*
|
|
17
|
+
* ─── SSR Import Discovery Audit (Step1) ─────────────────────
|
|
18
|
+
*
|
|
19
|
+
* This file discovers islands but does NOT import them (static scan only):
|
|
20
|
+
*
|
|
21
|
+
* 1. Local island files:
|
|
22
|
+
* - Scanned by `scanIslands()`
|
|
23
|
+
* - Metadata read by `scanIslandMeta()` (static, no import)
|
|
24
|
+
* - SSR decision: `openElement.ssr` field (static read, no import)
|
|
25
|
+
*
|
|
26
|
+
* 2. Package manifest islands:
|
|
27
|
+
* - Discovered by `scanPackageManifests()`
|
|
28
|
+
* - Imports package module to read `manifest` export
|
|
29
|
+
* - Browser-only packages: caught by try/catch
|
|
30
|
+
* - SSR decision: `manifest.declarations[].openElement.ssr` field
|
|
31
|
+
*
|
|
32
|
+
* 3. CEM manifests (v0.18.0):
|
|
33
|
+
* - Discovered by `scanCemManifests()` - reads custom-elements.json from
|
|
34
|
+
* node_modules packages WITHOUT importing package code
|
|
35
|
+
* - Results fed into the compatibility classifier (parseCem + classifyCemManifest)
|
|
36
|
+
*
|
|
37
|
+
* 4. Nested custom elements (from the VNode tree):
|
|
38
|
+
* - NOT handled in this file
|
|
39
|
+
* - See: `packages/core/src/jsx-render-string.ts` and `renderDsdTree()`
|
|
40
|
+
*
|
|
41
|
+
* Audit completed: 2026-05-17
|
|
42
|
+
* Auditor: AI agent (openElement v0.17.4 SOP compliance check)
|
|
43
|
+
*
|
|
44
|
+
* ─── v0.41.0-alpha.1: AST removed ────────────────────────────
|
|
45
|
+
*
|
|
46
|
+
* Replaced TypeScript AST scanning with regex/glob-based extraction.
|
|
47
|
+
* Route and island modules are simple ESM files; parsing the whole source
|
|
48
|
+
* with the TypeScript compiler is overkill and adds a heavy dependency.
|
|
49
|
+
*/ import { formatError, OpenElementError } from '@openelement/core/errors';
|
|
50
|
+
import { createLogger } from '@openelement/core/logger';
|
|
51
|
+
import { join, posix, sep } from 'node:path';
|
|
52
|
+
import { classifyCemManifest, parseCem } from './cem-compat.js';
|
|
53
|
+
import { safeReadDir, safeReadFile, safeStat } from './route-scanner-fs.js';
|
|
54
|
+
const log = createLogger('core');
|
|
55
|
+
/**
|
|
56
|
+
* Read a static string export (e.g. `export const tagName = '...'`) from source text.
|
|
57
|
+
*/ export function readRouteTagName(source) {
|
|
58
|
+
const match = source.match(/export\s+const\s+tagName\s*=\s*(["'`])([^"'`]+)\1/);
|
|
59
|
+
return match?.[2];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Read `export const tagName = '...'` from a route file using regex scanning.
|
|
63
|
+
*/ export async function readRouteTagNameFromModule(filePath) {
|
|
64
|
+
const source = await safeReadFile(filePath);
|
|
65
|
+
if (source === undefined) return undefined;
|
|
66
|
+
return readRouteTagName(source);
|
|
67
|
+
}
|
|
68
|
+
function staticOpenElementError(message) {
|
|
69
|
+
return new OpenElementError(`Invalid static island metadata export "openElement": ${message}. Accepted shape: export const openElement = defineIslandConfig({ ssr?: boolean, dsd?: boolean, hydrate?: "load" | "idle" | "visible" | "only" }).`, {
|
|
70
|
+
code: 'ISLAND_METADATA_ERROR',
|
|
71
|
+
statusCode: 500,
|
|
72
|
+
recoverable: false
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* v0.41.0-alpha.1: Regex-based extraction of
|
|
77
|
+
* `export const openElement = defineIslandConfig({ ... })`.
|
|
78
|
+
*
|
|
79
|
+
* The scanner intentionally does not execute island modules. It accepts only a
|
|
80
|
+
* defineIslandConfig() call with boolean `ssr`/`dsd` and string `hydrate`
|
|
81
|
+
* literal values. Dynamic metadata is rejected instead of guessed.
|
|
82
|
+
*/ export function readStaticOpenElementExport(source) {
|
|
83
|
+
const declMatch = source.match(/export\s+const\s+openElement\s*=/);
|
|
84
|
+
if (!declMatch) return null;
|
|
85
|
+
const afterEquals = source.slice(declMatch.index + declMatch[0].length).trimStart();
|
|
86
|
+
// Must call defineIslandConfig(...) - reject legacy object literals.
|
|
87
|
+
const callMatch = afterEquals.match(/^defineIslandConfig\s*\(\s*(\{[\s\S]*?\})\s*\)/);
|
|
88
|
+
if (!callMatch) {
|
|
89
|
+
const hasCall = /^defineIslandConfig\s*\(/.test(afterEquals);
|
|
90
|
+
if (hasCall) {
|
|
91
|
+
throw staticOpenElementError('defineIslandConfig() argument must be a static object literal');
|
|
92
|
+
}
|
|
93
|
+
throw staticOpenElementError('openElement export must call defineIslandConfig(...)');
|
|
94
|
+
}
|
|
95
|
+
const body = callMatch[1].slice(1, -1);
|
|
96
|
+
const meta = {};
|
|
97
|
+
// Match key: value pairs, skipping nested braces/strings.
|
|
98
|
+
const propRe = /\b(ssr|dsd|hydrate|[^\s:,{}]+)\s*:\s*(true|false|["']([^"']*)["'])/g;
|
|
99
|
+
let m;
|
|
100
|
+
while((m = propRe.exec(body)) !== null){
|
|
101
|
+
const key = m[1];
|
|
102
|
+
const raw = m[2];
|
|
103
|
+
if (![
|
|
104
|
+
'ssr',
|
|
105
|
+
'dsd',
|
|
106
|
+
'hydrate'
|
|
107
|
+
].includes(key)) {
|
|
108
|
+
throw staticOpenElementError(`unsupported openElement metadata key "${key}"`);
|
|
109
|
+
}
|
|
110
|
+
const typedKey = key;
|
|
111
|
+
if (typedKey === 'ssr' || typedKey === 'dsd') {
|
|
112
|
+
if (raw !== 'true' && raw !== 'false') {
|
|
113
|
+
throw staticOpenElementError(`openElement.${typedKey} must be a boolean literal`);
|
|
114
|
+
}
|
|
115
|
+
meta[typedKey] = raw === 'true';
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const value = raw.slice(1, -1);
|
|
119
|
+
if (![
|
|
120
|
+
'load',
|
|
121
|
+
'idle',
|
|
122
|
+
'visible',
|
|
123
|
+
'only'
|
|
124
|
+
].includes(value)) {
|
|
125
|
+
throw staticOpenElementError(`openElement.hydrate has unsupported value "${value}"`);
|
|
126
|
+
}
|
|
127
|
+
meta.hydrate = value;
|
|
128
|
+
}
|
|
129
|
+
return meta;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Convert a file path to a URL path pattern.
|
|
133
|
+
* e.g., 'index.ts' -> '/', 'about.ts' -> '/about', 'posts/[id].ts' -> '/posts/:id'
|
|
134
|
+
*
|
|
135
|
+
* v0.6': Uses URLPattern-compatible syntax where possible.
|
|
136
|
+
* URLPattern is the WHATWG standard for URL matching (section7.2).
|
|
137
|
+
* Pattern :param is compatible with both Hono and URLPattern.
|
|
138
|
+
*/ function filePathToRoutePath(filePath) {
|
|
139
|
+
// Normalize separators - handle Windows backslash paths
|
|
140
|
+
// v0.14.3: Use posix.join to ensure all output paths use forward slashes
|
|
141
|
+
// regardless of platform. This prevents \ from leaking into URL patterns.
|
|
142
|
+
let p = filePath.split(sep).join(posix.sep);
|
|
143
|
+
// v0.25: AST-verified — path utility, regex is the appropriate tool
|
|
144
|
+
p = p.replace(/\.[^.]+$/, '');
|
|
145
|
+
// v0.25: AST-verified — path utility, converts [param] to :param
|
|
146
|
+
p = p.replace(/\[([^\]]+)\]/g, ':$1');
|
|
147
|
+
// Handle index
|
|
148
|
+
if (p === 'index') return '/';
|
|
149
|
+
if (p.endsWith('/index')) {
|
|
150
|
+
p = p.slice(0, -6); // Remove trailing /index
|
|
151
|
+
// After stripping /index, check if the result is the root index
|
|
152
|
+
if (p === 'index' || p === '') return '/';
|
|
153
|
+
}
|
|
154
|
+
// Ensure leading slash
|
|
155
|
+
if (!p.startsWith('/')) p = '/' + p;
|
|
156
|
+
return p;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Determine route type from file path.
|
|
160
|
+
* Files under 'api/' subdirectory are API routes.
|
|
161
|
+
*/ function getRouteType(filePath) {
|
|
162
|
+
const normalized = filePath.split(sep).join(posix.sep);
|
|
163
|
+
return normalized.startsWith('api/') || normalized.includes('/api/') ? 'api' : 'page';
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Generate a valid JS variable name from a route path.
|
|
167
|
+
* e.g., '/' -> 'RouteIndex', '/about' -> 'RouteAbout', '/posts/:id' -> 'RoutePostsId'
|
|
168
|
+
*/ function pathToVarName(path) {
|
|
169
|
+
// v0.25: AST-verified — path-to-identifier transformation, regex is the appropriate tool
|
|
170
|
+
let name = path.replace(/^\//, '').replace(/\/$/, '').replace(/:([^/]+)/g, '$1').replace(/[^a-zA-Z0-9]/g, '_');
|
|
171
|
+
if (!name || name === '_') name = 'Index';
|
|
172
|
+
return 'Route_' + name.charAt(0).toUpperCase() + name.slice(1);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Identify special file types by name.
|
|
176
|
+
* _renderer.ts -> renderer, _middleware.ts -> middleware
|
|
177
|
+
*/ // ponytail: inline lookup replaces 2-case switch
|
|
178
|
+
function getSpecialFileType(fileName) {
|
|
179
|
+
const baseName = fileName.replace(/\.[^.]+$/, '');
|
|
180
|
+
return ({
|
|
181
|
+
_renderer: 'renderer',
|
|
182
|
+
_middleware: 'middleware'
|
|
183
|
+
})[baseName] ?? null;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Check if a file should be ignored for routing.
|
|
187
|
+
* Dot-files are always ignored.
|
|
188
|
+
*/ function isIgnoredFile(fileName) {
|
|
189
|
+
return fileName.startsWith('.');
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Recursively scan a directory for route files.
|
|
193
|
+
* Also collects _renderer.ts and _middleware.ts special files.
|
|
194
|
+
*/ export async function scanRoutes(routesDir, baseDir = '') {
|
|
195
|
+
const entries = [];
|
|
196
|
+
const files = await safeReadDir(routesDir);
|
|
197
|
+
if (files === undefined) {
|
|
198
|
+
log.debug(`Routes directory "${routesDir}" not found`);
|
|
199
|
+
return entries;
|
|
200
|
+
}
|
|
201
|
+
for (const file of files){
|
|
202
|
+
if (isIgnoredFile(file)) continue;
|
|
203
|
+
const fullPath = join(routesDir, file);
|
|
204
|
+
const relativePath = baseDir ? join(baseDir, file) : file;
|
|
205
|
+
const fileStat = await safeStat(fullPath);
|
|
206
|
+
if (!fileStat) {
|
|
207
|
+
log.debug(`File vanished before stat: ${fullPath}`);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (fileStat.isDirectory()) {
|
|
211
|
+
// Recurse into subdirectories
|
|
212
|
+
const subEntries = await scanRoutes(fullPath, relativePath);
|
|
213
|
+
entries.push(...subEntries);
|
|
214
|
+
} else if (/\.(ts|tsx|js|jsx)$/.test(file)) {
|
|
215
|
+
// Check for special files
|
|
216
|
+
const specialType = getSpecialFileType(file);
|
|
217
|
+
if (specialType) {
|
|
218
|
+
// Add as a special entry - not a route handler, but loadable
|
|
219
|
+
entries.push({
|
|
220
|
+
path: filePathToRoutePath(relativePath),
|
|
221
|
+
filePath: relativePath.split(sep).join(posix.sep),
|
|
222
|
+
type: 'special',
|
|
223
|
+
varName: `Special_${specialType}_${baseDir.replace(/[\\/]/g, '_') || 'root'}`,
|
|
224
|
+
special: specialType
|
|
225
|
+
});
|
|
226
|
+
} else if (!file.startsWith('_')) {
|
|
227
|
+
// Regular route file
|
|
228
|
+
const routePath = filePathToRoutePath(relativePath);
|
|
229
|
+
const routeType = getRouteType(relativePath);
|
|
230
|
+
// v0.25: AST-verified — path utility, extracts [param] patterns
|
|
231
|
+
const paramMatches = relativePath.match(/\[([^\]]+)\]/g);
|
|
232
|
+
const params = paramMatches ? paramMatches.map((m)=>m.slice(1, -1)) : undefined;
|
|
233
|
+
let tagName;
|
|
234
|
+
if (routeType === 'page') {
|
|
235
|
+
// Regex-based scanning reads `export const tagName` without executing the module.
|
|
236
|
+
tagName = await readRouteTagNameFromModule(fullPath);
|
|
237
|
+
if (tagName === undefined) {
|
|
238
|
+
// tagName not found is normal — not all page routes define one
|
|
239
|
+
log.debug(`No tagName export found in route module: ${fullPath}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
entries.push({
|
|
243
|
+
path: routePath,
|
|
244
|
+
filePath: relativePath.split(sep).join(posix.sep),
|
|
245
|
+
type: routeType,
|
|
246
|
+
varName: pathToVarName(routePath),
|
|
247
|
+
tagName,
|
|
248
|
+
params
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
// Other _-prefixed files (not _renderer/_middleware) are silently skipped
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Sort routes: static paths first, then dynamic
|
|
255
|
+
entries.sort((a, b)=>{
|
|
256
|
+
// Special files go to the end
|
|
257
|
+
if (a.special || b.special) {
|
|
258
|
+
if (a.special && !b.special) return 1;
|
|
259
|
+
if (!a.special && b.special) return -1;
|
|
260
|
+
return 0;
|
|
261
|
+
}
|
|
262
|
+
const aDynamic = a.path.includes(':');
|
|
263
|
+
const bDynamic = b.path.includes(':');
|
|
264
|
+
if (aDynamic !== bDynamic) return aDynamic ? 1 : -1;
|
|
265
|
+
return a.path.localeCompare(b.path);
|
|
266
|
+
});
|
|
267
|
+
return entries;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* v0.25: AST-verified — converts file name to a valid Custom Element tag name.
|
|
271
|
+
* Uses regex for path manipulation since tag names are derived from file paths.
|
|
272
|
+
*
|
|
273
|
+
* Examples:
|
|
274
|
+
* 'my-counter.ts' -> 'my-counter'
|
|
275
|
+
* 'posts/index.ts' -> 'posts-index'
|
|
276
|
+
* 'admin\\dashboard.ts' -> 'admin-dashboard'
|
|
277
|
+
*/ export function fileToTagName(fileName) {
|
|
278
|
+
return fileName.replace(/\.[^.]+$/, '') // v0.25: AST-verified — remove extension
|
|
279
|
+
.replace(/[\\/]/g, '-') // v0.25: AST-verified — replace path separators with hyphens
|
|
280
|
+
.toLowerCase();
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Scan islands directory recursively for island files.
|
|
284
|
+
* Returns paths relative to islandsDir (e.g., ['my-counter.ts', 'posts/index.ts']).
|
|
285
|
+
*/ export async function scanIslands(islandsDir, relativeDir = '') {
|
|
286
|
+
const files = [];
|
|
287
|
+
const entries = await safeReadDir(islandsDir);
|
|
288
|
+
if (entries === undefined) {
|
|
289
|
+
log.debug(`Islands directory "${islandsDir}" not found`);
|
|
290
|
+
return files;
|
|
291
|
+
}
|
|
292
|
+
for (const entry of entries){
|
|
293
|
+
if (entry.startsWith('.')) continue;
|
|
294
|
+
const fullPath = join(islandsDir, entry);
|
|
295
|
+
const fileStat = await safeStat(fullPath);
|
|
296
|
+
if (!fileStat) {
|
|
297
|
+
log.debug(`Island file vanished before stat: ${fullPath}`);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const relativePath = relativeDir ? join(relativeDir, entry) : entry;
|
|
301
|
+
if (fileStat.isDirectory()) {
|
|
302
|
+
const subFiles = await scanIslands(fullPath, relativePath);
|
|
303
|
+
files.push(...subFiles);
|
|
304
|
+
} else if (/\.(ts|tsx|js|jsx)$/.test(entry)) {
|
|
305
|
+
files.push(relativePath);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return files.sort();
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* v0.41.0-alpha.1: Regex-based — reads island metadata by statically scanning the module
|
|
312
|
+
* source for `export const openElement = defineIslandConfig({ ... })`.
|
|
313
|
+
*
|
|
314
|
+
* Supported form:
|
|
315
|
+
* export const openElement = defineIslandConfig({ ssr: false, dsd: false, hydrate: 'only' })
|
|
316
|
+
*
|
|
317
|
+
* This is more reliable than regex because it handles:
|
|
318
|
+
* - Comments inside the object literal
|
|
319
|
+
* - Computed properties
|
|
320
|
+
* - Destructured/re-exported values
|
|
321
|
+
* - Canonical defineIslandConfig(...) calls
|
|
322
|
+
*
|
|
323
|
+
* If a module cannot be read, its metadata is silently skipped.
|
|
324
|
+
*/ export async function scanIslandMeta(islandsDir, islandFiles) {
|
|
325
|
+
const meta = {};
|
|
326
|
+
for (const filePath of islandFiles){
|
|
327
|
+
const tagName = fileToTagName(filePath);
|
|
328
|
+
const fullPath = join(islandsDir, filePath);
|
|
329
|
+
const source = await safeReadFile(fullPath);
|
|
330
|
+
if (source === undefined) {
|
|
331
|
+
log.debug(`Unable to read island module for metadata: ${fullPath}`);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
// Read the `openElement` export directly; no regex needed.
|
|
335
|
+
const openElementExport = readStaticOpenElementExport(source);
|
|
336
|
+
if (!openElementExport) continue;
|
|
337
|
+
const hydrate = openElementExport.hydrate && [
|
|
338
|
+
'load',
|
|
339
|
+
'idle',
|
|
340
|
+
'visible',
|
|
341
|
+
'only'
|
|
342
|
+
].includes(openElementExport.hydrate) ? openElementExport.hydrate : undefined;
|
|
343
|
+
meta[tagName] = {
|
|
344
|
+
tagName,
|
|
345
|
+
filePath,
|
|
346
|
+
ssr: hydrate === 'only' ? false : openElementExport.ssr,
|
|
347
|
+
dsd: hydrate === 'only' ? false : openElementExport.dsd,
|
|
348
|
+
hydrate,
|
|
349
|
+
reason: hydrate === 'only' ? 'local island exports openElement.hydrate=only' : openElementExport.ssr === false ? 'local island exports openElement.ssr=false' : undefined
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
return meta;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Scan package exports for OpenElementPackageManifest.
|
|
356
|
+
* Packages should export a `manifest` OpenElementPackageManifest in their main entry.
|
|
357
|
+
*
|
|
358
|
+
* Example package export:
|
|
359
|
+
* ```ts
|
|
360
|
+
* // @openelement/ui/index.ts
|
|
361
|
+
* export { manifest } from './manifest.js';
|
|
362
|
+
* ```
|
|
363
|
+
*
|
|
364
|
+
* @param packageNames - List of package names to scan (e.g., ['@openelement/ui'])
|
|
365
|
+
* @returns Array of OpenElementPackageManifest
|
|
366
|
+
*/ export async function scanPackageManifests(packageNames) {
|
|
367
|
+
const allManifests = [];
|
|
368
|
+
for (const pkg of packageNames){
|
|
369
|
+
// @vite-ignore suppresses unanalyzable-dynamic-import JSR warning.
|
|
370
|
+
let mod;
|
|
371
|
+
try {
|
|
372
|
+
mod = await import(/* @vite-ignore */ pkg);
|
|
373
|
+
} catch (e) {
|
|
374
|
+
if (isBrowserOnlyPackageImportError(e)) {
|
|
375
|
+
log.warn(`Skipping package manifest from "${pkg}": browser-only package cannot be imported during SSR discovery`);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
throw new OpenElementError(`Failed to scan package manifest from "${pkg}": ${formatError(e)}`, {
|
|
379
|
+
code: 'PACKAGE_SCAN_ERROR',
|
|
380
|
+
statusCode: 500,
|
|
381
|
+
recoverable: false
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
if (mod.manifest && typeof mod.manifest === 'object') {
|
|
385
|
+
const manifest = mod.manifest;
|
|
386
|
+
if (manifest.packageName && manifest.declarations) {
|
|
387
|
+
allManifests.push(manifest);
|
|
388
|
+
} else {
|
|
389
|
+
throw new OpenElementError(`Invalid manifest in ${pkg}: missing packageName or declarations`, {
|
|
390
|
+
code: 'PACKAGE_MANIFEST_ERROR',
|
|
391
|
+
statusCode: 500,
|
|
392
|
+
recoverable: false
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
} else {
|
|
396
|
+
throw new OpenElementError(`Package ${pkg} does not export a manifest`, {
|
|
397
|
+
code: 'PACKAGE_MANIFEST_ERROR',
|
|
398
|
+
statusCode: 500,
|
|
399
|
+
recoverable: false
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return allManifests;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* v0.25: AST-verified — error message classification, regex is the appropriate tool
|
|
407
|
+
* for matching runtime error strings from failed dynamic imports.
|
|
408
|
+
*/ function isBrowserOnlyPackageImportError(error) {
|
|
409
|
+
const message = formatError(error);
|
|
410
|
+
return /\b(window|document|HTMLElement|customElements|navigator)\b.*\bis not defined\b/i.test(message);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Scan node_modules for packages that ship a `custom-elements.json`.
|
|
414
|
+
*
|
|
415
|
+
* Strategy:
|
|
416
|
+
* 1. Read node_modules directory entries (top-level packages + scoped orgs)
|
|
417
|
+
* 2. For each package, check if `<pkg>/custom-elements.json` exists
|
|
418
|
+
* 3. Return the raw JSON - caller is responsible for parsing + classifying
|
|
419
|
+
*
|
|
420
|
+
* This function reads files only. It never imports or executes package code.
|
|
421
|
+
*
|
|
422
|
+
* @param nodeModulesDir - Absolute path to the node_modules directory
|
|
423
|
+
* @returns Array of found CEM manifests
|
|
424
|
+
*/ export async function scanCemManifests(nodeModulesDir) {
|
|
425
|
+
const results = [];
|
|
426
|
+
const entries = await safeReadDir(nodeModulesDir);
|
|
427
|
+
if (!entries) return results;
|
|
428
|
+
for (const entry of entries){
|
|
429
|
+
if (entry.startsWith('.')) continue;
|
|
430
|
+
if (entry.startsWith('@')) {
|
|
431
|
+
// Scoped package directory - recurse one level
|
|
432
|
+
const scopeDir = join(nodeModulesDir, entry);
|
|
433
|
+
const scopedEntries = await safeReadDir(scopeDir);
|
|
434
|
+
if (!scopedEntries) continue;
|
|
435
|
+
for (const scopedEntry of scopedEntries){
|
|
436
|
+
if (scopedEntry.startsWith('.')) continue;
|
|
437
|
+
const packageName = `${entry}/${scopedEntry}`;
|
|
438
|
+
const cemPath = join(nodeModulesDir, entry, scopedEntry, 'custom-elements.json');
|
|
439
|
+
const result = await tryReadCemFile(cemPath, packageName);
|
|
440
|
+
if (result) results.push(result);
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
// Regular (non-scoped) package
|
|
444
|
+
const cemPath = join(nodeModulesDir, entry, 'custom-elements.json');
|
|
445
|
+
const result = await tryReadCemFile(cemPath, entry);
|
|
446
|
+
if (result) results.push(result);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return results;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Try to read a custom-elements.json file.
|
|
453
|
+
* Returns null if the file doesn't exist or can't be read.
|
|
454
|
+
*/ async function tryReadCemFile(cemPath, packageName) {
|
|
455
|
+
const json = await safeReadFile(cemPath);
|
|
456
|
+
return json === undefined ? null : {
|
|
457
|
+
packageName,
|
|
458
|
+
cemPath,
|
|
459
|
+
json
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Run CEM auto-detection: scan node_modules, parse each manifest,
|
|
464
|
+
* and classify all discovered components.
|
|
465
|
+
*
|
|
466
|
+
* This is the high-level function called from the Vite plugin buildStart().
|
|
467
|
+
* It combines scanCemManifests() + parseCem() + classifyCemManifest()
|
|
468
|
+
* into a single pipeline.
|
|
469
|
+
*
|
|
470
|
+
* @param nodeModulesDir - Absolute path to node_modules
|
|
471
|
+
* @returns Array of compatibility classifications (may be empty if no CEM found)
|
|
472
|
+
*/ export async function detectAndClassifyCemPackages(nodeModulesDir) {
|
|
473
|
+
const cemResults = await scanCemManifests(nodeModulesDir);
|
|
474
|
+
if (cemResults.length === 0) return [];
|
|
475
|
+
const allClassifications = [];
|
|
476
|
+
for (const { packageName, json } of cemResults){
|
|
477
|
+
const parseResult = parseCem(json);
|
|
478
|
+
if (!parseResult.success || !parseResult.manifest) {
|
|
479
|
+
log.debug(`Skipping invalid CEM manifest from "${packageName}": ` + parseResult.errors.map((e)=>e.message).join('; '));
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
// Attach package name to the manifest for better diagnostics
|
|
483
|
+
const manifest = {
|
|
484
|
+
...parseResult.manifest,
|
|
485
|
+
packageName
|
|
486
|
+
};
|
|
487
|
+
const classResult = classifyCemManifest(manifest);
|
|
488
|
+
// Log summary
|
|
489
|
+
const { stats } = classResult;
|
|
490
|
+
if (stats.totalComponents > 0) {
|
|
491
|
+
log.info(`CEM: ${packageName} - ${stats.totalComponents} component(s): ` + `${stats.ssrCapableCount} ssr-capable, ${stats.clientOnlyCount} client-only` + (stats.rejectedCount > 0 ? `, ${stats.rejectedCount} rejected` : '') + (stats.experimentalDomCount > 0 ? `, ${stats.experimentalDomCount} experimental` : ''));
|
|
492
|
+
}
|
|
493
|
+
allClassifications.push(...classResult.classifications);
|
|
494
|
+
}
|
|
495
|
+
return allClassifications;
|
|
496
|
+
}
|
|
497
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9zc2cvc3JjL3JvdXRlLXNjYW5uZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAb3BlbmVsZW1lbnQvY29yZSAtIFJvdXRlIHNjYW5uZXJcbiAqIFNjYW5zIHRoZSByb3V0ZXMgZGlyZWN0b3J5IGFuZCBnZW5lcmF0ZXMgYSByb3V0ZSBtYXAuXG4gKiBQcm9kdWNlcyB0aGUgdmlydHVhbDpyb3V0ZXMgbW9kdWxlLlxuICpcbiAqIFBoYXNlIDEgZW5oYW5jZW1lbnQ6IHN1cHBvcnQgZm9yIF9yZW5kZXJlci50cyAobGF5b3V0KSBhbmRcbiAqIF9taWRkbGV3YXJlLnRzIChIb25vIG1pZGRsZXdhcmUpIHNwZWNpYWwgZmlsZXMuXG4gKlxuICogUGhhc2UgMiBlbmhhbmNlbWVudDogc3VwcG9ydCBmb3IgcGFja2FnZSBpc2xhbmRzIGF1dG8tZGV0ZWN0aW9uLlxuICogUGFja2FnZXMgY2FuIGV4cG9ydCBhbiBgaXNsYW5kc2AgYXJyYXkgaW4gdGhlaXIgbWFpbiBlbnRyeS5cbiAqXG4gKiBDb252ZW50aW9uIChtaW5pbWFsIGF1Z21lbnRhdGlvbik6XG4gKiAtIF9yZW5kZXJlci50czogZXhwb3J0cyBhIHJlbmRlcmVyIHRoYXQgd3JhcHMgcm91dGUgVk5vZGVzXG4gKiAtIF9taWRkbGV3YXJlLnRzOiBleHBvcnRzIGEgSG9ubyBtaWRkbGV3YXJlIGZ1bmN0aW9uIGFwcGxpZWQgYmVmb3JlIHRoZSByb3V0ZVxuICogLSBGaWxlcyBzdGFydGluZyB3aXRoIF8gYXJlIG5vdCByb3V0ZSBoYW5kbGVycyBidXQgYXJlIGxvYWRlZCBieSB0aGUgZnJhbWV3b3JrXG4gKlxuICog4pSA4pSA4pSAIFNTUiBJbXBvcnQgRGlzY292ZXJ5IEF1ZGl0IChTdGVwMSkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gKlxuICogVGhpcyBmaWxlIGRpc2NvdmVycyBpc2xhbmRzIGJ1dCBkb2VzIE5PVCBpbXBvcnQgdGhlbSAoc3RhdGljIHNjYW4gb25seSk6XG4gKlxuICogMS4gTG9jYWwgaXNsYW5kIGZpbGVzOlxuICogICAgLSBTY2FubmVkIGJ5IGBzY2FuSXNsYW5kcygpYFxuICogICAgLSBNZXRhZGF0YSByZWFkIGJ5IGBzY2FuSXNsYW5kTWV0YSgpYCAoc3RhdGljLCBubyBpbXBvcnQpXG4gKiAgICAtIFNTUiBkZWNpc2lvbjogYG9wZW5FbGVtZW50LnNzcmAgZmllbGQgKHN0YXRpYyByZWFkLCBubyBpbXBvcnQpXG4gKlxuICogMi4gUGFja2FnZSBtYW5pZmVzdCBpc2xhbmRzOlxuICogICAgLSBEaXNjb3ZlcmVkIGJ5IGBzY2FuUGFja2FnZU1hbmlmZXN0cygpYFxuICogICAgLSBJbXBvcnRzIHBhY2thZ2UgbW9kdWxlIHRvIHJlYWQgYG1hbmlmZXN0YCBleHBvcnRcbiAqICAgIC0gQnJvd3Nlci1vbmx5IHBhY2thZ2VzOiBjYXVnaHQgYnkgdHJ5L2NhdGNoXG4gKiAgICAtIFNTUiBkZWNpc2lvbjogYG1hbmlmZXN0LmRlY2xhcmF0aW9uc1tdLm9wZW5FbGVtZW50LnNzcmAgZmllbGRcbiAqXG4gKiAzLiBDRU0gbWFuaWZlc3RzICh2MC4xOC4wKTpcbiAqICAgIC0gRGlzY292ZXJlZCBieSBgc2NhbkNlbU1hbmlmZXN0cygpYCAtIHJlYWRzIGN1c3RvbS1lbGVtZW50cy5qc29uIGZyb21cbiAqICAgICAgbm9kZV9tb2R1bGVzIHBhY2thZ2VzIFdJVEhPVVQgaW1wb3J0aW5nIHBhY2thZ2UgY29kZVxuICogICAgLSBSZXN1bHRzIGZlZCBpbnRvIHRoZSBjb21wYXRpYmlsaXR5IGNsYXNzaWZpZXIgKHBhcnNlQ2VtICsgY2xhc3NpZnlDZW1NYW5pZmVzdClcbiAqXG4gKiA0LiBOZXN0ZWQgY3VzdG9tIGVsZW1lbnRzIChmcm9tIHRoZSBWTm9kZSB0cmVlKTpcbiAqICAgIC0gTk9UIGhhbmRsZWQgaW4gdGhpcyBmaWxlXG4gKiAgICAtIFNlZTogYHBhY2thZ2VzL2NvcmUvc3JjL2pzeC1yZW5kZXItc3RyaW5nLnRzYCBhbmQgYHJlbmRlckRzZFRyZWUoKWBcbiAqXG4gKiBBdWRpdCBjb21wbGV0ZWQ6IDIwMjYtMDUtMTdcbiAqIEF1ZGl0b3I6IEFJIGFnZW50IChvcGVuRWxlbWVudCB2MC4xNy40IFNPUCBjb21wbGlhbmNlIGNoZWNrKVxuICpcbiAqIOKUgOKUgOKUgCB2MC40MS4wLWFscGhhLjE6IEFTVCByZW1vdmVkIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICpcbiAqIFJlcGxhY2VkIFR5cGVTY3JpcHQgQVNUIHNjYW5uaW5nIHdpdGggcmVnZXgvZ2xvYi1iYXNlZCBleHRyYWN0aW9uLlxuICogUm91dGUgYW5kIGlzbGFuZCBtb2R1bGVzIGFyZSBzaW1wbGUgRVNNIGZpbGVzOyBwYXJzaW5nIHRoZSB3aG9sZSBzb3VyY2VcbiAqIHdpdGggdGhlIFR5cGVTY3JpcHQgY29tcGlsZXIgaXMgb3ZlcmtpbGwgYW5kIGFkZHMgYSBoZWF2eSBkZXBlbmRlbmN5LlxuICovXG5cbmltcG9ydCB0eXBlIHtcbiAgQ29tcGF0aWJpbGl0eUNsYXNzaWZpY2F0aW9uLFxuICBSb3V0ZUVudHJ5LFxuICBTcGVjaWFsRmlsZVR5cGUsXG59IGZyb20gJ0BvcGVuZWxlbWVudC9wcm90b2NvbC9mcmFtZXdvcmsnO1xuaW1wb3J0IHR5cGUgeyBPcGVuRWxlbWVudFBhY2thZ2VNYW5pZmVzdCB9IGZyb20gJ0BvcGVuZWxlbWVudC9wcm90b2NvbC9tYW5pZmVzdCc7XG5pbXBvcnQgeyBmb3JtYXRFcnJvciwgT3BlbkVsZW1lbnRFcnJvciB9IGZyb20gJ0BvcGVuZWxlbWVudC9jb3JlL2Vycm9ycyc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZS9sb2dnZXInO1xuaW1wb3J0IHsgam9pbiwgcG9zaXgsIHNlcCB9IGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBjbGFzc2lmeUNlbU1hbmlmZXN0LCBwYXJzZUNlbSB9IGZyb20gJy4vY2VtLWNvbXBhdC5qcyc7XG5pbXBvcnQgeyBzYWZlUmVhZERpciwgc2FmZVJlYWRGaWxlLCBzYWZlU3RhdCB9IGZyb20gJy4vcm91dGUtc2Nhbm5lci1mcy5qcyc7XG5cbmNvbnN0IGxvZyA9IGNyZWF0ZUxvZ2dlcignY29yZScpO1xuXG4vKiogTG9jYWwgaXNsYW5kIG1ldGFkYXRhIGluZGV4ZWQgYnkgdGFnIG5hbWUuICovXG5leHBvcnQgaW50ZXJmYWNlIExvY2FsSXNsYW5kTWV0YSB7XG4gIHRhZ05hbWU6IHN0cmluZztcbiAgZmlsZVBhdGg6IHN0cmluZztcbiAgc3NyPzogYm9vbGVhbjtcbiAgZHNkPzogYm9vbGVhbjtcbiAgaHlkcmF0ZT86ICdsb2FkJyB8ICdpZGxlJyB8ICd2aXNpYmxlJyB8ICdvbmx5JztcbiAgcmVhc29uPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIFJlYWQgYSBzdGF0aWMgc3RyaW5nIGV4cG9ydCAoZS5nLiBgZXhwb3J0IGNvbnN0IHRhZ05hbWUgPSAnLi4uJ2ApIGZyb20gc291cmNlIHRleHQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWFkUm91dGVUYWdOYW1lKHNvdXJjZTogc3RyaW5nKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgbWF0Y2ggPSBzb3VyY2UubWF0Y2goL2V4cG9ydFxccytjb25zdFxccyt0YWdOYW1lXFxzKj1cXHMqKFtcIidgXSkoW15cIidgXSspXFwxLyk7XG4gIHJldHVybiBtYXRjaD8uWzJdO1xufVxuXG4vKipcbiAqIFJlYWQgYGV4cG9ydCBjb25zdCB0YWdOYW1lID0gJy4uLidgIGZyb20gYSByb3V0ZSBmaWxlIHVzaW5nIHJlZ2V4IHNjYW5uaW5nLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZFJvdXRlVGFnTmFtZUZyb21Nb2R1bGUoZmlsZVBhdGg6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IHNvdXJjZSA9IGF3YWl0IHNhZmVSZWFkRmlsZShmaWxlUGF0aCk7XG4gIGlmIChzb3VyY2UgPT09IHVuZGVmaW5lZCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgcmV0dXJuIHJlYWRSb3V0ZVRhZ05hbWUoc291cmNlKTtcbn1cblxuZnVuY3Rpb24gc3RhdGljT3BlbkVsZW1lbnRFcnJvcihtZXNzYWdlOiBzdHJpbmcpOiBPcGVuRWxlbWVudEVycm9yIHtcbiAgcmV0dXJuIG5ldyBPcGVuRWxlbWVudEVycm9yKFxuICAgIGBJbnZhbGlkIHN0YXRpYyBpc2xhbmQgbWV0YWRhdGEgZXhwb3J0IFwib3BlbkVsZW1lbnRcIjogJHttZXNzYWdlfS4gQWNjZXB0ZWQgc2hhcGU6IGV4cG9ydCBjb25zdCBvcGVuRWxlbWVudCA9IGRlZmluZUlzbGFuZENvbmZpZyh7IHNzcj86IGJvb2xlYW4sIGRzZD86IGJvb2xlYW4sIGh5ZHJhdGU/OiBcImxvYWRcIiB8IFwiaWRsZVwiIHwgXCJ2aXNpYmxlXCIgfCBcIm9ubHlcIiB9KS5gLFxuICAgIHtcbiAgICAgIGNvZGU6ICdJU0xBTkRfTUVUQURBVEFfRVJST1InLFxuICAgICAgc3RhdHVzQ29kZTogNTAwLFxuICAgICAgcmVjb3ZlcmFibGU6IGZhbHNlLFxuICAgIH0sXG4gICk7XG59XG5cbi8qKlxuICogdjAuNDEuMC1hbHBoYS4xOiBSZWdleC1iYXNlZCBleHRyYWN0aW9uIG9mXG4gKiBgZXhwb3J0IGNvbnN0IG9wZW5FbGVtZW50ID0gZGVmaW5lSXNsYW5kQ29uZmlnKHsgLi4uIH0pYC5cbiAqXG4gKiBUaGUgc2Nhbm5lciBpbnRlbnRpb25hbGx5IGRvZXMgbm90IGV4ZWN1dGUgaXNsYW5kIG1vZHVsZXMuIEl0IGFjY2VwdHMgb25seSBhXG4gKiBkZWZpbmVJc2xhbmRDb25maWcoKSBjYWxsIHdpdGggYm9vbGVhbiBgc3NyYC9gZHNkYCBhbmQgc3RyaW5nIGBoeWRyYXRlYFxuICogbGl0ZXJhbCB2YWx1ZXMuIER5bmFtaWMgbWV0YWRhdGEgaXMgcmVqZWN0ZWQgaW5zdGVhZCBvZiBndWVzc2VkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVhZFN0YXRpY09wZW5FbGVtZW50RXhwb3J0KHNvdXJjZTogc3RyaW5nKToge1xuICBzc3I/OiBib29sZWFuO1xuICBkc2Q/OiBib29sZWFuO1xuICBoeWRyYXRlPzogTG9jYWxJc2xhbmRNZXRhWydoeWRyYXRlJ107XG59IHwgbnVsbCB7XG4gIGNvbnN0IGRlY2xNYXRjaCA9IHNvdXJjZS5tYXRjaCgvZXhwb3J0XFxzK2NvbnN0XFxzK29wZW5FbGVtZW50XFxzKj0vKTtcbiAgaWYgKCFkZWNsTWF0Y2gpIHJldHVybiBudWxsO1xuXG4gIGNvbnN0IGFmdGVyRXF1YWxzID0gc291cmNlLnNsaWNlKGRlY2xNYXRjaC5pbmRleCEgKyBkZWNsTWF0Y2hbMF0ubGVuZ3RoKS50cmltU3RhcnQoKTtcblxuICAvLyBNdXN0IGNhbGwgZGVmaW5lSXNsYW5kQ29uZmlnKC4uLikgLSByZWplY3QgbGVnYWN5IG9iamVjdCBsaXRlcmFscy5cbiAgY29uc3QgY2FsbE1hdGNoID0gYWZ0ZXJFcXVhbHMubWF0Y2goL15kZWZpbmVJc2xhbmRDb25maWdcXHMqXFwoXFxzKihcXHtbXFxzXFxTXSo/XFx9KVxccypcXCkvKTtcbiAgaWYgKCFjYWxsTWF0Y2gpIHtcbiAgICBjb25zdCBoYXNDYWxsID0gL15kZWZpbmVJc2xhbmRDb25maWdcXHMqXFwoLy50ZXN0KGFmdGVyRXF1YWxzKTtcbiAgICBpZiAoaGFzQ2FsbCkge1xuICAgICAgdGhyb3cgc3RhdGljT3BlbkVsZW1lbnRFcnJvcihcbiAgICAgICAgJ2RlZmluZUlzbGFuZENvbmZpZygpIGFyZ3VtZW50IG11c3QgYmUgYSBzdGF0aWMgb2JqZWN0IGxpdGVyYWwnLFxuICAgICAgKTtcbiAgICB9XG4gICAgdGhyb3cgc3RhdGljT3BlbkVsZW1lbnRFcnJvcignb3BlbkVsZW1lbnQgZXhwb3J0IG11c3QgY2FsbCBkZWZpbmVJc2xhbmRDb25maWcoLi4uKScpO1xuICB9XG5cbiAgY29uc3QgYm9keSA9IGNhbGxNYXRjaFsxXS5zbGljZSgxLCAtMSk7XG4gIGNvbnN0IG1ldGE6IHsgc3NyPzogYm9vbGVhbjsgZHNkPzogYm9vbGVhbjsgaHlkcmF0ZT86IExvY2FsSXNsYW5kTWV0YVsnaHlkcmF0ZSddIH0gPSB7fTtcblxuICAvLyBNYXRjaCBrZXk6IHZhbHVlIHBhaXJzLCBza2lwcGluZyBuZXN0ZWQgYnJhY2VzL3N0cmluZ3MuXG4gIGNvbnN0IHByb3BSZSA9IC9cXGIoc3NyfGRzZHxoeWRyYXRlfFteXFxzOix7fV0rKVxccyo6XFxzKih0cnVlfGZhbHNlfFtcIiddKFteXCInXSopW1wiJ10pL2c7XG4gIGxldCBtOiBSZWdFeHBFeGVjQXJyYXkgfCBudWxsO1xuXG4gIHdoaWxlICgobSA9IHByb3BSZS5leGVjKGJvZHkpKSAhPT0gbnVsbCkge1xuICAgIGNvbnN0IGtleSA9IG1bMV07XG4gICAgY29uc3QgcmF3ID0gbVsyXTtcblxuICAgIGlmICghWydzc3InLCAnZHNkJywgJ2h5ZHJhdGUnXS5pbmNsdWRlcyhrZXkpKSB7XG4gICAgICB0aHJvdyBzdGF0aWNPcGVuRWxlbWVudEVycm9yKGB1bnN1cHBvcnRlZCBvcGVuRWxlbWVudCBtZXRhZGF0YSBrZXkgXCIke2tleX1cImApO1xuICAgIH1cblxuICAgIGNvbnN0IHR5cGVkS2V5ID0ga2V5IGFzICdzc3InIHwgJ2RzZCcgfCAnaHlkcmF0ZSc7XG5cbiAgICBpZiAodHlwZWRLZXkgPT09ICdzc3InIHx8IHR5cGVkS2V5ID09PSAnZHNkJykge1xuICAgICAgaWYgKHJhdyAhPT0gJ3RydWUnICYmIHJhdyAhPT0gJ2ZhbHNlJykge1xuICAgICAgICB0aHJvdyBzdGF0aWNPcGVuRWxlbWVudEVycm9yKGBvcGVuRWxlbWVudC4ke3R5cGVkS2V5fSBtdXN0IGJlIGEgYm9vbGVhbiBsaXRlcmFsYCk7XG4gICAgICB9XG4gICAgICBtZXRhW3R5cGVkS2V5XSA9IHJhdyA9PT0gJ3RydWUnO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgY29uc3QgdmFsdWUgPSByYXcuc2xpY2UoMSwgLTEpO1xuICAgIGlmICghWydsb2FkJywgJ2lkbGUnLCAndmlzaWJsZScsICdvbmx5J10uaW5jbHVkZXModmFsdWUpKSB7XG4gICAgICB0aHJvdyBzdGF0aWNPcGVuRWxlbWVudEVycm9yKGBvcGVuRWxlbWVudC5oeWRyYXRlIGhhcyB1bnN1cHBvcnRlZCB2YWx1ZSBcIiR7dmFsdWV9XCJgKTtcbiAgICB9XG4gICAgbWV0YS5oeWRyYXRlID0gdmFsdWUgYXMgTG9jYWxJc2xhbmRNZXRhWydoeWRyYXRlJ107XG4gIH1cblxuICByZXR1cm4gbWV0YTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IGEgZmlsZSBwYXRoIHRvIGEgVVJMIHBhdGggcGF0dGVybi5cbiAqIGUuZy4sICdpbmRleC50cycgLT4gJy8nLCAnYWJvdXQudHMnIC0+ICcvYWJvdXQnLCAncG9zdHMvW2lkXS50cycgLT4gJy9wb3N0cy86aWQnXG4gKlxuICogdjAuNic6IFVzZXMgVVJMUGF0dGVybi1jb21wYXRpYmxlIHN5bnRheCB3aGVyZSBwb3NzaWJsZS5cbiAqIFVSTFBhdHRlcm4gaXMgdGhlIFdIQVRXRyBzdGFuZGFyZCBmb3IgVVJMIG1hdGNoaW5nIChzZWN0aW9uNy4yKS5cbiAqIFBhdHRlcm4gOnBhcmFtIGlzIGNvbXBhdGlibGUgd2l0aCBib3RoIEhvbm8gYW5kIFVSTFBhdHRlcm4uXG4gKi9cbmZ1bmN0aW9uIGZpbGVQYXRoVG9Sb3V0ZVBhdGgoZmlsZVBhdGg6IHN0cmluZyk6IHN0cmluZyB7XG4gIC8vIE5vcm1hbGl6ZSBzZXBhcmF0b3JzIC0gaGFuZGxlIFdpbmRvd3MgYmFja3NsYXNoIHBhdGhzXG4gIC8vIHYwLjE0LjM6IFVzZSBwb3NpeC5qb2luIHRvIGVuc3VyZSBhbGwgb3V0cHV0IHBhdGhzIHVzZSBmb3J3YXJkIHNsYXNoZXNcbiAgLy8gcmVnYXJkbGVzcyBvZiBwbGF0Zm9ybS4gVGhpcyBwcmV2ZW50cyBcXCBmcm9tIGxlYWtpbmcgaW50byBVUkwgcGF0dGVybnMuXG4gIGxldCBwID0gZmlsZVBhdGguc3BsaXQoc2VwKS5qb2luKHBvc2l4LnNlcCk7XG5cbiAgLy8gdjAuMjU6IEFTVC12ZXJpZmllZCDigJQgcGF0aCB1dGlsaXR5LCByZWdleCBpcyB0aGUgYXBwcm9wcmlhdGUgdG9vbFxuICBwID0gcC5yZXBsYWNlKC9cXC5bXi5dKyQvLCAnJyk7XG5cbiAgLy8gdjAuMjU6IEFTVC12ZXJpZmllZCDigJQgcGF0aCB1dGlsaXR5LCBjb252ZXJ0cyBbcGFyYW1dIHRvIDpwYXJhbVxuICBwID0gcC5yZXBsYWNlKC9cXFsoW15cXF1dKylcXF0vZywgJzokMScpO1xuXG4gIC8vIEhhbmRsZSBpbmRleFxuICBpZiAocCA9PT0gJ2luZGV4JykgcmV0dXJuICcvJztcbiAgaWYgKHAuZW5kc1dpdGgoJy9pbmRleCcpKSB7XG4gICAgcCA9IHAuc2xpY2UoMCwgLTYpOyAvLyBSZW1vdmUgdHJhaWxpbmcgL2luZGV4XG4gICAgLy8gQWZ0ZXIgc3RyaXBwaW5nIC9pbmRleCwgY2hlY2sgaWYgdGhlIHJlc3VsdCBpcyB0aGUgcm9vdCBpbmRleFxuICAgIGlmIChwID09PSAnaW5kZXgnIHx8IHAgPT09ICcnKSByZXR1cm4gJy8nO1xuICB9XG5cbiAgLy8gRW5zdXJlIGxlYWRpbmcgc2xhc2hcbiAgaWYgKCFwLnN0YXJ0c1dpdGgoJy8nKSkgcCA9ICcvJyArIHA7XG5cbiAgcmV0dXJuIHA7XG59XG5cbi8qKlxuICogRGV0ZXJtaW5lIHJvdXRlIHR5cGUgZnJvbSBmaWxlIHBhdGguXG4gKiBGaWxlcyB1bmRlciAnYXBpLycgc3ViZGlyZWN0b3J5IGFyZSBBUEkgcm91dGVzLlxuICovXG5mdW5jdGlvbiBnZXRSb3V0ZVR5cGUoZmlsZVBhdGg6IHN0cmluZyk6ICdwYWdlJyB8ICdhcGknIHtcbiAgY29uc3Qgbm9ybWFsaXplZCA9IGZpbGVQYXRoLnNwbGl0KHNlcCkuam9pbihwb3NpeC5zZXApO1xuICByZXR1cm4gbm9ybWFsaXplZC5zdGFydHNXaXRoKCdhcGkvJykgfHwgbm9ybWFsaXplZC5pbmNsdWRlcygnL2FwaS8nKSA/ICdhcGknIDogJ3BhZ2UnO1xufVxuXG4vKipcbiAqIEdlbmVyYXRlIGEgdmFsaWQgSlMgdmFyaWFibGUgbmFtZSBmcm9tIGEgcm91dGUgcGF0aC5cbiAqIGUuZy4sICcvJyAtPiAnUm91dGVJbmRleCcsICcvYWJvdXQnIC0+ICdSb3V0ZUFib3V0JywgJy9wb3N0cy86aWQnIC0+ICdSb3V0ZVBvc3RzSWQnXG4gKi9cbmZ1bmN0aW9uIHBhdGhUb1Zhck5hbWUocGF0aDogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gdjAuMjU6IEFTVC12ZXJpZmllZCDigJQgcGF0aC10by1pZGVudGlmaWVyIHRyYW5zZm9ybWF0aW9uLCByZWdleCBpcyB0aGUgYXBwcm9wcmlhdGUgdG9vbFxuICBsZXQgbmFtZSA9IHBhdGhcbiAgICAucmVwbGFjZSgvXlxcLy8sICcnKVxuICAgIC5yZXBsYWNlKC9cXC8kLywgJycpXG4gICAgLnJlcGxhY2UoLzooW14vXSspL2csICckMScpXG4gICAgLnJlcGxhY2UoL1teYS16QS1aMC05XS9nLCAnXycpO1xuICBpZiAoIW5hbWUgfHwgbmFtZSA9PT0gJ18nKSBuYW1lID0gJ0luZGV4JztcbiAgcmV0dXJuICdSb3V0ZV8nICsgbmFtZS5jaGFyQXQoMCkudG9VcHBlckNhc2UoKSArIG5hbWUuc2xpY2UoMSk7XG59XG5cbi8qKlxuICogSWRlbnRpZnkgc3BlY2lhbCBmaWxlIHR5cGVzIGJ5IG5hbWUuXG4gKiBfcmVuZGVyZXIudHMgLT4gcmVuZGVyZXIsIF9taWRkbGV3YXJlLnRzIC0+IG1pZGRsZXdhcmVcbiAqL1xuLy8gcG9ueXRhaWw6IGlubGluZSBsb29rdXAgcmVwbGFjZXMgMi1jYXNlIHN3aXRjaFxuZnVuY3Rpb24gZ2V0U3BlY2lhbEZpbGVUeXBlKGZpbGVOYW1lOiBzdHJpbmcpOiBTcGVjaWFsRmlsZVR5cGUgfCBudWxsIHtcbiAgY29uc3QgYmFzZU5hbWUgPSBmaWxlTmFtZS5yZXBsYWNlKC9cXC5bXi5dKyQvLCAnJyk7XG4gIHJldHVybiAoeyBfcmVuZGVyZXI6ICdyZW5kZXJlcicsIF9taWRkbGV3YXJlOiAnbWlkZGxld2FyZScgfSBhcyBSZWNvcmQ8c3RyaW5nLCBTcGVjaWFsRmlsZVR5cGU+KVtcbiAgICBiYXNlTmFtZVxuICBdID8/IG51bGw7XG59XG5cbi8qKlxuICogQ2hlY2sgaWYgYSBmaWxlIHNob3VsZCBiZSBpZ25vcmVkIGZvciByb3V0aW5nLlxuICogRG90LWZpbGVzIGFyZSBhbHdheXMgaWdub3JlZC5cbiAqL1xuZnVuY3Rpb24gaXNJZ25vcmVkRmlsZShmaWxlTmFtZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiBmaWxlTmFtZS5zdGFydHNXaXRoKCcuJyk7XG59XG5cbi8qKlxuICogUmVjdXJzaXZlbHkgc2NhbiBhIGRpcmVjdG9yeSBmb3Igcm91dGUgZmlsZXMuXG4gKiBBbHNvIGNvbGxlY3RzIF9yZW5kZXJlci50cyBhbmQgX21pZGRsZXdhcmUudHMgc3BlY2lhbCBmaWxlcy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNjYW5Sb3V0ZXMoXG4gIHJvdXRlc0Rpcjogc3RyaW5nLFxuICBiYXNlRGlyOiBzdHJpbmcgPSAnJyxcbik6IFByb21pc2U8Um91dGVFbnRyeVtdPiB7XG4gIGNvbnN0IGVudHJpZXM6IFJvdXRlRW50cnlbXSA9IFtdO1xuICBjb25zdCBmaWxlcyA9IGF3YWl0IHNhZmVSZWFkRGlyKHJvdXRlc0Rpcik7XG5cbiAgaWYgKGZpbGVzID09PSB1bmRlZmluZWQpIHtcbiAgICBsb2cuZGVidWcoYFJvdXRlcyBkaXJlY3RvcnkgXCIke3JvdXRlc0Rpcn1cIiBub3QgZm91bmRgKTtcbiAgICByZXR1cm4gZW50cmllcztcbiAgfVxuXG4gIGZvciAoY29uc3QgZmlsZSBvZiBmaWxlcykge1xuICAgIGlmIChpc0lnbm9yZWRGaWxlKGZpbGUpKSBjb250aW51ZTtcblxuICAgIGNvbnN0IGZ1bGxQYXRoID0gam9pbihyb3V0ZXNEaXIsIGZpbGUpO1xuICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IGJhc2VEaXIgPyBqb2luKGJhc2VEaXIsIGZpbGUpIDogZmlsZTtcbiAgICBjb25zdCBmaWxlU3RhdCA9IGF3YWl0IHNhZmVTdGF0KGZ1bGxQYXRoKTtcbiAgICBpZiAoIWZpbGVTdGF0KSB7XG4gICAgICBsb2cuZGVidWcoYEZpbGUgdmFuaXNoZWQgYmVmb3JlIHN0YXQ6ICR7ZnVsbFBhdGh9YCk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBpZiAoZmlsZVN0YXQuaXNEaXJlY3RvcnkoKSkge1xuICAgICAgLy8gUmVjdXJzZSBpbnRvIHN1YmRpcmVjdG9yaWVzXG4gICAgICBjb25zdCBzdWJFbnRyaWVzID0gYXdhaXQgc2NhblJvdXRlcyhmdWxsUGF0aCwgcmVsYXRpdmVQYXRoKTtcbiAgICAgIGVudHJpZXMucHVzaCguLi5zdWJFbnRyaWVzKTtcbiAgICB9IGVsc2UgaWYgKC9cXC4odHN8dHN4fGpzfGpzeCkkLy50ZXN0KGZpbGUpKSB7XG4gICAgICAvLyBDaGVjayBmb3Igc3BlY2lhbCBmaWxlc1xuICAgICAgY29uc3Qgc3BlY2lhbFR5cGUgPSBnZXRTcGVjaWFsRmlsZVR5cGUoZmlsZSk7XG4gICAgICBpZiAoc3BlY2lhbFR5cGUpIHtcbiAgICAgICAgLy8gQWRkIGFzIGEgc3BlY2lhbCBlbnRyeSAtIG5vdCBhIHJvdXRlIGhhbmRsZXIsIGJ1dCBsb2FkYWJsZVxuICAgICAgICBlbnRyaWVzLnB1c2goe1xuICAgICAgICAgIHBhdGg6IGZpbGVQYXRoVG9Sb3V0ZVBhdGgocmVsYXRpdmVQYXRoKSxcbiAgICAgICAgICBmaWxlUGF0aDogcmVsYXRpdmVQYXRoLnNwbGl0KHNlcCkuam9pbihwb3NpeC5zZXApLFxuICAgICAgICAgIHR5cGU6ICdzcGVjaWFsJywgLy8gTm90IGEgcGFnZSBvciBBUEkgcm91dGUgLSByZW5kZXJlci9taWRkbGV3YXJlIG9ubHlcbiAgICAgICAgICB2YXJOYW1lOiBgU3BlY2lhbF8ke3NwZWNpYWxUeXBlfV8ke2Jhc2VEaXIucmVwbGFjZSgvW1xcXFwvXS9nLCAnXycpIHx8ICdyb290J31gLFxuICAgICAgICAgIHNwZWNpYWw6IHNwZWNpYWxUeXBlLFxuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSBpZiAoIWZpbGUuc3RhcnRzV2l0aCgnXycpKSB7XG4gICAgICAgIC8vIFJlZ3VsYXIgcm91dGUgZmlsZVxuICAgICAgICBjb25zdCByb3V0ZVBhdGggPSBmaWxlUGF0aFRvUm91dGVQYXRoKHJlbGF0aXZlUGF0aCk7XG4gICAgICAgIGNvbnN0IHJvdXRlVHlwZSA9IGdldFJvdXRlVHlwZShyZWxhdGl2ZVBhdGgpO1xuICAgICAgICAvLyB2MC4yNTogQVNULXZlcmlmaWVkIOKAlCBwYXRoIHV0aWxpdHksIGV4dHJhY3RzIFtwYXJhbV0gcGF0dGVybnNcbiAgICAgICAgY29uc3QgcGFyYW1NYXRjaGVzID0gcmVsYXRpdmVQYXRoLm1hdGNoKC9cXFsoW15cXF1dKylcXF0vZyk7XG4gICAgICAgIGNvbnN0IHBhcmFtcyA9IHBhcmFtTWF0Y2hlcyA/IHBhcmFtTWF0Y2hlcy5tYXAoKG0pID0+IG0uc2xpY2UoMSwgLTEpKSA6IHVuZGVmaW5lZDtcbiAgICAgICAgbGV0IHRhZ05hbWU6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICAgICAgaWYgKHJvdXRlVHlwZSA9PT0gJ3BhZ2UnKSB7XG4gICAgICAgICAgLy8gUmVnZXgtYmFzZWQgc2Nhbm5pbmcgcmVhZHMgYGV4cG9ydCBjb25zdCB0YWdOYW1lYCB3aXRob3V0IGV4ZWN1dGluZyB0aGUgbW9kdWxlLlxuICAgICAgICAgIHRhZ05hbWUgPSBhd2FpdCByZWFkUm91dGVUYWdOYW1lRnJvbU1vZHVsZShmdWxsUGF0aCk7XG4gICAgICAgICAgaWYgKHRhZ05hbWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgLy8gdGFnTmFtZSBub3QgZm91bmQgaXMgbm9ybWFsIOKAlCBub3QgYWxsIHBhZ2Ugcm91dGVzIGRlZmluZSBvbmVcbiAgICAgICAgICAgIGxvZy5kZWJ1ZyhgTm8gdGFnTmFtZSBleHBvcnQgZm91bmQgaW4gcm91dGUgbW9kdWxlOiAke2Z1bGxQYXRofWApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBlbnRyaWVzLnB1c2goe1xuICAgICAgICAgIHBhdGg6IHJvdXRlUGF0aCxcbiAgICAgICAgICBmaWxlUGF0aDogcmVsYXRpdmVQYXRoLnNwbGl0KHNlcCkuam9pbihwb3NpeC5zZXApLFxuICAgICAgICAgIHR5cGU6IHJvdXRlVHlwZSxcbiAgICAgICAgICB2YXJOYW1lOiBwYXRoVG9WYXJOYW1lKHJvdXRlUGF0aCksXG4gICAgICAgICAgdGFnTmFtZSxcbiAgICAgICAgICBwYXJhbXMsXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgLy8gT3RoZXIgXy1wcmVmaXhlZCBmaWxlcyAobm90IF9yZW5kZXJlci9fbWlkZGxld2FyZSkgYXJlIHNpbGVudGx5IHNraXBwZWRcbiAgICB9XG4gIH1cblxuICAvLyBTb3J0IHJvdXRlczogc3RhdGljIHBhdGhzIGZpcnN0LCB0aGVuIGR5bmFtaWNcbiAgZW50cmllcy5zb3J0KChhLCBiKSA9PiB7XG4gICAgLy8gU3BlY2lhbCBmaWxlcyBnbyB0byB0aGUgZW5kXG4gICAgaWYgKGEuc3BlY2lhbCB8fCBiLnNwZWNpYWwpIHtcbiAgICAgIGlmIChhLnNwZWNpYWwgJiYgIWIuc3BlY2lhbCkgcmV0dXJuIDE7XG4gICAgICBpZiAoIWEuc3BlY2lhbCAmJiBiLnNwZWNpYWwpIHJldHVybiAtMTtcbiAgICAgIHJldHVybiAwO1xuICAgIH1cbiAgICBjb25zdCBhRHluYW1pYyA9IGEucGF0aC5pbmNsdWRlcygnOicpO1xuICAgIGNvbnN0IGJEeW5hbWljID0gYi5wYXRoLmluY2x1ZGVzKCc6Jyk7XG4gICAgaWYgKGFEeW5hbWljICE9PSBiRHluYW1pYykgcmV0dXJuIGFEeW5hbWljID8gMSA6IC0xO1xuICAgIHJldHVybiBhLnBhdGgubG9jYWxlQ29tcGFyZShiLnBhdGgpO1xuICB9KTtcblxuICByZXR1cm4gZW50cmllcztcbn1cblxuLyoqXG4gKiB2MC4yNTogQVNULXZlcmlmaWVkIOKAlCBjb252ZXJ0cyBmaWxlIG5hbWUgdG8gYSB2YWxpZCBDdXN0b20gRWxlbWVudCB0YWcgbmFtZS5cbiAqIFVzZXMgcmVnZXggZm9yIHBhdGggbWFuaXB1bGF0aW9uIHNpbmNlIHRhZyBuYW1lcyBhcmUgZGVyaXZlZCBmcm9tIGZpbGUgcGF0aHMuXG4gKlxuICogRXhhbXBsZXM6XG4gKiAgICdteS1jb3VudGVyLnRzJyAgICAgICAgLT4gJ215LWNvdW50ZXInXG4gKiAgICdwb3N0cy9pbmRleC50cycgICAgICAgLT4gJ3Bvc3RzLWluZGV4J1xuICogICAnYWRtaW5cXFxcZGFzaGJvYXJkLnRzJyAgLT4gJ2FkbWluLWRhc2hib2FyZCdcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZpbGVUb1RhZ05hbWUoZmlsZU5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBmaWxlTmFtZVxuICAgIC5yZXBsYWNlKC9cXC5bXi5dKyQvLCAnJykgLy8gdjAuMjU6IEFTVC12ZXJpZmllZCDigJQgcmVtb3ZlIGV4dGVuc2lvblxuICAgIC5yZXBsYWNlKC9bXFxcXC9dL2csICctJykgLy8gdjAuMjU6IEFTVC12ZXJpZmllZCDigJQgcmVwbGFjZSBwYXRoIHNlcGFyYXRvcnMgd2l0aCBoeXBoZW5zXG4gICAgLnRvTG93ZXJDYXNlKCk7XG59XG5cbi8qKlxuICogU2NhbiBpc2xhbmRzIGRpcmVjdG9yeSByZWN1cnNpdmVseSBmb3IgaXNsYW5kIGZpbGVzLlxuICogUmV0dXJucyBwYXRocyByZWxhdGl2ZSB0byBpc2xhbmRzRGlyIChlLmcuLCBbJ215LWNvdW50ZXIudHMnLCAncG9zdHMvaW5kZXgudHMnXSkuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzY2FuSXNsYW5kcyhcbiAgaXNsYW5kc0Rpcjogc3RyaW5nLFxuICByZWxhdGl2ZURpcjogc3RyaW5nID0gJycsXG4pOiBQcm9taXNlPHN0cmluZ1tdPiB7XG4gIGNvbnN0IGZpbGVzOiBzdHJpbmdbXSA9IFtdO1xuICBjb25zdCBlbnRyaWVzID0gYXdhaXQgc2FmZVJlYWREaXIoaXNsYW5kc0Rpcik7XG5cbiAgaWYgKGVudHJpZXMgPT09IHVuZGVmaW5lZCkge1xuICAgIGxvZy5kZWJ1ZyhgSXNsYW5kcyBkaXJlY3RvcnkgXCIke2lzbGFuZHNEaXJ9XCIgbm90IGZvdW5kYCk7XG4gICAgcmV0dXJuIGZpbGVzO1xuICB9XG5cbiAgZm9yIChjb25zdCBlbnRyeSBvZiBlbnRyaWVzKSB7XG4gICAgaWYgKGVudHJ5LnN0YXJ0c1dpdGgoJy4nKSkgY29udGludWU7XG5cbiAgICBjb25zdCBmdWxsUGF0aCA9IGpvaW4oaXNsYW5kc0RpciwgZW50cnkpO1xuICAgIGNvbnN0IGZpbGVTdGF0ID0gYXdhaXQgc2FmZVN0YXQoZnVsbFBhdGgpO1xuICAgIGlmICghZmlsZVN0YXQpIHtcbiAgICAgIGxvZy5kZWJ1ZyhgSXNsYW5kIGZpbGUgdmFuaXNoZWQgYmVmb3JlIHN0YXQ6ICR7ZnVsbFBhdGh9YCk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBjb25zdCByZWxhdGl2ZVBhdGggPSByZWxhdGl2ZURpciA/IGpvaW4ocmVsYXRpdmVEaXIsIGVudHJ5KSA6IGVudHJ5O1xuXG4gICAgaWYgKGZpbGVTdGF0LmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgIGNvbnN0IHN1YkZpbGVzID0gYXdhaXQgc2NhbklzbGFuZHMoZnVsbFBhdGgsIHJlbGF0aXZlUGF0aCk7XG4gICAgICBmaWxlcy5wdXNoKC4uLnN1YkZpbGVzKTtcbiAgICB9IGVsc2UgaWYgKC9cXC4odHN8dHN4fGpzfGpzeCkkLy50ZXN0KGVudHJ5KSkge1xuICAgICAgZmlsZXMucHVzaChyZWxhdGl2ZVBhdGgpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBmaWxlcy5zb3J0KCk7XG59XG5cbi8qKlxuICogdjAuNDEuMC1hbHBoYS4xOiBSZWdleC1iYXNlZCDigJQgcmVhZHMgaXNsYW5kIG1ldGFkYXRhIGJ5IHN0YXRpY2FsbHkgc2Nhbm5pbmcgdGhlIG1vZHVsZVxuICogc291cmNlIGZvciBgZXhwb3J0IGNvbnN0IG9wZW5FbGVtZW50ID0gZGVmaW5lSXNsYW5kQ29uZmlnKHsgLi4uIH0pYC5cbiAqXG4gKiBTdXBwb3J0ZWQgZm9ybTpcbiAqICAgZXhwb3J0IGNvbnN0IG9wZW5FbGVtZW50ID0gZGVmaW5lSXNsYW5kQ29uZmlnKHsgc3NyOiBmYWxzZSwgZHNkOiBmYWxzZSwgaHlkcmF0ZTogJ29ubHknIH0pXG4gKlxuICogVGhpcyBpcyBtb3JlIHJlbGlhYmxlIHRoYW4gcmVnZXggYmVjYXVzZSBpdCBoYW5kbGVzOlxuICogLSBDb21tZW50cyBpbnNpZGUgdGhlIG9iamVjdCBsaXRlcmFsXG4gKiAtIENvbXB1dGVkIHByb3BlcnRpZXNcbiAqIC0gRGVzdHJ1Y3R1cmVkL3JlLWV4cG9ydGVkIHZhbHVlc1xuICogLSBDYW5vbmljYWwgZGVmaW5lSXNsYW5kQ29uZmlnKC4uLikgY2FsbHNcbiAqXG4gKiBJZiBhIG1vZHVsZSBjYW5ub3QgYmUgcmVhZCwgaXRzIG1ldGFkYXRhIGlzIHNpbGVudGx5IHNraXBwZWQuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzY2FuSXNsYW5kTWV0YShcbiAgaXNsYW5kc0Rpcjogc3RyaW5nLFxuICBpc2xhbmRGaWxlczogc3RyaW5nW10sXG4pOiBQcm9taXNlPFJlY29yZDxzdHJpbmcsIExvY2FsSXNsYW5kTWV0YT4+IHtcbiAgY29uc3QgbWV0YTogUmVjb3JkPHN0cmluZywgTG9jYWxJc2xhbmRNZXRhPiA9IHt9O1xuXG4gIGZvciAoY29uc3QgZmlsZVBhdGggb2YgaXNsYW5kRmlsZXMpIHtcbiAgICBjb25zdCB0YWdOYW1lID0gZmlsZVRvVGFnTmFtZShmaWxlUGF0aCk7XG4gICAgY29uc3QgZnVsbFBhdGggPSBqb2luKGlzbGFuZHNEaXIsIGZpbGVQYXRoKTtcblxuICAgIGNvbnN0IHNvdXJjZSA9IGF3YWl0IHNhZmVSZWFkRmlsZShmdWxsUGF0aCk7XG4gICAgaWYgKHNvdXJjZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBsb2cuZGVidWcoYFVuYWJsZSB0byByZWFkIGlzbGFuZCBtb2R1bGUgZm9yIG1ldGFkYXRhOiAke2Z1bGxQYXRofWApO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gUmVhZCB0aGUgYG9wZW5FbGVtZW50YCBleHBvcnQgZGlyZWN0bHk7IG5vIHJlZ2V4IG5lZWRlZC5cbiAgICBjb25zdCBvcGVuRWxlbWVudEV4cG9ydCA9IHJlYWRTdGF0aWNPcGVuRWxlbWVudEV4cG9ydChzb3VyY2UpO1xuICAgIGlmICghb3BlbkVsZW1lbnRFeHBvcnQpIGNvbnRpbnVlO1xuXG4gICAgY29uc3QgaHlkcmF0ZTogTG9jYWxJc2xhbmRNZXRhWydoeWRyYXRlJ10gPSBvcGVuRWxlbWVudEV4cG9ydC5oeWRyYXRlICYmXG4gICAgICAgIFsnbG9hZCcsICdpZGxlJywgJ3Zpc2libGUnLCAnb25seSddLmluY2x1ZGVzKG9wZW5FbGVtZW50RXhwb3J0Lmh5ZHJhdGUpXG4gICAgICA/IG9wZW5FbGVtZW50RXhwb3J0Lmh5ZHJhdGVcbiAgICAgIDogdW5kZWZpbmVkO1xuXG4gICAgbWV0YVt0YWdOYW1lXSA9IHtcbiAgICAgIHRhZ05hbWUsXG4gICAgICBmaWxlUGF0aCxcbiAgICAgIHNzcjogaHlkcmF0ZSA9PT0gJ29ubHknID8gZmFsc2UgOiBvcGVuRWxlbWVudEV4cG9ydC5zc3IsXG4gICAgICBkc2Q6IGh5ZHJhdGUgPT09ICdvbmx5JyA/IGZhbHNlIDogb3BlbkVsZW1lbnRFeHBvcnQuZHNkLFxuICAgICAgaHlkcmF0ZSxcbiAgICAgIHJlYXNvbjogaHlkcmF0ZSA9PT0gJ29ubHknXG4gICAgICAgID8gJ2xvY2FsIGlzbGFuZCBleHBvcnRzIG9wZW5FbGVtZW50Lmh5ZHJhdGU9b25seSdcbiAgICAgICAgOiBvcGVuRWxlbWVudEV4cG9ydC5zc3IgPT09IGZhbHNlXG4gICAgICAgID8gJ2xvY2FsIGlzbGFuZCBleHBvcnRzIG9wZW5FbGVtZW50LnNzcj1mYWxzZSdcbiAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgfTtcbiAgfVxuXG4gIHJldHVybiBtZXRhO1xufVxuXG4vKipcbiAqIFNjYW4gcGFja2FnZSBleHBvcnRzIGZvciBPcGVuRWxlbWVudFBhY2thZ2VNYW5pZmVzdC5cbiAqIFBhY2thZ2VzIHNob3VsZCBleHBvcnQgYSBgbWFuaWZlc3RgIE9wZW5FbGVtZW50UGFja2FnZU1hbmlmZXN0IGluIHRoZWlyIG1haW4gZW50cnkuXG4gKlxuICogRXhhbXBsZSBwYWNrYWdlIGV4cG9ydDpcbiAqIGBgYHRzXG4gKiAvLyBAb3BlbmVsZW1lbnQvdWkvaW5kZXgudHNcbiAqIGV4cG9ydCB7IG1hbmlmZXN0IH0gZnJvbSAnLi9tYW5pZmVzdC5qcyc7XG4gKiBgYGBcbiAqXG4gKiBAcGFyYW0gcGFja2FnZU5hbWVzIC0gTGlzdCBvZiBwYWNrYWdlIG5hbWVzIHRvIHNjYW4gKGUuZy4sIFsnQG9wZW5lbGVtZW50L3VpJ10pXG4gKiBAcmV0dXJucyBBcnJheSBvZiBPcGVuRWxlbWVudFBhY2thZ2VNYW5pZmVzdFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2NhblBhY2thZ2VNYW5pZmVzdHMoXG4gIHBhY2thZ2VOYW1lczogc3RyaW5nW10sXG4pOiBQcm9taXNlPE9wZW5FbGVtZW50UGFja2FnZU1hbmlmZXN0W10+IHtcbiAgY29uc3QgYWxsTWFuaWZlc3RzOiBPcGVuRWxlbWVudFBhY2thZ2VNYW5pZmVzdFtdID0gW107XG5cbiAgZm9yIChjb25zdCBwa2cgb2YgcGFja2FnZU5hbWVzKSB7XG4gICAgLy8gQHZpdGUtaWdub3JlIHN1cHByZXNzZXMgdW5hbmFseXphYmxlLWR5bmFtaWMtaW1wb3J0IEpTUiB3YXJuaW5nLlxuICAgIGxldCBtb2Q6IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgIHRyeSB7XG4gICAgICBtb2QgPSBhd2FpdCBpbXBvcnQoLyogQHZpdGUtaWdub3JlICovIHBrZykgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgaWYgKGlzQnJvd3Nlck9ubHlQYWNrYWdlSW1wb3J0RXJyb3IoZSkpIHtcbiAgICAgICAgbG9nLndhcm4oXG4gICAgICAgICAgYFNraXBwaW5nIHBhY2thZ2UgbWFuaWZlc3QgZnJvbSBcIiR7cGtnfVwiOiBicm93c2VyLW9ubHkgcGFja2FnZSBjYW5ub3QgYmUgaW1wb3J0ZWQgZHVyaW5nIFNTUiBkaXNjb3ZlcnlgLFxuICAgICAgICApO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIHRocm93IG5ldyBPcGVuRWxlbWVudEVycm9yKFxuICAgICAgICBgRmFpbGVkIHRvIHNjYW4gcGFja2FnZSBtYW5pZmVzdCBmcm9tIFwiJHtwa2d9XCI6ICR7Zm9ybWF0RXJyb3IoZSl9YCxcbiAgICAgICAge1xuICAgICAgICAgIGNvZGU6ICdQQUNLQUdFX1NDQU5fRVJST1InLFxuICAgICAgICAgIHN0YXR1c0NvZGU6IDUwMCxcbiAgICAgICAgICByZWNvdmVyYWJsZTogZmFsc2UsXG4gICAgICAgIH0sXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAobW9kLm1hbmlmZXN0ICYmIHR5cGVvZiBtb2QubWFuaWZlc3QgPT09ICdvYmplY3QnKSB7XG4gICAgICBjb25zdCBtYW5pZmVzdCA9IG1vZC5tYW5pZmVzdCBhcyBPcGVuRWxlbWVudFBhY2thZ2VNYW5pZmVzdDtcbiAgICAgIGlmIChtYW5pZmVzdC5wYWNrYWdlTmFtZSAmJiBtYW5pZmVzdC5kZWNsYXJhdGlvbnMpIHtcbiAgICAgICAgYWxsTWFuaWZlc3RzLnB1c2gobWFuaWZlc3QpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhyb3cgbmV3IE9wZW5FbGVtZW50RXJyb3IoXG4gICAgICAgICAgYEludmFsaWQgbWFuaWZlc3QgaW4gJHtwa2d9OiBtaXNzaW5nIHBhY2thZ2VOYW1lIG9yIGRlY2xhcmF0aW9uc2AsXG4gICAgICAgICAge1xuICAgICAgICAgICAgY29kZTogJ1BBQ0tBR0VfTUFOSUZFU1RfRVJST1InLFxuICAgICAgICAgICAgc3RhdHVzQ29kZTogNTAwLFxuICAgICAgICAgICAgcmVjb3ZlcmFibGU6IGZhbHNlLFxuICAgICAgICAgIH0sXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBPcGVuRWxlbWVudEVycm9yKFxuICAgICAgICBgUGFja2FnZSAke3BrZ30gZG9lcyBub3QgZXhwb3J0IGEgbWFuaWZlc3RgLFxuICAgICAgICB7XG4gICAgICAgICAgY29kZTogJ1BBQ0tBR0VfTUFOSUZFU1RfRVJST1InLFxuICAgICAgICAgIHN0YXR1c0NvZGU6IDUwMCxcbiAgICAgICAgICByZWNvdmVyYWJsZTogZmFsc2UsXG4gICAgICAgIH0sXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBhbGxNYW5pZmVzdHM7XG59XG5cbi8qKlxuICogdjAuMjU6IEFTVC12ZXJpZmllZCDigJQgZXJyb3IgbWVzc2FnZSBjbGFzc2lmaWNhdGlvbiwgcmVnZXggaXMgdGhlIGFwcHJvcHJpYXRlIHRvb2xcbiAqIGZvciBtYXRjaGluZyBydW50aW1lIGVycm9yIHN0cmluZ3MgZnJvbSBmYWlsZWQgZHluYW1pYyBpbXBvcnRzLlxuICovXG5mdW5jdGlvbiBpc0Jyb3dzZXJPbmx5UGFja2FnZUltcG9ydEVycm9yKGVycm9yOiB1bmtub3duKTogYm9vbGVhbiB7XG4gIGNvbnN0IG1lc3NhZ2UgPSBmb3JtYXRFcnJvcihlcnJvcik7XG4gIHJldHVybiAvXFxiKHdpbmRvd3xkb2N1bWVudHxIVE1MRWxlbWVudHxjdXN0b21FbGVtZW50c3xuYXZpZ2F0b3IpXFxiLipcXGJpcyBub3QgZGVmaW5lZFxcYi9pLnRlc3QoXG4gICAgbWVzc2FnZSxcbiAgKTtcbn1cblxuLy8g4pSA4pSA4pSAIENFTSBBdXRvLURldGVjdGlvbiAodjAuMTguMCkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbi8qKiBSZXN1bHQgb2Ygc2Nhbm5pbmcgbm9kZV9tb2R1bGVzIGZvciBDRU0gbWFuaWZlc3RzICovXG5leHBvcnQgaW50ZXJmYWNlIENlbVNjYW5SZXN1bHQge1xuICAvKiogUGFja2FnZSBuYW1lIChlLmcuICdAb3BlbmVsZW1lbnQvdWknKSAqL1xuICBwYWNrYWdlTmFtZTogc3RyaW5nO1xuICAvKiogQWJzb2x1dGUgcGF0aCB0byBjdXN0b20tZWxlbWVudHMuanNvbiAqL1xuICBjZW1QYXRoOiBzdHJpbmc7XG4gIC8qKiBSYXcgSlNPTiBjb250ZW50ICovXG4gIGpzb246IHN0cmluZztcbn1cblxuLyoqXG4gKiBTY2FuIG5vZGVfbW9kdWxlcyBmb3IgcGFja2FnZXMgdGhhdCBzaGlwIGEgYGN1c3RvbS1lbGVtZW50cy5qc29uYC5cbiAqXG4gKiBTdHJhdGVneTpcbiAqICAgMS4gUmVhZCBub2RlX21vZHVsZXMgZGlyZWN0b3J5IGVudHJpZXMgKHRvcC1sZXZlbCBwYWNrYWdlcyArIHNjb3BlZCBvcmdzKVxuICogICAyLiBGb3IgZWFjaCBwYWNrYWdlLCBjaGVjayBpZiBgPHBrZz4vY3VzdG9tLWVsZW1lbnRzLmpzb25gIGV4aXN0c1xuICogICAzLiBSZXR1cm4gdGhlIHJhdyBKU09OIC0gY2FsbGVyIGlzIHJlc3BvbnNpYmxlIGZvciBwYXJzaW5nICsgY2xhc3NpZnlpbmdcbiAqXG4gKiBUaGlzIGZ1bmN0aW9uIHJlYWRzIGZpbGVzIG9ubHkuIEl0IG5ldmVyIGltcG9ydHMgb3IgZXhlY3V0ZXMgcGFja2FnZSBjb2RlLlxuICpcbiAqIEBwYXJhbSBub2RlTW9kdWxlc0RpciAtIEFic29sdXRlIHBhdGggdG8gdGhlIG5vZGVfbW9kdWxlcyBkaXJlY3RvcnlcbiAqIEByZXR1cm5zIEFycmF5IG9mIGZvdW5kIENFTSBtYW5pZmVzdHNcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNjYW5DZW1NYW5pZmVzdHMoXG4gIG5vZGVNb2R1bGVzRGlyOiBzdHJpbmcsXG4pOiBQcm9taXNlPENlbVNjYW5SZXN1bHRbXT4ge1xuICBjb25zdCByZXN1bHRzOiBDZW1TY2FuUmVzdWx0W10gPSBbXTtcblxuICBjb25zdCBlbnRyaWVzID0gYXdhaXQgc2FmZVJlYWREaXIobm9kZU1vZHVsZXNEaXIpO1xuICBpZiAoIWVudHJpZXMpIHJldHVybiByZXN1bHRzO1xuXG4gIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgIGlmIChlbnRyeS5zdGFydHNXaXRoKCcuJykpIGNvbnRpbnVlO1xuXG4gICAgaWYgKGVudHJ5LnN0YXJ0c1dpdGgoJ0AnKSkge1xuICAgICAgLy8gU2NvcGVkIHBhY2thZ2UgZGlyZWN0b3J5IC0gcmVjdXJzZSBvbmUgbGV2ZWxcbiAgICAgIGNvbnN0IHNjb3BlRGlyID0gam9pbihub2RlTW9kdWxlc0RpciwgZW50cnkpO1xuICAgICAgY29uc3Qgc2NvcGVkRW50cmllcyA9IGF3YWl0IHNhZmVSZWFkRGlyKHNjb3BlRGlyKTtcbiAgICAgIGlmICghc2NvcGVkRW50cmllcykgY29udGludWU7XG4gICAgICBmb3IgKGNvbnN0IHNjb3BlZEVudHJ5IG9mIHNjb3BlZEVudHJpZXMpIHtcbiAgICAgICAgaWYgKHNjb3BlZEVudHJ5LnN0YXJ0c1dpdGgoJy4nKSkgY29udGludWU7XG4gICAgICAgIGNvbnN0IHBhY2thZ2VOYW1lID0gYCR7ZW50cnl9LyR7c2NvcGVkRW50cnl9YDtcbiAgICAgICAgY29uc3QgY2VtUGF0aCA9IGpvaW4obm9kZU1vZHVsZXNEaXIsIGVudHJ5LCBzY29wZWRFbnRyeSwgJ2N1c3RvbS1lbGVtZW50cy5qc29uJyk7XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRyeVJlYWRDZW1GaWxlKGNlbVBhdGgsIHBhY2thZ2VOYW1lKTtcbiAgICAgICAgaWYgKHJlc3VsdCkgcmVzdWx0cy5wdXNoKHJlc3VsdCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFJlZ3VsYXIgKG5vbi1zY29wZWQpIHBhY2thZ2VcbiAgICAgIGNvbnN0IGNlbVBhdGggPSBqb2luKG5vZGVNb2R1bGVzRGlyLCBlbnRyeSwgJ2N1c3RvbS1lbGVtZW50cy5qc29uJyk7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0cnlSZWFkQ2VtRmlsZShjZW1QYXRoLCBlbnRyeSk7XG4gICAgICBpZiAocmVzdWx0KSByZXN1bHRzLnB1c2gocmVzdWx0KTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcmVzdWx0cztcbn1cblxuLyoqXG4gKiBUcnkgdG8gcmVhZCBhIGN1c3RvbS1lbGVtZW50cy5qc29uIGZpbGUuXG4gKiBSZXR1cm5zIG51bGwgaWYgdGhlIGZpbGUgZG9lc24ndCBleGlzdCBvciBjYW4ndCBiZSByZWFkLlxuICovXG5hc3luYyBmdW5jdGlvbiB0cnlSZWFkQ2VtRmlsZShcbiAgY2VtUGF0aDogc3RyaW5nLFxuICBwYWNrYWdlTmFtZTogc3RyaW5nLFxuKTogUHJvbWlzZTxDZW1TY2FuUmVzdWx0IHwgbnVsbD4ge1xuICBjb25zdCBqc29uID0gYXdhaXQgc2FmZVJlYWRGaWxlKGNlbVBhdGgpO1xuICByZXR1cm4ganNvbiA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IHsgcGFja2FnZU5hbWUsIGNlbVBhdGgsIGpzb24gfTtcbn1cblxuLyoqXG4gKiBSdW4gQ0VNIGF1dG8tZGV0ZWN0aW9uOiBzY2FuIG5vZGVfbW9kdWxlcywgcGFyc2UgZWFjaCBtYW5pZmVzdCxcbiAqIGFuZCBjbGFzc2lmeSBhbGwgZGlzY292ZXJlZCBjb21wb25lbnRzLlxuICpcbiAqIFRoaXMgaXMgdGhlIGhpZ2gtbGV2ZWwgZnVuY3Rpb24gY2FsbGVkIGZyb20gdGhlIFZpdGUgcGx1Z2luIGJ1aWxkU3RhcnQoKS5cbiAqIEl0IGNvbWJpbmVzIHNjYW5DZW1NYW5pZmVzdHMoKSArIHBhcnNlQ2VtKCkgKyBjbGFzc2lmeUNlbU1hbmlmZXN0KClcbiAqIGludG8gYSBzaW5nbGUgcGlwZWxpbmUuXG4gKlxuICogQHBhcmFtIG5vZGVNb2R1bGVzRGlyIC0gQWJzb2x1dGUgcGF0aCB0byBub2RlX21vZHVsZXNcbiAqIEByZXR1cm5zIEFycmF5IG9mIGNvbXBhdGliaWxpdHkgY2xhc3NpZmljYXRpb25zIChtYXkgYmUgZW1wdHkgaWYgbm8gQ0VNIGZvdW5kKVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZGV0ZWN0QW5kQ2xhc3NpZnlDZW1QYWNrYWdlcyhcbiAgbm9kZU1vZHVsZXNEaXI6IHN0cmluZyxcbik6IFByb21pc2U8Q29tcGF0aWJpbGl0eUNsYXNzaWZpY2F0aW9uW10+IHtcbiAgY29uc3QgY2VtUmVzdWx0cyA9IGF3YWl0IHNjYW5DZW1NYW5pZmVzdHMobm9kZU1vZHVsZXNEaXIpO1xuICBpZiAoY2VtUmVzdWx0cy5sZW5ndGggPT09IDApIHJldHVybiBbXTtcblxuICBjb25zdCBhbGxDbGFzc2lmaWNhdGlvbnM6IENvbXBhdGliaWxpdHlDbGFzc2lmaWNhdGlvbltdID0gW107XG5cbiAgZm9yIChjb25zdCB7IHBhY2thZ2VOYW1lLCBqc29uIH0gb2YgY2VtUmVzdWx0cykge1xuICAgIGNvbnN0IHBhcnNlUmVzdWx0ID0gcGFyc2VDZW0oanNvbik7XG4gICAgaWYgKCFwYXJzZVJlc3VsdC5zdWNjZXNzIHx8ICFwYXJzZVJlc3VsdC5tYW5pZmVzdCkge1xuICAgICAgbG9nLmRlYnVnKFxuICAgICAgICBgU2tpcHBpbmcgaW52YWxpZCBDRU0gbWFuaWZlc3QgZnJvbSBcIiR7cGFja2FnZU5hbWV9XCI6IGAgK1xuICAgICAgICAgIHBhcnNlUmVzdWx0LmVycm9ycy5tYXAoKGUpID0+IGUubWVzc2FnZSkuam9pbignOyAnKSxcbiAgICAgICk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICAvLyBBdHRhY2ggcGFja2FnZSBuYW1lIHRvIHRoZSBtYW5pZmVzdCBmb3IgYmV0dGVyIGRpYWdub3N0aWNzXG4gICAgY29uc3QgbWFuaWZlc3QgPSB7IC4uLnBhcnNlUmVzdWx0Lm1hbmlmZXN0LCBwYWNrYWdlTmFtZSB9O1xuICAgIGNvbnN0IGNsYXNzUmVzdWx0ID0gY2xhc3NpZnlDZW1NYW5pZmVzdChtYW5pZmVzdCk7XG5cbiAgICAvLyBMb2cgc3VtbWFyeVxuICAgIGNvbnN0IHsgc3RhdHMgfSA9IGNsYXNzUmVzdWx0O1xuICAgIGlmIChzdGF0cy50b3RhbENvbXBvbmVudHMgPiAwKSB7XG4gICAgICBsb2cuaW5mbyhcbiAgICAgICAgYENFTTogJHtwYWNrYWdlTmFtZX0gLSAke3N0YXRzLnRvdGFsQ29tcG9uZW50c30gY29tcG9uZW50KHMpOiBgICtcbiAgICAgICAgICBgJHtzdGF0cy5zc3JDYXBhYmxlQ291bnR9IHNzci1jYXBhYmxlLCAke3N0YXRzLmNsaWVudE9ubHlDb3VudH0gY2xpZW50LW9ubHlgICtcbiAgICAgICAgICAoc3RhdHMucmVqZWN0ZWRDb3VudCA+IDAgPyBgLCAke3N0YXRzLnJlamVjdGVkQ291bnR9IHJlamVjdGVkYCA6ICcnKSArXG4gICAgICAgICAgKHN0YXRzLmV4cGVyaW1lbnRhbERvbUNvdW50ID4gMCA/IGAsICR7c3RhdHMuZXhwZXJpbWVudGFsRG9tQ291bnR9IGV4cGVyaW1lbnRhbGAgOiAnJyksXG4gICAgICApO1xuICAgIH1cblxuICAgIGFsbENsYXNzaWZpY2F0aW9ucy5wdXNoKC4uLmNsYXNzUmVzdWx0LmNsYXNzaWZpY2F0aW9ucyk7XG4gIH1cblxuICByZXR1cm4gYWxsQ2xhc3NpZmljYXRpb25zO1xufVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Q0FnREMsR0FRRCxTQUFTLFdBQVcsRUFBRSxnQkFBZ0IsUUFBUSwyQkFBMkI7QUFDekUsU0FBUyxZQUFZLFFBQVEsMkJBQTJCO0FBQ3hELFNBQVMsSUFBSSxFQUFFLEtBQUssRUFBRSxHQUFHLFFBQVEsWUFBWTtBQUM3QyxTQUFTLG1CQUFtQixFQUFFLFFBQVEsUUFBUSxrQkFBa0I7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
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @openelement/adapter-vite - Route Type Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates `.openElement/routes.d.ts` with type-safe route parameter definitions
|
|
5
|
+
* for the `virtual:open-routes` module. Only routes that have dynamic [param]
|
|
6
|
+
* segments are included in the output.
|
|
7
|
+
*
|
|
8
|
+
* v0.25.0: Initial implementation for build-time route type code generation.
|
|
9
|
+
*/ import type { RouteEntry } from '@openelement/protocol/framework';
|
|
10
|
+
/**
|
|
11
|
+
* Generate the `.openElement/routes.d.ts` declaration file content.
|
|
12
|
+
*
|
|
13
|
+
* Only routes that have dynamic parameters (non-empty `params` array)
|
|
14
|
+
* are included in the generated output. Static routes with no params
|
|
15
|
+
* are excluded since they don't need type-safe parameter access.
|
|
16
|
+
*
|
|
17
|
+
* Example output:
|
|
18
|
+
* ```typescript
|
|
19
|
+
* declare module 'virtual:open-routes' {
|
|
20
|
+
* interface RouteParams {
|
|
21
|
+
* '/blog/[slug]': { slug: string };
|
|
22
|
+
* '/registry/[package]/[component]': { package: string; component: string };
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @param routes - Array of route entries from the route scanner
|
|
28
|
+
* @returns TypeScript declaration file content as a string
|
|
29
|
+
*/ export declare function generateRouteTypes(routes: RouteEntry[]): string;
|