@oscarpalmer/atoms 0.219.0 → 0.221.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.
@@ -64,9 +64,7 @@ function assertInstanceOf(constructor, message, error) {
64
64
  * @returns _Asserter_
65
65
  */
66
66
  function assertIs(condition, message, error) {
67
- return (value) => {
68
- assert(() => condition(value), message, error);
69
- };
67
+ return assertCondition(condition, message, error);
70
68
  }
71
69
  /**
72
70
  * Creates an _Asserter_ that asserts a property of a value exists and satisfies a condition, throwing an error if it does not
@@ -1,3 +1,4 @@
1
+ import { StringCaseOptions } from "../../models/string/string.case.model.mjs";
1
2
  //#region src/internal/string/case.d.ts
2
3
  /**
3
4
  * Convert a string to camel case _(thisIsCamelCase)_
@@ -103,6 +104,7 @@ export declare function snakeCase(value: string): string;
103
104
  * ```
104
105
  */
105
106
  export declare function titleCase(value: string): string;
107
+ export declare function toCaseCallback(this: StringCaseOptions, value: string): string;
106
108
  /**
107
109
  * Convert a string to upper case
108
110
  *
@@ -1,6 +1,6 @@
1
1
  import { join, words } from "./misc.mjs";
2
2
  import { memoize } from "../function/memoize.mjs";
3
- import { STRING_CASE_CAMEL, STRING_CASE_KEBAB, STRING_CASE_PASCAL, STRING_CASE_SNAKE, STRING_EXPRESSION_ACRONYM, STRING_EXPRESSION_CAMEL_CASE, STRING_REPLACEMENT_CAMEL_CASE } from "../../models/string/string.case.model.mjs";
3
+ import { STRING_CASE_CAMEL, STRING_CASE_CAPITALIZE, STRING_CASE_KEBAB, STRING_CASE_LOWER, STRING_CASE_PASCAL, STRING_CASE_SNAKE, STRING_CASE_TITLE, STRING_CASE_UPPER, STRING_EXPRESSION_ACRONYM, STRING_EXPRESSION_CAMEL_CASE, STRING_REPLACEMENT_CAMEL_CASE, ignoreCachingCases } from "../../models/string/string.case.model.mjs";
4
4
  //#region src/internal/string/case.ts
5
5
  /**
6
6
  * Convert a string to camel case _(thisIsCamelCase)_
@@ -34,9 +34,11 @@ function camelCase(value) {
34
34
  * ```
35
35
  */
36
36
  function capitalize(value) {
37
- if (typeof value !== "string" || value.length === 0) return "";
38
- memoizedCapitalize ??= memoize((v) => v.length === 1 ? v.toLocaleUpperCase() : `${v.charAt(0).toLocaleUpperCase()}${v.slice(1).toLocaleLowerCase()}`);
39
- return memoizedCapitalize.run(value);
37
+ return toCase(STRING_CASE_CAPITALIZE, value, true, true, capitalizeHandler);
38
+ }
39
+ function capitalizeHandler(value) {
40
+ if (value.length === 1) return value.toLocaleUpperCase();
41
+ return `${value.charAt(0).toLocaleUpperCase()}${value.slice(1).toLocaleLowerCase()}`;
40
42
  }
41
43
  /**
42
44
  * Convert a string to kebab case _(this-is-kebab-case)_
@@ -69,9 +71,10 @@ function kebabCase(value) {
69
71
  * ```
70
72
  */
71
73
  function lowerCase(value) {
72
- if (typeof value !== "string") return "";
73
- memoizedLowerCase ??= memoize((v) => v.toLocaleLowerCase());
74
- return memoizedLowerCase.run(value);
74
+ return toCase(STRING_CASE_LOWER, value, false, false, lowerCaseHandler);
75
+ }
76
+ function lowerCaseHandler(value) {
77
+ return value.toLocaleLowerCase();
75
78
  }
76
79
  /**
77
80
  * Convert a string to pascal case _(ThisIsPascalCase)_
@@ -122,33 +125,39 @@ function snakeCase(value) {
122
125
  * ```
123
126
  */
124
127
  function titleCase(value) {
125
- if (typeof value !== "string" || value.length === 0) return "";
126
- memoizedTitleCase ??= memoize((v) => v.length < 2 ? capitalize(v) : join(words(v).map(capitalize), " "));
127
- return memoizedTitleCase.run(value);
128
+ return toCase(STRING_CASE_TITLE, value, true, true, titleCaseHandler);
128
129
  }
129
- function toCase(type, value, capitalizeAny, capitalizeFirst) {
130
- caseMemoizers[type] ??= memoize(toCaseCallback.bind({
131
- type,
132
- capitalizeAny,
133
- capitalizeFirst
134
- }));
135
- return caseMemoizers[type].run(value);
130
+ function titleCaseHandler(value) {
131
+ if (value.length < 2) return capitalize(value);
132
+ return join(words(value).map(capitalize), " ");
136
133
  }
137
- function toCaseCallback(value) {
134
+ function toCase(type, value, capitalizeAny, capitalizeFirst, handler) {
138
135
  if (typeof value !== "string") return "";
139
- if (value.length < 1) return value;
136
+ if (value.trim().length === 0) return value;
137
+ if (handler == null) {
138
+ caseMemoizers[type] ??= memoize(toCaseCallback.bind({
139
+ type,
140
+ capitalizeAny,
141
+ capitalizeFirst
142
+ }));
143
+ return caseMemoizers[type].run(value);
144
+ }
145
+ if (ignoreCachingCases.has(type)) return handler(value);
146
+ simpleMemoizers[type] ??= memoize(handler);
147
+ return simpleMemoizers[type].run(value);
148
+ }
149
+ function toCaseCallback(value) {
140
150
  const { capitalizeAny, capitalizeFirst, type } = this;
141
151
  const parts = words(value);
142
152
  const partsLength = parts.length;
143
153
  const cased = [];
144
154
  for (let partIndex = 0; partIndex < partsLength; partIndex += 1) {
145
- const items = parts[partIndex].replace(STRING_EXPRESSION_ACRONYM, (full, one, two, three) => three === "s" ? full : `${one}-${two}${three}`).replace(STRING_EXPRESSION_CAMEL_CASE, STRING_REPLACEMENT_CAMEL_CASE).split("-");
155
+ const items = parts[partIndex].replace(STRING_EXPRESSION_ACRONYM, "-").replace(STRING_EXPRESSION_CAMEL_CASE, STRING_REPLACEMENT_CAMEL_CASE).split("-");
146
156
  const itemsLength = items.length;
147
157
  const partResult = [];
148
158
  let itemCount = 0;
149
159
  for (let itemIndex = 0; itemIndex < itemsLength; itemIndex += 1) {
150
160
  const item = items[itemIndex];
151
- if (item.length === 0) continue;
152
161
  if (!capitalizeAny || itemCount === 0 && partIndex === 0 && !capitalizeFirst) partResult.push(item.toLocaleLowerCase());
153
162
  else partResult.push(capitalize(item));
154
163
  itemCount += 1;
@@ -172,9 +181,10 @@ function toCaseCallback(value) {
172
181
  * ```
173
182
  */
174
183
  function upperCase(value) {
175
- if (typeof value !== "string" || value.length === 0) return "";
176
- memoizedUpperCase ??= memoize((v) => v.toLocaleUpperCase());
177
- return memoizedUpperCase.run(value);
184
+ return toCase(STRING_CASE_UPPER, value, false, false, upperCaseHandler);
185
+ }
186
+ function upperCaseHandler(value) {
187
+ return value.toLocaleUpperCase();
178
188
  }
179
189
  const caseMemoizers = {};
180
190
  const delimiters = {
@@ -183,9 +193,6 @@ const delimiters = {
183
193
  [STRING_CASE_PASCAL]: "",
184
194
  [STRING_CASE_SNAKE]: "_"
185
195
  };
186
- let memoizedCapitalize;
187
- let memoizedLowerCase;
188
- let memoizedTitleCase;
189
- let memoizedUpperCase;
196
+ const simpleMemoizers = {};
190
197
  //#endregion
191
- export { camelCase, capitalize, kebabCase, lowerCase, pascalCase, snakeCase, titleCase, upperCase };
198
+ export { camelCase, capitalize, kebabCase, lowerCase, pascalCase, snakeCase, titleCase, toCaseCallback, upperCase };
@@ -42,9 +42,7 @@ function addSubscription(instance) {
42
42
  return;
43
43
  }
44
44
  /* istanbul ignore if */
45
- if (items.keyed == null || values.from.keyed == null || values.to.keyed == null)
46
- // istanbul ignore next
47
- return;
45
+ if (items.keyed == null || values.from.keyed == null || values.to.keyed == null) return;
48
46
  let keyedItems = items.keyed.get(key);
49
47
  if (keyedItems == null) {
50
48
  keyedItems = /* @__PURE__ */ new Set();
@@ -194,9 +192,7 @@ function removeSubscription() {
194
192
  }
195
193
  const keyed = items.keyed?.get(key);
196
194
  /* istanbul ignore if */
197
- if (items.keyed == null || keyed == null)
198
- // istanbul ignore next
199
- return;
195
+ if (items.keyed == null || keyed == null) return;
200
196
  removeFromStore(keyed, values, this, value, key);
201
197
  if (keyed.size === 0) items.keyed.delete(key);
202
198
  }
@@ -1,19 +1,24 @@
1
1
  //#region src/models/string/string.case.model.d.ts
2
- export type StringCase = "camel" | "kebab" | "pascal" | "snake";
2
+ export type StringCase = "camel" | "capitalize" | "kebab" | "lower" | "pascal" | "snake" | "title" | "upper";
3
3
  export type StringCaseOptions = {
4
4
  capitalizeAny: boolean;
5
5
  capitalizeFirst: boolean;
6
6
  type: StringCase;
7
7
  };
8
8
  export declare const STRING_CASE_CAMEL: StringCase;
9
+ export declare const STRING_CASE_CAPITALIZE: StringCase;
9
10
  export declare const STRING_CASE_KEBAB: StringCase;
11
+ export declare const STRING_CASE_LOWER: StringCase;
10
12
  export declare const STRING_CASE_PASCAL: StringCase;
11
13
  export declare const STRING_CASE_SNAKE: StringCase;
12
- export declare const STRING_DELIMTER_EMPTY = "";
14
+ export declare const STRING_CASE_TITLE: StringCase;
15
+ export declare const STRING_CASE_UPPER: StringCase;
16
+ export declare const STRING_DELIMITER_EMPTY = "";
13
17
  export declare const STRING_DELIMITER_HYPHEN = "-";
18
+ export declare const STRING_DELIMITER_SPACE = " ";
14
19
  export declare const STRING_DELIMITER_UNDERSCORE = "_";
15
20
  export declare const STRING_EXPRESSION_CAMEL_CASE: RegExp;
16
21
  export declare const STRING_EXPRESSION_ACRONYM: RegExp;
17
22
  export declare const STRING_REPLACEMENT_CAMEL_CASE = "$1-$2";
18
- export declare const STRING_S = "s";
23
+ export declare const ignoreCachingCases: Set<StringCase>;
19
24
  //#endregion
@@ -1,14 +1,19 @@
1
1
  //#region src/models/string/string.case.model.ts
2
2
  const STRING_CASE_CAMEL = "camel";
3
+ const STRING_CASE_CAPITALIZE = "capitalize";
3
4
  const STRING_CASE_KEBAB = "kebab";
5
+ const STRING_CASE_LOWER = "lower";
4
6
  const STRING_CASE_PASCAL = "pascal";
5
7
  const STRING_CASE_SNAKE = "snake";
6
- const STRING_DELIMTER_EMPTY = "";
8
+ const STRING_CASE_TITLE = "title";
9
+ const STRING_CASE_UPPER = "upper";
10
+ const STRING_DELIMITER_EMPTY = "";
7
11
  const STRING_DELIMITER_HYPHEN = "-";
12
+ const STRING_DELIMITER_SPACE = " ";
8
13
  const STRING_DELIMITER_UNDERSCORE = "_";
9
14
  const STRING_EXPRESSION_CAMEL_CASE = /(\p{Ll})(\p{Lu})/gu;
10
- const STRING_EXPRESSION_ACRONYM = /(\p{Lu}*)(\p{Lu})(\p{Ll}+)/gu;
15
+ const STRING_EXPRESSION_ACRONYM = /(?<=\p{Lu})(?=\p{Lu}\p{Ll})(?!\p{Lu}s(?!\p{Ll}))/gu;
11
16
  const STRING_REPLACEMENT_CAMEL_CASE = "$1-$2";
12
- const STRING_S = "s";
17
+ const ignoreCachingCases = /* @__PURE__ */ new Set(["lower", "upper"]);
13
18
  //#endregion
14
- export { STRING_CASE_CAMEL, STRING_CASE_KEBAB, STRING_CASE_PASCAL, STRING_CASE_SNAKE, STRING_DELIMITER_HYPHEN, STRING_DELIMITER_UNDERSCORE, STRING_DELIMTER_EMPTY, STRING_EXPRESSION_ACRONYM, STRING_EXPRESSION_CAMEL_CASE, STRING_REPLACEMENT_CAMEL_CASE, STRING_S };
19
+ export { STRING_CASE_CAMEL, STRING_CASE_CAPITALIZE, STRING_CASE_KEBAB, STRING_CASE_LOWER, STRING_CASE_PASCAL, STRING_CASE_SNAKE, STRING_CASE_TITLE, STRING_CASE_UPPER, STRING_DELIMITER_EMPTY, STRING_DELIMITER_HYPHEN, STRING_DELIMITER_SPACE, STRING_DELIMITER_UNDERSCORE, STRING_EXPRESSION_ACRONYM, STRING_EXPRESSION_CAMEL_CASE, STRING_REPLACEMENT_CAMEL_CASE, ignoreCachingCases };
@@ -27,26 +27,28 @@ export type Fuzzy<Item> = {
27
27
  *
28
28
  * @param value Value to search for
29
29
  * @param options Search options
30
- * @returns Search results, with exact matches _(ordered)_ and similar matches _(ordered by relevance)_
30
+ * @returns Search results, with exact, included, and similar matches
31
31
  */
32
32
  search(value: string, options?: FuzzyOptions): FuzzyResult<Item>;
33
33
  /**
34
34
  * Search for items matching the given value
35
35
  *
36
36
  * @param value Value to search for
37
- * @param limit Maximum number of combined items to return in `exact` and `similar`
38
- * @returns Search results, with exact matches _(ordered)_ and similar matches _(ordered by relevance)_
37
+ * @param limit Maximum number of combined items to return in `exact`, `included`, and `similar`
38
+ * @returns Search results, with exact, included, and similar matches
39
39
  */
40
40
  search(value: string, limit: number): FuzzyResult<Item>;
41
41
  };
42
42
  export type FuzzyConfiguration<Item> = {
43
+ handler?: (item: Item) => string;
43
44
  /**
44
- * Handler to stringify items
45
+ * Function to compare the stringified items for results _(defaults to `Intl.Collator().compare`)_
45
46
  *
46
- * - 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
47
- * - Defaults to `getString`
47
+ * @param first First string to compare
48
+ * @param second Second string to compare
49
+ * @returns Comparison result, similar to `Array.prototype.sort` callback
48
50
  */
49
- handler?: (item: Item) => string;
51
+ compare?: (first: string, second: string) => number;
50
52
  } & (Item extends PlainObject ? {
51
53
  /**
52
54
  * Key to use to stringify items
@@ -55,13 +57,13 @@ export type FuzzyConfiguration<Item> = {
55
57
  */
56
58
  key?: keyof Item;
57
59
  } : {}) & FuzzyOptions;
58
- export type FuzzyItem<Item> = {
59
- item: Item;
60
- haystack: string;
60
+ export type FuzzyItem<Data> = {
61
+ data: Data;
62
+ string: FuzzyString;
61
63
  };
62
64
  export type FuzzyOptions = {
63
65
  /**
64
- * Maximum number of combined items to return in `exact` and `similar` _(defaults to all matches)_
66
+ * Maximum number of combined items to return in `exact`, `included`, and `similar` _(defaults to all matches)_
65
67
  */
66
68
  limit?: number;
67
69
  /**
@@ -72,16 +74,27 @@ export type FuzzyOptions = {
72
74
  */
73
75
  tolerance?: number;
74
76
  };
77
+ export type FuzzyRanked<Data> = {
78
+ position: number;
79
+ /**
80
+ * `0` for starting with the value, `1` for the value starting a word, `2` for anything else
81
+ */
82
+ rank: number;
83
+ } & FuzzyItem<Data>;
75
84
  /**
76
- * Search results from a fuzzy search, with exact and similar matches
85
+ * Search results from a fuzzy search, with exact, included, and similar matches
77
86
  */
78
87
  export type FuzzyResult<Item> = {
79
88
  /**
80
- * Ordered items that exactly match the search value
89
+ * Items whose strings exactly match the search value, ordered alphabetically
81
90
  */
82
91
  exact: Item[];
83
92
  /**
84
- * Ordered items that are similar to the search value, ranked by relevance
93
+ * Items whose strings contain the search value, ordered alphabetically
94
+ */
95
+ included: Item[];
96
+ /**
97
+ * Items whose strings are similar to the search value, ranked by relevance, sorted by score, and ordered alphabetically within the same score
85
98
  */
86
99
  similar: Item[];
87
100
  };
@@ -90,19 +103,34 @@ export type FuzzyResult<Item> = {
90
103
  */
91
104
  export type FuzzySearchOptions = { [Key in keyof FuzzyOptions]: FuzzyOptions[Key]; };
92
105
  export type FuzzyState<Item> = {
106
+ compare: FuzzyStateCompare<Item>;
93
107
  handler(item: Item): string;
94
- items: Item[];
95
- limit?: number;
96
- strings: string[];
108
+ items: FuzzyStateItems<Item>;
109
+ limit: number;
97
110
  tolerance: number;
98
111
  };
112
+ type FuzzyStateCompare<Item = unknown> = {
113
+ item: (first: FuzzyItem<Item>, second: FuzzyItem<Item>) => number;
114
+ ranked: (first: FuzzyRanked<Item>, second: FuzzyRanked<Item>) => number;
115
+ };
116
+ type FuzzyStateItems<Item> = {
117
+ original: Array<FuzzyItem<Item>>;
118
+ sorted?: Array<FuzzyItem<Item>>;
119
+ };
120
+ export type FuzzyString = {
121
+ lowercase: string;
122
+ original: string;
123
+ };
99
124
  export type InternalFuzzy<Item = unknown> = {
100
125
  [FUZZY_SYMBOL]: FuzzyState<Item>;
101
126
  } & Fuzzy<Item>;
127
+ export declare const FUZZY_BOUNDARY_BONUS = 2;
128
+ export declare const FUZZY_BOUNDARY_SEPARATOR: RegExp;
102
129
  export declare const FUZZY_LENGTH_DIVISOR = 3;
103
130
  export declare const FUZZY_MESSAGE_ARRAY = "Fuzzy requires an array of items";
104
131
  export declare const FUZZY_MESSAGE_HANDLER = "Fuzzy requires a key or function to stringify items";
105
132
  export declare const FUZZY_PROPERTY = "$fuzzy";
106
133
  export declare const FUZZY_PROXIMITY_THRESHOLD = 5;
107
134
  export declare const FUZZY_SYMBOL: unique symbol;
108
- //#endregion
135
+ //#endregion
136
+ export {};
@@ -1,4 +1,6 @@
1
1
  //#region src/models/string/string.fuzzy.model.ts
2
+ const FUZZY_BOUNDARY_BONUS = 2;
3
+ const FUZZY_BOUNDARY_SEPARATOR = /[\s./:\\_-]/;
2
4
  const FUZZY_LENGTH_DIVISOR = 3;
3
5
  const FUZZY_MESSAGE_ARRAY = "Fuzzy requires an array of items";
4
6
  const FUZZY_MESSAGE_HANDLER = "Fuzzy requires a key or function to stringify items";
@@ -6,4 +8,4 @@ const FUZZY_PROPERTY = "$fuzzy";
6
8
  const FUZZY_PROXIMITY_THRESHOLD = 5;
7
9
  const FUZZY_SYMBOL = Symbol(FUZZY_PROPERTY);
8
10
  //#endregion
9
- export { FUZZY_LENGTH_DIVISOR, FUZZY_MESSAGE_ARRAY, FUZZY_MESSAGE_HANDLER, FUZZY_PROPERTY, FUZZY_PROXIMITY_THRESHOLD, FUZZY_SYMBOL };
11
+ export { FUZZY_BOUNDARY_BONUS, FUZZY_BOUNDARY_SEPARATOR, FUZZY_LENGTH_DIVISOR, FUZZY_MESSAGE_ARRAY, FUZZY_MESSAGE_HANDLER, FUZZY_PROPERTY, FUZZY_PROXIMITY_THRESHOLD, FUZZY_SYMBOL };
@@ -2,7 +2,7 @@
2
2
  const STRING_DELIMITER_UUID_DEFAULT = "-";
3
3
  const STRING_DELIMITER_UUID_HTML = "_";
4
4
  const STRING_EXPRESSION_IGNORED = /(^|\.)(__proto__|constructor|prototype)(\.|$)/i;
5
- const STRING_EXPRESSION_WORDS = /([^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+)/g;
5
+ const STRING_EXPRESSION_WORDS = /[\dA-Za-z\P{ASCII}]+/gu;
6
6
  const STRING_EXPRESSION_WHITESPACE_PREFIX = /^(\s+)/;
7
7
  const STRING_MATCH_ENDS_WITH = "endsWith";
8
8
  const STRING_MATCH_INCLUDES = "includes";
@@ -1,5 +1,5 @@
1
1
  //#region src/models/string/string.template.model.ts
2
- const TEMPLATE_EXPRESSION_VARIABLE = /{{([\s\S]+?)}}/g;
2
+ const TEMPLATE_EXPRESSION_VARIABLE = /{{([^{}]+)}}/g;
3
3
  const TEMPLATE_SYMBOL = Symbol("template");
4
4
  //#endregion
5
5
  export { TEMPLATE_EXPRESSION_VARIABLE, TEMPLATE_SYMBOL };
@@ -1,7 +1,7 @@
1
1
  import { PlainObject } from "../models/index.mjs";
2
- import { Fuzzy, FuzzyConfiguration, FuzzyItem, FuzzyOptions, FuzzyResult, FuzzyState } from "../models/string/string.fuzzy.model.mjs";
2
+ import { Fuzzy, FuzzyConfiguration, FuzzyOptions, FuzzyResult, FuzzyState, InternalFuzzy } from "../models/string/string.fuzzy.model.mjs";
3
3
  //#region src/string/fuzzy.d.ts
4
- declare function Fuzzy(this: any, state: FuzzyState<unknown>): void;
4
+ declare function Fuzzy(this: InternalFuzzy, state: FuzzyState<unknown>): void;
5
5
  /**
6
6
  * Create a fuzzy searcher for an array of items
7
7
  *
@@ -50,4 +50,4 @@ export declare namespace fuzzy {
50
50
  export var match: typeof fuzzyMatch;
51
51
  }
52
52
  //#endregion
53
- export type { Fuzzy, FuzzyItem, FuzzyOptions, FuzzyResult };
53
+ export type { Fuzzy, FuzzyOptions, FuzzyResult };