@fougere/schema 0.12.1-alpha.0 → 0.12.2-alpha.0
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/dist/FougereFieldAxes.d.ts.map +1 -0
- package/dist/FougereFieldAxes.js.map +1 -0
- package/dist/Schema.d.ts +4 -3
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +3 -0
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaConstraints.d.ts +13 -10
- package/dist/SchemaConstraints.d.ts.map +1 -1
- package/dist/SchemaConstraints.js +32 -20
- package/dist/SchemaConstraints.js.map +1 -1
- package/dist/SchemaDefinition.d.ts.map +1 -1
- package/dist/SchemaDefinition.js +6 -5
- package/dist/SchemaDefinition.js.map +1 -1
- package/dist/SchemaError.d.ts +4 -1
- package/dist/SchemaError.d.ts.map +1 -1
- package/dist/SchemaError.js +5 -2
- package/dist/SchemaError.js.map +1 -1
- package/dist/SchemaView.d.ts +3 -2
- package/dist/SchemaView.d.ts.map +1 -1
- package/dist/axis/Axes.d.ts +1 -1
- package/dist/axis/Axes.js +3 -3
- package/dist/axis/Axis.d.ts +12 -2
- package/dist/axis/Axis.d.ts.map +1 -1
- package/dist/axis/boundary/Boundary.d.ts +9 -2
- package/dist/axis/boundary/Boundary.d.ts.map +1 -1
- package/dist/axis/boundary/Boundary.js +6 -2
- package/dist/axis/boundary/Boundary.js.map +1 -1
- package/dist/axis/boundary/Decoder.js.map +1 -1
- package/dist/axis/lifecycle/Lifecycle.d.ts +13 -4
- package/dist/axis/lifecycle/Lifecycle.d.ts.map +1 -1
- package/dist/axis/lifecycle/Lifecycle.js +8 -4
- package/dist/axis/lifecycle/Lifecycle.js.map +1 -1
- package/dist/axis/lifecycle/apply.js +1 -1
- package/dist/axis/lifecycle/apply.js.map +1 -1
- package/dist/axis/role/Role.d.ts +17 -10
- package/dist/axis/role/Role.d.ts.map +1 -1
- package/dist/axis/role/Role.js +30 -10
- package/dist/axis/role/Role.js.map +1 -1
- package/dist/axis/shape/Shape.d.ts +32 -9
- package/dist/axis/shape/Shape.d.ts.map +1 -1
- package/dist/axis/shape/Shape.js +43 -12
- package/dist/axis/shape/Shape.js.map +1 -1
- package/dist/axis/shape/ShapeFormat.d.ts +10 -0
- package/dist/axis/shape/ShapeFormat.d.ts.map +1 -0
- package/dist/axis/shape/ShapeFormat.js +32 -0
- package/dist/axis/shape/ShapeFormat.js.map +1 -0
- package/dist/entity/EntityDeclarations.d.ts +3 -2
- package/dist/entity/EntityDeclarations.d.ts.map +1 -1
- package/dist/entity/FieldGroups.d.ts +9 -0
- package/dist/entity/FieldGroups.d.ts.map +1 -0
- package/dist/entity/FieldGroups.js +2 -0
- package/dist/entity/FieldGroups.js.map +1 -0
- package/dist/field/Field.d.ts +35 -16
- package/dist/field/Field.d.ts.map +1 -1
- package/dist/field/Field.js +53 -8
- package/dist/field/Field.js.map +1 -1
- package/dist/field/FieldFormat.d.ts +1 -1
- package/dist/field/FieldFormat.js +4 -4
- package/dist/field/FieldFormat.js.map +1 -1
- package/dist/field/FieldSet.d.ts +12 -3
- package/dist/field/FieldSet.d.ts.map +1 -1
- package/dist/field/FieldSet.js +22 -15
- package/dist/field/FieldSet.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/Format.d.ts +21 -2
- package/dist/lib/Format.d.ts.map +1 -1
- package/dist/lib/Format.js +31 -3
- package/dist/lib/Format.js.map +1 -1
- package/dist/lib/UnionToIntersection.d.ts +7 -0
- package/dist/lib/UnionToIntersection.d.ts.map +1 -0
- package/dist/lib/UnionToIntersection.js +2 -0
- package/dist/lib/UnionToIntersection.js.map +1 -0
- package/dist/lib/utils.d.ts +1 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/projection/Cases.d.ts +1 -1
- package/dist/projection/Cases.d.ts.map +1 -1
- package/dist/projection/Cases.js +20 -8
- package/dist/projection/Cases.js.map +1 -1
- package/dist/projection/Visibility.d.ts.map +1 -1
- package/dist/projection/Visibility.js +6 -6
- package/dist/projection/Visibility.js.map +1 -1
- package/dist/projection/card/Bundle.d.ts.map +1 -1
- package/dist/projection/card/Bundle.js.map +1 -1
- package/dist/projection/card/Card.d.ts.map +1 -1
- package/dist/projection/card/Card.js +21 -30
- package/dist/projection/card/Card.js.map +1 -1
- package/dist/projection/card/CardForms.js +1 -1
- package/dist/projection/card/Diff.js.map +1 -1
- package/dist/projection/card/FieldExtension.d.ts +2 -2
- package/dist/projection/card/FieldExtension.d.ts.map +1 -1
- package/dist/projection/card/RoleDescriptor.d.ts +7 -2
- package/dist/projection/card/RoleDescriptor.d.ts.map +1 -1
- package/dist/projection/card/RoleDescriptor.js +2 -2
- package/dist/projection/card/RoleDescriptor.js.map +1 -1
- package/dist/projection/card/ShapeKeywords.d.ts +1 -1
- package/dist/projection/card/ShapeKeywords.d.ts.map +1 -1
- package/dist/validator/FieldDeclarationValidator.d.ts.map +1 -1
- package/dist/validator/FieldDeclarationValidator.js +7 -10
- package/dist/validator/FieldDeclarationValidator.js.map +1 -1
- package/dist/validator/FieldValueValidator.d.ts.map +1 -1
- package/dist/validator/FieldValueValidator.js +0 -2
- package/dist/validator/FieldValueValidator.js.map +1 -1
- package/dist/validator/InputValidator.d.ts +7 -1
- package/dist/validator/InputValidator.d.ts.map +1 -1
- package/dist/validator/InputValidator.js +15 -14
- package/dist/validator/InputValidator.js.map +1 -1
- package/dist/vocabulary/preset/created.d.ts +2 -2
- package/dist/vocabulary/preset/created.d.ts.map +1 -1
- package/dist/vocabulary/preset/created.js +2 -2
- package/dist/vocabulary/preset/created.js.map +1 -1
- package/dist/vocabulary/preset/updated.d.ts +2 -2
- package/dist/vocabulary/preset/updated.d.ts.map +1 -1
- package/dist/vocabulary/preset/updated.js +2 -2
- package/dist/vocabulary/preset/updated.js.map +1 -1
- package/dist/vocabulary/primitive/bool.d.ts +2 -5
- package/dist/vocabulary/primitive/bool.d.ts.map +1 -1
- package/dist/vocabulary/primitive/bool.js +1 -5
- package/dist/vocabulary/primitive/bool.js.map +1 -1
- package/dist/vocabulary/primitive/date.d.ts +2 -3
- package/dist/vocabulary/primitive/date.d.ts.map +1 -1
- package/dist/vocabulary/primitive/date.js +2 -3
- package/dist/vocabulary/primitive/date.js.map +1 -1
- package/dist/vocabulary/primitive/json.d.ts +3 -3
- package/dist/vocabulary/primitive/json.d.ts.map +1 -1
- package/dist/vocabulary/primitive/json.js +11 -11
- package/dist/vocabulary/primitive/json.js.map +1 -1
- package/dist/vocabulary/primitive/list.d.ts +6 -5
- package/dist/vocabulary/primitive/list.d.ts.map +1 -1
- package/dist/vocabulary/primitive/list.js +13 -7
- package/dist/vocabulary/primitive/list.js.map +1 -1
- package/dist/vocabulary/primitive/many.d.ts +2 -4
- package/dist/vocabulary/primitive/many.d.ts.map +1 -1
- package/dist/vocabulary/primitive/many.js +2 -4
- package/dist/vocabulary/primitive/many.js.map +1 -1
- package/dist/vocabulary/primitive/number.d.ts +2 -4
- package/dist/vocabulary/primitive/number.d.ts.map +1 -1
- package/dist/vocabulary/primitive/number.js +1 -3
- package/dist/vocabulary/primitive/number.js.map +1 -1
- package/dist/vocabulary/primitive/oneOf.d.ts +4 -7
- package/dist/vocabulary/primitive/oneOf.d.ts.map +1 -1
- package/dist/vocabulary/primitive/oneOf.js +19 -11
- package/dist/vocabulary/primitive/oneOf.js.map +1 -1
- package/dist/vocabulary/primitive/primary.d.ts +2 -2
- package/dist/vocabulary/primitive/primary.d.ts.map +1 -1
- package/dist/vocabulary/primitive/primary.js +2 -3
- package/dist/vocabulary/primitive/primary.js.map +1 -1
- package/dist/vocabulary/primitive/ref.d.ts +2 -6
- package/dist/vocabulary/primitive/ref.d.ts.map +1 -1
- package/dist/vocabulary/primitive/ref.js +1 -1
- package/dist/vocabulary/primitive/ref.js.map +1 -1
- package/dist/vocabulary/primitive/text.d.ts +2 -4
- package/dist/vocabulary/primitive/text.d.ts.map +1 -1
- package/dist/vocabulary/primitive/text.js +1 -3
- package/dist/vocabulary/primitive/text.js.map +1 -1
- package/dist/vocabulary/rule/nullable.d.ts +0 -1
- package/dist/vocabulary/rule/nullable.d.ts.map +1 -1
- package/dist/vocabulary/rule/nullable.js +0 -1
- package/dist/vocabulary/rule/nullable.js.map +1 -1
- package/dist/vocabulary/rule/optional.d.ts +0 -1
- package/dist/vocabulary/rule/optional.d.ts.map +1 -1
- package/dist/vocabulary/rule/optional.js +0 -1
- package/dist/vocabulary/rule/optional.js.map +1 -1
- package/dist/vocabulary/rule/readOnly.d.ts +0 -2
- package/dist/vocabulary/rule/readOnly.d.ts.map +1 -1
- package/dist/vocabulary/rule/readOnly.js +0 -2
- package/dist/vocabulary/rule/readOnly.js.map +1 -1
- package/dist/vocabulary/rule/writeOnly.d.ts +0 -2
- package/dist/vocabulary/rule/writeOnly.d.ts.map +1 -1
- package/dist/vocabulary/rule/writeOnly.js +0 -2
- package/dist/vocabulary/rule/writeOnly.js.map +1 -1
- package/dist/vocabulary/vocabulary.d.ts +2 -3
- package/dist/vocabulary/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary/vocabulary.js +17 -21
- package/dist/vocabulary/vocabulary.js.map +1 -1
- package/package.json +1 -1
- package/src/Schema.ts +11 -8
- package/src/SchemaConstraints.ts +40 -22
- package/src/SchemaDefinition.ts +7 -5
- package/src/SchemaError.ts +8 -2
- package/src/SchemaView.ts +3 -2
- package/src/axis/Axes.ts +3 -3
- package/src/axis/Axis.ts +13 -2
- package/src/axis/boundary/Boundary.ts +16 -2
- package/src/axis/boundary/Decoder.ts +2 -0
- package/src/axis/lifecycle/Lifecycle.ts +15 -4
- package/src/axis/lifecycle/apply.ts +1 -1
- package/src/axis/role/Role.ts +41 -9
- package/src/axis/shape/Shape.ts +89 -23
- package/src/axis/shape/ShapeFormat.ts +37 -0
- package/src/entity/EntityDeclarations.ts +3 -2
- package/src/entity/FieldGroups.ts +9 -0
- package/src/field/Field.ts +84 -21
- package/src/field/FieldFormat.ts +5 -5
- package/src/field/FieldSet.ts +27 -19
- package/src/index.ts +1 -0
- package/src/lib/Format.ts +36 -3
- package/src/lib/UnionToIntersection.ts +10 -0
- package/src/lib/utils.ts +5 -1
- package/src/projection/Cases.ts +25 -8
- package/src/projection/Visibility.ts +9 -6
- package/src/projection/card/Bundle.ts +2 -0
- package/src/projection/card/Card.ts +26 -27
- package/src/projection/card/CardForms.ts +1 -1
- package/src/projection/card/Diff.ts +4 -0
- package/src/projection/card/FieldExtension.ts +2 -2
- package/src/projection/card/RoleDescriptor.ts +9 -4
- package/src/projection/card/ShapeKeywords.ts +1 -1
- package/src/validator/FieldDeclarationValidator.ts +7 -10
- package/src/validator/FieldValueValidator.ts +3 -1
- package/src/validator/InputValidator.ts +17 -12
- package/src/vocabulary/preset/created.ts +3 -3
- package/src/vocabulary/preset/updated.ts +4 -3
- package/src/vocabulary/primitive/bool.ts +3 -8
- package/src/vocabulary/primitive/date.ts +5 -4
- package/src/vocabulary/primitive/json.ts +27 -12
- package/src/vocabulary/primitive/list.ts +16 -9
- package/src/vocabulary/primitive/many.ts +3 -4
- package/src/vocabulary/primitive/number.ts +3 -8
- package/src/vocabulary/primitive/oneOf.ts +40 -24
- package/src/vocabulary/primitive/primary.ts +9 -5
- package/src/vocabulary/primitive/ref.ts +3 -7
- package/src/vocabulary/primitive/text.ts +3 -8
- package/src/vocabulary/rule/nullable.ts +0 -1
- package/src/vocabulary/rule/optional.ts +0 -1
- package/src/vocabulary/rule/readOnly.ts +0 -2
- package/src/vocabulary/rule/writeOnly.ts +0 -2
- package/src/vocabulary/vocabulary.ts +18 -22
- package/dist/entity/CompositeUnique.d.ts +0 -4
- package/dist/entity/CompositeUnique.d.ts.map +0 -1
- package/dist/entity/CompositeUnique.js +0 -2
- package/dist/entity/CompositeUnique.js.map +0 -1
- package/dist/field/FougereFieldAxes.d.ts.map +0 -1
- package/dist/field/FougereFieldAxes.js.map +0 -1
- package/dist/field/Meta.d.ts +0 -6
- package/dist/field/Meta.d.ts.map +0 -1
- package/dist/field/Meta.js +0 -3
- package/dist/field/Meta.js.map +0 -1
- package/src/entity/CompositeUnique.ts +0 -6
- package/src/field/Meta.ts +0 -5
- /package/dist/{field/FougereFieldAxes.d.ts → FougereFieldAxes.d.ts} +0 -0
- /package/dist/{field/FougereFieldAxes.js → FougereFieldAxes.js} +0 -0
- /package/src/{field/FougereFieldAxes.ts → FougereFieldAxes.ts} +0 -0
|
@@ -15,21 +15,23 @@ export class Visibility {
|
|
|
15
15
|
const result: Fields = {};
|
|
16
16
|
for (const [name, field] of Object.entries(this.fields)) {
|
|
17
17
|
const role = Role.of(field);
|
|
18
|
-
if (role.isPrimary) continue;
|
|
19
|
-
if (Lifecycle.of(field).stampedAtCreate) continue;
|
|
20
|
-
if (role.isCollection) continue;
|
|
21
|
-
if (Boundary.of(field).readOnly) continue;
|
|
18
|
+
if (role.isPrimary()) continue;
|
|
19
|
+
if (Lifecycle.of(field).stampedAtCreate()) continue;
|
|
20
|
+
if (role.isCollection()) continue;
|
|
21
|
+
if (Boundary.of(field).readOnly()) continue;
|
|
22
22
|
result[name] = field;
|
|
23
23
|
}
|
|
24
|
+
|
|
24
25
|
return result;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
get output(): Fields {
|
|
28
29
|
const result: Fields = {};
|
|
29
30
|
for (const [name, field] of Object.entries(this.fields)) {
|
|
30
|
-
if (Boundary.of(field).writeOnly) continue;
|
|
31
|
+
if (Boundary.of(field).writeOnly()) continue;
|
|
31
32
|
result[name] = field;
|
|
32
33
|
}
|
|
34
|
+
|
|
33
35
|
return result;
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -60,7 +62,7 @@ export class Visibility {
|
|
|
60
62
|
for (const [key, field] of Object.entries(this.fields)) {
|
|
61
63
|
if (!(key in record)) continue;
|
|
62
64
|
const boundary = Boundary.of(field);
|
|
63
|
-
if (boundary.writeOnly) {
|
|
65
|
+
if (boundary.writeOnly()) {
|
|
64
66
|
delete out[key];
|
|
65
67
|
continue;
|
|
66
68
|
}
|
|
@@ -68,6 +70,7 @@ export class Visibility {
|
|
|
68
70
|
if (value === null || value === undefined) continue;
|
|
69
71
|
out[key] = boundary.encode(value);
|
|
70
72
|
}
|
|
73
|
+
|
|
71
74
|
return out;
|
|
72
75
|
}
|
|
73
76
|
}
|
|
@@ -56,6 +56,7 @@ export class Bundle {
|
|
|
56
56
|
byName[name.toLowerCase()] = schema as unknown as EntityConstructor;
|
|
57
57
|
schemas[name] = schema;
|
|
58
58
|
}
|
|
59
|
+
|
|
59
60
|
return schemas;
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -72,6 +73,7 @@ export class Bundle {
|
|
|
72
73
|
);
|
|
73
74
|
if (diff.changes.length > 0 || diff.ambiguous.length > 0) entities[name] = diff;
|
|
74
75
|
}
|
|
76
|
+
|
|
75
77
|
return {
|
|
76
78
|
entitiesAdded: Object.keys(after).filter((name) => !(name in before)),
|
|
77
79
|
entitiesRemoved: Object.keys(before).filter((name) => !(name in after)),
|
|
@@ -39,10 +39,11 @@ export class Card<T = Values<Fields>> {
|
|
|
39
39
|
const required: string[] = [];
|
|
40
40
|
for (const [key, field] of Object.entries(fields)) {
|
|
41
41
|
properties[key] = describeField(field, key);
|
|
42
|
-
if (validator.
|
|
42
|
+
if (validator.requires(field)) required.push(key);
|
|
43
43
|
}
|
|
44
|
-
for (const
|
|
45
|
-
for (const
|
|
44
|
+
for (const kind of ['unique', 'index'] as const)
|
|
45
|
+
for (const group of (kind === 'unique' ? schema.getUnique() : schema.getIndex()) ?? [])
|
|
46
|
+
for (const member of group) carryGroup(properties[member], kind, group);
|
|
46
47
|
|
|
47
48
|
const descriptor: SchemaDescriptor = {
|
|
48
49
|
type: 'object',
|
|
@@ -56,6 +57,7 @@ export class Card<T = Values<Fields>> {
|
|
|
56
57
|
|
|
57
58
|
const origin = originOf(schema);
|
|
58
59
|
if (origin) descriptor['x-fougere-derived'] = origin;
|
|
60
|
+
|
|
59
61
|
return new Card<Values<TFields>>(descriptor);
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -95,7 +97,7 @@ export class Card<T = Values<Fields>> {
|
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
const fields: Fields = {};
|
|
98
|
-
const groups: string[][] = [];
|
|
100
|
+
const groups: { unique: string[][]; index: string[][] } = { unique: [], index: [] };
|
|
99
101
|
for (const [key, property] of Object.entries(descriptor.properties)) {
|
|
100
102
|
if (!isObject(property)) {
|
|
101
103
|
refuse(
|
|
@@ -104,13 +106,15 @@ export class Card<T = Values<Fields>> {
|
|
|
104
106
|
);
|
|
105
107
|
}
|
|
106
108
|
fields[key] = reconstructField(property, key, resolve);
|
|
107
|
-
for (const
|
|
108
|
-
|
|
109
|
+
for (const kind of ['unique', 'index'] as const)
|
|
110
|
+
for (const group of property['x-fougere']?.role?.[kind] ?? [])
|
|
111
|
+
if (group.length > 1) groups[kind].push([...group]);
|
|
109
112
|
}
|
|
110
113
|
const schema = Schema.of({ fields, constraints: SchemaConstraints.of(groups) });
|
|
111
114
|
const title = name ?? descriptor.title;
|
|
112
115
|
if (title)
|
|
113
116
|
Object.defineProperty(schema, 'name', { value: title, configurable: true });
|
|
117
|
+
|
|
114
118
|
return schema as unknown as SchemaConstructor<FieldsOf<T>>;
|
|
115
119
|
}
|
|
116
120
|
|
|
@@ -127,14 +131,13 @@ export class Card<T = Values<Fields>> {
|
|
|
127
131
|
*/
|
|
128
132
|
function describeExtension(field: Field, key: string): FieldExtension | undefined {
|
|
129
133
|
const extension: Record<string, unknown> = {};
|
|
130
|
-
for (const
|
|
131
|
-
const
|
|
132
|
-
if (declared === undefined) continue;
|
|
133
|
-
const form = CardForms.find(slot);
|
|
134
|
+
for (const [name, declared] of Object.entries(field.axes)) {
|
|
135
|
+
const form = CardForms.find(name);
|
|
134
136
|
const wire = form ? form.describe(declared, key) : declared;
|
|
135
|
-
if (wire !== undefined) extension[
|
|
137
|
+
if (wire !== undefined) extension[name] = wire;
|
|
136
138
|
}
|
|
137
139
|
clean(extension);
|
|
140
|
+
|
|
138
141
|
return Object.keys(extension).length ? (extension as FieldExtension) : undefined;
|
|
139
142
|
}
|
|
140
143
|
|
|
@@ -142,9 +145,9 @@ function describeField(field: Field, key: string): FieldDescriptor {
|
|
|
142
145
|
// The shape IS JSON Schema, so it lands whole rather than key by key — which is what let
|
|
143
146
|
// it be copied under a computed key into a type that names its own.
|
|
144
147
|
const descriptor: FieldDescriptor = clean({ ...field.shape }) as FieldDescriptor;
|
|
145
|
-
if (field.meta?.description) descriptor.description = field.meta.description;
|
|
146
148
|
const extension = describeExtension(field, key);
|
|
147
149
|
if (extension) descriptor['x-fougere'] = extension;
|
|
150
|
+
|
|
148
151
|
return descriptor;
|
|
149
152
|
}
|
|
150
153
|
|
|
@@ -159,6 +162,7 @@ function originOf(schema: SchemaView): DerivedFrom | undefined {
|
|
|
159
162
|
const nameOf = Object.fromEntries(
|
|
160
163
|
Object.entries(derivation.nameOf).map(([key, value]) => [key, value ?? null]),
|
|
161
164
|
);
|
|
165
|
+
|
|
162
166
|
return { from: derivation.sourceName, nameOf };
|
|
163
167
|
}
|
|
164
168
|
|
|
@@ -177,7 +181,7 @@ function reconstructShape(property: FieldDescriptor): Field['shape'] | undefined
|
|
|
177
181
|
|
|
178
182
|
// `describeField` writes the shape whole, so it is read whole: a list of keywords here
|
|
179
183
|
// would be a second inventory, and the one that forgets a keyword loses it in silence.
|
|
180
|
-
const { 'x-fougere': _extension,
|
|
184
|
+
const { 'x-fougere': _extension, ...shape } = property;
|
|
181
185
|
if (shape.items) shape.items = reconstructShape(shape.items) as FieldDescriptor;
|
|
182
186
|
|
|
183
187
|
return clean(shape) as Field['shape'];
|
|
@@ -203,19 +207,13 @@ function reconstructField(
|
|
|
203
207
|
admitPatterns(shape, `Field '${key}'`);
|
|
204
208
|
const extension = property['x-fougere'];
|
|
205
209
|
const axes: Record<string, unknown> = {};
|
|
206
|
-
for (const
|
|
207
|
-
const wire = (extension as Record<string, unknown> | undefined)?.[
|
|
208
|
-
const form = CardForms.find(
|
|
209
|
-
if (wire !== undefined) axes[
|
|
210
|
+
for (const name of Axes.names) {
|
|
211
|
+
const wire = (extension as Record<string, unknown> | undefined)?.[name];
|
|
212
|
+
const form = CardForms.find(name);
|
|
213
|
+
if (wire !== undefined) axes[name] = form ? form.reconstruct(wire, resolve) : wire;
|
|
210
214
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
...axes,
|
|
214
|
-
meta:
|
|
215
|
-
property.description !== undefined
|
|
216
|
-
? { description: property.description }
|
|
217
|
-
: undefined,
|
|
218
|
-
} as never, key);
|
|
215
|
+
|
|
216
|
+
return new Field({ shape, ...axes } as never, key);
|
|
219
217
|
}
|
|
220
218
|
|
|
221
219
|
/**
|
|
@@ -225,10 +223,11 @@ function reconstructField(
|
|
|
225
223
|
*/
|
|
226
224
|
function carryGroup(
|
|
227
225
|
property: FieldDescriptor | undefined,
|
|
226
|
+
kind: 'unique' | 'index',
|
|
228
227
|
group: readonly string[],
|
|
229
228
|
): void {
|
|
230
229
|
if (!property) return;
|
|
231
|
-
const extension = (property['x-fougere'] ??= {}) as { role?:
|
|
230
|
+
const extension = (property['x-fougere'] ??= {}) as { role?: Record<string, string[][]> };
|
|
232
231
|
const role = (extension.role ??= {});
|
|
233
|
-
(role
|
|
232
|
+
(role[kind] ??= []).push([...group]);
|
|
234
233
|
}
|
|
@@ -8,6 +8,6 @@ import { roleOnCard } from './RoleDescriptor.js';
|
|
|
8
8
|
* FR : les axes qu'une carte écrit autrement que déclarés ; les autres voyagent tels quels.
|
|
9
9
|
* `CardForms.register('tenancy', tenancyOnCard)`
|
|
10
10
|
*/
|
|
11
|
-
export const CardForms = new Registry<CardForm>('card form', 'call CardForms.register(
|
|
11
|
+
export const CardForms = new Registry<CardForm>('card form', 'call CardForms.register(name, form)', [
|
|
12
12
|
['role', roleOnCard as CardForm],
|
|
13
13
|
]);
|
|
@@ -107,6 +107,7 @@ function restated(
|
|
|
107
107
|
|
|
108
108
|
function shapeOf(descriptor: FieldDescriptor): Record<string, unknown> {
|
|
109
109
|
const { 'x-fougere': _extension, description: _description, ...shape } = descriptor;
|
|
110
|
+
|
|
110
111
|
return shape as Record<string, unknown>;
|
|
111
112
|
}
|
|
112
113
|
|
|
@@ -118,6 +119,7 @@ function shapeOf(descriptor: FieldDescriptor): Record<string, unknown> {
|
|
|
118
119
|
function typesOf(descriptor: FieldDescriptor): TypeSet {
|
|
119
120
|
const type = descriptor.type;
|
|
120
121
|
if (type === undefined) return [];
|
|
122
|
+
|
|
121
123
|
return (Array.isArray(type) ? [...type] : [type]).sort();
|
|
122
124
|
}
|
|
123
125
|
|
|
@@ -128,6 +130,7 @@ function typesOf(descriptor: FieldDescriptor): TypeSet {
|
|
|
128
130
|
*/
|
|
129
131
|
function boundsOf(descriptor: FieldDescriptor): Record<string, unknown> {
|
|
130
132
|
const { type: _type, ...rest } = shapeOf(descriptor);
|
|
133
|
+
|
|
131
134
|
return rest;
|
|
132
135
|
}
|
|
133
136
|
|
|
@@ -153,5 +156,6 @@ function candidates(
|
|
|
153
156
|
const now = Object.keys(after);
|
|
154
157
|
const apart = ({ removed: gone, added: appeared }: RenameCandidate): number =>
|
|
155
158
|
Math.abs(now.indexOf(appeared) - was.indexOf(gone));
|
|
159
|
+
|
|
156
160
|
return found.sort((a, b) => apart(a) - apart(b));
|
|
157
161
|
}
|
|
@@ -7,6 +7,6 @@ export interface FieldExtension {
|
|
|
7
7
|
lifecycle?: LifecycleRules;
|
|
8
8
|
boundary?: BoundaryRef;
|
|
9
9
|
|
|
10
|
-
/** An axis registered from outside writes
|
|
11
|
-
[
|
|
10
|
+
/** An axis registered from outside writes here, under its own name. */
|
|
11
|
+
[axis: string]: unknown;
|
|
12
12
|
}
|
|
@@ -5,9 +5,14 @@ import type { Resolver } from '../../axis/Resolver.js';
|
|
|
5
5
|
import { lowerFirst } from '../../lib/utils.js';
|
|
6
6
|
import type { CardForm } from './CardForm.js';
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* What a role becomes on a card: a target is a NAME there, and `unique` and `index` are both
|
|
10
|
+
* GROUPS — the field says `true`, the card says which names go together, since a group of
|
|
11
|
+
* several belongs to no single field.
|
|
12
|
+
*/
|
|
13
|
+
export type RoleDescriptor = Pick<RoleRules, 'primary'> & {
|
|
10
14
|
unique?: string[][];
|
|
15
|
+
index?: string[][];
|
|
11
16
|
relation?: RelationDescriptor;
|
|
12
17
|
};
|
|
13
18
|
|
|
@@ -21,7 +26,7 @@ export const roleOnCard: CardForm<RoleRules, RoleDescriptor> = {
|
|
|
21
26
|
const descriptor: Mutable<RoleDescriptor> = {};
|
|
22
27
|
if (role.primary) descriptor.primary = true;
|
|
23
28
|
if (role.unique) descriptor.unique = [[key]];
|
|
24
|
-
if (role.index) descriptor.index =
|
|
29
|
+
if (role.index) descriptor.index = [[key]];
|
|
25
30
|
if (role.relation) {
|
|
26
31
|
const target = role.relation.to() as { name?: string };
|
|
27
32
|
descriptor.relation = {
|
|
@@ -38,7 +43,7 @@ export const roleOnCard: CardForm<RoleRules, RoleDescriptor> = {
|
|
|
38
43
|
const rules: Mutable<RoleRules> = {};
|
|
39
44
|
if (wire.primary) rules.primary = true;
|
|
40
45
|
if (wire.unique?.some((group) => group.length === 1)) rules.unique = true;
|
|
41
|
-
if (wire.index) rules.index = true;
|
|
46
|
+
if (wire.index?.some((group) => group.length === 1)) rules.index = true;
|
|
42
47
|
if (wire.relation) {
|
|
43
48
|
const name = wire.relation.to;
|
|
44
49
|
rules.relation = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Shape } from '../../axis/shape/Shape.js';
|
|
2
|
+
import type { UnionToIntersection } from '../../lib/UnionToIntersection.js';
|
|
2
3
|
|
|
3
4
|
type KeywordsOf<S> = S extends unknown ? Partial<Omit<S, 'type' | 'items' | 'properties'>> : never;
|
|
4
|
-
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
5
5
|
|
|
6
6
|
/** Every keyword any shape may carry, flattened — what a descriptor records of one. */
|
|
7
7
|
export type ShapeKeywords = UnionToIntersection<KeywordsOf<Shape>>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Axes } from '../axis/Axes.js';
|
|
2
|
-
import { Shapes } from '../axis/shape/Shape.js';
|
|
3
2
|
import type { Field } from '../field/Field.js';
|
|
4
|
-
import { isObject
|
|
3
|
+
import { isObject } from '../lib/utils.js';
|
|
4
|
+
import { SchemaError } from '../SchemaError.js';
|
|
5
5
|
import type { ValidationError } from '../lib/ValidationError.js';
|
|
6
6
|
import type { ValidationResult } from '../lib/ValidationResult.js';
|
|
7
7
|
import { JsonSchemaValidator } from './JsonSchemaValidator.js';
|
|
@@ -22,7 +22,7 @@ export class FieldDeclarationValidator {
|
|
|
22
22
|
errors: [
|
|
23
23
|
{
|
|
24
24
|
path: [],
|
|
25
|
-
message: `Expected an object — got ${
|
|
25
|
+
message: `Expected an object — got ${SchemaError.inspect(declaration)}`,
|
|
26
26
|
},
|
|
27
27
|
],
|
|
28
28
|
};
|
|
@@ -30,17 +30,14 @@ export class FieldDeclarationValidator {
|
|
|
30
30
|
|
|
31
31
|
const errors: ValidationError[] = [];
|
|
32
32
|
|
|
33
|
-
if (
|
|
34
|
-
errors.push({
|
|
35
|
-
path: ['shape'],
|
|
36
|
-
message: `Every field states a shape — got ${shown(declaration.shape)}`,
|
|
37
|
-
});
|
|
33
|
+
if (declaration.shape === undefined) {
|
|
34
|
+
errors.push({ path: ['shape'], message: 'Every field states a shape' });
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
errors.push(...JsonSchemaValidator.of(Axes.fieldFormat).refusalsOf(declaration, []));
|
|
41
38
|
|
|
42
|
-
for (const [
|
|
43
|
-
errors.push(...(axis.refusals?.(declaration[
|
|
39
|
+
for (const [name, axis] of Axes.entries) {
|
|
40
|
+
errors.push(...(axis.refusals?.(declaration[name], declaration) ?? []));
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
return errors.length
|
|
@@ -30,7 +30,6 @@ export class FieldValueValidator {
|
|
|
30
30
|
const type = Shapes.typeOf(shape);
|
|
31
31
|
const base = Shapes.of(shape).base;
|
|
32
32
|
if (value !== null) {
|
|
33
|
-
if (base?.type === 'object' && !base.properties) return { value };
|
|
34
33
|
if (type === 'date' && value instanceof Date) {
|
|
35
34
|
return Number.isNaN(value.getTime()) ? { message: 'Invalid date' } : { value };
|
|
36
35
|
}
|
|
@@ -46,6 +45,7 @@ export class FieldValueValidator {
|
|
|
46
45
|
if (plan.custom && typeof value === 'string' && !plan.custom(value)) {
|
|
47
46
|
return { message: `String does not match format "${plan.formatName}".` };
|
|
48
47
|
}
|
|
48
|
+
|
|
49
49
|
return { value };
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -72,6 +72,7 @@ export class FieldValueValidator {
|
|
|
72
72
|
};
|
|
73
73
|
this.plans.set(shape, plan);
|
|
74
74
|
}
|
|
75
|
+
|
|
75
76
|
return plan;
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -83,6 +84,7 @@ export class FieldValueValidator {
|
|
|
83
84
|
`the engine validates ${Object.keys(engineFormats).length} formats natively and this is not one of them.`,
|
|
84
85
|
);
|
|
85
86
|
}
|
|
87
|
+
|
|
86
88
|
return custom;
|
|
87
89
|
}
|
|
88
90
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Axes } from '../axis/Axes.js';
|
|
1
2
|
import { Boundary } from '../axis/boundary/Boundary.js';
|
|
2
3
|
import { Lifecycle } from '../axis/lifecycle/Lifecycle.js';
|
|
3
4
|
import { Role } from '../axis/role/Role.js';
|
|
@@ -20,11 +21,14 @@ export class InputValidator {
|
|
|
20
21
|
return new InputValidator(fields, options);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
/**
|
|
25
|
+
* The three REGISTERED axes decide, and the shape is not one of them — it could not answer
|
|
26
|
+
* anyway: `nullable(text())` and `optional(text())` both shape `['string', 'null']`, and only
|
|
27
|
+
* the second dispenses. JSON Schema draws the same line, where `required` is a list on the
|
|
28
|
+
* OBJECT and never a keyword on the property.
|
|
29
|
+
*/
|
|
30
|
+
requires(field: Field): boolean {
|
|
31
|
+
return !Axes.all.some((axis) => axis.admitsAbsence?.(field));
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
/** Hands on the value it PARSED, so a handler never re-checks a row. */
|
|
@@ -43,7 +47,8 @@ export class InputValidator {
|
|
|
43
47
|
for (const [key, field] of Object.entries(this.fields)) {
|
|
44
48
|
const verdict = this.admit(field, data[key]);
|
|
45
49
|
if (verdict === undefined) continue;
|
|
46
|
-
if ('message' in verdict)
|
|
50
|
+
if ('message' in verdict)
|
|
51
|
+
errors.push({ path: [key, ...(verdict.path ?? [])], message: verdict.message });
|
|
47
52
|
else row[key] = verdict.value;
|
|
48
53
|
}
|
|
49
54
|
|
|
@@ -61,8 +66,11 @@ export class InputValidator {
|
|
|
61
66
|
private admit(field: Field, value: unknown): Verdict | undefined {
|
|
62
67
|
if (value === undefined) return this.whenAbsent(field);
|
|
63
68
|
|
|
64
|
-
if (Boundary.of(field).readOnly) return { message: InputRefusal.readOnly };
|
|
65
|
-
|
|
69
|
+
if (Boundary.of(field).readOnly()) return { message: InputRefusal.readOnly };
|
|
70
|
+
// The other side of the relation carries the key: there is nothing to write here.
|
|
71
|
+
if (Role.of(field).isCollection()) return { message: InputRefusal.readOnly };
|
|
72
|
+
if (this.options.patch && Lifecycle.of(field).immutable())
|
|
73
|
+
return { message: InputRefusal.immutable };
|
|
66
74
|
|
|
67
75
|
return FieldValueValidator.of(field).parse(value);
|
|
68
76
|
}
|
|
@@ -70,9 +78,6 @@ export class InputValidator {
|
|
|
70
78
|
private whenAbsent(field: Field): Verdict | undefined {
|
|
71
79
|
if (this.options.patch) return undefined;
|
|
72
80
|
|
|
73
|
-
|
|
74
|
-
if (absence === null) return { message: InputRefusal.required };
|
|
75
|
-
|
|
76
|
-
return absence === 'empty-list' ? { value: [] } : undefined;
|
|
81
|
+
return this.requires(field) ? { message: InputRefusal.required } : undefined;
|
|
77
82
|
}
|
|
78
83
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Field } from '../../field/Field.js';
|
|
1
|
+
import { Field, type Described } from '../../field/Field.js';
|
|
2
2
|
import { date } from '../primitive/date.js';
|
|
3
3
|
|
|
4
|
-
export function created(): Field<Date> {
|
|
5
|
-
return date().with({
|
|
4
|
+
export function created(opts?: Described): Field<Date> {
|
|
5
|
+
return date(opts).with({
|
|
6
6
|
lifecycle: { create: 'now', update: 'forbidden' },
|
|
7
7
|
});
|
|
8
8
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { Field, type Described } from '../../field/Field.js';
|
|
1
2
|
import { created } from './created.js';
|
|
2
|
-
import { Field } from '../../field/Field.js';
|
|
3
3
|
|
|
4
|
-
export function updated(): Field<Date> {
|
|
5
|
-
const base = created();
|
|
4
|
+
export function updated(opts?: Described): Field<Date> {
|
|
5
|
+
const base = created(opts);
|
|
6
|
+
|
|
6
7
|
return base.with({ lifecycle: { ...base.lifecycle, update: 'now' } });
|
|
7
8
|
}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import { Field } from '../../field/Field.js';
|
|
1
|
+
import { Field, type Shared } from '../../field/Field.js';
|
|
2
2
|
|
|
3
|
-
export function bool(opts?:
|
|
4
|
-
return new Field<boolean>({
|
|
5
|
-
shape: { type: 'boolean' },
|
|
6
|
-
lifecycle:
|
|
7
|
-
opts?.default !== undefined ? { create: { value: opts.default } } : undefined,
|
|
8
|
-
meta: opts?.description !== undefined ? { description: opts.description } : undefined,
|
|
9
|
-
});
|
|
3
|
+
export function bool(opts?: Shared<boolean>): Field<boolean> {
|
|
4
|
+
return new Field<boolean>({ shape: { type: 'boolean' } }).setShared(opts);
|
|
10
5
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Field } from '../../field/Field.js';
|
|
1
|
+
import { Field, type Shared } from '../../field/Field.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A `Date` in a handler, an ISO string on the wire — the `isoDate` boundary converts.
|
|
5
|
-
* FR : un `Date` dans un handler, une chaîne ISO sur le fil — `isoDate` convertit.
|
|
6
5
|
* `date()` → `{ type: 'string', format: 'date-time' }`
|
|
7
6
|
*/
|
|
8
|
-
export function date(): Field<Date> {
|
|
9
|
-
return new Field<Date>({ shape: { type: 'string', format: 'date-time' } })
|
|
7
|
+
export function date(opts?: Shared<Date>): Field<Date> {
|
|
8
|
+
return new Field<Date>({ shape: { type: 'string', format: 'date-time' } }).setShared(
|
|
9
|
+
opts,
|
|
10
|
+
);
|
|
10
11
|
}
|
|
@@ -1,29 +1,44 @@
|
|
|
1
|
-
import { Field } from '../../field/Field.js';
|
|
1
|
+
import { Field, type Shared } from '../../field/Field.js';
|
|
2
2
|
import type { SchemaView } from '../../SchemaView.js';
|
|
3
3
|
import { InputValidator } from '../../validator/InputValidator.js';
|
|
4
|
+
import { SchemaError } from '../../SchemaError.js';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
type Entity = SchemaView & (new (...args: never[]) => unknown);
|
|
7
|
+
|
|
8
|
+
export function json<T = unknown>(opts?: Shared<T>): Field<T>;
|
|
6
9
|
export function json<E extends SchemaView & (new (...args: any[]) => any)>(
|
|
7
10
|
of: E,
|
|
11
|
+
opts?: Shared<InstanceType<E>>,
|
|
8
12
|
): Field<InstanceType<E>>;
|
|
13
|
+
|
|
9
14
|
/**
|
|
10
|
-
* `json(Address)` where the object has a shape; `json()` alone admits any shape forever.
|
|
11
|
-
* FR : `json(Address)` quand l'objet a une forme ; `json()` seul admet tout, à jamais.
|
|
12
15
|
* `json(Address)` → the entity's properties, and its required keys
|
|
13
16
|
*/
|
|
14
|
-
export function json(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
export function json(
|
|
18
|
+
of?: Entity | Shared<unknown>,
|
|
19
|
+
opts?: Shared<unknown>,
|
|
20
|
+
): Field<unknown> {
|
|
21
|
+
const [schema, shared] = typeof of === 'function' ? [of, opts] : [undefined, of];
|
|
22
|
+
|
|
23
|
+
if (schema && typeof schema.getFields !== 'function')
|
|
24
|
+
throw new SchemaError(
|
|
25
|
+
'json() takes an entity, such as json(Address) — got a function that is not one',
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (!schema) return new Field({ shape: { type: 'object' } }).setShared(shared);
|
|
29
|
+
|
|
30
|
+
const fields = schema.getFields();
|
|
17
31
|
const validator = InputValidator.of(fields);
|
|
18
32
|
const properties: Record<string, unknown> = {};
|
|
19
33
|
const required: string[] = [];
|
|
34
|
+
|
|
20
35
|
for (const [key, field] of Object.entries(fields)) {
|
|
21
|
-
properties[key] = field.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (validator.onAbsent(field) === null) required.push(key);
|
|
36
|
+
properties[key] = field.shape;
|
|
37
|
+
|
|
38
|
+
if (validator.requires(field)) required.push(key);
|
|
25
39
|
}
|
|
40
|
+
|
|
26
41
|
return new Field({
|
|
27
42
|
shape: { type: 'object', properties, ...(required.length ? { required } : {}) },
|
|
28
|
-
});
|
|
43
|
+
}).setShared(shared);
|
|
29
44
|
}
|
|
@@ -1,22 +1,29 @@
|
|
|
1
|
-
import { Field } from '../../field/Field.js';
|
|
1
|
+
import { Field, type Shared } from '../../field/Field.js';
|
|
2
2
|
import { SchemaError } from '../../SchemaError.js';
|
|
3
3
|
|
|
4
|
-
export interface ListOptions {
|
|
4
|
+
export interface ListOptions<T = unknown> extends Shared<T[]> {
|
|
5
5
|
min?: number;
|
|
6
6
|
max?: number;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* A list of
|
|
11
|
-
*
|
|
10
|
+
* A list of values. It keeps the shape of the item and nothing else: `nullable()` and a
|
|
11
|
+
* `description` write in the shape, so they work. A rule writes beside it, and an item of a
|
|
12
|
+
* list has no key, no column and no name to keep it on.
|
|
12
13
|
* `list(text(), { max: 5 })` → `{ type: 'array', items: { type: 'string' }, maxItems: 5 }`
|
|
13
14
|
*/
|
|
14
|
-
export function list<T>(
|
|
15
|
-
if (
|
|
15
|
+
export function list<T>(field: Field<T>, opts?: ListOptions<T>): Field<T[]> {
|
|
16
|
+
if (field.hasAxes())
|
|
16
17
|
throw new SchemaError(
|
|
17
|
-
'list() takes a value
|
|
18
|
+
'list() takes a value, not a rule. Put the rule on the list: unique(list(text())).',
|
|
18
19
|
);
|
|
20
|
+
|
|
19
21
|
return new Field<T[]>({
|
|
20
|
-
shape: {
|
|
21
|
-
|
|
22
|
+
shape: {
|
|
23
|
+
type: 'array',
|
|
24
|
+
items: field.shape,
|
|
25
|
+
minItems: opts?.min,
|
|
26
|
+
maxItems: opts?.max,
|
|
27
|
+
},
|
|
28
|
+
}).setShared(opts);
|
|
22
29
|
}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { type EntityConstructor } from '../../axis/role/EntityConstructor.js';
|
|
2
2
|
import { Relation } from '../../axis/role/Relation.js';
|
|
3
|
-
import { Field } from '../../field/Field.js';
|
|
3
|
+
import { Field, type Described } from '../../field/Field.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* The far side of a `ref()`: no column of its own, and absent from what a client sends.
|
|
7
|
-
* FR : l'autre côté d'un `ref()` : aucune colonne à lui, et absent de ce qu'un client envoie.
|
|
8
6
|
* `many(() => Post)` → `{ type: 'array' }` with `role.relation.kind` `'many'`
|
|
9
7
|
*/
|
|
10
8
|
export function many<E extends EntityConstructor>(
|
|
11
9
|
target: E | (() => E),
|
|
10
|
+
opts?: Described,
|
|
12
11
|
): Field<InstanceType<E>[]> {
|
|
13
12
|
return new Field<InstanceType<E>[]>({
|
|
14
13
|
shape: { type: 'array' },
|
|
15
14
|
role: { relation: Relation.many(target) },
|
|
16
|
-
});
|
|
15
|
+
}).setShared(opts);
|
|
17
16
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { Field } from '../../field/Field.js';
|
|
1
|
+
import { Field, type Shared } from '../../field/Field.js';
|
|
2
2
|
|
|
3
|
-
export interface NumberOptions {
|
|
3
|
+
export interface NumberOptions extends Shared<number> {
|
|
4
4
|
min?: number;
|
|
5
5
|
max?: number;
|
|
6
6
|
integer?: boolean;
|
|
7
|
-
default?: number;
|
|
8
|
-
description?: string;
|
|
9
7
|
}
|
|
10
8
|
|
|
11
9
|
export function number(opts?: NumberOptions): Field<number> {
|
|
@@ -15,8 +13,5 @@ export function number(opts?: NumberOptions): Field<number> {
|
|
|
15
13
|
minimum: opts?.min,
|
|
16
14
|
maximum: opts?.max,
|
|
17
15
|
},
|
|
18
|
-
|
|
19
|
-
opts?.default !== undefined ? { create: { value: opts.default } } : undefined,
|
|
20
|
-
meta: opts?.description !== undefined ? { description: opts.description } : undefined,
|
|
21
|
-
});
|
|
16
|
+
}).setShared(opts);
|
|
22
17
|
}
|