@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.
Files changed (113) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/array/add-to-map-array.js +7 -8
  3. package/dist/array/is-defined.js +12 -5
  4. package/dist/array/sort-by-order.js +60 -18
  5. package/dist/consts/content-types.js +13 -14
  6. package/dist/dom/freeze-element.js +56 -42
  7. package/dist/dom/scroll-to-id.js +33 -27
  8. package/dist/file/json2xml.js +79 -61
  9. package/dist/general/create-limiter.js +47 -31
  10. package/dist/general/debounce.js +86 -66
  11. package/dist/general/extract-config-secrets.js +29 -27
  12. package/dist/general/has-modifier.js +9 -8
  13. package/dist/general/is-mac-os.d.ts +1 -5
  14. package/dist/general/is-mac-os.d.ts.map +1 -1
  15. package/dist/general/is-mac-os.js +24 -11
  16. package/dist/http/can-method-have-body.js +4 -6
  17. package/dist/http/http-info.js +63 -61
  18. package/dist/http/http-methods.js +4 -7
  19. package/dist/http/http-status-codes.js +316 -320
  20. package/dist/http/is-http-method.js +3 -6
  21. package/dist/http/normalize-http-method.js +19 -19
  22. package/dist/json/escape-json-pointer.js +6 -5
  23. package/dist/json/parse-json-pointer-segments.js +11 -6
  24. package/dist/json/pretty-print-json.d.ts +9 -0
  25. package/dist/json/pretty-print-json.d.ts.map +1 -0
  26. package/dist/json/pretty-print-json.js +42 -0
  27. package/dist/json/unescape-json-pointer.js +7 -5
  28. package/dist/node/path.js +168 -138
  29. package/dist/object/get-value-at-path.js +17 -11
  30. package/dist/object/is-object.js +24 -10
  31. package/dist/object/local-storage.js +50 -42
  32. package/dist/object/object-entries.js +2 -5
  33. package/dist/object/object-keys.js +5 -5
  34. package/dist/object/object-replace.js +13 -12
  35. package/dist/object/omit-undefined-values.js +18 -18
  36. package/dist/object/prevent-pollution.js +27 -10
  37. package/dist/object/to-json-compatible.js +71 -62
  38. package/dist/queue/queue.js +103 -84
  39. package/dist/regex/find-variables.js +13 -8
  40. package/dist/regex/regex-helpers.js +17 -17
  41. package/dist/regex/replace-variables.js +21 -19
  42. package/dist/string/camel-to-title.js +10 -5
  43. package/dist/string/capitalize.js +5 -5
  44. package/dist/string/create-hash.js +19 -16
  45. package/dist/string/generate-hash.js +153 -127
  46. package/dist/string/iterate-title.js +13 -11
  47. package/dist/string/truncate.js +16 -9
  48. package/dist/testing/console-spies.js +19 -24
  49. package/dist/testing/measure.js +42 -19
  50. package/dist/testing/measure.test-d.js +12 -9
  51. package/dist/testing/sleep.js +5 -5
  52. package/dist/url/ensure-protocol.js +8 -10
  53. package/dist/url/extract-server-from-path.js +53 -28
  54. package/dist/url/is-local-url.js +24 -18
  55. package/dist/url/is-relative-path.js +15 -13
  56. package/dist/url/is-valid-url.js +17 -10
  57. package/dist/url/make-url-absolute.js +39 -28
  58. package/dist/url/merge-urls.js +74 -52
  59. package/dist/url/redirect-to-proxy.js +68 -39
  60. package/package.json +5 -9
  61. package/dist/array/add-to-map-array.js.map +0 -7
  62. package/dist/array/is-defined.js.map +0 -7
  63. package/dist/array/sort-by-order.js.map +0 -7
  64. package/dist/consts/content-types.js.map +0 -7
  65. package/dist/dom/freeze-element.js.map +0 -7
  66. package/dist/dom/scroll-to-id.js.map +0 -7
  67. package/dist/file/json2xml.js.map +0 -7
  68. package/dist/general/create-limiter.js.map +0 -7
  69. package/dist/general/debounce.js.map +0 -7
  70. package/dist/general/extract-config-secrets.js.map +0 -7
  71. package/dist/general/has-modifier.js.map +0 -7
  72. package/dist/general/is-mac-os.js.map +0 -7
  73. package/dist/http/can-method-have-body.js.map +0 -7
  74. package/dist/http/http-info.js.map +0 -7
  75. package/dist/http/http-methods.js.map +0 -7
  76. package/dist/http/http-status-codes.js.map +0 -7
  77. package/dist/http/is-http-method.js.map +0 -7
  78. package/dist/http/normalize-http-method.js.map +0 -7
  79. package/dist/json/escape-json-pointer.js.map +0 -7
  80. package/dist/json/parse-json-pointer-segments.js.map +0 -7
  81. package/dist/json/unescape-json-pointer.js.map +0 -7
  82. package/dist/node/path.js.map +0 -7
  83. package/dist/object/get-value-at-path.js.map +0 -7
  84. package/dist/object/is-object.js.map +0 -7
  85. package/dist/object/local-storage.js.map +0 -7
  86. package/dist/object/object-entries.js.map +0 -7
  87. package/dist/object/object-keys.js.map +0 -7
  88. package/dist/object/object-replace.js.map +0 -7
  89. package/dist/object/omit-undefined-values.js.map +0 -7
  90. package/dist/object/prevent-pollution.js.map +0 -7
  91. package/dist/object/to-json-compatible.js.map +0 -7
  92. package/dist/queue/queue.js.map +0 -7
  93. package/dist/regex/find-variables.js.map +0 -7
  94. package/dist/regex/regex-helpers.js.map +0 -7
  95. package/dist/regex/replace-variables.js.map +0 -7
  96. package/dist/string/camel-to-title.js.map +0 -7
  97. package/dist/string/capitalize.js.map +0 -7
  98. package/dist/string/create-hash.js.map +0 -7
  99. package/dist/string/generate-hash.js.map +0 -7
  100. package/dist/string/iterate-title.js.map +0 -7
  101. package/dist/string/truncate.js.map +0 -7
  102. package/dist/testing/console-spies.js.map +0 -7
  103. package/dist/testing/measure.js.map +0 -7
  104. package/dist/testing/measure.test-d.js.map +0 -7
  105. package/dist/testing/sleep.js.map +0 -7
  106. package/dist/url/ensure-protocol.js.map +0 -7
  107. package/dist/url/extract-server-from-path.js.map +0 -7
  108. package/dist/url/is-local-url.js.map +0 -7
  109. package/dist/url/is-relative-path.js.map +0 -7
  110. package/dist/url/is-valid-url.js.map +0 -7
  111. package/dist/url/make-url-absolute.js.map +0 -7
  112. package/dist/url/merge-urls.js.map +0 -7
  113. 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
- const addToMapArray = (map, key, value) => {
2
- const prev = map.get(key) ?? [];
3
- prev.push(value);
4
- map.set(key, prev);
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
@@ -1,5 +1,12 @@
1
- const isDefined = (value) => value !== null && value !== void 0;
2
- export {
3
- isDefined
4
- };
5
- //# sourceMappingURL=is-defined.js.map
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
- function sortByOrder(arr, order, getId) {
2
- const orderMap = /* @__PURE__ */ new Map();
3
- order.forEach((e, idx) => orderMap.set(e, idx));
4
- const sorted = [];
5
- const untagged = [];
6
- arr.forEach((e) => {
7
- const sortedIdx = orderMap.get(getId(e));
8
- if (sortedIdx === void 0) {
9
- untagged.push(e);
10
- return;
11
- }
12
- sorted[sortedIdx] = e;
13
- });
14
- return [...sorted.filter((it) => it !== void 0), ...untagged];
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
- const CONTENT_TYPES = {
2
- "multipart/form-data": "Multipart Form",
3
- "application/x-www-form-urlencoded": "Form URL Encoded",
4
- "application/octet-stream": "Binary File",
5
- "application/json": "JSON",
6
- "application/xml": "XML",
7
- "application/yaml": "YAML",
8
- "application/edn": "EDN",
9
- "other": "Other",
10
- "none": "None"
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
- const freezeElement = (element) => {
2
- if (!element) {
3
- return () => null;
4
- }
5
- const rect = element.getBoundingClientRect();
6
- const initialViewportTop = rect.top;
7
- let rafId = null;
8
- const observer = new MutationObserver((mutations) => {
9
- const shouldProcess = mutations.some(
10
- (mutation) => mutation.type === "childList" || mutation.type === "attributes" && (mutation.attributeName === "style" || mutation.attributeName === "class")
11
- );
12
- if (!shouldProcess) {
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
- if (rafId !== null) {
16
- cancelAnimationFrame(rafId);
17
- }
18
- rafId = requestAnimationFrame(() => {
19
- const newRect = element.getBoundingClientRect();
20
- const currentViewportTop = newRect.top;
21
- if (currentViewportTop !== initialViewportTop) {
22
- const diff = currentViewportTop - initialViewportTop;
23
- window.scrollBy(0, diff);
24
- }
25
- rafId = null;
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
- observer.observe(document.body, {
29
- childList: true,
30
- subtree: true,
31
- attributes: true,
32
- attributeFilter: ["style", "class"],
33
- characterData: false
34
- });
35
- return () => {
36
- if (rafId !== null) {
37
- cancelAnimationFrame(rafId);
38
- }
39
- observer.disconnect();
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
@@ -1,29 +1,35 @@
1
- const scrollToId = (id, focus) => {
2
- const scrollToElement = (element2) => {
3
- element2.scrollIntoView();
4
- if (focus) {
5
- element2.focus();
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
- const element = document.getElementById(id);
9
- if (element) {
10
- scrollToElement(element);
11
- return;
12
- }
13
- const stopTime = Date.now() + 1e3;
14
- const tryScroll = () => {
15
- const element2 = document.getElementById(id);
16
- if (element2) {
17
- scrollToElement(element2);
18
- return;
19
- }
20
- if (Date.now() < stopTime) {
21
- requestAnimationFrame(tryScroll);
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
@@ -1,71 +1,89 @@
1
+ /**
2
+ * Character map for XML escaping to prevent XML injection attacks.
3
+ */
1
4
  const XML_ESCAPE_MAP = {
2
- "&": "&amp;",
3
- "<": "&lt;",
4
- ">": "&gt;",
5
- '"': "&quot;",
6
- "'": "&apos;"
5
+ '&': '&amp;',
6
+ '<': '&lt;',
7
+ '>': '&gt;',
8
+ '"': '&quot;',
9
+ "'": '&apos;',
7
10
  };
11
+ /**
12
+ * Escapes special XML characters to prevent injection attacks.
13
+ */
8
14
  function escapeXml(str) {
9
- return str.replace(/[&<>"']/g, (char) => XML_ESCAPE_MAP[char] ?? char);
15
+ return str.replace(/[&<>"']/g, (char) => XML_ESCAPE_MAP[char] ?? char);
10
16
  }
11
- function json2xml(data, options = {}) {
12
- const { indent = " ", format = true, xmlDeclaration = true } = options;
13
- const toXml = (value, key, currentIndent) => {
14
- let xml2 = "";
15
- if (Array.isArray(value)) {
16
- for (let i = 0, n = value.length; i < n; i++) {
17
- xml2 += toXml(value[i], key, currentIndent);
18
- }
19
- } else if (typeof value === "object" && value !== null) {
20
- let hasChild = false;
21
- let attributes = "";
22
- let children = "";
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
- for (const child in value) {
29
- if (child === "#text") {
30
- children += escapeXml(value[child]?.toString() ?? "");
31
- } else if (child === "#cdata") {
32
- const cdataContent = value[child]?.toString() ?? "";
33
- children += "<![CDATA[" + cdataContent.replace(/]]>/g, "]]]]><![CDATA[>") + "]]>";
34
- } else if (child.charAt(0) !== "@") {
35
- hasChild = true;
36
- children += toXml(value[child], child, currentIndent + indent);
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
- return xml2;
50
- };
51
- let xml = "";
52
- if (xmlDeclaration) {
53
- xml += '<?xml version="1.0" encoding="UTF-8"?>';
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
- for (const key in data) {
59
- if (Object.hasOwn(data, key)) {
60
- xml += toXml(data[key], key, "");
83
+ // Format or compact the output
84
+ if (format) {
85
+ return xml.trim();
61
86
  }
62
- }
63
- if (format) {
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 "../queue/queue.js";
2
- function createLimiter(maxConcurrent) {
3
- let activeCount = 0;
4
- const queue = new Queue();
5
- const next = () => {
6
- if (queue.isEmpty() || activeCount >= maxConcurrent) {
7
- return;
8
- }
9
- const resolve = queue.dequeue();
10
- if (resolve) {
11
- resolve();
12
- }
13
- };
14
- const run = async (fn) => {
15
- if (activeCount >= maxConcurrent) {
16
- await new Promise((resolve) => queue.enqueue(resolve));
17
- }
18
- activeCount++;
19
- try {
20
- const result = await fn();
21
- return result;
22
- } finally {
23
- activeCount--;
24
- next();
25
- }
26
- };
27
- return run;
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