@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,44 +1,37 @@
|
|
|
1
1
|
import type { BoundaryRules } from './Boundary.js';
|
|
2
|
+
import { Registry } from '../../lib/Registry.js';
|
|
2
3
|
|
|
3
4
|
export type Decoder = (value: unknown) => { value: unknown } | { error: string };
|
|
4
5
|
export type Encoder = (value: unknown) => unknown;
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
`Boundaries.register${kind[0].toUpperCase()}${kind.slice(1)}('${name}', …).`,
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
return fn;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
Boundaries.registerDecoder('isoDate', (value) => {
|
|
7
|
+
/**
|
|
8
|
+
* `decoders` for a value coming in, `encoders` for one going out, `aliases` for the word
|
|
9
|
+
* that names a pair. Three registries, so a codec is never half declared.
|
|
10
|
+
* FR : `decoders` à l'entrée, `encoders` à la sortie, `aliases` pour le mot qui nomme la paire.
|
|
11
|
+
* `Boundaries.aliases.register('isoDate', { in: { decode: 'isoDate' }, out: { encode: 'isoDate' } })`
|
|
12
|
+
* → a field saying `boundary: 'isoDate'` decodes to a `Date` and leaves as a string
|
|
13
|
+
*/
|
|
14
|
+
export const Boundaries = {
|
|
15
|
+
decoders: new Registry<Decoder>(
|
|
16
|
+
'boundary decoder',
|
|
17
|
+
'call Boundaries.decoders.register(name, fn)',
|
|
18
|
+
),
|
|
19
|
+
encoders: new Registry<Encoder>(
|
|
20
|
+
'boundary encoder',
|
|
21
|
+
'call Boundaries.encoders.register(name, fn)',
|
|
22
|
+
),
|
|
23
|
+
aliases: new Registry<BoundaryRules>('boundary alias'),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
Boundaries.decoders.register('isoDate', (value) => {
|
|
34
27
|
if (value instanceof Date) return { value };
|
|
35
28
|
if (typeof value === 'string') {
|
|
36
|
-
const
|
|
37
|
-
return Number.isNaN(
|
|
29
|
+
const date = new Date(value);
|
|
30
|
+
return Number.isNaN(date.getTime()) ? { error: 'Invalid date' } : { value: date };
|
|
38
31
|
}
|
|
39
32
|
return { error: 'Expected a date' };
|
|
40
33
|
});
|
|
41
|
-
Boundaries.
|
|
34
|
+
Boundaries.encoders.register('isoDate', (value) =>
|
|
42
35
|
value instanceof Date ? value.toISOString() : value,
|
|
43
36
|
);
|
|
44
|
-
Boundaries.
|
|
37
|
+
Boundaries.aliases.register('isoDate', { in: { decode: 'isoDate' }, out: { encode: 'isoDate' } });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Decoder, Encoder } from './Boundaries.js';
|
|
2
2
|
import { Boundaries } from './Boundaries.js';
|
|
3
|
-
import type { Field } from '../../
|
|
4
|
-
import {
|
|
3
|
+
import type { Field } from '../../field/Field.js';
|
|
4
|
+
import { Shapes, type Shape } from '../shape/Shape.js';
|
|
5
5
|
|
|
6
6
|
export interface BoundaryRules {
|
|
7
7
|
in?: 'closed' | { decode: string };
|
|
@@ -19,19 +19,23 @@ export class Boundary implements BoundaryRules {
|
|
|
19
19
|
readonly decode: Decoder;
|
|
20
20
|
readonly encode: Encoder;
|
|
21
21
|
|
|
22
|
-
private constructor(
|
|
22
|
+
private constructor(
|
|
23
|
+
rules: BoundaryRules = {},
|
|
24
|
+
codecs?: { decode: Decoder; encode: Encoder },
|
|
25
|
+
) {
|
|
23
26
|
this.in = rules.in;
|
|
24
27
|
this.out = rules.out;
|
|
25
28
|
this.decode = codecs?.decode ?? identityDecoder;
|
|
26
29
|
this.encode = codecs?.encode ?? identityEncoder;
|
|
27
30
|
}
|
|
28
31
|
|
|
32
|
+
/** An unknown alias stops here. */
|
|
29
33
|
static declared(field: Field): Boundary {
|
|
30
34
|
const ref = field.boundary;
|
|
31
35
|
if (ref === undefined) return new Boundary();
|
|
32
36
|
if (typeof ref !== 'string') return new Boundary(ref);
|
|
33
37
|
|
|
34
|
-
const alias = Boundaries.
|
|
38
|
+
const alias = Boundaries.aliases.find(ref);
|
|
35
39
|
if (!alias) throw new Error(`Unknown boundary alias: '${ref}'`);
|
|
36
40
|
return new Boundary(alias);
|
|
37
41
|
}
|
|
@@ -39,17 +43,27 @@ export class Boundary implements BoundaryRules {
|
|
|
39
43
|
static of(field: Field): Boundary {
|
|
40
44
|
const declared = Boundary.declared(field);
|
|
41
45
|
const derived = Boundary.forShape(field.shape);
|
|
42
|
-
const rules: BoundaryRules = {
|
|
46
|
+
const rules: BoundaryRules = {
|
|
47
|
+
in: declared.in ?? derived.in,
|
|
48
|
+
out: declared.out ?? derived.out,
|
|
49
|
+
};
|
|
43
50
|
return new Boundary(rules, {
|
|
44
|
-
decode:
|
|
45
|
-
|
|
51
|
+
decode:
|
|
52
|
+
typeof rules.in === 'object'
|
|
53
|
+
? Boundaries.decoders.resolve(rules.in.decode)
|
|
54
|
+
: identityDecoder,
|
|
55
|
+
encode:
|
|
56
|
+
typeof rules.out === 'object'
|
|
57
|
+
? Boundaries.encoders.resolve(rules.out.encode)
|
|
58
|
+
: identityEncoder,
|
|
46
59
|
});
|
|
47
60
|
}
|
|
48
61
|
|
|
62
|
+
/** `date-time` means a `Date` on both sides, without a word in the entity. */
|
|
49
63
|
static forShape(shape: Shape | undefined): Boundary {
|
|
50
|
-
const base =
|
|
64
|
+
const base = Shapes.of(shape).base;
|
|
51
65
|
if (base?.type === 'string' && base.format === 'date-time') {
|
|
52
|
-
return new Boundary(Boundaries.
|
|
66
|
+
return new Boundary(Boundaries.aliases.find('isoDate')!);
|
|
53
67
|
}
|
|
54
68
|
return new Boundary();
|
|
55
69
|
}
|
|
@@ -65,5 +79,4 @@ export class Boundary implements BoundaryRules {
|
|
|
65
79
|
get writeOnly(): boolean {
|
|
66
80
|
return this.out === 'closed';
|
|
67
81
|
}
|
|
68
|
-
|
|
69
82
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Axis } from '../Axis.js';
|
|
2
|
-
import { admitWire } from '../../card/admission.js';
|
|
3
|
-
import { isObject } from '../../
|
|
2
|
+
import { admitWire } from '../../projection/card/admission.js';
|
|
3
|
+
import { isObject } from '../../lib/utils.js';
|
|
4
4
|
import type { BoundaryRef } from './Boundary.js';
|
|
5
5
|
|
|
6
6
|
export const boundaryAxis: Axis<BoundaryRef, BoundaryRef> = {
|
|
7
7
|
slot: 'boundary',
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
validator(value, errors) {
|
|
10
10
|
if (typeof value === 'string') return;
|
|
11
11
|
if (!isObject(value)) {
|
|
12
12
|
errors.push({
|
|
@@ -26,7 +26,7 @@ export const boundaryAxis: Axis<BoundaryRef, BoundaryRef> = {
|
|
|
26
26
|
|
|
27
27
|
describe: (value) => value,
|
|
28
28
|
reconstruct: (wire) => {
|
|
29
|
-
admitWire(boundaryAxis.
|
|
29
|
+
admitWire(boundaryAxis.validator, wire, 'boundary');
|
|
30
30
|
return wire;
|
|
31
31
|
},
|
|
32
32
|
};
|
|
@@ -16,7 +16,7 @@ export class Clock {
|
|
|
16
16
|
return this.reading();
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
/**
|
|
19
|
+
/** One substitution moves time for every stamp in the process at once. */
|
|
20
20
|
static freeze(at: number | Date): () => void {
|
|
21
21
|
const previous = this.reading;
|
|
22
22
|
const instant = at instanceof Date ? at.getTime() : at;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createId } from '@paralleldrive/cuid2';
|
|
2
2
|
|
|
3
|
+
import { Registry } from '../../lib/Registry.js';
|
|
4
|
+
|
|
3
5
|
export type GeneratorRef = 'cuid2' | 'uuid' | 'nanoid' | (string & {});
|
|
4
6
|
|
|
5
7
|
const NANOID_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
@@ -11,36 +13,17 @@ function nanoid(): string {
|
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
|
-
* Who answers a `generate
|
|
15
|
-
* `
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* refusal back inside: a registry whose unknown name is refused by a `default:` branch in
|
|
19
|
-
* its caller does not know what it holds, so it cannot say so.
|
|
16
|
+
* Who answers a `generate:` name. `cuid2`, `uuid` and `nanoid` are registered like any
|
|
17
|
+
* other, so `Generators.resolve('ulid')` is refused here and lists the three.
|
|
18
|
+
* FR : pour qu'un `generate:` soit répondu par un registre, builtins compris.
|
|
19
|
+
* `Generators.register('ulid', ulid)` → `create: { generate: 'ulid' }` now resolves
|
|
20
20
|
*/
|
|
21
|
-
export
|
|
22
|
-
|
|
21
|
+
export const Generators = new Registry<() => string>(
|
|
22
|
+
'generator',
|
|
23
|
+
'call Generators.register(name, fn)',
|
|
24
|
+
[
|
|
23
25
|
['cuid2', createId],
|
|
24
26
|
['uuid', () => globalThis.crypto.randomUUID()],
|
|
25
27
|
['nanoid', nanoid],
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
static register(name: string, fn: () => string): void {
|
|
29
|
-
this.registry.set(name, fn);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** The generator this name stands for, refused by name when nothing does. */
|
|
33
|
-
static resolve(ref: GeneratorRef): () => string {
|
|
34
|
-
const found = this.registry.get(ref);
|
|
35
|
-
if (found) return found;
|
|
36
|
-
throw new Error(
|
|
37
|
-
`Unknown generator '${ref}' — register it with Generators.register('${ref}', fn). ` +
|
|
38
|
-
`This process answers ${[...this.registry.keys()].join(', ')}.`,
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Whether a name is answered, for a caller that must not throw to find out. */
|
|
43
|
-
static answers(ref: GeneratorRef): boolean {
|
|
44
|
-
return this.registry.has(ref);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
28
|
+
],
|
|
29
|
+
);
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import type { Axis } from '../Axis.js';
|
|
2
|
-
import type { ValidationError } from '../../
|
|
3
|
-
import { admitWire } from '../../card/admission.js';
|
|
4
|
-
import { isObject
|
|
2
|
+
import type { ValidationError } from '../../validation.js';
|
|
3
|
+
import { admitWire, oneOfTokens } from '../../projection/card/admission.js';
|
|
4
|
+
import { isObject } from '../../lib/utils.js';
|
|
5
5
|
import { CREATE_TOKENS, UPDATE_TOKENS, type LifecycleRules } from './Lifecycle.js';
|
|
6
6
|
|
|
7
7
|
export const lifecycleAxis: Axis<LifecycleRules, LifecycleRules> = {
|
|
8
8
|
slot: 'lifecycle',
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
validator(value, errors) {
|
|
11
11
|
if (!isObject(value)) {
|
|
12
12
|
errors.push({ path: 'lifecycle', message: `Expected an object — got ${JSON.stringify(value)}` });
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
|
-
if (value.create !== undefined)
|
|
15
|
+
if (value.create !== undefined) validateCreate(value.create, errors);
|
|
16
16
|
if (value.update !== undefined && !oneOfTokens(value.update, UPDATE_TOKENS)) {
|
|
17
17
|
errors.push({
|
|
18
18
|
path: 'lifecycle.update',
|
|
@@ -23,12 +23,17 @@ export const lifecycleAxis: Axis<LifecycleRules, LifecycleRules> = {
|
|
|
23
23
|
|
|
24
24
|
describe: (value) => value,
|
|
25
25
|
reconstruct: (wire) => {
|
|
26
|
-
admitWire(lifecycleAxis.
|
|
26
|
+
admitWire(lifecycleAxis.validator, wire, 'lifecycle');
|
|
27
27
|
return wire;
|
|
28
28
|
},
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* So the four legal ways to declare a creation are stated once, and anything else names them.
|
|
33
|
+
* FR : pour que les quatre façons de déclarer une création soient dites une fois.
|
|
34
|
+
* `create: 3` → `Expected 'now', 'optional', { value } or { generate } — got 3`
|
|
35
|
+
*/
|
|
36
|
+
function validateCreate(rule: unknown, errors: ValidationError[]): void {
|
|
32
37
|
if (oneOfTokens(rule, CREATE_TOKENS)) return;
|
|
33
38
|
if (isObject(rule)) {
|
|
34
39
|
if ('value' in rule) return;
|
|
@@ -1,38 +1,53 @@
|
|
|
1
1
|
import { Lifecycle } from './Lifecycle.js';
|
|
2
2
|
import { Generators } from './Generators.js';
|
|
3
3
|
import { Clock } from './Clock.js';
|
|
4
|
-
import { type Field, type Fields } from '../../
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
import { type Field, type Fields } from '../../field/Field.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* So every storage realizes a create the same way.
|
|
8
|
+
* FR : pour que chaque storage réalise une création de la même façon.
|
|
9
|
+
* `applyCreate({ id: primary(), createdAt: created() }, { title: 'a' })`
|
|
10
|
+
* → `id` generated, `createdAt` stamped, `title` untouched
|
|
11
|
+
*/
|
|
12
|
+
export function applyCreate(fields: Fields, input: Record<string, unknown>): Record<string, unknown> {
|
|
13
|
+
const values: Record<string, unknown> = { ...input };
|
|
10
14
|
const instant = Clock.now();
|
|
11
15
|
|
|
12
16
|
for (const [name, field] of Object.entries(fields) as [string, Field][]) {
|
|
13
|
-
if (name in
|
|
17
|
+
if (name in values) continue;
|
|
14
18
|
const rule = Lifecycle.of(field);
|
|
15
19
|
|
|
16
|
-
if (rule.create === 'now')
|
|
17
|
-
else if (rule.literal)
|
|
18
|
-
else if (rule.generator)
|
|
20
|
+
if (rule.create === 'now') values[name] = new Date(instant);
|
|
21
|
+
else if (rule.literal) values[name] = freshValue(rule.literal.value);
|
|
22
|
+
else if (rule.generator) values[name] = Generators.resolve(rule.generator)();
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
return
|
|
25
|
+
return values;
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
/**
|
|
29
|
+
* So two rows created from one declared default never end up sharing the same object.
|
|
30
|
+
* FR : pour que deux lignes nées d'un même défaut ne partagent pas l'objet.
|
|
31
|
+
* `create: { value: [] }` → each instance gets its own array
|
|
32
|
+
*/
|
|
24
33
|
function freshValue(value: unknown): unknown {
|
|
25
34
|
if (value === null || typeof value !== 'object') return value;
|
|
26
35
|
return structuredClone(value);
|
|
27
36
|
}
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
/**
|
|
39
|
+
* So `updated()` is stamped by the storage, and no handler has to remember it.
|
|
40
|
+
* FR : pour qu'`updated()` soit estampé par le storage, pas par le handler.
|
|
41
|
+
* `applyUpdate(fields, { title: 'b' })` → `updatedAt` added, nothing else
|
|
42
|
+
*/
|
|
43
|
+
export function applyUpdate(fields: Fields, patch: Record<string, unknown>): Record<string, unknown> {
|
|
44
|
+
const values: Record<string, unknown> = { ...patch };
|
|
31
45
|
const instant = Clock.now();
|
|
32
46
|
|
|
33
47
|
for (const [name, field] of Object.entries(fields) as [string, Field][]) {
|
|
34
|
-
if (Lifecycle.of(field).stampedOnUpdate && !(name in
|
|
48
|
+
if (Lifecycle.of(field).stampedOnUpdate && !(name in values))
|
|
49
|
+
values[name] = new Date(instant);
|
|
35
50
|
}
|
|
36
51
|
|
|
37
|
-
return
|
|
52
|
+
return values;
|
|
38
53
|
}
|
|
@@ -10,22 +10,23 @@ export interface Relation {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* How a relation is built — the one place a target becomes
|
|
13
|
+
* How a relation is built — the one place a target becomes `() => Entity`.
|
|
14
14
|
*
|
|
15
15
|
* A target arrives as the class or as a function returning it, because a cycle between two
|
|
16
16
|
* entities can only be written the second way. Recognised by FORM, `getFields` on the
|
|
17
|
-
* value: a class answers it,
|
|
17
|
+
* value: a class answers it, `() => Post` does not.
|
|
18
18
|
*/
|
|
19
19
|
export const Relation = {
|
|
20
|
+
/** `() => Post` so two entities can point at each other. */
|
|
20
21
|
one(target: EntityConstructor | (() => EntityConstructor), cascade?: boolean): Relation {
|
|
21
|
-
return { to:
|
|
22
|
+
return { to: normalizeTarget(target), kind: 'one', onDelete: cascade ? 'cascade' : undefined };
|
|
22
23
|
},
|
|
23
24
|
|
|
24
25
|
many(target: EntityConstructor | (() => EntityConstructor)): Relation {
|
|
25
|
-
return { to:
|
|
26
|
+
return { to: normalizeTarget(target), kind: 'many' };
|
|
26
27
|
},
|
|
27
28
|
};
|
|
28
29
|
|
|
29
|
-
function
|
|
30
|
+
function normalizeTarget<E extends EntityConstructor>(target: E | (() => E)): () => E {
|
|
30
31
|
return 'getFields' in target ? () => target as E : (target as () => E);
|
|
31
32
|
}
|
package/src/axis/role/Role.ts
CHANGED
|
@@ -1,39 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Unique } from "../../constraint/Unique.js";
|
|
3
|
-
import type { EntityConstructor, Relation } from "./Relation.js";
|
|
1
|
+
import type { EntityConstructor, Relation } from './Relation.js';
|
|
4
2
|
|
|
5
3
|
export interface RoleRules {
|
|
6
4
|
primary?: boolean;
|
|
7
5
|
index?: boolean;
|
|
6
|
+
unique?: boolean;
|
|
8
7
|
relation?: Relation;
|
|
9
|
-
rules?: readonly FieldGroup[];
|
|
10
8
|
}
|
|
11
9
|
|
|
12
10
|
export class Role implements RoleRules {
|
|
13
11
|
readonly primary?: boolean;
|
|
14
12
|
readonly index?: boolean;
|
|
13
|
+
readonly unique?: boolean;
|
|
15
14
|
readonly relation?: Relation;
|
|
16
|
-
readonly rules?: readonly FieldGroup[];
|
|
17
15
|
|
|
18
|
-
private constructor(
|
|
19
|
-
this.primary =
|
|
20
|
-
this.index =
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
16
|
+
private constructor(rules: RoleRules = {}) {
|
|
17
|
+
this.primary = rules.primary;
|
|
18
|
+
this.index = rules.index;
|
|
19
|
+
this.unique = rules.unique;
|
|
20
|
+
this.relation = rules.relation;
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
static of(field: { role?: RoleRules }): Role {
|
|
26
24
|
return new Role(field.role);
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
/** A group written with no members means "this field": only the key it is declared under resolves it. */
|
|
30
|
-
static resolvedOn(role: RoleRules | undefined, key?: string): RoleRules | undefined {
|
|
31
|
-
let rules = FieldGroup.normalize(role?.rules, (members) => new Unique(members));
|
|
32
|
-
if (rules && key !== undefined && rules.some((group) => group.isSelf))
|
|
33
|
-
rules = rules.map((group) => group.resolvedOn(key));
|
|
34
|
-
return rules === role?.rules ? role : { ...role, rules };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
27
|
get isPrimary(): boolean {
|
|
38
28
|
return this.primary === true;
|
|
39
29
|
}
|
|
@@ -42,14 +32,20 @@ export class Role implements RoleRules {
|
|
|
42
32
|
return this.index === true;
|
|
43
33
|
}
|
|
44
34
|
|
|
35
|
+
/** A field unique on its own. A group spanning several belongs to the schema. */
|
|
36
|
+
get isUnique(): boolean {
|
|
37
|
+
return this.unique === true;
|
|
38
|
+
}
|
|
39
|
+
|
|
45
40
|
get isCollection(): boolean {
|
|
46
|
-
return this.relation?.kind ===
|
|
41
|
+
return this.relation?.kind === 'many';
|
|
47
42
|
}
|
|
48
43
|
|
|
49
44
|
get isReference(): boolean {
|
|
50
|
-
return this.relation?.kind ===
|
|
45
|
+
return this.relation?.kind === 'one';
|
|
51
46
|
}
|
|
52
47
|
|
|
48
|
+
/** Calls `() => Post` so no caller has to. */
|
|
53
49
|
get target(): EntityConstructor | undefined {
|
|
54
50
|
return this.relation?.to();
|
|
55
51
|
}
|
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
import type { Axis, Resolver } from '../Axis.js';
|
|
2
|
-
import { refuse } from '../../card/admission.js';
|
|
3
|
-
import { isObject,
|
|
4
|
-
import type { ValidationError } from '../../
|
|
2
|
+
import { refuse, oneOfTokens } from '../../projection/card/admission.js';
|
|
3
|
+
import { isObject, lowerFirst } from '../../lib/utils.js';
|
|
4
|
+
import type { ValidationError } from '../../validation.js';
|
|
5
5
|
import { ON_DELETE, RELATION_KINDS, type EntityConstructor, type Relation } from './Relation.js';
|
|
6
6
|
import { type RoleRules } from './Role.js';
|
|
7
|
-
import {
|
|
8
|
-
import { Unique } from '../../constraint/Unique.js';
|
|
9
|
-
import type { RoleDescriptor } from '../../card/Descriptor.js';
|
|
10
|
-
import { lowerFirst } from '../../utils.js';
|
|
7
|
+
import type { RoleDescriptor } from '../../projection/card/Descriptor.js';
|
|
11
8
|
|
|
12
9
|
export const roleAxis: Axis<RoleRules, RoleDescriptor> = {
|
|
13
10
|
slot: 'role',
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
validator(value, errors) {
|
|
16
13
|
if (!isObject(value)) {
|
|
17
14
|
errors.push({ path: 'role', message: `Expected an object — got ${JSON.stringify(value)}` });
|
|
18
15
|
return;
|
|
19
16
|
}
|
|
20
|
-
for (const flag of ['primary', 'index'] as const) {
|
|
17
|
+
for (const flag of ['primary', 'index', 'unique'] as const) {
|
|
21
18
|
if (value[flag] !== undefined && typeof value[flag] !== 'boolean') {
|
|
22
19
|
errors.push({
|
|
23
20
|
path: `role.${flag}`,
|
|
@@ -25,48 +22,33 @@ export const roleAxis: Axis<RoleRules, RoleDescriptor> = {
|
|
|
25
22
|
});
|
|
26
23
|
}
|
|
27
24
|
}
|
|
28
|
-
if (value.
|
|
29
|
-
const legal =
|
|
30
|
-
Array.isArray(value.rules) &&
|
|
31
|
-
value.rules.every(
|
|
32
|
-
(rule) =>
|
|
33
|
-
rule instanceof FieldGroup ||
|
|
34
|
-
(Array.isArray(rule) && rule.every((member) => typeof member === 'string')),
|
|
35
|
-
);
|
|
36
|
-
if (!legal) {
|
|
37
|
-
errors.push({
|
|
38
|
-
path: 'role.rules',
|
|
39
|
-
message: 'Expected field groups, or the member lists they denote — string[][]',
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (value.relation !== undefined) judgeRelation(value.relation, errors);
|
|
25
|
+
if (value.relation !== undefined) validateRelation(value.relation, errors);
|
|
44
26
|
},
|
|
45
27
|
|
|
28
|
+
/** A card carries the target's NAME: a class cannot cross a process boundary. */
|
|
46
29
|
describe(role, key) {
|
|
47
|
-
const
|
|
48
|
-
if (role.primary)
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
if (role.index) out.index = true;
|
|
30
|
+
const descriptor: Mutable<RoleDescriptor> = {};
|
|
31
|
+
if (role.primary) descriptor.primary = true;
|
|
32
|
+
if (role.unique) descriptor.unique = [[key]];
|
|
33
|
+
if (role.index) descriptor.index = true;
|
|
52
34
|
if (role.relation) {
|
|
53
35
|
const target = role.relation.to() as { name?: string };
|
|
54
|
-
|
|
36
|
+
descriptor.relation = {
|
|
55
37
|
to: lowerFirst(target.name ?? ''),
|
|
56
38
|
kind: role.relation.kind,
|
|
57
39
|
...(role.relation.onDelete ? { onDelete: role.relation.onDelete } : {}),
|
|
58
40
|
};
|
|
59
41
|
}
|
|
60
|
-
return Object.keys(
|
|
42
|
+
return Object.keys(descriptor).length ? descriptor : undefined;
|
|
61
43
|
},
|
|
62
44
|
|
|
63
45
|
reconstruct(wire, resolve?: Resolver) {
|
|
64
|
-
const
|
|
65
|
-
if (wire.primary)
|
|
66
|
-
if (wire.unique?.
|
|
67
|
-
if (wire.index)
|
|
46
|
+
const rules: RoleRules = {};
|
|
47
|
+
if (wire.primary) rules.primary = true;
|
|
48
|
+
if (wire.unique?.some((group) => group.length === 1)) rules.unique = true;
|
|
49
|
+
if (wire.index) rules.index = true;
|
|
68
50
|
if (wire.relation) {
|
|
69
|
-
// `
|
|
51
|
+
// `validator` cannot serve here: it demands `() => Post` where a card carries a name.
|
|
70
52
|
if (!oneOfTokens(wire.relation.kind, RELATION_KINDS)) {
|
|
71
53
|
refuse(
|
|
72
54
|
`role.relation.kind is ${JSON.stringify(wire.relation.kind)}`,
|
|
@@ -80,17 +62,22 @@ export const roleAxis: Axis<RoleRules, RoleDescriptor> = {
|
|
|
80
62
|
);
|
|
81
63
|
}
|
|
82
64
|
const name = wire.relation.to;
|
|
83
|
-
|
|
65
|
+
rules.relation = {
|
|
84
66
|
to: () => (resolve?.(name) ?? ({ name } as unknown)) as EntityConstructor,
|
|
85
67
|
kind: wire.relation.kind as Relation['kind'],
|
|
86
68
|
...(wire.relation.onDelete ? { onDelete: wire.relation.onDelete as Relation['onDelete'] } : {}),
|
|
87
69
|
} as RoleRules['relation'];
|
|
88
70
|
}
|
|
89
|
-
return
|
|
71
|
+
return rules;
|
|
90
72
|
},
|
|
91
73
|
};
|
|
92
74
|
|
|
93
|
-
|
|
75
|
+
/**
|
|
76
|
+
* So a relation is validated where `() => Post` is required — a card carries a name.
|
|
77
|
+
* FR : pour qu'une relation soit jugée là où `() => Post` est exigé, contrairement à une carte.
|
|
78
|
+
* `{ to: User, kind: 'one' }` → error `Expected a function returning the target entity, such as () => Post`
|
|
79
|
+
*/
|
|
80
|
+
function validateRelation(relation: unknown, errors: ValidationError[]): void {
|
|
94
81
|
if (!isObject(relation)) {
|
|
95
82
|
errors.push({ path: 'role.relation', message: `Expected an object — got ${JSON.stringify(relation)}` });
|
|
96
83
|
return;
|
|
@@ -102,7 +89,7 @@ function judgeRelation(relation: unknown, errors: ValidationError[]): void {
|
|
|
102
89
|
});
|
|
103
90
|
}
|
|
104
91
|
if (typeof relation.to !== 'function') {
|
|
105
|
-
errors.push({ path: 'role.relation.to', message: 'Expected a
|
|
92
|
+
errors.push({ path: 'role.relation.to', message: 'Expected a function returning the target entity, such as () => Post' });
|
|
106
93
|
}
|
|
107
94
|
if (relation.onDelete !== undefined && !oneOfTokens(relation.onDelete, ON_DELETE)) {
|
|
108
95
|
errors.push({
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Registry } from '../../lib/Registry.js';
|
|
2
|
+
|
|
1
3
|
export type StringFormat =
|
|
2
4
|
| 'date-time'
|
|
3
5
|
| 'date'
|
|
@@ -9,14 +11,10 @@ export type StringFormat =
|
|
|
9
11
|
|
|
10
12
|
export type FormatPredicate = (value: string) => boolean;
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
static resolve(name: string): FormatPredicate | undefined {
|
|
20
|
-
return this.registry.get(name);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
14
|
+
/**
|
|
15
|
+
* String formats the JSON Schema engine does not know. `find` answers `undefined` for
|
|
16
|
+
* `email` or `uuid`, which it validates on its own.
|
|
17
|
+
* FR : les formats de chaîne que le moteur ignore ; `find` rend `undefined` pour les siens.
|
|
18
|
+
* `Formats.register('siret', (v) => /^\d{14}$/.test(v))`
|
|
19
|
+
*/
|
|
20
|
+
export const Formats = new Registry<FormatPredicate>('format');
|