@nestia/e2e 14.0.0 → 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.
@@ -22,27 +22,40 @@
22
22
  * - Return > 0 if first element should come after second
23
23
  * - Return 0 if elements are equal
24
24
  *
25
+ * Processing cost: Each comparison extracts both key lists and visits their
26
+ * common prefix until the first difference; date keys additionally parse all
27
+ * supplied strings once per comparison. No sort or repeated scan is performed
28
+ * inside a comparison.
29
+ *
25
30
  * @author Jeongho Nam - https://github.com/samchon
26
31
  * @example
27
- * ```typescript
28
32
  * // Basic usage with single fields
29
- * users.sort(GaffComparator.strings(user => user.name));
30
- * posts.sort(GaffComparator.dates(post => post.createdAt));
31
- * products.sort(GaffComparator.numbers(product => product.price));
33
+ * users.sort(GaffComparator.strings((user) => user.name));
34
+ * posts.sort(GaffComparator.dates((post) => post.createdAt));
35
+ * products.sort(GaffComparator.numbers((product) => product.price));
32
36
  *
33
37
  * // Multi-field sorting with arrays
34
- * users.sort(GaffComparator.strings(user => [user.lastName, user.firstName]));
35
- * events.sort(GaffComparator.dates(event => [event.startDate, event.endDate]));
38
+ * users.sort(
39
+ * GaffComparator.strings((user) => [user.lastName, user.firstName]),
40
+ * );
41
+ * events.sort(
42
+ * GaffComparator.dates((event) => [event.startDate, event.endDate]),
43
+ * );
36
44
  *
37
45
  * // Integration with TestValidator's currying pattern
38
- * const validator = TestValidator.sort("user sorting",
39
- * (sortable) => api.getUsers({ sort: sortable })
40
- * )("name", "email")(
41
- * GaffComparator.strings(user => [user.name, user.email])
42
- * );
46
+ * const validator = TestValidator.sort("user sorting", (sortable) =>
47
+ * api.getUsers({ sort: sortable }),
48
+ * )(
49
+ * "name",
50
+ * "email",
51
+ * )(GaffComparator.strings((user) => [user.name, user.email]));
43
52
  * await validator("+"); // ascending
44
53
  * await validator("-"); // descending
45
- * ```;
54
+ *
55
+ * @evidence contracts/common.md#principled-implementation Each comparator maps both operands to arrays of comparable values and decides by the first position where they differ, then by array length, which is a lexicographic order that satisfies the sort contract for well-defined values; it is used to check server-side sorting from the client.
56
+ * @evidence contracts/common.md#clear-and-simple-design Three comparators share scalar-to-array wrapping. Strings compare each key by collation; dates and numbers use a shared strict mismatch search over their numeric keys.
57
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The ordering rule is generic in the getter, with no field, entity, or locale special case.
58
+ * @evidence contracts/common.md#meaningful-documentation The namespace prose lists the features and the sort contract with an example.
46
59
  */
47
60
  export namespace GaffComparator {
48
61
  /**
@@ -56,52 +69,89 @@ export namespace GaffComparator {
56
69
  *
57
70
  * When comparing arrays, performs lexicographic ordering: compares the first
58
71
  * elements, then the second elements if the first are equal, and so on. This
59
- * enables complex sorting like "sort by last name, then by first name".
72
+ * enables complex sorting like "sort by last name, then by first name". Keys
73
+ * that compare equal under the runtime's collation, including different
74
+ * canonically equivalent Unicode spellings, continue to the next key. If the
75
+ * whole common prefix compares equal, the shorter key list sorts first.
76
+ *
77
+ * Processing cost: Both getters run once and a bounded loop visits only
78
+ * common keys until the first collation difference. Work depends on examined
79
+ * keys and string comparison cost; scalar wrapping adds constant space.
60
80
  *
61
81
  * @example
62
- * ```typescript
63
82
  * interface User {
64
83
  * id: string;
65
84
  * firstName: string;
66
85
  * lastName: string;
67
86
  * email: string;
68
- * status: 'active' | 'inactive';
87
+ * status: "active" | "inactive";
69
88
  * }
70
89
  *
71
90
  * const users: User[] = [
72
- * { id: '1', firstName: 'John', lastName: 'Doe', email: 'john@example.com', status: 'active' },
73
- * { id: '2', firstName: 'Jane', lastName: 'Doe', email: 'jane@example.com', status: 'inactive' },
74
- * { id: '3', firstName: 'Bob', lastName: 'Smith', email: 'bob@example.com', status: 'active' }
91
+ * {
92
+ * id: "1",
93
+ * firstName: "John",
94
+ * lastName: "Doe",
95
+ * email: "john@example.com",
96
+ * status: "active",
97
+ * },
98
+ * {
99
+ * id: "2",
100
+ * firstName: "Jane",
101
+ * lastName: "Doe",
102
+ * email: "jane@example.com",
103
+ * status: "inactive",
104
+ * },
105
+ * {
106
+ * id: "3",
107
+ * firstName: "Bob",
108
+ * lastName: "Smith",
109
+ * email: "bob@example.com",
110
+ * status: "active",
111
+ * },
75
112
  * ];
76
113
  *
77
114
  * // Single field sorting
78
- * users.sort(GaffComparator.strings(user => user.lastName));
115
+ * users.sort(GaffComparator.strings((user) => user.lastName));
79
116
  * // Result: Doe, Doe, Smith
80
117
  *
81
118
  * // Multi-field sorting: last name, then first name
82
- * users.sort(GaffComparator.strings(user => [user.lastName, user.firstName]));
119
+ * users.sort(
120
+ * GaffComparator.strings((user) => [user.lastName, user.firstName]),
121
+ * );
83
122
  * // Result: Doe Jane, Doe John, Smith Bob
84
123
  *
85
124
  * // Status-based sorting
86
- * users.sort(GaffComparator.strings(user => user.status));
125
+ * users.sort(GaffComparator.strings((user) => user.status));
87
126
  * // Result: active users first, then inactive
88
127
  *
89
128
  * // Complex multi-field: status, then last name, then first name
90
- * users.sort(GaffComparator.strings(user => [user.status, user.lastName, user.firstName]));
129
+ * users.sort(
130
+ * GaffComparator.strings((user) => [
131
+ * user.status,
132
+ * user.lastName,
133
+ * user.firstName,
134
+ * ]),
135
+ * );
91
136
  *
92
137
  * // Integration with TestValidator sorting validation
93
- * const sortValidator = TestValidator.sort("user name sorting",
94
- * (sortFields) => userApi.getUsers({ sort: sortFields })
95
- * )("lastName", "firstName")(
96
- * GaffComparator.strings(user => [user.lastName, user.firstName])
97
- * );
138
+ * const sortValidator = TestValidator.sort(
139
+ * "user name sorting",
140
+ * (sortFields) => userApi.getUsers({ sort: sortFields }),
141
+ * )(
142
+ * "lastName",
143
+ * "firstName",
144
+ * )(GaffComparator.strings((user) => [user.lastName, user.firstName]));
98
145
  * await sortValidator("+"); // test ascending order
99
146
  * await sortValidator("-"); // test descending order
100
- * ```;
101
147
  *
102
148
  * @template T - The type of objects being compared
103
149
  * @param getter - Function that extracts string value(s) from input objects
104
150
  * @returns A comparator function suitable for Array.sort()
151
+ * @evidence contracts/common.md#principled-implementation Each common key position is compared once with localeCompare; a nonzero result decides the order, while collation-equal spellings continue to later keys. Only an entirely equal common prefix falls through to array length, establishing lexicographic order under the runtime's actual collation without assuming strict string equality.
152
+ * @evidence contracts/common.md#clear-and-simple-design Shared wrapping accepts scalar or array keys; one bounded loop returns the first nonzero collation result or the length difference.
153
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The comparison is the platform's locale collation, not a hand-written ordering.
154
+ * @evidence contracts/common.md#meaningful-documentation The comment states the locale sensitivity, the multi-value rule, and the arguments, with examples.
105
155
  */
106
156
  export const strings =
107
157
  <T>(getter: (input: T) => string | string[]) =>
@@ -109,8 +159,11 @@ export namespace GaffComparator {
109
159
  const a: string[] = wrap(getter(x));
110
160
  const b: string[] = wrap(getter(y));
111
161
 
112
- const idx: number = a.findIndex((v, i) => v !== b[i]);
113
- return idx !== -1 ? compare(a[idx]!, b[idx]!) : a.length - b.length;
162
+ for (let index = 0; index < Math.min(a.length, b.length); ++index) {
163
+ const result: number = compare(a[index]!, b[index]!);
164
+ if (result !== 0) return result;
165
+ }
166
+ return a.length - b.length;
114
167
  };
115
168
 
116
169
  /**
@@ -127,8 +180,11 @@ export namespace GaffComparator {
127
180
  * The comparison is performed on millisecond timestamps for precise
128
181
  * ordering.
129
182
  *
183
+ * Processing cost: Both getters run once, each returned string is parsed
184
+ * once, and the bounded mismatch search visits at most the common key count.
185
+ * Numeric key arrays require space proportional to the two lists.
186
+ *
130
187
  * @example
131
- * ```typescript
132
188
  * interface Event {
133
189
  * id: string;
134
190
  * title: string;
@@ -140,53 +196,59 @@ export namespace GaffComparator {
140
196
  *
141
197
  * const events: Event[] = [
142
198
  * {
143
- * id: '1',
144
- * title: 'Conference',
145
- * startDate: '2024-03-15T09:00:00Z',
146
- * endDate: '2024-03-15T17:00:00Z',
147
- * createdAt: '2024-01-10T10:00:00Z',
148
- * updatedAt: '2024-02-01T15:30:00Z'
199
+ * id: "1",
200
+ * title: "Conference",
201
+ * startDate: "2024-03-15T09:00:00Z",
202
+ * endDate: "2024-03-15T17:00:00Z",
203
+ * createdAt: "2024-01-10T10:00:00Z",
204
+ * updatedAt: "2024-02-01T15:30:00Z",
149
205
  * },
150
206
  * {
151
- * id: '2',
152
- * title: 'Workshop',
153
- * startDate: '2024-03-10T14:00:00Z',
154
- * endDate: '2024-03-10T16:00:00Z',
155
- * createdAt: '2024-01-15T11:00:00Z',
156
- * updatedAt: '2024-01-20T09:15:00Z'
157
- * }
207
+ * id: "2",
208
+ * title: "Workshop",
209
+ * startDate: "2024-03-10T14:00:00Z",
210
+ * endDate: "2024-03-10T16:00:00Z",
211
+ * createdAt: "2024-01-15T11:00:00Z",
212
+ * updatedAt: "2024-01-20T09:15:00Z",
213
+ * },
158
214
  * ];
159
215
  *
160
216
  * // Sort by start date (chronological order)
161
- * events.sort(GaffComparator.dates(event => event.startDate));
217
+ * events.sort(GaffComparator.dates((event) => event.startDate));
162
218
  *
163
219
  * // Sort by creation date (oldest first)
164
- * events.sort(GaffComparator.dates(event => event.createdAt));
220
+ * events.sort(GaffComparator.dates((event) => event.createdAt));
165
221
  *
166
222
  * // Multi-field: start date, then end date
167
- * events.sort(GaffComparator.dates(event => [event.startDate, event.endDate]));
223
+ * events.sort(
224
+ * GaffComparator.dates((event) => [event.startDate, event.endDate]),
225
+ * );
168
226
  *
169
227
  * // Sort by modification history: created date, then updated date
170
- * events.sort(GaffComparator.dates(event => [event.createdAt, event.updatedAt]));
228
+ * events.sort(
229
+ * GaffComparator.dates((event) => [event.createdAt, event.updatedAt]),
230
+ * );
171
231
  *
172
232
  * // Validate API date sorting with TestValidator
173
- * const dateValidator = TestValidator.sort("event chronological sorting",
174
- * (sortFields) => eventApi.getEvents({ sort: sortFields })
175
- * )("startDate")(
176
- * GaffComparator.dates(event => event.startDate)
177
- * );
233
+ * const dateValidator = TestValidator.sort(
234
+ * "event chronological sorting",
235
+ * (sortFields) => eventApi.getEvents({ sort: sortFields }),
236
+ * )("startDate")(GaffComparator.dates((event) => event.startDate));
178
237
  * await dateValidator("+", true); // ascending with trace logging
179
238
  *
180
239
  * // Test complex date-based sorting
181
- * const sortByEventSchedule = GaffComparator.dates(event => [
240
+ * const sortByEventSchedule = GaffComparator.dates((event) => [
182
241
  * event.startDate,
183
- * event.endDate
242
+ * event.endDate,
184
243
  * ]);
185
- * ```;
186
244
  *
187
245
  * @template T - The type of objects being compared
188
246
  * @param getter - Function that extracts date string(s) from input objects
189
247
  * @returns A comparator function suitable for Array.sort()
248
+ * @evidence contracts/common.md#principled-implementation Each value is parsed by the `Date` constructor and compared by its millisecond timestamp, so ISO 8601 and the other formats the constructor accepts order correctly; a string the constructor cannot parse yields NaN, and the comparator then returns NaN, which a sort treats as equality, a limit the caller must respect by giving valid dates.
249
+ * @evidence contracts/common.md#clear-and-simple-design It uses the shared `wrap` and `mismatch`, and adds only the parsing.
250
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Parsing is the standard constructor with no format special cases.
251
+ * @evidence contracts/common.md#meaningful-documentation The comment states the accepted formats and the millisecond comparison, with examples.
190
252
  */
191
253
  export const dates =
192
254
  <T>(getter: (input: T) => string | string[]) =>
@@ -196,7 +258,7 @@ export namespace GaffComparator {
196
258
  const a: number[] = take(x);
197
259
  const b: number[] = take(y);
198
260
 
199
- const idx: number = a.findIndex((v, i) => v !== b[i]);
261
+ const idx: number = mismatch(a, b);
200
262
  return idx !== -1 ? a[idx]! - b[idx]! : a.length - b.length;
201
263
  };
202
264
 
@@ -214,8 +276,11 @@ export namespace GaffComparator {
214
276
  * on. This enables sophisticated sorting like "sort by price ascending, then
215
277
  * by rating descending".
216
278
  *
279
+ * Processing cost: Both getters run once and mismatch visits only the common
280
+ * prefix until the first unequal key. Existing key arrays are reused, and
281
+ * scalar wrapping takes constant auxiliary space.
282
+ *
217
283
  * @example
218
- * ```typescript
219
284
  * interface Product {
220
285
  * id: string;
221
286
  * name: string;
@@ -227,49 +292,90 @@ export namespace GaffComparator {
227
292
  * }
228
293
  *
229
294
  * const products: Product[] = [
230
- * { id: '1', name: 'Laptop', price: 999.99, rating: 4.5, stock: 15, categoryId: 1, salesCount: 150 },
231
- * { id: '2', name: 'Mouse', price: 29.99, rating: 4.2, stock: 50, categoryId: 1, salesCount: 300 },
232
- * { id: '3', name: 'Keyboard', price: 79.99, rating: 4.8, stock: 25, categoryId: 1, salesCount: 200 }
295
+ * {
296
+ * id: "1",
297
+ * name: "Laptop",
298
+ * price: 999.99,
299
+ * rating: 4.5,
300
+ * stock: 15,
301
+ * categoryId: 1,
302
+ * salesCount: 150,
303
+ * },
304
+ * {
305
+ * id: "2",
306
+ * name: "Mouse",
307
+ * price: 29.99,
308
+ * rating: 4.2,
309
+ * stock: 50,
310
+ * categoryId: 1,
311
+ * salesCount: 300,
312
+ * },
313
+ * {
314
+ * id: "3",
315
+ * name: "Keyboard",
316
+ * price: 79.99,
317
+ * rating: 4.8,
318
+ * stock: 25,
319
+ * categoryId: 1,
320
+ * salesCount: 200,
321
+ * },
233
322
  * ];
234
323
  *
235
324
  * // Sort by price (ascending)
236
- * products.sort(GaffComparator.numbers(product => product.price));
325
+ * products.sort(GaffComparator.numbers((product) => product.price));
237
326
  * // Result: Mouse ($29.99), Keyboard ($79.99), Laptop ($999.99)
238
327
  *
239
328
  * // Sort by rating (descending requires negation)
240
- * products.sort(GaffComparator.numbers(product => -product.rating));
329
+ * products.sort(GaffComparator.numbers((product) => -product.rating));
241
330
  * // Result: Keyboard (4.8), Laptop (4.5), Mouse (4.2)
242
331
  *
243
332
  * // Multi-field: category, then price
244
- * products.sort(GaffComparator.numbers(product => [product.categoryId, product.price]));
333
+ * products.sort(
334
+ * GaffComparator.numbers((product) => [
335
+ * product.categoryId,
336
+ * product.price,
337
+ * ]),
338
+ * );
245
339
  *
246
340
  * // Complex business logic: popularity (sales) then rating
247
- * products.sort(GaffComparator.numbers(product => [-product.salesCount, -product.rating]));
341
+ * products.sort(
342
+ * GaffComparator.numbers((product) => [
343
+ * -product.salesCount,
344
+ * -product.rating,
345
+ * ]),
346
+ * );
248
347
  * // Negative values for descending order
249
348
  *
250
349
  * // Sort by inventory priority: low stock first, then by sales
251
- * products.sort(GaffComparator.numbers(product => [product.stock, -product.salesCount]));
350
+ * products.sort(
351
+ * GaffComparator.numbers((product) => [
352
+ * product.stock,
353
+ * -product.salesCount,
354
+ * ]),
355
+ * );
252
356
  *
253
357
  * // Validate API numerical sorting with TestValidator
254
- * const priceValidator = TestValidator.sort("product price sorting",
255
- * (sortFields) => productApi.getProducts({ sort: sortFields })
256
- * )("price")(
257
- * GaffComparator.numbers(product => product.price)
258
- * );
358
+ * const priceValidator = TestValidator.sort(
359
+ * "product price sorting",
360
+ * (sortFields) => productApi.getProducts({ sort: sortFields }),
361
+ * )("price")(GaffComparator.numbers((product) => product.price));
259
362
  * await priceValidator("+"); // test ascending order
260
363
  * await priceValidator("-"); // test descending order
261
364
  *
262
365
  * // Test multi-criteria sorting
263
- * const sortByBusinessValue = GaffComparator.numbers(product => [
264
- * -product.salesCount, // High sales first
265
- * -product.rating, // High rating first
266
- * product.price // Low price first (for tie-breaking)
366
+ * const sortByBusinessValue = GaffComparator.numbers((product) => [
367
+ * -product.salesCount, // High sales first
368
+ * -product.rating, // High rating first
369
+ * product.price, // Low price first (for tie-breaking)
267
370
  * ]);
268
- * ```;
269
371
  *
270
372
  * @template T - The type of objects being compared
271
373
  * @param closure - Function that extracts number value(s) from input objects
272
374
  * @returns A comparator function suitable for Array.sort()
375
+ * @evidence contracts/common.md#principled-implementation The sign of subtraction at the first differing position orders finite numeric values, and a shorter equal prefix sorts first. Subtraction can round or overflow, so the result's magnitude is not an exact-distance guarantee; callers must supply ordered numeric values rather than NaN.
376
+ * @evidence contracts/common.md#clear-and-simple-design It uses the shared `wrap` and `mismatch`, and adds only the subtraction.
377
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The comparison is the arithmetic difference, with no rounding or thresholds.
378
+ * @evidence contracts/common.md#meaningful-documentation The comment states the multi-value rule and the arguments, with examples.
273
379
  */
274
380
  export const numbers =
275
381
  <T>(closure: (input: T) => number | number[]) =>
@@ -277,11 +383,23 @@ export namespace GaffComparator {
277
383
  const a: number[] = wrap(closure(x));
278
384
  const b: number[] = wrap(closure(y));
279
385
 
280
- const idx: number = a.findIndex((v, i) => v !== b[i]);
386
+ const idx: number = mismatch(a, b);
281
387
  return idx !== -1 ? a[idx]! - b[idx]! : a.length - b.length;
282
388
  };
283
389
 
284
390
  const compare = (x: string, y: string) => x.localeCompare(y);
285
391
 
392
+ /**
393
+ * The first index where the two key lists differ within their common length,
394
+ * or -1; beyond it a proper prefix orders before its extension, in both
395
+ * argument orders.
396
+ */
397
+ const mismatch = <K>(a: K[], b: K[]): number => {
398
+ const length: number = Math.min(a.length, b.length);
399
+ for (let index: number = 0; index < length; ++index)
400
+ if (a[index] !== b[index]) return index;
401
+ return -1;
402
+ };
403
+
286
404
  const wrap = <T>(elem: T | T[]): T[] => (Array.isArray(elem) ? elem : [elem]);
287
405
  }
package/src/MapUtil.ts CHANGED
@@ -5,9 +5,21 @@
5
5
  * objects, providing convenient methods for common Map operations like
6
6
  * retrieving values with lazy initialization.
7
7
  *
8
+ * Processing cost: The get-or-create operation uses native Map membership and
9
+ * lookup, and calls the factory only for an absent key; its dominant extra cost
10
+ * is the caller factory.
11
+ *
12
+ * Reuse: Stored values, including promises and undefined, are reused while the
13
+ * supplied Map retains the key. The caller must key every relevant dependency
14
+ * and remove stale or rejected entries; this helper cannot establish
15
+ * external-data freshness or prevent reentrant factory calls.
16
+ *
17
+ * Resource ownership: New values are transferred into the caller-owned Map,
18
+ * whose growth and eviction remain the caller responsibility; the helper
19
+ * acquires no external handle or background task.
20
+ *
8
21
  * @author Jeongho Nam - https://github.com/samchon
9
22
  * @example
10
- * ```typescript
11
23
  * // Create a cache with lazy initialization
12
24
  * const cache = new Map<string, ExpensiveObject>();
13
25
  *
@@ -19,7 +31,11 @@
19
31
  * // Subsequent calls return cached value without re-creating
20
32
  * const sameObj = MapUtil.take(cache, "key1", () => new ExpensiveObject());
21
33
  * console.log(obj === sameObj); // true
22
- * ```;
34
+ *
35
+ * @evidence contracts/common.md#principled-implementation The namespace offers the get-or-create pattern over the native `Map`, whose `has` distinguishes an absent key from a stored `undefined`.
36
+ * @evidence contracts/common.md#clear-and-simple-design One function, without options or state.
37
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It uses only the public Map methods.
38
+ * @evidence contracts/common.md#meaningful-documentation The comment states the lazy initialization pattern with an example.
23
39
  */
24
40
  export namespace MapUtil {
25
41
  /**
@@ -32,8 +48,20 @@ export namespace MapUtil {
32
48
  * returns it. The factory function is only called when the key doesn't exist,
33
49
  * enabling lazy initialization and caching patterns.
34
50
  *
51
+ * Processing cost: One membership test and lookup, or one factory invocation
52
+ * and insertion, avoid a map scan; native Map access is sublinear on average
53
+ * and factory cost belongs to the supplied computation.
54
+ *
55
+ * Reuse: Stored values, including promises and undefined, are reused while
56
+ * the supplied Map retains the key. The caller must key every relevant
57
+ * dependency and remove stale or rejected entries; this helper cannot
58
+ * establish external-data freshness or prevent reentrant factory calls.
59
+ *
60
+ * Resource ownership: New values are transferred into the caller-owned Map,
61
+ * whose growth and eviction remain the caller responsibility; the helper
62
+ * acquires no external handle or background task.
63
+ *
35
64
  * @example
36
- * ```typescript
37
65
  * // Simple caching example
38
66
  * const userCache = new Map<number, User>();
39
67
  *
@@ -48,7 +76,7 @@ export namespace MapUtil {
48
76
  * const dbConfig = MapUtil.take(configs, "database", () => ({
49
77
  * host: "localhost",
50
78
  * port: 5432,
51
- * database: "myapp"
79
+ * database: "myapp",
52
80
  * }));
53
81
  *
54
82
  * // Lazy computation results
@@ -64,9 +92,8 @@ export namespace MapUtil {
64
92
  * const key: [number, number] = [rows, cols];
65
93
  *
66
94
  * const matrix = MapUtil.take(cache, key, () =>
67
- * generateIdentityMatrix(rows, cols)
95
+ * generateIdentityMatrix(rows, cols),
68
96
  * );
69
- * ```;
70
97
  *
71
98
  * @template K - The type of keys in the Map
72
99
  * @template V - The type of values in the Map
@@ -75,6 +102,10 @@ export namespace MapUtil {
75
102
  * @param value - A factory function that creates the value if key doesn't
76
103
  * exist
77
104
  * @returns The existing value if found, or the newly created value
105
+ * @evidence contracts/common.md#principled-implementation The key is looked up with `has`, so an existing value, even `undefined`, is returned without calling the factory; otherwise the factory is called once, its value is stored, and the same value is returned.
106
+ * @evidence contracts/common.md#clear-and-simple-design A single check, one factory call, and one store.
107
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The factory result is stored as returned, with no copying or expiry.
108
+ * @evidence contracts/common.md#meaningful-documentation The comment states when the factory runs and documents the parameters with several examples.
78
109
  */
79
110
  export function take<K, V>(map: Map<K, V>, key: K, value: () => V): V {
80
111
  if (map.has(key)) {