@web-ts-toolkit/utils 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,8 +10,8 @@ pnpm add @web-ts-toolkit/utils
10
10
 
11
11
  ## Highlights
12
12
 
13
- - object-path helpers such as `get(...)` and `set(...)`
14
- - collection helpers such as `map(...)`, `filter(...)`, `uniq(...)`, and `orderBy(...)`
13
+ - object-path helpers such as `get(...)`, `set(...)`, and `hasOwn(...)`
14
+ - collection helpers such as `map(...)`, `filter(...)`, `eachRight(...)`, `join(...)`, `uniq(...)`, `uniqBy(...)`, and `orderBy(...)`
15
15
  - small type guards
16
16
  - URL helpers such as `normalizeUrlPath(...)`
17
17
  - async helpers such as `mapValuesAsync(...)`
@@ -19,7 +19,16 @@ pnpm add @web-ts-toolkit/utils
19
19
  ## Quick Start
20
20
 
21
21
  ```ts
22
- import { get, groupBy, normalizeUrlPath, parseBooleanString, startCase, sumBy } from '@web-ts-toolkit/utils';
22
+ import {
23
+ get,
24
+ groupBy,
25
+ hasOwn,
26
+ normalizeUrlPath,
27
+ parseBooleanString,
28
+ startCase,
29
+ sumBy,
30
+ uniqBy,
31
+ } from '@web-ts-toolkit/utils';
23
32
 
24
33
  const payload = {
25
34
  user: {
@@ -30,6 +39,7 @@ const payload = {
30
39
  };
31
40
 
32
41
  get(payload, 'user.profile.name');
42
+ hasOwn(payload.user.profile, 'name');
33
43
  groupBy(
34
44
  [
35
45
  { type: 'fruit', name: 'apple' },
@@ -38,6 +48,14 @@ groupBy(
38
48
  ],
39
49
  'type',
40
50
  );
51
+ uniqBy(
52
+ [
53
+ { id: 'a', name: 'Ada' },
54
+ { id: 'a', name: 'Ada Lovelace' },
55
+ { id: 'b', name: 'Grace' },
56
+ ],
57
+ 'id',
58
+ );
41
59
  startCase('api_response_time');
42
60
  sumBy([{ hours: 2 }, { hours: 3 }], 'hours');
43
61
  normalizeUrlPath('api//users');
@@ -46,8 +64,8 @@ parseBooleanString('true', false);
46
64
 
47
65
  ## Main Exports
48
66
 
49
- - object helpers: `get`, `set`, `pick`, `pickBy`, `omit`, `assign`, `cloneDeep`, `mapKeys`
50
- - collection helpers: `map`, `filter`, `reduce`, `find`, `flatten`, `uniq`, `orderBy`, `groupBy`, `sum`, `sumBy`
67
+ - object helpers: `get`, `set`, `hasOwn`, `pick`, `pickBy`, `omit`, `omitBy`, `assign`, `cloneDeep`, `mapKeys`
68
+ - collection helpers: `map`, `filter`, `eachRight`, `join`, `reduce`, `find`, `flatten`, `uniq`, `uniqBy`, `orderBy`, `groupBy`, `sum`, `sumBy`
51
69
  - string helpers: `startCase`, `upperCase`
52
70
  - guards: `isArray`, `isPlainObject`, `isString`, `isPromise`
53
71
  - URL helpers: `addLeadingSlash`, `removeConsecutiveSlashesFromUrl`, `normalizeUrlPath`
package/index.d.mts CHANGED
@@ -12,6 +12,9 @@ declare function compact<T>(array: T[] | null | undefined): T[];
12
12
 
13
13
  declare function difference<T>(array: T[] | null | undefined, ...values: Array<T[] | null | undefined>): T[];
14
14
 
15
+ declare function eachRight<T>(collection: T[] | null | undefined, iteratee: (value: T, key: number, collection: T[]) => unknown): T[] | null | undefined;
16
+ declare function eachRight<T extends object>(collection: T | null | undefined, iteratee: (value: T[keyof T], key: string, collection: T) => unknown): T | null | undefined;
17
+
15
18
  declare function filter<T>(collection: T[] | Record<string, T> | null | undefined, predicate: (value: T, key: number | string) => boolean): T[];
16
19
 
17
20
  declare function find<T>(collection: T[] | Record<string, T> | null | undefined, predicate: (value: T, key: number | string) => boolean): T | undefined;
@@ -29,6 +32,8 @@ declare function get<T = unknown>(object: unknown, path: PropertyPath, defaultVa
29
32
 
30
33
  declare function groupBy<T>(collection: T[] | Record<string, T> | null | undefined, iteratee: string | number | ((value: T, key: number | string, collection: T[] | Record<string, T>) => unknown)): Record<string, T[]>;
31
34
 
35
+ declare function hasOwn<TKey extends PropertyKey>(value: unknown, key: TKey): value is Record<TKey, unknown>;
36
+
32
37
  declare function intersection<T>(...arrays: Array<T[] | null | undefined>): T[];
33
38
 
34
39
  declare function intersectionBy<T>(...args: unknown[]): T[];
@@ -61,6 +66,8 @@ declare function isString(value: unknown): value is string;
61
66
 
62
67
  declare function isUndefined(value: unknown): value is undefined;
63
68
 
69
+ declare function join(array: unknown[] | null | undefined, separator?: string): string;
70
+
64
71
  declare function keys(value: unknown): string[];
65
72
 
66
73
  declare function map<T, TResult>(collection: T[] | Record<string, T> | null | undefined, iteratee: string | number | ((value: T, key: number | string, collection: T[] | Record<string, T>) => TResult)): TResult[];
@@ -75,6 +82,8 @@ declare function noop(): void;
75
82
 
76
83
  declare function omit<T extends object>(object: T, paths: PropertyPath | PropertyPath[]): Partial<T>;
77
84
 
85
+ declare function omitBy<TValue>(object: Record<string, TValue> | null | undefined, predicate: (value: TValue, key: string, object: Record<string, TValue>) => boolean): Record<string, TValue>;
86
+
78
87
  declare function parseBooleanString(str: string | undefined, defaultValue?: boolean): boolean | undefined;
79
88
 
80
89
  declare function orderBy<T>(collection: T[] | null | undefined, iteratees?: Array<string | number | ((value: T) => unknown)>, orders?: string[]): T[];
@@ -106,6 +115,8 @@ declare function toStringRecord(value: unknown): Record<string, string> | undefi
106
115
 
107
116
  declare function uniq<T>(array: T[] | null | undefined): T[];
108
117
 
118
+ declare function uniqBy<T>(array: T[] | null | undefined, iteratee?: string | number | ((value: T, key: number, collection: T[]) => unknown)): T[];
119
+
109
120
  declare function upperCase(value: unknown): string;
110
121
 
111
- export { addLeadingSlash, arrayToRecord, assign, castArray, cloneDeep, compact, difference, filter, find, flatten, flattenDeep, forEach, get, groupBy, intersection, intersectionBy, isArray, isBoolean, isEmpty, isEqual, isFunction, isMatch, isNaNValue as isNaN, isNil, isNumber, isObject, isPlainObject, isPromise, isString, isUndefined, keys, map, mapKeys, mapValues, mapValuesAsync, noop, normalizeUrlPath, omit, orderBy, padEnd, parseBooleanString, pick, pickBy, reduce, removeConsecutiveSlashesFromUrl, set, startCase, sum, sumBy, toAsyncFn, toStringRecord, uniq, upperCase };
122
+ export { addLeadingSlash, arrayToRecord, assign, castArray, cloneDeep, compact, difference, eachRight, filter, find, flatten, flattenDeep, forEach, get, groupBy, hasOwn, intersection, intersectionBy, isArray, isBoolean, isEmpty, isEqual, isFunction, isMatch, isNaNValue as isNaN, isNil, isNumber, isObject, isPlainObject, isPromise, isString, isUndefined, join, keys, map, mapKeys, mapValues, mapValuesAsync, noop, normalizeUrlPath, omit, omitBy, orderBy, padEnd, parseBooleanString, pick, pickBy, reduce, removeConsecutiveSlashesFromUrl, set, startCase, sum, sumBy, toAsyncFn, toStringRecord, uniq, uniqBy, upperCase };
package/index.d.ts CHANGED
@@ -12,6 +12,9 @@ declare function compact<T>(array: T[] | null | undefined): T[];
12
12
 
13
13
  declare function difference<T>(array: T[] | null | undefined, ...values: Array<T[] | null | undefined>): T[];
14
14
 
15
+ declare function eachRight<T>(collection: T[] | null | undefined, iteratee: (value: T, key: number, collection: T[]) => unknown): T[] | null | undefined;
16
+ declare function eachRight<T extends object>(collection: T | null | undefined, iteratee: (value: T[keyof T], key: string, collection: T) => unknown): T | null | undefined;
17
+
15
18
  declare function filter<T>(collection: T[] | Record<string, T> | null | undefined, predicate: (value: T, key: number | string) => boolean): T[];
16
19
 
17
20
  declare function find<T>(collection: T[] | Record<string, T> | null | undefined, predicate: (value: T, key: number | string) => boolean): T | undefined;
@@ -29,6 +32,8 @@ declare function get<T = unknown>(object: unknown, path: PropertyPath, defaultVa
29
32
 
30
33
  declare function groupBy<T>(collection: T[] | Record<string, T> | null | undefined, iteratee: string | number | ((value: T, key: number | string, collection: T[] | Record<string, T>) => unknown)): Record<string, T[]>;
31
34
 
35
+ declare function hasOwn<TKey extends PropertyKey>(value: unknown, key: TKey): value is Record<TKey, unknown>;
36
+
32
37
  declare function intersection<T>(...arrays: Array<T[] | null | undefined>): T[];
33
38
 
34
39
  declare function intersectionBy<T>(...args: unknown[]): T[];
@@ -61,6 +66,8 @@ declare function isString(value: unknown): value is string;
61
66
 
62
67
  declare function isUndefined(value: unknown): value is undefined;
63
68
 
69
+ declare function join(array: unknown[] | null | undefined, separator?: string): string;
70
+
64
71
  declare function keys(value: unknown): string[];
65
72
 
66
73
  declare function map<T, TResult>(collection: T[] | Record<string, T> | null | undefined, iteratee: string | number | ((value: T, key: number | string, collection: T[] | Record<string, T>) => TResult)): TResult[];
@@ -75,6 +82,8 @@ declare function noop(): void;
75
82
 
76
83
  declare function omit<T extends object>(object: T, paths: PropertyPath | PropertyPath[]): Partial<T>;
77
84
 
85
+ declare function omitBy<TValue>(object: Record<string, TValue> | null | undefined, predicate: (value: TValue, key: string, object: Record<string, TValue>) => boolean): Record<string, TValue>;
86
+
78
87
  declare function parseBooleanString(str: string | undefined, defaultValue?: boolean): boolean | undefined;
79
88
 
80
89
  declare function orderBy<T>(collection: T[] | null | undefined, iteratees?: Array<string | number | ((value: T) => unknown)>, orders?: string[]): T[];
@@ -106,6 +115,8 @@ declare function toStringRecord(value: unknown): Record<string, string> | undefi
106
115
 
107
116
  declare function uniq<T>(array: T[] | null | undefined): T[];
108
117
 
118
+ declare function uniqBy<T>(array: T[] | null | undefined, iteratee?: string | number | ((value: T, key: number, collection: T[]) => unknown)): T[];
119
+
109
120
  declare function upperCase(value: unknown): string;
110
121
 
111
- export { addLeadingSlash, arrayToRecord, assign, castArray, cloneDeep, compact, difference, filter, find, flatten, flattenDeep, forEach, get, groupBy, intersection, intersectionBy, isArray, isBoolean, isEmpty, isEqual, isFunction, isMatch, isNaNValue as isNaN, isNil, isNumber, isObject, isPlainObject, isPromise, isString, isUndefined, keys, map, mapKeys, mapValues, mapValuesAsync, noop, normalizeUrlPath, omit, orderBy, padEnd, parseBooleanString, pick, pickBy, reduce, removeConsecutiveSlashesFromUrl, set, startCase, sum, sumBy, toAsyncFn, toStringRecord, uniq, upperCase };
122
+ export { addLeadingSlash, arrayToRecord, assign, castArray, cloneDeep, compact, difference, eachRight, filter, find, flatten, flattenDeep, forEach, get, groupBy, hasOwn, intersection, intersectionBy, isArray, isBoolean, isEmpty, isEqual, isFunction, isMatch, isNaNValue as isNaN, isNil, isNumber, isObject, isPlainObject, isPromise, isString, isUndefined, join, keys, map, mapKeys, mapValues, mapValuesAsync, noop, normalizeUrlPath, omit, omitBy, orderBy, padEnd, parseBooleanString, pick, pickBy, reduce, removeConsecutiveSlashesFromUrl, set, startCase, sum, sumBy, toAsyncFn, toStringRecord, uniq, uniqBy, upperCase };
package/index.js CHANGED
@@ -26,6 +26,7 @@ __export(index_exports, {
26
26
  cloneDeep: () => cloneDeep,
27
27
  compact: () => compact,
28
28
  difference: () => difference,
29
+ eachRight: () => eachRight,
29
30
  filter: () => filter,
30
31
  find: () => find,
31
32
  flatten: () => flatten,
@@ -33,6 +34,7 @@ __export(index_exports, {
33
34
  forEach: () => forEach,
34
35
  get: () => get,
35
36
  groupBy: () => groupBy,
37
+ hasOwn: () => hasOwn,
36
38
  intersection: () => intersection,
37
39
  intersectionBy: () => intersectionBy,
38
40
  isArray: () => isArray,
@@ -49,6 +51,7 @@ __export(index_exports, {
49
51
  isPromise: () => isPromise,
50
52
  isString: () => isString,
51
53
  isUndefined: () => isUndefined,
54
+ join: () => join,
52
55
  keys: () => keys,
53
56
  map: () => map,
54
57
  mapKeys: () => mapKeys,
@@ -57,6 +60,7 @@ __export(index_exports, {
57
60
  noop: () => noop,
58
61
  normalizeUrlPath: () => normalizeUrlPath,
59
62
  omit: () => omit,
63
+ omitBy: () => omitBy,
60
64
  orderBy: () => orderBy,
61
65
  padEnd: () => padEnd,
62
66
  parseBooleanString: () => parseBooleanString,
@@ -71,6 +75,7 @@ __export(index_exports, {
71
75
  toAsyncFn: () => toAsyncFn,
72
76
  toStringRecord: () => toStringRecord,
73
77
  uniq: () => uniq,
78
+ uniqBy: () => uniqBy,
74
79
  upperCase: () => upperCase
75
80
  });
76
81
  module.exports = __toCommonJS(index_exports);
@@ -398,6 +403,30 @@ function difference(array, ...values) {
398
403
  return array.filter((item) => !arrayIncludes(excluded, item));
399
404
  }
400
405
 
406
+ // src/eachRight.ts
407
+ function eachRight(collection, iteratee) {
408
+ if (!collection) {
409
+ return collection;
410
+ }
411
+ const callback = iteratee;
412
+ if (Array.isArray(collection)) {
413
+ for (let index = collection.length - 1; index >= 0; index--) {
414
+ if (callback(collection[index], index, collection) === false) {
415
+ break;
416
+ }
417
+ }
418
+ return collection;
419
+ }
420
+ const keys2 = Object.keys(collection);
421
+ for (let index = keys2.length - 1; index >= 0; index--) {
422
+ const key = keys2[index];
423
+ if (callback(collection[key], key, collection) === false) {
424
+ break;
425
+ }
426
+ }
427
+ return collection;
428
+ }
429
+
401
430
  // src/filter.ts
402
431
  function filter(collection, predicate) {
403
432
  if (!collection) {
@@ -537,6 +566,14 @@ function groupBy(collection, iteratee) {
537
566
  return result;
538
567
  }
539
568
 
569
+ // src/hasOwn.ts
570
+ function hasOwn(value, key) {
571
+ if (value === null || value === void 0) {
572
+ return false;
573
+ }
574
+ return Object.hasOwn(Object(value), key);
575
+ }
576
+
540
577
  // src/intersection.ts
541
578
  function intersection(...arrays) {
542
579
  const [first = [], ...rest] = arrays;
@@ -661,6 +698,14 @@ function isUndefined(value) {
661
698
  return value === void 0;
662
699
  }
663
700
 
701
+ // src/join.ts
702
+ function join(array, separator) {
703
+ if (!Array.isArray(array)) {
704
+ return "";
705
+ }
706
+ return array.join(separator);
707
+ }
708
+
664
709
  // src/keys.ts
665
710
  function keys(value) {
666
711
  if (value == null) {
@@ -744,6 +789,23 @@ function omit(object, paths) {
744
789
  return result;
745
790
  }
746
791
 
792
+ // src/omitBy.ts
793
+ function omitBy(object, predicate) {
794
+ if (!object) {
795
+ return {};
796
+ }
797
+ const result = {};
798
+ const keys2 = Object.keys(object);
799
+ for (let index = 0; index < keys2.length; index++) {
800
+ const key = keys2[index];
801
+ const value = object[key];
802
+ if (!predicate(value, key, object)) {
803
+ result[key] = value;
804
+ }
805
+ }
806
+ return result;
807
+ }
808
+
747
809
  // src/parseBooleanString.ts
748
810
  function parseBooleanString(str, defaultValue) {
749
811
  return str ? str === "true" : defaultValue;
@@ -919,6 +981,26 @@ function uniq(array) {
919
981
  return baseUniq(array);
920
982
  }
921
983
 
984
+ // src/uniqBy.ts
985
+ function uniqBy(array, iteratee) {
986
+ if (!Array.isArray(array)) {
987
+ return [];
988
+ }
989
+ const callback = getIteratee(iteratee);
990
+ const result = [];
991
+ const seen = [];
992
+ for (let index = 0; index < array.length; index++) {
993
+ const value = array[index];
994
+ const computed = callback(value, index, array);
995
+ if (seen.some((entry) => sameValueZero(entry, computed))) {
996
+ continue;
997
+ }
998
+ seen.push(computed);
999
+ result.push(value);
1000
+ }
1001
+ return result;
1002
+ }
1003
+
922
1004
  // src/upperCase.ts
923
1005
  function upperCase(value) {
924
1006
  return words(value).join(" ").toUpperCase();
@@ -932,6 +1014,7 @@ function upperCase(value) {
932
1014
  cloneDeep,
933
1015
  compact,
934
1016
  difference,
1017
+ eachRight,
935
1018
  filter,
936
1019
  find,
937
1020
  flatten,
@@ -939,6 +1022,7 @@ function upperCase(value) {
939
1022
  forEach,
940
1023
  get,
941
1024
  groupBy,
1025
+ hasOwn,
942
1026
  intersection,
943
1027
  intersectionBy,
944
1028
  isArray,
@@ -955,6 +1039,7 @@ function upperCase(value) {
955
1039
  isPromise,
956
1040
  isString,
957
1041
  isUndefined,
1042
+ join,
958
1043
  keys,
959
1044
  map,
960
1045
  mapKeys,
@@ -963,6 +1048,7 @@ function upperCase(value) {
963
1048
  noop,
964
1049
  normalizeUrlPath,
965
1050
  omit,
1051
+ omitBy,
966
1052
  orderBy,
967
1053
  padEnd,
968
1054
  parseBooleanString,
@@ -977,5 +1063,6 @@ function upperCase(value) {
977
1063
  toAsyncFn,
978
1064
  toStringRecord,
979
1065
  uniq,
1066
+ uniqBy,
980
1067
  upperCase
981
1068
  });
package/index.mjs CHANGED
@@ -321,6 +321,30 @@ function difference(array, ...values) {
321
321
  return array.filter((item) => !arrayIncludes(excluded, item));
322
322
  }
323
323
 
324
+ // src/eachRight.ts
325
+ function eachRight(collection, iteratee) {
326
+ if (!collection) {
327
+ return collection;
328
+ }
329
+ const callback = iteratee;
330
+ if (Array.isArray(collection)) {
331
+ for (let index = collection.length - 1; index >= 0; index--) {
332
+ if (callback(collection[index], index, collection) === false) {
333
+ break;
334
+ }
335
+ }
336
+ return collection;
337
+ }
338
+ const keys2 = Object.keys(collection);
339
+ for (let index = keys2.length - 1; index >= 0; index--) {
340
+ const key = keys2[index];
341
+ if (callback(collection[key], key, collection) === false) {
342
+ break;
343
+ }
344
+ }
345
+ return collection;
346
+ }
347
+
324
348
  // src/filter.ts
325
349
  function filter(collection, predicate) {
326
350
  if (!collection) {
@@ -460,6 +484,14 @@ function groupBy(collection, iteratee) {
460
484
  return result;
461
485
  }
462
486
 
487
+ // src/hasOwn.ts
488
+ function hasOwn(value, key) {
489
+ if (value === null || value === void 0) {
490
+ return false;
491
+ }
492
+ return Object.hasOwn(Object(value), key);
493
+ }
494
+
463
495
  // src/intersection.ts
464
496
  function intersection(...arrays) {
465
497
  const [first = [], ...rest] = arrays;
@@ -584,6 +616,14 @@ function isUndefined(value) {
584
616
  return value === void 0;
585
617
  }
586
618
 
619
+ // src/join.ts
620
+ function join(array, separator) {
621
+ if (!Array.isArray(array)) {
622
+ return "";
623
+ }
624
+ return array.join(separator);
625
+ }
626
+
587
627
  // src/keys.ts
588
628
  function keys(value) {
589
629
  if (value == null) {
@@ -667,6 +707,23 @@ function omit(object, paths) {
667
707
  return result;
668
708
  }
669
709
 
710
+ // src/omitBy.ts
711
+ function omitBy(object, predicate) {
712
+ if (!object) {
713
+ return {};
714
+ }
715
+ const result = {};
716
+ const keys2 = Object.keys(object);
717
+ for (let index = 0; index < keys2.length; index++) {
718
+ const key = keys2[index];
719
+ const value = object[key];
720
+ if (!predicate(value, key, object)) {
721
+ result[key] = value;
722
+ }
723
+ }
724
+ return result;
725
+ }
726
+
670
727
  // src/parseBooleanString.ts
671
728
  function parseBooleanString(str, defaultValue) {
672
729
  return str ? str === "true" : defaultValue;
@@ -842,6 +899,26 @@ function uniq(array) {
842
899
  return baseUniq(array);
843
900
  }
844
901
 
902
+ // src/uniqBy.ts
903
+ function uniqBy(array, iteratee) {
904
+ if (!Array.isArray(array)) {
905
+ return [];
906
+ }
907
+ const callback = getIteratee(iteratee);
908
+ const result = [];
909
+ const seen = [];
910
+ for (let index = 0; index < array.length; index++) {
911
+ const value = array[index];
912
+ const computed = callback(value, index, array);
913
+ if (seen.some((entry) => sameValueZero(entry, computed))) {
914
+ continue;
915
+ }
916
+ seen.push(computed);
917
+ result.push(value);
918
+ }
919
+ return result;
920
+ }
921
+
845
922
  // src/upperCase.ts
846
923
  function upperCase(value) {
847
924
  return words(value).join(" ").toUpperCase();
@@ -854,6 +931,7 @@ export {
854
931
  cloneDeep,
855
932
  compact,
856
933
  difference,
934
+ eachRight,
857
935
  filter,
858
936
  find,
859
937
  flatten,
@@ -861,6 +939,7 @@ export {
861
939
  forEach,
862
940
  get,
863
941
  groupBy,
942
+ hasOwn,
864
943
  intersection,
865
944
  intersectionBy,
866
945
  isArray,
@@ -877,6 +956,7 @@ export {
877
956
  isPromise,
878
957
  isString,
879
958
  isUndefined,
959
+ join,
880
960
  keys,
881
961
  map,
882
962
  mapKeys,
@@ -885,6 +965,7 @@ export {
885
965
  noop,
886
966
  normalizeUrlPath,
887
967
  omit,
968
+ omitBy,
888
969
  orderBy,
889
970
  padEnd,
890
971
  parseBooleanString,
@@ -899,5 +980,6 @@ export {
899
980
  toAsyncFn,
900
981
  toStringRecord,
901
982
  uniq,
983
+ uniqBy,
902
984
  upperCase
903
985
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@web-ts-toolkit/utils",
3
3
  "description": "Shared collection, object, async, and URL helpers used across workspace packages",
4
4
  "homepage": "https://web-ts-toolkit.pages.dev/docs/packages/utils",
5
- "version": "0.29.0",
5
+ "version": "0.31.0",
6
6
  "sideEffects": false,
7
7
  "keywords": [
8
8
  "utilities",