@fougere/schema 0.2.0-alpha.2 → 0.4.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 +1 -1
- package/dist/EntityAdapters.d.ts +26 -0
- package/dist/EntityAdapters.d.ts.map +1 -0
- package/dist/EntityAdapters.js +71 -0
- package/dist/EntityAdapters.js.map +1 -0
- package/dist/EntityDeclarations.d.ts +15 -0
- package/dist/EntityDeclarations.d.ts.map +1 -0
- package/dist/EntityDeclarations.js +2 -0
- package/dist/EntityDeclarations.js.map +1 -0
- package/dist/axis/Axis.d.ts +12 -0
- package/dist/axis/Axis.d.ts.map +1 -0
- package/dist/axis/Axis.js +11 -0
- package/dist/axis/Axis.js.map +1 -0
- package/dist/axis/Meta.d.ts +4 -0
- package/dist/axis/Meta.d.ts.map +1 -0
- package/dist/axis/Meta.js +2 -0
- package/dist/axis/Meta.js.map +1 -0
- package/dist/axis/boundary/Boundaries.d.ts +20 -0
- package/dist/axis/boundary/Boundaries.d.ts.map +1 -0
- package/dist/axis/boundary/Boundaries.js +31 -0
- package/dist/axis/boundary/Boundaries.js.map +1 -0
- package/dist/axis/boundary/Boundary.d.ts +26 -0
- package/dist/axis/boundary/Boundary.d.ts.map +1 -0
- package/dist/axis/boundary/Boundary.js +53 -0
- package/dist/axis/boundary/Boundary.js.map +1 -0
- package/dist/axis/boundary/BoundaryAxis.d.ts +4 -0
- package/dist/axis/boundary/BoundaryAxis.d.ts.map +1 -0
- package/dist/axis/boundary/BoundaryAxis.js +30 -0
- package/dist/axis/boundary/BoundaryAxis.js.map +1 -0
- package/dist/axis/lifecycle/Clock.d.ts +18 -0
- package/dist/axis/lifecycle/Clock.d.ts.map +1 -0
- package/dist/axis/lifecycle/Clock.js +25 -0
- package/dist/axis/lifecycle/Clock.js.map +1 -0
- package/dist/axis/lifecycle/Generators.d.ts +18 -0
- package/dist/axis/lifecycle/Generators.d.ts.map +1 -0
- package/dist/axis/lifecycle/Generators.js +38 -0
- package/dist/axis/lifecycle/Generators.js.map +1 -0
- package/dist/axis/lifecycle/Lifecycle.d.ts +28 -0
- package/dist/axis/lifecycle/Lifecycle.d.ts.map +1 -0
- package/dist/axis/lifecycle/Lifecycle.js +38 -0
- package/dist/axis/lifecycle/Lifecycle.js.map +1 -0
- package/dist/axis/lifecycle/LifecycleAxis.d.ts +4 -0
- package/dist/axis/lifecycle/LifecycleAxis.d.ts.map +1 -0
- package/dist/axis/lifecycle/LifecycleAxis.js +47 -0
- package/dist/axis/lifecycle/LifecycleAxis.js.map +1 -0
- package/dist/axis/lifecycle/apply.d.ts +6 -0
- package/dist/axis/lifecycle/apply.d.ts.map +1 -0
- package/dist/axis/lifecycle/apply.js +34 -0
- package/dist/axis/lifecycle/apply.js.map +1 -0
- package/dist/axis/role/Relation.d.ts +20 -0
- package/dist/axis/role/Relation.d.ts.map +1 -0
- package/dist/axis/role/Relation.js +21 -0
- package/dist/axis/role/Relation.js.map +1 -0
- package/dist/axis/role/Role.d.ts +26 -0
- package/dist/axis/role/Role.d.ts.map +1 -0
- package/dist/axis/role/Role.js +40 -0
- package/dist/axis/role/Role.js.map +1 -0
- package/dist/axis/role/RoleAxis.d.ts +5 -0
- package/dist/axis/role/RoleAxis.d.ts.map +1 -0
- package/dist/axis/role/RoleAxis.js +102 -0
- package/dist/axis/role/RoleAxis.js.map +1 -0
- package/dist/axis/shape/Formats.d.ts +8 -0
- package/dist/axis/shape/Formats.d.ts.map +1 -0
- package/dist/axis/shape/Formats.js +10 -0
- package/dist/axis/shape/Formats.js.map +1 -0
- package/dist/axis/shape/Shape.d.ts +58 -0
- package/dist/axis/shape/Shape.d.ts.map +1 -0
- package/dist/{field/shape.js → axis/shape/Shape.js} +10 -25
- package/dist/axis/shape/Shape.js.map +1 -0
- package/dist/card/Bundle.d.ts +15 -0
- package/dist/card/Bundle.d.ts.map +1 -0
- package/dist/card/Bundle.js +67 -0
- package/dist/card/Bundle.js.map +1 -0
- package/dist/card/Card.d.ts +27 -0
- package/dist/card/Card.d.ts.map +1 -0
- package/dist/card/Card.js +230 -0
- package/dist/card/Card.js.map +1 -0
- package/dist/card/Descriptor.d.ts +55 -0
- package/dist/card/Descriptor.d.ts.map +1 -0
- package/dist/card/Descriptor.js +2 -0
- package/dist/card/Descriptor.js.map +1 -0
- package/dist/card/EntityTypeSource.d.ts +12 -0
- package/dist/card/EntityTypeSource.d.ts.map +1 -0
- package/dist/card/EntityTypeSource.js +78 -0
- package/dist/card/EntityTypeSource.js.map +1 -0
- package/dist/card/FacadeTypeSource.d.ts +19 -0
- package/dist/card/FacadeTypeSource.d.ts.map +1 -0
- package/dist/card/FacadeTypeSource.js +40 -0
- package/dist/card/FacadeTypeSource.js.map +1 -0
- package/dist/card/admission.d.ts +9 -0
- package/dist/card/admission.d.ts.map +1 -0
- package/dist/card/admission.js +16 -0
- package/dist/card/admission.js.map +1 -0
- package/dist/card/diff.d.ts +96 -0
- package/dist/card/diff.d.ts.map +1 -0
- package/dist/card/diff.js +2 -0
- package/dist/card/diff.js.map +1 -0
- package/dist/constraint/FieldGroup.d.ts +21 -0
- package/dist/constraint/FieldGroup.d.ts.map +1 -0
- package/dist/constraint/FieldGroup.js +65 -0
- package/dist/constraint/FieldGroup.js.map +1 -0
- package/dist/constraint/Unique.d.ts +4 -0
- package/dist/constraint/Unique.d.ts.map +1 -0
- package/dist/constraint/Unique.js +4 -0
- package/dist/constraint/Unique.js.map +1 -0
- package/dist/entity.d.ts +4 -19
- package/dist/entity.d.ts.map +1 -1
- package/dist/entity.js +8 -39
- package/dist/entity.js.map +1 -1
- package/dist/fields/Field.d.ts +24 -0
- package/dist/fields/Field.d.ts.map +1 -0
- package/dist/fields/Field.js +52 -0
- package/dist/fields/Field.js.map +1 -0
- package/dist/fields/FieldSet.d.ts +28 -0
- package/dist/fields/FieldSet.d.ts.map +1 -0
- package/dist/fields/FieldSet.js +55 -0
- package/dist/fields/FieldSet.js.map +1 -0
- package/dist/index.d.ts +59 -37
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -48
- package/dist/index.js.map +1 -1
- package/dist/judge/Cases.d.ts +50 -0
- package/dist/judge/Cases.d.ts.map +1 -0
- package/dist/judge/Cases.js +108 -0
- package/dist/judge/Cases.js.map +1 -0
- package/dist/judge/FieldJudge.d.ts +9 -0
- package/dist/judge/FieldJudge.d.ts.map +1 -0
- package/dist/judge/FieldJudge.js +54 -0
- package/dist/judge/FieldJudge.js.map +1 -0
- package/dist/judge/RowJudge.d.ts +12 -0
- package/dist/judge/RowJudge.d.ts.map +1 -0
- package/dist/judge/RowJudge.js +81 -0
- package/dist/judge/RowJudge.js.map +1 -0
- package/dist/judge/RowRefusal.d.ts +17 -0
- package/dist/judge/RowRefusal.d.ts.map +1 -0
- package/dist/judge/RowRefusal.js +16 -0
- package/dist/judge/RowRefusal.js.map +1 -0
- package/dist/judge/ValueForm.d.ts +3 -0
- package/dist/judge/ValueForm.d.ts.map +1 -0
- package/dist/judge/ValueForm.js +3 -0
- package/dist/judge/ValueForm.js.map +1 -0
- package/dist/judge/ValueJudge.d.ts +12 -0
- package/dist/judge/ValueJudge.d.ts.map +1 -0
- package/dist/judge/ValueJudge.js +60 -0
- package/dist/judge/ValueJudge.js.map +1 -0
- package/dist/judge/options.d.ts +4 -0
- package/dist/judge/options.d.ts.map +1 -0
- package/dist/judge/options.js +2 -0
- package/dist/judge/options.js.map +1 -0
- package/dist/judge/result.d.ts +17 -0
- package/dist/judge/result.d.ts.map +1 -0
- package/dist/judge/result.js +2 -0
- package/dist/judge/result.js.map +1 -0
- package/dist/projection/SchemaOrCard.d.ts +7 -0
- package/dist/projection/SchemaOrCard.d.ts.map +1 -0
- package/dist/projection/SchemaOrCard.js +11 -0
- package/dist/projection/SchemaOrCard.js.map +1 -0
- package/dist/projection/Visibility.d.ts +10 -0
- package/dist/projection/Visibility.d.ts.map +1 -0
- package/dist/projection/Visibility.js +55 -0
- package/dist/projection/Visibility.js.map +1 -0
- package/dist/{projections → projection}/standard.d.ts +2 -10
- package/dist/projection/standard.d.ts.map +1 -0
- package/dist/projection/standard.js +2 -0
- package/dist/projection/standard.js.map +1 -0
- package/dist/schema/Schema.d.ts +63 -0
- package/dist/schema/Schema.d.ts.map +1 -0
- package/dist/schema/Schema.js +129 -0
- package/dist/schema/Schema.js.map +1 -0
- package/dist/schema/SchemaDefinition.d.ts +46 -0
- package/dist/schema/SchemaDefinition.d.ts.map +1 -0
- package/dist/schema/SchemaDefinition.js +64 -0
- package/dist/schema/SchemaDefinition.js.map +1 -0
- package/dist/schema/SchemaDerivation.d.ts +28 -0
- package/dist/schema/SchemaDerivation.d.ts.map +1 -0
- package/dist/schema/SchemaDerivation.js +37 -0
- package/dist/schema/SchemaDerivation.js.map +1 -0
- package/dist/schema/SchemaView.d.ts +23 -0
- package/dist/schema/SchemaView.d.ts.map +1 -0
- package/dist/schema/SchemaView.js +2 -0
- package/dist/schema/SchemaView.js.map +1 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +13 -0
- package/dist/utils.js.map +1 -0
- package/dist/vocabulary/bool.d.ts +1 -1
- package/dist/vocabulary/bool.d.ts.map +1 -1
- package/dist/vocabulary/bool.js +1 -1
- package/dist/vocabulary/bool.js.map +1 -1
- package/dist/vocabulary/created.d.ts +1 -5
- package/dist/vocabulary/created.d.ts.map +1 -1
- package/dist/vocabulary/created.js +1 -5
- package/dist/vocabulary/created.js.map +1 -1
- package/dist/vocabulary/date.d.ts +1 -1
- package/dist/vocabulary/date.d.ts.map +1 -1
- package/dist/vocabulary/date.js +1 -1
- package/dist/vocabulary/date.js.map +1 -1
- package/dist/vocabulary/email.d.ts +1 -2
- package/dist/vocabulary/email.d.ts.map +1 -1
- package/dist/vocabulary/email.js +0 -1
- package/dist/vocabulary/email.js.map +1 -1
- package/dist/vocabulary/immutable.d.ts +2 -6
- package/dist/vocabulary/immutable.d.ts.map +1 -1
- package/dist/vocabulary/immutable.js +4 -7
- package/dist/vocabulary/immutable.js.map +1 -1
- package/dist/vocabulary/indexed.d.ts +2 -7
- package/dist/vocabulary/indexed.d.ts.map +1 -1
- package/dist/vocabulary/indexed.js +2 -8
- package/dist/vocabulary/indexed.js.map +1 -1
- package/dist/vocabulary/json.d.ts +2 -10
- package/dist/vocabulary/json.d.ts.map +1 -1
- package/dist/vocabulary/json.js +12 -7
- package/dist/vocabulary/json.js.map +1 -1
- package/dist/vocabulary/list.d.ts +1 -12
- package/dist/vocabulary/list.d.ts.map +1 -1
- package/dist/vocabulary/list.js +1 -14
- package/dist/vocabulary/list.js.map +1 -1
- package/dist/vocabulary/many.d.ts +2 -5
- package/dist/vocabulary/many.d.ts.map +1 -1
- package/dist/vocabulary/many.js +3 -6
- package/dist/vocabulary/many.js.map +1 -1
- package/dist/vocabulary/nullable.d.ts +2 -6
- package/dist/vocabulary/nullable.d.ts.map +1 -1
- package/dist/vocabulary/nullable.js +5 -8
- package/dist/vocabulary/nullable.js.map +1 -1
- package/dist/vocabulary/number.d.ts +1 -1
- package/dist/vocabulary/number.d.ts.map +1 -1
- package/dist/vocabulary/number.js +1 -1
- package/dist/vocabulary/number.js.map +1 -1
- package/dist/vocabulary/oneOf.d.ts +1 -8
- package/dist/vocabulary/oneOf.d.ts.map +1 -1
- package/dist/vocabulary/oneOf.js +1 -1
- package/dist/vocabulary/oneOf.js.map +1 -1
- package/dist/vocabulary/optional.d.ts +2 -9
- package/dist/vocabulary/optional.d.ts.map +1 -1
- package/dist/vocabulary/optional.js +6 -14
- package/dist/vocabulary/optional.js.map +1 -1
- package/dist/vocabulary/primary.d.ts +2 -15
- package/dist/vocabulary/primary.d.ts.map +1 -1
- package/dist/vocabulary/primary.js +4 -11
- package/dist/vocabulary/primary.js.map +1 -1
- package/dist/vocabulary/readOnly.d.ts +2 -6
- package/dist/vocabulary/readOnly.d.ts.map +1 -1
- package/dist/vocabulary/readOnly.js +5 -8
- package/dist/vocabulary/readOnly.js.map +1 -1
- package/dist/vocabulary/ref.d.ts +2 -7
- package/dist/vocabulary/ref.d.ts.map +1 -1
- package/dist/vocabulary/ref.js +3 -14
- package/dist/vocabulary/ref.js.map +1 -1
- package/dist/vocabulary/text.d.ts +2 -2
- package/dist/vocabulary/text.d.ts.map +1 -1
- package/dist/vocabulary/text.js +1 -1
- package/dist/vocabulary/text.js.map +1 -1
- package/dist/vocabulary/unique.d.ts +2 -12
- package/dist/vocabulary/unique.d.ts.map +1 -1
- package/dist/vocabulary/unique.js +5 -13
- package/dist/vocabulary/unique.js.map +1 -1
- package/dist/vocabulary/updated.d.ts +1 -6
- package/dist/vocabulary/updated.d.ts.map +1 -1
- package/dist/vocabulary/updated.js +0 -5
- package/dist/vocabulary/updated.js.map +1 -1
- package/dist/vocabulary/url.d.ts +1 -2
- package/dist/vocabulary/url.d.ts.map +1 -1
- package/dist/vocabulary/url.js +0 -1
- package/dist/vocabulary/url.js.map +1 -1
- package/dist/vocabulary/vocabulary.d.ts +4 -0
- package/dist/vocabulary/vocabulary.d.ts.map +1 -0
- package/dist/vocabulary/vocabulary.js +31 -0
- package/dist/vocabulary/vocabulary.js.map +1 -0
- package/dist/vocabulary/writeOnly.d.ts +2 -6
- package/dist/vocabulary/writeOnly.d.ts.map +1 -1
- package/dist/vocabulary/writeOnly.js +5 -8
- package/dist/vocabulary/writeOnly.js.map +1 -1
- package/package.json +8 -4
- package/src/EntityAdapters.ts +95 -0
- package/src/EntityDeclarations.ts +19 -0
- package/src/axis/Axis.ts +25 -0
- package/src/axis/Meta.ts +3 -0
- package/src/axis/boundary/Boundaries.ts +44 -0
- package/src/axis/boundary/Boundary.ts +69 -0
- package/src/axis/boundary/BoundaryAxis.ts +32 -0
- package/src/axis/lifecycle/Clock.ts +26 -0
- package/src/axis/lifecycle/Generators.ts +46 -0
- package/src/axis/lifecycle/Lifecycle.ts +53 -0
- package/src/axis/lifecycle/LifecycleAxis.ts +49 -0
- package/src/axis/lifecycle/apply.ts +38 -0
- package/src/axis/role/Relation.ts +31 -0
- package/src/axis/role/Role.ts +56 -0
- package/src/axis/role/RoleAxis.ts +115 -0
- package/src/axis/shape/Formats.ts +22 -0
- package/src/axis/shape/Shape.ts +91 -0
- package/src/card/Bundle.ts +80 -0
- package/src/card/Card.ts +256 -0
- package/src/card/Descriptor.ts +68 -0
- package/src/card/EntityTypeSource.ts +90 -0
- package/src/card/FacadeTypeSource.ts +55 -0
- package/src/card/admission.ts +25 -0
- package/src/card/diff.ts +56 -0
- package/src/constraint/FieldGroup.ts +91 -0
- package/src/constraint/Unique.ts +3 -0
- package/src/entity.ts +15 -0
- package/src/fields/Field.ts +80 -0
- package/src/fields/FieldSet.ts +62 -0
- package/src/index.ts +64 -0
- package/src/judge/Cases.ts +139 -0
- package/src/judge/FieldJudge.ts +61 -0
- package/src/judge/RowJudge.ts +84 -0
- package/src/judge/RowRefusal.ts +17 -0
- package/src/judge/ValueForm.ts +7 -0
- package/src/judge/ValueJudge.ts +72 -0
- package/src/judge/options.ts +3 -0
- package/src/judge/result.ts +10 -0
- package/src/projection/SchemaOrCard.ts +18 -0
- package/src/projection/Visibility.ts +47 -0
- package/src/projection/standard.ts +47 -0
- package/src/schema/Schema.ts +167 -0
- package/src/schema/SchemaDefinition.ts +87 -0
- package/src/schema/SchemaDerivation.ts +46 -0
- package/src/schema/SchemaView.ts +25 -0
- package/src/utils.ts +13 -0
- package/src/vocabulary/bool.ts +9 -0
- package/src/vocabulary/created.ts +8 -0
- package/src/vocabulary/date.ts +5 -0
- package/src/vocabulary/email.ts +6 -0
- package/src/vocabulary/immutable.ts +6 -0
- package/src/vocabulary/indexed.ts +4 -0
- package/src/vocabulary/json.ts +22 -0
- package/src/vocabulary/list.ts +13 -0
- package/src/vocabulary/many.ts +9 -0
- package/src/vocabulary/nullable.ts +7 -0
- package/src/vocabulary/number.ts +17 -0
- package/src/vocabulary/oneOf.ts +24 -0
- package/src/vocabulary/optional.ts +8 -0
- package/src/vocabulary/primary.ts +33 -0
- package/src/vocabulary/readOnly.ts +7 -0
- package/src/vocabulary/ref.ts +13 -0
- package/src/vocabulary/text.ts +19 -0
- package/src/vocabulary/unique.ts +10 -0
- package/src/vocabulary/updated.ts +7 -0
- package/src/vocabulary/url.ts +6 -0
- package/src/vocabulary/vocabulary.ts +40 -0
- package/src/vocabulary/writeOnly.ts +7 -0
- package/dist/field/boundary.d.ts +0 -69
- package/dist/field/boundary.d.ts.map +0 -1
- package/dist/field/boundary.js +0 -82
- package/dist/field/boundary.js.map +0 -1
- package/dist/field/field.d.ts +0 -21
- package/dist/field/field.d.ts.map +0 -1
- package/dist/field/field.js +0 -24
- package/dist/field/field.js.map +0 -1
- package/dist/field/format.d.ts +0 -29
- package/dist/field/format.d.ts.map +0 -1
- package/dist/field/format.js +0 -24
- package/dist/field/format.js.map +0 -1
- package/dist/field/index.d.ts +0 -8
- package/dist/field/index.d.ts.map +0 -1
- package/dist/field/index.js +0 -10
- package/dist/field/index.js.map +0 -1
- package/dist/field/lifecycle.d.ts +0 -31
- package/dist/field/lifecycle.d.ts.map +0 -1
- package/dist/field/lifecycle.js +0 -20
- package/dist/field/lifecycle.js.map +0 -1
- package/dist/field/meta.d.ts +0 -5
- package/dist/field/meta.d.ts.map +0 -1
- package/dist/field/meta.js +0 -5
- package/dist/field/meta.js.map +0 -1
- package/dist/field/role.d.ts +0 -47
- package/dist/field/role.d.ts.map +0 -1
- package/dist/field/role.js +0 -35
- package/dist/field/role.js.map +0 -1
- package/dist/field/shape.d.ts +0 -89
- package/dist/field/shape.d.ts.map +0 -1
- package/dist/field/shape.js.map +0 -1
- package/dist/hints.d.ts +0 -28
- package/dist/hints.d.ts.map +0 -1
- package/dist/hints.js +0 -23
- package/dist/hints.js.map +0 -1
- package/dist/name.d.ts +0 -8
- package/dist/name.d.ts.map +0 -1
- package/dist/name.js +0 -10
- package/dist/name.js.map +0 -1
- package/dist/projections/card.d.ts +0 -98
- package/dist/projections/card.d.ts.map +0 -1
- package/dist/projections/card.js +0 -8
- package/dist/projections/card.js.map +0 -1
- package/dist/projections/describe.d.ts +0 -25
- package/dist/projections/describe.d.ts.map +0 -1
- package/dist/projections/describe.js +0 -131
- package/dist/projections/describe.js.map +0 -1
- package/dist/projections/encode.d.ts +0 -11
- package/dist/projections/encode.d.ts.map +0 -1
- package/dist/projections/encode.js +0 -26
- package/dist/projections/encode.js.map +0 -1
- package/dist/projections/io.d.ts +0 -24
- package/dist/projections/io.d.ts.map +0 -1
- package/dist/projections/io.js +0 -46
- package/dist/projections/io.js.map +0 -1
- package/dist/projections/lifecycle.d.ts +0 -29
- package/dist/projections/lifecycle.d.ts.map +0 -1
- package/dist/projections/lifecycle.js +0 -93
- package/dist/projections/lifecycle.js.map +0 -1
- package/dist/projections/reconstruct.d.ts +0 -31
- package/dist/projections/reconstruct.d.ts.map +0 -1
- package/dist/projections/reconstruct.js +0 -146
- package/dist/projections/reconstruct.js.map +0 -1
- package/dist/projections/source.d.ts +0 -18
- package/dist/projections/source.d.ts.map +0 -1
- package/dist/projections/source.js +0 -18
- package/dist/projections/source.js.map +0 -1
- package/dist/projections/standard.d.ts.map +0 -1
- package/dist/projections/standard.js +0 -10
- package/dist/projections/standard.js.map +0 -1
- package/dist/projections/typescript.d.ts +0 -37
- package/dist/projections/typescript.d.ts.map +0 -1
- package/dist/projections/typescript.js +0 -157
- package/dist/projections/typescript.js.map +0 -1
- package/dist/schema/index.d.ts +0 -3
- package/dist/schema/index.d.ts.map +0 -1
- package/dist/schema/index.js +0 -2
- package/dist/schema/index.js.map +0 -1
- package/dist/schema/schema.d.ts +0 -74
- package/dist/schema/schema.d.ts.map +0 -1
- package/dist/schema/schema.js +0 -162
- package/dist/schema/schema.js.map +0 -1
- package/dist/schema/view.d.ts +0 -26
- package/dist/schema/view.d.ts.map +0 -1
- package/dist/schema/view.js +0 -2
- package/dist/schema/view.js.map +0 -1
- package/dist/unique.d.ts +0 -40
- package/dist/unique.d.ts.map +0 -1
- package/dist/unique.js +0 -57
- package/dist/unique.js.map +0 -1
- package/dist/validation/index.d.ts +0 -4
- package/dist/validation/index.d.ts.map +0 -1
- package/dist/validation/index.js +0 -2
- package/dist/validation/index.js.map +0 -1
- package/dist/validation/judge.d.ts +0 -55
- package/dist/validation/judge.d.ts.map +0 -1
- package/dist/validation/judge.js +0 -351
- package/dist/validation/judge.js.map +0 -1
- package/dist/validation/options.d.ts +0 -6
- package/dist/validation/options.d.ts.map +0 -1
- package/dist/validation/options.js +0 -3
- package/dist/validation/options.js.map +0 -1
- package/dist/validation/result.d.ts +0 -20
- package/dist/validation/result.d.ts.map +0 -1
- package/dist/validation/result.js +0 -3
- package/dist/validation/result.js.map +0 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Validator, format as engineFormats } from '@cfworker/json-schema';
|
|
2
|
+
import { Formats, type FormatPredicate } from '../axis/shape/Formats.js';
|
|
3
|
+
import { Anatomy, type Shape } from '../axis/shape/Shape.js';
|
|
4
|
+
import type { Field } from '../fields/Field.js';
|
|
5
|
+
import type { Checked } from './result.js';
|
|
6
|
+
|
|
7
|
+
interface ShapePlan {
|
|
8
|
+
validator: Validator;
|
|
9
|
+
custom?: FormatPredicate;
|
|
10
|
+
formatName?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class ValueJudge {
|
|
14
|
+
private static readonly plans = new WeakMap<object, ShapePlan>();
|
|
15
|
+
|
|
16
|
+
private constructor(private readonly field: Field) {}
|
|
17
|
+
|
|
18
|
+
static of(field: Field): ValueJudge {
|
|
19
|
+
return new ValueJudge(field);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
check(value: unknown): Checked {
|
|
23
|
+
const shape = this.field.shape;
|
|
24
|
+
const base = Anatomy.of(shape).base;
|
|
25
|
+
if (value !== null) {
|
|
26
|
+
if (base?.type === 'object' && !base.properties) return { value };
|
|
27
|
+
if (base?.type === 'string' && base.format === 'date-time' && value instanceof Date) {
|
|
28
|
+
return Number.isNaN(value.getTime()) ? { error: 'Invalid date' } : { value };
|
|
29
|
+
}
|
|
30
|
+
if (
|
|
31
|
+
(base?.type === 'number' || base?.type === 'integer') &&
|
|
32
|
+
typeof value === 'number' &&
|
|
33
|
+
Number.isNaN(value)
|
|
34
|
+
) {
|
|
35
|
+
return { error: 'Expected a number' };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const plan = ValueJudge.planFor(shape);
|
|
39
|
+
const result = plan.validator.validate(value);
|
|
40
|
+
if (!result.valid) return { error: result.errors[0]?.error ?? 'Invalid value' };
|
|
41
|
+
if (plan.custom && typeof value === 'string' && !plan.custom(value)) {
|
|
42
|
+
return { error: `String does not match format "${plan.formatName}".` };
|
|
43
|
+
}
|
|
44
|
+
return { value };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private static planFor(shape: Shape): ShapePlan {
|
|
48
|
+
let plan = this.plans.get(shape);
|
|
49
|
+
if (!plan) {
|
|
50
|
+
const base = Anatomy.of(shape).base;
|
|
51
|
+
const formatName = base?.type === 'string' ? base.format : undefined;
|
|
52
|
+
plan = {
|
|
53
|
+
validator: new Validator(shape as object, '2020-12', true),
|
|
54
|
+
custom: formatName === undefined ? undefined : this.customFormatOf(formatName),
|
|
55
|
+
formatName,
|
|
56
|
+
};
|
|
57
|
+
this.plans.set(shape, plan);
|
|
58
|
+
}
|
|
59
|
+
return plan;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private static customFormatOf(name: string): FormatPredicate | undefined {
|
|
63
|
+
const custom = Formats.resolve(name);
|
|
64
|
+
if (!custom && !(name in engineFormats)) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Unknown format: '${name}'. Register it with Formats.register('${name}', …) — ` +
|
|
67
|
+
`the engine judges ${Object.keys(engineFormats).length} formats natively and this is not one of them.`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return custom;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Fields } from '../fields/Field.js';
|
|
2
|
+
import type { SchemaView } from '../schema/SchemaView.js';
|
|
3
|
+
import type { SchemaDescriptor } from '../card/Descriptor.js';
|
|
4
|
+
import { Card } from '../card/Card.js';
|
|
5
|
+
|
|
6
|
+
export type SchemaOrCard = SchemaView | SchemaDescriptor;
|
|
7
|
+
|
|
8
|
+
function isDescriptor(source: SchemaOrCard): source is SchemaDescriptor {
|
|
9
|
+
return typeof (source as SchemaView).getFields !== 'function';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function schemaOf(source: SchemaOrCard): SchemaView {
|
|
13
|
+
return isDescriptor(source) ? Card.fromDescriptor(source).toSchema() : source;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function fieldsOf(source: SchemaOrCard): Fields {
|
|
17
|
+
return schemaOf(source).getFields();
|
|
18
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Boundary } from '../axis/boundary/Boundary.js';
|
|
2
|
+
import { Lifecycle } from '../axis/lifecycle/Lifecycle.js';
|
|
3
|
+
import { Role } from '../axis/role/Role.js';
|
|
4
|
+
import type { Fields } from '../fields/Field.js';
|
|
5
|
+
|
|
6
|
+
export class Visibility {
|
|
7
|
+
private constructor(private readonly fields: Fields) {}
|
|
8
|
+
|
|
9
|
+
static of(fields: Fields): Visibility {
|
|
10
|
+
return new Visibility(fields);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get input(): Fields {
|
|
14
|
+
const result: Fields = {};
|
|
15
|
+
for (const [name, field] of Object.entries(this.fields)) {
|
|
16
|
+
const role = Role.of(field);
|
|
17
|
+
if (role.isPrimary) continue;
|
|
18
|
+
if (Lifecycle.of(field).create === 'now') continue;
|
|
19
|
+
if (role.isCollection) continue;
|
|
20
|
+
if (Boundary.of(field).readOnly) continue;
|
|
21
|
+
result[name] = field;
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get output(): Fields {
|
|
27
|
+
const result: Fields = {};
|
|
28
|
+
for (const [name, field] of Object.entries(this.fields)) {
|
|
29
|
+
if (Boundary.of(field).writeOnly) continue;
|
|
30
|
+
result[name] = field;
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
encode(record: Record<string, unknown>): Record<string, unknown> {
|
|
36
|
+
const out: Record<string, unknown> = { ...record };
|
|
37
|
+
for (const [key, field] of Object.entries(this.fields)) {
|
|
38
|
+
if (!(key in record)) continue;
|
|
39
|
+
const boundary = Boundary.of(field);
|
|
40
|
+
if (boundary.writeOnly) { delete out[key]; continue; }
|
|
41
|
+
const value = record[key];
|
|
42
|
+
if (value === null || value === undefined) continue;
|
|
43
|
+
out[key] = boundary.encode(value);
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
2
|
+
readonly '~standard': StandardSchemaV1.Props<Input, Output>;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export declare namespace StandardSchemaV1 {
|
|
6
|
+
interface Props<Input = unknown, Output = Input> {
|
|
7
|
+
readonly version: 1;
|
|
8
|
+
readonly vendor: string;
|
|
9
|
+
readonly validate: (
|
|
10
|
+
value: unknown,
|
|
11
|
+
) => Result<Output> | Promise<Result<Output>>;
|
|
12
|
+
readonly types?: Types<Input, Output> | undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
16
|
+
|
|
17
|
+
interface SuccessResult<Output> {
|
|
18
|
+
readonly value: Output;
|
|
19
|
+
readonly issues?: undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface FailureResult {
|
|
23
|
+
readonly issues: readonly Issue[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface Issue {
|
|
27
|
+
readonly message: string;
|
|
28
|
+
readonly path?: readonly (PropertyKey | PathSegment)[] | undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface PathSegment {
|
|
32
|
+
readonly key: PropertyKey;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface Types<Input = unknown, Output = Input> {
|
|
36
|
+
readonly input: Input;
|
|
37
|
+
readonly output: Output;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type InferInput<Schema extends StandardSchemaV1> = NonNullable<
|
|
41
|
+
Schema['~standard']['types']
|
|
42
|
+
>['input'];
|
|
43
|
+
|
|
44
|
+
type InferOutput<Schema extends StandardSchemaV1> = NonNullable<
|
|
45
|
+
Schema['~standard']['types']
|
|
46
|
+
>['output'];
|
|
47
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { Boundary } from '../axis/boundary/Boundary.js';
|
|
2
|
+
import { type Fields } from '../fields/Field.js';
|
|
3
|
+
import { type PreviousNames } from '../EntityDeclarations.js';
|
|
4
|
+
import { type EntityAdapters } from '../EntityAdapters.js';
|
|
5
|
+
import { FieldGroup } from '../constraint/FieldGroup.js';
|
|
6
|
+
import { Unique } from '../constraint/Unique.js';
|
|
7
|
+
import { type CompositeUnique } from '../EntityDeclarations.js';
|
|
8
|
+
import { RowJudge } from '../judge/RowJudge.js';
|
|
9
|
+
import { SchemaDerivation } from './SchemaDerivation.js';
|
|
10
|
+
import { SchemaDefinition } from './SchemaDefinition.js';
|
|
11
|
+
import { type ValidateOptions } from '../judge/options.js';
|
|
12
|
+
import type { StandardSchemaV1 } from '../projection/standard.js';
|
|
13
|
+
import type { PartialRow, Row, SchemaView } from './SchemaView.js';
|
|
14
|
+
|
|
15
|
+
export const ANONYMOUS_SCHEMA_NAME = "Schema";
|
|
16
|
+
|
|
17
|
+
export interface SchemaConstructor<TFields extends Fields> extends SchemaView<TFields> {
|
|
18
|
+
new (data: PartialRow<TFields>): Row<TFields>;
|
|
19
|
+
readonly "~standard": StandardSchemaV1.Props<Record<string, unknown>, Row<TFields>>;
|
|
20
|
+
readonly derivation?: SchemaDerivation;
|
|
21
|
+
readonly previous?: PreviousNames<TFields>;
|
|
22
|
+
from(data: Record<string, unknown>): Row<TFields>;
|
|
23
|
+
pick<K extends string & keyof TFields>(...keys: K[]): SchemaConstructor<Pick<TFields, K>>;
|
|
24
|
+
omit<K extends string & keyof TFields>(...keys: K[]): SchemaConstructor<Omit<TFields, K>>;
|
|
25
|
+
partial(): SchemaConstructor<TFields>;
|
|
26
|
+
extend<E extends Fields>(extra: E): SchemaConstructor<TFields & E>;
|
|
27
|
+
named(name: string): SchemaConstructor<TFields>;
|
|
28
|
+
rename(mapping: Partial<Record<string & keyof TFields, string>>): SchemaConstructor<Fields>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class Schema {
|
|
32
|
+
/** The one place a schema holds what it is. The five readings below are its projections. */
|
|
33
|
+
static definition: SchemaDefinition = SchemaDefinition.stated({ fields: {} });
|
|
34
|
+
|
|
35
|
+
static get fields(): Fields { return this.definition.fields; }
|
|
36
|
+
static get adapters(): EntityAdapters<Fields> | undefined { return this.definition.adapters; }
|
|
37
|
+
static get opts(): ValidateOptions { return this.definition.opts; }
|
|
38
|
+
static get derivation(): SchemaDerivation | undefined { return this.definition.derivation; }
|
|
39
|
+
static get previous(): PreviousNames<Fields> | undefined { return this.definition.previous; }
|
|
40
|
+
|
|
41
|
+
constructor(data?: Record<string, unknown>) {
|
|
42
|
+
if (!data) return;
|
|
43
|
+
for (const [key, value] of Object.entries(data)) {
|
|
44
|
+
Object.defineProperty(this, key, { value, writable: true, enumerable: true, configurable: true });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static getFields() { return this.fields; }
|
|
49
|
+
static getAdapters() { return this.adapters; }
|
|
50
|
+
static getUnique(): CompositeUnique<Fields> | undefined {
|
|
51
|
+
const groups = FieldGroup.groupsOf(this.fields, Unique);
|
|
52
|
+
return groups.length ? groups.map((group) => [...group.members]) : undefined;
|
|
53
|
+
}
|
|
54
|
+
static getOpts() { return this.opts; }
|
|
55
|
+
|
|
56
|
+
static validate(input: unknown) {
|
|
57
|
+
return RowJudge.of(this.fields, this.opts).check(input);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static from(data: Record<string, unknown>) {
|
|
61
|
+
const out: Record<string, unknown> = {};
|
|
62
|
+
for (const [key, field] of Object.entries(this.fields)) {
|
|
63
|
+
if (!(key in data)) continue;
|
|
64
|
+
const value = data[key];
|
|
65
|
+
if (value === null || value === undefined) { out[key] = value; continue; }
|
|
66
|
+
const decoded = Boundary.of(field).decode(value);
|
|
67
|
+
out[key] = "error" in decoded ? value : decoded.value;
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static get ["~standard"](): StandardSchemaV1.Props<Record<string, unknown>> {
|
|
73
|
+
const { fields, opts } = this;
|
|
74
|
+
return {
|
|
75
|
+
version: 1,
|
|
76
|
+
vendor: "fougere",
|
|
77
|
+
validate(value: unknown) {
|
|
78
|
+
const result = RowJudge.of(fields, opts).check(value);
|
|
79
|
+
if (result.success) return { value: result.data };
|
|
80
|
+
return {
|
|
81
|
+
issues: result.errors.map((e) => ({
|
|
82
|
+
message: e.message,
|
|
83
|
+
path: e.path && e.path !== "." ? [{ key: e.path }] : undefined,
|
|
84
|
+
})),
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static pick(...keys: string[]) {
|
|
91
|
+
assertKnownKeys("pick", keys, this.fields);
|
|
92
|
+
const picked: Fields = {};
|
|
93
|
+
for (const key of keys) if (this.fields[key]) picked[key] = this.fields[key];
|
|
94
|
+
return this.derive(picked, (k) => (keys.includes(k) ? k : undefined));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static omit(...keys: string[]) {
|
|
98
|
+
assertKnownKeys("omit", keys, this.fields);
|
|
99
|
+
const kept: Fields = {};
|
|
100
|
+
for (const [key, field] of Object.entries(this.fields)) if (!keys.includes(key)) kept[key] = field;
|
|
101
|
+
return this.derive(kept, (k) => (keys.includes(k) ? undefined : k));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static rename(mapping: Record<string, string>) {
|
|
105
|
+
assertKnownKeys("rename", Object.keys(mapping), this.fields);
|
|
106
|
+
const renamed: Fields = {};
|
|
107
|
+
for (const [key, field] of Object.entries(this.fields)) renamed[mapping[key] ?? key] = field;
|
|
108
|
+
return this.derive(renamed, (k) => mapping[k] ?? k);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static partial() {
|
|
112
|
+
return Schema.subclass(this.definition.patched(this));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static extend(extra: Fields) {
|
|
116
|
+
return Schema.subclass(this.definition.extended(extra, this));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static named(name: string) {
|
|
120
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) {
|
|
121
|
+
throw new Error(`named(): \`${name}\` is not a valid class name.`);
|
|
122
|
+
}
|
|
123
|
+
if (this.name !== ANONYMOUS_SCHEMA_NAME) {
|
|
124
|
+
throw new Error(`named(): \`${this.name}\` is already named by its class declaration.`);
|
|
125
|
+
}
|
|
126
|
+
Object.defineProperty(this, "name", { value: name, configurable: true });
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static compose<T extends SchemaView[]>(...sources: T): SchemaConstructor<Merged<T>> {
|
|
131
|
+
return Schema.subclass(SchemaDefinition.merged(sources)) as unknown as SchemaConstructor<Merged<T>>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
static of<TFields extends Fields>(stated: {
|
|
135
|
+
fields: TFields;
|
|
136
|
+
derivation?: SchemaDerivation;
|
|
137
|
+
adapters?: EntityAdapters<TFields>;
|
|
138
|
+
opts?: ValidateOptions;
|
|
139
|
+
previous?: PreviousNames<TFields>;
|
|
140
|
+
}): SchemaConstructor<TFields> {
|
|
141
|
+
return Schema.subclass(SchemaDefinition.stated(stated)) as unknown as SchemaConstructor<TFields>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** The class a definition becomes. The one place a Schema subclass is born. */
|
|
145
|
+
private static subclass(definition: SchemaDefinition): SchemaConstructor<Fields> {
|
|
146
|
+
class Derived extends Schema {}
|
|
147
|
+
Derived.definition = definition;
|
|
148
|
+
Object.defineProperty(Derived, "name", { value: ANONYMOUS_SCHEMA_NAME, configurable: true });
|
|
149
|
+
return Derived as unknown as SchemaConstructor<Fields>;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private static derive(fields: Fields, survives: (key: string) => string | undefined) {
|
|
153
|
+
return Schema.subclass(this.definition.cut(fields, survives, this));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function assertKnownKeys(operation: string, keys: string[], fields: Fields): void {
|
|
158
|
+
const strangers = keys.filter((key) => !(key in fields));
|
|
159
|
+
if (strangers.length === 0) return;
|
|
160
|
+
throw new Error(
|
|
161
|
+
`${operation}(): unknown field ${strangers.map((s) => `\`${s}\``).join(", ")}. ` +
|
|
162
|
+
`This schema carries ${Object.keys(fields).join(", ")}.`,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
167
|
+
type Merged<T extends SchemaView[]> = UnionToIntersection<T[number] extends { getFields(): infer F } ? F : Fields> & Fields;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type Fields } from '../fields/Field.js';
|
|
2
|
+
import { type PreviousNames } from '../EntityDeclarations.js';
|
|
3
|
+
import { EntityAdapterSet, type EntityAdapters } from '../EntityAdapters.js';
|
|
4
|
+
import { SchemaDerivation } from './SchemaDerivation.js';
|
|
5
|
+
import { type ValidateOptions } from '../judge/options.js';
|
|
6
|
+
import type { SchemaView } from './SchemaView.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Everything a schema IS, apart from being a class whose instances are rows.
|
|
10
|
+
*
|
|
11
|
+
* The five members used to be five statics assigned side by side, and every derivation
|
|
12
|
+
* re-threaded them by hand — which is how one of them came to be dropped from a list
|
|
13
|
+
* without anyone noticing. A transformation here returns a COMPLETE definition or it does
|
|
14
|
+
* not compile, so no gesture can mix the fields of one with the options of another.
|
|
15
|
+
*
|
|
16
|
+
* Internal: it is not exported from the package and never appears on `SchemaConstructor`.
|
|
17
|
+
* A user derives with `Order.pick(…)`, never `Order.definition.pick(…)`.
|
|
18
|
+
*/
|
|
19
|
+
export class SchemaDefinition {
|
|
20
|
+
private constructor(
|
|
21
|
+
readonly fields: Fields,
|
|
22
|
+
readonly adapters: EntityAdapters<Fields> | undefined,
|
|
23
|
+
readonly opts: ValidateOptions,
|
|
24
|
+
readonly previous: PreviousNames<Fields> | undefined,
|
|
25
|
+
readonly derivation: SchemaDerivation | undefined,
|
|
26
|
+
) {}
|
|
27
|
+
|
|
28
|
+
static stated(said: {
|
|
29
|
+
fields: Fields;
|
|
30
|
+
adapters?: EntityAdapters<Fields>;
|
|
31
|
+
opts?: ValidateOptions;
|
|
32
|
+
previous?: PreviousNames<Fields>;
|
|
33
|
+
derivation?: SchemaDerivation;
|
|
34
|
+
}): SchemaDefinition {
|
|
35
|
+
return new SchemaDefinition(
|
|
36
|
+
said.fields, EntityAdapterSet.of(said.adapters)?.stated, said.opts ?? {}, said.previous, said.derivation,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* `pick`, `omit` and `rename` are one gesture: the fields that remain, and what became
|
|
42
|
+
* of each name. What each adapter was handed follows the same correspondence, and so does the origin.
|
|
43
|
+
*/
|
|
44
|
+
cut(fields: Fields, survives: (key: string) => string | undefined, root: SchemaView): SchemaDefinition {
|
|
45
|
+
const renamed: Fields = {};
|
|
46
|
+
for (const [key, field] of Object.entries(fields)) renamed[key] = field.rename(survives);
|
|
47
|
+
return new SchemaDefinition(
|
|
48
|
+
renamed,
|
|
49
|
+
EntityAdapterSet.of(this.adapters)?.cut(survives)?.stated,
|
|
50
|
+
this.opts,
|
|
51
|
+
undefined,
|
|
52
|
+
this.origin(root).compose(survives),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The same fields, judged as an update. */
|
|
57
|
+
patched(root: SchemaView): SchemaDefinition {
|
|
58
|
+
return new SchemaDefinition(
|
|
59
|
+
{ ...this.fields }, this.adapters, { ...this.opts, patch: true }, undefined, this.origin(root),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The added fields have no origin, so the derivation is unchanged: it speaks of the root. */
|
|
64
|
+
extended(extra: Fields, root: SchemaView): SchemaDefinition {
|
|
65
|
+
return new SchemaDefinition(
|
|
66
|
+
{ ...this.fields, ...extra }, this.adapters, this.opts, undefined, this.origin(root),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Several views folded into one definition, which therefore has no single origin. */
|
|
71
|
+
static merged(views: readonly SchemaView[]): SchemaDefinition {
|
|
72
|
+
const fields: Fields = {};
|
|
73
|
+
let opts: ValidateOptions = {};
|
|
74
|
+
for (const view of views) {
|
|
75
|
+
Object.assign(fields, view.getFields());
|
|
76
|
+
opts = { ...opts, ...view.getOpts() };
|
|
77
|
+
}
|
|
78
|
+
const adapters = EntityAdapterSet.merged(views.map((view) => view.getAdapters()))?.stated;
|
|
79
|
+
|
|
80
|
+
return new SchemaDefinition(fields, adapters, opts, undefined, undefined);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The origin this definition already carries, or the one `root` becomes. */
|
|
84
|
+
private origin(root: SchemaView): SchemaDerivation {
|
|
85
|
+
return this.derivation ?? SchemaDerivation.first(root, this.fields);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type Fields } from '../fields/Field.js';
|
|
2
|
+
import type { SchemaView } from './SchemaView.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* What a derived schema says about its ORIGIN: the class it was cut from, and what the cut
|
|
6
|
+
* left of each of that class's fields.
|
|
7
|
+
*
|
|
8
|
+
* The origin is the ROOT — `Post.pick(a, b).omit(b)` answers `Post` and never the
|
|
9
|
+
* intermediate — so a chain is flattened rather than journalled, and the two halves cannot
|
|
10
|
+
* disagree: a derivation always carries both, which `source` and `survived` as independent
|
|
11
|
+
* properties could not promise.
|
|
12
|
+
*/
|
|
13
|
+
export class SchemaDerivation {
|
|
14
|
+
private constructor(
|
|
15
|
+
readonly source: SchemaView,
|
|
16
|
+
readonly survived: Readonly<Record<string, string | undefined>>,
|
|
17
|
+
) {}
|
|
18
|
+
|
|
19
|
+
/** The first cut: every field of the origin still answers to its own name. */
|
|
20
|
+
static first(source: SchemaView, fields: Fields): SchemaDerivation {
|
|
21
|
+
return new SchemaDerivation(source, Object.fromEntries(Object.keys(fields).map((key) => [key, key])));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The same origin, seen through one more cut. */
|
|
25
|
+
compose(survives: (key: string) => string | undefined): SchemaDerivation {
|
|
26
|
+
return new SchemaDerivation(
|
|
27
|
+
this.source,
|
|
28
|
+
Object.fromEntries(
|
|
29
|
+
Object.entries(this.survived).map(([origin, here]) => [origin, here === undefined ? undefined : survives(here)]),
|
|
30
|
+
),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get sourceName(): string {
|
|
35
|
+
return this.source.name;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* What the ROOT calls `key`, translated to the name it carries here — `undefined` when a
|
|
40
|
+
* cut dropped it. A reader that needs something the root declared about a field asks the
|
|
41
|
+
* root and comes back through this; it does not keep a copy of the answer.
|
|
42
|
+
*/
|
|
43
|
+
hereFor(key: string): string | undefined {
|
|
44
|
+
return this.survived[key];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Field, Fields } from '../fields/Field.js';
|
|
2
|
+
import type { CompositeUnique, PreviousNames } from '../EntityDeclarations.js';
|
|
3
|
+
import type { EntityAdapters } from '../EntityAdapters.js';
|
|
4
|
+
import type { ValidationResult } from '../judge/result.js';
|
|
5
|
+
import type { ValidateOptions } from '../judge/options.js';
|
|
6
|
+
import type { SchemaDerivation } from './SchemaDerivation.js';
|
|
7
|
+
|
|
8
|
+
export type Row<TFields extends Fields> = {
|
|
9
|
+
[K in keyof TFields]: TFields[K] extends Field<infer T> ? T : never;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type PartialRow<TFields extends Fields> = Partial<Row<TFields>>;
|
|
13
|
+
|
|
14
|
+
export interface SchemaView<TFields extends Fields = Fields> {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
/** What this schema was cut from, when it was cut from anything. */
|
|
17
|
+
readonly derivation?: SchemaDerivation;
|
|
18
|
+
/** What its fields were called before, keyed by the names they carry now. */
|
|
19
|
+
readonly previous?: PreviousNames<TFields>;
|
|
20
|
+
getFields(): TFields;
|
|
21
|
+
getAdapters(): EntityAdapters<TFields> | undefined;
|
|
22
|
+
getUnique(): CompositeUnique<TFields> | undefined;
|
|
23
|
+
getOpts(): ValidateOptions;
|
|
24
|
+
validate(input: unknown): ValidationResult<Row<TFields>>;
|
|
25
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function lowerFirst(text: string): string {
|
|
2
|
+
return text ? text[0].toLowerCase() + text.slice(1) : text;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function upperFirst(text: string): string {
|
|
6
|
+
return text ? text[0].toUpperCase() + text.slice(1) : text;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function clean<T extends Record<string, unknown>>(obj: T): T {
|
|
10
|
+
for (const key of Object.keys(obj)) if (obj[key] === undefined) delete obj[key];
|
|
11
|
+
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Field } from '../fields/Field.js';
|
|
2
|
+
|
|
3
|
+
export function bool(opts?: { default?: boolean; description?: string }): Field<boolean> {
|
|
4
|
+
return new Field<boolean>({
|
|
5
|
+
shape: { type: 'boolean' },
|
|
6
|
+
lifecycle: opts?.default !== undefined ? { create: { value: opts.default } } : undefined,
|
|
7
|
+
meta: opts?.description !== undefined ? { description: opts.description } : undefined,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Field } from '../fields/Field.js';
|
|
2
|
+
import type { SchemaView } from '../schema/SchemaView.js';
|
|
3
|
+
import { RowJudge } from '../judge/RowJudge.js';
|
|
4
|
+
|
|
5
|
+
export function json<T = unknown>(): Field<T>;
|
|
6
|
+
export function json<E extends SchemaView & (new (...args: any[]) => any)>(of: E): Field<InstanceType<E>>;
|
|
7
|
+
export function json(of?: SchemaView): Field<unknown> {
|
|
8
|
+
if (!of) return new Field({ shape: { type: 'object' } });
|
|
9
|
+
const fields = of.getFields();
|
|
10
|
+
const judge = RowJudge.of(fields);
|
|
11
|
+
const properties: Record<string, unknown> = {};
|
|
12
|
+
const required: string[] = [];
|
|
13
|
+
for (const [key, field] of Object.entries(fields)) {
|
|
14
|
+
properties[key] = field.meta?.description
|
|
15
|
+
? { ...field.shape, description: field.meta.description }
|
|
16
|
+
: field.shape;
|
|
17
|
+
if (judge.onAbsent(field) === null) required.push(key);
|
|
18
|
+
}
|
|
19
|
+
return new Field({
|
|
20
|
+
shape: { type: 'object', properties, ...(required.length ? { required } : {}) },
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Field } from '../fields/Field.js';
|
|
2
|
+
|
|
3
|
+
export interface ListOptions {
|
|
4
|
+
min?: number;
|
|
5
|
+
max?: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function list<T>(item: Field<T>, opts?: ListOptions): Field<T[]> {
|
|
9
|
+
if (item.role?.relation) throw new Error('list() takes a value field (text(), number()…) — a relation has no value shape');
|
|
10
|
+
return new Field<T[]>({
|
|
11
|
+
shape: { type: 'array', items: item.shape, minItems: opts?.min, maxItems: opts?.max },
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Relation, type EntityConstructor } from '../axis/role/Relation.js';
|
|
2
|
+
import { Field } from '../fields/Field.js';
|
|
3
|
+
|
|
4
|
+
export function many<E extends EntityConstructor>(target: E | (() => E)): Field<InstanceType<E>[]> {
|
|
5
|
+
return new Field<InstanceType<E>[]>({
|
|
6
|
+
shape: { type: 'array' },
|
|
7
|
+
role: { relation: Relation.many(target) },
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { vocabulary } from './vocabulary.js';
|
|
2
|
+
import { Anatomy } from '../axis/shape/Shape.js';
|
|
3
|
+
import { type Field } from '../fields/Field.js';
|
|
4
|
+
|
|
5
|
+
export const nullable: <T>(field: Field<T>) => Field<T | null> = vocabulary('nullable', (field) => ({
|
|
6
|
+
shape: Anatomy.nullable(field.shape),
|
|
7
|
+
}));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Field } from '../fields/Field.js';
|
|
2
|
+
|
|
3
|
+
export interface NumberOptions {
|
|
4
|
+
min?: number;
|
|
5
|
+
max?: number;
|
|
6
|
+
integer?: boolean;
|
|
7
|
+
default?: number;
|
|
8
|
+
description?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function number(opts?: NumberOptions): Field<number> {
|
|
12
|
+
return new Field<number>({
|
|
13
|
+
shape: { type: opts?.integer ? 'integer' : 'number', minimum: opts?.min, maximum: opts?.max },
|
|
14
|
+
lifecycle: opts?.default !== undefined ? { create: { value: opts.default } } : undefined,
|
|
15
|
+
meta: opts?.description !== undefined ? { description: opts.description } : undefined,
|
|
16
|
+
});
|
|
17
|
+
}
|