@e22m4u/js-repository 0.0.31

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 (183) hide show
  1. package/.c8rc +9 -0
  2. package/.commitlintrc +5 -0
  3. package/.editorconfig +13 -0
  4. package/.eslintignore +1 -0
  5. package/.eslintrc.cjs +27 -0
  6. package/.husky/commit-msg +4 -0
  7. package/.husky/pre-commit +9 -0
  8. package/.mocharc.cjs +7 -0
  9. package/.prettierrc +7 -0
  10. package/LICENSE +21 -0
  11. package/README.md +523 -0
  12. package/mocha.setup.js +10 -0
  13. package/package.json +57 -0
  14. package/src/adapter/adapter-loader.d.ts +16 -0
  15. package/src/adapter/adapter-loader.js +63 -0
  16. package/src/adapter/adapter-loader.spec.js +31 -0
  17. package/src/adapter/adapter-registry.d.ts +14 -0
  18. package/src/adapter/adapter-registry.js +36 -0
  19. package/src/adapter/adapter-registry.spec.js +36 -0
  20. package/src/adapter/adapter.d.ts +118 -0
  21. package/src/adapter/adapter.js +181 -0
  22. package/src/adapter/adapter.spec.js +144 -0
  23. package/src/adapter/builtin/memory-adapter.d.ts +118 -0
  24. package/src/adapter/builtin/memory-adapter.js +342 -0
  25. package/src/adapter/builtin/memory-adapter.spec.js +2925 -0
  26. package/src/adapter/decorator/data-sanitizing-decorator.d.ts +13 -0
  27. package/src/adapter/decorator/data-sanitizing-decorator.js +44 -0
  28. package/src/adapter/decorator/data-sanitizing-decorator.spec.js +59 -0
  29. package/src/adapter/decorator/data-validation-decorator.d.ts +13 -0
  30. package/src/adapter/decorator/data-validation-decorator.js +41 -0
  31. package/src/adapter/decorator/data-validation-decorator.spec.js +59 -0
  32. package/src/adapter/decorator/default-values-decorator.d.ts +13 -0
  33. package/src/adapter/decorator/default-values-decorator.js +57 -0
  34. package/src/adapter/decorator/default-values-decorator.spec.js +141 -0
  35. package/src/adapter/decorator/fields-filtering-decorator.d.ts +13 -0
  36. package/src/adapter/decorator/fields-filtering-decorator.js +72 -0
  37. package/src/adapter/decorator/fields-filtering-decorator.spec.js +119 -0
  38. package/src/adapter/decorator/inclusion-decorator.d.ts +13 -0
  39. package/src/adapter/decorator/inclusion-decorator.js +78 -0
  40. package/src/adapter/decorator/inclusion-decorator.spec.js +117 -0
  41. package/src/adapter/decorator/index.d.ts +5 -0
  42. package/src/adapter/decorator/index.js +5 -0
  43. package/src/adapter/index.d.ts +3 -0
  44. package/src/adapter/index.js +3 -0
  45. package/src/definition/datasource/datasource-definition-validator.d.ts +14 -0
  46. package/src/definition/datasource/datasource-definition-validator.js +33 -0
  47. package/src/definition/datasource/datasource-definition-validator.spec.js +63 -0
  48. package/src/definition/datasource/datasource-definition.d.ts +7 -0
  49. package/src/definition/datasource/index.d.ts +2 -0
  50. package/src/definition/datasource/index.js +1 -0
  51. package/src/definition/definition-registry.d.ts +50 -0
  52. package/src/definition/definition-registry.js +98 -0
  53. package/src/definition/definition-registry.spec.js +78 -0
  54. package/src/definition/index.d.ts +3 -0
  55. package/src/definition/index.js +3 -0
  56. package/src/definition/model/index.d.ts +7 -0
  57. package/src/definition/model/index.js +6 -0
  58. package/src/definition/model/model-data-sanitizer.d.ts +15 -0
  59. package/src/definition/model/model-data-sanitizer.js +33 -0
  60. package/src/definition/model/model-data-validator.d.ts +32 -0
  61. package/src/definition/model/model-data-validator.js +144 -0
  62. package/src/definition/model/model-data-validator.spec.js +1889 -0
  63. package/src/definition/model/model-definition-utils.d.ts +161 -0
  64. package/src/definition/model/model-definition-utils.js +371 -0
  65. package/src/definition/model/model-definition-utils.spec.js +1474 -0
  66. package/src/definition/model/model-definition-validator.d.ts +14 -0
  67. package/src/definition/model/model-definition-validator.js +83 -0
  68. package/src/definition/model/model-definition-validator.spec.js +143 -0
  69. package/src/definition/model/model-definition.d.ts +28 -0
  70. package/src/definition/model/properties/data-type.d.ts +11 -0
  71. package/src/definition/model/properties/data-type.js +11 -0
  72. package/src/definition/model/properties/default-values-definition-validator.d.ts +15 -0
  73. package/src/definition/model/properties/default-values-definition-validator.js +53 -0
  74. package/src/definition/model/properties/default-values-definition-validator.spec.js +136 -0
  75. package/src/definition/model/properties/index.d.ts +5 -0
  76. package/src/definition/model/properties/index.js +4 -0
  77. package/src/definition/model/properties/primary-keys-definition-validator.d.ts +15 -0
  78. package/src/definition/model/properties/primary-keys-definition-validator.js +55 -0
  79. package/src/definition/model/properties/primary-keys-definition-validator.spec.js +145 -0
  80. package/src/definition/model/properties/properties-definition-validator.d.ts +15 -0
  81. package/src/definition/model/properties/properties-definition-validator.js +194 -0
  82. package/src/definition/model/properties/properties-definition-validator.spec.js +373 -0
  83. package/src/definition/model/properties/property-definition.d.ts +20 -0
  84. package/src/definition/model/relations/index.d.ts +3 -0
  85. package/src/definition/model/relations/index.js +2 -0
  86. package/src/definition/model/relations/relation-definition.d.ts +254 -0
  87. package/src/definition/model/relations/relation-type.d.ts +9 -0
  88. package/src/definition/model/relations/relation-type.js +9 -0
  89. package/src/definition/model/relations/relations-definition-validator.d.ts +15 -0
  90. package/src/definition/model/relations/relations-definition-validator.js +449 -0
  91. package/src/definition/model/relations/relations-definition-validator.spec.js +772 -0
  92. package/src/errors/index.d.ts +3 -0
  93. package/src/errors/index.js +3 -0
  94. package/src/errors/invalid-argument-error.d.ts +6 -0
  95. package/src/errors/invalid-argument-error.js +6 -0
  96. package/src/errors/invalid-argument-error.spec.js +33 -0
  97. package/src/errors/invalid-operator-value-error.d.ts +13 -0
  98. package/src/errors/invalid-operator-value-error.js +24 -0
  99. package/src/errors/invalid-operator-value-error.spec.js +11 -0
  100. package/src/errors/not-implemented-error.d.ts +6 -0
  101. package/src/errors/not-implemented-error.js +6 -0
  102. package/src/errors/not-implemented-error.spec.js +33 -0
  103. package/src/filter/fields-clause-tool.d.ts +38 -0
  104. package/src/filter/fields-clause-tool.js +88 -0
  105. package/src/filter/fields-clause-tool.spec.js +133 -0
  106. package/src/filter/filter.d.ts +335 -0
  107. package/src/filter/include-clause-tool.d.ts +53 -0
  108. package/src/filter/include-clause-tool.js +364 -0
  109. package/src/filter/include-clause-tool.spec.js +653 -0
  110. package/src/filter/index.d.ts +7 -0
  111. package/src/filter/index.js +6 -0
  112. package/src/filter/operator-clause-tool.d.ts +223 -0
  113. package/src/filter/operator-clause-tool.js +515 -0
  114. package/src/filter/operator-clause-tool.spec.js +1064 -0
  115. package/src/filter/order-clause-tool.d.ts +32 -0
  116. package/src/filter/order-clause-tool.js +97 -0
  117. package/src/filter/order-clause-tool.spec.js +438 -0
  118. package/src/filter/slice-clause-tool.d.ts +30 -0
  119. package/src/filter/slice-clause-tool.js +65 -0
  120. package/src/filter/slice-clause-tool.spec.js +117 -0
  121. package/src/filter/where-clause-tool.d.ts +23 -0
  122. package/src/filter/where-clause-tool.js +165 -0
  123. package/src/filter/where-clause-tool.spec.js +280 -0
  124. package/src/index.d.ts +9 -0
  125. package/src/index.js +8 -0
  126. package/src/relations/belongs-to-resolver.d.ts +46 -0
  127. package/src/relations/belongs-to-resolver.js +242 -0
  128. package/src/relations/belongs-to-resolver.spec.js +1047 -0
  129. package/src/relations/has-many-resolver.d.ts +67 -0
  130. package/src/relations/has-many-resolver.js +317 -0
  131. package/src/relations/has-many-resolver.spec.js +2911 -0
  132. package/src/relations/has-one-resolver.d.ts +67 -0
  133. package/src/relations/has-one-resolver.js +311 -0
  134. package/src/relations/has-one-resolver.spec.js +2274 -0
  135. package/src/relations/index.d.ts +4 -0
  136. package/src/relations/index.js +4 -0
  137. package/src/relations/references-many-resolver.d.ts +27 -0
  138. package/src/relations/references-many-resolver.js +113 -0
  139. package/src/relations/references-many-resolver.spec.js +631 -0
  140. package/src/repository/index.d.ts +2 -0
  141. package/src/repository/index.js +2 -0
  142. package/src/repository/repository-registry.d.ts +29 -0
  143. package/src/repository/repository-registry.js +57 -0
  144. package/src/repository/repository-registry.spec.js +38 -0
  145. package/src/repository/repository.d.ts +164 -0
  146. package/src/repository/repository.js +207 -0
  147. package/src/repository/repository.spec.js +202 -0
  148. package/src/schema.d.ts +37 -0
  149. package/src/schema.js +41 -0
  150. package/src/types.d.ts +30 -0
  151. package/src/utils/capitalize.d.ts +6 -0
  152. package/src/utils/capitalize.js +10 -0
  153. package/src/utils/capitalize.spec.js +14 -0
  154. package/src/utils/clone-deep.d.ts +6 -0
  155. package/src/utils/clone-deep.js +61 -0
  156. package/src/utils/clone-deep.spec.js +28 -0
  157. package/src/utils/exclude-object-keys.d.ts +10 -0
  158. package/src/utils/exclude-object-keys.js +20 -0
  159. package/src/utils/exclude-object-keys.spec.js +49 -0
  160. package/src/utils/get-ctor-name.d.ts +6 -0
  161. package/src/utils/get-ctor-name.js +11 -0
  162. package/src/utils/get-ctor-name.spec.js +17 -0
  163. package/src/utils/get-value-by-path.d.ts +12 -0
  164. package/src/utils/get-value-by-path.js +23 -0
  165. package/src/utils/get-value-by-path.spec.js +36 -0
  166. package/src/utils/index.d.ts +10 -0
  167. package/src/utils/index.js +10 -0
  168. package/src/utils/is-ctor.d.ts +7 -0
  169. package/src/utils/is-ctor.js +10 -0
  170. package/src/utils/is-ctor.spec.js +26 -0
  171. package/src/utils/is-pure-object.d.ts +6 -0
  172. package/src/utils/is-pure-object.js +15 -0
  173. package/src/utils/is-pure-object.spec.js +25 -0
  174. package/src/utils/select-object-keys.d.ts +10 -0
  175. package/src/utils/select-object-keys.js +37 -0
  176. package/src/utils/select-object-keys.spec.js +40 -0
  177. package/src/utils/singularize.d.ts +6 -0
  178. package/src/utils/singularize.js +22 -0
  179. package/src/utils/singularize.spec.js +23 -0
  180. package/src/utils/string-to-regexp.d.ts +10 -0
  181. package/src/utils/string-to-regexp.js +22 -0
  182. package/src/utils/string-to-regexp.spec.js +35 -0
  183. package/tsconfig.json +9 -0
package/src/schema.js ADDED
@@ -0,0 +1,41 @@
1
+ import {Service} from '@e22m4u/js-service';
2
+ import {Repository} from './repository/index.js';
3
+ import {DefinitionRegistry} from './definition/index.js';
4
+ import {RepositoryRegistry} from './repository/index.js';
5
+
6
+ /**
7
+ * Schema.
8
+ */
9
+ export class Schema extends Service {
10
+ /**
11
+ * Define datasource.
12
+ *
13
+ * @param {object} datasourceDef
14
+ * @returns {this}
15
+ */
16
+ defineDatasource(datasourceDef) {
17
+ this.getService(DefinitionRegistry).addDatasource(datasourceDef);
18
+ return this;
19
+ }
20
+
21
+ /**
22
+ * Define model.
23
+ *
24
+ * @param {object} modelDef
25
+ * @returns {this}
26
+ */
27
+ defineModel(modelDef) {
28
+ this.getService(DefinitionRegistry).addModel(modelDef);
29
+ return this;
30
+ }
31
+
32
+ /**
33
+ * Get repository.
34
+ *
35
+ * @param {string} modelName
36
+ * @returns {Repository}
37
+ */
38
+ getRepository(modelName) {
39
+ return this.getService(RepositoryRegistry).getRepository(modelName);
40
+ }
41
+ }
package/src/types.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Free-form object with open properties.
3
+ */
4
+ export declare type AnyObject = {
5
+ [property: string]: unknown;
6
+ };
7
+
8
+ /**
9
+ * Makes specific field as optional.
10
+ */
11
+ export declare type PartialBy<T, K extends keyof T> = Omit<T, K> &
12
+ Partial<Pick<T, K>>;
13
+
14
+ /**
15
+ * Model data.
16
+ */
17
+ export declare type ModelData = {
18
+ [property: string]: unknown;
19
+ };
20
+
21
+ /**
22
+ * Model id.
23
+ */
24
+ export declare type ModelId = unknown;
25
+
26
+ /**
27
+ * Flatten.
28
+ */
29
+ type Identity<T> = T;
30
+ export declare type Flatten<T> = Identity<{[k in keyof T]: T[k]}>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Capitalize.
3
+ *
4
+ * @param string
5
+ */
6
+ export declare function capitalize(string: string): string;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Capitalize.
3
+ *
4
+ * @param {string} string
5
+ * @returns {string}
6
+ */
7
+ export function capitalize(string) {
8
+ if (!string || typeof string !== 'string') return string;
9
+ return string.charAt(0).toUpperCase() + string.slice(1);
10
+ }
@@ -0,0 +1,14 @@
1
+ import {expect} from 'chai';
2
+ import {capitalize} from './capitalize.js';
3
+
4
+ describe('capitalize', function () {
5
+ it('makes the first letter to upper case', function () {
6
+ expect(capitalize('foo')).to.be.eq('Foo');
7
+ expect(capitalize('foo bar')).to.be.eq('Foo bar');
8
+ expect(capitalize(10)).to.be.eq(10);
9
+ expect(capitalize(true)).to.be.eq(true);
10
+ expect(capitalize(false)).to.be.eq(false);
11
+ expect(capitalize(undefined)).to.be.eq(undefined);
12
+ expect(capitalize(null)).to.be.eq(null);
13
+ });
14
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Clone deep.
3
+ *
4
+ * @param value
5
+ */
6
+ export declare function cloneDeep<T>(value: T): T;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Clone deep.
3
+ *
4
+ * @author https://stackoverflow.com/a/4460624
5
+ * @param {*} value
6
+ * @returns {*}
7
+ */
8
+ export function cloneDeep(value) {
9
+ if (!value) return value; // null, undefined values check
10
+
11
+ const types = [Number, String, Boolean];
12
+ let result;
13
+
14
+ // normalizing primitives if someone did new String('aaa'),
15
+ // or new Number('444');
16
+ types.forEach(type => {
17
+ if (value instanceof type) result = type(value);
18
+ });
19
+
20
+ if (result === undefined) {
21
+ if (Array.isArray(value)) {
22
+ result = [];
23
+ value.forEach((child, index) => {
24
+ result[index] = cloneDeep(child);
25
+ });
26
+ } else if (typeof value === 'object') {
27
+ // testing that this is DOM
28
+ if (
29
+ 'nodeType' in value &&
30
+ value.nodeType &&
31
+ 'cloneNode' in value &&
32
+ typeof value.cloneNode === 'function'
33
+ ) {
34
+ result = value.cloneNode(true);
35
+ // check that this is a literal
36
+ } else if (!('prototype' in value) || !value.prototype) {
37
+ if (value instanceof Date) {
38
+ result = new Date(value);
39
+ } else if (value.constructor && value.constructor.name === 'Object') {
40
+ // it is an object literal
41
+ result = {};
42
+ for (const key in value) {
43
+ result[key] = cloneDeep(value[key]);
44
+ }
45
+ } else {
46
+ // just keep the reference,
47
+ // or create new object
48
+ result = value;
49
+ }
50
+ } else {
51
+ // just keep the reference,
52
+ // or create new object
53
+ result = value;
54
+ }
55
+ } else {
56
+ result = value;
57
+ }
58
+ }
59
+
60
+ return result;
61
+ }
@@ -0,0 +1,28 @@
1
+ import {expect} from 'chai';
2
+ import {cloneDeep} from './clone-deep.js';
3
+
4
+ describe('cloneDeep', function () {
5
+ it('returns a deep copy of a given object', function () {
6
+ const value = {
7
+ stringProp: 'string',
8
+ numberProp: 10,
9
+ booleanProp: true,
10
+ arrayProp: [1, 2, 3],
11
+ objectProp: {
12
+ foo: 'string',
13
+ bar: 'string',
14
+ },
15
+ dateProp: new Date(),
16
+ nullProp: null,
17
+ };
18
+ const result = cloneDeep(value);
19
+ expect(result).to.be.eql(value);
20
+ expect(result).to.be.not.eq(value);
21
+ expect(result.arrayProp).to.be.not.eq(value.arrayProp);
22
+ expect(result.arrayProp).to.be.eql(value.arrayProp);
23
+ expect(result.objectProp).to.be.not.eq(value.objectProp);
24
+ expect(result.objectProp).to.be.eql(value.objectProp);
25
+ expect(result.dateProp).to.be.not.eq(value.dateProp);
26
+ expect(result.dateProp.getTime()).to.be.eq(value.dateProp.getTime());
27
+ });
28
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Exclude object keys.
3
+ *
4
+ * @param obj
5
+ * @param keys
6
+ */
7
+ export declare function excludeObjectKeys<T extends object>(
8
+ obj: T,
9
+ keys: string | string[],
10
+ ): Partial<T>;
@@ -0,0 +1,20 @@
1
+ import {InvalidArgumentError} from '../errors/index.js';
2
+
3
+ /**
4
+ * Exclude object keys.
5
+ *
6
+ * @param {object} obj
7
+ * @param {string|string[]} keys
8
+ * @returns {object}
9
+ */
10
+ export function excludeObjectKeys(obj, keys) {
11
+ if (typeof obj !== 'object' || !obj || Array.isArray(obj))
12
+ throw new InvalidArgumentError(
13
+ 'Cannot exclude keys from a non-Object value, %v given.',
14
+ obj,
15
+ );
16
+ const result = {...obj};
17
+ keys = Array.isArray(keys) ? keys : [keys];
18
+ keys.forEach(key => delete result[key]);
19
+ return result;
20
+ }
@@ -0,0 +1,49 @@
1
+ import {expect} from 'chai';
2
+ import {format} from '@e22m4u/js-format';
3
+ import {excludeObjectKeys} from './exclude-object-keys.js';
4
+
5
+ describe('excludeObjectKeys', function () {
6
+ it('returns a given object without a specified key', function () {
7
+ const input = {
8
+ foo: 'string',
9
+ bar: 10,
10
+ baz: true,
11
+ qux: [1, 2, 3],
12
+ };
13
+ const result = excludeObjectKeys(input, 'bar');
14
+ expect(result).to.be.not.eq(input);
15
+ expect(result).to.be.eql({
16
+ foo: 'string',
17
+ baz: true,
18
+ qux: [1, 2, 3],
19
+ });
20
+ });
21
+
22
+ it('returns a given object without a specified keys', function () {
23
+ const input = {
24
+ foo: 'string',
25
+ bar: 10,
26
+ baz: true,
27
+ qux: [1, 2, 3],
28
+ };
29
+ const result = excludeObjectKeys(input, ['bar', 'qux']);
30
+ expect(result).to.be.not.eq(input);
31
+ expect(result).to.be.eql({
32
+ foo: 'string',
33
+ baz: true,
34
+ });
35
+ });
36
+
37
+ it('throws an error for a non-object values', function () {
38
+ const throwable = v => () => excludeObjectKeys(v, 'key');
39
+ const error = v =>
40
+ format('Cannot exclude keys from a non-Object value, %s given.', v);
41
+ expect(throwable('str')).to.throw(error('"str"'));
42
+ expect(throwable(10)).to.throw(error('10'));
43
+ expect(throwable(true)).to.throw(error('true'));
44
+ expect(throwable(false)).to.throw(error('false'));
45
+ expect(throwable([])).to.throw(error('Array'));
46
+ expect(throwable(null)).to.throw(error('null'));
47
+ expect(throwable(undefined)).to.throw(error('undefined'));
48
+ });
49
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Get ctor name.
3
+ *
4
+ * @param value
5
+ */
6
+ export declare function getCtorName(value: unknown): string | undefined;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Get ctor name.
3
+ *
4
+ * @param {*} value
5
+ * @returns {string|undefined}
6
+ */
7
+ export function getCtorName(value) {
8
+ if (value === null) return 'Null';
9
+ if (value === undefined) return 'Undefined';
10
+ return (value.constructor && value.constructor.name) || undefined;
11
+ }
@@ -0,0 +1,17 @@
1
+ import {expect} from 'chai';
2
+ import {getCtorName} from './get-ctor-name.js';
3
+
4
+ describe('getCtorName', function () {
5
+ it('returns a constructor name of a given value', function () {
6
+ expect(getCtorName({})).to.be.eq('Object');
7
+ expect(getCtorName(new Date())).to.be.eq('Date');
8
+ expect(getCtorName([])).to.be.eq('Array');
9
+ expect(getCtorName(() => undefined)).to.be.eq('Function');
10
+ expect(getCtorName('string')).to.be.eq('String');
11
+ expect(getCtorName(10)).to.be.eq('Number');
12
+ expect(getCtorName(true)).to.be.eq('Boolean');
13
+ expect(getCtorName(false)).to.be.eq('Boolean');
14
+ expect(getCtorName(null)).to.be.eq('Null');
15
+ expect(getCtorName(undefined)).to.be.eq('Undefined');
16
+ });
17
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Get value by path.
3
+ *
4
+ * @param obj
5
+ * @param path
6
+ * @param orElse
7
+ */
8
+ export declare function getValueByPath(
9
+ obj: object,
10
+ path: string,
11
+ orElse?: unknown,
12
+ ): unknown;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Get value by path.
3
+ *
4
+ * @param {object} obj
5
+ * @param {string} path
6
+ * @param {*} orElse
7
+ * @returns {*}
8
+ */
9
+ export function getValueByPath(obj, path, orElse = undefined) {
10
+ if (!obj || typeof obj !== 'object') return orElse;
11
+ if (!path || typeof path !== 'string') return orElse;
12
+ const keys = path.split('.');
13
+ let value = obj;
14
+ for (const key of keys) {
15
+ if (typeof value === 'object' && value !== null && key in value) {
16
+ value = value[key];
17
+ } else {
18
+ value = orElse;
19
+ break;
20
+ }
21
+ }
22
+ return value;
23
+ }
@@ -0,0 +1,36 @@
1
+ import {expect} from 'chai';
2
+ import {getValueByPath} from './get-value-by-path.js';
3
+
4
+ const VAL = 'value';
5
+
6
+ describe('getValueByPath', function () {
7
+ it('returns undefined if no value', function () {
8
+ expect(getValueByPath({}, 'foo')).to.be.undefined;
9
+ expect(getValueByPath({}, 'foo.bar')).to.be.undefined;
10
+ expect(getValueByPath({foo: {}}, 'foo.bar.baz')).to.be.undefined;
11
+ });
12
+
13
+ it('returns a value by given path', function () {
14
+ expect(getValueByPath({foo: VAL}, 'foo')).to.be.eq(VAL);
15
+ expect(getValueByPath({foo: {bar: VAL}}, 'foo.bar')).to.be.eq(VAL);
16
+ expect(getValueByPath({foo: {bar: {baz: VAL}}}, 'foo.bar.baz')).to.be.eq(
17
+ VAL,
18
+ );
19
+ });
20
+
21
+ it('returns a given fallback if no value', function () {
22
+ expect(getValueByPath({}, 'foo', VAL)).to.be.eq(VAL);
23
+ expect(getValueByPath({}, 'foo.bar', VAL)).to.be.eq(VAL);
24
+ expect(getValueByPath({foo: {}}, 'foo.bar.baz', VAL)).to.be.eq(VAL);
25
+ });
26
+
27
+ it('returns a given fallback for null or undefined object', function () {
28
+ expect(getValueByPath(null, 'foo', VAL)).to.be.eq(VAL);
29
+ expect(getValueByPath(undefined, 'foo', VAL)).to.be.eq(VAL);
30
+ });
31
+
32
+ it('returns a given fallback for null or undefined key', function () {
33
+ expect(getValueByPath({}, null, VAL)).to.be.eq(VAL);
34
+ expect(getValueByPath({}, undefined, VAL)).to.be.eq(VAL);
35
+ });
36
+ });
@@ -0,0 +1,10 @@
1
+ export * from './is-ctor.js';
2
+ export * from './capitalize.js';
3
+ export * from './clone-deep.js';
4
+ export * from './singularize.js';
5
+ export * from './get-ctor-name.js';
6
+ export * from './is-pure-object.js';
7
+ export * from './string-to-regexp.js';
8
+ export * from './get-value-by-path.js';
9
+ export * from './select-object-keys.js';
10
+ export * from './exclude-object-keys.js';
@@ -0,0 +1,10 @@
1
+ export * from './is-ctor.js';
2
+ export * from './capitalize.js';
3
+ export * from './clone-deep.js';
4
+ export * from './singularize.js';
5
+ export * from './get-ctor-name.js';
6
+ export * from './is-pure-object.js';
7
+ export * from './string-to-regexp.js';
8
+ export * from './get-value-by-path.js';
9
+ export * from './select-object-keys.js';
10
+ export * from './exclude-object-keys.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Is ctor.
3
+ *
4
+ * @param {*} value
5
+ * @returns {boolean}
6
+ */
7
+ export declare function isCtor(value: unknown): boolean;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Is ctor.
3
+ *
4
+ * @param {*} value
5
+ * @returns {boolean}
6
+ */
7
+ export function isCtor(value) {
8
+ if (!value) return false;
9
+ return typeof value === 'function' && 'prototype' in value;
10
+ }
@@ -0,0 +1,26 @@
1
+ import {expect} from 'chai';
2
+ import {isCtor} from './is-ctor.js';
3
+
4
+ describe('isConstructor', function () {
5
+ it('returns true if a given value is a constructor', function () {
6
+ expect(isCtor(Date)).to.be.true;
7
+ expect(isCtor(Number)).to.be.true;
8
+ expect(isCtor(String)).to.be.true;
9
+ class MyClass {}
10
+ expect(isCtor(MyClass)).to.be.true;
11
+ // eslint-disable-next-line jsdoc/require-jsdoc
12
+ function FunctionCtor() {}
13
+ expect(isCtor(FunctionCtor)).to.be.true;
14
+ });
15
+
16
+ it('returns false if a given value is not a constructor', function () {
17
+ expect(isCtor(() => undefined)).to.be.false;
18
+ expect(isCtor('string')).to.be.false;
19
+ expect(isCtor(10)).to.be.false;
20
+ expect(isCtor(true)).to.be.false;
21
+ expect(isCtor({})).to.be.false;
22
+ expect(isCtor([])).to.be.false;
23
+ expect(isCtor(undefined)).to.be.false;
24
+ expect(isCtor(null)).to.be.false;
25
+ });
26
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Is pure object.
3
+ *
4
+ * @param value
5
+ */
6
+ export declare function isPureObject(value: unknown): boolean;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Is pure object.
3
+ *
4
+ * @param {*} value
5
+ * @returns {boolean}
6
+ */
7
+ export function isPureObject(value) {
8
+ return Boolean(
9
+ typeof value === 'object' &&
10
+ value &&
11
+ !Array.isArray(value) &&
12
+ (!value.constructor ||
13
+ (value.constructor && value.constructor.name === 'Object')),
14
+ );
15
+ }
@@ -0,0 +1,25 @@
1
+ import {expect} from 'chai';
2
+ import {isPureObject} from './is-pure-object.js';
3
+
4
+ describe('isPureObject', function () {
5
+ it('returns ture if a pure object given', function () {
6
+ expect(isPureObject({})).to.be.true;
7
+ expect(isPureObject({key: 'val'})).to.be.true;
8
+ expect(isPureObject(Object.create(null))).to.be.true;
9
+ });
10
+
11
+ it('returns false if a non-pure object given', function () {
12
+ expect(isPureObject(new Date())).to.be.false;
13
+ expect(isPureObject([])).to.be.false;
14
+ expect(isPureObject(() => undefined)).to.be.false;
15
+ });
16
+
17
+ it('returns false if a non-object given', function () {
18
+ expect(isPureObject('string')).to.be.false;
19
+ expect(isPureObject(10)).to.be.false;
20
+ expect(isPureObject(true)).to.be.false;
21
+ expect(isPureObject(false)).to.be.false;
22
+ expect(isPureObject(null)).to.be.false;
23
+ expect(isPureObject(undefined)).to.be.false;
24
+ });
25
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Select object keys.
3
+ *
4
+ * @param obj
5
+ * @param keys
6
+ */
7
+ export declare function selectObjectKeys<T extends object>(
8
+ obj: T,
9
+ keys: string[],
10
+ ): Partial<T>;
@@ -0,0 +1,37 @@
1
+ import {InvalidArgumentError} from '../errors/index.js';
2
+
3
+ /**
4
+ * Select object keys.
5
+ *
6
+ * @param {object} obj
7
+ * @param {string[]} keys
8
+ * @returns {object}
9
+ */
10
+ export function selectObjectKeys(obj, keys) {
11
+ if (!obj || typeof obj !== 'object' || Array.isArray(obj))
12
+ throw new InvalidArgumentError(
13
+ 'A first argument of selectObjectKeys ' +
14
+ 'should be an Object, but %v given.',
15
+ obj,
16
+ );
17
+ if (!Array.isArray(keys))
18
+ throw new InvalidArgumentError(
19
+ 'A second argument of selectObjectKeys ' +
20
+ 'should be an Array of String, but %v given.',
21
+ keys,
22
+ );
23
+ keys.forEach(key => {
24
+ if (typeof key !== 'string')
25
+ throw new InvalidArgumentError(
26
+ 'A second argument of selectObjectKeys ' +
27
+ 'should be an Array of String, but %v given.',
28
+ key,
29
+ );
30
+ });
31
+ const result = {};
32
+ const allKeys = Object.keys(obj);
33
+ allKeys.forEach(key => {
34
+ if (keys.includes(key)) result[key] = obj[key];
35
+ });
36
+ return result;
37
+ }
@@ -0,0 +1,40 @@
1
+ import {expect} from 'chai';
2
+ import {selectObjectKeys} from './select-object-keys.js';
3
+
4
+ describe('selectObjectKeys', function () {
5
+ it('returns a new object with selected fields', function () {
6
+ const input = {foo: 'foo', bar: 'bar', baz: 'baz'};
7
+ const result = selectObjectKeys(input, ['bar', 'baz']);
8
+ expect(result).to.be.eql({bar: 'bar', baz: 'baz'});
9
+ });
10
+
11
+ it('does not throw an error if a selected property is not found', function () {
12
+ const input = {foo: 'foo', bar: 'bar', baz: 'baz'};
13
+ const result = selectObjectKeys(input, ['bar', 'qux']);
14
+ expect(result).to.be.eql({bar: 'bar'});
15
+ });
16
+
17
+ it('throws an error if a given value is not an Object', function () {
18
+ const throwable = () => selectObjectKeys(10, ['key']);
19
+ expect(throwable).to.throw(
20
+ 'A first argument of selectObjectKeys ' +
21
+ 'should be an Object, but 10 given.',
22
+ );
23
+ });
24
+
25
+ it('throws an error if a given keys is not an Array', function () {
26
+ const throwable = () => selectObjectKeys({});
27
+ expect(throwable).to.throw(
28
+ 'A second argument of selectObjectKeys ' +
29
+ 'should be an Array of String, but undefined given.',
30
+ );
31
+ });
32
+
33
+ it('throws an error if a given keys is not an String', function () {
34
+ const throwable = () => selectObjectKeys({}, [10]);
35
+ expect(throwable).to.throw(
36
+ 'A second argument of selectObjectKeys ' +
37
+ 'should be an Array of String, but 10 given.',
38
+ );
39
+ });
40
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Singularize.
3
+ *
4
+ * @param noun
5
+ */
6
+ export declare function singularize(noun: string): string;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Singularize.
3
+ *
4
+ * @param {string} noun
5
+ * @returns {string}
6
+ */
7
+ export function singularize(noun) {
8
+ if (!noun || typeof noun !== 'string') return noun;
9
+ const endings = {
10
+ ves: 'fe',
11
+ ies: 'y',
12
+ i: 'us',
13
+ zes: 'ze',
14
+ ses: 's',
15
+ es: 'e',
16
+ s: '',
17
+ };
18
+ return noun.replace(
19
+ new RegExp(`(${Object.keys(endings).join('|')})$`),
20
+ r => endings[r],
21
+ );
22
+ }
@@ -0,0 +1,23 @@
1
+ import {expect} from 'chai';
2
+ import {singularize} from './singularize.js';
3
+
4
+ describe('singularize', function () {
5
+ it('returns a singular noun', function () {
6
+ expect(singularize('papers')).to.be.eq('paper');
7
+ expect(singularize('strategies')).to.be.eq('strategy');
8
+ expect(singularize('lives')).to.be.eq('life');
9
+ expect(singularize('games')).to.be.eq('game');
10
+ expect(singularize('cacti')).to.be.eq('cactus');
11
+ expect(singularize('dozes')).to.be.eq('doze');
12
+ });
13
+
14
+ it('returns non-string values as is', function () {
15
+ expect(singularize(10)).to.be.eq(10);
16
+ expect(singularize([])).to.be.eql([]);
17
+ expect(singularize({})).to.be.eql({});
18
+ expect(singularize(true)).to.be.eq(true);
19
+ expect(singularize(false)).to.be.eq(false);
20
+ expect(singularize(undefined)).to.be.eq(undefined);
21
+ expect(singularize(null)).to.be.eq(null);
22
+ });
23
+ });