@openelement/adapter-vite 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.
@@ -0,0 +1,248 @@
1
+ /**
2
+ * @openelement/adapter-vite — Head injection validation & serialization.
3
+ *
4
+ * Extracted from index.ts in v0.22 (SOP-004: adapter-vite decomposition).
5
+ *
6
+ * Provides:
7
+ * - assertNoScriptTags() — script tag safety check for head fragments
8
+ * - validateSafeUrl() — URL protocol validation against XSS vectors
9
+ * - buildHeadExtras() — serialize headFragments, stylesheets, and scripts
10
+ * into a single headExtras string
11
+ */ import { OpenElementError } from '@openelement/core/errors';
12
+ import { escapeAttr as escapeHtmlAttr } from '@openelement/core';
13
+ import { createLogger } from '@openelement/core/logger';
14
+ // @deno-types="npm:@types/sanitize-html@^2"
15
+ import sanitizeHtml from 'sanitize-html';
16
+ const log = createLogger('adapter-vite:head-injection');
17
+ const SAFE_SCHEMES = [
18
+ 'http',
19
+ 'https',
20
+ 'mailto',
21
+ 'tel',
22
+ 'sms'
23
+ ];
24
+ const HEAD_SANITIZE_OPTIONS = {
25
+ allowedTags: [
26
+ 'base',
27
+ 'link',
28
+ 'meta',
29
+ 'noscript',
30
+ 'style',
31
+ 'title'
32
+ ],
33
+ allowedAttributes: {
34
+ base: [
35
+ 'href',
36
+ 'target'
37
+ ],
38
+ link: [
39
+ 'as',
40
+ 'crossorigin',
41
+ 'href',
42
+ 'hreflang',
43
+ 'imagesizes',
44
+ 'imagesrcset',
45
+ 'integrity',
46
+ 'media',
47
+ 'referrerpolicy',
48
+ 'rel',
49
+ 'sizes',
50
+ 'title',
51
+ 'type'
52
+ ],
53
+ meta: [
54
+ 'charset',
55
+ 'content',
56
+ 'http-equiv',
57
+ 'name',
58
+ 'property'
59
+ ],
60
+ noscript: [],
61
+ style: [
62
+ 'media',
63
+ 'nonce',
64
+ 'title'
65
+ ],
66
+ title: []
67
+ },
68
+ allowedSchemes: SAFE_SCHEMES,
69
+ allowedSchemesByTag: {
70
+ link: SAFE_SCHEMES,
71
+ base: [
72
+ 'http',
73
+ 'https'
74
+ ]
75
+ },
76
+ allowedSchemesAppliedToAttributes: [
77
+ 'href',
78
+ 'src',
79
+ 'action',
80
+ 'formaction',
81
+ 'xlink:href'
82
+ ],
83
+ allowProtocolRelative: false,
84
+ allowVulnerableTags: true
85
+ };
86
+ function sanitizeHeadHtml(html, context) {
87
+ const sanitized = sanitizeHtml(html, HEAD_SANITIZE_OPTIONS);
88
+ if (sanitized.trim() !== html.trim()) {
89
+ log.warn(`${context} contained unsafe head markup; sanitized before injection`);
90
+ }
91
+ return sanitized;
92
+ }
93
+ function assertSafeAttributeName(name, context) {
94
+ if (!/^[A-Za-z_:][A-Za-z0-9_.:-]*$/.test(name) || /^on/i.test(name)) {
95
+ throw new OpenElementError(`Unsafe attribute in ${context}: "${name}"`, {
96
+ code: 'UNSAFE_HEAD_INJECTION',
97
+ statusCode: 400,
98
+ recoverable: false
99
+ });
100
+ }
101
+ }
102
+ /**
103
+ * Assert that HTML content does NOT contain <script> tags.
104
+ * Scripts must go through inject.scripts for URL validation.
105
+ */ export function assertNoScriptTags(html, context) {
106
+ if (/<script[\s>/]/i.test(html)) {
107
+ throw new OpenElementError(`${context} must not contain <script> tags. Use inject.scripts for scripts so ` + 'openElement can validate script URLs and mark the generated head injection as trusted.', {
108
+ code: 'UNSAFE_HEAD_INJECTION',
109
+ statusCode: 400,
110
+ recoverable: false
111
+ });
112
+ }
113
+ }
114
+ /**
115
+ * Validate a URL string for known XSS vectors (javascript:, data:, etc.).
116
+ * Also validates percent-encoding is not malformed.
117
+ * Returns the normalized URL string.
118
+ */ export function validateSafeUrl(url, context) {
119
+ // Normalise: decode URL encoding, strip whitespace, lowercase
120
+ const normalised = url.trim();
121
+ try {
122
+ const decoded = decodeURIComponent(normalised); // catch malformed %XX
123
+ const lower = decoded.toLowerCase().trim();
124
+ const blockedProtocols = [
125
+ 'javascript:',
126
+ 'data:',
127
+ 'vbscript:',
128
+ 'file:'
129
+ ];
130
+ for (const proto of blockedProtocols){
131
+ if (lower.startsWith(proto)) {
132
+ throw new OpenElementError(`Unsafe URL in ${context}: "${url}" - ${proto} protocol is not allowed`, {
133
+ code: 'UNSAFE_URL',
134
+ statusCode: 400,
135
+ recoverable: false
136
+ });
137
+ }
138
+ }
139
+ } catch (e) {
140
+ // H-01 fix: Re-throw OpenElementError so security warnings are not swallowed
141
+ if (e instanceof OpenElementError) throw e;
142
+ // v0.14.3: decodeURIComponent can throw for two reasons:
143
+ // 1. Malicious URLs with invalid percent-encoding (e.g., "%ZZ")
144
+ // 2. Legitimate URLs with lone surrogates (rare, but valid URI-encoded)
145
+ // We treat actual URIError as unsafe, but log the distinction for debugging.
146
+ if (e instanceof URIError) {
147
+ log.debug(`decodeURIComponent failed for URL in ${context}: "${url}" - ${e.message}. ` + 'This may be a legitimate encoding issue or a malicious URL.');
148
+ }
149
+ throw new OpenElementError(`Invalid URL in ${context}: "${url}" - malformed percent-encoding`, {
150
+ code: 'UNSAFE_URL',
151
+ statusCode: 400,
152
+ recoverable: false
153
+ });
154
+ }
155
+ return normalised;
156
+ }
157
+ /**
158
+ * Build the headExtras string from FrameworkOptions.inject.
159
+ *
160
+ * Serializes headFragments, stylesheets, and scripts into a single
161
+ * HTML string to inject into <head>. Validates all URLs and ensures
162
+ * no raw <script> tags bypass the structured injection APIs.
163
+ */ export function buildHeadExtras(options) {
164
+ // If direct headExtras provided, validate and return
165
+ if (options.headExtras) {
166
+ assertNoScriptTags(options.headExtras, 'headExtras');
167
+ return {
168
+ headExtras: sanitizeHeadHtml(options.headExtras, 'headExtras'),
169
+ allowHeadExtrasScripts: false
170
+ };
171
+ }
172
+ // No inject config - no head extras
173
+ if (!options.inject) {
174
+ return {
175
+ headExtras: undefined,
176
+ allowHeadExtrasScripts: false
177
+ };
178
+ }
179
+ const fragments = [];
180
+ // headFragments FIRST (meta, styles, anti-flash) - must exist in DOM
181
+ // before scripts that reference them (e.g. theme-init.js removes anti-flash).
182
+ for (const frag of options.inject.headFragments || []){
183
+ assertNoScriptTags(frag, 'inject.headFragments');
184
+ fragments.push(sanitizeHeadHtml(frag, 'inject.headFragments'));
185
+ }
186
+ // Stylesheets second
187
+ for (const entry of options.inject.stylesheets || []){
188
+ const isObj = typeof entry === 'object';
189
+ const href = isObj ? entry.href : entry;
190
+ validateSafeUrl(href, 'inject.stylesheets');
191
+ const safeHref = escapeHtmlAttr(href);
192
+ const linkAttrs = [
193
+ `rel="stylesheet"`,
194
+ `href="${safeHref}"`
195
+ ];
196
+ if (isObj) {
197
+ if (entry.integrity) linkAttrs.push(`integrity="${escapeHtmlAttr(entry.integrity)}"`);
198
+ if (entry.crossorigin) linkAttrs.push(`crossorigin="${escapeHtmlAttr(entry.crossorigin)}"`);
199
+ if (entry.integrity && !entry.crossorigin) linkAttrs.push('crossorigin="anonymous"');
200
+ if (entry.attrs) {
201
+ for (const [k, v] of Object.entries(entry.attrs)){
202
+ if (v !== undefined && v !== false) {
203
+ assertSafeAttributeName(k, 'inject.stylesheets.attrs');
204
+ linkAttrs.push(v === true ? escapeHtmlAttr(k) : `${escapeHtmlAttr(k)}="${escapeHtmlAttr(String(v))}"`);
205
+ }
206
+ }
207
+ }
208
+ }
209
+ fragments.push(`<link ${linkAttrs.join(' ')} />`);
210
+ }
211
+ // Scripts last - depend on headFragments being in DOM
212
+ for (const script of options.inject.scripts || []){
213
+ const isObjectScript = typeof script === 'object';
214
+ const src = isObjectScript ? script.src : script;
215
+ validateSafeUrl(src, 'inject.scripts');
216
+ const attrs = {
217
+ ...!isObjectScript || script.type ? {
218
+ type: isObjectScript ? script.type : 'module'
219
+ } : {},
220
+ ...isObjectScript && script.defer ? {
221
+ defer: true
222
+ } : {},
223
+ ...isObjectScript && script.async ? {
224
+ async: true
225
+ } : {},
226
+ src
227
+ };
228
+ if (isObjectScript && script.attrs) {
229
+ for (const [k, v] of Object.entries(script.attrs)){
230
+ assertSafeAttributeName(k, 'inject.scripts.attrs');
231
+ attrs[k] = v;
232
+ }
233
+ }
234
+ // H-04/05 fix: Add SRI attributes for CDN security
235
+ if (isObjectScript) {
236
+ if (script.integrity) attrs.integrity = script.integrity;
237
+ if (script.crossorigin) attrs.crossorigin = script.crossorigin;
238
+ else if (script.integrity) attrs.crossorigin = 'anonymous';
239
+ }
240
+ const attrText = Object.entries(attrs).filter(([, value])=>value !== undefined && value !== false).map(([name, value])=>value === true ? escapeHtmlAttr(name) : `${escapeHtmlAttr(name)}="${escapeHtmlAttr(String(value))}"`).join(' ');
241
+ fragments.push(`<script ${attrText}></script>`);
242
+ }
243
+ return {
244
+ headExtras: fragments.join('\n '),
245
+ allowHeadExtrasScripts: true
246
+ };
247
+ }
248
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hZGFwdGVyLXZpdGUvc3JjL2hlYWQtaW5qZWN0aW9uLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQG9wZW5lbGVtZW50L2FkYXB0ZXItdml0ZSDigJQgSGVhZCBpbmplY3Rpb24gdmFsaWRhdGlvbiAmIHNlcmlhbGl6YXRpb24uXG4gKlxuICogRXh0cmFjdGVkIGZyb20gaW5kZXgudHMgaW4gdjAuMjIgKFNPUC0wMDQ6IGFkYXB0ZXItdml0ZSBkZWNvbXBvc2l0aW9uKS5cbiAqXG4gKiBQcm92aWRlczpcbiAqIC0gYXNzZXJ0Tm9TY3JpcHRUYWdzKCkg4oCUIHNjcmlwdCB0YWcgc2FmZXR5IGNoZWNrIGZvciBoZWFkIGZyYWdtZW50c1xuICogLSB2YWxpZGF0ZVNhZmVVcmwoKSAgICDigJQgVVJMIHByb3RvY29sIHZhbGlkYXRpb24gYWdhaW5zdCBYU1MgdmVjdG9yc1xuICogLSBidWlsZEhlYWRFeHRyYXMoKSAgICDigJQgc2VyaWFsaXplIGhlYWRGcmFnbWVudHMsIHN0eWxlc2hlZXRzLCBhbmQgc2NyaXB0c1xuICogICAgICAgICAgICAgICAgICAgICAgICAgIGludG8gYSBzaW5nbGUgaGVhZEV4dHJhcyBzdHJpbmdcbiAqL1xuXG5pbXBvcnQgdHlwZSB7IEZyYW1ld29ya09wdGlvbnMgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvZnJhbWV3b3JrJztcblxuaW1wb3J0IHsgT3BlbkVsZW1lbnRFcnJvciB9IGZyb20gJ0BvcGVuZWxlbWVudC9jb3JlL2Vycm9ycyc7XG5pbXBvcnQgeyBlc2NhcGVBdHRyIGFzIGVzY2FwZUh0bWxBdHRyIH0gZnJvbSAnQG9wZW5lbGVtZW50L2NvcmUnO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnQG9wZW5lbGVtZW50L2NvcmUvbG9nZ2VyJztcbi8vIEBkZW5vLXR5cGVzPVwibnBtOkB0eXBlcy9zYW5pdGl6ZS1odG1sQF4yXCJcbmltcG9ydCBzYW5pdGl6ZUh0bWwsIHsgdHlwZSBJT3B0aW9ucyBhcyBTYW5pdGl6ZUh0bWxPcHRpb25zIH0gZnJvbSAnc2FuaXRpemUtaHRtbCc7XG5cbmNvbnN0IGxvZyA9IGNyZWF0ZUxvZ2dlcignYWRhcHRlci12aXRlOmhlYWQtaW5qZWN0aW9uJyk7XG5cbmNvbnN0IFNBRkVfU0NIRU1FUyA9IFsnaHR0cCcsICdodHRwcycsICdtYWlsdG8nLCAndGVsJywgJ3NtcyddO1xuY29uc3QgSEVBRF9TQU5JVElaRV9PUFRJT05TOiBTYW5pdGl6ZUh0bWxPcHRpb25zID0ge1xuICBhbGxvd2VkVGFnczogWydiYXNlJywgJ2xpbmsnLCAnbWV0YScsICdub3NjcmlwdCcsICdzdHlsZScsICd0aXRsZSddLFxuICBhbGxvd2VkQXR0cmlidXRlczoge1xuICAgIGJhc2U6IFsnaHJlZicsICd0YXJnZXQnXSxcbiAgICBsaW5rOiBbXG4gICAgICAnYXMnLFxuICAgICAgJ2Nyb3Nzb3JpZ2luJyxcbiAgICAgICdocmVmJyxcbiAgICAgICdocmVmbGFuZycsXG4gICAgICAnaW1hZ2VzaXplcycsXG4gICAgICAnaW1hZ2VzcmNzZXQnLFxuICAgICAgJ2ludGVncml0eScsXG4gICAgICAnbWVkaWEnLFxuICAgICAgJ3JlZmVycmVycG9saWN5JyxcbiAgICAgICdyZWwnLFxuICAgICAgJ3NpemVzJyxcbiAgICAgICd0aXRsZScsXG4gICAgICAndHlwZScsXG4gICAgXSxcbiAgICBtZXRhOiBbJ2NoYXJzZXQnLCAnY29udGVudCcsICdodHRwLWVxdWl2JywgJ25hbWUnLCAncHJvcGVydHknXSxcbiAgICBub3NjcmlwdDogW10sXG4gICAgc3R5bGU6IFsnbWVkaWEnLCAnbm9uY2UnLCAndGl0bGUnXSxcbiAgICB0aXRsZTogW10sXG4gIH0sXG4gIGFsbG93ZWRTY2hlbWVzOiBTQUZFX1NDSEVNRVMsXG4gIGFsbG93ZWRTY2hlbWVzQnlUYWc6IHtcbiAgICBsaW5rOiBTQUZFX1NDSEVNRVMsXG4gICAgYmFzZTogWydodHRwJywgJ2h0dHBzJ10sXG4gIH0sXG4gIGFsbG93ZWRTY2hlbWVzQXBwbGllZFRvQXR0cmlidXRlczogWydocmVmJywgJ3NyYycsICdhY3Rpb24nLCAnZm9ybWFjdGlvbicsICd4bGluazpocmVmJ10sXG4gIGFsbG93UHJvdG9jb2xSZWxhdGl2ZTogZmFsc2UsXG4gIGFsbG93VnVsbmVyYWJsZVRhZ3M6IHRydWUsXG59O1xuXG5mdW5jdGlvbiBzYW5pdGl6ZUhlYWRIdG1sKGh0bWw6IHN0cmluZywgY29udGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3Qgc2FuaXRpemVkID0gc2FuaXRpemVIdG1sKGh0bWwsIEhFQURfU0FOSVRJWkVfT1BUSU9OUyk7XG4gIGlmIChzYW5pdGl6ZWQudHJpbSgpICE9PSBodG1sLnRyaW0oKSkge1xuICAgIGxvZy53YXJuKGAke2NvbnRleHR9IGNvbnRhaW5lZCB1bnNhZmUgaGVhZCBtYXJrdXA7IHNhbml0aXplZCBiZWZvcmUgaW5qZWN0aW9uYCk7XG4gIH1cbiAgcmV0dXJuIHNhbml0aXplZDtcbn1cblxuZnVuY3Rpb24gYXNzZXJ0U2FmZUF0dHJpYnV0ZU5hbWUobmFtZTogc3RyaW5nLCBjb250ZXh0OiBzdHJpbmcpOiB2b2lkIHtcbiAgaWYgKCEvXltBLVphLXpfOl1bQS1aYS16MC05Xy46LV0qJC8udGVzdChuYW1lKSB8fCAvXm9uL2kudGVzdChuYW1lKSkge1xuICAgIHRocm93IG5ldyBPcGVuRWxlbWVudEVycm9yKGBVbnNhZmUgYXR0cmlidXRlIGluICR7Y29udGV4dH06IFwiJHtuYW1lfVwiYCwge1xuICAgICAgY29kZTogJ1VOU0FGRV9IRUFEX0lOSkVDVElPTicsXG4gICAgICBzdGF0dXNDb2RlOiA0MDAsXG4gICAgICByZWNvdmVyYWJsZTogZmFsc2UsXG4gICAgfSk7XG4gIH1cbn1cblxuLyoqXG4gKiBBc3NlcnQgdGhhdCBIVE1MIGNvbnRlbnQgZG9lcyBOT1QgY29udGFpbiA8c2NyaXB0PiB0YWdzLlxuICogU2NyaXB0cyBtdXN0IGdvIHRocm91Z2ggaW5qZWN0LnNjcmlwdHMgZm9yIFVSTCB2YWxpZGF0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYXNzZXJ0Tm9TY3JpcHRUYWdzKGh0bWw6IHN0cmluZywgY29udGV4dDogc3RyaW5nKTogdm9pZCB7XG4gIGlmICgvPHNjcmlwdFtcXHM+L10vaS50ZXN0KGh0bWwpKSB7XG4gICAgdGhyb3cgbmV3IE9wZW5FbGVtZW50RXJyb3IoXG4gICAgICBgJHtjb250ZXh0fSBtdXN0IG5vdCBjb250YWluIDxzY3JpcHQ+IHRhZ3MuIFVzZSBpbmplY3Quc2NyaXB0cyBmb3Igc2NyaXB0cyBzbyBgICtcbiAgICAgICAgJ29wZW5FbGVtZW50IGNhbiB2YWxpZGF0ZSBzY3JpcHQgVVJMcyBhbmQgbWFyayB0aGUgZ2VuZXJhdGVkIGhlYWQgaW5qZWN0aW9uIGFzIHRydXN0ZWQuJyxcbiAgICAgIHtcbiAgICAgICAgY29kZTogJ1VOU0FGRV9IRUFEX0lOSkVDVElPTicsXG4gICAgICAgIHN0YXR1c0NvZGU6IDQwMCxcbiAgICAgICAgcmVjb3ZlcmFibGU6IGZhbHNlLFxuICAgICAgfSxcbiAgICApO1xuICB9XG59XG5cbi8qKlxuICogVmFsaWRhdGUgYSBVUkwgc3RyaW5nIGZvciBrbm93biBYU1MgdmVjdG9ycyAoamF2YXNjcmlwdDosIGRhdGE6LCBldGMuKS5cbiAqIEFsc28gdmFsaWRhdGVzIHBlcmNlbnQtZW5jb2RpbmcgaXMgbm90IG1hbGZvcm1lZC5cbiAqIFJldHVybnMgdGhlIG5vcm1hbGl6ZWQgVVJMIHN0cmluZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlU2FmZVVybCh1cmw6IHN0cmluZywgY29udGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gTm9ybWFsaXNlOiBkZWNvZGUgVVJMIGVuY29kaW5nLCBzdHJpcCB3aGl0ZXNwYWNlLCBsb3dlcmNhc2VcbiAgY29uc3Qgbm9ybWFsaXNlZCA9IHVybC50cmltKCk7XG4gIHRyeSB7XG4gICAgY29uc3QgZGVjb2RlZCA9IGRlY29kZVVSSUNvbXBvbmVudChub3JtYWxpc2VkKTsgLy8gY2F0Y2ggbWFsZm9ybWVkICVYWFxuICAgIGNvbnN0IGxvd2VyID0gZGVjb2RlZC50b0xvd2VyQ2FzZSgpLnRyaW0oKTtcbiAgICBjb25zdCBibG9ja2VkUHJvdG9jb2xzID0gWydqYXZhc2NyaXB0OicsICdkYXRhOicsICd2YnNjcmlwdDonLCAnZmlsZTonXTtcbiAgICBmb3IgKGNvbnN0IHByb3RvIG9mIGJsb2NrZWRQcm90b2NvbHMpIHtcbiAgICAgIGlmIChsb3dlci5zdGFydHNXaXRoKHByb3RvKSkge1xuICAgICAgICB0aHJvdyBuZXcgT3BlbkVsZW1lbnRFcnJvcihcbiAgICAgICAgICBgVW5zYWZlIFVSTCBpbiAke2NvbnRleHR9OiBcIiR7dXJsfVwiIC0gJHtwcm90b30gcHJvdG9jb2wgaXMgbm90IGFsbG93ZWRgLFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGNvZGU6ICdVTlNBRkVfVVJMJyxcbiAgICAgICAgICAgIHN0YXR1c0NvZGU6IDQwMCxcbiAgICAgICAgICAgIHJlY292ZXJhYmxlOiBmYWxzZSxcbiAgICAgICAgICB9LFxuICAgICAgICApO1xuICAgICAgfVxuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIC8vIEgtMDEgZml4OiBSZS10aHJvdyBPcGVuRWxlbWVudEVycm9yIHNvIHNlY3VyaXR5IHdhcm5pbmdzIGFyZSBub3Qgc3dhbGxvd2VkXG4gICAgaWYgKGUgaW5zdGFuY2VvZiBPcGVuRWxlbWVudEVycm9yKSB0aHJvdyBlO1xuICAgIC8vIHYwLjE0LjM6IGRlY29kZVVSSUNvbXBvbmVudCBjYW4gdGhyb3cgZm9yIHR3byByZWFzb25zOlxuICAgIC8vICAgMS4gTWFsaWNpb3VzIFVSTHMgd2l0aCBpbnZhbGlkIHBlcmNlbnQtZW5jb2RpbmcgKGUuZy4sIFwiJVpaXCIpXG4gICAgLy8gICAyLiBMZWdpdGltYXRlIFVSTHMgd2l0aCBsb25lIHN1cnJvZ2F0ZXMgKHJhcmUsIGJ1dCB2YWxpZCBVUkktZW5jb2RlZClcbiAgICAvLyBXZSB0cmVhdCBhY3R1YWwgVVJJRXJyb3IgYXMgdW5zYWZlLCBidXQgbG9nIHRoZSBkaXN0aW5jdGlvbiBmb3IgZGVidWdnaW5nLlxuICAgIGlmIChlIGluc3RhbmNlb2YgVVJJRXJyb3IpIHtcbiAgICAgIGxvZy5kZWJ1ZyhcbiAgICAgICAgYGRlY29kZVVSSUNvbXBvbmVudCBmYWlsZWQgZm9yIFVSTCBpbiAke2NvbnRleHR9OiBcIiR7dXJsfVwiIC0gJHtlLm1lc3NhZ2V9LiBgICtcbiAgICAgICAgICAnVGhpcyBtYXkgYmUgYSBsZWdpdGltYXRlIGVuY29kaW5nIGlzc3VlIG9yIGEgbWFsaWNpb3VzIFVSTC4nLFxuICAgICAgKTtcbiAgICB9XG4gICAgdGhyb3cgbmV3IE9wZW5FbGVtZW50RXJyb3IoXG4gICAgICBgSW52YWxpZCBVUkwgaW4gJHtjb250ZXh0fTogXCIke3VybH1cIiAtIG1hbGZvcm1lZCBwZXJjZW50LWVuY29kaW5nYCxcbiAgICAgIHtcbiAgICAgICAgY29kZTogJ1VOU0FGRV9VUkwnLFxuICAgICAgICBzdGF0dXNDb2RlOiA0MDAsXG4gICAgICAgIHJlY292ZXJhYmxlOiBmYWxzZSxcbiAgICAgIH0sXG4gICAgKTtcbiAgfVxuICByZXR1cm4gbm9ybWFsaXNlZDtcbn1cblxuLyoqIFJlc3VsdCBvZiBidWlsZGluZyBoZWFkIGV4dHJhcyBmcm9tIEZyYW1ld29ya09wdGlvbnMuICovXG5leHBvcnQgaW50ZXJmYWNlIEhlYWRFeHRyYXNSZXN1bHQge1xuICBoZWFkRXh0cmFzOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGFsbG93SGVhZEV4dHJhc1NjcmlwdHM6IGJvb2xlYW47XG59XG5cbi8qKlxuICogQnVpbGQgdGhlIGhlYWRFeHRyYXMgc3RyaW5nIGZyb20gRnJhbWV3b3JrT3B0aW9ucy5pbmplY3QuXG4gKlxuICogU2VyaWFsaXplcyBoZWFkRnJhZ21lbnRzLCBzdHlsZXNoZWV0cywgYW5kIHNjcmlwdHMgaW50byBhIHNpbmdsZVxuICogSFRNTCBzdHJpbmcgdG8gaW5qZWN0IGludG8gPGhlYWQ+LiBWYWxpZGF0ZXMgYWxsIFVSTHMgYW5kIGVuc3VyZXNcbiAqIG5vIHJhdyA8c2NyaXB0PiB0YWdzIGJ5cGFzcyB0aGUgc3RydWN0dXJlZCBpbmplY3Rpb24gQVBJcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkSGVhZEV4dHJhcyhvcHRpb25zOiBGcmFtZXdvcmtPcHRpb25zKTogSGVhZEV4dHJhc1Jlc3VsdCB7XG4gIC8vIElmIGRpcmVjdCBoZWFkRXh0cmFzIHByb3ZpZGVkLCB2YWxpZGF0ZSBhbmQgcmV0dXJuXG4gIGlmIChvcHRpb25zLmhlYWRFeHRyYXMpIHtcbiAgICBhc3NlcnROb1NjcmlwdFRhZ3Mob3B0aW9ucy5oZWFkRXh0cmFzLCAnaGVhZEV4dHJhcycpO1xuICAgIHJldHVybiB7XG4gICAgICBoZWFkRXh0cmFzOiBzYW5pdGl6ZUhlYWRIdG1sKG9wdGlvbnMuaGVhZEV4dHJhcywgJ2hlYWRFeHRyYXMnKSxcbiAgICAgIGFsbG93SGVhZEV4dHJhc1NjcmlwdHM6IGZhbHNlLFxuICAgIH07XG4gIH1cblxuICAvLyBObyBpbmplY3QgY29uZmlnIC0gbm8gaGVhZCBleHRyYXNcbiAgaWYgKCFvcHRpb25zLmluamVjdCkge1xuICAgIHJldHVybiB7IGhlYWRFeHRyYXM6IHVuZGVmaW5lZCwgYWxsb3dIZWFkRXh0cmFzU2NyaXB0czogZmFsc2UgfTtcbiAgfVxuXG4gIGNvbnN0IGZyYWdtZW50czogc3RyaW5nW10gPSBbXTtcblxuICAvLyBoZWFkRnJhZ21lbnRzIEZJUlNUIChtZXRhLCBzdHlsZXMsIGFudGktZmxhc2gpIC0gbXVzdCBleGlzdCBpbiBET01cbiAgLy8gYmVmb3JlIHNjcmlwdHMgdGhhdCByZWZlcmVuY2UgdGhlbSAoZS5nLiB0aGVtZS1pbml0LmpzIHJlbW92ZXMgYW50aS1mbGFzaCkuXG4gIGZvciAoY29uc3QgZnJhZyBvZiBvcHRpb25zLmluamVjdC5oZWFkRnJhZ21lbnRzIHx8IFtdKSB7XG4gICAgYXNzZXJ0Tm9TY3JpcHRUYWdzKGZyYWcsICdpbmplY3QuaGVhZEZyYWdtZW50cycpO1xuICAgIGZyYWdtZW50cy5wdXNoKHNhbml0aXplSGVhZEh0bWwoZnJhZywgJ2luamVjdC5oZWFkRnJhZ21lbnRzJykpO1xuICB9XG5cbiAgLy8gU3R5bGVzaGVldHMgc2Vjb25kXG4gIGZvciAoY29uc3QgZW50cnkgb2Ygb3B0aW9ucy5pbmplY3Quc3R5bGVzaGVldHMgfHwgW10pIHtcbiAgICBjb25zdCBpc09iaiA9IHR5cGVvZiBlbnRyeSA9PT0gJ29iamVjdCc7XG4gICAgY29uc3QgaHJlZiA9IGlzT2JqID8gZW50cnkuaHJlZiA6IGVudHJ5O1xuICAgIHZhbGlkYXRlU2FmZVVybChocmVmLCAnaW5qZWN0LnN0eWxlc2hlZXRzJyk7XG4gICAgY29uc3Qgc2FmZUhyZWYgPSBlc2NhcGVIdG1sQXR0cihocmVmKTtcbiAgICBjb25zdCBsaW5rQXR0cnM6IHN0cmluZ1tdID0gW2ByZWw9XCJzdHlsZXNoZWV0XCJgLCBgaHJlZj1cIiR7c2FmZUhyZWZ9XCJgXTtcbiAgICBpZiAoaXNPYmopIHtcbiAgICAgIGlmIChlbnRyeS5pbnRlZ3JpdHkpIGxpbmtBdHRycy5wdXNoKGBpbnRlZ3JpdHk9XCIke2VzY2FwZUh0bWxBdHRyKGVudHJ5LmludGVncml0eSl9XCJgKTtcbiAgICAgIGlmIChlbnRyeS5jcm9zc29yaWdpbikgbGlua0F0dHJzLnB1c2goYGNyb3Nzb3JpZ2luPVwiJHtlc2NhcGVIdG1sQXR0cihlbnRyeS5jcm9zc29yaWdpbil9XCJgKTtcbiAgICAgIGlmIChlbnRyeS5pbnRlZ3JpdHkgJiYgIWVudHJ5LmNyb3Nzb3JpZ2luKSBsaW5rQXR0cnMucHVzaCgnY3Jvc3NvcmlnaW49XCJhbm9ueW1vdXNcIicpO1xuICAgICAgaWYgKGVudHJ5LmF0dHJzKSB7XG4gICAgICAgIGZvciAoY29uc3QgW2ssIHZdIG9mIE9iamVjdC5lbnRyaWVzKGVudHJ5LmF0dHJzKSkge1xuICAgICAgICAgIGlmICh2ICE9PSB1bmRlZmluZWQgJiYgdiAhPT0gZmFsc2UpIHtcbiAgICAgICAgICAgIGFzc2VydFNhZmVBdHRyaWJ1dGVOYW1lKGssICdpbmplY3Quc3R5bGVzaGVldHMuYXR0cnMnKTtcbiAgICAgICAgICAgIGxpbmtBdHRycy5wdXNoKFxuICAgICAgICAgICAgICB2ID09PSB0cnVlXG4gICAgICAgICAgICAgICAgPyBlc2NhcGVIdG1sQXR0cihrKVxuICAgICAgICAgICAgICAgIDogYCR7ZXNjYXBlSHRtbEF0dHIoayl9PVwiJHtlc2NhcGVIdG1sQXR0cihTdHJpbmcodikpfVwiYCxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIGZyYWdtZW50cy5wdXNoKGA8bGluayAke2xpbmtBdHRycy5qb2luKCcgJyl9IC8+YCk7XG4gIH1cblxuICAvLyBTY3JpcHRzIGxhc3QgLSBkZXBlbmQgb24gaGVhZEZyYWdtZW50cyBiZWluZyBpbiBET01cbiAgZm9yIChjb25zdCBzY3JpcHQgb2Ygb3B0aW9ucy5pbmplY3Quc2NyaXB0cyB8fCBbXSkge1xuICAgIGNvbnN0IGlzT2JqZWN0U2NyaXB0ID0gdHlwZW9mIHNjcmlwdCA9PT0gJ29iamVjdCc7XG4gICAgY29uc3Qgc3JjID0gaXNPYmplY3RTY3JpcHQgPyBzY3JpcHQuc3JjIDogc2NyaXB0O1xuICAgIHZhbGlkYXRlU2FmZVVybChzcmMsICdpbmplY3Quc2NyaXB0cycpO1xuICAgIGNvbnN0IGF0dHJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmcgfCBudW1iZXIgfCBib29sZWFuPiA9IHtcbiAgICAgIC4uLighaXNPYmplY3RTY3JpcHQgfHwgc2NyaXB0LnR5cGUgPyB7IHR5cGU6IGlzT2JqZWN0U2NyaXB0ID8gc2NyaXB0LnR5cGUhIDogJ21vZHVsZScgfSA6IHt9KSxcbiAgICAgIC4uLihpc09iamVjdFNjcmlwdCAmJiBzY3JpcHQuZGVmZXIgPyB7IGRlZmVyOiB0cnVlIH0gOiB7fSksXG4gICAgICAuLi4oaXNPYmplY3RTY3JpcHQgJiYgc2NyaXB0LmFzeW5jID8geyBhc3luYzogdHJ1ZSB9IDoge30pLFxuICAgICAgc3JjLFxuICAgIH07XG4gICAgaWYgKGlzT2JqZWN0U2NyaXB0ICYmIHNjcmlwdC5hdHRycykge1xuICAgICAgZm9yIChjb25zdCBbaywgdl0gb2YgT2JqZWN0LmVudHJpZXMoc2NyaXB0LmF0dHJzKSkge1xuICAgICAgICBhc3NlcnRTYWZlQXR0cmlidXRlTmFtZShrLCAnaW5qZWN0LnNjcmlwdHMuYXR0cnMnKTtcbiAgICAgICAgYXR0cnNba10gPSB2O1xuICAgICAgfVxuICAgIH1cbiAgICAvLyBILTA0LzA1IGZpeDogQWRkIFNSSSBhdHRyaWJ1dGVzIGZvciBDRE4gc2VjdXJpdHlcbiAgICBpZiAoaXNPYmplY3RTY3JpcHQpIHtcbiAgICAgIGlmIChzY3JpcHQuaW50ZWdyaXR5KSBhdHRycy5pbnRlZ3JpdHkgPSBzY3JpcHQuaW50ZWdyaXR5O1xuICAgICAgaWYgKHNjcmlwdC5jcm9zc29yaWdpbikgYXR0cnMuY3Jvc3NvcmlnaW4gPSBzY3JpcHQuY3Jvc3NvcmlnaW47XG4gICAgICBlbHNlIGlmIChzY3JpcHQuaW50ZWdyaXR5KSBhdHRycy5jcm9zc29yaWdpbiA9ICdhbm9ueW1vdXMnO1xuICAgIH1cbiAgICBjb25zdCBhdHRyVGV4dCA9IE9iamVjdC5lbnRyaWVzKGF0dHJzKVxuICAgICAgLmZpbHRlcigoWywgdmFsdWVdKSA9PiB2YWx1ZSAhPT0gdW5kZWZpbmVkICYmIHZhbHVlICE9PSBmYWxzZSlcbiAgICAgIC5tYXAoKFtuYW1lLCB2YWx1ZV0pID0+XG4gICAgICAgIHZhbHVlID09PSB0cnVlXG4gICAgICAgICAgPyBlc2NhcGVIdG1sQXR0cihuYW1lKVxuICAgICAgICAgIDogYCR7ZXNjYXBlSHRtbEF0dHIobmFtZSl9PVwiJHtlc2NhcGVIdG1sQXR0cihTdHJpbmcodmFsdWUpKX1cImBcbiAgICAgIClcbiAgICAgIC5qb2luKCcgJyk7XG4gICAgZnJhZ21lbnRzLnB1c2goYDxzY3JpcHQgJHthdHRyVGV4dH0+PC9zY3JpcHQ+YCk7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIGhlYWRFeHRyYXM6IGZyYWdtZW50cy5qb2luKCdcXG4gICcpLFxuICAgIGFsbG93SGVhZEV4dHJhc1NjcmlwdHM6IHRydWUsXG4gIH07XG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Q0FVQyxHQUlELFNBQVMsZ0JBQWdCLFFBQVEsMkJBQTJCO0FBQzVELFNBQVMsY0FBYyxjQUFjLFFBQVEsb0JBQW9CO0FBQ2pFLFNBQVMsWUFBWSxRQUFRLDJCQUEyQjtBQUN4RCw0Q0FBNEM7QUFDNUMsT0FBTyxrQkFBNEQsZ0JBQWdCO0FBRW5GLE1BQU0sTUFBTSxhQUFhO0FBRXpCLE1BQU0sZUFBZTtFQUFDO0VBQVE7RUFBUztFQUFVO0VBQU87Q0FBTTtBQUM5RCxNQUFNLHdCQUE2QztFQUNqRCxhQUFhO0lBQUM7SUFBUTtJQUFRO0lBQVE7SUFBWTtJQUFTO0dBQVE7RUFDbkUsbUJBQW1CO0lBQ2pCLE1BQU07TUFBQztNQUFRO0tBQVM7SUFDeEIsTUFBTTtNQUNKO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO0tBQ0Q7SUFDRCxNQUFNO01BQUM7TUFBVztNQUFXO01BQWM7TUFBUTtLQUFXO0lBQzlELFVBQVUsRUFBRTtJQUNaLE9BQU87TUFBQztNQUFTO01BQVM7S0FBUTtJQUNsQyxPQUFPLEVBQUU7RUFDWDtFQUNBLGdCQUFnQjtFQUNoQixxQkFBcUI7SUFDbkIsTUFBTTtJQUNOLE1BQU07TUFBQztNQUFRO0tBQVE7RUFDekI7RUFDQSxtQ0FBbUM7SUFBQztJQUFRO0lBQU87SUFBVTtJQUFjO0dBQWE7RUFDeEYsdUJBQXVCO0VBQ3ZCLHFCQUFxQjtBQUN2QjtBQUVBLFNBQVMsaUJBQWlCLElBQVksRUFBRSxPQUFlO0VBQ3JELE1BQU0sWUFBWSxhQUFhLE1BQU07RUFDckMsSUFBSSxVQUFVLElBQUksT0FBTyxLQUFLLElBQUksSUFBSTtJQUNwQyxJQUFJLElBQUksQ0FBQyxHQUFHLFFBQVEseURBQXlELENBQUM7RUFDaEY7RUFDQSxPQUFPO0FBQ1Q7QUFFQSxTQUFTLHdCQUF3QixJQUFZLEVBQUUsT0FBZTtFQUM1RCxJQUFJLENBQUMsK0JBQStCLElBQUksQ0FBQyxTQUFTLE9BQU8sSUFBSSxDQUFDLE9BQU87SUFDbkUsTUFBTSxJQUFJLGlCQUFpQixDQUFDLG9CQUFvQixFQUFFLFFBQVEsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDLEVBQUU7TUFDdEUsTUFBTTtNQUNOLFlBQVk7TUFDWixhQUFhO0lBQ2Y7RUFDRjtBQUNGO0FBRUE7OztDQUdDLEdBQ0QsT0FBTyxTQUFTLG1CQUFtQixJQUFZLEVBQUUsT0FBZTtFQUM5RCxJQUFJLGlCQUFpQixJQUFJLENBQUMsT0FBTztJQUMvQixNQUFNLElBQUksaUJBQ1IsR0FBRyxRQUFRLG1FQUFtRSxDQUFDLEdBQzdFLDBGQUNGO01BQ0UsTUFBTTtNQUNOLFlBQVk7TUFDWixhQUFhO0lBQ2Y7RUFFSjtBQUNGO0FBRUE7Ozs7Q0FJQyxHQUNELE9BQU8sU0FBUyxnQkFBZ0IsR0FBVyxFQUFFLE9BQWU7RUFDMUQsOERBQThEO0VBQzlELE1BQU0sYUFBYSxJQUFJLElBQUk7RUFDM0IsSUFBSTtJQUNGLE1BQU0sVUFBVSxtQkFBbUIsYUFBYSxzQkFBc0I7SUFDdEUsTUFBTSxRQUFRLFFBQVEsV0FBVyxHQUFHLElBQUk7SUFDeEMsTUFBTSxtQkFBbUI7TUFBQztNQUFlO01BQVM7TUFBYTtLQUFRO0lBQ3ZFLEtBQUssTUFBTSxTQUFTLGlCQUFrQjtNQUNwQyxJQUFJLE1BQU0sVUFBVSxDQUFDLFFBQVE7UUFDM0IsTUFBTSxJQUFJLGlCQUNSLENBQUMsY0FBYyxFQUFFLFFBQVEsR0FBRyxFQUFFLElBQUksSUFBSSxFQUFFLE1BQU0sd0JBQXdCLENBQUMsRUFDdkU7VUFDRSxNQUFNO1VBQ04sWUFBWTtVQUNaLGFBQWE7UUFDZjtNQUVKO0lBQ0Y7RUFDRixFQUFFLE9BQU8sR0FBRztJQUNWLDZFQUE2RTtJQUM3RSxJQUFJLGFBQWEsa0JBQWtCLE1BQU07SUFDekMseURBQXlEO0lBQ3pELGtFQUFrRTtJQUNsRSwwRUFBMEU7SUFDMUUsNkVBQTZFO0lBQzdFLElBQUksYUFBYSxVQUFVO01BQ3pCLElBQUksS0FBSyxDQUNQLENBQUMscUNBQXFDLEVBQUUsUUFBUSxHQUFHLEVBQUUsSUFBSSxJQUFJLEVBQUUsRUFBRSxPQUFPLENBQUMsRUFBRSxDQUFDLEdBQzFFO0lBRU47SUFDQSxNQUFNLElBQUksaUJBQ1IsQ0FBQyxlQUFlLEVBQUUsUUFBUSxHQUFHLEVBQUUsSUFBSSw4QkFBOEIsQ0FBQyxFQUNsRTtNQUNFLE1BQU07TUFDTixZQUFZO01BQ1osYUFBYTtJQUNmO0VBRUo7RUFDQSxPQUFPO0FBQ1Q7QUFRQTs7Ozs7O0NBTUMsR0FDRCxPQUFPLFNBQVMsZ0JBQWdCLE9BQXlCO0VBQ3ZELHFEQUFxRDtFQUNyRCxJQUFJLFFBQVEsVUFBVSxFQUFFO0lBQ3RCLG1CQUFtQixRQUFRLFVBQVUsRUFBRTtJQUN2QyxPQUFPO01BQ0wsWUFBWSxpQkFBaUIsUUFBUSxVQUFVLEVBQUU7TUFDakQsd0JBQXdCO0lBQzFCO0VBQ0Y7RUFFQSxvQ0FBb0M7RUFDcEMsSUFBSSxDQUFDLFFBQVEsTUFBTSxFQUFFO0lBQ25CLE9BQU87TUFBRSxZQUFZO01BQVcsd0JBQXdCO0lBQU07RUFDaEU7RUFFQSxNQUFNLFlBQXNCLEVBQUU7RUFFOUIscUVBQXFFO0VBQ3JFLDhFQUE4RTtFQUM5RSxLQUFLLE1BQU0sUUFBUSxRQUFRLE1BQU0sQ0FBQyxhQUFhLElBQUksRUFBRSxDQUFFO0lBQ3JELG1CQUFtQixNQUFNO0lBQ3pCLFVBQVUsSUFBSSxDQUFDLGlCQUFpQixNQUFNO0VBQ3hDO0VBRUEscUJBQXFCO0VBQ3JCLEtBQUssTUFBTSxTQUFTLFFBQVEsTUFBTSxDQUFDLFdBQVcsSUFBSSxFQUFFLENBQUU7SUFDcEQsTUFBTSxRQUFRLE9BQU8sVUFBVTtJQUMvQixNQUFNLE9BQU8sUUFBUSxNQUFNLElBQUksR0FBRztJQUNsQyxnQkFBZ0IsTUFBTTtJQUN0QixNQUFNLFdBQVcsZUFBZTtJQUNoQyxNQUFNLFlBQXNCO01BQUMsQ0FBQyxnQkFBZ0IsQ0FBQztNQUFFLENBQUMsTUFBTSxFQUFFLFNBQVMsQ0FBQyxDQUFDO0tBQUM7SUFDdEUsSUFBSSxPQUFPO01BQ1QsSUFBSSxNQUFNLFNBQVMsRUFBRSxVQUFVLElBQUksQ0FBQyxDQUFDLFdBQVcsRUFBRSxlQUFlLE1BQU0sU0FBUyxFQUFFLENBQUMsQ0FBQztNQUNwRixJQUFJLE1BQU0sV0FBVyxFQUFFLFVBQVUsSUFBSSxDQUFDLENBQUMsYUFBYSxFQUFFLGVBQWUsTUFBTSxXQUFXLEVBQUUsQ0FBQyxDQUFDO01BQzFGLElBQUksTUFBTSxTQUFTLElBQUksQ0FBQyxNQUFNLFdBQVcsRUFBRSxVQUFVLElBQUksQ0FBQztNQUMxRCxJQUFJLE1BQU0sS0FBSyxFQUFFO1FBQ2YsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLElBQUksT0FBTyxPQUFPLENBQUMsTUFBTSxLQUFLLEVBQUc7VUFDaEQsSUFBSSxNQUFNLGFBQWEsTUFBTSxPQUFPO1lBQ2xDLHdCQUF3QixHQUFHO1lBQzNCLFVBQVUsSUFBSSxDQUNaLE1BQU0sT0FDRixlQUFlLEtBQ2YsR0FBRyxlQUFlLEdBQUcsRUFBRSxFQUFFLGVBQWUsT0FBTyxJQUFJLENBQUMsQ0FBQztVQUU3RDtRQUNGO01BQ0Y7SUFDRjtJQUNBLFVBQVUsSUFBSSxDQUFDLENBQUMsTUFBTSxFQUFFLFVBQVUsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDO0VBQ2xEO0VBRUEsc0RBQXNEO0VBQ3RELEtBQUssTUFBTSxVQUFVLFFBQVEsTUFBTSxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUU7SUFDakQsTUFBTSxpQkFBaUIsT0FBTyxXQUFXO0lBQ3pDLE1BQU0sTUFBTSxpQkFBaUIsT0FBTyxHQUFHLEdBQUc7SUFDMUMsZ0JBQWdCLEtBQUs7SUFDckIsTUFBTSxRQUFtRDtNQUN2RCxHQUFJLENBQUMsa0JBQWtCLE9BQU8sSUFBSSxHQUFHO1FBQUUsTUFBTSxpQkFBaUIsT0FBTyxJQUFJLEdBQUk7TUFBUyxJQUFJLENBQUMsQ0FBQztNQUM1RixHQUFJLGtCQUFrQixPQUFPLEtBQUssR0FBRztRQUFFLE9BQU87TUFBSyxJQUFJLENBQUMsQ0FBQztNQUN6RCxHQUFJLGtCQUFrQixPQUFPLEtBQUssR0FBRztRQUFFLE9BQU87TUFBSyxJQUFJLENBQUMsQ0FBQztNQUN6RDtJQUNGO0lBQ0EsSUFBSSxrQkFBa0IsT0FBTyxLQUFLLEVBQUU7TUFDbEMsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLElBQUksT0FBTyxPQUFPLENBQUMsT0FBTyxLQUFLLEVBQUc7UUFDakQsd0JBQXdCLEdBQUc7UUFDM0IsS0FBSyxDQUFDLEVBQUUsR0FBRztNQUNiO0lBQ0Y7SUFDQSxtREFBbUQ7SUFDbkQsSUFBSSxnQkFBZ0I7TUFDbEIsSUFBSSxPQUFPLFNBQVMsRUFBRSxNQUFNLFNBQVMsR0FBRyxPQUFPLFNBQVM7TUFDeEQsSUFBSSxPQUFPLFdBQVcsRUFBRSxNQUFNLFdBQVcsR0FBRyxPQUFPLFdBQVc7V0FDekQsSUFBSSxPQUFPLFNBQVMsRUFBRSxNQUFNLFdBQVcsR0FBRztJQUNqRDtJQUNBLE1BQU0sV0FBVyxPQUFPLE9BQU8sQ0FBQyxPQUM3QixNQUFNLENBQUMsQ0FBQyxHQUFHLE1BQU0sR0FBSyxVQUFVLGFBQWEsVUFBVSxPQUN2RCxHQUFHLENBQUMsQ0FBQyxDQUFDLE1BQU0sTUFBTSxHQUNqQixVQUFVLE9BQ04sZUFBZSxRQUNmLEdBQUcsZUFBZSxNQUFNLEVBQUUsRUFBRSxlQUFlLE9BQU8sUUFBUSxDQUFDLENBQUMsRUFFakUsSUFBSSxDQUFDO0lBQ1IsVUFBVSxJQUFJLENBQUMsQ0FBQyxRQUFRLEVBQUUsU0FBUyxVQUFVLENBQUM7RUFDaEQ7RUFFQSxPQUFPO0lBQ0wsWUFBWSxVQUFVLElBQUksQ0FBQztJQUMzQix3QkFBd0I7RUFDMUI7QUFDRiJ9
package/src/index.d.ts ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @openelement/adapter-vite - Vite build orchestration adapter.
3
+ *
4
+ * Provides the `openPipeline()` Vite plugin that handles:
5
+ * - Route scanning and virtual Hono entry generation
6
+ * - Dev server integration via @hono/vite-dev-server
7
+ * - Island marking transform
8
+ * - SSG build pipeline (Phase 1/2/3)
9
+ * - Core subpath resolution (ADR 0016)
10
+ *
11
+ * Runtime code (renderDsd, defineIsland, escapeHtml, etc.) lives in @openelement/core.
12
+ * This package only contains Vite-specific build orchestration.
13
+ *
14
+ * For the unified openElement() entry, use @openelement/app/vite instead.
15
+ *
16
+ * v0.22 (SOP-004): Decomposed into focused modules:
17
+ * head-injection.ts - HTML fragment validation & serialization
18
+ * plugin.ts - Internal plugin factory (used by openPipeline)
19
+ * subpath-resolver.ts - JSR remote resolution (ADR 0016)
20
+ * generated-data-resolver.ts - Generated app data namespace resolver
21
+ *
22
+ * This file is now a pure re-export hub (~60 lines).
23
+ */ import type { Plugin } from 'vite';
24
+ import type { FrameworkOptions } from '@openelement/protocol/framework';
25
+ export interface OpenPipelineConfig {
26
+ routes?: {
27
+ dir?: string;
28
+ };
29
+ i18n?: {
30
+ locales: string[];
31
+ defaultLocale?: string;
32
+ };
33
+ output?: {
34
+ outDir?: string;
35
+ };
36
+ island?: {
37
+ dir?: string;
38
+ upgradeStrategy?: string;
39
+ };
40
+ viewTransition?: boolean;
41
+ headExtras?: string;
42
+ }
43
+ export declare function openPipeline(config?: OpenPipelineConfig): Plugin[];
44
+ export type { FrameworkOptions };
45
+ export { OpenElementBuildContext } from "./build-context.js";
46
+ export type { ArtifactInfo, BuildManifest } from "./build-manifest.js";
47
+ export { printBuildManifest, scanClientBuild, scanSSGOutput } from "./build-manifest.js";
48
+ export { buildIslandChunkMap, buildSpeculationRulesJson, extractCustomElementTags, generateIslandManifests, injectClientScript, injectCspMeta, injectDsdPolyfill, injectSpeculationRules, injectViewTransitionMeta, insertAfterHead, type IslandLayerMap, type IslandManifestEntry, type IslandStrategyMap, type PageIslandManifest, writeIslandManifests } from '@openelement/ssg';
49
+ export type { SpeculationRulesOptions } from '@openelement/protocol/ssg';
50
+ export type { ExternalManifest } from '@openelement/protocol/ssg';
51
+ export { CORE_SUBPATHS, VIRTUAL_CORE_PREFIX } from "./subpath-resolver.js";
52
+ export { assertNoScriptTags, buildHeadExtras, validateSafeUrl } from "./head-injection.js";
53
+ export type { HeadExtrasResult } from "./head-injection.js";
54
+ export { mdxPlugin, openMdx } from "./plugin-mdx.js";
55
+ export type { OpenMdxPluginOptions } from "./plugin-mdx.js";
56
+ export { createOpenElementNitroHandler } from "./nitro-mount.js";
57
+ export type { NitroLikeRequestEvent, NitroLikeResponse, OpenElementNitroMountOptions } from "./nitro-mount.js";
58
+ export { openPipeline as default };
package/src/index.js ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @openelement/adapter-vite - Vite build orchestration adapter.
3
+ *
4
+ * Provides the `openPipeline()` Vite plugin that handles:
5
+ * - Route scanning and virtual Hono entry generation
6
+ * - Dev server integration via @hono/vite-dev-server
7
+ * - Island marking transform
8
+ * - SSG build pipeline (Phase 1/2/3)
9
+ * - Core subpath resolution (ADR 0016)
10
+ *
11
+ * Runtime code (renderDsd, defineIsland, escapeHtml, etc.) lives in @openelement/core.
12
+ * This package only contains Vite-specific build orchestration.
13
+ *
14
+ * For the unified openElement() entry, use @openelement/app/vite instead.
15
+ *
16
+ * v0.22 (SOP-004): Decomposed into focused modules:
17
+ * head-injection.ts - HTML fragment validation & serialization
18
+ * plugin.ts - Internal plugin factory (used by openPipeline)
19
+ * subpath-resolver.ts - JSR remote resolution (ADR 0016)
20
+ * generated-data-resolver.ts - Generated app data namespace resolver
21
+ *
22
+ * This file is now a pure re-export hub (~60 lines).
23
+ */ // Primary public API
24
+ import { createOpenPlugin } from './plugin.js';
25
+ export function openPipeline(config = {}) {
26
+ const options = {
27
+ routesDir: config.routes?.dir || 'app/routes',
28
+ islandsDir: config.island?.dir || 'app/islands',
29
+ componentsDir: 'app/components',
30
+ viewTransition: config.viewTransition ?? true,
31
+ headExtras: config.headExtras,
32
+ island: config.island,
33
+ build: config.output
34
+ };
35
+ return createOpenPlugin(options);
36
+ }
37
+ // Build context
38
+ export { OpenElementBuildContext } from './build-context.js';
39
+ export { printBuildManifest, scanClientBuild, scanSSGOutput } from './build-manifest.js';
40
+ // SSG post-processing & island manifests (adapter-vite internal build helpers)
41
+ export { buildIslandChunkMap, buildSpeculationRulesJson, extractCustomElementTags, generateIslandManifests, injectClientScript, injectCspMeta, injectDsdPolyfill, injectSpeculationRules, injectViewTransitionMeta, insertAfterHead, writeIslandManifests } from '@openelement/ssg';
42
+ // Subpath resolver (public constants)
43
+ export { CORE_SUBPATHS, VIRTUAL_CORE_PREFIX } from './subpath-resolver.js';
44
+ // Head injection (public helpers)
45
+ export { assertNoScriptTags, buildHeadExtras, validateSafeUrl } from './head-injection.js';
46
+ // MDX integration
47
+ export { mdxPlugin, openMdx } from './plugin-mdx.js';
48
+ // Nitro runtime proof boundary
49
+ export { createOpenElementNitroHandler } from './nitro-mount.js';
50
+ // Default export
51
+ export { openPipeline as default };
52
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hZGFwdGVyLXZpdGUvc3JjL2luZGV4LnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQG9wZW5lbGVtZW50L2FkYXB0ZXItdml0ZSAtIFZpdGUgYnVpbGQgb3JjaGVzdHJhdGlvbiBhZGFwdGVyLlxuICpcbiAqIFByb3ZpZGVzIHRoZSBgb3BlblBpcGVsaW5lKClgIFZpdGUgcGx1Z2luIHRoYXQgaGFuZGxlczpcbiAqIC0gUm91dGUgc2Nhbm5pbmcgYW5kIHZpcnR1YWwgSG9ubyBlbnRyeSBnZW5lcmF0aW9uXG4gKiAtIERldiBzZXJ2ZXIgaW50ZWdyYXRpb24gdmlhIEBob25vL3ZpdGUtZGV2LXNlcnZlclxuICogLSBJc2xhbmQgbWFya2luZyB0cmFuc2Zvcm1cbiAqIC0gU1NHIGJ1aWxkIHBpcGVsaW5lIChQaGFzZSAxLzIvMylcbiAqIC0gQ29yZSBzdWJwYXRoIHJlc29sdXRpb24gKEFEUiAwMDE2KVxuICpcbiAqIFJ1bnRpbWUgY29kZSAocmVuZGVyRHNkLCBkZWZpbmVJc2xhbmQsIGVzY2FwZUh0bWwsIGV0Yy4pIGxpdmVzIGluIEBvcGVuZWxlbWVudC9jb3JlLlxuICogVGhpcyBwYWNrYWdlIG9ubHkgY29udGFpbnMgVml0ZS1zcGVjaWZpYyBidWlsZCBvcmNoZXN0cmF0aW9uLlxuICpcbiAqIEZvciB0aGUgdW5pZmllZCBvcGVuRWxlbWVudCgpIGVudHJ5LCB1c2UgQG9wZW5lbGVtZW50L2FwcC92aXRlIGluc3RlYWQuXG4gKlxuICogdjAuMjIgKFNPUC0wMDQpOiBEZWNvbXBvc2VkIGludG8gZm9jdXNlZCBtb2R1bGVzOlxuICogICBoZWFkLWluamVjdGlvbi50cyAgICAgIC0gSFRNTCBmcmFnbWVudCB2YWxpZGF0aW9uICYgc2VyaWFsaXphdGlvblxuICogICBwbHVnaW4udHMgICAgICAgICAgICAgIC0gSW50ZXJuYWwgcGx1Z2luIGZhY3RvcnkgKHVzZWQgYnkgb3BlblBpcGVsaW5lKVxuICogICBzdWJwYXRoLXJlc29sdmVyLnRzICAgIC0gSlNSIHJlbW90ZSByZXNvbHV0aW9uIChBRFIgMDAxNilcbiAqICAgZ2VuZXJhdGVkLWRhdGEtcmVzb2x2ZXIudHMgLSBHZW5lcmF0ZWQgYXBwIGRhdGEgbmFtZXNwYWNlIHJlc29sdmVyXG4gKlxuICogVGhpcyBmaWxlIGlzIG5vdyBhIHB1cmUgcmUtZXhwb3J0IGh1YiAofjYwIGxpbmVzKS5cbiAqL1xuXG4vLyBQcmltYXJ5IHB1YmxpYyBBUElcbmltcG9ydCB0eXBlIHsgUGx1Z2luIH0gZnJvbSAndml0ZSc7XG5pbXBvcnQgdHlwZSB7IEZyYW1ld29ya09wdGlvbnMgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvZnJhbWV3b3JrJztcbmltcG9ydCB7IGNyZWF0ZU9wZW5QbHVnaW4gfSBmcm9tICcuL3BsdWdpbi5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgT3BlblBpcGVsaW5lQ29uZmlnIHtcbiAgcm91dGVzPzogeyBkaXI/OiBzdHJpbmcgfTtcbiAgaTE4bj86IHsgbG9jYWxlczogc3RyaW5nW107IGRlZmF1bHRMb2NhbGU/OiBzdHJpbmcgfTtcbiAgb3V0cHV0PzogeyBvdXREaXI/OiBzdHJpbmcgfTtcbiAgaXNsYW5kPzogeyBkaXI/OiBzdHJpbmc7IHVwZ3JhZGVTdHJhdGVneT86IHN0cmluZyB9O1xuICB2aWV3VHJhbnNpdGlvbj86IGJvb2xlYW47XG4gIGhlYWRFeHRyYXM/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBvcGVuUGlwZWxpbmUoY29uZmlnOiBPcGVuUGlwZWxpbmVDb25maWcgPSB7fSk6IFBsdWdpbltdIHtcbiAgY29uc3Qgb3B0aW9uczogRnJhbWV3b3JrT3B0aW9ucyA9IHtcbiAgICByb3V0ZXNEaXI6IGNvbmZpZy5yb3V0ZXM/LmRpciB8fCAnYXBwL3JvdXRlcycsXG4gICAgaXNsYW5kc0RpcjogY29uZmlnLmlzbGFuZD8uZGlyIHx8ICdhcHAvaXNsYW5kcycsXG4gICAgY29tcG9uZW50c0RpcjogJ2FwcC9jb21wb25lbnRzJyxcbiAgICB2aWV3VHJhbnNpdGlvbjogY29uZmlnLnZpZXdUcmFuc2l0aW9uID8/IHRydWUsXG4gICAgaGVhZEV4dHJhczogY29uZmlnLmhlYWRFeHRyYXMsXG4gICAgaXNsYW5kOiBjb25maWcuaXNsYW5kIGFzIEZyYW1ld29ya09wdGlvbnNbJ2lzbGFuZCddLFxuICAgIGJ1aWxkOiBjb25maWcub3V0cHV0IGFzIEZyYW1ld29ya09wdGlvbnNbJ2J1aWxkJ10sXG4gIH07XG4gIHJldHVybiBjcmVhdGVPcGVuUGx1Z2luKG9wdGlvbnMpO1xufVxuXG5leHBvcnQgdHlwZSB7IEZyYW1ld29ya09wdGlvbnMgfTtcblxuLy8gQnVpbGQgY29udGV4dFxuZXhwb3J0IHsgT3BlbkVsZW1lbnRCdWlsZENvbnRleHQgfSBmcm9tICcuL2J1aWxkLWNvbnRleHQuanMnO1xuXG4vLyBCdWlsZCBtYW5pZmVzdFxuZXhwb3J0IHR5cGUgeyBBcnRpZmFjdEluZm8sIEJ1aWxkTWFuaWZlc3QgfSBmcm9tICcuL2J1aWxkLW1hbmlmZXN0LmpzJztcbmV4cG9ydCB7IHByaW50QnVpbGRNYW5pZmVzdCwgc2NhbkNsaWVudEJ1aWxkLCBzY2FuU1NHT3V0cHV0IH0gZnJvbSAnLi9idWlsZC1tYW5pZmVzdC5qcyc7XG5cbi8vIFNTRyBwb3N0LXByb2Nlc3NpbmcgJiBpc2xhbmQgbWFuaWZlc3RzIChhZGFwdGVyLXZpdGUgaW50ZXJuYWwgYnVpbGQgaGVscGVycylcbmV4cG9ydCB7XG4gIGJ1aWxkSXNsYW5kQ2h1bmtNYXAsXG4gIGJ1aWxkU3BlY3VsYXRpb25SdWxlc0pzb24sXG4gIGV4dHJhY3RDdXN0b21FbGVtZW50VGFncyxcbiAgZ2VuZXJhdGVJc2xhbmRNYW5pZmVzdHMsXG4gIGluamVjdENsaWVudFNjcmlwdCxcbiAgaW5qZWN0Q3NwTWV0YSxcbiAgaW5qZWN0RHNkUG9seWZpbGwsXG4gIGluamVjdFNwZWN1bGF0aW9uUnVsZXMsXG4gIGluamVjdFZpZXdUcmFuc2l0aW9uTWV0YSxcbiAgaW5zZXJ0QWZ0ZXJIZWFkLFxuICB0eXBlIElzbGFuZExheWVyTWFwLFxuICB0eXBlIElzbGFuZE1hbmlmZXN0RW50cnksXG4gIHR5cGUgSXNsYW5kU3RyYXRlZ3lNYXAsXG4gIHR5cGUgUGFnZUlzbGFuZE1hbmlmZXN0LFxuICB3cml0ZUlzbGFuZE1hbmlmZXN0cyxcbn0gZnJvbSAnQG9wZW5lbGVtZW50L3NzZyc7XG5cbi8vIFByb3RvY29sIHR5cGUgcmUtZXhwb3J0c1xuZXhwb3J0IHR5cGUgeyBTcGVjdWxhdGlvblJ1bGVzT3B0aW9ucyB9IGZyb20gJ0BvcGVuZWxlbWVudC9wcm90b2NvbC9zc2cnO1xuXG4vLyBFeHRlcm5hbCByZXNvbHZlciB0eXBlcyB1c2VkIGJ5IHRoZSBhZGFwdGVyLXZpdGUgYnVpbGQgcGlwZWxpbmVcbmV4cG9ydCB0eXBlIHsgRXh0ZXJuYWxNYW5pZmVzdCB9IGZyb20gJ0BvcGVuZWxlbWVudC9wcm90b2NvbC9zc2cnO1xuXG4vLyBTdWJwYXRoIHJlc29sdmVyIChwdWJsaWMgY29uc3RhbnRzKVxuZXhwb3J0IHsgQ09SRV9TVUJQQVRIUywgVklSVFVBTF9DT1JFX1BSRUZJWCB9IGZyb20gJy4vc3VicGF0aC1yZXNvbHZlci5qcyc7XG5cbi8vIEhlYWQgaW5qZWN0aW9uIChwdWJsaWMgaGVscGVycylcbmV4cG9ydCB7IGFzc2VydE5vU2NyaXB0VGFncywgYnVpbGRIZWFkRXh0cmFzLCB2YWxpZGF0ZVNhZmVVcmwgfSBmcm9tICcuL2hlYWQtaW5qZWN0aW9uLmpzJztcbmV4cG9ydCB0eXBlIHsgSGVhZEV4dHJhc1Jlc3VsdCB9IGZyb20gJy4vaGVhZC1pbmplY3Rpb24uanMnO1xuXG4vLyBNRFggaW50ZWdyYXRpb25cbmV4cG9ydCB7IG1keFBsdWdpbiwgb3Blbk1keCB9IGZyb20gJy4vcGx1Z2luLW1keC5qcyc7XG5leHBvcnQgdHlwZSB7IE9wZW5NZHhQbHVnaW5PcHRpb25zIH0gZnJvbSAnLi9wbHVnaW4tbWR4LmpzJztcblxuLy8gTml0cm8gcnVudGltZSBwcm9vZiBib3VuZGFyeVxuZXhwb3J0IHsgY3JlYXRlT3BlbkVsZW1lbnROaXRyb0hhbmRsZXIgfSBmcm9tICcuL25pdHJvLW1vdW50LmpzJztcbmV4cG9ydCB0eXBlIHtcbiAgTml0cm9MaWtlUmVxdWVzdEV2ZW50LFxuICBOaXRyb0xpa2VSZXNwb25zZSxcbiAgT3BlbkVsZW1lbnROaXRyb01vdW50T3B0aW9ucyxcbn0gZnJvbSAnLi9uaXRyby1tb3VudC5qcyc7XG5cbi8vIERlZmF1bHQgZXhwb3J0XG5leHBvcnQgeyBvcGVuUGlwZWxpbmUgYXMgZGVmYXVsdCB9O1xuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBc0JDLEdBRUQscUJBQXFCO0FBR3JCLFNBQVMsZ0JBQWdCLFFBQVEsY0FBYztBQVcvQyxPQUFPLFNBQVMsYUFBYSxTQUE2QixDQUFDLENBQUM7RUFDMUQsTUFBTSxVQUE0QjtJQUNoQyxXQUFXLE9BQU8sTUFBTSxFQUFFLE9BQU87SUFDakMsWUFBWSxPQUFPLE1BQU0sRUFBRSxPQUFPO0lBQ2xDLGVBQWU7SUFDZixnQkFBZ0IsT0FBTyxjQUFjLElBQUk7SUFDekMsWUFBWSxPQUFPLFVBQVU7SUFDN0IsUUFBUSxPQUFPLE1BQU07SUFDckIsT0FBTyxPQUFPLE1BQU07RUFDdEI7RUFDQSxPQUFPLGlCQUFpQjtBQUMxQjtBQUlBLGdCQUFnQjtBQUNoQixTQUFTLHVCQUF1QixRQUFRLHFCQUFxQjtBQUk3RCxTQUFTLGtCQUFrQixFQUFFLGVBQWUsRUFBRSxhQUFhLFFBQVEsc0JBQXNCO0FBRXpGLCtFQUErRTtBQUMvRSxTQUNFLG1CQUFtQixFQUNuQix5QkFBeUIsRUFDekIsd0JBQXdCLEVBQ3hCLHVCQUF1QixFQUN2QixrQkFBa0IsRUFDbEIsYUFBYSxFQUNiLGlCQUFpQixFQUNqQixzQkFBc0IsRUFDdEIsd0JBQXdCLEVBQ3hCLGVBQWUsRUFLZixvQkFBb0IsUUFDZixtQkFBbUI7QUFRMUIsc0NBQXNDO0FBQ3RDLFNBQVMsYUFBYSxFQUFFLG1CQUFtQixRQUFRLHdCQUF3QjtBQUUzRSxrQ0FBa0M7QUFDbEMsU0FBUyxrQkFBa0IsRUFBRSxlQUFlLEVBQUUsZUFBZSxRQUFRLHNCQUFzQjtBQUczRixrQkFBa0I7QUFDbEIsU0FBUyxTQUFTLEVBQUUsT0FBTyxRQUFRLGtCQUFrQjtBQUdyRCwrQkFBK0I7QUFDL0IsU0FBUyw2QkFBNkIsUUFBUSxtQkFBbUI7QUFPakUsaUJBQWlCO0FBQ2pCLFNBQVMsZ0JBQWdCLE9BQU8sR0FBRyJ9
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @openelement/adapter-vite — Island transform Vite plugin.
3
+ *
4
+ * Thin wrapper around @openelement/core/island-transform.
5
+ * The core logic is a pure function with zero Vite dependency;
6
+ * this file only adapts it to the Vite Plugin interface.
7
+ */ import { transformIslandSource } from '@openelement/core';
8
+ /** Vite plugin that injects `__island` and `__tagName` markers into island components */ export function islandTransformPlugin(islandsDir) {
9
+ const normalizedDir = islandsDir.replace(/\\/g, '/');
10
+ return {
11
+ name: 'open:island-transform',
12
+ transform (code, id) {
13
+ try {
14
+ const result = transformIslandSource(code, {
15
+ islandsDir: normalizedDir,
16
+ filePath: id.replace(/\\/g, '/')
17
+ });
18
+ if (result.islands.length === 0) return null;
19
+ return result.code;
20
+ } catch (e) {
21
+ // Route errors through Vite's this.error() for proper build failure reporting
22
+ this.error(e.message);
23
+ }
24
+ }
25
+ };
26
+ }
27
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hZGFwdGVyLXZpdGUvc3JjL2lzbGFuZC10cmFuc2Zvcm0udHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAb3BlbmVsZW1lbnQvYWRhcHRlci12aXRlIOKAlCBJc2xhbmQgdHJhbnNmb3JtIFZpdGUgcGx1Z2luLlxuICpcbiAqIFRoaW4gd3JhcHBlciBhcm91bmQgQG9wZW5lbGVtZW50L2NvcmUvaXNsYW5kLXRyYW5zZm9ybS5cbiAqIFRoZSBjb3JlIGxvZ2ljIGlzIGEgcHVyZSBmdW5jdGlvbiB3aXRoIHplcm8gVml0ZSBkZXBlbmRlbmN5O1xuICogdGhpcyBmaWxlIG9ubHkgYWRhcHRzIGl0IHRvIHRoZSBWaXRlIFBsdWdpbiBpbnRlcmZhY2UuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBQbHVnaW4gfSBmcm9tICd2aXRlJztcbmltcG9ydCB7IHRyYW5zZm9ybUlzbGFuZFNvdXJjZSB9IGZyb20gJ0BvcGVuZWxlbWVudC9jb3JlJztcblxuLyoqIFZpdGUgcGx1Z2luIHRoYXQgaW5qZWN0cyBgX19pc2xhbmRgIGFuZCBgX190YWdOYW1lYCBtYXJrZXJzIGludG8gaXNsYW5kIGNvbXBvbmVudHMgKi9cbmV4cG9ydCBmdW5jdGlvbiBpc2xhbmRUcmFuc2Zvcm1QbHVnaW4oaXNsYW5kc0Rpcjogc3RyaW5nKTogUGx1Z2luIHtcbiAgY29uc3Qgbm9ybWFsaXplZERpciA9IGlzbGFuZHNEaXIucmVwbGFjZSgvXFxcXC9nLCAnLycpO1xuXG4gIHJldHVybiB7XG4gICAgbmFtZTogJ29wZW46aXNsYW5kLXRyYW5zZm9ybScsXG5cbiAgICB0cmFuc2Zvcm0oY29kZSwgaWQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IHRyYW5zZm9ybUlzbGFuZFNvdXJjZShjb2RlLCB7XG4gICAgICAgICAgaXNsYW5kc0Rpcjogbm9ybWFsaXplZERpcixcbiAgICAgICAgICBmaWxlUGF0aDogaWQucmVwbGFjZSgvXFxcXC9nLCAnLycpLFxuICAgICAgICB9KTtcblxuICAgICAgICBpZiAocmVzdWx0LmlzbGFuZHMubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbDtcblxuICAgICAgICByZXR1cm4gcmVzdWx0LmNvZGU7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIC8vIFJvdXRlIGVycm9ycyB0aHJvdWdoIFZpdGUncyB0aGlzLmVycm9yKCkgZm9yIHByb3BlciBidWlsZCBmYWlsdXJlIHJlcG9ydGluZ1xuICAgICAgICB0aGlzLmVycm9yKChlIGFzIEVycm9yKS5tZXNzYWdlKTtcbiAgICAgIH1cbiAgICB9LFxuICB9O1xufVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Q0FNQyxHQUdELFNBQVMscUJBQXFCLFFBQVEsb0JBQW9CO0FBRTFELHVGQUF1RixHQUN2RixPQUFPLFNBQVMsc0JBQXNCLFVBQWtCO0VBQ3RELE1BQU0sZ0JBQWdCLFdBQVcsT0FBTyxDQUFDLE9BQU87RUFFaEQsT0FBTztJQUNMLE1BQU07SUFFTixXQUFVLElBQUksRUFBRSxFQUFFO01BQ2hCLElBQUk7UUFDRixNQUFNLFNBQVMsc0JBQXNCLE1BQU07VUFDekMsWUFBWTtVQUNaLFVBQVUsR0FBRyxPQUFPLENBQUMsT0FBTztRQUM5QjtRQUVBLElBQUksT0FBTyxPQUFPLENBQUMsTUFBTSxLQUFLLEdBQUcsT0FBTztRQUV4QyxPQUFPLE9BQU8sSUFBSTtNQUNwQixFQUFFLE9BQU8sR0FBRztRQUNWLDhFQUE4RTtRQUM5RSxJQUFJLENBQUMsS0FBSyxDQUFDLEFBQUMsRUFBWSxPQUFPO01BQ2pDO0lBQ0Y7RUFDRjtBQUNGIn0=
@@ -0,0 +1,24 @@
1
+ import type { OpenElementRequestHandler } from '@openelement/core/runtime';
2
+ export interface NitroLikeRequestEvent<Env extends Record<string, unknown> = Record<string, unknown>> {
3
+ request?: Request;
4
+ method?: string;
5
+ path?: string;
6
+ url?: string;
7
+ headers?: HeadersInit;
8
+ body?: BodyInit | null;
9
+ env?: Env;
10
+ platform?: unknown;
11
+ }
12
+ export interface NitroLikeResponse {
13
+ status: number;
14
+ headers: Headers;
15
+ body: BodyInit | null;
16
+ response: Response;
17
+ }
18
+ export interface OpenElementNitroMountOptions<Env extends Record<string, unknown> = Record<string, unknown>> {
19
+ handler: OpenElementRequestHandler<Env>;
20
+ baseUrl?: string;
21
+ env?: Env;
22
+ platform?: unknown;
23
+ }
24
+ export declare function createOpenElementNitroHandler<Env extends Record<string, unknown> = Record<string, unknown>>(options: OpenElementNitroMountOptions<Env>): (event: NitroLikeRequestEvent<Env>) => Promise<NitroLikeResponse>;
@@ -0,0 +1,27 @@
1
+ function toRequest(event, baseUrl) {
2
+ if (event.request) return event.request;
3
+ const url = event.url ? new URL(event.url, baseUrl) : new URL(event.path || '/', baseUrl);
4
+ return new Request(url, {
5
+ method: event.method || 'GET',
6
+ headers: event.headers,
7
+ body: event.body
8
+ });
9
+ }
10
+ export function createOpenElementNitroHandler(options) {
11
+ const baseUrl = options.baseUrl || 'http://localhost';
12
+ return async (event)=>{
13
+ const request = toRequest(event, baseUrl);
14
+ const context = {
15
+ env: event.env || options.env,
16
+ platform: event.platform || options.platform
17
+ };
18
+ const response = await options.handler(request, context);
19
+ return {
20
+ status: response.status,
21
+ headers: response.headers,
22
+ body: response.body,
23
+ response
24
+ };
25
+ };
26
+ }
27
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hZGFwdGVyLXZpdGUvc3JjL25pdHJvLW1vdW50LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgT3BlbkVsZW1lbnRSZXF1ZXN0SGFuZGxlciwgUnVudGltZUNvbnRleHQgfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZS9ydW50aW1lJztcblxuZXhwb3J0IGludGVyZmFjZSBOaXRyb0xpa2VSZXF1ZXN0RXZlbnQ8XG4gIEVudiBleHRlbmRzIFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0gUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4+IHtcbiAgcmVxdWVzdD86IFJlcXVlc3Q7XG4gIG1ldGhvZD86IHN0cmluZztcbiAgcGF0aD86IHN0cmluZztcbiAgdXJsPzogc3RyaW5nO1xuICBoZWFkZXJzPzogSGVhZGVyc0luaXQ7XG4gIGJvZHk/OiBCb2R5SW5pdCB8IG51bGw7XG4gIGVudj86IEVudjtcbiAgcGxhdGZvcm0/OiB1bmtub3duO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE5pdHJvTGlrZVJlc3BvbnNlIHtcbiAgc3RhdHVzOiBudW1iZXI7XG4gIGhlYWRlcnM6IEhlYWRlcnM7XG4gIGJvZHk6IEJvZHlJbml0IHwgbnVsbDtcbiAgcmVzcG9uc2U6IFJlc3BvbnNlO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE9wZW5FbGVtZW50Tml0cm9Nb3VudE9wdGlvbnM8XG4gIEVudiBleHRlbmRzIFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0gUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4+IHtcbiAgaGFuZGxlcjogT3BlbkVsZW1lbnRSZXF1ZXN0SGFuZGxlcjxFbnY+O1xuICBiYXNlVXJsPzogc3RyaW5nO1xuICBlbnY/OiBFbnY7XG4gIHBsYXRmb3JtPzogdW5rbm93bjtcbn1cblxuZnVuY3Rpb24gdG9SZXF1ZXN0KGV2ZW50OiBOaXRyb0xpa2VSZXF1ZXN0RXZlbnQsIGJhc2VVcmw6IHN0cmluZyk6IFJlcXVlc3Qge1xuICBpZiAoZXZlbnQucmVxdWVzdCkgcmV0dXJuIGV2ZW50LnJlcXVlc3Q7XG5cbiAgY29uc3QgdXJsID0gZXZlbnQudXJsID8gbmV3IFVSTChldmVudC51cmwsIGJhc2VVcmwpIDogbmV3IFVSTChldmVudC5wYXRoIHx8ICcvJywgYmFzZVVybCk7XG5cbiAgcmV0dXJuIG5ldyBSZXF1ZXN0KHVybCwge1xuICAgIG1ldGhvZDogZXZlbnQubWV0aG9kIHx8ICdHRVQnLFxuICAgIGhlYWRlcnM6IGV2ZW50LmhlYWRlcnMsXG4gICAgYm9keTogZXZlbnQuYm9keSxcbiAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVPcGVuRWxlbWVudE5pdHJvSGFuZGxlcjxcbiAgRW52IGV4dGVuZHMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbj4oXG4gIG9wdGlvbnM6IE9wZW5FbGVtZW50Tml0cm9Nb3VudE9wdGlvbnM8RW52Pixcbik6IChldmVudDogTml0cm9MaWtlUmVxdWVzdEV2ZW50PEVudj4pID0+IFByb21pc2U8Tml0cm9MaWtlUmVzcG9uc2U+IHtcbiAgY29uc3QgYmFzZVVybCA9IG9wdGlvbnMuYmFzZVVybCB8fCAnaHR0cDovL2xvY2FsaG9zdCc7XG5cbiAgcmV0dXJuIGFzeW5jIChldmVudDogTml0cm9MaWtlUmVxdWVzdEV2ZW50PEVudj4pOiBQcm9taXNlPE5pdHJvTGlrZVJlc3BvbnNlPiA9PiB7XG4gICAgY29uc3QgcmVxdWVzdCA9IHRvUmVxdWVzdChldmVudCwgYmFzZVVybCk7XG4gICAgY29uc3QgY29udGV4dDogUnVudGltZUNvbnRleHQ8RW52PiA9IHtcbiAgICAgIGVudjogZXZlbnQuZW52IHx8IG9wdGlvbnMuZW52LFxuICAgICAgcGxhdGZvcm06IGV2ZW50LnBsYXRmb3JtIHx8IG9wdGlvbnMucGxhdGZvcm0sXG4gICAgfTtcbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IG9wdGlvbnMuaGFuZGxlcihyZXF1ZXN0LCBjb250ZXh0KTtcblxuICAgIHJldHVybiB7XG4gICAgICBzdGF0dXM6IHJlc3BvbnNlLnN0YXR1cyxcbiAgICAgIGhlYWRlcnM6IHJlc3BvbnNlLmhlYWRlcnMsXG4gICAgICBib2R5OiByZXNwb25zZS5ib2R5LFxuICAgICAgcmVzcG9uc2UsXG4gICAgfTtcbiAgfTtcbn1cbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUErQkEsU0FBUyxVQUFVLEtBQTRCLEVBQUUsT0FBZTtFQUM5RCxJQUFJLE1BQU0sT0FBTyxFQUFFLE9BQU8sTUFBTSxPQUFPO0VBRXZDLE1BQU0sTUFBTSxNQUFNLEdBQUcsR0FBRyxJQUFJLElBQUksTUFBTSxHQUFHLEVBQUUsV0FBVyxJQUFJLElBQUksTUFBTSxJQUFJLElBQUksS0FBSztFQUVqRixPQUFPLElBQUksUUFBUSxLQUFLO0lBQ3RCLFFBQVEsTUFBTSxNQUFNLElBQUk7SUFDeEIsU0FBUyxNQUFNLE9BQU87SUFDdEIsTUFBTSxNQUFNLElBQUk7RUFDbEI7QUFDRjtBQUVBLE9BQU8sU0FBUyw4QkFHZCxPQUEwQztFQUUxQyxNQUFNLFVBQVUsUUFBUSxPQUFPLElBQUk7RUFFbkMsT0FBTyxPQUFPO0lBQ1osTUFBTSxVQUFVLFVBQVUsT0FBTztJQUNqQyxNQUFNLFVBQStCO01BQ25DLEtBQUssTUFBTSxHQUFHLElBQUksUUFBUSxHQUFHO01BQzdCLFVBQVUsTUFBTSxRQUFRLElBQUksUUFBUSxRQUFRO0lBQzlDO0lBQ0EsTUFBTSxXQUFXLE1BQU0sUUFBUSxPQUFPLENBQUMsU0FBUztJQUVoRCxPQUFPO01BQ0wsUUFBUSxTQUFTLE1BQU07TUFDdkIsU0FBUyxTQUFTLE9BQU87TUFDekIsTUFBTSxTQUFTLElBQUk7TUFDbkI7SUFDRjtFQUNGO0FBQ0YifQ==
@@ -0,0 +1,7 @@
1
+ import type { Plugin } from 'vite';
2
+ export interface OpenMdxPluginOptions {
3
+ jsxImportSource?: string;
4
+ development?: boolean;
5
+ }
6
+ export declare function mdxPlugin(options?: OpenMdxPluginOptions): Plugin;
7
+ export { mdxPlugin as openMdx };
@@ -0,0 +1,15 @@
1
+ import mdx from '@mdx-js/rollup';
2
+ export function mdxPlugin(options = {}) {
3
+ const plugin = mdx({
4
+ jsxImportSource: options.jsxImportSource ?? '@openelement/core',
5
+ providerImportSource: undefined,
6
+ development: options.development ?? false
7
+ });
8
+ return {
9
+ ...plugin,
10
+ name: 'open:mdx',
11
+ enforce: 'pre'
12
+ };
13
+ }
14
+ export { mdxPlugin as openMdx };
15
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9hZGFwdGVyLXZpdGUvc3JjL3BsdWdpbi1tZHgudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBQbHVnaW4gfSBmcm9tICd2aXRlJztcbmltcG9ydCBtZHggZnJvbSAnQG1keC1qcy9yb2xsdXAnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE9wZW5NZHhQbHVnaW5PcHRpb25zIHtcbiAganN4SW1wb3J0U291cmNlPzogc3RyaW5nO1xuICBkZXZlbG9wbWVudD86IGJvb2xlYW47XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtZHhQbHVnaW4ob3B0aW9uczogT3Blbk1keFBsdWdpbk9wdGlvbnMgPSB7fSk6IFBsdWdpbiB7XG4gIGNvbnN0IHBsdWdpbiA9IG1keCh7XG4gICAganN4SW1wb3J0U291cmNlOiBvcHRpb25zLmpzeEltcG9ydFNvdXJjZSA/PyAnQG9wZW5lbGVtZW50L2NvcmUnLFxuICAgIHByb3ZpZGVySW1wb3J0U291cmNlOiB1bmRlZmluZWQsXG4gICAgZGV2ZWxvcG1lbnQ6IG9wdGlvbnMuZGV2ZWxvcG1lbnQgPz8gZmFsc2UsXG4gIH0pIGFzIFBsdWdpbjtcblxuICByZXR1cm4ge1xuICAgIC4uLnBsdWdpbixcbiAgICBuYW1lOiAnb3BlbjptZHgnLFxuICAgIGVuZm9yY2U6ICdwcmUnLFxuICB9O1xufVxuXG5leHBvcnQgeyBtZHhQbHVnaW4gYXMgb3Blbk1keCB9O1xuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sU0FBUyxpQkFBaUI7QUFPakMsT0FBTyxTQUFTLFVBQVUsVUFBZ0MsQ0FBQyxDQUFDO0VBQzFELE1BQU0sU0FBUyxJQUFJO0lBQ2pCLGlCQUFpQixRQUFRLGVBQWUsSUFBSTtJQUM1QyxzQkFBc0I7SUFDdEIsYUFBYSxRQUFRLFdBQVcsSUFBSTtFQUN0QztFQUVBLE9BQU87SUFDTCxHQUFHLE1BQU07SUFDVCxNQUFNO0lBQ04sU0FBUztFQUNYO0FBQ0Y7QUFFQSxTQUFTLGFBQWEsT0FBTyxHQUFHIn0=
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Extracted from index.ts in v0.22 (SOP-004: adapter-vite decomposition).
3
+ *
4
+ * This is the core build plugin implementation. It is NOT part of the
5
+ * public API. Use `openPipeline()` from the main entry instead.
6
+ *
7
+ * Internal only: called by openPipeline() and the @openelement/app umbrella.
8
+ */ import type { Plugin } from 'vite';
9
+ import type { FrameworkOptions } from '@openelement/protocol/framework';
10
+ import { OpenElementBuildContext } from "./build-context.js";
11
+ export declare function optionalPackageStubsPlugin(): Plugin;
12
+ /**
13
+ * This is the core build plugin implementation. It is NOT part of the
14
+ * public API. Use `openPipeline()` from @openelement/adapter-vite instead.
15
+ *
16
+ * Internal only: called by openPipeline() and the @openelement/app umbrella.
17
+ * Jamstack: M=SSG+DSD, A=API Routes, J=Islands.
18
+ *
19
+ * @param options - Framework options
20
+ * @param externalCtx - Optional shared OpenElementBuildContext (used by openElement() umbrella)
21
+ * @internal
22
+ */ export declare function createOpenPlugin(options?: FrameworkOptions, externalCtx?: OpenElementBuildContext): Plugin[];