@echojs-ecosystem/url-state 0.1.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/README.md +86 -0
- package/dist/index.d.ts +210 -0
- package/dist/index.js +993 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# @echojs-ecosystem/url-state
|
|
4
|
+
|
|
5
|
+
**Type-safe URL search params — nuqs-style, signal-native.**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@echojs-ecosystem/url-state)
|
|
8
|
+
[](https://echojs.dev/docs/packages/url-state)
|
|
9
|
+
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
Manage **query string state** with typed parsers, defaults, and router integration. Built for EchoJS signals — no React hooks.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **`createQueryParam`** — single typed param
|
|
19
|
+
- **`createQueryParams`** — object of params with batch updates
|
|
20
|
+
- **Rich parsers** — string, int, float, bool, literal, array, JSON + Standard Schema
|
|
21
|
+
- **Router adapter** — auto-sync with `@echojs-ecosystem/router/hyperdom`
|
|
22
|
+
- **History control** — `push` / `replace` per update
|
|
23
|
+
- **`urlKeys`** — remap param names in the URL
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @echojs-ecosystem/url-state @echojs-ecosystem/reactivity
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import {
|
|
35
|
+
createQueryParams,
|
|
36
|
+
parseAsInteger,
|
|
37
|
+
parseAsString,
|
|
38
|
+
parseAsBoolean,
|
|
39
|
+
} from "@echojs-ecosystem/url-state";
|
|
40
|
+
|
|
41
|
+
const filters = createQueryParams({
|
|
42
|
+
q: parseAsString.withDefault(""),
|
|
43
|
+
page: parseAsInteger.withDefault(1),
|
|
44
|
+
inStock: parseAsBoolean.withDefault(false),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
filters.value(); // { q: "", page: 1, inStock: false }
|
|
48
|
+
filters.set({ q: "bike", page: 2 });
|
|
49
|
+
filters.update((v) => ({ ...v, page: v.page + 1 }));
|
|
50
|
+
filters.reset();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### With router
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { createRouter } from "@echojs-ecosystem/router/hyperdom";
|
|
57
|
+
|
|
58
|
+
export const appRouter = createRouter({ routes });
|
|
59
|
+
|
|
60
|
+
export const filters = appRouter.createQueryParams({
|
|
61
|
+
q: parseAsString.withDefault(""),
|
|
62
|
+
page: parseAsInteger.withDefault(1),
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Parsers
|
|
67
|
+
|
|
68
|
+
| Parser | Example |
|
|
69
|
+
|--------|---------|
|
|
70
|
+
| `parseAsString` | `?q=hello` |
|
|
71
|
+
| `parseAsInteger` / `parseAsFloat` | `?page=2` |
|
|
72
|
+
| `parseAsBoolean` | `?open=true` |
|
|
73
|
+
| `parseAsLiteral(["a","b"])` | Enum values |
|
|
74
|
+
| `parseAsArrayOf` | `?tag=a&tag=b` |
|
|
75
|
+
| `parseAsJson(schema)` | Complex objects |
|
|
76
|
+
|
|
77
|
+
## Related packages
|
|
78
|
+
|
|
79
|
+
| Package | Role |
|
|
80
|
+
|---------|------|
|
|
81
|
+
| [`@echojs-ecosystem/router`](https://www.npmjs.com/package/@echojs-ecosystem/router) | SPA URL sync |
|
|
82
|
+
| [`@echojs-ecosystem/reactivity`](https://www.npmjs.com/package/@echojs-ecosystem/reactivity) | Signal-backed param state |
|
|
83
|
+
|
|
84
|
+
## Documentation
|
|
85
|
+
|
|
86
|
+
[echojs.dev/docs/packages/url-state](https://echojs.dev/docs/packages/url-state)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { ReadValue, Signal } from '@echojs-ecosystem/reactivity';
|
|
2
|
+
|
|
3
|
+
type DebounceController = {
|
|
4
|
+
kind: "debounce";
|
|
5
|
+
ms: number;
|
|
6
|
+
schedule(fn: () => void): void;
|
|
7
|
+
cancel(): void;
|
|
8
|
+
};
|
|
9
|
+
declare const debounce: (ms: number) => DebounceController;
|
|
10
|
+
|
|
11
|
+
type ThrottleController = {
|
|
12
|
+
kind: "throttle";
|
|
13
|
+
ms: number;
|
|
14
|
+
schedule(fn: () => void): void;
|
|
15
|
+
cancel(): void;
|
|
16
|
+
};
|
|
17
|
+
declare const throttle: (ms: number) => ThrottleController;
|
|
18
|
+
|
|
19
|
+
type SearchParamValue = string | readonly string[] | null;
|
|
20
|
+
|
|
21
|
+
type HistoryMode = "replace" | "push";
|
|
22
|
+
/**
|
|
23
|
+
* Whether default values appear in the URL for the whole query-params group.
|
|
24
|
+
*
|
|
25
|
+
* - `"hide"` — defaults omitted (`?page=1` absent when default is `1`)
|
|
26
|
+
* - `"show"` — defaults serialized in the URL
|
|
27
|
+
*/
|
|
28
|
+
type DefaultVisibility = "hide" | "show";
|
|
29
|
+
/** @deprecated Use {@link DefaultVisibility} */
|
|
30
|
+
type DefaultVisibilityMode = DefaultVisibility;
|
|
31
|
+
/** @deprecated Use {@link DefaultVisibility} */
|
|
32
|
+
type DefaultInUrlBehavior = DefaultVisibility;
|
|
33
|
+
/** @deprecated Use {@link DefaultVisibility} */
|
|
34
|
+
type DefaultParamsBehavior = DefaultVisibility;
|
|
35
|
+
type UrlStateAdapter = {
|
|
36
|
+
kind: string;
|
|
37
|
+
getSearch(): string;
|
|
38
|
+
setSearch(search: string, options?: {
|
|
39
|
+
history?: HistoryMode;
|
|
40
|
+
shallow?: boolean;
|
|
41
|
+
scroll?: boolean;
|
|
42
|
+
}): void;
|
|
43
|
+
subscribe(listener: () => void): () => void;
|
|
44
|
+
};
|
|
45
|
+
type QueryStateSetOptions = {
|
|
46
|
+
adapter?: UrlStateAdapter;
|
|
47
|
+
history?: HistoryMode;
|
|
48
|
+
shallow?: boolean;
|
|
49
|
+
scroll?: boolean;
|
|
50
|
+
defaultVisibility?: DefaultVisibility;
|
|
51
|
+
/** @deprecated Use {@link defaultVisibility} */
|
|
52
|
+
defaultParamsBehavior?: DefaultVisibility;
|
|
53
|
+
/** @deprecated Use {@link defaultVisibility} */
|
|
54
|
+
clearOnDefault?: boolean;
|
|
55
|
+
limitUrlUpdates?: ReturnType<typeof throttle> | ReturnType<typeof debounce> | false;
|
|
56
|
+
equals?: false | ((a: unknown, b: unknown) => boolean);
|
|
57
|
+
};
|
|
58
|
+
type QueryStateOptions = QueryStateSetOptions;
|
|
59
|
+
type CreateQueryParamsOptions<Schema extends Record<string, QueryParamParser<any>>> = QueryStateOptions & {
|
|
60
|
+
urlKeys?: Partial<Record<keyof Schema, string>>;
|
|
61
|
+
};
|
|
62
|
+
type ParserMode = "single" | "multi";
|
|
63
|
+
type Parser<Value> = {
|
|
64
|
+
parserMode?: ParserMode;
|
|
65
|
+
parse(value: SearchParamValue): Value | null;
|
|
66
|
+
serialize(value: Value): SearchParamValue;
|
|
67
|
+
/** Custom equality for `defaultVisibility: "hide"` (objects, arrays, …). */
|
|
68
|
+
eq?: (a: Value, b: Value) => boolean;
|
|
69
|
+
defaultValue?: Value | undefined;
|
|
70
|
+
options?: Partial<QueryStateOptions> | undefined;
|
|
71
|
+
withDefault(defaultValue: Value): ParserWithDefault<Value>;
|
|
72
|
+
withOptions(options: Partial<QueryStateOptions>): Parser<Value>;
|
|
73
|
+
};
|
|
74
|
+
/** Parser for repeated query keys (`?tag=a&tag=b`). */
|
|
75
|
+
type MultiParser<Value> = Parser<Value> & {
|
|
76
|
+
parserMode: "multi";
|
|
77
|
+
};
|
|
78
|
+
type QueryParamParser<Value> = Parser<Value> | MultiParser<Value>;
|
|
79
|
+
type ParserWithDefault<Value> = Parser<Value> & {
|
|
80
|
+
defaultValue: Value;
|
|
81
|
+
};
|
|
82
|
+
type ParsedValue<P> = P extends ParserWithDefault<infer Value> ? Value : P extends Parser<infer Value> ? Value | null : never;
|
|
83
|
+
type ParsedSchema<Schema extends Record<string, QueryParamParser<any>>> = {
|
|
84
|
+
[K in keyof Schema]: ParsedValue<Schema[K]>;
|
|
85
|
+
};
|
|
86
|
+
type QueryParamState<Value> = {
|
|
87
|
+
kind: "query-param";
|
|
88
|
+
key: string;
|
|
89
|
+
value(): ReadValue<Value>;
|
|
90
|
+
set(value: Value | null, options?: QueryStateSetOptions): void;
|
|
91
|
+
update(updater: (value: ReadValue<Value>) => Value | null, options?: QueryStateSetOptions): void;
|
|
92
|
+
reset(options?: QueryStateSetOptions): void;
|
|
93
|
+
clear(options?: QueryStateSetOptions): void;
|
|
94
|
+
$value: Signal<Value>;
|
|
95
|
+
$rawValue: Signal<SearchParamValue>;
|
|
96
|
+
subscribe(listener: (value: ReadValue<Value>, prevValue: ReadValue<Value>) => void): () => void;
|
|
97
|
+
};
|
|
98
|
+
type QueryParamsState<Schema extends Record<string, QueryParamParser<any>>> = {
|
|
99
|
+
kind: "query-params";
|
|
100
|
+
value(): ReadValue<ParsedSchema<Schema>>;
|
|
101
|
+
set(value: Partial<ParsedSchema<Schema>> | null, options?: QueryStateSetOptions): void;
|
|
102
|
+
update(updater: (value: ReadValue<ParsedSchema<Schema>>) => Partial<ParsedSchema<Schema>> | null, options?: QueryStateSetOptions): void;
|
|
103
|
+
reset(options?: QueryStateSetOptions): void;
|
|
104
|
+
clear(options?: QueryStateSetOptions): void;
|
|
105
|
+
$value: Signal<ParsedSchema<Schema>>;
|
|
106
|
+
subscribe(listener: (value: ReadValue<ParsedSchema<Schema>>, prevValue: ReadValue<ParsedSchema<Schema>>) => void): () => void;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
declare const createQueryParam: <Value>(key: string, parser: Parser<Value>, options?: QueryStateOptions) => QueryParamState<Value>;
|
|
110
|
+
|
|
111
|
+
declare const createQueryParams: <Schema extends Record<string, Parser<any>>>(schema: Schema, options?: CreateQueryParamsOptions<Schema>) => QueryParamsState<Schema>;
|
|
112
|
+
|
|
113
|
+
declare const createBrowserUrlStateAdapter: () => UrlStateAdapter;
|
|
114
|
+
|
|
115
|
+
declare const createMemoryUrlStateAdapter: (initialSearch?: string) => UrlStateAdapter;
|
|
116
|
+
|
|
117
|
+
type RouterLike = {
|
|
118
|
+
readonly $fullPath: {
|
|
119
|
+
value(): string;
|
|
120
|
+
subscribe(fn: () => void): () => void;
|
|
121
|
+
};
|
|
122
|
+
go(path: string, options?: {
|
|
123
|
+
replace?: boolean;
|
|
124
|
+
}): void;
|
|
125
|
+
replace?(path: string): void;
|
|
126
|
+
};
|
|
127
|
+
declare const createRouterUrlStateAdapter: (router: RouterLike) => UrlStateAdapter | undefined;
|
|
128
|
+
|
|
129
|
+
type RouterBoundQueryParams = <Schema extends Record<string, Parser<any>>>(schema: Schema, options?: Omit<CreateQueryParamsOptions<Schema>, "adapter">) => QueryParamsState<Schema>;
|
|
130
|
+
type RouterWithQueryParams<TRouter extends RouterLike> = TRouter & {
|
|
131
|
+
createQueryParams: RouterBoundQueryParams;
|
|
132
|
+
};
|
|
133
|
+
declare const attachRouterQueryParams: <TRouter extends RouterLike>(router: TRouter) => RouterWithQueryParams<TRouter>;
|
|
134
|
+
|
|
135
|
+
declare const getUrlStateRouter: () => RouterLike | null;
|
|
136
|
+
|
|
137
|
+
declare const parseAsString: Parser<string>;
|
|
138
|
+
|
|
139
|
+
declare const parseAsInteger: Parser<number>;
|
|
140
|
+
|
|
141
|
+
declare const parseAsFloat: Parser<number>;
|
|
142
|
+
|
|
143
|
+
declare const parseAsBoolean: Parser<boolean>;
|
|
144
|
+
|
|
145
|
+
declare const parseAsLiteral: <const Values extends readonly (string | number)[]>(values: Values) => Parser<Values[number]>;
|
|
146
|
+
declare const parseAsStringLiteral: <const Values extends readonly string[]>(values: Values) => Parser<Values[number]>;
|
|
147
|
+
declare const parseAsNumberLiteral: <const Values extends readonly number[]>(values: Values) => Parser<Values[number]>;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Array serialized as repeated keys or a custom separator in one key (see second argument).
|
|
151
|
+
*
|
|
152
|
+
* For native `?tag=a&tag=b` only, prefer {@link parseAsNativeArrayOf}.
|
|
153
|
+
*/
|
|
154
|
+
declare const parseAsArrayOf: <Item>(item: Parser<Item>, separator?: string) => MultiParser<Item[]>;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Native URL arrays via repeated keys: `?tag=a&tag=b`.
|
|
158
|
+
*
|
|
159
|
+
* @see https://nuqs.dev/docs/parsers/built-in#native-arrays
|
|
160
|
+
*/
|
|
161
|
+
declare const parseAsNativeArrayOf: <Item>(item: Parser<Item>) => ParserWithDefault<Item[]>;
|
|
162
|
+
|
|
163
|
+
/** Minimal Standard Schema v1 shape (Zod 4, Valibot, ArkType, …). */
|
|
164
|
+
type StandardSchemaLike<T = unknown> = {
|
|
165
|
+
"~standard": {
|
|
166
|
+
readonly version: 1;
|
|
167
|
+
readonly vendor: string;
|
|
168
|
+
readonly validate: (value: unknown) => StandardSchemaResult<T>;
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
type StandardSchemaResult<T> = {
|
|
172
|
+
readonly value: T;
|
|
173
|
+
readonly issues?: undefined;
|
|
174
|
+
} | {
|
|
175
|
+
readonly issues: ReadonlyArray<{
|
|
176
|
+
readonly message: string;
|
|
177
|
+
}>;
|
|
178
|
+
};
|
|
179
|
+
type JsonSchema<T> = StandardSchemaLike<T> | ((value: unknown) => T | null);
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* JSON in a single query param. Optional Standard Schema (Zod, Valibot, …) or sync validator.
|
|
183
|
+
*
|
|
184
|
+
* @see https://nuqs.dev/docs/parsers/built-in#json
|
|
185
|
+
*/
|
|
186
|
+
declare const parseAsJson: <T = unknown>(schema?: JsonSchema<T>) => Parser<T>;
|
|
187
|
+
|
|
188
|
+
type CustomParserConfig<Value> = {
|
|
189
|
+
parse: (value: SearchParamValue) => Value | null;
|
|
190
|
+
serialize: (value: Value) => SearchParamValue;
|
|
191
|
+
/** Used for `defaultVisibility: "hide"` equality checks (like nuqs `eq`). */
|
|
192
|
+
eq?: (a: Value, b: Value) => boolean;
|
|
193
|
+
};
|
|
194
|
+
declare const createCustomParser: <Value>(config: CustomParserConfig<Value>) => Parser<Value>;
|
|
195
|
+
|
|
196
|
+
type CustomMultiParserConfig<Value> = {
|
|
197
|
+
parse: (values: readonly string[]) => Value | null;
|
|
198
|
+
serialize: (value: Value) => readonly string[];
|
|
199
|
+
eq?: (a: Value, b: Value) => boolean;
|
|
200
|
+
};
|
|
201
|
+
declare const createCustomMultiParser: <Value>(config: CustomMultiParserConfig<Value>) => MultiParser<Value>;
|
|
202
|
+
declare const isMultiParser: <Value>(parser: {
|
|
203
|
+
parserMode?: string;
|
|
204
|
+
}) => parser is MultiParser<Value>;
|
|
205
|
+
|
|
206
|
+
declare const parseAsIsoDate: Parser<Date>;
|
|
207
|
+
|
|
208
|
+
declare const parseAsTimestamp: Parser<number>;
|
|
209
|
+
|
|
210
|
+
export { type CustomMultiParserConfig, type CustomParserConfig, type DefaultInUrlBehavior, type DefaultParamsBehavior, type DefaultVisibility, type DefaultVisibilityMode, type JsonSchema, type MultiParser, type Parser, type ParserMode, type ParserWithDefault, type QueryParamParser, type QueryParamState, type QueryParamsState, type QueryStateOptions, type QueryStateSetOptions, type RouterBoundQueryParams, type RouterLike, type RouterWithQueryParams, type StandardSchemaLike, type UrlStateAdapter, attachRouterQueryParams, createBrowserUrlStateAdapter, createCustomMultiParser, createCustomParser, createMemoryUrlStateAdapter, createQueryParam, createQueryParams, createRouterUrlStateAdapter, debounce, getUrlStateRouter, isMultiParser, parseAsArrayOf, parseAsBoolean, parseAsFloat, parseAsInteger, parseAsIsoDate, parseAsJson, parseAsLiteral, parseAsNativeArrayOf, parseAsNumberLiteral, parseAsString, parseAsStringLiteral, parseAsTimestamp, throttle };
|