@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.
Files changed (234) hide show
  1. package/README.md +56 -8
  2. package/{lib → dist}/Core.d.ts +3 -17
  3. package/dist/Core.d.ts.map +1 -0
  4. package/dist/Core.js +105 -0
  5. package/dist/Core.js.map +1 -0
  6. package/dist/common/DataGenerator.d.ts +3 -0
  7. package/dist/common/DataGenerator.d.ts.map +1 -0
  8. package/dist/common/DataGenerator.js +86 -0
  9. package/dist/common/DataGenerator.js.map +1 -0
  10. package/{lib → dist}/common/ResourcesErrors.d.ts +1 -0
  11. package/dist/common/ResourcesErrors.d.ts.map +1 -0
  12. package/{lib → dist}/common/ResourcesErrors.js +1 -0
  13. package/dist/common/ResourcesErrors.js.map +1 -0
  14. package/{lib → dist}/common/statuses.d.ts +1 -0
  15. package/dist/common/statuses.d.ts.map +1 -0
  16. package/{lib → dist}/common/statuses.js +1 -0
  17. package/dist/common/statuses.js.map +1 -0
  18. package/{lib → dist}/components/AbstractObject.d.ts +1 -5
  19. package/dist/components/AbstractObject.d.ts.map +1 -0
  20. package/{lib → dist}/components/AbstractObject.js +7 -10
  21. package/dist/components/AbstractObject.js.map +1 -0
  22. package/{lib → dist}/components/BaseObject.d.ts +4 -17
  23. package/dist/components/BaseObject.d.ts.map +1 -0
  24. package/dist/components/BaseObject.js +71 -0
  25. package/dist/components/BaseObject.js.map +1 -0
  26. package/dist/components/BaseObjectProperties.d.ts +2 -0
  27. package/dist/components/BaseObjectProperties.d.ts.map +1 -0
  28. package/{lib → dist}/components/BaseObjectProperties.js +1 -2
  29. package/dist/components/BaseObjectProperties.js.map +1 -0
  30. package/{lib → dist}/components/DataObject.d.ts +2 -51
  31. package/dist/components/DataObject.d.ts.map +1 -0
  32. package/{lib → dist}/components/DataObject.js +38 -111
  33. package/dist/components/DataObject.js.map +1 -0
  34. package/{lib → dist}/components/Entity.d.ts +1 -0
  35. package/dist/components/Entity.d.ts.map +1 -0
  36. package/dist/components/Entity.js +48 -0
  37. package/dist/components/Entity.js.map +1 -0
  38. package/{lib → dist}/components/ObjectUri.d.ts +1 -26
  39. package/dist/components/ObjectUri.d.ts.map +1 -0
  40. package/{lib → dist}/components/ObjectUri.js +13 -41
  41. package/dist/components/ObjectUri.js.map +1 -0
  42. package/{lib → dist}/components/User.d.ts +1 -0
  43. package/dist/components/User.d.ts.map +1 -0
  44. package/{lib → dist}/components/User.js +7 -26
  45. package/dist/components/User.js.map +1 -0
  46. package/dist/components/__test__/fixtures/dao.d.ts +37 -0
  47. package/dist/components/__test__/fixtures/dao.d.ts.map +1 -0
  48. package/dist/components/__test__/fixtures/dao.js +100 -0
  49. package/dist/components/__test__/fixtures/dao.js.map +1 -0
  50. package/{lib → dist}/components/index.d.ts +5 -2
  51. package/dist/components/index.d.ts.map +1 -0
  52. package/{lib → dist}/components/index.js +1 -0
  53. package/dist/components/index.js.map +1 -0
  54. package/{lib → dist}/components/types/AbstractObjectClass.d.ts +1 -0
  55. package/dist/components/types/AbstractObjectClass.d.ts.map +1 -0
  56. package/dist/components/types/AbstractObjectClass.js +3 -0
  57. package/dist/components/types/AbstractObjectClass.js.map +1 -0
  58. package/{lib → dist}/components/types/BaseObjectClass.d.ts +1 -0
  59. package/dist/components/types/BaseObjectClass.d.ts.map +1 -0
  60. package/{lib → dist}/components/types/BaseObjectClass.js +1 -0
  61. package/dist/components/types/BaseObjectClass.js.map +1 -0
  62. package/{lib → dist}/components/types/BaseObjectType.d.ts +2 -1
  63. package/dist/components/types/BaseObjectType.d.ts.map +1 -0
  64. package/{lib → dist}/components/types/BaseObjectType.js +1 -0
  65. package/dist/components/types/BaseObjectType.js.map +1 -0
  66. package/{lib → dist}/components/types/DataObjectClass.d.ts +1 -0
  67. package/dist/components/types/DataObjectClass.d.ts.map +1 -0
  68. package/{lib → dist}/components/types/DataObjectClass.js +1 -0
  69. package/dist/components/types/DataObjectClass.js.map +1 -0
  70. package/dist/components/types/DataObjectParams.d.ts +8 -0
  71. package/dist/components/types/DataObjectParams.d.ts.map +1 -0
  72. package/dist/components/types/DataObjectParams.js +3 -0
  73. package/dist/components/types/DataObjectParams.js.map +1 -0
  74. package/{lib → dist}/components/types/DataObjectType.d.ts +1 -0
  75. package/dist/components/types/DataObjectType.d.ts.map +1 -0
  76. package/{lib → dist}/components/types/DataObjectType.js +1 -0
  77. package/dist/components/types/DataObjectType.js.map +1 -0
  78. package/{lib → dist}/components/types/EntityClass.d.ts +1 -0
  79. package/dist/components/types/EntityClass.d.ts.map +1 -0
  80. package/{lib → dist}/components/types/EntityClass.js +1 -0
  81. package/dist/components/types/EntityClass.js.map +1 -0
  82. package/{lib → dist}/components/types/Payload.d.ts +1 -0
  83. package/dist/components/types/Payload.d.ts.map +1 -0
  84. package/{lib → dist}/components/types/Payload.js +1 -0
  85. package/dist/components/types/Payload.js.map +1 -0
  86. package/{lib → dist}/components/types/Persisted.d.ts +1 -0
  87. package/dist/components/types/Persisted.d.ts.map +1 -0
  88. package/{lib → dist}/components/types/Persisted.js +1 -0
  89. package/dist/components/types/Persisted.js.map +1 -0
  90. package/dist/components/types/Properties.d.ts +2 -0
  91. package/dist/components/types/Properties.d.ts.map +1 -0
  92. package/{lib/components/types/AbstractObjectClass.js → dist/components/types/Properties.js} +1 -0
  93. package/dist/components/types/Properties.js.map +1 -0
  94. package/{lib → dist}/components/types/ProxyConstructor.d.ts +1 -0
  95. package/dist/components/types/ProxyConstructor.d.ts.map +1 -0
  96. package/{lib → dist}/components/types/ProxyConstructor.js +1 -0
  97. package/dist/components/types/ProxyConstructor.js.map +1 -0
  98. package/{lib → dist}/components/types/toJSONParams.d.ts +1 -0
  99. package/dist/components/types/toJSONParams.d.ts.map +1 -0
  100. package/{lib → dist}/components/types/toJSONParams.js +1 -0
  101. package/dist/components/types/toJSONParams.js.map +1 -0
  102. package/{lib → dist}/index.d.ts +5 -1
  103. package/dist/index.d.ts.map +1 -0
  104. package/{lib → dist}/index.js +4 -1
  105. package/dist/index.js.map +1 -0
  106. package/{lib → dist}/properties/ArrayProperty.d.ts +1 -0
  107. package/dist/properties/ArrayProperty.d.ts.map +1 -0
  108. package/{lib → dist}/properties/ArrayProperty.js +5 -4
  109. package/dist/properties/ArrayProperty.js.map +1 -0
  110. package/{lib → dist}/properties/BaseProperty.d.ts +3 -2
  111. package/dist/properties/BaseProperty.d.ts.map +1 -0
  112. package/{lib → dist}/properties/BaseProperty.js +18 -11
  113. package/dist/properties/BaseProperty.js.map +1 -0
  114. package/{lib → dist}/properties/BooleanProperty.d.ts +1 -0
  115. package/dist/properties/BooleanProperty.d.ts.map +1 -0
  116. package/{lib → dist}/properties/BooleanProperty.js +2 -1
  117. package/dist/properties/BooleanProperty.js.map +1 -0
  118. package/{lib → dist}/properties/CollectionProperty.d.ts +1 -0
  119. package/dist/properties/CollectionProperty.d.ts.map +1 -0
  120. package/{lib → dist}/properties/CollectionProperty.js +6 -4
  121. package/dist/properties/CollectionProperty.js.map +1 -0
  122. package/{lib → dist}/properties/DateTimeProperty.d.ts +1 -0
  123. package/dist/properties/DateTimeProperty.d.ts.map +1 -0
  124. package/{lib → dist}/properties/DateTimeProperty.js +12 -5
  125. package/dist/properties/DateTimeProperty.js.map +1 -0
  126. package/{lib → dist}/properties/EnumProperty.d.ts +1 -0
  127. package/dist/properties/EnumProperty.d.ts.map +1 -0
  128. package/{lib → dist}/properties/EnumProperty.js +4 -3
  129. package/dist/properties/EnumProperty.js.map +1 -0
  130. package/{lib → dist}/properties/FileProperty.d.ts +1 -0
  131. package/dist/properties/FileProperty.d.ts.map +1 -0
  132. package/{lib → dist}/properties/FileProperty.js +3 -2
  133. package/dist/properties/FileProperty.js.map +1 -0
  134. package/{lib → dist}/properties/HashProperty.d.ts +2 -7
  135. package/dist/properties/HashProperty.d.ts.map +1 -0
  136. package/{lib → dist}/properties/HashProperty.js +15 -23
  137. package/dist/properties/HashProperty.js.map +1 -0
  138. package/{lib → dist}/properties/MapProperty.d.ts +1 -0
  139. package/dist/properties/MapProperty.d.ts.map +1 -0
  140. package/{lib → dist}/properties/MapProperty.js +2 -1
  141. package/dist/properties/MapProperty.js.map +1 -0
  142. package/{lib → dist}/properties/NumberProperty.d.ts +1 -0
  143. package/dist/properties/NumberProperty.d.ts.map +1 -0
  144. package/{lib → dist}/properties/NumberProperty.js +15 -14
  145. package/dist/properties/NumberProperty.js.map +1 -0
  146. package/{lib → dist}/properties/ObjectProperty.d.ts +1 -0
  147. package/dist/properties/ObjectProperty.d.ts.map +1 -0
  148. package/{lib → dist}/properties/ObjectProperty.js +4 -3
  149. package/dist/properties/ObjectProperty.js.map +1 -0
  150. package/{lib → dist}/properties/Property.d.ts +1 -0
  151. package/dist/properties/Property.d.ts.map +1 -0
  152. package/{lib → dist}/properties/Property.js +12 -11
  153. package/dist/properties/Property.js.map +1 -0
  154. package/{lib → dist}/properties/StringProperty.d.ts +3 -5
  155. package/dist/properties/StringProperty.d.ts.map +1 -0
  156. package/{lib → dist}/properties/StringProperty.js +20 -28
  157. package/dist/properties/StringProperty.js.map +1 -0
  158. package/{lib → dist}/properties/index.d.ts +4 -2
  159. package/dist/properties/index.d.ts.map +1 -0
  160. package/{lib → dist}/properties/index.js +1 -0
  161. package/dist/properties/index.js.map +1 -0
  162. package/{lib → dist}/properties/types/AbstractPropertyType.d.ts +1 -0
  163. package/dist/properties/types/AbstractPropertyType.d.ts.map +1 -0
  164. package/{lib → dist}/properties/types/AbstractPropertyType.js +1 -0
  165. package/dist/properties/types/AbstractPropertyType.js.map +1 -0
  166. package/{lib → dist}/properties/types/PropertyClassType.d.ts +1 -0
  167. package/dist/properties/types/PropertyClassType.d.ts.map +1 -0
  168. package/{lib → dist}/properties/types/PropertyClassType.js +1 -0
  169. package/dist/properties/types/PropertyClassType.js.map +1 -0
  170. package/{lib → dist}/properties/types/PropertyHTMLType.d.ts +1 -0
  171. package/dist/properties/types/PropertyHTMLType.d.ts.map +1 -0
  172. package/{lib → dist}/properties/types/PropertyHTMLType.js +1 -2
  173. package/dist/properties/types/PropertyHTMLType.js.map +1 -0
  174. package/{lib → dist}/properties/types/PropertyTypes.d.ts +1 -0
  175. package/dist/properties/types/PropertyTypes.d.ts.map +1 -0
  176. package/{lib → dist}/properties/types/PropertyTypes.js +1 -0
  177. package/dist/properties/types/PropertyTypes.js.map +1 -0
  178. package/package.json +47 -46
  179. package/src/Core.ts +145 -0
  180. package/src/common/DataGenerator.ts +81 -0
  181. package/src/common/ResourcesErrors.ts +17 -0
  182. package/src/common/statuses.ts +20 -0
  183. package/src/components/AbstractObject.ts +71 -0
  184. package/src/components/BaseObject.ts +123 -0
  185. package/src/components/BaseObjectProperties.ts +65 -0
  186. package/src/components/DataObject.ts +418 -0
  187. package/src/components/Entity.ts +38 -0
  188. package/src/components/ObjectUri.test.ts +74 -0
  189. package/src/components/ObjectUri.ts +174 -0
  190. package/src/components/User.ts +111 -0
  191. package/src/components/index.ts +19 -0
  192. package/src/components/types/AbstractObjectClass.ts +7 -0
  193. package/src/components/types/BaseObjectClass.ts +8 -0
  194. package/src/components/types/BaseObjectType.ts +10 -0
  195. package/src/components/types/DataObjectClass.ts +27 -0
  196. package/src/components/types/DataObjectParams.ts +8 -0
  197. package/src/components/types/DataObjectType.ts +27 -0
  198. package/src/components/types/EntityClass.ts +3 -0
  199. package/src/components/types/Payload.ts +11 -0
  200. package/src/components/types/Persisted.ts +7 -0
  201. package/src/components/types/Properties.ts +1 -0
  202. package/src/components/types/ProxyConstructor.ts +13 -0
  203. package/src/components/types/toJSONParams.ts +12 -0
  204. package/src/index.ts +97 -0
  205. package/src/properties/ArrayProperty.test.ts +46 -0
  206. package/src/properties/ArrayProperty.ts +87 -0
  207. package/src/properties/BaseProperty.test.ts +79 -0
  208. package/src/properties/BaseProperty.ts +140 -0
  209. package/src/properties/BooleanProperty.ts +11 -0
  210. package/src/properties/CollectionProperty.test.ts +22 -0
  211. package/src/properties/CollectionProperty.ts +43 -0
  212. package/src/properties/DateTimeProperty.ts +47 -0
  213. package/src/properties/EnumProperty.ts +36 -0
  214. package/src/properties/FileProperty.ts +14 -0
  215. package/src/properties/HashProperty.test.ts +108 -0
  216. package/src/properties/HashProperty.ts +70 -0
  217. package/src/properties/MapProperty.ts +19 -0
  218. package/src/properties/NumberProperty.test.ts +38 -0
  219. package/src/properties/NumberProperty.ts +110 -0
  220. package/src/properties/ObjectProperty.ts +118 -0
  221. package/src/properties/Property.ts +85 -0
  222. package/src/properties/StringProperty.test.ts +46 -0
  223. package/src/properties/StringProperty.ts +121 -0
  224. package/src/properties/index.ts +62 -0
  225. package/src/properties/types/AbstractPropertyType.ts +8 -0
  226. package/src/properties/types/PropertyClassType.ts +11 -0
  227. package/src/properties/types/PropertyHTMLType.ts +23 -0
  228. package/src/properties/types/PropertyTypes.ts +13 -0
  229. package/lib/Core.js +0 -133
  230. package/lib/components/BaseObject.js +0 -103
  231. package/lib/components/BaseObjectProperties.d.ts +0 -1
  232. package/lib/components/Entity.js +0 -71
  233. package/lib/components/types/Properties.d.ts +0 -3
  234. 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
+ }