@fougere/schema 0.5.0-alpha.1 → 0.7.0-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/README.md +5 -4
- package/dist/{schema/Schema.d.ts → Schema.d.ts} +18 -28
- package/dist/Schema.d.ts.map +1 -0
- package/dist/Schema.js +136 -0
- package/dist/Schema.js.map +1 -0
- package/dist/SchemaDefinition.d.ts +73 -0
- package/dist/SchemaDefinition.d.ts.map +1 -0
- package/dist/SchemaDefinition.js +184 -0
- package/dist/SchemaDefinition.js.map +1 -0
- package/dist/SchemaDerivation.d.ts +16 -0
- package/dist/SchemaDerivation.d.ts.map +1 -0
- package/dist/SchemaDerivation.js +31 -0
- package/dist/SchemaDerivation.js.map +1 -0
- package/dist/{schema/SchemaView.d.ts → SchemaView.d.ts} +9 -9
- package/dist/SchemaView.d.ts.map +1 -0
- package/dist/{schema/SchemaView.js.map → SchemaView.js.map} +1 -1
- package/dist/axis/Axis.d.ts +2 -2
- package/dist/axis/Axis.d.ts.map +1 -1
- package/dist/axis/boundary/Boundaries.d.ts +13 -12
- package/dist/axis/boundary/Boundaries.d.ts.map +1 -1
- package/dist/axis/boundary/Boundaries.js +18 -24
- package/dist/axis/boundary/Boundaries.js.map +1 -1
- package/dist/axis/boundary/Boundary.d.ts +3 -1
- package/dist/axis/boundary/Boundary.d.ts.map +1 -1
- package/dist/axis/boundary/Boundary.js +16 -7
- package/dist/axis/boundary/Boundary.js.map +1 -1
- package/dist/axis/boundary/BoundaryAxis.js +4 -4
- package/dist/axis/boundary/BoundaryAxis.js.map +1 -1
- package/dist/axis/lifecycle/Clock.d.ts +1 -1
- package/dist/axis/lifecycle/Clock.d.ts.map +1 -1
- package/dist/axis/lifecycle/Clock.js +1 -1
- package/dist/axis/lifecycle/Clock.js.map +1 -1
- package/dist/axis/lifecycle/Generators.d.ts +6 -14
- package/dist/axis/lifecycle/Generators.d.ts.map +1 -1
- package/dist/axis/lifecycle/Generators.js +10 -28
- package/dist/axis/lifecycle/Generators.js.map +1 -1
- package/dist/axis/lifecycle/LifecycleAxis.js +11 -6
- package/dist/axis/lifecycle/LifecycleAxis.js.map +1 -1
- package/dist/axis/lifecycle/apply.d.ts +14 -5
- package/dist/axis/lifecycle/apply.d.ts.map +1 -1
- package/dist/axis/lifecycle/apply.js +26 -10
- package/dist/axis/lifecycle/apply.js.map +1 -1
- package/dist/axis/role/Relation.d.ts +3 -2
- package/dist/axis/role/Relation.d.ts.map +1 -1
- package/dist/axis/role/Relation.js +6 -5
- package/dist/axis/role/Relation.js.map +1 -1
- package/dist/axis/role/Role.d.ts +6 -6
- package/dist/axis/role/Role.d.ts.map +1 -1
- package/dist/axis/role/Role.js +13 -17
- package/dist/axis/role/Role.js.map +1 -1
- package/dist/axis/role/RoleAxis.d.ts +1 -1
- package/dist/axis/role/RoleAxis.d.ts.map +1 -1
- package/dist/axis/role/RoleAxis.js +28 -37
- package/dist/axis/role/RoleAxis.js.map +1 -1
- package/dist/axis/shape/Formats.d.ts +8 -5
- package/dist/axis/shape/Formats.d.ts.map +1 -1
- package/dist/axis/shape/Formats.js +8 -9
- package/dist/axis/shape/Formats.js.map +1 -1
- package/dist/axis/shape/Shape.d.ts +5 -3
- package/dist/axis/shape/Shape.d.ts.map +1 -1
- package/dist/axis/shape/Shape.js +13 -13
- package/dist/axis/shape/Shape.js.map +1 -1
- package/dist/entity/EntityAdapterSet.d.ts +22 -0
- package/dist/entity/EntityAdapterSet.d.ts.map +1 -0
- package/dist/entity/EntityAdapterSet.js +63 -0
- package/dist/entity/EntityAdapterSet.js.map +1 -0
- package/dist/entity/EntityAdapters.d.ts +5 -0
- package/dist/entity/EntityAdapters.d.ts.map +1 -0
- package/dist/entity/EntityAdapters.js +2 -0
- package/dist/entity/EntityAdapters.js.map +1 -0
- package/dist/{EntityDeclarations.d.ts → entity/EntityDeclarations.d.ts} +2 -7
- package/dist/entity/EntityDeclarations.d.ts.map +1 -0
- package/dist/entity/EntityDeclarations.js.map +1 -0
- package/dist/entity.d.ts +8 -3
- package/dist/entity.d.ts.map +1 -1
- package/dist/entity.js +6 -1
- package/dist/entity.js.map +1 -1
- package/dist/{fields → field}/Field.d.ts +8 -9
- package/dist/field/Field.d.ts.map +1 -0
- package/dist/field/Field.js +35 -0
- package/dist/field/Field.js.map +1 -0
- package/dist/field/FieldSet.d.ts +22 -0
- package/dist/field/FieldSet.d.ts.map +1 -0
- package/dist/field/FieldSet.js +58 -0
- package/dist/field/FieldSet.js.map +1 -0
- package/dist/index.d.ts +24 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -21
- package/dist/index.js.map +1 -1
- package/dist/lib/Registry.d.ts +12 -0
- package/dist/lib/Registry.d.ts.map +1 -0
- package/dist/lib/Registry.js +29 -0
- package/dist/lib/Registry.js.map +1 -0
- package/dist/{utils.d.ts → lib/utils.d.ts} +1 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/{utils.js → lib/utils.js} +1 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/projection/Cases.d.ts +39 -0
- package/dist/projection/Cases.d.ts.map +1 -0
- package/dist/projection/Cases.js +146 -0
- package/dist/projection/Cases.js.map +1 -0
- package/dist/projection/Visibility.d.ts +2 -1
- package/dist/projection/Visibility.d.ts.map +1 -1
- package/dist/projection/Visibility.js +1 -0
- package/dist/projection/Visibility.js.map +1 -1
- package/dist/{card → projection/card}/Bundle.d.ts +1 -1
- package/dist/projection/card/Bundle.d.ts.map +1 -0
- package/dist/{card → projection/card}/Bundle.js +7 -7
- package/dist/projection/card/Bundle.js.map +1 -0
- package/dist/projection/card/Card.d.ts +21 -0
- package/dist/projection/card/Card.d.ts.map +1 -0
- package/dist/{card → projection/card}/Card.js +119 -58
- package/dist/projection/card/Card.js.map +1 -0
- package/dist/{card → projection/card}/Descriptor.d.ts +7 -7
- package/dist/projection/card/Descriptor.d.ts.map +1 -0
- package/dist/projection/card/Descriptor.js.map +1 -0
- package/dist/projection/card/admission.d.ts +20 -0
- package/dist/projection/card/admission.d.ts.map +1 -0
- package/dist/projection/card/admission.js +27 -0
- package/dist/projection/card/admission.js.map +1 -0
- package/dist/{card → projection/card}/diff.d.ts +3 -3
- package/dist/projection/card/diff.d.ts.map +1 -0
- package/dist/{card → projection/card}/diff.js.map +1 -1
- package/dist/{judge/result.d.ts → validation.d.ts} +1 -1
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +2 -0
- package/dist/validation.js.map +1 -0
- package/dist/validator/AdapterFieldValidator.d.ts +14 -0
- package/dist/validator/AdapterFieldValidator.d.ts.map +1 -0
- package/dist/validator/AdapterFieldValidator.js +36 -0
- package/dist/validator/AdapterFieldValidator.js.map +1 -0
- package/dist/validator/FieldDeclarationValidator.d.ts +9 -0
- package/dist/validator/FieldDeclarationValidator.d.ts.map +1 -0
- package/dist/{judge/FieldJudge.js → validator/FieldDeclarationValidator.js} +13 -13
- package/dist/validator/FieldDeclarationValidator.js.map +1 -0
- package/dist/validator/FieldValueValidator.d.ts +13 -0
- package/dist/validator/FieldValueValidator.d.ts.map +1 -0
- package/dist/{judge/ValueJudge.js → validator/FieldValueValidator.js} +14 -11
- package/dist/validator/FieldValueValidator.js.map +1 -0
- package/dist/{judge/RowRefusal.d.ts → validator/InputRefusal.d.ts} +3 -4
- package/dist/validator/InputRefusal.d.ts.map +1 -0
- package/dist/{judge/RowRefusal.js → validator/InputRefusal.js} +3 -3
- package/dist/validator/InputRefusal.js.map +1 -0
- package/dist/validator/InputValidator.d.ts +15 -0
- package/dist/validator/InputValidator.d.ts.map +1 -0
- package/dist/{judge/RowJudge.js → validator/InputValidator.js} +21 -17
- package/dist/validator/InputValidator.js.map +1 -0
- package/dist/vocabulary/bool.d.ts +6 -1
- package/dist/vocabulary/bool.d.ts.map +1 -1
- package/dist/vocabulary/bool.js +6 -1
- package/dist/vocabulary/bool.js.map +1 -1
- package/dist/vocabulary/created.d.ts +6 -1
- package/dist/vocabulary/created.d.ts.map +1 -1
- package/dist/vocabulary/created.js +6 -1
- package/dist/vocabulary/created.js.map +1 -1
- package/dist/vocabulary/date.d.ts +6 -1
- package/dist/vocabulary/date.d.ts.map +1 -1
- package/dist/vocabulary/date.js +6 -1
- package/dist/vocabulary/date.js.map +1 -1
- package/dist/vocabulary/email.d.ts +6 -1
- package/dist/vocabulary/email.d.ts.map +1 -1
- package/dist/vocabulary/email.js +5 -0
- package/dist/vocabulary/email.js.map +1 -1
- package/dist/vocabulary/immutable.d.ts +6 -1
- package/dist/vocabulary/immutable.d.ts.map +1 -1
- package/dist/vocabulary/immutable.js +5 -0
- package/dist/vocabulary/immutable.js.map +1 -1
- package/dist/vocabulary/indexed.d.ts +6 -1
- package/dist/vocabulary/indexed.d.ts.map +1 -1
- package/dist/vocabulary/indexed.js +8 -1
- package/dist/vocabulary/indexed.js.map +1 -1
- package/dist/vocabulary/json.d.ts +2 -2
- package/dist/vocabulary/json.d.ts.map +1 -1
- package/dist/vocabulary/json.js +9 -4
- package/dist/vocabulary/json.js.map +1 -1
- package/dist/vocabulary/list.d.ts +6 -1
- package/dist/vocabulary/list.d.ts.map +1 -1
- package/dist/vocabulary/list.js +6 -1
- package/dist/vocabulary/list.js.map +1 -1
- package/dist/vocabulary/many.d.ts +6 -1
- package/dist/vocabulary/many.d.ts.map +1 -1
- package/dist/vocabulary/many.js +6 -1
- package/dist/vocabulary/many.js.map +1 -1
- package/dist/vocabulary/nullable.d.ts +6 -1
- package/dist/vocabulary/nullable.d.ts.map +1 -1
- package/dist/vocabulary/nullable.js +7 -2
- package/dist/vocabulary/nullable.js.map +1 -1
- package/dist/vocabulary/number.d.ts +6 -1
- package/dist/vocabulary/number.d.ts.map +1 -1
- package/dist/vocabulary/number.js +11 -2
- package/dist/vocabulary/number.js.map +1 -1
- package/dist/vocabulary/oneOf.d.ts +1 -1
- package/dist/vocabulary/oneOf.d.ts.map +1 -1
- package/dist/vocabulary/oneOf.js +6 -1
- package/dist/vocabulary/oneOf.js.map +1 -1
- package/dist/vocabulary/optional.d.ts +6 -1
- package/dist/vocabulary/optional.d.ts.map +1 -1
- package/dist/vocabulary/optional.js +10 -3
- package/dist/vocabulary/optional.js.map +1 -1
- package/dist/vocabulary/primary.d.ts +1 -1
- package/dist/vocabulary/primary.d.ts.map +1 -1
- package/dist/vocabulary/primary.js +10 -5
- package/dist/vocabulary/primary.js.map +1 -1
- package/dist/vocabulary/readOnly.d.ts +6 -1
- package/dist/vocabulary/readOnly.d.ts.map +1 -1
- package/dist/vocabulary/readOnly.js +5 -0
- package/dist/vocabulary/readOnly.js.map +1 -1
- package/dist/vocabulary/ref.d.ts +6 -1
- package/dist/vocabulary/ref.d.ts.map +1 -1
- package/dist/vocabulary/ref.js +6 -1
- package/dist/vocabulary/ref.js.map +1 -1
- package/dist/vocabulary/text.d.ts +6 -1
- package/dist/vocabulary/text.d.ts.map +1 -1
- package/dist/vocabulary/text.js +13 -2
- package/dist/vocabulary/text.js.map +1 -1
- package/dist/vocabulary/unique.d.ts +6 -1
- package/dist/vocabulary/unique.d.ts.map +1 -1
- package/dist/vocabulary/unique.js +8 -4
- package/dist/vocabulary/unique.js.map +1 -1
- package/dist/vocabulary/updated.d.ts +6 -1
- package/dist/vocabulary/updated.d.ts.map +1 -1
- package/dist/vocabulary/updated.js +5 -0
- package/dist/vocabulary/updated.js.map +1 -1
- package/dist/vocabulary/url.d.ts +6 -1
- package/dist/vocabulary/url.d.ts.map +1 -1
- package/dist/vocabulary/url.js +5 -0
- package/dist/vocabulary/url.js.map +1 -1
- package/dist/vocabulary/vocabulary.d.ts +6 -1
- package/dist/vocabulary/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary/vocabulary.js +21 -11
- package/dist/vocabulary/vocabulary.js.map +1 -1
- package/dist/vocabulary/writeOnly.d.ts +6 -1
- package/dist/vocabulary/writeOnly.d.ts.map +1 -1
- package/dist/vocabulary/writeOnly.js +5 -0
- package/dist/vocabulary/writeOnly.js.map +1 -1
- package/package.json +1 -1
- package/src/Schema.ts +208 -0
- package/src/SchemaDefinition.ts +257 -0
- package/src/SchemaDerivation.ts +44 -0
- package/src/{schema/SchemaView.ts → SchemaView.ts} +9 -9
- package/src/axis/Axis.ts +2 -2
- package/src/axis/boundary/Boundaries.ts +25 -32
- package/src/axis/boundary/Boundary.ts +23 -10
- package/src/axis/boundary/BoundaryAxis.ts +4 -4
- package/src/axis/lifecycle/Clock.ts +1 -1
- package/src/axis/lifecycle/Generators.ts +12 -29
- package/src/axis/lifecycle/LifecycleAxis.ts +12 -7
- package/src/axis/lifecycle/apply.ts +30 -15
- package/src/axis/role/Relation.ts +6 -5
- package/src/axis/role/Role.ts +16 -20
- package/src/axis/role/RoleAxis.ts +28 -41
- package/src/axis/shape/Formats.ts +9 -11
- package/src/axis/shape/Shape.ts +18 -18
- package/src/entity/EntityAdapterSet.ts +81 -0
- package/src/entity/EntityAdapters.ts +8 -0
- package/src/entity/EntityDeclarations.ts +16 -0
- package/src/entity.ts +8 -3
- package/src/field/Field.ts +61 -0
- package/src/field/FieldSet.ts +75 -0
- package/src/index.ts +50 -28
- package/src/lib/Registry.ts +37 -0
- package/src/{utils.ts → lib/utils.ts} +3 -0
- package/src/projection/Cases.ts +176 -0
- package/src/projection/Visibility.ts +6 -2
- package/src/{card → projection/card}/Bundle.ts +9 -9
- package/src/projection/card/Card.ts +342 -0
- package/src/{card → projection/card}/Descriptor.ts +7 -7
- package/src/projection/card/admission.ts +40 -0
- package/src/{card → projection/card}/diff.ts +3 -3
- package/src/validator/AdapterFieldValidator.ts +40 -0
- package/src/{judge/FieldJudge.ts → validator/FieldDeclarationValidator.ts} +15 -15
- package/src/{judge/ValueJudge.ts → validator/FieldValueValidator.ts} +18 -13
- package/src/{judge/RowRefusal.ts → validator/InputRefusal.ts} +2 -4
- package/src/{judge/RowJudge.ts → validator/InputValidator.ts} +27 -20
- package/src/vocabulary/bool.ts +8 -2
- package/src/vocabulary/created.ts +7 -2
- package/src/vocabulary/date.ts +6 -1
- package/src/vocabulary/email.ts +6 -1
- package/src/vocabulary/immutable.ts +12 -4
- package/src/vocabulary/indexed.ts +9 -2
- package/src/vocabulary/json.ts +13 -6
- package/src/vocabulary/list.ts +10 -2
- package/src/vocabulary/many.ts +9 -2
- package/src/vocabulary/nullable.ts +13 -5
- package/src/vocabulary/number.ts +13 -3
- package/src/vocabulary/oneOf.ts +11 -3
- package/src/vocabulary/optional.ts +16 -6
- package/src/vocabulary/primary.ts +10 -5
- package/src/vocabulary/readOnly.ts +12 -4
- package/src/vocabulary/ref.ts +10 -2
- package/src/vocabulary/text.ts +15 -3
- package/src/vocabulary/unique.ts +10 -9
- package/src/vocabulary/updated.ts +6 -1
- package/src/vocabulary/url.ts +6 -1
- package/src/vocabulary/vocabulary.ts +21 -12
- package/src/vocabulary/writeOnly.ts +12 -4
- package/dist/EntityAdapters.d.ts +0 -28
- package/dist/EntityAdapters.d.ts.map +0 -1
- package/dist/EntityAdapters.js +0 -75
- package/dist/EntityAdapters.js.map +0 -1
- package/dist/EntityDeclarations.d.ts.map +0 -1
- package/dist/EntityDeclarations.js.map +0 -1
- package/dist/card/Bundle.d.ts.map +0 -1
- package/dist/card/Bundle.js.map +0 -1
- package/dist/card/Card.d.ts +0 -27
- package/dist/card/Card.d.ts.map +0 -1
- package/dist/card/Card.js.map +0 -1
- package/dist/card/Descriptor.d.ts.map +0 -1
- package/dist/card/Descriptor.js.map +0 -1
- package/dist/card/EntityTypeSource.d.ts +0 -12
- package/dist/card/EntityTypeSource.d.ts.map +0 -1
- package/dist/card/EntityTypeSource.js +0 -78
- package/dist/card/EntityTypeSource.js.map +0 -1
- package/dist/card/FacadeTypeSource.d.ts +0 -19
- package/dist/card/FacadeTypeSource.d.ts.map +0 -1
- package/dist/card/FacadeTypeSource.js +0 -40
- package/dist/card/FacadeTypeSource.js.map +0 -1
- package/dist/card/admission.d.ts +0 -9
- package/dist/card/admission.d.ts.map +0 -1
- package/dist/card/admission.js +0 -16
- package/dist/card/admission.js.map +0 -1
- package/dist/card/diff.d.ts.map +0 -1
- package/dist/constraint/FieldGroup.d.ts +0 -21
- package/dist/constraint/FieldGroup.d.ts.map +0 -1
- package/dist/constraint/FieldGroup.js +0 -65
- package/dist/constraint/FieldGroup.js.map +0 -1
- package/dist/constraint/Unique.d.ts +0 -4
- package/dist/constraint/Unique.d.ts.map +0 -1
- package/dist/constraint/Unique.js +0 -4
- package/dist/constraint/Unique.js.map +0 -1
- package/dist/fields/Field.d.ts.map +0 -1
- package/dist/fields/Field.js +0 -52
- package/dist/fields/Field.js.map +0 -1
- package/dist/fields/FieldSet.d.ts +0 -28
- package/dist/fields/FieldSet.d.ts.map +0 -1
- package/dist/fields/FieldSet.js +0 -55
- package/dist/fields/FieldSet.js.map +0 -1
- package/dist/judge/Cases.d.ts +0 -50
- package/dist/judge/Cases.d.ts.map +0 -1
- package/dist/judge/Cases.js +0 -108
- package/dist/judge/Cases.js.map +0 -1
- package/dist/judge/FieldJudge.d.ts +0 -9
- package/dist/judge/FieldJudge.d.ts.map +0 -1
- package/dist/judge/FieldJudge.js.map +0 -1
- package/dist/judge/RowJudge.d.ts +0 -12
- package/dist/judge/RowJudge.d.ts.map +0 -1
- package/dist/judge/RowJudge.js.map +0 -1
- package/dist/judge/RowRefusal.d.ts.map +0 -1
- package/dist/judge/RowRefusal.js.map +0 -1
- package/dist/judge/ValueForm.d.ts +0 -3
- package/dist/judge/ValueForm.d.ts.map +0 -1
- package/dist/judge/ValueForm.js +0 -3
- package/dist/judge/ValueForm.js.map +0 -1
- package/dist/judge/ValueJudge.d.ts +0 -12
- package/dist/judge/ValueJudge.d.ts.map +0 -1
- package/dist/judge/ValueJudge.js.map +0 -1
- package/dist/judge/options.d.ts +0 -4
- package/dist/judge/options.d.ts.map +0 -1
- package/dist/judge/options.js +0 -2
- package/dist/judge/options.js.map +0 -1
- package/dist/judge/result.d.ts.map +0 -1
- package/dist/judge/result.js +0 -2
- package/dist/judge/result.js.map +0 -1
- package/dist/projection/SchemaOrCard.d.ts +0 -7
- package/dist/projection/SchemaOrCard.d.ts.map +0 -1
- package/dist/projection/SchemaOrCard.js +0 -11
- package/dist/projection/SchemaOrCard.js.map +0 -1
- package/dist/schema/Schema.d.ts.map +0 -1
- package/dist/schema/Schema.js +0 -150
- package/dist/schema/Schema.js.map +0 -1
- package/dist/schema/SchemaDefinition.d.ts +0 -60
- package/dist/schema/SchemaDefinition.d.ts.map +0 -1
- package/dist/schema/SchemaDefinition.js +0 -85
- package/dist/schema/SchemaDefinition.js.map +0 -1
- package/dist/schema/SchemaDerivation.d.ts +0 -34
- package/dist/schema/SchemaDerivation.d.ts.map +0 -1
- package/dist/schema/SchemaDerivation.js +0 -46
- package/dist/schema/SchemaDerivation.js.map +0 -1
- package/dist/schema/SchemaView.d.ts.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js.map +0 -1
- package/src/EntityAdapters.ts +0 -100
- package/src/EntityDeclarations.ts +0 -19
- package/src/card/Card.ts +0 -256
- package/src/card/EntityTypeSource.ts +0 -90
- package/src/card/FacadeTypeSource.ts +0 -55
- package/src/card/admission.ts +0 -25
- package/src/constraint/FieldGroup.ts +0 -91
- package/src/constraint/Unique.ts +0 -3
- package/src/fields/Field.ts +0 -80
- package/src/fields/FieldSet.ts +0 -62
- package/src/judge/Cases.ts +0 -139
- package/src/judge/ValueForm.ts +0 -7
- package/src/judge/options.ts +0 -3
- package/src/projection/SchemaOrCard.ts +0 -18
- package/src/schema/Schema.ts +0 -197
- package/src/schema/SchemaDefinition.ts +0 -120
- package/src/schema/SchemaDerivation.ts +0 -57
- /package/dist/{schema/SchemaView.js → SchemaView.js} +0 -0
- /package/dist/{EntityDeclarations.js → entity/EntityDeclarations.js} +0 -0
- /package/dist/{card → projection/card}/Descriptor.js +0 -0
- /package/dist/{card → projection/card}/diff.js +0 -0
- /package/src/{judge/result.ts → validation.ts} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Validator, format as engineFormats } from '@cfworker/json-schema';
|
|
2
2
|
import { Formats, type FormatPredicate } from '../axis/shape/Formats.js';
|
|
3
|
-
import {
|
|
4
|
-
import type { Field } from '../
|
|
5
|
-
import type { Checked } from '
|
|
3
|
+
import { Shapes, type Shape } from '../axis/shape/Shape.js';
|
|
4
|
+
import type { Field } from '../field/Field.js';
|
|
5
|
+
import type { Checked } from '../validation.js';
|
|
6
6
|
|
|
7
7
|
interface ShapePlan {
|
|
8
8
|
validator: Validator;
|
|
@@ -10,21 +10,26 @@ interface ShapePlan {
|
|
|
10
10
|
formatName?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export class
|
|
13
|
+
export class FieldValueValidator {
|
|
14
14
|
private static readonly plans = new WeakMap<object, ShapePlan>();
|
|
15
15
|
|
|
16
16
|
private constructor(private readonly field: Field) {}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
/** The shape is compiled once and reused, not rebuilt per value. */
|
|
19
|
+
static of(field: Field): FieldValueValidator {
|
|
20
|
+
return new FieldValueValidator(field);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
validate(value: unknown): Checked {
|
|
23
24
|
const shape = this.field.shape;
|
|
24
|
-
const base =
|
|
25
|
+
const base = Shapes.of(shape).base;
|
|
25
26
|
if (value !== null) {
|
|
26
27
|
if (base?.type === 'object' && !base.properties) return { value };
|
|
27
|
-
if (
|
|
28
|
+
if (
|
|
29
|
+
base?.type === 'string' &&
|
|
30
|
+
base.format === 'date-time' &&
|
|
31
|
+
value instanceof Date
|
|
32
|
+
) {
|
|
28
33
|
return Number.isNaN(value.getTime()) ? { error: 'Invalid date' } : { value };
|
|
29
34
|
}
|
|
30
35
|
if (
|
|
@@ -35,7 +40,7 @@ export class ValueJudge {
|
|
|
35
40
|
return { error: 'Expected a number' };
|
|
36
41
|
}
|
|
37
42
|
}
|
|
38
|
-
const plan =
|
|
43
|
+
const plan = FieldValueValidator.planFor(shape);
|
|
39
44
|
const result = plan.validator.validate(value);
|
|
40
45
|
if (!result.valid) return { error: result.errors[0]?.error ?? 'Invalid value' };
|
|
41
46
|
if (plan.custom && typeof value === 'string' && !plan.custom(value)) {
|
|
@@ -47,7 +52,7 @@ export class ValueJudge {
|
|
|
47
52
|
private static planFor(shape: Shape): ShapePlan {
|
|
48
53
|
let plan = this.plans.get(shape);
|
|
49
54
|
if (!plan) {
|
|
50
|
-
const base =
|
|
55
|
+
const base = Shapes.of(shape).base;
|
|
51
56
|
const formatName = base?.type === 'string' ? base.format : undefined;
|
|
52
57
|
plan = {
|
|
53
58
|
validator: new Validator(shape as object, '2020-12', true),
|
|
@@ -60,11 +65,11 @@ export class ValueJudge {
|
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
private static customFormatOf(name: string): FormatPredicate | undefined {
|
|
63
|
-
const custom = Formats.
|
|
68
|
+
const custom = Formats.find(name);
|
|
64
69
|
if (!custom && !(name in engineFormats)) {
|
|
65
70
|
throw new Error(
|
|
66
71
|
`Unknown format: '${name}'. Register it with Formats.register('${name}', …) — ` +
|
|
67
|
-
`the engine
|
|
72
|
+
`the engine validates ${Object.keys(engineFormats).length} formats natively and this is not one of them.`,
|
|
68
73
|
);
|
|
69
74
|
}
|
|
70
75
|
return custom;
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A value refused by its own shape carries the engine's message and a decoder refused by
|
|
5
5
|
* user code carries that code's; neither is structural and neither belongs here. These
|
|
6
|
-
* five are `
|
|
6
|
+
* five are `InputValidator`'s own, and a table of cases enumerates them instead of reading the
|
|
7
7
|
* text of a function — which a minifier rewrites without a word.
|
|
8
8
|
*/
|
|
9
|
-
export const
|
|
9
|
+
export const InputRefusal = {
|
|
10
10
|
notAnObject: 'Expected an object',
|
|
11
11
|
unknownField: 'Unknown field',
|
|
12
12
|
required: 'Required',
|
|
13
13
|
readOnly: 'Read-only',
|
|
14
14
|
immutable: 'Immutable',
|
|
15
15
|
} as const;
|
|
16
|
-
|
|
17
|
-
export type RowRefusal = (typeof RowRefusal)[keyof typeof RowRefusal];
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { Boundary } from '../axis/boundary/Boundary.js';
|
|
2
2
|
import { Lifecycle } from '../axis/lifecycle/Lifecycle.js';
|
|
3
3
|
import { Role } from '../axis/role/Role.js';
|
|
4
|
-
import type { Field, Fields } from '../
|
|
5
|
-
import type {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import { RowRefusal } from './RowRefusal.js';
|
|
4
|
+
import type { Field, Fields } from '../field/Field.js';
|
|
5
|
+
import type { ValidationError, ValidationResult } from '../validation.js';
|
|
6
|
+
import { FieldValueValidator } from './FieldValueValidator.js';
|
|
7
|
+
import { InputRefusal } from './InputRefusal.js';
|
|
9
8
|
|
|
10
|
-
export
|
|
9
|
+
export interface ValidateOptions {
|
|
10
|
+
patch?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class InputValidator {
|
|
11
14
|
private constructor(
|
|
12
15
|
private readonly fields: Fields,
|
|
13
16
|
private readonly options: ValidateOptions,
|
|
14
17
|
) {}
|
|
15
18
|
|
|
16
|
-
static of(fields: Fields, options: ValidateOptions = {}):
|
|
17
|
-
return new
|
|
19
|
+
static of(fields: Fields, options: ValidateOptions = {}): InputValidator {
|
|
20
|
+
return new InputValidator(fields, options);
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
onAbsent(field: Field): 'skip' | 'empty-list' | null {
|
|
@@ -24,18 +27,22 @@ export class RowJudge {
|
|
|
24
27
|
return null;
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
/** Hands on the value it PARSED, so a handler never re-checks a row. */
|
|
31
|
+
validate(input: unknown): ValidationResult<Record<string, unknown>> {
|
|
28
32
|
if (typeof input !== 'object' || input === null) {
|
|
29
|
-
return {
|
|
33
|
+
return {
|
|
34
|
+
success: false,
|
|
35
|
+
errors: [{ path: '.', message: InputRefusal.notAnObject }],
|
|
36
|
+
};
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
const data = input as Record<string, unknown>;
|
|
33
40
|
const errors: ValidationError[] = [];
|
|
34
|
-
const
|
|
41
|
+
const row: Record<string, unknown> = {};
|
|
35
42
|
|
|
36
43
|
for (const key of Object.keys(data)) {
|
|
37
44
|
if (!(key in this.fields)) {
|
|
38
|
-
errors.push({ path: key, message:
|
|
45
|
+
errors.push({ path: key, message: InputRefusal.unknownField });
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
|
|
@@ -47,38 +54,38 @@ export class RowJudge {
|
|
|
47
54
|
if (this.options.patch) continue;
|
|
48
55
|
const absence = this.onAbsent(field);
|
|
49
56
|
if (absence === null) {
|
|
50
|
-
errors.push({ path, message:
|
|
57
|
+
errors.push({ path, message: InputRefusal.required });
|
|
51
58
|
continue;
|
|
52
59
|
}
|
|
53
|
-
if (absence === 'empty-list')
|
|
60
|
+
if (absence === 'empty-list') row[key] = [];
|
|
54
61
|
continue;
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
const boundary = Boundary.of(field);
|
|
58
65
|
if (boundary.readOnly) {
|
|
59
|
-
errors.push({ path, message:
|
|
66
|
+
errors.push({ path, message: InputRefusal.readOnly });
|
|
60
67
|
continue;
|
|
61
68
|
}
|
|
62
69
|
if (this.options.patch && Lifecycle.of(field).immutable) {
|
|
63
|
-
errors.push({ path, message:
|
|
70
|
+
errors.push({ path, message: InputRefusal.immutable });
|
|
64
71
|
continue;
|
|
65
72
|
}
|
|
66
73
|
|
|
67
|
-
const checked =
|
|
74
|
+
const checked = FieldValueValidator.of(field).validate(value);
|
|
68
75
|
if ('error' in checked) {
|
|
69
76
|
errors.push({ path, message: checked.error });
|
|
70
77
|
continue;
|
|
71
78
|
}
|
|
72
79
|
if (checked.value === null) {
|
|
73
|
-
|
|
80
|
+
row[key] = null;
|
|
74
81
|
continue;
|
|
75
82
|
}
|
|
76
83
|
const decoded = boundary.decode(checked.value);
|
|
77
84
|
if ('error' in decoded) errors.push({ path, message: decoded.error });
|
|
78
|
-
else
|
|
85
|
+
else row[key] = decoded.value;
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
if (errors.length > 0) return { success: false, errors };
|
|
82
|
-
return { success: true, data:
|
|
89
|
+
return { success: true, data: row };
|
|
83
90
|
}
|
|
84
91
|
}
|
package/src/vocabulary/bool.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { Field } from '../
|
|
1
|
+
import { Field } from '../field/Field.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* So a default is a lifecycle rule, not a value the handler remembers to set.
|
|
5
|
+
* FR : pour qu'un défaut soit une règle de cycle de vie, pas un oubli possible du handler.
|
|
6
|
+
* `bool({ default: false })`
|
|
7
|
+
*/
|
|
3
8
|
export function bool(opts?: { default?: boolean; description?: string }): Field<boolean> {
|
|
4
9
|
return new Field<boolean>({
|
|
5
10
|
shape: { type: 'boolean' },
|
|
6
|
-
lifecycle:
|
|
11
|
+
lifecycle:
|
|
12
|
+
opts?.default !== undefined ? { create: { value: opts.default } } : undefined,
|
|
7
13
|
meta: opts?.description !== undefined ? { description: opts.description } : undefined,
|
|
8
14
|
});
|
|
9
15
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { Field } from '../
|
|
1
|
+
import { Field } from '../field/Field.js';
|
|
2
2
|
import { date } from './date.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* So a creation stamp is written by the storage and refused from a client.
|
|
6
|
+
* FR : pour qu'un horodatage de création soit écrit par le storage et refusé au client.
|
|
7
|
+
* `created()` → `create: 'now'`, `update: 'forbidden'`
|
|
8
|
+
*/
|
|
4
9
|
export function created(): Field<Date> {
|
|
5
10
|
return date().with({
|
|
6
|
-
lifecycle: { create:
|
|
11
|
+
lifecycle: { create: 'now', update: 'forbidden' },
|
|
7
12
|
});
|
|
8
13
|
}
|
package/src/vocabulary/date.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import { Field } from '../
|
|
1
|
+
import { Field } from '../field/Field.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* So a date is a string on the wire and a `Date` in a handler, with no word said.
|
|
5
|
+
* FR : pour qu'une date soit une chaîne sur le fil et une `Date` dans un handler.
|
|
6
|
+
* `date()` → `{ type: 'string', format: 'date-time' }`, decoded by the `isoDate` boundary
|
|
7
|
+
*/
|
|
3
8
|
export function date(): Field<Date> {
|
|
4
9
|
return new Field<Date>({ shape: { type: 'string', format: 'date-time' } });
|
|
5
10
|
}
|
package/src/vocabulary/email.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { text, type TextOptions } from './text.js';
|
|
2
|
-
import type { Field } from '../
|
|
2
|
+
import type { Field } from '../field/Field.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* So the format is stated once, where everyone would otherwise write the same regex.
|
|
6
|
+
* FR : pour que le format soit énoncé une fois, là où chacun réécrirait la même regex.
|
|
7
|
+
* `email({ max: 254 })`
|
|
8
|
+
*/
|
|
4
9
|
export function email(opts?: Omit<TextOptions, 'format'>): Field<string> {
|
|
5
10
|
return text({ ...opts, format: 'email' });
|
|
6
11
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { vocabulary } from './vocabulary.js';
|
|
2
|
-
import { Field } from '../
|
|
2
|
+
import { Field } from '../field/Field.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* So a write-once field is refused on a patch by the validator, not by the table.
|
|
6
|
+
* FR : pour qu'un champ écrit une fois soit refusé par le juge sur une modification.
|
|
7
|
+
* `immutable(text())` → `update: 'forbidden'`
|
|
8
|
+
*/
|
|
9
|
+
export const immutable: <T>(field: Field<T>) => Field<T> = vocabulary(
|
|
10
|
+
'immutable',
|
|
11
|
+
() => ({
|
|
12
|
+
lifecycle: { update: 'forbidden' },
|
|
13
|
+
}),
|
|
14
|
+
);
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { vocabulary } from './vocabulary.js';
|
|
2
|
-
import { Field } from '../
|
|
2
|
+
import { Field } from '../field/Field.js';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* So the entity states the index, and the DDL is the one that emits it.
|
|
6
|
+
* FR : pour que l'entité énonce l'index, le DDL étant celui qui l'émet.
|
|
7
|
+
* `indexed(text())` → `role.index` is `true`
|
|
8
|
+
*/
|
|
9
|
+
export const indexed: <T>(field: Field<T>) => Field<T> = vocabulary('indexed', () => ({
|
|
10
|
+
role: { index: true },
|
|
11
|
+
}));
|
package/src/vocabulary/json.ts
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
|
-
import { Field } from '../
|
|
2
|
-
import type { SchemaView } from '../
|
|
3
|
-
import {
|
|
1
|
+
import { Field } from '../field/Field.js';
|
|
2
|
+
import type { SchemaView } from '../SchemaView.js';
|
|
3
|
+
import { InputValidator } from '../validator/InputValidator.js';
|
|
4
4
|
|
|
5
5
|
export function json<T = unknown>(): Field<T>;
|
|
6
|
-
export function json<E extends SchemaView & (new (...args: any[]) => any)>(
|
|
6
|
+
export function json<E extends SchemaView & (new (...args: any[]) => any)>(
|
|
7
|
+
of: E,
|
|
8
|
+
): Field<InstanceType<E>>;
|
|
9
|
+
/**
|
|
10
|
+
* So a stored object can carry a schema, instead of admitting any shape forever.
|
|
11
|
+
* FR : pour qu'un objet stocké porte un schéma, au lieu d'admettre toute forme à jamais.
|
|
12
|
+
* `json(Address)` → the object's properties and its required keys
|
|
13
|
+
*/
|
|
7
14
|
export function json(of?: SchemaView): Field<unknown> {
|
|
8
15
|
if (!of) return new Field({ shape: { type: 'object' } });
|
|
9
16
|
const fields = of.getFields();
|
|
10
|
-
const
|
|
17
|
+
const validator = InputValidator.of(fields);
|
|
11
18
|
const properties: Record<string, unknown> = {};
|
|
12
19
|
const required: string[] = [];
|
|
13
20
|
for (const [key, field] of Object.entries(fields)) {
|
|
14
21
|
properties[key] = field.meta?.description
|
|
15
22
|
? { ...field.shape, description: field.meta.description }
|
|
16
23
|
: field.shape;
|
|
17
|
-
if (
|
|
24
|
+
if (validator.onAbsent(field) === null) required.push(key);
|
|
18
25
|
}
|
|
19
26
|
return new Field({
|
|
20
27
|
shape: { type: 'object', properties, ...(required.length ? { required } : {}) },
|
package/src/vocabulary/list.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
import { Field } from '../
|
|
1
|
+
import { Field } from '../field/Field.js';
|
|
2
2
|
|
|
3
3
|
export interface ListOptions {
|
|
4
4
|
min?: number;
|
|
5
5
|
max?: number;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* So a list of values is told apart from a relation, which `many()` declares.
|
|
10
|
+
* FR : pour qu'une liste de valeurs se distingue d'une relation, que `many()` déclare.
|
|
11
|
+
* `list(text(), { max: 5 })`; `list(ref(User))` → throws
|
|
12
|
+
*/
|
|
8
13
|
export function list<T>(item: Field<T>, opts?: ListOptions): Field<T[]> {
|
|
9
|
-
if (item.role?.relation)
|
|
14
|
+
if (item.role?.relation)
|
|
15
|
+
throw new Error(
|
|
16
|
+
'list() takes a value field (text(), number()…) — a relation has no value shape',
|
|
17
|
+
);
|
|
10
18
|
return new Field<T[]>({
|
|
11
19
|
shape: { type: 'array', items: item.shape, minItems: opts?.min, maxItems: opts?.max },
|
|
12
20
|
});
|
package/src/vocabulary/many.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { Relation, type EntityConstructor } from '../axis/role/Relation.js';
|
|
2
|
-
import { Field } from '../
|
|
2
|
+
import { Field } from '../field/Field.js';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* So the other side of a reference is declared, and holds no column.
|
|
6
|
+
* FR : pour que l'autre côté d'une référence soit déclaré, sans porter de colonne.
|
|
7
|
+
* `many(() => Post)` → a collection, absent from the row a client sends
|
|
8
|
+
*/
|
|
9
|
+
export function many<E extends EntityConstructor>(
|
|
10
|
+
target: E | (() => E),
|
|
11
|
+
): Field<InstanceType<E>[]> {
|
|
5
12
|
return new Field<InstanceType<E>[]>({
|
|
6
13
|
shape: { type: 'array' },
|
|
7
14
|
role: { relation: Relation.many(target) },
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { vocabulary } from './vocabulary.js';
|
|
2
|
-
import {
|
|
3
|
-
import { type Field } from '../
|
|
2
|
+
import { Shapes } from '../axis/shape/Shape.js';
|
|
3
|
+
import { type Field } from '../field/Field.js';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* So `null` becomes a legal value, without touching whether the field is required.
|
|
7
|
+
* FR : pour que `null` devienne légal, sans toucher au caractère obligatoire du champ.
|
|
8
|
+
* `nullable(text())` → `{ type: ['string', 'null'] }`, still required at create
|
|
9
|
+
*/
|
|
10
|
+
export const nullable: <T>(field: Field<T>) => Field<T | null> = vocabulary(
|
|
11
|
+
'nullable',
|
|
12
|
+
(field) => ({
|
|
13
|
+
shape: Shapes.nullable(field.shape),
|
|
14
|
+
}),
|
|
15
|
+
);
|
package/src/vocabulary/number.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Field } from '../
|
|
1
|
+
import { Field } from '../field/Field.js';
|
|
2
2
|
|
|
3
3
|
export interface NumberOptions {
|
|
4
4
|
min?: number;
|
|
@@ -8,10 +8,20 @@ export interface NumberOptions {
|
|
|
8
8
|
description?: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* So `integer` is a shape, not a second word to remember.
|
|
13
|
+
* FR : pour qu'`integer` soit une forme, pas un second mot à retenir.
|
|
14
|
+
* `number({ integer: true, min: 0 })` → `{ type: 'integer', minimum: 0 }`
|
|
15
|
+
*/
|
|
11
16
|
export function number(opts?: NumberOptions): Field<number> {
|
|
12
17
|
return new Field<number>({
|
|
13
|
-
shape: {
|
|
14
|
-
|
|
18
|
+
shape: {
|
|
19
|
+
type: opts?.integer ? 'integer' : 'number',
|
|
20
|
+
minimum: opts?.min,
|
|
21
|
+
maximum: opts?.max,
|
|
22
|
+
},
|
|
23
|
+
lifecycle:
|
|
24
|
+
opts?.default !== undefined ? { create: { value: opts.default } } : undefined,
|
|
15
25
|
meta: opts?.description !== undefined ? { description: opts.description } : undefined,
|
|
16
26
|
});
|
|
17
27
|
}
|
package/src/vocabulary/oneOf.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import { Field } from '../
|
|
1
|
+
import { Field } from '../field/Field.js';
|
|
2
2
|
|
|
3
3
|
interface OneOfOptions<V extends string = string> {
|
|
4
4
|
description?: string;
|
|
5
5
|
default?: V;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export function oneOf<const T extends readonly string[]>(
|
|
8
|
+
export function oneOf<const T extends readonly string[]>(
|
|
9
|
+
...values: [...T]
|
|
10
|
+
): Field<T[number]>;
|
|
9
11
|
export function oneOf<const T extends readonly string[]>(
|
|
10
12
|
...args: [...T, OneOfOptions<T[number]>]
|
|
11
13
|
): Field<T[number]>;
|
|
14
|
+
/**
|
|
15
|
+
* So the set is in the type and in the CHECK constraint, from one declaration.
|
|
16
|
+
* FR : pour que l'ensemble soit dans le type et dans la contrainte CHECK, d'une déclaration.
|
|
17
|
+
* `oneOf('draft', 'published', { default: 'draft' })`
|
|
18
|
+
*/
|
|
12
19
|
export function oneOf<const T extends readonly string[]>(
|
|
13
20
|
...args: [...T] | [...T, OneOfOptions<T[number]>]
|
|
14
21
|
): Field<T[number]> {
|
|
@@ -18,7 +25,8 @@ export function oneOf<const T extends readonly string[]>(
|
|
|
18
25
|
const opts = hasOpts ? (last as OneOfOptions) : {};
|
|
19
26
|
return new Field<T[number]>({
|
|
20
27
|
shape: { type: 'string', enum: values },
|
|
21
|
-
lifecycle:
|
|
28
|
+
lifecycle:
|
|
29
|
+
opts.default !== undefined ? { create: { value: opts.default } } : undefined,
|
|
22
30
|
meta: opts.description !== undefined ? { description: opts.description } : undefined,
|
|
23
31
|
});
|
|
24
32
|
}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { vocabulary } from './vocabulary.js';
|
|
2
|
-
import {
|
|
3
|
-
import { type Field } from '../
|
|
2
|
+
import { Shapes } from '../axis/shape/Shape.js';
|
|
3
|
+
import { type Field } from '../field/Field.js';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/**
|
|
6
|
+
* So the two questions are settled at once: `null` is legal, and absence is too.
|
|
7
|
+
* FR : pour que les deux questions soient réglées d'un coup : `null` légal, absence permise.
|
|
8
|
+
* `optional(text())` → nullable shape and `create: 'optional'`
|
|
9
|
+
*/
|
|
10
|
+
export const optional: <T>(field: Field<T>) => Field<T | null> = vocabulary(
|
|
11
|
+
'optional',
|
|
12
|
+
(field) => ({
|
|
13
|
+
shape: Shapes.nullable(field.shape),
|
|
14
|
+
...(field.lifecycle?.create === undefined
|
|
15
|
+
? { lifecycle: { create: 'optional' as const } }
|
|
16
|
+
: {}),
|
|
17
|
+
}),
|
|
18
|
+
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Field } from '../
|
|
1
|
+
import { Field } from '../field/Field.js';
|
|
2
2
|
import { Generators, type GeneratorRef } from '../axis/lifecycle/Generators.js';
|
|
3
3
|
|
|
4
4
|
interface PrimaryOptions {
|
|
@@ -7,12 +7,17 @@ interface PrimaryOptions {
|
|
|
7
7
|
|
|
8
8
|
export function primary(opts?: PrimaryOptions): Field<string>;
|
|
9
9
|
export function primary<T>(field: Field<T>): Field<T>;
|
|
10
|
+
/**
|
|
11
|
+
* So an id is generated and write-once by declaring nothing else.
|
|
12
|
+
* FR : pour qu'un identifiant soit généré et écrit une fois sans rien déclarer d'autre.
|
|
13
|
+
* `primary()` → cuid2, `update: 'forbidden'`; `primary(number())` → the same on a number
|
|
14
|
+
*/
|
|
10
15
|
export function primary(fieldOrOptions?: Field | PrimaryOptions): Field {
|
|
11
16
|
if (Field.is(fieldOrOptions)) {
|
|
12
17
|
const field = fieldOrOptions;
|
|
13
18
|
return field.with({
|
|
14
19
|
role: { ...field.role, primary: true },
|
|
15
|
-
lifecycle: { ...field.lifecycle, update:
|
|
20
|
+
lifecycle: { ...field.lifecycle, update: 'forbidden' },
|
|
16
21
|
});
|
|
17
22
|
}
|
|
18
23
|
|
|
@@ -23,11 +28,11 @@ export function primary(fieldOrOptions?: Field | PrimaryOptions): Field {
|
|
|
23
28
|
Generators.register(name, fn);
|
|
24
29
|
generate = name;
|
|
25
30
|
} else {
|
|
26
|
-
generate = opts.generate ??
|
|
31
|
+
generate = opts.generate ?? 'cuid2';
|
|
27
32
|
}
|
|
28
33
|
return new Field<string>({
|
|
29
|
-
shape: { type:
|
|
34
|
+
shape: { type: 'string' },
|
|
30
35
|
role: { primary: true },
|
|
31
|
-
lifecycle: { create: { generate }, update:
|
|
36
|
+
lifecycle: { create: { generate }, update: 'forbidden' },
|
|
32
37
|
});
|
|
33
38
|
}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { vocabulary } from './vocabulary.js';
|
|
2
2
|
import { Boundary } from '../axis/boundary/Boundary.js';
|
|
3
|
-
import { Field } from '../
|
|
3
|
+
import { Field } from '../field/Field.js';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* So a field a client may read but never write says so once, on the field.
|
|
7
|
+
* FR : pour qu'un champ lisible mais non écrivable le dise une fois, sur le champ.
|
|
8
|
+
* `readOnly(text())` → `boundary.in` is `'closed'`, absent from every form
|
|
9
|
+
*/
|
|
10
|
+
export const readOnly: <T>(field: Field<T>) => Field<T> = vocabulary(
|
|
11
|
+
'readOnly',
|
|
12
|
+
(field) => ({
|
|
13
|
+
boundary: Boundary.declared(field).with({ in: 'closed' }),
|
|
14
|
+
}),
|
|
15
|
+
);
|
package/src/vocabulary/ref.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { Relation, type EntityConstructor } from '../axis/role/Relation.js';
|
|
2
|
-
import { Field } from '../
|
|
2
|
+
import { Field } from '../field/Field.js';
|
|
3
3
|
|
|
4
4
|
export interface RefOptions {
|
|
5
5
|
cascade?: boolean;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* So a reference names the target entity, and the column type follows from it.
|
|
10
|
+
* FR : pour qu'une référence nomme l'entité cible, la colonne en découlant.
|
|
11
|
+
* `ref(() => User, { cascade: true })`
|
|
12
|
+
*/
|
|
13
|
+
export function ref<E extends EntityConstructor>(
|
|
14
|
+
target: E | (() => E),
|
|
15
|
+
opts?: RefOptions,
|
|
16
|
+
): Field<string> {
|
|
9
17
|
return new Field<string>({
|
|
10
18
|
shape: { type: 'string' },
|
|
11
19
|
role: { relation: Relation.one(target, opts?.cascade) },
|
package/src/vocabulary/text.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Field } from '../
|
|
1
|
+
import { Field } from '../field/Field.js';
|
|
2
2
|
import { type StringFormat } from '../axis/shape/Formats.js';
|
|
3
3
|
|
|
4
4
|
export interface TextOptions {
|
|
@@ -10,10 +10,22 @@ export interface TextOptions {
|
|
|
10
10
|
description?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* So a string field states its bounds where the DDL and the validator both read them.
|
|
15
|
+
* FR : pour qu'un champ texte énonce ses bornes là où le DDL et le juge les lisent.
|
|
16
|
+
* `text({ max: 200, format: 'email' })`
|
|
17
|
+
*/
|
|
13
18
|
export function text(opts?: TextOptions): Field<string> {
|
|
14
19
|
return new Field<string>({
|
|
15
|
-
shape: {
|
|
16
|
-
|
|
20
|
+
shape: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
minLength: opts?.min,
|
|
23
|
+
maxLength: opts?.max,
|
|
24
|
+
pattern: opts?.pattern,
|
|
25
|
+
format: opts?.format,
|
|
26
|
+
},
|
|
27
|
+
lifecycle:
|
|
28
|
+
opts?.default !== undefined ? { create: { value: opts.default } } : undefined,
|
|
17
29
|
meta: opts?.description !== undefined ? { description: opts.description } : undefined,
|
|
18
30
|
});
|
|
19
31
|
}
|
package/src/vocabulary/unique.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { vocabulary } from
|
|
2
|
-
import { Field } from
|
|
3
|
-
import { Unique } from "../constraint/Unique.js";
|
|
1
|
+
import { vocabulary } from './vocabulary.js';
|
|
2
|
+
import { Field } from '../field/Field.js';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
)
|
|
4
|
+
/**
|
|
5
|
+
* So a field is unique on its own, whatever key it ends up under.
|
|
6
|
+
* FR : pour qu'un champ soit unique à lui seul, quelle que soit sa clé.
|
|
7
|
+
* `unique(email)` → `role.unique` is true
|
|
8
|
+
*/
|
|
9
|
+
export const unique: <T>(field: Field<T>) => Field<T> = vocabulary('unique', () => ({
|
|
10
|
+
role: { unique: true },
|
|
11
|
+
}));
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { created } from './created.js';
|
|
2
|
-
import { Field } from '../
|
|
2
|
+
import { Field } from '../field/Field.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* So the dual of `created()` is re-stamped at every write, by the same hand.
|
|
6
|
+
* FR : pour que le dual de `created()` soit ré-estampé à chaque écriture, par la même main.
|
|
7
|
+
* `updated()` → `create: 'now'`, `update: 'now'`
|
|
8
|
+
*/
|
|
4
9
|
export function updated(): Field<Date> {
|
|
5
10
|
const base = created();
|
|
6
11
|
return base.with({ lifecycle: { ...base.lifecycle, update: 'now' } });
|