@vesk/adapter 0.1.14 → 0.2.9
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/dist/api-function.d.ts.map +1 -1
- package/dist/api-function.js +8 -5
- package/dist/client-bundle.d.ts.map +1 -1
- package/dist/client-bundle.js +167 -27
- package/dist/dev-server.d.ts +3 -0
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +47 -22
- package/dist/esbuild-fallback.d.ts +0 -1
- package/dist/esbuild-fallback.d.ts.map +1 -1
- package/dist/esbuild-fallback.js +10 -20
- package/dist/hmr.d.ts.map +1 -1
- package/dist/hmr.js +19 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +8 -2
- package/dist/paths.d.ts +16 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +81 -0
- package/dist/platform-handler.d.ts.map +1 -1
- package/dist/platform-handler.js +11 -2
- package/dist/prod-server.d.ts +5 -0
- package/dist/prod-server.d.ts.map +1 -1
- package/dist/prod-server.js +88 -32
- package/dist/runtime-bundle.d.ts.map +1 -1
- package/dist/runtime-bundle.js +9 -4
- package/dist/ssr-function.d.ts.map +1 -1
- package/dist/ssr-function.js +24 -13
- package/dist/static.d.ts.map +1 -1
- package/dist/static.js +9 -1
- package/dist/types.d.ts +1 -181
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-function.d.ts","sourceRoot":"","sources":["../src/api-function.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAOhF,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,kBAAkB,GAC3B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"api-function.d.ts","sourceRoot":"","sources":["../src/api-function.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAOhF,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,kBAAkB,GAC3B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAyLtD"}
|
package/dist/api-function.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { stripCodeTypes } from '@vesk/compiler/src/strip-ts';
|
|
4
4
|
function apiRouteName(fullPath) {
|
|
5
5
|
const parts = fullPath.split('/').filter(Boolean);
|
|
6
6
|
return parts.map(s => s.startsWith(':') ? s.slice(1) || 'param' : s).join('_') || 'index';
|
|
@@ -10,6 +10,8 @@ export function generateApiFunction(apiNode, _apiDir, outDir, options) {
|
|
|
10
10
|
const name = apiRouteName(apiNode.fullPath);
|
|
11
11
|
const funcPath = resolve(outDir, 'server', 'api', `${name}.js`);
|
|
12
12
|
const routeFilePath = apiNode.filePath;
|
|
13
|
+
if (!routeFilePath)
|
|
14
|
+
throw new Error(`api route ${apiNode.fullPath} has no filePath`);
|
|
13
15
|
let routeSrc = readFileSync(routeFilePath, 'utf-8');
|
|
14
16
|
routeSrc = routeSrc
|
|
15
17
|
.replace(/from\s+['"]@vesk\/runtime['"]\s*;?/g, "from '../runtime.js';")
|
|
@@ -18,8 +20,7 @@ export function generateApiFunction(apiNode, _apiDir, outDir, options) {
|
|
|
18
20
|
});
|
|
19
21
|
if (routeFilePath.endsWith('.ts')) {
|
|
20
22
|
try {
|
|
21
|
-
|
|
22
|
-
routeSrc = result.code;
|
|
23
|
+
routeSrc = stripCodeTypes(routeSrc);
|
|
23
24
|
}
|
|
24
25
|
catch {
|
|
25
26
|
// fall back to original source if stripping fails
|
|
@@ -57,12 +58,13 @@ export function generateApiFunction(apiNode, _apiDir, outDir, options) {
|
|
|
57
58
|
' get: () => Object.fromEntries(url.searchParams.entries()),',
|
|
58
59
|
' enumerable: true,',
|
|
59
60
|
' });',
|
|
61
|
+
" const __rootLocals = (globalThis.__vesk_request && globalThis.__vesk_request.locals) || {};",
|
|
60
62
|
' // ── Middleware context ──',
|
|
61
63
|
' const __ctx = {',
|
|
62
64
|
' request,',
|
|
63
65
|
' params,',
|
|
64
66
|
' url,',
|
|
65
|
-
' locals: {},',
|
|
67
|
+
' locals: Object.assign({}, __rootLocals),',
|
|
66
68
|
" cookies: parseCookies(request.headers.get('cookie') || ''),",
|
|
67
69
|
' set(key, value) { this.locals[key] = value; },',
|
|
68
70
|
' get(key) { return this.locals[key]; },',
|
|
@@ -124,12 +126,13 @@ export function generateApiFunction(apiNode, _apiDir, outDir, options) {
|
|
|
124
126
|
' get: () => Object.fromEntries(url.searchParams.entries()),',
|
|
125
127
|
' enumerable: true,',
|
|
126
128
|
' });',
|
|
129
|
+
" const __rootLocals = (globalThis.__vesk_request && globalThis.__vesk_request.locals) || {};",
|
|
127
130
|
' const ctx = {',
|
|
128
131
|
" headers: Object.fromEntries(request.headers.entries()),",
|
|
129
132
|
' url: request.url,',
|
|
130
133
|
' method,',
|
|
131
134
|
" cookies: parseCookies(request.headers.get('cookie') || ''),",
|
|
132
|
-
' locals: {},',
|
|
135
|
+
' locals: Object.assign({}, __rootLocals),',
|
|
133
136
|
' };',
|
|
134
137
|
" Object.defineProperty(request, 'locals', {",
|
|
135
138
|
' get: () => ctx.locals,',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-bundle.d.ts","sourceRoot":"","sources":["../src/client-bundle.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client-bundle.d.ts","sourceRoot":"","sources":["../src/client-bundle.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,kBAAkB,EAAqC,MAAM,yBAAyB,CAAC;AAoCrI,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,SAAS,EAAE,EACtB,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAoT7B;AAMD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA+B3D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAUlE;AAID;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BrG"}
|
package/dist/client-bundle.js
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
import { readFileSync, existsSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, existsSync, writeFileSync, unlinkSync, statSync } from 'node:fs';
|
|
2
2
|
import { resolve, join, dirname, relative, sep } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { build
|
|
5
|
-
import {
|
|
4
|
+
import { build } from './esbuild-fallback.js';
|
|
5
|
+
import { stripCodeTypes } from '@vesk/compiler/src/strip-ts';
|
|
6
|
+
import { compileClient, compileClientBoth } from '@vesk/compiler/src/client-codegen';
|
|
6
7
|
import { resolveComponentName } from '@vesk/compiler/src/server-codegen';
|
|
7
8
|
import { collectVskImportPaths, vskImportLines } from '@vesk/compiler/src/vsk-imports';
|
|
9
|
+
import { inlineMdContentAttrs, guessProjectRoots } from '@vesk/compiler/src/md-inline';
|
|
8
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
function fileUnchanged(filePath, cached) {
|
|
12
|
+
try {
|
|
13
|
+
const st = statSync(filePath);
|
|
14
|
+
return st.mtimeMs === cached.mtimeMs && st.size === cached.size;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
9
20
|
function buildRouterOpts(options) {
|
|
10
21
|
const ttl = options?.routeDataCache;
|
|
11
22
|
if (typeof ttl === 'number' && ttl > 0) {
|
|
@@ -33,8 +44,21 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
|
|
|
33
44
|
const seen = new Set();
|
|
34
45
|
const chunks = [];
|
|
35
46
|
const runtimeImportNames = new Set();
|
|
47
|
+
const cache = options?.cache;
|
|
48
|
+
const only = options?.only && options.only.length > 0 ? new Set(options.only) : null;
|
|
49
|
+
const returnEdited = !!options?.returnEditedSources && !!only;
|
|
50
|
+
const editedSources = returnEdited ? new Map() : undefined;
|
|
51
|
+
const editedNames = returnEdited ? new Map() : undefined;
|
|
52
|
+
let cachedFileHits = 0;
|
|
53
|
+
let compiledFiles = 0;
|
|
54
|
+
let mainFromCache = false;
|
|
55
|
+
function mustReuseWithoutStat(filePath) {
|
|
56
|
+
return !!only && !only.has(filePath);
|
|
57
|
+
}
|
|
36
58
|
function collectRuntimeImports(code) {
|
|
37
|
-
|
|
59
|
+
// matches bare '@vesk/runtime' and any of its subpaths
|
|
60
|
+
// ('@vesk/runtime/router', '@vesk/runtime/server', …)
|
|
61
|
+
const re = /^import\s*\{([^}]*)\}\s*from\s*['"]@vesk\/runtime(\/[a-zA-Z0-9-]+)*['"];?\s*\n?/gm;
|
|
38
62
|
for (const m of code.matchAll(re)) {
|
|
39
63
|
for (const name of m[1].split(',')) {
|
|
40
64
|
const trimmed = name.trim().replace(/^(\w+)\s+as\s+.*$/, '$1');
|
|
@@ -45,7 +69,7 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
|
|
|
45
69
|
}
|
|
46
70
|
}
|
|
47
71
|
function stripRuntimeImport(code) {
|
|
48
|
-
return code.replace(/^import\s*\{[^}]*\}\s*from\s*['"]@vesk\/runtime['"];?\s*\n?/gm, '')
|
|
72
|
+
return code.replace(/^import\s*\{[^}]*\}\s*from\s*['"]@vesk\/runtime(\/[a-zA-Z0-9-]+)*['"];?\s*\n?/gm, '')
|
|
49
73
|
.replace(/const\s+__components\s*=\s*\{\};\s*\n?/g, '')
|
|
50
74
|
.replace(/^function __cleanup\(start, end\) \{[\s\S]*?\n\}\s*\n?/gm, '')
|
|
51
75
|
.replace(/^function __place\(start, end, nodes, fallback\) \{[\s\S]*?\n\}\s*\n?/gm, '');
|
|
@@ -55,16 +79,22 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
|
|
|
55
79
|
}
|
|
56
80
|
function resolveVskImports(filePath, compile) {
|
|
57
81
|
const src = readFileSync(filePath, 'utf-8');
|
|
82
|
+
const resolved = [];
|
|
58
83
|
for (const importPath of collectVskImportPaths(vskImportLines(src), filePath)) {
|
|
59
|
-
let importedName = null;
|
|
60
84
|
try {
|
|
61
|
-
|
|
85
|
+
readFileSync(importPath);
|
|
62
86
|
}
|
|
63
87
|
catch {
|
|
64
88
|
continue;
|
|
65
89
|
}
|
|
66
|
-
|
|
90
|
+
// resolvedName is intentionally null: compileFile derives the same
|
|
91
|
+
// name from its own IR pass, and the alias branch is unreachable
|
|
92
|
+
// when resolvedName matches — this avoids a second full parse of
|
|
93
|
+
// every imported file.
|
|
94
|
+
resolved.push(importPath);
|
|
95
|
+
compile(importPath, null);
|
|
67
96
|
}
|
|
97
|
+
return resolved;
|
|
68
98
|
}
|
|
69
99
|
function stripExports(code) {
|
|
70
100
|
return code
|
|
@@ -75,26 +105,88 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
|
|
|
75
105
|
if (seen.has(filePath))
|
|
76
106
|
return;
|
|
77
107
|
seen.add(filePath);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
108
|
+
// Cache-hit fast path: replay the entry's recorded contribution with no
|
|
109
|
+
// file reads or parses. Deps recurse through the same path so each hit
|
|
110
|
+
// costs one stat call (zero with `only`).
|
|
111
|
+
const cachedEntry = cache?.files.get(filePath);
|
|
112
|
+
// If one of this file's .md dependencies was the edited target, its
|
|
113
|
+
// inlined content is stale by definition — force a recompile.
|
|
114
|
+
let dependsOnEdited = false;
|
|
115
|
+
if (cachedEntry && only && !only.has(filePath)) {
|
|
116
|
+
for (const p of only) {
|
|
117
|
+
if (cachedEntry.imports.includes(p)) {
|
|
118
|
+
dependsOnEdited = true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const cacheUsable = !!cachedEntry && !dependsOnEdited &&
|
|
124
|
+
(mustReuseWithoutStat(filePath) || fileUnchanged(filePath, cachedEntry));
|
|
125
|
+
const cached = cacheUsable ? cachedEntry : undefined;
|
|
126
|
+
if (cached && cacheUsable) {
|
|
127
|
+
cachedFileHits++;
|
|
128
|
+
for (const dep of cached.imports)
|
|
129
|
+
compileFile(dep, cache?.files.get(dep)?.actualName ?? '', output);
|
|
130
|
+
if (cached.compCode)
|
|
131
|
+
output.push(cached.compCode);
|
|
132
|
+
if (cached.hydCode)
|
|
133
|
+
output.push(cached.hydCode);
|
|
134
|
+
for (const n of cached.runtimeNames)
|
|
135
|
+
runtimeImportNames.add(n);
|
|
136
|
+
if (cached.actualName && resolvedName !== null && cached.actualName !== resolvedName) {
|
|
137
|
+
output.push(`Object.defineProperty(__components, ${JSON.stringify(resolvedName)}, { get: () => __components[${JSON.stringify(cached.actualName)}], configurable: true });`);
|
|
138
|
+
output.push(`Object.defineProperty(__hydrators, ${JSON.stringify(resolvedName)}, { get: () => __hydrators[${JSON.stringify(cached.actualName)}], configurable: true });`);
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
compiledFiles++;
|
|
143
|
+
let src = readFileSync(filePath, 'utf-8');
|
|
144
|
+
if (/content=["'][^"']*\.md["']/i.test(src)) {
|
|
145
|
+
src = inlineMdContentAttrs(src, dirname(filePath), guessProjectRoots(appDir));
|
|
85
146
|
}
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
147
|
+
const namesBefore = cache ? new Set(runtimeImportNames) : null;
|
|
148
|
+
const importedPaths = resolveVskImports(filePath, (p, n) => compileFile(p, n || '', output));
|
|
149
|
+
// One parse/IR pass feeds both client modes AND the component-name
|
|
150
|
+
// lookup — the dev hot path pays the acorn+TS parse once per edit
|
|
151
|
+
// instead of three times.
|
|
152
|
+
const { comp: rawComp, hyd: rawHyd, name: actualName } = compileClientBoth(src, null);
|
|
153
|
+
const compCode = rawComp ? stripExports(stripVskImports(stripRuntimeImport(rawComp))).replace(/^\n+/, '').replace(/\n+$/, '') : '';
|
|
154
|
+
const hydCode = rawHyd ? stripExports(stripVskImports(stripRuntimeImport(rawHyd))).replace(/__components/g, '__hydrators').replace(/^\n+/, '').replace(/\n+$/, '') : '';
|
|
155
|
+
if (rawComp)
|
|
156
|
+
collectRuntimeImports(rawComp);
|
|
157
|
+
if (rawHyd)
|
|
158
|
+
collectRuntimeImports(rawHyd);
|
|
159
|
+
if (returnEdited && only.has(filePath)) {
|
|
160
|
+
// Mirrors the HMR fnSources preparation in the dev servers: drop every
|
|
161
|
+
// remaining top-level import so the snippet can be eval'd in the page
|
|
162
|
+
// context where runtime globals already exist.
|
|
163
|
+
const bare = rawComp
|
|
164
|
+
? stripExports(stripVskImports(stripRuntimeImport(rawComp)))
|
|
165
|
+
.replace(/^import\s*[\s\S]*?from\s*['"][^'"]+['"];?\s*\n?/gm, '')
|
|
166
|
+
: '';
|
|
167
|
+
editedSources.set(filePath, bare);
|
|
168
|
+
editedNames.set(filePath, actualName);
|
|
92
169
|
}
|
|
93
|
-
|
|
94
|
-
|
|
170
|
+
if (compCode)
|
|
171
|
+
output.push(compCode);
|
|
172
|
+
if (hydCode)
|
|
173
|
+
output.push(hydCode);
|
|
174
|
+
if (actualName && resolvedName !== null && actualName !== resolvedName) {
|
|
95
175
|
output.push(`Object.defineProperty(__components, ${JSON.stringify(resolvedName)}, { get: () => __components[${JSON.stringify(actualName)}], configurable: true });`);
|
|
96
176
|
output.push(`Object.defineProperty(__hydrators, ${JSON.stringify(resolvedName)}, { get: () => __hydrators[${JSON.stringify(actualName)}], configurable: true });`);
|
|
97
177
|
}
|
|
178
|
+
if (cache && namesBefore) {
|
|
179
|
+
const st = statSync(filePath);
|
|
180
|
+
cache.files.set(filePath, {
|
|
181
|
+
mtimeMs: st.mtimeMs,
|
|
182
|
+
size: st.size,
|
|
183
|
+
compCode,
|
|
184
|
+
hydCode,
|
|
185
|
+
actualName,
|
|
186
|
+
runtimeNames: [...runtimeImportNames].filter((n) => !namesBefore.has(n)),
|
|
187
|
+
imports: importedPaths,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
98
190
|
}
|
|
99
191
|
function buildChunkName(node) {
|
|
100
192
|
const dir = relative(appDir, node.sourceDir || '');
|
|
@@ -124,6 +216,14 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
|
|
|
124
216
|
if (node.notFound && existsSync(notFoundPath)) {
|
|
125
217
|
compileFile(notFoundPath, node.notFound, chunkCode);
|
|
126
218
|
}
|
|
219
|
+
const offlinePath = resolve(appDir, node.sourceDir, 'offline.vsk');
|
|
220
|
+
if (node.offline && existsSync(offlinePath)) {
|
|
221
|
+
compileFile(offlinePath, node.offline, chunkCode);
|
|
222
|
+
}
|
|
223
|
+
const networkPath = resolve(appDir, node.sourceDir, 'network.vsk');
|
|
224
|
+
if (node.network && existsSync(networkPath)) {
|
|
225
|
+
compileFile(networkPath, node.network, chunkCode);
|
|
226
|
+
}
|
|
127
227
|
const loadingPath = resolve(appDir, node.sourceDir, 'loading.vsk');
|
|
128
228
|
if (node.loading && existsSync(loadingPath)) {
|
|
129
229
|
compileFile(loadingPath, node.loading, chunkCode);
|
|
@@ -162,8 +262,18 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
|
|
|
162
262
|
}
|
|
163
263
|
}
|
|
164
264
|
annotate(routeTree);
|
|
165
|
-
const
|
|
166
|
-
|
|
265
|
+
const mainKey = JSON.stringify(routeTree) + '|' + [...runtimeImportNames].sort().join(',') + `|${!!options?.hmr}|${!!options?.importRuntime}|${options?.routeDataCache ?? ''}`;
|
|
266
|
+
let main;
|
|
267
|
+
if (cache?.mainBundle && cache.mainBundle.key === mainKey) {
|
|
268
|
+
mainFromCache = true;
|
|
269
|
+
main = cache.mainBundle.code;
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
main = await buildMainBundle(routeTree, runtimeDir, true, {}, !!options?.hmr, !!options?.importRuntime, runtimeImportNames, options?.routeDataCache);
|
|
273
|
+
if (cache)
|
|
274
|
+
cache.mainBundle = { key: mainKey, code: main };
|
|
275
|
+
}
|
|
276
|
+
return { main, chunks, cachedFileHits, compiledFiles, mainFromCache, editedSources, editedNames };
|
|
167
277
|
}
|
|
168
278
|
else {
|
|
169
279
|
let componentLines = [];
|
|
@@ -209,6 +319,12 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
|
|
|
209
319
|
const notFoundPath = resolve(appDir, node.sourceDir, 'not-found.vsk');
|
|
210
320
|
if (node.notFound && existsSync(notFoundPath))
|
|
211
321
|
compileFileMono(notFoundPath, node.notFound);
|
|
322
|
+
const offlinePath = resolve(appDir, node.sourceDir, 'offline.vsk');
|
|
323
|
+
if (node.offline && existsSync(offlinePath))
|
|
324
|
+
compileFileMono(offlinePath, node.offline);
|
|
325
|
+
const networkPath = resolve(appDir, node.sourceDir, 'network.vsk');
|
|
326
|
+
if (node.network && existsSync(networkPath))
|
|
327
|
+
compileFileMono(networkPath, node.network);
|
|
212
328
|
const loadingPath = resolve(appDir, node.sourceDir, 'loading.vsk');
|
|
213
329
|
if (node.loading && existsSync(loadingPath))
|
|
214
330
|
compileFileMono(loadingPath, node.loading);
|
|
@@ -223,11 +339,12 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
|
|
|
223
339
|
const main = await buildMainBundle(routeTree, runtimeDir, false, {
|
|
224
340
|
componentLines, hydratorLines, aliasLines, hydratorAliasLines,
|
|
225
341
|
}, !!options?.hmr, !!options?.importRuntime, runtimeImportNames, options?.routeDataCache);
|
|
342
|
+
// Mono (non-codeSplit) builds are the production path — no incremental cache.
|
|
226
343
|
return { main, chunks: [] };
|
|
227
344
|
}
|
|
228
345
|
}
|
|
229
346
|
function stripTypes(code) {
|
|
230
|
-
return
|
|
347
|
+
return stripCodeTypes(code);
|
|
231
348
|
}
|
|
232
349
|
export function buildRuntimeCode(runtimeDir) {
|
|
233
350
|
const runtimeFiles = [
|
|
@@ -324,9 +441,22 @@ export async function buildTreeShakenRuntime(runtimeDir, usedNames) {
|
|
|
324
441
|
catch { /* ignore */ }
|
|
325
442
|
}
|
|
326
443
|
}
|
|
444
|
+
/**
|
|
445
|
+
* Appends the HMR eval hook to the dev client bundle.
|
|
446
|
+
*
|
|
447
|
+
* The gadget is gated by a per-server-session nonce: the dev server generates
|
|
448
|
+
* `globalThis.__vesk_hmr_nonce` at boot and broadcasts it inside every
|
|
449
|
+
* `update` message. The browser evaluates component sources only when the
|
|
450
|
+
* message nonce matches, so a cross-site WebSocket (or any other injection
|
|
451
|
+
* vector) cannot drive arbitrary eval without also reading the nonce from a
|
|
452
|
+
* same-origin page — which the WS origin check already blocks.
|
|
453
|
+
*/
|
|
327
454
|
function appendHmrGlobals(code) {
|
|
328
455
|
return code +
|
|
329
|
-
"globalThis.__vesk_hmr_eval = (code) =>
|
|
456
|
+
"globalThis.__vesk_hmr_eval = (code, n) => {\n" +
|
|
457
|
+
" if (!n || n !== globalThis.__vesk_hmr_nonce) throw new Error('vesk hmr: invalid nonce');\n" +
|
|
458
|
+
" return eval(code);\n" +
|
|
459
|
+
"};\n";
|
|
330
460
|
}
|
|
331
461
|
async function buildMainBundle(routeTree, runtimeDir, codeSplit, mono, hmr, importRuntime, runtimeImportNames, routeDataCache) {
|
|
332
462
|
const baseRuntimeImports = ['createFileRouter', 'get', 'set', 'effect', 'track', 'destroy_block', 'getActiveComponent', 'setActiveComponent', 'NavLink', 'Link', 'reactiveProps', 'matchRoute', 'ensureChunk'];
|
|
@@ -366,6 +496,8 @@ async function buildMainBundle(routeTree, runtimeDir, codeSplit, mono, hmr, impo
|
|
|
366
496
|
" if (n._layoutName && __components[n._layoutName]) n.layout = __components[n._layoutName];\n" +
|
|
367
497
|
" if (n._errorName && __components[n._errorName]) n.error = __components[n._errorName];\n" +
|
|
368
498
|
" if (n._notFoundName && __components[n._notFoundName]) n.notFound = __components[n._notFoundName];\n" +
|
|
499
|
+
" if (n._offlineName && __components[n._offlineName]) n.offline = __components[n._offlineName];\n" +
|
|
500
|
+
" if (n._networkName && __components[n._networkName]) n.network = __components[n._networkName];\n" +
|
|
369
501
|
' if (n.children) __updateComponents(n.children);\n' +
|
|
370
502
|
' }\n' +
|
|
371
503
|
'}\n';
|
|
@@ -379,6 +511,8 @@ async function buildMainBundle(routeTree, runtimeDir, codeSplit, mono, hmr, impo
|
|
|
379
511
|
" if (typeof n.layout === 'string') n._layoutName = n.layout;\n" +
|
|
380
512
|
" if (typeof n.error === 'string') n._errorName = n.error;\n" +
|
|
381
513
|
" if (typeof n.notFound === 'string') n._notFoundName = n.notFound;\n" +
|
|
514
|
+
" if (typeof n.offline === 'string') n._offlineName = n.offline;\n" +
|
|
515
|
+
" if (typeof n.network === 'string') n._networkName = n.network;\n" +
|
|
382
516
|
' if (n.children) __resolveNames(n.children);\n' +
|
|
383
517
|
' }\n' +
|
|
384
518
|
'}\n';
|
|
@@ -464,6 +598,10 @@ async function buildMainBundle(routeTree, runtimeDir, codeSplit, mono, hmr, impo
|
|
|
464
598
|
cleanupFn +
|
|
465
599
|
placeFn +
|
|
466
600
|
'globalThis.__components = __components;\n' +
|
|
601
|
+
'globalThis.__runtime_comps = __runtime_comps;\n' +
|
|
602
|
+
'globalThis.__cleanup = __cleanup;\n' +
|
|
603
|
+
'globalThis.__place = __place;\n' +
|
|
604
|
+
allRuntimeImports.map(n => `globalThis.${n} = ${n};\n`).join('') +
|
|
467
605
|
'function __resolveNames(nodes) {\n' +
|
|
468
606
|
' for (const n of nodes) {\n' +
|
|
469
607
|
" if (typeof n.page === 'string') {\n" +
|
|
@@ -476,6 +614,8 @@ async function buildMainBundle(routeTree, runtimeDir, codeSplit, mono, hmr, impo
|
|
|
476
614
|
' }\n' +
|
|
477
615
|
" if (typeof n.error === 'string') n.error = __components[n.error];\n" +
|
|
478
616
|
" if (typeof n.notFound === 'string') n.notFound = __components[n.notFound];\n" +
|
|
617
|
+
" if (typeof n.offline === 'string') n.offline = __components[n.offline];\n" +
|
|
618
|
+
" if (typeof n.network === 'string') n.network = __components[n.network];\n" +
|
|
479
619
|
' if (n.children) __resolveNames(n.children);\n' +
|
|
480
620
|
' }\n' +
|
|
481
621
|
'}\n' +
|
package/dist/dev-server.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { type Server } from 'node:http';
|
|
2
2
|
import type { DevServerOptions } from '@vesk/adapter/src/types';
|
|
3
|
+
export declare function bodyTooLarge(maxBytes: number): Error & {
|
|
4
|
+
status: number;
|
|
5
|
+
};
|
|
3
6
|
export declare function startDevServer(appDir: string, options?: DevServerOptions): Promise<Server | void>;
|
|
4
7
|
//# sourceMappingURL=dev-server.d.ts.map
|
package/dist/dev-server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../src/dev-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,MAAM,EAA6C,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../src/dev-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,MAAM,EAA6C,MAAM,WAAW,CAAC;AAQjG,OAAO,KAAK,EAAa,gBAAgB,EAAY,MAAM,yBAAyB,CAAC;AAWrF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAIzE;AA6DD,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoTvG"}
|
package/dist/dev-server.js
CHANGED
|
@@ -2,18 +2,34 @@ import { readFileSync, existsSync, watch, statSync } from 'node:fs';
|
|
|
2
2
|
import { resolve, extname, dirname } from 'node:path';
|
|
3
3
|
import { createServer } from 'node:http';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import {
|
|
5
|
+
import { stripCodeTypes } from '@vesk/compiler/src/strip-ts';
|
|
6
|
+
import { DEFAULT_MAX_BODY_BYTES } from '@vesk/compiler/src/server-codegen';
|
|
6
7
|
import { build } from '@vesk/adapter/src/index';
|
|
7
8
|
import { createHmrServer } from '@vesk/adapter/src/hmr';
|
|
8
9
|
import { buildRuntimeCode } from '@vesk/adapter/src/client-bundle';
|
|
10
|
+
import { resolveWithin } from '@vesk/adapter/src/paths';
|
|
9
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
|
|
12
|
+
export function bodyTooLarge(maxBytes) {
|
|
13
|
+
const err = new Error(`Request body exceeds limit (${maxBytes} bytes)`);
|
|
14
|
+
err.status = 413;
|
|
15
|
+
return err;
|
|
16
|
+
}
|
|
17
|
+
function errorStatus(e, fallback) {
|
|
18
|
+
const s = e?.status ?? e?.statusCode;
|
|
19
|
+
return typeof s === 'number' && s >= 400 && s < 600 ? s : fallback;
|
|
20
|
+
}
|
|
21
|
+
async function readBody(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
|
|
11
22
|
const chunks = [];
|
|
12
|
-
|
|
23
|
+
let total = 0;
|
|
24
|
+
for await (const chunk of req) {
|
|
25
|
+
total += chunk.byteLength;
|
|
26
|
+
if (total > maxBytes)
|
|
27
|
+
throw bodyTooLarge(maxBytes);
|
|
13
28
|
chunks.push(chunk);
|
|
29
|
+
}
|
|
14
30
|
return Buffer.concat(chunks);
|
|
15
31
|
}
|
|
16
|
-
function makeWebRequest(nodeReq, url) {
|
|
32
|
+
function makeWebRequest(nodeReq, url, maxBodyBytes = DEFAULT_MAX_BODY_BYTES) {
|
|
17
33
|
const parsedUrl = new URL(url, `http://${nodeReq.headers.host || 'localhost'}`);
|
|
18
34
|
const method = nodeReq.method || 'GET';
|
|
19
35
|
let _bodyBuffer = null;
|
|
@@ -21,8 +37,13 @@ function makeWebRequest(nodeReq, url) {
|
|
|
21
37
|
if (_bodyBuffer)
|
|
22
38
|
return _bodyBuffer;
|
|
23
39
|
const chunks = [];
|
|
24
|
-
|
|
40
|
+
let total = 0;
|
|
41
|
+
for await (const chunk of nodeReq) {
|
|
42
|
+
total += chunk.byteLength;
|
|
43
|
+
if (total > maxBodyBytes)
|
|
44
|
+
throw bodyTooLarge(maxBodyBytes);
|
|
25
45
|
chunks.push(Buffer.from(chunk));
|
|
46
|
+
}
|
|
26
47
|
_bodyBuffer = Buffer.concat(chunks);
|
|
27
48
|
return _bodyBuffer;
|
|
28
49
|
}
|
|
@@ -59,6 +80,10 @@ const MIME = {
|
|
|
59
80
|
};
|
|
60
81
|
export async function startDevServer(appDir, options) {
|
|
61
82
|
const port = options?.port || 3000;
|
|
83
|
+
const host = options?.host || '127.0.0.1';
|
|
84
|
+
const maxBodyBytes = options?.maxBodyBytes || DEFAULT_MAX_BODY_BYTES;
|
|
85
|
+
if (!process.env.NODE_ENV)
|
|
86
|
+
process.env.NODE_ENV = 'development';
|
|
62
87
|
const devDir = resolve(appDir, '..', '.vesk', 'dev');
|
|
63
88
|
const publicDir = options?.publicDir || resolve(appDir, '..', 'public');
|
|
64
89
|
let componentMap = new Map();
|
|
@@ -110,7 +135,7 @@ export async function startDevServer(appDir, options) {
|
|
|
110
135
|
if (hmrPath) {
|
|
111
136
|
let hmrContent = readFileSync(hmrPath, 'utf-8');
|
|
112
137
|
if (hmrPath.endsWith('.ts')) {
|
|
113
|
-
hmrContent =
|
|
138
|
+
hmrContent = stripCodeTypes(hmrContent);
|
|
114
139
|
}
|
|
115
140
|
res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
|
116
141
|
res.end(hmrContent);
|
|
@@ -123,9 +148,9 @@ export async function startDevServer(appDir, options) {
|
|
|
123
148
|
return;
|
|
124
149
|
}
|
|
125
150
|
if (url.pathname.startsWith('/_vesk/static/')) {
|
|
126
|
-
const relPath = url.pathname.
|
|
127
|
-
const staticPath = resolve(devDir, 'static', relPath);
|
|
128
|
-
if (!staticPath
|
|
151
|
+
const relPath = url.pathname.slice('/_vesk/static/'.length);
|
|
152
|
+
const staticPath = resolveWithin(resolve(devDir, 'static'), relPath);
|
|
153
|
+
if (!staticPath) {
|
|
129
154
|
res.writeHead(403);
|
|
130
155
|
res.end('Forbidden');
|
|
131
156
|
return;
|
|
@@ -138,17 +163,16 @@ export async function startDevServer(appDir, options) {
|
|
|
138
163
|
}
|
|
139
164
|
}
|
|
140
165
|
if (url.pathname !== '/') {
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
if (sourcePath.startsWith(publicDir) && existsSync(sourcePath) && statSync(sourcePath).isFile()) {
|
|
166
|
+
const sourcePath = url.pathname.length > 1 ? resolveWithin(publicDir, url.pathname.slice(1)) : null;
|
|
167
|
+
if (sourcePath && existsSync(sourcePath) && statSync(sourcePath).isFile()) {
|
|
144
168
|
const ext = extname(sourcePath);
|
|
145
169
|
res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' });
|
|
146
170
|
res.end(readFileSync(sourcePath));
|
|
147
171
|
return;
|
|
148
172
|
}
|
|
149
173
|
const buildPublicDir = resolve(devDir, 'static', 'public');
|
|
150
|
-
const buildPath =
|
|
151
|
-
if (buildPath
|
|
174
|
+
const buildPath = url.pathname.length > 1 ? resolveWithin(buildPublicDir, url.pathname.slice(1)) : null;
|
|
175
|
+
if (buildPath && existsSync(buildPath) && statSync(buildPath).isFile()) {
|
|
152
176
|
const ext = extname(buildPath);
|
|
153
177
|
res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' });
|
|
154
178
|
res.end(readFileSync(buildPath));
|
|
@@ -164,7 +188,7 @@ export async function startDevServer(appDir, options) {
|
|
|
164
188
|
try {
|
|
165
189
|
const mod = await import(`${handlerPath}?t=${ssrVersion}`);
|
|
166
190
|
if (mod.handleAction) {
|
|
167
|
-
const webRequest = makeWebRequest(req, url.href);
|
|
191
|
+
const webRequest = makeWebRequest(req, url.href, maxBodyBytes);
|
|
168
192
|
const response = await mod.handleAction(webRequest, actionId);
|
|
169
193
|
const body = await response.text();
|
|
170
194
|
const headers = Object.fromEntries(response.headers);
|
|
@@ -179,7 +203,8 @@ export async function startDevServer(appDir, options) {
|
|
|
179
203
|
}
|
|
180
204
|
}
|
|
181
205
|
catch (e) {
|
|
182
|
-
|
|
206
|
+
const status = errorStatus(e, 500);
|
|
207
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
183
208
|
res.end(JSON.stringify({ ok: false, error: e instanceof Error ? e.message : String(e) }));
|
|
184
209
|
return;
|
|
185
210
|
}
|
|
@@ -196,7 +221,7 @@ export async function startDevServer(appDir, options) {
|
|
|
196
221
|
if (existsSync(handlerPath)) {
|
|
197
222
|
try {
|
|
198
223
|
const mod = await import(`${handlerPath}?t=${ssrVersion}`);
|
|
199
|
-
const webRequest = makeWebRequest(req, url.href);
|
|
224
|
+
const webRequest = makeWebRequest(req, url.href, maxBodyBytes);
|
|
200
225
|
const response = await mod.handle(webRequest);
|
|
201
226
|
const body = await response.text();
|
|
202
227
|
res.writeHead(response.status, Object.fromEntries(response.headers));
|
|
@@ -218,7 +243,7 @@ export async function startDevServer(appDir, options) {
|
|
|
218
243
|
if (existsSync(handlerPath)) {
|
|
219
244
|
try {
|
|
220
245
|
const mod = await import(`${handlerPath}?t=${ssrVersion}`);
|
|
221
|
-
const webRequest = makeWebRequest(req, url.href);
|
|
246
|
+
const webRequest = makeWebRequest(req, url.href, maxBodyBytes);
|
|
222
247
|
const response = await mod.handle(webRequest);
|
|
223
248
|
const body = await response.text();
|
|
224
249
|
const headers = Object.fromEntries(response.headers);
|
|
@@ -245,7 +270,7 @@ export async function startDevServer(appDir, options) {
|
|
|
245
270
|
if (existsSync(handlerPath)) {
|
|
246
271
|
try {
|
|
247
272
|
const mod = await import(`${handlerPath}?t=${ssrVersion}`);
|
|
248
|
-
const webRequest = makeWebRequest(req, url.href);
|
|
273
|
+
const webRequest = makeWebRequest(req, url.href, maxBodyBytes);
|
|
249
274
|
const response = await mod.handle(webRequest);
|
|
250
275
|
const body = await response.text();
|
|
251
276
|
const headers = Object.fromEntries(response.headers);
|
|
@@ -320,15 +345,15 @@ export async function startDevServer(appDir, options) {
|
|
|
320
345
|
else if (files.length > 0) {
|
|
321
346
|
hmr.handleFileChange(files[0], doBuild, routeTree);
|
|
322
347
|
}
|
|
323
|
-
},
|
|
348
|
+
}, 40);
|
|
324
349
|
});
|
|
325
350
|
}
|
|
326
351
|
catch (e) {
|
|
327
352
|
console.error('vesk dev: file watching unavailable');
|
|
328
353
|
}
|
|
329
354
|
await new Promise(resolve => {
|
|
330
|
-
server.listen(port, () => {
|
|
331
|
-
console.error(`vesk dev server at http://localhost:${port}`);
|
|
355
|
+
server.listen(port, host, () => {
|
|
356
|
+
console.error(`vesk dev server at http://localhost:${port} (listening on ${host})`);
|
|
332
357
|
resolve();
|
|
333
358
|
});
|
|
334
359
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esbuild-fallback.d.ts","sourceRoot":"","sources":["../src/esbuild-fallback.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"esbuild-fallback.d.ts","sourceRoot":"","sources":["../src/esbuild-fallback.ts"],"names":[],"mappings":"AAkCA,wBAAsB,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAiBtD"}
|
package/dist/esbuild-fallback.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
+
// esbuild access with a WASM fallback for machines where the native binary
|
|
2
|
+
// cannot run (older CPUs raise SIGILL, unsupported platforms skip install).
|
|
3
|
+
// Only `build()` is routed here: every synchronous TS-strip call site now uses
|
|
4
|
+
// the compiler's dependency-free `stripCodeTypes` (acorn-based), so no
|
|
5
|
+
// transformSync path exists anymore.
|
|
1
6
|
let _nativeBuild = null;
|
|
2
|
-
let
|
|
7
|
+
let _nativeDead = false;
|
|
3
8
|
let _wasm = null;
|
|
4
9
|
let _wasmReady = null;
|
|
5
10
|
async function loadNative() {
|
|
6
|
-
if (_nativeBuild
|
|
11
|
+
if (_nativeBuild || _nativeDead)
|
|
7
12
|
return;
|
|
8
13
|
try {
|
|
9
14
|
const m = await import('esbuild');
|
|
10
15
|
_nativeBuild = m.build.bind(m);
|
|
11
|
-
_nativeTransform = m.transformSync.bind(m);
|
|
12
16
|
}
|
|
13
17
|
catch {
|
|
14
|
-
// native esbuild not installed
|
|
18
|
+
// native esbuild not installed — wasm handles it
|
|
15
19
|
}
|
|
16
20
|
}
|
|
17
21
|
async function getWasm() {
|
|
@@ -23,7 +27,7 @@ async function getWasm() {
|
|
|
23
27
|
return m;
|
|
24
28
|
}).catch(() => {
|
|
25
29
|
_wasmReady = null;
|
|
26
|
-
throw new Error('esbuild-wasm not available');
|
|
30
|
+
throw new Error('esbuild-wasm not available — install it with: npm install esbuild-wasm');
|
|
27
31
|
});
|
|
28
32
|
}
|
|
29
33
|
return _wasmReady;
|
|
@@ -38,6 +42,7 @@ export async function build(options) {
|
|
|
38
42
|
const msg = e instanceof Error ? e.message : String(e);
|
|
39
43
|
if (msg.includes('SIGILL') || msg.includes('illegal hardware instruction') || msg.includes('cannot execute binary file')) {
|
|
40
44
|
console.warn('vesk: native esbuild failed, falling back to esbuild-wasm:', msg);
|
|
45
|
+
_nativeDead = true;
|
|
41
46
|
}
|
|
42
47
|
else {
|
|
43
48
|
throw e;
|
|
@@ -47,18 +52,3 @@ export async function build(options) {
|
|
|
47
52
|
const wasm = await getWasm();
|
|
48
53
|
return wasm.build(options);
|
|
49
54
|
}
|
|
50
|
-
export function transformSync(code, options) {
|
|
51
|
-
if (_nativeTransform) {
|
|
52
|
-
try {
|
|
53
|
-
return _nativeTransform(code, options);
|
|
54
|
-
}
|
|
55
|
-
catch (e) {
|
|
56
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
57
|
-
if (msg.includes('SIGILL') || msg.includes('illegal hardware instruction') || msg.includes('cannot execute binary file')) {
|
|
58
|
-
throw new Error('esbuild-wasm fallback for transformSync not yet implemented — use native esbuild or convert to async transform');
|
|
59
|
-
}
|
|
60
|
-
throw e;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
throw new Error('esbuild not installed — run `npm install esbuild` or use `vesk build` which falls back to esbuild-wasm for bundling');
|
|
64
|
-
}
|
package/dist/hmr.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAQxC,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,yBAAyB,CAAC;AA6RzE,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC;IAAE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAAC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAmI/L"}
|