@wirebench/engine 2.2.1 → 3.1.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 +134 -1
- package/dist/account/schema.d.ts +34 -0
- package/dist/account/schema.js +29 -0
- package/dist/assert/callback.d.ts +76 -0
- package/dist/assert/callback.js +320 -0
- package/dist/assert/capture-source.d.ts +50 -0
- package/dist/assert/capture-source.js +89 -0
- package/dist/assert/check.d.ts +8 -0
- package/dist/assert/check.js +18 -0
- package/dist/assert/header.d.ts +5 -0
- package/dist/assert/header.js +48 -0
- package/dist/assert/index.d.ts +2 -2
- package/dist/assert/index.js +12 -0
- package/dist/assert/match.d.ts +6 -0
- package/dist/assert/match.js +13 -4
- package/dist/assert/model.d.ts +110 -3
- package/dist/assert/model.js +16 -1
- package/dist/assert/schema.d.ts +194 -1
- package/dist/assert/schema.js +154 -12
- package/dist/assert/status-names.d.ts +9 -0
- package/dist/assert/status-names.js +27 -0
- package/dist/assert/status.d.ts +4 -1
- package/dist/assert/status.js +27 -3
- package/dist/asyncapi/browser.d.ts +7 -0
- package/dist/asyncapi/browser.js +6 -0
- package/dist/asyncapi/frame-check-worker-host.d.ts +44 -0
- package/dist/asyncapi/frame-check-worker-host.js +175 -0
- package/dist/asyncapi/frame-check-worker.d.ts +12 -0
- package/dist/asyncapi/frame-check-worker.js +25 -0
- package/dist/asyncapi/frame-check.d.ts +25 -0
- package/dist/asyncapi/frame-check.js +78 -0
- package/dist/asyncapi/import.d.ts +25 -0
- package/dist/asyncapi/import.js +34 -0
- package/dist/asyncapi/map.d.ts +40 -0
- package/dist/asyncapi/map.js +230 -0
- package/dist/asyncapi/model.d.ts +84 -0
- package/dist/asyncapi/model.js +7 -0
- package/dist/asyncapi/normalise-2.d.ts +13 -0
- package/dist/asyncapi/normalise-2.js +113 -0
- package/dist/asyncapi/normalise-3.d.ts +14 -0
- package/dist/asyncapi/normalise-3.js +133 -0
- package/dist/asyncapi/parse.d.ts +31 -0
- package/dist/asyncapi/parse.js +85 -0
- package/dist/asyncapi/read.d.ts +35 -0
- package/dist/asyncapi/read.js +186 -0
- package/dist/asyncapi/security.d.ts +14 -0
- package/dist/asyncapi/security.js +55 -0
- package/dist/asyncapi/update.d.ts +53 -0
- package/dist/asyncapi/update.js +220 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.js +22 -0
- package/dist/grpc/events.d.ts +22 -0
- package/dist/grpc/events.js +1 -0
- package/dist/grpc/expand.d.ts +1 -1
- package/dist/grpc/expand.js +15 -5
- package/dist/grpc/files.d.ts +271 -0
- package/dist/grpc/files.js +62 -0
- package/dist/grpc/model.d.ts +11 -1
- package/dist/grpc/module.d.ts +2 -0
- package/dist/grpc/module.js +13 -0
- package/dist/grpc/run.d.ts +73 -0
- package/dist/grpc/run.js +407 -0
- package/dist/grpc/script-types.d.ts +8 -0
- package/dist/grpc/script-types.js +149 -0
- package/dist/grpc/scripting.d.ts +34 -0
- package/dist/grpc/scripting.js +147 -0
- package/dist/grpc/send-input.d.ts +46 -0
- package/dist/grpc/send-input.js +42 -0
- package/dist/grpc/send.d.ts +2 -2
- package/dist/grpc/status.d.ts +9 -2
- package/dist/grpc/status.js +11 -24
- package/dist/grpc/storage.d.ts +9 -0
- package/dist/grpc/storage.js +167 -0
- package/dist/{rest/auth.d.ts → http/auth/apply-auth.d.ts} +4 -4
- package/dist/{rest/auth.js → http/auth/apply-auth.js} +1 -1
- package/dist/http/auth/apply.d.ts +1 -1
- package/dist/{rest → http/auth}/oauth2.d.ts +2 -2
- package/dist/{rest → http/auth}/oauth2.js +1 -1
- package/dist/http/auth/send-auth.d.ts +57 -0
- package/dist/http/auth/send-auth.js +7 -0
- package/dist/http/client.js +150 -8
- package/dist/http/cookies.d.ts +79 -0
- package/dist/http/cookies.js +81 -0
- package/dist/http/curl.d.ts +4 -14
- package/dist/http/curl.js +4 -154
- package/dist/http/decompress.d.ts +7 -0
- package/dist/http/decompress.js +23 -1
- package/dist/http/document-fetch.d.ts +42 -0
- package/dist/http/document-fetch.js +207 -0
- package/dist/http/entries.d.ts +27 -0
- package/dist/http/entries.js +16 -0
- package/dist/http/escape.d.ts +18 -0
- package/dist/http/escape.js +30 -0
- package/dist/http/fetch-document.d.ts +28 -0
- package/dist/{wsdl/fetch.js → http/fetch-document.js} +8 -16
- package/dist/http/media-type.d.ts +9 -0
- package/dist/http/media-type.js +12 -0
- package/dist/http/transcript-cap.d.ts +26 -0
- package/dist/http/transcript-cap.js +36 -0
- package/dist/http/types.d.ts +59 -0
- package/dist/http/webhook-signature.d.ts +81 -0
- package/dist/http/webhook-signature.js +227 -0
- package/dist/import-detect.d.ts +2 -2
- package/dist/import-detect.js +10 -1
- package/dist/index.d.ts +207 -55
- package/dist/index.js +129 -32
- package/dist/json/cursor.d.ts +2 -0
- package/dist/json/cursor.js +1 -0
- package/dist/json/pointer-range.d.ts +18 -0
- package/dist/json/pointer-range.js +157 -0
- package/dist/json/schema/model.d.ts +59 -0
- package/dist/json/schema/model.js +8 -0
- package/dist/json/schema/parse-text.d.ts +16 -0
- package/dist/json/schema/parse-text.js +37 -0
- package/dist/{rest/openapi → json/schema}/refs.d.ts +2 -2
- package/dist/{rest/openapi → json/schema}/refs.js +3 -3
- package/dist/{rest/openapi → json/schema}/sample.d.ts +4 -0
- package/dist/{rest/openapi → json/schema}/sample.js +2 -2
- package/dist/json/schema/source.d.ts +21 -0
- package/dist/json/schema/source.js +8 -0
- package/dist/json/schema-validate.d.ts +49 -0
- package/dist/json/schema-validate.js +557 -0
- package/dist/{wss/keystore → keystore}/index.js +1 -1
- package/dist/{wss/keystore → keystore}/pem.js +1 -1
- package/dist/{wss/keystore → keystore}/pkcs12.js +1 -1
- package/dist/project/attachments-cache.d.ts +3 -1
- package/dist/{wsdl → project}/cache-naming.js +1 -1
- package/dist/project/endpoints.d.ts +18 -8
- package/dist/project/endpoints.js +21 -6
- package/dist/project/environments.d.ts +7 -5
- package/dist/project/history.d.ts +105 -9
- package/dist/project/history.js +195 -23
- package/dist/project/inherit.d.ts +7 -0
- package/dist/project/inherit.js +9 -0
- package/dist/project/keystores.d.ts +1 -1
- package/dist/project/load-helpers.d.ts +89 -0
- package/dist/project/load-helpers.js +177 -0
- package/dist/project/load.d.ts +30 -4
- package/dist/project/load.js +168 -545
- package/dist/project/managed-files.d.ts +36 -0
- package/dist/project/managed-files.js +97 -0
- package/dist/project/migrate.d.ts +2 -2
- package/dist/project/migrate.js +2 -2
- package/dist/project/model.d.ts +92 -14
- package/dist/project/model.js +50 -4
- package/dist/project/paths.d.ts +10 -1
- package/dist/project/paths.js +10 -1
- package/dist/project/preferences.d.ts +11 -2
- package/dist/project/preferences.js +5 -1
- package/dist/project/properties.d.ts +31 -17
- package/dist/project/properties.js +89 -70
- package/dist/project/request-location.d.ts +20 -0
- package/dist/project/request-location.js +62 -0
- package/dist/project/save.d.ts +12 -2
- package/dist/project/save.js +186 -141
- package/dist/project/schema-parts.d.ts +337 -0
- package/dist/project/schema-parts.js +248 -0
- package/dist/project/schema.d.ts +72 -928
- package/dist/project/schema.js +17 -466
- package/dist/project/sequence-guards.d.ts +43 -0
- package/dist/project/sequence-guards.js +109 -0
- package/dist/project/serialize-helpers.d.ts +60 -0
- package/dist/project/serialize-helpers.js +108 -0
- package/dist/project/serialize.d.ts +7 -8
- package/dist/project/serialize.js +34 -293
- package/dist/protocol/features.d.ts +51 -0
- package/dist/protocol/features.js +64 -0
- package/dist/protocol/index.d.ts +7 -0
- package/dist/protocol/index.js +4 -0
- package/dist/protocol/module.d.ts +247 -0
- package/dist/protocol/module.js +48 -0
- package/dist/protocol/registry.d.ts +42 -0
- package/dist/protocol/registry.js +47 -0
- package/dist/protocols.d.ts +77 -0
- package/dist/protocols.js +40 -0
- package/dist/redact/index.d.ts +12 -1
- package/dist/redact/index.js +55 -7
- package/dist/redact/literal.d.ts +10 -0
- package/dist/redact/literal.js +110 -12
- package/dist/rest/body.d.ts +2 -10
- package/dist/rest/body.js +3 -25
- package/dist/rest/browser.d.ts +4 -0
- package/dist/rest/browser.js +5 -0
- package/dist/rest/contract-check-worker-host.d.ts +36 -0
- package/dist/rest/contract-check-worker-host.js +158 -0
- package/dist/rest/contract-check-worker.d.ts +7 -0
- package/dist/rest/contract-check-worker.js +28 -0
- package/dist/rest/contract-check.d.ts +43 -0
- package/dist/rest/contract-check.js +217 -0
- package/dist/rest/cookie-jar.d.ts +62 -0
- package/dist/rest/cookie-jar.js +262 -0
- package/dist/rest/cookies.d.ts +6 -22
- package/dist/rest/cookies.js +6 -42
- package/dist/rest/curl.js +5 -1
- package/dist/rest/events.d.ts +12 -0
- package/dist/rest/events.js +1 -0
- package/dist/rest/expand.d.ts +6 -1
- package/dist/rest/expand.js +29 -11
- package/dist/rest/files.d.ts +439 -0
- package/dist/rest/files.js +107 -0
- package/dist/rest/json-form.d.ts +97 -0
- package/dist/rest/json-form.js +396 -0
- package/dist/rest/model.d.ts +35 -24
- package/dist/rest/model.js +6 -9
- package/dist/rest/module.d.ts +2 -0
- package/dist/rest/module.js +13 -0
- package/dist/rest/openapi/cache.d.ts +7 -2
- package/dist/rest/openapi/cache.js +2 -2
- package/dist/rest/openapi/import.d.ts +11 -16
- package/dist/rest/openapi/import.js +6 -2
- package/dist/rest/openapi/map.d.ts +38 -1
- package/dist/rest/openapi/map.js +151 -41
- package/dist/rest/openapi/match.d.ts +20 -0
- package/dist/rest/openapi/match.js +117 -0
- package/dist/rest/openapi/model.d.ts +33 -45
- package/dist/rest/openapi/parse.d.ts +3 -12
- package/dist/rest/openapi/parse.js +138 -67
- package/dist/rest/openapi/responses.d.ts +23 -0
- package/dist/rest/openapi/responses.js +59 -0
- package/dist/rest/openapi/runtime-expression.d.ts +69 -0
- package/dist/rest/openapi/runtime-expression.js +213 -0
- package/dist/rest/openapi/update.d.ts +116 -0
- package/dist/rest/openapi/update.js +456 -0
- package/dist/rest/postman/map.d.ts +1 -0
- package/dist/rest/postman/map.js +66 -4
- package/dist/rest/postman/model.d.ts +9 -0
- package/dist/rest/postman/parse.js +33 -10
- package/dist/rest/response.d.ts +2 -23
- package/dist/rest/response.js +3 -75
- package/dist/rest/run.d.ts +56 -0
- package/dist/rest/run.js +395 -0
- package/dist/rest/script-types.d.ts +7 -0
- package/dist/rest/script-types.js +83 -0
- package/dist/rest/scripting.d.ts +38 -0
- package/dist/rest/scripting.js +282 -0
- package/dist/rest/send-input.d.ts +54 -0
- package/dist/rest/send-input.js +73 -0
- package/dist/rest/send.d.ts +77 -4
- package/dist/rest/send.js +128 -7
- package/dist/rest/sse-transcript.d.ts +25 -0
- package/dist/rest/sse-transcript.js +95 -0
- package/dist/rest/sse.d.ts +43 -0
- package/dist/rest/sse.js +195 -0
- package/dist/rest/storage.d.ts +26 -0
- package/dist/rest/storage.js +262 -0
- package/dist/rest/url.d.ts +10 -4
- package/dist/rest/url.js +38 -5
- package/dist/run/baseline-update.d.ts +35 -0
- package/dist/run/baseline-update.js +97 -0
- package/dist/run/baseline.d.ts +42 -0
- package/dist/run/baseline.js +93 -0
- package/dist/run/context.d.ts +86 -0
- package/dist/run/context.js +31 -0
- package/dist/run/current-values.d.ts +32 -0
- package/dist/run/current-values.js +43 -0
- package/dist/run/event-queue.d.ts +14 -0
- package/dist/run/event-queue.js +46 -0
- package/dist/run/exchange.d.ts +62 -0
- package/dist/run/exchange.js +65 -0
- package/dist/run/host.d.ts +89 -0
- package/dist/run/host.js +1 -0
- package/dist/run/index.d.ts +20 -7
- package/dist/run/index.js +12 -4
- package/dist/run/oauth2-token.d.ts +42 -0
- package/dist/run/oauth2-token.js +92 -0
- package/dist/run/open.d.ts +12 -0
- package/dist/run/open.js +16 -0
- package/dist/run/run.d.ts +124 -4
- package/dist/run/run.js +293 -134
- package/dist/run/scope.d.ts +11 -0
- package/dist/run/scope.js +26 -0
- package/dist/run/script-support.d.ts +53 -0
- package/dist/run/script-support.js +68 -0
- package/dist/run/secret-needs.d.ts +11 -4
- package/dist/run/secret-needs.js +48 -96
- package/dist/run/select.d.ts +34 -25
- package/dist/run/select.js +45 -54
- package/dist/run/send-helpers.d.ts +93 -0
- package/dist/run/send-helpers.js +255 -0
- package/dist/run/tree.d.ts +50 -0
- package/dist/run/tree.js +54 -0
- package/dist/script/api/postman.d.ts +10 -0
- package/dist/script/api/postman.js +264 -0
- package/dist/script/api/prelude.d.ts +20 -0
- package/dist/script/api/prelude.js +215 -0
- package/dist/script/apply.d.ts +44 -0
- package/dist/script/apply.js +120 -0
- package/dist/script/check/host.d.ts +41 -0
- package/dist/script/check/host.js +143 -0
- package/dist/script/check/service.d.ts +52 -0
- package/dist/script/check/service.js +195 -0
- package/dist/script/check/worker.d.ts +1 -0
- package/dist/script/check/worker.js +40 -0
- package/dist/script/index.d.ts +16 -0
- package/dist/script/index.js +10 -0
- package/dist/script/lookup.d.ts +10 -0
- package/dist/script/lookup.js +23 -0
- package/dist/script/model.d.ts +82 -0
- package/dist/script/model.js +18 -0
- package/dist/script/props.d.ts +8 -0
- package/dist/script/props.js +28 -0
- package/dist/script/request-scripts.d.ts +83 -0
- package/dist/script/request-scripts.js +160 -0
- package/dist/script/run.d.ts +47 -0
- package/dist/script/run.js +146 -0
- package/dist/script/sandbox/execute.d.ts +26 -0
- package/dist/script/sandbox/execute.js +212 -0
- package/dist/script/sandbox/host-api.d.ts +27 -0
- package/dist/script/sandbox/host-api.js +101 -0
- package/dist/script/sandbox/host.d.ts +18 -0
- package/dist/script/sandbox/host.js +172 -0
- package/dist/script/sandbox/model.d.ts +69 -0
- package/dist/script/sandbox/model.js +24 -0
- package/dist/script/sandbox/worker.d.ts +1 -0
- package/dist/script/sandbox/worker.js +20 -0
- package/dist/script/send.d.ts +27 -0
- package/dist/script/send.js +76 -0
- package/dist/script/strip.d.ts +5 -0
- package/dist/script/strip.js +43 -0
- package/dist/script/types/api.d.ts +38 -0
- package/dist/script/types/api.js +113 -0
- package/dist/script/types/json-schema.d.ts +26 -0
- package/dist/script/types/json-schema.js +206 -0
- package/dist/secrets/env-names.d.ts +9 -1
- package/dist/secrets/env-names.js +17 -1
- package/dist/secrets/resolve.d.ts +44 -4
- package/dist/secrets/resolve.js +58 -1
- package/dist/secrets/scan/apply.d.ts +36 -0
- package/dist/secrets/scan/apply.js +194 -0
- package/dist/secrets/scan/rules.d.ts +32 -0
- package/dist/secrets/scan/rules.js +346 -0
- package/dist/secrets/scan/scan.d.ts +6 -0
- package/dist/secrets/scan/scan.js +57 -0
- package/dist/secrets/scan/walk.d.ts +70 -0
- package/dist/secrets/scan/walk.js +156 -0
- package/dist/secrets/secret-refs.d.ts +8 -0
- package/dist/secrets/secret-refs.js +25 -0
- package/dist/secrets/secret-token.d.ts +17 -0
- package/dist/secrets/secret-token.js +25 -0
- package/dist/sequence/file.d.ts +169 -0
- package/dist/sequence/file.js +237 -0
- package/dist/sequence/index.d.ts +7 -0
- package/dist/sequence/index.js +5 -0
- package/dist/sequence/load.d.ts +32 -0
- package/dist/sequence/load.js +56 -0
- package/dist/sequence/model.d.ts +115 -0
- package/dist/sequence/model.js +53 -0
- package/dist/sequence/run.d.ts +131 -0
- package/dist/sequence/run.js +191 -0
- package/dist/sequence/transfer.d.ts +24 -0
- package/dist/sequence/transfer.js +75 -0
- package/dist/server-api/audit.d.ts +455 -0
- package/dist/server-api/audit.js +186 -0
- package/dist/server-api/ci-tokens.d.ts +50 -0
- package/dist/server-api/ci-tokens.js +35 -0
- package/dist/server-api/hooks.d.ts +337 -0
- package/dist/server-api/hooks.js +152 -0
- package/dist/server-api/identity.d.ts +210 -0
- package/dist/server-api/identity.js +115 -0
- package/dist/server-api/licensing.d.ts +80 -0
- package/dist/server-api/licensing.js +53 -0
- package/dist/server-api/live.d.ts +132 -0
- package/dist/server-api/live.js +102 -0
- package/dist/server-api/meta.d.ts +31 -0
- package/dist/server-api/meta.js +21 -0
- package/dist/server-api/sync.d.ts +179 -0
- package/dist/server-api/sync.js +124 -0
- package/dist/server-api/teams.d.ts +306 -0
- package/dist/server-api/teams.js +113 -0
- package/dist/snapshot/diff.d.ts +30 -0
- package/dist/snapshot/diff.js +75 -0
- package/dist/snapshot/format.d.ts +17 -0
- package/dist/snapshot/format.js +40 -0
- package/dist/snapshot/golden-file.d.ts +45 -0
- package/dist/snapshot/golden-file.js +173 -0
- package/dist/snapshot/ignore.d.ts +14 -0
- package/dist/snapshot/ignore.js +79 -0
- package/dist/snapshot/index.d.ts +9 -0
- package/dist/snapshot/index.js +9 -0
- package/dist/snapshot/json-diff.d.ts +3 -0
- package/dist/snapshot/json-diff.js +93 -0
- package/dist/snapshot/xml-diff.d.ts +10 -0
- package/dist/snapshot/xml-diff.js +194 -0
- package/dist/soap/auth.d.ts +26 -0
- package/dist/soap/auth.js +54 -0
- package/dist/soap/curl.d.ts +20 -0
- package/dist/soap/curl.js +161 -0
- package/dist/soap/expand.d.ts +24 -0
- package/dist/soap/expand.js +95 -0
- package/dist/soap/files.d.ts +384 -0
- package/dist/soap/files.js +114 -0
- package/dist/soap/form-request.d.ts +13 -0
- package/dist/soap/form-request.js +2 -2
- package/dist/soap/generate.d.ts +12 -0
- package/dist/{generate.js → soap/generate.js} +4 -4
- package/dist/{import.d.ts → soap/import.d.ts} +4 -4
- package/dist/{import.js → soap/import.js} +13 -13
- package/dist/soap/json-operation.d.ts +35 -0
- package/dist/soap/json-operation.js +225 -0
- package/dist/soap/legacy-project/env-refs.d.ts +19 -0
- package/dist/soap/legacy-project/env-refs.js +105 -0
- package/dist/soap/legacy-project/map.d.ts +2 -2
- package/dist/soap/legacy-project/map.js +6 -0
- package/dist/soap/mime/multipart.d.ts +2 -2
- package/dist/soap/mime/multipart.js +2 -5
- package/dist/soap/mime/send-pipeline.d.ts +1 -1
- package/dist/soap/mime/send-pipeline.js +1 -1
- package/dist/soap/mime/types.d.ts +1 -3
- package/dist/soap/module.d.ts +2 -0
- package/dist/soap/module.js +13 -0
- package/dist/soap/namespace-scope.js +1 -1
- package/dist/soap/operations.d.ts +12 -0
- package/dist/{operations.js → soap/operations.js} +4 -4
- package/dist/soap/run.d.ts +70 -0
- package/dist/soap/run.js +549 -0
- package/dist/soap/script-types.d.ts +65 -0
- package/dist/soap/script-types.js +379 -0
- package/dist/soap/scripting.d.ts +45 -0
- package/dist/soap/scripting.js +221 -0
- package/dist/soap/send-input.d.ts +61 -0
- package/dist/{send-options.js → soap/send-input.js} +5 -109
- package/dist/{send.d.ts → soap/send.d.ts} +1 -1
- package/dist/{send.js → soap/send.js} +28 -12
- package/dist/soap/storage.d.ts +9 -0
- package/dist/soap/storage.js +237 -0
- package/dist/soap/transforms.d.ts +3 -12
- package/dist/soap/transforms.js +3 -14
- package/dist/{types.d.ts → soap/types.d.ts} +34 -84
- package/dist/sync/git-cli.d.ts +202 -0
- package/dist/sync/git-cli.js +684 -0
- package/dist/sync/three-way-merge.d.ts +41 -0
- package/dist/sync/three-way-merge.js +85 -0
- package/dist/sync/tree-paths.d.ts +34 -0
- package/dist/sync/tree-paths.js +131 -0
- package/dist/team-secrets/envelope.d.ts +10 -0
- package/dist/team-secrets/envelope.js +121 -0
- package/dist/team-secrets/errors.d.ts +26 -0
- package/dist/team-secrets/errors.js +27 -0
- package/dist/team-secrets/index.d.ts +14 -0
- package/dist/team-secrets/index.js +8 -0
- package/dist/team-secrets/keys.d.ts +52 -0
- package/dist/team-secrets/keys.js +158 -0
- package/dist/team-secrets/log.d.ts +48 -0
- package/dist/team-secrets/log.js +175 -0
- package/dist/team-secrets/resolve-conflict.d.ts +7 -0
- package/dist/team-secrets/resolve-conflict.js +14 -0
- package/dist/team-secrets/schema.d.ts +97 -0
- package/dist/team-secrets/schema.js +157 -0
- package/dist/team-secrets/sign.d.ts +24 -0
- package/dist/team-secrets/sign.js +71 -0
- package/dist/team-secrets/vault.d.ts +49 -0
- package/dist/team-secrets/vault.js +148 -0
- package/dist/validate/wsi/run-message.d.ts +2 -2
- package/dist/validate/wsi/run-message.js +1 -1
- package/dist/validate/wsi/run-wsdl.d.ts +2 -2
- package/dist/validate/wsi/run-wsdl.js +1 -1
- package/dist/validate/wsi/types.d.ts +1 -1
- package/dist/webhooks/model.d.ts +138 -0
- package/dist/webhooks/model.js +150 -0
- package/dist/workspace/commit-message.d.ts +1 -1
- package/dist/workspace/commit-message.js +6 -1
- package/dist/workspace/environments.d.ts +11 -4
- package/dist/workspace/environments.js +12 -0
- package/dist/workspace/index.d.ts +3 -3
- package/dist/workspace/index.js +2 -2
- package/dist/workspace/paths.d.ts +5 -2
- package/dist/workspace/paths.js +5 -2
- package/dist/workspace/schema.d.ts +7 -3
- package/dist/workspace/schema.js +24 -3
- package/dist/workspace/share.d.ts +30 -11
- package/dist/workspace/share.js +32 -6
- package/dist/ws/call.d.ts +1 -1
- package/dist/ws/call.js +2 -2
- package/dist/ws/connect.d.ts +60 -0
- package/dist/ws/connect.js +131 -0
- package/dist/ws/events.d.ts +17 -0
- package/dist/ws/events.js +1 -0
- package/dist/ws/expand.d.ts +1 -1
- package/dist/ws/expand.js +1 -1
- package/dist/ws/files.d.ts +284 -0
- package/dist/ws/files.js +71 -0
- package/dist/ws/history-entry.d.ts +44 -0
- package/dist/ws/history-entry.js +93 -0
- package/dist/ws/model.d.ts +44 -3
- package/dist/ws/model.js +3 -0
- package/dist/ws/module.d.ts +2 -0
- package/dist/ws/module.js +11 -0
- package/dist/ws/run.d.ts +63 -0
- package/dist/ws/run.js +470 -0
- package/dist/ws/session.d.ts +18 -0
- package/dist/ws/session.js +52 -25
- package/dist/ws/storage.d.ts +9 -0
- package/dist/ws/storage.js +176 -0
- package/dist/ws/transcript.d.ts +3 -4
- package/dist/ws/transcript.js +26 -17
- package/dist/ws/url.d.ts +1 -1
- package/dist/wsdl/cache.js +1 -1
- package/dist/wsdl/docs-generator.d.ts +3 -3
- package/dist/wsdl/docs-generator.js +1 -1
- package/dist/wsdl/export-definition.js +1 -1
- package/dist/wsdl/qname.d.ts +2 -5
- package/dist/wsdl/resolver.d.ts +4 -11
- package/dist/wsdl/resolver.js +3 -3
- package/dist/wsdl/update-definition.d.ts +5 -5
- package/dist/wsdl/update-definition.js +6 -6
- package/dist/wss/apply.js +1 -1
- package/dist/{project/wss-configs.d.ts → wss/configs.d.ts} +2 -2
- package/dist/{project/wss-configs.js → wss/configs.js} +2 -2
- package/dist/wss/incoming/decrypt.d.ts +1 -1
- package/dist/wss/incoming/index.js +1 -1
- package/dist/wss/incoming/verify.d.ts +1 -1
- package/dist/wss/incoming/verify.js +1 -1
- package/dist/wss/key-identifiers.js +1 -1
- package/dist/wss/model.d.ts +1 -1
- package/dist/wss/outgoing/encryption.d.ts +1 -1
- package/dist/wss/outgoing/signature.d.ts +1 -1
- package/dist/xml/decode.d.ts +15 -0
- package/dist/xml/decode.js +22 -0
- package/dist/xml/entitize.d.ts +17 -0
- package/dist/xml/entitize.js +19 -0
- package/dist/xml/index.d.ts +1 -1
- package/dist/xml/index.js +1 -1
- package/dist/xml/locate.d.ts +39 -0
- package/dist/xml/locate.js +117 -0
- package/dist/xml/qname.d.ts +12 -0
- package/dist/xml/qname.js +8 -0
- package/dist/xpath/evaluate-async.d.ts +19 -0
- package/dist/xpath/evaluate-async.js +31 -11
- package/dist/xpath/evaluate.d.ts +1 -1
- package/dist/xpath/namespaces.js +1 -1
- package/dist/xpath/worker.d.ts +11 -5
- package/dist/xpath/worker.js +24 -7
- package/dist/xsd/form-model.d.ts +19 -2
- package/dist/xsd/form-model.js +14 -2
- package/dist/xsd/json-bridge.d.ts +152 -0
- package/dist/xsd/json-bridge.js +1213 -0
- package/dist/xsd/locate.d.ts +2 -30
- package/dist/xsd/locate.js +1 -109
- package/dist/xsd/xml-scan.d.ts +2 -0
- package/dist/xsd/xml-scan.js +1 -1
- package/dist/xsd/xml-writer.d.ts +2 -0
- package/dist/xsd/xml-writer.js +4 -0
- package/package.json +12 -1
- package/dist/generate.d.ts +0 -12
- package/dist/operations.d.ts +0 -12
- package/dist/run/effective-auth.d.ts +0 -10
- package/dist/run/effective-auth.js +0 -19
- package/dist/run/prepare.d.ts +0 -43
- package/dist/run/prepare.js +0 -280
- package/dist/send-options.d.ts +0 -141
- package/dist/wsdl/fetch.d.ts +0 -13
- /package/dist/{soap → http}/charset.d.ts +0 -0
- /package/dist/{soap → http}/charset.js +0 -0
- /package/dist/{wsdl → http}/ref-policy.d.ts +0 -0
- /package/dist/{wsdl → http}/ref-policy.js +0 -0
- /package/dist/{wss/keystore → keystore}/certificate.d.ts +0 -0
- /package/dist/{wss/keystore → keystore}/certificate.js +0 -0
- /package/dist/{wss/keystore → keystore}/index.d.ts +0 -0
- /package/dist/{wss/keystore → keystore}/model.d.ts +0 -0
- /package/dist/{wss/keystore → keystore}/model.js +0 -0
- /package/dist/{wss/keystore → keystore}/pem.d.ts +0 -0
- /package/dist/{wss/keystore → keystore}/pkcs12.d.ts +0 -0
- /package/dist/{wsdl → project}/cache-naming.d.ts +0 -0
- /package/dist/{types.js → soap/types.js} +0 -0
- /package/dist/{soap → xml}/prefixes.d.ts +0 -0
- /package/dist/{soap → xml}/prefixes.js +0 -0
|
@@ -0,0 +1,1213 @@
|
|
|
1
|
+
import { qnameEquals, qnameToString } from '../wsdl/qname.js';
|
|
2
|
+
import { NS } from '../xml/namespaces.js';
|
|
3
|
+
import { parseXmlDetailed } from '../xml/parse.js';
|
|
4
|
+
import { prefixForNamespace } from '../xml/prefixes.js';
|
|
5
|
+
import { applyForm, buildForm, buildFormForDecl, buildFormForType, Pool } from './form-model.js';
|
|
6
|
+
import { firstConcreteDerived, resolveType } from './sample-types.js';
|
|
7
|
+
import { builtinBaseOf } from './sample-values.js';
|
|
8
|
+
import { decodeEntities, scanXml } from './xml-scan.js';
|
|
9
|
+
import { escapeAttribute, escapeText } from './xml-writer.js';
|
|
10
|
+
/** A property's description is the element's documentation, cut to this many characters. */
|
|
11
|
+
export const MAX_PROPERTY_DESCRIPTION = 200;
|
|
12
|
+
const FRAGMENT_DESCRIPTION = 'An XML fragment, inserted as written';
|
|
13
|
+
export const ONCE = { min: 1, max: 1 };
|
|
14
|
+
/** True when a particle may appear more than once. */
|
|
15
|
+
export function repeats(occurs) {
|
|
16
|
+
return occurs.max === 'unbounded' || occurs.max > 1;
|
|
17
|
+
}
|
|
18
|
+
/** The keys of one JSON object: unique, and stable for one content model. */
|
|
19
|
+
class KeySpace {
|
|
20
|
+
taken = new Set();
|
|
21
|
+
prefixes = new Set();
|
|
22
|
+
elementNamespaces = new Map();
|
|
23
|
+
attributeNamespaces = new Map();
|
|
24
|
+
element(name) {
|
|
25
|
+
return this.unique(this.qualified(name, this.elementNamespaces));
|
|
26
|
+
}
|
|
27
|
+
attribute(name) {
|
|
28
|
+
return this.unique(`@${this.qualified(name, this.attributeNamespaces)}`);
|
|
29
|
+
}
|
|
30
|
+
reserve(key) {
|
|
31
|
+
return this.unique(key);
|
|
32
|
+
}
|
|
33
|
+
/** The local name; a second namespace for it gets a prefix. */
|
|
34
|
+
qualified(name, seen) {
|
|
35
|
+
const first = seen.get(name.localName);
|
|
36
|
+
if (first === undefined) {
|
|
37
|
+
seen.set(name.localName, name.namespaceUri);
|
|
38
|
+
return name.localName;
|
|
39
|
+
}
|
|
40
|
+
if (first === name.namespaceUri) {
|
|
41
|
+
return name.localName;
|
|
42
|
+
}
|
|
43
|
+
const prefix = prefixForNamespace(name.namespaceUri, this.prefixes);
|
|
44
|
+
this.prefixes.add(prefix);
|
|
45
|
+
return `${prefix}:${name.localName}`;
|
|
46
|
+
}
|
|
47
|
+
unique(key) {
|
|
48
|
+
let candidate = key;
|
|
49
|
+
for (let n = 2; this.taken.has(candidate); n += 1) {
|
|
50
|
+
candidate = `${key}_${String(n)}`;
|
|
51
|
+
}
|
|
52
|
+
this.taken.add(candidate);
|
|
53
|
+
return candidate;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** The declaration a particle stands for, an abstract head replaced by its first concrete member. */
|
|
57
|
+
function elementTarget(set, particle) {
|
|
58
|
+
if (particle.kind === 'localElement') {
|
|
59
|
+
return { decl: particle.decl, names: [particle.decl.name] };
|
|
60
|
+
}
|
|
61
|
+
if (particle.kind !== 'elementRef') {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
const head = set.lookupElement(particle.ref);
|
|
65
|
+
if (head === undefined) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const substitutions = set.substitutionsFor(particle.ref);
|
|
69
|
+
const decl = head.abstract ? (substitutions.find((candidate) => !candidate.abstract) ?? head) : head;
|
|
70
|
+
return { decl, names: [head.name, ...substitutions.map((member) => member.name)] };
|
|
71
|
+
}
|
|
72
|
+
/** The members a particle contributes, mirroring `buildParticle` in `form-model.ts` node for node. */
|
|
73
|
+
function membersOf(set, particle, keys, optional, notes) {
|
|
74
|
+
if (particle === undefined) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const target = elementTarget(set, particle);
|
|
78
|
+
if (target !== undefined) {
|
|
79
|
+
return [
|
|
80
|
+
{
|
|
81
|
+
kind: 'element',
|
|
82
|
+
key: keys.element(target.decl.name),
|
|
83
|
+
decl: target.decl,
|
|
84
|
+
names: target.names,
|
|
85
|
+
occurs: particle.occurs,
|
|
86
|
+
optional: optional || particle.occurs.min === 0,
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
switch (particle.kind) {
|
|
91
|
+
case 'localElement':
|
|
92
|
+
return [];
|
|
93
|
+
case 'elementRef':
|
|
94
|
+
notes.push(`no element ${qnameToString(particle.ref)} in the schema`);
|
|
95
|
+
return [];
|
|
96
|
+
case 'groupRef':
|
|
97
|
+
// `resolveContent` expands group references before this runs.
|
|
98
|
+
return [];
|
|
99
|
+
case 'any':
|
|
100
|
+
return [
|
|
101
|
+
{
|
|
102
|
+
kind: 'any',
|
|
103
|
+
key: keys.reserve('#any'),
|
|
104
|
+
occurs: particle.occurs,
|
|
105
|
+
optional: optional || particle.occurs.min === 0,
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
case 'choice': {
|
|
109
|
+
const choiceOptional = optional || particle.occurs.min === 0;
|
|
110
|
+
if (repeats(particle.occurs)) {
|
|
111
|
+
const itemKeys = new KeySpace();
|
|
112
|
+
return [
|
|
113
|
+
{
|
|
114
|
+
kind: 'group',
|
|
115
|
+
key: keys.reserve('#choice'),
|
|
116
|
+
compositor: 'choice',
|
|
117
|
+
occurs: particle.occurs,
|
|
118
|
+
optional: choiceOptional,
|
|
119
|
+
members: [
|
|
120
|
+
{
|
|
121
|
+
kind: 'choice',
|
|
122
|
+
occurs: ONCE,
|
|
123
|
+
optional: false,
|
|
124
|
+
branches: particle.particles.map((branch) => membersOf(set, branch, itemKeys, false, notes)),
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
return [
|
|
131
|
+
{
|
|
132
|
+
kind: 'choice',
|
|
133
|
+
occurs: particle.occurs,
|
|
134
|
+
optional: choiceOptional,
|
|
135
|
+
branches: particle.particles.map((branch) => membersOf(set, branch, keys, false, notes)),
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
}
|
|
139
|
+
case 'sequence':
|
|
140
|
+
case 'all': {
|
|
141
|
+
const groupOptional = optional || particle.occurs.min === 0;
|
|
142
|
+
if (repeats(particle.occurs)) {
|
|
143
|
+
const itemKeys = new KeySpace();
|
|
144
|
+
return [
|
|
145
|
+
{
|
|
146
|
+
kind: 'group',
|
|
147
|
+
key: keys.reserve(`#${particle.kind}`),
|
|
148
|
+
compositor: particle.kind,
|
|
149
|
+
occurs: particle.occurs,
|
|
150
|
+
optional: groupOptional,
|
|
151
|
+
members: particle.particles.flatMap((child) => membersOf(set, child, itemKeys, false, notes)),
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
}
|
|
155
|
+
return particle.particles.flatMap((child) => membersOf(set, child, keys, groupOptional, notes));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** An abstract type stands for its first concrete derivation, as the form model builds it. */
|
|
160
|
+
function concrete(set, type) {
|
|
161
|
+
return type.abstract ? (firstConcreteDerived(set, type) ?? type) : type;
|
|
162
|
+
}
|
|
163
|
+
/** A complex type's attributes (SOAP-encoding bookkeeping left out, as the form does) and members. */
|
|
164
|
+
export function contentShape(set, type, notes) {
|
|
165
|
+
const content = set.resolveContent(concrete(set, type));
|
|
166
|
+
const keys = new KeySpace();
|
|
167
|
+
const attributes = content.attributes
|
|
168
|
+
.filter((attribute) => attribute.name.namespaceUri !== NS.SOAP11_ENC)
|
|
169
|
+
.map((attribute) => ({ key: keys.attribute(attribute.name), attribute }));
|
|
170
|
+
return { content, attributes, members: membersOf(set, content.particle, keys, false, notes) };
|
|
171
|
+
}
|
|
172
|
+
/** Every key a member puts in its object. */
|
|
173
|
+
export function memberKeys(member) {
|
|
174
|
+
return member.kind === 'choice' ? member.branches.flat().flatMap(memberKeys) : [member.key];
|
|
175
|
+
}
|
|
176
|
+
/** Every element name a list of members can claim. */
|
|
177
|
+
export function namesOf(members) {
|
|
178
|
+
return members.flatMap((member) => {
|
|
179
|
+
switch (member.kind) {
|
|
180
|
+
case 'element':
|
|
181
|
+
return [...member.names];
|
|
182
|
+
case 'any':
|
|
183
|
+
return [];
|
|
184
|
+
case 'choice':
|
|
185
|
+
return member.branches.flatMap(namesOf);
|
|
186
|
+
case 'group':
|
|
187
|
+
return namesOf(member.members);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
export function typeOfDecl(set, decl) {
|
|
192
|
+
return resolveType(set, decl.type ?? decl.anonymousType);
|
|
193
|
+
}
|
|
194
|
+
/** The type of a target's content, or undefined when its element is not in the schema. */
|
|
195
|
+
export function targetType(set, target) {
|
|
196
|
+
if ('element' in target) {
|
|
197
|
+
const decl = set.lookupElement(target.element);
|
|
198
|
+
return decl === undefined ? undefined : typeOfDecl(set, decl);
|
|
199
|
+
}
|
|
200
|
+
return resolveType(set, target.type);
|
|
201
|
+
}
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// Simple types
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
/** The value range of each built-in integer type; `undefined` where it is open. */
|
|
206
|
+
const INTEGER_BOUNDS = {
|
|
207
|
+
integer: [undefined, undefined],
|
|
208
|
+
nonPositiveInteger: [undefined, 0],
|
|
209
|
+
negativeInteger: [undefined, -1],
|
|
210
|
+
long: [-(2 ** 63), 2 ** 63 - 1],
|
|
211
|
+
int: [-(2 ** 31), 2 ** 31 - 1],
|
|
212
|
+
short: [-32768, 32767],
|
|
213
|
+
byte: [-128, 127],
|
|
214
|
+
nonNegativeInteger: [0, undefined],
|
|
215
|
+
unsignedLong: [0, 2 ** 64 - 1],
|
|
216
|
+
unsignedInt: [0, 2 ** 32 - 1],
|
|
217
|
+
unsignedShort: [0, 65535],
|
|
218
|
+
unsignedByte: [0, 255],
|
|
219
|
+
positiveInteger: [1, undefined],
|
|
220
|
+
};
|
|
221
|
+
const NUMBER_TYPES = new Set(['decimal', 'float', 'double']);
|
|
222
|
+
const FORMATS = { date: 'date', dateTime: 'date-time', time: 'time' };
|
|
223
|
+
const ENCODINGS = { base64Binary: 'base64', hexBinary: 'base16' };
|
|
224
|
+
/** A simple type and the user-declared types it restricts, nearest first; built-ins end the chain. */
|
|
225
|
+
function simpleChain(set, ref) {
|
|
226
|
+
const chain = [];
|
|
227
|
+
const seen = new Set();
|
|
228
|
+
let current = ref;
|
|
229
|
+
while (current !== undefined) {
|
|
230
|
+
let simple;
|
|
231
|
+
if ('kind' in current) {
|
|
232
|
+
simple = current;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
const key = qnameToString(current);
|
|
236
|
+
if (seen.has(key) || set.builtin(current) !== undefined) {
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
seen.add(key);
|
|
240
|
+
const found = set.lookupType(current);
|
|
241
|
+
simple = found?.kind === 'simpleType' ? found : undefined;
|
|
242
|
+
}
|
|
243
|
+
if (simple === undefined) {
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
chain.push(simple);
|
|
247
|
+
if (simple.variety !== 'atomic') {
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
current = simple.base ?? simple.baseType;
|
|
251
|
+
}
|
|
252
|
+
return chain;
|
|
253
|
+
}
|
|
254
|
+
/** The nearest facet of `kind` in a restriction chain. */
|
|
255
|
+
function facet(chain, kind) {
|
|
256
|
+
for (const simple of chain) {
|
|
257
|
+
const found = simple.facets.find((candidate) => candidate.kind === kind);
|
|
258
|
+
if (found !== undefined) {
|
|
259
|
+
return found;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
/** The XSD built-in a simple type restricts, by local name; undefined when it is no XSD built-in. */
|
|
265
|
+
function builtinLocal(set, ref) {
|
|
266
|
+
const builtin = builtinBaseOf(set, ref);
|
|
267
|
+
return builtin !== undefined && builtin.name.namespaceUri === NS.XSD ? builtin.name.localName : undefined;
|
|
268
|
+
}
|
|
269
|
+
/** The JSON type a simple type's values take. A list or union is a string. */
|
|
270
|
+
export function jsonKind(set, ref) {
|
|
271
|
+
if (simpleChain(set, ref).some((simple) => simple.variety !== 'atomic')) {
|
|
272
|
+
return 'string';
|
|
273
|
+
}
|
|
274
|
+
const local = builtinLocal(set, ref);
|
|
275
|
+
if (local === undefined) {
|
|
276
|
+
return 'string';
|
|
277
|
+
}
|
|
278
|
+
if (Object.hasOwn(INTEGER_BOUNDS, local)) {
|
|
279
|
+
return 'integer';
|
|
280
|
+
}
|
|
281
|
+
if (NUMBER_TYPES.has(local)) {
|
|
282
|
+
return 'number';
|
|
283
|
+
}
|
|
284
|
+
return local === 'boolean' ? 'boolean' : 'string';
|
|
285
|
+
}
|
|
286
|
+
const INTEGER_TEXT = /^[+-]?\d+$/;
|
|
287
|
+
const NUMBER_TEXT = /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;
|
|
288
|
+
/** A lexical value as its JSON type, or undefined when it is not lexically valid for it. */
|
|
289
|
+
export function typedLexical(kind, text) {
|
|
290
|
+
switch (kind) {
|
|
291
|
+
case 'string':
|
|
292
|
+
return text;
|
|
293
|
+
case 'integer': {
|
|
294
|
+
if (!INTEGER_TEXT.test(text))
|
|
295
|
+
return undefined;
|
|
296
|
+
const value = Number(text);
|
|
297
|
+
return Number.isSafeInteger(value) ? value : undefined;
|
|
298
|
+
}
|
|
299
|
+
case 'number':
|
|
300
|
+
return NUMBER_TEXT.test(text) ? Number(text) : undefined;
|
|
301
|
+
case 'boolean':
|
|
302
|
+
return text === 'true' || text === '1' ? true : text === 'false' || text === '0' ? false : undefined;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* XSD-only regex syntax that ECMAScript has no form for, and any Unicode property escape: the engine's
|
|
307
|
+
* JSON Schema validator compiles patterns without the `u` flag, where `\p{…}` means a literal `p`.
|
|
308
|
+
*/
|
|
309
|
+
const XSD_ONLY_PATTERN = /\\[iIcC]|\\[pP]\{|-\[/;
|
|
310
|
+
/** An XSD pattern's body as an ECMAScript one (XSD patterns are anchored by the caller), or undefined. */
|
|
311
|
+
function jsonPatternBody(pattern) {
|
|
312
|
+
if (XSD_ONLY_PATTERN.test(pattern)) {
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
return new RegExp(`^(?:${pattern})$`, 'u').source.length > 0 ? pattern : undefined;
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return undefined;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/** The pattern facets of the nearest restriction step that has any: alternatives of one another. */
|
|
323
|
+
function patternsOf(chain) {
|
|
324
|
+
for (const simple of chain) {
|
|
325
|
+
const found = simple.facets.flatMap((candidate) => (candidate.kind === 'pattern' ? [candidate.value] : []));
|
|
326
|
+
if (found.length > 0) {
|
|
327
|
+
return found;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
function simpleSchema(set, ref, notes, where) {
|
|
333
|
+
const chain = simpleChain(set, ref);
|
|
334
|
+
if (chain.some((simple) => simple.variety !== 'atomic')) {
|
|
335
|
+
return { type: 'string' };
|
|
336
|
+
}
|
|
337
|
+
const local = builtinLocal(set, ref);
|
|
338
|
+
const kind = jsonKind(set, ref);
|
|
339
|
+
const schema = { type: kind };
|
|
340
|
+
const format = local === undefined ? undefined : FORMATS[local];
|
|
341
|
+
if (format !== undefined) {
|
|
342
|
+
schema['format'] = format;
|
|
343
|
+
}
|
|
344
|
+
const enumeration = facet(chain, 'enumeration');
|
|
345
|
+
if (enumeration !== undefined) {
|
|
346
|
+
schema['enum'] = enumeration.values.map((value) => typedLexical(kind, value) ?? value);
|
|
347
|
+
}
|
|
348
|
+
// Several patterns in one restriction step are alternatives; a step further up is a further constraint
|
|
349
|
+
// that JSON Schema's single `pattern` cannot add, so only the nearest step with patterns is used.
|
|
350
|
+
const patterns = patternsOf(chain);
|
|
351
|
+
if (patterns.length > 0) {
|
|
352
|
+
const translated = patterns.map(jsonPatternBody);
|
|
353
|
+
if (translated.some((body) => body === undefined)) {
|
|
354
|
+
notes.push(`${where}: the XSD pattern ${patterns.join(' | ')} has no JSON Schema form; the XSD check still applies it`);
|
|
355
|
+
}
|
|
356
|
+
else if (kind === 'string') {
|
|
357
|
+
schema['pattern'] =
|
|
358
|
+
translated.length === 1
|
|
359
|
+
? `^(?:${translated[0] ?? ''})$`
|
|
360
|
+
: `^(?:${translated.map((body) => `(?:${body ?? ''})`).join('|')})$`;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (kind === 'integer' || kind === 'number') {
|
|
364
|
+
const [low, high] = (local === undefined ? undefined : INTEGER_BOUNDS[local]) ?? [undefined, undefined];
|
|
365
|
+
const bound = (name) => {
|
|
366
|
+
const found = facet(chain, name);
|
|
367
|
+
const value = found === undefined ? undefined : Number(found.value);
|
|
368
|
+
return value !== undefined && Number.isFinite(value) ? value : undefined;
|
|
369
|
+
};
|
|
370
|
+
const minimum = bound('minInclusive') ?? (bound('minExclusive') === undefined ? low : undefined);
|
|
371
|
+
const maximum = bound('maxInclusive') ?? (bound('maxExclusive') === undefined ? high : undefined);
|
|
372
|
+
const exclusiveMinimum = bound('minExclusive');
|
|
373
|
+
const exclusiveMaximum = bound('maxExclusive');
|
|
374
|
+
if (minimum !== undefined)
|
|
375
|
+
schema['minimum'] = minimum;
|
|
376
|
+
if (maximum !== undefined)
|
|
377
|
+
schema['maximum'] = maximum;
|
|
378
|
+
if (exclusiveMinimum !== undefined)
|
|
379
|
+
schema['exclusiveMinimum'] = exclusiveMinimum;
|
|
380
|
+
if (exclusiveMaximum !== undefined)
|
|
381
|
+
schema['exclusiveMaximum'] = exclusiveMaximum;
|
|
382
|
+
}
|
|
383
|
+
// On a binary type the length facets count octets, not characters: the XSD check enforces them.
|
|
384
|
+
if (kind === 'string' && !(local !== undefined && Object.hasOwn(ENCODINGS, local))) {
|
|
385
|
+
const length = facet(chain, 'length');
|
|
386
|
+
const minLength = facet(chain, 'minLength')?.value ?? length?.value;
|
|
387
|
+
const maxLength = facet(chain, 'maxLength')?.value ?? length?.value;
|
|
388
|
+
if (minLength !== undefined)
|
|
389
|
+
schema['minLength'] = minLength;
|
|
390
|
+
if (maxLength !== undefined)
|
|
391
|
+
schema['maxLength'] = maxLength;
|
|
392
|
+
}
|
|
393
|
+
const encoding = local === undefined ? undefined : ENCODINGS[local];
|
|
394
|
+
if (encoding !== undefined) {
|
|
395
|
+
schema['contentEncoding'] = encoding;
|
|
396
|
+
}
|
|
397
|
+
return schema;
|
|
398
|
+
}
|
|
399
|
+
// ---------------------------------------------------------------------------
|
|
400
|
+
// JSON Schema
|
|
401
|
+
// ---------------------------------------------------------------------------
|
|
402
|
+
function arrayOf(items, occurs) {
|
|
403
|
+
return {
|
|
404
|
+
type: 'array',
|
|
405
|
+
items,
|
|
406
|
+
...(occurs.min > 0 ? { minItems: occurs.min } : {}),
|
|
407
|
+
...(occurs.max !== 'unbounded' ? { maxItems: occurs.max } : {}),
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
/** The pieces of one object schema while its members are written. */
|
|
411
|
+
class ObjectParts {
|
|
412
|
+
properties = {};
|
|
413
|
+
required = [];
|
|
414
|
+
allOf = [];
|
|
415
|
+
schema() {
|
|
416
|
+
return {
|
|
417
|
+
type: 'object',
|
|
418
|
+
properties: this.properties,
|
|
419
|
+
...(this.required.length > 0 ? { required: this.required } : {}),
|
|
420
|
+
additionalProperties: false,
|
|
421
|
+
...(this.allOf.length > 0 ? { allOf: this.allOf } : {}),
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
const requiring = (keys) => keys.map((key) => ({ required: [key] }));
|
|
426
|
+
class SchemaWriter {
|
|
427
|
+
set;
|
|
428
|
+
found = [];
|
|
429
|
+
definitions = new Map();
|
|
430
|
+
/** Clark name of a named complex type → its `$defs` key. */
|
|
431
|
+
defKeys = new Map();
|
|
432
|
+
/** Anonymous complex types being written, and the `$defs` key one took when it was reached again. */
|
|
433
|
+
active = new Map();
|
|
434
|
+
constructor(set) {
|
|
435
|
+
this.set = set;
|
|
436
|
+
}
|
|
437
|
+
schemaOf(target) {
|
|
438
|
+
const type = targetType(this.set, target);
|
|
439
|
+
if (type === undefined) {
|
|
440
|
+
this.found.push(`no element ${'element' in target ? qnameToString(target.element) : ''} in the schema`);
|
|
441
|
+
return {};
|
|
442
|
+
}
|
|
443
|
+
return this.content(type, true, '', 'element' in target ? target.element.localName : target.name.localName);
|
|
444
|
+
}
|
|
445
|
+
defs() {
|
|
446
|
+
return this.definitions.size === 0 ? undefined : Object.fromEntries(this.definitions);
|
|
447
|
+
}
|
|
448
|
+
notes() {
|
|
449
|
+
return this.found;
|
|
450
|
+
}
|
|
451
|
+
content(type, inline, where, label = 'Anonymous') {
|
|
452
|
+
switch (type.kind) {
|
|
453
|
+
case 'anyType':
|
|
454
|
+
return { type: 'string', description: FRAGMENT_DESCRIPTION };
|
|
455
|
+
case 'soapencArray':
|
|
456
|
+
this.found.push(`${where}: a SOAP-encoded array is taken as an XML fragment`);
|
|
457
|
+
return { type: 'string', description: FRAGMENT_DESCRIPTION };
|
|
458
|
+
case 'simple':
|
|
459
|
+
return simpleSchema(this.set, type.ref, this.found, where);
|
|
460
|
+
case 'complex':
|
|
461
|
+
if (type.type.name !== undefined) {
|
|
462
|
+
return inline ? this.object(type.type) : this.ref(type.type, type.type.name);
|
|
463
|
+
}
|
|
464
|
+
return this.anonymous(type.type, label);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/** An anonymous complex type; one that reaches itself again (through an element reference) becomes a `$defs` entry. */
|
|
468
|
+
anonymous(type, label) {
|
|
469
|
+
const active = this.active.get(type);
|
|
470
|
+
if (active !== undefined) {
|
|
471
|
+
active.key ??= this.freshKey(label);
|
|
472
|
+
return { $ref: `#/$defs/${active.key}` };
|
|
473
|
+
}
|
|
474
|
+
const state = { key: undefined };
|
|
475
|
+
this.active.set(type, state);
|
|
476
|
+
const schema = this.object(type);
|
|
477
|
+
this.active.delete(type);
|
|
478
|
+
if (state.key === undefined) {
|
|
479
|
+
return schema;
|
|
480
|
+
}
|
|
481
|
+
this.definitions.set(state.key, schema);
|
|
482
|
+
return { $ref: `#/$defs/${state.key}` };
|
|
483
|
+
}
|
|
484
|
+
freshKey(base) {
|
|
485
|
+
let key = base;
|
|
486
|
+
for (let n = 2; this.definitions.has(key); n += 1) {
|
|
487
|
+
key = `${base}_${String(n)}`;
|
|
488
|
+
}
|
|
489
|
+
this.definitions.set(key, {});
|
|
490
|
+
return key;
|
|
491
|
+
}
|
|
492
|
+
ref(type, name) {
|
|
493
|
+
const clark = qnameToString(name);
|
|
494
|
+
let key = this.defKeys.get(clark);
|
|
495
|
+
if (key === undefined) {
|
|
496
|
+
key = name.localName;
|
|
497
|
+
for (let n = 2; this.definitions.has(key); n += 1) {
|
|
498
|
+
key = `${name.localName}_${String(n)}`;
|
|
499
|
+
}
|
|
500
|
+
this.defKeys.set(clark, key);
|
|
501
|
+
// Taken before the type is written, so a recursive reference finds it.
|
|
502
|
+
this.definitions.set(key, {});
|
|
503
|
+
this.definitions.set(key, this.object(type));
|
|
504
|
+
}
|
|
505
|
+
return { $ref: `#/$defs/${key}` };
|
|
506
|
+
}
|
|
507
|
+
object(type) {
|
|
508
|
+
const shape = contentShape(this.set, type, this.found);
|
|
509
|
+
const parts = new ObjectParts();
|
|
510
|
+
for (const { key, attribute } of shape.attributes) {
|
|
511
|
+
parts.properties[key] = simpleSchema(this.set, attribute.type ?? attribute.anonymousType, this.found, key);
|
|
512
|
+
if (attribute.use === 'required' && attribute.fixed === undefined) {
|
|
513
|
+
parts.required.push(key);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
if (shape.content.simpleContentBase !== undefined) {
|
|
517
|
+
parts.properties['#text'] = simpleSchema(this.set, shape.content.simpleContentBase, this.found, '#text');
|
|
518
|
+
}
|
|
519
|
+
else if (shape.content.mixed) {
|
|
520
|
+
parts.properties['#text'] = { type: 'string' };
|
|
521
|
+
}
|
|
522
|
+
for (const member of shape.members) {
|
|
523
|
+
this.member(member, parts);
|
|
524
|
+
}
|
|
525
|
+
return parts.schema();
|
|
526
|
+
}
|
|
527
|
+
member(member, parts) {
|
|
528
|
+
switch (member.kind) {
|
|
529
|
+
case 'element': {
|
|
530
|
+
const { decl } = member;
|
|
531
|
+
let schema = this.content(typeOfDecl(this.set, decl), false, member.key, decl.name.localName);
|
|
532
|
+
if (decl.nillable) {
|
|
533
|
+
schema = { anyOf: [schema, { type: 'null' }] };
|
|
534
|
+
}
|
|
535
|
+
if (repeats(member.occurs)) {
|
|
536
|
+
schema = arrayOf(schema, member.occurs);
|
|
537
|
+
}
|
|
538
|
+
if (decl.documentation !== undefined && decl.documentation.length > 0) {
|
|
539
|
+
schema = { ...schema, description: decl.documentation.slice(0, MAX_PROPERTY_DESCRIPTION) };
|
|
540
|
+
}
|
|
541
|
+
parts.properties[member.key] = schema;
|
|
542
|
+
if (!member.optional)
|
|
543
|
+
parts.required.push(member.key);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
case 'any':
|
|
547
|
+
parts.properties[member.key] = { type: 'string', description: FRAGMENT_DESCRIPTION };
|
|
548
|
+
if (!member.optional)
|
|
549
|
+
parts.required.push(member.key);
|
|
550
|
+
return;
|
|
551
|
+
case 'group': {
|
|
552
|
+
const item = new ObjectParts();
|
|
553
|
+
for (const inner of member.members) {
|
|
554
|
+
this.member(inner, item);
|
|
555
|
+
}
|
|
556
|
+
parts.properties[member.key] = arrayOf(item.schema(), member.occurs);
|
|
557
|
+
if (!member.optional)
|
|
558
|
+
parts.required.push(member.key);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
case 'choice':
|
|
562
|
+
this.choice(member, parts);
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
choice(member, parts) {
|
|
567
|
+
const branches = member.branches.map((branch) => {
|
|
568
|
+
const inner = new ObjectParts();
|
|
569
|
+
for (const child of branch) {
|
|
570
|
+
this.member(child, inner);
|
|
571
|
+
}
|
|
572
|
+
return inner;
|
|
573
|
+
});
|
|
574
|
+
const keysOf = branches.map((inner) => Object.keys(inner.properties));
|
|
575
|
+
const all = keysOf.flat();
|
|
576
|
+
const options = branches.map((inner, index) => {
|
|
577
|
+
const own = keysOf[index] ?? [];
|
|
578
|
+
const others = all.filter((key) => !own.includes(key));
|
|
579
|
+
return {
|
|
580
|
+
...(inner.required.length > 0 ? { required: inner.required } : own.length > 0 ? { anyOf: requiring(own) } : {}),
|
|
581
|
+
...(others.length > 0 ? { not: { anyOf: requiring(others) } } : {}),
|
|
582
|
+
...(inner.allOf.length > 0 ? { allOf: inner.allOf } : {}),
|
|
583
|
+
};
|
|
584
|
+
});
|
|
585
|
+
const emptyLegal = member.optional || branches.some((inner) => inner.required.length === 0);
|
|
586
|
+
if (emptyLegal && all.length > 0 && !keysOf.some((keys) => keys.length === 0)) {
|
|
587
|
+
options.push({ not: { anyOf: requiring(all) } });
|
|
588
|
+
}
|
|
589
|
+
for (const inner of branches) {
|
|
590
|
+
Object.assign(parts.properties, inner.properties);
|
|
591
|
+
}
|
|
592
|
+
if (options.length > 0) {
|
|
593
|
+
parts.allOf.push({ oneOf: options });
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
export function createJsonSchemaWriter(schemaSet) {
|
|
598
|
+
return new SchemaWriter(schemaSet);
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* The JSON Schema of `target`'s content: an object of its attributes (`@a`), its text (`#text`) and its
|
|
602
|
+
* child elements for a complex type, the value's own schema for a simple one. Named complex types the
|
|
603
|
+
* content reaches are `$defs` entries referenced with `$ref`, which also carries recursion.
|
|
604
|
+
*/
|
|
605
|
+
export function jsonSchemaOf(schemaSet, target) {
|
|
606
|
+
const writer = createJsonSchemaWriter(schemaSet);
|
|
607
|
+
const schema = writer.schemaOf(target);
|
|
608
|
+
const defs = writer.defs();
|
|
609
|
+
return { schema: defs === undefined ? schema : { ...schema, $defs: defs }, notes: writer.notes() };
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* How many times one path through a value may be expanded below the form's depth cut (spec revision
|
|
613
|
+
* R10), each expansion five levels deep. A value nested deeper is refused, never cut short.
|
|
614
|
+
*/
|
|
615
|
+
export const MAX_FILL_ROUNDS = 64;
|
|
616
|
+
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
617
|
+
/**
|
|
618
|
+
* A JSON scalar as text: strings as they are, numbers and booleans with `String`, anything else as
|
|
619
|
+
* its JSON. The XML bridge writes element text with it; a REST tool argument's rows use it too.
|
|
620
|
+
*/
|
|
621
|
+
export function lexical(value) {
|
|
622
|
+
if (typeof value === 'string')
|
|
623
|
+
return value;
|
|
624
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
625
|
+
return String(value);
|
|
626
|
+
return JSON.stringify(value) ?? '';
|
|
627
|
+
}
|
|
628
|
+
/** A value's items: an array as it is, one value as a list of one, nothing as none. */
|
|
629
|
+
const itemsOf = (value) => value === undefined ? [] : Array.isArray(value) ? value : [value];
|
|
630
|
+
/** A synthetic node `applyForm` writes verbatim: a mixed text run (escaped), or an XML fragment. */
|
|
631
|
+
function verbatim(id, raw) {
|
|
632
|
+
return {
|
|
633
|
+
id,
|
|
634
|
+
kind: 'any',
|
|
635
|
+
name: { namespaceUri: '', localName: '#text' },
|
|
636
|
+
label: '',
|
|
637
|
+
required: false,
|
|
638
|
+
occurs: ONCE,
|
|
639
|
+
present: true,
|
|
640
|
+
raw,
|
|
641
|
+
children: [],
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
/** A node, and everything under it, left out of the document. */
|
|
645
|
+
function absent(node) {
|
|
646
|
+
return {
|
|
647
|
+
id: node.id,
|
|
648
|
+
kind: node.kind,
|
|
649
|
+
name: node.name,
|
|
650
|
+
label: node.label,
|
|
651
|
+
required: node.required,
|
|
652
|
+
occurs: node.occurs,
|
|
653
|
+
present: false,
|
|
654
|
+
...(node.type !== undefined ? { type: node.type } : {}),
|
|
655
|
+
...(node.kind === 'field' || node.kind === 'attribute' ? { value: node.value ?? '' } : {}),
|
|
656
|
+
children: node.children.map(absent),
|
|
657
|
+
...(node.choice !== undefined ? { choice: {} } : {}),
|
|
658
|
+
...(node.repeat !== undefined ? { repeat: { ...node.repeat, instances: [] } } : {}),
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
/** The element a target's root is: its global declaration (an abstract head replaced), or a synthetic one. */
|
|
662
|
+
function rootDecl(set, target) {
|
|
663
|
+
if (!('element' in target)) {
|
|
664
|
+
return {
|
|
665
|
+
kind: 'element',
|
|
666
|
+
name: target.name,
|
|
667
|
+
type: target.type,
|
|
668
|
+
nillable: false,
|
|
669
|
+
abstract: false,
|
|
670
|
+
source: { location: '<synthetic>' },
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
const decl = set.lookupElement(target.element);
|
|
674
|
+
if (decl === undefined || !decl.abstract) {
|
|
675
|
+
return decl;
|
|
676
|
+
}
|
|
677
|
+
return set.substitutionsFor(decl.name).find((candidate) => !candidate.abstract) ?? decl;
|
|
678
|
+
}
|
|
679
|
+
const ENTITY_OR_REFERENCE = /^&(?:amp|lt|gt|quot|apos|#[0-9]+|#x[0-9a-fA-F]+);/;
|
|
680
|
+
const MARKUP_START = /^<[A-Za-z_:/!?]/;
|
|
681
|
+
const COMMENT = ['<!--', '-->'];
|
|
682
|
+
const LITERAL_SECTIONS = [['<![CDATA[', ']]>'], COMMENT, ['<?', '?>']];
|
|
683
|
+
/**
|
|
684
|
+
* `text` without the given sections, in one linear pass. The text is untrusted (a tool argument or
|
|
685
|
+
* a response), so no backtracking regex walks it. An unterminated section is kept as it is: the
|
|
686
|
+
* XML parser reports it.
|
|
687
|
+
*/
|
|
688
|
+
export function withoutSections(text, sections) {
|
|
689
|
+
let out = '';
|
|
690
|
+
let at = 0;
|
|
691
|
+
for (let lt = text.indexOf('<'); lt !== -1; lt = text.indexOf('<', at)) {
|
|
692
|
+
const section = sections.find(([open]) => text.startsWith(open, lt));
|
|
693
|
+
if (section === undefined) {
|
|
694
|
+
out += text.slice(at, lt + 1);
|
|
695
|
+
at = lt + 1;
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
const end = text.indexOf(section[1], lt + section[0].length);
|
|
699
|
+
if (end === -1) {
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
out += text.slice(at, lt);
|
|
703
|
+
at = end + section[1].length;
|
|
704
|
+
}
|
|
705
|
+
return out + text.slice(at);
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* What the parser lets through that is not well formed: a `&` that starts no predefined entity or
|
|
709
|
+
* character reference, and a `<` that starts no markup. CDATA sections, comments and processing
|
|
710
|
+
* instructions are skipped, since both are literal there.
|
|
711
|
+
*/
|
|
712
|
+
function fragmentLexicalProblem(text) {
|
|
713
|
+
const outside = withoutSections(text, LITERAL_SECTIONS);
|
|
714
|
+
for (let at = outside.indexOf('&'); at !== -1; at = outside.indexOf('&', at + 1)) {
|
|
715
|
+
if (!ENTITY_OR_REFERENCE.test(outside.slice(at))) {
|
|
716
|
+
return `an unescaped "&" (write &) at "${outside.slice(at, at + 12)}"`;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
for (let at = outside.indexOf('<'); at !== -1; at = outside.indexOf('<', at + 1)) {
|
|
720
|
+
if (!MARKUP_START.test(outside.slice(at))) {
|
|
721
|
+
return `an unescaped "<" (write <) at "${outside.slice(at, at + 12)}"`;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return undefined;
|
|
725
|
+
}
|
|
726
|
+
/** Fills one form tree from a JSON value, as the desktop's form edits would. */
|
|
727
|
+
class Filler {
|
|
728
|
+
set;
|
|
729
|
+
inScope;
|
|
730
|
+
notes = [];
|
|
731
|
+
problems = [];
|
|
732
|
+
/** Namespace URI → prefix bound at the root: around the fragment, or declared on the root. */
|
|
733
|
+
known;
|
|
734
|
+
/** Prefix → namespace URI an expansion had to declare; hoisted to the root. */
|
|
735
|
+
declared = {};
|
|
736
|
+
/** Expansions below the depth cut on the path being filled. */
|
|
737
|
+
expansions = 0;
|
|
738
|
+
constructor(set, inScope, prefixes, rootNamespaces) {
|
|
739
|
+
this.set = set;
|
|
740
|
+
this.inScope = inScope;
|
|
741
|
+
this.known = { ...prefixes };
|
|
742
|
+
for (const [prefix, uri] of Object.entries(rootNamespaces)) {
|
|
743
|
+
this.known[uri] ??= prefix;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
element(node, decl, value, where) {
|
|
747
|
+
if (value === undefined) {
|
|
748
|
+
return node.kind === 'field' && node.fixed !== undefined && node.required
|
|
749
|
+
? { ...node, present: true, value: node.fixed }
|
|
750
|
+
: absent(node);
|
|
751
|
+
}
|
|
752
|
+
if (value === null) {
|
|
753
|
+
return this.nil(node, decl, where);
|
|
754
|
+
}
|
|
755
|
+
const resolved = typeOfDecl(this.set, decl);
|
|
756
|
+
if (resolved.kind === 'anyType' || resolved.kind === 'soapencArray') {
|
|
757
|
+
return this.fragment(node, lexical(value), where);
|
|
758
|
+
}
|
|
759
|
+
if (resolved.kind === 'simple') {
|
|
760
|
+
const scalar = isObject(value) && '#text' in value ? value['#text'] : value;
|
|
761
|
+
return { ...node, present: true, value: lexical(scalar) };
|
|
762
|
+
}
|
|
763
|
+
if (!isObject(value)) {
|
|
764
|
+
this.notes.push(`${where}: expected an object; written empty`);
|
|
765
|
+
}
|
|
766
|
+
const fields = isObject(value) ? value : {};
|
|
767
|
+
const shape = contentShape(this.set, resolved.type, []);
|
|
768
|
+
const attributes = node.children
|
|
769
|
+
.filter((child) => child.kind === 'attribute')
|
|
770
|
+
.map((child) => this.attribute(child, shape, fields));
|
|
771
|
+
if (node.kind === 'field') {
|
|
772
|
+
// Simple content: the text beside the attributes.
|
|
773
|
+
const text = fields['#text'];
|
|
774
|
+
return {
|
|
775
|
+
...node,
|
|
776
|
+
present: true,
|
|
777
|
+
value: text === undefined ? (node.fixed ?? '') : lexical(text),
|
|
778
|
+
children: attributes,
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
if (node.truncated === true) {
|
|
782
|
+
return this.expand(node, decl, value, where);
|
|
783
|
+
}
|
|
784
|
+
const particles = node.children.filter((child) => child.kind !== 'attribute');
|
|
785
|
+
const text = shape.content.mixed && fields['#text'] !== undefined
|
|
786
|
+
? [verbatim(`${node.id}/t`, escapeText(lexical(fields['#text'])))]
|
|
787
|
+
: [];
|
|
788
|
+
return {
|
|
789
|
+
...node,
|
|
790
|
+
present: true,
|
|
791
|
+
children: [...attributes, ...text, ...this.members(particles, shape.members, fields, where)],
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* An element the form cut at its depth limit, built again from its declaration with the same
|
|
796
|
+
* prefixes, so the value reaches as deep as it goes. Each item of a repeat, and each branch of a
|
|
797
|
+
* repeating choice, is expanded on its own.
|
|
798
|
+
*/
|
|
799
|
+
expand(node, decl, value, where) {
|
|
800
|
+
if (this.expansions >= MAX_FILL_ROUNDS) {
|
|
801
|
+
this.problems.push(`${where}: nested more than ${String(MAX_FILL_ROUNDS)} times the form's depth limit (5 levels); it cannot be written`);
|
|
802
|
+
return absent(node);
|
|
803
|
+
}
|
|
804
|
+
const fresh = buildFormForDecl(this.set, decl, undefined, { prefixes: this.known });
|
|
805
|
+
const { namespaces, ...rest } = fresh;
|
|
806
|
+
for (const [prefix, uri] of Object.entries(namespaces ?? {})) {
|
|
807
|
+
this.declared[prefix] = uri;
|
|
808
|
+
this.known[uri] = prefix;
|
|
809
|
+
}
|
|
810
|
+
this.expansions += 1;
|
|
811
|
+
try {
|
|
812
|
+
return this.element({ ...rest, id: node.id }, decl, value, where);
|
|
813
|
+
}
|
|
814
|
+
finally {
|
|
815
|
+
this.expansions -= 1;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
/** An `anyType` element's content: text alone as its text, markup inserted as written. */
|
|
819
|
+
fragment(node, text, where) {
|
|
820
|
+
if (!this.wellFormed(text, where)) {
|
|
821
|
+
return absent(node);
|
|
822
|
+
}
|
|
823
|
+
if (!text.includes('<')) {
|
|
824
|
+
// Written as the element's text, so no line breaks are added around it; the writer escapes it again.
|
|
825
|
+
return { ...node, kind: 'field', present: true, value: decodeEntities(text), children: [] };
|
|
826
|
+
}
|
|
827
|
+
return { ...node, kind: 'group', present: true, children: [verbatim(`${node.id}/x`, text)] };
|
|
828
|
+
}
|
|
829
|
+
nil(node, decl, where) {
|
|
830
|
+
if (!decl.nillable) {
|
|
831
|
+
this.notes.push(`${where}: null for an element that is not nillable; left out`);
|
|
832
|
+
return absent(node);
|
|
833
|
+
}
|
|
834
|
+
const kept = (node.extraAttributes ?? []).filter((extra) => extra.name !== 'xsi:nil' && extra.name !== 'xmlns:xsi');
|
|
835
|
+
return {
|
|
836
|
+
...node,
|
|
837
|
+
kind: 'field',
|
|
838
|
+
present: true,
|
|
839
|
+
value: '',
|
|
840
|
+
children: [],
|
|
841
|
+
extraAttributes: [...kept, { name: 'xmlns:xsi', value: NS.XSI }, { name: 'xsi:nil', value: 'true' }],
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
attribute(node, shape, fields) {
|
|
845
|
+
const match = shape.attributes.find((candidate) => qnameEquals(candidate.attribute.name, node.name));
|
|
846
|
+
const value = match === undefined ? undefined : fields[match.key];
|
|
847
|
+
if (value === undefined) {
|
|
848
|
+
return node.fixed !== undefined && node.required ? { ...node, present: true, value: node.fixed } : absent(node);
|
|
849
|
+
}
|
|
850
|
+
return { ...node, present: true, value: lexical(value) };
|
|
851
|
+
}
|
|
852
|
+
/** The particle nodes of one element (or one repeat occurrence), filled member by member. */
|
|
853
|
+
members(nodes, members, fields, where) {
|
|
854
|
+
if (nodes.length !== members.length) {
|
|
855
|
+
this.notes.push(`${where}: the form and the schema disagree on this content; it is left out`);
|
|
856
|
+
return nodes.map(absent);
|
|
857
|
+
}
|
|
858
|
+
return members.map((member, index) => this.member(nodes[index], member, fields, where));
|
|
859
|
+
}
|
|
860
|
+
member(node, member, fields, where) {
|
|
861
|
+
switch (member.kind) {
|
|
862
|
+
case 'element': {
|
|
863
|
+
const value = fields[member.key];
|
|
864
|
+
const at = `${where}/${member.key}`;
|
|
865
|
+
if (!repeats(member.occurs)) {
|
|
866
|
+
return this.element(node, member.decl, value, at);
|
|
867
|
+
}
|
|
868
|
+
return this.repeat(node, itemsOf(value), (slot, item, index) => this.element(slot, member.decl, item, `${at}/${String(index)}`));
|
|
869
|
+
}
|
|
870
|
+
case 'any': {
|
|
871
|
+
const value = fields[member.key];
|
|
872
|
+
if (value === undefined) {
|
|
873
|
+
return absent(node);
|
|
874
|
+
}
|
|
875
|
+
const text = lexical(value);
|
|
876
|
+
return this.wellFormed(text, `${where}/${member.key}`) ? { ...node, present: true, raw: text } : absent(node);
|
|
877
|
+
}
|
|
878
|
+
case 'choice':
|
|
879
|
+
return this.choice(node, member, fields, where);
|
|
880
|
+
case 'group': {
|
|
881
|
+
const items = itemsOf(fields[member.key]);
|
|
882
|
+
const at = `${where}/${member.key}`;
|
|
883
|
+
if (member.compositor === 'choice') {
|
|
884
|
+
// The form models a repeating choice as one choice node: one copy of it per item.
|
|
885
|
+
const inner = member.members[0];
|
|
886
|
+
if (inner?.kind !== 'choice') {
|
|
887
|
+
return absent(node);
|
|
888
|
+
}
|
|
889
|
+
const instances = items.map((item, index) => this.choice(node, inner, isObject(item) ? item : {}, `${at}/${String(index)}`));
|
|
890
|
+
return {
|
|
891
|
+
id: node.id,
|
|
892
|
+
kind: 'repeat',
|
|
893
|
+
name: node.name,
|
|
894
|
+
label: node.label,
|
|
895
|
+
required: node.required,
|
|
896
|
+
occurs: member.occurs,
|
|
897
|
+
present: instances.length > 0,
|
|
898
|
+
children: [],
|
|
899
|
+
repeat: { instances, template: node, canAdd: true, canRemove: true },
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
return this.repeat(node, items, (slot, item, index) => ({
|
|
903
|
+
...slot,
|
|
904
|
+
present: true,
|
|
905
|
+
children: this.members(slot.children, member.members, isObject(item) ? item : {}, `${at}/${String(index)}`),
|
|
906
|
+
}));
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
repeat(node, items, fill) {
|
|
911
|
+
const slots = node.repeat;
|
|
912
|
+
if (node.kind !== 'repeat' || slots === undefined) {
|
|
913
|
+
this.notes.push(`${node.label}: the form has no repeat here; left out`);
|
|
914
|
+
return absent(node);
|
|
915
|
+
}
|
|
916
|
+
// An occurrence an earlier round wrote is built from the XML, below the cut; a new one from the template.
|
|
917
|
+
const instances = items.map((item, index) => fill(slots.instances[index] ?? slots.template, item, index));
|
|
918
|
+
return { ...node, present: instances.length > 0, repeat: { ...slots, instances } };
|
|
919
|
+
}
|
|
920
|
+
choice(node, member, fields, where) {
|
|
921
|
+
if (node.kind !== 'choice') {
|
|
922
|
+
this.notes.push(`${where}: the form has no choice here; left out`);
|
|
923
|
+
return absent(node);
|
|
924
|
+
}
|
|
925
|
+
const chosen = member.branches.findIndex((branch) => branch.flatMap(memberKeys).some((key) => fields[key] !== undefined));
|
|
926
|
+
const children = node.children.map((branchNode, index) => {
|
|
927
|
+
const branch = member.branches[index] ?? [];
|
|
928
|
+
if (index !== chosen) {
|
|
929
|
+
return absent(branchNode);
|
|
930
|
+
}
|
|
931
|
+
const [only] = branch;
|
|
932
|
+
if (branch.length === 1 && only !== undefined) {
|
|
933
|
+
return this.member(branchNode, only, fields, where);
|
|
934
|
+
}
|
|
935
|
+
return { ...branchNode, present: true, children: this.members(branchNode.children, branch, fields, where) };
|
|
936
|
+
});
|
|
937
|
+
return { ...node, present: chosen !== -1, children, choice: chosen === -1 ? {} : { selected: chosen } };
|
|
938
|
+
}
|
|
939
|
+
/** An XML fragment is inserted as written, once it passes the lexical check and a parser reads it whole. */
|
|
940
|
+
wellFormed(text, where) {
|
|
941
|
+
const lexicalProblem = fragmentLexicalProblem(text);
|
|
942
|
+
if (lexicalProblem !== undefined) {
|
|
943
|
+
this.problems.push(`${where}: not well-formed XML: ${lexicalProblem}`);
|
|
944
|
+
return false;
|
|
945
|
+
}
|
|
946
|
+
const declarations = Object.entries(this.inScope)
|
|
947
|
+
.map(([prefix, uri]) => ` xmlns:${prefix}="${escapeAttribute(uri)}"`)
|
|
948
|
+
.join('');
|
|
949
|
+
try {
|
|
950
|
+
const { problems } = parseXmlDetailed(`<wirebench-fragment${declarations}>${text}</wirebench-fragment>`);
|
|
951
|
+
const errors = problems.filter((problem) => problem.level === 'error');
|
|
952
|
+
if (errors.length === 0) {
|
|
953
|
+
return true;
|
|
954
|
+
}
|
|
955
|
+
this.problems.push(`${where}: not well-formed XML: ${errors.map((problem) => problem.message).join('; ')}`);
|
|
956
|
+
}
|
|
957
|
+
catch (error) {
|
|
958
|
+
this.problems.push(`${where}: not well-formed XML: ${error instanceof Error ? error.message : String(error)}`);
|
|
959
|
+
}
|
|
960
|
+
return false;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* XML for `target` from a JSON value shaped by {@link jsonSchemaOf}: the form model is built, filled,
|
|
965
|
+
* and serialised, so names, prefixes and element order are the desktop form's. Where the value reaches
|
|
966
|
+
* below the form's depth cut, that element is built again from its declaration and filled in turn
|
|
967
|
+
* (spec revision R10), up to {@link MAX_FILL_ROUNDS} times along one path; deeper is a problem.
|
|
968
|
+
* Written with no indentation, so text values stay exact.
|
|
969
|
+
*/
|
|
970
|
+
export function xmlFromJson(schemaSet, target, value, options = {}) {
|
|
971
|
+
const decl = rootDecl(schemaSet, target);
|
|
972
|
+
if (decl === undefined) {
|
|
973
|
+
const name = 'element' in target ? qnameToString(target.element) : '';
|
|
974
|
+
return { xml: '', notes: [], problems: [`no element ${name} in the schema`] };
|
|
975
|
+
}
|
|
976
|
+
const formOptions = options.prefixes !== undefined ? { prefixes: options.prefixes } : {};
|
|
977
|
+
const form = 'element' in target
|
|
978
|
+
? buildForm(schemaSet, target.element, undefined, formOptions)
|
|
979
|
+
: buildFormForType(schemaSet, target.name, target.type, undefined, formOptions);
|
|
980
|
+
const filler = new Filler(schemaSet, options.inScope ?? {}, options.prefixes ?? {}, form.namespaces ?? {});
|
|
981
|
+
const filled = filler.element(form, decl, value, form.label);
|
|
982
|
+
if (filler.problems.length > 0) {
|
|
983
|
+
return { xml: '', notes: filler.notes, problems: filler.problems };
|
|
984
|
+
}
|
|
985
|
+
const namespaces = { ...filled.namespaces, ...filler.declared };
|
|
986
|
+
const root = Object.keys(namespaces).length > 0 ? { ...filled, namespaces } : filled;
|
|
987
|
+
return { xml: applyForm(root, { indent: '' }), notes: filler.notes, problems: [] };
|
|
988
|
+
}
|
|
989
|
+
/** `name`, or `name_2`, `name_3`… when `out` already has that key: where an unread child is kept. */
|
|
990
|
+
export function freeKey(out, name) {
|
|
991
|
+
let key = name;
|
|
992
|
+
for (let n = 2; key in out; n += 1) {
|
|
993
|
+
key = `${name}_${String(n)}`;
|
|
994
|
+
}
|
|
995
|
+
return key;
|
|
996
|
+
}
|
|
997
|
+
const localOf = (name) => (name.includes(':') ? name.slice(name.indexOf(':') + 1) : name);
|
|
998
|
+
const sameName = (name, el) => name.localName === el.localName && name.namespaceUri === el.namespaceUri;
|
|
999
|
+
class Reader {
|
|
1000
|
+
set;
|
|
1001
|
+
source;
|
|
1002
|
+
notes = [];
|
|
1003
|
+
constructor(set, source) {
|
|
1004
|
+
this.set = set;
|
|
1005
|
+
this.source = source;
|
|
1006
|
+
}
|
|
1007
|
+
element(el, decl, where) {
|
|
1008
|
+
if (el.attributes.some((attribute) => attribute.name.endsWith(':nil') && (attribute.value === 'true' || attribute.value === '1'))) {
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
1011
|
+
const type = typeOfDecl(this.set, decl);
|
|
1012
|
+
switch (type.kind) {
|
|
1013
|
+
case 'anyType':
|
|
1014
|
+
case 'soapencArray':
|
|
1015
|
+
return this.inner(el);
|
|
1016
|
+
case 'simple':
|
|
1017
|
+
return this.typed(jsonKind(this.set, type.ref), el.text?.value ?? '', where);
|
|
1018
|
+
case 'complex':
|
|
1019
|
+
return this.object(el, type.type, where);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
object(el, type, where) {
|
|
1023
|
+
const shape = contentShape(this.set, type, []);
|
|
1024
|
+
const out = {};
|
|
1025
|
+
for (const attribute of el.attributes) {
|
|
1026
|
+
if (attribute.name === 'xmlns' || attribute.name.startsWith('xmlns:')) {
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
const local = localOf(attribute.name);
|
|
1030
|
+
const match = shape.attributes.find((candidate) => candidate.attribute.name.localName === local);
|
|
1031
|
+
if (match === undefined) {
|
|
1032
|
+
// A prefixed one (`xsi:type`, a wildcard's) is bookkeeping, not data.
|
|
1033
|
+
if (!attribute.name.includes(':')) {
|
|
1034
|
+
this.notes.push(`${where}/@${local}: not in the schema; left out`);
|
|
1035
|
+
}
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
const ref = match.attribute.type ?? match.attribute.anonymousType;
|
|
1039
|
+
out[match.key] = this.typed(jsonKind(this.set, ref), attribute.value, `${where}/@${local}`);
|
|
1040
|
+
}
|
|
1041
|
+
if (shape.content.simpleContentBase !== undefined) {
|
|
1042
|
+
out['#text'] = this.typed(jsonKind(this.set, shape.content.simpleContentBase), el.text?.value ?? '', `${where}/#text`);
|
|
1043
|
+
return out;
|
|
1044
|
+
}
|
|
1045
|
+
if (shape.content.mixed) {
|
|
1046
|
+
const text = this.mixedText(el);
|
|
1047
|
+
if (text !== '') {
|
|
1048
|
+
out['#text'] = text;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
const pool = new Pool(el.children);
|
|
1052
|
+
this.members(shape.members, pool, out, where);
|
|
1053
|
+
const leftovers = pool.leftovers();
|
|
1054
|
+
const wildcard = shape.members.find((member) => member.kind === 'any');
|
|
1055
|
+
if (leftovers.length > 0 && wildcard !== undefined) {
|
|
1056
|
+
out[wildcard.key] = leftovers.map((child) => this.slice(child)).join('\n');
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
for (const child of leftovers) {
|
|
1060
|
+
out[freeKey(out, child.localName)] = this.slice(child);
|
|
1061
|
+
this.notes.push(`${where}/${child.localName}: not in the schema; kept as its XML`);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return out;
|
|
1065
|
+
}
|
|
1066
|
+
members(members, pool, out, where) {
|
|
1067
|
+
for (const member of members) {
|
|
1068
|
+
switch (member.kind) {
|
|
1069
|
+
case 'element': {
|
|
1070
|
+
const at = `${where}/${member.key}`;
|
|
1071
|
+
if (!repeats(member.occurs)) {
|
|
1072
|
+
const child = pool.take(member.names);
|
|
1073
|
+
if (child !== undefined) {
|
|
1074
|
+
out[member.key] = this.element(child, this.declFor(member, child), at);
|
|
1075
|
+
}
|
|
1076
|
+
break;
|
|
1077
|
+
}
|
|
1078
|
+
const max = member.occurs.max === 'unbounded' ? Number.POSITIVE_INFINITY : member.occurs.max;
|
|
1079
|
+
const items = [];
|
|
1080
|
+
let child = pool.take(member.names);
|
|
1081
|
+
while (child !== undefined) {
|
|
1082
|
+
items.push(this.element(child, this.declFor(member, child), `${at}/${String(items.length)}`));
|
|
1083
|
+
child = items.length < max ? pool.take(member.names) : undefined;
|
|
1084
|
+
}
|
|
1085
|
+
if (items.length > 0) {
|
|
1086
|
+
out[member.key] = items;
|
|
1087
|
+
}
|
|
1088
|
+
break;
|
|
1089
|
+
}
|
|
1090
|
+
case 'any':
|
|
1091
|
+
break;
|
|
1092
|
+
case 'choice': {
|
|
1093
|
+
// The branch of the next unclaimed child, in document order, that any branch can claim.
|
|
1094
|
+
const next = pool
|
|
1095
|
+
.leftovers()
|
|
1096
|
+
.find((child) => namesOf(member.branches.flat()).some((name) => sameName(name, child)));
|
|
1097
|
+
const branch = next === undefined
|
|
1098
|
+
? undefined
|
|
1099
|
+
: member.branches.find((candidate) => namesOf(candidate).some((name) => sameName(name, next)));
|
|
1100
|
+
if (branch !== undefined) {
|
|
1101
|
+
this.members(branch, pool, out, where);
|
|
1102
|
+
}
|
|
1103
|
+
break;
|
|
1104
|
+
}
|
|
1105
|
+
case 'group': {
|
|
1106
|
+
const names = namesOf(member.members);
|
|
1107
|
+
const max = member.occurs.max === 'unbounded' ? Number.POSITIVE_INFINITY : member.occurs.max;
|
|
1108
|
+
const items = [];
|
|
1109
|
+
while (items.length < max && pool.has(names)) {
|
|
1110
|
+
const before = pool.leftovers().length;
|
|
1111
|
+
const item = {};
|
|
1112
|
+
this.members(member.members, pool, item, `${where}/${member.key}/${String(items.length)}`);
|
|
1113
|
+
if (pool.leftovers().length === before) {
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1116
|
+
items.push(item);
|
|
1117
|
+
}
|
|
1118
|
+
if (items.length > 0) {
|
|
1119
|
+
out[member.key] = items;
|
|
1120
|
+
}
|
|
1121
|
+
break;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
/** The declaration for the name actually written: a substitution group member when it is one. */
|
|
1127
|
+
declFor(member, child) {
|
|
1128
|
+
const name = member.names.find((candidate) => sameName(candidate, child));
|
|
1129
|
+
if (name === undefined || qnameEquals(name, member.decl.name)) {
|
|
1130
|
+
return member.decl;
|
|
1131
|
+
}
|
|
1132
|
+
return this.set.lookupElement(name) ?? member.decl;
|
|
1133
|
+
}
|
|
1134
|
+
typed(kind, text, where) {
|
|
1135
|
+
if (kind === 'string') {
|
|
1136
|
+
return text;
|
|
1137
|
+
}
|
|
1138
|
+
const value = typedLexical(kind, text.trim());
|
|
1139
|
+
if (value === undefined) {
|
|
1140
|
+
this.notes.push(`${where}: "${text}" is not a valid ${kind}; kept as a string`);
|
|
1141
|
+
return text;
|
|
1142
|
+
}
|
|
1143
|
+
return value;
|
|
1144
|
+
}
|
|
1145
|
+
slice(el) {
|
|
1146
|
+
return this.source.slice(el.range.start, el.range.end);
|
|
1147
|
+
}
|
|
1148
|
+
/** Where an element's content starts: after its start tag's `>`. */
|
|
1149
|
+
contentStart(el) {
|
|
1150
|
+
const last = el.attributes.at(-1);
|
|
1151
|
+
return this.source.indexOf('>', last === undefined ? el.range.start : last.valueRange.end) + 1;
|
|
1152
|
+
}
|
|
1153
|
+
closeStart(el) {
|
|
1154
|
+
return this.source.lastIndexOf('</', el.range.end);
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* An `anyType` element's content as the fragment it is: its markup as written (trimmed), or its text
|
|
1158
|
+
* as written, entities and all, so that writing it back gives the same element.
|
|
1159
|
+
*/
|
|
1160
|
+
inner(el) {
|
|
1161
|
+
if (el.selfClosing) {
|
|
1162
|
+
return '';
|
|
1163
|
+
}
|
|
1164
|
+
const raw = this.source.slice(this.contentStart(el), this.closeStart(el));
|
|
1165
|
+
return el.children.length === 0 ? raw : raw.trim();
|
|
1166
|
+
}
|
|
1167
|
+
/** Mixed content's text: the runs outside the child elements, each trimmed, joined by one space. */
|
|
1168
|
+
mixedText(el) {
|
|
1169
|
+
if (el.selfClosing) {
|
|
1170
|
+
return '';
|
|
1171
|
+
}
|
|
1172
|
+
if (el.children.length === 0) {
|
|
1173
|
+
return (el.text?.value ?? '').trim();
|
|
1174
|
+
}
|
|
1175
|
+
const bounds = [
|
|
1176
|
+
this.contentStart(el),
|
|
1177
|
+
...el.children.flatMap((child) => [child.range.start, child.range.end]),
|
|
1178
|
+
this.closeStart(el),
|
|
1179
|
+
];
|
|
1180
|
+
const runs = [];
|
|
1181
|
+
for (let at = 0; at + 1 < bounds.length; at += 2) {
|
|
1182
|
+
const raw = withoutSections(this.source.slice(bounds[at], bounds[at + 1]), [COMMENT]);
|
|
1183
|
+
const run = decodeEntities(raw).trim();
|
|
1184
|
+
if (run !== '') {
|
|
1185
|
+
runs.push(run);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
return runs.join(' ');
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* JSON for `xml` (one element, `target`'s) by the mapping of {@link jsonSchemaOf}, in reverse: arrays
|
|
1193
|
+
* from `maxOccurs` (one occurrence is still an array), numbers and booleans typed when lexically
|
|
1194
|
+
* valid, an element the schema does not declare kept as its XML text with a note.
|
|
1195
|
+
*/
|
|
1196
|
+
export function jsonFromXml(schemaSet, target, xml, options = {}) {
|
|
1197
|
+
const scanned = scanXml(xml, options.inScope ?? {});
|
|
1198
|
+
const root = scanned.elements[0];
|
|
1199
|
+
if (root === undefined) {
|
|
1200
|
+
return { value: undefined, notes: ['no element to read', ...scanned.problems] };
|
|
1201
|
+
}
|
|
1202
|
+
const reader = new Reader(schemaSet, xml);
|
|
1203
|
+
const decl = rootDecl(schemaSet, target);
|
|
1204
|
+
if (decl === undefined) {
|
|
1205
|
+
return {
|
|
1206
|
+
value: xml.slice(root.range.start, root.range.end),
|
|
1207
|
+
notes: [`${root.localName}: not in the schema; kept as its XML`],
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
const named = schemaSet.substitutionsFor(decl.name).find((candidate) => sameName(candidate.name, root));
|
|
1211
|
+
const value = reader.element(root, named ?? decl, root.localName);
|
|
1212
|
+
return { value, notes: reader.notes };
|
|
1213
|
+
}
|