void 1.5.0 → 1.6.0

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 (178) hide show
  1. package/dist/{account-cmd-ChVKPZ-w.mjs → account-cmd-DRhmCWcm.mjs} +3 -3
  2. package/dist/{application-analysis-2vGZrKzg.mjs → application-analysis-HRFsZOv3.mjs} +3 -3
  3. package/dist/{application-routing-DnUQoOJD.mjs → application-routing-P4TggvWp.mjs} +1 -1
  4. package/dist/{auth-link-D4uMdOsw.mjs → auth-link-CVXP1G6G.mjs} +3 -3
  5. package/dist/{auth-router-62dna450.mjs → auth-router-D1pGdbvB.mjs} +2 -2
  6. package/dist/{better-auth-shared-CiclgPde.mjs → better-auth-shared-C958aM7W.mjs} +28 -3
  7. package/dist/{build-cmd-D93K_4Sa.mjs → build-cmd-DmjbuT6d.mjs} +1 -1
  8. package/dist/{cache-CiIzEED4.mjs → cache-2khBO1MR.mjs} +1 -1
  9. package/dist/{cancel-deploy-BNoGfn2e.mjs → cancel-deploy-_itbbvY3.mjs} +1 -1
  10. package/dist/cli/cli.mjs +27 -27
  11. package/dist/client-CEtr5UOR.mjs +1 -0
  12. package/dist/{client-Qayy9xEh.mjs → client-CoxPEhJq.mjs} +6 -2
  13. package/dist/{cloudflare-cmd-DiFGaLow.mjs → cloudflare-cmd-fZ6AMSWk.mjs} +1 -1
  14. package/dist/{cloudflare-config-Chet188_.mjs → cloudflare-config-eQunqX6A.mjs} +1 -1
  15. package/dist/{cloudflare-connect-BdgAQBc7.mjs → cloudflare-connect-9OZe4XVQ.mjs} +1 -1
  16. package/dist/{cloudflare-operations-CWA24vCh.mjs → cloudflare-operations-CrskD4_O.mjs} +4 -4
  17. package/dist/{cloudflare-operations-D8MoezuN.mjs → cloudflare-operations-oLvAd9Nd.mjs} +1 -1
  18. package/dist/{config-Bt4rw93x.mjs → config-BBlVXZCU.mjs} +1 -1
  19. package/dist/{config-DkA9jYF2.mjs → config-BITKQ3O6.mjs} +2 -1
  20. package/dist/{config-BszFVhpk.mjs → config-BgysUhnm.mjs} +1 -1
  21. package/dist/{config-CzsNzGVH.d.mts → config-CPxJmt4M.d.mts} +2 -0
  22. package/dist/config-entry.d.mts +1 -1
  23. package/dist/{connect-b9mJbb6X.mjs → connect-CKvmkl2R.mjs} +4 -4
  24. package/dist/{create-project-rnjum4_V.mjs → create-project-BiMmR5wA.mjs} +1 -1
  25. package/dist/{create-project-eomwm0wa.mjs → create-project-Bq0M8MtO.mjs} +1 -1
  26. package/dist/{db-UPWmiSk1.mjs → db-cq5RW4Vy.mjs} +17 -17
  27. package/dist/{delete-BeeYi1WO.mjs → delete-CY4L8fi2.mjs} +1 -1
  28. package/dist/deploy-B029oHlE.mjs +2 -0
  29. package/dist/{deploy-CwmCcTlq.mjs → deploy-D_gEojD2.mjs} +79 -42
  30. package/dist/{deploy-managed-static-gPDxyp3v.mjs → deploy-managed-static-BHVbB_-A.mjs} +11 -11
  31. package/dist/{deploy-transport-L6zzPE9T.mjs → deploy-transport-DEyOUfac.mjs} +2 -2
  32. package/dist/deploy.mjs +5 -4
  33. package/dist/{dev-inbox-CwH5VuZj.mjs → dev-inbox-CUUS6iUg.mjs} +1 -1
  34. package/dist/{dispatch-CiJ1Xrba.mjs → dispatch-BNLB5_Gm.mjs} +1 -1
  35. package/dist/{domain-3xdH9p2L.mjs → domain-BlH90Ods.mjs} +2 -2
  36. package/dist/{email-Du3YRnM5.mjs → email-Bkq8ZL1p.mjs} +6 -6
  37. package/dist/{email-cloudflare-CtD8qE13.mjs → email-cloudflare-MvJIxrwx.mjs} +5 -107
  38. package/dist/{env-Cnvp0pGy.mjs → env-AvuCSeCH.mjs} +2 -2
  39. package/dist/{gen-BvVmpZ5f.mjs → gen-BD-dfjxY.mjs} +3 -3
  40. package/dist/gen-CIHXh9I8.mjs +1 -0
  41. package/dist/{github-cmd-X2GKiLi-.mjs → github-cmd-CA-lBdkn.mjs} +1 -1
  42. package/dist/{handler-Drm_lyrV.d.mts → handler-BR6Tvjkp.d.mts} +8 -3
  43. package/dist/{handler-xmzz0U38.mjs → handler-CKeTtYID.mjs} +2 -12
  44. package/dist/headers-CuTEdP9C.mjs +445 -0
  45. package/dist/{help-EWDNxST-.mjs → help-1OCFDsRr.mjs} +9 -1
  46. package/dist/help-Ba8Dq5-r.mjs +1 -0
  47. package/dist/{inbound-DSKSHDUj.d.mts → inbound-BHt4dIlL.d.mts} +16 -5
  48. package/dist/{inbound-DQfekMtz.mjs → inbound-CdO-qY0w.mjs} +2 -76
  49. package/dist/index.d.mts +1 -1
  50. package/dist/index.mjs +65 -37
  51. package/dist/{info-jW8_d3VI.mjs → info-CJxua_db.mjs} +4 -4
  52. package/dist/{init-jDQ5G9QC.mjs → init-B-4CluYx.mjs} +12 -11
  53. package/dist/{isr-config-DhBkzCdY.mjs → isr-config-Czzb4mIq.mjs} +1 -47
  54. package/dist/{link-DOyxX5Xs.mjs → link-DAXOT-G_.mjs} +2 -2
  55. package/dist/{list-C9pTYDtl.mjs → list-BYSOo9fm.mjs} +2 -2
  56. package/dist/{live-CDBHyYiR.d.mts → live-0xrcVpyq.d.mts} +1 -1
  57. package/dist/{local-d1-CKfek6Z4.mjs → local-d1-BLgsNUj9.mjs} +1 -1
  58. package/dist/login-BpbYeu-n.mjs +1 -0
  59. package/dist/{login-koQbjzPd.mjs → login-CJopTWX4.mjs} +2 -2
  60. package/dist/{logs-cTU5TsRW.mjs → logs-DE0weBsR.mjs} +13 -11
  61. package/dist/{migrate-C62pGTfr.mjs → migrate-ChHHZBTi.mjs} +3 -3
  62. package/dist/migrate-HdA6Kh2p.mjs +1 -0
  63. package/dist/{mime-DKkO7ZtA.mjs → mime-CtP6-qAF.mjs} +143 -15
  64. package/dist/nitro.mjs +3 -3
  65. package/dist/{node-BfAt2rz8.mjs → node-CZ6lGNLn.mjs} +1 -1
  66. package/dist/{operator-cmd-CnAvSqJ5.mjs → operator-cmd-BUqsdk2K.mjs} +28 -10
  67. package/dist/operator-cmd-wBYyp8iQ.mjs +1 -0
  68. package/dist/pages/client.d.mts +143 -11
  69. package/dist/pages/client.mjs +173 -12
  70. package/dist/pages/index.d.mts +22 -3
  71. package/dist/pages/index.mjs +18 -4
  72. package/dist/pages/islands-plugin.d.mts +46 -2
  73. package/dist/pages/islands-plugin.mjs +91 -5
  74. package/dist/pages/prefetch.d.mts +2 -2
  75. package/dist/pages/prefetch.mjs +1 -1
  76. package/dist/pages/protocol.d.mts +2 -2
  77. package/dist/pages/protocol.mjs +2 -2
  78. package/dist/platform-args-C7XUw9mw.mjs +1 -0
  79. package/dist/{platform-args-Bv9hoLEL.mjs → platform-args-Cx1M-7bq.mjs} +18 -4
  80. package/dist/{platform-auth-config-CLj0EvnB.mjs → platform-auth-config-K4m_LE8b.mjs} +4 -4
  81. package/dist/{platform-auth-protection-DQDSxVhr.mjs → platform-auth-protection-KF5Vo-1I.mjs} +2 -2
  82. package/dist/{platform-auth-recovery-DD3oLerq.mjs → platform-auth-recovery-BOdFrl7R.mjs} +2 -2
  83. package/dist/{platform-cmd-WnOmdLzR.mjs → platform-cmd-DpCHawGF.mjs} +1 -1
  84. package/dist/{platform-cmd-PLNLg-2N.mjs → platform-cmd-Ds3EAV5t.mjs} +5 -4
  85. package/dist/{platform-domain-Bnq0vg0q.mjs → platform-domain-rWJshybd.mjs} +2 -2
  86. package/dist/{platform-lifecycle-BQiN9HN5.mjs → platform-lifecycle-BH34I0rh.mjs} +1 -1
  87. package/dist/{platform-lifecycle-BCsFg1Tl.mjs → platform-lifecycle-D9RdGub1.mjs} +66 -31
  88. package/dist/{platform-management-nzCDwtiN.mjs → platform-management-BRefdHGD.mjs} +21 -3
  89. package/dist/{platform-management-CMJR8Zi-.mjs → platform-management-BfLW0Zlo.mjs} +1 -1
  90. package/dist/{platform-recovery-DBNoME3I.mjs → platform-recovery-DZuZGJtt.mjs} +2 -2
  91. package/dist/platform-runtime-logs-CdJGnk3f.mjs +68 -0
  92. package/dist/{platform-setup-VEcHu0Vq.mjs → platform-setup-Vw1AaUN8.mjs} +15 -2
  93. package/dist/{plugin-inference-DmwjC3y2.mjs → plugin-inference-CdLbxZL_.mjs} +1 -1
  94. package/dist/{prefetch-L93_1Jk7.mjs → prefetch-Dx2jIbtg.mjs} +19 -1
  95. package/dist/{prefetch-CG0K6JPd.d.mts → prefetch-NdNeubbH.d.mts} +7 -1
  96. package/dist/{prepare-pfl2LIMy.mjs → prepare-BJW-f1f1.mjs} +4 -4
  97. package/dist/{prepare-BPsfLWaW.mjs → prepare-BMGeKL16.mjs} +1 -1
  98. package/dist/prepare-BdxJJCMv.mjs +1 -0
  99. package/dist/{project-cmd-5Izu4Pg3.mjs → project-cmd-B_bUyZ3J.mjs} +15 -15
  100. package/dist/{project-team-DSUyjZFh.mjs → project-team-MjT5K4rQ.mjs} +1 -1
  101. package/dist/{project-token-wj09YQSB.mjs → project-token-BKHGwY3k.mjs} +1 -1
  102. package/dist/{project-zero-trust-BMHf2YF0.mjs → project-zero-trust-Bzdd05-h.mjs} +1 -1
  103. package/dist/{protocol-0-lp9ZYx.mjs → protocol-COyySMki.mjs} +130 -107
  104. package/dist/{protocol-xdrl-In0.d.mts → protocol-CkwjASep.d.mts} +2 -2
  105. package/dist/{provision-CRiNnlfE.mjs → provision-BbCzpbXs.mjs} +1 -1
  106. package/dist/{provision-CxHIGBIZ.mjs → provision-DQF6Kw-6.mjs} +1 -1
  107. package/dist/{requests-DicgB5jU.mjs → requests-daJmVpjk.mjs} +1 -1
  108. package/dist/{rollback-DrDZOKNE.mjs → rollback-BgL0wY1J.mjs} +2 -2
  109. package/dist/{route-types-irZc1txT.mjs → route-types-CZc1YXjF.mjs} +40 -4
  110. package/dist/route-url-C_YIH_zO.d.mts +37 -0
  111. package/dist/routes-stub.d.mts +6 -2
  112. package/dist/runtime/better-auth-mysql.mjs +1 -1
  113. package/dist/runtime/better-auth-pg.mjs +1 -1
  114. package/dist/runtime/better-auth.mjs +1 -1
  115. package/dist/runtime/email/testing.d.mts +1 -1
  116. package/dist/runtime/email/testing.mjs +3 -3
  117. package/dist/runtime/email.d.mts +1 -1
  118. package/dist/runtime/email.mjs +3 -3
  119. package/dist/runtime/handler.d.mts +1 -1
  120. package/dist/runtime/handler.mjs +2 -2
  121. package/dist/runtime/live-client.d.mts +1 -1
  122. package/dist/runtime/live.d.mts +1 -1
  123. package/dist/runtime/route-url.d.mts +2 -2
  124. package/dist/runtime/routing.d.mts +3 -3
  125. package/dist/runtime/routing.mjs +37 -16
  126. package/dist/runtime/validator.d.mts +1 -1
  127. package/dist/runtime/ws-server.d.mts +1 -1
  128. package/dist/runtime/ws.d.mts +1 -1
  129. package/dist/{scan-DSQtH3oS.mjs → scan-X2rDDRNa.mjs} +18 -10
  130. package/dist/{secret-C_QtTzJG.mjs → secret-CPq-B_vp.mjs} +2 -2
  131. package/dist/{static-asset-marker-NR1XS2z5.mjs → static-asset-marker-DsQKz6lt.mjs} +8 -1
  132. package/dist/validation-error-uAAjlsiW.mjs +82 -0
  133. package/dist/{version-metadata-Blf5H9BQ.mjs → version-metadata-C8hLzQkv.mjs} +1 -1
  134. package/dist/{wrangler-p-kLCJqF.mjs → wrangler-DESSbN8L.mjs} +2 -2
  135. package/dist/wrangler-DUB3MGHV.mjs +1 -0
  136. package/dist/{wrapper-iejSJDwL.mjs → wrapper-C9SUxoj7.mjs} +2 -2
  137. package/dist/{ws-DzBD-NUj.d.mts → ws-Dmjg5NH2.d.mts} +1 -1
  138. package/docs/guide/edge/headers.md +2 -0
  139. package/docs/guide/edge/rewrites.md +1 -1
  140. package/docs/guide/email/receiving.md +1 -1
  141. package/docs/guide/email/sending.md +42 -12
  142. package/docs/guide/pages-routing/actions-and-forms.md +21 -1
  143. package/docs/guide/pages-routing/islands.md +3 -3
  144. package/docs/guide/pages-routing/layouts.md +16 -2
  145. package/docs/guide/pages-routing/loaders.md +2 -2
  146. package/docs/guide/pages-routing/markdown.md +1 -1
  147. package/docs/guide/pages-routing/overview.md +103 -0
  148. package/docs/guide/platform/administration/connected-apps.md +4 -2
  149. package/docs/guide/platform/installation/credentials.md +6 -4
  150. package/docs/guide/platform/installation/maintenance.md +13 -0
  151. package/docs/guide/platform/installation/setup.md +22 -1
  152. package/docs/guide/ssg.md +3 -3
  153. package/docs/guide/type-safety.md +42 -1
  154. package/docs/integrations/frameworks/astro.md +17 -12
  155. package/docs/integrations/frameworks/overview.md +2 -2
  156. package/docs/reference/api/pages.md +101 -50
  157. package/docs/reference/api/rewrites.md +4 -1
  158. package/docs/reference/api/types.md +14 -10
  159. package/docs/reference/api.md +1 -1
  160. package/docs/reference/cli/platform-installation.md +4 -1
  161. package/docs/reference/cli/setup.md +1 -1
  162. package/docs/reference/config.md +10 -0
  163. package/package.json +8 -7
  164. package/schema.json +4 -0
  165. package/skills/void/SKILL.md +5 -0
  166. package/dist/client-DVtAKeUm.mjs +0 -1
  167. package/dist/deploy-CfU7CR-b.mjs +0 -2
  168. package/dist/gen-i61rdQgg.mjs +0 -1
  169. package/dist/headers-DEUBAM6W.mjs +0 -225
  170. package/dist/help-BcZDUcTw.mjs +0 -1
  171. package/dist/login-C0MiQrYs.mjs +0 -1
  172. package/dist/migrate-BG09-KOv.mjs +0 -1
  173. package/dist/operator-cmd-BhHXIL2G.mjs +0 -1
  174. package/dist/platform-args-C1kqYUen.mjs +0 -1
  175. package/dist/prepare-BJMmfq3W.mjs +0 -1
  176. package/dist/route-url-BGOiyKNz.d.mts +0 -24
  177. package/dist/validation-error-rDVbUJet.mjs +0 -47
  178. package/dist/wrangler-CPRX2d9A.mjs +0 -1
@@ -1,7 +1,7 @@
1
1
  import { i as searchWithoutActionKey } from "../action-key-XtKKbbsi.mjs";
2
2
  import { t as ISLAND_FRONTMATTER_ID } from "../serialize-DUuYuaY3.mjs";
3
3
  import { r as toUrlString, t as interpolateRouteParams } from "../route-url-CRDF-I0I.mjs";
4
- import { n as parseCacheFor, t as PrefetchCache } from "../prefetch-L93_1Jk7.mjs";
4
+ import { r as parseCacheFor, t as PrefetchCache } from "../prefetch-Dx2jIbtg.mjs";
5
5
  import { readUsageLimit } from "@void/edge";
6
6
  // src/pages/action-result.ts
7
7
  var VoidActionError = class extends Error {
@@ -10,9 +10,9 @@ var VoidActionError = class extends Error {
10
10
  status;
11
11
  statusText;
12
12
  url;
13
- constructor({ body, status, statusText, url }) {
13
+ constructor({ body, message, status, statusText, url }) {
14
14
  const limit = status === 429 ? readUsageLimit(body) : null;
15
- super(limit?.message ?? (statusText ? `Action failed with ${status} ${statusText}` : `Action failed with ${status}`));
15
+ super(message ?? limit?.message ?? (statusText ? `Action failed with ${status} ${statusText}` : `Action failed with ${status}`));
16
16
  this.name = "VoidActionError";
17
17
  this.body = body;
18
18
  this.limit = limit;
@@ -52,9 +52,16 @@ async function readActionErrorBody(response) {
52
52
  return null;
53
53
  }
54
54
  }
55
+ function readActionErrorMessage(response, body) {
56
+ if (response.status < 400 || response.status >= 500) return;
57
+ if (!response.headers.get("content-type")?.includes("text/plain")) return;
58
+ return typeof body === "string" && body.trim() ? body : void 0;
59
+ }
55
60
  async function createActionErrorFromResponse(response, url) {
61
+ const body = await readActionErrorBody(response);
56
62
  return new VoidActionError({
57
- body: await readActionErrorBody(response),
63
+ body,
64
+ message: readActionErrorMessage(response, body),
58
65
  status: response.status,
59
66
  statusText: response.statusText,
60
67
  url
@@ -362,7 +369,7 @@ async function fetchPageResponse(fetchUrl, init) {
362
369
  }
363
370
  }
364
371
  async function createPageNavigationEngine(config) {
365
- const { adapter, components, layoutTree, routeMeta, staticPageData: STATIC_PAGE_DATA_ENABLED = false, staticPageDataFastPath: STATIC_PAGE_DATA_FAST_PATH = STATIC_PAGE_DATA_ENABLED, prefetchHoverDelay: PREFETCH_HOVER_DELAY, prefetchDefaultCacheFor: PREFETCH_DEFAULT_CACHE_FOR, cacheVisitedPageData: CACHE_VISITED_PAGE_DATA = false } = config;
372
+ const { adapter, components, layoutTree, routeMeta, staticPageData: STATIC_PAGE_DATA_ENABLED = false, staticPageDataFastPath: STATIC_PAGE_DATA_FAST_PATH = STATIC_PAGE_DATA_ENABLED, prefetchHoverDelay: PREFETCH_HOVER_DELAY, prefetchDefaultCacheFor: PREFETCH_DEFAULT_CACHE_FOR, prefetchDefaultStrategy: PREFETCH_DEFAULT_STRATEGY = false, cacheVisitedPageData: CACHE_VISITED_PAGE_DATA = false } = config;
366
373
  const matchRoute = (url) => config.matchRoute(decodePagePath(new URL(url, "http://x").pathname));
367
374
  const _prefetchCache = new PrefetchCache();
368
375
  let _nextNavigationId = 0;
@@ -659,6 +666,7 @@ async function createPageNavigationEngine(config) {
659
666
  url = fetchUrl + redirectUrl.hash;
660
667
  commitTarget.fetchUrl = fetchUrl;
661
668
  commitTarget.hash = redirectUrl.hash;
669
+ commitTarget.method = method;
662
670
  }
663
671
  if (response && method !== "GET" && !response.ok) throw await createActionErrorFromResponse(response, fetchUrl);
664
672
  if (response && !response.headers.get("X-VoidPages")) {
@@ -743,6 +751,7 @@ async function createPageNavigationEngine(config) {
743
751
  const result = await shell;
744
752
  if (!result) return false;
745
753
  if (resourceId < _urlNavigationId) return false;
754
+ const preserveScroll = options.preserveScroll ?? (commitTarget.method !== "GET" && commitTarget.fetchUrl === window.location.pathname + window.location.search);
746
755
  if (!options._popstate) {
747
756
  const historyUrl = commitTarget.fetchUrl + commitTarget.hash;
748
757
  const resource = getResource();
@@ -777,7 +786,7 @@ async function createPageNavigationEngine(config) {
777
786
  _currentFetchUrl = commitTarget.fetchUrl;
778
787
  scrollRestoration.commit();
779
788
  requestAnimationFrame(() => {
780
- if (!options.preserveScroll) restoreScroll(options._scrollPosition, commitTarget.hash);
789
+ if (!preserveScroll) restoreScroll(options._scrollPosition, commitTarget.hash);
781
790
  });
782
791
  return true;
783
792
  };
@@ -791,6 +800,7 @@ async function createPageNavigationEngine(config) {
791
800
  _htmlDeferredRegistry = initialDeferredRegistry;
792
801
  return {
793
802
  _hoverDelay: PREFETCH_HOVER_DELAY,
803
+ _prefetchDefaultStrategy: PREFETCH_DEFAULT_STRATEGY,
794
804
  initialPageData,
795
805
  initialComponent,
796
806
  initialLayouts,
@@ -834,7 +844,8 @@ async function createPageNavigationEngine(config) {
834
844
  const deferredRegistry = new DeferredRegistry();
835
845
  const commitTarget = {
836
846
  fetchUrl: effectiveFetchUrl,
837
- hash
847
+ hash,
848
+ method
838
849
  };
839
850
  const shell = loadNavigationShell(id, url, effectiveFetchUrl, method, options, controller.signal, commitTarget, deferredRegistry);
840
851
  let commitResource = async () => false;
@@ -971,6 +982,147 @@ function isModifiedEvent(e) {
971
982
  const target = e.currentTarget.getAttribute("target");
972
983
  return target !== null && target !== "_self" || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || e.button !== 0;
973
984
  }
985
+ // src/pages/link.ts
986
+ /** Normalizes the method and destination, rejecting options that only GET links support. */
987
+ function resolveLink(props) {
988
+ const method = (props.method ?? "GET").toUpperCase();
989
+ const isGet = method === "GET";
990
+ if (props.prefetch !== void 0 && props.prefetch !== false && !isGet) throw new Error("Link: prefetch only supports GET links. Remove `prefetch` or use method=\"GET\".");
991
+ if (props.reloadDocument && !isGet) throw new Error("Link: reloadDocument only supports GET links.");
992
+ return {
993
+ method,
994
+ isGet,
995
+ href: isGet ? mergeDataIntoHref(props.href, props.data) : props.href
996
+ };
997
+ }
998
+ /**
999
+ * The events that prefetch a Link. A Link without a `prefetch` prop uses the app's
1000
+ * `defaultStrategy`, but only when it is a GET link, the only kind that can prefetch.
1001
+ */
1002
+ function linkPrefetchStrategies(props, link, defaultStrategy = false) {
1003
+ if (props.reloadDocument) return [];
1004
+ const prefetch = props.prefetch ?? (link.isGet ? defaultStrategy : false);
1005
+ if (prefetch === false) return [];
1006
+ if (prefetch === true) return ["hover"];
1007
+ if (typeof prefetch === "string") return [prefetch];
1008
+ return prefetch;
1009
+ }
1010
+ /**
1011
+ * Marks a GET link to the current page. Pathnames compare the way the server
1012
+ * matches pages: case-sensitive, trailing slash significant, decoded like Hono's
1013
+ * getPath. The query and hash are ignored. `prefix` also matches nested pages by
1014
+ * whole segment, except for `/`, but only an exact match is `aria-current`.
1015
+ * Spread the result before the user's attributes so theirs win.
1016
+ */
1017
+ function linkActiveAttributes(router, link, match = "exact") {
1018
+ if (!router || !link.isGet) return {};
1019
+ const current = router.url;
1020
+ const target = resolveSameOrigin(link.href, current);
1021
+ if (!target) return {};
1022
+ const currentPath = decodePagePath(current.pathname);
1023
+ const linkPath = decodePagePath(target.pathname);
1024
+ if (linkPath === currentPath) return {
1025
+ "aria-current": "page",
1026
+ "data-void-active": ""
1027
+ };
1028
+ if (match === "prefix" && linkPath !== "/") {
1029
+ const parent = linkPath.endsWith("/") ? linkPath : `${linkPath}/`;
1030
+ if (currentPath.startsWith(parent)) return { "data-void-active": "" };
1031
+ }
1032
+ return {};
1033
+ }
1034
+ /**
1035
+ * Marks a link while a visit to its destination is loading: the visit uses the link's
1036
+ * method and goes to its path, query and hash. Unlike active links, the query counts,
1037
+ * so `?page=2` isn't pending during a visit to `?page=3`. Every link to the destination
1038
+ * is marked, not only the one that was clicked.
1039
+ * Spread the result before the user's attributes so theirs win.
1040
+ */
1041
+ function linkPendingAttributes(router, navigation, link) {
1042
+ if (!router || !navigation?.location || navigation.state === "idle" || navigation.method !== link.method) return {};
1043
+ const destination = navigation.location;
1044
+ const target = resolveSameOrigin(link.href, router.url);
1045
+ if (target && target.host === destination.host && target.protocol === destination.protocol && decodePagePath(target.pathname) === decodePagePath(destination.pathname) && target.search === destination.search && target.hash === destination.hash) return { "data-void-pending": "" };
1046
+ return {};
1047
+ }
1048
+ /** Resolves `href` against `base`, or returns `null` for a malformed or other-origin href. */
1049
+ function resolveSameOrigin(href, base) {
1050
+ let target;
1051
+ try {
1052
+ target = new URL(href, base);
1053
+ } catch {
1054
+ return null;
1055
+ }
1056
+ if (target.protocol !== base.protocol || target.host !== base.host) return null;
1057
+ return target;
1058
+ }
1059
+ /** Data saver is on, or the connection is 2G-class. Read per call: both change while the page is open. */
1060
+ function isConstrainedConnection() {
1061
+ const connection = navigator.connection;
1062
+ return connection?.saveData === true || connection?.effectiveType === "2g" || connection?.effectiveType === "slow-2g";
1063
+ }
1064
+ /** Link's automatic prefetch. `router.prefetch()` called directly is not limited by the connection. */
1065
+ function prefetchLink(router, href, cacheFor) {
1066
+ if (!router?.prefetch || isConstrainedConnection()) return;
1067
+ if (new URL(href, window.location.href).origin !== window.location.origin) return;
1068
+ if (href === window.location.pathname + window.location.search) return;
1069
+ const options = {};
1070
+ if (cacheFor !== void 0) options.cacheFor = cacheFor;
1071
+ router.prefetch(href, options);
1072
+ }
1073
+ /**
1074
+ * Runs a client-side visit for a plain same-origin click. Modified clicks, downloads,
1075
+ * cross-origin destinations and `reloadDocument` links are left to the browser.
1076
+ * Call it after the user's own click handler so its `preventDefault()` is respected.
1077
+ */
1078
+ function handleLinkClick(event, router, link, props) {
1079
+ if (props.reloadDocument || !router || event.defaultPrevented || isModifiedEvent(event)) return;
1080
+ if (event.currentTarget.hasAttribute("download")) return;
1081
+ if (new URL(link.href, window.location.href).origin !== window.location.origin) return;
1082
+ let navigatePrevented = false;
1083
+ props.onNavigate?.({ preventDefault() {
1084
+ navigatePrevented = true;
1085
+ } });
1086
+ event.preventDefault();
1087
+ if (navigatePrevented) return;
1088
+ router.visit(link.href, {
1089
+ method: link.method,
1090
+ data: link.isGet ? void 0 : props.data,
1091
+ preserveScroll: props.preserveScroll,
1092
+ preserveState: props.preserveState,
1093
+ replace: props.replace,
1094
+ viewTransition: props.viewTransition
1095
+ });
1096
+ }
1097
+ /**
1098
+ * Calls `onVisible` once, after `element` has stayed in the viewport for
1099
+ * `LINK_VISIBLE_PREFETCH_DELAY` ms. Leaving the viewport cancels the wait and entering
1100
+ * again restarts it. Returns a cleanup that also cancels a pending wait.
1101
+ */
1102
+ function observeLinkVisible(element, onVisible) {
1103
+ let timer;
1104
+ let done = false;
1105
+ const stop = () => {
1106
+ done = true;
1107
+ clearTimeout(timer);
1108
+ timer = void 0;
1109
+ observer.disconnect();
1110
+ };
1111
+ const observer = new IntersectionObserver((entries) => {
1112
+ if (done) return;
1113
+ if (entries.some((entry) => !entry.isIntersecting)) {
1114
+ clearTimeout(timer);
1115
+ timer = void 0;
1116
+ }
1117
+ if (!entries.at(-1)?.isIntersecting) return;
1118
+ timer ??= setTimeout(() => {
1119
+ stop();
1120
+ onVisible();
1121
+ }, 300);
1122
+ }, { rootMargin: "0px" });
1123
+ observer.observe(element);
1124
+ return stop;
1125
+ }
974
1126
  // src/pages/island-action.ts
975
1127
  async function submitIslandAction(url, method, data) {
976
1128
  const response = await fetch(url, {
@@ -1081,8 +1233,12 @@ function currentUrl() {
1081
1233
  function getUrlOrigin() {
1082
1234
  return typeof window === "undefined" ? "http://localhost" : window.location.origin;
1083
1235
  }
1236
+ /** Relative URLs such as `?page=2` resolve against the current page, as in an anchor. */
1237
+ function getUrlBase() {
1238
+ return typeof window === "undefined" ? "http://localhost/" : getUrlOrigin() + currentUrl();
1239
+ }
1084
1240
  function toUrl(value) {
1085
- return value instanceof URL ? new URL(value.href) : new URL(value || "/", getUrlOrigin());
1241
+ return value instanceof URL ? new URL(value.href) : new URL(value || "/", getUrlBase());
1086
1242
  }
1087
1243
  function normalizeUrl(value) {
1088
1244
  const urlString = toUrlString(value);
@@ -1115,8 +1271,11 @@ function createRouterFacade(router, getCurrentUrl, getCurrentParams = () => rout
1115
1271
  get _hoverDelay() {
1116
1272
  return router._hoverDelay;
1117
1273
  },
1274
+ get _prefetchDefaultStrategy() {
1275
+ return router._prefetchDefaultStrategy;
1276
+ },
1118
1277
  get url() {
1119
- return toUrl(getCurrentUrl());
1278
+ return new URL(getCurrentUrl() || "/", router.url);
1120
1279
  },
1121
1280
  get path() {
1122
1281
  return this.url.pathname;
@@ -1144,11 +1303,11 @@ function createRouterFacade(router, getCurrentUrl, getCurrentParams = () => rout
1144
1303
  }
1145
1304
  };
1146
1305
  }
1147
- function createSsrRouter(url = "/", params = {}) {
1306
+ function createSsrRouter(url = "/", params = {}, requestUrl) {
1148
1307
  return {
1149
1308
  _hoverDelay: 75,
1150
1309
  get url() {
1151
- return toUrl(url);
1310
+ return new URL(url || "/", requestUrl ?? getUrlBase());
1152
1311
  },
1153
1312
  get path() {
1154
1313
  return this.url.pathname;
@@ -1229,6 +1388,7 @@ function createActionClient(defaults = {}) {
1229
1388
  return submitAction(_router, resolvedUrl, {
1230
1389
  method,
1231
1390
  data,
1391
+ preserveScroll: options?.preserveScroll,
1232
1392
  ...defaults
1233
1393
  });
1234
1394
  }
@@ -1320,6 +1480,7 @@ async function createVoidRouter(config) {
1320
1480
  }
1321
1481
  const router = {
1322
1482
  _hoverDelay: engine._hoverDelay,
1483
+ _prefetchDefaultStrategy: engine._prefetchDefaultStrategy,
1323
1484
  get url() {
1324
1485
  return toUrl(routeUrl);
1325
1486
  },
@@ -1397,4 +1558,4 @@ function initIslands(islands, hydrateFn) {
1397
1558
  if (loader) scheduleHydration(el, loader, props, strategy, hydrate);
1398
1559
  });
1399
1560
  }
1400
- export { VoidActionError, categorizeActionError, cloneFormValue, createActionClient, createPageNavigationEngine, createRouterFacade, createSsrRouter, createVoidRouter, formErrorMessage, formRetryMessage, formSubmissionBlocked, getValidationErrors, hasFiles, idleNavigationState, initIslands, interpolateRouteParams, isAbortError, isCallSiteActionError, isEqualFormValue, isFile, isFormResetOptions, isModifiedEvent, mergeDataIntoHref, objectToFormData, parseFormResetArgs, refreshPage, scheduleFormRetry, scheduleHydration, ssrProxy, submitAction, submitIslandAction, toUrlString };
1561
+ export { VoidActionError, categorizeActionError, cloneFormValue, createActionClient, createPageNavigationEngine, createRouterFacade, createSsrRouter, createVoidRouter, formErrorMessage, formRetryMessage, formSubmissionBlocked, getValidationErrors, handleLinkClick, hasFiles, idleNavigationState, initIslands, interpolateRouteParams, isAbortError, isCallSiteActionError, isEqualFormValue, isFile, isFormResetOptions, isModifiedEvent, linkActiveAttributes, linkPendingAttributes, linkPrefetchStrategies, mergeDataIntoHref, objectToFormData, observeLinkVisible, parseFormResetArgs, prefetchLink, refreshPage, resolveLink, scheduleFormRetry, scheduleHydration, ssrProxy, submitAction, submitIslandAction, toUrlString };
@@ -1,6 +1,7 @@
1
1
  import { a as renderHtmlAttrs, i as renderHeadToString, n as HeadDescriptor, o as resolveHead, r as renderBodyAttrs, t as HeadConfig } from "../head-MeF_5M_3.mjs";
2
- import "../config-CzsNzGVH.mjs";
2
+ import "../config-CPxJmt4M.mjs";
3
3
  import { i as PageDefinition, n as LayoutDefinition, o as PageScanResult, r as NamedLayoutDefinition, s as RouteEnvironment } from "../types-agfx0alu.mjs";
4
+ import { a as LinkPrefetchValue, n as CacheForInput } from "../prefetch-NdNeubbH.mjs";
4
5
  import { r as serializePageData } from "../serialize-I4EmS-Wf.mjs";
5
6
  import { Plugin, UserConfig } from "vite";
6
7
  type ProjectPaths = {
@@ -72,7 +73,25 @@ type PagesPluginContext = {
72
73
  isDev: boolean;
73
74
  scan: () => Promise<PageScanResult | null>;
74
75
  };
76
+ /** The `prefetch` option of a framework adapter plugin. */
77
+ type PrefetchConfig = {
78
+ /** How a Link without its own `prefetch` prop prefetches. Accepts the same values. */
79
+ defaultStrategy?: LinkPrefetchValue;
80
+ hoverDelay?: number;
81
+ cacheFor?: CacheForInput;
82
+ };
83
+ /** Options of a framework adapter's pages plugin. */
75
84
  type PagesPluginOptions = {
85
+ root?: string;
86
+ viewTransitions?: boolean;
87
+ prefetch?: PrefetchConfig;
88
+ };
89
+ /**
90
+ * `prefetch` with its defaults: Links prefetch only when they ask to, with a 75 ms
91
+ * hover delay and a 30 second cache.
92
+ */
93
+ export declare function resolvePrefetchConfig(prefetch: PrefetchConfig | undefined): Required<PrefetchConfig>;
94
+ type CreatePagesPluginOptions = {
76
95
  name: string;
77
96
  root?: string;
78
97
  generateRenderer: (scan: PageScanResult, isDev: boolean) => string;
@@ -80,5 +99,5 @@ type PagesPluginOptions = {
80
99
  config: (context: PagesPluginContext) => UserConfig | void | Promise<UserConfig | void>;
81
100
  };
82
101
  /** Shared Vite lifecycle; adapters own their generated code and environment configuration. */
83
- export declare function createPagesPlugin(pagesDir: string, options: PagesPluginOptions): Plugin;
84
- export { type HeadConfig, type HeadDescriptor, type LayoutDefinition, type NamedLayoutDefinition, type PageDefinition, type PageScanResult, type StaticPageDataBuildOptions, renderBodyAttrs, renderHeadToString, renderHtmlAttrs, resolveHead, serializePageData };
102
+ export declare function createPagesPlugin(pagesDir: string, options: CreatePagesPluginOptions): Plugin;
103
+ export { type HeadConfig, type HeadDescriptor, type LayoutDefinition, type NamedLayoutDefinition, type PageDefinition, type PageScanResult, type PagesPluginOptions, type PrefetchConfig, type StaticPageDataBuildOptions, renderBodyAttrs, renderHeadToString, renderHtmlAttrs, resolveHead, serializePageData };
@@ -1,10 +1,11 @@
1
1
  import { a as join, n as dirname } from "../dist-DMYe8CZJ.mjs";
2
2
  import { r as resolvePagesDir } from "../project-paths-CdGv9Tmi.mjs";
3
- import { p as readConfig } from "../config-DkA9jYF2.mjs";
4
- import { a as resolveLayoutChain, i as resolveEffectiveLayoutChain, n as scanPages, r as generateComponentManifest, s as resolveNamedLayout, t as pageFileEnvironment } from "../scan-DSQtH3oS.mjs";
5
- import { r as parseRedirectsFile } from "../headers-DEUBAM6W.mjs";
3
+ import { p as readConfig } from "../config-BITKQ3O6.mjs";
4
+ import { a as resolveLayoutChain, i as resolveEffectiveLayoutChain, n as scanPages, r as generateComponentManifest, s as resolveNamedLayout, t as pageFileEnvironment } from "../scan-X2rDDRNa.mjs";
5
+ import { r as parseRedirectsFile } from "../headers-CuTEdP9C.mjs";
6
6
  import { i as resolveHead, n as renderHeadToString, r as renderHtmlAttrs, t as renderBodyAttrs } from "../head-5XPcnG-j.mjs";
7
7
  import { r as serializePageData } from "../serialize-DUuYuaY3.mjs";
8
+ import { n as assertLinkPrefetchValue } from "../prefetch-Dx2jIbtg.mjs";
8
9
  import { existsSync } from "node:fs";
9
10
  import { normalizePath } from "vite";
10
11
  // src/pages/static-data.ts
@@ -27,6 +28,19 @@ function resolveStaticPageDataBuildOptions(root, options = {}) {
27
28
  // src/pages/plugin.ts
28
29
  const RENDERER_ID = "virtual:void-pages-renderer";
29
30
  const CLIENT_ENTRY_ID = "virtual:void-pages-client";
31
+ /**
32
+ * `prefetch` with its defaults: Links prefetch only when they ask to, with a 75 ms
33
+ * hover delay and a 30 second cache.
34
+ */
35
+ function resolvePrefetchConfig(prefetch) {
36
+ const defaultStrategy = prefetch?.defaultStrategy ?? false;
37
+ assertLinkPrefetchValue(defaultStrategy, "prefetch.defaultStrategy");
38
+ return {
39
+ defaultStrategy,
40
+ hoverDelay: prefetch?.hoverDelay ?? 75,
41
+ cacheFor: prefetch?.cacheFor ?? "30s"
42
+ };
43
+ }
30
44
  /** Shared Vite lifecycle; adapters own their generated code and environment configuration. */
31
45
  function createPagesPlugin(pagesDir, options) {
32
46
  let pageScan = null;
@@ -112,4 +126,4 @@ function createPagesPlugin(pagesDir, options) {
112
126
  }
113
127
  };
114
128
  }
115
- export { createPagesPlugin, generateComponentManifest, pageFileEnvironment, renderBodyAttrs, renderHeadToString, renderHtmlAttrs, resolveEffectiveLayoutChain, resolveHead, resolveLayoutChain, resolveNamedLayout, resolvePagesDir, resolveStaticPageDataBuildOptions, scanPages, serializePageData };
129
+ export { createPagesPlugin, generateComponentManifest, pageFileEnvironment, renderBodyAttrs, renderHeadToString, renderHtmlAttrs, resolveEffectiveLayoutChain, resolveHead, resolveLayoutChain, resolveNamedLayout, resolvePagesDir, resolvePrefetchConfig, resolveStaticPageDataBuildOptions, scanPages, serializePageData };
@@ -1,5 +1,5 @@
1
1
  import { o as PageScanResult } from "../types-agfx0alu.mjs";
2
- import { Plugin, ResolvedConfig, ViteDevServer } from "vite";
2
+ import { Plugin, ResolvedConfig, UserConfig, ViteDevServer } from "vite";
3
3
  //#region src/SourceMap.d.ts
4
4
  interface SourceMapOptions {
5
5
  /**
@@ -306,14 +306,43 @@ export type IslandEntry = {
306
306
  islandId: string;
307
307
  hydrate: string;
308
308
  };
309
+ /** The pages whose Markdown script `@void/md` serves to the browser. */
310
+ export declare function mdClientScriptIds(resolvedConfig: ResolvedConfig | null): Set<string>;
309
311
  /** Resolve a relative import specifier to an absolute path by checking extensions. */
310
- export declare function resolveSpecifier(specifier: string, importerDir: string, extensions: Array<string>): string | null;
312
+ export declare function resolveSpecifier(specifier: string, importerDir: string, extensions: ReadonlyArray<string>): string | null;
313
+ /** The islands that island pages and layouts import, for the islands client entry. */
314
+ export type IslandManifest = {
315
+ readonly entries: Map<string, IslandEntry>;
316
+ /**
317
+ * Resolve an island import and record it. An import that does not resolve
318
+ * keeps its specifier as its id and is not recorded.
319
+ */
320
+ register(specifier: string, importerDir: string, hydrate: string): {
321
+ absPath: string | null;
322
+ islandId: string;
323
+ };
324
+ /**
325
+ * Record the island imports of every island page and layout. The client
326
+ * build may load the islands client entry before their transforms run.
327
+ */
328
+ registerPages(pageScan: PageScanResult, pagesDir: string): void;
329
+ };
330
+ /** `extensions` are tried, in order, for an island import without one. */
331
+ export declare function createIslandManifest(root: string, extensions: ReadonlyArray<string>): IslandManifest;
332
+ /**
333
+ * The island pages that match `componentPattern` and the layouts of every
334
+ * island page. Without a server build, the islands client entry imports their
335
+ * own styles and stylesheet imports.
336
+ */
337
+ export declare function islandCssFiles(pageScan: PageScanResult, pagesDir: string, componentPattern: RegExp): Set<string>;
311
338
  /**
312
339
  * Whether the app has island pages (`*.island.*` files or Markdown). Their
313
340
  * layouts can reach a stylesheet through a component, which only the module
314
341
  * graph shows, so the islands client entry is built for every such app.
315
342
  */
316
343
  export declare function hasIslandPages(root: string, pagesDir: string): Promise<boolean>;
344
+ /** Build config that adds the islands client entry to the client build of an app with island pages. */
345
+ export declare function islandsBuildConfig(root: string, pagesDir: string): Promise<UserConfig | undefined>;
317
346
  /**
318
347
  * The layouts of every island page, outermost first, then the pages. All
319
348
  * island pages share one production stylesheet, so layout styles come first.
@@ -357,6 +386,21 @@ export declare function islandStylesPlugin(pagesDir: string): Plugin;
357
386
  * virtual module. Absolute and bare specifiers are kept as-is.
358
387
  */
359
388
  export declare function rewriteCssImportForVirtualModule(matchedLine: string, specifier: string, sourceFilePath: string): string;
389
+ export type IslandClientEntryOptions = {
390
+ manifest: IslandManifest;
391
+ /** From `islandCssFiles`. */
392
+ cssFiles: ReadonlySet<string>;
393
+ /** From `recordedIslandStyles`. */
394
+ styles: ReadonlyArray<string> | null;
395
+ /** Whether a built page loads the entry for its islands. */
396
+ needsRuntime: boolean;
397
+ isDev: boolean;
398
+ /** Imports for the style blocks of a Vue or Svelte component. */
399
+ styleBlockImports?: (file: string, content: string) => Array<string>;
400
+ /** Source that defines `async function hydrateIsland(el, mod, props, frontmatter)`. */
401
+ hydrateIsland: string;
402
+ };
403
+ export declare function generateIslandClientEntry(options: IslandClientEntryOptions): string;
360
404
  /** Extract CSS import statements from a file and return them as @import rules. */
361
405
  export declare function extractCssImports(filePath: string): string;
362
406
  /** Compute which island pages need client-side JavaScript. */
@@ -1,5 +1,5 @@
1
- import { c as resolve, n as dirname } from "../dist-DMYe8CZJ.mjs";
2
- import { c as editIslandSource, i as resolveEffectiveLayoutChain, l as findIslandImports, n as scanPages, u as findStaticCssImports } from "../scan-DSQtH3oS.mjs";
1
+ import { c as resolve, n as dirname, s as relative } from "../dist-DMYe8CZJ.mjs";
2
+ import { c as editIslandSource, i as resolveEffectiveLayoutChain, l as findIslandImports, n as scanPages, u as findStaticCssImports } from "../scan-X2rDDRNa.mjs";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { isCSSRequest, normalizePath } from "vite";
5
5
  // src/pages/islands-plugin.ts
@@ -8,6 +8,11 @@ const ISLANDS_CLIENT_ENTRY_ID = "virtual:void-islands-client";
8
8
  function hasIslandImports(code, filename) {
9
9
  return findIslandImports(code, filename).length > 0;
10
10
  }
11
+ /** The pages whose Markdown script `@void/md` serves to the browser. */
12
+ function mdClientScriptIds(resolvedConfig) {
13
+ const mdPlugin = resolvedConfig?.plugins?.find((plugin) => plugin.name === "void-md");
14
+ return new Set(mdPlugin?.api?.getClientScripts?.()?.keys() ?? []);
15
+ }
11
16
  /** Resolve a relative import specifier to an absolute path by checking extensions. */
12
17
  function resolveSpecifier(specifier, importerDir, extensions) {
13
18
  const base = normalizePath(resolve(importerDir, specifier));
@@ -15,6 +20,61 @@ function resolveSpecifier(specifier, importerDir, extensions) {
15
20
  if (existsSync(base)) return base;
16
21
  return null;
17
22
  }
23
+ /** `extensions` are tried, in order, for an island import without one. */
24
+ function createIslandManifest(root, extensions) {
25
+ const entries = /* @__PURE__ */ new Map();
26
+ function register(specifier, importerDir, hydrate) {
27
+ const absPath = resolveSpecifier(specifier, importerDir, extensions);
28
+ if (!absPath) return {
29
+ absPath,
30
+ islandId: specifier
31
+ };
32
+ const islandId = normalizePath(relative(root, absPath)).replace(/\.[^.]+$/, "");
33
+ entries.set(islandId, {
34
+ componentPath: absPath,
35
+ islandId,
36
+ hydrate
37
+ });
38
+ return {
39
+ absPath,
40
+ islandId
41
+ };
42
+ }
43
+ return {
44
+ entries,
45
+ register,
46
+ registerPages(pageScan, pagesDir) {
47
+ const files = [
48
+ ...pageScan.pages.map((page) => ({
49
+ filePath: page.componentPath,
50
+ island: page.island
51
+ })),
52
+ ...pageScan.layouts,
53
+ ...pageScan.namedLayouts
54
+ ];
55
+ for (const entry of files) {
56
+ if (!entry.island) continue;
57
+ const filePath = resolve(pagesDir, entry.filePath);
58
+ const code = readFileSync(filePath, "utf-8");
59
+ for (const islandImport of findIslandImports(code, filePath)) register(islandImport.specifier, dirname(filePath), islandImport.hydrate);
60
+ }
61
+ }
62
+ };
63
+ }
64
+ /**
65
+ * The island pages that match `componentPattern` and the layouts of every
66
+ * island page. Without a server build, the islands client entry imports their
67
+ * own styles and stylesheet imports.
68
+ */
69
+ function islandCssFiles(pageScan, pagesDir, componentPattern) {
70
+ const files = /* @__PURE__ */ new Set();
71
+ for (const page of pageScan.pages) {
72
+ if (!page.island) continue;
73
+ if (componentPattern.test(page.componentPath)) files.add(normalizePath(resolve(pagesDir, page.componentPath)));
74
+ for (const layout of resolveEffectiveLayoutChain(page, pageScan.layouts, pageScan.namedLayouts)) files.add(normalizePath(resolve(pagesDir, layout.filePath)));
75
+ }
76
+ return files;
77
+ }
18
78
  /**
19
79
  * Whether the app has island pages (`*.island.*` files or Markdown). Their
20
80
  * layouts can reach a stylesheet through a component, which only the module
@@ -31,6 +91,14 @@ async function hasIslandPages(root, pagesDir) {
31
91
  return false;
32
92
  }
33
93
  }
94
+ /** Build config that adds the islands client entry to the client build of an app with island pages. */
95
+ async function islandsBuildConfig(root, pagesDir) {
96
+ if (!await hasIslandPages(root, pagesDir)) return;
97
+ return { environments: { client: { build: {
98
+ manifest: true,
99
+ rollupOptions: { input: { "islands-client": ISLANDS_CLIENT_ENTRY_ID } }
100
+ } } } };
101
+ }
34
102
  /**
35
103
  * The layouts of every island page, outermost first, then the pages. All
36
104
  * island pages share one production stylesheet, so layout styles come first.
@@ -131,6 +199,25 @@ function rewriteCssImportForVirtualModule(matchedLine, specifier, sourceFilePath
131
199
  const statement = matchedLine.trim();
132
200
  return statement.endsWith(";") ? statement : statement + ";";
133
201
  }
202
+ function generateIslandClientEntry(options) {
203
+ const uniqueIslands = /* @__PURE__ */ new Map();
204
+ for (const entry of options.manifest.entries.values()) uniqueIslands.set(entry.islandId, entry.componentPath);
205
+ const lines = [];
206
+ if (!options.isDev && options.styles) for (const id of options.styles) lines.push(`import ${JSON.stringify(id)};`);
207
+ else if (!options.isDev) for (const file of options.cssFiles) {
208
+ const content = readFileSync(file, "utf-8");
209
+ lines.push(...options.styleBlockImports?.(file, content) ?? []);
210
+ for (const item of findStaticCssImports(content, file)) lines.push(rewriteCssImportForVirtualModule(item.statement, item.specifier, file));
211
+ }
212
+ if (!options.needsRuntime && !options.isDev) return lines.join("\n");
213
+ lines.push("import { initIslands } from \"void/pages-client\";");
214
+ lines.push("const islands = {");
215
+ for (const [id, path] of uniqueIslands) lines.push(` ${JSON.stringify(id)}: () => import(${JSON.stringify(path)}),`);
216
+ lines.push("};");
217
+ lines.push(options.hydrateIsland);
218
+ lines.push("initIslands(islands, hydrateIsland);");
219
+ return lines.join("\n");
220
+ }
134
221
  /** Extract CSS import statements from a file and return them as @import rules. */
135
222
  function extractCssImports(filePath) {
136
223
  const code = readFileSync(filePath, "utf-8");
@@ -138,8 +225,7 @@ function extractCssImports(filePath) {
138
225
  }
139
226
  /** Compute which island pages need client-side JavaScript. */
140
227
  function computePagesNeedingClientJS(pageScan, pagesDir, resolvedConfig) {
141
- const mdPlugin = resolvedConfig?.plugins?.find((plugin) => plugin.name === "void-md");
142
- const mdScriptIds = new Set(mdPlugin?.api?.getClientScripts?.()?.keys() ?? []);
228
+ const mdScriptIds = mdClientScriptIds(resolvedConfig);
143
229
  const layoutDirsWithIslands = /* @__PURE__ */ new Set();
144
230
  for (const layout of pageScan.layouts) {
145
231
  if (!layout.island) continue;
@@ -205,4 +291,4 @@ function watchPagesForRescan(server, pagesDir, root, onRescan) {
205
291
  });
206
292
  return { flush: () => pending ?? Promise.resolve() };
207
293
  }
208
- export { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, collectIslandStyles, computePagesNeedingClientJS, editIslandSource, extractCssImports, findIslandImports, findStaticCssImports, hasIslandPages, islandStyleRoots, islandStylesPlugin, recordIslandStyles, recordedIslandStyles, resolveSpecifier, rewriteCssImportForVirtualModule, watchPagesForRescan };
294
+ export { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, collectIslandStyles, computePagesNeedingClientJS, createIslandManifest, editIslandSource, extractCssImports, findIslandImports, findStaticCssImports, generateIslandClientEntry, hasIslandPages, islandCssFiles, islandStyleRoots, islandStylesPlugin, islandsBuildConfig, mdClientScriptIds, recordIslandStyles, recordedIslandStyles, resolveSpecifier, rewriteCssImportForVirtualModule, watchPagesForRescan };
@@ -1,2 +1,2 @@
1
- import { a as parseCacheFor, i as PrefetchCache, n as CacheForInput, r as CacheForResult, t as CacheEntry } from "../prefetch-CG0K6JPd.mjs";
2
- export { CacheEntry, CacheForInput, CacheForResult, PrefetchCache, parseCacheFor };
1
+ import { a as LinkPrefetchValue, c as parseCacheFor, i as LinkPrefetchStrategy, n as CacheForInput, o as PrefetchCache, r as CacheForResult, s as assertLinkPrefetchValue, t as CacheEntry } from "../prefetch-NdNeubbH.mjs";
2
+ export { CacheEntry, CacheForInput, CacheForResult, LinkPrefetchStrategy, LinkPrefetchValue, PrefetchCache, assertLinkPrefetchValue, parseCacheFor };
@@ -1 +1 @@
1
- export { t as PrefetchCache, n as parseCacheFor } from "../prefetch-L93_1Jk7.mjs";
1
+ export { t as PrefetchCache, n as assertLinkPrefetchValue, r as parseCacheFor } from "../prefetch-Dx2jIbtg.mjs";
@@ -1,3 +1,3 @@
1
- import { w as ValidationError } from "../handler-Drm_lyrV.mjs";
2
- import { a as handlePageMutation, i as handlePageGet, n as PageRenderResult, o as safeJsonStringify, r as flattenValidationErrors, t as PageObject } from "../protocol-xdrl-In0.mjs";
1
+ import { w as ValidationError } from "../handler-BR6Tvjkp.mjs";
2
+ import { a as handlePageMutation, i as handlePageGet, n as PageRenderResult, o as safeJsonStringify, r as flattenValidationErrors, t as PageObject } from "../protocol-CkwjASep.mjs";
3
3
  export { PageObject, PageRenderResult, ValidationError, flattenValidationErrors, handlePageGet, handlePageMutation, safeJsonStringify };
@@ -1,3 +1,3 @@
1
- import { t as ValidationError } from "../validation-error-rDVbUJet.mjs";
2
- import { i as safeJsonStringify, n as handlePageGet, r as handlePageMutation, t as flattenValidationErrors } from "../protocol-0-lp9ZYx.mjs";
1
+ import { t as ValidationError } from "../validation-error-uAAjlsiW.mjs";
2
+ import { i as safeJsonStringify, n as handlePageGet, r as handlePageMutation, t as flattenValidationErrors } from "../protocol-COyySMki.mjs";
3
3
  export { ValidationError, flattenValidationErrors, handlePageGet, handlePageMutation, safeJsonStringify };
@@ -0,0 +1 @@
1
+ export { t as parsePlatformArgs } from "./platform-args-Cx1M-7bq.mjs";