@quatrain/core 1.1.24 → 1.1.29
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/README.md +56 -8
- package/{lib → dist}/Core.d.ts +3 -17
- package/dist/Core.d.ts.map +1 -0
- package/dist/Core.js +105 -0
- package/dist/Core.js.map +1 -0
- package/dist/common/DataGenerator.d.ts +3 -0
- package/dist/common/DataGenerator.d.ts.map +1 -0
- package/dist/common/DataGenerator.js +86 -0
- package/dist/common/DataGenerator.js.map +1 -0
- package/{lib → dist}/common/ResourcesErrors.d.ts +1 -0
- package/dist/common/ResourcesErrors.d.ts.map +1 -0
- package/{lib → dist}/common/ResourcesErrors.js +1 -0
- package/dist/common/ResourcesErrors.js.map +1 -0
- package/{lib → dist}/common/statuses.d.ts +1 -0
- package/dist/common/statuses.d.ts.map +1 -0
- package/{lib → dist}/common/statuses.js +1 -0
- package/dist/common/statuses.js.map +1 -0
- package/{lib → dist}/components/AbstractObject.d.ts +1 -5
- package/dist/components/AbstractObject.d.ts.map +1 -0
- package/{lib → dist}/components/AbstractObject.js +7 -10
- package/dist/components/AbstractObject.js.map +1 -0
- package/{lib → dist}/components/BaseObject.d.ts +4 -17
- package/dist/components/BaseObject.d.ts.map +1 -0
- package/dist/components/BaseObject.js +71 -0
- package/dist/components/BaseObject.js.map +1 -0
- package/dist/components/BaseObjectProperties.d.ts +2 -0
- package/dist/components/BaseObjectProperties.d.ts.map +1 -0
- package/{lib → dist}/components/BaseObjectProperties.js +1 -2
- package/dist/components/BaseObjectProperties.js.map +1 -0
- package/{lib → dist}/components/DataObject.d.ts +2 -51
- package/dist/components/DataObject.d.ts.map +1 -0
- package/{lib → dist}/components/DataObject.js +38 -111
- package/dist/components/DataObject.js.map +1 -0
- package/{lib → dist}/components/Entity.d.ts +1 -0
- package/dist/components/Entity.d.ts.map +1 -0
- package/dist/components/Entity.js +48 -0
- package/dist/components/Entity.js.map +1 -0
- package/{lib → dist}/components/ObjectUri.d.ts +1 -26
- package/dist/components/ObjectUri.d.ts.map +1 -0
- package/{lib → dist}/components/ObjectUri.js +13 -41
- package/dist/components/ObjectUri.js.map +1 -0
- package/{lib → dist}/components/User.d.ts +1 -0
- package/dist/components/User.d.ts.map +1 -0
- package/{lib → dist}/components/User.js +7 -26
- package/dist/components/User.js.map +1 -0
- package/dist/components/__test__/fixtures/dao.d.ts +37 -0
- package/dist/components/__test__/fixtures/dao.d.ts.map +1 -0
- package/dist/components/__test__/fixtures/dao.js +100 -0
- package/dist/components/__test__/fixtures/dao.js.map +1 -0
- package/{lib → dist}/components/index.d.ts +5 -2
- package/dist/components/index.d.ts.map +1 -0
- package/{lib → dist}/components/index.js +1 -0
- package/dist/components/index.js.map +1 -0
- package/{lib → dist}/components/types/AbstractObjectClass.d.ts +1 -0
- package/dist/components/types/AbstractObjectClass.d.ts.map +1 -0
- package/dist/components/types/AbstractObjectClass.js +3 -0
- package/dist/components/types/AbstractObjectClass.js.map +1 -0
- package/{lib → dist}/components/types/BaseObjectClass.d.ts +1 -0
- package/dist/components/types/BaseObjectClass.d.ts.map +1 -0
- package/{lib → dist}/components/types/BaseObjectClass.js +1 -0
- package/dist/components/types/BaseObjectClass.js.map +1 -0
- package/{lib → dist}/components/types/BaseObjectType.d.ts +2 -1
- package/dist/components/types/BaseObjectType.d.ts.map +1 -0
- package/{lib → dist}/components/types/BaseObjectType.js +1 -0
- package/dist/components/types/BaseObjectType.js.map +1 -0
- package/{lib → dist}/components/types/DataObjectClass.d.ts +1 -0
- package/dist/components/types/DataObjectClass.d.ts.map +1 -0
- package/{lib → dist}/components/types/DataObjectClass.js +1 -0
- package/dist/components/types/DataObjectClass.js.map +1 -0
- package/dist/components/types/DataObjectParams.d.ts +8 -0
- package/dist/components/types/DataObjectParams.d.ts.map +1 -0
- package/dist/components/types/DataObjectParams.js +3 -0
- package/dist/components/types/DataObjectParams.js.map +1 -0
- package/{lib → dist}/components/types/DataObjectType.d.ts +1 -0
- package/dist/components/types/DataObjectType.d.ts.map +1 -0
- package/{lib → dist}/components/types/DataObjectType.js +1 -0
- package/dist/components/types/DataObjectType.js.map +1 -0
- package/{lib → dist}/components/types/EntityClass.d.ts +1 -0
- package/dist/components/types/EntityClass.d.ts.map +1 -0
- package/{lib → dist}/components/types/EntityClass.js +1 -0
- package/dist/components/types/EntityClass.js.map +1 -0
- package/{lib → dist}/components/types/Payload.d.ts +1 -0
- package/dist/components/types/Payload.d.ts.map +1 -0
- package/{lib → dist}/components/types/Payload.js +1 -0
- package/dist/components/types/Payload.js.map +1 -0
- package/{lib → dist}/components/types/Persisted.d.ts +1 -0
- package/dist/components/types/Persisted.d.ts.map +1 -0
- package/{lib → dist}/components/types/Persisted.js +1 -0
- package/dist/components/types/Persisted.js.map +1 -0
- package/dist/components/types/Properties.d.ts +2 -0
- package/dist/components/types/Properties.d.ts.map +1 -0
- package/{lib/components/types/AbstractObjectClass.js → dist/components/types/Properties.js} +1 -0
- package/dist/components/types/Properties.js.map +1 -0
- package/{lib → dist}/components/types/ProxyConstructor.d.ts +1 -0
- package/dist/components/types/ProxyConstructor.d.ts.map +1 -0
- package/{lib → dist}/components/types/ProxyConstructor.js +1 -0
- package/dist/components/types/ProxyConstructor.js.map +1 -0
- package/{lib → dist}/components/types/toJSONParams.d.ts +1 -0
- package/dist/components/types/toJSONParams.d.ts.map +1 -0
- package/{lib → dist}/components/types/toJSONParams.js +1 -0
- package/dist/components/types/toJSONParams.js.map +1 -0
- package/{lib → dist}/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -0
- package/{lib → dist}/index.js +4 -1
- package/dist/index.js.map +1 -0
- package/{lib → dist}/properties/ArrayProperty.d.ts +1 -0
- package/dist/properties/ArrayProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/ArrayProperty.js +5 -4
- package/dist/properties/ArrayProperty.js.map +1 -0
- package/{lib → dist}/properties/BaseProperty.d.ts +3 -2
- package/dist/properties/BaseProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/BaseProperty.js +18 -11
- package/dist/properties/BaseProperty.js.map +1 -0
- package/{lib → dist}/properties/BooleanProperty.d.ts +1 -0
- package/dist/properties/BooleanProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/BooleanProperty.js +2 -1
- package/dist/properties/BooleanProperty.js.map +1 -0
- package/{lib → dist}/properties/CollectionProperty.d.ts +1 -0
- package/dist/properties/CollectionProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/CollectionProperty.js +6 -4
- package/dist/properties/CollectionProperty.js.map +1 -0
- package/{lib → dist}/properties/DateTimeProperty.d.ts +1 -0
- package/dist/properties/DateTimeProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/DateTimeProperty.js +12 -5
- package/dist/properties/DateTimeProperty.js.map +1 -0
- package/{lib → dist}/properties/EnumProperty.d.ts +1 -0
- package/dist/properties/EnumProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/EnumProperty.js +4 -3
- package/dist/properties/EnumProperty.js.map +1 -0
- package/{lib → dist}/properties/FileProperty.d.ts +1 -0
- package/dist/properties/FileProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/FileProperty.js +3 -2
- package/dist/properties/FileProperty.js.map +1 -0
- package/{lib → dist}/properties/HashProperty.d.ts +2 -7
- package/dist/properties/HashProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/HashProperty.js +15 -23
- package/dist/properties/HashProperty.js.map +1 -0
- package/{lib → dist}/properties/MapProperty.d.ts +1 -0
- package/dist/properties/MapProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/MapProperty.js +2 -1
- package/dist/properties/MapProperty.js.map +1 -0
- package/{lib → dist}/properties/NumberProperty.d.ts +1 -0
- package/dist/properties/NumberProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/NumberProperty.js +15 -14
- package/dist/properties/NumberProperty.js.map +1 -0
- package/{lib → dist}/properties/ObjectProperty.d.ts +1 -0
- package/dist/properties/ObjectProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/ObjectProperty.js +4 -3
- package/dist/properties/ObjectProperty.js.map +1 -0
- package/{lib → dist}/properties/Property.d.ts +1 -0
- package/dist/properties/Property.d.ts.map +1 -0
- package/{lib → dist}/properties/Property.js +12 -11
- package/dist/properties/Property.js.map +1 -0
- package/{lib → dist}/properties/StringProperty.d.ts +3 -5
- package/dist/properties/StringProperty.d.ts.map +1 -0
- package/{lib → dist}/properties/StringProperty.js +20 -28
- package/dist/properties/StringProperty.js.map +1 -0
- package/{lib → dist}/properties/index.d.ts +4 -2
- package/dist/properties/index.d.ts.map +1 -0
- package/{lib → dist}/properties/index.js +1 -0
- package/dist/properties/index.js.map +1 -0
- package/{lib → dist}/properties/types/AbstractPropertyType.d.ts +1 -0
- package/dist/properties/types/AbstractPropertyType.d.ts.map +1 -0
- package/{lib → dist}/properties/types/AbstractPropertyType.js +1 -0
- package/dist/properties/types/AbstractPropertyType.js.map +1 -0
- package/{lib → dist}/properties/types/PropertyClassType.d.ts +1 -0
- package/dist/properties/types/PropertyClassType.d.ts.map +1 -0
- package/{lib → dist}/properties/types/PropertyClassType.js +1 -0
- package/dist/properties/types/PropertyClassType.js.map +1 -0
- package/{lib → dist}/properties/types/PropertyHTMLType.d.ts +1 -0
- package/dist/properties/types/PropertyHTMLType.d.ts.map +1 -0
- package/{lib → dist}/properties/types/PropertyHTMLType.js +1 -2
- package/dist/properties/types/PropertyHTMLType.js.map +1 -0
- package/{lib → dist}/properties/types/PropertyTypes.d.ts +1 -0
- package/dist/properties/types/PropertyTypes.d.ts.map +1 -0
- package/{lib → dist}/properties/types/PropertyTypes.js +1 -0
- package/dist/properties/types/PropertyTypes.js.map +1 -0
- package/package.json +47 -46
- package/src/Core.ts +145 -0
- package/src/common/DataGenerator.ts +81 -0
- package/src/common/ResourcesErrors.ts +17 -0
- package/src/common/statuses.ts +20 -0
- package/src/components/AbstractObject.ts +71 -0
- package/src/components/BaseObject.ts +123 -0
- package/src/components/BaseObjectProperties.ts +65 -0
- package/src/components/DataObject.ts +418 -0
- package/src/components/Entity.ts +38 -0
- package/src/components/ObjectUri.test.ts +74 -0
- package/src/components/ObjectUri.ts +174 -0
- package/src/components/User.ts +111 -0
- package/src/components/index.ts +19 -0
- package/src/components/types/AbstractObjectClass.ts +7 -0
- package/src/components/types/BaseObjectClass.ts +8 -0
- package/src/components/types/BaseObjectType.ts +10 -0
- package/src/components/types/DataObjectClass.ts +27 -0
- package/src/components/types/DataObjectParams.ts +8 -0
- package/src/components/types/DataObjectType.ts +27 -0
- package/src/components/types/EntityClass.ts +3 -0
- package/src/components/types/Payload.ts +11 -0
- package/src/components/types/Persisted.ts +7 -0
- package/src/components/types/Properties.ts +1 -0
- package/src/components/types/ProxyConstructor.ts +13 -0
- package/src/components/types/toJSONParams.ts +12 -0
- package/src/index.ts +97 -0
- package/src/properties/ArrayProperty.test.ts +46 -0
- package/src/properties/ArrayProperty.ts +87 -0
- package/src/properties/BaseProperty.test.ts +79 -0
- package/src/properties/BaseProperty.ts +140 -0
- package/src/properties/BooleanProperty.ts +11 -0
- package/src/properties/CollectionProperty.test.ts +22 -0
- package/src/properties/CollectionProperty.ts +43 -0
- package/src/properties/DateTimeProperty.ts +47 -0
- package/src/properties/EnumProperty.ts +36 -0
- package/src/properties/FileProperty.ts +14 -0
- package/src/properties/HashProperty.test.ts +108 -0
- package/src/properties/HashProperty.ts +70 -0
- package/src/properties/MapProperty.ts +19 -0
- package/src/properties/NumberProperty.test.ts +38 -0
- package/src/properties/NumberProperty.ts +110 -0
- package/src/properties/ObjectProperty.ts +118 -0
- package/src/properties/Property.ts +85 -0
- package/src/properties/StringProperty.test.ts +46 -0
- package/src/properties/StringProperty.ts +121 -0
- package/src/properties/index.ts +62 -0
- package/src/properties/types/AbstractPropertyType.ts +8 -0
- package/src/properties/types/PropertyClassType.ts +11 -0
- package/src/properties/types/PropertyHTMLType.ts +23 -0
- package/src/properties/types/PropertyTypes.ts +13 -0
- package/lib/Core.js +0 -133
- package/lib/components/BaseObject.js +0 -103
- package/lib/components/BaseObjectProperties.d.ts +0 -1
- package/lib/components/Entity.js +0 -71
- package/lib/components/types/Properties.d.ts +0 -3
- package/lib/components/types/Properties.js +0 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { BaseProperty, BasePropertyType } from './BaseProperty'
|
|
2
|
+
import { StringProperty } from './StringProperty'
|
|
3
|
+
|
|
4
|
+
export interface ArrayPropertyType extends BasePropertyType {
|
|
5
|
+
minLength?: number
|
|
6
|
+
maxLength?: number
|
|
7
|
+
allowNumbers?: boolean
|
|
8
|
+
allowStrings?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class ArrayProperty extends BaseProperty {
|
|
12
|
+
static TYPE = 'array'
|
|
13
|
+
protected _value: Array<any> | undefined = undefined
|
|
14
|
+
|
|
15
|
+
protected _minLength: number = 0
|
|
16
|
+
protected _maxLength: number = 0
|
|
17
|
+
|
|
18
|
+
constructor(config: ArrayPropertyType) {
|
|
19
|
+
super(config)
|
|
20
|
+
this.minLength = config.minLength || 0
|
|
21
|
+
this.maxLength = config.maxLength || 0
|
|
22
|
+
if (this._enable(config.allowNumbers)) {
|
|
23
|
+
this._allows.push(StringProperty.ALLOW_NUMBERS)
|
|
24
|
+
}
|
|
25
|
+
if (this._enable(config.allowStrings)) {
|
|
26
|
+
this._allows.push(StringProperty.ALLOW_STRINGS)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
set minLength(min: number) {
|
|
31
|
+
this._minLength = min >= 0 ? min : 0
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get minLength() {
|
|
35
|
+
return this._minLength
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
set maxLength(max: number) {
|
|
39
|
+
this._maxLength = max >= 0 ? max : 0
|
|
40
|
+
}
|
|
41
|
+
get maxLength() {
|
|
42
|
+
return this._maxLength
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
set(value: Array<any>, setChanged = true) {
|
|
46
|
+
if (value === null) {
|
|
47
|
+
value = []
|
|
48
|
+
}
|
|
49
|
+
if (!Array.isArray(value)) {
|
|
50
|
+
throw new Error(`value ${JSON.stringify(value)} is not an array`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (this._minLength > 0 && value.length < this._minLength) {
|
|
54
|
+
throw new Error(`Array has too few values`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (this._maxLength > 0 && value.length > this._maxLength) {
|
|
58
|
+
throw new Error(`Array values are too many`)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const joined = value.join()
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
this._allows.includes(StringProperty.ALLOW_STRINGS) === false &&
|
|
65
|
+
/[a-zA-Z]/.test(joined)
|
|
66
|
+
) {
|
|
67
|
+
throw new Error(`Strings are not allowed in value`)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (
|
|
71
|
+
this._allows.includes(StringProperty.ALLOW_NUMBERS) === false &&
|
|
72
|
+
/\d/.test(joined)
|
|
73
|
+
) {
|
|
74
|
+
throw new Error(`Numbers are not allowed in value`)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return super.set(value, setChanged)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get(transform: Function | undefined = undefined) {
|
|
81
|
+
return transform ? transform(this._value) : this._value
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
toJSON() {
|
|
85
|
+
return this._value
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { BaseProperty } from './BaseProperty'
|
|
2
|
+
|
|
3
|
+
describe('BaseProperty', () => {
|
|
4
|
+
it('should initialize with name and default values', () => {
|
|
5
|
+
const prop = new BaseProperty({ name: 'TestProp' })
|
|
6
|
+
expect(prop.name).toBe('TestProp')
|
|
7
|
+
expect(prop.val()).toBeUndefined()
|
|
8
|
+
expect(prop.hasChanged).toBe(false)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('should handle functional default values', () => {
|
|
12
|
+
const prop = new BaseProperty({
|
|
13
|
+
name: 'test',
|
|
14
|
+
defaultValue: () => 'dynamic-default',
|
|
15
|
+
})
|
|
16
|
+
expect(prop.val()).toBe('dynamic-default')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('should set and get values correctly', () => {
|
|
20
|
+
const prop = new BaseProperty({ name: 'test' })
|
|
21
|
+
prop.set('new-value')
|
|
22
|
+
expect(prop.val()).toBe('new-value')
|
|
23
|
+
expect(prop.hasChanged).toBe(true)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('should respect the protected attribute (write-once logic)', () => {
|
|
27
|
+
const prop = new BaseProperty({
|
|
28
|
+
name: 'test',
|
|
29
|
+
protected: true,
|
|
30
|
+
defaultValue: 'initial',
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
// First change is allowed if it moves away from default
|
|
34
|
+
prop.set('first-change')
|
|
35
|
+
expect(prop.val()).toBe('first-change')
|
|
36
|
+
|
|
37
|
+
// Subsequent changes should throw
|
|
38
|
+
expect(() => prop.set('second-change')).toThrow(/protected from change/)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should trigger onChange event when value changes', () => {
|
|
42
|
+
const onChange = jest.fn()
|
|
43
|
+
const prop = new BaseProperty({ name: 'test', onChange })
|
|
44
|
+
|
|
45
|
+
prop.set('updated')
|
|
46
|
+
expect(onChange).toHaveBeenCalled()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should transform values using the val() method', () => {
|
|
50
|
+
const prop = new BaseProperty({ name: 'test' })
|
|
51
|
+
prop.set(10)
|
|
52
|
+
const doubled = prop.val((v: number) => v * 2)
|
|
53
|
+
expect(doubled).toBe(20)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should clone the property correctly', () => {
|
|
57
|
+
const prop = new BaseProperty({ name: 'test' })
|
|
58
|
+
prop.set('original')
|
|
59
|
+
const cloned = prop.clone()
|
|
60
|
+
|
|
61
|
+
expect(cloned.name).toBe(prop.name)
|
|
62
|
+
expect(cloned.val()).toBe(prop.val())
|
|
63
|
+
expect(cloned).not.toBe(prop)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should return falsy values (0, false, "") correctly from val()', () => {
|
|
67
|
+
const propNum = new BaseProperty({ name: 'num', defaultValue: 10 })
|
|
68
|
+
propNum.set(0)
|
|
69
|
+
expect(propNum.val()).toBe(0)
|
|
70
|
+
|
|
71
|
+
const propBool = new BaseProperty({ name: 'bool', defaultValue: true })
|
|
72
|
+
propBool.set(false)
|
|
73
|
+
expect(propBool.val()).toBe(false)
|
|
74
|
+
|
|
75
|
+
const propStr = new BaseProperty({ name: 'str', defaultValue: 'default' })
|
|
76
|
+
propStr.set('')
|
|
77
|
+
expect(propStr.val()).toBe('')
|
|
78
|
+
})
|
|
79
|
+
})
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { DataObjectClass } from '../components/types/DataObjectClass'
|
|
2
|
+
import { AbstractPropertyType } from './types/AbstractPropertyType'
|
|
3
|
+
import { PropertyClassType } from './types/PropertyClassType'
|
|
4
|
+
import { PropertyHTMLType } from './types/PropertyHTMLType'
|
|
5
|
+
|
|
6
|
+
export type EventTypes =
|
|
7
|
+
| typeof BaseProperty.EVENT_ONCHANGE
|
|
8
|
+
| typeof BaseProperty.EVENT_ONDELETE
|
|
9
|
+
|
|
10
|
+
export interface BasePropertyType extends AbstractPropertyType {
|
|
11
|
+
parent?: DataObjectClass<any>
|
|
12
|
+
protected?: boolean
|
|
13
|
+
mandatory?: boolean
|
|
14
|
+
defaultValue?: any
|
|
15
|
+
htmlType?: PropertyHTMLType
|
|
16
|
+
onChange?: (dao: DataObjectClass<any>) => DataObjectClass<any>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class BaseProperty implements PropertyClassType {
|
|
20
|
+
static TYPE = 'any'
|
|
21
|
+
static EVENT_ONCHANGE = 'onChange'
|
|
22
|
+
static EVENT_ONDELETE = 'onDelete'
|
|
23
|
+
|
|
24
|
+
protected _parent: DataObjectClass<any> | undefined
|
|
25
|
+
protected _id: string
|
|
26
|
+
protected _name: string
|
|
27
|
+
protected _value: any = undefined
|
|
28
|
+
protected _mandatory: boolean = false
|
|
29
|
+
protected _protected: boolean = false
|
|
30
|
+
protected _allows: string[] = []
|
|
31
|
+
protected _htmlType: PropertyHTMLType = 'off'
|
|
32
|
+
protected _defaultValue: any
|
|
33
|
+
protected _hasChanged: boolean
|
|
34
|
+
protected _events: {
|
|
35
|
+
[key: EventTypes]: Function
|
|
36
|
+
} = {}
|
|
37
|
+
|
|
38
|
+
constructor(config: BasePropertyType) {
|
|
39
|
+
this._parent = config.parent
|
|
40
|
+
this._id = config.id || config.name.toLowerCase()
|
|
41
|
+
this._name = config.name
|
|
42
|
+
this._protected = config.protected || false
|
|
43
|
+
this._mandatory = config.mandatory || false
|
|
44
|
+
this._defaultValue =
|
|
45
|
+
typeof config.defaultValue === 'function'
|
|
46
|
+
? config.defaultValue()
|
|
47
|
+
: config.defaultValue
|
|
48
|
+
this._value = this._defaultValue
|
|
49
|
+
this._hasChanged = false
|
|
50
|
+
this._htmlType = config.htmlType || 'off'
|
|
51
|
+
if (typeof config.onChange === 'function') {
|
|
52
|
+
this._events.onChange = config.onChange
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get name() {
|
|
57
|
+
return this._name
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get mandatory() {
|
|
61
|
+
return this._mandatory
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get protected() {
|
|
65
|
+
return this._protected
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get allows() {
|
|
69
|
+
return this._allows
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
set htmlType(type: PropertyHTMLType) {
|
|
73
|
+
this._htmlType = type
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get htmlType() {
|
|
77
|
+
return this._htmlType
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get hasChanged() {
|
|
81
|
+
return this._hasChanged
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
set hasChanged(val: boolean) {
|
|
85
|
+
this._hasChanged = val
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
set(value: any, setChanged: boolean = true) {
|
|
89
|
+
if (
|
|
90
|
+
this._value !== undefined &&
|
|
91
|
+
this._value !== null &&
|
|
92
|
+
this._value !== this._defaultValue &&
|
|
93
|
+
this._protected === true
|
|
94
|
+
) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Value '${this._name}' already defined as '${this._value}' and protected from change`
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (
|
|
101
|
+
JSON.stringify({ value: value }) !==
|
|
102
|
+
JSON.stringify({ value: this._value })
|
|
103
|
+
) {
|
|
104
|
+
this._value = value
|
|
105
|
+
this._hasChanged = setChanged
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (this._events[BaseProperty.EVENT_ONCHANGE]) {
|
|
109
|
+
this._events[BaseProperty.EVENT_ONCHANGE](this._parent)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return this
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
val(transform: any = undefined): any {
|
|
116
|
+
// console.log(transform && transform(this._value), typeof transform);
|
|
117
|
+
return typeof transform === 'function'
|
|
118
|
+
? transform(this._value)
|
|
119
|
+
: this._value !== undefined
|
|
120
|
+
? this._value
|
|
121
|
+
: this._defaultValue
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
protected _enable(value: boolean | undefined) {
|
|
125
|
+
return value === undefined || value === true
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
toJSON() {
|
|
129
|
+
return this._value
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
clone() {
|
|
133
|
+
const cloned = Object.create(
|
|
134
|
+
Object.getPrototypeOf(this),
|
|
135
|
+
Object.getOwnPropertyDescriptors(this)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return cloned
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseProperty, BasePropertyType } from './BaseProperty'
|
|
2
|
+
|
|
3
|
+
export interface BooleanPropertyType extends BasePropertyType {}
|
|
4
|
+
|
|
5
|
+
export class BooleanProperty extends BaseProperty {
|
|
6
|
+
static TYPE = 'boolean'
|
|
7
|
+
|
|
8
|
+
set(value: boolean, setChanged = true) {
|
|
9
|
+
return super.set(value, setChanged)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Entity } from '../components/Entity'
|
|
2
|
+
import { User } from '../components/User'
|
|
3
|
+
import { DataGenerator } from '../common/DataGenerator'
|
|
4
|
+
import * as statuses from '../common/statuses'
|
|
5
|
+
|
|
6
|
+
describe('Collection Property', () => {
|
|
7
|
+
test('can retrieve user records matching relation', async () => {
|
|
8
|
+
// const entity = await Entity.factory()
|
|
9
|
+
// const user = await User.factory()
|
|
10
|
+
|
|
11
|
+
// // Generate 3 users not associated to any entity
|
|
12
|
+
// await DataGenerator(user, 3, { status: statuses.CREATED })
|
|
13
|
+
|
|
14
|
+
// // Generate 3 users associated with entity 1
|
|
15
|
+
// await DataGenerator(user, 3, { status: statuses.CREATED, entity })
|
|
16
|
+
|
|
17
|
+
// const value = await entity.val('users')
|
|
18
|
+
// expect(value).toBeInstanceOf(Array)
|
|
19
|
+
// expect(value.length).toBe(3)
|
|
20
|
+
//
|
|
21
|
+
})
|
|
22
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { BaseProperty, BasePropertyType } from './BaseProperty'
|
|
2
|
+
import { ObjectUri } from '../components/ObjectUri'
|
|
3
|
+
import { Core } from '../Core'
|
|
4
|
+
import { DataObjectClass } from '../components/types/DataObjectClass'
|
|
5
|
+
import { BaseObject } from '../components/BaseObject'
|
|
6
|
+
|
|
7
|
+
export interface CollectionPropertyType extends BasePropertyType {
|
|
8
|
+
instanceOf: typeof BaseObject
|
|
9
|
+
backend?: any
|
|
10
|
+
parentKey?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class CollectionProperty extends BaseProperty {
|
|
14
|
+
static TYPE = 'collection'
|
|
15
|
+
protected _value:
|
|
16
|
+
| Array<any>
|
|
17
|
+
| Array<DataObjectClass<any>>
|
|
18
|
+
| Array<ObjectUri>
|
|
19
|
+
| undefined = undefined
|
|
20
|
+
protected _instanceOf: typeof BaseObject
|
|
21
|
+
protected _parentKey: string
|
|
22
|
+
|
|
23
|
+
constructor(config: CollectionPropertyType) {
|
|
24
|
+
super(config)
|
|
25
|
+
if (!config.instanceOf) {
|
|
26
|
+
throw new Error(`Parameter 'instanceOf' is mandatory`)
|
|
27
|
+
}
|
|
28
|
+
this._instanceOf =
|
|
29
|
+
typeof config.instanceOf === 'string'
|
|
30
|
+
? Core.classRegistry[config.instanceOf]
|
|
31
|
+
: config.instanceOf
|
|
32
|
+
this._parentKey =
|
|
33
|
+
config.parentKey || this._parent?.uri?.collection || 'unknown'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
set(value: Array<any>, setChanged = true) {
|
|
37
|
+
return super.set(value, setChanged)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
toJSON() {
|
|
41
|
+
return this._value
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BaseProperty, BasePropertyType } from './BaseProperty'
|
|
2
|
+
|
|
3
|
+
export interface DateTimePropertyType extends BasePropertyType {
|
|
4
|
+
timezone?: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class DateTimeProperty extends BaseProperty {
|
|
8
|
+
static AS_IS = 'asis'
|
|
9
|
+
static UNIX_TIMESTAMP = 'unix_timestamp'
|
|
10
|
+
|
|
11
|
+
static TYPE = 'datetime'
|
|
12
|
+
|
|
13
|
+
static RETURN_AS: string = DateTimeProperty.AS_IS
|
|
14
|
+
|
|
15
|
+
protected _timezone: string
|
|
16
|
+
|
|
17
|
+
constructor(config: DateTimePropertyType) {
|
|
18
|
+
super(config)
|
|
19
|
+
this._timezone = config.timezone || 'Z'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set(value: string | Date | number, setChanged = true) {
|
|
23
|
+
if (value && DateTimeProperty.RETURN_AS === 'unix_timestamp') {
|
|
24
|
+
if (typeof value === 'string') {
|
|
25
|
+
// Parse date strings as UTC
|
|
26
|
+
// If the string already has timezone info (ends with Z or +/-offset), use as-is
|
|
27
|
+
// Otherwise, treat as UTC by appending 'Z'
|
|
28
|
+
let utcString = value.trim()
|
|
29
|
+
if (
|
|
30
|
+
!utcString.endsWith('Z') &&
|
|
31
|
+
!/[+-]\d{2}:\d{2}$/.test(utcString)
|
|
32
|
+
) {
|
|
33
|
+
// Replace space with T for ISO format and append Z for UTC
|
|
34
|
+
utcString = utcString.replace(' ', 'T') + 'Z'
|
|
35
|
+
}
|
|
36
|
+
value = Date.parse(utcString)
|
|
37
|
+
} else if (typeof value === 'object') {
|
|
38
|
+
value = (value as Date).getTime()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return super.set(value, setChanged)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get timezone() {
|
|
45
|
+
return this._timezone
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { BaseProperty, BasePropertyType } from './BaseProperty'
|
|
2
|
+
|
|
3
|
+
export interface EnumPropertyType extends BasePropertyType {
|
|
4
|
+
values?: string[]
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class EnumProperty extends BaseProperty {
|
|
8
|
+
static WILDCARD = '*'
|
|
9
|
+
static TYPE = 'enum'
|
|
10
|
+
protected _values: string[] = []
|
|
11
|
+
|
|
12
|
+
constructor(config: EnumPropertyType) {
|
|
13
|
+
super(config)
|
|
14
|
+
this._values = config.values || []
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
set(value: string, setChanged = true) {
|
|
18
|
+
if (
|
|
19
|
+
value !== null &&
|
|
20
|
+
(!this._values.includes(EnumProperty.WILDCARD) &&
|
|
21
|
+
!this._values.includes(value))
|
|
22
|
+
) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Value '${value}' is not acceptable, acceptable values are: ${this._values.join(
|
|
25
|
+
', '
|
|
26
|
+
)}`
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return super.set(value, setChanged)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get values() {
|
|
34
|
+
return this._values
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ObjectUri } from '../components/ObjectUri'
|
|
2
|
+
import { BaseProperty, BasePropertyType } from './BaseProperty'
|
|
3
|
+
import { BaseObjectClass } from '../components/types/BaseObjectClass'
|
|
4
|
+
|
|
5
|
+
export interface FilePropertyType extends BasePropertyType {}
|
|
6
|
+
|
|
7
|
+
export class FileProperty extends BaseProperty {
|
|
8
|
+
static TYPE = 'file'
|
|
9
|
+
_value: BaseObjectClass | ObjectUri | undefined = undefined
|
|
10
|
+
|
|
11
|
+
constructor(config: FilePropertyType) {
|
|
12
|
+
super(config)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { HashProperty } from './HashProperty'
|
|
2
|
+
|
|
3
|
+
describe('Hash Property supports MD5 algorithm', () => {
|
|
4
|
+
test('value is hashed with MD5', () => {
|
|
5
|
+
const prop = new HashProperty({
|
|
6
|
+
name: 'hash',
|
|
7
|
+
algorithm: HashProperty.ALGORITHM_MD5,
|
|
8
|
+
})
|
|
9
|
+
expect(prop.set('azertyuiop').get()).toBe(
|
|
10
|
+
'7682fe272099ea26efe39c890b33675b'
|
|
11
|
+
)
|
|
12
|
+
})
|
|
13
|
+
test('prefix can be added', () => {
|
|
14
|
+
const prop = new HashProperty({
|
|
15
|
+
name: 'hash',
|
|
16
|
+
algorithm: HashProperty.ALGORITHM_MD5,
|
|
17
|
+
prefixed: true,
|
|
18
|
+
})
|
|
19
|
+
expect(prop.set('azertyuiop').get()).toBe(
|
|
20
|
+
'md5-7682fe272099ea26efe39c890b33675b'
|
|
21
|
+
)
|
|
22
|
+
})
|
|
23
|
+
test('salt can be added', () => {
|
|
24
|
+
const prop = new HashProperty({
|
|
25
|
+
name: 'hash',
|
|
26
|
+
algorithm: HashProperty.ALGORITHM_MD5,
|
|
27
|
+
salt: '123456',
|
|
28
|
+
})
|
|
29
|
+
expect(prop.set('azertyuiop').get()).toBe(
|
|
30
|
+
'5560d200374e249898dcb68d5fa14117'
|
|
31
|
+
)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('Hash Property supports SHA1 algorithm', () => {
|
|
36
|
+
test('value is hashed with SHA1 and optional salt', () => {
|
|
37
|
+
const prop = new HashProperty({
|
|
38
|
+
name: 'hash',
|
|
39
|
+
algorithm: HashProperty.ALGORITHM_SHA1,
|
|
40
|
+
})
|
|
41
|
+
expect(prop.set('azertyuiop').get()).toBe(
|
|
42
|
+
'58ad983135fe15c5a8e2e15fb5b501aedcf70dc2'
|
|
43
|
+
)
|
|
44
|
+
})
|
|
45
|
+
test('prefix can be added', () => {
|
|
46
|
+
const prop = new HashProperty({
|
|
47
|
+
name: 'hash',
|
|
48
|
+
algorithm: HashProperty.ALGORITHM_SHA1,
|
|
49
|
+
prefixed: true,
|
|
50
|
+
})
|
|
51
|
+
expect(prop.set('azertyuiop').get()).toBe(
|
|
52
|
+
'sha1-58ad983135fe15c5a8e2e15fb5b501aedcf70dc2'
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
test('salt can be added', () => {
|
|
56
|
+
const prop = new HashProperty({
|
|
57
|
+
name: 'hash',
|
|
58
|
+
algorithm: HashProperty.ALGORITHM_SHA1,
|
|
59
|
+
salt: '123456',
|
|
60
|
+
})
|
|
61
|
+
expect(prop.set('azertyuiop').get()).toBe(
|
|
62
|
+
'19ed93bb52146515d89fb3c223277b878fc0cd6c'
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe('Hash Property supports SHA256 algorithm', () => {
|
|
68
|
+
test('value is hashed with SHA256 and optional salt', () => {
|
|
69
|
+
const prop = new HashProperty({
|
|
70
|
+
name: 'hash',
|
|
71
|
+
algorithm: HashProperty.ALGORITHM_SHA256,
|
|
72
|
+
})
|
|
73
|
+
expect(prop.set('azertyuiop').get()).toBe(
|
|
74
|
+
'aa3d2fe4f6d301dbd6b8fb2d2fddfb7aeebf3bec53ffff4b39a0967afa88c609'
|
|
75
|
+
)
|
|
76
|
+
})
|
|
77
|
+
test('prefix can be added', () => {
|
|
78
|
+
const prop = new HashProperty({
|
|
79
|
+
name: 'hash',
|
|
80
|
+
algorithm: HashProperty.ALGORITHM_SHA256,
|
|
81
|
+
prefixed: true,
|
|
82
|
+
})
|
|
83
|
+
expect(prop.set('azertyuiop').get()).toBe(
|
|
84
|
+
'sha256-aa3d2fe4f6d301dbd6b8fb2d2fddfb7aeebf3bec53ffff4b39a0967afa88c609'
|
|
85
|
+
)
|
|
86
|
+
})
|
|
87
|
+
test('salt can be added', () => {
|
|
88
|
+
const prop = new HashProperty({
|
|
89
|
+
name: 'hash',
|
|
90
|
+
algorithm: HashProperty.ALGORITHM_SHA256,
|
|
91
|
+
salt: '123456',
|
|
92
|
+
})
|
|
93
|
+
expect(prop.set('azertyuiop').get()).toBe(
|
|
94
|
+
'fd0142a229c2924d50c20576ac5850d9f3165f56d7630b62ad6e66a99bf5adc4'
|
|
95
|
+
)
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe('Hash Property allows comparison of string and hash value', () => {
|
|
100
|
+
test('compare string with hash', () => {
|
|
101
|
+
const prop = new HashProperty({
|
|
102
|
+
name: 'hash',
|
|
103
|
+
algorithm: HashProperty.ALGORITHM_SHA256,
|
|
104
|
+
})
|
|
105
|
+
expect(prop.set('azertyuiop').compare('azertyuiop')).toBe(true)
|
|
106
|
+
expect(prop.set('azertyuiop').compare('qwertz')).toBe(false)
|
|
107
|
+
})
|
|
108
|
+
})
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { StringProperty, StringPropertyType } from './StringProperty'
|
|
3
|
+
|
|
4
|
+
export type HashPropertyAlgos =
|
|
5
|
+
| typeof HashProperty.ALGORITHM_MD5
|
|
6
|
+
| typeof HashProperty.ALGORITHM_SHA1
|
|
7
|
+
| typeof HashProperty.ALGORITHM_SHA256
|
|
8
|
+
| typeof HashProperty.ALGORITHM_BCRYPT
|
|
9
|
+
|
|
10
|
+
export interface HashPropertyType extends StringPropertyType {
|
|
11
|
+
algorithm?: HashPropertyAlgos
|
|
12
|
+
salt?: string
|
|
13
|
+
prefixed?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class HashProperty extends StringProperty {
|
|
17
|
+
static TYPE = 'hash'
|
|
18
|
+
static ALGORITHM_MD5 = 'md5'
|
|
19
|
+
static ALGORITHM_SHA1 = 'sha1'
|
|
20
|
+
static ALGORITHM_SHA256 = 'sha256'
|
|
21
|
+
static ALGORITHM_BCRYPT = 'bcrypt'
|
|
22
|
+
|
|
23
|
+
protected _algorithm: HashPropertyAlgos
|
|
24
|
+
protected _salt: string = ''
|
|
25
|
+
protected _prefixed = false
|
|
26
|
+
|
|
27
|
+
constructor(config: HashPropertyType) {
|
|
28
|
+
super(config)
|
|
29
|
+
this._algorithm = config.algorithm || HashProperty.ALGORITHM_MD5
|
|
30
|
+
this._salt = config.salt || ''
|
|
31
|
+
this._prefixed = config.prefixed || false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_hash(value: string): string {
|
|
35
|
+
let algo
|
|
36
|
+
switch (this._algorithm) {
|
|
37
|
+
case HashProperty.ALGORITHM_MD5:
|
|
38
|
+
algo = createHash('md5')
|
|
39
|
+
break
|
|
40
|
+
|
|
41
|
+
case HashProperty.ALGORITHM_SHA1:
|
|
42
|
+
algo = createHash('sha1')
|
|
43
|
+
break
|
|
44
|
+
|
|
45
|
+
case HashProperty.ALGORITHM_SHA256:
|
|
46
|
+
algo = createHash('sha256')
|
|
47
|
+
break
|
|
48
|
+
|
|
49
|
+
default:
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Unsupported or missing hash algorithm: ${this._algorithm}`
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let hash = this._prefixed ? `${this._algorithm}-` : ''
|
|
56
|
+
hash += algo.update(`${this._salt}${value}`).digest('hex')
|
|
57
|
+
|
|
58
|
+
this._rawValue = false // don't test some constraints after hashing
|
|
59
|
+
|
|
60
|
+
return hash
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
set(value: string, setChanged = true) {
|
|
64
|
+
return super.set(this._hash(value), setChanged)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
compare(value: string): boolean {
|
|
68
|
+
return this._hash(value) === this._value
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BaseProperty, BasePropertyType } from './BaseProperty'
|
|
2
|
+
|
|
3
|
+
export interface MapPropertyType extends BasePropertyType {}
|
|
4
|
+
|
|
5
|
+
export class MapProperty extends BaseProperty {
|
|
6
|
+
static TYPE = 'map'
|
|
7
|
+
|
|
8
|
+
set(value: any, setChanged = true) {
|
|
9
|
+
if (typeof value! !== 'object') {
|
|
10
|
+
throw new Error(`value ${JSON.stringify(value)} is not an object`)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return super.set(value, setChanged)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
toJSON() {
|
|
17
|
+
return this._value ? JSON.stringify(this._value) : {}
|
|
18
|
+
}
|
|
19
|
+
}
|