use-prms 0.2.0 → 0.4.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 +265 -56
- package/dist/hash.cjs +901 -51
- package/dist/hash.cjs.map +1 -1
- package/dist/hash.d.cts +1 -1
- package/dist/hash.d.ts +1 -1
- package/dist/hash.js +874 -49
- package/dist/hash.js.map +1 -1
- package/dist/index.cjs +901 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +462 -22
- package/dist/index.d.ts +462 -22
- package/dist/index.js +874 -49
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,35 +1,57 @@
|
|
|
1
1
|
# use-prms
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/use-prms)
|
|
4
|
+
[](https://github.com/runsascoded/use-prms/blob/main/LICENSE)
|
|
5
|
+
[](https://bundlephobia.com/package/use-prms)
|
|
6
|
+
|
|
7
|
+
Type-safe URL-parameter (query and hash) management with minimal, human-readable encoding and decoding.
|
|
8
|
+
|
|
9
|
+
<!-- `toc` -->
|
|
10
|
+
- [Features](#features)
|
|
11
|
+
- [Installation](#install)
|
|
12
|
+
- [Quick Start](#quick-start)
|
|
13
|
+
- [Built-in Param Types](#param-types)
|
|
14
|
+
- [Custom Params](#custom)
|
|
15
|
+
- [Batch Updates](#batch)
|
|
16
|
+
- [URL Encoding](#encoding)
|
|
17
|
+
- [Binary Encoding](#binary)
|
|
18
|
+
- [Framework-Agnostic Core](#core)
|
|
19
|
+
- [Hash Params](#hash)
|
|
20
|
+
- [API Reference](#api)
|
|
21
|
+
- [Examples](#examples)
|
|
22
|
+
- [Reverse Inspo](#reverse-inspo)
|
|
23
|
+
- [License](#license)
|
|
24
|
+
|
|
25
|
+
## Features <a id="features"></a>
|
|
6
26
|
|
|
7
27
|
- 🎯 **Type-safe**: Full TypeScript support with generic `Param<T>` interface
|
|
8
28
|
- 📦 **Tiny URLs**: Smart encoding - omit defaults, use short keys, `+` for spaces
|
|
9
|
-
- ⚛️ **React hooks**: `
|
|
29
|
+
- ⚛️ **React hooks**: `useUrlState()` and `useUrlStates()` for seamless integration
|
|
10
30
|
- 🔧 **Framework-agnostic**: Core utilities work anywhere, React hooks are optional
|
|
11
31
|
- 🌳 **Tree-shakeable**: ESM + CJS builds with TypeScript declarations
|
|
12
32
|
- 0️⃣ **Zero dependencies**: Except React (peer dependency, optional)
|
|
13
33
|
- 🔁 **Multi-value params**: Support for repeated keys like `?tag=a&tag=b`
|
|
14
34
|
- #️⃣ **Hash params**: Use hash fragment (`#key=value`) instead of query string
|
|
15
35
|
|
|
16
|
-
## Installation
|
|
36
|
+
## Installation <a id="install"></a>
|
|
17
37
|
|
|
18
38
|
```bash
|
|
19
39
|
npm install use-prms
|
|
20
|
-
|
|
40
|
+
```
|
|
41
|
+
Or:
|
|
42
|
+
```bash
|
|
21
43
|
pnpm add use-prms
|
|
22
44
|
```
|
|
23
45
|
|
|
24
|
-
## Quick Start
|
|
46
|
+
## Quick Start <a id="quick-start"></a>
|
|
25
47
|
|
|
26
48
|
```typescript
|
|
27
|
-
import {
|
|
49
|
+
import { useUrlState, boolParam, stringParam, intParam } from 'use-prms'
|
|
28
50
|
|
|
29
51
|
function MyComponent() {
|
|
30
|
-
const [zoom, setZoom] =
|
|
31
|
-
const [device, setDevice] =
|
|
32
|
-
const [count, setCount] =
|
|
52
|
+
const [zoom, setZoom] = useUrlState('z', boolParam)
|
|
53
|
+
const [device, setDevice] = useUrlState('d', stringParam())
|
|
54
|
+
const [count, setCount] = useUrlState('n', intParam(10))
|
|
33
55
|
|
|
34
56
|
// URL: ?z&d=gym&n=5
|
|
35
57
|
// zoom = true, device = "gym", count = 5
|
|
@@ -44,35 +66,35 @@ function MyComponent() {
|
|
|
44
66
|
}
|
|
45
67
|
```
|
|
46
68
|
|
|
47
|
-
## Built-in Param Types
|
|
69
|
+
## Built-in Param Types <a id="param-types"></a>
|
|
48
70
|
|
|
49
71
|
### Boolean
|
|
50
72
|
```typescript
|
|
51
|
-
const [enabled, setEnabled] =
|
|
73
|
+
const [enabled, setEnabled] = useUrlState('e', boolParam)
|
|
52
74
|
// ?e → true
|
|
53
75
|
// (absent) → false
|
|
54
76
|
```
|
|
55
77
|
|
|
56
78
|
### Strings
|
|
57
79
|
```typescript
|
|
58
|
-
const [name, setName] =
|
|
59
|
-
const [mode, setMode] =
|
|
80
|
+
const [name, setName] = useUrlState('n', stringParam()) // optional
|
|
81
|
+
const [mode, setMode] = useUrlState('m', defStringParam('auto')) // with default
|
|
60
82
|
// ?n=foo → "foo"
|
|
61
83
|
// (absent) → undefined / "auto"
|
|
62
84
|
```
|
|
63
85
|
|
|
64
86
|
### Numbers
|
|
65
87
|
```typescript
|
|
66
|
-
const [count, setCount] =
|
|
67
|
-
const [ratio, setRatio] =
|
|
68
|
-
const [id, setId] =
|
|
88
|
+
const [count, setCount] = useUrlState('c', intParam(0))
|
|
89
|
+
const [ratio, setRatio] = useUrlState('r', floatParam(1.0))
|
|
90
|
+
const [id, setId] = useUrlState('id', optIntParam) // number | null
|
|
69
91
|
// ?c=5&r=1.5&id=123 → 5, 1.5, 123
|
|
70
92
|
// (absent) → 0, 1.0, null
|
|
71
93
|
```
|
|
72
94
|
|
|
73
95
|
### Enums
|
|
74
96
|
```typescript
|
|
75
|
-
const [theme, setTheme] =
|
|
97
|
+
const [theme, setTheme] = useUrlState(
|
|
76
98
|
't',
|
|
77
99
|
enumParam('light', ['light', 'dark', 'auto'] as const)
|
|
78
100
|
)
|
|
@@ -82,20 +104,20 @@ const [theme, setTheme] = useUrlParam(
|
|
|
82
104
|
|
|
83
105
|
### Arrays (delimiter-separated)
|
|
84
106
|
```typescript
|
|
85
|
-
const [tags, setTags] =
|
|
86
|
-
const [ids, setIds] =
|
|
107
|
+
const [tags, setTags] = useUrlState('tags', stringsParam([], ','))
|
|
108
|
+
const [ids, setIds] = useUrlState('ids', numberArrayParam([]))
|
|
87
109
|
// ?tags=foo,bar,baz → ["foo", "bar", "baz"]
|
|
88
110
|
// ?ids=1,2,3 → [1, 2, 3]
|
|
89
111
|
```
|
|
90
112
|
|
|
91
113
|
### Multi-value Arrays (repeated keys)
|
|
92
114
|
```typescript
|
|
93
|
-
import {
|
|
115
|
+
import { useMultiUrlState, multiStringParam, multiIntParam } from 'use-prms'
|
|
94
116
|
|
|
95
|
-
const [tags, setTags] =
|
|
117
|
+
const [tags, setTags] = useMultiUrlState('tag', multiStringParam())
|
|
96
118
|
// ?tag=foo&tag=bar&tag=baz → ["foo", "bar", "baz"]
|
|
97
119
|
|
|
98
|
-
const [ids, setIds] =
|
|
120
|
+
const [ids, setIds] = useMultiUrlState('id', multiIntParam())
|
|
99
121
|
// ?id=1&id=2&id=3 → [1, 2, 3]
|
|
100
122
|
|
|
101
123
|
// Also available: multiFloatParam()
|
|
@@ -104,14 +126,14 @@ const [ids, setIds] = useMultiUrlParam('id', multiIntParam())
|
|
|
104
126
|
### Compact Code Mapping
|
|
105
127
|
```typescript
|
|
106
128
|
// Single value with short codes
|
|
107
|
-
const [metric, setMetric] =
|
|
129
|
+
const [metric, setMetric] = useUrlState('y', codeParam('Rides', {
|
|
108
130
|
Rides: 'r',
|
|
109
131
|
Minutes: 'm',
|
|
110
132
|
}))
|
|
111
133
|
// ?y=m → "Minutes", omitted for default "Rides"
|
|
112
134
|
|
|
113
135
|
// Multi-value with short codes (omits when all selected)
|
|
114
|
-
const [regions, setRegions] =
|
|
136
|
+
const [regions, setRegions] = useUrlState('r', codesParam(
|
|
115
137
|
['NYC', 'JC', 'HOB'],
|
|
116
138
|
{ NYC: 'n', JC: 'j', HOB: 'h' }
|
|
117
139
|
))
|
|
@@ -120,7 +142,7 @@ const [regions, setRegions] = useUrlParam('r', codesParam(
|
|
|
120
142
|
|
|
121
143
|
### Pagination
|
|
122
144
|
```typescript
|
|
123
|
-
const [page, setPage] =
|
|
145
|
+
const [page, setPage] = useUrlState('p', paginationParam(20))
|
|
124
146
|
// Encodes offset + pageSize compactly using + as delimiter:
|
|
125
147
|
// { offset: 0, pageSize: 20 } → (omitted)
|
|
126
148
|
// { offset: 0, pageSize: 50 } → ?p=+50
|
|
@@ -128,7 +150,7 @@ const [page, setPage] = useUrlParam('p', paginationParam(20))
|
|
|
128
150
|
// { offset: 100, pageSize: 50 } → ?p=100+50
|
|
129
151
|
```
|
|
130
152
|
|
|
131
|
-
## Custom Params
|
|
153
|
+
## Custom Params <a id="custom"></a>
|
|
132
154
|
|
|
133
155
|
Create your own param encoders/decoders:
|
|
134
156
|
|
|
@@ -152,18 +174,18 @@ const dateParam: Param<Date> = {
|
|
|
152
174
|
}
|
|
153
175
|
}
|
|
154
176
|
|
|
155
|
-
const [date, setDate] =
|
|
177
|
+
const [date, setDate] = useUrlState('d', dateParam)
|
|
156
178
|
// ?d=251123 → Date(2025, 10, 23)
|
|
157
179
|
```
|
|
158
180
|
|
|
159
|
-
## Batch Updates
|
|
181
|
+
## Batch Updates <a id="batch"></a>
|
|
160
182
|
|
|
161
|
-
Use `
|
|
183
|
+
Use `useUrlStates()` to update multiple parameters atomically:
|
|
162
184
|
|
|
163
185
|
```typescript
|
|
164
|
-
import {
|
|
186
|
+
import { useUrlStates, intParam, boolParam } from 'use-prms'
|
|
165
187
|
|
|
166
|
-
const { values, setValues } =
|
|
188
|
+
const { values, setValues } = useUrlStates({
|
|
167
189
|
page: intParam(1),
|
|
168
190
|
size: intParam(20),
|
|
169
191
|
grid: boolParam
|
|
@@ -173,7 +195,7 @@ const { values, setValues } = useUrlParams({
|
|
|
173
195
|
setValues({ page: 2, size: 50 })
|
|
174
196
|
```
|
|
175
197
|
|
|
176
|
-
## URL Encoding
|
|
198
|
+
## URL Encoding <a id="encoding"></a>
|
|
177
199
|
|
|
178
200
|
- **Spaces**: Encoded as `+` (standard form-urlencoded)
|
|
179
201
|
- **Defaults**: Omitted from URL (keeps URLs minimal)
|
|
@@ -182,44 +204,105 @@ setValues({ page: 2, size: 50 })
|
|
|
182
204
|
|
|
183
205
|
Example:
|
|
184
206
|
```typescript
|
|
185
|
-
const [devices, setDevices] =
|
|
207
|
+
const [devices, setDevices] = useUrlState('d', stringsParam([], ' '))
|
|
186
208
|
setDevices(['gym', 'bedroom'])
|
|
187
209
|
// URL: ?d=gym+bedroom
|
|
188
210
|
```
|
|
189
211
|
|
|
190
|
-
##
|
|
212
|
+
## Binary Encoding <a id="binary"></a>
|
|
213
|
+
|
|
214
|
+
For complex data that doesn't fit well into string encoding, `use-prms` provides binary encoding utilities with URL-safe base64.
|
|
215
|
+
|
|
216
|
+
### BitBuffer
|
|
217
|
+
|
|
218
|
+
Low-level bit packing for custom binary formats:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { BitBuffer } from 'use-prms'
|
|
222
|
+
|
|
223
|
+
// Encoding
|
|
224
|
+
const buf = new BitBuffer()
|
|
225
|
+
buf.encodeInt(myEnum, 3) // 3 bits for enum (0-7)
|
|
226
|
+
buf.encodeInt(myCount, 8) // 8 bits for count (0-255)
|
|
227
|
+
buf.encodeBigInt(myId, 48) // 48 bits for ID
|
|
228
|
+
const urlParam = buf.toBase64()
|
|
229
|
+
|
|
230
|
+
// Decoding
|
|
231
|
+
const buf = BitBuffer.fromBase64(urlParam)
|
|
232
|
+
const myEnum = buf.decodeInt(3)
|
|
233
|
+
const myCount = buf.decodeInt(8)
|
|
234
|
+
const myId = buf.decodeBigInt(48)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Float Params
|
|
238
|
+
|
|
239
|
+
Encode floats compactly as base64:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { floatParam } from 'use-prms'
|
|
243
|
+
|
|
244
|
+
// Lossless (11 chars, exact IEEE 754)
|
|
245
|
+
const [zoom, setZoom] = useUrlState('z', floatParam(1.0))
|
|
246
|
+
|
|
247
|
+
// Lossy (fewer chars, configurable precision)
|
|
248
|
+
const [lat, setLat] = useUrlState('lat', floatParam({
|
|
249
|
+
default: 0,
|
|
250
|
+
exp: 5, // exponent bits
|
|
251
|
+
mant: 22, // mantissa bits (~7 decimal digits)
|
|
252
|
+
}))
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Custom Alphabets
|
|
256
|
+
|
|
257
|
+
Choose between standard base64url or ASCII-sorted alphabet:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import { ALPHABETS, binaryParam, floatParam } from 'use-prms'
|
|
261
|
+
|
|
262
|
+
// Standard RFC 4648 (default)
|
|
263
|
+
// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_
|
|
264
|
+
|
|
265
|
+
// ASCII-sorted (lexicographic sort = numeric sort)
|
|
266
|
+
// -0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz
|
|
267
|
+
|
|
268
|
+
const param = floatParam({ default: 0, alphabet: 'sortable' })
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The `sortable` alphabet is useful when encoded strings need to sort in the same order as their numeric values (e.g., for database indexing).
|
|
272
|
+
|
|
273
|
+
## Framework-Agnostic Core <a id="core"></a>
|
|
191
274
|
|
|
192
275
|
Use the core utilities without React:
|
|
193
276
|
|
|
194
277
|
```typescript
|
|
195
|
-
import { boolParam,
|
|
278
|
+
import { boolParam, serializeMultiParams, parseMultiParams } from 'use-prms'
|
|
196
279
|
|
|
197
280
|
// Encode
|
|
198
|
-
const params = { z: boolParam.encode(true), d: 'gym' }
|
|
199
|
-
const search =
|
|
281
|
+
const params = { z: [boolParam.encode(true) ?? ''], d: ['gym'] }
|
|
282
|
+
const search = serializeMultiParams(params) // "z&d=gym"
|
|
200
283
|
|
|
201
284
|
// Decode
|
|
202
|
-
const parsed =
|
|
203
|
-
const zoom = boolParam.decode(parsed.z) // true
|
|
285
|
+
const parsed = parseMultiParams(window.location.search)
|
|
286
|
+
const zoom = boolParam.decode(parsed.z?.[0]) // true
|
|
204
287
|
```
|
|
205
288
|
|
|
206
|
-
## Hash Params
|
|
289
|
+
## Hash Params <a id="hash"></a>
|
|
207
290
|
|
|
208
291
|
Use hash fragment (`#key=value`) instead of query string (`?key=value`):
|
|
209
292
|
|
|
210
293
|
```typescript
|
|
211
294
|
// Just change the import path
|
|
212
|
-
import {
|
|
295
|
+
import { useUrlState, boolParam } from 'use-prms/hash'
|
|
213
296
|
|
|
214
|
-
const [zoom, setZoom] =
|
|
297
|
+
const [zoom, setZoom] = useUrlState('z', boolParam)
|
|
215
298
|
// URL: https://example.com/#z (instead of ?z)
|
|
216
299
|
```
|
|
217
300
|
|
|
218
301
|
Same API, different URL location. Useful when query strings conflict with server routing or you want params to survive page reloads without server involvement.
|
|
219
302
|
|
|
220
|
-
## API Reference
|
|
303
|
+
## API Reference <a id="api"></a>
|
|
221
304
|
|
|
222
|
-
### `
|
|
305
|
+
### `useUrlState<T>(key: string, param: Param<T>, push?: boolean)`
|
|
223
306
|
|
|
224
307
|
React hook for managing a single URL parameter.
|
|
225
308
|
|
|
@@ -228,7 +311,7 @@ React hook for managing a single URL parameter.
|
|
|
228
311
|
- `push`: Use pushState (true) or replaceState (false, default)
|
|
229
312
|
- Returns: `[value: T, setValue: (value: T) => void]`
|
|
230
313
|
|
|
231
|
-
### `
|
|
314
|
+
### `useUrlStates<P>(params: P, push?: boolean)`
|
|
232
315
|
|
|
233
316
|
React hook for managing multiple URL parameters together.
|
|
234
317
|
|
|
@@ -236,7 +319,7 @@ React hook for managing multiple URL parameters together.
|
|
|
236
319
|
- `push`: Use pushState (true) or replaceState (false, default)
|
|
237
320
|
- Returns: `{ values, setValues }`
|
|
238
321
|
|
|
239
|
-
### `
|
|
322
|
+
### `useMultiUrlState<T>(key: string, param: MultiParam<T>, push?: boolean)`
|
|
240
323
|
|
|
241
324
|
React hook for managing a multi-value URL parameter (repeated keys).
|
|
242
325
|
|
|
@@ -292,6 +375,17 @@ type MultiParam<T> = {
|
|
|
292
375
|
| `multiIntParam(init?)` | `MultiParam<number[]>` | Repeated integer params |
|
|
293
376
|
| `multiFloatParam(init?)` | `MultiParam<number[]>` | Repeated float params |
|
|
294
377
|
|
|
378
|
+
### Binary Encoding
|
|
379
|
+
|
|
380
|
+
| Export | Description |
|
|
381
|
+
|--------|-------------|
|
|
382
|
+
| `BitBuffer` | Bit-level buffer for packing/unpacking arbitrary bit widths |
|
|
383
|
+
| `binaryParam(opts)` | Create param from `toBytes`/`fromBytes` converters |
|
|
384
|
+
| `base64Param(toBytes, fromBytes)` | Shorthand for `binaryParam` |
|
|
385
|
+
| `base64Encode(bytes, opts?)` | Encode `Uint8Array` to base64 string |
|
|
386
|
+
| `base64Decode(str, opts?)` | Decode base64 string to `Uint8Array` |
|
|
387
|
+
| `ALPHABETS` | Preset alphabets: `rfc4648` (default), `sortable` (ASCII-ordered) |
|
|
388
|
+
|
|
295
389
|
### Core Utilities
|
|
296
390
|
|
|
297
391
|
- `serializeParams(params)`: Convert params object to URL query string *(deprecated, use `serializeMultiParams`)*
|
|
@@ -301,20 +395,135 @@ type MultiParam<T> = {
|
|
|
301
395
|
- `getCurrentParams()`: Get current URL params (browser only)
|
|
302
396
|
- `updateUrl(params, push?)`: Update URL without reloading (browser only)
|
|
303
397
|
|
|
304
|
-
## Examples
|
|
398
|
+
## Examples <a id="examples"></a>
|
|
305
399
|
|
|
306
400
|
Projects using `use-prms`:
|
|
307
401
|
|
|
308
|
-
- [runsascoded
|
|
402
|
+
- **[awair.runsascoded.com]** – Air quality dashboard ([GitHub][awair-gh], [usage][awair-search])
|
|
309
403
|
|
|
310
|
-
Example: [
|
|
311
|
-
- `d=+br`:
|
|
312
|
-
- `y=thZ`:
|
|
313
|
-
- `t=-3d`: time range
|
|
404
|
+
Example: [`?d=+br&y=thZ&t=-3d`][awair-example]
|
|
405
|
+
- `d=+br`: devices (leading space = "include default")
|
|
406
|
+
- `y=thZ`: Y-axes config
|
|
407
|
+
- `t=-3d`: time range
|
|
314
408
|
|
|
315
|
-
[
|
|
409
|
+
- **[ctbk.dev]** – Citi Bike trip data explorer ([GitHub][ctbk-gh], [usage][ctbk-search])
|
|
410
|
+
|
|
411
|
+
- **[kbd.rbw.sh]** – Keyboard shortcut manager demo site ([GitHub][use-kbd-gh], [usage][use-kbd-search])
|
|
412
|
+
|
|
413
|
+
[awair.runsascoded.com]: https://awair.runsascoded.com
|
|
414
|
+
[awair-gh]: https://github.com/runsascoded/awair
|
|
415
|
+
[awair-search]: https://github.com/search?q=repo%3Arunsascoded%2Fawair+use-prms&type=code
|
|
316
416
|
[awair-example]: https://awair.runsascoded.com/?d=+br&y=thZ&t=-3d
|
|
317
417
|
|
|
318
|
-
|
|
418
|
+
[ctbk.dev]: https://ctbk.dev
|
|
419
|
+
[ctbk-gh]: https://github.com/hudcostreets/ctbk.dev
|
|
420
|
+
[ctbk-search]: https://github.com/search?q=repo%3Ahudcostreets%2Fctbk.dev+use-prms&type=code
|
|
421
|
+
|
|
422
|
+
[kbd.rbw.sh]: https://kbd.rbw.sh
|
|
423
|
+
[use-kbd-gh]: https://github.com/runsascoded/use-kbd
|
|
424
|
+
[use-kbd-search]: https://github.com/search?q=repo%3Arunsascoded%2Fuse-kbd+use-prms&type=code
|
|
425
|
+
|
|
426
|
+
## Reverse Inspo <a id="reverse-inspo"></a>
|
|
427
|
+
|
|
428
|
+
It's nice when URLs are concise but also reasonably human-readable. Some examples I've seen in the wild that exhibit room for improvement:
|
|
429
|
+
|
|
430
|
+
### UUID Soup (OpenAI Careers)
|
|
431
|
+
```
|
|
432
|
+
https://openai.com/careers/search/
|
|
433
|
+
?l=e8062547-b090-4206-8f1e-7329e0014e98%2C07ed9191-5bc6-421b-9883-f1ac2e276ad7
|
|
434
|
+
&c=e1e973fe-6f0a-475f-9361-a9b6c095d869%2Cf002fe09-4cec-46b0-8add-8bf9ff438a62
|
|
435
|
+
%2Cab2b9da4-24a4-47df-8bed-1ed5a39c7036%2C687d87ec-1505-40e7-a2b5-cc7f31c0ea48
|
|
436
|
+
%2Cd36236ec-fb74-49bd-bd3f-9d8365e2e2cb%2C27c9a852-c401-450e-9480-d3b507b8f64a
|
|
437
|
+
%2C6dd4a467-446d-4093-8d57-d4633a571123%2C7cba3ac0-2b6e-4d52-ad38-e39a5f61c73f
|
|
438
|
+
%2C0f06f916-a404-414f-813f-6ac7ff781c61%2Cfb2b77c5-5f20-4a93-a1c4-c3d640d88e04
|
|
439
|
+
```
|
|
440
|
+
12 UUIDs for location and category filters. Each UUID is 36 characters. With short codes, this could be `?l=sf,ny&c=eng,res,des,acct,data,hr,infra,accel,acq,bus`.
|
|
441
|
+
|
|
442
|
+
### Encrypted Blobs (Supercast, Priceline)
|
|
443
|
+
```
|
|
444
|
+
https://feeds.supercast.com/episodes/8a1aa9e2dde4319825e6a8171b4d51fa1835ef4a
|
|
445
|
+
6730170db60a92c8f0670bb08c3cef884f0e4288c970c980083820e89cd692f582c44cde
|
|
446
|
+
544c7aae86fc721f69ed9f695a43e5e21f4d344b32e70bae48a8fe0ae8b472d99502041a
|
|
447
|
+
bad3dc650a6973653c094eae0631f637d96bb42ab5d26b8ea6b1638b7ffa23f66e46282b
|
|
448
|
+
52970b59b2c13f9e6214251ad793be244bb9dc7e5bd7cefe77b6ec71b06c85e3bc9c194a
|
|
449
|
+
d4ca10b27cfd7b8b1c181b3d9aea144bb978d1d790f08d89049d5a29a477651f1b799eec
|
|
450
|
+
827ed95209dc741207e2b331170cb01c625d51982913eb8757ef2b2037235624a7bbfab9
|
|
451
|
+
8a641e98a507ee096d0678c8ab458fd87731a9a7a0bdc87a99fbbfe684be10f5d4259265
|
|
452
|
+
68b041a308017ce2901b3c6bf4b3bc89a2b13f3c54047d2fc5f69e9a5053b5e5bb2e0f70
|
|
453
|
+
a2a77d9a25c97b890faec970e29f1c6961b1e00ccd1d8ba9c4006ba8b657193fe5a5b8e4
|
|
454
|
+
6aa6a86492c381c79afe09d347d25c550c195d080695e3b97c012be3ebf1e2e64bd9f6c2
|
|
455
|
+
9977e4b34184858bcf99164010dc3746f49d90df559f7dfa6f029f50f35f7777c44d1247
|
|
456
|
+
ecdfc7861969f172d63eb3acc620ac25919cdc5caf4397793b7d564ccc4b0519118027.mp3
|
|
457
|
+
?key=8kSKDMBUEi2TCGyzhdzZBVSN&v=0
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
```
|
|
461
|
+
https://www.priceline.com/relax/at/2003205/from/20240628/to/20240629/rooms/1
|
|
462
|
+
?meta-id=AyOy_-ov9Edvq6cYGWUbaO9KdvlksSZCnHtEiIUqbvfIqUNLp0ZV0WiDB-MXSyZhxM
|
|
463
|
+
mSw6xJm0HTePNNo_NwvV_Mzo1DeUvJhE53dMnjIqnwb7rfeGGSHOuOML_0zcWCYppfcv6Cf8T
|
|
464
|
+
Na_TIadYlC8PJkvC_qY7bm0lXIqygsn03MyXPyXyUCXNRcKiIm2QS5bWoOeiO48zWgHRtLUDm
|
|
465
|
+
cNx8o6rdlIukl18vqu8RQYajSd3Yt9bbWwDTBjeEduJ2sfoh4Mi3XtGzbqy8YpUrRgIUCGCYf
|
|
466
|
+
DHBdaS47dUkqKfqtQvY7yCPh9Y4YNUZtt9w-TRqndd6AdvbOMprSAbawg8IU5wIj-yEbZr82e
|
|
467
|
+
CcQg2dylETYccSaRK07WHSEJx7
|
|
468
|
+
&pclnId=0571D9ABC99167E702D55CD454625E1BD51BC6742D4EB3A6869799404CB9B21E0E31
|
|
469
|
+
CA463BDC3DE5A56EDB9C6B55C3F06EB5CBBC77502608C5279D0943A5F2545B3F0E4366F3FB
|
|
470
|
+
CCDE32424FB9D2CC10B7E2B68DD59C89151023C9B800744FDDF1C7D85AEB2CF27E
|
|
471
|
+
&gid=5369&cityId=3000035889&cur=USD&backlink-id=gotjhpxt5bp
|
|
472
|
+
```
|
|
473
|
+
900 hex characters, 400-char tracking IDs, session blobs.
|
|
474
|
+
|
|
475
|
+
### Tracking Parameter Avalanche (Apple TV)
|
|
476
|
+
```
|
|
477
|
+
https://tv.apple.com/us/show/severance/umc.cmc.1srk2goyh2q2zdxcx605w8vtx
|
|
478
|
+
?ign-itscg=MC_20000&ign-itsct=atvp_brand_omd
|
|
479
|
+
&mttn3pid=Google%20AdWords&mttnagencyid=a5e&mttncc=US
|
|
480
|
+
&mttnsiteid=143238&mttnsubad=OUS2019927_1-592764821446-m
|
|
481
|
+
&mttnsubkw=133111427260__zxnj5jSX_&mttnsubplmnt=
|
|
482
|
+
```
|
|
483
|
+
Seven `mttn*` tracking parameters that are excessively verbose (and come from a single ad click).
|
|
484
|
+
|
|
485
|
+
### Base64-Encoded Redirect URLs (Wired)
|
|
486
|
+
```
|
|
487
|
+
https://link.wired.com/external/39532383.1121/aHR0cHM6Ly9jb25kZW5hc3Quem9vbS
|
|
488
|
+
51cy93ZWJpbmFyL3JlZ2lzdGVyL1dOX29kcldRdE5uUkdhSUN3MHZob0N3ckE_dXRtX3Nvd
|
|
489
|
+
XJjZT1ubCZ1dG1fYnJhbmQ9d2lyZWQmdXRtX21haWxpbmc9V0lSX1BheXdhbGxTdWJzXzA0
|
|
490
|
+
MjMyNV9TcGVjaWFsX0FJVW5sb2NrZWRfTkxTVUJTSW52aXRlJnV0bV9jYW1wYWlnbj1hdWQ
|
|
491
|
+
tZGV2JnV0bV9tZWRpdW09ZW1haWwmdXRtX2NvbnRlbnQ9V0lSX1BheXdhbGxTdWJzXzA0Mj
|
|
492
|
+
MyNV9TcGVjaWFsX0FJVW5sb2NrZWRfTkxTVUJTSW52aXRlJmJ4aWQ9NWNjOWUwZjdmYzk0M
|
|
493
|
+
mQxM2ViMWY0YjhjJmNuZGlkPTUwNTQyMzY4Jmhhc2hhPTQwODY5ZjRmY2ExOWRkZjU2NTUz
|
|
494
|
+
M2Q2NzMxYmVkMTExJmhhc2hiPWFjNzQxNjk4NjkyMTE1YWExOGRkNzg5N2JjMTIxNmIwNWM
|
|
495
|
+
0YmI2ODgmaGFzaGM9ZTA5YTA4NzM0MTM3NDA4ODE3NzZlNjExNzQ3NzQ3NDM5ZDYzMGM2YT
|
|
496
|
+
k0NGVmYTIwOGFhMzhhYTMwZjljYTE0NyZlc3JjPU9JRENfU0VMRUNUX0FDQ09VTlRfUEFHR
|
|
497
|
+
Q/5cc9e0f7fc942d13eb1f4b8cB8513f7ce
|
|
498
|
+
```
|
|
499
|
+
A URL containing another (base64-encoded) URL containing UTM params, hashes, and tracking IDs.
|
|
500
|
+
|
|
501
|
+
### Kitchen Sink (Grubhub)
|
|
502
|
+
```
|
|
503
|
+
https://www.grubhub.com/restaurant/bobs-noodle-house-123-main-st-newark/4857291
|
|
504
|
+
/grouporder/Xk7rPwchQfDsT3J9yCtghR
|
|
505
|
+
?pageNum=1&pageSize=20
|
|
506
|
+
&facet=scheduled%3Afalse&facet=orderType%3AALL
|
|
507
|
+
&includePartnerOrders=true&sorts=default&blockModal=true
|
|
508
|
+
&utm_source=grubhub_web&utm_medium=content_owned
|
|
509
|
+
&utm_campaign=product_sharedcart_join&utm_content=share-link
|
|
510
|
+
```
|
|
511
|
+
Session IDs, pagination defaults that could be omitted, boolean flags, four UTM parameters, and all more verbose than necessary, resulting in an unwieldy URL.
|
|
512
|
+
|
|
513
|
+
### The `use-prms` way
|
|
514
|
+
|
|
515
|
+
This may not be best in all cases, but `use-prms` encourages encoding the same information more compactly:
|
|
516
|
+
|
|
517
|
+
| Verbose | Compact | Meaning |
|
|
518
|
+
|----------------------------------------|----------------------------------|--------------------------------------|
|
|
519
|
+
| `?show_grid=true` | `?g` | Boolean flag |
|
|
520
|
+
| `?page_number=5&page_size=50` | `?p=5x50` | Compact, combined state |
|
|
521
|
+
| `?page_number=5&page_size=20` | `?p=5` | Default values omitted |
|
|
522
|
+
| `?category=e1e973fe-6f0a-...` | `?c=eng` | Short, human-readable codes for enums |
|
|
523
|
+
| `?latitude=40.7128&longitude=-74.0060` | `?ll=40.7128-74.0060` | Compact, combined state |
|
|
524
|
+
|
|
525
|
+
URLs are part of your UI. Treat them with the same care as your design.
|
|
526
|
+
|
|
527
|
+
## License <a id="license"></a>
|
|
319
528
|
|
|
320
529
|
MIT
|