objectmodel 4.3.0 → 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.cjs → add-banner.js} +13 -13
- package/build/bundle-entry.dev.js +2 -2
- package/build/bundle-entry.js +11 -11
- package/build/{update-docs.cjs → update-docs.js} +7 -5
- package/dist/object-model.cjs +466 -472
- package/dist/object-model.js +466 -472
- 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 +1603 -1603
- package/package.json +20 -10
- 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/object-model.js +4 -3
- 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/index.html +27 -27
- 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 -1327
- 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/test/lib/qunit.css +0 -436
- package/test/lib/qunit.js +0 -6582
- package/tsconfig.json +0 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "objectmodel",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"description": "Strong Dynamically Typed Object Modeling for JavaScript",
|
|
5
5
|
"author": "Sylvain Pollet-Villard",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,21 +18,24 @@
|
|
|
18
18
|
"watch:js": "npm run build:js -- --watch",
|
|
19
19
|
"watch:min": "npm run build:min -- --watch",
|
|
20
20
|
"info": "npm-run-all info:*",
|
|
21
|
-
"info:banner": "node build/add-banner.
|
|
22
|
-
"info:docs": "node build/update-docs.
|
|
21
|
+
"info:banner": "node build/add-banner.js",
|
|
22
|
+
"info:docs": "node build/update-docs.js",
|
|
23
23
|
"lint": "eslint . --fix",
|
|
24
|
-
"test": "
|
|
24
|
+
"test": "npm-run-all test:*",
|
|
25
|
+
"test:unit": "qunit test/index.cjs",
|
|
26
|
+
"test:tsd": "tsd"
|
|
25
27
|
},
|
|
26
28
|
"devDependencies": {
|
|
27
29
|
"cjyes": "^0.3.1",
|
|
28
|
-
"eslint": "^
|
|
30
|
+
"eslint": "^8.18.0",
|
|
29
31
|
"esm": "^3.2.25",
|
|
30
|
-
"
|
|
31
|
-
"gzip-size": "^6.0.0",
|
|
32
|
+
"gzip-size": "^7.0.0",
|
|
32
33
|
"npm-run-all": "^4.1.5",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"rollup
|
|
34
|
+
"pretty-bytes": "^6.0.0",
|
|
35
|
+
"qunit": "^2.19.1",
|
|
36
|
+
"rollup": "^2.75.6",
|
|
37
|
+
"rollup-plugin-terser": "^7.0.2",
|
|
38
|
+
"tsd": "^0.24.1"
|
|
36
39
|
},
|
|
37
40
|
"type": "module",
|
|
38
41
|
"main": "dist/object-model.js",
|
|
@@ -41,6 +44,13 @@
|
|
|
41
44
|
"require": "./dist/object-model.cjs"
|
|
42
45
|
},
|
|
43
46
|
"types": "types/index.d.ts",
|
|
47
|
+
"tsd": {
|
|
48
|
+
"directory": "test"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"npm": ">=7.0.0",
|
|
52
|
+
"node": ">=17.5.0"
|
|
53
|
+
},
|
|
44
54
|
"keywords": [
|
|
45
55
|
"typed",
|
|
46
56
|
"types",
|
package/rollup.config.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { terser } from "rollup-plugin-terser";
|
|
2
|
-
|
|
3
|
-
const isProduction = process.env.BUILD === 'production';
|
|
4
|
-
|
|
5
|
-
export default {
|
|
6
|
-
input: 'build/bundle-entry' + (isProduction ? '.js' : '.dev.js'),
|
|
7
|
-
output: {
|
|
8
|
-
file: 'dist/object-model' + (isProduction ? '.min.js' : '.js'),
|
|
9
|
-
format: 'esm',
|
|
10
|
-
sourcemap: true,
|
|
11
|
-
extend: true
|
|
12
|
-
},
|
|
13
|
-
plugins: isProduction ? [terser()] : []
|
|
1
|
+
import { terser } from "rollup-plugin-terser";
|
|
2
|
+
|
|
3
|
+
const isProduction = process.env.BUILD === 'production';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
input: 'build/bundle-entry' + (isProduction ? '.js' : '.dev.js'),
|
|
7
|
+
output: {
|
|
8
|
+
file: 'dist/object-model' + (isProduction ? '.min.js' : '.js'),
|
|
9
|
+
format: 'esm',
|
|
10
|
+
sourcemap: true,
|
|
11
|
+
extend: true
|
|
12
|
+
},
|
|
13
|
+
plugins: isProduction ? [terser()] : []
|
|
14
14
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ModelDefinition, FromDefinition } from "../types/definitions";
|
|
2
|
+
import { Model } from "./object-model";
|
|
3
|
+
|
|
4
|
+
export interface ArrayModel<D> extends Model<D> {
|
|
5
|
+
(array: FromDefinition<D>[]): FromDefinition<D>[];
|
|
6
|
+
new(array: FromDefinition<D>[]): FromDefinition<D>[];
|
|
7
|
+
|
|
8
|
+
extend<E extends ModelDefinition[]>(...extensions: E): ArrayModel<E extends [] ? D : [D, ...E]>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ArrayModelConstructor {
|
|
12
|
+
<D extends ModelDefinition>(itemDefinition: D): ArrayModel<D>;
|
|
13
|
+
new<D extends ModelDefinition>(itemDefinition: D): ArrayModel<D>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const ArrayModel: ArrayModelConstructor;
|
package/src/array-model.js
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
import {
|
|
2
|
-
_original, _check, cast, checkAssertions, checkDefinition,
|
|
3
|
-
extendDefinition, extendModel, formatDefinition, Model, stackError, unstackErrors
|
|
4
|
-
} from "./object-model.js"
|
|
5
|
-
import { initListModel } from "./list-model.js"
|
|
6
|
-
import { extend } from "./helpers.js"
|
|
7
|
-
|
|
8
|
-
export default function ArrayModel(initialDefinition) {
|
|
9
|
-
const model = initListModel(
|
|
10
|
-
Array,
|
|
11
|
-
ArrayModel,
|
|
12
|
-
initialDefinition,
|
|
13
|
-
a => Array.isArray(a) ? a.map(arg => cast(arg, model.definition)) : a,
|
|
14
|
-
a => [...a],
|
|
15
|
-
{
|
|
16
|
-
"copyWithin": [],
|
|
17
|
-
"fill": [0, 0],
|
|
18
|
-
"pop": [],
|
|
19
|
-
"push": [0],
|
|
20
|
-
"reverse": [],
|
|
21
|
-
"shift": [],
|
|
22
|
-
"sort": [],
|
|
23
|
-
"splice": [2],
|
|
24
|
-
"unshift": [0]
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
set(arr, key, val) {
|
|
28
|
-
return controlMutation(model, arr, key, val, (a, v) => a[key] = v, true)
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
deleteProperty(arr, key) {
|
|
32
|
-
return controlMutation(model, arr, key, undefined, a => delete a[key])
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
return model
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
extend(ArrayModel, Model, {
|
|
41
|
-
toString(stack) {
|
|
42
|
-
return "Array of " + formatDefinition(this.definition, stack)
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
[_check](arr, path, errors, stack) {
|
|
46
|
-
if (Array.isArray(arr))
|
|
47
|
-
(arr[_original] || arr).forEach((a, i) => checkDefinition(a, this.definition, `${path || "Array"}[${i}]`, errors, stack))
|
|
48
|
-
else stackError(errors, this, arr, path)
|
|
49
|
-
|
|
50
|
-
checkAssertions(arr, this, path, errors)
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
extend(...newParts) {
|
|
54
|
-
return extendModel(new ArrayModel(extendDefinition(this.definition, newParts)), this)
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
const controlMutation = (model, array, key, value, applyMutation, canBeExtended) => {
|
|
59
|
-
const path = `Array[${key}]`
|
|
60
|
-
const isInDef = (+key >= 0 && (canBeExtended || key in array))
|
|
61
|
-
if (isInDef) value = checkDefinition(value, model.definition, path, model.errors, [], true)
|
|
62
|
-
|
|
63
|
-
const testArray = [...array]
|
|
64
|
-
applyMutation(testArray)
|
|
65
|
-
checkAssertions(testArray, model, path)
|
|
66
|
-
const isSuccess = !unstackErrors(model)
|
|
67
|
-
if (isSuccess) applyMutation(array, value)
|
|
68
|
-
return isSuccess
|
|
1
|
+
import {
|
|
2
|
+
_original, _check, cast, checkAssertions, checkDefinition,
|
|
3
|
+
extendDefinition, extendModel, formatDefinition, Model, stackError, unstackErrors
|
|
4
|
+
} from "./object-model.js"
|
|
5
|
+
import { initListModel } from "./list-model.js"
|
|
6
|
+
import { extend } from "./helpers.js"
|
|
7
|
+
|
|
8
|
+
export default function ArrayModel(initialDefinition) {
|
|
9
|
+
const model = initListModel(
|
|
10
|
+
Array,
|
|
11
|
+
ArrayModel,
|
|
12
|
+
initialDefinition,
|
|
13
|
+
a => Array.isArray(a) ? a.map(arg => cast(arg, model.definition)) : a,
|
|
14
|
+
a => [...a],
|
|
15
|
+
{
|
|
16
|
+
"copyWithin": [],
|
|
17
|
+
"fill": [0, 0],
|
|
18
|
+
"pop": [],
|
|
19
|
+
"push": [0],
|
|
20
|
+
"reverse": [],
|
|
21
|
+
"shift": [],
|
|
22
|
+
"sort": [],
|
|
23
|
+
"splice": [2],
|
|
24
|
+
"unshift": [0]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
set(arr, key, val) {
|
|
28
|
+
return controlMutation(model, arr, key, val, (a, v) => a[key] = v, true)
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
deleteProperty(arr, key) {
|
|
32
|
+
return controlMutation(model, arr, key, undefined, a => delete a[key])
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return model
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
extend(ArrayModel, Model, {
|
|
41
|
+
toString(stack) {
|
|
42
|
+
return "Array of " + formatDefinition(this.definition, stack)
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
[_check](arr, path, errors, stack) {
|
|
46
|
+
if (Array.isArray(arr))
|
|
47
|
+
(arr[_original] || arr).forEach((a, i) => checkDefinition(a, this.definition, `${path || "Array"}[${i}]`, errors, stack))
|
|
48
|
+
else stackError(errors, this, arr, path)
|
|
49
|
+
|
|
50
|
+
checkAssertions(arr, this, path, errors)
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
extend(...newParts) {
|
|
54
|
+
return extendModel(new ArrayModel(extendDefinition(this.definition, newParts)), this)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const controlMutation = (model, array, key, value, applyMutation, canBeExtended) => {
|
|
59
|
+
const path = `Array[${key}]`
|
|
60
|
+
const isInDef = (+key >= 0 && (canBeExtended || key in array))
|
|
61
|
+
if (isInDef) value = checkDefinition(value, model.definition, path, model.errors, [], true)
|
|
62
|
+
|
|
63
|
+
const testArray = [...array]
|
|
64
|
+
applyMutation(testArray)
|
|
65
|
+
checkAssertions(testArray, model, path)
|
|
66
|
+
const isSuccess = !unstackErrors(model)
|
|
67
|
+
if (isSuccess) applyMutation(array, value)
|
|
68
|
+
return isSuccess
|
|
69
69
|
}
|
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;
|