@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,80 @@
|
|
|
1
|
+
import type { Resolver } from '../axis/Axis.js';
|
|
2
|
+
import type { EntityConstructor } from '../axis/role/Relation.js';
|
|
3
|
+
import { lowerFirst } from '../utils.js';
|
|
4
|
+
import type { SchemaView } from '../schema/SchemaView.js';
|
|
5
|
+
import { Card } from './Card.js';
|
|
6
|
+
import type { SchemaBundle, SchemaDescriptor } from './Descriptor.js';
|
|
7
|
+
import type { Diff, SetDiff, SetDiffOptions } from './diff.js';
|
|
8
|
+
|
|
9
|
+
type SchemaSet = Record<string, SchemaView> | SchemaView[];
|
|
10
|
+
|
|
11
|
+
/** A portable schema map and the decisions that only a map can make. */
|
|
12
|
+
export class Bundle {
|
|
13
|
+
private constructor(readonly descriptor: SchemaBundle) {}
|
|
14
|
+
|
|
15
|
+
static fromSchemas(schemas: SchemaSet): Bundle {
|
|
16
|
+
const definitions: Record<string, SchemaDescriptor> = {};
|
|
17
|
+
const claimedBy = new Map<string, string>();
|
|
18
|
+
const entries = Array.isArray(schemas)
|
|
19
|
+
? schemas.map((schema) => {
|
|
20
|
+
const card = Card.fromSchema(schema);
|
|
21
|
+
return { name: card.descriptor.title ?? '', schema };
|
|
22
|
+
})
|
|
23
|
+
: Object.entries(schemas).map(([name, schema]) => ({ name, schema }));
|
|
24
|
+
|
|
25
|
+
for (const entry of entries) {
|
|
26
|
+
const key = lowerFirst(entry.name);
|
|
27
|
+
const previous = claimedBy.get(key);
|
|
28
|
+
if (previous !== undefined) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Schemas '${previous}' and '${entry.name}' both claim bundle key '${key}'. `
|
|
31
|
+
+ 'Each schema in a bundle must have a distinct registration key.',
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
claimedBy.set(key, entry.name);
|
|
35
|
+
definitions[key] = Card.fromSchema(entry.schema, key).descriptor;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return new Bundle({
|
|
39
|
+
$defs: definitions,
|
|
40
|
+
'x-fougere-version': 1,
|
|
41
|
+
'x-fougere-vendor': 'fougere',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static fromDescriptor(descriptor: SchemaBundle): Bundle {
|
|
46
|
+
return new Bundle(descriptor);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
toSchemas(): Record<string, SchemaView> {
|
|
50
|
+
const map: Record<string, EntityConstructor> = {};
|
|
51
|
+
const resolve: Resolver = (name) => map[name.toLowerCase()];
|
|
52
|
+
const schemas: Record<string, SchemaView> = {};
|
|
53
|
+
for (const [name, descriptor] of Object.entries(this.descriptor.$defs)) {
|
|
54
|
+
const schema = Card.fromDescriptor(descriptor).toSchema(resolve, name);
|
|
55
|
+
map[name.toLowerCase()] = schema as unknown as EntityConstructor;
|
|
56
|
+
schemas[name] = schema;
|
|
57
|
+
}
|
|
58
|
+
return schemas;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
diff(other: Bundle, options: SetDiffOptions = {}): SetDiff {
|
|
62
|
+
const before = this.descriptor.$defs ?? {};
|
|
63
|
+
const after = other.descriptor.$defs ?? {};
|
|
64
|
+
const entities: Record<string, Diff> = {};
|
|
65
|
+
for (const [name, descriptor] of Object.entries(before)) {
|
|
66
|
+
const target = after[name];
|
|
67
|
+
if (target === undefined) continue;
|
|
68
|
+
const answer = Card.fromDescriptor(descriptor).diff(
|
|
69
|
+
Card.fromDescriptor(target),
|
|
70
|
+
{ renamed: options.renamed?.[name] ?? {} },
|
|
71
|
+
);
|
|
72
|
+
if (answer.changes.length > 0 || answer.ambiguous.length > 0) entities[name] = answer;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
entitiesAdded: Object.keys(after).filter((name) => !(name in before)),
|
|
76
|
+
entitiesRemoved: Object.keys(before).filter((name) => !(name in after)),
|
|
77
|
+
entities,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/card/Card.ts
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { BoundaryRef } from '../axis/boundary/Boundary.js';
|
|
2
|
+
import type { LifecycleRules } from '../axis/lifecycle/Lifecycle.js';
|
|
3
|
+
import type { Relation } from '../axis/role/Relation.js';
|
|
4
|
+
import type { RoleRules } from '../axis/role/Role.js';
|
|
5
|
+
import type { Shape } from '../axis/shape/Shape.js';
|
|
6
|
+
|
|
7
|
+
type JsonSchemaType =
|
|
8
|
+
| 'string'
|
|
9
|
+
| 'number'
|
|
10
|
+
| 'integer'
|
|
11
|
+
| 'boolean'
|
|
12
|
+
| 'object'
|
|
13
|
+
| 'array'
|
|
14
|
+
| 'null';
|
|
15
|
+
|
|
16
|
+
type ShapeKeywords = UnionToIntersection<KeywordsOf<Shape>>;
|
|
17
|
+
|
|
18
|
+
type KeywordsOf<S> = S extends unknown ? Partial<Omit<S, 'type' | 'items' | 'properties'>> : never;
|
|
19
|
+
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
20
|
+
|
|
21
|
+
export type FieldDescriptor = ShapeKeywords & {
|
|
22
|
+
type?: JsonSchemaType | JsonSchemaType[];
|
|
23
|
+
items?: FieldDescriptor;
|
|
24
|
+
properties?: Record<string, FieldDescriptor>;
|
|
25
|
+
description?: string;
|
|
26
|
+
'x-fougere'?: FieldExtension;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface FieldExtension {
|
|
30
|
+
role?: RoleDescriptor;
|
|
31
|
+
lifecycle?: LifecycleRules;
|
|
32
|
+
boundary?: BoundaryRef;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type RoleDescriptor = Pick<RoleRules, 'primary' | 'index'> & {
|
|
36
|
+
unique?: string[][];
|
|
37
|
+
relation?: RelationDescriptor;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type RelationDescriptor = Pick<Relation, 'kind' | 'onDelete'> & { to: string };
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* What a derivation was cut from, and what the cut left — `survived` keyed by the
|
|
44
|
+
* ORIGIN's field names. Absent on a declaration that derives from nothing.
|
|
45
|
+
*
|
|
46
|
+
* A dropped field is `null` and never `undefined`: JSON.stringify erases the second,
|
|
47
|
+
* which would leave the card saying only what remains — what `properties` already says.
|
|
48
|
+
*/
|
|
49
|
+
export interface DerivedFrom {
|
|
50
|
+
from: string;
|
|
51
|
+
survived: Record<string, string | null>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface SchemaDescriptor {
|
|
55
|
+
title?: string;
|
|
56
|
+
type: 'object';
|
|
57
|
+
properties: Record<string, FieldDescriptor>;
|
|
58
|
+
required?: string[];
|
|
59
|
+
'x-fougere-derived'?: DerivedFrom;
|
|
60
|
+
'x-fougere-version': 1;
|
|
61
|
+
'x-fougere-vendor': 'fougere';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface SchemaBundle {
|
|
65
|
+
$defs: Record<string, SchemaDescriptor>;
|
|
66
|
+
'x-fougere-version': 1;
|
|
67
|
+
'x-fougere-vendor': 'fougere';
|
|
68
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
}
|
package/src/card/diff.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { BoundaryRef } from '../axis/boundary/Boundary.js';
|
|
2
|
+
import type { LifecycleRules } from '../axis/lifecycle/Lifecycle.js';
|
|
3
|
+
import type { FieldDescriptor, RoleDescriptor } from './Descriptor.js';
|
|
4
|
+
|
|
5
|
+
/** One named difference, at one place. Each kind exists because a reader asks for it. */
|
|
6
|
+
export type Change =
|
|
7
|
+
/** DDL: add the column. Codec: the old caller never sends it. Boot: refuse when required. */
|
|
8
|
+
| { kind: 'added'; field: string; to: FieldDescriptor; required: boolean }
|
|
9
|
+
/** DDL: drop the column. Codec: the old caller still sends it, and it goes nowhere. */
|
|
10
|
+
| { kind: 'removed'; field: string; from: FieldDescriptor; required: boolean }
|
|
11
|
+
/** DDL: rename the column. Codec and ORM: one more entry in the field-to-column map. */
|
|
12
|
+
| { kind: 'renamed'; from: string; to: string; field: FieldDescriptor }
|
|
13
|
+
/** DDL: alter the type. Codec: convert the value, when it can. */
|
|
14
|
+
| { kind: 'retyped'; field: string; from: TypeSet; to: TypeSet }
|
|
15
|
+
/** Same type, different bounds: a CHECK moves, and a value legal yesterday may not be. */
|
|
16
|
+
| { kind: 'reshaped'; field: string; from: FieldDescriptor; to: FieldDescriptor }
|
|
17
|
+
/** NOT NULL in either direction. Boot: an old writer cannot fill what it never knew. */
|
|
18
|
+
| { kind: 'required'; field: string; from: boolean; to: boolean }
|
|
19
|
+
/** An axis other than shape was restated. */
|
|
20
|
+
| { kind: 'restated'; field: string; axis: 'role'; from?: RoleDescriptor; to?: RoleDescriptor }
|
|
21
|
+
| { kind: 'restated'; field: string; axis: 'lifecycle'; from?: LifecycleRules; to?: LifecycleRules }
|
|
22
|
+
| { kind: 'restated'; field: string; axis: 'boundary'; from?: BoundaryRef; to?: BoundaryRef };
|
|
23
|
+
|
|
24
|
+
/** A field's declared types, always represented as a set. */
|
|
25
|
+
export type TypeSet = string[];
|
|
26
|
+
|
|
27
|
+
/** A removal and an addition that could be one rename: same shape, different name. */
|
|
28
|
+
export interface RenameCandidate {
|
|
29
|
+
removed: string;
|
|
30
|
+
added: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Diff {
|
|
34
|
+
changes: Change[];
|
|
35
|
+
/** Questions the comparison refuses to decide without an explicit rename declaration. */
|
|
36
|
+
ambiguous: RenameCandidate[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface DiffOptions {
|
|
40
|
+
/** Renames as declared at the time: old name to new name. */
|
|
41
|
+
renamed?: Record<string, string>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface SetDiff {
|
|
45
|
+
/** Entities the target bundle has and the source bundle had not. */
|
|
46
|
+
entitiesAdded: string[];
|
|
47
|
+
/** Entities the source bundle had and the target bundle has not. */
|
|
48
|
+
entitiesRemoved: string[];
|
|
49
|
+
/** Differences for entities present in both bundles; unchanged entities are absent. */
|
|
50
|
+
entities: Record<string, Diff>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface SetDiffOptions {
|
|
54
|
+
/** Declared renames, per entity: `{ post: { body: 'content' } }`. */
|
|
55
|
+
renamed?: Record<string, Record<string, string>>;
|
|
56
|
+
}
|