@vesk/adapter 0.2.72 → 0.2.76

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.
@@ -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,CAqOtD"}
1
+ {"version":3,"file":"api-function.d.ts","sourceRoot":"","sources":["../src/api-function.ts"],"names":[],"mappings":"AAIA,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,CAkQtD"}
@@ -1,6 +1,7 @@
1
1
  import { readFileSync } from 'node:fs';
2
- import { resolve } from 'node:path';
2
+ import { resolve, dirname } from 'node:path';
3
3
  import { stripCodeTypes } from '@vesk/compiler/src/strip-ts';
4
+ import { resolveImportPath } from '@vesk/compiler/src/module-imports';
4
5
  function apiRouteName(fullPath) {
5
6
  const parts = fullPath.split('/').filter(Boolean);
6
7
  return parts.map(s => s.startsWith(':') ? s.slice(1) || 'param' : s).join('_') || 'index';
@@ -13,6 +14,20 @@ export function generateApiFunction(apiNode, _apiDir, outDir, options) {
13
14
  if (!routeFilePath)
14
15
  throw new Error(`api route ${apiNode.fullPath} has no filePath`);
15
16
  let routeSrc = readFileSync(routeFilePath, 'utf-8');
17
+ // The function file lives in dist/server/api/, so the route's own relative
18
+ // specifiers (`../../../../src/_lib/...`) would resolve against that layout
19
+ // and fail to load. Resolve them against the route file's real directory
20
+ // while it is still known, then point them at build-machine absolute paths:
21
+ // `vesk start` imports the file directly, and platform bundling inlines it.
22
+ const routeDir = dirname(routeFilePath);
23
+ routeSrc = routeSrc.replace(/(from\s+['"]|import\s*\(\s*['"])(\.{1,2}\/[^'"]+)(['"])/g, (match, prefix, spec, suffix) => {
24
+ try {
25
+ return prefix + resolveImportPath(spec, routeDir) + suffix;
26
+ }
27
+ catch {
28
+ return match;
29
+ }
30
+ });
16
31
  routeSrc = routeSrc
17
32
  .replace(/from\s+['"]@vesk\/runtime['"]\s*;?/g, "from '../runtime.js';")
18
33
  .replace(/from\s+['"]@vesk\/runtime\/(\w+)['"]\s*;?/g, () => {
@@ -29,9 +44,16 @@ export function generateApiFunction(apiNode, _apiDir, outDir, options) {
29
44
  const urlParts = apiNode.fullPath.split('/').filter(Boolean);
30
45
  const extracts = [];
31
46
  let partIdx = 0;
32
- for (const p of urlParts) {
33
- if (p.startsWith(':') && p.includes('...')) {
47
+ for (let i = 0; i < urlParts.length; i++) {
48
+ const p = urlParts[i];
49
+ // Catch-all routes carry `isCatchAll` separately from their `:name`
50
+ // path segment, so the shape of the segment alone is not enough to tell
51
+ // `[...rest]` from `[id]`. Consume the whole remaining tail or the param
52
+ // would silently stop after the first segment.
53
+ const isCatchAll = apiNode.isCatchAll && i === urlParts.length - 1;
54
+ if (p.startsWith(':') && (isCatchAll || p.includes('...'))) {
34
55
  extracts.push(`${JSON.stringify(p.slice(1))}: urlParts.slice(${partIdx}).join('/')`);
56
+ partIdx = urlParts.length;
35
57
  }
36
58
  else if (p.startsWith(':')) {
37
59
  extracts.push(`${JSON.stringify(p.slice(1))}: urlParts[${partIdx}]`);
@@ -222,10 +244,15 @@ export function generateApiFunction(apiNode, _apiDir, outDir, options) {
222
244
  middlewareCode ? "import { parseCookies as __parseCookies } from '../runtime.js';" : '',
223
245
  '',
224
246
  middlewareCode || '',
225
- 'export async function handle(request) {',
247
+ // Underscored internal name: route source is inlined above and commonly
248
+ // imports a user helper called `handle`, which would otherwise clash with
249
+ // (and crash) this wrapper. The public export name is preserved.
250
+ 'async function __vesk_api_handle(request) {',
226
251
  handleBody,
227
252
  '}',
228
253
  '',
254
+ 'export { __vesk_api_handle as handle };',
255
+ '',
229
256
  ].filter(Boolean).join('\n');
230
257
  return { funcPath, funcCode, name };
231
258
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client-bundle.d.ts","sourceRoot":"","sources":["../src/client-bundle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client-bundle.d.ts","sourceRoot":"","sources":["../src/client-bundle.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,7 @@ import { VskComponentOwners } from '@vesk/compiler/src/vsk-collision';
11
11
  import { resolveComponentName } from '@vesk/compiler/src/server-codegen';
12
12
  import { collectVskImportPaths, vskImportLines } from '@vesk/compiler/src/vsk-imports';
13
13
  import { inlineMdContentAttrs, guessProjectRoots } from '@vesk/compiler/src/md-inline';
14
- import { resolveImportPath, findTsconfigPath } from '@vesk/compiler/src/module-imports';
14
+ import { resolveImportPath, resolveSsrModule, findTsconfigPath } from '@vesk/compiler/src/module-imports';
15
15
  import { serverOnlyPlugin } from '@vesk/adapter/src/server-only-plugin';
16
16
  import { availableParallelism } from 'node:os';
17
17
  /**
@@ -332,7 +332,10 @@ function chunkHasImports(headLen, mergeAborted) {
332
332
  * The shared-module pass needs them so the entry bundle publishes exactly the
333
333
  * bindings the shared code will read back off `globalThis`.
334
334
  */
335
- function runtimeImportsOf(filePath) {
335
+ function runtimeImportsOf(filePath, seen = new Set()) {
336
+ if (seen.has(filePath))
337
+ return [];
338
+ seen.add(filePath);
336
339
  let text;
337
340
  try {
338
341
  text = readFileSync(filePath, 'utf8');
@@ -354,6 +357,29 @@ function runtimeImportsOf(filePath) {
354
357
  names.push(name);
355
358
  }
356
359
  }
360
+ // Shared modules import each other (student.ts -> ./store), and the shim
361
+ // redirects EVERY @vesk/runtime import in the shared bundle — including the
362
+ // transitive ones. Exporting only the direct sources' bindings makes
363
+ // esbuild reject the build ("No matching export in runtime-shim.js"), the
364
+ // pass falls back to per-chunk inlining, and every chunk gets its own copy
365
+ // of the store: writes land in one Cell, components subscribe to another.
366
+ const relativeImport = /import\s+(?:type\s+)?(?:[^'"]*?\sfrom\s*)?['"](\.[^'"]+)['"]/g;
367
+ for (const match of text.matchAll(relativeImport)) {
368
+ const base = resolve(dirname(filePath), match[1]);
369
+ const candidates = [base, `${base}.ts`, `${base}.tsx`, `${base}.js`, `${base}.jsx`,
370
+ join(base, 'index.ts'), join(base, 'index.tsx'), join(base, 'index.js')];
371
+ for (const candidate of candidates) {
372
+ try {
373
+ if (!existsSync(candidate) || !statSync(candidate).isFile())
374
+ continue;
375
+ }
376
+ catch {
377
+ continue;
378
+ }
379
+ names.push(...runtimeImportsOf(candidate, seen));
380
+ break;
381
+ }
382
+ }
357
383
  return names;
358
384
  }
359
385
  /**
@@ -977,6 +1003,13 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
977
1003
  }
978
1004
  if (codeSplit) {
979
1005
  const chunkEntries = [];
1006
+ // Import tables of EVERY route chunk, even the ones `onlyRoutes` filters
1007
+ // out. Shared-module selection must not depend on the subset being built:
1008
+ // the entry bundle publishes `globalThis.__veskShared` once at startup,
1009
+ // and an on-demand chunk built from a single route would otherwise see each
1010
+ // module "used once" and inline a duplicate copy — the exact module-scope
1011
+ // duplication (auth/session cells) the shared pass exists to prevent.
1012
+ const allRouteImportTables = [];
980
1013
  function walkSplit(nodes, _chain) {
981
1014
  for (const node of nodes) {
982
1015
  // Each chunk must be self-contained: reset the compile dedupe so a
@@ -1019,6 +1052,11 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1019
1052
  }
1020
1053
  if (chunkCode.length > 0) {
1021
1054
  const chunkName = `page-${buildChunkName(appDir, node)}.js`;
1055
+ // Record the import table before the route-level filter below:
1056
+ // shared-module selection (line ~1130) needs the FULL app graph so
1057
+ // that an on-demand `onlyRoutes` build picks the same shared set as
1058
+ // the entry bundle built at startup.
1059
+ allRouteImportTables.push(chunkImports);
1022
1060
  // Route-level filter for on-demand dev builds. `only` is FILE-level:
1023
1061
  // it keeps a recompile cheap but still walks every route, so it
1024
1062
  // cannot make a COLD build lazy.
@@ -1077,18 +1115,55 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1077
1115
  * bundle for every page.
1078
1116
  */
1079
1117
  const sharedUsage = new Map();
1080
- for (const entry of chunkEntries) {
1081
- for (const source of new Set(entry.importsTable.sources())) {
1118
+ const sharedUsageTables = [...allRouteImportTables];
1119
+ if (sharedCode.length > 0)
1120
+ sharedUsageTables.push(sharedImports);
1121
+ for (const table of sharedUsageTables) {
1122
+ for (const source of new Set(table.sources())) {
1082
1123
  sharedUsage.set(source, (sharedUsage.get(source) ?? 0) + 1);
1083
1124
  }
1084
1125
  }
1085
1126
  const isAppLocal = (source) => !source.includes('node_modules') && source.startsWith(resolve(appDir, '..'));
1086
- const sharedSources = [...sharedUsage.entries()]
1087
- .filter(([source, uses]) => uses > 1 && isAppLocal(source))
1088
- .map(([source]) => source);
1127
+ // Resolve bare package specifiers (`vesk-paystack/client`) to their real
1128
+ // files before inspecting them. The import tables key bare specifiers as
1129
+ // written, while the shared bundle needs a path esbuild can load from the
1130
+ // temp directory (the app's node_modules is not an ancestor of it).
1131
+ const sharedFilesFor = new Map();
1132
+ for (const [source, uses] of sharedUsage) {
1133
+ let file = source;
1134
+ if (!source.startsWith('/')) {
1135
+ try {
1136
+ file = resolveSsrModule(source, appDir) ?? source;
1137
+ }
1138
+ catch {
1139
+ // Unresolvable specifier — not hoistable.
1140
+ }
1141
+ }
1142
+ if (file.endsWith('.vsk') || !existsSync(file))
1143
+ continue;
1144
+ // A module that imports the runtime carries reactive state: `track()`
1145
+ // cells created in one bundled copy of @vesk/runtime are invisible to
1146
+ // effects subscribed against the entry bundle's copy. Package modules
1147
+ // (`node_modules`) were excluded historically because third-party code
1148
+ // has "no module state worth sharing" — but a hook library that owns a
1149
+ // tracked cell is exactly that state. Hoist any such module into the
1150
+ // shared bundle, where its runtime imports are redirected to the single
1151
+ // entry instance, even when only one chunk uses it.
1152
+ let needsRuntime = false;
1153
+ try {
1154
+ needsRuntime = runtimeImportsOf(file).length > 0;
1155
+ }
1156
+ catch {
1157
+ // Unparseable/unreadable — leave it to the per-chunk bundler.
1158
+ }
1159
+ if (needsRuntime || (uses > 1 && isAppLocal(source))) {
1160
+ sharedFilesFor.set(source, file);
1161
+ }
1162
+ }
1163
+ const sharedSources = [...new Set(sharedFilesFor.values())];
1089
1164
  if (process.env.VESK_DEBUG_SHARED) {
1090
1165
  console.error('[shared] candidates:', [...sharedUsage.entries()].map(([s, n]) => `${n}x ${s}`).join('\n'));
1091
- console.error('[shared] selected:', sharedSources.join(', '));
1166
+ console.error('[shared] selected:', [...sharedFilesFor.entries()].map(([s, f]) => `${s} -> ${f}`).join('\n'));
1092
1167
  }
1093
1168
  const chunkAlias = {};
1094
1169
  let sharedBundleCode = '';
@@ -1147,8 +1222,10 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1147
1222
  });
1148
1223
  sharedBundleCode = result.outputFiles[0].text;
1149
1224
  // Per-chunk shims: `export const login = __veskShared['…'].login`.
1150
- // esbuild inlines these tiny stubs instead of the real module.
1151
- sharedSources.forEach((source, i) => {
1225
+ // esbuild inlines these tiny stubs instead of the real module. A
1226
+ // package can be imported by its bare specifier in one chunk and by
1227
+ // its absolute path in another; both aliases read the same file entry.
1228
+ [...sharedFilesFor.entries()].forEach(([source, file], i) => {
1152
1229
  const named = new Set();
1153
1230
  let hasDefault = false;
1154
1231
  for (const entry of chunkEntries) {
@@ -1157,7 +1234,7 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1157
1234
  hasDefault = hasDefault || bindings.hasDefault;
1158
1235
  }
1159
1236
  const shim = join(tmpBase, `shim-${i}.js`);
1160
- const shimLines = [`const m = globalThis.__veskShared[${JSON.stringify(source)}];`];
1237
+ const shimLines = [`const m = globalThis.__veskShared[${JSON.stringify(file)}];`];
1161
1238
  if (hasDefault)
1162
1239
  shimLines.push(`export default m;`);
1163
1240
  for (const name of [...named].sort()) {
@@ -1333,7 +1410,10 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1333
1410
  // Concurrency is capped below the core count: esbuild's own service is
1334
1411
  // already multi-process, so oversubscribing every chunk at once trades
1335
1412
  // throughput for memory on the constrained machines this runs on.
1336
- const CHUNK_CONCURRENCY = Math.max(1, Math.min(6, (availableParallelism?.() ?? 4) - 1));
1413
+ const envConcurrency = process.env.VESK_CHUNK_CONCURRENCY ? Number(process.env.VESK_CHUNK_CONCURRENCY) : NaN;
1414
+ const CHUNK_CONCURRENCY = Number.isFinite(envConcurrency) && envConcurrency >= 1
1415
+ ? Math.floor(envConcurrency)
1416
+ : Math.max(1, Math.min(6, (availableParallelism?.() ?? 4) - 1));
1337
1417
  const built = await mapWithConcurrency(effectiveChunkEntries, CHUNK_CONCURRENCY, buildChunkEntry);
1338
1418
  for (const c of built) {
1339
1419
  if (c !== null)
@@ -1353,7 +1433,7 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1353
1433
  const fileName = options?.hmr ? chunkName : hashedName(chunkName, emitted.code);
1354
1434
  node.chunk = `/_vesk/static/${fileName}`;
1355
1435
  }
1356
- else if (options?.skipRouteChunks) {
1436
+ else if (options?.skipRouteChunks && (node.page || node.layout)) {
1357
1437
  // The body is deferred, but the URL must still be published now.
1358
1438
  // The route tree — chunk URLs included — is baked into the ENTRY
1359
1439
  // bundle and the client router fetches `node.chunk` with no server
@@ -1362,6 +1442,19 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1362
1442
  // Dev chunk URLs are unhashed, so they depend only on the route and
1363
1443
  // are identical whether or not the body has been built — which also
1364
1444
  // keeps the entry bundle's cache key stable across lazy builds.
1445
+ //
1446
+ // Layout-only container nodes (e.g. `app/public` with `layout.vsk`
1447
+ // but no `page.vsk`) DO own a chunk: hydration calls the layout
1448
+ // hydrator for every chain node, and without this URL the layout
1449
+ // chunk is never loaded -> `hydLayout is not a function` -> the
1450
+ // default 500 page replaces the SSR output.
1451
+ //
1452
+ // Page-less and layout-less container nodes (a directory with only
1453
+ // a dynamic child, e.g. `app/portal/assessments/[assessmentId]`) get
1454
+ // no chunk, exactly as in a production build. Publishing one here
1455
+ // makes the router eagerly preload a URL that is never built: the
1456
+ // 404 surfaces as a dev overlay ("Failed to load chunk") on
1457
+ // unrelated pages.
1365
1458
  node.chunk = `/_vesk/static/${chunkName}`;
1366
1459
  }
1367
1460
  annotate(node.children || []);
@@ -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;AASjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAO7C,OAAO,KAAK,EAAa,gBAAgB,EAAwB,MAAM,yBAAyB,CAAC;AAgBjG;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,IAAI,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;IAC9C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB;+EAC2E;IAC3E,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,IAAI,WAAW,CAEhD;AAgDD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,mHAAmH;IACnH,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B;;8BAE0B;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,WAAW,CAAC;IAChC,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,wBAAwB,GAAG;IACvE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CAChH,CAUA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAa3C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIrD;AAUD,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAorBvG"}
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;AASjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAO7C,OAAO,KAAK,EAAa,gBAAgB,EAAwB,MAAM,yBAAyB,CAAC;AAgBjG;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,IAAI,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;IAC9C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB;+EAC2E;IAC3E,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,IAAI,WAAW,CAEhD;AAwDD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,mHAAmH;IACnH,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B;;8BAE0B;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,WAAW,CAAC;IAChC,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,wBAAwB,GAAG;IACvE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CAChH,CAUA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAa3C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIrD;AAUD,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAorBvG"}
@@ -38,8 +38,16 @@ const devHmrState = typeof hmrApi.getHmrState === 'function'
38
38
  * Writes a handler Response to the socket, piping a streaming body
39
39
  * chunk-by-chunk (SSE / text streams) instead of buffering everything.
40
40
  */
41
+ /** Preserve every Set-Cookie header — Headers collapses them otherwise. */
42
+ function toNodeHeaders(response) {
43
+ const headers = Object.fromEntries(response.headers);
44
+ const cookies = response.headers.getSetCookie?.() ?? [];
45
+ if (cookies.length > 0)
46
+ headers['set-cookie'] = cookies;
47
+ return headers;
48
+ }
41
49
  async function deliverResponse(res, response) {
42
- res.writeHead(response.status, Object.fromEntries(response.headers));
50
+ res.writeHead(response.status, toNodeHeaders(response));
43
51
  const body = response.body;
44
52
  if (body && typeof body.getReader === 'function') {
45
53
  const reader = body.getReader();
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;AAWxC,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEvG,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,yBAAyB,CAAC;AAIzE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAaD,wBAAgB,WAAW,IAAI;IAC7B,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAQA;AAED,MAAM,WAAW,wBAAwB;IACvC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,wBAA6B,GAClC,eAAe,CAsGjB;AAqTD,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;IACD,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAClE,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,CAAC;IACvH,WAAW,EAAE,MAAM,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;CACnD,CA6KA"}
1
+ {"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAWxC,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEvG,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,yBAAyB,CAAC;AAIzE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAaD,wBAAgB,WAAW,IAAI;IAC7B,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAQA;AAED,MAAM,WAAW,wBAAwB;IACvC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,wBAA6B,GAClC,eAAe,CAsGjB;AAyTD,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;IACD,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAClE,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,CAAC;IACvH,WAAW,EAAE,MAAM,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;CACnD,CA6KA"}
package/dist/hmr.js CHANGED
@@ -446,12 +446,16 @@ function regenerateSsrFunction(routeNode, appDir, outDir, componentMap, options)
446
446
  '',
447
447
  persistedStoreSsrCode(),
448
448
  '',
449
- 'export async function handle(request) {',
449
+ // Underscored internal name (see api-function.ts): page source is inlined
450
+ // above and may declare its own `handle`.
451
+ 'async function __vesk_ssr_handle(request) {',
450
452
  ' const url = new URL(request.url);',
451
453
  " const urlParts = url.pathname.split('/').filter(Boolean);",
452
454
  ` ${paramsCode}`,
453
455
  renderCode,
454
456
  '}',
457
+ '',
458
+ 'export { __vesk_ssr_handle as handle };',
455
459
  ].join('\n');
456
460
  writeFileSync(funcPath, funcCode, 'utf-8');
457
461
  }
@@ -1,5 +1,6 @@
1
- import { type Server } from 'node:http';
1
+ import { type IncomingMessage, type Server } from 'node:http';
2
2
  import type { CompressionConfig, ProdServerOptions } from '@vesk/adapter/src/types';
3
3
  export type { CompressionConfig, ProdServerOptions };
4
+ export declare function resolveRequestUrl(req: IncomingMessage, port: number, trustProxy: boolean): URL;
4
5
  export declare function startProdServer(outDir: string, options?: ProdServerOptions): Promise<Server>;
5
6
  //# sourceMappingURL=prod-server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prod-server.d.ts","sourceRoot":"","sources":["../src/prod-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2D,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAejG,OAAO,KAAK,EAAkB,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAIpG,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAC;AAyHrD,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwoBlG"}
1
+ {"version":3,"file":"prod-server.d.ts","sourceRoot":"","sources":["../src/prod-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,eAAe,EAAuB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAejG,OAAO,KAAK,EAAkB,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAIpG,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAC;AAuIrD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,GAAG,CAY9F;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAooBlG"}
@@ -43,8 +43,16 @@ const DELIVER_BUFFER_LIMIT = 1024 * 1024;
43
43
  * or that declares an open-ended content type, falls back to streaming so the
44
44
  * memory and time-to-first-byte cost of a genuinely large body is unchanged.
45
45
  */
46
- async function deliverResponse(res, response) {
46
+ /** Preserve every Set-Cookie header — Headers collapses them otherwise. */
47
+ function toNodeHeaders(response) {
47
48
  const headers = Object.fromEntries(response.headers);
49
+ const cookies = response.headers.getSetCookie?.() ?? [];
50
+ if (cookies.length > 0)
51
+ headers['set-cookie'] = cookies;
52
+ return headers;
53
+ }
54
+ async function deliverResponse(res, response) {
55
+ const headers = toNodeHeaders(response);
48
56
  const body = response.body;
49
57
  if (!body || typeof body.getReader !== 'function') {
50
58
  res.writeHead(response.status, headers);
@@ -107,6 +115,25 @@ function errorStatus(e, fallback) {
107
115
  const s = e?.status ?? e?.statusCode;
108
116
  return typeof s === 'number' && s >= 400 && s < 600 ? s : fallback;
109
117
  }
118
+ function firstHeaderValue(value) {
119
+ const raw = Array.isArray(value) ? value[0] : value;
120
+ const trimmed = raw?.split(',')[0]?.trim();
121
+ return trimmed || undefined;
122
+ }
123
+ export function resolveRequestUrl(req, port, trustProxy) {
124
+ const proto = req.socket.encrypted
125
+ ? 'https'
126
+ : getClientProtocol({ headers: req.headers }, trustProxy);
127
+ const forwardedHost = trustProxy ? firstHeaderValue(req.headers['x-forwarded-host']) : undefined;
128
+ const host = forwardedHost || req.headers.host || `localhost:${port}`;
129
+ const path = req.url || '/';
130
+ try {
131
+ return new URL(path, `${proto}://${host}`);
132
+ }
133
+ catch {
134
+ return new URL(path, `http://localhost:${port}`);
135
+ }
136
+ }
110
137
  export async function startProdServer(outDir, options) {
111
138
  const port = options?.port || 3000;
112
139
  const host = options?.host || '127.0.0.1';
@@ -276,12 +303,8 @@ export async function startProdServer(outDir, options) {
276
303
  return req.headers['x-real-ip'] || req.socket?.remoteAddress || 'unknown';
277
304
  }
278
305
  const server = createServer(async (req, res) => {
279
- const url = new URL(req.url || '/', `http://localhost:${port}`);
280
- const reqHost = req.headers.host || `localhost:${port}`;
281
- const proto = req.socket.encrypted
282
- ? 'https'
283
- : getClientProtocol({ headers: req.headers }, !!securityConfig?.trustProxy);
284
- globalThis.__vesk_ssr_base_url = `${proto}://${reqHost}`;
306
+ const url = resolveRequestUrl(req, port, !!securityConfig?.trustProxy);
307
+ globalThis.__vesk_ssr_base_url = url.origin;
285
308
  // Liveness probe. Deliberately the FIRST thing in the handler, ahead of the
286
309
  // rate limiter, middleware and the pipeline below:
287
310
  // - a probe that can be answered 429 is indistinguishable from a dead
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-function.d.ts","sourceRoot":"","sources":["../src/ssr-function.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAkB,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7F;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQlG;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEjF;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEpF;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQvG;AAgED;;;2EAG2E;AAC3E,wBAAgB,qBAAqB,IAAI,MAAM,CA4C9C;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,OAAO,CAAC,EAAE,kBAAkB,GAC3B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA6etD"}
1
+ {"version":3,"file":"ssr-function.d.ts","sourceRoot":"","sources":["../src/ssr-function.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAkB,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7F;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQlG;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEjF;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEpF;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQvG;AAgED;;;2EAG2E;AAC3E,wBAAgB,qBAAqB,IAAI,MAAM,CA4C9C;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,OAAO,CAAC,EAAE,kBAAkB,GAC3B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAiftD"}
@@ -528,7 +528,9 @@ export function generateSsrFunction(routeNode, appDir, outDir, componentMap, opt
528
528
  '',
529
529
  ].join('\n');
530
530
  const actionCode = [
531
- 'export async function handleAction(request, id) {',
531
+ // Underscored internal name (see api-function.ts): page source is inlined
532
+ // into this module and may declare/import `handleAction` itself.
533
+ 'async function __vesk_handleAction(request, id) {',
532
534
  ' // CSRF defense: cross-site browser POSTs always carry an Origin header.',
533
535
  ' try {',
534
536
  ' assertSameOrigin(request);',
@@ -606,6 +608,8 @@ export function generateSsrFunction(routeNode, appDir, outDir, componentMap, opt
606
608
  ' }',
607
609
  '}',
608
610
  '',
611
+ 'export { __vesk_handleAction as handleAction };',
612
+ '',
609
613
  ].join('\n');
610
614
  const funcCode = [
611
615
  "import { renderFullPage, renderPageStream, renderPage, hydratePrecompile, parseCookies, getAction, validateActionInput, issuesToFieldMap, storeDataScriptGlobal, withSsrStore, assertSameOrigin, VeskRequest, installPersistedStoreServerHooks, persistedStoreCookieName, getPersistedStoreKeys, resetPersistedStoreKeys, buildStorePrePaintPayload, storeInitScriptGlobal, storeInitScriptTag } from '../runtime.js';",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/adapter",
3
- "version": "0.2.72",
3
+ "version": "0.2.76",
4
4
  "type": "module",
5
5
  "description": "Vesk adapter — builds deployable .vesk/ output for Deno and Node serverless",
6
6
  "main": "./dist/index.js",
@@ -23,9 +23,9 @@
23
23
  "sharp": "^0.35.3"
24
24
  },
25
25
  "dependencies": {
26
- "@vesk/compiler": "^0.2.72",
27
- "@vesk/runtime": "^0.2.72",
28
- "@vesk/types": "^0.2.72"
26
+ "@vesk/compiler": "^0.2.76",
27
+ "@vesk/runtime": "^0.2.76",
28
+ "@vesk/types": "^0.2.76"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/ws": "^8.18.1",