@nestia/e2e 14.0.1 → 14.0.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.
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * RandomGenerator provides a collection of functions for generating random data
5
5
  * including strings, names, content, dates, and array sampling. All functions
6
- * are designed to be deterministic within a single execution but produce varied
7
- * output across different runs, making them ideal for testing scenarios.
6
+ * draw from `Math.random()`, so they are not seeded and produce varied output
7
+ * across executions, which suits testing scenarios that need arbitrary data.
8
8
  *
9
9
  * The namespace includes specialized generators for:
10
10
  *
@@ -13,21 +13,33 @@
13
13
  * - Date ranges and time-based data
14
14
  * - Array sampling and element selection
15
15
  *
16
+ * Processing cost: Text construction scales with generated characters, sparse
17
+ * sampling with selected positions, and fixed-size draws use constant work. The
18
+ * namespace retains only fixed character tables; each generator owns its
19
+ * invocation-local result.
20
+ *
16
21
  * @author Jeongho Nam - https://github.com/samchon
17
22
  * @example
18
- * ```typescript
19
23
  * // Generate test user data
20
24
  * const testUser = {
21
25
  * id: RandomGenerator.alphaNumeric(8),
22
26
  * name: RandomGenerator.name(),
23
- * bio: RandomGenerator.paragraph({ sentences: 3, wordMin: 5, wordMax: 10 }),
27
+ * bio: RandomGenerator.paragraph({
28
+ * sentences: 3,
29
+ * wordMin: 5,
30
+ * wordMax: 10,
31
+ * }),
24
32
  * phone: RandomGenerator.mobile(),
25
- * createdAt: RandomGenerator.date(new Date(), 1000 * 60 * 60 * 24 * 30) // 30 days
33
+ * createdAt: RandomGenerator.date(new Date(), 1000 * 60 * 60 * 24 * 30), // 30 days
26
34
  * };
27
35
  *
28
36
  * // Sample data for testing
29
37
  * const testSample = RandomGenerator.sample(allUsers, 5);
30
- * ```;
38
+ *
39
+ * @evidence contracts/common.md#principled-implementation The private integer helper partitions Math.random output into equally sized bins for finite ordered integer bounds, and generators compose text, dates and selections from those draws. Math.random is approximately uniform and not cryptographically secure; exact probability guarantees do not follow from its finite precision.
40
+ * @evidence contracts/common.md#clear-and-simple-design Small generators build on each other (`name` on `paragraph`, `content` on `paragraph`, both on `alphabets`), so one integer helper decides the randomness.
41
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The data are drawn, not taken from a fixed list of test values.
42
+ * @evidence contracts/common.md#meaningful-documentation The namespace prose says the functions are not seeded and lists the kinds of data, with an example.
31
43
  */
32
44
  export namespace RandomGenerator {
33
45
  /** Character set containing lowercase alphabetical characters a-z */
@@ -48,10 +60,12 @@ export namespace RandomGenerator {
48
60
  * appear multiple times. Useful for generating random identifiers, test
49
61
  * names, or placeholder text.
50
62
  *
63
+ * Processing cost: Constructing L characters costs O(L) time and
64
+ * intermediate/result space.
65
+ *
51
66
  * @example
52
- * ```typescript
53
- * RandomGenerator.alphabets(5); // e.g. "kxqpw"
54
- * RandomGenerator.alphabets(3); // e.g. "mzr"
67
+ * RandomGenerator.alphabets(5); // e.g. "kxqpw"
68
+ * RandomGenerator.alphabets(3); // e.g. "mzr"
55
69
  * RandomGenerator.alphabets(10); // e.g. "qwertasdfg"
56
70
  *
57
71
  * // Generate random CSS class names
@@ -59,10 +73,13 @@ export namespace RandomGenerator {
59
73
  *
60
74
  * // Create random variable names for testing
61
75
  * const varName = RandomGenerator.alphabets(8);
62
- * ```
63
76
  *
64
77
  * @param length - The desired length of the generated alphabetic string
65
78
  * @returns A string containing only lowercase letters of the specified length
79
+ * @evidence contracts/common.md#principled-implementation Each requested character maps a Math.random draw to one of 26 lowercase letters. Repeats are possible, and distribution inherits Math.random rather than guaranteeing exact uniformity.
80
+ * @evidence contracts/common.md#clear-and-simple-design One expression over the shared integer helper.
81
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The characters are drawn, not enumerated from a list of known values.
82
+ * @evidence contracts/common.md#meaningful-documentation The comment states the character set, the independence of the draws, and examples.
66
83
  */
67
84
  export const alphabets = (length: number): string =>
68
85
  new Array(length)
@@ -79,9 +96,11 @@ export namespace RandomGenerator {
79
96
  * character set. Ideal for generating random IDs, tokens, passwords, or
80
97
  * unique identifiers that need both numeric and alphabetic characters.
81
98
  *
99
+ * Processing cost: Constructing L characters costs O(L) time and
100
+ * intermediate/result space.
101
+ *
82
102
  * @example
83
- * ```typescript
84
- * RandomGenerator.alphaNumeric(8); // e.g. "a1b2c3d4"
103
+ * RandomGenerator.alphaNumeric(8); // e.g. "a1b2c3d4"
85
104
  * RandomGenerator.alphaNumeric(12); // e.g. "x9y8z7w6v5u4"
86
105
  *
87
106
  * // Generate random API keys
@@ -92,11 +111,14 @@ export namespace RandomGenerator {
92
111
  *
93
112
  * // Generate test database IDs
94
113
  * const testId = RandomGenerator.alphaNumeric(10);
95
- * ```
96
114
  *
97
115
  * @param length - The desired length of the generated alphanumeric string
98
116
  * @returns A string containing digits and lowercase letters of the specified
99
117
  * length
118
+ * @evidence contracts/common.md#principled-implementation Each character maps a Math.random draw to one of ten digits and 26 lowercase letters. Repeated characters are allowed and exact uniformity is not guaranteed.
119
+ * @evidence contracts/common.md#clear-and-simple-design One expression over the shared integer helper.
120
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The characters are drawn, not enumerated from a list of known values.
121
+ * @evidence contracts/common.md#meaningful-documentation The comment states the character set and gives examples.
100
122
  */
101
123
  export const alphaNumeric = (length: number): string =>
102
124
  new Array(length)
@@ -112,26 +134,31 @@ export namespace RandomGenerator {
112
134
  * Each word is between 3-7 characters by default, creating realistic-looking
113
135
  * names.
114
136
  *
137
+ * Processing cost: The delegated paragraph work scales with the requested
138
+ * word count and total generated characters.
139
+ *
115
140
  * @example
116
- * ```typescript
117
- * RandomGenerator.name(); // e.g. "lorem ipsum" (2-3 words)
118
- * RandomGenerator.name(1); // e.g. "dolor" (single word)
119
- * RandomGenerator.name(3); // e.g. "sit amet consectetur" (3 words)
141
+ * RandomGenerator.name(); // e.g. "lorem ipsum" (2-3 words)
142
+ * RandomGenerator.name(1); // e.g. "dolor" (single word)
143
+ * RandomGenerator.name(3); // e.g. "sit amet consectetur" (3 words)
120
144
  *
121
145
  * // Generate test user names
122
146
  * const users = Array.from({ length: 10 }, () => ({
123
- * id: RandomGenerator.alphaNumeric(8),
124
- * name: RandomGenerator.name(),
125
- * email: `${RandomGenerator.name(1)}@test.com`
147
+ * id: RandomGenerator.alphaNumeric(8),
148
+ * name: RandomGenerator.name(),
149
+ * email: `${RandomGenerator.name(1)}@test.com`,
126
150
  * }));
127
151
  *
128
152
  * // Create random author names for blog posts
129
153
  * const authorName = RandomGenerator.name();
130
- * ```
131
154
  *
132
155
  * @param length - Number of words in the name (default: random between 2-3)
133
156
  * @returns A space-separated string of random words (each 3-7 chars by
134
157
  * default)
158
+ * @evidence contracts/common.md#principled-implementation A name is a short paragraph of two or three lowercase words by default, so it reads as a person or entity name without an external name list.
159
+ * @evidence contracts/common.md#clear-and-simple-design A one-line delegation to `paragraph` with the sentence count as the word count.
160
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The words are drawn letters, not a fixed list of names.
161
+ * @evidence contracts/common.md#meaningful-documentation The comment states the default length and gives examples.
135
162
  */
136
163
  export const name = (length: number = randint(2, 3)): string =>
137
164
  paragraph({
@@ -146,13 +173,16 @@ export namespace RandomGenerator {
146
173
  * joined with spaces. Accepts an optional configuration object for fine-tuned
147
174
  * control over the paragraph structure.
148
175
  *
176
+ * Processing cost: Each requested word is generated once; time and temporary
177
+ * space scale with the total generated character count and the number of
178
+ * words.
179
+ *
149
180
  * @example
150
- * ```typescript
151
181
  * // Generate with defaults (random 2-5 words, 3-7 characters each)
152
- * RandomGenerator.paragraph(); // e.g. "lorem ipsum dolor"
182
+ * RandomGenerator.paragraph(); // e.g. "lorem ipsum dolor"
153
183
  *
154
184
  * // Specific number of sentences
155
- * RandomGenerator.paragraph({ sentences: 5 }); // "lorem ipsum dolor sit amet"
185
+ * RandomGenerator.paragraph({ sentences: 5 }); // "lorem ipsum dolor sit amet"
156
186
  *
157
187
  * // Custom word length ranges
158
188
  * RandomGenerator.paragraph({ sentences: 4, wordMin: 2, wordMax: 5 });
@@ -162,20 +192,23 @@ export namespace RandomGenerator {
162
192
  * const description = RandomGenerator.paragraph({
163
193
  * sentences: 8,
164
194
  * wordMin: 4,
165
- * wordMax: 8
195
+ * wordMax: 8,
166
196
  * });
167
197
  *
168
198
  * // Create test content for forms
169
199
  * const placeholder = RandomGenerator.paragraph({
170
200
  * sentences: 3,
171
201
  * wordMin: 5,
172
- * wordMax: 10
202
+ * wordMax: 10,
173
203
  * });
174
- * ```;
175
204
  *
176
205
  * @param props - Optional configuration object with sentences count and word
177
206
  * length ranges
178
207
  * @returns A string containing the generated paragraph
208
+ * @evidence contracts/common.md#principled-implementation A paragraph is a number of words joined by spaces, each word a random lowercase string of a length between the minimum and maximum, with the defaults of two to five words of three to seven letters.
209
+ * @evidence contracts/common.md#clear-and-simple-design One expression over `alphabets`, with three optional numbers.
210
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The words are drawn letters, not a fixed text.
211
+ * @evidence contracts/common.md#meaningful-documentation The comment states the parameters and their defaults, with examples.
179
212
  */
180
213
  export const paragraph = (
181
214
  props?: Partial<{
@@ -198,8 +231,10 @@ export namespace RandomGenerator {
198
231
  * character lengths. Ideal for generating realistic-looking text content for
199
232
  * testing.
200
233
  *
234
+ * Processing cost: Every paragraph is generated once; time and temporary
235
+ * space scale with the total output characters and paragraph/word counts.
236
+ *
201
237
  * @example
202
- * ```typescript
203
238
  * // Generate with all defaults
204
239
  * const article = RandomGenerator.content();
205
240
  *
@@ -209,7 +244,7 @@ export namespace RandomGenerator {
209
244
  * sentenceMin: 15,
210
245
  * sentenceMax: 25,
211
246
  * wordMin: 4,
212
- * wordMax: 8
247
+ * wordMax: 8,
213
248
  * });
214
249
  *
215
250
  * // Short content with brief sentences
@@ -218,7 +253,7 @@ export namespace RandomGenerator {
218
253
  * sentenceMin: 5,
219
254
  * sentenceMax: 8,
220
255
  * wordMin: 2,
221
- * wordMax: 4
256
+ * wordMax: 4,
222
257
  * });
223
258
  *
224
259
  * // Generate blog post content
@@ -229,9 +264,9 @@ export namespace RandomGenerator {
229
264
  * sentenceMin: 10,
230
265
  * sentenceMax: 20,
231
266
  * wordMin: 3,
232
- * wordMax: 7
267
+ * wordMax: 7,
233
268
  * }),
234
- * summary: RandomGenerator.paragraph({ sentences: 2 })
269
+ * summary: RandomGenerator.paragraph({ sentences: 2 }),
235
270
  * };
236
271
  *
237
272
  * // Create test data for CMS
@@ -240,14 +275,17 @@ export namespace RandomGenerator {
240
275
  * content: RandomGenerator.content({
241
276
  * paragraphs: randint(2, 6),
242
277
  * sentenceMin: 8,
243
- * sentenceMax: 15
244
- * })
278
+ * sentenceMax: 15,
279
+ * }),
245
280
  * }));
246
- * ```;
247
281
  *
248
282
  * @param props - Optional configuration object with paragraph, sentence, and
249
283
  * word parameters
250
284
  * @returns A string containing the generated multi-paragraph content
285
+ * @evidence contracts/common.md#principled-implementation The content has three to eight paragraphs by default, each containing ten to forty space-separated words of one to seven lowercase letters, joined by blank lines. The legacy sentence-named options count words, not grammatical sentences.
286
+ * @evidence contracts/common.md#clear-and-simple-design One expression over `paragraph`, with five optional numbers.
287
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The text is drawn, not a fixed corpus.
288
+ * @evidence contracts/common.md#meaningful-documentation The comment states the parameters and their defaults, with examples.
251
289
  */
252
290
  export const content = (
253
291
  props?: Partial<{
@@ -280,13 +318,15 @@ export namespace RandomGenerator {
280
318
  * Automatically trims whitespace from the beginning and end of the result.
281
319
  * Useful for creating excerpts, search terms, or partial content samples.
282
320
  *
321
+ * Processing cost: Two draws choose the slice bounds; substring and trimming
322
+ * cost at most O(N) in the input length.
323
+ *
283
324
  * @example
284
- * ```typescript
285
325
  * const text = "The quick brown fox jumps over the lazy dog";
286
326
  *
287
- * RandomGenerator.substring(text); // e.g. "quick brown fox"
288
- * RandomGenerator.substring(text); // e.g. "jumps over"
289
- * RandomGenerator.substring(text); // e.g. "fox jumps over the lazy"
327
+ * RandomGenerator.substring(text); // e.g. "quick brown fox"
328
+ * RandomGenerator.substring(text); // e.g. "jumps over"
329
+ * RandomGenerator.substring(text); // e.g. "fox jumps over the lazy"
290
330
  *
291
331
  * // Generate search terms from content
292
332
  * const searchQuery = RandomGenerator.substring(articleContent);
@@ -299,10 +339,13 @@ export namespace RandomGenerator {
299
339
  *
300
340
  * // Create random selections for highlight testing
301
341
  * const selectedText = RandomGenerator.substring(documentContent);
302
- * ```;
303
342
  *
304
343
  * @param content - The source string to extract a substring from
305
344
  * @returns A trimmed substring of the original content
345
+ * @evidence contracts/common.md#principled-implementation For nonempty input, the first index is a valid position and the last is strictly later up to the length. Trimming can empty a whitespace slice. Empty input also returns an empty string; substring clamps its drawn bounds to the empty input.
346
+ * @evidence contracts/common.md#clear-and-simple-design Two draws and one slice.
347
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The slice bounds are drawn, not fixed.
348
+ * @evidence contracts/common.md#meaningful-documentation The comment states that the slice is random and trimmed, with examples.
306
349
  */
307
350
  export const substring = (content: string): string => {
308
351
  const first: number = randint(0, content.length - 1);
@@ -320,31 +363,39 @@ export namespace RandomGenerator {
320
363
  * if necessary. Commonly used for generating Korean mobile phone numbers or
321
364
  * similar formats.
322
365
  *
366
+ * Processing cost: Two bounded integer draws and fixed-width decimal
367
+ * formatting use constant work; joining the caller prefix costs O(P) output
368
+ * time and space for P prefix characters.
369
+ *
323
370
  * @example
324
- * ```typescript
325
- * RandomGenerator.mobile(); // e.g. "0103341234" or "01012345678"
326
- * RandomGenerator.mobile("011"); // e.g. "0119876543" or "01112345678"
327
- * RandomGenerator.mobile("+82"); // e.g. "+823341234" or "+8212345678"
371
+ * RandomGenerator.mobile(); // e.g. "0103341234" or "01012345678"
372
+ * RandomGenerator.mobile("011"); // e.g. "0119876543" or "01112345678"
373
+ * RandomGenerator.mobile("+82"); // e.g. "+823341234" or "+8212345678"
328
374
  *
329
375
  * // Generate test user phone numbers
330
376
  * const testUsers = Array.from({ length: 100 }, () => ({
331
377
  * name: RandomGenerator.name(),
332
378
  * phone: RandomGenerator.mobile(),
333
- * altPhone: RandomGenerator.mobile("011")
379
+ * altPhone: RandomGenerator.mobile("011"),
334
380
  * }));
335
381
  *
336
382
  * // Create international phone numbers
337
383
  * const internationalPhone = RandomGenerator.mobile("+821");
338
384
  *
339
385
  * // Generate contact list for testing
340
- * const contacts = ["010", "011", "016", "017", "018", "019"].map(prefix => ({
341
- * carrier: prefix,
342
- * number: RandomGenerator.mobile(prefix)
343
- * }));
344
- * ```;
386
+ * const contacts = ["010", "011", "016", "017", "018", "019"].map(
387
+ * (prefix) => ({
388
+ * carrier: prefix,
389
+ * number: RandomGenerator.mobile(prefix),
390
+ * }),
391
+ * );
345
392
  *
346
393
  * @param prefix - The prefix string for the phone number (default: "010")
347
394
  * @returns A formatted mobile phone number string
395
+ * @evidence contracts/common.md#principled-implementation A mobile number is the prefix, a middle group of three digits when the drawn value is below 1,000 and four digits otherwise, and a four-digit group, all zero-padded, which matches the 010-XXX-XXXX and 010-XXXX-XXXX Korean formats the default prefix implies.
396
+ * @evidence contracts/common.md#clear-and-simple-design One expression with two draws.
397
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The prefix is a parameter whose default is the documented Korean one, and the digits are drawn.
398
+ * @evidence contracts/common.md#meaningful-documentation The comment states the format, the default prefix, and examples.
348
399
  */
349
400
  export const mobile = (prefix: string = "010"): string =>
350
401
  [
@@ -364,8 +415,10 @@ export namespace RandomGenerator {
364
415
  * date. Useful for generating timestamps, creation dates, or scheduling test
365
416
  * data.
366
417
  *
418
+ * Processing cost: One integer draw and one Date construction use constant
419
+ * work.
420
+ *
367
421
  * @example
368
- * ```typescript
369
422
  * const now = new Date();
370
423
  * const oneDay = 24 * 60 * 60 * 1000;
371
424
  * const oneMonth = 30 * oneDay;
@@ -379,40 +432,52 @@ export namespace RandomGenerator {
379
432
  *
380
433
  * // Generate random creation dates for test data
381
434
  * const startOfYear = new Date(2024, 0, 1);
382
- * const endOfYear = new Date(2024, 11, 31).getTime() - startOfYear.getTime();
383
- * const randomCreationDate = RandomGenerator.date(startOfYear, endOfYear);
435
+ * const endOfYear =
436
+ * new Date(2024, 11, 31).getTime() - startOfYear.getTime();
437
+ * const randomCreationDate = RandomGenerator.date(
438
+ * startOfYear,
439
+ * endOfYear,
440
+ * );
384
441
  *
385
442
  * // Create test events with random timestamps
386
443
  * const events = Array.from({ length: 50 }, () => ({
387
444
  * id: RandomGenerator.alphaNumeric(8),
388
445
  * title: RandomGenerator.name(2),
389
446
  * createdAt: RandomGenerator.date(new Date(), oneMonth),
390
- * scheduledFor: RandomGenerator.date(new Date(), oneMonth * 3)
447
+ * scheduledFor: RandomGenerator.date(new Date(), oneMonth * 3),
391
448
  * }));
392
- * ```;
393
449
  *
394
450
  * @param from - The starting date for the random range
395
451
  * @param range - The range in milliseconds from the starting date
396
452
  * @returns A random date within the specified range
453
+ * @evidence contracts/common.md#principled-implementation For a valid start date and finite nonnegative integer range whose endpoint is Date-representable, the result adds an integer offset from zero through range. Unsupported invalid dates or bounds are not validated and can produce an Invalid Date or an out-of-range result.
454
+ * @evidence contracts/common.md#clear-and-simple-design One expression over the shared integer helper.
455
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The offset is drawn, not fixed.
456
+ * @evidence contracts/common.md#meaningful-documentation The comment states the interval and the unit of the range, with examples.
397
457
  */
398
458
  export const date = (from: Date, range: number): Date =>
399
459
  new Date(from.getTime() + randint(0, range));
400
460
 
401
461
  /**
402
- * Randomly samples a specified number of unique elements from an array.
462
+ * Randomly samples a specified number of positions from an array.
463
+ *
464
+ * Selects input positions without replacement. Equal values at different
465
+ * positions remain separate candidates and can both appear in the result. The
466
+ * sample size is automatically capped at the array length to prevent errors.
467
+ * Uses a partial Fisher-Yates shuffle over a sparse index map, so every draw
468
+ * is final and the cost grows with the sample size, not with the array
469
+ * length. Ideal for creating test datasets or selecting random subsets for
470
+ * validation.
403
471
  *
404
- * Selects random elements from the input array without replacement, ensuring
405
- * all returned elements are unique. The sample size is automatically capped
406
- * at the array length to prevent errors. Uses a Set-based approach to
407
- * guarantee uniqueness of selected indices. Ideal for creating test datasets
408
- * or selecting random subsets for validation.
472
+ * Processing cost: For K selected positions, sparse partial Fisher-Yates uses
473
+ * K map lookups/updates and K output entries: expected O(K) time and O(K)
474
+ * space, without copying the N-element input.
409
475
  *
410
476
  * @example
411
- * ```typescript
412
477
  * const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
413
478
  *
414
- * RandomGenerator.sample(numbers, 3); // e.g. [2, 7, 9]
415
- * RandomGenerator.sample(numbers, 5); // e.g. [1, 4, 6, 8, 10]
479
+ * RandomGenerator.sample(numbers, 3); // e.g. [2, 7, 9]
480
+ * RandomGenerator.sample(numbers, 5); // e.g. [1, 4, 6, 8, 10]
416
481
  * RandomGenerator.sample(numbers, 15); // returns all 10 elements (capped at array length)
417
482
  *
418
483
  * // Sample users for testing
@@ -427,62 +492,80 @@ export namespace RandomGenerator {
427
492
  *
428
493
  * // Random A/B testing groups
429
494
  * const groupA = RandomGenerator.sample(allParticipants, 50);
430
- * const remaining = allParticipants.filter(p => !groupA.includes(p));
495
+ * const remaining = allParticipants.filter((p) => !groupA.includes(p));
431
496
  * const groupB = RandomGenerator.sample(remaining, 50);
432
- * ```;
433
497
  *
434
498
  * @param array - The source array to sample from
435
499
  * @param count - The number of elements to sample
436
500
  * @returns An array containing the randomly selected elements
501
+ * @evidence contracts/common.md#principled-implementation A partial Fisher-Yates shuffle over a sparse map of moved positions draws each result element from the not-yet-chosen positions uniformly, so the sample is without replacement, approximately uniform under Math.random, and its cost grows with the sample size; the count is capped at the array length, a positive fractional count rounds upward through the loop condition, and a count that is zero, negative, or NaN yields an empty array.
502
+ * @evidence contracts/common.md#clear-and-simple-design One loop with one map, and no rejection of repeated draws.
503
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The array is not mutated or copied, and no element is favored or excluded.
504
+ * @evidence contracts/common.md#meaningful-documentation The comment states without-replacement sampling, the cap, and the algorithm, with examples.
437
505
  */
438
506
  export const sample = <T>(array: T[], count: number): T[] => {
439
507
  count = Math.min(count, array.length);
440
- const indexes: Set<number> = new Set();
441
- while (indexes.size < count) indexes.add(randint(0, array.length - 1));
442
- return Array.from(indexes).map((i) => array[i]!);
508
+ // `moved` holds the positions whose element differs from the identity, so
509
+ // the shuffle needs no copy of the array
510
+ const moved: Map<number, number> = new Map();
511
+ const output: T[] = [];
512
+ for (let i: number = 0; i < count; ++i) {
513
+ const j: number = randint(i, array.length - 1);
514
+ output.push(array[moved.get(j) ?? j]!);
515
+ moved.set(j, moved.get(i) ?? i);
516
+ }
517
+ return output;
443
518
  };
444
519
 
445
520
  /**
446
521
  * Randomly selects a single element from an array.
447
522
  *
448
- * Chooses one element at random from the provided array using uniform
449
- * distribution. Each element has an equal probability of being selected. This
450
- * is a convenience function equivalent to sampling with a count of 1, but
451
- * returns the element directly rather than an array containing one element.
523
+ * Chooses one element at random from the provided array using approximately
524
+ * uniform distribution inherited from Math.random. This is a convenience
525
+ * function equivalent to sampling with a count of 1, but returns the element
526
+ * directly rather than an array containing one element.
527
+ *
528
+ * Processing cost: One bounded draw and one indexed lookup use constant work.
452
529
  *
453
530
  * @example
454
- * ```typescript
455
- * const colors = ['red', 'blue', 'green', 'yellow', 'purple'];
456
- * const fruits = ['apple', 'banana', 'orange', 'grape', 'kiwi'];
531
+ * const colors = ["red", "blue", "green", "yellow", "purple"];
532
+ * const fruits = ["apple", "banana", "orange", "grape", "kiwi"];
457
533
  *
458
- * RandomGenerator.pick(colors); // e.g. "blue"
459
- * RandomGenerator.pick(fruits); // e.g. "apple"
534
+ * RandomGenerator.pick(colors); // e.g. "blue"
535
+ * RandomGenerator.pick(fruits); // e.g. "apple"
460
536
  *
461
537
  * // Select random configuration options
462
- * const randomTheme = RandomGenerator.pick(['light', 'dark', 'auto']);
463
- * const randomLocale = RandomGenerator.pick(['en', 'ko', 'ja', 'zh']);
538
+ * const randomTheme = RandomGenerator.pick(["light", "dark", "auto"]);
539
+ * const randomLocale = RandomGenerator.pick(["en", "ko", "ja", "zh"]);
464
540
  *
465
541
  * // Choose random test scenarios
466
542
  * const testScenario = RandomGenerator.pick([
467
- * 'happy_path',
468
- * 'edge_case',
469
- * 'error_condition',
470
- * 'boundary_test'
543
+ * "happy_path",
544
+ * "edge_case",
545
+ * "error_condition",
546
+ * "boundary_test",
471
547
  * ]);
472
548
  *
473
549
  * // Random user role assignment
474
- * const userRole = RandomGenerator.pick(['admin', 'user', 'moderator']);
550
+ * const userRole = RandomGenerator.pick(["admin", "user", "moderator"]);
475
551
  *
476
552
  * // Select random API endpoints for testing
477
- * const endpoints = ['/users', '/posts', '/comments', '/categories'];
553
+ * const endpoints = ["/users", "/posts", "/comments", "/categories"];
478
554
  * const randomEndpoint = RandomGenerator.pick(endpoints);
479
- * ```;
480
555
  *
481
556
  * @param array - The source array to pick an element from
482
557
  * @returns A randomly selected element from the array
558
+ * @throws RangeError when the array is empty
559
+ * @evidence contracts/common.md#principled-implementation One index maps a Math.random draw to a valid position, giving approximately uniform selection; an empty array has no element to return, so it raises a `RangeError` instead of returning `undefined` under the element type.
560
+ * @evidence contracts/common.md#clear-and-simple-design One guard and one draw.
561
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The guard states the contract for the empty array and no default element is invented.
562
+ * @evidence contracts/common.md#meaningful-documentation The comment states the approximately uniform selection, the relation to sampling one element, and the `RangeError` for an empty array, with examples.
483
563
  */
484
- export const pick = <T>(array: readonly T[]): T =>
485
- array[randint(0, array.length - 1)]!;
564
+ export const pick = <T>(array: readonly T[]): T => {
565
+ if (array.length === 0)
566
+ throw new RangeError("RandomGenerator.pick(): the array is empty.");
567
+ return array[randint(0, array.length - 1)]!;
568
+ };
486
569
  }
487
570
 
488
571
  /** @internal */