objectmodel 4.3.1 → 4.4.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 +8 -8
- package/.eslintrc.json +25 -25
- package/.travis.yml +2 -2
- package/LICENSE +22 -22
- package/README.md +67 -67
- package/build/add-banner.js +13 -13
- package/build/bundle-entry.dev.js +2 -2
- package/build/bundle-entry.js +11 -11
- package/dist/object-model.cjs +462 -469
- package/dist/object-model.js +462 -469
- package/dist/object-model.js.map +1 -1
- package/dist/object-model.min.js +2 -2
- package/dist/object-model.min.js.map +1 -1
- package/index.html +4 -4
- package/package.json +9 -3
- package/rollup.config.js +13 -13
- package/src/array-model.d.ts +16 -0
- package/src/array-model.js +68 -68
- package/src/devtool-formatter.js +198 -198
- package/src/function-model.d.ts +24 -0
- package/src/function-model.js +58 -65
- package/src/helpers.js +43 -43
- package/src/index.js +4 -4
- package/src/list-model.js +43 -43
- package/src/map-model.d.ts +18 -0
- package/src/map-model.js +48 -48
- package/src/object-model.d.ts +74 -0
- package/src/set-model.d.ts +16 -0
- package/src/set-model.js +41 -41
- package/test/array-model.spec.cjs +291 -291
- package/test/array-model.test-d.ts +24 -0
- package/test/basic-model.spec.cjs +263 -263
- package/test/basic-model.test-d.ts +30 -0
- package/test/bench/array.html +51 -51
- package/test/bench/bench-lib.js +49 -49
- package/test/bench/map-no-cast.html +53 -53
- package/test/bench/map-set.html +52 -52
- package/test/bench/map.html +51 -51
- package/test/bench/object-models.html +87 -87
- package/test/function-model.spec.cjs +161 -162
- package/test/function-model.test-d.ts +18 -0
- package/test/index.cjs +13 -13
- package/test/map-model.spec.cjs +224 -224
- package/test/map-model.test-d.ts +21 -0
- package/test/model.spec.cjs +30 -30
- package/test/object-model.spec.cjs +1345 -1346
- package/test/object-model.test-d.ts +53 -0
- package/test/set-model.spec.cjs +213 -213
- package/test/set-model.test-d.ts +17 -0
- package/test/umd.html +25 -25
- package/types/definitions.d.ts +43 -0
- package/types/helpers.d.ts +4 -0
- package/types/index.d.ts +6 -128
package/src/devtool-formatter.js
CHANGED
|
@@ -1,199 +1,199 @@
|
|
|
1
|
-
import { _original, Model, Any, BasicModel, ObjectModel } from "./object-model.js"
|
|
2
|
-
import ArrayModel from "./array-model.js"
|
|
3
|
-
import SetModel from "./set-model.js"
|
|
4
|
-
import MapModel from "./map-model.js"
|
|
5
|
-
import FunctionModel from "./function-model.js"
|
|
6
|
-
import { getProto, has, is, isFunction, isPlainObject } from "./helpers.js"
|
|
7
|
-
|
|
8
|
-
const styles = {
|
|
9
|
-
list: "list-style-type: none; padding: 0; margin: 0;",
|
|
10
|
-
listItem: "padding: 0 0 0 1em;",
|
|
11
|
-
model: "color: #3e999f;",
|
|
12
|
-
instance: "color: #718c00; font-style: italic",
|
|
13
|
-
function: "color: #4271AE",
|
|
14
|
-
string: "color: #C41A16",
|
|
15
|
-
number: "color: #1C00CF",
|
|
16
|
-
boolean: "color: #AA0D91",
|
|
17
|
-
property: "color: #8959a8",
|
|
18
|
-
private: "color: #C19ED8",
|
|
19
|
-
constant: "color: #8959a8; font-weight: bold",
|
|
20
|
-
privateConstant: "color: #C19ED8; font-weight: bold",
|
|
21
|
-
null: "color: #8e908c",
|
|
22
|
-
undeclared: "color: #C0C0C0;",
|
|
23
|
-
proto: "color: #B871BD; font-style: italic"
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const getModel = (instance) => {
|
|
27
|
-
if (instance === undefined || instance === null)
|
|
28
|
-
return null
|
|
29
|
-
|
|
30
|
-
const proto = getProto(instance);
|
|
31
|
-
if (!proto || !proto.constructor || !is(Model, proto.constructor))
|
|
32
|
-
return null
|
|
33
|
-
|
|
34
|
-
return proto.constructor
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const span = (style, ...children) => ["span", { style }, ...children]
|
|
38
|
-
|
|
39
|
-
const format = (x, config = {}) => {
|
|
40
|
-
if (x === null || x === undefined)
|
|
41
|
-
return span(styles.null, "" + x);
|
|
42
|
-
|
|
43
|
-
if (typeof x === "boolean")
|
|
44
|
-
return span(styles.boolean, x);
|
|
45
|
-
|
|
46
|
-
if (typeof x === "number")
|
|
47
|
-
return span(styles.number, x);
|
|
48
|
-
|
|
49
|
-
if (typeof x === "string")
|
|
50
|
-
return span(styles.string, `"${x}"`);
|
|
51
|
-
|
|
52
|
-
if (Array.isArray(x) && config.isModelDefinition) {
|
|
53
|
-
return span("", ...x.flatMap(part => [format(part, config), " or "]).slice(0, -1))
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (isPlainObject(x))
|
|
57
|
-
return formatObject(x, getModel(x), config)
|
|
58
|
-
|
|
59
|
-
if (isFunction(x) && !is(Model, x) && config.isModelDefinition)
|
|
60
|
-
return span(styles.function, x.name || x.toString());
|
|
61
|
-
|
|
62
|
-
return ["object", { object: x, config }]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const formatObject = (o, model, config) => span("",
|
|
66
|
-
"{",
|
|
67
|
-
["ol", { style: styles.list }, ...Object.keys(o).map(prop =>
|
|
68
|
-
["li", { style: styles.listItem }, span(styles.property, prop), ": ", format(o[prop], config)])
|
|
69
|
-
],
|
|
70
|
-
"}"
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
const formatModel = model => {
|
|
74
|
-
const
|
|
75
|
-
cfg = { isModelDefinition: true },
|
|
76
|
-
def = model.definition,
|
|
77
|
-
formatList = (list, map) => list.flatMap(e => [map(e), ", "]).slice(0, -1);
|
|
78
|
-
let parts = []
|
|
79
|
-
|
|
80
|
-
if (is(BasicModel, model)) parts = [format(def, cfg)]
|
|
81
|
-
if (is(ArrayModel, model)) parts = ["Array of ", format(def, cfg)]
|
|
82
|
-
if (is(SetModel, model)) parts = ["Set of ", format(def, cfg)]
|
|
83
|
-
if (is(MapModel, model)) parts = ["Map of ", format(def.key, cfg), " : ", format(def.value, cfg)]
|
|
84
|
-
if (is(FunctionModel, model)) {
|
|
85
|
-
parts = ["Function(", ...formatList(def.arguments, arg => format(arg, cfg)), ")"]
|
|
86
|
-
if ("return" in def) parts.push(" => ", format(def.return, cfg))
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (model.assertions.length > 0) {
|
|
90
|
-
parts.push("\n(assertions: ", ...formatList(model.assertions, f => ["object", { object: f }]), ")")
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return span(styles.model, ...parts)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const ModelFormatter = {
|
|
97
|
-
header(x, config = {}) {
|
|
98
|
-
if (x === Any)
|
|
99
|
-
return span(styles.model, "Any")
|
|
100
|
-
|
|
101
|
-
if (is(Any.remaining, x))
|
|
102
|
-
return span(styles.model, "...", format(x.definition, { isModelDefinition: true }))
|
|
103
|
-
|
|
104
|
-
if (is(ObjectModel, x))
|
|
105
|
-
return span(styles.model, x.name)
|
|
106
|
-
|
|
107
|
-
if (is(Model, x)) {
|
|
108
|
-
return formatModel(x)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (config.isModelDefinition && isPlainObject(x))
|
|
112
|
-
return format(x, config)
|
|
113
|
-
|
|
114
|
-
return null;
|
|
115
|
-
},
|
|
116
|
-
hasBody(x) {
|
|
117
|
-
return is(ObjectModel, x)
|
|
118
|
-
},
|
|
119
|
-
body(model) {
|
|
120
|
-
return span("",
|
|
121
|
-
"{",
|
|
122
|
-
["ol", { style: styles.list }, ...Object.keys(model.definition).map(prop => {
|
|
123
|
-
const isPrivate = model.conventionForPrivate(prop)
|
|
124
|
-
const isConstant = model.conventionForConstant(prop)
|
|
125
|
-
const hasDefault = model.default && has(model.default, prop);
|
|
126
|
-
let style = styles.property;
|
|
127
|
-
|
|
128
|
-
if (isPrivate) {
|
|
129
|
-
style = isConstant ? styles.privateConstant : styles.private
|
|
130
|
-
} else if (isConstant) {
|
|
131
|
-
style = styles.constant
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return ["li", { style: styles.listItem },
|
|
135
|
-
span(style, prop), ": ", format(model.definition[prop], { isModelDefinition: true }),
|
|
136
|
-
hasDefault ? span(styles.proto, " = ", format(model.default[prop])) : ""
|
|
137
|
-
]
|
|
138
|
-
})],
|
|
139
|
-
"}"
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const ModelInstanceFormatter = {
|
|
145
|
-
header(x, config = {}) {
|
|
146
|
-
if (config.isInstanceProperty && isPlainObject(x)) {
|
|
147
|
-
return format(x, config)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const model = getModel(x);
|
|
151
|
-
if (is(Model, model)) {
|
|
152
|
-
const parts = is(ObjectModel, model) ? [model.name] : [["object", { object: x[_original] }], ` (${model.name})`];
|
|
153
|
-
return span(styles.instance, ...parts)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return null;
|
|
157
|
-
},
|
|
158
|
-
hasBody(x) {
|
|
159
|
-
return x && is(ObjectModel, getModel(x))
|
|
160
|
-
},
|
|
161
|
-
body(x) {
|
|
162
|
-
const model = getModel(x)
|
|
163
|
-
const o = x[_original] || x;
|
|
164
|
-
return span("",
|
|
165
|
-
"{",
|
|
166
|
-
[
|
|
167
|
-
"ol",
|
|
168
|
-
{ style: styles.list },
|
|
169
|
-
...Object.keys(o).map(prop => {
|
|
170
|
-
const isPrivate = model.conventionForPrivate(prop)
|
|
171
|
-
const isConstant = model.conventionForConstant(prop)
|
|
172
|
-
const isDeclared = prop in model.definition;
|
|
173
|
-
let style = styles.property;
|
|
174
|
-
|
|
175
|
-
if (!isDeclared) {
|
|
176
|
-
style = styles.undeclared
|
|
177
|
-
} else if (isPrivate) {
|
|
178
|
-
style = isConstant ? styles.privateConstant : styles.private
|
|
179
|
-
} else if (isConstant) {
|
|
180
|
-
style = styles.constant
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return ["li", { style: styles.listItem },
|
|
184
|
-
span(style, prop), ": ", format(o[prop], { isInstanceProperty: true })
|
|
185
|
-
]
|
|
186
|
-
}),
|
|
187
|
-
["li", { style: styles.listItem },
|
|
188
|
-
span(styles.proto, "__proto__", ": ", ["object", { object: getProto(x) }])
|
|
189
|
-
]
|
|
190
|
-
],
|
|
191
|
-
"}"
|
|
192
|
-
)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (typeof window !== "undefined") {
|
|
197
|
-
window.devtoolsFormatters = (window.devtoolsFormatters || [])
|
|
198
|
-
.concat(ModelFormatter, ModelInstanceFormatter);
|
|
1
|
+
import { _original, Model, Any, BasicModel, ObjectModel } from "./object-model.js"
|
|
2
|
+
import ArrayModel from "./array-model.js"
|
|
3
|
+
import SetModel from "./set-model.js"
|
|
4
|
+
import MapModel from "./map-model.js"
|
|
5
|
+
import FunctionModel from "./function-model.js"
|
|
6
|
+
import { getProto, has, is, isFunction, isPlainObject } from "./helpers.js"
|
|
7
|
+
|
|
8
|
+
const styles = {
|
|
9
|
+
list: "list-style-type: none; padding: 0; margin: 0;",
|
|
10
|
+
listItem: "padding: 0 0 0 1em;",
|
|
11
|
+
model: "color: #3e999f;",
|
|
12
|
+
instance: "color: #718c00; font-style: italic",
|
|
13
|
+
function: "color: #4271AE",
|
|
14
|
+
string: "color: #C41A16",
|
|
15
|
+
number: "color: #1C00CF",
|
|
16
|
+
boolean: "color: #AA0D91",
|
|
17
|
+
property: "color: #8959a8",
|
|
18
|
+
private: "color: #C19ED8",
|
|
19
|
+
constant: "color: #8959a8; font-weight: bold",
|
|
20
|
+
privateConstant: "color: #C19ED8; font-weight: bold",
|
|
21
|
+
null: "color: #8e908c",
|
|
22
|
+
undeclared: "color: #C0C0C0;",
|
|
23
|
+
proto: "color: #B871BD; font-style: italic"
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const getModel = (instance) => {
|
|
27
|
+
if (instance === undefined || instance === null)
|
|
28
|
+
return null
|
|
29
|
+
|
|
30
|
+
const proto = getProto(instance);
|
|
31
|
+
if (!proto || !proto.constructor || !is(Model, proto.constructor))
|
|
32
|
+
return null
|
|
33
|
+
|
|
34
|
+
return proto.constructor
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const span = (style, ...children) => ["span", { style }, ...children]
|
|
38
|
+
|
|
39
|
+
const format = (x, config = {}) => {
|
|
40
|
+
if (x === null || x === undefined)
|
|
41
|
+
return span(styles.null, "" + x);
|
|
42
|
+
|
|
43
|
+
if (typeof x === "boolean")
|
|
44
|
+
return span(styles.boolean, x);
|
|
45
|
+
|
|
46
|
+
if (typeof x === "number")
|
|
47
|
+
return span(styles.number, x);
|
|
48
|
+
|
|
49
|
+
if (typeof x === "string")
|
|
50
|
+
return span(styles.string, `"${x}"`);
|
|
51
|
+
|
|
52
|
+
if (Array.isArray(x) && config.isModelDefinition) {
|
|
53
|
+
return span("", ...x.flatMap(part => [format(part, config), " or "]).slice(0, -1))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (isPlainObject(x))
|
|
57
|
+
return formatObject(x, getModel(x), config)
|
|
58
|
+
|
|
59
|
+
if (isFunction(x) && !is(Model, x) && config.isModelDefinition)
|
|
60
|
+
return span(styles.function, x.name || x.toString());
|
|
61
|
+
|
|
62
|
+
return ["object", { object: x, config }]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const formatObject = (o, model, config) => span("",
|
|
66
|
+
"{",
|
|
67
|
+
["ol", { style: styles.list }, ...Object.keys(o).map(prop =>
|
|
68
|
+
["li", { style: styles.listItem }, span(styles.property, prop), ": ", format(o[prop], config)])
|
|
69
|
+
],
|
|
70
|
+
"}"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const formatModel = model => {
|
|
74
|
+
const
|
|
75
|
+
cfg = { isModelDefinition: true },
|
|
76
|
+
def = model.definition,
|
|
77
|
+
formatList = (list, map) => list.flatMap(e => [map(e), ", "]).slice(0, -1);
|
|
78
|
+
let parts = []
|
|
79
|
+
|
|
80
|
+
if (is(BasicModel, model)) parts = [format(def, cfg)]
|
|
81
|
+
if (is(ArrayModel, model)) parts = ["Array of ", format(def, cfg)]
|
|
82
|
+
if (is(SetModel, model)) parts = ["Set of ", format(def, cfg)]
|
|
83
|
+
if (is(MapModel, model)) parts = ["Map of ", format(def.key, cfg), " : ", format(def.value, cfg)]
|
|
84
|
+
if (is(FunctionModel, model)) {
|
|
85
|
+
parts = ["Function(", ...formatList(def.arguments, arg => format(arg, cfg)), ")"]
|
|
86
|
+
if ("return" in def) parts.push(" => ", format(def.return, cfg))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (model.assertions.length > 0) {
|
|
90
|
+
parts.push("\n(assertions: ", ...formatList(model.assertions, f => ["object", { object: f }]), ")")
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return span(styles.model, ...parts)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const ModelFormatter = {
|
|
97
|
+
header(x, config = {}) {
|
|
98
|
+
if (x === Any)
|
|
99
|
+
return span(styles.model, "Any")
|
|
100
|
+
|
|
101
|
+
if (is(Any.remaining, x))
|
|
102
|
+
return span(styles.model, "...", format(x.definition, { isModelDefinition: true }))
|
|
103
|
+
|
|
104
|
+
if (is(ObjectModel, x))
|
|
105
|
+
return span(styles.model, x.name)
|
|
106
|
+
|
|
107
|
+
if (is(Model, x)) {
|
|
108
|
+
return formatModel(x)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (config.isModelDefinition && isPlainObject(x))
|
|
112
|
+
return format(x, config)
|
|
113
|
+
|
|
114
|
+
return null;
|
|
115
|
+
},
|
|
116
|
+
hasBody(x) {
|
|
117
|
+
return is(ObjectModel, x)
|
|
118
|
+
},
|
|
119
|
+
body(model) {
|
|
120
|
+
return span("",
|
|
121
|
+
"{",
|
|
122
|
+
["ol", { style: styles.list }, ...Object.keys(model.definition).map(prop => {
|
|
123
|
+
const isPrivate = model.conventionForPrivate(prop)
|
|
124
|
+
const isConstant = model.conventionForConstant(prop)
|
|
125
|
+
const hasDefault = model.default && has(model.default, prop);
|
|
126
|
+
let style = styles.property;
|
|
127
|
+
|
|
128
|
+
if (isPrivate) {
|
|
129
|
+
style = isConstant ? styles.privateConstant : styles.private
|
|
130
|
+
} else if (isConstant) {
|
|
131
|
+
style = styles.constant
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return ["li", { style: styles.listItem },
|
|
135
|
+
span(style, prop), ": ", format(model.definition[prop], { isModelDefinition: true }),
|
|
136
|
+
hasDefault ? span(styles.proto, " = ", format(model.default[prop])) : ""
|
|
137
|
+
]
|
|
138
|
+
})],
|
|
139
|
+
"}"
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const ModelInstanceFormatter = {
|
|
145
|
+
header(x, config = {}) {
|
|
146
|
+
if (config.isInstanceProperty && isPlainObject(x)) {
|
|
147
|
+
return format(x, config)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const model = getModel(x);
|
|
151
|
+
if (is(Model, model)) {
|
|
152
|
+
const parts = is(ObjectModel, model) ? [model.name] : [["object", { object: x[_original] }], ` (${model.name})`];
|
|
153
|
+
return span(styles.instance, ...parts)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return null;
|
|
157
|
+
},
|
|
158
|
+
hasBody(x) {
|
|
159
|
+
return x && is(ObjectModel, getModel(x))
|
|
160
|
+
},
|
|
161
|
+
body(x) {
|
|
162
|
+
const model = getModel(x)
|
|
163
|
+
const o = x[_original] || x;
|
|
164
|
+
return span("",
|
|
165
|
+
"{",
|
|
166
|
+
[
|
|
167
|
+
"ol",
|
|
168
|
+
{ style: styles.list },
|
|
169
|
+
...Object.keys(o).map(prop => {
|
|
170
|
+
const isPrivate = model.conventionForPrivate(prop)
|
|
171
|
+
const isConstant = model.conventionForConstant(prop)
|
|
172
|
+
const isDeclared = prop in model.definition;
|
|
173
|
+
let style = styles.property;
|
|
174
|
+
|
|
175
|
+
if (!isDeclared) {
|
|
176
|
+
style = styles.undeclared
|
|
177
|
+
} else if (isPrivate) {
|
|
178
|
+
style = isConstant ? styles.privateConstant : styles.private
|
|
179
|
+
} else if (isConstant) {
|
|
180
|
+
style = styles.constant
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return ["li", { style: styles.listItem },
|
|
184
|
+
span(style, prop), ": ", format(o[prop], { isInstanceProperty: true })
|
|
185
|
+
]
|
|
186
|
+
}),
|
|
187
|
+
["li", { style: styles.listItem },
|
|
188
|
+
span(styles.proto, "__proto__", ": ", ["object", { object: getProto(x) }])
|
|
189
|
+
]
|
|
190
|
+
],
|
|
191
|
+
"}"
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (typeof window !== "undefined") {
|
|
197
|
+
window.devtoolsFormatters = (window.devtoolsFormatters || [])
|
|
198
|
+
.concat(ModelFormatter, ModelInstanceFormatter);
|
|
199
199
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FromDefinition, ModelDefinition } from "../types/definitions";
|
|
2
|
+
import { Model } from "./object-model";
|
|
3
|
+
|
|
4
|
+
type FromArgsDef<T> = T extends [infer A, ...infer Rest] ? [FromDefinition<A>, ...FromArgsDef<Rest>] : T
|
|
5
|
+
|
|
6
|
+
export type FunctionSignature<Args extends any[], Return> = {
|
|
7
|
+
(...args: FromArgsDef<Args>): FromDefinition<Return>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface FunctionModel<Args extends ModelDefinition[], Return extends ModelDefinition> extends Model<{ arguments: Args, return: Return }> {
|
|
11
|
+
(): FunctionSignature<Args, Return>;
|
|
12
|
+
(fn: FunctionSignature<Args, Return>): FunctionSignature<Args, Return>;
|
|
13
|
+
new (fn: FunctionSignature<Args, Return>): FunctionSignature<Args, Return>;
|
|
14
|
+
definition: { arguments: Args, return: Return };
|
|
15
|
+
|
|
16
|
+
return<R extends ModelDefinition>(returnValueDefinition: R): FunctionModel<Args, R>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface FunctionModelConstructor {
|
|
20
|
+
<Args extends ModelDefinition[]>(...argumentsDefinitions: Args): FunctionModel<Args, any>;
|
|
21
|
+
new<Args extends ModelDefinition[]>(...argumentsDefinitions: Args): FunctionModel<Args, any>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const FunctionModel: FunctionModelConstructor;
|
package/src/function-model.js
CHANGED
|
@@ -1,66 +1,59 @@
|
|
|
1
|
-
import {
|
|
2
|
-
_check, _original, Any, checkAssertions, checkDefinition, extendDefinition, extendModel,
|
|
3
|
-
formatDefinition, initModel, Model, stackError, unstackErrors
|
|
4
|
-
} from "./object-model.js"
|
|
5
|
-
import { extend, is, isFunction } from "./helpers.js"
|
|
6
|
-
|
|
7
|
-
export default function FunctionModel(...argsDef) {
|
|
8
|
-
return initModel({ arguments: argsDef }, FunctionModel, Function, null, model => ({
|
|
9
|
-
getPrototypeOf: () => model.prototype,
|
|
10
|
-
|
|
11
|
-
get(fn, key) {
|
|
12
|
-
return key === _original ? fn : fn[key]
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
apply(fn, ctx, args) {
|
|
16
|
-
const def = model.definition
|
|
17
|
-
const remainingArgDef = def.arguments.find(argDef => is(Any.remaining, argDef))
|
|
18
|
-
const nbArgsToCheck = remainingArgDef ? Math.max(args.length, def.arguments.length - 1) : def.arguments.length
|
|
19
|
-
|
|
20
|
-
for (let i = 0; i < nbArgsToCheck; i++) {
|
|
21
|
-
const argDef = remainingArgDef && i >= def.arguments.length - 1 ? remainingArgDef.definition : def.arguments[i]
|
|
22
|
-
args[i] = checkDefinition(args[i], argDef, `arguments[${i}]`, model.errors, [], true)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
checkAssertions(args, model, "arguments")
|
|
26
|
-
|
|
27
|
-
let result
|
|
28
|
-
if (!model.errors.length) {
|
|
29
|
-
result = Reflect.apply(fn, ctx, args)
|
|
30
|
-
if ("return" in def)
|
|
31
|
-
result = checkDefinition(result, def.return, "return value", model.errors, [], true)
|
|
32
|
-
}
|
|
33
|
-
unstackErrors(model)
|
|
34
|
-
return result
|
|
35
|
-
}
|
|
36
|
-
}))
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
extend(FunctionModel, Model, {
|
|
40
|
-
toString(stack = []) {
|
|
41
|
-
let out = `Function(${this.definition.arguments.map(
|
|
42
|
-
argDef => formatDefinition(argDef, [...stack])
|
|
43
|
-
).join(", ")})`
|
|
44
|
-
|
|
45
|
-
if ("return" in this.definition) {
|
|
46
|
-
out += " => " + formatDefinition(this.definition.return, stack)
|
|
47
|
-
}
|
|
48
|
-
return out
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
return(def) {
|
|
52
|
-
this.definition.return = def
|
|
53
|
-
return this
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
mixedReturns = extendDefinition(this.definition.return, newReturns)
|
|
60
|
-
return extendModel(new FunctionModel(...mixedArgs).return(mixedReturns), this)
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
[_check](f, path, errors) {
|
|
64
|
-
if (!isFunction(f)) stackError(errors, "Function", f, path)
|
|
65
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
_check, _original, Any, checkAssertions, checkDefinition, extendDefinition, extendModel,
|
|
3
|
+
formatDefinition, initModel, Model, stackError, unstackErrors
|
|
4
|
+
} from "./object-model.js"
|
|
5
|
+
import { extend, is, isFunction } from "./helpers.js"
|
|
6
|
+
|
|
7
|
+
export default function FunctionModel(...argsDef) {
|
|
8
|
+
return initModel({ arguments: argsDef }, FunctionModel, Function, null, model => ({
|
|
9
|
+
getPrototypeOf: () => model.prototype,
|
|
10
|
+
|
|
11
|
+
get(fn, key) {
|
|
12
|
+
return key === _original ? fn : fn[key]
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
apply(fn, ctx, args) {
|
|
16
|
+
const def = model.definition
|
|
17
|
+
const remainingArgDef = def.arguments.find(argDef => is(Any.remaining, argDef))
|
|
18
|
+
const nbArgsToCheck = remainingArgDef ? Math.max(args.length, def.arguments.length - 1) : def.arguments.length
|
|
19
|
+
|
|
20
|
+
for (let i = 0; i < nbArgsToCheck; i++) {
|
|
21
|
+
const argDef = remainingArgDef && i >= def.arguments.length - 1 ? remainingArgDef.definition : def.arguments[i]
|
|
22
|
+
args[i] = checkDefinition(args[i], argDef, `arguments[${i}]`, model.errors, [], true)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
checkAssertions(args, model, "arguments")
|
|
26
|
+
|
|
27
|
+
let result
|
|
28
|
+
if (!model.errors.length) {
|
|
29
|
+
result = Reflect.apply(fn, ctx, args)
|
|
30
|
+
if ("return" in def)
|
|
31
|
+
result = checkDefinition(result, def.return, "return value", model.errors, [], true)
|
|
32
|
+
}
|
|
33
|
+
unstackErrors(model)
|
|
34
|
+
return result
|
|
35
|
+
}
|
|
36
|
+
}))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
extend(FunctionModel, Model, {
|
|
40
|
+
toString(stack = []) {
|
|
41
|
+
let out = `Function(${this.definition.arguments.map(
|
|
42
|
+
argDef => formatDefinition(argDef, [...stack])
|
|
43
|
+
).join(", ")})`
|
|
44
|
+
|
|
45
|
+
if ("return" in this.definition) {
|
|
46
|
+
out += " => " + formatDefinition(this.definition.return, stack)
|
|
47
|
+
}
|
|
48
|
+
return out
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
return(def) {
|
|
52
|
+
this.definition.return = def
|
|
53
|
+
return this
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
[_check](f, path, errors) {
|
|
57
|
+
if (!isFunction(f)) stackError(errors, "Function", f, path)
|
|
58
|
+
}
|
|
66
59
|
})
|
package/src/helpers.js
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
export const
|
|
2
|
-
ObjectProto = Object.prototype,
|
|
3
|
-
bettertypeof = x => ObjectProto.toString.call(x).match(/\s([a-zA-Z]+)/)[1],
|
|
4
|
-
getProto = Object.getPrototypeOf,
|
|
5
|
-
setProto = Object.setPrototypeOf,
|
|
6
|
-
|
|
7
|
-
has = (o, prop) => ObjectProto.hasOwnProperty.call(o, prop),
|
|
8
|
-
is = (Constructor, obj) => obj instanceof Constructor,
|
|
9
|
-
isFunction = f => typeof f === "function",
|
|
10
|
-
isObject = o => o && typeof o === "object",
|
|
11
|
-
isString = s => typeof s === "string",
|
|
12
|
-
isPlainObject = o => isObject(o) && getProto(o) === ObjectProto,
|
|
13
|
-
isIterable = x => x && isFunction(x[Symbol.iterator]),
|
|
14
|
-
|
|
15
|
-
proxify = (val, traps) => new Proxy(val, traps),
|
|
16
|
-
|
|
17
|
-
merge = (target, src = {}) => {
|
|
18
|
-
for (let key in src) {
|
|
19
|
-
if (isPlainObject(src[key])) {
|
|
20
|
-
const o = {}
|
|
21
|
-
merge(o, target[key])
|
|
22
|
-
merge(o, src[key])
|
|
23
|
-
target[key] = o
|
|
24
|
-
} else {
|
|
25
|
-
target[key] = src[key]
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return target
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
define = (obj, key, value, enumerable = false) => {
|
|
32
|
-
Object.defineProperty(obj, key, { value, enumerable, writable: true, configurable: true })
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
extend = (child, parent, props) => {
|
|
36
|
-
child.prototype = Object.assign(Object.create(parent.prototype, {
|
|
37
|
-
constructor: {
|
|
38
|
-
value: child,
|
|
39
|
-
writable: true,
|
|
40
|
-
configurable: true
|
|
41
|
-
}
|
|
42
|
-
}), props)
|
|
43
|
-
setProto(child, parent)
|
|
1
|
+
export const
|
|
2
|
+
ObjectProto = Object.prototype,
|
|
3
|
+
bettertypeof = x => ObjectProto.toString.call(x).match(/\s([a-zA-Z]+)/)[1],
|
|
4
|
+
getProto = Object.getPrototypeOf,
|
|
5
|
+
setProto = Object.setPrototypeOf,
|
|
6
|
+
|
|
7
|
+
has = (o, prop) => ObjectProto.hasOwnProperty.call(o, prop),
|
|
8
|
+
is = (Constructor, obj) => obj instanceof Constructor,
|
|
9
|
+
isFunction = f => typeof f === "function",
|
|
10
|
+
isObject = o => o && typeof o === "object",
|
|
11
|
+
isString = s => typeof s === "string",
|
|
12
|
+
isPlainObject = o => isObject(o) && getProto(o) === ObjectProto,
|
|
13
|
+
isIterable = x => x && isFunction(x[Symbol.iterator]),
|
|
14
|
+
|
|
15
|
+
proxify = (val, traps) => new Proxy(val, traps),
|
|
16
|
+
|
|
17
|
+
merge = (target, src = {}) => {
|
|
18
|
+
for (let key in src) {
|
|
19
|
+
if (isPlainObject(src[key])) {
|
|
20
|
+
const o = {}
|
|
21
|
+
merge(o, target[key])
|
|
22
|
+
merge(o, src[key])
|
|
23
|
+
target[key] = o
|
|
24
|
+
} else {
|
|
25
|
+
target[key] = src[key]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return target
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
define = (obj, key, value, enumerable = false) => {
|
|
32
|
+
Object.defineProperty(obj, key, { value, enumerable, writable: true, configurable: true })
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
extend = (child, parent, props) => {
|
|
36
|
+
child.prototype = Object.assign(Object.create(parent.prototype, {
|
|
37
|
+
constructor: {
|
|
38
|
+
value: child,
|
|
39
|
+
writable: true,
|
|
40
|
+
configurable: true
|
|
41
|
+
}
|
|
42
|
+
}), props)
|
|
43
|
+
setProto(child, parent)
|
|
44
44
|
}
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { Model, BasicModel, ObjectModel, Any } from "./object-model.js"
|
|
2
|
-
export { default as ArrayModel } from "./array-model.js"
|
|
3
|
-
export { default as FunctionModel } from "./function-model.js"
|
|
4
|
-
export { default as MapModel } from "./map-model.js"
|
|
1
|
+
export { Model, BasicModel, ObjectModel, Any } from "./object-model.js"
|
|
2
|
+
export { default as ArrayModel } from "./array-model.js"
|
|
3
|
+
export { default as FunctionModel } from "./function-model.js"
|
|
4
|
+
export { default as MapModel } from "./map-model.js"
|
|
5
5
|
export { default as SetModel } from "./set-model.js"
|