@oscarpalmer/atoms 0.192.0 → 0.193.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/dist/index.d.mts +25 -23
  2. package/dist/index.mjs +30 -31
  3. package/dist/queue.d.mts +25 -23
  4. package/dist/queue.mjs +27 -28
  5. package/package.json +3 -4
  6. package/src/array/difference.ts +0 -73
  7. package/src/array/exists.ts +0 -93
  8. package/src/array/filter.ts +0 -182
  9. package/src/array/find.ts +0 -183
  10. package/src/array/first.ts +0 -196
  11. package/src/array/flatten.ts +0 -20
  12. package/src/array/from.ts +0 -129
  13. package/src/array/get.ts +0 -131
  14. package/src/array/group-by.ts +0 -369
  15. package/src/array/index.ts +0 -26
  16. package/src/array/insert.ts +0 -52
  17. package/src/array/intersection.ts +0 -72
  18. package/src/array/last.ts +0 -194
  19. package/src/array/match.ts +0 -436
  20. package/src/array/move.ts +0 -315
  21. package/src/array/partition.ts +0 -91
  22. package/src/array/push.ts +0 -23
  23. package/src/array/reverse.ts +0 -33
  24. package/src/array/select.ts +0 -260
  25. package/src/array/single.ts +0 -93
  26. package/src/array/slice.ts +0 -335
  27. package/src/array/sort.ts +0 -607
  28. package/src/array/splice.ts +0 -106
  29. package/src/array/swap.ts +0 -322
  30. package/src/array/to-map.ts +0 -371
  31. package/src/array/to-record.ts +0 -351
  32. package/src/array/to-set.ts +0 -80
  33. package/src/array/toggle.ts +0 -79
  34. package/src/array/union.ts +0 -72
  35. package/src/array/unique.ts +0 -67
  36. package/src/array/update.ts +0 -75
  37. package/src/beacon.ts +0 -378
  38. package/src/color/constants.ts +0 -137
  39. package/src/color/index.ts +0 -83
  40. package/src/color/instance.ts +0 -162
  41. package/src/color/misc/alpha.ts +0 -56
  42. package/src/color/misc/format.ts +0 -29
  43. package/src/color/misc/get.ts +0 -210
  44. package/src/color/misc/is.ts +0 -221
  45. package/src/color/misc/state.ts +0 -243
  46. package/src/color/models.ts +0 -389
  47. package/src/color/space/hex.ts +0 -184
  48. package/src/color/space/hsl.ts +0 -174
  49. package/src/color/space/hwb.ts +0 -153
  50. package/src/color/space/rgb.ts +0 -240
  51. package/src/function/assert.ts +0 -174
  52. package/src/function/index.ts +0 -2
  53. package/src/function/limit.ts +0 -83
  54. package/src/function/memoize.ts +0 -202
  55. package/src/function/once.ts +0 -200
  56. package/src/function/retry.ts +0 -175
  57. package/src/function/work.ts +0 -1007
  58. package/src/herald.ts +0 -216
  59. package/src/index.ts +0 -68
  60. package/src/internal/array/callbacks.ts +0 -46
  61. package/src/internal/array/chunk.ts +0 -51
  62. package/src/internal/array/compact.ts +0 -56
  63. package/src/internal/array/find.ts +0 -200
  64. package/src/internal/array/group.ts +0 -42
  65. package/src/internal/array/index-of.ts +0 -182
  66. package/src/internal/array/insert.ts +0 -80
  67. package/src/internal/array/overlap.ts +0 -35
  68. package/src/internal/array/sets.ts +0 -74
  69. package/src/internal/array/shuffle.ts +0 -33
  70. package/src/internal/array/update.ts +0 -52
  71. package/src/internal/function/misc.ts +0 -8
  72. package/src/internal/function/timer.ts +0 -168
  73. package/src/internal/is.ts +0 -244
  74. package/src/internal/math/aggregate.ts +0 -164
  75. package/src/internal/math/misc.ts +0 -28
  76. package/src/internal/number.ts +0 -129
  77. package/src/internal/random.ts +0 -49
  78. package/src/internal/result.ts +0 -92
  79. package/src/internal/sized.ts +0 -25
  80. package/src/internal/string.ts +0 -128
  81. package/src/internal/subscription.ts +0 -264
  82. package/src/internal/value/compare.ts +0 -170
  83. package/src/internal/value/equal.ts +0 -465
  84. package/src/internal/value/get.ts +0 -57
  85. package/src/internal/value/handlers.ts +0 -95
  86. package/src/internal/value/has.ts +0 -118
  87. package/src/internal/value/misc.ts +0 -125
  88. package/src/internal/value/partial.ts +0 -46
  89. package/src/internal/value/set.ts +0 -132
  90. package/src/is.ts +0 -182
  91. package/src/logger.ts +0 -230
  92. package/src/math.ts +0 -281
  93. package/src/models.ts +0 -286
  94. package/src/number.ts +0 -1
  95. package/src/promise/delay.ts +0 -62
  96. package/src/promise/helpers.ts +0 -96
  97. package/src/promise/index.ts +0 -300
  98. package/src/promise/misc.ts +0 -96
  99. package/src/promise/models.ts +0 -195
  100. package/src/promise/timed.ts +0 -91
  101. package/src/query.ts +0 -147
  102. package/src/queue.ts +0 -814
  103. package/src/random.ts +0 -125
  104. package/src/result/index.ts +0 -105
  105. package/src/result/match.ts +0 -124
  106. package/src/result/misc.ts +0 -129
  107. package/src/result/models.ts +0 -83
  108. package/src/result/work/flow.ts +0 -488
  109. package/src/result/work/pipe.ts +0 -496
  110. package/src/sized/map.ts +0 -110
  111. package/src/sized/set.ts +0 -107
  112. package/src/string/case.ts +0 -236
  113. package/src/string/fuzzy.ts +0 -444
  114. package/src/string/index.ts +0 -199
  115. package/src/string/match.ts +0 -80
  116. package/src/string/normalize.ts +0 -205
  117. package/src/string/template.ts +0 -161
  118. package/src/value/clone.ts +0 -319
  119. package/src/value/collection.ts +0 -63
  120. package/src/value/diff.ts +0 -232
  121. package/src/value/freeze.ts +0 -322
  122. package/src/value/handle.ts +0 -3
  123. package/src/value/index.ts +0 -5
  124. package/src/value/merge.ts +0 -278
  125. package/src/value/omit.ts +0 -20
  126. package/src/value/pick.ts +0 -20
  127. package/src/value/shake.ts +0 -45
  128. package/src/value/smush.ts +0 -89
  129. package/src/value/transform.ts +0 -153
  130. package/src/value/unsmush.ts +0 -84
@@ -1,236 +0,0 @@
1
- import {memoize, type Memoized} from '../function/memoize';
2
- import {join, words} from '../internal/string';
3
-
4
- // #region Types
5
-
6
- type Case = 'camel' | 'kebab' | 'pascal' | 'snake';
7
-
8
- type Options = {
9
- capitalizeAny: boolean;
10
- capitalizeFirst: boolean;
11
- type: Case;
12
- };
13
-
14
- // #endregion
15
-
16
- // #region Functions
17
-
18
- /**
19
- * Convert a string to camel case _(thisIsCamelCase)_
20
- *
21
- * @param value String to convert
22
- * @returns Camel-cased string
23
- */
24
- export function camelCase(value: string): string {
25
- return toCase(CASE_CAMEL, value, true, false);
26
- }
27
-
28
- /**
29
- * Capitalize the first letter of a string _(and lowercase the rest)_
30
- *
31
- * @param value String to capitalize
32
- * @returns Capitalized string
33
- */
34
- export function capitalize(value: string): string {
35
- if (typeof value !== 'string' || value.length === 0) {
36
- return '';
37
- }
38
-
39
- memoizedCapitalize ??= memoize(v =>
40
- v.length === 1
41
- ? v.toLocaleUpperCase()
42
- : `${v.charAt(0).toLocaleUpperCase()}${v.slice(1).toLocaleLowerCase()}`,
43
- );
44
-
45
- return memoizedCapitalize.run(value);
46
- }
47
-
48
- /**
49
- * Convert a string to kebab case _(this-is-kebab-case)_
50
- *
51
- * @param value String to convert
52
- * @returns Kebab-cased string
53
- */
54
- export function kebabCase(value: string): string {
55
- return toCase(CASE_KEBAB, value, false, false);
56
- }
57
-
58
- /**
59
- * Convert a string to lower case
60
- *
61
- * @param value String to convert
62
- * @returns Lower-cased string
63
- */
64
- export function lowerCase(value: string): string {
65
- if (typeof value !== 'string') {
66
- return '';
67
- }
68
-
69
- memoizedLowerCase ??= memoize(v => v.toLocaleLowerCase());
70
-
71
- return memoizedLowerCase.run(value);
72
- }
73
-
74
- /**
75
- * Convert a string to pascal case _(ThisIsPascalCase)_
76
- *
77
- * @param value String to convert
78
- * @returns Pascal-cased string
79
- */
80
- export function pascalCase(value: string): string {
81
- return toCase(CASE_PASCAL, value, true, true);
82
- }
83
-
84
- /**
85
- * Convert a string to snake case _(this_is_snake_case)_
86
- *
87
- * @param value String to convert
88
- * @returns Snake-cased string
89
- */
90
- export function snakeCase(value: string): string {
91
- return toCase(CASE_SNAKE, value, false, false);
92
- }
93
-
94
- /**
95
- * Convert a string to title case _(Capitalizing Every Word)_
96
- *
97
- * @param value String to convert
98
- * @returns Title-cased string
99
- */
100
- export function titleCase(value: string): string {
101
- if (typeof value !== 'string' || value.length === 0) {
102
- return '';
103
- }
104
-
105
- memoizedTitleCase ??= memoize(v =>
106
- v.length < 2 ? capitalize(v) : join(words(v).map(capitalize), ' '),
107
- );
108
-
109
- return memoizedTitleCase.run(value);
110
- }
111
-
112
- function toCase(
113
- type: Case,
114
- value: string,
115
- capitalizeAny: boolean,
116
- capitalizeFirst: boolean,
117
- ): string {
118
- caseMemoizers[type] ??= memoize(toCaseCallback.bind({type, capitalizeAny, capitalizeFirst}));
119
-
120
- return caseMemoizers[type].run(value);
121
- }
122
-
123
- function toCaseCallback(this: Options, value: string): string {
124
- if (typeof value !== 'string') {
125
- return '';
126
- }
127
-
128
- if (value.length < 1) {
129
- return value;
130
- }
131
-
132
- const {capitalizeAny, capitalizeFirst, type} = this;
133
-
134
- const parts = words(value);
135
- const partsLength = parts.length;
136
-
137
- const cased: string[] = [];
138
-
139
- for (let partIndex = 0; partIndex < partsLength; partIndex += 1) {
140
- const part = parts[partIndex];
141
-
142
- const acronymParts = part.replace(EXPRESSION_ACRONYM, (full, one, two, three) =>
143
- three === S ? full : `${one}-${two}${three}`,
144
- );
145
-
146
- const camelCaseParts = acronymParts.replace(EXPRESSION_CAMEL_CASE, REPLACEMENT_CAMEL_CASE);
147
-
148
- const items = camelCaseParts.split('-');
149
- const itemsLength = items.length;
150
-
151
- const partResult: string[] = [];
152
-
153
- let itemCount = 0;
154
-
155
- for (let itemIndex = 0; itemIndex < itemsLength; itemIndex += 1) {
156
- const item = items[itemIndex];
157
-
158
- if (item.length === 0) {
159
- continue;
160
- }
161
-
162
- if (!capitalizeAny || (itemCount === 0 && partIndex === 0 && !capitalizeFirst)) {
163
- partResult.push(item.toLocaleLowerCase());
164
- } else {
165
- partResult.push(capitalize(item));
166
- }
167
-
168
- itemCount += 1;
169
- }
170
-
171
- cased.push(join(partResult, delimiters[type]));
172
- }
173
-
174
- return join(cased, delimiters[type]);
175
- }
176
-
177
- /**
178
- * Convert a string to upper case
179
- *
180
- * @param value String to convert
181
- * @returns Upper-cased string
182
- */
183
- export function upperCase(value: string): string {
184
- if (typeof value !== 'string' || value.length === 0) {
185
- return '';
186
- }
187
-
188
- memoizedUpperCase ??= memoize(v => v.toLocaleUpperCase());
189
-
190
- return memoizedUpperCase.run(value);
191
- }
192
-
193
- // #endregion
194
-
195
- // #region Variables
196
-
197
- const CASE_CAMEL: Case = 'camel';
198
-
199
- const CASE_KEBAB: Case = 'kebab';
200
-
201
- const CASE_PASCAL: Case = 'pascal';
202
-
203
- const CASE_SNAKE: Case = 'snake';
204
-
205
- const DELIMTER_EMPTY = '';
206
-
207
- const DELIMITER_HYPHEN = '-';
208
-
209
- const DELIMITER_UNDERSCORE = '_';
210
-
211
- const EXPRESSION_CAMEL_CASE = /(\p{Ll})(\p{Lu})/gu;
212
-
213
- const EXPRESSION_ACRONYM = /(\p{Lu}*)(\p{Lu})(\p{Ll}+)/gu;
214
-
215
- const REPLACEMENT_CAMEL_CASE = '$1-$2';
216
-
217
- const S = 's';
218
-
219
- const caseMemoizers: Partial<Record<string, Memoized<typeof toCaseCallback>>> = {};
220
-
221
- const delimiters: Record<Case, string> = {
222
- [CASE_CAMEL]: DELIMTER_EMPTY,
223
- [CASE_KEBAB]: DELIMITER_HYPHEN,
224
- [CASE_PASCAL]: DELIMTER_EMPTY,
225
- [CASE_SNAKE]: DELIMITER_UNDERSCORE,
226
- };
227
-
228
- let memoizedCapitalize: Memoized<(value: string) => string>;
229
-
230
- let memoizedLowerCase: Memoized<(value: string) => string>;
231
-
232
- let memoizedTitleCase: Memoized<(value: string) => string>;
233
-
234
- let memoizedUpperCase: Memoized<(value: string) => string>;
235
-
236
- // #endregion
@@ -1,444 +0,0 @@
1
- import {isPlainObject} from '../internal/is';
2
- import {getString} from '../internal/string';
3
- import type {PlainObject, RequiredKeys} from '../models';
4
- import {lowerCase} from './case';
5
- import {includes} from './match';
6
-
7
- // #region Types
8
-
9
- /**
10
- * Fuzzy searcher for an array of items
11
- */
12
- export type Fuzzy<Item> = {
13
- /**
14
- * Get items currently being searched through
15
- *
16
- * @returns Original items
17
- */
18
- get items(): Item[];
19
-
20
- /**
21
- * Set new items to search through
22
- *
23
- * @param items New items to search through
24
- */
25
- set items(items: Item[]);
26
-
27
- /**
28
- * Get strings currently being searched through _(the stringified version of `items`)_
29
- *
30
- * @returns Stringified items
31
- */
32
- get strings(): string[];
33
-
34
- /**
35
- * Search for items matching the given value
36
- *
37
- * @param value Value to search for
38
- * @param options Search options
39
- * @returns Search results, with exact matches _(ordered)_ and similar matches _(ordered by relevance)_
40
- */
41
- search(value: string, options?: FuzzyOptions): FuzzyResult<Item>;
42
-
43
- /**
44
- * Search for items matching the given value
45
- *
46
- * @param value Value to search for
47
- * @param limit Maximum number of combined items to return in `exact` and `similar`
48
- * @returns Search results, with exact matches _(ordered)_ and similar matches _(ordered by relevance)_
49
- */
50
- search(value: string, limit: number): FuzzyResult<Item>;
51
- };
52
-
53
- export type FuzzyConfiguration<Item> = {
54
- /**
55
- * Handler to stringify items
56
- *
57
- * - May be a function that takes an item and returns a string, or if items are plain objects, a key of the item to use to grab a string value from
58
- * - Defaults to `getString`
59
- */
60
- handler?: (item: Item) => string;
61
- } & (Item extends PlainObject
62
- ? {
63
- /**
64
- * Key to use to stringify items
65
- *
66
- * _Prioritized over `handler`_
67
- */
68
- key?: keyof Item;
69
- }
70
- : {}) &
71
- FuzzyOptions;
72
-
73
- type FuzzyItem<Item> = {
74
- item: Item;
75
- haystack: string;
76
- };
77
-
78
- export type FuzzyOptions = {
79
- /**
80
- * Maximum number of combined items to return in `exact` and `similar` _(defaults to all matches)_
81
- */
82
- limit?: number;
83
- /**
84
- * Maximum score difference between the best and worst similar matches included in results
85
- *
86
- * - Higher values cast a wider net
87
- * - Defaults to `5`
88
- */
89
- tolerance?: number;
90
- };
91
-
92
- /**
93
- * Search results from a fuzzy search, with exact and similar matches
94
- */
95
- export type FuzzyResult<Item> = {
96
- /**
97
- * Ordered items that exactly match the search value
98
- */
99
- exact: Item[];
100
- /**
101
- * Ordered items that are similar to the search value, ranked by relevance
102
- */
103
- similar: Item[];
104
- };
105
-
106
- /**
107
- * Options for fuzzy searching
108
- */
109
- export type FuzzySearchOptions = FuzzyOptions;
110
-
111
- type FuzzyState<Item> = {
112
- handler: (item: Item) => string;
113
- items: Item[];
114
- limit?: number;
115
- strings: string[];
116
- tolerance: number;
117
- };
118
-
119
- // #endregion
120
-
121
- // #region Functions
122
-
123
- function getHandler<Item>(input: unknown): (item: Item) => string {
124
- if (input == null || input === getString) {
125
- return getString;
126
- }
127
-
128
- switch (typeof input) {
129
- case 'function':
130
- return input as (item: Item) => string;
131
-
132
- case 'string':
133
- return (item: Item) => (item as PlainObject)[input] as string;
134
-
135
- default: {
136
- if (isPlainObject(input)) {
137
- return getHandler(
138
- (input as FuzzyConfiguration<PlainObject>).key ??
139
- (input as FuzzyConfiguration<Item>).handler,
140
- );
141
- }
142
-
143
- throw new TypeError(MESSAGE_HANDLER);
144
- }
145
- }
146
- }
147
-
148
- function getItems<Item>(items: FuzzyItem<Item>[]): Item[] {
149
- return items
150
- .sort((first, second) => first.haystack.localeCompare(second.haystack))
151
- .map(({item}) => item);
152
- }
153
-
154
- function getFuzzyOptions<Item>(
155
- input: unknown,
156
- state?: FuzzyState<Item>,
157
- ): RequiredKeys<FuzzyOptions, 'tolerance'> {
158
- const options: FuzzyOptions = isPlainObject(input) ? input : {};
159
-
160
- const limit = typeof input === 'number' ? input : options.limit;
161
-
162
- if (typeof limit === 'number' && !Number.isNaN(limit) && limit >= 1) {
163
- options.limit = Math.floor(limit);
164
- } else {
165
- options.limit = state?.limit;
166
- }
167
-
168
- options.tolerance = getTolerance(options.tolerance, state);
169
-
170
- return options as RequiredKeys<FuzzyOptions, 'tolerance'>;
171
- }
172
-
173
- function getFuzzyState<Item>(items: Item[], input: unknown): FuzzyState<Item> {
174
- const handler = getHandler(input);
175
- const options = getFuzzyOptions(input);
176
-
177
- return {
178
- handler,
179
- items: items.slice(),
180
- limit: options.limit,
181
- strings: items.map(handler),
182
- tolerance: options.tolerance,
183
- };
184
- }
185
-
186
- function getTolerance<Item>(input: unknown, state?: FuzzyState<Item>): number {
187
- if (typeof input === 'number' && !Number.isNaN(input)) {
188
- return input < 0 ? 0 : Math.floor(input);
189
- }
190
-
191
- return state?.tolerance ?? PROXIMITY_THRESHOLD;
192
- }
193
-
194
- /**
195
- * Create a fuzzy searcher for an array of items
196
- *
197
- * @param items Items to search through
198
- * @param key Key to use to stringify items
199
- * @returns Fuzzy searcher
200
- */
201
- export function fuzzy<Item extends PlainObject, ItemKey extends keyof Item>(
202
- items: Item[],
203
- key?: ItemKey,
204
- ): Fuzzy<Item>;
205
-
206
- /**
207
- * Create a fuzzy searcher for an array of items
208
- *
209
- * @param items Items to search through
210
- * @param handler Handler to stringify items
211
- * @returns Fuzzy searcher
212
- */
213
- export function fuzzy<Item>(items: Item[], handler?: (item: Item) => string): Fuzzy<Item>;
214
-
215
- /**
216
- * Create a fuzzy searcher for an array of items
217
- *
218
- * @param items Items to search through
219
- * @param configuration Fuzzy configuration
220
- * @returns Fuzzy searcher
221
- */
222
- export function fuzzy<Item>(items: Item[], configuration?: FuzzyConfiguration<Item>): Fuzzy<Item>;
223
-
224
- export function fuzzy(items: unknown[], configuration?: unknown): Fuzzy<unknown> {
225
- if (!Array.isArray(items)) {
226
- throw new TypeError(MESSAGE_ARRAY);
227
- }
228
-
229
- const state = getFuzzyState(items, configuration);
230
-
231
- const instance = {
232
- search: (value: string, options?: number | FuzzyOptions) =>
233
- search(
234
- state.items,
235
- state.strings,
236
- value,
237
- options == null ? state : getFuzzyOptions(options, state),
238
- ),
239
- };
240
-
241
- Object.defineProperties(instance, {
242
- items: {
243
- get: () => state.items.slice(),
244
- set: (items: unknown[]) => {
245
- if (!Array.isArray(items)) {
246
- throw new TypeError(MESSAGE_ARRAY);
247
- }
248
-
249
- state.items = items.slice();
250
- state.strings = items.map(state.handler);
251
- },
252
- },
253
- strings: {
254
- get: () => state.strings.slice(),
255
- },
256
- });
257
-
258
- return Object.freeze(instance) as Fuzzy<unknown>;
259
- }
260
-
261
- fuzzy.match = fuzzyMatch;
262
-
263
- /**
264
- * Does the needle match the haystack in a fuzzy way?
265
- *
266
- * @param haystack Haystack to search through
267
- * @param needle Needle to search for
268
- * @returns `true` if the needle matches the haystack in a fuzzy way, otherwise `false`
269
- */
270
- export function fuzzyMatch(haystack: string, needle: string): boolean {
271
- if (typeof haystack !== 'string' || typeof needle !== 'string') {
272
- return false;
273
- }
274
-
275
- const trimmed = needle.trim();
276
-
277
- if (includes(haystack, trimmed, true)) {
278
- return true;
279
- }
280
-
281
- return getScore(haystack, trimmed) > -1;
282
- }
283
-
284
- function isSubsequence(haystack: string, needle: string): boolean {
285
- const lowerCaseHaystack = lowerCase(haystack);
286
- const lowerCaseNeedle = lowerCase(needle);
287
-
288
- const haystackLength = lowerCaseHaystack.length;
289
- const needleLength = lowerCaseNeedle.length;
290
-
291
- let needleIndex = 0;
292
-
293
- for (let haystackIndex = 0; haystackIndex < haystackLength; haystackIndex += 1) {
294
- // Advance needle pointer only on a matching character
295
- if (lowerCaseHaystack[haystackIndex] === lowerCaseNeedle[needleIndex]) {
296
- needleIndex += 1;
297
- }
298
-
299
- // All needle characters matched in order
300
- if (needleIndex === needleLength) {
301
- return true;
302
- }
303
- }
304
-
305
- return false;
306
- }
307
-
308
- function getScore(haystack: string, needle: string): number {
309
- if (!isSubsequence(haystack, needle)) {
310
- return -1;
311
- }
312
-
313
- const lowerCaseHaystack = lowerCase(haystack);
314
- const lowerCaseNeedle = lowerCase(needle);
315
-
316
- const needleLength = lowerCaseNeedle.length;
317
-
318
- let needleIndex = 0;
319
- let previousMatchIndex = -1;
320
- let score = 0;
321
-
322
- for (let haystackIndex = 0; haystackIndex < lowerCaseHaystack.length; haystackIndex += 1) {
323
- if (lowerCaseHaystack[haystackIndex] === lowerCaseNeedle[needleIndex]) {
324
- // +1 for each matched character
325
- score += 1;
326
-
327
- // Bonus for matching at the start of the string
328
- if (haystackIndex === 0) {
329
- score += 1;
330
- }
331
-
332
- // Proximity bonus: decays as gap between consecutive matches widens
333
- if (previousMatchIndex !== -1) {
334
- const gap = haystackIndex - previousMatchIndex - 1;
335
-
336
- score += Math.max(0, PROXIMITY_THRESHOLD - gap);
337
- }
338
-
339
- previousMatchIndex = haystackIndex;
340
-
341
- needleIndex += 1;
342
- }
343
-
344
- // All needle characters matched; no need to scan further
345
- if (needleIndex === needleLength) {
346
- break;
347
- }
348
- }
349
-
350
- // Penalty for longer strings to favour tighter matches
351
- score -= Math.floor(lowerCaseHaystack.length / LENGTH_DIVISOR);
352
-
353
- return Math.max(0, score);
354
- }
355
-
356
- function search<Item>(
357
- items: Item[],
358
- strings: string[],
359
- input: string,
360
- options: RequiredKeys<FuzzyOptions, 'tolerance'>,
361
- ) {
362
- const result: FuzzyResult<Item> = {
363
- exact: [],
364
- similar: [],
365
- };
366
-
367
- const value = typeof input === 'string' ? input.trim() : '';
368
-
369
- if (value.length === 0) {
370
- result.exact = items.slice(0, options.limit);
371
-
372
- return result;
373
- }
374
-
375
- let {length} = items;
376
-
377
- const exact: Array<FuzzyItem<Item>> = [];
378
- const similar: Array<Item> = [];
379
-
380
- const scored: Record<number, Array<FuzzyItem<Item>>> = {};
381
-
382
- for (let index = 0; index < length; index += 1) {
383
- const item = items[index];
384
- const haystack = strings[index];
385
-
386
- if (includes(haystack, value, true)) {
387
- exact.push({item, haystack});
388
-
389
- continue;
390
- }
391
-
392
- const score = getScore(haystack, value);
393
-
394
- if (score > -1) {
395
- scored[score] ??= [];
396
-
397
- scored[score].push({item, haystack});
398
- }
399
- }
400
-
401
- const keys = Object.keys(scored)
402
- .map(Number)
403
- .sort((first, second) => second - first);
404
-
405
- length = keys.length;
406
-
407
- if (length > 0 && options.tolerance > 0) {
408
- const maxScore = keys[0];
409
-
410
- for (let index = 0; index < length; index += 1) {
411
- const key = keys[index];
412
-
413
- if (maxScore - key > options.tolerance) {
414
- break;
415
- }
416
-
417
- similar.push(...getItems(scored[key]));
418
- }
419
- }
420
-
421
- result.exact = getItems(options.limit == null ? exact : exact.slice(0, options.limit));
422
-
423
- if (options.limit == null) {
424
- result.similar = similar;
425
- } else {
426
- result.similar = similar.slice(0, options.limit - result.exact.length);
427
- }
428
-
429
- return result;
430
- }
431
-
432
- // #endregion
433
-
434
- // #region Variables
435
-
436
- const LENGTH_DIVISOR = 3;
437
-
438
- const MESSAGE_ARRAY = 'Fuzzy requires an array of items';
439
-
440
- const MESSAGE_HANDLER = 'Fuzzy requires a key or function to stringify items';
441
-
442
- const PROXIMITY_THRESHOLD = 5;
443
-
444
- // #endregion