@versionzero/schema 1.0.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/LICENSE +177 -0
- package/README.md +246 -0
- package/package.json +84 -0
- package/src/compilation/handler-compilation.js +28 -0
- package/src/compilation/metadata-compilation.js +35 -0
- package/src/compilation/schema-compilation.js +142 -0
- package/src/compilation/selection-compilation.js +84 -0
- package/src/compilation/union-compilation.js +510 -0
- package/src/compilation/values-compilation.js +35 -0
- package/src/compiled-schema.js +1709 -0
- package/src/constants.js +1 -0
- package/src/core-library/index.js +32 -0
- package/src/core-library/processors/aggregation-operators.js +75 -0
- package/src/core-library/processors/alpha-constraint.js +20 -0
- package/src/core-library/processors/alphanum-constraint.js +20 -0
- package/src/core-library/processors/array-operator.js +51 -0
- package/src/core-library/processors/assert-constraint.js +75 -0
- package/src/core-library/processors/base64-constraint.js +26 -0
- package/src/core-library/processors/camel-case-operator.js +24 -0
- package/src/core-library/processors/capitalize-operator.js +16 -0
- package/src/core-library/processors/cardnum-constraint.js +193 -0
- package/src/core-library/processors/ceil-operator.js +44 -0
- package/src/core-library/processors/collapse-operator.js +29 -0
- package/src/core-library/processors/compact-operator.js +34 -0
- package/src/core-library/processors/compile-operator.js +65 -0
- package/src/core-library/processors/concat-operator.js +51 -0
- package/src/core-library/processors/conditional-operators.js +301 -0
- package/src/core-library/processors/constant-case-operator.js +16 -0
- package/src/core-library/processors/data-size-operator.js +86 -0
- package/src/core-library/processors/date-object-operator.js +54 -0
- package/src/core-library/processors/date-operator.js +67 -0
- package/src/core-library/processors/date-range-constraint.js +76 -0
- package/src/core-library/processors/defined-constraint.js +30 -0
- package/src/core-library/processors/each-operator.js +57 -0
- package/src/core-library/processors/email-constraint.js +112 -0
- package/src/core-library/processors/entries-operator.js +25 -0
- package/src/core-library/processors/eq-constraint.js +37 -0
- package/src/core-library/processors/filter-operator.js +74 -0
- package/src/core-library/processors/find-schema-operator.js +45 -0
- package/src/core-library/processors/flatten-operator.js +40 -0
- package/src/core-library/processors/floor-operator.js +47 -0
- package/src/core-library/processors/get-operator.js +44 -0
- package/src/core-library/processors/group-by-operator.js +84 -0
- package/src/core-library/processors/has-prefix-constraint.js +37 -0
- package/src/core-library/processors/has-suffix-constraint.js +35 -0
- package/src/core-library/processors/hex-constraint.js +20 -0
- package/src/core-library/processors/hostname-constraint.js +22 -0
- package/src/core-library/processors/http-url-constraint.js +27 -0
- package/src/core-library/processors/in-constraint.js +66 -0
- package/src/core-library/processors/index-by-operator.js +98 -0
- package/src/core-library/processors/index.js +131 -0
- package/src/core-library/processors/input-operator.js +23 -0
- package/src/core-library/processors/instanceof-constraint.js +38 -0
- package/src/core-library/processors/integer-constraint.js +22 -0
- package/src/core-library/processors/invoke-operator.js +33 -0
- package/src/core-library/processors/ipv4-constraint.js +188 -0
- package/src/core-library/processors/ipv6-constraint.js +205 -0
- package/src/core-library/processors/is-array-constraint.js +21 -0
- package/src/core-library/processors/is-date-constraint.js +22 -0
- package/src/core-library/processors/is-number-constraint.js +21 -0
- package/src/core-library/processors/is-object-constraint.js +21 -0
- package/src/core-library/processors/is-string-constraint.js +21 -0
- package/src/core-library/processors/join-operator.js +41 -0
- package/src/core-library/processors/json-constraint.js +22 -0
- package/src/core-library/processors/json-decode-operator.js +25 -0
- package/src/core-library/processors/json-encode-operator.js +35 -0
- package/src/core-library/processors/kebab-case-operator.js +23 -0
- package/src/core-library/processors/keys-operator.js +20 -0
- package/src/core-library/processors/length-constraint.js +85 -0
- package/src/core-library/processors/lookup-operator.js +84 -0
- package/src/core-library/processors/lowercase-operator.js +14 -0
- package/src/core-library/processors/map-operator.js +84 -0
- package/src/core-library/processors/match-operator.js +64 -0
- package/src/core-library/processors/matches-constraint.js +54 -0
- package/src/core-library/processors/math-operators.js +151 -0
- package/src/core-library/processors/merge-deep-operator.js +61 -0
- package/src/core-library/processors/merge-operator.js +54 -0
- package/src/core-library/processors/metadata-operator.js +100 -0
- package/src/core-library/processors/negative-constraint.js +23 -0
- package/src/core-library/processors/never-constraint.js +69 -0
- package/src/core-library/processors/non-empty-constraint.js +59 -0
- package/src/core-library/processors/not-constraint.js +71 -0
- package/src/core-library/processors/number-operator.js +24 -0
- package/src/core-library/processors/numeric-constraint.js +22 -0
- package/src/core-library/processors/object-operator.js +38 -0
- package/src/core-library/processors/omit-operator.js +57 -0
- package/src/core-library/processors/parallel-operator.js +64 -0
- package/src/core-library/processors/pascal-case-operator.js +16 -0
- package/src/core-library/processors/phone-constraint.js +235 -0
- package/src/core-library/processors/pick-operator.js +62 -0
- package/src/core-library/processors/pipeline-operator.js +63 -0
- package/src/core-library/processors/port-constraint.js +22 -0
- package/src/core-library/processors/positive-constraint.js +23 -0
- package/src/core-library/processors/process-operator.js +55 -0
- package/src/core-library/processors/property-operator.js +49 -0
- package/src/core-library/processors/range-constraint.js +72 -0
- package/src/core-library/processors/reference-operator.js +79 -0
- package/src/core-library/processors/require-constraint.js +74 -0
- package/src/core-library/processors/reverse-operator.js +20 -0
- package/src/core-library/processors/round-operator.js +53 -0
- package/src/core-library/processors/schema-handler-operators.js +54 -0
- package/src/core-library/processors/semver-constraint.js +282 -0
- package/src/core-library/processors/sequence-processors.js +406 -0
- package/src/core-library/processors/sort-operator.js +52 -0
- package/src/core-library/processors/split-operator.js +43 -0
- package/src/core-library/processors/string-extra-operators.js +141 -0
- package/src/core-library/processors/string-operator.js +34 -0
- package/src/core-library/processors/target-operator.js +30 -0
- package/src/core-library/processors/template-operator.js +60 -0
- package/src/core-library/processors/title-case-operator.js +17 -0
- package/src/core-library/processors/trim-operator.js +14 -0
- package/src/core-library/processors/truthy-constraint.js +35 -0
- package/src/core-library/processors/type-operator.js +24 -0
- package/src/core-library/processors/unique-operator.js +21 -0
- package/src/core-library/processors/uppercase-operator.js +14 -0
- package/src/core-library/processors/url-constraint.js +31 -0
- package/src/core-library/processors/url-decode-operator.js +50 -0
- package/src/core-library/processors/url-encode-operator.js +44 -0
- package/src/core-library/processors/uuid-constraint.js +31 -0
- package/src/core-library/processors/values-operator.js +20 -0
- package/src/core-library/schemas/any-schema.js +23 -0
- package/src/core-library/schemas/array-schema.js +8 -0
- package/src/core-library/schemas/boolean-schema.js +10 -0
- package/src/core-library/schemas/date-schema.js +12 -0
- package/src/core-library/schemas/function-schema.js +40 -0
- package/src/core-library/schemas/number-schema.js +9 -0
- package/src/core-library/schemas/object-schema.js +10 -0
- package/src/core-library/schemas/root-schema.js +21 -0
- package/src/core-library/schemas/string-schema.js +9 -0
- package/src/core-library-node/index.js +47 -0
- package/src/core-library-node/processors/base64-decode-operator.js +20 -0
- package/src/core-library-node/processors/base64-encode-operator.js +20 -0
- package/src/core-library-node/processors/buffer-operator.js +39 -0
- package/src/core-library-node/processors/directory-constraint.js +35 -0
- package/src/core-library-node/processors/executable-constraint.js +34 -0
- package/src/core-library-node/processors/file-constraint.js +34 -0
- package/src/core-library-node/processors/file-size-constraint.js +94 -0
- package/src/core-library-node/processors/is-buffer-constraint.js +21 -0
- package/src/core-library-node/processors/reachable-constraint.js +28 -0
- package/src/core-library-node/processors/readable-constraint.js +34 -0
- package/src/core-library-node/processors/writable-constraint.js +59 -0
- package/src/core-library-node/schemas/buffer-schema.js +10 -0
- package/src/errors.js +209 -0
- package/src/executor/array-executor.js +78 -0
- package/src/executor/conditional-executor.js +134 -0
- package/src/executor/each-executor.js +68 -0
- package/src/executor/executor.js +123 -0
- package/src/executor/object-executor.js +98 -0
- package/src/executor/parallel-executor.js +43 -0
- package/src/executor/pipeline-executor.js +65 -0
- package/src/executor/sequence-executor.js +206 -0
- package/src/executor/serial-executor.js +24 -0
- package/src/executor/step-executor.js +68 -0
- package/src/helpers/case.js +124 -0
- package/src/helpers/data-size.js +144 -0
- package/src/helpers/debug-sink.js +15 -0
- package/src/helpers/deep.js +280 -0
- package/src/helpers/format.js +121 -0
- package/src/helpers/has-string-properties.js +30 -0
- package/src/helpers/index.js +16 -0
- package/src/helpers/object.js +115 -0
- package/src/helpers/parse-date.js +75 -0
- package/src/helpers/path.js +28 -0
- package/src/helpers/regex.js +18 -0
- package/src/helpers/stringify.js +309 -0
- package/src/helpers/to-data.js +64 -0
- package/src/helpers/truthy.js +55 -0
- package/src/index.js +29 -0
- package/src/schema-compiler.js +531 -0
- package/src/schema-location.js +200 -0
- package/src/schema-resolver.js +546 -0
- package/src/schema.js +1182 -0
- package/src/traversal/executors/check-condition.js +42 -0
- package/src/traversal/executors/check-input.js +27 -0
- package/src/traversal/executors/check-required.js +19 -0
- package/src/traversal/executors/check-schema.js +45 -0
- package/src/traversal/executors/defaults.js +21 -0
- package/src/traversal/executors/enter-existing.js +25 -0
- package/src/traversal/executors/enter-input.js +25 -0
- package/src/traversal/executors/enter.js +37 -0
- package/src/traversal/executors/exit.js +74 -0
- package/src/traversal/executors/finalize.js +64 -0
- package/src/traversal/executors/index.js +42 -0
- package/src/traversal/executors/normalize.js +38 -0
- package/src/traversal/executors/prepare-existing.js +27 -0
- package/src/traversal/executors/prepare-pending.js +54 -0
- package/src/traversal/executors/resolve-union.js +50 -0
- package/src/traversal/executors/serialize.js +48 -0
- package/src/traversal/executors/transform-early.js +51 -0
- package/src/traversal/executors/transform.js +68 -0
- package/src/traversal/executors/traversal-state-executor.js +46 -0
- package/src/traversal/executors/validate.js +63 -0
- package/src/traversal/traversal-context.js +231 -0
- package/src/traversal/traversal-state.js +809 -0
- package/src/types.js +102 -0
- package/src/value-processor/composed-value-processor.js +43 -0
- package/src/value-processor/defined-value-processor.js +72 -0
- package/src/value-processor/function-value-processor.js +68 -0
- package/src/value-processor/parameterized-value-processor.js +45 -0
- package/src/value-processor/parameters-value-processor.js +178 -0
- package/src/value-processor/spec.js +89 -0
- package/src/value-processor/value-processor.js +105 -0
- package/types/compilation/handler-compilation.d.ts +13 -0
- package/types/compilation/metadata-compilation.d.ts +6 -0
- package/types/compilation/schema-compilation.d.ts +32 -0
- package/types/compilation/selection-compilation.d.ts +9 -0
- package/types/compilation/union-compilation.d.ts +42 -0
- package/types/compilation/values-compilation.d.ts +12 -0
- package/types/compiled-schema.d.ts +883 -0
- package/types/constants.d.ts +1 -0
- package/types/core-library/index.d.ts +7 -0
- package/types/core-library/processors/aggregation-operators.d.ts +24 -0
- package/types/core-library/processors/alpha-constraint.d.ts +9 -0
- package/types/core-library/processors/alphanum-constraint.d.ts +9 -0
- package/types/core-library/processors/array-operator.d.ts +12 -0
- package/types/core-library/processors/assert-constraint.d.ts +30 -0
- package/types/core-library/processors/base64-constraint.d.ts +11 -0
- package/types/core-library/processors/camel-case-operator.d.ts +17 -0
- package/types/core-library/processors/capitalize-operator.d.ts +11 -0
- package/types/core-library/processors/cardnum-constraint.d.ts +51 -0
- package/types/core-library/processors/ceil-operator.d.ts +30 -0
- package/types/core-library/processors/collapse-operator.d.ts +24 -0
- package/types/core-library/processors/compact-operator.d.ts +29 -0
- package/types/core-library/processors/compile-operator.d.ts +34 -0
- package/types/core-library/processors/concat-operator.d.ts +23 -0
- package/types/core-library/processors/conditional-operators.d.ts +219 -0
- package/types/core-library/processors/constant-case-operator.d.ts +9 -0
- package/types/core-library/processors/data-size-operator.d.ts +31 -0
- package/types/core-library/processors/date-object-operator.d.ts +16 -0
- package/types/core-library/processors/date-operator.d.ts +21 -0
- package/types/core-library/processors/date-range-constraint.d.ts +26 -0
- package/types/core-library/processors/defined-constraint.d.ts +20 -0
- package/types/core-library/processors/each-operator.d.ts +34 -0
- package/types/core-library/processors/email-constraint.d.ts +54 -0
- package/types/core-library/processors/entries-operator.d.ts +13 -0
- package/types/core-library/processors/eq-constraint.d.ts +20 -0
- package/types/core-library/processors/filter-operator.d.ts +35 -0
- package/types/core-library/processors/find-schema-operator.d.ts +28 -0
- package/types/core-library/processors/flatten-operator.d.ts +26 -0
- package/types/core-library/processors/floor-operator.d.ts +33 -0
- package/types/core-library/processors/get-operator.d.ts +31 -0
- package/types/core-library/processors/group-by-operator.d.ts +36 -0
- package/types/core-library/processors/has-prefix-constraint.d.ts +22 -0
- package/types/core-library/processors/has-suffix-constraint.d.ts +20 -0
- package/types/core-library/processors/hex-constraint.d.ts +9 -0
- package/types/core-library/processors/hostname-constraint.d.ts +11 -0
- package/types/core-library/processors/http-url-constraint.d.ts +9 -0
- package/types/core-library/processors/in-constraint.d.ts +27 -0
- package/types/core-library/processors/index-by-operator.d.ts +26 -0
- package/types/core-library/processors/index.d.ts +8 -0
- package/types/core-library/processors/input-operator.d.ts +20 -0
- package/types/core-library/processors/instanceof-constraint.d.ts +23 -0
- package/types/core-library/processors/integer-constraint.d.ts +9 -0
- package/types/core-library/processors/invoke-operator.d.ts +12 -0
- package/types/core-library/processors/ipv4-constraint.d.ts +37 -0
- package/types/core-library/processors/ipv6-constraint.d.ts +34 -0
- package/types/core-library/processors/is-array-constraint.d.ts +10 -0
- package/types/core-library/processors/is-date-constraint.d.ts +10 -0
- package/types/core-library/processors/is-number-constraint.d.ts +10 -0
- package/types/core-library/processors/is-object-constraint.d.ts +10 -0
- package/types/core-library/processors/is-string-constraint.d.ts +10 -0
- package/types/core-library/processors/join-operator.d.ts +29 -0
- package/types/core-library/processors/json-constraint.d.ts +10 -0
- package/types/core-library/processors/json-decode-operator.d.ts +9 -0
- package/types/core-library/processors/json-encode-operator.d.ts +27 -0
- package/types/core-library/processors/kebab-case-operator.d.ts +16 -0
- package/types/core-library/processors/keys-operator.d.ts +9 -0
- package/types/core-library/processors/length-constraint.d.ts +34 -0
- package/types/core-library/processors/lookup-operator.d.ts +36 -0
- package/types/core-library/processors/lowercase-operator.d.ts +9 -0
- package/types/core-library/processors/map-operator.d.ts +38 -0
- package/types/core-library/processors/match-operator.d.ts +34 -0
- package/types/core-library/processors/matches-constraint.d.ts +29 -0
- package/types/core-library/processors/math-operators.d.ts +91 -0
- package/types/core-library/processors/merge-deep-operator.d.ts +32 -0
- package/types/core-library/processors/merge-operator.d.ts +26 -0
- package/types/core-library/processors/metadata-operator.d.ts +56 -0
- package/types/core-library/processors/negative-constraint.d.ts +13 -0
- package/types/core-library/processors/never-constraint.d.ts +26 -0
- package/types/core-library/processors/non-empty-constraint.d.ts +28 -0
- package/types/core-library/processors/not-constraint.d.ts +28 -0
- package/types/core-library/processors/number-operator.d.ts +9 -0
- package/types/core-library/processors/numeric-constraint.d.ts +10 -0
- package/types/core-library/processors/object-operator.d.ts +10 -0
- package/types/core-library/processors/omit-operator.d.ts +24 -0
- package/types/core-library/processors/parallel-operator.d.ts +41 -0
- package/types/core-library/processors/pascal-case-operator.d.ts +9 -0
- package/types/core-library/processors/phone-constraint.d.ts +65 -0
- package/types/core-library/processors/pick-operator.d.ts +27 -0
- package/types/core-library/processors/pipeline-operator.d.ts +40 -0
- package/types/core-library/processors/port-constraint.d.ts +11 -0
- package/types/core-library/processors/positive-constraint.d.ts +13 -0
- package/types/core-library/processors/process-operator.d.ts +37 -0
- package/types/core-library/processors/property-operator.d.ts +34 -0
- package/types/core-library/processors/range-constraint.d.ts +30 -0
- package/types/core-library/processors/reference-operator.d.ts +38 -0
- package/types/core-library/processors/require-constraint.d.ts +29 -0
- package/types/core-library/processors/reverse-operator.d.ts +9 -0
- package/types/core-library/processors/round-operator.d.ts +34 -0
- package/types/core-library/processors/schema-handler-operators.d.ts +28 -0
- package/types/core-library/processors/semver-constraint.d.ts +43 -0
- package/types/core-library/processors/sequence-processors.d.ts +213 -0
- package/types/core-library/processors/sort-operator.d.ts +31 -0
- package/types/core-library/processors/split-operator.d.ts +33 -0
- package/types/core-library/processors/string-extra-operators.d.ts +83 -0
- package/types/core-library/processors/string-operator.d.ts +10 -0
- package/types/core-library/processors/target-operator.d.ts +27 -0
- package/types/core-library/processors/template-operator.d.ts +31 -0
- package/types/core-library/processors/title-case-operator.d.ts +12 -0
- package/types/core-library/processors/trim-operator.d.ts +9 -0
- package/types/core-library/processors/truthy-constraint.d.ts +23 -0
- package/types/core-library/processors/type-operator.d.ts +11 -0
- package/types/core-library/processors/unique-operator.d.ts +10 -0
- package/types/core-library/processors/uppercase-operator.d.ts +9 -0
- package/types/core-library/processors/url-constraint.d.ts +20 -0
- package/types/core-library/processors/url-decode-operator.d.ts +31 -0
- package/types/core-library/processors/url-encode-operator.d.ts +36 -0
- package/types/core-library/processors/uuid-constraint.d.ts +20 -0
- package/types/core-library/processors/values-operator.d.ts +9 -0
- package/types/core-library/schemas/any-schema.d.ts +2 -0
- package/types/core-library/schemas/array-schema.d.ts +2 -0
- package/types/core-library/schemas/boolean-schema.d.ts +2 -0
- package/types/core-library/schemas/date-schema.d.ts +2 -0
- package/types/core-library/schemas/function-schema.d.ts +2 -0
- package/types/core-library/schemas/number-schema.d.ts +2 -0
- package/types/core-library/schemas/object-schema.d.ts +2 -0
- package/types/core-library/schemas/root-schema.d.ts +2 -0
- package/types/core-library/schemas/string-schema.d.ts +2 -0
- package/types/core-library-node/index.d.ts +12 -0
- package/types/core-library-node/processors/base64-decode-operator.d.ts +9 -0
- package/types/core-library-node/processors/base64-encode-operator.d.ts +9 -0
- package/types/core-library-node/processors/buffer-operator.d.ts +15 -0
- package/types/core-library-node/processors/directory-constraint.d.ts +14 -0
- package/types/core-library-node/processors/executable-constraint.d.ts +17 -0
- package/types/core-library-node/processors/file-constraint.d.ts +13 -0
- package/types/core-library-node/processors/file-size-constraint.d.ts +43 -0
- package/types/core-library-node/processors/is-buffer-constraint.d.ts +10 -0
- package/types/core-library-node/processors/reachable-constraint.d.ts +13 -0
- package/types/core-library-node/processors/readable-constraint.d.ts +17 -0
- package/types/core-library-node/processors/writable-constraint.d.ts +18 -0
- package/types/core-library-node/schemas/buffer-schema.d.ts +2 -0
- package/types/errors.d.ts +58 -0
- package/types/executor/array-executor.d.ts +17 -0
- package/types/executor/conditional-executor.d.ts +45 -0
- package/types/executor/each-executor.d.ts +15 -0
- package/types/executor/executor.d.ts +84 -0
- package/types/executor/object-executor.d.ts +14 -0
- package/types/executor/parallel-executor.d.ts +27 -0
- package/types/executor/pipeline-executor.d.ts +11 -0
- package/types/executor/sequence-executor.d.ts +32 -0
- package/types/executor/serial-executor.d.ts +16 -0
- package/types/executor/step-executor.d.ts +14 -0
- package/types/helpers/case.d.ts +30 -0
- package/types/helpers/data-size.d.ts +25 -0
- package/types/helpers/debug-sink.d.ts +9 -0
- package/types/helpers/deep.d.ts +33 -0
- package/types/helpers/format.d.ts +14 -0
- package/types/helpers/has-string-properties.d.ts +5 -0
- package/types/helpers/index.d.ts +13 -0
- package/types/helpers/object.d.ts +46 -0
- package/types/helpers/parse-date.d.ts +6 -0
- package/types/helpers/path.d.ts +13 -0
- package/types/helpers/regex.d.ts +7 -0
- package/types/helpers/stringify.d.ts +33 -0
- package/types/helpers/to-data.d.ts +13 -0
- package/types/helpers/truthy.d.ts +26 -0
- package/types/index.d.ts +6 -0
- package/types/schema-compiler.d.ts +49 -0
- package/types/schema-location.d.ts +64 -0
- package/types/schema-resolver.d.ts +145 -0
- package/types/schema.d.ts +586 -0
- package/types/traversal/executors/check-condition.d.ts +8 -0
- package/types/traversal/executors/check-input.d.ts +6 -0
- package/types/traversal/executors/check-required.d.ts +6 -0
- package/types/traversal/executors/check-schema.d.ts +7 -0
- package/types/traversal/executors/defaults.d.ts +8 -0
- package/types/traversal/executors/enter-existing.d.ts +6 -0
- package/types/traversal/executors/enter-input.d.ts +8 -0
- package/types/traversal/executors/enter.d.ts +7 -0
- package/types/traversal/executors/exit.d.ts +6 -0
- package/types/traversal/executors/finalize.d.ts +6 -0
- package/types/traversal/executors/index.d.ts +15 -0
- package/types/traversal/executors/normalize.d.ts +7 -0
- package/types/traversal/executors/prepare-existing.d.ts +6 -0
- package/types/traversal/executors/prepare-pending.d.ts +6 -0
- package/types/traversal/executors/resolve-union.d.ts +6 -0
- package/types/traversal/executors/serialize.d.ts +11 -0
- package/types/traversal/executors/transform-early.d.ts +6 -0
- package/types/traversal/executors/transform.d.ts +6 -0
- package/types/traversal/executors/traversal-state-executor.d.ts +19 -0
- package/types/traversal/executors/validate.d.ts +6 -0
- package/types/traversal/traversal-context.d.ts +67 -0
- package/types/traversal/traversal-state.d.ts +97 -0
- package/types/types.d.ts +218 -0
- package/types/value-processor/composed-value-processor.d.ts +17 -0
- package/types/value-processor/defined-value-processor.d.ts +16 -0
- package/types/value-processor/function-value-processor.d.ts +15 -0
- package/types/value-processor/parameterized-value-processor.d.ts +14 -0
- package/types/value-processor/parameters-value-processor.d.ts +28 -0
- package/types/value-processor/spec.d.ts +22 -0
- package/types/value-processor/value-processor.d.ts +92 -0
package/src/schema.js
ADDED
|
@@ -0,0 +1,1182 @@
|
|
|
1
|
+
import { toData } from './helpers/to-data.js';
|
|
2
|
+
import { CompiledSchema } from './compiled-schema.js';
|
|
3
|
+
import { SchemaError } from './errors.js';
|
|
4
|
+
import { deepValue } from './helpers/deep.js';
|
|
5
|
+
|
|
6
|
+
/** @import { ValueProcessor, ValueProcessorFunction, ValueProcessorSpec } from './value-processor/value-processor.js' */
|
|
7
|
+
/** @import { ISchemaMetadata, ISchemaOptions, SchemaData, ISchema, } from './types.js' */
|
|
8
|
+
|
|
9
|
+
/** @typedef {ISchemaOptions} SchemaOptions */
|
|
10
|
+
/** @typedef {ISchemaMetadata} SchemaMetadata */
|
|
11
|
+
/** @typedef {{[key:string]: ISchema}} SchemaProperties */
|
|
12
|
+
/** @typedef {{[key:string]: ISchema}} SchemaUnionSchemas */
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {object} SchemaHandlers
|
|
16
|
+
* @property {Array<ValueProcessorSpec>} [normalizers]
|
|
17
|
+
* @property {Array<ValueProcessorSpec>} [transformers]
|
|
18
|
+
* @property {Array<ValueProcessorSpec>} [finalizers]
|
|
19
|
+
* @property {Array<ValueProcessorSpec>} [validators]
|
|
20
|
+
* @property {Array<ValueProcessorSpec>} [serializers]
|
|
21
|
+
* @property {Array<ValueProcessorSpec>} [conditions]
|
|
22
|
+
* @property {Array<ValueProcessorSpec>} [discriminators]
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Schema - allows the definition of structure and rules for data
|
|
27
|
+
*
|
|
28
|
+
* Essentially acts as a fluent builder; must be compiled by SchemaCompiler for use.
|
|
29
|
+
*
|
|
30
|
+
* @typedef {import("./types.js").ISchema} ISchema
|
|
31
|
+
* @augments {ISchema}
|
|
32
|
+
*/
|
|
33
|
+
export class Schema
|
|
34
|
+
{
|
|
35
|
+
/** @type {string|undefined} */
|
|
36
|
+
#base;
|
|
37
|
+
|
|
38
|
+
/** @type {SchemaProperties} */
|
|
39
|
+
#properties = {};
|
|
40
|
+
|
|
41
|
+
/** @type {SchemaHandlers} */
|
|
42
|
+
#handlers = {};
|
|
43
|
+
|
|
44
|
+
/** @type {SchemaOptions} */
|
|
45
|
+
#options = {};
|
|
46
|
+
|
|
47
|
+
/** @type {ISchemaMetadata} */
|
|
48
|
+
#metadata = {};
|
|
49
|
+
|
|
50
|
+
/**@type {SchemaUnionSchemas} */
|
|
51
|
+
#unionSchemas = {};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Construct a Schema.
|
|
55
|
+
*
|
|
56
|
+
* Pass a string name of a registered schema to resolve as the base, or pass a schema-shaped object to extend.
|
|
57
|
+
*
|
|
58
|
+
* Prefer the fluent setters over passing in options/metadata or attributes.
|
|
59
|
+
*
|
|
60
|
+
* @param {string|ISchema|SchemaData} [base] - schema type or base to extend
|
|
61
|
+
* @param {object} [options] - schema options (also supports "attribute" shorthand syntax, but prefer being explicit)
|
|
62
|
+
* @param {ISchemaMetadata} [metadata] - schema metadata
|
|
63
|
+
*/
|
|
64
|
+
constructor(base, options, metadata) {
|
|
65
|
+
if (typeof base === 'string') {
|
|
66
|
+
this.base = base;
|
|
67
|
+
if (options) {
|
|
68
|
+
this._setAttributes(options);
|
|
69
|
+
}
|
|
70
|
+
if (metadata) {
|
|
71
|
+
this.addMetadata(metadata);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else if ((base instanceof Schema) || (base instanceof CompiledSchema)) {
|
|
75
|
+
this.extend(base);
|
|
76
|
+
if (base instanceof Schema) {
|
|
77
|
+
this.base = base.base;
|
|
78
|
+
}
|
|
79
|
+
// Apply additional options/metadata after extending
|
|
80
|
+
if (options) {
|
|
81
|
+
this._setAttributes(options);
|
|
82
|
+
}
|
|
83
|
+
if (metadata) {
|
|
84
|
+
this.addMetadata(metadata);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else if (typeof base === 'object' && base !== null) {
|
|
88
|
+
// It's a SchemaData/ISchema object
|
|
89
|
+
this.extend(base);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Name of a schema registered in SchemaResolver that this schema extends
|
|
95
|
+
*
|
|
96
|
+
* @type {string|undefined}
|
|
97
|
+
*/
|
|
98
|
+
get base() {
|
|
99
|
+
return this.#base;
|
|
100
|
+
}
|
|
101
|
+
set base(base) {
|
|
102
|
+
this.#base = base;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Properties are named child schemas.
|
|
107
|
+
*
|
|
108
|
+
* Use the property setter rather than direct access to ensure data consistency.
|
|
109
|
+
*
|
|
110
|
+
* @type {SchemaProperties}
|
|
111
|
+
*/
|
|
112
|
+
get properties() {
|
|
113
|
+
return this.#properties;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Handlers are grouped lists of value processors.
|
|
118
|
+
*
|
|
119
|
+
* Assign using the individual value processor setters.
|
|
120
|
+
*
|
|
121
|
+
* @type {SchemaHandlers}
|
|
122
|
+
*/
|
|
123
|
+
get handlers() {
|
|
124
|
+
return this.#handlers;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Options are settings that define how the schema behaves.
|
|
129
|
+
*
|
|
130
|
+
* @returns {SchemaOptions}
|
|
131
|
+
*/
|
|
132
|
+
get options() {
|
|
133
|
+
return this.#options;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Metadata defines settings that describe how the schema should interact with users.
|
|
138
|
+
*
|
|
139
|
+
* @type {SchemaMetadata}
|
|
140
|
+
*/
|
|
141
|
+
get metadata() {
|
|
142
|
+
return this.#metadata;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Unions are sets of alternative schemas; a discriminator selects which to use.
|
|
147
|
+
*
|
|
148
|
+
* @type {SchemaUnionSchemas}
|
|
149
|
+
*/
|
|
150
|
+
get unionSchemas() {
|
|
151
|
+
return this.#unionSchemas; // overridden just for type narrowing
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Extract the contents of this schema and its children as a regular object.
|
|
156
|
+
*
|
|
157
|
+
* @returns {SchemaData}
|
|
158
|
+
*/
|
|
159
|
+
toData() {
|
|
160
|
+
return toData(this);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Attributes were initially a convenient shorthand for constructing schemas, but now just add confusion.
|
|
165
|
+
*
|
|
166
|
+
* @deprecated
|
|
167
|
+
* @param {string} attributeName
|
|
168
|
+
* @param {any} attributeValue
|
|
169
|
+
* @returns {Schema}
|
|
170
|
+
* @internal
|
|
171
|
+
*/
|
|
172
|
+
_setAttribute(attributeName, attributeValue) {
|
|
173
|
+
if (attributeName === 'base') {
|
|
174
|
+
this.base = attributeValue;
|
|
175
|
+
return this;
|
|
176
|
+
}
|
|
177
|
+
else if (attributeName === 'metadata') {
|
|
178
|
+
return this.addMetadata(attributeValue);
|
|
179
|
+
}
|
|
180
|
+
else if (attributeName === 'options') {
|
|
181
|
+
return this.addOptions(attributeValue ?? {});
|
|
182
|
+
}
|
|
183
|
+
else if (attributeName === 'handlers') {
|
|
184
|
+
return this.addHandlers(attributeValue ?? {});
|
|
185
|
+
}
|
|
186
|
+
else if (attributeName === 'properties') {
|
|
187
|
+
return this.addProperties(attributeValue ?? {});
|
|
188
|
+
}
|
|
189
|
+
else if (attributeName === 'unionSchemas') {
|
|
190
|
+
return this.addUnionSchemas(attributeValue ?? {})
|
|
191
|
+
}
|
|
192
|
+
else if (Object.getPrototypeOf(this)?.hasOwnProperty(attributeName)) {
|
|
193
|
+
if (!(typeof this[attributeName] === 'function')) {
|
|
194
|
+
throw new SchemaError('Unknown attribute!')
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
return this[attributeName].call(this, attributeValue);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
// ignore
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (attributeName.startsWith('_')) {
|
|
205
|
+
// This was a terrible hack. I'm still paying the price. Don't use this approach.
|
|
206
|
+
return this.meta(attributeName.slice(1), attributeValue);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
return this.option(attributeName, attributeValue);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Attributes were initially a convenient shorthand for constructing schemas, but now just add confusion.
|
|
215
|
+
*
|
|
216
|
+
* @deprecated
|
|
217
|
+
* @param {object} attributes
|
|
218
|
+
* @returns {Schema}
|
|
219
|
+
* @internal
|
|
220
|
+
*/
|
|
221
|
+
_setAttributes(attributes = {}) {
|
|
222
|
+
if (typeof attributes !== 'object') {
|
|
223
|
+
throw new SchemaError('Expected an object containing options and metadata attributes');
|
|
224
|
+
}
|
|
225
|
+
for (const [attributeName, attributeValue] of Object.entries(attributes)) {
|
|
226
|
+
this._setAttribute(attributeName, attributeValue);
|
|
227
|
+
}
|
|
228
|
+
return this;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Attach a named child schema
|
|
233
|
+
*
|
|
234
|
+
* @param {string} propertyName - property name
|
|
235
|
+
* @param {Schema|CompiledSchema|string|undefined} propertySchema - schema to associate with the property, undefined to delete current
|
|
236
|
+
* @returns {Schema} - returns self for fluent chaining
|
|
237
|
+
*/
|
|
238
|
+
property(propertyName, propertySchema) {
|
|
239
|
+
if (typeof propertyName !== 'string') {
|
|
240
|
+
throw new SchemaError('Properties must be associated with a valid name');
|
|
241
|
+
}
|
|
242
|
+
if (propertySchema === 'string') {
|
|
243
|
+
// you usually wouldn't want to do this because you almost always want to edit the property schema
|
|
244
|
+
propertySchema = new Schema(propertySchema);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (!(propertySchema instanceof Schema) && !(propertySchema instanceof CompiledSchema)) {
|
|
248
|
+
if (propertySchema === undefined) {
|
|
249
|
+
delete this.properties[propertyName];
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
throw new SchemaError('Property value must be a schema');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
this.properties[propertyName] = propertySchema;
|
|
257
|
+
|
|
258
|
+
// If you don't want the default object/array because their validation rules don't match your intent,
|
|
259
|
+
// either use "any" as the base, or explicitly set options.type ahead of time so that it's clear you
|
|
260
|
+
// know what you're doing. TODO - probably should only check this during compilation so that order doesn't matter!
|
|
261
|
+
if (this.base === undefined && this.options.type === undefined) {
|
|
262
|
+
//this.base = Number.isInteger(propertyName)? 'array' : 'object';
|
|
263
|
+
}
|
|
264
|
+
this.options.container ??= true;
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Bulk-add properties
|
|
270
|
+
*
|
|
271
|
+
* @param {SchemaProperties} properties - property name
|
|
272
|
+
* @param {symbol} [policy] - specify whether to overwrite or only initialize
|
|
273
|
+
* @returns {Schema} - returns self for fluent chaining
|
|
274
|
+
* @internal
|
|
275
|
+
*/
|
|
276
|
+
addProperties(properties, policy = SchemaPolicy.INITIALIZE) {
|
|
277
|
+
if (typeof properties !== 'object') {
|
|
278
|
+
throw new SchemaError('Invalid properties definition');
|
|
279
|
+
}
|
|
280
|
+
for (const [key, schema] of Object.entries(properties)) {
|
|
281
|
+
if (policy === SchemaPolicy.OVERWRITE || this.properties[key] === undefined) {
|
|
282
|
+
this.property(key, Schema.createFromModel(schema));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Define a schema option
|
|
290
|
+
*
|
|
291
|
+
* Options are settings that define how the schema behaves.
|
|
292
|
+
*
|
|
293
|
+
* @param {string} option - option
|
|
294
|
+
* @param {any} [value] - option value
|
|
295
|
+
* @returns {Schema} - returns self for fluent chaining
|
|
296
|
+
*/
|
|
297
|
+
option(option, value) {
|
|
298
|
+
if (typeof option !== 'string') {
|
|
299
|
+
throw new SchemaError('Options must be associated with a valid key');
|
|
300
|
+
}
|
|
301
|
+
else if (option.startsWith('_')) {
|
|
302
|
+
throw new SchemaError('Option keys cannot have a leading underscore');
|
|
303
|
+
}
|
|
304
|
+
if (value === undefined) {
|
|
305
|
+
value = true;
|
|
306
|
+
}
|
|
307
|
+
if (value === null) {
|
|
308
|
+
delete this.options[option];
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
this.options[option] = value;
|
|
312
|
+
}
|
|
313
|
+
return this;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Bulk add options
|
|
318
|
+
*
|
|
319
|
+
* @param {object} options
|
|
320
|
+
* @param {symbol} [policy]
|
|
321
|
+
* @returns {Schema}
|
|
322
|
+
* @internal
|
|
323
|
+
*/
|
|
324
|
+
addOptions(options, policy = SchemaPolicy.INITIALIZE) {
|
|
325
|
+
if (policy !== SchemaPolicy.INITIALIZE && policy !== SchemaPolicy.OVERWRITE) {
|
|
326
|
+
throw new SchemaError('Unsupported policy');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (typeof options !== 'object') {
|
|
330
|
+
throw new SchemaError('Options definition must be an object');
|
|
331
|
+
}
|
|
332
|
+
for (const [key, value] of Object.entries(options)) {
|
|
333
|
+
if (policy === SchemaPolicy.OVERWRITE || this.options[key] === undefined) {
|
|
334
|
+
this.option(key, value);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return this;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Helper function for the fluent handler api calls
|
|
342
|
+
*
|
|
343
|
+
* @param {string} handlerName
|
|
344
|
+
* @param {Array<ValueProcessorSpec>} specs
|
|
345
|
+
* @param {symbol} [policy]
|
|
346
|
+
* @returns {Schema}
|
|
347
|
+
* @private
|
|
348
|
+
*/
|
|
349
|
+
handler(handlerName, specs = [], policy = SchemaPolicy.APPEND) {
|
|
350
|
+
if (typeof handlerName !== 'string') {
|
|
351
|
+
throw new SchemaError('Handlers must be associated with a valid key');
|
|
352
|
+
}
|
|
353
|
+
if (!Object.values(SchemaPolicy).includes(policy)) {
|
|
354
|
+
throw new SchemaError('Unknown policy');
|
|
355
|
+
}
|
|
356
|
+
if (specs === undefined) {
|
|
357
|
+
if (policy === SchemaPolicy.OVERWRITE) {
|
|
358
|
+
delete this.handlers[handlerName];
|
|
359
|
+
}
|
|
360
|
+
return this;
|
|
361
|
+
}
|
|
362
|
+
if (!Array.isArray(specs)) {
|
|
363
|
+
specs = [specs];
|
|
364
|
+
}
|
|
365
|
+
// sanitize values that lead to weird schema parsing issues
|
|
366
|
+
specs = specs.map(spec => {
|
|
367
|
+
if (spec === undefined) {
|
|
368
|
+
return '$undefined';
|
|
369
|
+
}
|
|
370
|
+
else if (spec === null) {
|
|
371
|
+
return '$null';
|
|
372
|
+
}
|
|
373
|
+
return spec;
|
|
374
|
+
})
|
|
375
|
+
if (policy === SchemaPolicy.INITIALIZE && Array.isArray(this.handlers[handlerName])) {
|
|
376
|
+
return this;
|
|
377
|
+
}
|
|
378
|
+
if (policy === SchemaPolicy.OVERWRITE || policy === SchemaPolicy.INITIALIZE) {
|
|
379
|
+
this.handlers[handlerName] = specs;
|
|
380
|
+
return this;
|
|
381
|
+
}
|
|
382
|
+
if (!Array.isArray(this.handlers[handlerName])) {
|
|
383
|
+
this.handlers[handlerName] = [];
|
|
384
|
+
}
|
|
385
|
+
if (policy === SchemaPolicy.PREPEND) {
|
|
386
|
+
this.handlers[handlerName].unshift(...specs);
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
this.handlers[handlerName].push(...specs);
|
|
390
|
+
}
|
|
391
|
+
return this;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Bulk add handlers
|
|
396
|
+
*
|
|
397
|
+
* @param {object} handlers
|
|
398
|
+
* @param {symbol} [policy]
|
|
399
|
+
* @returns {Schema}
|
|
400
|
+
* @internal
|
|
401
|
+
*/
|
|
402
|
+
addHandlers(handlers, policy = SchemaPolicy.INITIALIZE) {
|
|
403
|
+
if (typeof handlers !== 'object') {
|
|
404
|
+
throw new SchemaError('Handlers definition must be an object')
|
|
405
|
+
}
|
|
406
|
+
for (const [key, value] of Object.entries(handlers)) {
|
|
407
|
+
this.handler(key, value, policy);
|
|
408
|
+
}
|
|
409
|
+
return this;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Define schema metadata (like options, but for humans and ConfigurationSource hints) - todo: locale-aware
|
|
414
|
+
*
|
|
415
|
+
* (Note: named "meta" instead of "metadata" to differentiate from the object getter)
|
|
416
|
+
*
|
|
417
|
+
* @param {string} meta - metadata key
|
|
418
|
+
* @param {any} [value] - option value
|
|
419
|
+
* @returns {Schema} - returns self for fluent chaining
|
|
420
|
+
*/
|
|
421
|
+
meta(meta, value) {
|
|
422
|
+
|
|
423
|
+
if (typeof meta !== 'string') {
|
|
424
|
+
throw new SchemaError('Metadata must be associated with a valid key');
|
|
425
|
+
}
|
|
426
|
+
if (value === undefined) {
|
|
427
|
+
value = true;
|
|
428
|
+
}
|
|
429
|
+
if (value === null) {
|
|
430
|
+
delete this.metadata[meta];
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
if (meta.startsWith('_')) {
|
|
434
|
+
meta = meta.slice(1);
|
|
435
|
+
}
|
|
436
|
+
this.metadata[meta] = value;
|
|
437
|
+
}
|
|
438
|
+
return this;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Bulk-add metadata
|
|
443
|
+
*
|
|
444
|
+
* @param {object} metadata
|
|
445
|
+
* @param {symbol} [policy]
|
|
446
|
+
* @returns {Schema}
|
|
447
|
+
* @internal
|
|
448
|
+
*/
|
|
449
|
+
addMetadata(metadata, policy = SchemaPolicy.INITIALIZE) {
|
|
450
|
+
if (typeof metadata !== 'object') {
|
|
451
|
+
throw new SchemaError('Invalid metadata definition');
|
|
452
|
+
}
|
|
453
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
454
|
+
if (policy === SchemaPolicy.OVERWRITE || this.metadata[key] === undefined) {
|
|
455
|
+
this.meta(key, value);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return this;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* The discriminator handler returns the key or schema of the union member that should be used
|
|
463
|
+
* This function appends a single value processor to the handler pipeline.
|
|
464
|
+
*
|
|
465
|
+
* @param {ValueProcessorSpec} spec
|
|
466
|
+
* @returns {Schema} - returns self for fluent chaining
|
|
467
|
+
*/
|
|
468
|
+
unionDiscriminator(spec) {
|
|
469
|
+
return this.unionDiscriminators(spec);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* The discriminator handler returns the key or schema of the union member that should be used
|
|
474
|
+
* This function applies multiple value processors to the handler pipeline.
|
|
475
|
+
* (Note that it would be highly unusual to want more than one!)
|
|
476
|
+
*
|
|
477
|
+
* @param {Array<ValueProcessorSpec>} specs
|
|
478
|
+
* @param {symbol} [policy]
|
|
479
|
+
* @returns {Schema} - returns self for fluent chaining
|
|
480
|
+
*/
|
|
481
|
+
unionDiscriminators(specs, policy) {
|
|
482
|
+
return this.handler('discriminators', specs, policy);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Add a schema as an alternative member of this schema's union.
|
|
487
|
+
*
|
|
488
|
+
* @param {string} key - union schema key (used by some discriminators to select this schema)
|
|
489
|
+
* @param {Schema|CompiledSchema} unionSchema - schema that the discriminator selects, or true/false override if a group
|
|
490
|
+
* @returns {Schema}
|
|
491
|
+
*/
|
|
492
|
+
unionSchema(key, unionSchema) {
|
|
493
|
+
|
|
494
|
+
if (!(unionSchema instanceof Schema || unionSchema instanceof CompiledSchema)) {
|
|
495
|
+
throw new SchemaError(`Invalid schema for union member ${key}`);
|
|
496
|
+
}
|
|
497
|
+
this.unionSchemas[key] = unionSchema;
|
|
498
|
+
|
|
499
|
+
return this;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Bulk-add union schemas
|
|
504
|
+
*
|
|
505
|
+
* @param {SchemaUnionSchemas} unionSchemas
|
|
506
|
+
* @param {symbol} [policy]
|
|
507
|
+
* @returns {Schema}
|
|
508
|
+
* @internal
|
|
509
|
+
*/
|
|
510
|
+
addUnionSchemas(unionSchemas, policy = SchemaPolicy.INITIALIZE) {
|
|
511
|
+
if (policy !== SchemaPolicy.INITIALIZE && policy !== SchemaPolicy.OVERWRITE) {
|
|
512
|
+
throw new SchemaError('Unsupported policy');
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (typeof unionSchemas !== 'object') {
|
|
516
|
+
throw new SchemaError('Invalid union schemas object');
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
for (const [key, unionSchema] of Object.entries(unionSchemas)) {
|
|
520
|
+
if (policy === SchemaPolicy.OVERWRITE || this.unionSchemas[key] === undefined) {
|
|
521
|
+
this.unionSchema(key, Schema.createFromModel(unionSchema));
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return this;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Mark this schema as containing (and only permitting) storage of union keys.
|
|
529
|
+
*
|
|
530
|
+
* @param {boolean} [value]
|
|
531
|
+
* @returns {Schema}
|
|
532
|
+
*/
|
|
533
|
+
unionKey(value) {
|
|
534
|
+
this.options.unionKey = Boolean(value ?? true);
|
|
535
|
+
return this;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Mark this schema as a selector.
|
|
540
|
+
*
|
|
541
|
+
* Selectors are a convenience wrapper for controlling selection conditions that only are true when
|
|
542
|
+
* the selector contains the correct selection value.
|
|
543
|
+
*
|
|
544
|
+
* @param {boolean} [value]
|
|
545
|
+
* @returns {Schema}
|
|
546
|
+
*/
|
|
547
|
+
selector(value) {
|
|
548
|
+
this.options.selector = Boolean(value ?? true);
|
|
549
|
+
|
|
550
|
+
return this;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Mark this schema as a selection.
|
|
555
|
+
*
|
|
556
|
+
* A selection schema automatically creates a condition that only activates the schema if the corresponding
|
|
557
|
+
* selector has the correct value.
|
|
558
|
+
*
|
|
559
|
+
* With the default argument, the selection schema condition uses the property name as the selector value.
|
|
560
|
+
*
|
|
561
|
+
* @param {NonNullable<any>} [value]
|
|
562
|
+
* @returns {Schema}
|
|
563
|
+
*/
|
|
564
|
+
selection(value) {
|
|
565
|
+
this.options.selection = value ?? true;
|
|
566
|
+
// fixme - this shouldn't need to be async! normalize during compilation!
|
|
567
|
+
this.condition(async (_, target, location) => {
|
|
568
|
+
if (!location.schema.isSelection) {
|
|
569
|
+
throw new SchemaError(`Conditional expected a selection schema!`, {location});
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const selectionValue = (this.options.selection === true)? location.name : this.options.selection;
|
|
573
|
+
|
|
574
|
+
const selectorLocation = location.parent?.findPropertyLocation(pl => pl.schema.isSelector)
|
|
575
|
+
|
|
576
|
+
if (selectorLocation) {
|
|
577
|
+
const ss = selectorLocation.schema;
|
|
578
|
+
const selectorValue = deepValue(target, selectorLocation.path);
|
|
579
|
+
|
|
580
|
+
return (await ss.normalizeValue(selectorValue, target, selectorLocation) === (await ss.normalizeValue(selectionValue, target, selectorLocation)));
|
|
581
|
+
}
|
|
582
|
+
return false;
|
|
583
|
+
})
|
|
584
|
+
|
|
585
|
+
return this;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Mark this schema as defining a required value (or not)
|
|
590
|
+
*
|
|
591
|
+
* Schema requirements are enforced during validation.
|
|
592
|
+
*
|
|
593
|
+
* Requirements are shallow; this can be changed via the deep() option.
|
|
594
|
+
*
|
|
595
|
+
* @param {boolean} [value]
|
|
596
|
+
* @returns {Schema}
|
|
597
|
+
*/
|
|
598
|
+
required(value) {
|
|
599
|
+
this.options.required = value ?? true;
|
|
600
|
+
return this;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Mark this schema as defining an optional value (or not); the default is optional.
|
|
605
|
+
*
|
|
606
|
+
* (Syntactic sugar to negate required())
|
|
607
|
+
* Schema requirements are enforced during validation.
|
|
608
|
+
*
|
|
609
|
+
* Requirements are shallow; this can be changed via the deep() option.
|
|
610
|
+
*
|
|
611
|
+
* @param {boolean} [value]
|
|
612
|
+
* @returns {Schema}
|
|
613
|
+
*/
|
|
614
|
+
optional(value) {
|
|
615
|
+
this.options.required = !(value);
|
|
616
|
+
return this;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Define a default value for this schema to use if there is no input.
|
|
621
|
+
*
|
|
622
|
+
* Defaults are shallow and will not cause children of undefined inputs to populate;
|
|
623
|
+
* this can be changed via the deep() option.
|
|
624
|
+
*
|
|
625
|
+
* @param {NonNullable<any>|ValueProcessorFunction|ValueProcessor} value
|
|
626
|
+
* @returns {Schema}
|
|
627
|
+
*/
|
|
628
|
+
default(value) {
|
|
629
|
+
this.options.default = value;
|
|
630
|
+
return this;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Indicate that this schema should be deeply traversed even if the input is empty
|
|
635
|
+
* (e.g. to enable deep defaults and requirements)
|
|
636
|
+
*
|
|
637
|
+
* @param {boolean} [value]
|
|
638
|
+
* @returns {Schema}
|
|
639
|
+
*/
|
|
640
|
+
deep(value) {
|
|
641
|
+
this.options.deep = value ?? true;
|
|
642
|
+
return this;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Mark this array/string as allowing empty values.
|
|
647
|
+
*
|
|
648
|
+
* @param {boolean} [value]
|
|
649
|
+
* @returns {Schema}
|
|
650
|
+
*/
|
|
651
|
+
allowEmpty(value) {
|
|
652
|
+
this.options.allowEmpty = value ?? true;
|
|
653
|
+
return this;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Mark this schema as allowing incremental assignment to children.
|
|
658
|
+
*
|
|
659
|
+
* (The default is true for any schema with children, so most of the time you'd be disabling it.)
|
|
660
|
+
*
|
|
661
|
+
* Deprecated; use the "opaque" option as it more clearly indicates the actual intent.
|
|
662
|
+
*
|
|
663
|
+
* @param {boolean} [value]
|
|
664
|
+
* @returns {Schema}
|
|
665
|
+
* @deprecated
|
|
666
|
+
*/
|
|
667
|
+
allowIncremental(value) {
|
|
668
|
+
this.options.allowIncremental = value ?? true;
|
|
669
|
+
return this;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Mark this schema as defining a value whose internal structure is hidden after transformation.
|
|
674
|
+
*
|
|
675
|
+
* This has implications both for assignment processing and validation.
|
|
676
|
+
*
|
|
677
|
+
* Deep property assignments usually result in any mid-path containers being automatically created
|
|
678
|
+
* (normalized and transformed) and property values are incrementally assigned.
|
|
679
|
+
*
|
|
680
|
+
* Opaque schemas do not allow incremental assignments, so they only create normalized mid-path containers
|
|
681
|
+
* that are staged until all relevant assignments are complete. The transform is then run, and passed
|
|
682
|
+
* the staged normalized container contents as input.
|
|
683
|
+
*
|
|
684
|
+
* Validators for opaque schemas only run on the value itself, and do not traverse into any child properties.
|
|
685
|
+
* Opaque schemas thus generally require custom validators that know how to properly handle the value.
|
|
686
|
+
*
|
|
687
|
+
* @param {boolean} [value]
|
|
688
|
+
* @returns {Schema}
|
|
689
|
+
*/
|
|
690
|
+
opaque(value = true) {
|
|
691
|
+
this.options.allowIncremental = !value;
|
|
692
|
+
return this;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Mark this schema as requiring strict enforcement (the default)
|
|
697
|
+
*
|
|
698
|
+
* Strict mode means that the data cannot have any extra data, and exactly matches the schema definition.
|
|
699
|
+
* Lax mode allows a more "fuzzy" interpretation of the data, but the data still must pass all value processors
|
|
700
|
+
* including the validation phases. (To prevent exceptions during validation, wrap the processors in a
|
|
701
|
+
* $filter, which will just return undefined.)
|
|
702
|
+
*
|
|
703
|
+
* @param {boolean} [value]
|
|
704
|
+
* @returns {Schema}
|
|
705
|
+
*/
|
|
706
|
+
strict(value) {
|
|
707
|
+
this.options.strict = value ?? true;
|
|
708
|
+
return this;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Syntactic sugar for the opposite of strict
|
|
713
|
+
*
|
|
714
|
+
* @param {boolean} [value]
|
|
715
|
+
* @returns {Schema}
|
|
716
|
+
*/
|
|
717
|
+
lax(value) {
|
|
718
|
+
this.options.strict = (value === undefined) ? false : !value;
|
|
719
|
+
return this;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Mark this schema as implicit in the transformed output (so no need to assign or check)
|
|
724
|
+
*
|
|
725
|
+
* @param {boolean} [value]
|
|
726
|
+
* @returns {Schema}
|
|
727
|
+
*/
|
|
728
|
+
implicit(value) {
|
|
729
|
+
this.options.implicit = value ?? true;
|
|
730
|
+
return this;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Define a legal input value this schema will accept.
|
|
736
|
+
*
|
|
737
|
+
* Values will be normalized for comparison.
|
|
738
|
+
*
|
|
739
|
+
* @param {NonNullable<any>} v
|
|
740
|
+
* @returns {Schema}
|
|
741
|
+
*/
|
|
742
|
+
value(v) {
|
|
743
|
+
return this.values([v])
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Define a list of one or more legal values this schema will accept
|
|
748
|
+
*
|
|
749
|
+
* Values will be normalized for comparison.
|
|
750
|
+
*
|
|
751
|
+
* @param {Array<NonNullable<any>>} va
|
|
752
|
+
* @param {symbol} [policy]
|
|
753
|
+
* @returns {Schema}
|
|
754
|
+
*/
|
|
755
|
+
values(va = [], policy = SchemaPolicy.APPEND) {
|
|
756
|
+
|
|
757
|
+
if (policy === SchemaPolicy.INITIALIZE && Array.isArray(this.options.values)) {
|
|
758
|
+
return this;
|
|
759
|
+
}
|
|
760
|
+
if (policy === SchemaPolicy.OVERWRITE || !Array.isArray(this.options.values)) {
|
|
761
|
+
this.options.values = [];
|
|
762
|
+
}
|
|
763
|
+
if (!Array.isArray(va)) {
|
|
764
|
+
va = [va];
|
|
765
|
+
}
|
|
766
|
+
if (policy === SchemaPolicy.PREPEND) {
|
|
767
|
+
this.options.values.unshift(...va);
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
this.options.values.push(...va);
|
|
771
|
+
}
|
|
772
|
+
return this;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* The condition handler determines if the schema should be processed at all.
|
|
777
|
+
* This function appends a single value processor to the handler pipeline
|
|
778
|
+
*
|
|
779
|
+
* @param {ValueProcessorSpec} spec
|
|
780
|
+
* @returns {Schema}
|
|
781
|
+
*/
|
|
782
|
+
condition(spec) {
|
|
783
|
+
return this.conditions(spec);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* The condition handler determines if the schema should be processed at all.
|
|
788
|
+
* This call applies one or more value processors to the handler pipeline (default policy = append)
|
|
789
|
+
*
|
|
790
|
+
* @param {Array<ValueProcessorSpec>} specs
|
|
791
|
+
* @param {symbol} [policy]
|
|
792
|
+
* @returns {Schema}
|
|
793
|
+
*/
|
|
794
|
+
conditions(specs, policy) {
|
|
795
|
+
return this.handler('conditions', specs, policy);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* The normalizer handler ensures input is in a format that the transformer can handle.
|
|
800
|
+
* This call appends a single value processor to the handler pipeline.
|
|
801
|
+
*
|
|
802
|
+
* @param {ValueProcessorSpec} spec
|
|
803
|
+
* @returns {Schema}
|
|
804
|
+
*/
|
|
805
|
+
normalizer(spec) {
|
|
806
|
+
return this.normalizers(spec);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* The normalizer handler ensures input is in a format that the transformer can handle.
|
|
811
|
+
* This call applies one or more value processors to the handler pipeline.
|
|
812
|
+
*
|
|
813
|
+
* @param {Array<ValueProcessorSpec>} specs
|
|
814
|
+
* @param {symbol} [policy]
|
|
815
|
+
* @returns {Schema}
|
|
816
|
+
*/
|
|
817
|
+
normalizers(specs, policy) {
|
|
818
|
+
return this.handler('normalizers', specs, policy);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* The transformer handler converts a normalized input value into the final output value for the schema.
|
|
823
|
+
* This call appends a single value processor to the handler pipeline.
|
|
824
|
+
*
|
|
825
|
+
* @param {ValueProcessorSpec} spec
|
|
826
|
+
* @returns {Schema}
|
|
827
|
+
*/
|
|
828
|
+
transformer(spec) {
|
|
829
|
+
return this.transformers(spec);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* The transformer handler converts a normalized input value into the final output value for the schema.
|
|
834
|
+
* This call applies one or more value processors to the handler pipeline.
|
|
835
|
+
*
|
|
836
|
+
* @param {Array<ValueProcessorSpec>} specs
|
|
837
|
+
* @param {symbol} [policy]
|
|
838
|
+
* @returns {Schema}
|
|
839
|
+
*/
|
|
840
|
+
transformers(specs, policy) {
|
|
841
|
+
return this.handler('transformers', specs, policy);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* The finalizer handler performs any required post-processing of a transformed value.
|
|
846
|
+
*
|
|
847
|
+
* @param {ValueProcessorSpec} spec
|
|
848
|
+
* @returns {Schema}
|
|
849
|
+
*/
|
|
850
|
+
finalizer(spec) {
|
|
851
|
+
return this.finalizers(spec);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* The finalizer handler does any required post-processing of a transformed value.
|
|
856
|
+
* This call applies one or more value processors to the handler pipeline.
|
|
857
|
+
*
|
|
858
|
+
* @param {Array<ValueProcessorSpec>} specs
|
|
859
|
+
* @param {symbol} [policy]
|
|
860
|
+
* @returns {Schema}
|
|
861
|
+
*/
|
|
862
|
+
finalizers(specs, policy) {
|
|
863
|
+
return this.handler('finalizers', specs, policy);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* The validator handler ensures an input value matches the schema, and returns a (potentially enhanced) fully validated output value.
|
|
869
|
+
* This call appends a single value processor to the handler pipeline.
|
|
870
|
+
*
|
|
871
|
+
* @param {ValueProcessorSpec} spec
|
|
872
|
+
* @returns {Schema}
|
|
873
|
+
*/
|
|
874
|
+
validator(spec) {
|
|
875
|
+
return this.validators(spec);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* The validator handler ensures an input value matches the schema, and returns a (potentially enhanced) fully validated output value.
|
|
880
|
+
* This call applies one or more value processors to the handler pipeline.
|
|
881
|
+
*
|
|
882
|
+
* @param {Array<ValueProcessorSpec>} specs
|
|
883
|
+
* @param {symbol} [policy]
|
|
884
|
+
* @returns {Schema}
|
|
885
|
+
*/
|
|
886
|
+
validators(specs, policy) {
|
|
887
|
+
return this.handler('validators', specs, policy);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* The serialize handler restores a configuration value to its pre-transform normalized form.
|
|
892
|
+
* This call appends a single value processor to the handler pipeline.
|
|
893
|
+
*
|
|
894
|
+
* @param {ValueProcessorSpec} spec
|
|
895
|
+
* @returns {Schema}
|
|
896
|
+
*/
|
|
897
|
+
serializer(spec) {
|
|
898
|
+
return this.serializers(spec);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* The serialize handler restores a configuration value to its pre-transform normalized form.
|
|
903
|
+
* This call applies one or more value processors to the handler pipeline.
|
|
904
|
+
*
|
|
905
|
+
* @param {Array<ValueProcessorSpec>} specs
|
|
906
|
+
* @param {symbol} [policy]
|
|
907
|
+
* @returns {Schema}
|
|
908
|
+
*/
|
|
909
|
+
serializers(specs, policy) {
|
|
910
|
+
return this.handler('serializers', specs, policy);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* Use another schema to extend the current one without overwriting.
|
|
916
|
+
*
|
|
917
|
+
* @param {ISchema|SchemaData} otherSchema - source schema
|
|
918
|
+
* @param {Map<any,any>} [seen]
|
|
919
|
+
* @returns {Schema} - returns self
|
|
920
|
+
*/
|
|
921
|
+
extend(otherSchema, seen = new Map()) {
|
|
922
|
+
if (typeof otherSchema !== 'object') {
|
|
923
|
+
throw new SchemaError(`Invalid schema to extend`)
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// Set base if not already set
|
|
927
|
+
if (!this.base && otherSchema.base) {
|
|
928
|
+
this.base = otherSchema.base;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
this.addProperties(Object.fromEntries(
|
|
932
|
+
Object.entries(otherSchema.properties ?? {})
|
|
933
|
+
.map(([propertyName, propertySchema]) => [propertyName, Schema.createFromModel(propertySchema, seen)])));
|
|
934
|
+
this.addUnionSchemas(Object.fromEntries(
|
|
935
|
+
Object.entries(otherSchema.unionSchemas ?? {})
|
|
936
|
+
.map(([unionKey, unionSchema]) => [unionKey, Schema.createFromModel(unionSchema, seen)])));
|
|
937
|
+
|
|
938
|
+
this.addOptions(otherSchema.options ?? {});
|
|
939
|
+
this.addMetadata(otherSchema.metadata ?? {});
|
|
940
|
+
this.addHandlers(otherSchema.handlers ?? {}); // todo - this behaves differently than compilation!?
|
|
941
|
+
|
|
942
|
+
return this;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Make a copy of this schema
|
|
947
|
+
*
|
|
948
|
+
* @returns {Schema}
|
|
949
|
+
*/
|
|
950
|
+
clone() {
|
|
951
|
+
return Schema.createFromModel(this);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Create a new Schema from something schema-shaped
|
|
956
|
+
*
|
|
957
|
+
* @param {ISchema|SchemaData|string} model
|
|
958
|
+
* @param {Map<any,any>} [seen]
|
|
959
|
+
* @returns {Schema}
|
|
960
|
+
*/
|
|
961
|
+
static createFromModel(model, seen = new Map()) {
|
|
962
|
+
if (typeof model === 'string') {
|
|
963
|
+
return new Schema(model);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
if (seen.has(model)) {
|
|
967
|
+
return seen.get(model);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
const schema = new Schema();
|
|
971
|
+
seen.set(model, schema);
|
|
972
|
+
|
|
973
|
+
if (model.base) {
|
|
974
|
+
schema.base = model.base;
|
|
975
|
+
}
|
|
976
|
+
return schema.extend(model, seen);
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Static schema factory (useful for aliasing to reduce typing!)
|
|
980
|
+
*
|
|
981
|
+
* Prefer using fluent setters over passing options/metadata to this call
|
|
982
|
+
*
|
|
983
|
+
* @param {string|ISchema|SchemaData|Schema|CompiledSchema} [base] - schema
|
|
984
|
+
* @param {object} [options] - schema options
|
|
985
|
+
* @param {ISchemaMetadata} [metadata] - schema metadata
|
|
986
|
+
* @returns {Schema}
|
|
987
|
+
*/
|
|
988
|
+
static create(base, options, metadata) {
|
|
989
|
+
return new Schema(base, options, metadata);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/**
|
|
993
|
+
* Static schema factory for special schemas that ignore assignments and produce a single defined value
|
|
994
|
+
*
|
|
995
|
+
* Prefer using fluent setters over passing options/metadata to this call
|
|
996
|
+
*
|
|
997
|
+
* @param {any} literalValue - the value this schema will always emit
|
|
998
|
+
* @param {object} [options] - additional options
|
|
999
|
+
* @param {ISchemaMetadata} [metadata] - additional metadata
|
|
1000
|
+
* @returns {Schema}
|
|
1001
|
+
*/
|
|
1002
|
+
static literal(literalValue, options, metadata) {
|
|
1003
|
+
let base = 'any';
|
|
1004
|
+
if (typeof literalValue === 'string') {
|
|
1005
|
+
base = 'string';
|
|
1006
|
+
}
|
|
1007
|
+
else if (typeof literalValue === 'number') {
|
|
1008
|
+
base = 'number';
|
|
1009
|
+
}
|
|
1010
|
+
else if (typeof literalValue === 'boolean') {
|
|
1011
|
+
base = 'boolean';
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
const schema = new Schema(base)
|
|
1015
|
+
.option('values', [literalValue])
|
|
1016
|
+
.option('default', literalValue)
|
|
1017
|
+
.option('compileHook', (eventName, hookSchema) => {
|
|
1018
|
+
if (eventName === 'finalize') {
|
|
1019
|
+
// Verify that the schema wasn't mangled into something weird
|
|
1020
|
+
if (hookSchema.options.values?.length !== 1) {
|
|
1021
|
+
throw new SchemaError(`Literal schema needs one value defined`);
|
|
1022
|
+
}
|
|
1023
|
+
if (hookSchema.hasChildren) {
|
|
1024
|
+
throw new SchemaError(`Literal schema should not have child properties`)
|
|
1025
|
+
}
|
|
1026
|
+
if (hookSchema.isUnion) {
|
|
1027
|
+
throw new SchemaError(`Literal schema should not be a union`)
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
})
|
|
1031
|
+
// Important note: we do *not* return the literal during normalization
|
|
1032
|
+
// because hoisting union discriminators wants a compatible normalizer.
|
|
1033
|
+
// The values option prevents assignment if the normalized input does not
|
|
1034
|
+
// match the required literal value.
|
|
1035
|
+
.transformer(() => literalValue)
|
|
1036
|
+
|
|
1037
|
+
if (options) {
|
|
1038
|
+
schema._setAttributes(options);
|
|
1039
|
+
}
|
|
1040
|
+
if (metadata) {
|
|
1041
|
+
schema.addMetadata(metadata);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
return schema;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/**
|
|
1048
|
+
* Static schema factory for creating a schema that inherits its value from the first parent with the property name.
|
|
1049
|
+
* If no property name is provided, the inherit schema's property name is used, so it will look for the same name
|
|
1050
|
+
* higher in the schema hierarchy.
|
|
1051
|
+
*
|
|
1052
|
+
* TODO - restore compilation hook for checking whether this is a legal setup
|
|
1053
|
+
*
|
|
1054
|
+
* @returns {Schema}
|
|
1055
|
+
* @param {string} [propertyName]
|
|
1056
|
+
* @internal
|
|
1057
|
+
*/
|
|
1058
|
+
static inherit(propertyName) {
|
|
1059
|
+
|
|
1060
|
+
return new Schema()
|
|
1061
|
+
.option('reference', true)
|
|
1062
|
+
.transformer(/** @type {ValueProcessorFunction} */ (_value, config, location, options) => {
|
|
1063
|
+
const name = propertyName ?? location.name;
|
|
1064
|
+
if (location.parent === undefined) {
|
|
1065
|
+
if (location.schema.strict !== false) {
|
|
1066
|
+
throw new SchemaError('A top-level schema cannot have an inherited value');
|
|
1067
|
+
}
|
|
1068
|
+
return undefined;
|
|
1069
|
+
}
|
|
1070
|
+
let ancestorLocation = location.parent?.parent;
|
|
1071
|
+
|
|
1072
|
+
while (ancestorLocation !== undefined) {
|
|
1073
|
+
const candidate = ancestorLocation.relative(name);
|
|
1074
|
+
|
|
1075
|
+
if (candidate !== undefined) {
|
|
1076
|
+
return deepValue(config, candidate.path);
|
|
1077
|
+
}
|
|
1078
|
+
ancestorLocation = ancestorLocation.parent;
|
|
1079
|
+
}
|
|
1080
|
+
if (location.schema.strict !== false) {
|
|
1081
|
+
throw new SchemaError(`Inherited property "${name}" not found in any ancestor of "${location}"`);
|
|
1082
|
+
}
|
|
1083
|
+
return undefined;
|
|
1084
|
+
|
|
1085
|
+
})
|
|
1086
|
+
|
|
1087
|
+
.serializer(() => undefined)
|
|
1088
|
+
.default(/** @type {ValueProcessorFunction} */ (_value, config, location) => {
|
|
1089
|
+
// todo - check for dynamic=false during compilation!
|
|
1090
|
+
return propertyName ?? location.name;
|
|
1091
|
+
})
|
|
1092
|
+
.meta('omitFromSerialize')
|
|
1093
|
+
.meta('internal')
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/**
|
|
1097
|
+
* Static schema factory for creating a schema that gets its value from another location based on a path.
|
|
1098
|
+
*
|
|
1099
|
+
* By default, `path` is interpreted as an absolute path from the root. If the `relative` flag is
|
|
1100
|
+
* set or navigation metacharacters start the path, it will be relative to the current schema.
|
|
1101
|
+
* - An absolute path prefixed with `/` starts at the root.
|
|
1102
|
+
* - A path prefixed with `^` navigates up one level from the current schema path.
|
|
1103
|
+
*
|
|
1104
|
+
* TODO - restore hook for checking whether the provided path is known at compilation time
|
|
1105
|
+
*
|
|
1106
|
+
* @param {string} path
|
|
1107
|
+
* @param {boolean} [relative]
|
|
1108
|
+
* @returns {Schema}
|
|
1109
|
+
* @internal
|
|
1110
|
+
*/
|
|
1111
|
+
static reference(path, relative = false) {
|
|
1112
|
+
|
|
1113
|
+
const c = path?.charAt(0);
|
|
1114
|
+
|
|
1115
|
+
if (c === '^' || c === '/' || c === '.') {
|
|
1116
|
+
relative = true;
|
|
1117
|
+
}
|
|
1118
|
+
return new Schema()
|
|
1119
|
+
.option('reference', true)
|
|
1120
|
+
.default(path)
|
|
1121
|
+
.normalizer(() => path)
|
|
1122
|
+
.transformer(/** @type {ValueProcessorFunction} */ (_, target, location, options) => {
|
|
1123
|
+
const referenceLocation = relative? location.relative(path) : location.absolute(path);
|
|
1124
|
+
if (referenceLocation === undefined) {
|
|
1125
|
+
throw new SchemaError(`Reference path ${path} not found`);
|
|
1126
|
+
}
|
|
1127
|
+
const referenceSchema = referenceLocation.schema;
|
|
1128
|
+
if (referenceSchema === undefined) {
|
|
1129
|
+
if (options?.context?.final) {
|
|
1130
|
+
throw new SchemaError(`Schema for reference path ${path} not found`);
|
|
1131
|
+
}
|
|
1132
|
+
return undefined;
|
|
1133
|
+
}
|
|
1134
|
+
return deepValue(target, referenceLocation.path);
|
|
1135
|
+
})
|
|
1136
|
+
.validator(/** @type {ValueProcessorFunction} */ (value, target, location, options) => {
|
|
1137
|
+
const referenceLocation = relative? location.relative(path) : location.absolute(path);
|
|
1138
|
+
if (referenceLocation === undefined) {
|
|
1139
|
+
throw new SchemaError(`Reference path ${path} not found`);
|
|
1140
|
+
}
|
|
1141
|
+
const referenceSchema = referenceLocation.schema;
|
|
1142
|
+
if (referenceSchema === undefined) {
|
|
1143
|
+
if (options?.context?.final) {
|
|
1144
|
+
throw new SchemaError(`Schema for reference path ${path} not found`);
|
|
1145
|
+
}
|
|
1146
|
+
return undefined;
|
|
1147
|
+
}
|
|
1148
|
+
const targetValue = deepValue(target, referenceLocation.path);
|
|
1149
|
+
|
|
1150
|
+
// If identical, we're done.
|
|
1151
|
+
if (targetValue === value) {
|
|
1152
|
+
return value;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// simple values and opaque values must have an identical reference
|
|
1156
|
+
if (!referenceSchema?.hasChildren || referenceSchema.isOpaque) {
|
|
1157
|
+
throw new SchemaError(`Reference is not exactly the same as ${path}`)
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// this feels wrong, but there's no guarantee a container wasn't rebuilt during validation
|
|
1161
|
+
return value;
|
|
1162
|
+
|
|
1163
|
+
})
|
|
1164
|
+
.serializer(() => undefined)
|
|
1165
|
+
.meta('omitFromSerialize')
|
|
1166
|
+
.meta('internal')
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* Policies for fine-grained control of composite schema internals
|
|
1173
|
+
* @readonly
|
|
1174
|
+
* @enum {symbol}
|
|
1175
|
+
*/
|
|
1176
|
+
export const SchemaPolicy = Object.freeze({
|
|
1177
|
+
INITIALIZE: Symbol('INITIALIZE'), // only set if not already set
|
|
1178
|
+
OVERWRITE: Symbol('OVERWRITE'), // overwrite
|
|
1179
|
+
APPEND: Symbol('APPEND'), // add values to the end
|
|
1180
|
+
PREPEND: Symbol('PREPEND') // add values to the beginning
|
|
1181
|
+
});
|
|
1182
|
+
|