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/dist/hash.cjs CHANGED
@@ -30,6 +30,25 @@ function serializeMultiParams(params) {
30
30
  }
31
31
  return result;
32
32
  }
33
+ var LOCATION_CHANGE_EVENT = "use-prms:locationchange";
34
+ var historyPatched = false;
35
+ function patchHistoryApi() {
36
+ if (typeof window === "undefined" || historyPatched) return;
37
+ historyPatched = true;
38
+ const originalPushState = history.pushState.bind(history);
39
+ const originalReplaceState = history.replaceState.bind(history);
40
+ history.pushState = function(state, title, url) {
41
+ originalPushState(state, title, url);
42
+ window.dispatchEvent(new CustomEvent(LOCATION_CHANGE_EVENT));
43
+ window.dispatchEvent(new PopStateEvent("popstate", { state }));
44
+ };
45
+ history.replaceState = function(state, title, url) {
46
+ originalReplaceState(state, title, url);
47
+ window.dispatchEvent(new CustomEvent(LOCATION_CHANGE_EVENT));
48
+ window.dispatchEvent(new PopStateEvent("popstate", { state }));
49
+ };
50
+ }
51
+ patchHistoryApi();
33
52
  var queryStrategy = {
34
53
  getRaw() {
35
54
  if (typeof window === "undefined") return "";
@@ -47,7 +66,11 @@ var queryStrategy = {
47
66
  if (typeof window === "undefined") return () => {
48
67
  };
49
68
  window.addEventListener("popstate", callback);
50
- return () => window.removeEventListener("popstate", callback);
69
+ window.addEventListener(LOCATION_CHANGE_EVENT, callback);
70
+ return () => {
71
+ window.removeEventListener("popstate", callback);
72
+ window.removeEventListener(LOCATION_CHANGE_EVENT, callback);
73
+ };
51
74
  }
52
75
  };
53
76
  var hashStrategy = {
@@ -70,12 +93,28 @@ var hashStrategy = {
70
93
  };
71
94
  window.addEventListener("hashchange", callback);
72
95
  window.addEventListener("popstate", callback);
96
+ window.addEventListener(LOCATION_CHANGE_EVENT, callback);
73
97
  return () => {
74
98
  window.removeEventListener("hashchange", callback);
75
99
  window.removeEventListener("popstate", callback);
100
+ window.removeEventListener(LOCATION_CHANGE_EVENT, callback);
76
101
  };
77
102
  }
78
103
  };
104
+ function notifyLocationChange() {
105
+ if (typeof window === "undefined") return;
106
+ window.dispatchEvent(new CustomEvent(LOCATION_CHANGE_EVENT));
107
+ }
108
+ function clearParams(strategy = "query") {
109
+ if (typeof window === "undefined") return;
110
+ const url = new URL(window.location.href);
111
+ if (strategy === "hash") {
112
+ url.hash = "";
113
+ } else {
114
+ url.search = "";
115
+ }
116
+ window.history.replaceState({}, "", url.toString());
117
+ }
79
118
  var defaultStrategy = queryStrategy;
80
119
  function getDefaultStrategy() {
81
120
  return defaultStrategy;
@@ -104,19 +143,21 @@ var boolParam = {
104
143
  function intParam(init) {
105
144
  return {
106
145
  encode: (value) => value === init ? void 0 : value.toString(),
107
- decode: (encoded) => encoded !== void 0 ? parseInt(encoded, 10) : init
146
+ decode: (encoded) => {
147
+ if (encoded === void 0 || encoded === "") return init;
148
+ const parsed = parseInt(encoded, 10);
149
+ return isNaN(parsed) ? init : parsed;
150
+ }
108
151
  };
109
152
  }
110
153
  var optIntParam = {
111
154
  encode: (value) => value === null ? void 0 : value.toString(),
112
- decode: (encoded) => encoded !== void 0 ? parseInt(encoded, 10) : null
155
+ decode: (encoded) => {
156
+ if (encoded === void 0 || encoded === "") return null;
157
+ const parsed = parseInt(encoded, 10);
158
+ return isNaN(parsed) ? null : parsed;
159
+ }
113
160
  };
114
- function floatParam(init) {
115
- return {
116
- encode: (value) => value === init ? void 0 : value.toString(),
117
- decode: (encoded) => encoded !== void 0 ? parseFloat(encoded) : init
118
- };
119
- }
120
161
  function enumParam(init, values) {
121
162
  const validSet = new Set(values);
122
163
  return {
@@ -268,6 +309,23 @@ function multiFloatParam(init = []) {
268
309
  function arraysEqual(a, b) {
269
310
  return a.length === b.length && a.every((v, i) => v === b[i]);
270
311
  }
312
+ function debounce(fn, ms) {
313
+ let timeoutId = null;
314
+ const debounced = ((...args) => {
315
+ if (timeoutId) clearTimeout(timeoutId);
316
+ timeoutId = setTimeout(() => {
317
+ timeoutId = null;
318
+ fn(...args);
319
+ }, ms);
320
+ });
321
+ debounced.cancel = () => {
322
+ if (timeoutId) {
323
+ clearTimeout(timeoutId);
324
+ timeoutId = null;
325
+ }
326
+ };
327
+ return debounced;
328
+ }
271
329
  var snapshotCache = /* @__PURE__ */ new WeakMap();
272
330
  function getSnapshot(strategy) {
273
331
  const raw = strategy.getRaw();
@@ -286,10 +344,13 @@ function multiToSingle(multi) {
286
344
  if (multi.length === 0) return void 0;
287
345
  return multi[0];
288
346
  }
289
- function useUrlParam(key, param, push = false) {
347
+ function useUrlState(key, param, options = {}) {
348
+ const opts = typeof options === "boolean" ? { push: options } : options;
349
+ const { debounce: debounceMs = 0, push = false } = opts;
290
350
  const strategy = getDefaultStrategy();
291
351
  const paramRef = react.useRef(param);
292
352
  paramRef.current = param;
353
+ const lastWrittenRef = react.useRef(null);
293
354
  const urlParams = react.useSyncExternalStore(
294
355
  (cb) => strategy.subscribe(cb),
295
356
  () => getSnapshot(strategy),
@@ -297,19 +358,24 @@ function useUrlParam(key, param, push = false) {
297
358
  );
298
359
  const encoded = multiToSingle(urlParams[key] ?? []);
299
360
  const cacheRef = react.useRef(null);
300
- if (cacheRef.current === null || cacheRef.current.encoded !== encoded || cacheRef.current.param !== param) {
301
- cacheRef.current = { encoded, param, decoded: param.decode(encoded) };
361
+ let value;
362
+ if (lastWrittenRef.current && lastWrittenRef.current.encoded === encoded) {
363
+ value = lastWrittenRef.current.decoded;
364
+ } else {
365
+ if (cacheRef.current === null || cacheRef.current.encoded !== encoded || cacheRef.current.param !== param) {
366
+ cacheRef.current = { encoded, param, decoded: param.decode(encoded) };
367
+ }
368
+ value = cacheRef.current.decoded;
369
+ lastWrittenRef.current = null;
302
370
  }
303
- const value = cacheRef.current.decoded;
304
- const setValue = react.useCallback(
305
- (newValue) => {
371
+ const writeToUrl = react.useCallback(
372
+ (newValue, newEncoded) => {
306
373
  if (typeof window === "undefined") return;
307
374
  const currentParams = strategy.parse();
308
- const encoded2 = paramRef.current.encode(newValue);
309
- if (encoded2 === void 0) {
375
+ if (newEncoded === void 0) {
310
376
  delete currentParams[key];
311
377
  } else {
312
- currentParams[key] = [encoded2];
378
+ currentParams[key] = [newEncoded];
313
379
  }
314
380
  const url = new URL(window.location.href);
315
381
  const newUrl = strategy.buildUrl(url, currentParams);
@@ -319,29 +385,59 @@ function useUrlParam(key, param, push = false) {
319
385
  },
320
386
  [key, push, strategy]
321
387
  );
388
+ const debouncedWriteRef = react.useRef(null);
389
+ react.useEffect(() => {
390
+ if (debounceMs > 0) {
391
+ debouncedWriteRef.current = debounce(writeToUrl, debounceMs);
392
+ } else {
393
+ debouncedWriteRef.current = null;
394
+ }
395
+ return () => {
396
+ debouncedWriteRef.current?.cancel();
397
+ };
398
+ }, [debounceMs, writeToUrl]);
399
+ const setValue = react.useCallback(
400
+ (newValue) => {
401
+ const newEncoded = paramRef.current.encode(newValue);
402
+ lastWrittenRef.current = { encoded: newEncoded, decoded: newValue };
403
+ if (debouncedWriteRef.current) {
404
+ debouncedWriteRef.current(newValue, newEncoded);
405
+ } else {
406
+ writeToUrl(newValue, newEncoded);
407
+ }
408
+ },
409
+ [writeToUrl]
410
+ );
322
411
  return [value, setValue];
323
412
  }
324
- function useUrlParams(params, push = false) {
413
+ function useUrlStates(params, options = {}) {
414
+ const opts = typeof options === "boolean" ? { push: options } : options;
415
+ const { debounce: debounceMs = 0, push = false } = opts;
325
416
  const strategy = getDefaultStrategy();
417
+ const lastWrittenRef = react.useRef({});
326
418
  const urlParams = react.useSyncExternalStore(
327
419
  (cb) => strategy.subscribe(cb),
328
420
  () => getSnapshot(strategy),
329
421
  getServerSnapshot
330
422
  );
331
423
  const values = Object.fromEntries(
332
- Object.entries(params).map(([key, param]) => [
333
- key,
334
- param.decode(multiToSingle(urlParams[key] ?? []))
335
- ])
424
+ Object.entries(params).map(([key, param]) => {
425
+ const encoded = multiToSingle(urlParams[key] ?? []);
426
+ const lastWritten = lastWrittenRef.current[key];
427
+ if (lastWritten && lastWritten.encoded === encoded) {
428
+ return [key, lastWritten.decoded];
429
+ } else {
430
+ const decoded = param.decode(encoded);
431
+ delete lastWrittenRef.current[key];
432
+ return [key, decoded];
433
+ }
434
+ })
336
435
  );
337
- const setValues = react.useCallback(
436
+ const writeToUrl = react.useCallback(
338
437
  (updates) => {
339
438
  if (typeof window === "undefined") return;
340
439
  const currentParams = strategy.parse();
341
- for (const [key, value] of Object.entries(updates)) {
342
- const param = params[key];
343
- if (!param) continue;
344
- const encoded = param.encode(value);
440
+ for (const [key, { encoded }] of Object.entries(updates)) {
345
441
  if (encoded === void 0) {
346
442
  delete currentParams[key];
347
443
  } else {
@@ -354,29 +450,67 @@ function useUrlParams(params, push = false) {
354
450
  window.history[method]({}, "", newUrl);
355
451
  window.dispatchEvent(new PopStateEvent("popstate"));
356
452
  },
357
- [params, push, strategy]
453
+ [push, strategy]
454
+ );
455
+ const debouncedWriteRef = react.useRef(null);
456
+ react.useEffect(() => {
457
+ if (debounceMs > 0) {
458
+ debouncedWriteRef.current = debounce(writeToUrl, debounceMs);
459
+ } else {
460
+ debouncedWriteRef.current = null;
461
+ }
462
+ return () => {
463
+ debouncedWriteRef.current?.cancel();
464
+ };
465
+ }, [debounceMs, writeToUrl]);
466
+ const setValues = react.useCallback(
467
+ (updates) => {
468
+ const encodedUpdates = {};
469
+ for (const [key, value] of Object.entries(updates)) {
470
+ const param = params[key];
471
+ if (!param) continue;
472
+ const encoded = param.encode(value);
473
+ encodedUpdates[key] = { encoded, decoded: value };
474
+ lastWrittenRef.current[key] = { encoded, decoded: value };
475
+ }
476
+ if (debouncedWriteRef.current) {
477
+ debouncedWriteRef.current(encodedUpdates);
478
+ } else {
479
+ writeToUrl(encodedUpdates);
480
+ }
481
+ },
482
+ [params, writeToUrl]
358
483
  );
359
484
  return { values, setValues };
360
485
  }
361
- function useMultiUrlParam(key, param, push = false) {
486
+ function useMultiUrlState(key, param, options = {}) {
487
+ const opts = typeof options === "boolean" ? { push: options } : options;
488
+ const { debounce: debounceMs = 0, push = false } = opts;
362
489
  const strategy = getDefaultStrategy();
363
490
  const paramRef = react.useRef(param);
364
491
  paramRef.current = param;
492
+ const lastWrittenRef = react.useRef(null);
365
493
  const urlParams = react.useSyncExternalStore(
366
494
  (cb) => strategy.subscribe(cb),
367
495
  () => getSnapshot(strategy),
368
496
  getServerSnapshot
369
497
  );
370
- const value = param.decode(urlParams[key] ?? []);
371
- const setValue = react.useCallback(
372
- (newValue) => {
498
+ const encoded = urlParams[key] ?? [];
499
+ let value;
500
+ if (lastWrittenRef.current && arraysEqual2(lastWrittenRef.current.encoded, encoded)) {
501
+ value = lastWrittenRef.current.decoded;
502
+ } else {
503
+ value = param.decode(encoded);
504
+ lastWrittenRef.current = null;
505
+ }
506
+ const writeToUrl = react.useCallback(
507
+ (newEncoded) => {
373
508
  if (typeof window === "undefined") return;
374
509
  const currentParams = strategy.parse();
375
- const encoded = paramRef.current.encode(newValue);
376
- if (encoded.length === 0) {
510
+ if (newEncoded.length === 0) {
377
511
  delete currentParams[key];
378
512
  } else {
379
- currentParams[key] = encoded;
513
+ currentParams[key] = newEncoded;
380
514
  }
381
515
  const url = new URL(window.location.href);
382
516
  const newUrl = strategy.buildUrl(url, currentParams);
@@ -386,29 +520,62 @@ function useMultiUrlParam(key, param, push = false) {
386
520
  },
387
521
  [key, push, strategy]
388
522
  );
523
+ const debouncedWriteRef = react.useRef(null);
524
+ react.useEffect(() => {
525
+ if (debounceMs > 0) {
526
+ debouncedWriteRef.current = debounce(writeToUrl, debounceMs);
527
+ } else {
528
+ debouncedWriteRef.current = null;
529
+ }
530
+ return () => {
531
+ debouncedWriteRef.current?.cancel();
532
+ };
533
+ }, [debounceMs, writeToUrl]);
534
+ const setValue = react.useCallback(
535
+ (newValue) => {
536
+ const newEncoded = paramRef.current.encode(newValue);
537
+ lastWrittenRef.current = { encoded: newEncoded, decoded: newValue };
538
+ if (debouncedWriteRef.current) {
539
+ debouncedWriteRef.current(newEncoded);
540
+ } else {
541
+ writeToUrl(newEncoded);
542
+ }
543
+ },
544
+ [writeToUrl]
545
+ );
389
546
  return [value, setValue];
390
547
  }
391
- function useMultiUrlParams(params, push = false) {
548
+ function arraysEqual2(a, b) {
549
+ return a.length === b.length && a.every((v, i) => v === b[i]);
550
+ }
551
+ function useMultiUrlStates(params, options = {}) {
552
+ const opts = typeof options === "boolean" ? { push: options } : options;
553
+ const { debounce: debounceMs = 0, push = false } = opts;
392
554
  const strategy = getDefaultStrategy();
555
+ const lastWrittenRef = react.useRef({});
393
556
  const urlParams = react.useSyncExternalStore(
394
557
  (cb) => strategy.subscribe(cb),
395
558
  () => getSnapshot(strategy),
396
559
  getServerSnapshot
397
560
  );
398
561
  const values = Object.fromEntries(
399
- Object.entries(params).map(([key, param]) => [
400
- key,
401
- param.decode(urlParams[key] ?? [])
402
- ])
562
+ Object.entries(params).map(([key, param]) => {
563
+ const encoded = urlParams[key] ?? [];
564
+ const lastWritten = lastWrittenRef.current[key];
565
+ if (lastWritten && arraysEqual2(lastWritten.encoded, encoded)) {
566
+ return [key, lastWritten.decoded];
567
+ } else {
568
+ const decoded = param.decode(encoded);
569
+ delete lastWrittenRef.current[key];
570
+ return [key, decoded];
571
+ }
572
+ })
403
573
  );
404
- const setValues = react.useCallback(
574
+ const writeToUrl = react.useCallback(
405
575
  (updates) => {
406
576
  if (typeof window === "undefined") return;
407
577
  const currentParams = strategy.parse();
408
- for (const [key, value] of Object.entries(updates)) {
409
- const param = params[key];
410
- if (!param) continue;
411
- const encoded = param.encode(value);
578
+ for (const [key, encoded] of Object.entries(updates)) {
412
579
  if (encoded.length === 0) {
413
580
  delete currentParams[key];
414
581
  } else {
@@ -421,11 +588,669 @@ function useMultiUrlParams(params, push = false) {
421
588
  window.history[method]({}, "", newUrl);
422
589
  window.dispatchEvent(new PopStateEvent("popstate"));
423
590
  },
424
- [params, push, strategy]
591
+ [push, strategy]
592
+ );
593
+ const debouncedWriteRef = react.useRef(null);
594
+ react.useEffect(() => {
595
+ if (debounceMs > 0) {
596
+ debouncedWriteRef.current = debounce(writeToUrl, debounceMs);
597
+ } else {
598
+ debouncedWriteRef.current = null;
599
+ }
600
+ return () => {
601
+ debouncedWriteRef.current?.cancel();
602
+ };
603
+ }, [debounceMs, writeToUrl]);
604
+ const setValues = react.useCallback(
605
+ (updates) => {
606
+ const encodedUpdates = {};
607
+ for (const [key, value] of Object.entries(updates)) {
608
+ const param = params[key];
609
+ if (!param) continue;
610
+ const encoded = param.encode(value);
611
+ encodedUpdates[key] = encoded;
612
+ lastWrittenRef.current[key] = { encoded, decoded: value };
613
+ }
614
+ if (debouncedWriteRef.current) {
615
+ debouncedWriteRef.current(encodedUpdates);
616
+ } else {
617
+ writeToUrl(encodedUpdates);
618
+ }
619
+ },
620
+ [params, writeToUrl]
425
621
  );
426
622
  return { values, setValues };
427
623
  }
428
624
 
625
+ // src/alphabet.ts
626
+ var ALPHABETS = {
627
+ /**
628
+ * RFC 4648 base64url alphabet (default)
629
+ * Standard URL-safe encoding, but NOT lexicographically sortable.
630
+ */
631
+ rfc4648: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
632
+ /**
633
+ * ASCII-ordered alphabet for lexicographic sortability
634
+ * Encoded strings sort in the same order as their numeric values.
635
+ * Uses URL-safe characters only (- and _).
636
+ */
637
+ sortable: "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
638
+ };
639
+ var URL_SAFE_CHARS = /^[A-Za-z0-9\-_]+$/;
640
+ function validateAlphabet(alphabet) {
641
+ if (alphabet.length !== 64) {
642
+ throw new Error(`Alphabet must be exactly 64 characters, got ${alphabet.length}`);
643
+ }
644
+ const seen = /* @__PURE__ */ new Set();
645
+ for (const char of alphabet) {
646
+ if (seen.has(char)) {
647
+ throw new Error(`Duplicate character in alphabet: '${char}'`);
648
+ }
649
+ seen.add(char);
650
+ }
651
+ if (!URL_SAFE_CHARS.test(alphabet)) {
652
+ const unsafe = [...alphabet].filter((c) => !URL_SAFE_CHARS.test(c));
653
+ throw new Error(`Alphabet contains non-URL-safe characters: ${unsafe.map((c) => `'${c}'`).join(", ")}`);
654
+ }
655
+ }
656
+ function resolveAlphabet(alphabet) {
657
+ if (alphabet in ALPHABETS) {
658
+ return ALPHABETS[alphabet];
659
+ }
660
+ validateAlphabet(alphabet);
661
+ return alphabet;
662
+ }
663
+ function createLookupMap(alphabet) {
664
+ return new Map(alphabet.split("").map((c, i) => [c, i]));
665
+ }
666
+
667
+ // src/binary.ts
668
+ var BASE64_CHARS = ALPHABETS.rfc4648;
669
+ var DEFAULT_LOOKUP = createLookupMap(ALPHABETS.rfc4648);
670
+ var lookupCache = /* @__PURE__ */ new Map();
671
+ function getLookupMap(alphabet) {
672
+ if (alphabet === ALPHABETS.rfc4648) return DEFAULT_LOOKUP;
673
+ let lookup = lookupCache.get(alphabet);
674
+ if (!lookup) {
675
+ lookup = createLookupMap(alphabet);
676
+ lookupCache.set(alphabet, lookup);
677
+ }
678
+ return lookup;
679
+ }
680
+ function base64Encode(bytes, options) {
681
+ const chars = options?.alphabet ? resolveAlphabet(options.alphabet) : ALPHABETS.rfc4648;
682
+ let result = "";
683
+ let i = 0;
684
+ while (i < bytes.length) {
685
+ const b0 = bytes[i++] ?? 0;
686
+ const b1 = bytes[i++] ?? 0;
687
+ const b2 = bytes[i++] ?? 0;
688
+ const n = b0 << 16 | b1 << 8 | b2;
689
+ result += chars[n >> 18 & 63];
690
+ result += chars[n >> 12 & 63];
691
+ if (i - 2 < bytes.length) {
692
+ result += chars[n >> 6 & 63];
693
+ }
694
+ if (i - 1 < bytes.length) {
695
+ result += chars[n & 63];
696
+ }
697
+ }
698
+ return result;
699
+ }
700
+ function base64Decode(str, options) {
701
+ const alphabet = options?.alphabet ? resolveAlphabet(options.alphabet) : ALPHABETS.rfc4648;
702
+ const lookup = getLookupMap(alphabet);
703
+ str = str.replace(/=+$/, "");
704
+ const bytes = [];
705
+ for (let i = 0; i < str.length; i += 4) {
706
+ const c0 = lookup.get(str[i]) ?? 0;
707
+ const c1 = lookup.get(str[i + 1]) ?? 0;
708
+ const c2 = i + 2 < str.length ? lookup.get(str[i + 2]) ?? 0 : 0;
709
+ const c3 = i + 3 < str.length ? lookup.get(str[i + 3]) ?? 0 : 0;
710
+ const n = c0 << 18 | c1 << 12 | c2 << 6 | c3;
711
+ bytes.push(n >> 16 & 255);
712
+ if (i + 2 < str.length) bytes.push(n >> 8 & 255);
713
+ if (i + 3 < str.length) bytes.push(n & 255);
714
+ }
715
+ return new Uint8Array(bytes);
716
+ }
717
+ function binaryParam(options) {
718
+ const { toBytes, fromBytes, alphabet } = options;
719
+ const encodeOpts = alphabet ? { alphabet } : void 0;
720
+ return {
721
+ encode: (value) => {
722
+ if (value === null) return void 0;
723
+ const bytes = toBytes(value);
724
+ if (bytes.length === 0) return void 0;
725
+ return base64Encode(bytes, encodeOpts);
726
+ },
727
+ decode: (encoded) => {
728
+ if (encoded === void 0 || encoded === "") return null;
729
+ try {
730
+ const bytes = base64Decode(encoded, encodeOpts);
731
+ return fromBytes(bytes);
732
+ } catch {
733
+ return null;
734
+ }
735
+ }
736
+ };
737
+ }
738
+ function base64Param(toBytes, fromBytes, alphabet) {
739
+ return binaryParam({ toBytes, fromBytes, alphabet });
740
+ }
741
+ function floatToBytes(value) {
742
+ const buf = new ArrayBuffer(8);
743
+ const view = new DataView(buf);
744
+ view.setFloat64(0, value, false);
745
+ return new Uint8Array(buf);
746
+ }
747
+ function bytesToFloat(bytes) {
748
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
749
+ return view.getFloat64(0, false);
750
+ }
751
+
752
+ // src/float.ts
753
+ var precisionSchemes = [
754
+ { expBits: 5, mantBits: 16 },
755
+ // ~5 decimal digits
756
+ { expBits: 5, mantBits: 22 },
757
+ // ~7 decimal digits
758
+ { expBits: 5, mantBits: 28 },
759
+ // ~8 decimal digits
760
+ { expBits: 5, mantBits: 34 },
761
+ // ~10 decimal digits
762
+ { expBits: 5, mantBits: 40 },
763
+ // ~12 decimal digits
764
+ { expBits: 5, mantBits: 46 },
765
+ // ~14 decimal digits
766
+ { expBits: 5, mantBits: 52 }
767
+ // ~16 decimal digits (near IEEE 754)
768
+ ];
769
+ var DEFAULT_EXP_BITS = 5;
770
+ var MIN_MANT_BITS = 8;
771
+ var MAX_MANT_BITS = 52;
772
+ function resolvePrecision(precision) {
773
+ if (precision === void 0) return { expBits: DEFAULT_EXP_BITS, mantBits: 22 };
774
+ if (typeof precision === "object") return precision;
775
+ if (precision < MIN_MANT_BITS || precision > MAX_MANT_BITS) {
776
+ throw new Error(`Precision must be ${MIN_MANT_BITS}-${MAX_MANT_BITS} bits, got ${precision}`);
777
+ }
778
+ return { expBits: DEFAULT_EXP_BITS, mantBits: precision };
779
+ }
780
+ var floatBuf = new ArrayBuffer(8);
781
+ var floatView = new DataView(floatBuf);
782
+ function toFloat(x) {
783
+ floatView.setFloat64(0, x, false);
784
+ const byte0 = floatView.getUint8(0);
785
+ const neg = !!(byte0 & 128);
786
+ const exp = ((floatView.getUint16(0, false) & 32752) >> 4) - 1023;
787
+ const mant = floatView.getBigUint64(0, false) & 0xfffffffffffffn;
788
+ return { neg, exp, mant };
789
+ }
790
+ function fromFloat({ neg, exp, mant }) {
791
+ floatView.setBigUint64(
792
+ 0,
793
+ (neg ? 0x8000000000000000n : 0n) | BigInt(exp + 1023) << 52n | mant,
794
+ false
795
+ );
796
+ return floatView.getFloat64(0, false);
797
+ }
798
+ function toFixedPoint(f, opts) {
799
+ let { neg, exp: fExp, mant } = f;
800
+ fExp++;
801
+ const exp = opts.exp === void 0 ? fExp : opts.exp;
802
+ if (fExp > exp) {
803
+ throw Error(`maxExp ${exp} < ${fExp}`);
804
+ }
805
+ const downshiftBy = exp - fExp + 53 - opts.mantBits;
806
+ const roundUp = mant & 1n << BigInt(downshiftBy - 1);
807
+ mant >>= BigInt(downshiftBy);
808
+ if (roundUp) {
809
+ mant += 1n;
810
+ }
811
+ mant |= 1n << BigInt(opts.mantBits - 1 - (exp - fExp));
812
+ return { neg, exp, mant };
813
+ }
814
+ function fromFixedPoint(f, mantBits) {
815
+ const { neg } = f;
816
+ const nonZeroBits = f.mant ? f.mant.toString(2).length : 0;
817
+ const exp = f.exp - (mantBits - nonZeroBits) - 1;
818
+ if (!f.mant) {
819
+ return { neg, exp: -1023, mant: 0n };
820
+ }
821
+ let mant = BigInt(f.mant);
822
+ mant = mant & (1n << BigInt(nonZeroBits - 1)) - 1n;
823
+ mant <<= BigInt(f.exp - exp);
824
+ mant <<= BigInt(52 - mantBits);
825
+ return { neg, exp, mant };
826
+ }
827
+ var BitBuffer = class _BitBuffer {
828
+ constructor(numBytes) {
829
+ this.buf = Array(numBytes || 0).fill(0);
830
+ this.byteOffset = 0;
831
+ this.bitOffset = 0;
832
+ this.end = 0;
833
+ }
834
+ get totalBitOffset() {
835
+ return this.byteOffset * 8 + this.bitOffset;
836
+ }
837
+ seek(totalBitOffset) {
838
+ this.byteOffset = totalBitOffset >> 3;
839
+ this.bitOffset = totalBitOffset & 7;
840
+ return this;
841
+ }
842
+ /**
843
+ * Encode an integer with specified bit width
844
+ */
845
+ encodeInt(n, numBits) {
846
+ let { buf, byteOffset, bitOffset } = this;
847
+ while (numBits > 0) {
848
+ if (byteOffset >= buf.length) {
849
+ buf.push(0);
850
+ }
851
+ const remainingBitsInByte = 8 - bitOffset;
852
+ const bitsToWrite = Math.min(numBits, remainingBitsInByte);
853
+ const bitsLeftInByte = remainingBitsInByte - bitsToWrite;
854
+ const bitsLeftToWrite = numBits - bitsToWrite;
855
+ const mask = (1 << bitsToWrite) - 1 << bitsLeftToWrite;
856
+ const shiftedBitsToWrite = (n & mask) >> bitsLeftToWrite;
857
+ buf[byteOffset] |= shiftedBitsToWrite << bitsLeftInByte;
858
+ n &= (1 << bitsLeftToWrite) - 1;
859
+ numBits -= bitsToWrite;
860
+ bitOffset += bitsToWrite;
861
+ if (bitOffset === 8) {
862
+ bitOffset = 0;
863
+ byteOffset++;
864
+ }
865
+ }
866
+ this.byteOffset = byteOffset;
867
+ this.bitOffset = bitOffset;
868
+ if (this.totalBitOffset > this.end) this.end = this.totalBitOffset;
869
+ return this;
870
+ }
871
+ /**
872
+ * Decode an integer with specified bit width
873
+ */
874
+ decodeInt(numBits) {
875
+ let { buf, byteOffset, bitOffset } = this;
876
+ let n = 0;
877
+ while (numBits > 0) {
878
+ const remainingBitsInByte = 8 - bitOffset;
879
+ const bitsToRead = Math.min(numBits, remainingBitsInByte);
880
+ const bitsLeftInByte = remainingBitsInByte - bitsToRead;
881
+ const mask = (1 << bitsToRead) - 1 << bitsLeftInByte;
882
+ const bits = (buf[byteOffset] & mask) >> bitsLeftInByte;
883
+ n = n << bitsToRead | bits;
884
+ numBits -= bitsToRead;
885
+ bitOffset += bitsToRead;
886
+ if (bitOffset === 8) {
887
+ bitOffset = 0;
888
+ byteOffset++;
889
+ }
890
+ }
891
+ this.byteOffset = byteOffset;
892
+ this.bitOffset = bitOffset;
893
+ return n;
894
+ }
895
+ /**
896
+ * Encode a bigint with specified bit width
897
+ */
898
+ encodeBigInt(n, numBits) {
899
+ let { buf, byteOffset, bitOffset } = this;
900
+ while (numBits > 0) {
901
+ if (byteOffset >= buf.length) {
902
+ buf.push(0);
903
+ }
904
+ const remainingBitsInByte = 8 - bitOffset;
905
+ const bitsToWrite = Math.min(numBits, remainingBitsInByte);
906
+ const bitsLeftInByte = remainingBitsInByte - bitsToWrite;
907
+ const bitsLeftToWrite = numBits - bitsToWrite;
908
+ const mask = (1n << BigInt(bitsToWrite)) - 1n << BigInt(bitsLeftToWrite);
909
+ const shiftedBitsToWrite = Number((n & mask) >> BigInt(bitsLeftToWrite));
910
+ buf[byteOffset] |= shiftedBitsToWrite << bitsLeftInByte;
911
+ n &= (1n << BigInt(bitsLeftToWrite)) - 1n;
912
+ numBits -= bitsToWrite;
913
+ bitOffset += bitsToWrite;
914
+ if (bitOffset === 8) {
915
+ bitOffset = 0;
916
+ byteOffset++;
917
+ }
918
+ }
919
+ this.byteOffset = byteOffset;
920
+ this.bitOffset = bitOffset;
921
+ if (this.totalBitOffset > this.end) this.end = this.totalBitOffset;
922
+ return this;
923
+ }
924
+ /**
925
+ * Decode a bigint with specified bit width
926
+ */
927
+ decodeBigInt(numBits) {
928
+ let { buf, byteOffset, bitOffset } = this;
929
+ let n = 0n;
930
+ while (numBits > 0) {
931
+ const remainingBitsInByte = 8 - bitOffset;
932
+ const bitsToRead = Math.min(numBits, remainingBitsInByte);
933
+ const bitsLeftInByte = remainingBitsInByte - bitsToRead;
934
+ const mask = (1 << bitsToRead) - 1 << bitsLeftInByte;
935
+ const bits = BigInt((buf[byteOffset] & mask) >> bitsLeftInByte);
936
+ n = n << BigInt(bitsToRead) | bits;
937
+ numBits -= bitsToRead;
938
+ bitOffset += bitsToRead;
939
+ if (bitOffset === 8) {
940
+ bitOffset = 0;
941
+ byteOffset++;
942
+ }
943
+ }
944
+ this.byteOffset = byteOffset;
945
+ this.bitOffset = bitOffset;
946
+ return n;
947
+ }
948
+ /**
949
+ * Encode an array of floats with shared exponent
950
+ */
951
+ encodeFixedPoints(vals, { expBits, mantBits }) {
952
+ const floats = vals.map(toFloat);
953
+ const maxExp = Math.max(...floats.map(({ exp }) => exp + 1));
954
+ if (maxExp >= 1 << expBits - 1) {
955
+ throw Error(`maxExp ${maxExp} >= ${1 << expBits}`);
956
+ }
957
+ const expToWrite = maxExp + (1 << expBits - 1) & (1 << expBits) - 1;
958
+ this.encodeInt(expToWrite, expBits);
959
+ const fixedPoints = floats.map((f) => toFixedPoint(f, { mantBits, exp: maxExp }));
960
+ fixedPoints.forEach(({ neg, mant }) => {
961
+ this.encodeInt(neg ? 1 : 0, 1);
962
+ this.encodeBigInt(mant, mantBits);
963
+ });
964
+ return this;
965
+ }
966
+ /**
967
+ * Decode an array of floats with shared exponent
968
+ */
969
+ decodeFixedPoints(count, { expBits, mantBits }) {
970
+ const expRaw = this.decodeInt(expBits);
971
+ const exp = expRaw - (1 << expBits - 1);
972
+ const result = [];
973
+ for (let i = 0; i < count; i++) {
974
+ const neg = this.decodeInt(1) === 1;
975
+ const mant = this.decodeBigInt(mantBits);
976
+ const fp = { neg, exp, mant };
977
+ const f = fromFixedPoint(fp, mantBits);
978
+ result.push(fromFloat(f));
979
+ }
980
+ return result;
981
+ }
982
+ /**
983
+ * Get bytes as Uint8Array
984
+ */
985
+ toBytes() {
986
+ const numBytes = Math.ceil(this.end / 8);
987
+ return new Uint8Array(this.buf.slice(0, numBytes));
988
+ }
989
+ /**
990
+ * Create from bytes
991
+ */
992
+ static fromBytes(bytes) {
993
+ const buf = new _BitBuffer();
994
+ buf.buf = Array.from(bytes);
995
+ buf.end = bytes.length * 8;
996
+ return buf;
997
+ }
998
+ /**
999
+ * Convert buffer to URL-safe base64 string
1000
+ *
1001
+ * Encodes bits directly to base64 (6 bits per character) for maximum compactness.
1002
+ * This is more efficient than going through bytes when bit count isn't a multiple of 8.
1003
+ *
1004
+ * @param options - Base64 options (alphabet)
1005
+ */
1006
+ toBase64(options) {
1007
+ const alphabet = resolveAlphabet(options?.alphabet ?? "rfc4648");
1008
+ const overhang = this.end % 6;
1009
+ if (overhang) {
1010
+ this.encodeInt(0, 6 - overhang);
1011
+ }
1012
+ const numChars = this.end / 6;
1013
+ this.seek(0);
1014
+ let result = "";
1015
+ for (let i = 0; i < numChars; i++) {
1016
+ result += alphabet[this.decodeInt(6)];
1017
+ }
1018
+ return result;
1019
+ }
1020
+ /**
1021
+ * Create a BitBuffer from a URL-safe base64 string
1022
+ *
1023
+ * Decodes base64 directly to bits (6 bits per character).
1024
+ *
1025
+ * @param str - The base64 string to decode
1026
+ * @param options - Base64 options (alphabet)
1027
+ */
1028
+ static fromBase64(str, options) {
1029
+ const alphabet = resolveAlphabet(options?.alphabet ?? "rfc4648");
1030
+ const lookup = createLookupMap(alphabet);
1031
+ const buf = new _BitBuffer();
1032
+ for (const char of str) {
1033
+ const idx = lookup.get(char);
1034
+ if (idx === void 0) {
1035
+ throw new Error(`Invalid base64 character: '${char}'`);
1036
+ }
1037
+ buf.encodeInt(idx, 6);
1038
+ }
1039
+ buf.seek(0);
1040
+ return buf;
1041
+ }
1042
+ };
1043
+ function parsePrecisionString(s) {
1044
+ const match = s.match(/^(\d+)\+(\d+)$/);
1045
+ if (!match) {
1046
+ throw new Error(`Invalid precision format: "${s}". Expected format like "5+22" (exp+mant)`);
1047
+ }
1048
+ return { exp: parseInt(match[1], 10), mant: parseInt(match[2], 10) };
1049
+ }
1050
+ function floatParam(optsOrDefault = 0) {
1051
+ const opts = typeof optsOrDefault === "number" ? { default: optsOrDefault } : optsOrDefault;
1052
+ const {
1053
+ default: defaultValue = 0,
1054
+ encoding = "base64",
1055
+ decimals,
1056
+ exp,
1057
+ mant,
1058
+ precision,
1059
+ alphabet
1060
+ } = opts;
1061
+ if (encoding === "string") {
1062
+ if (exp !== void 0 || mant !== void 0 || precision !== void 0) {
1063
+ throw new Error('exp/mant/precision options are only valid with encoding: "base64"');
1064
+ }
1065
+ }
1066
+ if (encoding === "base64") {
1067
+ if (decimals !== void 0) {
1068
+ throw new Error('decimals option is only valid with encoding: "string"');
1069
+ }
1070
+ const hasExpMant = exp !== void 0 || mant !== void 0;
1071
+ const hasPrecision = precision !== void 0;
1072
+ if (hasExpMant && hasPrecision) {
1073
+ throw new Error("Cannot specify both exp/mant and precision");
1074
+ }
1075
+ if (hasExpMant) {
1076
+ if (exp === void 0 || mant === void 0) {
1077
+ throw new Error("Both exp and mant must be specified together");
1078
+ }
1079
+ return createLossyBase64Param(defaultValue, { expBits: exp, mantBits: mant }, alphabet);
1080
+ }
1081
+ if (hasPrecision) {
1082
+ const { exp: e, mant: m } = parsePrecisionString(precision);
1083
+ return createLossyBase64Param(defaultValue, { expBits: e, mantBits: m }, alphabet);
1084
+ }
1085
+ return createLosslessBase64Param(defaultValue, alphabet);
1086
+ }
1087
+ if (decimals !== void 0) {
1088
+ return createTruncatedStringParam(defaultValue, decimals);
1089
+ }
1090
+ return createFullStringParam(defaultValue);
1091
+ }
1092
+ function createLosslessBase64Param(defaultValue, alphabet) {
1093
+ const opts = alphabet ? { alphabet } : void 0;
1094
+ return {
1095
+ encode: (value) => {
1096
+ if (value === defaultValue) return void 0;
1097
+ return base64Encode(floatToBytes(value), opts);
1098
+ },
1099
+ decode: (encoded) => {
1100
+ if (encoded === void 0 || encoded === "") return defaultValue;
1101
+ try {
1102
+ return bytesToFloat(base64Decode(encoded, opts));
1103
+ } catch {
1104
+ return defaultValue;
1105
+ }
1106
+ }
1107
+ };
1108
+ }
1109
+ function createLossyBase64Param(defaultValue, scheme, alphabet) {
1110
+ const opts = alphabet ? { alphabet } : void 0;
1111
+ return {
1112
+ encode: (value) => {
1113
+ if (value === defaultValue) return void 0;
1114
+ const buf = new BitBuffer();
1115
+ buf.encodeFixedPoints([value], scheme);
1116
+ return buf.toBase64(opts);
1117
+ },
1118
+ decode: (encoded) => {
1119
+ if (encoded === void 0 || encoded === "") return defaultValue;
1120
+ try {
1121
+ const buf = BitBuffer.fromBase64(encoded, opts);
1122
+ const [value] = buf.decodeFixedPoints(1, scheme);
1123
+ return value;
1124
+ } catch {
1125
+ return defaultValue;
1126
+ }
1127
+ }
1128
+ };
1129
+ }
1130
+ function createFullStringParam(defaultValue) {
1131
+ return {
1132
+ encode: (value) => {
1133
+ if (value === defaultValue) return void 0;
1134
+ return value.toString();
1135
+ },
1136
+ decode: (encoded) => {
1137
+ if (encoded === void 0 || encoded === "") return defaultValue;
1138
+ const parsed = parseFloat(encoded);
1139
+ return isNaN(parsed) ? defaultValue : parsed;
1140
+ }
1141
+ };
1142
+ }
1143
+ function createTruncatedStringParam(defaultValue, decimals) {
1144
+ const multiplier = Math.pow(10, decimals);
1145
+ return {
1146
+ encode: (value) => {
1147
+ if (value === defaultValue) return void 0;
1148
+ const truncated = Math.round(value * multiplier) / multiplier;
1149
+ return truncated.toFixed(decimals);
1150
+ },
1151
+ decode: (encoded) => {
1152
+ if (encoded === void 0 || encoded === "") return defaultValue;
1153
+ const parsed = parseFloat(encoded);
1154
+ return isNaN(parsed) ? defaultValue : parsed;
1155
+ }
1156
+ };
1157
+ }
1158
+ function base64FloatParam(optsOrDefault = 0) {
1159
+ const opts = typeof optsOrDefault === "number" ? { default: optsOrDefault } : optsOrDefault;
1160
+ return floatParam({ ...opts, encoding: "base64" });
1161
+ }
1162
+ function pointParam(opts = {}) {
1163
+ const {
1164
+ encoding = "base64",
1165
+ decimals = 2,
1166
+ precision,
1167
+ default: defaultPoint = null,
1168
+ alphabet
1169
+ } = opts;
1170
+ const scheme = resolvePrecision(precision);
1171
+ const multiplier = Math.pow(10, decimals);
1172
+ const base64Opts = alphabet ? { alphabet } : void 0;
1173
+ return {
1174
+ encode: (point) => {
1175
+ if (point === null) return void 0;
1176
+ if (defaultPoint && point.x === defaultPoint.x && point.y === defaultPoint.y) {
1177
+ return void 0;
1178
+ }
1179
+ if (encoding === "string") {
1180
+ const xTrunc = Math.round(point.x * multiplier) / multiplier;
1181
+ const yTrunc = Math.round(point.y * multiplier) / multiplier;
1182
+ const xStr = xTrunc.toFixed(decimals);
1183
+ const yStr = yTrunc.toFixed(decimals);
1184
+ const delimiter = yTrunc >= 0 ? " " : "";
1185
+ return `${xStr}${delimiter}${yStr}`;
1186
+ } else {
1187
+ const buf = new BitBuffer();
1188
+ buf.encodeFixedPoints([point.x, point.y], scheme);
1189
+ return buf.toBase64(base64Opts);
1190
+ }
1191
+ },
1192
+ decode: (encoded) => {
1193
+ if (encoded === void 0 || encoded === "") return defaultPoint;
1194
+ try {
1195
+ if (encoding === "string") {
1196
+ let x, y;
1197
+ if (encoded.includes(" ")) {
1198
+ const parts = encoded.split(" ");
1199
+ if (parts.length !== 2) return defaultPoint;
1200
+ x = parseFloat(parts[0]);
1201
+ y = parseFloat(parts[1]);
1202
+ } else {
1203
+ const minusIdx = encoded.indexOf("-", encoded[0] === "-" ? 1 : 0);
1204
+ if (minusIdx === -1) return defaultPoint;
1205
+ x = parseFloat(encoded.slice(0, minusIdx));
1206
+ y = parseFloat(encoded.slice(minusIdx));
1207
+ }
1208
+ if (isNaN(x) || isNaN(y)) return defaultPoint;
1209
+ return { x, y };
1210
+ } else {
1211
+ const buf = BitBuffer.fromBase64(encoded, base64Opts);
1212
+ const [x, y] = buf.decodeFixedPoints(2, scheme);
1213
+ return { x, y };
1214
+ }
1215
+ } catch {
1216
+ return defaultPoint;
1217
+ }
1218
+ }
1219
+ };
1220
+ }
1221
+ function encodeFloatAllModes(value, opts = {}) {
1222
+ const { decimals = 2, precision } = opts;
1223
+ const scheme = resolvePrecision(precision);
1224
+ const multiplier = Math.pow(10, decimals);
1225
+ const truncated = Math.round(value * multiplier) / multiplier;
1226
+ const stringEncoded = truncated.toFixed(decimals);
1227
+ const buf = new BitBuffer();
1228
+ buf.encodeFixedPoints([value], scheme);
1229
+ return {
1230
+ string: stringEncoded,
1231
+ base64: buf.toBase64(),
1232
+ bits: buf.end
1233
+ };
1234
+ }
1235
+ function encodePointAllModes(point, opts = {}) {
1236
+ const { decimals = 2, precision } = opts;
1237
+ const scheme = resolvePrecision(precision);
1238
+ const multiplier = Math.pow(10, decimals);
1239
+ const xTrunc = Math.round(point.x * multiplier) / multiplier;
1240
+ const yTrunc = Math.round(point.y * multiplier) / multiplier;
1241
+ const xStr = xTrunc.toFixed(decimals);
1242
+ const yStr = yTrunc.toFixed(decimals);
1243
+ const delimiter = yTrunc >= 0 ? "+" : "";
1244
+ const stringEncoded = `${xStr}${delimiter}${yStr}`;
1245
+ const buf = new BitBuffer();
1246
+ buf.encodeFixedPoints([point.x, point.y], scheme);
1247
+ return {
1248
+ string: stringEncoded,
1249
+ base64: buf.toBase64(),
1250
+ bits: buf.end
1251
+ };
1252
+ }
1253
+
429
1254
  // src/index.ts
430
1255
  function serializeParams(params) {
431
1256
  const searchParams = new URLSearchParams();
@@ -472,12 +1297,29 @@ function updateUrl(params, push = false) {
472
1297
  // src/hash.ts
473
1298
  setDefaultStrategy(hashStrategy);
474
1299
 
1300
+ exports.ALPHABETS = ALPHABETS;
1301
+ exports.BASE64_CHARS = BASE64_CHARS;
1302
+ exports.BitBuffer = BitBuffer;
1303
+ exports.PRECISION_SCHEMES = precisionSchemes;
1304
+ exports.base64Decode = base64Decode;
1305
+ exports.base64Encode = base64Encode;
1306
+ exports.base64FloatParam = base64FloatParam;
1307
+ exports.base64Param = base64Param;
1308
+ exports.binaryParam = binaryParam;
475
1309
  exports.boolParam = boolParam;
1310
+ exports.bytesToFloat = bytesToFloat;
1311
+ exports.clearParams = clearParams;
476
1312
  exports.codeParam = codeParam;
477
1313
  exports.codesParam = codesParam;
1314
+ exports.createLookupMap = createLookupMap;
478
1315
  exports.defStringParam = defStringParam;
1316
+ exports.encodeFloatAllModes = encodeFloatAllModes;
1317
+ exports.encodePointAllModes = encodePointAllModes;
479
1318
  exports.enumParam = enumParam;
480
1319
  exports.floatParam = floatParam;
1320
+ exports.floatToBytes = floatToBytes;
1321
+ exports.fromFixedPoint = fromFixedPoint;
1322
+ exports.fromFloat = fromFloat;
481
1323
  exports.getCurrentParams = getCurrentParams;
482
1324
  exports.getDefaultStrategy = getDefaultStrategy;
483
1325
  exports.hashStrategy = hashStrategy;
@@ -485,21 +1327,29 @@ exports.intParam = intParam;
485
1327
  exports.multiFloatParam = multiFloatParam;
486
1328
  exports.multiIntParam = multiIntParam;
487
1329
  exports.multiStringParam = multiStringParam;
1330
+ exports.notifyLocationChange = notifyLocationChange;
488
1331
  exports.numberArrayParam = numberArrayParam;
489
1332
  exports.optIntParam = optIntParam;
490
1333
  exports.paginationParam = paginationParam;
491
1334
  exports.parseMultiParams = parseMultiParams;
492
1335
  exports.parseParams = parseParams;
1336
+ exports.pointParam = pointParam;
1337
+ exports.precisionSchemes = precisionSchemes;
493
1338
  exports.queryStrategy = queryStrategy;
1339
+ exports.resolveAlphabet = resolveAlphabet;
1340
+ exports.resolvePrecision = resolvePrecision;
494
1341
  exports.serializeMultiParams = serializeMultiParams;
495
1342
  exports.serializeParams = serializeParams;
496
1343
  exports.setDefaultStrategy = setDefaultStrategy;
497
1344
  exports.stringParam = stringParam;
498
1345
  exports.stringsParam = stringsParam;
1346
+ exports.toFixedPoint = toFixedPoint;
1347
+ exports.toFloat = toFloat;
499
1348
  exports.updateUrl = updateUrl;
500
- exports.useMultiUrlParam = useMultiUrlParam;
501
- exports.useMultiUrlParams = useMultiUrlParams;
502
- exports.useUrlParam = useUrlParam;
503
- exports.useUrlParams = useUrlParams;
1349
+ exports.useMultiUrlState = useMultiUrlState;
1350
+ exports.useMultiUrlStates = useMultiUrlStates;
1351
+ exports.useUrlState = useUrlState;
1352
+ exports.useUrlStates = useUrlStates;
1353
+ exports.validateAlphabet = validateAlphabet;
504
1354
  //# sourceMappingURL=hash.cjs.map
505
1355
  //# sourceMappingURL=hash.cjs.map