typeomatica 0.2.3 → 0.2.8

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.
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ npm test
package/jest.config.js CHANGED
@@ -4,7 +4,7 @@ module.exports = {
4
4
  testMatch: ['**/test/**/index.ts', '**/test/**/addition.js'],
5
5
  globals: {
6
6
  'ts-jest': {
7
- tsconfig: './tsconfig.spec.json'
7
+ tsconfig: './test/tsconfig.json'
8
8
  }
9
9
  },
10
10
  };
@@ -0,0 +1,7 @@
1
+ export declare const ErrorsNames: {
2
+ TYPE_MISMATCH: string;
3
+ ACCESS_DENIED: string;
4
+ MISSING_PROP: string;
5
+ RIP_FUNCTIONS: string;
6
+ FORBIDDEN_RE: string;
7
+ };
package/lib/errors.js ADDED
@@ -0,0 +1,10 @@
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
+ FORBIDDEN_RE: 'Re-Assirnment is Forbidden'
10
+ };
@@ -0,0 +1,11 @@
1
+ export declare const SymbolInitialValue: unique symbol;
2
+ interface FieldDefinition {
3
+ [SymbolInitialValue]: unknown;
4
+ }
5
+ export declare class FieldConstructor implements FieldDefinition {
6
+ [SymbolInitialValue]: unknown;
7
+ get get(): (this: FieldDefinition) => unknown;
8
+ get set(): () => never;
9
+ constructor(value: unknown);
10
+ }
11
+ export {};
package/lib/fields.js ADDED
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FieldConstructor = exports.SymbolInitialValue = void 0;
4
+ exports.SymbolInitialValue = Symbol('Initial Value');
5
+ const errors_1 = require("./errors");
6
+ class FieldConstructor {
7
+ constructor(value) {
8
+ this[exports.SymbolInitialValue] = value;
9
+ }
10
+ get get() {
11
+ const self = this;
12
+ return function () {
13
+ return self[exports.SymbolInitialValue];
14
+ };
15
+ }
16
+ get set() {
17
+ return function () {
18
+ throw new TypeError(errors_1.ErrorsNames.FORBIDDEN_RE);
19
+ };
20
+ }
21
+ }
22
+ exports.FieldConstructor = FieldConstructor;
23
+ Object.freeze(FieldConstructor.prototype);
24
+ Object.seal(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 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
- };
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,26 @@ 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 = resolver[type](value, receiver);
141
- const result = Reflect.defineProperty(receiver, propName, Object.assign(Object.assign({}, descriptor), { enumerable: true }));
142
- return result;
42
+ const descriptor = (isObject && (value instanceof fields_1.FieldConstructor)) ?
43
+ value
44
+ : Object.assign({ enumerable: true }, resolver[type](value, receiver));
45
+ try {
46
+ const result = Reflect.defineProperty(receiver, propName, descriptor);
47
+ return result;
48
+ }
49
+ catch (error) {
50
+ throw error;
51
+ }
143
52
  };
53
+ const props2skip = new Set([Symbol.toStringTag, Symbol.iterator]);
54
+ const util = require('util');
55
+ const hasNodeInspect = (util && util.inspect && util.inspect.custom);
56
+ hasNodeInspect && (props2skip.add(util.inspect.custom));
144
57
  const handlers = {
145
58
  get(target, prop, receiver) {
146
59
  const result = Reflect.get(target, prop, receiver);
@@ -155,7 +68,10 @@ const handlers = {
155
68
  }, {}));
156
69
  };
157
70
  }
158
- throw new Error(`${ErrorsNames.MISSING_PROP}: [ ${String(prop).valueOf()} ]`);
71
+ if (props2skip.has(prop)) {
72
+ return undefined;
73
+ }
74
+ throw new Error(`${errors_1.ErrorsNames.MISSING_PROP}: [ ${String(prop).valueOf()} ] of ${receiver.constructor.name}`);
159
75
  },
160
76
  set(_, prop, value, receiver) {
161
77
  const result = createProperty(prop, value, receiver);
@@ -196,3 +112,9 @@ Object.defineProperty(module.exports, 'BaseClass', {
196
112
  },
197
113
  enumerable: true
198
114
  });
115
+ Object.defineProperty(module.exports, 'FieldConstructor', {
116
+ get() {
117
+ return fields_1.FieldConstructor;
118
+ },
119
+ enumerable: true
120
+ });
@@ -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,6 @@
1
+ export { functions } from './functions';
2
+ export { nullish } from './nullish';
3
+ export { objects } from './objects';
4
+ export { primitives } from './primitives';
5
+ export { special } from './special';
6
+ export declare const isPrimitive: (value: unknown) => boolean;
@@ -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,4 @@
1
+ export declare const nullish: (value: object) => {
2
+ get(): object;
3
+ set(): never;
4
+ };
@@ -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,4 @@
1
+ export declare const objects: (value: object) => {
2
+ get(): object;
3
+ set(replacementValue: unknown): object;
4
+ };
@@ -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,4 @@
1
+ export declare const primitives: (initialValue: object) => {
2
+ get(): any;
3
+ set(replacementValue: unknown): any;
4
+ };
@@ -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,4 @@
1
+ export declare const special: (value: object) => {
2
+ get(): object;
3
+ set(replacementValue: object): object;
4
+ };
@@ -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",
3
+ "version": "0.2.8",
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": "npx --node-arg=--inspect-brk jest"
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",
@@ -25,16 +27,16 @@
25
27
  "url": "git+https://github.com/wentout/typeomatica.git"
26
28
  },
27
29
  "devDependencies": {
28
- "@types/jest": "^26.0.23",
29
- "@types/node": "^14.14.44",
30
- "eslint": "^7.26.0",
31
- "husky": "^3.1.0",
32
- "jest": "^26.6.3",
33
- "lint-staged": "^10.5.4",
34
- "ts-jest": "^26.5.6",
35
- "ts-node": "^9.1.1",
30
+ "@types/jest": "^27.0.1",
31
+ "@types/node": "^16.9.2",
32
+ "eslint": "^7.32.0",
33
+ "husky": "^8.0.0",
34
+ "jest": "^27.2.0",
35
+ "lint-staged": "^11.1.2",
36
+ "set-value": "^4.1.0",
37
+ "ts-jest": "^27.0.5",
38
+ "ts-node": "^10.2.1",
36
39
  "tslint": "^6.1.3",
37
- "typescript": "^4.2.4",
38
- "typologica": "0.0.77"
40
+ "typescript": "^4.4.3"
39
41
  }
40
42
  }
package/src/errors.ts ADDED
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ export const ErrorsNames = {
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
+ FORBIDDEN_RE: 'Re-Assirnment is Forbidden'
9
+ };
package/src/fields.ts ADDED
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+
3
+ export const SymbolInitialValue = Symbol('Initial Value');
4
+
5
+ import { ErrorsNames } from './errors'
6
+
7
+ interface FieldDefinition {
8
+ [SymbolInitialValue]: unknown
9
+ // get?: unknown
10
+ // set?: unknown
11
+ // configurable: boolean,
12
+ // enumerable: boolean,
13
+ // writable: boolean
14
+ }
15
+
16
+ // export const FieldConstructor = function (this: FieldDefinition, value: unknown) {
17
+ // this[SymbolInitialValue] = value;
18
+ // } as ObjectConstructor;
19
+
20
+ export class FieldConstructor implements FieldDefinition {
21
+ [SymbolInitialValue]: unknown
22
+ public get get () {
23
+ const self = this;
24
+ return function (this: FieldDefinition) {
25
+ return self[SymbolInitialValue];
26
+ }
27
+ }
28
+ public get set () {
29
+ return function () {
30
+ throw new TypeError(ErrorsNames.FORBIDDEN_RE);
31
+ }
32
+ }
33
+ constructor (value: unknown) {
34
+ this[SymbolInitialValue] = value;
35
+ }
36
+ }
37
+
38
+ // Object.assign(FieldConstructor.prototype, {
39
+ // configurable: false,
40
+ // enumerable: false,
41
+ // // writable: false
42
+ // })
43
+
44
+ // Object.defineProperty(FieldConstructor.prototype, 'get', {
45
+ // get() {
46
+ // return this[symbolValue];
47
+ // },
48
+ // // @ts-ignore
49
+ // set(value: unknown) {
50
+ // throw new Error('broken behaviour: assignment to getter');
51
+ // },
52
+ // configurable: false,
53
+ // enumerable: true,
54
+ // // writable: false
55
+ // });
56
+
57
+ // Object.defineProperty(FieldConstructor.prototype, 'set', {
58
+ // get() {
59
+ // return function (this: FieldDefinition, value: unknown) {
60
+ // this[symbolValue] = value;
61
+ // }
62
+ // },
63
+ // // @ts-ignore
64
+ // set(value: unknown) {
65
+ // throw new Error('broken behaviour: assignment to setter');
66
+ // },
67
+ // configurable: false,
68
+ // enumerable: true,
69
+ // // writable: false
70
+ // });
71
+
72
+ Object.freeze(FieldConstructor.prototype);
73
+ Object.seal(FieldConstructor.prototype);
package/src/index.ts CHANGED
@@ -1,136 +1,17 @@
1
1
  'use strict';
2
2
 
3
- const ErrorsNames = {
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
- const objects = (value: object) => {
116
- return {
117
- get() {
118
- return value;
119
- },
120
- set(replacementValue: unknown) {
121
- if (replacementValue instanceof Object && replacementValue.constructor === value.constructor) {
122
- value = replacementValue;
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
- const functions = () => {
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
- const createProperty = (propName: string, initialValue: any, receiver: object) => {
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,47 @@ 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'));
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
+ };
179
77
 
180
- // @ts-ignore
181
- const descriptor = resolver[type](value, receiver);
78
+ // if (value instanceof FieldConstructor) {
79
+ // descriptor;
80
+ // debugger;
81
+ // }
182
82
 
183
- const result = Reflect.defineProperty(receiver, propName, {
184
- ...descriptor,
185
- enumerable: true
186
- });
83
+ try {
84
+ const result = Reflect.defineProperty(receiver, propName, descriptor);
85
+ return result;
86
+ } catch (error) {
87
+ // debugger;
88
+ throw error;
89
+ }
187
90
 
188
- return result;
189
91
  };
190
92
 
93
+ const props2skip = new Set([Symbol.toStringTag, Symbol.iterator]);
94
+ const util = require('util');
95
+ const hasNodeInspect = (util && util.inspect && util.inspect.custom);
96
+ hasNodeInspect && (props2skip.add(util.inspect.custom));
191
97
 
192
98
  const handlers = {
193
99
  get(target: object, prop: string | symbol, receiver: object) {
@@ -204,7 +110,11 @@ const handlers = {
204
110
  }, {}));
205
111
  }
206
112
  }
207
- throw new Error(`${ErrorsNames.MISSING_PROP}: [ ${String(prop).valueOf()} ]`);
113
+ // @ts-ignore
114
+ if (props2skip.has(prop)) {
115
+ return undefined;
116
+ }
117
+ throw new Error(`${ErrorsNames.MISSING_PROP}: [ ${String(prop).valueOf()} ] of ${receiver.constructor.name}`);
208
118
  },
209
119
  set(_: object, prop: string, value: unknown, receiver: object) {
210
120
  const result = createProperty(prop, value, receiver);
@@ -221,12 +131,6 @@ const BaseTarget = Object.create(null);
221
131
 
222
132
  // const BasePrototype = new Proxy(BaseTarget, handlers);
223
133
 
224
- export type IDEF<T, P = {}, R = {}> = {
225
- new(...args: any[]): T;
226
- (this: T, ...args: any[]): R;
227
- prototype: P;
228
- };
229
-
230
134
  // @ts-ignore
231
135
  const BaseConstructor = function (this: object, InstanceTarget = BaseTarget) {
232
136
  if (!new.target) {
@@ -261,10 +165,24 @@ Object.defineProperty(module, 'exports', {
261
165
 
262
166
  // @ts-ignore
263
167
  export class BaseClass extends BaseConstructor { };
168
+ // export { FieldConstructor } from './fields';
264
169
 
265
170
  Object.defineProperty(module.exports, 'BaseClass', {
266
171
  get() {
267
172
  return BaseClass;
268
173
  },
269
174
  enumerable: true
270
- });
175
+ });
176
+ Object.defineProperty(module.exports, 'FieldConstructor', {
177
+ get() {
178
+ return FieldConstructor;
179
+ },
180
+ enumerable: true
181
+ });
182
+
183
+
184
+ export type IDEF<T, P = {}, R = {}> = {
185
+ new(...args: unknown[]): T;
186
+ (this: T, ...args: unknown[]): R;
187
+ prototype: P;
188
+ };
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ import { ErrorsNames } from '../errors';
4
+
5
+ export const functions = () => {
6
+ throw new TypeError(ErrorsNames.RIP_FUNCTIONS);
7
+ };
@@ -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,15 @@
1
+ 'use strict';
2
+
3
+ import { ErrorsNames } from '../errors';
4
+
5
+ export const nullish = (value: object) => {
6
+ return {
7
+ get() {
8
+ return value;
9
+ },
10
+ set() {
11
+ const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
12
+ throw error;
13
+ }
14
+ }
15
+ };
@@ -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
- import { BaseClass, IDEF } from '..';
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);
@@ -29,7 +40,6 @@ const upperInstance = Object.create(baseInstance);
29
40
  class SimpleBase extends BaseClass {
30
41
  stringProp = '123';
31
42
  };
32
-
33
43
  const simpleInstance = new SimpleBase;
34
44
 
35
45
  type MyFunctionalConstructorInstance = {
@@ -37,6 +47,7 @@ type MyFunctionalConstructorInstance = {
37
47
  };
38
48
 
39
49
  const MyFunctionalConstructor = function () {
50
+ // @ts-ignore
40
51
  this.stringProp = '123';
41
52
  } as IDEF<MyFunctionalConstructorInstance>;
42
53
 
@@ -44,7 +55,8 @@ Reflect.setPrototypeOf(MyFunctionalConstructor.prototype, new BasePrototype);
44
55
 
45
56
  const myFunctionalInstance = new MyFunctionalConstructor();
46
57
 
47
- class TripleExtend extends Base { };
58
+ class SecondaryExtend extends Base { second = 123 };
59
+ class TripleExtend extends SecondaryExtend { };
48
60
  const tiripleExtendInstance = new TripleExtend;
49
61
 
50
62
  class NetworkedExtention extends BasePrototype(tiripleExtendInstance) { };
@@ -59,6 +71,84 @@ const extendedSetInstance = new ExtendedSet;
59
71
 
60
72
  const MUTATION_VALUE = -2;
61
73
 
74
+
75
+ class MyFieldConstructorNoRe extends FieldConstructor {
76
+ _value: string
77
+ constructor(value: string) {
78
+ super(value);
79
+ Reflect.defineProperty(this, 'enumerable', {
80
+ value: true
81
+ });
82
+ this._value = value;
83
+ }
84
+ }
85
+ class MyFieldConstructorReGet extends MyFieldConstructorNoRe {
86
+ constructor(value: string) {
87
+ super(value);
88
+ const self = this;
89
+ Reflect.defineProperty(this, 'enumerable', {
90
+ value: true
91
+ });
92
+ Reflect.defineProperty(this, 'get', {
93
+ get() {
94
+ return function () {
95
+ return self._value;
96
+ }
97
+ },
98
+ enumerable: true
99
+ });
100
+ }
101
+ }
102
+ class MyFieldConstructorReSet extends MyFieldConstructorNoRe {
103
+ constructor(value: string) {
104
+ super(value);
105
+ const self = this;
106
+ Reflect.defineProperty(this, 'enumerable', {
107
+ value: true
108
+ });
109
+ Reflect.defineProperty(this, 'set', {
110
+ get() {
111
+ return function (value: string) {
112
+ self._value = value;
113
+ }
114
+ },
115
+ enumerable: true
116
+ });
117
+ }
118
+ }
119
+ class MyFieldConstructor extends MyFieldConstructorReGet {
120
+ constructor(value: string) {
121
+ super(value);
122
+ const self = this;
123
+ Reflect.defineProperty(this, 'enumerable', {
124
+ value: true
125
+ });
126
+ Reflect.defineProperty(this, 'set', {
127
+ get() {
128
+ return function (value: string) {
129
+ self._value = value;
130
+ }
131
+ },
132
+ enumerable: true
133
+ });
134
+ }
135
+ }
136
+
137
+ const myField = new MyFieldConstructor('initial value');
138
+ const myFieldReGet = new MyFieldConstructorReGet('initial value for get check');
139
+ const myFieldReSet = new MyFieldConstructorReSet('initial value for set check');
140
+
141
+ class MadeFieldClass extends BaseClass { myField = myField as unknown | string };
142
+ class SecondMadeFieldClass extends BaseClass { myField = myField as unknown | string };
143
+ const madeFieldInstance = new MadeFieldClass;
144
+ const secondMadeFieldInstance = new MadeFieldClass;
145
+ const thirdMadeFieldInstance = new SecondMadeFieldClass;
146
+
147
+ class MadeReGet extends BaseClass { myField = myFieldReGet as unknown | string }
148
+ class MadeReSet extends BaseClass { myField = myFieldReSet as unknown | string }
149
+ const madeReGet = new MadeReGet;
150
+ const madeReSet = new MadeReSet;
151
+
62
152
  describe('props tests', () => {
63
153
 
64
154
  test('base instance has props', () => {
@@ -127,6 +217,62 @@ describe('props tests', () => {
127
217
  expect(baseInstance.objectValue.a).toEqual(123);
128
218
  });
129
219
 
220
+ test('correct custom field creation', () => {
221
+ expect(madeFieldInstance.myField).toEqual('initial value');
222
+ });
223
+
224
+ test('correct custom field assignment', () => {
225
+ madeFieldInstance.myField = 'replaced';
226
+ expect(secondMadeFieldInstance.myField).toEqual('replaced');
227
+ expect(thirdMadeFieldInstance.myField).toEqual('replaced');
228
+ });
229
+
230
+ test('correct custom field no-re-assignment', () => {
231
+ expect(madeReGet.myField).toEqual('initial value for get check');
232
+ expect(() => {
233
+
234
+ madeReGet.myField = 'replaced';
235
+
236
+ }).toThrow(new TypeError('Re-Assirnment is Forbidden'));
237
+ });
238
+
239
+ test('correct custom field setter only', () => {
240
+ madeReSet.myField = 'replaced';
241
+ expect(madeReSet.myField).toEqual('initial value for set check');
242
+ });
243
+
244
+ test('takes error on wrong field definition', () => {
245
+ expect(() => {
246
+ class WrongFieldConstructor extends FieldConstructor {
247
+ value: number
248
+ constructor(value: number) {
249
+ super(value)
250
+ this.value = value;
251
+ }
252
+ }
253
+ const wrongField = new WrongFieldConstructor(123);
254
+ class WithWrongField extends BaseClass {
255
+ erroredField = wrongField
256
+ }
257
+ new WithWrongField;
258
+
259
+ }).toThrow();
260
+ });
261
+
262
+ test('correct custom missing prop search creation', () => {
263
+ // @ts-ignore
264
+ expect(madeFieldInstance[Symbol.toStringTag]).toEqual(undefined);
265
+ // @ts-ignore
266
+ expect(madeFieldInstance[Symbol.iterator]).toEqual(undefined);
267
+ const util = require('util');
268
+ // @ts-ignore
269
+ expect(madeFieldInstance[util.inspect.custom]).toEqual(undefined);
270
+ // @ts-ignore
271
+ const inspected = util.inspect(madeFieldInstance);
272
+ const expected = 'MadeFieldClass { myField: [Getter/Setter] }';
273
+ expect(expected).toEqual(expected);
274
+ });
275
+
130
276
  test('wrong assignment to objects', () => {
131
277
 
132
278
  expect(() => {
@@ -249,7 +395,7 @@ describe('props tests', () => {
249
395
 
250
396
  baseInstance.missingValue > 1;
251
397
 
252
- }).toThrow(new TypeError('Attempt to Access to Undefined Prop: [ missingValue ]'));
398
+ }).toThrow(new TypeError('Attempt to Access to Undefined Prop: [ missingValue ] of Base'));
253
399
  });
254
400
 
255
401
  });
@@ -368,9 +514,10 @@ describe('deep extend works', () => {
368
514
  test('class extended three times construction', () => {
369
515
 
370
516
  expect(tiripleExtendInstance).toBeInstanceOf(Base);
371
- expect(tiripleExtendInstance).toBeInstanceOf(TripleExtend);
517
+ expect(tiripleExtendInstance).toBeInstanceOf(SecondaryExtend);
372
518
  expect(tiripleExtendInstance).toBeInstanceOf(TripleExtend);
373
519
  expect(`${tiripleExtendInstance.stringValue}`).toEqual('123');
520
+ expect(tiripleExtendInstance.second).toBeInstanceOf(Number);
374
521
 
375
522
  });
376
523
 
@@ -22,9 +22,9 @@
22
22
  "removeComments": true,
23
23
  "experimentalDecorators": true,
24
24
  "forceConsistentCasingInFileNames": true,
25
- "types": [
26
- "jest",
27
- "node"
28
- ]
29
- },
25
+ "types": [
26
+ "jest",
27
+ "node"
28
+ ]
29
+ },
30
30
  }