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.
- package/dist/{account-cmd-ChVKPZ-w.mjs → account-cmd-DRhmCWcm.mjs} +3 -3
- package/dist/{application-analysis-2vGZrKzg.mjs → application-analysis-HRFsZOv3.mjs} +3 -3
- package/dist/{application-routing-DnUQoOJD.mjs → application-routing-P4TggvWp.mjs} +1 -1
- package/dist/{auth-link-D4uMdOsw.mjs → auth-link-CVXP1G6G.mjs} +3 -3
- package/dist/{auth-router-62dna450.mjs → auth-router-D1pGdbvB.mjs} +2 -2
- package/dist/{better-auth-shared-CiclgPde.mjs → better-auth-shared-C958aM7W.mjs} +28 -3
- package/dist/{build-cmd-D93K_4Sa.mjs → build-cmd-DmjbuT6d.mjs} +1 -1
- package/dist/{cache-CiIzEED4.mjs → cache-2khBO1MR.mjs} +1 -1
- package/dist/{cancel-deploy-BNoGfn2e.mjs → cancel-deploy-_itbbvY3.mjs} +1 -1
- package/dist/cli/cli.mjs +27 -27
- package/dist/client-CEtr5UOR.mjs +1 -0
- package/dist/{client-Qayy9xEh.mjs → client-CoxPEhJq.mjs} +6 -2
- package/dist/{cloudflare-cmd-DiFGaLow.mjs → cloudflare-cmd-fZ6AMSWk.mjs} +1 -1
- package/dist/{cloudflare-config-Chet188_.mjs → cloudflare-config-eQunqX6A.mjs} +1 -1
- package/dist/{cloudflare-connect-BdgAQBc7.mjs → cloudflare-connect-9OZe4XVQ.mjs} +1 -1
- package/dist/{cloudflare-operations-CWA24vCh.mjs → cloudflare-operations-CrskD4_O.mjs} +4 -4
- package/dist/{cloudflare-operations-D8MoezuN.mjs → cloudflare-operations-oLvAd9Nd.mjs} +1 -1
- package/dist/{config-Bt4rw93x.mjs → config-BBlVXZCU.mjs} +1 -1
- package/dist/{config-DkA9jYF2.mjs → config-BITKQ3O6.mjs} +2 -1
- package/dist/{config-BszFVhpk.mjs → config-BgysUhnm.mjs} +1 -1
- package/dist/{config-CzsNzGVH.d.mts → config-CPxJmt4M.d.mts} +2 -0
- package/dist/config-entry.d.mts +1 -1
- package/dist/{connect-b9mJbb6X.mjs → connect-CKvmkl2R.mjs} +4 -4
- package/dist/{create-project-rnjum4_V.mjs → create-project-BiMmR5wA.mjs} +1 -1
- package/dist/{create-project-eomwm0wa.mjs → create-project-Bq0M8MtO.mjs} +1 -1
- package/dist/{db-UPWmiSk1.mjs → db-cq5RW4Vy.mjs} +17 -17
- package/dist/{delete-BeeYi1WO.mjs → delete-CY4L8fi2.mjs} +1 -1
- package/dist/deploy-B029oHlE.mjs +2 -0
- package/dist/{deploy-CwmCcTlq.mjs → deploy-D_gEojD2.mjs} +79 -42
- package/dist/{deploy-managed-static-gPDxyp3v.mjs → deploy-managed-static-BHVbB_-A.mjs} +11 -11
- package/dist/{deploy-transport-L6zzPE9T.mjs → deploy-transport-DEyOUfac.mjs} +2 -2
- package/dist/deploy.mjs +5 -4
- package/dist/{dev-inbox-CwH5VuZj.mjs → dev-inbox-CUUS6iUg.mjs} +1 -1
- package/dist/{dispatch-CiJ1Xrba.mjs → dispatch-BNLB5_Gm.mjs} +1 -1
- package/dist/{domain-3xdH9p2L.mjs → domain-BlH90Ods.mjs} +2 -2
- package/dist/{email-Du3YRnM5.mjs → email-Bkq8ZL1p.mjs} +6 -6
- package/dist/{email-cloudflare-CtD8qE13.mjs → email-cloudflare-MvJIxrwx.mjs} +5 -107
- package/dist/{env-Cnvp0pGy.mjs → env-AvuCSeCH.mjs} +2 -2
- package/dist/{gen-BvVmpZ5f.mjs → gen-BD-dfjxY.mjs} +3 -3
- package/dist/gen-CIHXh9I8.mjs +1 -0
- package/dist/{github-cmd-X2GKiLi-.mjs → github-cmd-CA-lBdkn.mjs} +1 -1
- package/dist/{handler-Drm_lyrV.d.mts → handler-BR6Tvjkp.d.mts} +8 -3
- package/dist/{handler-xmzz0U38.mjs → handler-CKeTtYID.mjs} +2 -12
- package/dist/headers-CuTEdP9C.mjs +445 -0
- package/dist/{help-EWDNxST-.mjs → help-1OCFDsRr.mjs} +9 -1
- package/dist/help-Ba8Dq5-r.mjs +1 -0
- package/dist/{inbound-DSKSHDUj.d.mts → inbound-BHt4dIlL.d.mts} +16 -5
- package/dist/{inbound-DQfekMtz.mjs → inbound-CdO-qY0w.mjs} +2 -76
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +65 -37
- package/dist/{info-jW8_d3VI.mjs → info-CJxua_db.mjs} +4 -4
- package/dist/{init-jDQ5G9QC.mjs → init-B-4CluYx.mjs} +12 -11
- package/dist/{isr-config-DhBkzCdY.mjs → isr-config-Czzb4mIq.mjs} +1 -47
- package/dist/{link-DOyxX5Xs.mjs → link-DAXOT-G_.mjs} +2 -2
- package/dist/{list-C9pTYDtl.mjs → list-BYSOo9fm.mjs} +2 -2
- package/dist/{live-CDBHyYiR.d.mts → live-0xrcVpyq.d.mts} +1 -1
- package/dist/{local-d1-CKfek6Z4.mjs → local-d1-BLgsNUj9.mjs} +1 -1
- package/dist/login-BpbYeu-n.mjs +1 -0
- package/dist/{login-koQbjzPd.mjs → login-CJopTWX4.mjs} +2 -2
- package/dist/{logs-cTU5TsRW.mjs → logs-DE0weBsR.mjs} +13 -11
- package/dist/{migrate-C62pGTfr.mjs → migrate-ChHHZBTi.mjs} +3 -3
- package/dist/migrate-HdA6Kh2p.mjs +1 -0
- package/dist/{mime-DKkO7ZtA.mjs → mime-CtP6-qAF.mjs} +143 -15
- package/dist/nitro.mjs +3 -3
- package/dist/{node-BfAt2rz8.mjs → node-CZ6lGNLn.mjs} +1 -1
- package/dist/{operator-cmd-CnAvSqJ5.mjs → operator-cmd-BUqsdk2K.mjs} +28 -10
- package/dist/operator-cmd-wBYyp8iQ.mjs +1 -0
- package/dist/pages/client.d.mts +143 -11
- package/dist/pages/client.mjs +173 -12
- package/dist/pages/index.d.mts +22 -3
- package/dist/pages/index.mjs +18 -4
- package/dist/pages/islands-plugin.d.mts +46 -2
- package/dist/pages/islands-plugin.mjs +91 -5
- package/dist/pages/prefetch.d.mts +2 -2
- package/dist/pages/prefetch.mjs +1 -1
- package/dist/pages/protocol.d.mts +2 -2
- package/dist/pages/protocol.mjs +2 -2
- package/dist/platform-args-C7XUw9mw.mjs +1 -0
- package/dist/{platform-args-Bv9hoLEL.mjs → platform-args-Cx1M-7bq.mjs} +18 -4
- package/dist/{platform-auth-config-CLj0EvnB.mjs → platform-auth-config-K4m_LE8b.mjs} +4 -4
- package/dist/{platform-auth-protection-DQDSxVhr.mjs → platform-auth-protection-KF5Vo-1I.mjs} +2 -2
- package/dist/{platform-auth-recovery-DD3oLerq.mjs → platform-auth-recovery-BOdFrl7R.mjs} +2 -2
- package/dist/{platform-cmd-WnOmdLzR.mjs → platform-cmd-DpCHawGF.mjs} +1 -1
- package/dist/{platform-cmd-PLNLg-2N.mjs → platform-cmd-Ds3EAV5t.mjs} +5 -4
- package/dist/{platform-domain-Bnq0vg0q.mjs → platform-domain-rWJshybd.mjs} +2 -2
- package/dist/{platform-lifecycle-BQiN9HN5.mjs → platform-lifecycle-BH34I0rh.mjs} +1 -1
- package/dist/{platform-lifecycle-BCsFg1Tl.mjs → platform-lifecycle-D9RdGub1.mjs} +66 -31
- package/dist/{platform-management-nzCDwtiN.mjs → platform-management-BRefdHGD.mjs} +21 -3
- package/dist/{platform-management-CMJR8Zi-.mjs → platform-management-BfLW0Zlo.mjs} +1 -1
- package/dist/{platform-recovery-DBNoME3I.mjs → platform-recovery-DZuZGJtt.mjs} +2 -2
- package/dist/platform-runtime-logs-CdJGnk3f.mjs +68 -0
- package/dist/{platform-setup-VEcHu0Vq.mjs → platform-setup-Vw1AaUN8.mjs} +15 -2
- package/dist/{plugin-inference-DmwjC3y2.mjs → plugin-inference-CdLbxZL_.mjs} +1 -1
- package/dist/{prefetch-L93_1Jk7.mjs → prefetch-Dx2jIbtg.mjs} +19 -1
- package/dist/{prefetch-CG0K6JPd.d.mts → prefetch-NdNeubbH.d.mts} +7 -1
- package/dist/{prepare-pfl2LIMy.mjs → prepare-BJW-f1f1.mjs} +4 -4
- package/dist/{prepare-BPsfLWaW.mjs → prepare-BMGeKL16.mjs} +1 -1
- package/dist/prepare-BdxJJCMv.mjs +1 -0
- package/dist/{project-cmd-5Izu4Pg3.mjs → project-cmd-B_bUyZ3J.mjs} +15 -15
- package/dist/{project-team-DSUyjZFh.mjs → project-team-MjT5K4rQ.mjs} +1 -1
- package/dist/{project-token-wj09YQSB.mjs → project-token-BKHGwY3k.mjs} +1 -1
- package/dist/{project-zero-trust-BMHf2YF0.mjs → project-zero-trust-Bzdd05-h.mjs} +1 -1
- package/dist/{protocol-0-lp9ZYx.mjs → protocol-COyySMki.mjs} +130 -107
- package/dist/{protocol-xdrl-In0.d.mts → protocol-CkwjASep.d.mts} +2 -2
- package/dist/{provision-CRiNnlfE.mjs → provision-BbCzpbXs.mjs} +1 -1
- package/dist/{provision-CxHIGBIZ.mjs → provision-DQF6Kw-6.mjs} +1 -1
- package/dist/{requests-DicgB5jU.mjs → requests-daJmVpjk.mjs} +1 -1
- package/dist/{rollback-DrDZOKNE.mjs → rollback-BgL0wY1J.mjs} +2 -2
- package/dist/{route-types-irZc1txT.mjs → route-types-CZc1YXjF.mjs} +40 -4
- package/dist/route-url-C_YIH_zO.d.mts +37 -0
- package/dist/routes-stub.d.mts +6 -2
- package/dist/runtime/better-auth-mysql.mjs +1 -1
- package/dist/runtime/better-auth-pg.mjs +1 -1
- package/dist/runtime/better-auth.mjs +1 -1
- package/dist/runtime/email/testing.d.mts +1 -1
- package/dist/runtime/email/testing.mjs +3 -3
- package/dist/runtime/email.d.mts +1 -1
- package/dist/runtime/email.mjs +3 -3
- package/dist/runtime/handler.d.mts +1 -1
- package/dist/runtime/handler.mjs +2 -2
- package/dist/runtime/live-client.d.mts +1 -1
- package/dist/runtime/live.d.mts +1 -1
- package/dist/runtime/route-url.d.mts +2 -2
- package/dist/runtime/routing.d.mts +3 -3
- package/dist/runtime/routing.mjs +37 -16
- package/dist/runtime/validator.d.mts +1 -1
- package/dist/runtime/ws-server.d.mts +1 -1
- package/dist/runtime/ws.d.mts +1 -1
- package/dist/{scan-DSQtH3oS.mjs → scan-X2rDDRNa.mjs} +18 -10
- package/dist/{secret-C_QtTzJG.mjs → secret-CPq-B_vp.mjs} +2 -2
- package/dist/{static-asset-marker-NR1XS2z5.mjs → static-asset-marker-DsQKz6lt.mjs} +8 -1
- package/dist/validation-error-uAAjlsiW.mjs +82 -0
- package/dist/{version-metadata-Blf5H9BQ.mjs → version-metadata-C8hLzQkv.mjs} +1 -1
- package/dist/{wrangler-p-kLCJqF.mjs → wrangler-DESSbN8L.mjs} +2 -2
- package/dist/wrangler-DUB3MGHV.mjs +1 -0
- package/dist/{wrapper-iejSJDwL.mjs → wrapper-C9SUxoj7.mjs} +2 -2
- package/dist/{ws-DzBD-NUj.d.mts → ws-Dmjg5NH2.d.mts} +1 -1
- package/docs/guide/edge/headers.md +2 -0
- package/docs/guide/edge/rewrites.md +1 -1
- package/docs/guide/email/receiving.md +1 -1
- package/docs/guide/email/sending.md +42 -12
- package/docs/guide/pages-routing/actions-and-forms.md +21 -1
- package/docs/guide/pages-routing/islands.md +3 -3
- package/docs/guide/pages-routing/layouts.md +16 -2
- package/docs/guide/pages-routing/loaders.md +2 -2
- package/docs/guide/pages-routing/markdown.md +1 -1
- package/docs/guide/pages-routing/overview.md +103 -0
- package/docs/guide/platform/administration/connected-apps.md +4 -2
- package/docs/guide/platform/installation/credentials.md +6 -4
- package/docs/guide/platform/installation/maintenance.md +13 -0
- package/docs/guide/platform/installation/setup.md +22 -1
- package/docs/guide/ssg.md +3 -3
- package/docs/guide/type-safety.md +42 -1
- package/docs/integrations/frameworks/astro.md +17 -12
- package/docs/integrations/frameworks/overview.md +2 -2
- package/docs/reference/api/pages.md +101 -50
- package/docs/reference/api/rewrites.md +4 -1
- package/docs/reference/api/types.md +14 -10
- package/docs/reference/api.md +1 -1
- package/docs/reference/cli/platform-installation.md +4 -1
- package/docs/reference/cli/setup.md +1 -1
- package/docs/reference/config.md +10 -0
- package/package.json +8 -7
- package/schema.json +4 -0
- package/skills/void/SKILL.md +5 -0
- package/dist/client-DVtAKeUm.mjs +0 -1
- package/dist/deploy-CfU7CR-b.mjs +0 -2
- package/dist/gen-i61rdQgg.mjs +0 -1
- package/dist/headers-DEUBAM6W.mjs +0 -225
- package/dist/help-BcZDUcTw.mjs +0 -1
- package/dist/login-C0MiQrYs.mjs +0 -1
- package/dist/migrate-BG09-KOv.mjs +0 -1
- package/dist/operator-cmd-BhHXIL2G.mjs +0 -1
- package/dist/platform-args-C1kqYUen.mjs +0 -1
- package/dist/prepare-BJMmfq3W.mjs +0 -1
- package/dist/route-url-BGOiyKNz.d.mts +0 -24
- package/dist/validation-error-rDVbUJet.mjs +0 -47
- package/dist/wrangler-CPRX2d9A.mjs +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as filterInternalBindings, M as createClientAssetTags, N as createManifestLoader, O as createAssetFallback, j as ClientAssetTags, k as createWorkerFetch } from "../handler-
|
|
2
|
-
import { a as handlePageMutation, i as handlePageGet } from "../protocol-
|
|
1
|
+
import { A as filterInternalBindings, M as createClientAssetTags, N as createManifestLoader, O as createAssetFallback, j as ClientAssetTags, k as createWorkerFetch } from "../handler-BR6Tvjkp.mjs";
|
|
2
|
+
import { a as handlePageMutation, i as handlePageGet } from "../protocol-CkwjASep.mjs";
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
4
|
import { Context, Hono, MiddlewareHandler, TypedResponse } from "hono";
|
|
5
5
|
import { RouteDestination, RouteUrlInput } from "void/routes";
|
|
@@ -100,7 +100,7 @@ export type RoutingRuntime = {
|
|
|
100
100
|
contextMiddleware: MiddlewareHandler;
|
|
101
101
|
requestMiddleware: MiddlewareHandler;
|
|
102
102
|
routingMiddleware: (rules: Array<RoutingRule>) => MiddlewareHandler;
|
|
103
|
-
headerMiddleware: (rules: Array<RoutingHeaderRule
|
|
103
|
+
headerMiddleware: (rules: Array<RoutingHeaderRule>, phase?: "auth-error") => MiddlewareHandler;
|
|
104
104
|
applyFallback: (context: Context, rules: Array<RoutingRule>) => Promise<Response | undefined>;
|
|
105
105
|
markNoMatch: (request: Request, response: Response) => Response;
|
|
106
106
|
};
|
package/dist/runtime/routing.mjs
CHANGED
|
@@ -2,10 +2,10 @@ import { n as createClientAssetTags, r as createManifestLoader, t as ASSET_EXTEN
|
|
|
2
2
|
import { t as renderDefaultErrorPage } from "../error-pages-COVBnzK7.mjs";
|
|
3
3
|
import { t as convertReturnValue } from "../response-HmNjB30w.mjs";
|
|
4
4
|
import { i as searchWithoutActionKey, n as hasActionKey, r as markActionKey, t as actionKeySegment } from "../action-key-XtKKbbsi.mjs";
|
|
5
|
-
import { n as handlePageGet, r as handlePageMutation } from "../protocol-
|
|
6
|
-
import { t as VoidAssetRewriteError } from "../handler-
|
|
5
|
+
import { n as handlePageGet, r as handlePageMutation } from "../protocol-COyySMki.mjs";
|
|
6
|
+
import { t as VoidAssetRewriteError } from "../handler-CKeTtYID.mjs";
|
|
7
|
+
import { i as authDependentRequests, n as markStaticAsset, r as SHARED_CACHE_HEADERS, t as isUnchangedStaticAsset } from "../static-asset-marker-DsQKz6lt.mjs";
|
|
7
8
|
import { r as toUrlString } from "../route-url-CRDF-I0I.mjs";
|
|
8
|
-
import { n as markStaticAsset } from "../static-asset-marker-NR1XS2z5.mjs";
|
|
9
9
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
10
|
import { compilePattern, matchPattern, matchPatternCaptures, mergeQueryIntoLocation, redirectToHttps, substituteParams } from "@void/edge";
|
|
11
11
|
// src/runtime/routing-worker.ts
|
|
@@ -747,6 +747,8 @@ function createErrorRenderer(options) {
|
|
|
747
747
|
function createRoutingRuntime(app, options = {}) {
|
|
748
748
|
const originalUrls = /* @__PURE__ */ new WeakMap();
|
|
749
749
|
const fallbackHandled = /* @__PURE__ */ new WeakSet();
|
|
750
|
+
const fallbackServed = /* @__PURE__ */ new WeakSet();
|
|
751
|
+
const headerApplied = /* @__PURE__ */ new WeakSet();
|
|
750
752
|
const warnedPatterns = /* @__PURE__ */ new Set();
|
|
751
753
|
const requestContext = new AsyncLocalStorage();
|
|
752
754
|
const assetExtensions = options.blockedAssetExtensions ?? ASSET_EXTENSIONS;
|
|
@@ -761,7 +763,11 @@ function createRoutingRuntime(app, options = {}) {
|
|
|
761
763
|
headers
|
|
762
764
|
});
|
|
763
765
|
};
|
|
764
|
-
const dispatch = (request, c
|
|
766
|
+
const dispatch = async (request, c, trace) => {
|
|
767
|
+
const response = annotate(await (options.nodeTarget ? app.fetch(request, c.env) : app.fetch(request, c.env, c.executionCtx)), trace);
|
|
768
|
+
if (authDependentRequests.has(request)) authDependentRequests.add(c.req.raw);
|
|
769
|
+
return response;
|
|
770
|
+
};
|
|
765
771
|
const rememberOriginal = (request, c) => {
|
|
766
772
|
originalUrls.set(request, originalUrls.get(c.req.raw) ?? new URL(c.req.url));
|
|
767
773
|
};
|
|
@@ -793,10 +799,12 @@ function createRoutingRuntime(app, options = {}) {
|
|
|
793
799
|
const request = new Request(rewriteUrl.toString(), c.req.raw);
|
|
794
800
|
rememberOriginal(request, c);
|
|
795
801
|
keepActionKey(c.req.raw, request, search);
|
|
796
|
-
if (fallback)
|
|
797
|
-
|
|
802
|
+
if (fallback) {
|
|
803
|
+
fallbackHandled.add(request);
|
|
804
|
+
fallbackServed.add(c.req.raw);
|
|
805
|
+
}
|
|
798
806
|
const kind = rule.force === true ? "rewrite" : "fallback";
|
|
799
|
-
return
|
|
807
|
+
return dispatch(request, c, `${kind}[${rule.source}] -> ${destination} (${rule.origin || "unknown"})`);
|
|
800
808
|
}
|
|
801
809
|
const location = mergeQueryIntoLocation(destination, url.search);
|
|
802
810
|
return annotate(c.redirect(location, rule.status), `redirect[${rule.source}] -> ${destination} ${rule.status} (${rule.origin || "unknown"})`);
|
|
@@ -825,7 +833,7 @@ function createRoutingRuntime(app, options = {}) {
|
|
|
825
833
|
const request = new Request(url.toString(), c.req.raw);
|
|
826
834
|
rememberOriginal(request, c);
|
|
827
835
|
keepActionKey(c.req.raw, request, search);
|
|
828
|
-
return dispatch(request, c
|
|
836
|
+
return dispatch(request, c, `c.rewrite -> ${path} (middleware)`);
|
|
829
837
|
};
|
|
830
838
|
const redirect = context.redirect.bind(context);
|
|
831
839
|
c.redirect = (destination, status) => {
|
|
@@ -855,8 +863,19 @@ function createRoutingRuntime(app, options = {}) {
|
|
|
855
863
|
c.res.headers.set("X-Void-Routing", "pass-through");
|
|
856
864
|
} catch {}
|
|
857
865
|
},
|
|
858
|
-
headerMiddleware: (rules) => async (c, next) => {
|
|
866
|
+
headerMiddleware: (rules, phase) => async (c, next) => {
|
|
867
|
+
const authErrorsOnly = phase === "auth-error";
|
|
859
868
|
await next();
|
|
869
|
+
if (authErrorsOnly && (!c.error || headerApplied.has(c))) return;
|
|
870
|
+
if (!authErrorsOnly) headerApplied.add(c);
|
|
871
|
+
const protectCache = authErrorsOnly || authDependentRequests.has(c.req.raw);
|
|
872
|
+
if (protectCache) {
|
|
873
|
+
authDependentRequests.add(c.req.raw);
|
|
874
|
+
c.header("Cache-Control", "no-store");
|
|
875
|
+
c.header("Pragma", "no-cache");
|
|
876
|
+
for (const name of SHARED_CACHE_HEADERS) if (c.res.headers.has(name)) c.header(name, void 0);
|
|
877
|
+
}
|
|
878
|
+
if (fallbackServed.has(c.req.raw)) return;
|
|
860
879
|
const pathname = new URL(c.req.url).pathname;
|
|
861
880
|
const matched = [];
|
|
862
881
|
for (const rule of rules) {
|
|
@@ -869,13 +888,15 @@ function createRoutingRuntime(app, options = {}) {
|
|
|
869
888
|
if (matchPattern(rule.pattern, pathname)) matched.push(...rule.headers);
|
|
870
889
|
}
|
|
871
890
|
if (!matched.length) return;
|
|
872
|
-
const
|
|
873
|
-
for (const [name, value] of matched)
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
891
|
+
const last = /* @__PURE__ */ new Map();
|
|
892
|
+
for (const [name, value] of matched) last.set(name.toLowerCase(), [name, value]);
|
|
893
|
+
const asset = isUnchangedStaticAsset(c);
|
|
894
|
+
for (const [key, [name, value]] of last) {
|
|
895
|
+
if (protectCache && (key === "cache-control" || key === "pragma" || SHARED_CACHE_HEADERS.includes(key))) continue;
|
|
896
|
+
if (key === "set-cookie" && c.res.headers.getSetCookie().at(-1) === value) continue;
|
|
897
|
+
c.header(name, value, { append: key === "set-cookie" });
|
|
898
|
+
}
|
|
899
|
+
if (asset) markStaticAsset(c, c.res);
|
|
879
900
|
},
|
|
880
901
|
applyFallback: (c, rules) => fallbackHandled.has(c.req.raw) ? Promise.resolve(void 0) : applyRules(c, rules, true),
|
|
881
902
|
markNoMatch
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { D as runValidation, E as ValidatorSlots, T as HandlerInput } from "../handler-
|
|
1
|
+
import { D as runValidation, E as ValidatorSlots, T as HandlerInput } from "../handler-BR6Tvjkp.mjs";
|
|
2
2
|
export { HandlerInput, ValidatorSlots, runValidation };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as AuthState } from "../auth-CH4PLAX1.mjs";
|
|
2
2
|
import { r as VoidAuthRuntimeOptions, t as VoidAuthEnv } from "../better-auth-shared-BJyG2mH0.mjs";
|
|
3
|
-
import { f as WebSocketInstanceKey, t as AnyWebSocketDefinition } from "../ws-
|
|
3
|
+
import { f as WebSocketInstanceKey, t as AnyWebSocketDefinition } from "../ws-Dmjg5NH2.mjs";
|
|
4
4
|
export type WebSocketRouteMeta = {
|
|
5
5
|
name?: string;
|
|
6
6
|
key?: WebSocketInstanceKey;
|
package/dist/runtime/ws.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as RoomDefinition, c as VoidSocket, d as WebSocketConnection, f as WebSocketInstanceKey, h as defineWebSocket, i as RoomController, l as VoidSocketCloseEvent, m as defineRoom, n as ConnectCommonOptions, o as SocketContext, p as connect, r as RoomContext, s as SocketDefinition, t as AnyWebSocketDefinition, u as WebSocketBaseContext } from "../ws-
|
|
1
|
+
import { a as RoomDefinition, c as VoidSocket, d as WebSocketConnection, f as WebSocketInstanceKey, h as defineWebSocket, i as RoomController, l as VoidSocketCloseEvent, m as defineRoom, n as ConnectCommonOptions, o as SocketContext, p as connect, r as RoomContext, s as SocketDefinition, t as AnyWebSocketDefinition, u as WebSocketBaseContext } from "../ws-Dmjg5NH2.mjs";
|
|
2
2
|
export { AnyWebSocketDefinition, ConnectCommonOptions, RoomContext, RoomController, RoomDefinition, SocketContext, SocketDefinition, VoidSocket, VoidSocketCloseEvent, WebSocketBaseContext, WebSocketConnection, WebSocketInstanceKey, connect, defineRoom, defineWebSocket };
|
|
@@ -575,11 +575,13 @@ async function scanPages(root, options) {
|
|
|
575
575
|
if (rendering.shouldPrerender && revalidate === void 0) revalidate = MAX_REVALIDATE;
|
|
576
576
|
let metadata;
|
|
577
577
|
let layout;
|
|
578
|
-
if (ext === ".md")
|
|
579
|
-
|
|
580
|
-
layout
|
|
581
|
-
|
|
582
|
-
|
|
578
|
+
if (ext === ".md") {
|
|
579
|
+
const { default: matter } = await import("gray-matter");
|
|
580
|
+
({metadata, layout} = deriveFromSource(join(pagesDir, file), "page-markdown", (source) => ({
|
|
581
|
+
metadata: extractFrontmatterMeta(source),
|
|
582
|
+
layout: extractFrontmatterLayout(source, matter)
|
|
583
|
+
})));
|
|
584
|
+
} else layout = deriveFromSource(join(pagesDir, file), "page-layout", (source) => componentLayout(source, file));
|
|
583
585
|
pages.push({
|
|
584
586
|
pattern: route.pattern,
|
|
585
587
|
params: route.params,
|
|
@@ -701,12 +703,18 @@ function extractFrontmatterMeta(source) {
|
|
|
701
703
|
};
|
|
702
704
|
}
|
|
703
705
|
/** Extract layout field from YAML frontmatter in markdown source. */
|
|
704
|
-
function extractFrontmatterLayout(source) {
|
|
706
|
+
function extractFrontmatterLayout(source, matter) {
|
|
705
707
|
const yaml = markdownFrontmatter(source);
|
|
706
708
|
if (yaml === void 0) return;
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
709
|
+
let frontmatter;
|
|
710
|
+
try {
|
|
711
|
+
frontmatter = matter(`---\n${yaml}\n---`, {}).data;
|
|
712
|
+
} catch {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const value = frontmatter.layout;
|
|
716
|
+
if (value === void 0 || value === null || value === "") return;
|
|
717
|
+
if (value === false || value === "false") return false;
|
|
718
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
711
719
|
}
|
|
712
720
|
export { resolveLayoutChain as a, editIslandSource as c, deriveFromSource as d, invalidateSourceAnalysis as f, ROUTE_EXTENSIONS as g, parseWebSocketFilename as h, resolveEffectiveLayoutChain as i, findIslandImports as l, parseRouteFilename as m, scanPages as n, resolveLayoutIds as o, isRouteEnabledForEnvironment as p, generateComponentManifest as r, resolveNamedLayout as s, pageFileEnvironment as t, findStaticCssImports as u };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as readDeploymentPlatform, r as readProjectConfig } from "./project-CLwxcEdq.mjs";
|
|
2
2
|
import { c as getToken } from "./auth-Dy8svcqQ.mjs";
|
|
3
3
|
import { A as confirm, H as outro, L as log, O as cancel, U as password, tt as isCancel } from "./dist-D23AAMxT.mjs";
|
|
4
|
-
import { r as PlatformClient } from "./client-
|
|
4
|
+
import { r as PlatformClient } from "./client-CoxPEhJq.mjs";
|
|
5
5
|
import { m as printSuccess, o as formatCount, p as note } from "./output-BCdLioKP.mjs";
|
|
6
6
|
import { a as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-D0YNnJfm.mjs";
|
|
7
7
|
import { _ as validateSecretValues } from "./env-validation-GV1oD4EV.mjs";
|
|
@@ -97,7 +97,7 @@ async function resolveSecretBackend(root, args) {
|
|
|
97
97
|
}
|
|
98
98
|
async function resolveCloudflareSecretBackend(root, args) {
|
|
99
99
|
if (args.projectName) throw new Error("secret: --project selects a Void platform project and is not used for direct Cloudflare apps.");
|
|
100
|
-
const { deleteCloudflareSecret, listCloudflareSecrets, putCloudflareSecret, resolveCloudflareOperationContext, syncCloudflareSecrets } = await import("./cloudflare-operations-
|
|
100
|
+
const { deleteCloudflareSecret, listCloudflareSecrets, putCloudflareSecret, resolveCloudflareOperationContext, syncCloudflareSecrets } = await import("./cloudflare-operations-oLvAd9Nd.mjs");
|
|
101
101
|
const context = resolveCloudflareOperationContext(root);
|
|
102
102
|
return {
|
|
103
103
|
target: "Cloudflare Worker",
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// src/runtime/auth-response.ts
|
|
2
|
+
const authDependentRequests = /* @__PURE__ */ new WeakSet();
|
|
3
|
+
const SHARED_CACHE_HEADERS = [
|
|
4
|
+
"cdn-cache-control",
|
|
5
|
+
"cloudflare-cdn-cache-control",
|
|
6
|
+
"surrogate-control"
|
|
7
|
+
];
|
|
1
8
|
// src/runtime/static-asset-marker.ts
|
|
2
9
|
const STATIC_ASSET_KEY = "__voidStaticAsset";
|
|
3
10
|
/**
|
|
@@ -28,4 +35,4 @@ function isUnchangedStaticAsset(c) {
|
|
|
28
35
|
304
|
|
29
36
|
].includes(c.res.status) && c.res.body === mark.body && (c.res.headers.get("cache-control") ?? "") === mark.policy && !c.res.headers.has("set-cookie");
|
|
30
37
|
}
|
|
31
|
-
export { markStaticAsset as n, isUnchangedStaticAsset as t };
|
|
38
|
+
export { authDependentRequests as i, markStaticAsset as n, SHARED_CACHE_HEADERS as r, isUnchangedStaticAsset as t };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// src/runtime/handler-composition.ts
|
|
2
|
+
const compositionKey = "__voidHandlerComposition";
|
|
3
|
+
function getHandlerComposition(handler) {
|
|
4
|
+
return handler?.[compositionKey];
|
|
5
|
+
}
|
|
6
|
+
/** Record the composition; a nested composed leaf is flattened into one chain. */
|
|
7
|
+
function setHandlerComposition(handler, middlewares, leaf) {
|
|
8
|
+
const nested = getHandlerComposition(leaf);
|
|
9
|
+
const composition = nested ? {
|
|
10
|
+
middlewares: [...middlewares, ...nested.middlewares],
|
|
11
|
+
leaf: nested.leaf
|
|
12
|
+
} : {
|
|
13
|
+
middlewares,
|
|
14
|
+
leaf
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(handler, compositionKey, { value: composition });
|
|
17
|
+
return handler;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Run middleware on the current context around `endpoint`, the way Hono's
|
|
21
|
+
* compose does: a Response returned by a middleware replaces `c.res`, the
|
|
22
|
+
* endpoint's Response becomes `c.res`, and middleware can edit `c.res` after
|
|
23
|
+
* `next()`. Returns the final `c.res`.
|
|
24
|
+
*/
|
|
25
|
+
async function executeComposedHandler(c, middlewares, endpoint) {
|
|
26
|
+
let index = 0;
|
|
27
|
+
const dispatch = async () => {
|
|
28
|
+
if (index < middlewares.length) {
|
|
29
|
+
const response = await middlewares[index++](c, dispatch);
|
|
30
|
+
if (response instanceof Response) c.res = response;
|
|
31
|
+
} else c.res = await endpoint();
|
|
32
|
+
};
|
|
33
|
+
await dispatch();
|
|
34
|
+
return c.res;
|
|
35
|
+
}
|
|
36
|
+
// src/pages/defer.ts
|
|
37
|
+
const DEFERRED = Symbol("deferred");
|
|
38
|
+
function defer(groupOrResolver, maybeResolver) {
|
|
39
|
+
const [group, resolver] = typeof groupOrResolver === "string" ? [groupOrResolver, maybeResolver] : [null, groupOrResolver];
|
|
40
|
+
return {
|
|
41
|
+
[DEFERRED]: true,
|
|
42
|
+
group,
|
|
43
|
+
resolver
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Separate a loader's return value into immediate props and deferred sentinels.
|
|
48
|
+
* For single-prop deferred (group=null), the object key becomes the group name.
|
|
49
|
+
* For named groups, all keys sharing the same group name are collected.
|
|
50
|
+
*/
|
|
51
|
+
function separateDeferred(props) {
|
|
52
|
+
const immediateProps = {};
|
|
53
|
+
const deferredMap = {};
|
|
54
|
+
for (const [key, value] of Object.entries(props)) if (value && typeof value === "object" && DEFERRED in value) {
|
|
55
|
+
const sentinel = value;
|
|
56
|
+
const groupName = sentinel.group ?? key;
|
|
57
|
+
if (!deferredMap[groupName]) deferredMap[groupName] = {
|
|
58
|
+
resolver: sentinel.resolver,
|
|
59
|
+
keys: []
|
|
60
|
+
};
|
|
61
|
+
deferredMap[groupName].keys.push(key);
|
|
62
|
+
immediateProps[key] = {
|
|
63
|
+
value: null,
|
|
64
|
+
loading: true,
|
|
65
|
+
error: null
|
|
66
|
+
};
|
|
67
|
+
} else immediateProps[key] = value;
|
|
68
|
+
return {
|
|
69
|
+
immediateProps,
|
|
70
|
+
deferredMap
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// src/pages/validation-error.ts
|
|
74
|
+
var ValidationError = class extends Error {
|
|
75
|
+
errors;
|
|
76
|
+
constructor(errors) {
|
|
77
|
+
super("Validation failed");
|
|
78
|
+
this.name = "ValidationError";
|
|
79
|
+
this.errors = errors;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
export { executeComposedHandler as a, separateDeferred as i, DEFERRED as n, getHandlerComposition as o, defer as r, setHandlerComposition as s, ValidationError as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as join } from "./dist-DMYe8CZJ.mjs";
|
|
2
|
-
import { l as bindingNamesForField, p as findBindingNameOwner } from "./wrangler-
|
|
2
|
+
import { l as bindingNamesForField, p as findBindingNameOwner } from "./wrangler-DESSbN8L.mjs";
|
|
3
3
|
// src/shared/untrusted-env.ts
|
|
4
4
|
/** Authentication inputs that Wrangler may consume from the environment. */
|
|
5
5
|
const WRANGLER_AUTH_CREDENTIAL_ENV_VARS = [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as join, c as resolve, n as dirname, o as normalize, t as basename } from "./dist-DMYe8CZJ.mjs";
|
|
2
2
|
import { o as TYPED_CONFIG_FILE, s as loadTypedVoidConfigFile } from "./project-paths-CdGv9Tmi.mjs";
|
|
3
|
-
import { h as validateConfig, p as readConfig } from "./config-
|
|
4
|
-
import { a as persistCloudflareConfig, i as materializeCloudflareConfig, r as ignoreMaterializedCloudflareConfig } from "./cloudflare-config-
|
|
3
|
+
import { h as validateConfig, p as readConfig } from "./config-BITKQ3O6.mjs";
|
|
4
|
+
import { a as persistCloudflareConfig, i as materializeCloudflareConfig, r as ignoreMaterializedCloudflareConfig } from "./cloudflare-config-eQunqX6A.mjs";
|
|
5
5
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { parseSync } from "vite";
|
|
7
7
|
import { appendSqliteClassMigration, isRecord } from "@void/deploy-core";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { b as readRootWranglerConfig } from "./wrangler-DESSbN8L.mjs";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { a as join, l as toSlash, n as dirname, r as extname } from "./dist-DMYe8CZJ.mjs";
|
|
2
2
|
import { n as readProjectPaths } from "./project-paths-CdGv9Tmi.mjs";
|
|
3
|
-
import { l as getDatabaseDialect, p as readConfig } from "./config-
|
|
3
|
+
import { l as getDatabaseDialect, p as readConfig } from "./config-BITKQ3O6.mjs";
|
|
4
4
|
import { n as isIdentifier, r as isLiteral } from "./ast-Cv0JwXqb.mjs";
|
|
5
5
|
import { t as MagicString } from "./dist-B6eN80gc.mjs";
|
|
6
|
-
import { c as voidDbBundlerPlugin, r as cloudflareQueueName, t as generateQueueResolverSource } from "./dispatch-
|
|
6
|
+
import { c as voidDbBundlerPlugin, r as cloudflareQueueName, t as generateQueueResolverSource } from "./dispatch-BNLB5_Gm.mjs";
|
|
7
7
|
import { t as WORKER_CONDITIONS } from "./worker-conditions-CBZ0RYsZ.mjs";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as StandardSchemaV1 } from "./standard-schema-CuDawHOC.mjs";
|
|
2
2
|
import { r as AuthUser } from "./auth-CH4PLAX1.mjs";
|
|
3
|
-
import { a as CloudEnv } from "./handler-
|
|
3
|
+
import { a as CloudEnv } from "./handler-BR6Tvjkp.mjs";
|
|
4
4
|
import { WebSocketRouteMap } from "void/routes";
|
|
5
5
|
type MaybePromise<T> = T | Promise<T>;
|
|
6
6
|
type ParamsRecord = Record<string, string>;
|
|
@@ -24,6 +24,8 @@ Define custom response headers in [`void.config.ts`](../../reference/config) usi
|
|
|
24
24
|
- **Header values** use `Name: value` format. The value may contain colons.
|
|
25
25
|
- All matching rules are merged. When multiple rules set the same header name, the **last match wins**.
|
|
26
26
|
- User-defined `Cache-Control` overrides the built-in default. The default still applies when no rule matches.
|
|
27
|
+
- With [authentication](../auth.md), responses to requests that send cookies or credentials stay private, and auth endpoints stay uncached, during local development, with `void deploy --platform cloudflare`, and on [other runtimes](../deployment.md#other-runtimes). On a [Void platform](../deployment.md#deploy-to-a-team-platform), a `Cache-Control` rule replaces the app's value, so do not match signed-in pages or `/api/auth/*` with a public cache rule.
|
|
28
|
+
- Rules apply to matching error responses too, including 404 responses. An immutable cache rule can therefore cache a missing file. Use immutable caching only for URLs that change when their content changes.
|
|
27
29
|
|
|
28
30
|
## Scope
|
|
29
31
|
|
|
@@ -56,7 +56,7 @@ Keep access checks on any destination that needs them.
|
|
|
56
56
|
|
|
57
57
|
A destination without `?` preserves the incoming query string. `c.rewrite('/search')` keeps `?q=...`, while `c.rewrite('/search?q=all')` replaces it.
|
|
58
58
|
|
|
59
|
-
Your editor suggests known routes through the [`RewriteDestination`](../../reference/api/rewrites.md#rewritedestination) type. Dynamic strings are also accepted.
|
|
59
|
+
Your editor suggests known routes through the [`RewriteDestination`](../../reference/api/rewrites.md#rewritedestination) type. Dynamic strings are also accepted. With [`routing.typedRoutes`](../../reference/config.md#routing-typedroutes), a string must match one of your routes; add `as RouteUrl` to a path built at runtime, such as ``c.rewrite(`/en${c.req.path}` as RouteUrl)``.
|
|
60
60
|
|
|
61
61
|
### Runtime rewrites cannot reach static assets
|
|
62
62
|
|
|
@@ -78,7 +78,7 @@ export default defineEmail(async (message, env, ctx, info) => {
|
|
|
78
78
|
});
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
`replyEmail()` replies to the original sender with a threaded subject. You can override `subject`.
|
|
81
|
+
`replyEmail()` replies to the original sender with a threaded subject. You can override `subject`. To reply after the handler returns, use [`sendEmail` with `threading`](./sending.md#reply-later).
|
|
82
82
|
|
|
83
83
|
On a Void platform, the default sender is your project's shared address, or the address that received mail on your registered domain. Use that default for shared mail; `message.to` has the project prefix removed. Sender overrides must belong to your project.
|
|
84
84
|
|
|
@@ -67,18 +67,19 @@ For mail to arbitrary users, [register your own domain](./domains.md#your-own-do
|
|
|
67
67
|
|
|
68
68
|
## Options {#options}
|
|
69
69
|
|
|
70
|
-
| Option | Type
|
|
71
|
-
| ---------------- |
|
|
72
|
-
| `from` | `string \| { email, name? }`
|
|
73
|
-
| `to` | `Address \| Address[]`
|
|
74
|
-
| `subject` | `string`
|
|
75
|
-
| `text` | `string`
|
|
76
|
-
| `html` | `string`
|
|
77
|
-
| `replyTo` | `Address`
|
|
78
|
-
| `cc`, `bcc` | `Address \| Address[]`
|
|
79
|
-
| `headers` | `Record<string, string>`
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
70
|
+
| Option | Type | Notes |
|
|
71
|
+
| ---------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| `from` | `string \| { email, name? }` | Optional. Pinned to the project sender — see below. |
|
|
73
|
+
| `to` | `Address \| Address[]` | Required. One or more recipients. |
|
|
74
|
+
| `subject` | `string` | Required. UTF-8 supported (encoded as RFC 2047). |
|
|
75
|
+
| `text` | `string` | At least one of `text` / `html` is required. |
|
|
76
|
+
| `html` | `string` | Sent as `multipart/alternative` if both are provided. |
|
|
77
|
+
| `replyTo` | `Address` | Optional `Reply-To` header. |
|
|
78
|
+
| `cc`, `bcc` | `Address \| Address[]` | Optional. Each recipient is sent its own message envelope. |
|
|
79
|
+
| `headers` | `Record<string, string>` | Custom headers; reserved headers (From, Date, etc.) are ignored. |
|
|
80
|
+
| `threading` | `{ inReplyTo?, references? }` | Optional. Threads the email under an earlier message. See [Reply later](#reply-later). |
|
|
81
|
+
| `attachments` | `Attachment[]` | See [Attachments](#attachments). |
|
|
82
|
+
| `idempotencyKey` | `string` | Optional on the Void Platform: 1–128 printable, non-space ASCII characters. Reuse for retries of the same send. |
|
|
82
83
|
|
|
83
84
|
You can send to at most 50 recipients across `to`, `cc`, and `bcc`. Use standard email addresses with ASCII local parts, and punycode for internationalized domains.
|
|
84
85
|
|
|
@@ -88,6 +89,35 @@ On the platform the sender is pinned to your project. `from` must be your projec
|
|
|
88
89
|
|
|
89
90
|
On your own Cloudflare account, `from` defaults to `email.from` from `void.config.ts` and must be on a domain your account can send from; Cloudflare rejects any other sender and `sendEmail` reports it as `INVALID_FROM`.
|
|
90
91
|
|
|
92
|
+
## Reply later {#reply-later}
|
|
93
|
+
|
|
94
|
+
`replyEmail()` works only while the inbound handler runs. To reply later, for example from a queue job, save the thread ids in the handler and pass them to `sendEmail` as `threading`:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// email/support.ts
|
|
98
|
+
export default defineEmail(async (message) => {
|
|
99
|
+
await tickets.save({
|
|
100
|
+
from: message.from,
|
|
101
|
+
subject: message.headers.get('Subject') ?? '',
|
|
102
|
+
messageId: message.headers.get('Message-ID') ?? '',
|
|
103
|
+
references: message.headers.get('References') ?? '',
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Later, in a queue job
|
|
108
|
+
await sendEmail({
|
|
109
|
+
to: ticket.from,
|
|
110
|
+
subject: `Re: ${ticket.subject}`,
|
|
111
|
+
text: 'We fixed it.',
|
|
112
|
+
threading: {
|
|
113
|
+
inReplyTo: ticket.messageId,
|
|
114
|
+
references: `${ticket.references} ${ticket.messageId}`,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`inReplyTo` is the parent message's `Message-ID`. `references` lists the thread's ids, oldest first, ending with the parent; it defaults to `inReplyTo`. Mail clients use them to show the reply in the same conversation. An id that cannot be sent, such as one with a control character, is left out. If `references` cannot be sent, it falls back to `inReplyTo`. The email still sends. `headers` cannot set `In-Reply-To` or `References`.
|
|
120
|
+
|
|
91
121
|
## Attachments {#attachments}
|
|
92
122
|
|
|
93
123
|
```ts
|
|
@@ -29,6 +29,8 @@ export const action = defineHandler.withValidator({
|
|
|
29
29
|
| Nothing (void) | Re-runs the loader, page re-renders with fresh props. |
|
|
30
30
|
| `c.redirect('/path')` | Navigates to another page. |
|
|
31
31
|
|
|
32
|
+
Middleware does not run again after the action. To change [shared data](./layouts.md#shared-data), update it in the action or redirect.
|
|
33
|
+
|
|
32
34
|
## Form
|
|
33
35
|
|
|
34
36
|
Pair `Form` with `useForm` to submit values and display validation and action errors. Inputs stay editable when a submission fails.
|
|
@@ -196,8 +198,26 @@ const form = useForm(
|
|
|
196
198
|
|
|
197
199
|
`onError` receives a field-error map or a `VoidActionError`. Callbacks can be async; `form.pending` stays true until they finish, and thrown errors reach the nearest React error boundary. Callbacks run before React commits the updated UI, so use an effect or ref callback to focus an element that appears after submission.
|
|
198
200
|
|
|
201
|
+
If a callback awaits `router.visit()` or `router.refresh()`, pass `{ resolveOn: 'data' }` so it can finish before React commits the navigation. See [React navigation completion](../../reference/api/pages.md#react-navigation-completion).
|
|
202
|
+
|
|
199
203
|
Vue, Svelte, and Solid submissions return a promise instead; use `await form.post()` to run work after a submission.
|
|
200
204
|
|
|
205
|
+
### Scroll after submission
|
|
206
|
+
|
|
207
|
+
Actions and forms keep the current scroll position when updating the page in place. Redirects and submissions that open another page scroll to the top, or to the destination's hash target.
|
|
208
|
+
|
|
209
|
+
Pass `preserveScroll: false` to reset scroll after an in-place update, or `true` to keep it even after a redirect:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
await action('/users/:id?update', {
|
|
213
|
+
params: { id: '42' },
|
|
214
|
+
data: { name: 'Alice' },
|
|
215
|
+
preserveScroll: false,
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
In React, set `preserveScroll` in the third argument to `useForm()`. In Vue, Svelte, and Solid, pass it to the submit method, such as `form.post({ preserveScroll: false })`.
|
|
220
|
+
|
|
201
221
|
## Named Actions
|
|
202
222
|
|
|
203
223
|
When a page needs multiple mutations, such as updating and deleting a user on the same page, export `actions` (plural) instead of `action`:
|
|
@@ -270,7 +290,7 @@ if (!result.ok) {
|
|
|
270
290
|
|
|
271
291
|
`action()` defaults to `POST`. Pass `data`, `params`, and `method` to set the body, route parameters, or HTTP method.
|
|
272
292
|
|
|
273
|
-
A successful call returns `{ ok: true, pageData }`. Expected errors, such as validation failures or conflicts, return `{ ok: false, error }`.
|
|
293
|
+
A successful call returns `{ ok: true, pageData }`. Expected errors, such as validation failures or conflicts, return `{ ok: false, error }`. `error.message` is the message of an [`HTTPException`](../../integrations/hono.md#utilities) with a `4xx` status, or the body of another plain-text `4xx` response. For other errors, it is a status summary such as `Action failed with 404 Not Found`.
|
|
274
294
|
|
|
275
295
|
## Validation Errors
|
|
276
296
|
|
|
@@ -96,11 +96,11 @@ export default function BlogIndex({ posts }) {
|
|
|
96
96
|
|
|
97
97
|
```vue [Vue]
|
|
98
98
|
<!-- pages/blog/index.island.vue -->
|
|
99
|
-
<script setup>
|
|
99
|
+
<script setup lang="ts">
|
|
100
100
|
import Counter from './_Counter.vue' with { island: 'load' };
|
|
101
101
|
import PostForm from './_PostForm.vue' with { island: 'visible' };
|
|
102
102
|
|
|
103
|
-
defineProps
|
|
103
|
+
const { posts } = defineProps<{ posts: { slug: string; title: string }[] }>();
|
|
104
104
|
</script>
|
|
105
105
|
|
|
106
106
|
<template>
|
|
@@ -249,7 +249,7 @@ export default function PostForm() {
|
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
```vue [Vue]
|
|
252
|
-
<script setup>
|
|
252
|
+
<script setup lang="ts">
|
|
253
253
|
import { useIslandForm } from '@void/vue';
|
|
254
254
|
|
|
255
255
|
const form = useIslandForm({ title: '', body: '' });
|
|
@@ -167,7 +167,7 @@ export default function Page() {
|
|
|
167
167
|
```
|
|
168
168
|
|
|
169
169
|
```vue [Vue]
|
|
170
|
-
<script>
|
|
170
|
+
<script lang="ts">
|
|
171
171
|
export const layout = 'landing';
|
|
172
172
|
</script>
|
|
173
173
|
|
|
@@ -238,4 +238,18 @@ export default defineMiddleware(async (c, next) => {
|
|
|
238
238
|
});
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
-
Call `useShared()` from your framework adapter, as in the layout examples above. Its return type is inferred from `CloudContextVariables`.
|
|
241
|
+
Call `useShared()` from your framework adapter, as in the layout examples above. Its return type is inferred from `CloudContextVariables`. See [Context variables](../type-safety.md#context-variables).
|
|
242
|
+
|
|
243
|
+
Middleware sets the starting value. A loader or action can update it before the page is sent:
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
// pages/profile.server.ts
|
|
247
|
+
import { defineHandler } from 'void';
|
|
248
|
+
|
|
249
|
+
export const action = defineHandler(async (c) => {
|
|
250
|
+
const user = await updateProfile(c);
|
|
251
|
+
c.set('shared', { ...c.get('shared'), auth: { user } });
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Middleware runs before the action. If an action changes data that shared data is built from, update shared data in the action, or redirect with `c.redirect()` so middleware runs again on the next page load.
|
|
@@ -51,7 +51,7 @@ export default function UsersPage({ users }: Props) {
|
|
|
51
51
|
<!-- pages/users/index.vue -->
|
|
52
52
|
<script setup lang="ts">
|
|
53
53
|
import type { Props } from './index.server';
|
|
54
|
-
defineProps<Props>();
|
|
54
|
+
const { users } = defineProps<Props>();
|
|
55
55
|
</script>
|
|
56
56
|
|
|
57
57
|
<template>
|
|
@@ -177,7 +177,7 @@ Check `loading` first, then `error`. TypeScript narrows `value` to `T` in the re
|
|
|
177
177
|
```vue [Vue]
|
|
178
178
|
<script setup lang="ts">
|
|
179
179
|
import type { Props } from './dashboard.server';
|
|
180
|
-
defineProps<Props>();
|
|
180
|
+
const { projects, usage } = defineProps<Props>();
|
|
181
181
|
</script>
|
|
182
182
|
|
|
183
183
|
<template>
|