functional-models 1.0.25 → 1.1.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/.eslintignore +1 -0
- package/.eslintrc +9 -15
- package/cucumber.js +10 -0
- package/features/arrayFields.feature +7 -7
- package/features/basic-ts.feature +13 -0
- package/features/functions.feature +2 -3
- package/package.json +34 -10
- package/src/constants.ts +15 -0
- package/src/errors.ts +18 -0
- package/src/index.ts +11 -0
- package/src/interfaces.ts +323 -0
- package/src/lazy.ts +24 -0
- package/src/methods.ts +30 -0
- package/src/models.ts +183 -0
- package/src/properties.ts +375 -0
- package/src/serialization.ts +39 -0
- package/src/utils.ts +42 -0
- package/src/validation.ts +390 -0
- package/{features/stepDefinitions/steps.js → stepDefinitions/oldSteps.ts} +76 -53
- package/stepDefinitions/tssteps.ts +107 -0
- package/test/src/errors.test.ts +31 -0
- package/test/src/{lazy.test.js → lazy.test.ts} +4 -4
- package/test/src/methods.test.ts +45 -0
- package/test/src/models.test.ts +417 -0
- package/test/src/{properties.test.js → properties.test.ts} +257 -64
- package/test/src/serialization.test.ts +80 -0
- package/test/src/{utils.test.js → utils.test.ts} +29 -7
- package/test/src/{validation.test.js → validation.test.ts} +278 -210
- package/tsconfig.json +100 -0
- package/src/functions.js +0 -7
- package/src/index.js +0 -7
- package/src/lazy.js +0 -19
- package/src/models.js +0 -150
- package/src/properties.js +0 -261
- package/src/serialization.js +0 -47
- package/src/utils.js +0 -42
- package/src/validation.js +0 -274
- package/test/base/index.test.js +0 -5
- package/test/src/functions.test.js +0 -45
- package/test/src/index.test.js +0 -5
- package/test/src/models.test.js +0 -380
- package/test/src/serialization.test.js +0 -127
package/.eslintignore
CHANGED
package/.eslintrc
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": ["eslint:recommended", "prettier"],
|
|
3
|
-
"plugins": ["import", "functional"],
|
|
3
|
+
"plugins": ["import", "functional", "@typescript-eslint"],
|
|
4
4
|
"env": {
|
|
5
5
|
"browser": true,
|
|
6
6
|
"node": true,
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"es6": true
|
|
9
9
|
},
|
|
10
10
|
"rules": {
|
|
11
|
+
"@typescript-eslint/no-unused-vars": "error",
|
|
12
|
+
|
|
11
13
|
"no-await-in-loop": ["error"],
|
|
12
14
|
"no-console": ["error", { "allow": ["warn", "error", "info"] }],
|
|
13
15
|
"no-constant-condition": ["error"],
|
|
@@ -91,15 +93,7 @@
|
|
|
91
93
|
"linebreak-style": 0,
|
|
92
94
|
"no-underscore-dangle": 0,
|
|
93
95
|
"no-unused-labels": 0,
|
|
94
|
-
"no-unused-vars":
|
|
95
|
-
"error",
|
|
96
|
-
{
|
|
97
|
-
"vars": "all",
|
|
98
|
-
"argsIgnorePattern": "(_+)|(action)",
|
|
99
|
-
"args": "after-used",
|
|
100
|
-
"ignoreRestSiblings": false
|
|
101
|
-
}
|
|
102
|
-
],
|
|
96
|
+
"no-unused-vars": 0,
|
|
103
97
|
"object-shorthand": 0,
|
|
104
98
|
"prefer-rest-params": 0,
|
|
105
99
|
"semi": ["error", "never"],
|
|
@@ -114,13 +108,13 @@
|
|
|
114
108
|
"functional/no-method-signature": ["error"],
|
|
115
109
|
"functional/prefer-readonly-type": ["error"],
|
|
116
110
|
"functional/no-class": ["error"],
|
|
117
|
-
"functional/no-mixed-type":
|
|
111
|
+
"functional/no-mixed-type": 0,
|
|
118
112
|
"functional/no-this-expression": ["error"],
|
|
119
|
-
"functional/prefer-type-literal":
|
|
113
|
+
"functional/prefer-type-literal": 0,
|
|
120
114
|
"functional/no-conditional-statement": 0,
|
|
121
115
|
"functional/no-expression-statement": 0,
|
|
122
116
|
"functional/no-loop-statement": ["error"],
|
|
123
|
-
"functional/no-return-void":
|
|
117
|
+
"functional/no-return-void": 0,
|
|
124
118
|
"functional/no-promise-reject": 0,
|
|
125
119
|
"functional/no-throw-statement": 0,
|
|
126
120
|
"functional/no-try-statement": ["error"],
|
|
@@ -156,7 +150,7 @@
|
|
|
156
150
|
"import/first": ["error"],
|
|
157
151
|
"import/exports-last": ["error"],
|
|
158
152
|
"import/no-duplicates": ["error"],
|
|
159
|
-
"import/no-namespace":
|
|
153
|
+
"import/no-namespace": 0,
|
|
160
154
|
"import/extensions": ["error"],
|
|
161
155
|
"import/order": ["error"],
|
|
162
156
|
"import/newline-after-import": ["error"],
|
|
@@ -182,5 +176,5 @@
|
|
|
182
176
|
"jsx": true
|
|
183
177
|
}
|
|
184
178
|
},
|
|
185
|
-
"parser": "
|
|
179
|
+
"parser": "@typescript-eslint/parser"
|
|
186
180
|
}
|
package/cucumber.js
ADDED
|
@@ -3,7 +3,7 @@ Feature: Array Property
|
|
|
3
3
|
Scenario: A model that has an array property is created holding an array of integers and is checked and validated
|
|
4
4
|
Given ArrayModel1 model is used
|
|
5
5
|
When ArrayModelData1 data is inserted
|
|
6
|
-
Then the
|
|
6
|
+
Then the ArrayProperty property is called on the model
|
|
7
7
|
Then the array values match
|
|
8
8
|
| array | [1,2,3,4,5]|
|
|
9
9
|
Then functions.validate is called
|
|
@@ -12,41 +12,41 @@ Feature: Array Property
|
|
|
12
12
|
Scenario: A model that has an array property but has a non-array inserted into it fails validation
|
|
13
13
|
Given ArrayModel1 model is used
|
|
14
14
|
When ArrayModelData2 data is inserted
|
|
15
|
-
Then the
|
|
15
|
+
Then the ArrayProperty property is called on the model
|
|
16
16
|
Then functions.validate is called
|
|
17
17
|
Then an array of 1 errors is shown
|
|
18
18
|
|
|
19
19
|
Scenario: A model that has an array property for integers but an array of strings is inserted into it, it should fail validation
|
|
20
20
|
Given ArrayModel1 model is used
|
|
21
21
|
When ArrayModelData3 data is inserted
|
|
22
|
-
Then the
|
|
22
|
+
Then the ArrayProperty property is called on the model
|
|
23
23
|
Then functions.validate is called
|
|
24
24
|
Then an array of 1 errors is shown
|
|
25
25
|
|
|
26
26
|
Scenario: A model that has an array property that has mixed values it should not fail validation.
|
|
27
27
|
Given ArrayModel2 model is used
|
|
28
28
|
When ArrayModelData4 data is inserted
|
|
29
|
-
Then the
|
|
29
|
+
Then the ArrayProperty property is called on the model
|
|
30
30
|
Then functions.validate is called
|
|
31
31
|
Then an array of 0 errors is shown
|
|
32
32
|
|
|
33
33
|
Scenario: A model that uses the arrayProperty property that has mixed values it should not fail validation.
|
|
34
34
|
Given ArrayModel3 model is used
|
|
35
35
|
When ArrayModelData4 data is inserted
|
|
36
|
-
Then the
|
|
36
|
+
Then the ArrayProperty property is called on the model
|
|
37
37
|
Then functions.validate is called
|
|
38
38
|
Then an array of 0 errors is shown
|
|
39
39
|
|
|
40
40
|
Scenario: A model that uses the arrayProperty with the choice validator should pass validation with no errors.
|
|
41
41
|
Given ArrayModel4 model is used
|
|
42
42
|
When ArrayModelData5 data is inserted
|
|
43
|
-
Then the
|
|
43
|
+
Then the ArrayProperty property is called on the model
|
|
44
44
|
Then functions.validate is called
|
|
45
45
|
Then an array of 0 errors is shown
|
|
46
46
|
|
|
47
47
|
Scenario: A model that uses the arrayProperty with the choice validator should fail validation when one value is outside the choices
|
|
48
48
|
Given ArrayModel4 model is used
|
|
49
49
|
When ArrayModelData6 data is inserted
|
|
50
|
-
Then the
|
|
50
|
+
Then the ArrayProperty property is called on the model
|
|
51
51
|
Then functions.validate is called
|
|
52
52
|
Then an array of 1 errors is shown
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Feature: Basic Typescript
|
|
2
|
+
|
|
3
|
+
Scenario: A model with properties, instance methods, and model methods works as expected.
|
|
4
|
+
Given model TE_FULL_TEST is used
|
|
5
|
+
When a model instanced is created is called on model with TE_FULL_TEST_1
|
|
6
|
+
And toObj is called on the model instance
|
|
7
|
+
And instance method myMethod is called
|
|
8
|
+
And model method myModelMethod is called
|
|
9
|
+
And validate is called on model instance
|
|
10
|
+
Then the results match TE_FULL_TEST_1 obj data
|
|
11
|
+
And the result of instance method is InstanceMethod
|
|
12
|
+
And the result of model method is ModelMethod
|
|
13
|
+
And the model instance validated successfully
|
|
@@ -3,9 +3,8 @@ Feature: Functions
|
|
|
3
3
|
Scenario: A model with 2 properties (name, id), 2 model functions (modelWrapper, toString), and 2 instance functions (toString, toJson)
|
|
4
4
|
Given FunctionModel1 model is used
|
|
5
5
|
When FunctionModelData1 data is inserted
|
|
6
|
-
Then
|
|
7
|
-
And
|
|
6
|
+
Then name property is found
|
|
7
|
+
And id property is found
|
|
8
8
|
And toString instance function is found
|
|
9
9
|
And toJson instance function is found
|
|
10
10
|
And modelWrapper model function is found
|
|
11
|
-
And toString model function is found
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "functional-models",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A library for creating JavaScript function based models.",
|
|
5
|
-
"main": "index.js",
|
|
5
|
+
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "
|
|
8
|
-
"
|
|
7
|
+
"test": "mocha -r ts-node/register test/**/*.test.ts",
|
|
8
|
+
"test:coverage": "nyc npm run test",
|
|
9
|
+
"feature-tests": "./node_modules/.bin/cucumber-js -p default",
|
|
9
10
|
"coverage": "nyc --all --reporter=lcov npm test"
|
|
10
11
|
},
|
|
11
12
|
"repository": {
|
|
@@ -27,18 +28,37 @@
|
|
|
27
28
|
},
|
|
28
29
|
"homepage": "https://github.com/monolithst/functional-models#readme",
|
|
29
30
|
"nyc": {
|
|
31
|
+
"extends": "@istanbuljs/nyc-config-typescript",
|
|
32
|
+
"check-coverage": true,
|
|
30
33
|
"all": true,
|
|
34
|
+
"include": [
|
|
35
|
+
"src/**/!(*.test.*).[tj]s?(x)"
|
|
36
|
+
],
|
|
31
37
|
"exclude": [
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
"src/_tests_/**/*.*"
|
|
39
|
+
],
|
|
40
|
+
"reporter": [
|
|
41
|
+
"html",
|
|
42
|
+
"lcov",
|
|
43
|
+
"text",
|
|
44
|
+
"text-summary"
|
|
45
|
+
],
|
|
46
|
+
"report-dir": "coverage"
|
|
36
47
|
},
|
|
37
48
|
"devDependencies": {
|
|
49
|
+
"@cucumber/cucumber": "^8.0.0-rc.1",
|
|
50
|
+
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
|
51
|
+
"@types/async-lock": "^1.1.3",
|
|
52
|
+
"@types/chai": "^4.2.22",
|
|
53
|
+
"@types/chai-as-promised": "^7.1.4",
|
|
54
|
+
"@types/lodash": "^4.14.176",
|
|
55
|
+
"@types/mocha": "^9.0.0",
|
|
56
|
+
"@types/proxyquire": "^1.3.28",
|
|
57
|
+
"@types/sinon": "^10.0.6",
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "^5.6.0",
|
|
38
59
|
"babel-eslint": "^10.1.0",
|
|
39
60
|
"chai": "^4.3.0",
|
|
40
61
|
"chai-as-promised": "^7.1.1",
|
|
41
|
-
"cucumber": "^7.0.0-rc.0",
|
|
42
62
|
"eslint": "^7.19.0",
|
|
43
63
|
"eslint-config-prettier": "^7.2.0",
|
|
44
64
|
"eslint-plugin-functional": "^3.2.1",
|
|
@@ -46,9 +66,13 @@
|
|
|
46
66
|
"mocha": "^8.2.1",
|
|
47
67
|
"nyc": "^15.1.0",
|
|
48
68
|
"proxyquire": "^2.1.3",
|
|
49
|
-
"sinon": "^11.1.2"
|
|
69
|
+
"sinon": "^11.1.2",
|
|
70
|
+
"ts-node": "^10.4.0",
|
|
71
|
+
"typescript": "^4.4.4"
|
|
50
72
|
},
|
|
51
73
|
"dependencies": {
|
|
74
|
+
"@typescript-eslint/parser": "^5.3.0",
|
|
75
|
+
"async-lock": "^1.3.0",
|
|
52
76
|
"get-random-values": "^1.2.2",
|
|
53
77
|
"lodash": "^4.17.21"
|
|
54
78
|
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
enum PROPERTY_TYPES {
|
|
2
|
+
UniqueId = 'UniqueId',
|
|
3
|
+
DateProperty = 'DateProperty',
|
|
4
|
+
ArrayProperty = 'ArrayProperty',
|
|
5
|
+
ReferenceProperty = 'ReferenceProperty',
|
|
6
|
+
IntegerProperty = 'IntegerProperty',
|
|
7
|
+
TextProperty = 'TextProperty',
|
|
8
|
+
ConstantValueProperty = 'ConstantValueProperty',
|
|
9
|
+
NumberProperty = 'NumberProperty',
|
|
10
|
+
ObjectProperty = 'ObjectProperty',
|
|
11
|
+
EmailProperty = 'EmailProperty',
|
|
12
|
+
BooleanProperty = 'BooleanProperty',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { PROPERTY_TYPES }
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type KeysToErrors = { [s: string]: string[] }
|
|
2
|
+
|
|
3
|
+
/* eslint-disable functional/no-this-expression */
|
|
4
|
+
/* eslint-disable functional/no-class */
|
|
5
|
+
class ValidationError extends Error {
|
|
6
|
+
public modelName: String
|
|
7
|
+
public keysToErrors: KeysToErrors
|
|
8
|
+
constructor(modelName: String, keysToErrors: KeysToErrors) {
|
|
9
|
+
super(`${modelName} did not pass validation`)
|
|
10
|
+
this.name = 'ValidationError'
|
|
11
|
+
this.modelName = modelName
|
|
12
|
+
this.keysToErrors = keysToErrors
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/* eslint-enable functional/no-this-expression */
|
|
16
|
+
/* eslint-enable functional/no-class */
|
|
17
|
+
|
|
18
|
+
export { ValidationError }
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as constants from './constants'
|
|
2
|
+
import * as errors from './errors'
|
|
3
|
+
import * as validation from './validation'
|
|
4
|
+
import * as serialization from './serialization'
|
|
5
|
+
import * as utils from './utils'
|
|
6
|
+
|
|
7
|
+
export * from './models'
|
|
8
|
+
export * from './properties'
|
|
9
|
+
export * from './methods'
|
|
10
|
+
|
|
11
|
+
export { constants, errors, validation, serialization, utils }
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
|
|
3
|
+
type MaybeFunction<T> = T | (() => T)
|
|
4
|
+
type MaybePromise<T> = T | Promise<T>
|
|
5
|
+
type Nullable<T> = T | null
|
|
6
|
+
type Maybe<T> = T | undefined | null
|
|
7
|
+
type Arrayable<T> = T | readonly T[]
|
|
8
|
+
type MaybeLazy<T> = Maybe<Promise<T>>
|
|
9
|
+
type MaybeEmpty<T> = T | null | undefined
|
|
10
|
+
type JsonAble =
|
|
11
|
+
| number
|
|
12
|
+
| string
|
|
13
|
+
| boolean
|
|
14
|
+
| null
|
|
15
|
+
| Arrayable<{ readonly [s: string]: JsonAble }>
|
|
16
|
+
type VeryPrimitivesTypes = null | string | number | boolean
|
|
17
|
+
type toObj = () => Promise<JsonAble>
|
|
18
|
+
|
|
19
|
+
type ValueIsOfType<T, V> = {
|
|
20
|
+
readonly [P in keyof T as T[P] extends V ? P : never]: T[P]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type ValueIsNotOfType<T, V> = {
|
|
24
|
+
readonly [P in keyof T as T[P] extends V ? never : P]: T[P]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type InstanceMethodGetters<T> = {
|
|
28
|
+
readonly [P in keyof T as T[P] extends ModelInstanceMethod
|
|
29
|
+
? P
|
|
30
|
+
: never]: ModelInstanceMethodClient
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type ModelMethodGetters<T> = {
|
|
34
|
+
readonly [P in keyof T as T[P] extends ModelMethod
|
|
35
|
+
? P
|
|
36
|
+
: never]: ModelMethodClient
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type ModelMethodTypes<T extends FunctionalModel> =
|
|
40
|
+
| ModelMethod
|
|
41
|
+
| ModelInstanceMethod
|
|
42
|
+
| ModelMethodTyped<T>
|
|
43
|
+
| ModelInstanceMethodTyped<T>
|
|
44
|
+
|
|
45
|
+
type PropertyGetters<T extends FunctionalModel> = {
|
|
46
|
+
readonly // NOTE: This is NOT ModelMethodTypes, its getting everything but.
|
|
47
|
+
[Property in keyof T as T[Property] extends ModelMethodTypes<T>
|
|
48
|
+
? never
|
|
49
|
+
: Property]: () => T[Property] | Promise<T[Property]>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type FunctionalModel =
|
|
53
|
+
| {
|
|
54
|
+
readonly [s: string]:
|
|
55
|
+
| Arrayable<number>
|
|
56
|
+
| Arrayable<string>
|
|
57
|
+
| Arrayable<boolean>
|
|
58
|
+
| Arrayable<null>
|
|
59
|
+
| Arrayable<FunctionalModel>
|
|
60
|
+
| Arrayable<Date>
|
|
61
|
+
| Arrayable<undefined>
|
|
62
|
+
| ReferenceValueType<any>
|
|
63
|
+
| ModelInstanceMethod
|
|
64
|
+
| ModelMethod
|
|
65
|
+
}
|
|
66
|
+
| JsonAble
|
|
67
|
+
|
|
68
|
+
type FunctionalType =
|
|
69
|
+
| JsonAble
|
|
70
|
+
| (() => FunctionalType)
|
|
71
|
+
| Arrayable<undefined>
|
|
72
|
+
| Arrayable<Date>
|
|
73
|
+
| Arrayable<FunctionalModel>
|
|
74
|
+
| Arrayable<{ readonly [s: string]: JsonAble }>
|
|
75
|
+
|
|
76
|
+
type ModelInstanceInputData<T extends FunctionalModel> = ValueIsNotOfType<
|
|
77
|
+
T,
|
|
78
|
+
ModelMethodTypes<T>
|
|
79
|
+
>
|
|
80
|
+
|
|
81
|
+
type PropertyValidatorComponentTypeAdvanced<
|
|
82
|
+
TValue,
|
|
83
|
+
TModel extends FunctionalModel
|
|
84
|
+
> = (
|
|
85
|
+
value: TValue,
|
|
86
|
+
instance: ModelInstance<TModel>,
|
|
87
|
+
instanceData: FunctionalModel
|
|
88
|
+
) => string | undefined
|
|
89
|
+
|
|
90
|
+
type PropertyValidatorComponentType<TValue> = (
|
|
91
|
+
value: TValue,
|
|
92
|
+
instance: ModelInstance<any>,
|
|
93
|
+
instanceData: FunctionalModel
|
|
94
|
+
) => string | undefined
|
|
95
|
+
|
|
96
|
+
type PropertyValidatorComponentSync = PropertyValidatorComponentType<any>
|
|
97
|
+
|
|
98
|
+
type PropertyValidatorComponentAsync = (
|
|
99
|
+
value: Arrayable<FunctionalModel>,
|
|
100
|
+
instance: ModelInstance<any>,
|
|
101
|
+
instanceData: FunctionalModel
|
|
102
|
+
) => Promise<string | undefined>
|
|
103
|
+
|
|
104
|
+
type PropertyValidatorComponent =
|
|
105
|
+
| PropertyValidatorComponentSync
|
|
106
|
+
| PropertyValidatorComponentAsync
|
|
107
|
+
|
|
108
|
+
type PropertyValidator = (
|
|
109
|
+
instance: ModelInstance<any>,
|
|
110
|
+
instanceData: FunctionalModel
|
|
111
|
+
) => Promise<ValidationErrors>
|
|
112
|
+
|
|
113
|
+
type ValidationError = string | undefined
|
|
114
|
+
type ValidationErrors = readonly ValidationError[]
|
|
115
|
+
type ModelError = string
|
|
116
|
+
type ModelErrors = {
|
|
117
|
+
readonly [s: string]: readonly ModelError[]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
type ModelComponentValidator = (
|
|
121
|
+
instance: ModelInstance<any>,
|
|
122
|
+
instanceData: FunctionalModel,
|
|
123
|
+
options?: object
|
|
124
|
+
) => Promise<ValidationErrors>
|
|
125
|
+
|
|
126
|
+
type ValueGetter = () =>
|
|
127
|
+
| MaybePromise<Arrayable<FunctionalType>>
|
|
128
|
+
| MaybePromise<ModelInstance<any>>
|
|
129
|
+
|
|
130
|
+
type PropertyInstance<T extends Arrayable<FunctionalType>> = {
|
|
131
|
+
readonly getConfig: () => object
|
|
132
|
+
readonly getChoices: () => readonly VeryPrimitivesTypes[]
|
|
133
|
+
readonly getDefaultValue: () => T
|
|
134
|
+
readonly getConstantValue: () => T
|
|
135
|
+
readonly getPropertyType: () => string
|
|
136
|
+
readonly createGetter: (value: T) => ValueGetter
|
|
137
|
+
readonly getValidator: (valueGetter: ValueGetter) => PropertyValidator
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
type PropertiesList<T> = {
|
|
141
|
+
readonly [P in keyof T as T[P] extends Arrayable<FunctionalType>
|
|
142
|
+
? P
|
|
143
|
+
: never]: PropertyInstance<any>
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface ReferencePropertyInstance<T extends FunctionalModel>
|
|
147
|
+
extends PropertyInstance<ModelInstance<T> | T | MaybeEmpty<PrimaryKeyType>> {
|
|
148
|
+
readonly getReferencedId: (
|
|
149
|
+
instanceValues: ReferenceValueType<T>
|
|
150
|
+
) => MaybeEmpty<PrimaryKeyType>
|
|
151
|
+
readonly getReferencedModel: () => Model<T>
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
type ReferenceValueType<T extends FunctionalModel> =
|
|
155
|
+
| ModelInstance<T>
|
|
156
|
+
| ModelInstanceInputData<T>
|
|
157
|
+
| string
|
|
158
|
+
| number
|
|
159
|
+
| null
|
|
160
|
+
| undefined
|
|
161
|
+
|
|
162
|
+
type DefaultPropertyValidators = {
|
|
163
|
+
readonly required?: boolean
|
|
164
|
+
readonly isInteger?: boolean
|
|
165
|
+
readonly isNumber?: boolean
|
|
166
|
+
readonly isString?: boolean
|
|
167
|
+
readonly isArray?: boolean
|
|
168
|
+
readonly isBoolean?: boolean
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
type PropertyConfigContents = {
|
|
172
|
+
readonly type?: string
|
|
173
|
+
readonly defaultValue?: Arrayable<FunctionalType>
|
|
174
|
+
readonly value?: Arrayable<FunctionalType>
|
|
175
|
+
readonly choices?: readonly VeryPrimitivesTypes[]
|
|
176
|
+
readonly lazyLoadMethod?: (
|
|
177
|
+
value: Arrayable<FunctionalType>
|
|
178
|
+
) => MaybeLazy<Arrayable<FunctionalType>>
|
|
179
|
+
readonly valueSelector?: (
|
|
180
|
+
instanceValue: MaybePromise<Arrayable<FunctionalType>>
|
|
181
|
+
) => Arrayable<FunctionalType>
|
|
182
|
+
readonly validators?: readonly PropertyValidatorComponent[]
|
|
183
|
+
readonly maxLength?: number
|
|
184
|
+
readonly minLength?: number
|
|
185
|
+
readonly maxValue?: number
|
|
186
|
+
readonly minValue?: number
|
|
187
|
+
readonly autoNow?: boolean
|
|
188
|
+
readonly fetcher?: (
|
|
189
|
+
model: Model<any>,
|
|
190
|
+
primaryKey: PrimaryKeyType
|
|
191
|
+
) => Promise<any>
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
type PropertyConfig =
|
|
195
|
+
| (PropertyConfigContents & DefaultPropertyValidators)
|
|
196
|
+
| undefined
|
|
197
|
+
|
|
198
|
+
type PrimaryKeyPropertyInstanceType =
|
|
199
|
+
| PropertyInstance<string>
|
|
200
|
+
| PropertyInstance<number>
|
|
201
|
+
type PrimaryKeyType = string | number
|
|
202
|
+
|
|
203
|
+
type ModelMethods<T extends FunctionalModel> = ValueIsOfType<
|
|
204
|
+
T,
|
|
205
|
+
ModelMethod | ModelMethodTyped<T>
|
|
206
|
+
>
|
|
207
|
+
type InstanceMethods<T extends FunctionalModel> = ValueIsOfType<
|
|
208
|
+
T,
|
|
209
|
+
ModelInstanceMethod | ModelInstanceMethodTyped<T>
|
|
210
|
+
>
|
|
211
|
+
type ModelDefinition<T extends FunctionalModel> = {
|
|
212
|
+
readonly getPrimaryKeyName?: () => string
|
|
213
|
+
readonly properties: PropertiesList<T> & {
|
|
214
|
+
readonly id?: PrimaryKeyPropertyInstanceType
|
|
215
|
+
}
|
|
216
|
+
readonly instanceMethods?: InstanceMethods<T>
|
|
217
|
+
readonly modelMethods?: ModelMethods<T>
|
|
218
|
+
readonly modelValidators?: readonly ModelComponentValidator[]
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
type Model<T extends FunctionalModel> = {
|
|
222
|
+
readonly getName: () => string
|
|
223
|
+
readonly getPrimaryKeyName: () => string
|
|
224
|
+
readonly getModelDefinition: () => ModelDefinition<T>
|
|
225
|
+
readonly getPrimaryKey: (t: ModelInstanceInputData<T>) => PrimaryKeyType
|
|
226
|
+
readonly create: (
|
|
227
|
+
data: ModelInstanceInputData<T> & { readonly id?: PrimaryKeyType }
|
|
228
|
+
) => ModelInstance<T>
|
|
229
|
+
readonly methods: ModelMethodGetters<T>
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
type ReferenceFunctions = {
|
|
233
|
+
readonly [s: string]: () => ReferenceValueType<any>
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
type PropertyValidators = {
|
|
237
|
+
readonly [s: string]: PropertyValidator
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
type ModelInstance<T extends FunctionalModel> = {
|
|
241
|
+
readonly get: PropertyGetters<T> & {
|
|
242
|
+
readonly id: () => MaybePromise<PrimaryKeyType>
|
|
243
|
+
}
|
|
244
|
+
readonly methods: InstanceMethodGetters<T>
|
|
245
|
+
readonly references: ReferenceFunctions
|
|
246
|
+
readonly toObj: toObj
|
|
247
|
+
readonly getPrimaryKeyName: () => string
|
|
248
|
+
readonly getPrimaryKey: () => PrimaryKeyType
|
|
249
|
+
readonly validators: PropertyValidators
|
|
250
|
+
readonly validate: (options?: {}) => Promise<ModelErrors>
|
|
251
|
+
readonly getModel: () => Model<T>
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
type ModelMethodTyped<T extends FunctionalModel> = (
|
|
255
|
+
model: Model<T>,
|
|
256
|
+
args?: readonly any[]
|
|
257
|
+
) => any
|
|
258
|
+
type ModelMethod = ModelMethodTyped<any>
|
|
259
|
+
type ModelMethodClient = (...args: readonly any[]) => any
|
|
260
|
+
type ModelInstanceMethodTyped<T extends FunctionalModel> = (
|
|
261
|
+
instance: ModelInstance<T>,
|
|
262
|
+
args?: readonly any[]
|
|
263
|
+
) => any
|
|
264
|
+
type ModelInstanceMethod = ModelInstanceMethodTyped<any>
|
|
265
|
+
type ModelInstanceMethodClient = (...args: readonly any[]) => any
|
|
266
|
+
|
|
267
|
+
type ModelOptions = {
|
|
268
|
+
readonly instanceCreatedCallback: Nullable<
|
|
269
|
+
Arrayable<(instance: ModelInstance<any>) => void>
|
|
270
|
+
>
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
type OptionalModelOptions =
|
|
274
|
+
| {
|
|
275
|
+
readonly instanceCreatedCallback?: Nullable<
|
|
276
|
+
Arrayable<(instance: ModelInstance<any>) => void>
|
|
277
|
+
>
|
|
278
|
+
}
|
|
279
|
+
| undefined
|
|
280
|
+
|
|
281
|
+
export {
|
|
282
|
+
MaybeFunction,
|
|
283
|
+
Maybe,
|
|
284
|
+
MaybePromise,
|
|
285
|
+
Nullable,
|
|
286
|
+
Arrayable,
|
|
287
|
+
MaybeLazy,
|
|
288
|
+
JsonAble,
|
|
289
|
+
toObj,
|
|
290
|
+
ModelInstance,
|
|
291
|
+
Model,
|
|
292
|
+
PropertyValidatorComponent,
|
|
293
|
+
PropertyValidatorComponentSync,
|
|
294
|
+
PropertyValidatorComponentAsync,
|
|
295
|
+
PropertyValidatorComponentType,
|
|
296
|
+
PropertyValidator,
|
|
297
|
+
ModelComponentValidator,
|
|
298
|
+
PropertyInstance,
|
|
299
|
+
PropertyConfig,
|
|
300
|
+
FunctionalType,
|
|
301
|
+
ValueGetter,
|
|
302
|
+
ReferenceValueType,
|
|
303
|
+
ModelDefinition,
|
|
304
|
+
ModelOptions,
|
|
305
|
+
ModelMethod,
|
|
306
|
+
OptionalModelOptions,
|
|
307
|
+
ReferencePropertyInstance,
|
|
308
|
+
PropertyGetters,
|
|
309
|
+
PropertyValidators,
|
|
310
|
+
PropertyValidatorComponentTypeAdvanced,
|
|
311
|
+
ModelInstanceMethod,
|
|
312
|
+
ModelInstanceMethodTyped,
|
|
313
|
+
FunctionalModel,
|
|
314
|
+
ModelInstanceInputData,
|
|
315
|
+
ModelMethodTyped,
|
|
316
|
+
ModelMethodGetters,
|
|
317
|
+
InstanceMethodGetters,
|
|
318
|
+
ReferenceFunctions,
|
|
319
|
+
ModelErrors,
|
|
320
|
+
MaybeEmpty,
|
|
321
|
+
PrimaryKeyType,
|
|
322
|
+
}
|
|
323
|
+
/* eslint-enable no-unused-vars */
|
package/src/lazy.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import AsyncLock from 'async-lock'
|
|
2
|
+
import { createUuid } from './utils'
|
|
3
|
+
|
|
4
|
+
const lazyValue = (method: Function) => {
|
|
5
|
+
const key = createUuid()
|
|
6
|
+
const lock = new AsyncLock()
|
|
7
|
+
/* eslint-disable functional/no-let */
|
|
8
|
+
let value: any = undefined
|
|
9
|
+
let called = false
|
|
10
|
+
return async (...args: readonly any[]) => {
|
|
11
|
+
return lock.acquire(key, async () => {
|
|
12
|
+
if (!called) {
|
|
13
|
+
called = true
|
|
14
|
+
value = await method(...args)
|
|
15
|
+
// eslint-disable-next-line require-atomic-updates
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return value
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
/* eslint-enable functional/no-let */
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { lazyValue }
|
package/src/methods.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ModelInstanceMethodTyped,
|
|
3
|
+
ModelInstance,
|
|
4
|
+
FunctionalModel,
|
|
5
|
+
Model,
|
|
6
|
+
ModelMethodTyped,
|
|
7
|
+
} from './interfaces'
|
|
8
|
+
|
|
9
|
+
const WrapperInstanceMethod = <T extends FunctionalModel>(
|
|
10
|
+
method: (instance: ModelInstance<T>, args?: readonly any[]) => any
|
|
11
|
+
) => {
|
|
12
|
+
const r: ModelInstanceMethodTyped<T> = (
|
|
13
|
+
instance: ModelInstance<T>,
|
|
14
|
+
...args: readonly any[]
|
|
15
|
+
) => {
|
|
16
|
+
return method(instance, ...args)
|
|
17
|
+
}
|
|
18
|
+
return r
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const WrapperModelMethod = <T extends FunctionalModel>(
|
|
22
|
+
method: (model: Model<T>, args?: readonly any[]) => any
|
|
23
|
+
) => {
|
|
24
|
+
const r: ModelMethodTyped<T> = (model: Model<T>, ...args: readonly any[]) => {
|
|
25
|
+
return method(model, ...args)
|
|
26
|
+
}
|
|
27
|
+
return r
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { WrapperInstanceMethod, WrapperModelMethod }
|