Comparing version 2.0.0-beta.1 to 2.0.0-snapshot.2024-05-19.17e1fabc
@@ -1,3 +0,3 @@ | ||
import { P as Parser, O as Options, N as Nullable } from './parsers-5ac22957.js'; | ||
export { H as HistoryOptions, a as ParserBuilder, c as createParser, m as parseAsArrayOf, f as parseAsBoolean, e as parseAsFloat, d as parseAsHex, b as parseAsInteger, h as parseAsIsoDateTime, l as parseAsJson, k as parseAsNumberLiteral, p as parseAsString, i as parseAsStringEnum, j as parseAsStringLiteral, g as parseAsTimestamp } from './parsers-5ac22957.js'; | ||
import { P as Parser, O as Options, N as Nullable } from './serializer-_l-WZKrY.js'; | ||
export { H as HistoryOptions, a as ParserBuilder, b as createParser, c as createSerializer, n as parseAsArrayOf, g as parseAsBoolean, f as parseAsFloat, e as parseAsHex, d as parseAsInteger, i as parseAsIsoDateTime, m as parseAsJson, l as parseAsNumberLiteral, p as parseAsString, j as parseAsStringEnum, k as parseAsStringLiteral, h as parseAsTimestamp } from './serializer-_l-WZKrY.js'; | ||
import 'react'; | ||
@@ -144,4 +144,4 @@ | ||
*/ | ||
declare function useQueryStates<KeyMap extends UseQueryStatesKeysMap>(keyMap: KeyMap, { history, scroll, shallow, throttleMs, startTransition }?: Partial<UseQueryStatesOptions>): UseQueryStatesReturn<KeyMap>; | ||
declare function useQueryStates<KeyMap extends UseQueryStatesKeysMap>(keyMap: KeyMap, { history, scroll, shallow, throttleMs, clearOnDefault, startTransition }?: Partial<UseQueryStatesOptions>): UseQueryStatesReturn<KeyMap>; | ||
export { Options, Parser, QueryUpdateNotificationArgs, QueryUpdateSource, SetValues, UseQueryStateOptions, UseQueryStateReturn, UseQueryStatesKeysMap, UseQueryStatesOptions, UseQueryStatesReturn, Values, useQueryState, useQueryStates }; | ||
export { Options, Parser, type QueryUpdateNotificationArgs, type QueryUpdateSource, type SetValues, type UseQueryStateOptions, type UseQueryStateReturn, type UseQueryStatesKeysMap, type UseQueryStatesOptions, type UseQueryStatesReturn, type Values, useQueryState, useQueryStates }; |
@@ -1,3 +0,3 @@ | ||
import { getDefaultThrottle, error, debug, safeParse } from './chunk-U6MGTLTY.js'; | ||
export { createParser, parseAsArrayOf, parseAsBoolean, parseAsFloat, parseAsHex, parseAsInteger, parseAsIsoDateTime, parseAsJson, parseAsNumberLiteral, parseAsString, parseAsStringEnum, parseAsStringLiteral, parseAsTimestamp } from './chunk-U6MGTLTY.js'; | ||
import { getDefaultThrottle, error, debug, safeParse, renderQueryString } from './chunk-ZKLWH2HF.js'; | ||
export { createParser, createSerializer, parseAsArrayOf, parseAsBoolean, parseAsFloat, parseAsHex, parseAsInteger, parseAsIsoDateTime, parseAsJson, parseAsNumberLiteral, parseAsString, parseAsStringEnum, parseAsStringLiteral, parseAsTimestamp } from './chunk-ZKLWH2HF.js'; | ||
import { useRouter, useSearchParams } from 'next/navigation.js'; | ||
@@ -7,17 +7,2 @@ import React from 'react'; | ||
// src/url-encoding.ts | ||
function renderQueryString(search) { | ||
if (search.size === 0) { | ||
return ""; | ||
} | ||
const query = []; | ||
for (const [key, value] of search.entries()) { | ||
query.push(`${key}=${encodeQueryValue(value)}`); | ||
} | ||
return "?" + query.join("&"); | ||
} | ||
function encodeQueryValue(input) { | ||
return input.replace(/%/g, "%25").replace(/\+/g, "%2B").replace(/ /g, "+").replace(/#/g, "%23").replace(/&/g, "%26").replace(/"/g, "%22").replace(/'/g, "%27").replace(/`/g, "%60").replace(/</g, "%3C").replace(/>/g, "%3E"); | ||
} | ||
// src/update-queue.ts | ||
@@ -142,5 +127,6 @@ var FLUSH_RATE_LIMIT_MS = getDefaultThrottle(); | ||
const updateMethod = options.history === "push" ? history.pushState : history.replaceState; | ||
const state = (window.next?.version ?? "") >= "14.1.0" ? null : history.state; | ||
updateMethod.call( | ||
history, | ||
history.state, | ||
state, | ||
// Our own updates have a marker to prevent syncing | ||
@@ -159,5 +145,3 @@ // when the URL changes (we've already sync'd them up | ||
router.replace(url, { | ||
scroll: false, | ||
// @ts-expect-error - pages router fix, but not exposed in navigation types | ||
shallow: false | ||
scroll: false | ||
}); | ||
@@ -202,3 +186,3 @@ }); | ||
function patchHistory() { | ||
const version = "2.0.0-beta.1"; | ||
const version = "2.0.0-snapshot.2024-05-19.17e1fabc"; | ||
const patched = history.__nuqs_patched; | ||
@@ -268,3 +252,5 @@ if (patched) { | ||
serialize = String, | ||
eq = (a, b) => a === b, | ||
defaultValue = void 0, | ||
clearOnDefault = false, | ||
startTransition | ||
@@ -278,2 +264,4 @@ } = { | ||
serialize: String, | ||
eq: (a, b) => a === b, | ||
clearOnDefault: false, | ||
defaultValue: void 0 | ||
@@ -285,9 +273,3 @@ }) { | ||
const queueValue = getQueuedValue(key); | ||
const urlValue = typeof location !== "object" ? ( | ||
// SSR | ||
initialSearchParams?.get(key) ?? null | ||
) : ( | ||
// Components mounted after page load must use the current URL value | ||
new URLSearchParams(location.search).get(key) ?? null | ||
); | ||
const urlValue = initialSearchParams?.get(key) ?? null; | ||
const value = queueValue ?? urlValue; | ||
@@ -326,3 +308,6 @@ return value === null ? null : safeParse(parse, value, key); | ||
(stateUpdater, options = {}) => { | ||
const newValue = isUpdaterFunction(stateUpdater) ? stateUpdater(stateRef.current ?? defaultValue ?? null) : stateUpdater; | ||
let newValue = isUpdaterFunction(stateUpdater) ? stateUpdater(stateRef.current ?? defaultValue ?? null) : stateUpdater; | ||
if ((options.clearOnDefault || clearOnDefault) && newValue !== null && defaultValue !== void 0 && eq(newValue, defaultValue)) { | ||
newValue = null; | ||
} | ||
emitter.emit(key, newValue); | ||
@@ -351,2 +336,3 @@ enqueueQueryStringUpdate(key, newValue, serialize, { | ||
throttleMs = FLUSH_RATE_LIMIT_MS, | ||
clearOnDefault = false, | ||
startTransition | ||
@@ -357,8 +343,5 @@ } = {}) { | ||
const initialSearchParams = useSearchParams(); | ||
const [internalState, setInternalState] = React.useState(() => { | ||
if (typeof location !== "object") { | ||
return parseMap(keyMap, initialSearchParams ?? new URLSearchParams()); | ||
} | ||
return parseMap(keyMap, new URLSearchParams(location.search)); | ||
}); | ||
const [internalState, setInternalState] = React.useState( | ||
() => parseMap(keyMap, initialSearchParams ?? new URLSearchParams()) | ||
); | ||
const stateRef = React.useRef(internalState); | ||
@@ -421,3 +404,3 @@ debug( | ||
debug("[nuq+ `%s`] setState: %O", keys, newState); | ||
for (const [key, value] of Object.entries(newState)) { | ||
for (let [key, value] of Object.entries(newState)) { | ||
const config = keyMap[key]; | ||
@@ -427,2 +410,5 @@ if (!config) { | ||
} | ||
if ((options.clearOnDefault || clearOnDefault) && value !== null && config.defaultValue !== void 0 && (config.eq ?? ((a, b) => a === b))(value, config.defaultValue)) { | ||
value = null; | ||
} | ||
emitter.emit(key, value); | ||
@@ -429,0 +415,0 @@ enqueueQueryStringUpdate(key, value, config.serialize ?? String, { |
@@ -1,3 +0,3 @@ | ||
import { a as ParserBuilder } from './parsers-5ac22957.js'; | ||
export { P as Parser, c as createParser, m as parseAsArrayOf, f as parseAsBoolean, e as parseAsFloat, d as parseAsHex, b as parseAsInteger, h as parseAsIsoDateTime, l as parseAsJson, k as parseAsNumberLiteral, p as parseAsString, i as parseAsStringEnum, j as parseAsStringLiteral, g as parseAsTimestamp } from './parsers-5ac22957.js'; | ||
import { a as ParserBuilder } from './serializer-_l-WZKrY.js'; | ||
export { P as Parser, b as createParser, c as createSerializer, n as parseAsArrayOf, g as parseAsBoolean, f as parseAsFloat, e as parseAsHex, d as parseAsInteger, i as parseAsIsoDateTime, m as parseAsJson, l as parseAsNumberLiteral, p as parseAsString, j as parseAsStringEnum, k as parseAsStringLiteral, h as parseAsTimestamp } from './serializer-_l-WZKrY.js'; | ||
import 'react'; | ||
@@ -13,2 +13,2 @@ | ||
export { ParserBuilder, SearchParams, createSearchParamsCache }; | ||
export { ParserBuilder, type SearchParams, createSearchParamsCache }; |
@@ -1,10 +0,16 @@ | ||
import { error } from './chunk-U6MGTLTY.js'; | ||
export { createParser, parseAsArrayOf, parseAsBoolean, parseAsFloat, parseAsHex, parseAsInteger, parseAsIsoDateTime, parseAsJson, parseAsNumberLiteral, parseAsString, parseAsStringEnum, parseAsStringLiteral, parseAsTimestamp } from './chunk-U6MGTLTY.js'; | ||
import { error } from './chunk-ZKLWH2HF.js'; | ||
export { createParser, createSerializer, parseAsArrayOf, parseAsBoolean, parseAsFloat, parseAsHex, parseAsInteger, parseAsIsoDateTime, parseAsJson, parseAsNumberLiteral, parseAsString, parseAsStringEnum, parseAsStringLiteral, parseAsTimestamp } from './chunk-ZKLWH2HF.js'; | ||
import { cache } from 'react'; | ||
var $input = Symbol("Input"); | ||
function createSearchParamsCache(parsers) { | ||
const getCache = cache(() => ({})); | ||
const getCache = cache(() => ({ | ||
searchParams: {} | ||
})); | ||
function parse(searchParams) { | ||
const c = getCache(); | ||
if (Object.isFrozen(c)) { | ||
if (Object.isFrozen(c.searchParams)) { | ||
if (searchParams === c[$input]) { | ||
return all(); | ||
} | ||
throw new Error(error(501)); | ||
@@ -14,16 +20,17 @@ } | ||
const parser = parsers[key]; | ||
c[key] = parser.parseServerSide(searchParams[key]); | ||
c.searchParams[key] = parser.parseServerSide(searchParams[key]); | ||
} | ||
return Object.freeze(c); | ||
c[$input] = searchParams; | ||
return Object.freeze(c.searchParams); | ||
} | ||
function all() { | ||
const c = getCache(); | ||
if (Object.keys(c).length === 0) { | ||
const { searchParams } = getCache(); | ||
if (Object.keys(searchParams).length === 0) { | ||
throw new Error(error(500)); | ||
} | ||
return c; | ||
return searchParams; | ||
} | ||
function get(key) { | ||
const c = getCache(); | ||
const entry = c[key]; | ||
const { searchParams } = getCache(); | ||
const entry = searchParams[key]; | ||
if (typeof entry === "undefined") { | ||
@@ -30,0 +37,0 @@ throw new Error( |
{ | ||
"name": "nuqs", | ||
"version": "2.0.0-beta.1", | ||
"version": "2.0.0-snapshot.2024-05-19.17e1fabc", | ||
"description": "Type-safe search params state manager for Next.js - Like React.useState, but stored in the URL query string", | ||
@@ -31,3 +31,4 @@ "license": "MIT", | ||
"dist/", | ||
"parsers.d.ts" | ||
"parsers.d.ts", | ||
"server.d.ts" | ||
], | ||
@@ -48,13 +49,4 @@ "type": "module", | ||
}, | ||
"scripts": { | ||
"dev": "tsup --watch --external=react", | ||
"build": "tsup --clean --external=react", | ||
"test": "run-p test:*", | ||
"test:types": "tsd", | ||
"test:unit": "vitest run", | ||
"test:size": "size-limit", | ||
"prepack": "./scripts/prepack.sh" | ||
}, | ||
"peerDependencies": { | ||
"next": ">=13.4 <14.0.2 || ^14.0.4", | ||
"next": ">=13.4 <14.0.2 || >=14.1.2", | ||
"react": "^18.2.0" | ||
@@ -66,15 +58,15 @@ }, | ||
"devDependencies": { | ||
"@size-limit/preset-small-lib": "^11.0.1", | ||
"@types/node": "^20.10.4", | ||
"@types/react": "^18.2.45", | ||
"@types/react-dom": "^18.2.17", | ||
"next": "14.0.1", | ||
"@size-limit/preset-small-lib": "^11.1.2", | ||
"@types/node": "^20.12.8", | ||
"@types/react": "^18.3.2", | ||
"@types/react-dom": "^18.3.0", | ||
"next": "^14.2.3", | ||
"npm-run-all": "^4.1.5", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"size-limit": "^11.0.1", | ||
"tsd": "^0.29.0", | ||
"tsup": "^7.2.0", | ||
"typescript": "^5.3.3", | ||
"vitest": "^0.34.6" | ||
"react": "rc", | ||
"react-dom": "rc", | ||
"size-limit": "^11.1.2", | ||
"tsd": "^0.30.7", | ||
"tsup": "^8.0.2", | ||
"typescript": "^5.4.5", | ||
"vitest": "^1.6.0" | ||
}, | ||
@@ -88,3 +80,3 @@ "tsd": { | ||
"path": "dist/index.js", | ||
"limit": "4 kB", | ||
"limit": "5 kB", | ||
"ignore": [ | ||
@@ -102,3 +94,12 @@ "react" | ||
} | ||
] | ||
} | ||
], | ||
"scripts": { | ||
"dev": "tsup --watch --external=react", | ||
"build": "tsup --clean --external=react", | ||
"postbuild": "size-limit --json > size.json", | ||
"test": "run-p test:*", | ||
"test:types": "tsd", | ||
"test:unit": "vitest run", | ||
"test:size": "size-limit" | ||
} | ||
} |
@@ -199,3 +199,3 @@ # useQueryState for Next.js | ||
See the [server-side parsing demo](<./packages/docs/src/app/(pages)/playground/server-side-parsing/>) | ||
See the [server-side parsing demo](<./packages/docs/src/app/playground/(demos)/pagination>) | ||
for a live example showing how to reuse parser configurations between | ||
@@ -437,3 +437,3 @@ client and server code. | ||
Note: see this example running in the [hex-colors demo](<./packages/docs/src/app/(pages)/playground/hex-colors/page.tsx>). | ||
Note: see this example running in the [hex-colors demo](<./packages/docs/src/app/playground/(demos)/hex-colors/page.tsx>). | ||
@@ -535,3 +535,3 @@ ## Multiple Queries (batching) | ||
} from 'nuqs/server' | ||
// Note: import from '…/server' to avoid the "use client" directive | ||
// Note: import from 'nuqs/server' to avoid the "use client" directive | ||
@@ -631,2 +631,61 @@ export const searchParamsCache = createSearchParamsCache({ | ||
## Serializer helper | ||
To populate `<Link>` components with state values, you can use the `createSerializer` | ||
helper. | ||
Pass it an object describing your search params, and it will give you a function | ||
to call with values, that generates a query string serialized as the hooks would do. | ||
Example: | ||
```ts | ||
import { | ||
createSerializer, | ||
parseAsInteger, | ||
parseAsIsoDateTime, | ||
parseAsString, | ||
parseAsStringLiteral | ||
} from 'nuqs/server' | ||
const searchParams = { | ||
search: parseAsString, | ||
limit: parseAsInteger, | ||
from: parseAsIsoDateTime, | ||
to: parseAsIsoDateTime, | ||
sortBy: parseAsStringLiteral(['asc', 'desc'] as const) | ||
} | ||
// Create a serializer function by passing the description of the search params to accept | ||
const serialize = createSerializer(searchParams) | ||
// Then later, pass it some values (a subset) and render them to a query string | ||
serialize({ | ||
search: 'foo bar', | ||
limit: 10, | ||
from: new Date('2024-01-01'), | ||
// here, we omit `to`, which won't be added | ||
sortBy: null // null values are also not rendered | ||
}) | ||
// ?search=foo+bar&limit=10&from=2024-01-01T00:00:00.000Z | ||
``` | ||
### Base parameter | ||
The returned `serialize` function can take a base parameter over which to | ||
append/amend the search params: | ||
```ts | ||
serialize('/path?baz=qux', { foo: 'bar' }) // /path?baz=qux&foo=bar | ||
const search = new URLSearchParams('?baz=qux') | ||
serialize(search, { foo: 'bar' }) // ?baz=qux&foo=bar | ||
const url = new URL('https://example.com/path?baz=qux') | ||
serialize(url, { foo: 'bar' }) // https://example.com/path?baz=qux&foo=bar | ||
// Passing null removes existing values | ||
serialize('?remove=me', { foo: 'bar', remove: null }) // ?foo=bar | ||
``` | ||
## Testing | ||
@@ -633,0 +692,0 @@ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
70643
10
1192
805