typeomatica 0.2.5 → 0.2.7
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/.husky/pre-commit +4 -0
- package/lib/errors.d.ts +6 -0
- package/lib/errors.js +9 -0
- package/lib/fields.d.ts +2 -0
- package/lib/fields.js +9 -0
- package/lib/index.d.ts +5 -5
- package/lib/index.js +27 -112
- package/lib/types/functions.d.ts +1 -0
- package/lib/types/functions.js +8 -0
- package/lib/types/index.d.ts +6 -0
- package/lib/types/index.js +22 -0
- package/lib/types/nullish.d.ts +4 -0
- package/lib/types/nullish.js +16 -0
- package/lib/types/objects.d.ts +4 -0
- package/lib/types/objects.js +20 -0
- package/lib/types/primitives.d.ts +4 -0
- package/lib/types/primitives.js +48 -0
- package/lib/types/special.d.ts +4 -0
- package/lib/types/special.js +20 -0
- package/package.json +5 -3
- package/src/errors.ts +8 -0
- package/src/fields.ts +53 -0
- package/src/index.ts +56 -146
- package/src/types/functions.ts +7 -0
- package/src/types/index.ts +17 -0
- package/src/types/nullish.ts +15 -0
- package/src/types/objects.ts +19 -0
- package/src/types/primitives.ts +68 -0
- package/src/types/special.ts +19 -0
- package/test/index.ts +67 -4
package/lib/errors.d.ts
ADDED
package/lib/errors.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ErrorsNames = void 0;
|
|
4
|
+
exports.ErrorsNames = {
|
|
5
|
+
TYPE_MISMATCH: 'Type Mismatch',
|
|
6
|
+
ACCESS_DENIED: 'Value Access Denied',
|
|
7
|
+
MISSING_PROP: 'Attempt to Access to Undefined Prop',
|
|
8
|
+
RIP_FUNCTIONS: 'Functions are Restricted'
|
|
9
|
+
};
|
package/lib/fields.d.ts
ADDED
package/lib/fields.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FieldConstructor = exports.SymbolInitialValue = void 0;
|
|
4
|
+
exports.SymbolInitialValue = Symbol('Initial Value');
|
|
5
|
+
exports.FieldConstructor = function (value) {
|
|
6
|
+
this[exports.SymbolInitialValue] = value;
|
|
7
|
+
};
|
|
8
|
+
Object.freeze(exports.FieldConstructor.prototype);
|
|
9
|
+
Object.seal(exports.FieldConstructor.prototype);
|
package/lib/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export declare type IDEF<T, P = {}, R = {}> = {
|
|
2
|
-
new (...args: any[]): T;
|
|
3
|
-
(this: T, ...args: any[]): R;
|
|
4
|
-
prototype: P;
|
|
5
|
-
};
|
|
6
1
|
declare const BaseConstructor: ObjectConstructor;
|
|
7
2
|
export declare class BaseClass extends BaseConstructor {
|
|
8
3
|
}
|
|
4
|
+
export declare type IDEF<T, P = {}, R = {}> = {
|
|
5
|
+
new (...args: unknown[]): T;
|
|
6
|
+
(this: T, ...args: unknown[]): R;
|
|
7
|
+
prototype: P;
|
|
8
|
+
};
|
|
9
9
|
export {};
|
package/lib/index.js
CHANGED
|
@@ -1,113 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BaseClass = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
MISSING_PROP: 'Attempt to Access to Undefined Prop',
|
|
8
|
-
RIP_FUNCTIONS: 'Functions are Restricted'
|
|
9
|
-
};
|
|
10
|
-
const PRIMITIVE_TYPES = [
|
|
11
|
-
'string',
|
|
12
|
-
'number',
|
|
13
|
-
'boolean',
|
|
14
|
-
];
|
|
15
|
-
const isPrimitive = (value) => {
|
|
16
|
-
return PRIMITIVE_TYPES.includes(typeof value);
|
|
17
|
-
};
|
|
18
|
-
const primitives = (initialValue) => {
|
|
19
|
-
let value = Object(initialValue);
|
|
20
|
-
const initialType = typeof initialValue;
|
|
21
|
-
return {
|
|
22
|
-
get() {
|
|
23
|
-
const proxyAsValue = new Proxy(value, {
|
|
24
|
-
get(_, prop) {
|
|
25
|
-
if (prop === Symbol.toPrimitive) {
|
|
26
|
-
return function (hint) {
|
|
27
|
-
if (hint !== initialType) {
|
|
28
|
-
throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
29
|
-
}
|
|
30
|
-
return value.valueOf();
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
if (prop === 'valueOf') {
|
|
34
|
-
return function () {
|
|
35
|
-
return value.valueOf();
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
if (value[prop] instanceof Function) {
|
|
39
|
-
return value[prop].bind(value);
|
|
40
|
-
}
|
|
41
|
-
return value[prop];
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
return proxyAsValue;
|
|
45
|
-
},
|
|
46
|
-
set(replacementValue) {
|
|
47
|
-
if (replacementValue instanceof value.constructor) {
|
|
48
|
-
value = replacementValue;
|
|
49
|
-
return value;
|
|
50
|
-
}
|
|
51
|
-
const prevalue = Object(replacementValue);
|
|
52
|
-
if (prevalue instanceof value.constructor) {
|
|
53
|
-
value = prevalue;
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
57
|
-
throw error;
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
const special = (value) => {
|
|
62
|
-
return {
|
|
63
|
-
get() {
|
|
64
|
-
return value;
|
|
65
|
-
},
|
|
66
|
-
set(replacementValue) {
|
|
67
|
-
if (typeof replacementValue === typeof value) {
|
|
68
|
-
value = replacementValue;
|
|
69
|
-
return value;
|
|
70
|
-
}
|
|
71
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
72
|
-
throw error;
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
};
|
|
76
|
-
const nullish = (value) => {
|
|
77
|
-
return {
|
|
78
|
-
get() {
|
|
79
|
-
return value;
|
|
80
|
-
},
|
|
81
|
-
set() {
|
|
82
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
};
|
|
87
|
-
const objects = (value) => {
|
|
88
|
-
return {
|
|
89
|
-
get() {
|
|
90
|
-
return value;
|
|
91
|
-
},
|
|
92
|
-
set(replacementValue) {
|
|
93
|
-
if (replacementValue instanceof Object && replacementValue.constructor === value.constructor) {
|
|
94
|
-
value = replacementValue;
|
|
95
|
-
return value;
|
|
96
|
-
}
|
|
97
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
98
|
-
throw error;
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
};
|
|
102
|
-
const functions = () => {
|
|
103
|
-
throw new TypeError(ErrorsNames.RIP_FUNCTIONS);
|
|
104
|
-
};
|
|
4
|
+
const errors_1 = require("./errors");
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
const fields_1 = require("./fields");
|
|
105
7
|
const resolver = Object.entries({
|
|
106
|
-
primitives,
|
|
107
|
-
special,
|
|
108
|
-
nullish,
|
|
109
|
-
objects,
|
|
110
|
-
functions
|
|
8
|
+
primitives: types_1.primitives,
|
|
9
|
+
special: types_1.special,
|
|
10
|
+
nullish: types_1.nullish,
|
|
11
|
+
objects: types_1.objects,
|
|
12
|
+
functions: types_1.functions
|
|
111
13
|
}).reduce((obj, [key, _handler]) => {
|
|
112
14
|
obj[key] = function (initialValue, receiver) {
|
|
113
15
|
const handler = _handler(initialValue);
|
|
@@ -115,14 +17,14 @@ const resolver = Object.entries({
|
|
|
115
17
|
get() {
|
|
116
18
|
const invocationThis = this;
|
|
117
19
|
if (invocationThis !== receiver) {
|
|
118
|
-
throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
20
|
+
throw new ReferenceError(errors_1.ErrorsNames.ACCESS_DENIED);
|
|
119
21
|
}
|
|
120
22
|
return handler.get();
|
|
121
23
|
},
|
|
122
24
|
set(replacementValue) {
|
|
123
25
|
const invocationThis = this;
|
|
124
26
|
if (invocationThis !== receiver) {
|
|
125
|
-
throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
27
|
+
throw new ReferenceError(errors_1.ErrorsNames.ACCESS_DENIED);
|
|
126
28
|
}
|
|
127
29
|
return handler.set(replacementValue);
|
|
128
30
|
}
|
|
@@ -132,15 +34,19 @@ const resolver = Object.entries({
|
|
|
132
34
|
}, {});
|
|
133
35
|
const createProperty = (propName, initialValue, receiver) => {
|
|
134
36
|
const value = initialValue;
|
|
135
|
-
const valueIsPrimitive = isPrimitive(initialValue);
|
|
37
|
+
const valueIsPrimitive = (0, types_1.isPrimitive)(initialValue);
|
|
136
38
|
const isObject = typeof initialValue === 'object';
|
|
137
39
|
const isFunction = initialValue instanceof Function;
|
|
138
40
|
const isNull = initialValue === null;
|
|
139
41
|
const type = valueIsPrimitive ? 'primitives' : (isObject ? (isNull ? 'nullish' : 'objects') : (isFunction ? 'functions' : 'special'));
|
|
140
|
-
const descriptor =
|
|
141
|
-
|
|
42
|
+
const descriptor = (isObject && (value instanceof fields_1.FieldConstructor)) ?
|
|
43
|
+
value
|
|
44
|
+
: Object.assign({ enumerable: true }, resolver[type](value, receiver));
|
|
45
|
+
const result = Reflect.defineProperty(receiver, propName, descriptor);
|
|
142
46
|
return result;
|
|
143
47
|
};
|
|
48
|
+
const util = require('util');
|
|
49
|
+
const props2skip = new Set([util.inspect.custom, Symbol.toStringTag, Symbol.iterator]);
|
|
144
50
|
const handlers = {
|
|
145
51
|
get(target, prop, receiver) {
|
|
146
52
|
const result = Reflect.get(target, prop, receiver);
|
|
@@ -155,7 +61,10 @@ const handlers = {
|
|
|
155
61
|
}, {}));
|
|
156
62
|
};
|
|
157
63
|
}
|
|
158
|
-
|
|
64
|
+
if (props2skip.has(prop)) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
throw new Error(`${errors_1.ErrorsNames.MISSING_PROP}: [ ${String(prop).valueOf()} ] of ${receiver.constructor.name}`);
|
|
159
68
|
},
|
|
160
69
|
set(_, prop, value, receiver) {
|
|
161
70
|
const result = createProperty(prop, value, receiver);
|
|
@@ -196,3 +105,9 @@ Object.defineProperty(module.exports, 'BaseClass', {
|
|
|
196
105
|
},
|
|
197
106
|
enumerable: true
|
|
198
107
|
});
|
|
108
|
+
Object.defineProperty(module.exports, 'FieldConstructor', {
|
|
109
|
+
get() {
|
|
110
|
+
return fields_1.FieldConstructor;
|
|
111
|
+
},
|
|
112
|
+
enumerable: true
|
|
113
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const functions: () => never;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.functions = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const functions = () => {
|
|
6
|
+
throw new TypeError(errors_1.ErrorsNames.RIP_FUNCTIONS);
|
|
7
|
+
};
|
|
8
|
+
exports.functions = functions;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPrimitive = exports.special = exports.primitives = exports.objects = exports.nullish = exports.functions = void 0;
|
|
4
|
+
var functions_1 = require("./functions");
|
|
5
|
+
Object.defineProperty(exports, "functions", { enumerable: true, get: function () { return functions_1.functions; } });
|
|
6
|
+
var nullish_1 = require("./nullish");
|
|
7
|
+
Object.defineProperty(exports, "nullish", { enumerable: true, get: function () { return nullish_1.nullish; } });
|
|
8
|
+
var objects_1 = require("./objects");
|
|
9
|
+
Object.defineProperty(exports, "objects", { enumerable: true, get: function () { return objects_1.objects; } });
|
|
10
|
+
var primitives_1 = require("./primitives");
|
|
11
|
+
Object.defineProperty(exports, "primitives", { enumerable: true, get: function () { return primitives_1.primitives; } });
|
|
12
|
+
var special_1 = require("./special");
|
|
13
|
+
Object.defineProperty(exports, "special", { enumerable: true, get: function () { return special_1.special; } });
|
|
14
|
+
const PRIMITIVE_TYPES = [
|
|
15
|
+
'string',
|
|
16
|
+
'number',
|
|
17
|
+
'boolean',
|
|
18
|
+
];
|
|
19
|
+
const isPrimitive = (value) => {
|
|
20
|
+
return PRIMITIVE_TYPES.includes(typeof value);
|
|
21
|
+
};
|
|
22
|
+
exports.isPrimitive = isPrimitive;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nullish = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const nullish = (value) => {
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
return value;
|
|
9
|
+
},
|
|
10
|
+
set() {
|
|
11
|
+
const error = new TypeError(errors_1.ErrorsNames.TYPE_MISMATCH);
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
exports.nullish = nullish;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.objects = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const objects = (value) => {
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
return value;
|
|
9
|
+
},
|
|
10
|
+
set(replacementValue) {
|
|
11
|
+
if (replacementValue instanceof Object && replacementValue.constructor === value.constructor) {
|
|
12
|
+
value = replacementValue;
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
const error = new TypeError(errors_1.ErrorsNames.TYPE_MISMATCH);
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
exports.objects = objects;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.primitives = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const primitives = (initialValue) => {
|
|
6
|
+
let value = Object(initialValue);
|
|
7
|
+
const initialType = typeof initialValue;
|
|
8
|
+
return {
|
|
9
|
+
get() {
|
|
10
|
+
const proxyAsValue = new Proxy(value, {
|
|
11
|
+
get(_, prop) {
|
|
12
|
+
if (prop === Symbol.toPrimitive) {
|
|
13
|
+
return function (hint) {
|
|
14
|
+
if (hint !== initialType) {
|
|
15
|
+
throw new ReferenceError(errors_1.ErrorsNames.ACCESS_DENIED);
|
|
16
|
+
}
|
|
17
|
+
return value.valueOf();
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (prop === 'valueOf') {
|
|
21
|
+
return function () {
|
|
22
|
+
return value.valueOf();
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (value[prop] instanceof Function) {
|
|
26
|
+
return value[prop].bind(value);
|
|
27
|
+
}
|
|
28
|
+
return value[prop];
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return proxyAsValue;
|
|
32
|
+
},
|
|
33
|
+
set(replacementValue) {
|
|
34
|
+
if (replacementValue instanceof value.constructor) {
|
|
35
|
+
value = replacementValue;
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
const prevalue = Object(replacementValue);
|
|
39
|
+
if (prevalue instanceof value.constructor) {
|
|
40
|
+
value = prevalue;
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
const error = new TypeError(errors_1.ErrorsNames.TYPE_MISMATCH);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
exports.primitives = primitives;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.special = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const special = (value) => {
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
return value;
|
|
9
|
+
},
|
|
10
|
+
set(replacementValue) {
|
|
11
|
+
if (typeof replacementValue === typeof value) {
|
|
12
|
+
value = replacementValue;
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
const error = new TypeError(errors_1.ErrorsNames.TYPE_MISMATCH);
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
exports.special = special;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typeomatica",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "type logic against javascript metaprogramming",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"lint": "npx eslint --fix --ignore-path .gitignore ./lib",
|
|
10
10
|
"test": "npx jest",
|
|
11
11
|
"test:cov": "npx jest --collectCoverage",
|
|
12
|
-
"debug": "
|
|
12
|
+
"debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand",
|
|
13
|
+
"debug:old": "npx --node-options=--inspect-brk jest",
|
|
14
|
+
"prepare": "husky install"
|
|
13
15
|
},
|
|
14
16
|
"keywords": [
|
|
15
17
|
"strict",
|
|
@@ -28,7 +30,7 @@
|
|
|
28
30
|
"@types/jest": "^27.0.1",
|
|
29
31
|
"@types/node": "^16.9.2",
|
|
30
32
|
"eslint": "^7.32.0",
|
|
31
|
-
"husky": "^
|
|
33
|
+
"husky": "^8.0.0",
|
|
32
34
|
"jest": "^27.2.0",
|
|
33
35
|
"lint-staged": "^11.1.2",
|
|
34
36
|
"set-value": "^4.1.0",
|
package/src/errors.ts
ADDED
package/src/fields.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
export const SymbolInitialValue = Symbol('Initial Value');
|
|
4
|
+
|
|
5
|
+
interface FieldDefinition {
|
|
6
|
+
[SymbolInitialValue]: unknown
|
|
7
|
+
// get?: unknown
|
|
8
|
+
// set?: unknown
|
|
9
|
+
// configurable: boolean,
|
|
10
|
+
// enumerable: boolean,
|
|
11
|
+
// writable: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const FieldConstructor = function (this: FieldDefinition, value: unknown) {
|
|
15
|
+
this[SymbolInitialValue] = value;
|
|
16
|
+
} as ObjectConstructor;
|
|
17
|
+
|
|
18
|
+
// Object.assign(FieldConstructor.prototype, {
|
|
19
|
+
// configurable: false,
|
|
20
|
+
// enumerable: false,
|
|
21
|
+
// // writable: false
|
|
22
|
+
// })
|
|
23
|
+
|
|
24
|
+
// Object.defineProperty(FieldConstructor.prototype, 'get', {
|
|
25
|
+
// get() {
|
|
26
|
+
// return this[symbolValue];
|
|
27
|
+
// },
|
|
28
|
+
// // @ts-ignore
|
|
29
|
+
// set(value: unknown) {
|
|
30
|
+
// throw new Error('broken behaviour: assignment to getter');
|
|
31
|
+
// },
|
|
32
|
+
// configurable: false,
|
|
33
|
+
// enumerable: true,
|
|
34
|
+
// // writable: false
|
|
35
|
+
// });
|
|
36
|
+
|
|
37
|
+
// Object.defineProperty(FieldConstructor.prototype, 'set', {
|
|
38
|
+
// get() {
|
|
39
|
+
// return function (this: FieldDefinition, value: unknown) {
|
|
40
|
+
// this[symbolValue] = value;
|
|
41
|
+
// }
|
|
42
|
+
// },
|
|
43
|
+
// // @ts-ignore
|
|
44
|
+
// set(value: unknown) {
|
|
45
|
+
// throw new Error('broken behaviour: assignment to setter');
|
|
46
|
+
// },
|
|
47
|
+
// configurable: false,
|
|
48
|
+
// enumerable: true,
|
|
49
|
+
// // writable: false
|
|
50
|
+
// });
|
|
51
|
+
|
|
52
|
+
Object.freeze(FieldConstructor.prototype);
|
|
53
|
+
Object.seal(FieldConstructor.prototype);
|
package/src/index.ts
CHANGED
|
@@ -1,136 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
TYPE_MISMATCH: 'Type Mismatch',
|
|
5
|
-
ACCESS_DENIED: 'Value Access Denied',
|
|
6
|
-
MISSING_PROP: 'Attempt to Access to Undefined Prop',
|
|
7
|
-
RIP_FUNCTIONS: 'Functions are Restricted'
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const PRIMITIVE_TYPES = [
|
|
11
|
-
'string',
|
|
12
|
-
'number',
|
|
13
|
-
'boolean',
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
const isPrimitive = (value: unknown) => {
|
|
17
|
-
return PRIMITIVE_TYPES.includes(typeof value);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const primitives = (initialValue: object) => {
|
|
21
|
-
let value = Object(initialValue);
|
|
22
|
-
const initialType = typeof initialValue;
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
get() {
|
|
26
|
-
const proxyAsValue = new Proxy(value, {
|
|
27
|
-
// get(target, prop, receiver) {
|
|
28
|
-
get(_, prop) {
|
|
29
|
-
|
|
30
|
-
if (prop === Symbol.toPrimitive) {
|
|
31
|
-
return function (hint: string) {
|
|
32
|
-
if (hint !== initialType) {
|
|
33
|
-
throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
34
|
-
}
|
|
35
|
-
return value.valueOf();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (prop === 'valueOf') {
|
|
40
|
-
return function () {
|
|
41
|
-
return value.valueOf();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// @ts-ignore
|
|
46
|
-
if (value[prop] instanceof Function) {
|
|
47
|
-
return value[prop].bind(value);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return value[prop];
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
return proxyAsValue;
|
|
54
|
-
},
|
|
55
|
-
// get() {
|
|
56
|
-
// const preparedValue = {
|
|
57
|
-
// [Symbol.toPrimitive]() {
|
|
58
|
-
// return function () {
|
|
59
|
-
// throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
60
|
-
// };
|
|
61
|
-
// }
|
|
62
|
-
// };
|
|
63
|
-
// Reflect.setPrototypeOf(preparedValue, value);
|
|
64
|
-
// debugger;
|
|
65
|
-
// return preparedValue;
|
|
66
|
-
// },
|
|
67
|
-
set(replacementValue: unknown) {
|
|
68
|
-
if (replacementValue instanceof value.constructor) {
|
|
69
|
-
value = replacementValue;
|
|
70
|
-
return value;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const prevalue = Object(replacementValue);
|
|
74
|
-
|
|
75
|
-
if (prevalue instanceof value.constructor) {
|
|
76
|
-
value = prevalue;
|
|
77
|
-
return value;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
81
|
-
throw error;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const special = (value: object) => {
|
|
87
|
-
return {
|
|
88
|
-
get() {
|
|
89
|
-
return value;
|
|
90
|
-
},
|
|
91
|
-
set(replacementValue: object) {
|
|
92
|
-
if (typeof replacementValue === typeof value) {
|
|
93
|
-
value = replacementValue;
|
|
94
|
-
return value;
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
98
|
-
throw error;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const nullish = (value: object) => {
|
|
104
|
-
return {
|
|
105
|
-
get() {
|
|
106
|
-
return value;
|
|
107
|
-
},
|
|
108
|
-
set() {
|
|
109
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
110
|
-
throw error;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
};
|
|
3
|
+
import { ErrorsNames } from './errors';
|
|
114
4
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return value;
|
|
124
|
-
}
|
|
125
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
126
|
-
throw error;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
};
|
|
5
|
+
import {
|
|
6
|
+
functions,
|
|
7
|
+
nullish,
|
|
8
|
+
objects,
|
|
9
|
+
primitives,
|
|
10
|
+
special,
|
|
11
|
+
isPrimitive
|
|
12
|
+
} from './types';
|
|
130
13
|
|
|
131
|
-
|
|
132
|
-
throw new TypeError(ErrorsNames.RIP_FUNCTIONS);
|
|
133
|
-
};
|
|
14
|
+
import { FieldConstructor } from './fields';
|
|
134
15
|
|
|
135
16
|
const resolver = Object.entries({
|
|
136
17
|
primitives,
|
|
@@ -163,7 +44,8 @@ const resolver = Object.entries({
|
|
|
163
44
|
return obj;
|
|
164
45
|
}, {});
|
|
165
46
|
|
|
166
|
-
|
|
47
|
+
|
|
48
|
+
const createProperty = (propName: string, initialValue: unknown, receiver: object) => {
|
|
167
49
|
|
|
168
50
|
const value = initialValue;
|
|
169
51
|
const valueIsPrimitive = isPrimitive(initialValue);
|
|
@@ -171,23 +53,40 @@ const createProperty = (propName: string, initialValue: any, receiver: object) =
|
|
|
171
53
|
const isFunction = initialValue instanceof Function;
|
|
172
54
|
const isNull = initialValue === null;
|
|
173
55
|
|
|
56
|
+
/**
|
|
57
|
+
* special: undefined or BigInt or Symbol
|
|
58
|
+
* or other non constructible type
|
|
59
|
+
*/
|
|
60
|
+
|
|
174
61
|
const type = valueIsPrimitive ? 'primitives' : (
|
|
175
62
|
isObject ? (
|
|
176
63
|
isNull ? 'nullish' : 'objects'
|
|
177
64
|
) : (
|
|
178
|
-
isFunction ? 'functions' : 'special'
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
65
|
+
isFunction ? 'functions' : 'special'
|
|
66
|
+
)
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const descriptor = (isObject && (value instanceof FieldConstructor)) ?
|
|
70
|
+
value
|
|
71
|
+
:
|
|
72
|
+
{
|
|
73
|
+
enumerable: true,
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
...resolver[type](value, receiver),
|
|
76
|
+
};
|
|
182
77
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
78
|
+
// if (value instanceof FieldConstructor) {
|
|
79
|
+
// descriptor;
|
|
80
|
+
// debugger;
|
|
81
|
+
// }
|
|
187
82
|
|
|
83
|
+
const result = Reflect.defineProperty(receiver, propName, descriptor);
|
|
188
84
|
return result;
|
|
85
|
+
|
|
189
86
|
};
|
|
190
87
|
|
|
88
|
+
const util = require('util');
|
|
89
|
+
const props2skip = new Set([util.inspect.custom, Symbol.toStringTag, Symbol.iterator]);
|
|
191
90
|
|
|
192
91
|
const handlers = {
|
|
193
92
|
get(target: object, prop: string | symbol, receiver: object) {
|
|
@@ -204,7 +103,10 @@ const handlers = {
|
|
|
204
103
|
}, {}));
|
|
205
104
|
}
|
|
206
105
|
}
|
|
207
|
-
|
|
106
|
+
if (props2skip.has(prop)) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
throw new Error(`${ErrorsNames.MISSING_PROP}: [ ${String(prop).valueOf()} ] of ${receiver.constructor.name}`);
|
|
208
110
|
},
|
|
209
111
|
set(_: object, prop: string, value: unknown, receiver: object) {
|
|
210
112
|
const result = createProperty(prop, value, receiver);
|
|
@@ -221,12 +123,6 @@ const BaseTarget = Object.create(null);
|
|
|
221
123
|
|
|
222
124
|
// const BasePrototype = new Proxy(BaseTarget, handlers);
|
|
223
125
|
|
|
224
|
-
export type IDEF<T, P = {}, R = {}> = {
|
|
225
|
-
new(...args: any[]): T;
|
|
226
|
-
(this: T, ...args: any[]): R;
|
|
227
|
-
prototype: P;
|
|
228
|
-
};
|
|
229
|
-
|
|
230
126
|
// @ts-ignore
|
|
231
127
|
const BaseConstructor = function (this: object, InstanceTarget = BaseTarget) {
|
|
232
128
|
if (!new.target) {
|
|
@@ -261,10 +157,24 @@ Object.defineProperty(module, 'exports', {
|
|
|
261
157
|
|
|
262
158
|
// @ts-ignore
|
|
263
159
|
export class BaseClass extends BaseConstructor { };
|
|
160
|
+
// export { FieldConstructor } from './fields';
|
|
264
161
|
|
|
265
162
|
Object.defineProperty(module.exports, 'BaseClass', {
|
|
266
163
|
get() {
|
|
267
164
|
return BaseClass;
|
|
268
165
|
},
|
|
269
166
|
enumerable: true
|
|
270
|
-
});
|
|
167
|
+
});
|
|
168
|
+
Object.defineProperty(module.exports, 'FieldConstructor', {
|
|
169
|
+
get() {
|
|
170
|
+
return FieldConstructor;
|
|
171
|
+
},
|
|
172
|
+
enumerable: true
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
export type IDEF<T, P = {}, R = {}> = {
|
|
177
|
+
new(...args: unknown[]): T;
|
|
178
|
+
(this: T, ...args: unknown[]): R;
|
|
179
|
+
prototype: P;
|
|
180
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
export { functions } from './functions';
|
|
4
|
+
export { nullish } from './nullish';
|
|
5
|
+
export { objects } from './objects';
|
|
6
|
+
export { primitives } from './primitives';
|
|
7
|
+
export { special } from './special';
|
|
8
|
+
|
|
9
|
+
const PRIMITIVE_TYPES = [
|
|
10
|
+
'string',
|
|
11
|
+
'number',
|
|
12
|
+
'boolean',
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export const isPrimitive = (value: unknown) => {
|
|
16
|
+
return PRIMITIVE_TYPES.includes(typeof value);
|
|
17
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { ErrorsNames } from '../errors';
|
|
4
|
+
|
|
5
|
+
export const objects = (value: object) => {
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
return value;
|
|
9
|
+
},
|
|
10
|
+
set(replacementValue: unknown) {
|
|
11
|
+
if (replacementValue instanceof Object && replacementValue.constructor === value.constructor) {
|
|
12
|
+
value = replacementValue;
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { ErrorsNames } from '../errors';
|
|
4
|
+
|
|
5
|
+
export const primitives = (initialValue: object) => {
|
|
6
|
+
let value = Object(initialValue);
|
|
7
|
+
const initialType = typeof initialValue;
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
get() {
|
|
11
|
+
const proxyAsValue = new Proxy(value, {
|
|
12
|
+
// get(target, prop, receiver) {
|
|
13
|
+
get(_, prop) {
|
|
14
|
+
if (prop === Symbol.toPrimitive) {
|
|
15
|
+
return function (hint: string) {
|
|
16
|
+
if (hint !== initialType) {
|
|
17
|
+
throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
18
|
+
}
|
|
19
|
+
return value.valueOf();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (prop === 'valueOf') {
|
|
24
|
+
return function () {
|
|
25
|
+
return value.valueOf();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
if (value[prop] instanceof Function) {
|
|
31
|
+
return value[prop].bind(value);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return value[prop];
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return proxyAsValue;
|
|
38
|
+
},
|
|
39
|
+
// get() {
|
|
40
|
+
// const preparedValue = {
|
|
41
|
+
// [Symbol.toPrimitive]() {
|
|
42
|
+
// return function () {
|
|
43
|
+
// throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
44
|
+
// };
|
|
45
|
+
// }
|
|
46
|
+
// };
|
|
47
|
+
// Reflect.setPrototypeOf(preparedValue, value);
|
|
48
|
+
// debugger;
|
|
49
|
+
// return preparedValue;
|
|
50
|
+
// },
|
|
51
|
+
set(replacementValue: unknown) {
|
|
52
|
+
if (replacementValue instanceof value.constructor) {
|
|
53
|
+
value = replacementValue;
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const prevalue = Object(replacementValue);
|
|
58
|
+
|
|
59
|
+
if (prevalue instanceof value.constructor) {
|
|
60
|
+
value = prevalue;
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { ErrorsNames } from '../errors';
|
|
4
|
+
|
|
5
|
+
export const special = (value: object) => {
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
return value;
|
|
9
|
+
},
|
|
10
|
+
set(replacementValue: object) {
|
|
11
|
+
if (typeof replacementValue === typeof value) {
|
|
12
|
+
value = replacementValue;
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
package/test/index.ts
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
// or meaningless
|
|
6
6
|
|
|
7
7
|
const BasePrototype = require('..');
|
|
8
|
-
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
import { BaseClass, IDEF, FieldConstructor } from '..';
|
|
10
|
+
|
|
11
|
+
debugger;
|
|
9
12
|
|
|
10
13
|
class Base extends BasePrototype({
|
|
11
14
|
additionalProp: 321,
|
|
@@ -14,14 +17,22 @@ class Base extends BasePrototype({
|
|
|
14
17
|
},
|
|
15
18
|
}) {
|
|
16
19
|
numberValue = 123;
|
|
20
|
+
|
|
21
|
+
get getterField() {
|
|
22
|
+
return '123';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
set setterField(value: string) {
|
|
26
|
+
this.stringValue = value;
|
|
27
|
+
}
|
|
28
|
+
|
|
17
29
|
constructor() {
|
|
18
30
|
super();
|
|
19
31
|
this.stringValue = '123';
|
|
20
32
|
this.booleanValue = true;
|
|
21
33
|
this.objectValue = {};
|
|
22
34
|
}
|
|
23
|
-
}
|
|
24
|
-
|
|
35
|
+
}
|
|
25
36
|
const baseInstance = new Base;
|
|
26
37
|
|
|
27
38
|
const upperInstance = Object.create(baseInstance);
|
|
@@ -36,6 +47,7 @@ type MyFunctionalConstructorInstance = {
|
|
|
36
47
|
};
|
|
37
48
|
|
|
38
49
|
const MyFunctionalConstructor = function () {
|
|
50
|
+
// @ts-ignore
|
|
39
51
|
this.stringProp = '123';
|
|
40
52
|
} as IDEF<MyFunctionalConstructorInstance>;
|
|
41
53
|
|
|
@@ -59,6 +71,38 @@ const extendedSetInstance = new ExtendedSet;
|
|
|
59
71
|
|
|
60
72
|
const MUTATION_VALUE = -2;
|
|
61
73
|
|
|
74
|
+
|
|
75
|
+
class MyFieldConstructor extends FieldConstructor {
|
|
76
|
+
constructor(value: string) {
|
|
77
|
+
super(value);
|
|
78
|
+
Reflect.defineProperty(this, 'enumerable', {
|
|
79
|
+
value: true
|
|
80
|
+
});
|
|
81
|
+
Reflect.defineProperty(this, 'get', {
|
|
82
|
+
get () {
|
|
83
|
+
return function () {
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
Reflect.defineProperty(this, 'set', {
|
|
89
|
+
get () {
|
|
90
|
+
return function (_value: string) {
|
|
91
|
+
value = _value;
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
enumerable: true
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const myField = new MyFieldConstructor('zzz');
|
|
100
|
+
class MadeFieldClass extends BaseClass { myField = myField };
|
|
101
|
+
class SecondMadeFieldClass extends BaseClass { myField = myField };
|
|
102
|
+
const madeFieldInstance = new MadeFieldClass;
|
|
103
|
+
const secondMadeFieldInstance = new MadeFieldClass;
|
|
104
|
+
const thirdMadeFieldInstance = new SecondMadeFieldClass;
|
|
105
|
+
|
|
62
106
|
describe('props tests', () => {
|
|
63
107
|
|
|
64
108
|
test('base instance has props', () => {
|
|
@@ -127,6 +171,25 @@ describe('props tests', () => {
|
|
|
127
171
|
expect(baseInstance.objectValue.a).toEqual(123);
|
|
128
172
|
});
|
|
129
173
|
|
|
174
|
+
test('correct custom field creation', () => {
|
|
175
|
+
expect(madeFieldInstance.myField).toEqual('zzz');
|
|
176
|
+
});
|
|
177
|
+
test('correct custom field assignment', () => {
|
|
178
|
+
madeFieldInstance.myField = 123;
|
|
179
|
+
expect(secondMadeFieldInstance.myField).toEqual(123);
|
|
180
|
+
expect(thirdMadeFieldInstance.myField).toEqual(123);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('correct custom missing prop search creation', () => {
|
|
184
|
+
// @ts-ignore
|
|
185
|
+
expect(madeFieldInstance[Symbol.toStringTag]).toEqual(undefined);
|
|
186
|
+
// @ts-ignore
|
|
187
|
+
expect(madeFieldInstance[Symbol.iterator]).toEqual(undefined);
|
|
188
|
+
const util = require('util');
|
|
189
|
+
// @ts-ignore
|
|
190
|
+
expect(madeFieldInstance[util.inspect.custom]).toEqual(undefined);
|
|
191
|
+
});
|
|
192
|
+
|
|
130
193
|
test('wrong assignment to objects', () => {
|
|
131
194
|
|
|
132
195
|
expect(() => {
|
|
@@ -249,7 +312,7 @@ describe('props tests', () => {
|
|
|
249
312
|
|
|
250
313
|
baseInstance.missingValue > 1;
|
|
251
314
|
|
|
252
|
-
}).toThrow(new TypeError('Attempt to Access to Undefined Prop: [ missingValue ]'));
|
|
315
|
+
}).toThrow(new TypeError('Attempt to Access to Undefined Prop: [ missingValue ] of Base'));
|
|
253
316
|
});
|
|
254
317
|
|
|
255
318
|
});
|