void 0.7.4 → 0.7.5

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.
@@ -26,9 +26,6 @@ const SCRYPT_OPTIONS = {
26
26
  p: 1,
27
27
  r: 16
28
28
  };
29
- function toHex(bytes) {
30
- return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
31
- }
32
29
  function derivePasswordKey(password, salt) {
33
30
  return new Promise((resolve, reject) => {
34
31
  scrypt(password.normalize("NFKC"), salt, SCRYPT_KEY_LENGTH, SCRYPT_OPTIONS, (error, key) => {
@@ -42,16 +39,15 @@ function derivePasswordKey(password, salt) {
42
39
  }
43
40
  const voidAuthPassword = {
44
41
  async hash(password) {
45
- const salt = toHex(randomBytes(PASSWORD_SALT_LENGTH));
42
+ const salt = randomBytes(PASSWORD_SALT_LENGTH).toString("hex");
46
43
  return `${salt}:${(await derivePasswordKey(password, salt)).toString("hex")}`;
47
44
  },
48
45
  async verify({ hash, password }) {
49
46
  const [salt, key, ...extra] = hash.split(":");
50
47
  if (!salt || !key || extra.length > 0) return false;
51
- const targetKey = await derivePasswordKey(password, salt);
52
48
  const storedKey = Buffer.from(key, "hex");
53
- if (storedKey.length !== targetKey.length) return false;
54
- return timingSafeEqual(targetKey, storedKey);
49
+ if (key.length !== SCRYPT_KEY_LENGTH * 2 || storedKey.length !== SCRYPT_KEY_LENGTH) return false;
50
+ return timingSafeEqual(await derivePasswordKey(password, salt), storedKey);
55
51
  }
56
52
  };
57
53
  function resolveEmailAndPassword(configuredProviders) {
@@ -1,4 +1,4 @@
1
- import { t as PageObject } from "../protocol-CK4OFwfR.mjs";
1
+ import { t as PageObject } from "../protocol-DxGzKEz2.mjs";
2
2
  import { CacheForInput, CacheForResult } from "./prefetch.mjs";
3
3
 
4
4
  //#region src/pages/action-result.d.ts
@@ -147,6 +147,7 @@ interface VoidRouter {
147
147
  readonly url: URL;
148
148
  readonly path: string;
149
149
  readonly query: URLSearchParams;
150
+ readonly params: Record<string, string>;
150
151
  visit(url: string | URL, options?: VisitOptions): Promise<unknown>;
151
152
  refresh(options?: VisitOptions): Promise<unknown>;
152
153
  prefetch(url: string | URL, options?: PrefetchOptions): Promise<unknown>;
@@ -178,8 +179,8 @@ interface VoidRouterConfig {
178
179
  onNavigationChange?(navigation: NavigationState): void;
179
180
  }
180
181
  declare function idleNavigationState(): NavigationState;
181
- declare function createRouterFacade(router: VoidRouter, getCurrentUrl: () => string): VoidRouter;
182
- declare function createSsrRouter(url?: string): VoidRouter;
182
+ declare function createRouterFacade(router: VoidRouter, getCurrentUrl: () => string, getCurrentParams?: () => Record<string, string>): VoidRouter;
183
+ declare function createSsrRouter(url?: string, params?: Record<string, string>): VoidRouter;
183
184
  declare function isFile(value: unknown): boolean;
184
185
  declare function hasFiles(data: unknown): boolean;
185
186
  declare function objectToFormData(source: Record<string, unknown>, form?: FormData, parentKey?: string): FormData;
@@ -756,7 +756,7 @@ function idleNavigationState() {
756
756
  method: null
757
757
  };
758
758
  }
759
- function createRouterFacade(router, getCurrentUrl) {
759
+ function createRouterFacade(router, getCurrentUrl, getCurrentParams = () => router.params) {
760
760
  return {
761
761
  get _hoverDelay() {
762
762
  return router._hoverDelay;
@@ -770,6 +770,9 @@ function createRouterFacade(router, getCurrentUrl) {
770
770
  get query() {
771
771
  return this.url.searchParams;
772
772
  },
773
+ get params() {
774
+ return getCurrentParams();
775
+ },
773
776
  visit(url, options = {}) {
774
777
  return router.visit(url, options);
775
778
  },
@@ -787,7 +790,7 @@ function createRouterFacade(router, getCurrentUrl) {
787
790
  }
788
791
  };
789
792
  }
790
- function createSsrRouter(url = "/") {
793
+ function createSsrRouter(url = "/", params = {}) {
791
794
  return {
792
795
  _hoverDelay: 75,
793
796
  get url() {
@@ -799,6 +802,9 @@ function createSsrRouter(url = "/") {
799
802
  get query() {
800
803
  return this.url.searchParams;
801
804
  },
805
+ get params() {
806
+ return params;
807
+ },
802
808
  visit() {
803
809
  throw new Error("Void Router: visit() is not available during SSR.");
804
810
  },
@@ -870,9 +876,11 @@ async function createVoidRouter(config) {
870
876
  {
871
877
  const engine = await createPageNavigationEngine(config);
872
878
  let routeUrl = currentUrl();
879
+ let routeParams = engine.initialPageData.params ?? {};
873
880
  let activeNavigationId = null;
874
- const setRouteUrl = (url) => {
881
+ const setRouteState = (url, params = routeParams) => {
875
882
  routeUrl = url;
883
+ routeParams = params;
876
884
  config.onRouteChange?.(url);
877
885
  };
878
886
  const setNavigation = (navigation) => {
@@ -884,11 +892,12 @@ async function createVoidRouter(config) {
884
892
  try {
885
893
  const result = await resource.shell;
886
894
  if (!result) return;
895
+ setRouteState(result.pageData.url || resource.fetchUrl + resource.hash, result.pageData.params ?? {});
887
896
  const applyUpdate = () => config.adapter.applyUpdate(result.pageData, result.component, result.layouts);
888
897
  if ((resource.options.viewTransition ?? config.viewTransitions) && document.startViewTransition) await document.startViewTransition(() => applyUpdate()).updateCallbackDone;
889
898
  else await applyUpdate();
890
899
  await resource.commit();
891
- if (activeNavigationId === resource.id) setRouteUrl(currentUrl());
900
+ if (activeNavigationId === resource.id) setRouteState(currentUrl(), result.pageData.params ?? {});
892
901
  return result.pageData;
893
902
  } catch (error) {
894
903
  if (resource.method.toUpperCase() === "GET" && !resource.options._resolveOnShell && isAbortError(error)) return;
@@ -896,7 +905,7 @@ async function createVoidRouter(config) {
896
905
  } finally {
897
906
  if (activeNavigationId === resource.id) {
898
907
  activeNavigationId = null;
899
- setRouteUrl(currentUrl());
908
+ setRouteState(currentUrl());
900
909
  setNavigation(idleNavigationState());
901
910
  }
902
911
  }
@@ -912,6 +921,9 @@ async function createVoidRouter(config) {
912
921
  get query() {
913
922
  return this.url.searchParams;
914
923
  },
924
+ get params() {
925
+ return routeParams;
926
+ },
915
927
  visit(url, options = {}) {
916
928
  return applyNavigationResource(engine.prepareVisit(normalizeUrl(url), options));
917
929
  },
@@ -932,7 +944,7 @@ async function createVoidRouter(config) {
932
944
  engine.flushAll();
933
945
  }
934
946
  };
935
- const router = createRouterFacade(rawRouter, () => routeUrl);
947
+ const router = createRouterFacade(rawRouter, () => routeUrl, () => routeParams);
936
948
  engine.setPopStateHandler((url, options) => {
937
949
  router.visit(url, options);
938
950
  });
@@ -1,3 +1,3 @@
1
1
  import { S as ValidationError } from "../handler-dKQWyF-G.mjs";
2
- import { a as handlePageMutation, i as handlePageGet, n as PageRenderResult, o as safeJsonStringify, r as flattenValidationErrors, t as PageObject } from "../protocol-CK4OFwfR.mjs";
2
+ import { a as handlePageMutation, i as handlePageGet, n as PageRenderResult, o as safeJsonStringify, r as flattenValidationErrors, t as PageObject } from "../protocol-DxGzKEz2.mjs";
3
3
  export { PageObject, PageRenderResult, ValidationError, flattenValidationErrors, handlePageGet, handlePageMutation, safeJsonStringify };
@@ -25,6 +25,9 @@ function pageJsonResponse(c, pageObj) {
25
25
  function requestPathWithSearch(c) {
26
26
  return c.req.path + new URL(c.req.url).search;
27
27
  }
28
+ function requestParams(c) {
29
+ return c.req.param();
30
+ }
28
31
  /** Extract props from a handler result, handling Response wrapping from withValidator */
29
32
  async function extractProps(result) {
30
33
  if (result instanceof Response) {
@@ -107,6 +110,7 @@ async function handlePageGet(handler, headFn, c, componentId, renderFn, getAsset
107
110
  const pageObj = {
108
111
  component: componentId,
109
112
  props: immediateProps,
113
+ params: requestParams(c),
110
114
  shared,
111
115
  url: requestPathWithSearch(c),
112
116
  errors: null,
@@ -159,6 +163,7 @@ async function handlePageMutation(mutationHandler, getHandler, headFn, c, compon
159
163
  return {
160
164
  component: componentId,
161
165
  props,
166
+ params: requestParams(c),
162
167
  shared,
163
168
  url: requestPathWithSearch(c),
164
169
  errors,
@@ -7,6 +7,7 @@ declare function safeJsonStringify(value: unknown): string;
7
7
  interface PageObject {
8
8
  component: string;
9
9
  props: Record<string, unknown>;
10
+ params: Record<string, string>;
10
11
  shared: Record<string, unknown>;
11
12
  url: string;
12
13
  errors: Record<string, string> | null;
@@ -1,5 +1,5 @@
1
1
  import { t as applyPgAuthMigrations } from "../auth-migrations-BwLPwRgH.mjs";
2
- import { t as createVoidAuthRuntime } from "../better-auth-shared-BrfZ78iY.mjs";
2
+ import { t as createVoidAuthRuntime } from "../better-auth-shared-CjnJZFC0.mjs";
3
3
  //#region src/runtime/better-auth-pg.ts
4
4
  function resolveConnectionString(env) {
5
5
  const connectionString = typeof env.DATABASE_URL === "string" && env.DATABASE_URL.length > 0 ? env.DATABASE_URL : typeof env.HYPERDRIVE?.connectionString === "string" && env.HYPERDRIVE.connectionString.length > 0 ? env.HYPERDRIVE.connectionString : void 0;
@@ -1,5 +1,5 @@
1
1
  import { n as applySqliteAuthMigrations } from "../auth-migrations-BwLPwRgH.mjs";
2
- import { t as createVoidAuthRuntime } from "../better-auth-shared-BrfZ78iY.mjs";
2
+ import { t as createVoidAuthRuntime } from "../better-auth-shared-CjnJZFC0.mjs";
3
3
  //#region src/runtime/better-auth.ts
4
4
  async function resolveDatabase(env) {
5
5
  if (!env.DB) throw new Error("auth: Better Auth requires the \"DB\" D1 binding for sqlite apps.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "void",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voidzero-dev/void.git",
@@ -332,7 +332,7 @@
332
332
  "valibot": ">=1.0.0-beta.7",
333
333
  "vite": "^8.0.0",
334
334
  "zod": "^3.25.0 || ^4.0.0",
335
- "@void/md": "0.7.4"
335
+ "@void/md": "0.7.5"
336
336
  },
337
337
  "peerDependenciesMeta": {
338
338
  "@void/md": {
@@ -195,6 +195,17 @@ router.refresh(); // re-fetch current page props
195
195
  router.visit('/logout', { method: 'POST' }); // non-GET navigation
196
196
  ```
197
197
 
198
+ For dynamic route params, use `useParams()` from the same adapter package:
199
+
200
+ ```tsx
201
+ import { useParams } from '@void/react';
202
+
203
+ export default function PostPage() {
204
+ const { id } = useParams<{ id: string }>();
205
+ return <h1>Post {id}</h1>;
206
+ }
207
+ ```
208
+
198
209
  ## Scroll Restoration
199
210
 
200
211
  The Void Router automatically saves and restores scroll position during client-side navigation:
@@ -742,7 +742,8 @@ Imported from `"@void/vue"`.
742
742
  | Export | Description |
743
743
  | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
744
744
  | `Link` | Vue component for SPA navigation. Renders `<a>` for GET, `<button>` for non-GET methods. Props: `href`, `method`, `data`, `prefetch`, `cacheFor`, `preserveScroll`, `preserveState`, `replace`, `reloadDocument`, `viewTransition`, `onNavigate`. |
745
- | `useRouter()` | Returns the Void Router with current route state (`url`, `path`, `query`) and navigation methods: `visit`, `refresh`, awaitable `prefetch`, `flush`, `flushAll`. |
745
+ | `useRouter()` | Returns the Void Router with current route state (`url`, `path`, `query`, `params`) and navigation methods: `visit`, `refresh`, awaitable `prefetch`, `flush`, `flushAll`. |
746
+ | `useParams()` | Returns dynamic route params for the page currently being rendered. |
746
747
  | `useNavigation()` | Returns pending navigation state: `{ state, location, method }`, where `state` is `"idle"`, `"loading"`, or `"submitting"` and `location` is the pending destination. |
747
748
  | `useForm(url, defaults, options?)` | Typed reactive form helper bound to a page action URL. Returns `{ data, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, clearErrors, clearError }`. |
748
749
  | `useShared()` | Returns shared data injected by middleware via `c.set("shared", {...})`. |
@@ -789,7 +790,8 @@ Imported from `"@void/react"`.
789
790
  | Export | Description |
790
791
  | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
791
792
  | `Link` | React component for SPA navigation. Renders `<a>` for GET, `<button>` for non-GET methods. Props: `href`, `method`, `data`, `prefetch`, `cacheFor`, `preserveScroll`, `preserveState`, `replace`, `reloadDocument`, `viewTransition`, `onNavigate`. |
792
- | `useRouter()` | Returns the Void Router with current route state (`url`, `path`, `query`) and navigation methods: `visit`, `refresh`, awaitable `prefetch`, `flush`, `flushAll`. React navigations are scheduled as transitions. |
793
+ | `useRouter()` | Returns the Void Router with current route state (`url`, `path`, `query`, `params`) and navigation methods: `visit`, `refresh`, awaitable `prefetch`, `flush`, `flushAll`. React navigations are scheduled as transitions. |
794
+ | `useParams()` | Returns dynamic route params for the page currently being rendered. |
793
795
  | `useNavigation()` | Returns pending navigation state: `{ state, location, method }`, where `state` is `"idle"`, `"loading"`, or `"submitting"` and `location` is the pending destination. |
794
796
  | `useForm(url, defaults, options?)` | Form helper hook. Returns `{ data, setData, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, clearErrors, clearError }`. |
795
797
  | `action(url, options?)` | Awaitable one-shot page action helper. Uses `POST` by default and accepts `{ data, method, params }`, where `method` can be `"PUT"`, `"PATCH"`, or `"DELETE"`. Returns an `ActionResult`. |
@@ -845,7 +847,8 @@ Imported from `"@void/svelte"`.
845
847
  | `Link` | Svelte component for SPA navigation. Renders `<a>` for GET, `<button>` for non-GET methods. Props: `href`, `method`, `data`, `prefetch`, `cacheFor`, `preserveScroll`, `preserveState`, `replace`, `reloadDocument`, `viewTransition`, `onNavigate`. |
846
848
  | `useForm(url, defaults, options?)` | Form helper using Svelte 5 runes. Returns `{ data, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, clearErrors, clearError }`. |
847
849
  | `useShared()` | Returns shared data injected by middleware via `c.set("shared", {...})`. |
848
- | `useRouter()` | Returns the Void Router with current route state (`url`, `path`, `query`) and navigation methods: `visit`, `refresh`, awaitable `prefetch`, `flush`, `flushAll`. |
850
+ | `useRouter()` | Returns the Void Router with current route state (`url`, `path`, `query`, `params`) and navigation methods: `visit`, `refresh`, awaitable `prefetch`, `flush`, `flushAll`. |
851
+ | `useParams()` | Returns dynamic route params for the page currently being rendered. |
849
852
  | `useNavigation()` | Returns pending navigation state: `{ state, location, method }`, where `state` is `"idle"`, `"loading"`, or `"submitting"` and `location` is the pending destination. |
850
853
 
851
854
  Svelte `Link` GET `data` is merged into the rendered `href` query string. Primitive values are serialized with `String(value)`, arrays become repeated keys, `null` and `undefined` are omitted, and nested objects throw. `prefetch` and `reloadDocument` are GET-only and throw for mutation links.
@@ -888,7 +891,8 @@ Imported from `"@void/solid"`.
888
891
  | `Link` | Solid component for SPA navigation. Renders `<a>` for GET, `<button>` for non-GET methods. Props: `href`, `method`, `data`, `prefetch`, `cacheFor`, `preserveScroll`, `preserveState`, `replace`, `reloadDocument`, `viewTransition`, `onNavigate`. |
889
892
  | `useForm(url, defaults, options?)` | Form helper using Solid stores. Returns `{ data, setData, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, clearErrors, clearError }`. |
890
893
  | `useShared()` | Returns shared data injected by middleware via `c.set("shared", {...})`. |
891
- | `useRouter()` | Returns the Void Router with current route state (`url`, `path`, `query`) and navigation methods: `visit`, `refresh`, awaitable `prefetch`, `flush`, `flushAll`. |
894
+ | `useRouter()` | Returns the Void Router with current route state (`url`, `path`, `query`, `params`) and navigation methods: `visit`, `refresh`, awaitable `prefetch`, `flush`, `flushAll`. |
895
+ | `useParams()` | Returns dynamic route params for the page currently being rendered. |
892
896
  | `useNavigation()` | Returns pending navigation state: `{ state, location, method }`, where `state` is `"idle"`, `"loading"`, or `"submitting"` and `location` is the pending destination. |
893
897
 
894
898
  Solid `Link` GET `data` is merged into the rendered `href` query string. Primitive values are serialized with `String(value)`, arrays become repeated keys, `null` and `undefined` are omitted, and nested objects throw. `prefetch` and `reloadDocument` are GET-only and throw for mutation links.