void 1.2.0 → 1.2.1

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.
@@ -50,6 +50,8 @@ type VisitOptions = {
50
50
  } | null;
51
51
  /** @internal Used by popstate handler to avoid writing history while traversing */
52
52
  _popstate?: boolean;
53
+ /** @internal Used by router.refresh() to refetch the current URL even when it has a hash */
54
+ _refresh?: boolean;
53
55
  };
54
56
  type PrefetchOptions = {
55
57
  method?: string;
@@ -170,6 +172,8 @@ export declare function idleNavigationState(): NavigationState;
170
172
  export declare function createRouterFacade(router: VoidRouter, getCurrentUrl: () => string, getCurrentParams?: () => Record<string, string>): VoidRouter;
171
173
  export declare function createSsrRouter(url?: string, params?: Record<string, string>): VoidRouter;
172
174
  export declare const ssrProxy: VoidRouter;
175
+ /** Refetch the current page in place: same history entry, URL hash, state and scroll. */
176
+ export declare function refreshPage(router: Pick<VoidRouter, "visit">, options?: VisitOptions): Promise<unknown>;
173
177
  export declare function submitAction(router: VoidRouter, url: string, options: VisitOptions): Promise<ActionResult>;
174
178
  type ActionMethod = "POST" | "PUT" | "PATCH" | "DELETE";
175
179
  type HasParams<U extends string> = [ResolveActionParams<U>] extends [never] ? false : true;
@@ -215,7 +215,7 @@ function createScrollRestoration() {
215
215
  },
216
216
  save,
217
217
  writeHistory(url, replace) {
218
- renderedKey = newKey();
218
+ renderedKey = (replace ? readKey(history.state) : null) ?? newKey();
219
219
  const state = {
220
220
  ...replace ? history.state : {},
221
221
  __voidScrollKey: renderedKey
@@ -726,7 +726,7 @@ async function createPageNavigationEngine(config) {
726
726
  if (preservedDeferredRegistry && preservedDeferredRegistry !== deferredRegistry) deferredRegistry.dispose();
727
727
  applyClientHead(result.pageData.head);
728
728
  _currentFetchUrl = commitTarget.fetchUrl;
729
- if (!options.preserveState) {
729
+ if (!options._popstate) {
730
730
  const historyUrl = commitTarget.fetchUrl + commitTarget.hash;
731
731
  scrollRestoration.writeHistory(historyUrl, !!options.replace);
732
732
  }
@@ -760,8 +760,8 @@ async function createPageNavigationEngine(config) {
760
760
  const hashNavigationBaseUrl = options._popstate ? _currentFetchUrl : currentPath;
761
761
  _pendingResource?.abort();
762
762
  _pendingResource = null;
763
- if (effectiveFetchUrl === hashNavigationBaseUrl && method === "GET" && !options.data && Boolean(hash || options._popstate)) {
764
- commitHashNavigation(effectiveFetchUrl, hash, options, !options._popstate && !options.preserveState);
763
+ if (!options._refresh && effectiveFetchUrl === hashNavigationBaseUrl && method === "GET" && !options.data && Boolean(hash || options._popstate)) {
764
+ commitHashNavigation(effectiveFetchUrl, hash, options, !options._popstate);
765
765
  return {
766
766
  id,
767
767
  url,
@@ -1069,6 +1069,17 @@ function createSsrRouter(url = "/", params = {}) {
1069
1069
  };
1070
1070
  }
1071
1071
  const ssrProxy = createSsrRouter();
1072
+ /** Refetch the current page in place: same history entry, URL hash, state and scroll. */
1073
+ function refreshPage(router, options = {}) {
1074
+ const { pathname, search, hash } = window.location;
1075
+ return router.visit(pathname + search + hash, {
1076
+ preserveState: true,
1077
+ preserveScroll: true,
1078
+ replace: true,
1079
+ ...options,
1080
+ _refresh: true
1081
+ });
1082
+ }
1072
1083
  async function submitAction(router, url, options) {
1073
1084
  try {
1074
1085
  const pageData = await router.visit(url, options);
@@ -1172,11 +1183,7 @@ async function createVoidRouter(config) {
1172
1183
  return applyNavigationResource(engine.prepareVisit(normalizeUrl(url), options));
1173
1184
  },
1174
1185
  refresh(options = {}) {
1175
- return router.visit(window.location.pathname + window.location.search, {
1176
- preserveState: true,
1177
- preserveScroll: true,
1178
- ...options
1179
- });
1186
+ return refreshPage(router, options);
1180
1187
  },
1181
1188
  prefetch(url, options = {}) {
1182
1189
  return engine.prefetch(normalizeUrl(url), options);
@@ -1238,4 +1245,4 @@ function initIslands(islands, hydrateFn) {
1238
1245
  });
1239
1246
  }
1240
1247
  //#endregion
1241
- export { VoidActionError, categorizeActionError, createActionClient, createPageNavigationEngine, createRouterFacade, createSsrRouter, createVoidRouter, formErrorMessage, formRetryMessage, formSubmissionBlocked, getValidationErrors, hasFiles, idleNavigationState, initIslands, interpolateRouteParams, isAbortError, isCallSiteActionError, isEqualFormValue, isFile, isModifiedEvent, mergeDataIntoHref, objectToFormData, scheduleFormRetry, scheduleHydration, ssrProxy, submitAction };
1248
+ export { VoidActionError, categorizeActionError, createActionClient, createPageNavigationEngine, createRouterFacade, createSsrRouter, createVoidRouter, formErrorMessage, formRetryMessage, formSubmissionBlocked, getValidationErrors, hasFiles, idleNavigationState, initIslands, interpolateRouteParams, isAbortError, isCallSiteActionError, isEqualFormValue, isFile, isModifiedEvent, mergeDataIntoHref, objectToFormData, refreshPage, scheduleFormRetry, scheduleHydration, ssrProxy, submitAction };
@@ -208,7 +208,7 @@ import { useRouter } from '@void/vue'; // or "@void/react", "@void/svelte", "@vo
208
208
 
209
209
  const router = useRouter();
210
210
  router.visit('/users');
211
- router.refresh(); // re-fetch current page props
211
+ router.refresh(); // re-fetch current page props, keeping the same history entry
212
212
  router.visit('/logout', { method: 'POST' }); // non-GET navigation
213
213
  ```
214
214
 
@@ -32,6 +32,11 @@ bun add -D void
32
32
  bunx void init
33
33
  ```
34
34
 
35
+ ```sh [vite+]
36
+ vp add -D void
37
+ vpx void init
38
+ ```
39
+
35
40
  :::
36
41
 
37
42
  Choose your UI framework, a starter, and a deployment target. Vite+ is the default build tool. D1 is a good first database: it runs locally without a separate database server. Choose Cloudflare to deploy to your own account, or skip deployment setup for now.
@@ -42,10 +47,30 @@ With pnpm, `pnpm create void my-app` also creates the project directory and conf
42
47
 
43
48
  ## Run locally
44
49
 
45
- ```sh
50
+ ::: code-group
51
+
52
+ ```sh [npm]
46
53
  npm run dev
47
54
  ```
48
55
 
56
+ ```sh [pnpm]
57
+ pnpm run dev
58
+ ```
59
+
60
+ ```sh [yarn]
61
+ yarn run dev
62
+ ```
63
+
64
+ ```sh [bun]
65
+ bun run dev
66
+ ```
67
+
68
+ ```sh [vite+]
69
+ vpr dev
70
+ ```
71
+
72
+ :::
73
+
49
74
  Open `http://localhost:5173`. In a Pages starter, `pages/` contains your UI and companion `.server.ts` files contain its server loaders and actions. Loaders return data to page components as typed props.
50
75
 
51
76
  ## Call a server route
@@ -120,8 +120,8 @@ See the [configuration reference](../reference/config.md#sandbox) for binding na
120
120
  Custom images must include the helper matching Void's installed Sandbox SDK version. The SDK image supplies this binary, but is not a runnable environment itself:
121
121
 
122
122
  ```dockerfile
123
- FROM node:26.10.0-trixie-slim
124
- COPY --from=docker.io/cloudflare/sandbox:1.0.1 /usr/local/bin/sandbox-shim /usr/local/bin/sandbox-shim
123
+ FROM node:26.12.1-trixie-slim
124
+ COPY --from=docker.io/cloudflare/sandbox:1.0.2 /usr/local/bin/sandbox-shim /usr/local/bin/sandbox-shim
125
125
  WORKDIR /workspace
126
126
  CMD ["sleep", "infinity"]
127
127
  ```
@@ -79,7 +79,7 @@ Use this page as a command reference. If you are setting up a project for the fi
79
79
 
80
80
  ## Binary Invocation {#binary-invocation}
81
81
 
82
- The docs use `void` for brevity. Outside package scripts, run it with your package manager: `npx void`, `pnpm void`, `yarn void`, or `bunx void`.
82
+ The docs use `void` for brevity. Outside package scripts, run it with your package manager: `npx void`, `pnpm void`, `yarn void`, `bunx void`, or `vpx void`.
83
83
 
84
84
  Alternatively, you can add `./node_modules/.bin` to your `PATH` so that you can invoke `void` directly when you are in the root directory of your app.
85
85
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "void",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Full-stack framework and deployment CLI for Cloudflare Workers, powered by Vite.",
5
5
  "homepage": "https://void.cloud",
6
6
  "bugs": {
@@ -348,11 +348,11 @@
348
348
  "@cloudflare/workers-auth": "^0.14.1",
349
349
  "@cloudflare/workers-types": "^5.20261019.1",
350
350
  "@napi-rs/keyring": "^2.1.2",
351
- "@void/deploy-cloudflare": "1.2.0",
352
- "@void/deploy-core": "1.2.0",
353
- "@void/edge": "1.2.0",
354
- "@void/isr": "1.2.0",
355
- "@void/platform": "1.2.0",
351
+ "@void/deploy-cloudflare": "1.2.1",
352
+ "@void/deploy-core": "1.2.1",
353
+ "@void/edge": "1.2.1",
354
+ "@void/isr": "1.2.1",
355
+ "@void/platform": "1.2.1",
356
356
  "better-auth": "^1.7.11",
357
357
  "better-sqlite3": "^13.0.3",
358
358
  "blake3-jit": "^1.1.0",
@@ -401,7 +401,7 @@
401
401
  "zod": "^4.6.7"
402
402
  },
403
403
  "peerDependencies": {
404
- "@void/md": "1.2.0",
404
+ "@void/md": "1.2.1",
405
405
  "arktype": ">=2.0.0",
406
406
  "valibot": ">=1.0.0-beta.7",
407
407
  "vite": "^8.0.0",
@@ -1,4 +1,4 @@
1
- FROM node:26.10.0-trixie-slim
1
+ FROM node:26.12.1-trixie-slim
2
2
  COPY --from=docker.io/cloudflare/sandbox:1.0.2 /usr/local/bin/sandbox-shim /usr/local/bin/sandbox-shim
3
3
  WORKDIR /workspace
4
4
  CMD ["sleep", "infinity"]