@koine/browser 1.0.88

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 ADDED
@@ -0,0 +1 @@
1
+ # @koine/browser
@@ -0,0 +1,53 @@
1
+ export declare type CreateStorageConfig = Record<string, any>;
2
+ /**
3
+ * Utility to create a storage instance to interact with `localStorage` using
4
+ * encrypted (encoded) key/values.
5
+ */
6
+ export declare const createStorage: <T extends CreateStorageConfig>(config: Partial<T>) => {
7
+ /**
8
+ * Get all storage value (it uses `localStorage.get()`).
9
+ *
10
+ * Unparseable values with `JSON.parse()` return their value as it is.
11
+ * If the given `key` argument is not found `null` is returned.
12
+ */
13
+ get<TKey extends keyof T>(key: TKey): T[TKey] | null;
14
+ /**
15
+ * Get all storage values (it uses `localStorage.get()`).
16
+ *
17
+ * `undefined` and `null` values are not returned.
18
+ */
19
+ getAll(): T;
20
+ /**
21
+ * Set a storage value (it uses `localStorage.set()`).
22
+ *
23
+ * Non-string values are stringified with `JSON.stringify()`
24
+ */
25
+ set<TKey_1 extends keyof T>(key: TKey_1, value: T[TKey_1]): void;
26
+ /**
27
+ * Set all given storage values (it uses `localStorage.set()`).
28
+ *
29
+ * Non-string values are stringified with `JSON.stringify()`, `undefined`
30
+ * and `null` values are removed from the storage
31
+ */
32
+ setMany(newValues: Partial<T>): void;
33
+ /**
34
+ * Check if a storage value is _truthy_ (it uses `localStorage.get()`).
35
+ */
36
+ has<TKey_2 extends keyof T>(key: TKey_2): boolean;
37
+ /**
38
+ * Remove a storage value (it uses `localStorage.remove()`).
39
+ */
40
+ remove<TKey_3 extends keyof T>(key: TKey_3): void;
41
+ /**
42
+ * Clear all storage values (it uses `localStorage.remove()`).
43
+ */
44
+ clear(): void;
45
+ /**
46
+ * Watch a storage value changes, this needs to be executed only in browser
47
+ * context (it uses `window.addEventListener("storage")`).
48
+ *
49
+ * Inspiration from [Multi Tab Logout in React — Redux](https://medium.com/front-end-weekly/multi-tab-logout-in-react-redux-4715f071c7fa)
50
+ */
51
+ watch: <TKey_4 extends keyof T>(keyToWatch: TKey_4, onRemoved?: () => void, onAdded?: () => void) => (() => void) | undefined;
52
+ };
53
+ export default createStorage;
@@ -0,0 +1,132 @@
1
+ import { __assign } from "tslib";
2
+ import { decode, encode, isBrowser, isNullOrUndefined, isString, } from "@koine/utils";
3
+ import { on } from "@koine/dom";
4
+ /**
5
+ * Utility to create a storage instance to interact with `localStorage` using
6
+ * encrypted (encoded) key/values.
7
+ */
8
+ export var createStorage = function (config) {
9
+ var methodsMap = { g: "getItem", s: "setItem", r: "removeItem" };
10
+ /**
11
+ * Super minifiable localStorage wrapper with SSR safety
12
+ */
13
+ var ls = function (method, key, value) {
14
+ return isBrowser
15
+ ? localStorage[methodsMap[method]](key, value)
16
+ : function () {
17
+ if (process.env["NODE_ENV"] !== "production") {
18
+ console.warn("[@koine/utils] createStorage: localStorage does not exists in this environment.");
19
+ }
20
+ };
21
+ };
22
+ var keys = Object.keys(config).reduce(function (map, key) {
23
+ var _a;
24
+ return (__assign(__assign({}, map), (_a = {}, _a[key] = encode(key), _a)));
25
+ }, {});
26
+ return {
27
+ /**
28
+ * Get all storage value (it uses `localStorage.get()`).
29
+ *
30
+ * Unparseable values with `JSON.parse()` return their value as it is.
31
+ * If the given `key` argument is not found `null` is returned.
32
+ */
33
+ get: function (key) {
34
+ var stored = ls("g", keys[key]);
35
+ if (stored) {
36
+ stored = decode(stored);
37
+ try {
38
+ return JSON.parse(stored);
39
+ }
40
+ catch (_e) {
41
+ return stored;
42
+ }
43
+ }
44
+ return null;
45
+ },
46
+ /**
47
+ * Get all storage values (it uses `localStorage.get()`).
48
+ *
49
+ * `undefined` and `null` values are not returned.
50
+ */
51
+ getAll: function () {
52
+ var all = {};
53
+ for (var key in keys) {
54
+ var value = this.get(key);
55
+ if (!isNullOrUndefined(value)) {
56
+ all[key] = value;
57
+ }
58
+ }
59
+ return all;
60
+ },
61
+ /**
62
+ * Set a storage value (it uses `localStorage.set()`).
63
+ *
64
+ * Non-string values are stringified with `JSON.stringify()`
65
+ */
66
+ set: function (key, value) {
67
+ ls("s", keys[key], isString(value) ? encode(value) : encode(JSON.stringify(value)));
68
+ },
69
+ /**
70
+ * Set all given storage values (it uses `localStorage.set()`).
71
+ *
72
+ * Non-string values are stringified with `JSON.stringify()`, `undefined`
73
+ * and `null` values are removed from the storage
74
+ */
75
+ setMany: function (newValues) {
76
+ for (var key in newValues) {
77
+ var value = newValues[key];
78
+ if (!isNullOrUndefined(value)) {
79
+ this.set(key, value);
80
+ }
81
+ else {
82
+ this.remove(key);
83
+ }
84
+ }
85
+ },
86
+ /**
87
+ * Check if a storage value is _truthy_ (it uses `localStorage.get()`).
88
+ */
89
+ has: function (key) {
90
+ var stored = ls("g", keys[key]);
91
+ return !!stored;
92
+ },
93
+ /**
94
+ * Remove a storage value (it uses `localStorage.remove()`).
95
+ */
96
+ remove: function (key) {
97
+ ls("r", keys[key]);
98
+ },
99
+ /**
100
+ * Clear all storage values (it uses `localStorage.remove()`).
101
+ */
102
+ clear: function () {
103
+ for (var key in keys) {
104
+ ls("r", keys[key]);
105
+ }
106
+ },
107
+ /**
108
+ * Watch a storage value changes, this needs to be executed only in browser
109
+ * context (it uses `window.addEventListener("storage")`).
110
+ *
111
+ * Inspiration from [Multi Tab Logout in React — Redux](https://medium.com/front-end-weekly/multi-tab-logout-in-react-redux-4715f071c7fa)
112
+ */
113
+ watch: function (keyToWatch, onRemoved, onAdded) {
114
+ var handler = function (event) {
115
+ var key = event.key, oldValue = event.oldValue, newValue = event.newValue;
116
+ if (key === keys[keyToWatch]) {
117
+ if (oldValue && !newValue) {
118
+ onRemoved === null || onRemoved === void 0 ? void 0 : onRemoved();
119
+ }
120
+ else if (!oldValue && newValue) {
121
+ onAdded === null || onAdded === void 0 ? void 0 : onAdded();
122
+ }
123
+ }
124
+ };
125
+ if (!isBrowser)
126
+ return function () { return void 0; };
127
+ var listener = on(window, "storage", handler);
128
+ return listener;
129
+ },
130
+ };
131
+ };
132
+ export default createStorage;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * It returns a `Date` object from a date `string` adjusted on the user timeZone,
3
+ * if a timeZone is not provided we try getting it from the `Intl` browwser native
4
+ * API. It gracefully falls back returning a _non-timezone-based_ `Date`.
5
+ *
6
+ * @category date
7
+ *
8
+ * @resources
9
+ * - to get the timeZone client side see [this article](https://attacomsian.com/blog/javascript-current-timezone)
10
+ * - for converting the date based on the time zone [date-fns docs](https://date-fns.org/v2.27.0/docs/Time-Zones) and [date-fns-tz docs](https://github.com/marnusw/date-fns-tz)
11
+ *
12
+ * @param dateString A parseable date as string, `Z` is automatically suffixed if not present to correctly get time zone based time from a UTC date.
13
+ * @param timeZone Optionally pass a timeZone (e.g. from user preference or from the server), it falls back trying to read it from the `Intl` browwser native API.
14
+ */
15
+ export declare function getZonedDate(dateString?: string, timeZone?: string): Date;
16
+ export default getZonedDate;
@@ -0,0 +1,37 @@
1
+ import utcToZonedTime from "date-fns-tz/utcToZonedTime";
2
+ import { isBrowser } from "@koine/utils";
3
+ /**
4
+ * It returns a `Date` object from a date `string` adjusted on the user timeZone,
5
+ * if a timeZone is not provided we try getting it from the `Intl` browwser native
6
+ * API. It gracefully falls back returning a _non-timezone-based_ `Date`.
7
+ *
8
+ * @category date
9
+ *
10
+ * @resources
11
+ * - to get the timeZone client side see [this article](https://attacomsian.com/blog/javascript-current-timezone)
12
+ * - for converting the date based on the time zone [date-fns docs](https://date-fns.org/v2.27.0/docs/Time-Zones) and [date-fns-tz docs](https://github.com/marnusw/date-fns-tz)
13
+ *
14
+ * @param dateString A parseable date as string, `Z` is automatically suffixed if not present to correctly get time zone based time from a UTC date.
15
+ * @param timeZone Optionally pass a timeZone (e.g. from user preference or from the server), it falls back trying to read it from the `Intl` browwser native API.
16
+ */
17
+ export function getZonedDate(dateString, timeZone) {
18
+ if (dateString === void 0) { dateString = ""; }
19
+ if (!dateString.endsWith("Z"))
20
+ dateString += "Z";
21
+ if (!timeZone && isBrowser) {
22
+ try {
23
+ timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
24
+ }
25
+ catch (e) {
26
+ if (process.env["NODE_ENV"] !== "production") {
27
+ console.warn("[@koine/utils:getZonedDate] failed reading timeZone, error", e);
28
+ }
29
+ // no need to do anything here, it just means `Intl` failed, probably
30
+ // because the browser does not support it
31
+ }
32
+ }
33
+ return timeZone
34
+ ? utcToZonedTime(new Date(dateString), timeZone)
35
+ : new Date(dateString);
36
+ }
37
+ export default getZonedDate;
@@ -0,0 +1,10 @@
1
+ export declare type GtmPageviewArgs = [
2
+ page_path?: string,
3
+ page_title?: string,
4
+ page_location?: string
5
+ ];
6
+ /**
7
+ * @category analytics-google
8
+ */
9
+ export declare const gtagPageview: (page_path?: string | undefined, page_title?: string | undefined, page_location?: string | undefined) => void;
10
+ export default gtagPageview;
@@ -0,0 +1,30 @@
1
+ import { isUndefined } from "@koine/utils";
2
+ /**
3
+ * @category analytics-google
4
+ */
5
+ export var gtagPageview = function () {
6
+ var args = [];
7
+ for (var _i = 0; _i < arguments.length; _i++) {
8
+ args[_i] = arguments[_i];
9
+ }
10
+ if (!isUndefined(window) && !isUndefined(window.gtag)) {
11
+ window.gtag("event", "page_view", {
12
+ page_path: args[0] || location.pathname,
13
+ page_title: args[1] || document.title,
14
+ page_location: args[2] || location.href,
15
+ // send_to: '<GA_MEASUREMENT_ID>'
16
+ });
17
+ }
18
+ };
19
+ export default gtagPageview;
20
+ // export type GtmEventArgs = [
21
+ // eventCategory?: string,
22
+ // eventAction?: string,
23
+ // eventLabel?: string,
24
+ // eventValue?: string
25
+ // ];
26
+ // export const event = (...args: GtmEventArgs) => {
27
+ // if (!isUndefined(window) && !isUndefined(window.gtag)) {
28
+ // window.gtag("send", "event", ...args);
29
+ // }
30
+ // };
package/index.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ export * from "./createStorage";
2
+ export * from "./getZonedDate";
3
+ export * from "./gtagPageview";
4
+ export * from "./isIE";
5
+ export * from "./isMobile";
6
+ export * from "./navigateToHash";
7
+ export * from "./navigateToHashParams";
8
+ export * from "./navigateToMergedHashParams";
9
+ export * from "./navigateToMergedParams";
10
+ export * from "./navigateToParams";
11
+ export * from "./navigateWithoutUrlParam";
12
+ export * from "./redirectTo";
package/index.js ADDED
@@ -0,0 +1,12 @@
1
+ export * from "./createStorage";
2
+ export * from "./getZonedDate";
3
+ export * from "./gtagPageview";
4
+ export * from "./isIE";
5
+ export * from "./isMobile";
6
+ export * from "./navigateToHash";
7
+ export * from "./navigateToHashParams";
8
+ export * from "./navigateToMergedHashParams";
9
+ export * from "./navigateToMergedParams";
10
+ export * from "./navigateToParams";
11
+ export * from "./navigateWithoutUrlParam";
12
+ export * from "./redirectTo";
package/isIE.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @category detect
3
+ * @category is
4
+ * @see https://stackoverflow.com/a/21712356/12285349
5
+ */
6
+ export declare function isIE(ssrValue?: boolean): boolean;
7
+ export default isIE;
package/isIE.js ADDED
@@ -0,0 +1,18 @@
1
+ import { isServer } from "@koine/utils";
2
+ /**
3
+ * @category detect
4
+ * @category is
5
+ * @see https://stackoverflow.com/a/21712356/12285349
6
+ */
7
+ export function isIE(ssrValue) {
8
+ if (ssrValue === void 0) { ssrValue = true; }
9
+ if (isServer) {
10
+ return ssrValue;
11
+ }
12
+ var ua = window.navigator.userAgent;
13
+ if (ua.indexOf("MSIE ") > 0 || ua.indexOf("Trident/") > 0) {
14
+ return true;
15
+ }
16
+ return false;
17
+ }
18
+ export default isIE;
package/isMobile.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @category detect
3
+ * @category is
4
+ * @see https://stackoverflow.com/a/3540295
5
+ */
6
+ export declare function isMobile(ssrValue?: boolean): boolean;
7
+ export default isMobile;
package/isMobile.js ADDED
@@ -0,0 +1,16 @@
1
+ import { isServer } from "@koine/utils";
2
+ /**
3
+ * @category detect
4
+ * @category is
5
+ * @see https://stackoverflow.com/a/3540295
6
+ */
7
+ export function isMobile(ssrValue) {
8
+ if (ssrValue === void 0) { ssrValue = true; }
9
+ if (isServer) {
10
+ return ssrValue;
11
+ }
12
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(window.navigator.userAgent);
13
+ // return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
14
+ // || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4)
15
+ }
16
+ export default isMobile;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * It updates the browser's location hash by replacing the history state.
3
+ * The non-silent standard way would simply be `location.hash = "#new-hash"`
4
+ *
5
+ * @category location
6
+ */
7
+ export declare function navigateToHash(hash?: string): void;
8
+ export default navigateToHash;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * It updates the browser's location hash by replacing the history state.
3
+ * The non-silent standard way would simply be `location.hash = "#new-hash"`
4
+ *
5
+ * @category location
6
+ */
7
+ export function navigateToHash(hash) {
8
+ if (hash === void 0) { hash = ""; }
9
+ var pathname = location.pathname, search = location.search;
10
+ history.replaceState(null, "", pathname + (search ? "?" + search : "") + "#" + hash);
11
+ }
12
+ export default navigateToHash;
@@ -0,0 +1,9 @@
1
+ import { type AnyQueryParams } from "@koine/utils";
2
+ /**
3
+ * It updates the `location.hash` with the given query params, it uses `location.hash`
4
+ * if a second argument `hash` is not provded
5
+ *
6
+ * @category location
7
+ */
8
+ export declare function navigateToHashParams(params?: string | AnyQueryParams, hash?: string): string;
9
+ export default navigateToHashParams;
@@ -0,0 +1,21 @@
1
+ import { buildUrlQueryString, getUrlHashPathname, } from "@koine/utils";
2
+ /**
3
+ * It updates the `location.hash` with the given query params, it uses `location.hash`
4
+ * if a second argument `hash` is not provded
5
+ *
6
+ * @category location
7
+ */
8
+ export function navigateToHashParams(params, hash) {
9
+ if (params === void 0) { params = {}; }
10
+ if (hash === void 0) { hash = ""; }
11
+ var useLocation = !hash;
12
+ hash = hash || location.hash;
13
+ var hashQueryLess = getUrlHashPathname(hash);
14
+ var queryString = typeof params === "string" ? params : buildUrlQueryString(params);
15
+ var newHash = "#/" + hashQueryLess + queryString;
16
+ if (useLocation) {
17
+ location.hash = newHash;
18
+ }
19
+ return newHash;
20
+ }
21
+ export default navigateToHashParams;
@@ -0,0 +1,8 @@
1
+ import { type AnyQueryParams } from "@koine/utils";
2
+ /**
3
+ * It updates the "query params" within the `location.hash`, it uses `location`
4
+ *
5
+ * @category location
6
+ */
7
+ export declare function navigateToMergedHashParams(params?: NonNullable<AnyQueryParams>, hash?: string): string;
8
+ export default navigateToMergedHashParams;
@@ -0,0 +1,13 @@
1
+ import { mergeUrlQueryParams, getUrlHashParams, } from "@koine/utils";
2
+ import { navigateToHashParams } from "./navigateToHashParams";
3
+ /**
4
+ * It updates the "query params" within the `location.hash`, it uses `location`
5
+ *
6
+ * @category location
7
+ */
8
+ export function navigateToMergedHashParams(params, hash) {
9
+ if (params === void 0) { params = {}; }
10
+ if (hash === void 0) { hash = ""; }
11
+ return navigateToHashParams(mergeUrlQueryParams(getUrlHashParams(hash), params), hash);
12
+ }
13
+ export default navigateToMergedHashParams;
@@ -0,0 +1,9 @@
1
+ import { type AnyQueryParams } from "@koine/utils";
2
+ /**
3
+ * Merge current URL query parameters with the given ones, it uses `history`.
4
+ *
5
+ * @category location
6
+ * @param replace Replace URL instead of pushing it in the history stack. By default it pushes it.
7
+ */
8
+ export declare function navigateToMergedParams(params?: NonNullable<AnyQueryParams>, replace?: boolean): string;
9
+ export default navigateToMergedParams;
@@ -0,0 +1,13 @@
1
+ import { mergeUrlQueryParams, getUrlQueryParams, } from "@koine/utils";
2
+ import { navigateToParams } from "./navigateToParams";
3
+ /**
4
+ * Merge current URL query parameters with the given ones, it uses `history`.
5
+ *
6
+ * @category location
7
+ * @param replace Replace URL instead of pushing it in the history stack. By default it pushes it.
8
+ */
9
+ export function navigateToMergedParams(params, replace) {
10
+ if (params === void 0) { params = {}; }
11
+ return navigateToParams(mergeUrlQueryParams(getUrlQueryParams(), params), replace);
12
+ }
13
+ export default navigateToMergedParams;
@@ -0,0 +1,10 @@
1
+ import { type AnyQueryParams } from "@koine/utils";
2
+ /**
3
+ * Change current URL query parameters, it uses `history`.
4
+ *
5
+ * @category location
6
+ * @param replace Replace URL instead of pushing it in the history stack. By default it pushes it.
7
+ * @returns The query string with initial `?`
8
+ */
9
+ export declare function navigateToParams(params?: string | AnyQueryParams, replace?: boolean): string;
10
+ export default navigateToParams;
@@ -0,0 +1,17 @@
1
+ import { isBrowser, buildUrlQueryString, } from "@koine/utils";
2
+ /**
3
+ * Change current URL query parameters, it uses `history`.
4
+ *
5
+ * @category location
6
+ * @param replace Replace URL instead of pushing it in the history stack. By default it pushes it.
7
+ * @returns The query string with initial `?`
8
+ */
9
+ export function navigateToParams(params, replace) {
10
+ if (params === void 0) { params = {}; }
11
+ var queryString = typeof params === "string" ? params : buildUrlQueryString(params);
12
+ if (isBrowser) {
13
+ history[replace ? "replaceState" : "pushState"](null, "", location.pathname + queryString);
14
+ }
15
+ return queryString;
16
+ }
17
+ export default navigateToParams;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Remove URL query parameter, it uses `history`
3
+ *
4
+ * @category location
5
+ * @param replace Replace URL instead of pushing it in the history stack. By default it pushes it.
6
+ */
7
+ export declare function navigateWithoutUrlParam(paramName?: string, replace?: boolean): string;
8
+ export default navigateWithoutUrlParam;
@@ -0,0 +1,19 @@
1
+ import { getUrlQueryParams } from "@koine/utils";
2
+ import { navigateToParams } from "./navigateToParams";
3
+ /**
4
+ * Remove URL query parameter, it uses `history`
5
+ *
6
+ * @category location
7
+ * @param replace Replace URL instead of pushing it in the history stack. By default it pushes it.
8
+ */
9
+ export function navigateWithoutUrlParam(paramName, replace) {
10
+ var params = {};
11
+ var currentParams = getUrlQueryParams();
12
+ for (var key in currentParams) {
13
+ if (key !== paramName) {
14
+ params[key] = currentParams[key];
15
+ }
16
+ }
17
+ return navigateToParams(params, replace);
18
+ }
19
+ export default navigateWithoutUrlParam;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createStorage = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var utils_1 = require("@koine/utils");
6
+ var dom_1 = require("@koine/dom");
7
+ /**
8
+ * Utility to create a storage instance to interact with `localStorage` using
9
+ * encrypted (encoded) key/values.
10
+ */
11
+ var createStorage = function (config) {
12
+ var methodsMap = { g: "getItem", s: "setItem", r: "removeItem" };
13
+ /**
14
+ * Super minifiable localStorage wrapper with SSR safety
15
+ */
16
+ var ls = function (method, key, value) {
17
+ return utils_1.isBrowser
18
+ ? localStorage[methodsMap[method]](key, value)
19
+ : function () {
20
+ if (process.env["NODE_ENV"] !== "production") {
21
+ console.warn("[@koine/utils] createStorage: localStorage does not exists in this environment.");
22
+ }
23
+ };
24
+ };
25
+ var keys = Object.keys(config).reduce(function (map, key) {
26
+ var _a;
27
+ return (tslib_1.__assign(tslib_1.__assign({}, map), (_a = {}, _a[key] = (0, utils_1.encode)(key), _a)));
28
+ }, {});
29
+ return {
30
+ /**
31
+ * Get all storage value (it uses `localStorage.get()`).
32
+ *
33
+ * Unparseable values with `JSON.parse()` return their value as it is.
34
+ * If the given `key` argument is not found `null` is returned.
35
+ */
36
+ get: function (key) {
37
+ var stored = ls("g", keys[key]);
38
+ if (stored) {
39
+ stored = (0, utils_1.decode)(stored);
40
+ try {
41
+ return JSON.parse(stored);
42
+ }
43
+ catch (_e) {
44
+ return stored;
45
+ }
46
+ }
47
+ return null;
48
+ },
49
+ /**
50
+ * Get all storage values (it uses `localStorage.get()`).
51
+ *
52
+ * `undefined` and `null` values are not returned.
53
+ */
54
+ getAll: function () {
55
+ var all = {};
56
+ for (var key in keys) {
57
+ var value = this.get(key);
58
+ if (!(0, utils_1.isNullOrUndefined)(value)) {
59
+ all[key] = value;
60
+ }
61
+ }
62
+ return all;
63
+ },
64
+ /**
65
+ * Set a storage value (it uses `localStorage.set()`).
66
+ *
67
+ * Non-string values are stringified with `JSON.stringify()`
68
+ */
69
+ set: function (key, value) {
70
+ ls("s", keys[key], (0, utils_1.isString)(value) ? (0, utils_1.encode)(value) : (0, utils_1.encode)(JSON.stringify(value)));
71
+ },
72
+ /**
73
+ * Set all given storage values (it uses `localStorage.set()`).
74
+ *
75
+ * Non-string values are stringified with `JSON.stringify()`, `undefined`
76
+ * and `null` values are removed from the storage
77
+ */
78
+ setMany: function (newValues) {
79
+ for (var key in newValues) {
80
+ var value = newValues[key];
81
+ if (!(0, utils_1.isNullOrUndefined)(value)) {
82
+ this.set(key, value);
83
+ }
84
+ else {
85
+ this.remove(key);
86
+ }
87
+ }
88
+ },
89
+ /**
90
+ * Check if a storage value is _truthy_ (it uses `localStorage.get()`).
91
+ */
92
+ has: function (key) {
93
+ var stored = ls("g", keys[key]);
94
+ return !!stored;
95
+ },
96
+ /**
97
+ * Remove a storage value (it uses `localStorage.remove()`).
98
+ */
99
+ remove: function (key) {
100
+ ls("r", keys[key]);
101
+ },
102
+ /**
103
+ * Clear all storage values (it uses `localStorage.remove()`).
104
+ */
105
+ clear: function () {
106
+ for (var key in keys) {
107
+ ls("r", keys[key]);
108
+ }
109
+ },
110
+ /**
111
+ * Watch a storage value changes, this needs to be executed only in browser
112
+ * context (it uses `window.addEventListener("storage")`).
113
+ *
114
+ * Inspiration from [Multi Tab Logout in React — Redux](https://medium.com/front-end-weekly/multi-tab-logout-in-react-redux-4715f071c7fa)
115
+ */
116
+ watch: function (keyToWatch, onRemoved, onAdded) {
117
+ var handler = function (event) {
118
+ var key = event.key, oldValue = event.oldValue, newValue = event.newValue;
119
+ if (key === keys[keyToWatch]) {
120
+ if (oldValue && !newValue) {
121
+ onRemoved === null || onRemoved === void 0 ? void 0 : onRemoved();
122
+ }
123
+ else if (!oldValue && newValue) {
124
+ onAdded === null || onAdded === void 0 ? void 0 : onAdded();
125
+ }
126
+ }
127
+ };
128
+ if (!utils_1.isBrowser)
129
+ return function () { return void 0; };
130
+ var listener = (0, dom_1.on)(window, "storage", handler);
131
+ return listener;
132
+ },
133
+ };
134
+ };
135
+ exports.createStorage = createStorage;
136
+ exports.default = exports.createStorage;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getZonedDate = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var utcToZonedTime_1 = tslib_1.__importDefault(require("date-fns-tz/utcToZonedTime"));
6
+ var utils_1 = require("@koine/utils");
7
+ /**
8
+ * It returns a `Date` object from a date `string` adjusted on the user timeZone,
9
+ * if a timeZone is not provided we try getting it from the `Intl` browwser native
10
+ * API. It gracefully falls back returning a _non-timezone-based_ `Date`.
11
+ *
12
+ * @category date
13
+ *
14
+ * @resources
15
+ * - to get the timeZone client side see [this article](https://attacomsian.com/blog/javascript-current-timezone)
16
+ * - for converting the date based on the time zone [date-fns docs](https://date-fns.org/v2.27.0/docs/Time-Zones) and [date-fns-tz docs](https://github.com/marnusw/date-fns-tz)
17
+ *
18
+ * @param dateString A parseable date as string, `Z` is automatically suffixed if not present to correctly get time zone based time from a UTC date.
19
+ * @param timeZone Optionally pass a timeZone (e.g. from user preference or from the server), it falls back trying to read it from the `Intl` browwser native API.
20
+ */
21
+ function getZonedDate(dateString, timeZone) {
22
+ if (dateString === void 0) { dateString = ""; }
23
+ if (!dateString.endsWith("Z"))
24
+ dateString += "Z";
25
+ if (!timeZone && utils_1.isBrowser) {
26
+ try {
27
+ timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
28
+ }
29
+ catch (e) {
30
+ if (process.env["NODE_ENV"] !== "production") {
31
+ console.warn("[@koine/utils:getZonedDate] failed reading timeZone, error", e);
32
+ }
33
+ // no need to do anything here, it just means `Intl` failed, probably
34
+ // because the browser does not support it
35
+ }
36
+ }
37
+ return timeZone
38
+ ? (0, utcToZonedTime_1.default)(new Date(dateString), timeZone)
39
+ : new Date(dateString);
40
+ }
41
+ exports.getZonedDate = getZonedDate;
42
+ exports.default = getZonedDate;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gtagPageview = void 0;
4
+ var utils_1 = require("@koine/utils");
5
+ /**
6
+ * @category analytics-google
7
+ */
8
+ var gtagPageview = function () {
9
+ var args = [];
10
+ for (var _i = 0; _i < arguments.length; _i++) {
11
+ args[_i] = arguments[_i];
12
+ }
13
+ if (!(0, utils_1.isUndefined)(window) && !(0, utils_1.isUndefined)(window.gtag)) {
14
+ window.gtag("event", "page_view", {
15
+ page_path: args[0] || location.pathname,
16
+ page_title: args[1] || document.title,
17
+ page_location: args[2] || location.href,
18
+ // send_to: '<GA_MEASUREMENT_ID>'
19
+ });
20
+ }
21
+ };
22
+ exports.gtagPageview = gtagPageview;
23
+ exports.default = exports.gtagPageview;
24
+ // export type GtmEventArgs = [
25
+ // eventCategory?: string,
26
+ // eventAction?: string,
27
+ // eventLabel?: string,
28
+ // eventValue?: string
29
+ // ];
30
+ // export const event = (...args: GtmEventArgs) => {
31
+ // if (!isUndefined(window) && !isUndefined(window.gtag)) {
32
+ // window.gtag("send", "event", ...args);
33
+ // }
34
+ // };
package/node/index.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./createStorage"), exports);
5
+ tslib_1.__exportStar(require("./getZonedDate"), exports);
6
+ tslib_1.__exportStar(require("./gtagPageview"), exports);
7
+ tslib_1.__exportStar(require("./isIE"), exports);
8
+ tslib_1.__exportStar(require("./isMobile"), exports);
9
+ tslib_1.__exportStar(require("./navigateToHash"), exports);
10
+ tslib_1.__exportStar(require("./navigateToHashParams"), exports);
11
+ tslib_1.__exportStar(require("./navigateToMergedHashParams"), exports);
12
+ tslib_1.__exportStar(require("./navigateToMergedParams"), exports);
13
+ tslib_1.__exportStar(require("./navigateToParams"), exports);
14
+ tslib_1.__exportStar(require("./navigateWithoutUrlParam"), exports);
15
+ tslib_1.__exportStar(require("./redirectTo"), exports);
package/node/isIE.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isIE = void 0;
4
+ var utils_1 = require("@koine/utils");
5
+ /**
6
+ * @category detect
7
+ * @category is
8
+ * @see https://stackoverflow.com/a/21712356/12285349
9
+ */
10
+ function isIE(ssrValue) {
11
+ if (ssrValue === void 0) { ssrValue = true; }
12
+ if (utils_1.isServer) {
13
+ return ssrValue;
14
+ }
15
+ var ua = window.navigator.userAgent;
16
+ if (ua.indexOf("MSIE ") > 0 || ua.indexOf("Trident/") > 0) {
17
+ return true;
18
+ }
19
+ return false;
20
+ }
21
+ exports.isIE = isIE;
22
+ exports.default = isIE;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isMobile = void 0;
4
+ var utils_1 = require("@koine/utils");
5
+ /**
6
+ * @category detect
7
+ * @category is
8
+ * @see https://stackoverflow.com/a/3540295
9
+ */
10
+ function isMobile(ssrValue) {
11
+ if (ssrValue === void 0) { ssrValue = true; }
12
+ if (utils_1.isServer) {
13
+ return ssrValue;
14
+ }
15
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(window.navigator.userAgent);
16
+ // return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
17
+ // || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4)
18
+ }
19
+ exports.isMobile = isMobile;
20
+ exports.default = isMobile;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.navigateToHash = void 0;
4
+ /**
5
+ * It updates the browser's location hash by replacing the history state.
6
+ * The non-silent standard way would simply be `location.hash = "#new-hash"`
7
+ *
8
+ * @category location
9
+ */
10
+ function navigateToHash(hash) {
11
+ if (hash === void 0) { hash = ""; }
12
+ var pathname = location.pathname, search = location.search;
13
+ history.replaceState(null, "", pathname + (search ? "?" + search : "") + "#" + hash);
14
+ }
15
+ exports.navigateToHash = navigateToHash;
16
+ exports.default = navigateToHash;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.navigateToHashParams = void 0;
4
+ var utils_1 = require("@koine/utils");
5
+ /**
6
+ * It updates the `location.hash` with the given query params, it uses `location.hash`
7
+ * if a second argument `hash` is not provded
8
+ *
9
+ * @category location
10
+ */
11
+ function navigateToHashParams(params, hash) {
12
+ if (params === void 0) { params = {}; }
13
+ if (hash === void 0) { hash = ""; }
14
+ var useLocation = !hash;
15
+ hash = hash || location.hash;
16
+ var hashQueryLess = (0, utils_1.getUrlHashPathname)(hash);
17
+ var queryString = typeof params === "string" ? params : (0, utils_1.buildUrlQueryString)(params);
18
+ var newHash = "#/" + hashQueryLess + queryString;
19
+ if (useLocation) {
20
+ location.hash = newHash;
21
+ }
22
+ return newHash;
23
+ }
24
+ exports.navigateToHashParams = navigateToHashParams;
25
+ exports.default = navigateToHashParams;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.navigateToMergedHashParams = void 0;
4
+ var utils_1 = require("@koine/utils");
5
+ var navigateToHashParams_1 = require("./navigateToHashParams");
6
+ /**
7
+ * It updates the "query params" within the `location.hash`, it uses `location`
8
+ *
9
+ * @category location
10
+ */
11
+ function navigateToMergedHashParams(params, hash) {
12
+ if (params === void 0) { params = {}; }
13
+ if (hash === void 0) { hash = ""; }
14
+ return (0, navigateToHashParams_1.navigateToHashParams)((0, utils_1.mergeUrlQueryParams)((0, utils_1.getUrlHashParams)(hash), params), hash);
15
+ }
16
+ exports.navigateToMergedHashParams = navigateToMergedHashParams;
17
+ exports.default = navigateToMergedHashParams;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.navigateToMergedParams = void 0;
4
+ var utils_1 = require("@koine/utils");
5
+ var navigateToParams_1 = require("./navigateToParams");
6
+ /**
7
+ * Merge current URL query parameters with the given ones, it uses `history`.
8
+ *
9
+ * @category location
10
+ * @param replace Replace URL instead of pushing it in the history stack. By default it pushes it.
11
+ */
12
+ function navigateToMergedParams(params, replace) {
13
+ if (params === void 0) { params = {}; }
14
+ return (0, navigateToParams_1.navigateToParams)((0, utils_1.mergeUrlQueryParams)((0, utils_1.getUrlQueryParams)(), params), replace);
15
+ }
16
+ exports.navigateToMergedParams = navigateToMergedParams;
17
+ exports.default = navigateToMergedParams;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.navigateToParams = void 0;
4
+ var utils_1 = require("@koine/utils");
5
+ /**
6
+ * Change current URL query parameters, it uses `history`.
7
+ *
8
+ * @category location
9
+ * @param replace Replace URL instead of pushing it in the history stack. By default it pushes it.
10
+ * @returns The query string with initial `?`
11
+ */
12
+ function navigateToParams(params, replace) {
13
+ if (params === void 0) { params = {}; }
14
+ var queryString = typeof params === "string" ? params : (0, utils_1.buildUrlQueryString)(params);
15
+ if (utils_1.isBrowser) {
16
+ history[replace ? "replaceState" : "pushState"](null, "", location.pathname + queryString);
17
+ }
18
+ return queryString;
19
+ }
20
+ exports.navigateToParams = navigateToParams;
21
+ exports.default = navigateToParams;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.navigateWithoutUrlParam = void 0;
4
+ var utils_1 = require("@koine/utils");
5
+ var navigateToParams_1 = require("./navigateToParams");
6
+ /**
7
+ * Remove URL query parameter, it uses `history`
8
+ *
9
+ * @category location
10
+ * @param replace Replace URL instead of pushing it in the history stack. By default it pushes it.
11
+ */
12
+ function navigateWithoutUrlParam(paramName, replace) {
13
+ var params = {};
14
+ var currentParams = (0, utils_1.getUrlQueryParams)();
15
+ for (var key in currentParams) {
16
+ if (key !== paramName) {
17
+ params[key] = currentParams[key];
18
+ }
19
+ }
20
+ return (0, navigateToParams_1.navigateToParams)(params, replace);
21
+ }
22
+ exports.navigateWithoutUrlParam = navigateWithoutUrlParam;
23
+ exports.default = navigateWithoutUrlParam;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.redirectTo = void 0;
4
+ var utils_1 = require("@koine/utils");
5
+ /**
6
+ * Redirect to url with params {optionally}, removes eventual trailing question
7
+ * marks from the given URL, it uses `location`
8
+ *
9
+ * @category location
10
+ */
11
+ function redirectTo(url, params) {
12
+ if (utils_1.isBrowser) {
13
+ var queryString = (0, utils_1.buildUrlQueryString)(params);
14
+ location.href = url.replace(/\?+$/g, "") + queryString;
15
+ }
16
+ }
17
+ exports.redirectTo = redirectTo;
18
+ exports.default = redirectTo;
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@koine/browser",
3
+ "sideEffects": false,
4
+ "main": "./node/index.js",
5
+ "typings": "./index.d.ts",
6
+ "dependencies": {
7
+ "@koine/utils": "1.0.88",
8
+ "ts-debounce": "^4.0.0",
9
+ "@koine/dom": "1.0.88",
10
+ "date-fns-tz": "^1.3.7",
11
+ "tslib": "^2.4.0"
12
+ },
13
+ "peerDependencies": {},
14
+ "version": "1.0.88",
15
+ "module": "./index.js",
16
+ "types": "./index.d.ts"
17
+ }
@@ -0,0 +1,9 @@
1
+ import { type AnyQueryParams } from "@koine/utils";
2
+ /**
3
+ * Redirect to url with params {optionally}, removes eventual trailing question
4
+ * marks from the given URL, it uses `location`
5
+ *
6
+ * @category location
7
+ */
8
+ export declare function redirectTo(url: string, params?: AnyQueryParams): void;
9
+ export default redirectTo;
package/redirectTo.js ADDED
@@ -0,0 +1,14 @@
1
+ import { isBrowser, buildUrlQueryString, } from "@koine/utils";
2
+ /**
3
+ * Redirect to url with params {optionally}, removes eventual trailing question
4
+ * marks from the given URL, it uses `location`
5
+ *
6
+ * @category location
7
+ */
8
+ export function redirectTo(url, params) {
9
+ if (isBrowser) {
10
+ var queryString = buildUrlQueryString(params);
11
+ location.href = url.replace(/\?+$/g, "") + queryString;
12
+ }
13
+ }
14
+ export default redirectTo;
package/typings.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * List here the global variables used by third party scripts supported within
3
+ * the `koine` ecosystem. For instance Google Analytics globally available
4
+ * variables.
5
+ */
6
+
7
+ // eslint-disable-next-line no-var,@typescript-eslint/no-explicit-any
8
+ declare var gtag: (...args: any[]) => any;
9
+
10
+ // declare type NullableRecord<T> = {
11
+ // [K in keyof T]: T[K] | null;
12
+ // };