@burnsred/entity 0.6.6 → 1.0.1
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 +174 -1
- package/dist/cleaner/as-boolean.d.ts +7 -0
- package/dist/cleaner/as-boolean.js +10 -0
- package/dist/cleaner/as-boolean.js.map +1 -0
- package/dist/cleaner/as-date.d.ts +1 -0
- package/dist/cleaner/as-date.js +5 -0
- package/dist/cleaner/as-date.js.map +1 -0
- package/dist/cleaner/as-integer.d.ts +1 -0
- package/dist/cleaner/as-integer.js +5 -0
- package/dist/cleaner/as-integer.js.map +1 -0
- package/dist/cleaner/as-number.d.ts +1 -0
- package/dist/cleaner/as-number.js +5 -0
- package/dist/cleaner/as-number.js.map +1 -0
- package/dist/cleaner/as-string.d.ts +1 -0
- package/dist/cleaner/as-string.js +4 -0
- package/dist/cleaner/as-string.js.map +1 -0
- package/dist/cleaner/collapse-spaces.d.ts +4 -0
- package/dist/cleaner/collapse-spaces.js +7 -0
- package/dist/cleaner/collapse-spaces.js.map +1 -0
- package/dist/cleaner/index.d.ts +7 -0
- package/dist/cleaner/index.js +8 -0
- package/dist/cleaner/index.js.map +1 -0
- package/dist/cleaner/trim.d.ts +1 -0
- package/dist/cleaner/trim.js +4 -0
- package/dist/cleaner/trim.js.map +1 -0
- package/dist/entity/Entity.d.ts +20 -0
- package/dist/entity/Entity.js +63 -0
- package/dist/entity/Entity.js.map +1 -0
- package/dist/entity/index.d.ts +8 -0
- package/dist/entity/index.js +9 -0
- package/dist/entity/index.js.map +1 -0
- package/dist/field/BooleanField.d.ts +7 -0
- package/dist/field/BooleanField.js +8 -0
- package/dist/field/BooleanField.js.map +1 -0
- package/dist/field/CharField.d.ts +7 -0
- package/dist/field/CharField.js +8 -0
- package/dist/field/CharField.js.map +1 -0
- package/dist/field/DateField.d.ts +9 -0
- package/dist/field/DateField.js +19 -0
- package/dist/field/DateField.js.map +1 -0
- package/dist/field/DateTimeField.d.ts +9 -0
- package/dist/field/DateTimeField.js +14 -0
- package/dist/field/DateTimeField.js.map +1 -0
- package/dist/field/EntityField.d.ts +24 -0
- package/dist/field/EntityField.js +63 -0
- package/dist/field/EntityField.js.map +1 -0
- package/dist/field/Field.d.ts +42 -0
- package/dist/field/Field.js +97 -0
- package/dist/field/Field.js.map +1 -0
- package/dist/field/IdField.d.ts +15 -0
- package/dist/field/IdField.js +23 -0
- package/dist/field/IdField.js.map +1 -0
- package/dist/field/IntegerField.d.ts +6 -0
- package/dist/field/IntegerField.js +9 -0
- package/dist/field/IntegerField.js.map +1 -0
- package/dist/field/NumberField.d.ts +7 -0
- package/dist/field/NumberField.js +8 -0
- package/dist/field/NumberField.js.map +1 -0
- package/dist/field/TextField.d.ts +4 -0
- package/dist/field/TextField.js +5 -0
- package/dist/field/TextField.js.map +1 -0
- package/dist/field/index.d.ts +68 -0
- package/dist/field/index.js +12 -0
- package/dist/field/index.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/validator/entity-valid.d.ts +9 -0
- package/dist/validator/entity-valid.js +12 -0
- package/dist/validator/entity-valid.js.map +1 -0
- package/dist/validator/index.d.ts +2 -0
- package/dist/validator/index.js +3 -0
- package/dist/validator/index.js.map +1 -0
- package/dist/validator/value-range.d.ts +8 -0
- package/dist/validator/value-range.js +24 -0
- package/dist/validator/value-range.js.map +1 -0
- package/package.json +21 -37
- package/CHANGELOG.md +0 -116
- package/LICENSE +0 -21
- package/dist/development.js +0 -1377
- package/dist/development.js.map +0 -1
- package/dist/entity/src/cleaner/remove-multi-space.test.d.ts +0 -1
- package/dist/entity/src/cleaner/slice-to-max-length.test.d.ts +0 -1
- package/dist/entity/src/entity/entity.test.d.ts +0 -1
- package/dist/entity/src/entity/sample-todo-entity.d.ts +0 -19
- package/dist/entity/src/entity/sample-useQuery.d.ts +0 -1
- package/dist/entity/src/field/field-any.test.d.ts +0 -1
- package/dist/entity/src/field/field-boolean.test.d.ts +0 -1
- package/dist/entity/src/field/field-char.test.d.ts +0 -1
- package/dist/entity/src/field/field-date.test.d.ts +0 -1
- package/dist/entity/src/field/field-datetime.test.d.ts +0 -1
- package/dist/entity/src/field/field-entity.test.d.ts +0 -1
- package/dist/entity/src/field/field-enum.test.d.ts +0 -1
- package/dist/entity/src/field/field-id.test.d.ts +0 -1
- package/dist/entity/src/field/field-integer.test.d.ts +0 -1
- package/dist/entity/src/field/field-number.test.d.ts +0 -1
- package/dist/entity/src/field/field-text.test.d.ts +0 -1
- package/dist/entity/src/field/field.test.d.ts +0 -1
- package/dist/entity/src/types.d.ts +0 -223
- package/dist/entity/src/validator/allow-blank.test.d.ts +0 -1
- package/dist/entity/src/validator/is-required.test.d.ts +0 -1
- package/dist/entity/src/validator/may-not-be-blank.test.d.ts +0 -1
- package/dist/legacy.js +0 -30
- package/dist/production.min.js +0 -1
- package/dist/production.min.js.map +0 -1
package/README.md
CHANGED
|
@@ -1 +1,174 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Entity
|
|
2
|
+
|
|
3
|
+
The Entity package provides the fundamental Entity class and it supporting
|
|
4
|
+
utilities.
|
|
5
|
+
|
|
6
|
+
An Entity is used to provide cleaned, validated data records.
|
|
7
|
+
|
|
8
|
+
## Defining an Entity
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
import { Entity, CharField } from "@burnsred/entity";
|
|
12
|
+
|
|
13
|
+
type User {
|
|
14
|
+
username: string;
|
|
15
|
+
email: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const UserEntity = new Entity<User>({
|
|
20
|
+
idField: 'username',
|
|
21
|
+
fields: {
|
|
22
|
+
username: new CharField(),
|
|
23
|
+
email: new CharField(),
|
|
24
|
+
name: new CharField({ blank: true }),
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Once your entity is defined, you can use it to construct an `User` from
|
|
30
|
+
API response data:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
const rec = UserEntity.fromData(data);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Declaring Fields
|
|
37
|
+
|
|
38
|
+
Each field can specify its own list of validators and cleaners.
|
|
39
|
+
|
|
40
|
+
Cleaners are used to cast input values from the User to the right type.
|
|
41
|
+
|
|
42
|
+
Validators are used to check supplied values, and emit Errors.
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
fields: {
|
|
46
|
+
uuid: new CharField({ cleaners=[trim] }),
|
|
47
|
+
size: new IntegerField({ validators=[maxValue(10)] })
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Common field arguments.
|
|
52
|
+
|
|
53
|
+
blank (boolean) false
|
|
54
|
+
: Is this field allowed to be omitted?
|
|
55
|
+
|
|
56
|
+
cleaners (FieldCleaner[])
|
|
57
|
+
: A list of cleaners to apply when cleaning an record.
|
|
58
|
+
|
|
59
|
+
validators (FieldValidator[])
|
|
60
|
+
: A list of validators to apply when validating an record.
|
|
61
|
+
|
|
62
|
+
many (boolean) false
|
|
63
|
+
: If true, the value for this field is an array of values of its type.
|
|
64
|
+
|
|
65
|
+
choices (any) []
|
|
66
|
+
: A list of valid choices. Can help with select lists.
|
|
67
|
+
|
|
68
|
+
defaultValue
|
|
69
|
+
: Override the field's default value.
|
|
70
|
+
|
|
71
|
+
Any extra properties passed will be stored in the `extra` property.
|
|
72
|
+
|
|
73
|
+
## Cleaners
|
|
74
|
+
|
|
75
|
+
Cleaner functions are primarily used for cleaning and casting data coming from
|
|
76
|
+
the User.
|
|
77
|
+
|
|
78
|
+
Since most HTML Input fields work with string values, many `Field`s will
|
|
79
|
+
default to using a cleaner which casts to the correct type.
|
|
80
|
+
|
|
81
|
+
## Validators
|
|
82
|
+
|
|
83
|
+
Validator functions are used to check constraints, emitting FieldErrors to
|
|
84
|
+
identify problems.
|
|
85
|
+
|
|
86
|
+
They can be specified either on the Field or Entitly.
|
|
87
|
+
|
|
88
|
+
Field validators are passed the value for that specific field, and are called
|
|
89
|
+
with `this` being the Field.
|
|
90
|
+
|
|
91
|
+
Entity validators are passed the whole record, and are called with `this` being
|
|
92
|
+
the Entity.
|
|
93
|
+
|
|
94
|
+
As an example, here is an Entity validator that ensures the start date is
|
|
95
|
+
before the end date:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
function dateRange(record) {
|
|
99
|
+
const start = record.get("start_date");
|
|
100
|
+
const end = record.get("end_date");
|
|
101
|
+
|
|
102
|
+
if (start.valueOf() >= end.valueOf()) {
|
|
103
|
+
return {
|
|
104
|
+
code: "date-range",
|
|
105
|
+
message: "Start date must be before End date.",
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
# Errors
|
|
112
|
+
|
|
113
|
+
Managing and handling validation errors requires records to contain them.
|
|
114
|
+
|
|
115
|
+
An individual validation error consists of a `code`, user meangful `message`,
|
|
116
|
+
and possibly some additional `details`.
|
|
117
|
+
|
|
118
|
+
For a scalar field, this can be managed as a list of such objects.
|
|
119
|
+
|
|
120
|
+
For vector values (i.e. many=True) a list of lists will suffice. Extracting
|
|
121
|
+
errors for a given position is as simple as indexing.
|
|
122
|
+
|
|
123
|
+
For an `EntityField`, a mapping is used, instead. Each value, keyed by
|
|
124
|
+
sub-field name, reflects the structure appropriate as described.
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
IntegerField.validate("1") // [{ code: 'type', message: 'Value must be a number' }]
|
|
128
|
+
|
|
129
|
+
EntityField.validate({ ... }) // { field: [...], ...}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
From the top down, an Entity will get errors by looking into the Error map for
|
|
133
|
+
the field name.
|
|
134
|
+
|
|
135
|
+
The values will be either a list of Errors, a list-of-lists of Errors, or
|
|
136
|
+
another map.
|
|
137
|
+
|
|
138
|
+
# A more complex example
|
|
139
|
+
|
|
140
|
+
Here's an example showing an Entity with a nested Entity as a field.
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
type Post = {
|
|
144
|
+
id: number;
|
|
145
|
+
user: User;
|
|
146
|
+
posted: Date;
|
|
147
|
+
content: string;
|
|
148
|
+
public: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
const PostEntity = new Entity<Post>({
|
|
153
|
+
fields={
|
|
154
|
+
id: IntegerField(),
|
|
155
|
+
user: EntityField<Post>({ entity: UserEntity }),
|
|
156
|
+
posted: DateField(),
|
|
157
|
+
content: TextField(),
|
|
158
|
+
public: BooleanField({ defaultVaule: false }),
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Now any record created by this Entity will contain a nested record in the `user`
|
|
164
|
+
field.
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
|
|
168
|
+
const post = PostEntity.fromData(...);
|
|
169
|
+
|
|
170
|
+
...
|
|
171
|
+
|
|
172
|
+
const errors = UserEntity.validate(post.user);
|
|
173
|
+
|
|
174
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"as-boolean.js","sourceRoot":"","sources":["../../src/cleaner/as-boolean.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,KAAc;IAC9C,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function asDate(value: unknown): Date | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"as-date.js","sourceRoot":"","sources":["../../src/cleaner/as-date.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,KAAc;IAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,KAAe,CAAC,CAAC;IAEvC,OAAO,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function asInteger(value: unknown): number | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"as-integer.js","sourceRoot":"","sources":["../../src/cleaner/as-integer.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,KAAc;IAC9C,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAe,EAAE,EAAE,CAAC,CAAC;IAEjD,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function asNumber(value: unknown): number | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"as-number.js","sourceRoot":"","sources":["../../src/cleaner/as-number.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,KAAc;IAC7C,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,KAAe,CAAC,CAAC;IAE/C,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function asString(value: unknown): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"as-string.js","sourceRoot":"","sources":["../../src/cleaner/as-string.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,KAAc;IAC7C,OAAQ,KAAgB,CAAC,QAAQ,EAAE,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collapse-spaces.js","sourceRoot":"","sources":["../../src/cleaner/collapse-spaces.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,KAAc;IACnD,OAAQ,KAAgB,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as collapseSpaces } from './collapse-spaces';
|
|
2
|
+
export { default as asString } from './as-string';
|
|
3
|
+
export { default as asBoolean } from './as-boolean';
|
|
4
|
+
export { default as asNumber } from './as-number';
|
|
5
|
+
export { default as asInteger } from './as-integer';
|
|
6
|
+
export { default as trim } from './trim';
|
|
7
|
+
export { default as asDate } from './as-date';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { default as collapseSpaces } from './collapse-spaces';
|
|
2
|
+
export { default as asString } from './as-string';
|
|
3
|
+
export { default as asBoolean } from './as-boolean';
|
|
4
|
+
export { default as asNumber } from './as-number';
|
|
5
|
+
export { default as asInteger } from './as-integer';
|
|
6
|
+
export { default as trim } from './trim';
|
|
7
|
+
export { default as asDate } from './as-date';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cleaner/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function trim(value: unknown): unknown;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trim.js","sourceRoot":"","sources":["../../src/cleaner/trim.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,KAAc;IACzC,OAAQ,KAAgB,CAAC,IAAI,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { EntityRecordCleaner, EntityRecordValidator, EntityType, FieldErrorMap } from '..';
|
|
2
|
+
import { EntityField, type FieldConstructorParams, type FieldType } from '../field';
|
|
3
|
+
type EntityConstructorParams<T> = Pick<EntityType<T>, 'idField' | 'fields'> & Partial<Pick<EntityType<T>, 'cleaners' | 'validators'>>;
|
|
4
|
+
export default class Entity<T> implements EntityType<T> {
|
|
5
|
+
idField: keyof T;
|
|
6
|
+
fields: {
|
|
7
|
+
[F in keyof T]: FieldType<T[F]>;
|
|
8
|
+
};
|
|
9
|
+
cleaners: EntityRecordCleaner<T>[];
|
|
10
|
+
validators: EntityRecordValidator<T>[];
|
|
11
|
+
extra: object;
|
|
12
|
+
constructor({ idField, fields, cleaners, validators, ...extra }: EntityConstructorParams<T>);
|
|
13
|
+
fromData(data: Record<string, unknown>): T;
|
|
14
|
+
toData(record: T): Record<string, unknown>;
|
|
15
|
+
clean(record: T): T;
|
|
16
|
+
validate(record: T): FieldErrorMap;
|
|
17
|
+
getId(record?: T): string | undefined;
|
|
18
|
+
getEntityField(options?: FieldConstructorParams<T>): EntityField<T>;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { EntityField, } from '../field';
|
|
2
|
+
export default class Entity {
|
|
3
|
+
idField;
|
|
4
|
+
fields;
|
|
5
|
+
cleaners;
|
|
6
|
+
validators;
|
|
7
|
+
extra;
|
|
8
|
+
constructor({ idField, fields, cleaners = [], validators = [], ...extra }) {
|
|
9
|
+
this.idField = idField;
|
|
10
|
+
this.fields = fields;
|
|
11
|
+
this.cleaners = cleaners;
|
|
12
|
+
this.validators = validators;
|
|
13
|
+
this.extra = extra;
|
|
14
|
+
// Set field names
|
|
15
|
+
Object.entries(this.fields).forEach(([fieldName, field]) => (field.name = fieldName));
|
|
16
|
+
}
|
|
17
|
+
fromData(data) {
|
|
18
|
+
return Object.fromEntries(Object.entries(this.fields).map(([fieldName, field]) => [
|
|
19
|
+
fieldName,
|
|
20
|
+
fieldName in data
|
|
21
|
+
? field.fromData(data[fieldName])
|
|
22
|
+
: field.default({ data }),
|
|
23
|
+
]));
|
|
24
|
+
}
|
|
25
|
+
toData(record) {
|
|
26
|
+
const result = {};
|
|
27
|
+
Object.entries(this.fields).forEach(([fieldName, field]) => {
|
|
28
|
+
if (field.readOnly)
|
|
29
|
+
return;
|
|
30
|
+
result[fieldName] = field.toData(record[fieldName]);
|
|
31
|
+
});
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
clean(record) {
|
|
35
|
+
record = Object.values(this.fields).reduce(
|
|
36
|
+
// TS can't infer the type of Field.clean()
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
(curr, field) => {
|
|
40
|
+
curr[field.name] = field.clean(curr[field.name]);
|
|
41
|
+
return curr;
|
|
42
|
+
}, record);
|
|
43
|
+
return this.cleaners.reduce((curr, cleaner) => cleaner.call(this, curr), record);
|
|
44
|
+
}
|
|
45
|
+
validate(record) {
|
|
46
|
+
const errors = Object.fromEntries(Object.entries(this.fields)
|
|
47
|
+
.map(([fieldName, field]) => [
|
|
48
|
+
fieldName,
|
|
49
|
+
field.validate(record[fieldName]),
|
|
50
|
+
])
|
|
51
|
+
// Remove empty entries
|
|
52
|
+
.filter(([_name, err]) => Array.isArray(err) ? err.length : err !== undefined));
|
|
53
|
+
this.validators.forEach((validator) => validator(record, errors));
|
|
54
|
+
return errors;
|
|
55
|
+
}
|
|
56
|
+
getId(record) {
|
|
57
|
+
return record == null ? undefined : record[this.idField];
|
|
58
|
+
}
|
|
59
|
+
getEntityField(options = {}) {
|
|
60
|
+
return new EntityField({ entity: this, ...options });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=Entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Entity.js","sourceRoot":"","sources":["../../src/entity/Entity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleaners provide casting and conversion functions for user input.
|
|
3
|
+
*
|
|
4
|
+
* This may include type conversion, whitespace trimming/normalising, etc.
|
|
5
|
+
*
|
|
6
|
+
* It does not include validation, and can not yield errors.
|
|
7
|
+
*/
|
|
8
|
+
export { default as Entity } from './Entity';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleaners provide casting and conversion functions for user input.
|
|
3
|
+
*
|
|
4
|
+
* This may include type conversion, whitespace trimming/normalising, etc.
|
|
5
|
+
*
|
|
6
|
+
* It does not include validation, and can not yield errors.
|
|
7
|
+
*/
|
|
8
|
+
export { default as Entity } from './Entity';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/entity/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BooleanField.js","sourceRoot":"","sources":["../../src/field/BooleanField.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC;AAE1B,MAAM,OAAO,YAAa,SAAQ,KAAc;IAC9C,MAAM,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,MAAM,CAAC,eAAe,GAAG,CAAC,SAAS,CAAC,CAAC;IAErC,IAAI,GAAG,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CharField.js","sourceRoot":"","sources":["../../src/field/CharField.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC;AAE1B,MAAM,OAAO,SAAU,SAAQ,KAAa;IAC1C,MAAM,CAAC,YAAY,GAAG,EAAE,CAAC;IACzB,MAAM,CAAC,eAAe,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEpC,IAAI,GAAG,MAAM,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { asDate } from '../cleaner';
|
|
2
|
+
import { Field } from '.';
|
|
3
|
+
export declare class DateField extends Field<Date | null> {
|
|
4
|
+
static defaultValue: null;
|
|
5
|
+
static defaultCleaners: (typeof asDate)[];
|
|
6
|
+
type: string;
|
|
7
|
+
fromData(value: unknown): Date | null;
|
|
8
|
+
toData(value: Date | null): unknown;
|
|
9
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { asDate } from '../cleaner';
|
|
2
|
+
import { Field } from '.';
|
|
3
|
+
export class DateField extends Field {
|
|
4
|
+
static defaultValue = null;
|
|
5
|
+
static defaultCleaners = [asDate];
|
|
6
|
+
type = 'date';
|
|
7
|
+
fromData(value) {
|
|
8
|
+
return asDate(value);
|
|
9
|
+
}
|
|
10
|
+
toData(value) {
|
|
11
|
+
// toISOString always converts to UTC
|
|
12
|
+
// We must adjust our timezone accordingly, lest we send the wrong date
|
|
13
|
+
if (value instanceof Date) {
|
|
14
|
+
value.setMinutes(value.getMinutes() - value.getTimezoneOffset());
|
|
15
|
+
}
|
|
16
|
+
return value ? value.toISOString().split('T')[0] : '';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=DateField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateField.js","sourceRoot":"","sources":["../../src/field/DateField.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC;AAE1B,MAAM,OAAO,SAAU,SAAQ,KAAkB;IAC/C,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,eAAe,GAAG,CAAC,MAAM,CAAC,CAAC;IAElC,IAAI,GAAG,MAAM,CAAC;IAEd,QAAQ,CAAC,KAAc;QACrB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,CAAC,KAAkB;QACvB,qCAAqC;QACrC,uEAAuE;QACvE,IAAI,KAAK,YAAY,IAAI,EAAE;YACzB,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,KAAK,CAAC,iBAAiB,EAAE,CAAC,CAAC;SAClE;QACD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACxD,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { asDate } from '../cleaner';
|
|
2
|
+
import { Field } from '.';
|
|
3
|
+
export declare class DateTimeField extends Field<Date | null> {
|
|
4
|
+
static defaultValue: null;
|
|
5
|
+
static defaultCleaners: (typeof asDate)[];
|
|
6
|
+
type: string;
|
|
7
|
+
fromData(value: unknown): Date | null;
|
|
8
|
+
toData(value: Date | null): unknown;
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { asDate } from '../cleaner';
|
|
2
|
+
import { Field } from '.';
|
|
3
|
+
export class DateTimeField extends Field {
|
|
4
|
+
static defaultValue = null;
|
|
5
|
+
static defaultCleaners = [asDate];
|
|
6
|
+
type = 'date';
|
|
7
|
+
fromData(value) {
|
|
8
|
+
return asDate(value);
|
|
9
|
+
}
|
|
10
|
+
toData(value) {
|
|
11
|
+
return value ? value.toISOString() : '';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=DateTimeField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateTimeField.js","sourceRoot":"","sources":["../../src/field/DateTimeField.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC;AAE1B,MAAM,OAAO,aAAc,SAAQ,KAAkB;IACnD,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,eAAe,GAAG,CAAC,MAAM,CAAC,CAAC;IAElC,IAAI,GAAG,MAAM,CAAC;IAEd,QAAQ,CAAC,KAAc;QACrB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,CAAC,KAAkB;QACvB,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1C,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type EntityType, type ErrorSet, type FieldErrorMap, type ValidatorResult } from '..';
|
|
2
|
+
import { Field } from '.';
|
|
3
|
+
import type { FieldConstructorParams, FieldType } from '.';
|
|
4
|
+
type EntityFieldConstructorParams<T> = FieldConstructorParams<T> & {
|
|
5
|
+
entity: EntityType<T>;
|
|
6
|
+
};
|
|
7
|
+
export declare class EntityField<T> extends Field<T> {
|
|
8
|
+
type: string;
|
|
9
|
+
entity: EntityType<T>;
|
|
10
|
+
constructor({ entity, ...options }: EntityFieldConstructorParams<T>);
|
|
11
|
+
fromData(value: Record<string, unknown>): T;
|
|
12
|
+
toData(value: T): Record<string, unknown> | undefined;
|
|
13
|
+
getValue(value: T, key?: string | number): T | NonNullable<T>[keyof T];
|
|
14
|
+
validate(value: unknown): ValidatorResult;
|
|
15
|
+
/**
|
|
16
|
+
* EntityField is a special case.
|
|
17
|
+
*
|
|
18
|
+
* A Form Input may be asking us for the errors of a specific field.
|
|
19
|
+
*/
|
|
20
|
+
getErrors(errors: FieldErrorMap, key?: number | string): ErrorSet;
|
|
21
|
+
getId(value?: T): string | undefined;
|
|
22
|
+
getField(name: keyof T): FieldType<T[keyof T]>;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Field } from '.';
|
|
2
|
+
export class EntityField extends Field {
|
|
3
|
+
type = 'entity';
|
|
4
|
+
entity;
|
|
5
|
+
constructor({ entity, ...options }) {
|
|
6
|
+
super(options);
|
|
7
|
+
this.entity = entity;
|
|
8
|
+
}
|
|
9
|
+
fromData(value) {
|
|
10
|
+
return this.entity.fromData(value);
|
|
11
|
+
}
|
|
12
|
+
toData(value) {
|
|
13
|
+
if (value == null)
|
|
14
|
+
return;
|
|
15
|
+
return this.entity.toData(value);
|
|
16
|
+
}
|
|
17
|
+
getValue(value, key) {
|
|
18
|
+
if (!value)
|
|
19
|
+
return value;
|
|
20
|
+
switch (typeof key) {
|
|
21
|
+
case 'string':
|
|
22
|
+
if (this.many) {
|
|
23
|
+
throw Error(`Field ${this.name} [${this.type}] does not support field lookups: ${key}`);
|
|
24
|
+
}
|
|
25
|
+
return value[key];
|
|
26
|
+
case 'number':
|
|
27
|
+
if (!this.many) {
|
|
28
|
+
throw Error(`Field ${this.name} [${this.type}] does not support index lookups`);
|
|
29
|
+
}
|
|
30
|
+
return value[key];
|
|
31
|
+
case 'undefined':
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
validate(value) {
|
|
36
|
+
if (!value) {
|
|
37
|
+
if (!this.blank) {
|
|
38
|
+
return [{ code: 'blank', message: 'Field must not be blank' }];
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
return this.entity.validate(value);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* EntityField is a special case.
|
|
46
|
+
*
|
|
47
|
+
* A Form Input may be asking us for the errors of a specific field.
|
|
48
|
+
*/
|
|
49
|
+
getErrors(errors, key) {
|
|
50
|
+
if (key !== undefined) {
|
|
51
|
+
return errors[key];
|
|
52
|
+
}
|
|
53
|
+
return errors;
|
|
54
|
+
}
|
|
55
|
+
getId(value) {
|
|
56
|
+
return value && this.entity.getId(value);
|
|
57
|
+
}
|
|
58
|
+
// Used by Form
|
|
59
|
+
getField(name) {
|
|
60
|
+
return this.entity.fields[name];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=EntityField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityField.js","sourceRoot":"","sources":["../../src/field/EntityField.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC;AAO1B,MAAM,OAAO,WAAe,SAAQ,KAAQ;IAC1C,IAAI,GAAG,QAAQ,CAAC;IAChB,MAAM,CAAgB;IAEtB,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,EAAmC;QACjE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,QAAQ,CAAC,KAA8B;QACrC,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,CAAC,KAAQ;QACb,IAAI,KAAK,IAAI,IAAI;YAAE,OAAO;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,QAAQ,CAAC,KAAQ,EAAE,GAAqB;QACtC,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACzB,QAAQ,OAAO,GAAG,EAAE;YAClB,KAAK,QAAQ;gBACX,IAAI,IAAI,CAAC,IAAI,EAAE;oBACb,MAAM,KAAK,CACT,SAAS,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,qCAAqC,GAAG,EAAE,CAC3E,CAAC;iBACH;gBACD,OAAO,KAAK,CAAC,GAAc,CAAC,CAAC;YAC/B,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;oBACd,MAAM,KAAK,CACT,SAAS,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,kCAAkC,CACnE,CAAC;iBACH;gBACD,OAAQ,KAAa,CAAC,GAAG,CAAC,CAAC;YAC7B,KAAK,WAAW;gBACd,OAAO,KAAK,CAAC;SAChB;IACH,CAAC;IAED,QAAQ,CAAC,KAAc;QACrB,IAAI,CAAC,KAAK,EAAE;YACV,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE;gBACf,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC,CAAC;aAChE;YACD,OAAO;SACR;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAU,CAAC,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,MAAqB,EAAE,GAAqB;QACpD,IAAI,GAAG,KAAK,SAAS,EAAE;YACrB,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;SACpB;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,KAAS;QACb,OAAO,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAU,CAAC,CAAC;IAChD,CAAC;IAED,eAAe;IACf,QAAQ,CAAC,IAAa;QACpB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;CACF"}
|