@cognite/underscore 1.0.0 → 1.48411.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/arrays.d.ts +31 -31
- package/dist/arrays.js +53 -18
- package/dist/arrays.js.map +1 -0
- package/dist/assert.d.ts +6 -6
- package/dist/assert.js +12 -5
- package/dist/assert.js.map +1 -0
- package/dist/brand.d.ts +41 -38
- package/dist/brand.js +39 -0
- package/dist/brand.js.map +1 -0
- package/dist/chunk-and-process-in-parallel.d.ts +112 -106
- package/dist/chunk-and-process-in-parallel.js +138 -29
- package/dist/chunk-and-process-in-parallel.js.map +1 -0
- package/dist/chunk-array.d.ts +25 -25
- package/dist/chunk-array.js +34 -6
- package/dist/chunk-array.js.map +1 -0
- package/dist/class.d.ts +8 -8
- package/dist/class.js +11 -5
- package/dist/class.js.map +1 -0
- package/dist/exhaustive-guard.d.ts +15 -15
- package/dist/exhaustive-guard.js +19 -5
- package/dist/exhaustive-guard.js.map +1 -0
- package/dist/exponential-backoff-with-jitter.d.ts +13 -13
- package/dist/exponential-backoff-with-jitter.js +23 -7
- package/dist/exponential-backoff-with-jitter.js.map +1 -0
- package/dist/generate-unique-name.d.ts +17 -17
- package/dist/generate-unique-name.js +46 -10
- package/dist/generate-unique-name.js.map +1 -0
- package/dist/get-numbers-between.d.ts +15 -15
- package/dist/get-numbers-between.js +21 -5
- package/dist/get-numbers-between.js.map +1 -0
- package/dist/humanize-value.d.ts +3 -3
- package/dist/humanize-value.js +33 -15
- package/dist/humanize-value.js.map +1 -0
- package/dist/index.d.ts +23 -23
- package/dist/index.js +23 -23
- package/dist/index.js.map +1 -0
- package/dist/is-function.d.ts +5 -5
- package/dist/is-function.js +9 -5
- package/dist/is-function.js.map +1 -0
- package/dist/is-primitive.d.ts +7 -7
- package/dist/is-primitive.js +16 -6
- package/dist/is-primitive.js.map +1 -0
- package/dist/is-valid-url.d.ts +11 -14
- package/dist/is-valid-url.js +39 -16
- package/dist/is-valid-url.js.map +1 -0
- package/dist/iterators.d.ts +7 -7
- package/dist/iterators.js +16 -6
- package/dist/iterators.js.map +1 -0
- package/dist/math.d.ts +10 -10
- package/dist/math.js +16 -7
- package/dist/math.js.map +1 -0
- package/dist/random.d.ts +24 -24
- package/dist/random.js +52 -18
- package/dist/random.js.map +1 -0
- package/dist/react/use-debounce.d.ts +21 -21
- package/dist/react/use-debounce.js +39 -14
- package/dist/react/use-debounce.js.map +1 -0
- package/dist/react/use-derived-promise.d.ts +28 -29
- package/dist/react/use-derived-promise.js +70 -33
- package/dist/react/use-derived-promise.js.map +1 -0
- package/dist/react/use-stable-sync-external-store.d.ts +25 -25
- package/dist/react/use-stable-sync-external-store.js +52 -11
- package/dist/react/use-stable-sync-external-store.js.map +1 -0
- package/dist/react.d.ts +3 -3
- package/dist/react.js +5 -4
- package/dist/react.js.map +1 -0
- package/dist/repeat.d.ts +5 -5
- package/dist/repeat.js +11 -5
- package/dist/repeat.js.map +1 -0
- package/dist/retry-async.d.ts +87 -87
- package/dist/retry-async.js +88 -17
- package/dist/retry-async.js.map +1 -0
- package/dist/sleep.d.ts +5 -5
- package/dist/sleep.js +9 -5
- package/dist/sleep.js.map +1 -0
- package/dist/string.d.ts +35 -35
- package/dist/string.js +48 -9
- package/dist/string.js.map +1 -0
- package/dist/structMap/index.d.ts +3 -3
- package/dist/structMap/index.js +4 -0
- package/dist/structMap/index.js.map +1 -0
- package/dist/structMap/stableStringify.d.ts +21 -21
- package/dist/structMap/stableStringify.js +69 -22
- package/dist/structMap/stableStringify.js.map +1 -0
- package/dist/structMap/structMap.d.ts +111 -108
- package/dist/structMap/structMap.js +131 -28
- package/dist/structMap/structMap.js.map +1 -0
- package/dist/structMap/testUtils.d.ts +8 -8
- package/dist/structMap/testUtils.js +26 -0
- package/dist/structMap/testUtils.js.map +1 -0
- package/dist/structMap/types.d.ts +4 -4
- package/dist/structMap/types.js +5 -0
- package/dist/structMap/types.js.map +1 -0
- package/package.json +7 -4
|
@@ -1,30 +1,139 @@
|
|
|
1
|
-
import { chunkArray
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
u = !0;
|
|
21
|
-
let r = [], i = [], a = [];
|
|
22
|
-
for (let t = 0; t < s.length; t++) d.has(t) ? (r.push(...s[t]), i.push(...c[t])) : t !== e && a.push(...s[t]);
|
|
23
|
-
throw new t(n instanceof Error ? n : Error(String(n)), r, i, s[e], a);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}, p = Array.from({ length: Math.min(o, s.length) }, () => f());
|
|
27
|
-
return await Promise.all(p), c.flat();
|
|
1
|
+
import { chunkArray } from "./chunk-array.js";
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when {@link chunkAndProcessInParallel} fails partway through processing.
|
|
4
|
+
*
|
|
5
|
+
* This error provides detailed information about which items were successfully processed,
|
|
6
|
+
* which items failed, and which items were never attempted.
|
|
7
|
+
*
|
|
8
|
+
* @template T - The type of input elements.
|
|
9
|
+
*/ export class PartialExecutionError extends Error {
|
|
10
|
+
constructor(reason, succeeded, succeededResults, failed, unprocessed){
|
|
11
|
+
super(`${reason.message} (succeeded: ${succeeded.length}, failed: ${failed.length}, unprocessed: ${unprocessed.length})`);
|
|
12
|
+
this.name = 'PartialExecutionError';
|
|
13
|
+
this.reason = reason;
|
|
14
|
+
this.succeeded = succeeded;
|
|
15
|
+
this.succeededResults = succeededResults;
|
|
16
|
+
this.failed = failed;
|
|
17
|
+
this.unprocessed = unprocessed;
|
|
18
|
+
this.message = `Processing failed for ${failed.length} items: ${reason.message}`;
|
|
19
|
+
}
|
|
28
20
|
}
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Splits an array into chunks and processes each chunk in parallel using a callback function.
|
|
23
|
+
*
|
|
24
|
+
* This function is useful for batch processing large datasets where you want to:
|
|
25
|
+
* - Limit the number of items processed at once (e.g., API limits)
|
|
26
|
+
* - Process multiple chunks simultaneously for better performance
|
|
27
|
+
* - Control the degree of parallelism to avoid overwhelming resources
|
|
28
|
+
*
|
|
29
|
+
* The function maintains a saturated queue up to `numWorkers` concurrent operations.
|
|
30
|
+
* When one chunk completes, the next chunk starts immediately, ensuring optimal throughput.
|
|
31
|
+
*
|
|
32
|
+
* All chunks are processed with controlled parallelism, and the results are flattened into
|
|
33
|
+
* a single array. If any chunk fails, the entire operation fails.
|
|
34
|
+
* The order of the result is maintained regardless of the completion order of the
|
|
35
|
+
* chunks, but not that there is no requirement that the callback must return one
|
|
36
|
+
* result per input element (i.e. the number of result elements might differ from the
|
|
37
|
+
* number of input elements).
|
|
38
|
+
*
|
|
39
|
+
* @template T - The type of elements in the input array.
|
|
40
|
+
* @template U - The type of elements in the output array.
|
|
41
|
+
* @param workItems - The array of items to process.
|
|
42
|
+
* @param chunkSize - The maximum size of each chunk. Must be a positive integer.
|
|
43
|
+
* @param performWorkCallback - An async function that processes a chunk and returns results.
|
|
44
|
+
* @param options - Optional configuration for parallelism control.
|
|
45
|
+
* @returns A promise that resolves to a flattened array of all results.
|
|
46
|
+
* @throws {Error} If `chunkSize` is not a positive integer or `numWorkers` is not a positive integer.
|
|
47
|
+
* @throws {PartialExecutionError} If `performWorkCallback` throws an error for any chunk, providing details about succeeded, failed, and unprocessed items.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* // Batch retrieve assets from CDF with controlled parallelism
|
|
52
|
+
* const assetIds = [1, 2, 3, ..., 10000];
|
|
53
|
+
* const assets = await chunkAndProcessInParallel(
|
|
54
|
+
* assetIds,
|
|
55
|
+
* 1000, // Retrieve 1000 assets per request
|
|
56
|
+
* async (chunk) => {
|
|
57
|
+
* const response = await client.assets.retrieve(
|
|
58
|
+
* chunk.map(id => ({ id }))
|
|
59
|
+
* );
|
|
60
|
+
* return response;
|
|
61
|
+
* },
|
|
62
|
+
* { numWorkers: 5 } // Process at most 5 chunks concurrently
|
|
63
|
+
* );
|
|
64
|
+
* // Result: All assets from all chunks combined
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* // Handle partial execution errors
|
|
70
|
+
* try {
|
|
71
|
+
* const results = await chunkAndProcessInParallel(
|
|
72
|
+
* largeDataset,
|
|
73
|
+
* 100,
|
|
74
|
+
* async (chunk) => processChunk(chunk),
|
|
75
|
+
* { numWorkers: 3 }
|
|
76
|
+
* );
|
|
77
|
+
* } catch (error) {
|
|
78
|
+
* if (error instanceof PartialExecutionError) {
|
|
79
|
+
* console.log(`Successfully processed ${error.succeeded.length} items`);
|
|
80
|
+
* console.log(`Failed to process ${error.failed.length} items`);
|
|
81
|
+
* console.log(`Did not attempt ${error.unprocessed.length} items`);
|
|
82
|
+
* console.error('Failure reason:', error.reason);
|
|
83
|
+
* // Optionally retry only the failed and unprocessed items
|
|
84
|
+
* }
|
|
85
|
+
* throw error;
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/ export async function chunkAndProcessInParallel(workItems, chunkSize, performWorkCallback, options) {
|
|
89
|
+
var _ref;
|
|
90
|
+
const numWorkers = (_ref = options === null || options === void 0 ? void 0 : options.numWorkers) !== null && _ref !== void 0 ? _ref : 10;
|
|
91
|
+
if (numWorkers <= 0 || !Number.isInteger(numWorkers)) {
|
|
92
|
+
throw new Error('numWorkers must be a positive integer');
|
|
93
|
+
}
|
|
94
|
+
const chunks = Array.from(chunkArray(workItems, chunkSize));
|
|
95
|
+
if (chunks.length === 0) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
// Results array to maintain order
|
|
99
|
+
const results = new Array(chunks.length);
|
|
100
|
+
// Track processing state
|
|
101
|
+
let nextChunkIndex = 0;
|
|
102
|
+
let stopped = false;
|
|
103
|
+
const completedChunks = new Set();
|
|
104
|
+
// Worker function that keeps processing chunks until done or stopped
|
|
105
|
+
const worker = async ()=>{
|
|
106
|
+
while(!stopped && nextChunkIndex < chunks.length){
|
|
107
|
+
const currentIndex = nextChunkIndex++;
|
|
108
|
+
try {
|
|
109
|
+
const result = await performWorkCallback(chunks[currentIndex]);
|
|
110
|
+
results[currentIndex] = result;
|
|
111
|
+
completedChunks.add(currentIndex);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
stopped = true;
|
|
114
|
+
// Calculate succeeded, failed, and unprocessed items
|
|
115
|
+
const succeeded = [];
|
|
116
|
+
const succeededResults = [];
|
|
117
|
+
const unprocessed = [];
|
|
118
|
+
for(let i = 0; i < chunks.length; i++){
|
|
119
|
+
if (completedChunks.has(i)) {
|
|
120
|
+
succeeded.push(...chunks[i]);
|
|
121
|
+
succeededResults.push(...results[i]);
|
|
122
|
+
} else if (i !== currentIndex) {
|
|
123
|
+
unprocessed.push(...chunks[i]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
127
|
+
throw new PartialExecutionError(errorObj, succeeded, succeededResults, chunks[currentIndex], unprocessed);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
// Start up to numWorkers workers
|
|
132
|
+
const workers = Array.from({
|
|
133
|
+
length: Math.min(numWorkers, chunks.length)
|
|
134
|
+
}, ()=>worker());
|
|
135
|
+
await Promise.all(workers);
|
|
136
|
+
return results.flat();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=chunk-and-process-in-parallel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chunk-and-process-in-parallel.ts"],"sourcesContent":["import { chunkArray } from './chunk-array';\n\n/**\n * Options for {@link chunkAndProcessInParallel}.\n */\nexport interface ChunkAndProcessOptions {\n /**\n * Maximum number of chunks to process in parallel.\n * @default 10\n */\n numWorkers?: number;\n}\n\n/**\n * Error thrown when {@link chunkAndProcessInParallel} fails partway through processing.\n *\n * This error provides detailed information about which items were successfully processed,\n * which items failed, and which items were never attempted.\n *\n * @template T - The type of input elements.\n */\nexport class PartialExecutionError<T, U> extends Error {\n /**\n * The underlying error that caused the failure.\n */\n public readonly reason: Error;\n\n /**\n * Input items from chunks that were successfully processed before the error occurred.\n */\n public readonly succeeded: T[];\n\n /**\n * Results from the chunks that were successfully processed before the error occurred.\n */\n public readonly succeededResults: U[];\n\n /**\n * Input items from the chunk that failed and caused the error.\n */\n public readonly failed: T[];\n\n /**\n * Input items from chunks that were never processed due to the error.\n * This includes both chunks that were never started and chunks that were\n * in-progress when the error occurred.\n */\n public readonly unprocessed: T[];\n\n constructor(\n reason: Error,\n succeeded: T[],\n succeededResults: U[],\n failed: T[],\n unprocessed: T[]\n ) {\n super(\n `${reason.message} (succeeded: ${succeeded.length}, failed: ${failed.length}, unprocessed: ${unprocessed.length})`\n );\n this.name = 'PartialExecutionError';\n this.reason = reason;\n this.succeeded = succeeded;\n this.succeededResults = succeededResults;\n this.failed = failed;\n this.unprocessed = unprocessed;\n this.message = `Processing failed for ${failed.length} items: ${reason.message}`;\n }\n}\n\n/**\n * Splits an array into chunks and processes each chunk in parallel using a callback function.\n *\n * This function is useful for batch processing large datasets where you want to:\n * - Limit the number of items processed at once (e.g., API limits)\n * - Process multiple chunks simultaneously for better performance\n * - Control the degree of parallelism to avoid overwhelming resources\n *\n * The function maintains a saturated queue up to `numWorkers` concurrent operations.\n * When one chunk completes, the next chunk starts immediately, ensuring optimal throughput.\n *\n * All chunks are processed with controlled parallelism, and the results are flattened into\n * a single array. If any chunk fails, the entire operation fails.\n * The order of the result is maintained regardless of the completion order of the\n * chunks, but not that there is no requirement that the callback must return one\n * result per input element (i.e. the number of result elements might differ from the\n * number of input elements).\n *\n * @template T - The type of elements in the input array.\n * @template U - The type of elements in the output array.\n * @param workItems - The array of items to process.\n * @param chunkSize - The maximum size of each chunk. Must be a positive integer.\n * @param performWorkCallback - An async function that processes a chunk and returns results.\n * @param options - Optional configuration for parallelism control.\n * @returns A promise that resolves to a flattened array of all results.\n * @throws {Error} If `chunkSize` is not a positive integer or `numWorkers` is not a positive integer.\n * @throws {PartialExecutionError} If `performWorkCallback` throws an error for any chunk, providing details about succeeded, failed, and unprocessed items.\n *\n * @example\n * ```ts\n * // Batch retrieve assets from CDF with controlled parallelism\n * const assetIds = [1, 2, 3, ..., 10000];\n * const assets = await chunkAndProcessInParallel(\n * assetIds,\n * 1000, // Retrieve 1000 assets per request\n * async (chunk) => {\n * const response = await client.assets.retrieve(\n * chunk.map(id => ({ id }))\n * );\n * return response;\n * },\n * { numWorkers: 5 } // Process at most 5 chunks concurrently\n * );\n * // Result: All assets from all chunks combined\n * ```\n *\n * @example\n * ```ts\n * // Handle partial execution errors\n * try {\n * const results = await chunkAndProcessInParallel(\n * largeDataset,\n * 100,\n * async (chunk) => processChunk(chunk),\n * { numWorkers: 3 }\n * );\n * } catch (error) {\n * if (error instanceof PartialExecutionError) {\n * console.log(`Successfully processed ${error.succeeded.length} items`);\n * console.log(`Failed to process ${error.failed.length} items`);\n * console.log(`Did not attempt ${error.unprocessed.length} items`);\n * console.error('Failure reason:', error.reason);\n * // Optionally retry only the failed and unprocessed items\n * }\n * throw error;\n * }\n * ```\n */\nexport async function chunkAndProcessInParallel<T, U>(\n workItems: T[],\n chunkSize: number,\n performWorkCallback: (workItemsChunk: T[]) => Promise<U[]>,\n options?: ChunkAndProcessOptions\n): Promise<U[]> {\n const numWorkers = options?.numWorkers ?? 10;\n if (numWorkers <= 0 || !Number.isInteger(numWorkers)) {\n throw new Error('numWorkers must be a positive integer');\n }\n\n const chunks = Array.from(chunkArray(workItems, chunkSize));\n if (chunks.length === 0) {\n return [];\n }\n\n // Results array to maintain order\n const results: U[][] = new Array(chunks.length);\n\n // Track processing state\n let nextChunkIndex = 0;\n let stopped = false;\n const completedChunks = new Set<number>();\n\n // Worker function that keeps processing chunks until done or stopped\n const worker = async (): Promise<void> => {\n while (!stopped && nextChunkIndex < chunks.length) {\n const currentIndex = nextChunkIndex++;\n try {\n const result = await performWorkCallback(chunks[currentIndex]);\n results[currentIndex] = result;\n completedChunks.add(currentIndex);\n } catch (error) {\n stopped = true;\n\n // Calculate succeeded, failed, and unprocessed items\n const succeeded: T[] = [];\n const succeededResults: U[] = [];\n const unprocessed: T[] = [];\n\n for (let i = 0; i < chunks.length; i++) {\n if (completedChunks.has(i)) {\n succeeded.push(...chunks[i]);\n succeededResults.push(...results[i]);\n } else if (i !== currentIndex) {\n unprocessed.push(...chunks[i]);\n }\n }\n\n const errorObj =\n error instanceof Error ? error : new Error(String(error));\n throw new PartialExecutionError<T, U>(\n errorObj,\n succeeded,\n succeededResults,\n chunks[currentIndex],\n unprocessed\n );\n }\n }\n };\n\n // Start up to numWorkers workers\n const workers = Array.from(\n { length: Math.min(numWorkers, chunks.length) },\n () => worker()\n );\n\n await Promise.all(workers);\n return results.flat();\n}\n"],"names":["chunkArray","PartialExecutionError","Error","reason","succeeded","succeededResults","failed","unprocessed","message","length","name","chunkAndProcessInParallel","workItems","chunkSize","performWorkCallback","options","numWorkers","Number","isInteger","chunks","Array","from","results","nextChunkIndex","stopped","completedChunks","Set","worker","currentIndex","result","add","error","i","has","push","errorObj","String","workers","Math","min","Promise","all","flat"],"mappings":"AAAA,SAASA,UAAU,QAAQ,mBAAgB;AAa3C;;;;;;;CAOC,GACD,OAAO,MAAMC,8BAAoCC;IA4B/C,YACEC,MAAa,EACbC,SAAc,EACdC,gBAAqB,EACrBC,MAAW,EACXC,WAAgB,CAChB;QACA,KAAK,CACH,GAAGJ,OAAOK,OAAO,CAAC,aAAa,EAAEJ,UAAUK,MAAM,CAAC,UAAU,EAAEH,OAAOG,MAAM,CAAC,eAAe,EAAEF,YAAYE,MAAM,CAAC,CAAC,CAAC;QAEpH,IAAI,CAACC,IAAI,GAAG;QACZ,IAAI,CAACP,MAAM,GAAGA;QACd,IAAI,CAACC,SAAS,GAAGA;QACjB,IAAI,CAACC,gBAAgB,GAAGA;QACxB,IAAI,CAACC,MAAM,GAAGA;QACd,IAAI,CAACC,WAAW,GAAGA;QACnB,IAAI,CAACC,OAAO,GAAG,CAAC,sBAAsB,EAAEF,OAAOG,MAAM,CAAC,QAAQ,EAAEN,OAAOK,OAAO,EAAE;IAClF;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmEC,GACD,OAAO,eAAeG,0BACpBC,SAAc,EACdC,SAAiB,EACjBC,mBAA0D,EAC1DC,OAAgC;;IAEhC,MAAMC,qBAAaD,oBAAAA,8BAAAA,QAASC,UAAU,uCAAI;IAC1C,IAAIA,cAAc,KAAK,CAACC,OAAOC,SAAS,CAACF,aAAa;QACpD,MAAM,IAAId,MAAM;IAClB;IAEA,MAAMiB,SAASC,MAAMC,IAAI,CAACrB,WAAWY,WAAWC;IAChD,IAAIM,OAAOV,MAAM,KAAK,GAAG;QACvB,OAAO,EAAE;IACX;IAEA,kCAAkC;IAClC,MAAMa,UAAiB,IAAIF,MAAMD,OAAOV,MAAM;IAE9C,yBAAyB;IACzB,IAAIc,iBAAiB;IACrB,IAAIC,UAAU;IACd,MAAMC,kBAAkB,IAAIC;IAE5B,qEAAqE;IACrE,MAAMC,SAAS;QACb,MAAO,CAACH,WAAWD,iBAAiBJ,OAAOV,MAAM,CAAE;YACjD,MAAMmB,eAAeL;YACrB,IAAI;gBACF,MAAMM,SAAS,MAAMf,oBAAoBK,MAAM,CAACS,aAAa;gBAC7DN,OAAO,CAACM,aAAa,GAAGC;gBACxBJ,gBAAgBK,GAAG,CAACF;YACtB,EAAE,OAAOG,OAAO;gBACdP,UAAU;gBAEV,qDAAqD;gBACrD,MAAMpB,YAAiB,EAAE;gBACzB,MAAMC,mBAAwB,EAAE;gBAChC,MAAME,cAAmB,EAAE;gBAE3B,IAAK,IAAIyB,IAAI,GAAGA,IAAIb,OAAOV,MAAM,EAAEuB,IAAK;oBACtC,IAAIP,gBAAgBQ,GAAG,CAACD,IAAI;wBAC1B5B,UAAU8B,IAAI,IAAIf,MAAM,CAACa,EAAE;wBAC3B3B,iBAAiB6B,IAAI,IAAIZ,OAAO,CAACU,EAAE;oBACrC,OAAO,IAAIA,MAAMJ,cAAc;wBAC7BrB,YAAY2B,IAAI,IAAIf,MAAM,CAACa,EAAE;oBAC/B;gBACF;gBAEA,MAAMG,WACJJ,iBAAiB7B,QAAQ6B,QAAQ,IAAI7B,MAAMkC,OAAOL;gBACpD,MAAM,IAAI9B,sBACRkC,UACA/B,WACAC,kBACAc,MAAM,CAACS,aAAa,EACpBrB;YAEJ;QACF;IACF;IAEA,iCAAiC;IACjC,MAAM8B,UAAUjB,MAAMC,IAAI,CACxB;QAAEZ,QAAQ6B,KAAKC,GAAG,CAACvB,YAAYG,OAAOV,MAAM;IAAE,GAC9C,IAAMkB;IAGR,MAAMa,QAAQC,GAAG,CAACJ;IAClB,OAAOf,QAAQoB,IAAI;AACrB"}
|
package/dist/chunk-array.d.ts
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
/**
|
|
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
|
-
|
|
2
|
+
* Splits an array into smaller chunks of a specified size using a generator. If the
|
|
3
|
+
* array length is not evenly divisible by `chunkSize`, the final chunk will contain
|
|
4
|
+
* the remaining elements.
|
|
5
|
+
*
|
|
6
|
+
* @template T - The type of elements in the array.
|
|
7
|
+
* @param array - The array to split into chunks.
|
|
8
|
+
* @param chunkSize - The maximum size of each chunk. Must be a positive integer.
|
|
9
|
+
* @returns A generator that yields arrays of size `chunkSize` (or smaller for the last chunk).
|
|
10
|
+
* @throws {Error} If `chunkSize` is not a positive integer.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* // Basic usage
|
|
15
|
+
* const chunks = Array.from(chunkArray([1, 2, 3, 4, 5], 2));
|
|
16
|
+
* // Result: [[1, 2], [3, 4], [5]]
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* // Process chunks lazily using for...of
|
|
22
|
+
* for (const chunk of chunkArray([1, 2, 3, 4, 5, 6], 3)) {
|
|
23
|
+
* console.log(chunk); // Logs: [1, 2, 3], then [4, 5, 6]
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
27
|
export declare function chunkArray<T>(array: T[], chunkSize: number): Generator<T[], void, unknown>;
|
package/dist/chunk-array.js
CHANGED
|
@@ -1,7 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Splits an array into smaller chunks of a specified size using a generator. If the
|
|
3
|
+
* array length is not evenly divisible by `chunkSize`, the final chunk will contain
|
|
4
|
+
* the remaining elements.
|
|
5
|
+
*
|
|
6
|
+
* @template T - The type of elements in the array.
|
|
7
|
+
* @param array - The array to split into chunks.
|
|
8
|
+
* @param chunkSize - The maximum size of each chunk. Must be a positive integer.
|
|
9
|
+
* @returns A generator that yields arrays of size `chunkSize` (or smaller for the last chunk).
|
|
10
|
+
* @throws {Error} If `chunkSize` is not a positive integer.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* // Basic usage
|
|
15
|
+
* const chunks = Array.from(chunkArray([1, 2, 3, 4, 5], 2));
|
|
16
|
+
* // Result: [[1, 2], [3, 4], [5]]
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* // Process chunks lazily using for...of
|
|
22
|
+
* for (const chunk of chunkArray([1, 2, 3, 4, 5, 6], 3)) {
|
|
23
|
+
* console.log(chunk); // Logs: [1, 2, 3], then [4, 5, 6]
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/ export function* chunkArray(array, chunkSize) {
|
|
27
|
+
if (chunkSize <= 0 || !Number.isInteger(chunkSize)) {
|
|
28
|
+
throw new Error(`chunkSize must be an integer greater than 0, but got ${chunkSize}`);
|
|
29
|
+
}
|
|
30
|
+
for(let i = 0; i < array.length; i += chunkSize){
|
|
31
|
+
yield array.slice(i, i + chunkSize);
|
|
32
|
+
}
|
|
5
33
|
}
|
|
6
|
-
|
|
7
|
-
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=chunk-array.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chunk-array.ts"],"sourcesContent":["/**\n * Splits an array into smaller chunks of a specified size using a generator. If the\n * array length is not evenly divisible by `chunkSize`, the final chunk will contain\n * the remaining elements.\n *\n * @template T - The type of elements in the array.\n * @param array - The array to split into chunks.\n * @param chunkSize - The maximum size of each chunk. Must be a positive integer.\n * @returns A generator that yields arrays of size `chunkSize` (or smaller for the last chunk).\n * @throws {Error} If `chunkSize` is not a positive integer.\n *\n * @example\n * ```ts\n * // Basic usage\n * const chunks = Array.from(chunkArray([1, 2, 3, 4, 5], 2));\n * // Result: [[1, 2], [3, 4], [5]]\n * ```\n *\n * @example\n * ```ts\n * // Process chunks lazily using for...of\n * for (const chunk of chunkArray([1, 2, 3, 4, 5, 6], 3)) {\n * console.log(chunk); // Logs: [1, 2, 3], then [4, 5, 6]\n * }\n * ```\n */\nexport function* chunkArray<T>(\n array: T[],\n chunkSize: number\n): Generator<T[], void, unknown> {\n if (chunkSize <= 0 || !Number.isInteger(chunkSize)) {\n throw new Error(\n `chunkSize must be an integer greater than 0, but got ${chunkSize}`\n );\n }\n\n for (let i = 0; i < array.length; i += chunkSize) {\n yield array.slice(i, i + chunkSize);\n }\n}\n"],"names":["chunkArray","array","chunkSize","Number","isInteger","Error","i","length","slice"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;CAyBC,GACD,OAAO,UAAUA,WACfC,KAAU,EACVC,SAAiB;IAEjB,IAAIA,aAAa,KAAK,CAACC,OAAOC,SAAS,CAACF,YAAY;QAClD,MAAM,IAAIG,MACR,CAAC,qDAAqD,EAAEH,WAAW;IAEvE;IAEA,IAAK,IAAII,IAAI,GAAGA,IAAIL,MAAMM,MAAM,EAAED,KAAKJ,UAAW;QAChD,MAAMD,MAAMO,KAAK,CAACF,GAAGA,IAAIJ;IAC3B;AACF"}
|
package/dist/class.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export type Class<T> = Function & {
|
|
2
|
-
|
|
2
|
+
prototype: T;
|
|
3
3
|
};
|
|
4
4
|
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
* Returns true if the value is an instance of the given class type.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of the class instance.
|
|
8
|
+
* @param value - The value to check
|
|
9
|
+
* @param classType - The class type to expect.
|
|
10
|
+
* @returns True if the value is an instance of the given class type.
|
|
11
|
+
*/
|
|
12
12
|
export declare function isInstanceOf<T>(value: unknown, classType: Class<T>): value is T;
|
package/dist/class.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Returns true if the value is an instance of the given class type.
|
|
3
|
+
*
|
|
4
|
+
* @template T - The type of the class instance.
|
|
5
|
+
* @param value - The value to check
|
|
6
|
+
* @param classType - The class type to expect.
|
|
7
|
+
* @returns True if the value is an instance of the given class type.
|
|
8
|
+
*/ export function isInstanceOf(value, classType) {
|
|
9
|
+
return value instanceof classType;
|
|
4
10
|
}
|
|
5
|
-
|
|
6
|
-
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=class.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/class.ts"],"sourcesContent":["export type Class<T> = Function & { prototype: T };\n\n/**\n * Returns true if the value is an instance of the given class type.\n *\n * @template T - The type of the class instance.\n * @param value - The value to check\n * @param classType - The class type to expect.\n * @returns True if the value is an instance of the given class type.\n */\nexport function isInstanceOf<T>(\n value: unknown,\n classType: Class<T>\n): value is T {\n return value instanceof classType;\n}\n"],"names":["isInstanceOf","value","classType"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,SAASA,aACdC,KAAc,EACdC,SAAmB;IAEnB,OAAOD,iBAAiBC;AAC1B"}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
2
|
+
* This will cause a type error if the switch statement is not exhaustive.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* type Value = 'a' | 'b';
|
|
7
|
+
* const value: Value = 'a';
|
|
8
|
+
* switch (value) {
|
|
9
|
+
* case 'a':
|
|
10
|
+
* return 'A';
|
|
11
|
+
* default:
|
|
12
|
+
* // This will cause a type error as we are missing the case for 'b'
|
|
13
|
+
* return exhaustiveGuard(value);
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
17
|
export declare function exhaustiveGuard(_value: never): never;
|
package/dist/exhaustive-guard.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* This will cause a type error if the switch statement is not exhaustive.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* type Value = 'a' | 'b';
|
|
7
|
+
* const value: Value = 'a';
|
|
8
|
+
* switch (value) {
|
|
9
|
+
* case 'a':
|
|
10
|
+
* return 'A';
|
|
11
|
+
* default:
|
|
12
|
+
* // This will cause a type error as we are missing the case for 'b'
|
|
13
|
+
* return exhaustiveGuard(value);
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/ export function exhaustiveGuard(_value) {
|
|
17
|
+
throw new Error(`ERROR! Reached forbidden guard function with unexpected value: ${JSON.stringify(_value)}`);
|
|
4
18
|
}
|
|
5
|
-
|
|
6
|
-
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=exhaustive-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/exhaustive-guard.ts"],"sourcesContent":["/**\n * This will cause a type error if the switch statement is not exhaustive.\n *\n * @example\n * ```ts\n * type Value = 'a' | 'b';\n * const value: Value = 'a';\n * switch (value) {\n * case 'a':\n * return 'A';\n * default:\n * // This will cause a type error as we are missing the case for 'b'\n * return exhaustiveGuard(value);\n * }\n * ```\n */\nexport function exhaustiveGuard(_value: never): never {\n throw new Error(\n `ERROR! Reached forbidden guard function with unexpected value: ${JSON.stringify(\n _value\n )}`\n );\n}\n"],"names":["exhaustiveGuard","_value","Error","JSON","stringify"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASA,gBAAgBC,MAAa;IAC3C,MAAM,IAAIC,MACR,CAAC,+DAA+D,EAAEC,KAAKC,SAAS,CAC9EH,SACC;AAEP"}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
* Calculates the delay in milliseconds for the next retry attempt. Uses "exponential backoff with
|
|
3
|
+
* full jitter" algorithm.
|
|
4
|
+
*
|
|
5
|
+
* `floor(random() * min(2 ** attempt * baseDelayInMs, maxDelayInMs))`
|
|
6
|
+
*
|
|
7
|
+
* @see https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
|
|
8
|
+
*
|
|
9
|
+
* @param attempt - The current attempt (1-based).
|
|
10
|
+
* @param baseDelayInMs - The base delay in milliseconds. Default is 250ms.
|
|
11
|
+
* @param maxDelayInMs - The maximum delay in milliseconds. Default is 15,000ms (15 seconds).
|
|
12
|
+
* @param randomCallback - A callback function that returns a random number between 0 (inclusive) and 1 (exclusive). Default is Math.random. Provided for testing/mocking purposes.
|
|
13
|
+
* @returns The delay in milliseconds for the next retry attempt.
|
|
14
|
+
*/
|
|
15
15
|
export declare function exponentialBackoffWithJitter(attempt: number, baseDelayInMs?: number, maxDelayInMs?: number, randomCallback?: typeof Math.random): number;
|
|
@@ -1,8 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Calculates the delay in milliseconds for the next retry attempt. Uses "exponential backoff with
|
|
3
|
+
* full jitter" algorithm.
|
|
4
|
+
*
|
|
5
|
+
* `floor(random() * min(2 ** attempt * baseDelayInMs, maxDelayInMs))`
|
|
6
|
+
*
|
|
7
|
+
* @see https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
|
|
8
|
+
*
|
|
9
|
+
* @param attempt - The current attempt (1-based).
|
|
10
|
+
* @param baseDelayInMs - The base delay in milliseconds. Default is 250ms.
|
|
11
|
+
* @param maxDelayInMs - The maximum delay in milliseconds. Default is 15,000ms (15 seconds).
|
|
12
|
+
* @param randomCallback - A callback function that returns a random number between 0 (inclusive) and 1 (exclusive). Default is Math.random. Provided for testing/mocking purposes.
|
|
13
|
+
* @returns The delay in milliseconds for the next retry attempt.
|
|
14
|
+
*/ export function exponentialBackoffWithJitter(attempt, baseDelayInMs = 250, maxDelayInMs = 15000, randomCallback = Math.random) {
|
|
15
|
+
if (attempt < 1) {
|
|
16
|
+
throw new Error('`attempt` must be 1 or greater');
|
|
17
|
+
}
|
|
18
|
+
// = floor(random() * min(2 ** attempt * baseDelayInMs, maxDelayInMs))
|
|
19
|
+
const exponentialDelay = 2 ** attempt * baseDelayInMs;
|
|
20
|
+
const cappedDelay = Math.min(exponentialDelay, maxDelayInMs);
|
|
21
|
+
return Math.floor(randomCallback() * cappedDelay);
|
|
6
22
|
}
|
|
7
|
-
|
|
8
|
-
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=exponential-backoff-with-jitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/exponential-backoff-with-jitter.ts"],"sourcesContent":["/**\n * Calculates the delay in milliseconds for the next retry attempt. Uses \"exponential backoff with\n * full jitter\" algorithm.\n *\n * `floor(random() * min(2 ** attempt * baseDelayInMs, maxDelayInMs))`\n *\n * @see https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/\n *\n * @param attempt - The current attempt (1-based).\n * @param baseDelayInMs - The base delay in milliseconds. Default is 250ms.\n * @param maxDelayInMs - The maximum delay in milliseconds. Default is 15,000ms (15 seconds).\n * @param randomCallback - A callback function that returns a random number between 0 (inclusive) and 1 (exclusive). Default is Math.random. Provided for testing/mocking purposes.\n * @returns The delay in milliseconds for the next retry attempt.\n */\nexport function exponentialBackoffWithJitter(\n attempt: number,\n baseDelayInMs = 250,\n maxDelayInMs = 15_000,\n randomCallback: typeof Math.random = Math.random\n): number {\n if (attempt < 1) {\n throw new Error('`attempt` must be 1 or greater');\n }\n\n // = floor(random() * min(2 ** attempt * baseDelayInMs, maxDelayInMs))\n const exponentialDelay = 2 ** attempt * baseDelayInMs;\n const cappedDelay = Math.min(exponentialDelay, maxDelayInMs);\n return Math.floor(randomCallback() * cappedDelay);\n}\n"],"names":["exponentialBackoffWithJitter","attempt","baseDelayInMs","maxDelayInMs","randomCallback","Math","random","Error","exponentialDelay","cappedDelay","min","floor"],"mappings":"AAAA;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASA,6BACdC,OAAe,EACfC,gBAAgB,GAAG,EACnBC,eAAe,KAAM,EACrBC,iBAAqCC,KAAKC,MAAM;IAEhD,IAAIL,UAAU,GAAG;QACf,MAAM,IAAIM,MAAM;IAClB;IAEA,sEAAsE;IACtE,MAAMC,mBAAmB,KAAKP,UAAUC;IACxC,MAAMO,cAAcJ,KAAKK,GAAG,CAACF,kBAAkBL;IAC/C,OAAOE,KAAKM,KAAK,CAACP,mBAAmBK;AACvC"}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
2
|
+
* Generates a unique name based on a prefix and existing names.
|
|
3
|
+
* If no existing names match the prefix, returns the prefix as-is.
|
|
4
|
+
* Otherwise, appends the next available number to the prefix.
|
|
5
|
+
*
|
|
6
|
+
* @param existingNames - Array of existing names to check against
|
|
7
|
+
* @param prefix - The prefix to use for the unique name
|
|
8
|
+
* @returns A unique name that doesn't conflict with existing names
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* generateUniqueName(['Untitled evaluation', 'Untitled evaluation 1'], 'Untitled evaluation')
|
|
13
|
+
* // Returns: 'Untitled evaluation 2'
|
|
14
|
+
*
|
|
15
|
+
* generateUniqueName(['Other name'], 'Untitled evaluation')
|
|
16
|
+
* // Returns: 'Untitled evaluation'
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
19
|
export declare function generateUniqueName(existingNames: string[], prefix: string): string;
|
|
@@ -1,11 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Generates a unique name based on a prefix and existing names.
|
|
3
|
+
* If no existing names match the prefix, returns the prefix as-is.
|
|
4
|
+
* Otherwise, appends the next available number to the prefix.
|
|
5
|
+
*
|
|
6
|
+
* @param existingNames - Array of existing names to check against
|
|
7
|
+
* @param prefix - The prefix to use for the unique name
|
|
8
|
+
* @returns A unique name that doesn't conflict with existing names
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* generateUniqueName(['Untitled evaluation', 'Untitled evaluation 1'], 'Untitled evaluation')
|
|
13
|
+
* // Returns: 'Untitled evaluation 2'
|
|
14
|
+
*
|
|
15
|
+
* generateUniqueName(['Other name'], 'Untitled evaluation')
|
|
16
|
+
* // Returns: 'Untitled evaluation'
|
|
17
|
+
* ```
|
|
18
|
+
*/ export function generateUniqueName(existingNames, prefix) {
|
|
19
|
+
const matchingNames = existingNames.filter((name)=>name.startsWith(prefix));
|
|
20
|
+
if (matchingNames.length === 0) {
|
|
21
|
+
return prefix;
|
|
22
|
+
}
|
|
23
|
+
const maxNumber = matchingNames.reduce((max, name)=>{
|
|
24
|
+
if (name === prefix) {
|
|
25
|
+
return Math.max(max, 0);
|
|
26
|
+
}
|
|
27
|
+
// Try to extract any number that appears after the prefix
|
|
28
|
+
// This handles cases like "Test (1)", "Test.1", "Test 1", etc.
|
|
29
|
+
const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
30
|
+
// Pattern 1: prefix + space + number + optional text
|
|
31
|
+
let match = name.match(new RegExp(`^${escapedPrefix} (\\d+)(?:.*)?$`));
|
|
32
|
+
if (match) {
|
|
33
|
+
return Math.max(max, parseInt(match[1], 10));
|
|
34
|
+
}
|
|
35
|
+
// Pattern 2: prefix + any non-word character + number + optional text
|
|
36
|
+
match = name.match(new RegExp(`^${escapedPrefix}[^\\w]*(\\d+)(?:.*)?$`));
|
|
37
|
+
if (match) {
|
|
38
|
+
return Math.max(max, parseInt(match[1], 10));
|
|
39
|
+
}
|
|
40
|
+
// If the name starts with prefix but doesn't follow any numbered pattern,
|
|
41
|
+
// treat it as if it's the base prefix (equivalent to having number 0)
|
|
42
|
+
return Math.max(max, 0);
|
|
43
|
+
}, 0);
|
|
44
|
+
return `${prefix} ${maxNumber + 1}`;
|
|
9
45
|
}
|
|
10
|
-
|
|
11
|
-
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=generate-unique-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/generate-unique-name.ts"],"sourcesContent":["/**\n * Generates a unique name based on a prefix and existing names.\n * If no existing names match the prefix, returns the prefix as-is.\n * Otherwise, appends the next available number to the prefix.\n *\n * @param existingNames - Array of existing names to check against\n * @param prefix - The prefix to use for the unique name\n * @returns A unique name that doesn't conflict with existing names\n *\n * @example\n * ```typescript\n * generateUniqueName(['Untitled evaluation', 'Untitled evaluation 1'], 'Untitled evaluation')\n * // Returns: 'Untitled evaluation 2'\n *\n * generateUniqueName(['Other name'], 'Untitled evaluation')\n * // Returns: 'Untitled evaluation'\n * ```\n */\nexport function generateUniqueName(\n existingNames: string[],\n prefix: string\n): string {\n const matchingNames = existingNames.filter((name) => name.startsWith(prefix));\n\n if (matchingNames.length === 0) {\n return prefix;\n }\n\n const maxNumber = matchingNames.reduce((max, name) => {\n if (name === prefix) {\n return Math.max(max, 0);\n }\n\n // Try to extract any number that appears after the prefix\n // This handles cases like \"Test (1)\", \"Test.1\", \"Test 1\", etc.\n const escapedPrefix = prefix.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n // Pattern 1: prefix + space + number + optional text\n let match = name.match(new RegExp(`^${escapedPrefix} (\\\\d+)(?:.*)?$`));\n if (match) {\n return Math.max(max, parseInt(match[1], 10));\n }\n\n // Pattern 2: prefix + any non-word character + number + optional text\n match = name.match(new RegExp(`^${escapedPrefix}[^\\\\w]*(\\\\d+)(?:.*)?$`));\n if (match) {\n return Math.max(max, parseInt(match[1], 10));\n }\n\n // If the name starts with prefix but doesn't follow any numbered pattern,\n // treat it as if it's the base prefix (equivalent to having number 0)\n return Math.max(max, 0);\n }, 0);\n\n return `${prefix} ${maxNumber + 1}`;\n}\n"],"names":["generateUniqueName","existingNames","prefix","matchingNames","filter","name","startsWith","length","maxNumber","reduce","max","Math","escapedPrefix","replace","match","RegExp","parseInt"],"mappings":"AAAA;;;;;;;;;;;;;;;;;CAiBC,GACD,OAAO,SAASA,mBACdC,aAAuB,EACvBC,MAAc;IAEd,MAAMC,gBAAgBF,cAAcG,MAAM,CAAC,CAACC,OAASA,KAAKC,UAAU,CAACJ;IAErE,IAAIC,cAAcI,MAAM,KAAK,GAAG;QAC9B,OAAOL;IACT;IAEA,MAAMM,YAAYL,cAAcM,MAAM,CAAC,CAACC,KAAKL;QAC3C,IAAIA,SAASH,QAAQ;YACnB,OAAOS,KAAKD,GAAG,CAACA,KAAK;QACvB;QAEA,0DAA0D;QAC1D,+DAA+D;QAC/D,MAAME,gBAAgBV,OAAOW,OAAO,CAAC,uBAAuB;QAE5D,qDAAqD;QACrD,IAAIC,QAAQT,KAAKS,KAAK,CAAC,IAAIC,OAAO,CAAC,CAAC,EAAEH,cAAc,eAAe,CAAC;QACpE,IAAIE,OAAO;YACT,OAAOH,KAAKD,GAAG,CAACA,KAAKM,SAASF,KAAK,CAAC,EAAE,EAAE;QAC1C;QAEA,sEAAsE;QACtEA,QAAQT,KAAKS,KAAK,CAAC,IAAIC,OAAO,CAAC,CAAC,EAAEH,cAAc,qBAAqB,CAAC;QACtE,IAAIE,OAAO;YACT,OAAOH,KAAKD,GAAG,CAACA,KAAKM,SAASF,KAAK,CAAC,EAAE,EAAE;QAC1C;QAEA,0EAA0E;QAC1E,sEAAsE;QACtE,OAAOH,KAAKD,GAAG,CAACA,KAAK;IACvB,GAAG;IAEH,OAAO,GAAGR,OAAO,CAAC,EAAEM,YAAY,GAAG;AACrC"}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
2
|
+
* Generates a sequence of numbers between the specified range.
|
|
3
|
+
*
|
|
4
|
+
* @param minInclusive - The starting number of the sequence (inclusive).
|
|
5
|
+
* @param maxExclusive - The ending number of the sequence (exclusive).
|
|
6
|
+
* @param step - The step value to increment by (default is 1).
|
|
7
|
+
* @returns A generator that yields numbers between the specified range.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const numbers = getNumbersBetween(1, 5);
|
|
12
|
+
* for (const num of numbers) {
|
|
13
|
+
* console.log(num); // 1, 2, 3, 4
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
17
|
export declare function getNumbersBetween(minInclusive: number, maxExclusive: number, step?: number): Generator<number>;
|