@scalar/helpers 0.4.0 → 0.4.2
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/CHANGELOG.md +12 -0
- package/dist/array/add-to-map-array.js +7 -8
- package/dist/array/is-defined.js +12 -5
- package/dist/array/sort-by-order.js +60 -18
- package/dist/consts/content-types.js +13 -14
- package/dist/dom/freeze-element.js +56 -42
- package/dist/dom/scroll-to-id.js +33 -27
- package/dist/file/json2xml.js +79 -61
- package/dist/general/create-limiter.js +47 -31
- package/dist/general/debounce.js +86 -66
- package/dist/general/extract-config-secrets.js +29 -27
- package/dist/general/has-modifier.js +9 -8
- package/dist/general/is-mac-os.d.ts +1 -5
- package/dist/general/is-mac-os.d.ts.map +1 -1
- package/dist/general/is-mac-os.js +24 -11
- package/dist/http/can-method-have-body.js +4 -6
- package/dist/http/http-info.js +63 -61
- package/dist/http/http-methods.js +4 -7
- package/dist/http/http-status-codes.js +316 -320
- package/dist/http/is-http-method.js +3 -6
- package/dist/http/normalize-http-method.js +19 -19
- package/dist/json/escape-json-pointer.js +6 -5
- package/dist/json/parse-json-pointer-segments.js +11 -6
- package/dist/json/pretty-print-json.d.ts +9 -0
- package/dist/json/pretty-print-json.d.ts.map +1 -0
- package/dist/json/pretty-print-json.js +42 -0
- package/dist/json/unescape-json-pointer.js +7 -5
- package/dist/node/path.js +168 -138
- package/dist/object/get-value-at-path.js +17 -11
- package/dist/object/is-object.js +24 -10
- package/dist/object/local-storage.js +50 -42
- package/dist/object/object-entries.js +2 -5
- package/dist/object/object-keys.js +5 -5
- package/dist/object/object-replace.js +13 -12
- package/dist/object/omit-undefined-values.js +18 -18
- package/dist/object/prevent-pollution.js +27 -10
- package/dist/object/to-json-compatible.js +71 -62
- package/dist/queue/queue.js +103 -84
- package/dist/regex/find-variables.js +13 -8
- package/dist/regex/regex-helpers.js +17 -17
- package/dist/regex/replace-variables.js +21 -19
- package/dist/string/camel-to-title.js +10 -5
- package/dist/string/capitalize.js +5 -5
- package/dist/string/create-hash.js +19 -16
- package/dist/string/generate-hash.js +153 -127
- package/dist/string/iterate-title.js +13 -11
- package/dist/string/truncate.js +16 -9
- package/dist/testing/console-spies.js +19 -24
- package/dist/testing/measure.js +42 -19
- package/dist/testing/measure.test-d.js +12 -9
- package/dist/testing/sleep.js +5 -5
- package/dist/url/ensure-protocol.js +8 -10
- package/dist/url/extract-server-from-path.js +53 -28
- package/dist/url/is-local-url.js +24 -18
- package/dist/url/is-relative-path.js +15 -13
- package/dist/url/is-valid-url.js +17 -10
- package/dist/url/make-url-absolute.js +39 -28
- package/dist/url/merge-urls.js +74 -52
- package/dist/url/redirect-to-proxy.js +68 -39
- package/package.json +5 -9
- package/dist/array/add-to-map-array.js.map +0 -7
- package/dist/array/is-defined.js.map +0 -7
- package/dist/array/sort-by-order.js.map +0 -7
- package/dist/consts/content-types.js.map +0 -7
- package/dist/dom/freeze-element.js.map +0 -7
- package/dist/dom/scroll-to-id.js.map +0 -7
- package/dist/file/json2xml.js.map +0 -7
- package/dist/general/create-limiter.js.map +0 -7
- package/dist/general/debounce.js.map +0 -7
- package/dist/general/extract-config-secrets.js.map +0 -7
- package/dist/general/has-modifier.js.map +0 -7
- package/dist/general/is-mac-os.js.map +0 -7
- package/dist/http/can-method-have-body.js.map +0 -7
- package/dist/http/http-info.js.map +0 -7
- package/dist/http/http-methods.js.map +0 -7
- package/dist/http/http-status-codes.js.map +0 -7
- package/dist/http/is-http-method.js.map +0 -7
- package/dist/http/normalize-http-method.js.map +0 -7
- package/dist/json/escape-json-pointer.js.map +0 -7
- package/dist/json/parse-json-pointer-segments.js.map +0 -7
- package/dist/json/unescape-json-pointer.js.map +0 -7
- package/dist/node/path.js.map +0 -7
- package/dist/object/get-value-at-path.js.map +0 -7
- package/dist/object/is-object.js.map +0 -7
- package/dist/object/local-storage.js.map +0 -7
- package/dist/object/object-entries.js.map +0 -7
- package/dist/object/object-keys.js.map +0 -7
- package/dist/object/object-replace.js.map +0 -7
- package/dist/object/omit-undefined-values.js.map +0 -7
- package/dist/object/prevent-pollution.js.map +0 -7
- package/dist/object/to-json-compatible.js.map +0 -7
- package/dist/queue/queue.js.map +0 -7
- package/dist/regex/find-variables.js.map +0 -7
- package/dist/regex/regex-helpers.js.map +0 -7
- package/dist/regex/replace-variables.js.map +0 -7
- package/dist/string/camel-to-title.js.map +0 -7
- package/dist/string/capitalize.js.map +0 -7
- package/dist/string/create-hash.js.map +0 -7
- package/dist/string/generate-hash.js.map +0 -7
- package/dist/string/iterate-title.js.map +0 -7
- package/dist/string/truncate.js.map +0 -7
- package/dist/testing/console-spies.js.map +0 -7
- package/dist/testing/measure.js.map +0 -7
- package/dist/testing/measure.test-d.js.map +0 -7
- package/dist/testing/sleep.js.map +0 -7
- package/dist/url/ensure-protocol.js.map +0 -7
- package/dist/url/extract-server-from-path.js.map +0 -7
- package/dist/url/is-local-url.js.map +0 -7
- package/dist/url/is-relative-path.js.map +0 -7
- package/dist/url/is-valid-url.js.map +0 -7
- package/dist/url/make-url-absolute.js.map +0 -7
- package/dist/url/merge-urls.js.map +0 -7
- package/dist/url/redirect-to-proxy.js.map +0 -7
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { REGEX } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { REGEX } from '../regex/regex-helpers.js';
|
|
2
|
+
/**
|
|
3
|
+
* Check if the URL is relative or if it's a domain without protocol
|
|
4
|
+
**/
|
|
5
|
+
export const isRelativePath = (url) => {
|
|
6
|
+
// Allow http:// https:// and other protocols such as file://
|
|
7
|
+
if (REGEX.PROTOCOL.test(url)) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
// Check if it looks like a domain (contains dots and no spaces)
|
|
11
|
+
// This catches cases like "galaxy.scalar.com/planets"
|
|
12
|
+
if (/^[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+(\/|$)/.test(url)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
10
16
|
};
|
|
11
|
-
export {
|
|
12
|
-
isRelativePath
|
|
13
|
-
};
|
|
14
|
-
//# sourceMappingURL=is-relative-path.js.map
|
package/dist/url/is-valid-url.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a given string is a valid URL.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} url - The string to be validated as a URL.
|
|
5
|
+
* @returns {boolean} Returns true if the string is a valid URL, false otherwise.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* isValidUrl('https://www.example.com'); // returns true
|
|
9
|
+
* isValidUrl('not a url'); // returns false
|
|
10
|
+
*/
|
|
11
|
+
export function isValidUrl(url) {
|
|
12
|
+
try {
|
|
13
|
+
return Boolean(new URL(url));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
7
18
|
}
|
|
8
|
-
export {
|
|
9
|
-
isValidUrl
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=is-valid-url.js.map
|
|
@@ -1,30 +1,41 @@
|
|
|
1
|
-
import { combineUrlAndPath } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
import { combineUrlAndPath } from '../url/merge-urls.js';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a relative URL to an absolute URL using the provided base URL or current window location.
|
|
4
|
+
* @param url - The URL to make absolute
|
|
5
|
+
* @param options - Configuration options
|
|
6
|
+
* @param options.baseUrl - Optional base URL to resolve against (defaults to window.location.href)
|
|
7
|
+
* @param options.basePath - If provided, combines with baseUrl or window.location.origin before resolving
|
|
8
|
+
* @returns The absolute URL, or the original URL if it's already absolute or invalid
|
|
9
|
+
*/
|
|
10
|
+
export const makeUrlAbsolute = (url, {
|
|
11
|
+
/** Optional base URL to resolve against (defaults to window.location.href) */
|
|
12
|
+
baseUrl,
|
|
13
|
+
/** If we have a basePath then we resolve against window.location.origin + basePath */
|
|
14
|
+
basePath, } = {}) => {
|
|
15
|
+
// If no base URL provided and we're not in a browser environment, return as-is
|
|
16
|
+
if (typeof window === 'undefined' && !baseUrl) {
|
|
17
|
+
return url;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
// If we can create a URL object without a base, it's already absolute
|
|
21
|
+
new URL(url);
|
|
22
|
+
return url;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// URL is relative, proceed with resolution
|
|
26
|
+
}
|
|
27
|
+
// Use URL constructor which handles path normalization automatically
|
|
28
|
+
try {
|
|
29
|
+
let base = baseUrl || window.location.href;
|
|
30
|
+
// If basePath is provided, combine it with the base URL
|
|
31
|
+
if (basePath) {
|
|
32
|
+
const origin = baseUrl ? new URL(baseUrl).origin : window.location.origin;
|
|
33
|
+
base = combineUrlAndPath(origin, basePath + '/');
|
|
34
|
+
}
|
|
35
|
+
return new URL(url, base).toString();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// If URL construction fails, return the original URL
|
|
39
|
+
return url;
|
|
21
40
|
}
|
|
22
|
-
return new URL(url, base).toString();
|
|
23
|
-
} catch {
|
|
24
|
-
return url;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
export {
|
|
28
|
-
makeUrlAbsolute
|
|
29
41
|
};
|
|
30
|
-
//# sourceMappingURL=make-url-absolute.js.map
|
package/dist/url/merge-urls.js
CHANGED
|
@@ -1,56 +1,78 @@
|
|
|
1
|
-
import { REGEX } from
|
|
2
|
-
import { isRelativePath } from
|
|
3
|
-
import { ensureProtocol } from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { REGEX } from '../regex/regex-helpers.js';
|
|
2
|
+
import { isRelativePath } from './is-relative-path.js';
|
|
3
|
+
import { ensureProtocol } from './ensure-protocol.js';
|
|
4
|
+
/**
|
|
5
|
+
* Merges multiple URLSearchParams objects, preserving multiple values per param
|
|
6
|
+
* within each source, but later sources overwrite earlier ones completely
|
|
7
|
+
* This should de-dupe our query params while allowing multiple keys for "arrays"
|
|
8
|
+
*/
|
|
9
|
+
export const mergeSearchParams = (...params) => {
|
|
10
|
+
// We keep a merged record to ensure the next group will overwrite the previous
|
|
11
|
+
const merged = {};
|
|
12
|
+
// Loops over each group and grabs unique keys
|
|
13
|
+
params.forEach((p) => {
|
|
14
|
+
const keys = Array.from(p.keys());
|
|
15
|
+
const uniqueKeys = new Set(keys);
|
|
16
|
+
uniqueKeys.forEach((key) => {
|
|
17
|
+
const values = p.getAll(key);
|
|
18
|
+
const value = values.length > 1 ? values : (values[0] ?? '');
|
|
19
|
+
merged[key] = value;
|
|
20
|
+
});
|
|
13
21
|
});
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const combineUrlAndPath = (url, path) => {
|
|
26
|
-
if (!path || url === path) {
|
|
27
|
-
return url.trim();
|
|
28
|
-
}
|
|
29
|
-
if (!url) {
|
|
30
|
-
return path.trim();
|
|
31
|
-
}
|
|
32
|
-
return `${url.trim()}/${path.trim()}`.replace(REGEX.MULTIPLE_SLASHES, "/");
|
|
22
|
+
const result = new URLSearchParams();
|
|
23
|
+
// We maintain multiple values for each key if they existed
|
|
24
|
+
Object.entries(merged).forEach(([key, value]) => {
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
value.forEach((v) => result.append(key, v));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
result.append(key, value);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return result;
|
|
33
33
|
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const search = mergedSearchParams.toString();
|
|
44
|
-
return search ? `${mergedUrl}?${search}` : mergedUrl;
|
|
45
|
-
}
|
|
46
|
-
if (path) {
|
|
47
|
-
return combineUrlAndPath(url, path);
|
|
48
|
-
}
|
|
49
|
-
return "";
|
|
34
|
+
/** Combines a base URL and a path ensuring there's only one slash between them */
|
|
35
|
+
export const combineUrlAndPath = (url, path) => {
|
|
36
|
+
if (!path || url === path) {
|
|
37
|
+
return url.trim();
|
|
38
|
+
}
|
|
39
|
+
if (!url) {
|
|
40
|
+
return path.trim();
|
|
41
|
+
}
|
|
42
|
+
return `${url.trim()}/${path.trim()}`.replace(REGEX.MULTIPLE_SLASHES, '/');
|
|
50
43
|
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Creates a URL from the path and server
|
|
46
|
+
* also optionally merges query params if you include urlSearchParams
|
|
47
|
+
* This was re-written without using URL to support variables in the scheme
|
|
48
|
+
*/
|
|
49
|
+
export const mergeUrls = (url, path, urlParams = new URLSearchParams(),
|
|
50
|
+
/** To disable prefixing the url with the origin or a scheme*/
|
|
51
|
+
disableOriginPrefix = false) => {
|
|
52
|
+
// Extract and merge all query params
|
|
53
|
+
if (url && (!isRelativePath(url) || typeof window !== 'undefined')) {
|
|
54
|
+
/** Prefix the url with the origin if it is relative and we wish to */
|
|
55
|
+
const base = disableOriginPrefix
|
|
56
|
+
? url
|
|
57
|
+
: isRelativePath(url)
|
|
58
|
+
? combineUrlAndPath(window.location.origin, url)
|
|
59
|
+
: ensureProtocol(url);
|
|
60
|
+
// Extract search params from base URL if any
|
|
61
|
+
const [baseUrl = '', baseQuery] = base.split('?');
|
|
62
|
+
const baseParams = new URLSearchParams(baseQuery || '');
|
|
63
|
+
// Extract search params from path if any
|
|
64
|
+
const [pathWithoutQuery = '', pathQuery] = path.split('?');
|
|
65
|
+
const pathParams = new URLSearchParams(pathQuery || '');
|
|
66
|
+
// Merge the baseUrl and path
|
|
67
|
+
const mergedUrl = url === path ? baseUrl : combineUrlAndPath(baseUrl, pathWithoutQuery);
|
|
68
|
+
// Merge all search params
|
|
69
|
+
const mergedSearchParams = mergeSearchParams(baseParams, pathParams, urlParams);
|
|
70
|
+
// Build the final URL
|
|
71
|
+
const search = mergedSearchParams.toString();
|
|
72
|
+
return search ? `${mergedUrl}?${search}` : mergedUrl;
|
|
73
|
+
}
|
|
74
|
+
if (path) {
|
|
75
|
+
return combineUrlAndPath(url, path);
|
|
76
|
+
}
|
|
77
|
+
return '';
|
|
55
78
|
};
|
|
56
|
-
//# sourceMappingURL=merge-urls.js.map
|
|
@@ -1,44 +1,73 @@
|
|
|
1
|
-
import { isLocalUrl } from
|
|
2
|
-
import { isRelativePath } from
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { isLocalUrl } from './is-local-url.js';
|
|
2
|
+
import { isRelativePath } from './is-relative-path.js';
|
|
3
|
+
/**
|
|
4
|
+
* Redirects the request to a proxy server with a given URL. But not for:
|
|
5
|
+
*
|
|
6
|
+
* - Relative URLs
|
|
7
|
+
* - URLs that seem to point to a local IP (except the proxy is on the same domain)
|
|
8
|
+
* - URLs that don't look like a domain
|
|
9
|
+
**/
|
|
10
|
+
export const redirectToProxy = (proxyUrl, url) => {
|
|
11
|
+
try {
|
|
12
|
+
if (!shouldUseProxy(proxyUrl, url)) {
|
|
13
|
+
return url ?? '';
|
|
14
|
+
}
|
|
15
|
+
// Create new URL object from url
|
|
16
|
+
const newUrl = new URL(url);
|
|
17
|
+
// Add temporary domain for relative proxy URLs
|
|
18
|
+
//
|
|
19
|
+
// Q: Why isn't proxyUrl type guarded?
|
|
20
|
+
// A: Type guarding works for one parameter only (as of now).
|
|
21
|
+
//
|
|
22
|
+
// Q: Why do we need to add http://localhost to relative proxy URLs?
|
|
23
|
+
// A: Because the `new URL()` would otherwise fail.
|
|
24
|
+
//
|
|
25
|
+
const temporaryProxyUrl = isRelativePath(proxyUrl) ? `http://localhost${proxyUrl}` : proxyUrl;
|
|
26
|
+
// Rewrite the URL with the proxy
|
|
27
|
+
newUrl.href = temporaryProxyUrl;
|
|
28
|
+
// Add the original URL as a query parameter
|
|
29
|
+
newUrl.searchParams.append('scalar_url', url);
|
|
30
|
+
// Remove the temporary domain if we added it, but only from the start of the URL
|
|
31
|
+
const result = isRelativePath(proxyUrl)
|
|
32
|
+
? newUrl.toString().replace(/^http:\/\/localhost/, '')
|
|
33
|
+
: newUrl.toString();
|
|
34
|
+
return result;
|
|
7
35
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
newUrl.href = temporaryProxyUrl;
|
|
11
|
-
newUrl.searchParams.append("scalar_url", url);
|
|
12
|
-
const result = isRelativePath(proxyUrl) ? newUrl.toString().replace(/^http:\/\/localhost/, "") : newUrl.toString();
|
|
13
|
-
return result;
|
|
14
|
-
} catch {
|
|
15
|
-
return url ?? "";
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
const shouldUseProxy = (proxyUrl, url) => {
|
|
19
|
-
try {
|
|
20
|
-
if (!proxyUrl || !url) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
if (isRelativePath(url)) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
if (isRelativePath(proxyUrl)) {
|
|
27
|
-
return true;
|
|
36
|
+
catch {
|
|
37
|
+
return url ?? '';
|
|
28
38
|
}
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Returns false for requests to localhost, relative URLs, if no proxy is defined …
|
|
42
|
+
**/
|
|
43
|
+
export const shouldUseProxy = (proxyUrl, url) => {
|
|
44
|
+
try {
|
|
45
|
+
// ❌ We don't have a proxy URL or the URL
|
|
46
|
+
if (!proxyUrl || !url) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
// ❌ Request to relative URLs (won't be blocked by CORS anyway)
|
|
50
|
+
if (isRelativePath(url)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
// ✅ Proxy URL is on the same domain (e.g. /proxy)
|
|
54
|
+
// It's more likely (not guaranteed, though) that the proxy has access to local domains.
|
|
55
|
+
if (isRelativePath(proxyUrl)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
// ✅ Proxy URL is local
|
|
59
|
+
if (isLocalUrl(proxyUrl)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
// ❌ Requests to localhost
|
|
63
|
+
// We won't reach them from a (likely remote) proxy.
|
|
64
|
+
if (isLocalUrl(url)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
// ✅ Seems fine (e.g. remote proxy + remote URL)
|
|
68
|
+
return true;
|
|
31
69
|
}
|
|
32
|
-
|
|
33
|
-
|
|
70
|
+
catch {
|
|
71
|
+
return false;
|
|
34
72
|
}
|
|
35
|
-
return true;
|
|
36
|
-
} catch {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
export {
|
|
41
|
-
redirectToProxy,
|
|
42
|
-
shouldUseProxy
|
|
43
73
|
};
|
|
44
|
-
//# sourceMappingURL=redirect-to-proxy.js.map
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"helpers",
|
|
15
15
|
"js"
|
|
16
16
|
],
|
|
17
|
-
"version": "0.4.
|
|
17
|
+
"version": "0.4.2",
|
|
18
18
|
"engines": {
|
|
19
19
|
"node": ">=22"
|
|
20
20
|
},
|
|
@@ -99,16 +99,12 @@
|
|
|
99
99
|
],
|
|
100
100
|
"devDependencies": {
|
|
101
101
|
"jsdom": "27.4.0",
|
|
102
|
-
"vite": "
|
|
103
|
-
"vitest": "4.0
|
|
104
|
-
"@scalar/build-tooling": "0.5.0"
|
|
102
|
+
"vite": "8.0.0",
|
|
103
|
+
"vitest": "4.1.0"
|
|
105
104
|
},
|
|
106
105
|
"scripts": {
|
|
107
|
-
"build": "
|
|
108
|
-
"lint:check": "biome lint --diagnostic-level=error",
|
|
109
|
-
"lint:fix": "biome lint --write",
|
|
106
|
+
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
110
107
|
"test": "vitest",
|
|
111
|
-
"types:
|
|
112
|
-
"types:check": "scalar-types-check"
|
|
108
|
+
"types:check": "tsc --noEmit"
|
|
113
109
|
}
|
|
114
110
|
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/array/add-to-map-array.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * A little helper to add a value to a map array where the type is Map<string, any[]>\n */\nexport const addToMapArray = (map: Map<string, unknown[]>, key: string, value: unknown) => {\n const prev = map.get(key) ?? []\n prev.push(value)\n map.set(key, prev)\n}\n"],
|
|
5
|
-
"mappings": "AAGO,MAAM,gBAAgB,CAAC,KAA6B,KAAa,UAAmB;AACzF,QAAM,OAAO,IAAI,IAAI,GAAG,KAAK,CAAC;AAC9B,OAAK,KAAK,KAAK;AACf,MAAI,IAAI,KAAK,IAAI;AACnB;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/array/is-defined.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Type safe alternative to array.filter(Boolean)\n *\n * @example\n *\n * ```ts\n * const dataArray = [1, null, 2, undefined, 3].filter(isDefined)\n * ```\n *\n * @see https://jaketrent.com/post/typescript-type-safe-filter-boolean/\n */\nexport const isDefined = <T>(value: T | null | undefined): value is NonNullable<T> =>\n value !== null && value !== undefined\n"],
|
|
5
|
-
"mappings": "AAWO,MAAM,YAAY,CAAI,UAC3B,UAAU,QAAQ,UAAU;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/array/sort-by-order.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Immutably sorts an array based on a given custom order, as specified by a separate list of identifiers.\n *\n * Note: Make sure that the identifier is unique for each item in the array.\n * If the identifier is not unique, only the last occurrence of the identifier will be sorted.\n * Any other elements will be overridden by the last occurrence of the identifier.\n *\n * This function efficiently arranges elements of an input array so that items whose IDs (obtained via the `getId` callback)\n * match the order array will appear first, strictly in the order specified. Items not found in the order array will be\n * appended after, maintaining their original order.\n *\n * This is a flexible utility: the identifier can be extracted from any data structure via the `getId` callback,\n * and any primitive (string, number, symbol, etc.) can serve as the identifier type.\n * Sorting is O(n) with respect to the array size, making it performant even for large arrays.\n *\n * @template T - The type of array elements.\n * @template N - The type of values in the order array and the identifier returned by the getId callback.\n * @param arr - The array to be sorted.\n * @param order - Array specifying the desired sequence (contains identifiers returned by getId).\n * @param getId - A callback to extract the unique identifier from each array element.\n * @returns A new array sorted according to the order provided, with unmatched elements following.\n *\n * @example\n * // Sorting an array of objects:\n * const items = [\n * { id: 'a', name: 'Alpha' },\n * { id: 'b', name: 'Bravo' },\n * { id: 'c', name: 'Charlie' }\n * ];\n * const order = ['c', 'a'];\n * const sorted = sortByOrder(items, order, item => item.id);\n * // Result:\n * // [\n * // { id: 'c', name: 'Charlie' },\n * // { id: 'a', name: 'Alpha' },\n * // { id: 'b', name: 'Bravo' }\n * // ]\n *\n * @example\n * // Sorting an array of primitive values:\n * const input = ['a', 'b', 'c', 'd'];\n * const order = ['c', 'a'];\n * sortByOrder(input, order, item => item);\n * // Result: ['c', 'a', 'b', 'd']\n */\nexport function sortByOrder<T, N>(arr: T[], order: N[], getId: (item: T) => N): T[] {\n // Map the order to keep a single lookup table\n const orderMap = new Map<N, number>()\n order.forEach((e, idx) => orderMap.set(e, idx))\n\n const sorted: T[] = []\n const untagged: T[] = []\n\n arr.forEach((e) => {\n const sortedIdx = orderMap.get(getId(e))\n if (sortedIdx === undefined) {\n untagged.push(e)\n return\n }\n sorted[sortedIdx] = e\n })\n\n return [...sorted.filter((it) => it !== undefined), ...untagged]\n}\n"],
|
|
5
|
-
"mappings": "AA6CO,SAAS,YAAkB,KAAU,OAAY,OAA4B;AAElF,QAAM,WAAW,oBAAI,IAAe;AACpC,QAAM,QAAQ,CAAC,GAAG,QAAQ,SAAS,IAAI,GAAG,GAAG,CAAC;AAE9C,QAAM,SAAc,CAAC;AACrB,QAAM,WAAgB,CAAC;AAEvB,MAAI,QAAQ,CAAC,MAAM;AACjB,UAAM,YAAY,SAAS,IAAI,MAAM,CAAC,CAAC;AACvC,QAAI,cAAc,QAAW;AAC3B,eAAS,KAAK,CAAC;AACf;AAAA,IACF;AACA,WAAO,SAAS,IAAI;AAAA,EACtB,CAAC;AAED,SAAO,CAAC,GAAG,OAAO,OAAO,CAAC,OAAO,OAAO,MAAS,GAAG,GAAG,QAAQ;AACjE;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/consts/content-types.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Content types that we automatically add in the client\n */\nexport const CONTENT_TYPES = {\n 'multipart/form-data': 'Multipart Form',\n 'application/x-www-form-urlencoded': 'Form URL Encoded',\n 'application/octet-stream': 'Binary File',\n 'application/json': 'JSON',\n 'application/xml': 'XML',\n 'application/yaml': 'YAML',\n 'application/edn': 'EDN',\n 'other': 'Other',\n 'none': 'None',\n} as const\n"],
|
|
5
|
-
"mappings": "AAGO,MAAM,gBAAgB;AAAA,EAC3B,uBAAuB;AAAA,EACvB,qCAAqC;AAAA,EACrC,4BAA4B;AAAA,EAC5B,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,SAAS;AAAA,EACT,QAAQ;AACV;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/dom/freeze-element.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Scroll Freezing Utility\n * \"Freezes\" the scroll position of an element, so that it doesn't move when the rest of the content changes\n *\n * @example\n * const unfreeze = freezeElement(document.querySelector('#your-element'))\n * ... content changes ...\n * unfreeze()\n */\nexport const freezeElement = (element: HTMLElement) => {\n if (!element) {\n return () => null\n }\n\n // Get initial position relative to viewport\n const rect = element.getBoundingClientRect()\n const initialViewportTop = rect.top\n let rafId: number | null = null\n\n // Create mutation observer to watch for DOM changes\n const observer = new MutationObserver((mutations) => {\n // Only process if we have mutations that might affect layout\n const shouldProcess = mutations.some(\n (mutation) =>\n mutation.type === 'childList' ||\n (mutation.type === 'attributes' && (mutation.attributeName === 'style' || mutation.attributeName === 'class')),\n )\n\n if (!shouldProcess) {\n return\n }\n\n // Cancel any pending animation frame\n if (rafId !== null) {\n cancelAnimationFrame(rafId)\n }\n\n // Schedule the scroll adjustment for the next frame\n rafId = requestAnimationFrame(() => {\n const newRect = element.getBoundingClientRect()\n const currentViewportTop = newRect.top\n\n // If element has moved from its initial viewport position\n if (currentViewportTop !== initialViewportTop) {\n // Calculate how far it moved\n const diff = currentViewportTop - initialViewportTop\n // Adjust scroll to maintain position\n window.scrollBy(0, diff)\n }\n rafId = null\n })\n })\n\n // Start observing with more specific configuration\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['style', 'class'],\n characterData: false,\n })\n\n // Return function to stop maintaining position\n return () => {\n if (rafId !== null) {\n cancelAnimationFrame(rafId)\n }\n observer.disconnect()\n }\n}\n"],
|
|
5
|
-
"mappings": "AASO,MAAM,gBAAgB,CAAC,YAAyB;AACrD,MAAI,CAAC,SAAS;AACZ,WAAO,MAAM;AAAA,EACf;AAGA,QAAM,OAAO,QAAQ,sBAAsB;AAC3C,QAAM,qBAAqB,KAAK;AAChC,MAAI,QAAuB;AAG3B,QAAM,WAAW,IAAI,iBAAiB,CAAC,cAAc;AAEnD,UAAM,gBAAgB,UAAU;AAAA,MAC9B,CAAC,aACC,SAAS,SAAS,eACjB,SAAS,SAAS,iBAAiB,SAAS,kBAAkB,WAAW,SAAS,kBAAkB;AAAA,IACzG;AAEA,QAAI,CAAC,eAAe;AAClB;AAAA,IACF;AAGA,QAAI,UAAU,MAAM;AAClB,2BAAqB,KAAK;AAAA,IAC5B;AAGA,YAAQ,sBAAsB,MAAM;AAClC,YAAM,UAAU,QAAQ,sBAAsB;AAC9C,YAAM,qBAAqB,QAAQ;AAGnC,UAAI,uBAAuB,oBAAoB;AAE7C,cAAM,OAAO,qBAAqB;AAElC,eAAO,SAAS,GAAG,IAAI;AAAA,MACzB;AACA,cAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AAGD,WAAS,QAAQ,SAAS,MAAM;AAAA,IAC9B,WAAW;AAAA,IACX,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,iBAAiB,CAAC,SAAS,OAAO;AAAA,IAClC,eAAe;AAAA,EACjB,CAAC;AAGD,SAAO,MAAM;AACX,QAAI,UAAU,MAAM;AAClB,2BAAqB,KAAK;AAAA,IAC5B;AACA,aAAS,WAAW;AAAA,EACtB;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/dom/scroll-to-id.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Tiny wrapper around the scrollIntoView API\n *\n * Also focuses the element if the focus flag is true\n */\nexport const scrollToId = (id: string, focus?: boolean): void => {\n const scrollToElement = (element: HTMLElement) => {\n element.scrollIntoView()\n if (focus) {\n element.focus()\n }\n }\n\n // Try to find the element immediately\n const element = document.getElementById(id)\n if (element) {\n scrollToElement(element)\n return\n }\n\n /** Try to find the element for up to 1 second\n * allowing it to render for instance in markdown heading usage\n */\n const stopTime = Date.now() + 1000\n\n const tryScroll = (): void => {\n const element = document.getElementById(id)\n if (element) {\n scrollToElement(element)\n return\n }\n\n if (Date.now() < stopTime) {\n requestAnimationFrame(tryScroll)\n }\n }\n\n // Start the retry process if the element doesn't exist yet\n requestAnimationFrame(tryScroll)\n}\n"],
|
|
5
|
-
"mappings": "AAKO,MAAM,aAAa,CAAC,IAAY,UAA0B;AAC/D,QAAM,kBAAkB,CAACA,aAAyB;AAChD,IAAAA,SAAQ,eAAe;AACvB,QAAI,OAAO;AACT,MAAAA,SAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AAGA,QAAM,UAAU,SAAS,eAAe,EAAE;AAC1C,MAAI,SAAS;AACX,oBAAgB,OAAO;AACvB;AAAA,EACF;AAKA,QAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,QAAM,YAAY,MAAY;AAC5B,UAAMA,WAAU,SAAS,eAAe,EAAE;AAC1C,QAAIA,UAAS;AACX,sBAAgBA,QAAO;AACvB;AAAA,IACF;AAEA,QAAI,KAAK,IAAI,IAAI,UAAU;AACzB,4BAAsB,SAAS;AAAA,IACjC;AAAA,EACF;AAGA,wBAAsB,SAAS;AACjC;",
|
|
6
|
-
"names": ["element"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/file/json2xml.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Character map for XML escaping to prevent XML injection attacks.\n */\nconst XML_ESCAPE_MAP: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n}\n\n/**\n * Escapes special XML characters to prevent injection attacks.\n */\nfunction escapeXml(str: string): string {\n return str.replace(/[&<>\"']/g, (char) => XML_ESCAPE_MAP[char] ?? char)\n}\n\n/**\n * This function converts an object to XML.\n * Values are automatically escaped to prevent XML injection attacks.\n */\nexport function json2xml(\n data: Record<string, any>,\n options: {\n indent?: string\n format?: boolean\n xmlDeclaration?: boolean\n } = {},\n) {\n const { indent = ' ', format = true, xmlDeclaration = true } = options\n\n const toXml = (value: any, key: string, currentIndent: string): string => {\n let xml = ''\n\n if (Array.isArray(value)) {\n for (let i = 0, n = value.length; i < n; i++) {\n xml += toXml(value[i], key, currentIndent)\n }\n } else if (typeof value === 'object' && value !== null) {\n let hasChild = false\n let attributes = ''\n let children = ''\n\n // Handle attributes (keys starting with @)\n for (const attr in value) {\n if (attr.charAt(0) === '@') {\n attributes += ' ' + attr.substr(1) + '=\"' + escapeXml(value[attr].toString()) + '\"'\n }\n }\n\n // Handle children and special content\n for (const child in value) {\n if (child === '#text') {\n children += escapeXml(value[child]?.toString() ?? '')\n } else if (child === '#cdata') {\n // Escape ]]> sequences to prevent CDATA injection\n const cdataContent = value[child]?.toString() ?? ''\n children += '<![CDATA[' + cdataContent.replace(/]]>/g, ']]]]><![CDATA[>') + ']]>'\n } else if (child.charAt(0) !== '@') {\n hasChild = true\n children += toXml(value[child], child, currentIndent + indent)\n }\n }\n\n if (hasChild || children) {\n xml += currentIndent + '<' + key + attributes + '>\\n'\n xml += children\n xml += currentIndent + '</' + key + '>\\n'\n } else {\n xml += currentIndent + '<' + key + attributes + '/>\\n'\n }\n } else {\n xml += currentIndent + '<' + key + '>' + escapeXml(value?.toString() || '') + '</' + key + '>\\n'\n }\n\n return xml\n }\n\n let xml = ''\n\n // Add XML declaration if requested\n if (xmlDeclaration) {\n xml += '<?xml version=\"1.0\" encoding=\"UTF-8\"?>'\n if (format) {\n xml += '\\n'\n }\n }\n\n // Convert data to XML\n for (const key in data) {\n if (Object.hasOwn(data, key)) {\n xml += toXml(data[key], key, '')\n }\n }\n\n // Format or compact the output\n if (format) {\n return xml.trim()\n }\n\n // Remove all newlines and extra spaces, but keep the XML declaration clean\n return xml.replace(/\\n/g, '').replace(/>\\s+</g, '><').trim()\n}\n"],
|
|
5
|
-
"mappings": "AAGA,MAAM,iBAAyC;AAAA,EAC7C,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAKA,SAAS,UAAU,KAAqB;AACtC,SAAO,IAAI,QAAQ,YAAY,CAAC,SAAS,eAAe,IAAI,KAAK,IAAI;AACvE;AAMO,SAAS,SACd,MACA,UAII,CAAC,GACL;AACA,QAAM,EAAE,SAAS,MAAM,SAAS,MAAM,iBAAiB,KAAK,IAAI;AAEhE,QAAM,QAAQ,CAAC,OAAY,KAAa,kBAAkC;AACxE,QAAIA,OAAM;AAEV,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAI,GAAG,KAAK;AAC5C,QAAAA,QAAO,MAAM,MAAM,CAAC,GAAG,KAAK,aAAa;AAAA,MAC3C;AAAA,IACF,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AACtD,UAAI,WAAW;AACf,UAAI,aAAa;AACjB,UAAI,WAAW;AAGf,iBAAW,QAAQ,OAAO;AACxB,YAAI,KAAK,OAAO,CAAC,MAAM,KAAK;AAC1B,wBAAc,MAAM,KAAK,OAAO,CAAC,IAAI,OAAO,UAAU,MAAM,IAAI,EAAE,SAAS,CAAC,IAAI;AAAA,QAClF;AAAA,MACF;AAGA,iBAAW,SAAS,OAAO;AACzB,YAAI,UAAU,SAAS;AACrB,sBAAY,UAAU,MAAM,KAAK,GAAG,SAAS,KAAK,EAAE;AAAA,QACtD,WAAW,UAAU,UAAU;AAE7B,gBAAM,eAAe,MAAM,KAAK,GAAG,SAAS,KAAK;AACjD,sBAAY,cAAc,aAAa,QAAQ,QAAQ,iBAAiB,IAAI;AAAA,QAC9E,WAAW,MAAM,OAAO,CAAC,MAAM,KAAK;AAClC,qBAAW;AACX,sBAAY,MAAM,MAAM,KAAK,GAAG,OAAO,gBAAgB,MAAM;AAAA,QAC/D;AAAA,MACF;AAEA,UAAI,YAAY,UAAU;AACxB,QAAAA,QAAO,gBAAgB,MAAM,MAAM,aAAa;AAChD,QAAAA,QAAO;AACP,QAAAA,QAAO,gBAAgB,OAAO,MAAM;AAAA,MACtC,OAAO;AACL,QAAAA,QAAO,gBAAgB,MAAM,MAAM,aAAa;AAAA,MAClD;AAAA,IACF,OAAO;AACL,MAAAA,QAAO,gBAAgB,MAAM,MAAM,MAAM,UAAU,OAAO,SAAS,KAAK,EAAE,IAAI,OAAO,MAAM;AAAA,IAC7F;AAEA,WAAOA;AAAA,EACT;AAEA,MAAI,MAAM;AAGV,MAAI,gBAAgB;AAClB,WAAO;AACP,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAAA,EACF;AAGA,aAAW,OAAO,MAAM;AACtB,QAAI,OAAO,OAAO,MAAM,GAAG,GAAG;AAC5B,aAAO,MAAM,KAAK,GAAG,GAAG,KAAK,EAAE;AAAA,IACjC;AAAA,EACF;AAGA,MAAI,QAAQ;AACV,WAAO,IAAI,KAAK;AAAA,EAClB;AAGA,SAAO,IAAI,QAAQ,OAAO,EAAE,EAAE,QAAQ,UAAU,IAAI,EAAE,KAAK;AAC7D;",
|
|
6
|
-
"names": ["xml"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/general/create-limiter.ts"],
|
|
4
|
-
"sourcesContent": ["import { Queue } from '../queue/queue'\n\n/**\n * Creates a function that limits the number of concurrent executions of async functions.\n *\n * @param maxConcurrent - Maximum number of concurrent executions allowed\n * @returns A function that wraps async functions to limit their concurrent execution\n *\n * @example\n * ```ts\n * const limiter = createLimiter(2) // Allow max 2 concurrent executions\n *\n * // These will run with max 2 at a time\n * const results = await Promise.all([\n * limiter(() => fetch('/api/1')),\n * limiter(() => fetch('/api/2')),\n * limiter(() => fetch('/api/3')),\n * limiter(() => fetch('/api/4'))\n * ])\n * ```\n */\nexport function createLimiter(maxConcurrent: number) {\n let activeCount = 0\n const queue = new Queue<() => void>()\n\n const next = () => {\n if (queue.isEmpty() || activeCount >= maxConcurrent) {\n return\n }\n\n const resolve = queue.dequeue()\n\n if (resolve) {\n resolve()\n }\n }\n\n const run = async <T>(fn: () => Promise<T>): Promise<T> => {\n if (activeCount >= maxConcurrent) {\n await new Promise<void>((resolve) => queue.enqueue(resolve))\n }\n\n activeCount++\n try {\n const result = await fn()\n return result\n } finally {\n activeCount--\n next()\n }\n }\n\n return run\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,aAAa;AAqBf,SAAS,cAAc,eAAuB;AACnD,MAAI,cAAc;AAClB,QAAM,QAAQ,IAAI,MAAkB;AAEpC,QAAM,OAAO,MAAM;AACjB,QAAI,MAAM,QAAQ,KAAK,eAAe,eAAe;AACnD;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,QAAQ;AAE9B,QAAI,SAAS;AACX,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,MAAM,OAAU,OAAqC;AACzD,QAAI,eAAe,eAAe;AAChC,YAAM,IAAI,QAAc,CAAC,YAAY,MAAM,QAAQ,OAAO,CAAC;AAAA,IAC7D;AAEA;AACA,QAAI;AACF,YAAM,SAAS,MAAM,GAAG;AACxB,aAAO;AAAA,IACT,UAAE;AACA;AACA,WAAK;AAAA,IACP;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/general/debounce.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Options for configuring the debounce behavior.\n */\nexport type DebounceOptions = {\n /** The delay in milliseconds before executing the function. Defaults to 100ms. */\n delay?: number\n /** Maximum time in milliseconds to wait before forcing execution, even with continuous calls. */\n maxWait?: number\n}\n\n/**\n * Creates a debounced function executor that delays execution until after a specified time.\n * Multiple calls with the same key will cancel previous pending executions.\n *\n * This is useful for batching rapid updates (like auto-save or API calls) to avoid\n * unnecessary processing or network requests.\n *\n * @param options - Configuration options for delay, maxWait, and key separator\n * @returns A function that accepts a key array and callback to execute\n *\n * @example\n * const debouncedSave = debounce({ delay: 328 })\n * debouncedSave.execute(['user', '123'], () => saveUser(user))\n *\n * @example\n * // With maxWait to guarantee execution even with continuous calls\n * const debouncedSave = debounce({ delay: 328, maxWait: 2000 })\n * debouncedSave.execute(['user', '123'], () => saveUser(user))\n */\nexport const debounce = (options: DebounceOptions = {}) => {\n const { delay = 328, maxWait } = options\n const timeouts = new Map<string, ReturnType<typeof setTimeout>>()\n const maxWaitTimeouts = new Map<string, ReturnType<typeof setTimeout>>()\n const latestFunctions = new Map<string, () => unknown | Promise<unknown>>()\n\n const cleanup = (): void => {\n timeouts.forEach(clearTimeout)\n maxWaitTimeouts.forEach(clearTimeout)\n timeouts.clear()\n maxWaitTimeouts.clear()\n latestFunctions.clear()\n }\n\n /** Executes the function and cleans up all associated timeouts */\n const executeAndCleanup = (key: string): void => {\n // Get the latest function for this key\n const fn = latestFunctions.get(key)\n\n // Clear both timeout types\n const timeout = timeouts.get(key)\n if (timeout !== undefined) {\n clearTimeout(timeout)\n timeouts.delete(key)\n }\n\n const maxWaitTimeout = maxWaitTimeouts.get(key)\n if (maxWaitTimeout !== undefined) {\n clearTimeout(maxWaitTimeout)\n maxWaitTimeouts.delete(key)\n }\n\n // Clear the latest function reference\n latestFunctions.delete(key)\n\n // Execute the function if it exists\n if (fn !== undefined) {\n try {\n fn()\n } catch {\n // Errors are silently caught to prevent the debounce mechanism from breaking\n }\n }\n }\n\n const execute = (key: string, fn: () => unknown | Promise<unknown>): void => {\n // Store the latest function for this key\n latestFunctions.set(key, fn)\n\n // Clear existing debounce timeout\n const existingTimeout = timeouts.get(key)\n if (existingTimeout !== undefined) {\n clearTimeout(existingTimeout)\n }\n\n // Set debounce timeout\n timeouts.set(\n key,\n setTimeout(() => executeAndCleanup(key), delay),\n )\n\n // Set maxWait timeout only if configured and this is a new sequence\n if (maxWait !== undefined && !maxWaitTimeouts.has(key)) {\n maxWaitTimeouts.set(\n key,\n setTimeout(() => executeAndCleanup(key), maxWait),\n )\n }\n }\n\n const flush = (key: string): void => {\n if (!latestFunctions.has(key)) {\n return\n }\n\n executeAndCleanup(key)\n }\n\n const flushAll = (): void => {\n const keys = [...latestFunctions.keys()]\n for (const key of keys) {\n executeAndCleanup(key)\n }\n }\n\n return { execute, cleanup, flush, flushAll }\n}\n"],
|
|
5
|
-
"mappings": "AA6BO,MAAM,WAAW,CAAC,UAA2B,CAAC,MAAM;AACzD,QAAM,EAAE,QAAQ,KAAK,QAAQ,IAAI;AACjC,QAAM,WAAW,oBAAI,IAA2C;AAChE,QAAM,kBAAkB,oBAAI,IAA2C;AACvE,QAAM,kBAAkB,oBAAI,IAA8C;AAE1E,QAAM,UAAU,MAAY;AAC1B,aAAS,QAAQ,YAAY;AAC7B,oBAAgB,QAAQ,YAAY;AACpC,aAAS,MAAM;AACf,oBAAgB,MAAM;AACtB,oBAAgB,MAAM;AAAA,EACxB;AAGA,QAAM,oBAAoB,CAAC,QAAsB;AAE/C,UAAM,KAAK,gBAAgB,IAAI,GAAG;AAGlC,UAAM,UAAU,SAAS,IAAI,GAAG;AAChC,QAAI,YAAY,QAAW;AACzB,mBAAa,OAAO;AACpB,eAAS,OAAO,GAAG;AAAA,IACrB;AAEA,UAAM,iBAAiB,gBAAgB,IAAI,GAAG;AAC9C,QAAI,mBAAmB,QAAW;AAChC,mBAAa,cAAc;AAC3B,sBAAgB,OAAO,GAAG;AAAA,IAC5B;AAGA,oBAAgB,OAAO,GAAG;AAG1B,QAAI,OAAO,QAAW;AACpB,UAAI;AACF,WAAG;AAAA,MACL,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,CAAC,KAAa,OAA+C;AAE3E,oBAAgB,IAAI,KAAK,EAAE;AAG3B,UAAM,kBAAkB,SAAS,IAAI,GAAG;AACxC,QAAI,oBAAoB,QAAW;AACjC,mBAAa,eAAe;AAAA,IAC9B;AAGA,aAAS;AAAA,MACP;AAAA,MACA,WAAW,MAAM,kBAAkB,GAAG,GAAG,KAAK;AAAA,IAChD;AAGA,QAAI,YAAY,UAAa,CAAC,gBAAgB,IAAI,GAAG,GAAG;AACtD,sBAAgB;AAAA,QACd;AAAA,QACA,WAAW,MAAM,kBAAkB,GAAG,GAAG,OAAO;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,CAAC,QAAsB;AACnC,QAAI,CAAC,gBAAgB,IAAI,GAAG,GAAG;AAC7B;AAAA,IACF;AAEA,sBAAkB,GAAG;AAAA,EACvB;AAEA,QAAM,WAAW,MAAY;AAC3B,UAAM,OAAO,CAAC,GAAG,gBAAgB,KAAK,CAAC;AACvC,eAAW,OAAO,MAAM;AACtB,wBAAkB,GAAG;AAAA,IACvB;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,SAAS,OAAO,SAAS;AAC7C;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/general/extract-config-secrets.ts"],
|
|
4
|
-
"sourcesContent": ["import { objectEntries } from '../object/object-entries'\n\n/**\n * Mapping of field names to their corresponding x-scalar-secret extension names.\n */\nconst SECRET_FIELD_MAPPINGS = {\n clientSecret: 'x-scalar-secret-client-secret',\n password: 'x-scalar-secret-password',\n token: 'x-scalar-secret-token',\n username: 'x-scalar-secret-username',\n value: 'x-scalar-secret-token',\n 'x-scalar-client-id': 'x-scalar-secret-client-id',\n 'x-scalar-redirect-uri': 'x-scalar-secret-redirect-uri',\n} as const\n\n/**\n * Extracts secret fields from the config or the old schemes\n * Maps original field names to their x-scalar-secret extension equivalents.\n */\nexport const extractConfigSecrets = (input: Record<string, unknown>): Record<string, string> =>\n objectEntries(SECRET_FIELD_MAPPINGS).reduce<Record<string, string>>((result, [field, secretField]) => {\n const value = input[field]\n if (value && typeof value === 'string') {\n result[secretField] = value\n }\n return result\n }, {})\n\n/** Set of all secret fields */\nconst SECRETS_SET = new Set<string>(\n objectEntries(SECRET_FIELD_MAPPINGS).flatMap(([oldSecret, newSecret]) => [oldSecret, newSecret]),\n)\n\n/** Removes all secret fields from the input object */\nexport const removeSecretFields = (input: Record<string, unknown>): Record<string, unknown> =>\n objectEntries(input).reduce<Record<string, unknown>>((result, [key, value]) => {\n if (!SECRETS_SET.has(key)) {\n result[key] = value\n }\n return result\n }, {})\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,qBAAqB;AAK9B,MAAM,wBAAwB;AAAA,EAC5B,cAAc;AAAA,EACd,UAAU;AAAA,EACV,OAAO;AAAA,EACP,UAAU;AAAA,EACV,OAAO;AAAA,EACP,sBAAsB;AAAA,EACtB,yBAAyB;AAC3B;AAMO,MAAM,uBAAuB,CAAC,UACnC,cAAc,qBAAqB,EAAE,OAA+B,CAAC,QAAQ,CAAC,OAAO,WAAW,MAAM;AACpG,QAAM,QAAQ,MAAM,KAAK;AACzB,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,WAAO,WAAW,IAAI;AAAA,EACxB;AACA,SAAO;AACT,GAAG,CAAC,CAAC;AAGP,MAAM,cAAc,IAAI;AAAA,EACtB,cAAc,qBAAqB,EAAE,QAAQ,CAAC,CAAC,WAAW,SAAS,MAAM,CAAC,WAAW,SAAS,CAAC;AACjG;AAGO,MAAM,qBAAqB,CAAC,UACjC,cAAc,KAAK,EAAE,OAAgC,CAAC,QAAQ,CAAC,KAAK,KAAK,MAAM;AAC7E,MAAI,CAAC,YAAY,IAAI,GAAG,GAAG;AACzB,WAAO,GAAG,IAAI;AAAA,EAChB;AACA,SAAO;AACT,GAAG,CAAC,CAAC;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/general/has-modifier.ts"],
|
|
4
|
-
"sourcesContent": ["import { isMacOS } from './is-mac-os'\n\n/**\n * Checks for the modifier key\n *\n * Which is meta on macOs and ctrl on linux/windows\n */\nexport const hasModifier = (keydown: KeyboardEvent) => {\n const modifier = isMacOS() ? 'metaKey' : 'ctrlKey'\n return keydown[modifier]\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,eAAe;AAOjB,MAAM,cAAc,CAAC,YAA2B;AACrD,QAAM,WAAW,QAAQ,IAAI,YAAY;AACzC,SAAO,QAAQ,QAAQ;AACzB;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/general/is-mac-os.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Checks whether the user is on macOS\n * Uses the modern navigator.userAgentData API with a fallback to navigator.userAgent\n */\nexport const isMacOS = () => {\n if (typeof navigator === 'undefined') {\n return false\n }\n\n // Modern approach using navigator.userAgentData\n // @ts-expect-error - userAgentData is new, we can remove this when its stable\n if (navigator.userAgentData?.platform) {\n // @ts-expect-error - userAgentData is new, we can remove this when its stable\n return navigator.userAgentData.platform.toLowerCase().includes('mac')\n }\n\n // Fallback to userAgent\n return /Mac/.test(navigator.userAgent)\n}\n"],
|
|
5
|
-
"mappings": "AAIO,MAAM,UAAU,MAAM;AAC3B,MAAI,OAAO,cAAc,aAAa;AACpC,WAAO;AAAA,EACT;AAIA,MAAI,UAAU,eAAe,UAAU;AAErC,WAAO,UAAU,cAAc,SAAS,YAAY,EAAE,SAAS,KAAK;AAAA,EACtE;AAGA,SAAO,MAAM,KAAK,UAAU,SAAS;AACvC;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/http/can-method-have-body.ts"],
|
|
4
|
-
"sourcesContent": ["import type { HttpMethod } from './http-methods'\n\n/** HTTP Methods which can have a body */\nconst BODY_METHODS = ['post', 'put', 'patch', 'delete'] as const satisfies HttpMethod[]\ntype BodyMethod = (typeof BODY_METHODS)[number]\n\n/** Makes a check to see if this method CAN have a body */\nexport const canMethodHaveBody = (method: HttpMethod): method is BodyMethod =>\n BODY_METHODS.includes(method.toLowerCase() as BodyMethod)\n"],
|
|
5
|
-
"mappings": "AAGA,MAAM,eAAe,CAAC,QAAQ,OAAO,SAAS,QAAQ;AAI/C,MAAM,oBAAoB,CAAC,WAChC,aAAa,SAAS,OAAO,YAAY,CAAe;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/http/http-info.ts"],
|
|
4
|
-
"sourcesContent": ["import type { HttpMethod } from './http-methods'\n\nexport type HttpInfo = {\n short: string\n colorClass: `text-${string}`\n colorVar: `var(--scalar-color-${string})`\n backgroundColor: string\n}\n\n/**\n * HTTP methods in a specific order\n * Do not change the order\n */\nexport const REQUEST_METHODS = {\n get: {\n short: 'GET',\n colorClass: 'text-blue',\n colorVar: 'var(--scalar-color-blue)',\n backgroundColor: 'bg-blue/10',\n },\n post: {\n short: 'POST',\n colorClass: 'text-green',\n colorVar: 'var(--scalar-color-green)',\n backgroundColor: 'bg-green/10',\n },\n put: {\n short: 'PUT',\n colorClass: 'text-orange',\n colorVar: 'var(--scalar-color-orange)',\n backgroundColor: 'bg-orange/10',\n },\n patch: {\n short: 'PATCH',\n colorClass: 'text-yellow',\n colorVar: 'var(--scalar-color-yellow)',\n backgroundColor: 'bg-yellow/10',\n },\n delete: {\n short: 'DEL',\n colorClass: 'text-red',\n colorVar: 'var(--scalar-color-red)',\n backgroundColor: 'bg-red/10',\n },\n options: {\n short: 'OPTS',\n colorClass: 'text-purple',\n colorVar: 'var(--scalar-color-purple)',\n backgroundColor: 'bg-purple/10',\n },\n head: {\n short: 'HEAD',\n colorClass: 'text-c-2',\n colorVar: 'var(--scalar-color-2)',\n backgroundColor: 'bg-c-2/10',\n },\n trace: {\n short: 'TRACE',\n colorClass: 'text-c-2',\n colorVar: 'var(--scalar-color-2)',\n backgroundColor: 'bg-c-2/10',\n },\n} as const satisfies Record<HttpMethod, HttpInfo>\n\n/**\n * Accepts an HTTP Method name and returns some properties for the tag\n */\nexport const getHttpMethodInfo = (methodName: string) => {\n const normalizedMethod = methodName.trim().toLowerCase() as HttpMethod\n return (\n REQUEST_METHODS[normalizedMethod] ?? {\n short: normalizedMethod,\n color: 'text-c-2',\n backgroundColor: 'bg-c-2',\n }\n )\n}\n"],
|
|
5
|
-
"mappings": "AAaO,MAAM,kBAAkB;AAAA,EAC7B,KAAK;AAAA,IACH,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA,KAAK;AAAA,IACH,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA,OAAO;AAAA,IACL,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA,QAAQ;AAAA,IACN,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA,SAAS;AAAA,IACP,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA,OAAO;AAAA,IACL,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AACF;AAKO,MAAM,oBAAoB,CAAC,eAAuB;AACvD,QAAM,mBAAmB,WAAW,KAAK,EAAE,YAAY;AACvD,SACE,gBAAgB,gBAAgB,KAAK;AAAA,IACnC,OAAO;AAAA,IACP,OAAO;AAAA,IACP,iBAAiB;AAAA,EACnB;AAEJ;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/http/http-methods.ts"],
|
|
4
|
-
"sourcesContent": ["/** All OpenAPI HTTP methods */\nexport const HTTP_METHODS = ['delete', 'get', 'head', 'options', 'patch', 'post', 'put', 'trace'] as const\n\n/** All http methods we support */\nexport type HttpMethod = (typeof HTTP_METHODS)[number]\n\n/** Set of all http methods we support */\nexport const httpMethods = Object.freeze(new Set(HTTP_METHODS))\n"],
|
|
5
|
-
"mappings": "AACO,MAAM,eAAe,CAAC,UAAU,OAAO,QAAQ,WAAW,SAAS,QAAQ,OAAO,OAAO;AAMzF,MAAM,cAAc,OAAO,OAAO,IAAI,IAAI,YAAY,CAAC;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|