@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
package/src/card/Card.ts
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import { EXTENSION_AXES, EXTENSION_SLOTS, type Resolver } from '../axis/Axis.js';
|
|
2
|
-
import { dequal } from 'dequal';
|
|
3
|
-
import { clean } from '../utils.js';
|
|
4
|
-
import { Field, type Fields } from '../fields/Field.js';
|
|
5
|
-
import { isObject } from '../judge/ValueForm.js';
|
|
6
|
-
import { RowJudge } from '../judge/RowJudge.js';
|
|
7
|
-
import { Schema, type SchemaConstructor } from '../schema/Schema.js';
|
|
8
|
-
import type { Row, SchemaView } from '../schema/SchemaView.js';
|
|
9
|
-
import { refuse } from './admission.js';
|
|
10
|
-
import type {
|
|
11
|
-
DerivedFrom,
|
|
12
|
-
FieldDescriptor,
|
|
13
|
-
FieldExtension,
|
|
14
|
-
SchemaDescriptor,
|
|
15
|
-
} from './Descriptor.js';
|
|
16
|
-
import type { Change, Diff, DiffOptions, RenameCandidate, TypeSet } from './diff.js';
|
|
17
|
-
|
|
18
|
-
type FieldsOf<T> = { [K in keyof T]-?: Field<T[K]> };
|
|
19
|
-
|
|
20
|
-
/** One portable schema description and every decision made about that description. */
|
|
21
|
-
export class Card<T = Row<Fields>> {
|
|
22
|
-
private constructor(readonly descriptor: SchemaDescriptor) {}
|
|
23
|
-
|
|
24
|
-
static fromSchema<TFields extends Fields>(schema: SchemaView<TFields>, name?: string): Card<Row<TFields>> {
|
|
25
|
-
const fields = schema.getFields();
|
|
26
|
-
const judge = RowJudge.of(fields);
|
|
27
|
-
const properties: Record<string, FieldDescriptor> = {};
|
|
28
|
-
const required: string[] = [];
|
|
29
|
-
for (const [key, field] of Object.entries(fields)) {
|
|
30
|
-
properties[key] = describeField(field, key);
|
|
31
|
-
if (judge.onAbsent(field) === null) required.push(key);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const descriptor: SchemaDescriptor = {
|
|
35
|
-
type: 'object',
|
|
36
|
-
properties,
|
|
37
|
-
'x-fougere-version': 1,
|
|
38
|
-
'x-fougere-vendor': 'fougere',
|
|
39
|
-
};
|
|
40
|
-
const title = name ?? schema.derivation?.sourceName ?? schema.name;
|
|
41
|
-
if (title) descriptor.title = title;
|
|
42
|
-
if (required.length) descriptor.required = required;
|
|
43
|
-
|
|
44
|
-
const origin = originOf(schema);
|
|
45
|
-
if (origin) descriptor['x-fougere-derived'] = origin;
|
|
46
|
-
return new Card<Row<TFields>>(descriptor);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
static fromDescriptor<T = Row<Fields>>(descriptor: SchemaDescriptor): Card<T> {
|
|
50
|
-
return new Card<T>(descriptor);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** The root derivation recorded by this card, when its schema was cut from another. */
|
|
54
|
-
get origin(): DerivedFrom | undefined {
|
|
55
|
-
return this.descriptor['x-fougere-derived'];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Rebuild the live schema. A bundle supplies `resolve` so relation thunks can find the
|
|
60
|
-
* other reconstructed cards; a lone card deliberately falls back to a name stand-in.
|
|
61
|
-
*/
|
|
62
|
-
toSchema(resolve?: Resolver, name?: string): SchemaConstructor<FieldsOf<T>> {
|
|
63
|
-
const descriptor = this.descriptor;
|
|
64
|
-
const where = name ? `schema '${name}'` : 'this schema';
|
|
65
|
-
if (!isObject(descriptor)) refuse(`${where} is not an object`, 'A card carries one JSON Schema per door.');
|
|
66
|
-
const version = descriptor['x-fougere-version'];
|
|
67
|
-
if (version !== 1) {
|
|
68
|
-
refuse(
|
|
69
|
-
`${where} states \`x-fougere-version: ${JSON.stringify(version)}\` and this reader speaks 1`,
|
|
70
|
-
'A producer and its readers move together: re-sync the consumer, or serve the version it speaks.',
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
if (!isObject(descriptor.properties)) {
|
|
74
|
-
refuse(`${where} carries no \`properties\` object`, 'A schema is its fields; there is nothing to rebuild.');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const fields: Fields = {};
|
|
78
|
-
for (const [key, property] of Object.entries(descriptor.properties)) {
|
|
79
|
-
fields[key] = reconstructField(property, key, resolve);
|
|
80
|
-
}
|
|
81
|
-
const schema = Schema.of({ fields });
|
|
82
|
-
const title = name ?? descriptor.title;
|
|
83
|
-
if (title) Object.defineProperty(schema, 'name', { value: title, configurable: true });
|
|
84
|
-
return schema as unknown as SchemaConstructor<FieldsOf<T>>;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** What must change to turn this card into `other`. */
|
|
88
|
-
diff(other: Card, options: DiffOptions = {}): Diff {
|
|
89
|
-
const changes: Change[] = [];
|
|
90
|
-
const renamed = options.renamed ?? {};
|
|
91
|
-
const before = this.descriptor.properties ?? {};
|
|
92
|
-
const after = other.descriptor.properties ?? {};
|
|
93
|
-
const requiredBefore = new Set(this.descriptor.required ?? []);
|
|
94
|
-
const requiredAfter = new Set(other.descriptor.required ?? []);
|
|
95
|
-
|
|
96
|
-
// Apply a declared rename first so subsequent differences use the new field name.
|
|
97
|
-
const nameAfter = (field: string): string => renamed[field] ?? field;
|
|
98
|
-
const removed: string[] = [];
|
|
99
|
-
for (const [field, descriptor] of Object.entries(before)) {
|
|
100
|
-
const now = nameAfter(field);
|
|
101
|
-
const target = after[now];
|
|
102
|
-
if (target === undefined) {
|
|
103
|
-
removed.push(field);
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (now !== field) changes.push({ kind: 'renamed', from: field, to: now, field: target });
|
|
108
|
-
|
|
109
|
-
const wasType = typesOf(descriptor);
|
|
110
|
-
const isType = typesOf(target);
|
|
111
|
-
if (!dequal(wasType, isType)) changes.push({ kind: 'retyped', field: now, from: wasType, to: isType });
|
|
112
|
-
else if (!dequal(boundsOf(descriptor), boundsOf(target))) {
|
|
113
|
-
changes.push({ kind: 'reshaped', field: now, from: descriptor, to: target });
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const wasRequired = requiredBefore.has(field);
|
|
117
|
-
const isRequired = requiredAfter.has(now);
|
|
118
|
-
if (wasRequired !== isRequired) {
|
|
119
|
-
changes.push({ kind: 'required', field: now, from: wasRequired, to: isRequired });
|
|
120
|
-
}
|
|
121
|
-
changes.push(...restated(now, descriptor['x-fougere'], target['x-fougere']));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const claimed = new Set(Object.values(renamed));
|
|
125
|
-
const added = Object.keys(after).filter((field) => !(field in before) && !claimed.has(field));
|
|
126
|
-
for (const field of removed) {
|
|
127
|
-
changes.push({ kind: 'removed', field, from: before[field], required: requiredBefore.has(field) });
|
|
128
|
-
}
|
|
129
|
-
for (const field of added) {
|
|
130
|
-
changes.push({ kind: 'added', field, to: after[field], required: requiredAfter.has(field) });
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return { changes, ambiguous: candidates(removed, added, before, after) };
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function describeExtension(field: Field, key: string): FieldExtension | undefined {
|
|
138
|
-
const extension: Record<string, unknown> = {};
|
|
139
|
-
for (const axis of EXTENSION_AXES) {
|
|
140
|
-
const declared = (field as unknown as Record<string, unknown>)[axis.slot];
|
|
141
|
-
if (declared === undefined) continue;
|
|
142
|
-
const wire = axis.describe(declared, key);
|
|
143
|
-
if (wire !== undefined) extension[axis.slot] = wire;
|
|
144
|
-
}
|
|
145
|
-
clean(extension);
|
|
146
|
-
return Object.keys(extension).length ? (extension as FieldExtension) : undefined;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function describeField(field: Field, key: string): FieldDescriptor {
|
|
150
|
-
const descriptor: FieldDescriptor = {};
|
|
151
|
-
for (const [shapeKey, value] of Object.entries(field.shape)) {
|
|
152
|
-
if (value !== undefined) (descriptor as Record<string, unknown>)[shapeKey] = value;
|
|
153
|
-
}
|
|
154
|
-
if (field.meta?.description) descriptor.description = field.meta.description;
|
|
155
|
-
const extension = describeExtension(field, key);
|
|
156
|
-
if (extension) descriptor['x-fougere'] = extension;
|
|
157
|
-
return descriptor;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function originOf(schema: SchemaView): DerivedFrom | undefined {
|
|
161
|
-
const { derivation } = schema;
|
|
162
|
-
if (!derivation) return undefined;
|
|
163
|
-
const survived = Object.fromEntries(Object.entries(derivation.survived).map(([key, value]) => [key, value ?? null]));
|
|
164
|
-
return { from: derivation.sourceName, survived };
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function reconstructShape(property: FieldDescriptor): Field['shape'] | undefined {
|
|
168
|
-
const types = Array.isArray(property.type) ? property.type : property.type ? [property.type] : [];
|
|
169
|
-
const base = types.find((type) => type !== 'null');
|
|
170
|
-
if (base === undefined) return undefined;
|
|
171
|
-
const type = property.type as Field['shape']['type'];
|
|
172
|
-
if (base === 'array') {
|
|
173
|
-
const items = property.items ? reconstructShape(property.items) : undefined;
|
|
174
|
-
return clean({ type, items, minItems: property.minItems, maxItems: property.maxItems }) as Field['shape'];
|
|
175
|
-
}
|
|
176
|
-
switch (base) {
|
|
177
|
-
case 'string':
|
|
178
|
-
return clean({
|
|
179
|
-
type,
|
|
180
|
-
minLength: property.minLength,
|
|
181
|
-
maxLength: property.maxLength,
|
|
182
|
-
pattern: property.pattern,
|
|
183
|
-
enum: property.enum,
|
|
184
|
-
format: property.format,
|
|
185
|
-
}) as Field['shape'];
|
|
186
|
-
case 'number':
|
|
187
|
-
case 'integer':
|
|
188
|
-
return clean({ type, minimum: property.minimum, maximum: property.maximum }) as Field['shape'];
|
|
189
|
-
case 'boolean':
|
|
190
|
-
return { type } as Field['shape'];
|
|
191
|
-
default:
|
|
192
|
-
return clean({ type, properties: property.properties, required: property.required }) as Field['shape'];
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function reconstructField(property: FieldDescriptor, key: string, resolve?: Resolver): Field {
|
|
197
|
-
const shape = reconstructShape(property);
|
|
198
|
-
if (!shape) {
|
|
199
|
-
throw new Error(
|
|
200
|
-
`Field '${key}': the card carries no \`type\` for it, so there is no shape to rebuild. `
|
|
201
|
-
+ 'A field always states one.',
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
const extension = property['x-fougere'];
|
|
205
|
-
const axes: Record<string, unknown> = {};
|
|
206
|
-
for (const axis of EXTENSION_AXES) {
|
|
207
|
-
const wire = (extension as Record<string, unknown> | undefined)?.[axis.slot];
|
|
208
|
-
if (wire !== undefined) axes[axis.slot] = axis.reconstruct(wire, resolve);
|
|
209
|
-
}
|
|
210
|
-
return new Field({
|
|
211
|
-
shape,
|
|
212
|
-
...axes,
|
|
213
|
-
meta: property.description !== undefined ? { description: property.description } : undefined,
|
|
214
|
-
} as never);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function restated(field: string, before: FieldExtension | undefined, after: FieldExtension | undefined): Change[] {
|
|
218
|
-
return EXTENSION_SLOTS.filter((axis) => !dequal(before?.[axis], after?.[axis])).map(
|
|
219
|
-
(axis) => ({ kind: 'restated', field, axis, from: before?.[axis], to: after?.[axis] }) as Change,
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function shapeOf(descriptor: FieldDescriptor): Record<string, unknown> {
|
|
224
|
-
const { 'x-fougere': _extension, ...shape } = descriptor;
|
|
225
|
-
return shape as Record<string, unknown>;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function typesOf(descriptor: FieldDescriptor): TypeSet {
|
|
229
|
-
const type = descriptor.type;
|
|
230
|
-
if (type === undefined) return [];
|
|
231
|
-
return (Array.isArray(type) ? [...type] : [type]).sort();
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function boundsOf(descriptor: FieldDescriptor): Record<string, unknown> {
|
|
235
|
-
const { type: _type, ...rest } = shapeOf(descriptor);
|
|
236
|
-
return rest;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function candidates(
|
|
240
|
-
removed: string[],
|
|
241
|
-
added: string[],
|
|
242
|
-
before: Record<string, FieldDescriptor>,
|
|
243
|
-
after: Record<string, FieldDescriptor>,
|
|
244
|
-
): RenameCandidate[] {
|
|
245
|
-
const found: RenameCandidate[] = [];
|
|
246
|
-
for (const gone of removed) {
|
|
247
|
-
for (const appeared of added) {
|
|
248
|
-
if (dequal(shapeOf(before[gone]), shapeOf(after[appeared]))) found.push({ removed: gone, added: appeared });
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
const was = Object.keys(before);
|
|
252
|
-
const now = Object.keys(after);
|
|
253
|
-
const apart = ({ removed: gone, added: appeared }: RenameCandidate): number =>
|
|
254
|
-
Math.abs(now.indexOf(appeared) - was.indexOf(gone));
|
|
255
|
-
return found.sort((a, b) => apart(a) - apart(b));
|
|
256
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { upperFirst } from '../utils.js';
|
|
2
|
-
import type { FieldDescriptor, SchemaDescriptor } from './Descriptor.js';
|
|
3
|
-
|
|
4
|
-
function typeOf(field: FieldDescriptor): string {
|
|
5
|
-
const types = Array.isArray(field.type) ? field.type : field.type ? [field.type] : [];
|
|
6
|
-
const nullable = types.includes('null');
|
|
7
|
-
const base = types.find((t) => t !== 'null');
|
|
8
|
-
const inner = baseTypeOf(base, field);
|
|
9
|
-
return nullable ? `${inner} | null` : inner;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function baseTypeOf(base: string | undefined, field: FieldDescriptor): string {
|
|
13
|
-
if (field.enum?.length) {
|
|
14
|
-
return field.enum.map((v) => (v === null ? 'null' : JSON.stringify(v))).join(' | ');
|
|
15
|
-
}
|
|
16
|
-
switch (base) {
|
|
17
|
-
case 'string':
|
|
18
|
-
return field.format === 'date-time' ? 'Date' : 'string';
|
|
19
|
-
case 'number':
|
|
20
|
-
case 'integer':
|
|
21
|
-
return 'number';
|
|
22
|
-
case 'boolean':
|
|
23
|
-
return 'boolean';
|
|
24
|
-
case 'array':
|
|
25
|
-
return field.items ? `${typeOf(field.items)}[]` : 'string[]';
|
|
26
|
-
case 'object':
|
|
27
|
-
return field.properties ? objectTypeOf(field.properties, field.required ?? []) : 'Record<string, unknown>';
|
|
28
|
-
default:
|
|
29
|
-
return 'unknown';
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function objectTypeOf(properties: Record<string, FieldDescriptor>, required: readonly string[]): string {
|
|
34
|
-
const members = Object.entries(properties).map(([name, field]) => {
|
|
35
|
-
const optional = required.includes(name) ? '' : '?';
|
|
36
|
-
return `${propertyKey(name)}${optional}: ${typeOf(field)}`;
|
|
37
|
-
});
|
|
38
|
-
return `{ ${members.join('; ')} }`;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function propertyKey(name: string): string {
|
|
42
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function docCommentOf(text: string | undefined, indent: string): string {
|
|
46
|
-
if (!text) return '';
|
|
47
|
-
return `${indent}/** ${text.replace(/\*\//g, '*\\/')} */\n`;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface EntityTypeSourceOptions {
|
|
51
|
-
name?: string;
|
|
52
|
-
exported?: boolean;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function shapeTypeOf(descriptor: SchemaDescriptor, indent = ''): string {
|
|
56
|
-
const entries = Object.entries(descriptor.properties ?? {});
|
|
57
|
-
if (entries.length === 0) return '{}';
|
|
58
|
-
|
|
59
|
-
const lines = entries.map(([key, field]) => {
|
|
60
|
-
const doc = docCommentOf(field.description, `${indent} `);
|
|
61
|
-
return `${doc}${indent} ${propertyKey(key)}: ${typeOf(field)};`;
|
|
62
|
-
});
|
|
63
|
-
return `{\n${lines.join('\n')}\n${indent}}`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export class EntityTypeSource {
|
|
67
|
-
private constructor(private readonly descriptor: SchemaDescriptor) {}
|
|
68
|
-
|
|
69
|
-
static of(descriptor: SchemaDescriptor): EntityTypeSource {
|
|
70
|
-
return new EntityTypeSource(descriptor);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
render(options: EntityTypeSourceOptions = {}): string {
|
|
74
|
-
const name = identifierOf(options.name ?? upperFirst(this.descriptor.title ?? 'Schema'));
|
|
75
|
-
const exported = options.exported === false ? '' : 'export ';
|
|
76
|
-
const card = JSON.stringify(this.descriptor, null, 2)
|
|
77
|
-
.split('\n')
|
|
78
|
-
.map((line, i) => (i === 0 ? line : ` ${line}`))
|
|
79
|
-
.join('\n');
|
|
80
|
-
|
|
81
|
-
return `${exported}class ${name} extends Card.fromDescriptor<${shapeTypeOf(this.descriptor)}>(${card}).toSchema() {}`;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function identifierOf(name: string): string {
|
|
86
|
-
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) {
|
|
87
|
-
throw new Error(`'${name}' is not a TypeScript identifier — it cannot name a generated declaration`);
|
|
88
|
-
}
|
|
89
|
-
return name;
|
|
90
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { SchemaDescriptor } from './Descriptor.js';
|
|
2
|
-
|
|
3
|
-
export interface FacadeTypeSourceOptions {
|
|
4
|
-
name?: string;
|
|
5
|
-
exported?: boolean;
|
|
6
|
-
rowType?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface OpDescriptor {
|
|
10
|
-
name: string;
|
|
11
|
-
description?: string;
|
|
12
|
-
output?: SchemaDescriptor;
|
|
13
|
-
cardinality?: 'one' | 'maybe' | 'many' | 'page' | 'none';
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function propertyKey(name: string): string {
|
|
17
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function docCommentOf(text: string | undefined, indent: string): string {
|
|
21
|
-
if (!text) return '';
|
|
22
|
-
return `${indent}/** ${text.replace(/\*\//g, '*\\/')} */\n`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function returnTypeOf(op: OpDescriptor, rowType: string): string {
|
|
26
|
-
switch (op.cardinality) {
|
|
27
|
-
case 'many': return `${rowType}[]`;
|
|
28
|
-
case 'page': return `${rowType}[] & { total?: number; endCursor?: string; hasMore?: boolean }`;
|
|
29
|
-
case 'maybe': return `${rowType} | undefined`;
|
|
30
|
-
case 'one': return rowType;
|
|
31
|
-
case 'none': return 'unknown';
|
|
32
|
-
default: return 'unknown';
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export class FacadeTypeSource {
|
|
37
|
-
private constructor(private readonly operations: readonly OpDescriptor[]) {}
|
|
38
|
-
|
|
39
|
-
static of(operations: readonly OpDescriptor[]): FacadeTypeSource {
|
|
40
|
-
return new FacadeTypeSource(operations);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
render(options: FacadeTypeSourceOptions = {}): string {
|
|
44
|
-
const name = options.name ?? 'Facade';
|
|
45
|
-
const exported = options.exported === false ? '' : 'export ';
|
|
46
|
-
const rowType = options.rowType ?? 'unknown';
|
|
47
|
-
const members = this.operations.map((operation) => {
|
|
48
|
-
const doc = docCommentOf(operation.description, ' ');
|
|
49
|
-
return `${doc} ${propertyKey(operation.name)}(invocation?: Invocation): Promise<${returnTypeOf(operation, rowType)}>;`;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
if (members.length === 0) return `${exported}interface ${name} {}`;
|
|
53
|
-
return `${exported}interface ${name} {\n${members.join('\n')}\n}`;
|
|
54
|
-
}
|
|
55
|
-
}
|
package/src/card/admission.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { ValidationError } from '../judge/result.js';
|
|
2
|
-
|
|
3
|
-
/** A card is refused by naming what is wrong AND the remedy — never by a bare throw. */
|
|
4
|
-
export function refuse(what: string, fix: string): never {
|
|
5
|
-
throw new Error(`This card cannot be read: ${what}.\n ${fix}`);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* A wire value admitted by the axis's OWN judge — legal only where its declared and wire
|
|
10
|
-
* forms are the same value, which `describe: v => v` states.
|
|
11
|
-
*/
|
|
12
|
-
export function admitWire(
|
|
13
|
-
judge: (value: unknown, errors: ValidationError[]) => void,
|
|
14
|
-
value: unknown,
|
|
15
|
-
slot: string,
|
|
16
|
-
): void {
|
|
17
|
-
const errors: ValidationError[] = [];
|
|
18
|
-
judge(value, errors);
|
|
19
|
-
if (errors.length) {
|
|
20
|
-
refuse(
|
|
21
|
-
`${slot} is malformed — ${errors.map((e) => `${e.path}: ${e.message}`).join('; ')}`,
|
|
22
|
-
'A card states an axis the way a declaration does.',
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import type { Field, Fields } from "../fields/Field.js";
|
|
2
|
-
|
|
3
|
-
export abstract class FieldGroup {
|
|
4
|
-
constructor(readonly members: readonly string[]) {}
|
|
5
|
-
|
|
6
|
-
protected withMembers(members: readonly string[]): this {
|
|
7
|
-
return new (this.constructor as new (members: readonly string[]) => this)(
|
|
8
|
-
members,
|
|
9
|
-
);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
get isSelf(): boolean {
|
|
13
|
-
return this.members.length === 0;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
resolvedOn(ownKey: string): this {
|
|
17
|
-
return this.isSelf ? this.withMembers([ownKey]) : this;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
covers(key: string): boolean {
|
|
21
|
-
return this.members.includes(key);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
equals(other: FieldGroup): boolean {
|
|
25
|
-
return (
|
|
26
|
-
this.constructor === other.constructor &&
|
|
27
|
-
this.members.length === other.members.length &&
|
|
28
|
-
this.members.every((member, i) => member === other.members[i])
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
onto(fields: Fields): Fields {
|
|
33
|
-
const missing = this.members.filter((key) => !(key in fields));
|
|
34
|
-
if (missing.length)
|
|
35
|
-
throw new Error(
|
|
36
|
-
`${this.constructor.name.toLowerCase()}: [${this.members.join(", ")}] names ` +
|
|
37
|
-
`${missing.map((m) => `'${m}'`).join(", ")}, which the entity does not declare.`,
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const out: Fields = { ...fields };
|
|
41
|
-
for (const key of this.members) {
|
|
42
|
-
const field = out[key]!;
|
|
43
|
-
out[key] = field.with({
|
|
44
|
-
role: { ...field.role, rules: [...(field.role?.rules ?? []), this] },
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
return out;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
rename(map: (key: string) => string | undefined): this | null {
|
|
51
|
-
if (this.isSelf) return this;
|
|
52
|
-
const carried = this.members.map(map);
|
|
53
|
-
return carried.every((key): key is string => key !== undefined)
|
|
54
|
-
? this.withMembers(carried)
|
|
55
|
-
: null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
toJSON(): string[] {
|
|
59
|
-
return [...this.members];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
static normalize<R extends FieldGroup>(
|
|
63
|
-
groups: readonly (R | readonly string[])[] | undefined,
|
|
64
|
-
make: (members: readonly string[]) => R,
|
|
65
|
-
): readonly R[] | undefined {
|
|
66
|
-
if (!groups) return undefined;
|
|
67
|
-
return groups.map((group) =>
|
|
68
|
-
group instanceof FieldGroup ? (group as R) : make(group),
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
static on<R extends FieldGroup>(
|
|
73
|
-
field: Field,
|
|
74
|
-
kind: Function & { prototype: R },
|
|
75
|
-
): R[] {
|
|
76
|
-
return (field.role?.rules ?? []).filter(
|
|
77
|
-
(rule): rule is R => rule instanceof kind,
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
static groupsOf<R extends FieldGroup>(
|
|
82
|
-
fields: Fields,
|
|
83
|
-
kind: Function & { prototype: R },
|
|
84
|
-
): R[] {
|
|
85
|
-
const seen = new Map<string, R>();
|
|
86
|
-
for (const field of Object.values(fields))
|
|
87
|
-
for (const rule of FieldGroup.on(field, kind))
|
|
88
|
-
if (rule.members.length > 1) seen.set(rule.members.join(" "), rule);
|
|
89
|
-
return [...seen.values()];
|
|
90
|
-
}
|
|
91
|
-
}
|
package/src/constraint/Unique.ts
DELETED
package/src/fields/Field.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import type { Shape } from "../axis/shape/Shape.js";
|
|
2
|
-
import { Role, type RoleRules } from "../axis/role/Role.js";
|
|
3
|
-
import type { LifecycleRules } from "../axis/lifecycle/Lifecycle.js";
|
|
4
|
-
import type { BoundaryRef } from "../axis/boundary/Boundary.js";
|
|
5
|
-
import type { Meta } from "../axis/Meta.js";
|
|
6
|
-
import type { Axis } from "../axis/Axis.js";
|
|
7
|
-
import { FieldJudge } from "../judge/FieldJudge.js";
|
|
8
|
-
import { ValueJudge } from "../judge/ValueJudge.js";
|
|
9
|
-
|
|
10
|
-
export type Fields = Record<string, Field>;
|
|
11
|
-
|
|
12
|
-
/** A key of a field set — the name a declaration addresses a field by. */
|
|
13
|
-
export type FieldName<TFields extends Fields> = Extract<keyof TFields, string>;
|
|
14
|
-
|
|
15
|
-
/** The carrier, the three extension axes, the annotation — the five the judge reads. */
|
|
16
|
-
export type FieldDeclaration = Pick<Field, "shape" | Axis["slot"] | "meta">;
|
|
17
|
-
|
|
18
|
-
export class Field<T = unknown> {
|
|
19
|
-
readonly shape: Shape;
|
|
20
|
-
readonly role?: RoleRules;
|
|
21
|
-
readonly lifecycle?: LifecycleRules;
|
|
22
|
-
readonly boundary?: BoundaryRef;
|
|
23
|
-
readonly meta?: Meta;
|
|
24
|
-
|
|
25
|
-
declare readonly _type?: T;
|
|
26
|
-
|
|
27
|
-
constructor(init: FieldDeclaration, key?: string) {
|
|
28
|
-
const verdict = FieldJudge.of(init).verdict;
|
|
29
|
-
|
|
30
|
-
if (!verdict.success) {
|
|
31
|
-
throw new Error(
|
|
32
|
-
`${key ? `Field '${key}': ` : ""}` +
|
|
33
|
-
verdict.errors.map((e) => `${e.path}: ${e.message}`).join("; "),
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
this.shape = init.shape;
|
|
38
|
-
this.role = Role.resolvedOn(init.role, key);
|
|
39
|
-
this.lifecycle = init.lifecycle;
|
|
40
|
-
this.boundary = init.boundary;
|
|
41
|
-
this.meta = init.meta;
|
|
42
|
-
|
|
43
|
-
const create = this.lifecycle?.create;
|
|
44
|
-
if (typeof create === "object" && create !== null && "value" in create) {
|
|
45
|
-
const checked = ValueJudge.of(this).check(create.value);
|
|
46
|
-
if ("error" in checked)
|
|
47
|
-
throw new Error(
|
|
48
|
-
`${key ? `Field '${key}': ` : ""}the declared default ${JSON.stringify(create.value)} ` +
|
|
49
|
-
`is not a legal value for it — ${checked.error}.`,
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
static is(value: unknown): value is Field {
|
|
55
|
-
return FieldJudge.of(value).verdict.success;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
with<U = T>(overrides: Partial<FieldDeclaration>): Field<U> {
|
|
59
|
-
return new Field<U>({ ...this, ...overrides });
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
rename(map: (key: string) => string | undefined): Field<T> {
|
|
63
|
-
const rules = this.role?.rules;
|
|
64
|
-
if (!rules?.length) return this;
|
|
65
|
-
|
|
66
|
-
const carried = rules
|
|
67
|
-
.map((group) => group.rename(map))
|
|
68
|
-
.filter((group) => group !== null);
|
|
69
|
-
const unchanged =
|
|
70
|
-
carried.length === rules.length &&
|
|
71
|
-
carried.every((group, i) => group.equals(rules[i]!));
|
|
72
|
-
if (unchanged) return this;
|
|
73
|
-
|
|
74
|
-
const { rules: _dropped, ...rest } = this.role!;
|
|
75
|
-
return this.with({
|
|
76
|
-
role: carried.length ? { ...rest, rules: carried } : rest,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
package/src/fields/FieldSet.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { Role } from '../axis/role/Role.js';
|
|
2
|
-
import { FieldGroup } from '../constraint/FieldGroup.js';
|
|
3
|
-
import { Unique } from '../constraint/Unique.js';
|
|
4
|
-
import type { CompositeUnique } from '../EntityDeclarations.js';
|
|
5
|
-
import { Field, type FieldName, type Fields } from './Field.js';
|
|
6
|
-
|
|
7
|
-
export class FieldSet<TFields extends Fields = Fields> {
|
|
8
|
-
private constructor(private readonly fields: TFields) {}
|
|
9
|
-
|
|
10
|
-
static of<TFields extends Fields>(fields: TFields): FieldSet<TFields> {
|
|
11
|
-
return new FieldSet(fields);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Every entry through the field door, then the groups declared over several of them.
|
|
16
|
-
* A group written here names its members; one written on a field named itself, and the
|
|
17
|
-
* key it sat under resolved it.
|
|
18
|
-
*/
|
|
19
|
-
static declared<TFields extends Fields>(
|
|
20
|
-
declared: TFields,
|
|
21
|
-
unique?: CompositeUnique<TFields>,
|
|
22
|
-
): TFields {
|
|
23
|
-
let fields: Fields = {};
|
|
24
|
-
for (const [key, field] of Object.entries(declared)) fields[key] = new Field(field, key);
|
|
25
|
-
for (const group of unique ?? []) fields = new Unique(group).onto(fields);
|
|
26
|
-
return fields as TFields;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The name of the field that carries `primary`, or `undefined` when none does.
|
|
31
|
-
*
|
|
32
|
-
* `Role.of(field).isPrimary` answers about ONE field; this answers about a shape, which
|
|
33
|
-
* is the question every reader actually had. It was the same three lines spelled five
|
|
34
|
-
* times — a mirror's key, a form's row identity, GraphQL's node id, the DDL's primary
|
|
35
|
-
* key and the ORM's `findById` column — so a shape with two primaries meant whichever
|
|
36
|
-
* one that loop happened to see first, five times over.
|
|
37
|
-
*
|
|
38
|
-
* The absence is answered and not defaulted: `'id'` is a fine fallback for a form and a
|
|
39
|
-
* lie for a DDL, so the caller decides.
|
|
40
|
-
*/
|
|
41
|
-
get primary(): FieldName<TFields> | undefined {
|
|
42
|
-
const primaries = Object.entries(this.fields)
|
|
43
|
-
.filter(([, field]) => Role.of(field).isPrimary)
|
|
44
|
-
.map(([name]) => name);
|
|
45
|
-
|
|
46
|
-
if (primaries.length > 1) {
|
|
47
|
-
throw new Error(
|
|
48
|
-
`FieldSet.primary: ${primaries.map((name) => JSON.stringify(name)).join(', ')} all declare ` +
|
|
49
|
-
'`primary`; a field set can have only one primary field.',
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return primaries[0] as FieldName<TFields> | undefined;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
get uniqueGroups(): CompositeUnique<TFields> | undefined {
|
|
57
|
-
const groups = FieldGroup.groupsOf(this.fields, Unique);
|
|
58
|
-
return groups.length
|
|
59
|
-
? groups.map((group) => [...group.members]) as unknown as CompositeUnique<TFields>
|
|
60
|
-
: undefined;
|
|
61
|
-
}
|
|
62
|
-
}
|