@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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @scalar/helpers
|
|
2
2
|
|
|
3
|
+
## 0.4.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#8466](https://github.com/scalar/scalar/pull/8466): chore: new build pipeline
|
|
8
|
+
|
|
9
|
+
## 0.4.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#8420](https://github.com/scalar/scalar/pull/8420): fix TypeScript access to navigator.userAgentData in isMacOS without ts-expect-error
|
|
14
|
+
|
|
3
15
|
## 0.4.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* A little helper to add a value to a map array where the type is Map<string, any[]>
|
|
3
|
+
*/
|
|
4
|
+
export const addToMapArray = (map, key, value) => {
|
|
5
|
+
const prev = map.get(key) ?? [];
|
|
6
|
+
prev.push(value);
|
|
7
|
+
map.set(key, prev);
|
|
5
8
|
};
|
|
6
|
-
export {
|
|
7
|
-
addToMapArray
|
|
8
|
-
};
|
|
9
|
-
//# sourceMappingURL=add-to-map-array.js.map
|
package/dist/array/is-defined.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Type safe alternative to array.filter(Boolean)
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* const dataArray = [1, null, 2, undefined, 3].filter(isDefined)
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* @see https://jaketrent.com/post/typescript-type-safe-filter-boolean/
|
|
11
|
+
*/
|
|
12
|
+
export const isDefined = (value) => value !== null && value !== undefined;
|
|
@@ -1,19 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Immutably sorts an array based on a given custom order, as specified by a separate list of identifiers.
|
|
3
|
+
*
|
|
4
|
+
* Note: Make sure that the identifier is unique for each item in the array.
|
|
5
|
+
* If the identifier is not unique, only the last occurrence of the identifier will be sorted.
|
|
6
|
+
* Any other elements will be overridden by the last occurrence of the identifier.
|
|
7
|
+
*
|
|
8
|
+
* This function efficiently arranges elements of an input array so that items whose IDs (obtained via the `getId` callback)
|
|
9
|
+
* match the order array will appear first, strictly in the order specified. Items not found in the order array will be
|
|
10
|
+
* appended after, maintaining their original order.
|
|
11
|
+
*
|
|
12
|
+
* This is a flexible utility: the identifier can be extracted from any data structure via the `getId` callback,
|
|
13
|
+
* and any primitive (string, number, symbol, etc.) can serve as the identifier type.
|
|
14
|
+
* Sorting is O(n) with respect to the array size, making it performant even for large arrays.
|
|
15
|
+
*
|
|
16
|
+
* @template T - The type of array elements.
|
|
17
|
+
* @template N - The type of values in the order array and the identifier returned by the getId callback.
|
|
18
|
+
* @param arr - The array to be sorted.
|
|
19
|
+
* @param order - Array specifying the desired sequence (contains identifiers returned by getId).
|
|
20
|
+
* @param getId - A callback to extract the unique identifier from each array element.
|
|
21
|
+
* @returns A new array sorted according to the order provided, with unmatched elements following.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Sorting an array of objects:
|
|
25
|
+
* const items = [
|
|
26
|
+
* { id: 'a', name: 'Alpha' },
|
|
27
|
+
* { id: 'b', name: 'Bravo' },
|
|
28
|
+
* { id: 'c', name: 'Charlie' }
|
|
29
|
+
* ];
|
|
30
|
+
* const order = ['c', 'a'];
|
|
31
|
+
* const sorted = sortByOrder(items, order, item => item.id);
|
|
32
|
+
* // Result:
|
|
33
|
+
* // [
|
|
34
|
+
* // { id: 'c', name: 'Charlie' },
|
|
35
|
+
* // { id: 'a', name: 'Alpha' },
|
|
36
|
+
* // { id: 'b', name: 'Bravo' }
|
|
37
|
+
* // ]
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Sorting an array of primitive values:
|
|
41
|
+
* const input = ['a', 'b', 'c', 'd'];
|
|
42
|
+
* const order = ['c', 'a'];
|
|
43
|
+
* sortByOrder(input, order, item => item);
|
|
44
|
+
* // Result: ['c', 'a', 'b', 'd']
|
|
45
|
+
*/
|
|
46
|
+
export function sortByOrder(arr, order, getId) {
|
|
47
|
+
// Map the order to keep a single lookup table
|
|
48
|
+
const orderMap = new Map();
|
|
49
|
+
order.forEach((e, idx) => orderMap.set(e, idx));
|
|
50
|
+
const sorted = [];
|
|
51
|
+
const untagged = [];
|
|
52
|
+
arr.forEach((e) => {
|
|
53
|
+
const sortedIdx = orderMap.get(getId(e));
|
|
54
|
+
if (sortedIdx === undefined) {
|
|
55
|
+
untagged.push(e);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
sorted[sortedIdx] = e;
|
|
59
|
+
});
|
|
60
|
+
return [...sorted.filter((it) => it !== undefined), ...untagged];
|
|
15
61
|
}
|
|
16
|
-
export {
|
|
17
|
-
sortByOrder
|
|
18
|
-
};
|
|
19
|
-
//# sourceMappingURL=sort-by-order.js.map
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Content types that we automatically add in the client
|
|
3
|
+
*/
|
|
4
|
+
export const CONTENT_TYPES = {
|
|
5
|
+
'multipart/form-data': 'Multipart Form',
|
|
6
|
+
'application/x-www-form-urlencoded': 'Form URL Encoded',
|
|
7
|
+
'application/octet-stream': 'Binary File',
|
|
8
|
+
'application/json': 'JSON',
|
|
9
|
+
'application/xml': 'XML',
|
|
10
|
+
'application/yaml': 'YAML',
|
|
11
|
+
'application/edn': 'EDN',
|
|
12
|
+
'other': 'Other',
|
|
13
|
+
'none': 'None',
|
|
11
14
|
};
|
|
12
|
-
export {
|
|
13
|
-
CONTENT_TYPES
|
|
14
|
-
};
|
|
15
|
-
//# sourceMappingURL=content-types.js.map
|
|
@@ -1,45 +1,59 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
return;
|
|
1
|
+
/**
|
|
2
|
+
* Scroll Freezing Utility
|
|
3
|
+
* "Freezes" the scroll position of an element, so that it doesn't move when the rest of the content changes
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* const unfreeze = freezeElement(document.querySelector('#your-element'))
|
|
7
|
+
* ... content changes ...
|
|
8
|
+
* unfreeze()
|
|
9
|
+
*/
|
|
10
|
+
export const freezeElement = (element) => {
|
|
11
|
+
if (!element) {
|
|
12
|
+
return () => null;
|
|
14
13
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
rafId =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
// Get initial position relative to viewport
|
|
15
|
+
const rect = element.getBoundingClientRect();
|
|
16
|
+
const initialViewportTop = rect.top;
|
|
17
|
+
let rafId = null;
|
|
18
|
+
// Create mutation observer to watch for DOM changes
|
|
19
|
+
const observer = new MutationObserver((mutations) => {
|
|
20
|
+
// Only process if we have mutations that might affect layout
|
|
21
|
+
const shouldProcess = mutations.some((mutation) => mutation.type === 'childList' ||
|
|
22
|
+
(mutation.type === 'attributes' && (mutation.attributeName === 'style' || mutation.attributeName === 'class')));
|
|
23
|
+
if (!shouldProcess) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Cancel any pending animation frame
|
|
27
|
+
if (rafId !== null) {
|
|
28
|
+
cancelAnimationFrame(rafId);
|
|
29
|
+
}
|
|
30
|
+
// Schedule the scroll adjustment for the next frame
|
|
31
|
+
rafId = requestAnimationFrame(() => {
|
|
32
|
+
const newRect = element.getBoundingClientRect();
|
|
33
|
+
const currentViewportTop = newRect.top;
|
|
34
|
+
// If element has moved from its initial viewport position
|
|
35
|
+
if (currentViewportTop !== initialViewportTop) {
|
|
36
|
+
// Calculate how far it moved
|
|
37
|
+
const diff = currentViewportTop - initialViewportTop;
|
|
38
|
+
// Adjust scroll to maintain position
|
|
39
|
+
window.scrollBy(0, diff);
|
|
40
|
+
}
|
|
41
|
+
rafId = null;
|
|
42
|
+
});
|
|
26
43
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
};
|
|
42
|
-
export {
|
|
43
|
-
freezeElement
|
|
44
|
+
// Start observing with more specific configuration
|
|
45
|
+
observer.observe(document.body, {
|
|
46
|
+
childList: true,
|
|
47
|
+
subtree: true,
|
|
48
|
+
attributes: true,
|
|
49
|
+
attributeFilter: ['style', 'class'],
|
|
50
|
+
characterData: false,
|
|
51
|
+
});
|
|
52
|
+
// Return function to stop maintaining position
|
|
53
|
+
return () => {
|
|
54
|
+
if (rafId !== null) {
|
|
55
|
+
cancelAnimationFrame(rafId);
|
|
56
|
+
}
|
|
57
|
+
observer.disconnect();
|
|
58
|
+
};
|
|
44
59
|
};
|
|
45
|
-
//# sourceMappingURL=freeze-element.js.map
|
package/dist/dom/scroll-to-id.js
CHANGED
|
@@ -1,29 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Tiny wrapper around the scrollIntoView API
|
|
3
|
+
*
|
|
4
|
+
* Also focuses the element if the focus flag is true
|
|
5
|
+
*/
|
|
6
|
+
export const scrollToId = (id, focus) => {
|
|
7
|
+
const scrollToElement = (element) => {
|
|
8
|
+
element.scrollIntoView();
|
|
9
|
+
if (focus) {
|
|
10
|
+
element.focus();
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
// Try to find the element immediately
|
|
14
|
+
const element = document.getElementById(id);
|
|
15
|
+
if (element) {
|
|
16
|
+
scrollToElement(element);
|
|
17
|
+
return;
|
|
6
18
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
};
|
|
24
|
-
requestAnimationFrame(tryScroll);
|
|
25
|
-
};
|
|
26
|
-
export {
|
|
27
|
-
scrollToId
|
|
19
|
+
/** Try to find the element for up to 1 second
|
|
20
|
+
* allowing it to render for instance in markdown heading usage
|
|
21
|
+
*/
|
|
22
|
+
const stopTime = Date.now() + 1000;
|
|
23
|
+
const tryScroll = () => {
|
|
24
|
+
const element = document.getElementById(id);
|
|
25
|
+
if (element) {
|
|
26
|
+
scrollToElement(element);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (Date.now() < stopTime) {
|
|
30
|
+
requestAnimationFrame(tryScroll);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
// Start the retry process if the element doesn't exist yet
|
|
34
|
+
requestAnimationFrame(tryScroll);
|
|
28
35
|
};
|
|
29
|
-
//# sourceMappingURL=scroll-to-id.js.map
|
package/dist/file/json2xml.js
CHANGED
|
@@ -1,71 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Character map for XML escaping to prevent XML injection attacks.
|
|
3
|
+
*/
|
|
1
4
|
const XML_ESCAPE_MAP = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
'&': '&',
|
|
6
|
+
'<': '<',
|
|
7
|
+
'>': '>',
|
|
8
|
+
'"': '"',
|
|
9
|
+
"'": ''',
|
|
7
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* Escapes special XML characters to prevent injection attacks.
|
|
13
|
+
*/
|
|
8
14
|
function escapeXml(str) {
|
|
9
|
-
|
|
15
|
+
return str.replace(/[&<>"']/g, (char) => XML_ESCAPE_MAP[char] ?? char);
|
|
10
16
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
for (const attr in value) {
|
|
24
|
-
if (attr.charAt(0) === "@") {
|
|
25
|
-
attributes += " " + attr.substr(1) + '="' + escapeXml(value[attr].toString()) + '"';
|
|
17
|
+
/**
|
|
18
|
+
* This function converts an object to XML.
|
|
19
|
+
* Values are automatically escaped to prevent XML injection attacks.
|
|
20
|
+
*/
|
|
21
|
+
export function json2xml(data, options = {}) {
|
|
22
|
+
const { indent = ' ', format = true, xmlDeclaration = true } = options;
|
|
23
|
+
const toXml = (value, key, currentIndent) => {
|
|
24
|
+
let xml = '';
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
for (let i = 0, n = value.length; i < n; i++) {
|
|
27
|
+
xml += toXml(value[i], key, currentIndent);
|
|
28
|
+
}
|
|
26
29
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
else if (typeof value === 'object' && value !== null) {
|
|
31
|
+
let hasChild = false;
|
|
32
|
+
let attributes = '';
|
|
33
|
+
let children = '';
|
|
34
|
+
// Handle attributes (keys starting with @)
|
|
35
|
+
for (const attr in value) {
|
|
36
|
+
if (attr.charAt(0) === '@') {
|
|
37
|
+
attributes += ' ' + attr.substr(1) + '="' + escapeXml(value[attr].toString()) + '"';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Handle children and special content
|
|
41
|
+
for (const child in value) {
|
|
42
|
+
if (child === '#text') {
|
|
43
|
+
children += escapeXml(value[child]?.toString() ?? '');
|
|
44
|
+
}
|
|
45
|
+
else if (child === '#cdata') {
|
|
46
|
+
// Escape ]]> sequences to prevent CDATA injection
|
|
47
|
+
const cdataContent = value[child]?.toString() ?? '';
|
|
48
|
+
children += '<![CDATA[' + cdataContent.replace(/]]>/g, ']]]]><![CDATA[>') + ']]>';
|
|
49
|
+
}
|
|
50
|
+
else if (child.charAt(0) !== '@') {
|
|
51
|
+
hasChild = true;
|
|
52
|
+
children += toXml(value[child], child, currentIndent + indent);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (hasChild || children) {
|
|
56
|
+
xml += currentIndent + '<' + key + attributes + '>\n';
|
|
57
|
+
xml += children;
|
|
58
|
+
xml += currentIndent + '</' + key + '>\n';
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
xml += currentIndent + '<' + key + attributes + '/>\n';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
xml += currentIndent + '<' + key + '>' + escapeXml(value?.toString() || '') + '</' + key + '>\n';
|
|
66
|
+
}
|
|
67
|
+
return xml;
|
|
68
|
+
};
|
|
69
|
+
let xml = '';
|
|
70
|
+
// Add XML declaration if requested
|
|
71
|
+
if (xmlDeclaration) {
|
|
72
|
+
xml += '<?xml version="1.0" encoding="UTF-8"?>';
|
|
73
|
+
if (format) {
|
|
74
|
+
xml += '\n';
|
|
37
75
|
}
|
|
38
|
-
}
|
|
39
|
-
if (hasChild || children) {
|
|
40
|
-
xml2 += currentIndent + "<" + key + attributes + ">\n";
|
|
41
|
-
xml2 += children;
|
|
42
|
-
xml2 += currentIndent + "</" + key + ">\n";
|
|
43
|
-
} else {
|
|
44
|
-
xml2 += currentIndent + "<" + key + attributes + "/>\n";
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
xml2 += currentIndent + "<" + key + ">" + escapeXml(value?.toString() || "") + "</" + key + ">\n";
|
|
48
76
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (format) {
|
|
55
|
-
xml += "\n";
|
|
77
|
+
// Convert data to XML
|
|
78
|
+
for (const key in data) {
|
|
79
|
+
if (Object.hasOwn(data, key)) {
|
|
80
|
+
xml += toXml(data[key], key, '');
|
|
81
|
+
}
|
|
56
82
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
xml += toXml(data[key], key, "");
|
|
83
|
+
// Format or compact the output
|
|
84
|
+
if (format) {
|
|
85
|
+
return xml.trim();
|
|
61
86
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return xml.trim();
|
|
65
|
-
}
|
|
66
|
-
return xml.replace(/\n/g, "").replace(/>\s+</g, "><").trim();
|
|
87
|
+
// Remove all newlines and extra spaces, but keep the XML declaration clean
|
|
88
|
+
return xml.replace(/\n/g, '').replace(/>\s+</g, '><').trim();
|
|
67
89
|
}
|
|
68
|
-
export {
|
|
69
|
-
json2xml
|
|
70
|
-
};
|
|
71
|
-
//# sourceMappingURL=json2xml.js.map
|
|
@@ -1,32 +1,48 @@
|
|
|
1
|
-
import { Queue } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
import { Queue } from '../queue/queue.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a function that limits the number of concurrent executions of async functions.
|
|
4
|
+
*
|
|
5
|
+
* @param maxConcurrent - Maximum number of concurrent executions allowed
|
|
6
|
+
* @returns A function that wraps async functions to limit their concurrent execution
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const limiter = createLimiter(2) // Allow max 2 concurrent executions
|
|
11
|
+
*
|
|
12
|
+
* // These will run with max 2 at a time
|
|
13
|
+
* const results = await Promise.all([
|
|
14
|
+
* limiter(() => fetch('/api/1')),
|
|
15
|
+
* limiter(() => fetch('/api/2')),
|
|
16
|
+
* limiter(() => fetch('/api/3')),
|
|
17
|
+
* limiter(() => fetch('/api/4'))
|
|
18
|
+
* ])
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function createLimiter(maxConcurrent) {
|
|
22
|
+
let activeCount = 0;
|
|
23
|
+
const queue = new Queue();
|
|
24
|
+
const next = () => {
|
|
25
|
+
if (queue.isEmpty() || activeCount >= maxConcurrent) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const resolve = queue.dequeue();
|
|
29
|
+
if (resolve) {
|
|
30
|
+
resolve();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const run = async (fn) => {
|
|
34
|
+
if (activeCount >= maxConcurrent) {
|
|
35
|
+
await new Promise((resolve) => queue.enqueue(resolve));
|
|
36
|
+
}
|
|
37
|
+
activeCount++;
|
|
38
|
+
try {
|
|
39
|
+
const result = await fn();
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
activeCount--;
|
|
44
|
+
next();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
return run;
|
|
28
48
|
}
|
|
29
|
-
export {
|
|
30
|
-
createLimiter
|
|
31
|
-
};
|
|
32
|
-
//# sourceMappingURL=create-limiter.js.map
|