@vesk/vesk-cli 0.2.38 → 0.2.39

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.
Files changed (2) hide show
  1. package/dist/cli.js +485 -135
  2. package/package.json +5 -5
package/dist/cli.js CHANGED
@@ -379,6 +379,14 @@ var init_ir = __esm({
379
379
  ComponentIR = class {
380
380
  name;
381
381
  paramNames;
382
+ /**
383
+ * The declared first parameter when it is a plain identifier
384
+ * (`component Card(p)`), i.e. the whole props object bound to another name.
385
+ * `null` for a destructuring pattern (`component Card({ title })`) or when
386
+ * there is no parameter. `paramNames` alone cannot express this: `p` and
387
+ * `{ p }` both flatten to `['p']` but need opposite bindings.
388
+ */
389
+ propsAlias;
382
390
  propsType;
383
391
  isClient;
384
392
  isAsync;
@@ -391,6 +399,7 @@ var init_ir = __esm({
391
399
  constructor(name, paramNames, body, opts = {}) {
392
400
  this.name = name;
393
401
  this.paramNames = paramNames;
402
+ this.propsAlias = opts.propsAlias ?? null;
394
403
  this.body = body;
395
404
  this.isClient = opts.isClient ?? false;
396
405
  this.isAsync = opts.isAsync ?? false;
@@ -1540,6 +1549,26 @@ function setActiveComponent(value) {
1540
1549
  function createContext(value) {
1541
1550
  return new Context(value);
1542
1551
  }
1552
+ function createLocals() {
1553
+ const store = {};
1554
+ return {
1555
+ set(key, value) {
1556
+ store[key] = value;
1557
+ },
1558
+ get(key) {
1559
+ return store[key];
1560
+ },
1561
+ has(key) {
1562
+ return Object.prototype.hasOwnProperty.call(store, key);
1563
+ },
1564
+ delete(key) {
1565
+ delete store[key];
1566
+ },
1567
+ all() {
1568
+ return { ...store };
1569
+ }
1570
+ };
1571
+ }
1543
1572
  var ctx, Context;
1544
1573
  var init_context = __esm({
1545
1574
  "../runtime/src/context.ts"() {
@@ -1547,9 +1576,17 @@ var init_context = __esm({
1547
1576
  ctx = { current: null };
1548
1577
  Context = class {
1549
1578
  _v;
1579
+ /** Identity of this context instance — a Map key in the provider chain. */
1580
+ id;
1550
1581
  constructor(value) {
1551
1582
  this._v = value;
1583
+ this.id = Symbol("vesk-context");
1552
1584
  }
1585
+ /**
1586
+ * Reads the nearest value set by an ancestor component, else the default.
1587
+ * Typed as `T`, so a context created with a type carries that type all the
1588
+ * way to every reader.
1589
+ */
1553
1590
  get() {
1554
1591
  let current2 = getActiveComponent();
1555
1592
  while (current2) {
@@ -1558,6 +1595,7 @@ var init_context = __esm({
1558
1595
  }
1559
1596
  return this._v;
1560
1597
  }
1598
+ /** Sets the value for this component and its descendants. Typed as `T`. */
1561
1599
  set(value) {
1562
1600
  const component = getActiveComponent();
1563
1601
  if (component === null) throw new Error("No active component found, cannot set context");
@@ -1999,6 +2037,32 @@ function revalidate(handle2) {
1999
2037
  handle2.controller?.abort();
2000
2038
  startRequest(handle2, true);
2001
2039
  }
2040
+ function mutate(key, data2) {
2041
+ if (isServer()) return;
2042
+ const registry = getRegistry().get(key);
2043
+ const prev = getInflight().get(key);
2044
+ if (data2 !== void 0) {
2045
+ getClientCache().set(key, { data: data2, fetchedAt: Date.now() });
2046
+ if (prev) {
2047
+ getInflight().delete(key);
2048
+ if (registry) registry.forEach((h) => h.controller?.abort());
2049
+ }
2050
+ if (registry) {
2051
+ registry.forEach((h) => {
2052
+ if (h.into) setInto(h.into, data2);
2053
+ set(h.state, { loading: false, error: null, data: data2 });
2054
+ });
2055
+ }
2056
+ return;
2057
+ }
2058
+ if (registry && registry.size > 0) {
2059
+ if (prev) {
2060
+ getInflight().delete(key);
2061
+ registry.forEach((h) => h.controller?.abort());
2062
+ }
2063
+ registry.forEach((h) => startRequest(h, true));
2064
+ }
2065
+ }
2002
2066
  function createResourceAccessor(handle2) {
2003
2067
  const state = handle2.state;
2004
2068
  const accessor = {};
@@ -5342,11 +5406,18 @@ function createRouter(routes, options = {}) {
5342
5406
  if (decision === false) return;
5343
5407
  if (typeof decision === "string" && decision !== url.pathname && (self._guardDepth ?? 0) < 5) {
5344
5408
  self._guardDepth = (self._guardDepth ?? 0) + 1;
5345
- try {
5346
- self.navigate(decision, { replace: true });
5347
- } finally {
5348
- self._guardDepth = 0;
5409
+ const hop = self.navigate(decision, { replace: true });
5410
+ if (hop && typeof hop.then === "function") {
5411
+ return hop.then(
5412
+ () => {
5413
+ self._guardDepth = 0;
5414
+ },
5415
+ () => {
5416
+ self._guardDepth = 0;
5417
+ }
5418
+ );
5349
5419
  }
5420
+ self._guardDepth = 0;
5350
5421
  return;
5351
5422
  }
5352
5423
  proceedNav();
@@ -5767,11 +5838,18 @@ function createFileRouter(routeTree, options = {}) {
5767
5838
  if (decision === false) return;
5768
5839
  if (typeof decision === "string" && decision !== url.pathname && (router._guardDepth || 0) < 5) {
5769
5840
  router._guardDepth = (router._guardDepth || 0) + 1;
5770
- try {
5771
- router.navigate(decision, { replace: true });
5772
- } finally {
5773
- router._guardDepth = 0;
5841
+ const hop = router.navigate(decision, { replace: true });
5842
+ if (hop && typeof hop.then === "function") {
5843
+ return hop.then(
5844
+ () => {
5845
+ router._guardDepth = 0;
5846
+ },
5847
+ () => {
5848
+ router._guardDepth = 0;
5849
+ }
5850
+ );
5774
5851
  }
5852
+ router._guardDepth = 0;
5775
5853
  return;
5776
5854
  }
5777
5855
  afterGuards();
@@ -17629,9 +17707,24 @@ function blankComments(text) {
17629
17707
  continue;
17630
17708
  }
17631
17709
  if (ch === "/" && text[i + 1] === "*") {
17632
- let j = i;
17633
- while (j < n && !(text[j] === "*" && text[j + 1] === "/")) j++;
17634
- const end = Math.min(j + 2, n);
17710
+ if (!lineStart) {
17711
+ i += 2;
17712
+ continue;
17713
+ }
17714
+ let j = i + 2;
17715
+ let end = -1;
17716
+ while (j < n) {
17717
+ if (text[j] === "*" && text[j + 1] === "/") {
17718
+ end = j + 2;
17719
+ break;
17720
+ }
17721
+ j++;
17722
+ }
17723
+ if (end === -1) {
17724
+ lineStart = false;
17725
+ i += 2;
17726
+ continue;
17727
+ }
17635
17728
  for (let k = i; k < end; k++) if (text[k] !== "\n") chars[k] = " ";
17636
17729
  lineStart = false;
17637
17730
  i = end;
@@ -20628,6 +20721,8 @@ function generateIR(ast, source, filename) {
20628
20721
  }
20629
20722
  const name = inner.id.name;
20630
20723
  const paramNames = getParamNames(inner.params, source);
20724
+ const firstParam = inner.params?.[0];
20725
+ const propsAlias = firstParam && firstParam.type === "Identifier" ? firstParam.name : null;
20631
20726
  const propsType = getPropsType(inner.params, source);
20632
20727
  const bodyStmts = inner.body.body;
20633
20728
  const isClientComp = !!inner.client;
@@ -20637,7 +20732,7 @@ function generateIR(ast, source, filename) {
20637
20732
  const raw2 = processStatementModeBody(source, bodyStmts, file);
20638
20733
  const { body, css } = extractStyle(raw2);
20639
20734
  validateBlocks(name, isClientComp, body, file, source);
20640
- const comp = new ComponentIR(name, paramNames, body, { mode: "statement", exported, defaultExport, isClient: inner.client, isAsync: inner.async, ssrAwait: componentUsesFetch(body), propsType });
20735
+ const comp = new ComponentIR(name, paramNames, body, { mode: "statement", exported, defaultExport, isClient: inner.client, isAsync: inner.async, ssrAwait: componentUsesFetch(body), propsType, propsAlias });
20641
20736
  comp.style = css;
20642
20737
  components.push(comp);
20643
20738
  __slotProps = prevSlotProps;
@@ -21634,11 +21729,14 @@ function tryParseImport(code) {
21634
21729
  return null;
21635
21730
  }
21636
21731
  }
21637
- function buildParamInit(paramNames) {
21732
+ function buildParamInit(paramNames, propsAlias = null) {
21733
+ if (propsAlias) {
21734
+ return propsAlias === "props" ? "" : `const ${propsAlias} = props;`;
21735
+ }
21736
+ if (paramNames.length === 0) return "";
21638
21737
  if (paramNames.length === 1 && paramNames[0] === "props") {
21639
21738
  return "";
21640
21739
  }
21641
- if (paramNames.length === 0) return "";
21642
21740
  return `const { ${paramNames.join(", ")} } = props;`;
21643
21741
  }
21644
21742
  function resolveComponentName(source) {
@@ -22056,6 +22154,12 @@ function webhook(options) {
22056
22154
  });
22057
22155
  }
22058
22156
  async function webhookHandler(request) {
22157
+ if (request.method !== "POST") {
22158
+ return new Response(JSON.stringify({ error: "Method not allowed" }), {
22159
+ status: 405,
22160
+ headers: { "Content-Type": "application/json", Allow: "POST" }
22161
+ });
22162
+ }
22059
22163
  const signature = request.headers.get(headerName);
22060
22164
  if (!signature) {
22061
22165
  return new Response(JSON.stringify({ error: "Missing signature header" }), {
@@ -22224,7 +22328,7 @@ async function readSignedCookie2(name, cookieString, host) {
22224
22328
  if (impl.readSignedCookie) return impl.readSignedCookie(name, cookieString, host);
22225
22329
  throw new Error("readSignedCookie: @vesk/compiler not available");
22226
22330
  }
22227
- var ServerRequest, ServerResponse, VeskRequest, _VeskResponse, VeskResponse, _signImpl;
22331
+ var ServerRequest, ServerResponse, DEFAULT_MAX_BODY_BYTES2, PayloadTooLargeError, VeskRequest, _VeskResponse, VeskResponse, _signImpl;
22228
22332
  var init_request = __esm({
22229
22333
  "../runtime/src/request.ts"() {
22230
22334
  "use strict";
@@ -22292,6 +22396,14 @@ var init_request = __esm({
22292
22396
  });
22293
22397
  }
22294
22398
  };
22399
+ DEFAULT_MAX_BODY_BYTES2 = 1024 * 1024;
22400
+ PayloadTooLargeError = class extends Error {
22401
+ status = 413;
22402
+ constructor(message = `Request body exceeds limit (${DEFAULT_MAX_BODY_BYTES2} bytes)`) {
22403
+ super(message);
22404
+ this.name = "PayloadTooLargeError";
22405
+ }
22406
+ };
22295
22407
  VeskRequest = class _VeskRequest extends ServerRequest {
22296
22408
  _query;
22297
22409
  _ip;
@@ -22500,14 +22612,35 @@ var init_request = __esm({
22500
22612
  this._flushSecurityHeaders();
22501
22613
  return this;
22502
22614
  }
22615
+ /**
22616
+ * Rejects a body that declares itself over the cap before any bytes are
22617
+ * read. The dev server additionally caps the Node stream itself; this is
22618
+ * the portable guard for platform/serverless builds, so a route that does
22619
+ * `await req.json()` on an oversized payload fails with 413 instead of an
22620
+ * opaque 500.
22621
+ */
22622
+ _assertBodyWithinLimit() {
22623
+ const limit = DEFAULT_MAX_BODY_BYTES2;
22624
+ const declared = Number(this.headers?.get("content-length") || "0");
22625
+ if (Number.isFinite(declared) && declared > limit) {
22626
+ throw new PayloadTooLargeError(`Request body exceeds limit (${limit} bytes)`);
22627
+ }
22628
+ }
22503
22629
  async text() {
22504
22630
  this._flushSecurityHeaders();
22631
+ this._assertBodyWithinLimit();
22505
22632
  return super.text();
22506
22633
  }
22507
22634
  async json() {
22508
22635
  this._flushSecurityHeaders();
22636
+ this._assertBodyWithinLimit();
22509
22637
  return super.json();
22510
22638
  }
22639
+ async formData() {
22640
+ this._flushSecurityHeaders();
22641
+ this._assertBodyWithinLimit();
22642
+ return super.formData();
22643
+ }
22511
22644
  setCookie(name, value, opts = {}) {
22512
22645
  const parts = [`${name}=${encodeURIComponent(value)}`];
22513
22646
  if (opts.httpOnly !== false) parts.push("HttpOnly");
@@ -25697,11 +25830,13 @@ __export(index_server_exports, {
25697
25830
  ArticleSchema: () => ArticleSchema,
25698
25831
  BreadcrumbListSchema: () => BreadcrumbListSchema,
25699
25832
  Context: () => Context,
25833
+ DEFAULT_MAX_BODY_BYTES: () => DEFAULT_MAX_BODY_BYTES2,
25700
25834
  Experiment: () => Experiment,
25701
25835
  FAQPageSchema: () => FAQPageSchema,
25702
25836
  Field: () => Field,
25703
25837
  For: () => For,
25704
25838
  Form: () => Form,
25839
+ HttpError: () => HttpError,
25705
25840
  Image: () => Image,
25706
25841
  JsonLd: () => JsonLd,
25707
25842
  Link: () => Link,
@@ -25713,6 +25848,7 @@ __export(index_server_exports, {
25713
25848
  NotFoundError: () => NotFoundError,
25714
25849
  OrganizationSchema: () => OrganizationSchema,
25715
25850
  Outlet: () => Outlet,
25851
+ PayloadTooLargeError: () => PayloadTooLargeError,
25716
25852
  Portal: () => Portal,
25717
25853
  ProductSchema: () => ProductSchema,
25718
25854
  Redirect: () => Redirect,
@@ -25720,6 +25856,7 @@ __export(index_server_exports, {
25720
25856
  ServerResponse: () => ServerResponse,
25721
25857
  Show: () => Show,
25722
25858
  Switch: () => Switch,
25859
+ TimeoutError: () => TimeoutError,
25723
25860
  VeskRequest: () => VeskRequest,
25724
25861
  VeskResponse: () => VeskResponse,
25725
25862
  VideoSchema: () => VideoSchema,
@@ -25740,6 +25877,7 @@ __export(index_server_exports, {
25740
25877
  cors: () => cors,
25741
25878
  createContext: () => createContext,
25742
25879
  createFileRouter: () => createFileRouter,
25880
+ createLocals: () => createLocals,
25743
25881
  createResource: () => createResource,
25744
25882
  createRouter: () => createRouter,
25745
25883
  create_component_ctx: () => create_component_ctx,
@@ -25771,6 +25909,7 @@ __export(index_server_exports, {
25771
25909
  locals: () => locals,
25772
25910
  maxLength: () => maxLength,
25773
25911
  minLength: () => minLength,
25912
+ mutate: () => mutate,
25774
25913
  notFound: () => notFound,
25775
25914
  on_destroy: () => on_destroy,
25776
25915
  pageIsr: () => pageIsr,
@@ -29896,7 +30035,7 @@ function generateComponent(comp, importedNames = /* @__PURE__ */ new Set(), hydr
29896
30035
  ctx2.push(indent2(`const $root = document.createDocumentFragment();`));
29897
30036
  }
29898
30037
  ctx2.push(indent2(`let __pendingChild = null;`));
29899
- const paramInit = buildParamInit2(comp.paramNames);
30038
+ const paramInit = buildParamInit2(comp.paramNames, comp.propsAlias);
29900
30039
  if (paramInit) ctx2.push(indent2(paramInit));
29901
30040
  for (const node of comp.body) {
29902
30041
  if (node instanceof TrackDecl) {
@@ -29945,7 +30084,8 @@ function generateComponent(comp, importedNames = /* @__PURE__ */ new Set(), hydr
29945
30084
  ctx2.push(`}`);
29946
30085
  return ctx2.getCode();
29947
30086
  }
29948
- function buildParamInit2(paramNames) {
30087
+ function buildParamInit2(paramNames, propsAlias = null) {
30088
+ if (propsAlias) return propsAlias === "props" ? "" : `const ${propsAlias} = props;`;
29949
30089
  if (paramNames.length === 1 && paramNames[0] === "props") return "";
29950
30090
  if (paramNames.length === 0) return "";
29951
30091
  return `const { ${paramNames.join(", ")} } = props;`;
@@ -30830,7 +30970,7 @@ function buildComponentEntries(irRoot) {
30830
30970
  const entries = [];
30831
30971
  for (const comp of irRoot.components) {
30832
30972
  const bodyCode = generateFunctionBody(comp, importedNames);
30833
- const paramInit = buildParamInit(comp.paramNames);
30973
+ const paramInit = buildParamInit(comp.paramNames, comp.propsAlias);
30834
30974
  const diag = process.env.VESK_SSR_LOG ? `console.error('[SSR-CALL]', ${JSON.stringify(comp.name)}, props ? JSON.stringify(props) : String(props));
30835
30975
  ` : "";
30836
30976
  const code = `${scopeDecl}${paramInit}${diag}
@@ -36894,6 +37034,54 @@ function isTrackDeclStatement(stmt) {
36894
37034
  (d) => d && d.id && d.id.type === "ArrayPattern" && d.id.lazy === true && d.init
36895
37035
  );
36896
37036
  }
37037
+ function stripCallTypeArgs(text) {
37038
+ let i = 0;
37039
+ while (i < text.length && /\s/.test(text[i])) i++;
37040
+ const nameStart = i;
37041
+ while (i < text.length && /[A-Za-z0-9_$]/.test(text[i])) i++;
37042
+ if (i === nameStart) return text;
37043
+ while (i < text.length && (text[i] === "." || text[i] === "?" || text[i] === "[")) {
37044
+ const ch = text[i];
37045
+ if (ch === "[") {
37046
+ const close = text.indexOf("]", i);
37047
+ if (close === -1) return text;
37048
+ i = close + 1;
37049
+ } else {
37050
+ i++;
37051
+ while (i < text.length && /[A-Za-z0-9_$]/.test(text[i])) i++;
37052
+ }
37053
+ }
37054
+ if (text[i] !== "<") return text;
37055
+ let depth = 0;
37056
+ let quote = "";
37057
+ for (let j = i; j < text.length; j++) {
37058
+ const c = text[j];
37059
+ if (quote) {
37060
+ if (c === "\\") {
37061
+ j++;
37062
+ continue;
37063
+ }
37064
+ if (c === quote) quote = "";
37065
+ continue;
37066
+ }
37067
+ if (c === '"' || c === "'" || c === "`") {
37068
+ quote = c;
37069
+ continue;
37070
+ }
37071
+ if (c === "<") depth++;
37072
+ else if (c === ">") {
37073
+ depth--;
37074
+ if (depth === 0) {
37075
+ const after = text.slice(j + 1);
37076
+ if (/^\s*\(/.test(after)) return text.slice(0, i) + after;
37077
+ return text;
37078
+ }
37079
+ } else if (c === ";" || c === "\n") {
37080
+ return text;
37081
+ }
37082
+ }
37083
+ return text;
37084
+ }
36897
37085
  function emitTrackDeclStatement(g2, source, stmt, indent4, isLast, opts) {
36898
37086
  const fullText = stripTrailingSemicolons(source.slice(stmt.start, stmt.end).trimEnd());
36899
37087
  const text = fullText + (!fullText.endsWith("}") && !isLast ? ";" : "");
@@ -36931,8 +37119,15 @@ function emitTrackDeclStatement(g2, source, stmt, indent4, isLast, opts) {
36931
37119
  if (opts.typedCells) {
36932
37120
  const cellName = g2.cellCount === 0 ? "__cell" : `__cell${g2.cellCount}`;
36933
37121
  g2.cellCount++;
36934
- g2.add(`const ${cellName} = `);
36935
- g2.add(initText, decl.init.start, decl.init.end);
37122
+ if (annotation) {
37123
+ g2.add(`const ${cellName}: Tracked<`);
37124
+ g2.add(annotation, annStart, annEnd);
37125
+ g2.add("> = ");
37126
+ } else {
37127
+ g2.add(`const ${cellName} = `);
37128
+ }
37129
+ const cellInit = annotation ? stripCallTypeArgs(initText) : initText;
37130
+ g2.add(cellInit, decl.init.start, decl.init.end);
36936
37131
  g2.add(";");
36937
37132
  g2.add(" let ");
36938
37133
  g2.add(first, firstRange[0], firstRange[1], REACTIVE_DATA);
@@ -36954,16 +37149,18 @@ function emitTrackDeclStatement(g2, source, stmt, indent4, isLast, opts) {
36954
37149
  g2.add(": ");
36955
37150
  g2.add(annotation, annStart, annEnd);
36956
37151
  g2.add(" = (");
36957
- g2.add(initText, decl.init.start, decl.init.end);
37152
+ g2.add(stripCallTypeArgs(initText), decl.init.start, decl.init.end);
36958
37153
  g2.add(" as unknown as ");
36959
37154
  g2.add(annotation, annStart, annEnd);
36960
37155
  g2.add(");");
36961
37156
  } else {
36962
- g2.add("let ");
36963
- g2.add(first, firstRange[0], firstRange[1], REACTIVE_DATA);
36964
- g2.add(": any = ");
37157
+ const cellName = g2.cellCount === 0 ? "__cell" : `__cell${g2.cellCount}`;
37158
+ g2.cellCount++;
37159
+ g2.add(`const ${cellName} = `);
36965
37160
  g2.add(initText, decl.init.start, decl.init.end);
36966
- g2.add(";");
37161
+ g2.add("; let ");
37162
+ g2.add(first, firstRange[0], firstRange[1], REACTIVE_DATA);
37163
+ g2.add(` = ${cellName}.get();`);
36967
37164
  }
36968
37165
  for (let n = 1; n < names.length; n++) {
36969
37166
  g2.add(" let ");
@@ -37225,10 +37422,18 @@ function emitIf(g2, source, stmt, indent4, opts) {
37225
37422
  emitBlock(g2, source, stmt.alternate, indent4, opts);
37226
37423
  }
37227
37424
  }
37425
+ function emitLoopVariable(g2, source, left) {
37426
+ if (left && left.type === "Identifier") {
37427
+ g2.addRaw("const ");
37428
+ g2.add(source.slice(left.start, left.end), left.start, left.end);
37429
+ return;
37430
+ }
37431
+ g2.add(source.slice(left.start, left.end), left.start, left.end);
37432
+ }
37228
37433
  function emitForOf(g2, source, stmt, indent4, opts) {
37229
37434
  g2.add(indent4);
37230
37435
  g2.addRaw("for (");
37231
- g2.add(source.slice(stmt.left.start, stmt.left.end), stmt.left.start, stmt.left.end);
37436
+ emitLoopVariable(g2, source, stmt.left);
37232
37437
  g2.addRaw(" of ");
37233
37438
  g2.add(source.slice(stmt.right.start, stmt.right.end), stmt.right.start, stmt.right.end);
37234
37439
  g2.addRaw(") ");
@@ -37237,7 +37442,7 @@ function emitForOf(g2, source, stmt, indent4, opts) {
37237
37442
  function emitForIn(g2, source, stmt, indent4, opts) {
37238
37443
  g2.add(indent4);
37239
37444
  g2.addRaw("for (");
37240
- g2.add(source.slice(stmt.left.start, stmt.left.end), stmt.left.start, stmt.left.end);
37445
+ emitLoopVariable(g2, source, stmt.left);
37241
37446
  g2.addRaw(" in ");
37242
37447
  g2.add(source.slice(stmt.right.start, stmt.right.end), stmt.right.start, stmt.right.end);
37243
37448
  g2.addRaw(") ");
@@ -37705,6 +37910,9 @@ function compileVskCodegen(source, opts = {}) {
37705
37910
  g2.prepend("declare const Head: (props: { children?: Component }) => Component;\n");
37706
37911
  code = g2.code;
37707
37912
  }
37913
+ if (!isModuleAst(ast)) {
37914
+ code += "\nexport {};\n";
37915
+ }
37708
37916
  return { code, mappings: g2.mappings, styleRegions: g2.styleRegions, errors: [] };
37709
37917
  }
37710
37918
  function vskToTsx(source) {
@@ -37972,7 +38180,8 @@ function createTypecheckHost(projectRoot, appDir, vskFiles) {
37972
38180
  resolveModuleNameLiterals(moduleLiterals, containingFile, redirectedReference, options) {
37973
38181
  return moduleLiterals.map(({ text }) => {
37974
38182
  if (text.endsWith(".vsk")) {
37975
- const abs = normalize(join25(dirname27(containingFile), text));
38183
+ const aliased = text.startsWith(".") ? null : resolveAliasModule(text, dirname27(containingFile));
38184
+ const abs = normalize(aliased ?? join25(dirname27(containingFile), text));
37976
38185
  return {
37977
38186
  resolvedModule: {
37978
38187
  resolvedFileName: abs + ".d.ts",
@@ -38055,6 +38264,8 @@ function typecheckProject(projectRoot, opts = {}) {
38055
38264
  esModuleInterop: true,
38056
38265
  allowSyntheticDefaultImports: true,
38057
38266
  allowImportingTsExtensions: true,
38267
+ // Required so the virtual `.vsk` files are actually pulled into the
38268
+ // program (without it tsc silently excludes them and reports nothing).
38058
38269
  allowNonTsExtensions: true,
38059
38270
  resolveJsonModule: true,
38060
38271
  lib: ["lib.es2022.d.ts", "lib.dom.d.ts", "lib.dom.iterable.d.ts"],
@@ -38102,6 +38313,7 @@ var init_typecheck = __esm({
38102
38313
  "use strict";
38103
38314
  init_parser();
38104
38315
  init_vsk_tsx();
38316
+ init_module_imports();
38105
38317
  SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".vesk", ".git", "dist", "tarballs", ".next"]);
38106
38318
  SKIP_FILES = /* @__PURE__ */ new Set(["__vesk_ambient.d.ts", "__vesk_runtime_override.d.ts"]);
38107
38319
  AMBIENT = `
@@ -38476,6 +38688,16 @@ declare namespace JSX {
38476
38688
  }
38477
38689
  declare type Component = string | number | boolean | null | undefined | Component[];
38478
38690
  declare const Head: (props: { children?: Component }) => Component;
38691
+ // Server-side .vsk code reads process.env (NODE_ENV, feature flags, secrets).
38692
+ // @types/node is not installed for browser targets, so declare the slice the
38693
+ // framework documents rather than failing every project that branches on env.
38694
+ declare const process: {
38695
+ env: Record<string, string | undefined>;
38696
+ argv: string[];
38697
+ platform: string;
38698
+ cwd(): string;
38699
+ exit(code?: number): never;
38700
+ };
38479
38701
  interface Tracked<T> {
38480
38702
  get(): T;
38481
38703
  set(value: T): void;
@@ -38493,7 +38715,26 @@ declare function peek<T>(fn: () => T): T;
38493
38715
  declare function tick(): Promise<void>;
38494
38716
  declare function flushSync(fn: () => void): void;
38495
38717
  declare function on_destroy(fn: () => void): void;
38496
- declare function createContext<T>(defaultValue?: T): { id: symbol; defaultValue: T | undefined };
38718
+ /**
38719
+ * A context created by createContext. The value type flows from the default
38720
+ * (or an explicit type argument) through set() and every get().
38721
+ */
38722
+ interface VeskContext<T> {
38723
+ readonly id: symbol;
38724
+ get(): T;
38725
+ set(value: T): void;
38726
+ }
38727
+ declare function createContext<const T>(defaultValue: T): VeskContext<T>;
38728
+ // Typed request-scoped store (middleware locals, event payloads). Declare the
38729
+ // shape once and every set/get is checked against it.
38730
+ interface VeskLocals<T extends object> {
38731
+ set<K extends keyof T>(key: K, value: T[K]): void;
38732
+ get<K extends keyof T>(key: K): T[K];
38733
+ has<K extends keyof T>(key: K): boolean;
38734
+ delete<K extends keyof T>(key: K): void;
38735
+ all(): T;
38736
+ }
38737
+ declare function createLocals<T extends object = Record<string, unknown>>(): VeskLocals<T>;
38497
38738
  // useFetch returns a THENABLE resource, not the data itself. In async
38498
38739
  // components you must \`await\` it before reading/iterating the payload;
38499
38740
  // passing \`into: <tracked cell>\` writes the payload into the cell and makes
@@ -38612,6 +38853,8 @@ declare module '@vesk/runtime' {
38612
38853
  export function track<T>(initialValue: T): Tracked<T>;
38613
38854
  export function track<T>(fn: () => T): Derived<T>;
38614
38855
  export function derived<T>(fn: () => T): Derived<T>;
38856
+ export function createContext<const T>(defaultValue: T): VeskContext<T>;
38857
+ export function createLocals<T extends object = Record<string, unknown>>(): VeskLocals<T>;
38615
38858
  }
38616
38859
  `;
38617
38860
  }
@@ -38672,10 +38915,14 @@ SECURITY_PRESETS["default"] = { ...SECURITY_PRESETS["strict"] };
38672
38915
  function preset(name, overrides = {}) {
38673
38916
  const presets = {
38674
38917
  production: { ...SECURITY_PRESETS["strict"] },
38918
+ // `strict` is the real name of the production preset; both resolve to it.
38919
+ strict: { ...SECURITY_PRESETS["strict"] },
38675
38920
  development: {
38676
38921
  ...SECURITY_PRESETS["strict"],
38677
38922
  contentSecurityPolicy: false
38678
- }
38923
+ },
38924
+ // autoEscape only, SAMEORIGIN framing, no CSRF/HSTS/CSP.
38925
+ minimal: { ...SECURITY_PRESETS["minimal"] }
38679
38926
  };
38680
38927
  if (!presets[name]) {
38681
38928
  throw VeskError.configError(`Unknown security preset: "${name}".`, Object.keys(presets));
@@ -38693,8 +38940,13 @@ function definePlugin(plugin) {
38693
38940
  }
38694
38941
  function defineConfig(config) {
38695
38942
  if (config.security === false || config.security === "off") {
38696
- config.security = {};
38697
- } else if (typeof config.security === "string") {
38943
+ config.security = { autoEscape: true };
38944
+ if (config.routeDataCache === void 0) config.routeDataCache = 0;
38945
+ const mdOff = normalizeMdConfig(config.md);
38946
+ if (mdOff !== void 0) config.md = mdOff;
38947
+ return config;
38948
+ }
38949
+ if (typeof config.security === "string") {
38698
38950
  const p = SECURITY_PRESETS[config.security];
38699
38951
  if (!p) throw VeskError.configError(`Unknown security preset string: "${config.security}".`, Object.keys(SECURITY_PRESETS));
38700
38952
  config.security = { ...p };
@@ -38813,7 +39065,7 @@ init_parser();
38813
39065
  init_ir_generator();
38814
39066
  init_scan();
38815
39067
  init_tokens();
38816
- var DEFAULT_MAX_BODY_BYTES2 = 1024 * 1024;
39068
+ var DEFAULT_MAX_BODY_BYTES3 = 1024 * 1024;
38817
39069
  function isAlnum2(code) {
38818
39070
  return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
38819
39071
  }
@@ -39298,6 +39550,8 @@ async function bundleRuntime(appDir, outDir) {
39298
39550
  "",
39299
39551
  "// Re-export runtime classes used by API routes",
39300
39552
  "export const VeskRequest = __veskRuntime.VeskRequest;",
39553
+ "export const PayloadTooLargeError = __veskRuntime.PayloadTooLargeError;",
39554
+ "export const DEFAULT_MAX_BODY_BYTES = __veskRuntime.DEFAULT_MAX_BODY_BYTES;",
39301
39555
  "export const VeskResponse = __veskRuntime.VeskResponse;",
39302
39556
  "",
39303
39557
  "// Server actions",
@@ -39307,6 +39561,39 @@ async function bundleRuntime(appDir, outDir) {
39307
39561
  "export const validateActionInput = __veskRuntime.validateActionInput;",
39308
39562
  "export const issuesToFieldMap = __veskRuntime.issuesToFieldMap;",
39309
39563
  "",
39564
+ "// API-route surface. `@vesk/runtime/server` imports in app/api/**/route.ts",
39565
+ "// are rewritten to this bundle, so anything a route is documented to",
39566
+ "// import must be re-exported here \u2014 otherwise it resolves in dev (which",
39567
+ "// imports the real barrel) and fails only in a production build.",
39568
+ "export const redirect = __veskRuntime.redirect;",
39569
+ "export const permanentRedirect = __veskRuntime.permanentRedirect;",
39570
+ "export const notFound = __veskRuntime.notFound;",
39571
+ "export const NotFoundError = __veskRuntime.NotFoundError;",
39572
+ "export const useParams = __veskRuntime.useParams;",
39573
+ "export const useBody = __veskRuntime.useBody;",
39574
+ "export const useRequest = __veskRuntime.useRequest;",
39575
+ "export const withValidation = __veskRuntime.withValidation;",
39576
+ "export const cors = __veskRuntime.cors;",
39577
+ "export const webhook = __veskRuntime.webhook;",
39578
+ "export const signCookie = __veskRuntime.signCookie;",
39579
+ "export const unsignCookie = __veskRuntime.unsignCookie;",
39580
+ "export const setSignedCookie = __veskRuntime.setSignedCookie;",
39581
+ "export const readSignedCookie = __veskRuntime.readSignedCookie;",
39582
+ "export const HttpError = __veskRuntime.HttpError;",
39583
+ "export const TimeoutError = __veskRuntime.TimeoutError;",
39584
+ "export const mutate = __veskRuntime.mutate;",
39585
+ "export const runHooks = __veskRuntime.runHooks;",
39586
+ "",
39587
+ "// ISR (server-only; revalidate*/clear* are no-ops outside a request).",
39588
+ "export const isr = __veskRuntime.isr;",
39589
+ "export const pageIsr = __veskRuntime.pageIsr;",
39590
+ "export const componentIsr = __veskRuntime.componentIsr;",
39591
+ "export const revalidatePath = __veskRuntime.revalidatePath;",
39592
+ "export const revalidateTag = __veskRuntime.revalidateTag;",
39593
+ "export const revalidateComponent = __veskRuntime.revalidateComponent;",
39594
+ "export const clearIsrCache = __veskRuntime.clearIsrCache;",
39595
+ "export const isrConfigToRevalidate = __veskRuntime.isrConfigToRevalidate;",
39596
+ "",
39310
39597
  "// Server-wide context store (server events)",
39311
39598
  "export const serverLocals = __veskRuntime.serverLocals;",
39312
39599
  "export const getServerContext = __veskRuntime.getServerContext;",
@@ -39976,19 +40263,122 @@ function generateApiFunction(apiNode, _apiDir, outDir, options) {
39976
40263
  }
39977
40264
  const paramsCode = extracts.length > 0 ? ` const params = { ${extracts.join(", ")} };
39978
40265
  ` : " const params = {};\n";
39979
- const handlerMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"].filter((m) => routeSrc.includes(`async function ${m}(`) || routeSrc.includes(`async function ${m} (`) || routeSrc.includes(`export async function ${m}`)).map((m) => `${m}`).join(", ");
40266
+ const sharedBody = [
40267
+ " const url = new URL(request.url);",
40268
+ " const urlParts = url.pathname.replace(/^\\/api\\/?/, '/').split('/').filter(Boolean);",
40269
+ " const method = request.method || 'GET';",
40270
+ `${paramsCode}`,
40271
+ " Object.defineProperty(request, 'query', {",
40272
+ " get: () => Object.fromEntries(url.searchParams.entries()),",
40273
+ " enumerable: true,",
40274
+ " });",
40275
+ " const __rootLocals = (globalThis.__vesk_request && globalThis.__vesk_request.locals) || {};"
40276
+ ];
40277
+ const dispatchBody = [
40278
+ " try {",
40279
+ " const __mod = {",
40280
+ ...["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"].map(
40281
+ (m) => ` ${m}: typeof ${m} === 'function' ? ${m} : undefined,`
40282
+ ),
40283
+ " };",
40284
+ " const __allowed = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'].filter((m) => __mod[m]);",
40285
+ " const handler = __mod[method];",
40286
+ " if (!handler) {",
40287
+ " if (method === 'OPTIONS') {",
40288
+ " return new Response(null, { status: 204, headers: { Allow: __allowed.join(', ') } });",
40289
+ " }",
40290
+ " return new Response(JSON.stringify({ error: `Method ${method} not allowed` }), {",
40291
+ " status: 405,",
40292
+ " headers: { 'Content-Type': 'application/json', Allow: __allowed.join(', ') },",
40293
+ " });",
40294
+ " }",
40295
+ " const __routeConfig = (typeof config !== 'undefined' && config) || {};",
40296
+ " // Default CSRF defense for mutating API calls: same-origin check unless",
40297
+ " // the route opts out via `config.csrf = false`.",
40298
+ " if (__routeConfig.csrf !== false && method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS') {",
40299
+ " try {",
40300
+ " assertSameOrigin({",
40301
+ " method,",
40302
+ " headers: Object.fromEntries([...request.headers.entries()].map(([k, v]) => [k, Array.isArray(v) ? v.join(', ') : v])),",
40303
+ " });",
40304
+ " } catch {",
40305
+ " return new Response(JSON.stringify({ error: 'Cross-origin request blocked' }), {",
40306
+ " status: 403, headers: { 'Content-Type': 'application/json' },",
40307
+ " });",
40308
+ " }",
40309
+ " }",
40310
+ " // Body cap. The handler receives the raw platform Request, so a route that",
40311
+ " // calls `req.json()` directly would bypass a VeskRequest-level guard \u2014",
40312
+ " // enforce it here, before any handler runs, on every target (dev, node,",
40313
+ " // serverless, edge).",
40314
+ " const __declared = Number(request.headers.get('content-length') || '0');",
40315
+ " if (Number.isFinite(__declared) && __declared > DEFAULT_MAX_BODY_BYTES) {",
40316
+ " return new Response(JSON.stringify({ error: `Request body exceeds limit (${DEFAULT_MAX_BODY_BYTES} bytes)` }), {",
40317
+ " status: 413, headers: { 'Content-Type': 'application/json' },",
40318
+ " });",
40319
+ " }",
40320
+ " let __signal = request.signal;",
40321
+ " let __timeout;",
40322
+ " if (__routeConfig.maxDuration) {",
40323
+ " const __controller = new AbortController();",
40324
+ " __timeout = setTimeout(",
40325
+ " () => __controller.abort(new Error(`Request timed out after ${__routeConfig.maxDuration}s`)),",
40326
+ " Number(__routeConfig.maxDuration) * 1000",
40327
+ " );",
40328
+ " __signal = __controller.signal;",
40329
+ " Object.defineProperty(request, 'signal', { value: __signal, writable: false });",
40330
+ " }",
40331
+ " // Route hooks may be a single function or an array of them.",
40332
+ " const __before = typeof beforeRequest === 'undefined' ? [] : (Array.isArray(beforeRequest) ? beforeRequest : [beforeRequest]);",
40333
+ " for (const __hook of __before) {",
40334
+ " const __hookResult = await __hook(request, { params: Promise.resolve(params), locals: ctx.locals });",
40335
+ " if (__hookResult instanceof Response) return __hookResult;",
40336
+ " }",
40337
+ " let __global = await runHooks('beforeRequest', request, { params, locals: ctx.locals });",
40338
+ " if (__global instanceof Response) return __global;",
40339
+ " let __response;",
40340
+ " try {",
40341
+ " __response = await handler(request, { params: Promise.resolve(params) });",
40342
+ " } catch (__e) {",
40343
+ " const __handled = await runHooks('onError', __e, request);",
40344
+ " if (__handled instanceof Response) return __handled;",
40345
+ " throw __e;",
40346
+ " } finally {",
40347
+ " if (__timeout) clearTimeout(__timeout);",
40348
+ " }",
40349
+ " const __after = typeof afterRequest === 'undefined' ? [] : (Array.isArray(afterRequest) ? afterRequest : [afterRequest]);",
40350
+ " for (const __hook of __after) {",
40351
+ " const __hookResult = await __hook(request, __response);",
40352
+ " if (__hookResult instanceof Response) __response = __hookResult;",
40353
+ " }",
40354
+ " __global = await runHooks('afterRequest', request, __response);",
40355
+ " if (__global instanceof Response) __response = __global;",
40356
+ " if (__response && typeof __response.build === 'function') __response.build();",
40357
+ " if (__response instanceof Response) return __response;",
40358
+ " return new Response(JSON.stringify(__response), {",
40359
+ " status: 200,",
40360
+ " headers: { 'Content-Type': 'application/json' },",
40361
+ " });",
40362
+ " } catch (e) {",
40363
+ " const err = /** @type {Error & Record<string, unknown>} */(e);",
40364
+ " if (err.name === 'Redirect') {",
40365
+ " return new Response(null, { status: Number(err.status) || 302, headers: { Location: String(err.url || '/') } });",
40366
+ " }",
40367
+ " if (err.name === 'NotFoundError') {",
40368
+ " return new Response(JSON.stringify({ error: 'Not Found' }), { status: 404, headers: { 'Content-Type': 'application/json' } });",
40369
+ " }",
40370
+ " if (err.name === 'PayloadTooLargeError') {",
40371
+ " return new Response(JSON.stringify({ error: 'Request body too large' }), { status: 413, headers: { 'Content-Type': 'application/json' } });",
40372
+ " }",
40373
+ " return new Response(JSON.stringify({ error: process.env.NODE_ENV === 'production' ? 'Internal Server Error' : err.message }), { status: 500, headers: { 'Content-Type': 'application/json' } });",
40374
+ " } finally {",
40375
+ " globalThis.__vesk_request = prev;",
40376
+ " }"
40377
+ ];
39980
40378
  let handleBody;
39981
40379
  if (middlewareCode) {
39982
40380
  handleBody = [
39983
- " const url = new URL(request.url);",
39984
- " const urlParts = url.pathname.replace(/^\\/api\\/?/, '/').split('/').filter(Boolean);",
39985
- " const method = request.method || 'GET';",
39986
- `${paramsCode}`,
39987
- " Object.defineProperty(request, 'query', {",
39988
- " get: () => Object.fromEntries(url.searchParams.entries()),",
39989
- " enumerable: true,",
39990
- " });",
39991
- " const __rootLocals = (globalThis.__vesk_request && globalThis.__vesk_request.locals) || {};",
40381
+ ...sharedBody,
39992
40382
  " // \u2500\u2500 Middleware context \u2500\u2500",
39993
40383
  " const __ctx = {",
39994
40384
  " request,",
@@ -40015,47 +40405,11 @@ function generateApiFunction(apiNode, _apiDir, outDir, options) {
40015
40405
  " });",
40016
40406
  " const prev = globalThis.__vesk_request;",
40017
40407
  " globalThis.__vesk_request = ctx;",
40018
- " try {",
40019
- ` const handler = { ${handlerMethods} }[method];`,
40020
- " if (!handler) {",
40021
- " return new Response(JSON.stringify({ error: 'Method not allowed' }), {",
40022
- " status: 405,",
40023
- " headers: { 'Content-Type': 'application/json' },",
40024
- " });",
40025
- " }",
40026
- " const response = await handler(request, { params: Promise.resolve(params) });",
40027
- " if (response instanceof Response) {",
40028
- " if (typeof response.build === 'function') response.build();",
40029
- " return response;",
40030
- " }",
40031
- " return new Response(JSON.stringify(response), {",
40032
- " status: 200,",
40033
- " headers: { 'Content-Type': 'application/json' },",
40034
- " });",
40035
- " } catch (e) {",
40036
- " const err = /** @type {Error & Record<string, unknown>} */(e);",
40037
- " if (err.name === 'Redirect') {",
40038
- ' return new Response(null, { status: Number(err.status) || 302, headers: { Location: String(err.url || "") } });',
40039
- " }",
40040
- " if (err.name === 'NotFoundError') {",
40041
- " return new Response(JSON.stringify({ error: 'Not Found' }), { status: 404, headers: { 'Content-Type': 'application/json' } });",
40042
- " }",
40043
- " return new Response(JSON.stringify({ error: err.message }), { status: 500, headers: { 'Content-Type': 'application/json' } });",
40044
- " } finally {",
40045
- " globalThis.__vesk_request = prev;",
40046
- " }"
40408
+ ...dispatchBody
40047
40409
  ].join("\n");
40048
40410
  } else {
40049
40411
  handleBody = [
40050
- " const url = new URL(request.url);",
40051
- " const urlParts = url.pathname.replace(/^\\/api\\/?/, '/').split('/').filter(Boolean);",
40052
- " const method = request.method || 'GET';",
40053
- `${paramsCode}`,
40054
- " Object.defineProperty(request, 'query', {",
40055
- " get: () => Object.fromEntries(url.searchParams.entries()),",
40056
- " enumerable: true,",
40057
- " });",
40058
- " const __rootLocals = (globalThis.__vesk_request && globalThis.__vesk_request.locals) || {};",
40412
+ ...sharedBody,
40059
40413
  " const ctx = {",
40060
40414
  " headers: Object.fromEntries(request.headers.entries()),",
40061
40415
  " url: request.url,",
@@ -40069,35 +40423,7 @@ function generateApiFunction(apiNode, _apiDir, outDir, options) {
40069
40423
  " });",
40070
40424
  " const prev = globalThis.__vesk_request;",
40071
40425
  " globalThis.__vesk_request = ctx;",
40072
- " try {",
40073
- ` const handler = { ${handlerMethods} }[method];`,
40074
- " if (!handler) {",
40075
- " return new Response(JSON.stringify({ error: 'Method not allowed' }), {",
40076
- " status: 405,",
40077
- " headers: { 'Content-Type': 'application/json' },",
40078
- " });",
40079
- " }",
40080
- " const response = await handler(request, { params: Promise.resolve(params) });",
40081
- " if (response instanceof Response) {",
40082
- " if (typeof response.build === 'function') response.build();",
40083
- " return response;",
40084
- " }",
40085
- " return new Response(JSON.stringify(response), {",
40086
- " status: 200,",
40087
- " headers: { 'Content-Type': 'application/json' },",
40088
- " });",
40089
- " } catch (e) {",
40090
- " const err = /** @type {Error & Record<string, unknown>} */(e);",
40091
- " if (err.name === 'Redirect') {",
40092
- ' return new Response(null, { status: Number(err.status) || 302, headers: { Location: String(err.url || "") } });',
40093
- " }",
40094
- " if (err.name === 'NotFoundError') {",
40095
- " return new Response(JSON.stringify({ error: 'Not Found' }), { status: 404, headers: { 'Content-Type': 'application/json' } });",
40096
- " }",
40097
- " return new Response(JSON.stringify({ error: err.message }), { status: 500, headers: { 'Content-Type': 'application/json' } });",
40098
- " } finally {",
40099
- " globalThis.__vesk_request = prev;",
40100
- " }"
40426
+ ...dispatchBody
40101
40427
  ].join("\n");
40102
40428
  }
40103
40429
  const funcCode = [
@@ -40106,7 +40432,7 @@ function generateApiFunction(apiNode, _apiDir, outDir, options) {
40106
40432
  routeSrc.trim(),
40107
40433
  "",
40108
40434
  "// \u2500\u2500 Request handler wrapper \u2500\u2500",
40109
- "import { parseCookies } from '../runtime.js';",
40435
+ "import { parseCookies, assertSameOrigin, runHooks, DEFAULT_MAX_BODY_BYTES } from '../runtime.js';",
40110
40436
  middlewareCode ? "import { parseCookies as __parseCookies } from '../runtime.js';" : "",
40111
40437
  "",
40112
40438
  middlewareCode || "",
@@ -44296,7 +44622,7 @@ function generateComponent2(comp, importedNames = /* @__PURE__ */ new Set(), hyd
44296
44622
  ctx2.push(indent3(`const $root = document.createDocumentFragment();`));
44297
44623
  }
44298
44624
  ctx2.push(indent3(`let __pendingChild = null;`));
44299
- const paramInit = buildParamInit3(comp.paramNames);
44625
+ const paramInit = buildParamInit3(comp.paramNames, comp.propsAlias);
44300
44626
  if (paramInit)
44301
44627
  ctx2.push(indent3(paramInit));
44302
44628
  for (const node of comp.body) {
@@ -44345,7 +44671,9 @@ function generateComponent2(comp, importedNames = /* @__PURE__ */ new Set(), hyd
44345
44671
  ctx2.push(`}`);
44346
44672
  return ctx2.getCode();
44347
44673
  }
44348
- function buildParamInit3(paramNames) {
44674
+ function buildParamInit3(paramNames, propsAlias = null) {
44675
+ if (propsAlias)
44676
+ return propsAlias === "props" ? "" : `const ${propsAlias} = props;`;
44349
44677
  if (paramNames.length === 1 && paramNames[0] === "props")
44350
44678
  return "";
44351
44679
  if (paramNames.length === 0)
@@ -45434,8 +45762,12 @@ async function executeApiRoute(filePath, method, request, params = {}, locals2 =
45434
45762
  signal = controller.signal;
45435
45763
  Object.defineProperty(request, "signal", { value: signal, writable: false });
45436
45764
  }
45437
- const beforeHooks = mod.beforeRequest || [];
45438
- for (const hook of beforeHooks) {
45765
+ const toHookList = (v) => {
45766
+ if (v == null)
45767
+ return [];
45768
+ return Array.isArray(v) ? v : [v];
45769
+ };
45770
+ for (const hook of toHookList(mod.beforeRequest)) {
45439
45771
  const hookResult = await hook(request, { params, locals: locals2 });
45440
45772
  if (hookResult instanceof Response)
45441
45773
  return hookResult;
@@ -45453,8 +45785,7 @@ async function executeApiRoute(filePath, method, request, params = {}, locals2 =
45453
45785
  return errorResult;
45454
45786
  throw e;
45455
45787
  }
45456
- const afterHooks = mod.afterRequest || [];
45457
- for (const hook of afterHooks) {
45788
+ for (const hook of toHookList(mod.afterRequest)) {
45458
45789
  const hookResult = await hook(request, response);
45459
45790
  if (hookResult instanceof Response)
45460
45791
  response = hookResult;
@@ -52850,7 +53181,7 @@ async function handleActionRequest(req, res, ctx2) {
52850
53181
  let tooLarge = false;
52851
53182
  for await (const chunk of req) {
52852
53183
  bodyTotal += chunk.byteLength;
52853
- if (bodyTotal > (ctx2.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES2)) {
53184
+ if (bodyTotal > (ctx2.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES3)) {
52854
53185
  tooLarge = true;
52855
53186
  break;
52856
53187
  }
@@ -52858,7 +53189,7 @@ async function handleActionRequest(req, res, ctx2) {
52858
53189
  }
52859
53190
  if (tooLarge) {
52860
53191
  res.writeHead(413, { "Content-Type": "application/json" });
52861
- res.end(JSON.stringify({ ok: false, error: `Request body exceeds limit (${ctx2.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES2} bytes)` }));
53192
+ res.end(JSON.stringify({ ok: false, error: `Request body exceeds limit (${ctx2.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES3} bytes)` }));
52862
53193
  return true;
52863
53194
  }
52864
53195
  const bodyBuffer = Buffer.concat(chunks);
@@ -53324,8 +53655,8 @@ async function startDevServer(port, projectDir, config, host) {
53324
53655
  if (!process.env.NODE_ENV) process.env.NODE_ENV = "development";
53325
53656
  const secCfg = config.security || {};
53326
53657
  const maxBodyBytes = (typeof secCfg.maxBodyBytes === "number" ? secCfg.maxBodyBytes : void 0) || DEFAULT_MAX_BODY_BYTES;
53327
- const appDirPath = join24(projectDir, "app");
53328
- const publicDir = join24(projectDir, "public");
53658
+ const appDirPath = resolve38(projectDir, typeof config.appDir === "string" ? config.appDir : "./app");
53659
+ const publicDir = resolve38(projectDir, typeof config.publicDir === "string" ? config.publicDir : "./public");
53329
53660
  installMdReadHook2([publicDir]);
53330
53661
  try {
53331
53662
  ensurePackagesBuilt();
@@ -54706,11 +55037,13 @@ __export(index_server_exports2, {
54706
55037
  ArticleSchema: () => ArticleSchema,
54707
55038
  BreadcrumbListSchema: () => BreadcrumbListSchema,
54708
55039
  Context: () => Context,
55040
+ DEFAULT_MAX_BODY_BYTES: () => DEFAULT_MAX_BODY_BYTES2,
54709
55041
  Experiment: () => Experiment,
54710
55042
  FAQPageSchema: () => FAQPageSchema,
54711
55043
  Field: () => Field,
54712
55044
  For: () => For,
54713
55045
  Form: () => Form,
55046
+ HttpError: () => HttpError,
54714
55047
  Image: () => Image,
54715
55048
  JsonLd: () => JsonLd,
54716
55049
  Link: () => Link,
@@ -54722,6 +55055,7 @@ __export(index_server_exports2, {
54722
55055
  NotFoundError: () => NotFoundError,
54723
55056
  OrganizationSchema: () => OrganizationSchema,
54724
55057
  Outlet: () => Outlet,
55058
+ PayloadTooLargeError: () => PayloadTooLargeError,
54725
55059
  Portal: () => Portal,
54726
55060
  ProductSchema: () => ProductSchema,
54727
55061
  Redirect: () => Redirect,
@@ -54729,6 +55063,7 @@ __export(index_server_exports2, {
54729
55063
  ServerResponse: () => ServerResponse,
54730
55064
  Show: () => Show,
54731
55065
  Switch: () => Switch,
55066
+ TimeoutError: () => TimeoutError,
54732
55067
  VeskRequest: () => VeskRequest,
54733
55068
  VeskResponse: () => VeskResponse,
54734
55069
  VideoSchema: () => VideoSchema,
@@ -54749,6 +55084,7 @@ __export(index_server_exports2, {
54749
55084
  cors: () => cors,
54750
55085
  createContext: () => createContext,
54751
55086
  createFileRouter: () => createFileRouter,
55087
+ createLocals: () => createLocals,
54752
55088
  createResource: () => createResource,
54753
55089
  createRouter: () => createRouter,
54754
55090
  create_component_ctx: () => create_component_ctx,
@@ -54780,6 +55116,7 @@ __export(index_server_exports2, {
54780
55116
  locals: () => locals,
54781
55117
  maxLength: () => maxLength,
54782
55118
  minLength: () => minLength,
55119
+ mutate: () => mutate,
54783
55120
  notFound: () => notFound,
54784
55121
  on_destroy: () => on_destroy,
54785
55122
  pageIsr: () => pageIsr,
@@ -54936,12 +55273,20 @@ async function loadConfig(projectDir) {
54936
55273
  }
54937
55274
  return config;
54938
55275
  }
55276
+ function resolveProjectDirs(projectDir, config) {
55277
+ const cfg = config || {};
55278
+ return {
55279
+ appDir: resolve40(projectDir, typeof cfg.appDir === "string" ? cfg.appDir : "./app"),
55280
+ publicDir: resolve40(projectDir, typeof cfg.publicDir === "string" ? cfg.publicDir : "./public"),
55281
+ outDir: resolve40(projectDir, typeof cfg.outDir === "string" ? cfg.outDir : ".vesk")
55282
+ };
55283
+ }
54939
55284
  if (cmd === "build") {
54940
55285
  const projectDir = process.cwd();
54941
- const appDirPath = join26(projectDir, "app");
54942
- const publicDir = join26(projectDir, "public");
55286
+ const rawConfig = await loadConfig(projectDir) || {};
55287
+ const { appDir: appDirPath, publicDir, outDir } = resolveProjectDirs(projectDir, rawConfig);
54943
55288
  if (!existsSync43(appDirPath)) {
54944
- console.error(`vesk build: no app/ directory found in ${projectDir}`);
55289
+ console.error(`vesk build: no app directory found in ${projectDir} (config appDir: ${rawConfig.appDir ?? "./app"})`);
54945
55290
  process.exit(1);
54946
55291
  }
54947
55292
  const restArgs = process.argv.slice(3);
@@ -54951,13 +55296,14 @@ if (cmd === "build") {
54951
55296
  const platform = platformIdx !== -1 ? restArgs[platformIdx + 1] : void 0;
54952
55297
  const targetIdx = restArgs.indexOf("--target");
54953
55298
  const target = targetIdx !== -1 && restArgs[targetIdx + 1] === "edge" ? "edge" : "node";
54954
- const config = await loadConfig(projectDir);
54955
- const plugins = config?.plugins || [];
54956
- const mdCfg = config?.md;
54957
- const opts = { publicDir, plugins, seo, strictSeo: strict, codeSplit: !restArgs.includes("--skip-split"), target };
55299
+ const config = rawConfig;
55300
+ const plugins = config.plugins || [];
55301
+ const mdCfg = config.md;
55302
+ const opts = { outDir, publicDir, plugins, seo, strictSeo: strict, codeSplit: !restArgs.includes("--skip-split"), target };
54958
55303
  if (mdCfg) opts.md = mdCfg;
54959
55304
  if (config.routeDataCache !== void 0) opts.routeDataCache = config.routeDataCache;
54960
55305
  if (platform) opts.platform = platform;
55306
+ if (config.ssg !== void 0 && config.ssg !== false) opts.ssg = true;
54961
55307
  try {
54962
55308
  await build2(appDirPath, opts);
54963
55309
  const { drainMdHtmlWarnings: drainMdHtmlWarnings3 } = await Promise.resolve().then(() => (init_md2(), md_exports2));
@@ -55010,9 +55356,9 @@ ${code}`;
55010
55356
  }
55011
55357
  if (cmd === "seo") {
55012
55358
  const projectDir = process.cwd();
55013
- const appDirPath = join26(projectDir, "app");
55359
+ const { appDir: appDirPath } = resolveProjectDirs(projectDir, await loadConfig(projectDir) || {});
55014
55360
  if (!existsSync43(appDirPath)) {
55015
- console.error(`vesk seo: no app/ directory found in ${projectDir}`);
55361
+ console.error(`vesk seo: no app directory found in ${projectDir}`);
55016
55362
  process.exit(1);
55017
55363
  }
55018
55364
  const strict = args.includes("--strict");
@@ -55025,9 +55371,9 @@ if (cmd === "seo") {
55025
55371
  }
55026
55372
  if (cmd === "typecheck") {
55027
55373
  const projectDir = process.cwd();
55028
- const appDirPath = join26(projectDir, "app");
55374
+ const { appDir: appDirPath } = resolveProjectDirs(projectDir, await loadConfig(projectDir) || {});
55029
55375
  if (!existsSync43(appDirPath)) {
55030
- console.error(`vesk typecheck: no app/ directory found in ${projectDir}`);
55376
+ console.error(`vesk typecheck: no app directory found in ${projectDir}`);
55031
55377
  process.exit(1);
55032
55378
  }
55033
55379
  const { typecheckProject: typecheckProject2, formatTypecheckErrors: formatTypecheckErrors2, formatTypecheckWarnings: formatTypecheckWarnings2 } = await Promise.resolve().then(() => (init_typecheck(), typecheck_exports));
@@ -55050,16 +55396,20 @@ if (cmd === "typecheck") {
55050
55396
  }
55051
55397
  if (cmd === "start") {
55052
55398
  const projectDir = process.cwd();
55053
- const outDir = join26(projectDir, ".vesk");
55399
+ const { outDir } = resolveProjectDirs(projectDir, await loadConfig(projectDir) || {});
55054
55400
  const port = parsePortArg(args);
55055
55401
  const host = parseHostArg(args);
55402
+ if (!existsSync43(outDir)) {
55403
+ console.error(`vesk start: no build found at ${outDir} \u2014 run "vesk build" first.`);
55404
+ process.exit(1);
55405
+ }
55056
55406
  startProdServer(outDir, { port, host });
55057
55407
  await new Promise(() => {
55058
55408
  });
55059
55409
  }
55060
55410
  if (cmd === "init") {
55061
55411
  const projectDir = process.cwd();
55062
- const appDirPath = join26(projectDir, "app");
55412
+ const { appDir: appDirPath } = resolveProjectDirs(projectDir, await loadConfig(projectDir) || {});
55063
55413
  const target = join26(appDirPath, "global.css");
55064
55414
  if (existsSync43(target)) {
55065
55415
  console.error(`vesk init: ${target} already exists \u2014 skipping`);
@@ -55079,7 +55429,7 @@ if (cmd === "init") {
55079
55429
  }
55080
55430
  if (cmd === "dev") {
55081
55431
  const projectDir = process.cwd();
55082
- const appDirPath = join26(projectDir, "app");
55432
+ const { appDir: appDirPath, publicDir } = resolveProjectDirs(projectDir, await loadConfig(projectDir) || {});
55083
55433
  const port = parsePortArg(args);
55084
55434
  const host = parseHostArg(args);
55085
55435
  if (!existsSync43(appDirPath)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/vesk-cli",
3
- "version": "0.2.38",
3
+ "version": "0.2.39",
4
4
  "type": "module",
5
5
  "description": "Vesk — compiler-first framework for the post-VDOM web. vesk dev, vesk build.",
6
6
  "bin": {
@@ -8,10 +8,10 @@
8
8
  },
9
9
  "main": "./dist/cli.js",
10
10
  "dependencies": {
11
- "@vesk/adapter": "^0.2.38",
12
- "@vesk/compiler": "^0.2.38",
13
- "@vesk/plugin-tailwind": "^0.2.38",
14
- "@vesk/runtime": "^0.2.38",
11
+ "@vesk/adapter": "^0.2.39",
12
+ "@vesk/compiler": "^0.2.39",
13
+ "@vesk/plugin-tailwind": "^0.2.39",
14
+ "@vesk/runtime": "^0.2.39",
15
15
  "esrap": "^2.3.0",
16
16
  "typescript": "^5.8.0",
17
17
  "ws": "^8.18.0",