@scalar/workspace-store 0.40.2 → 0.40.3
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/CHANGELOG.md +7 -0
- package/dist/client.d.ts +7 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +820 -681
- package/dist/entities/auth/index.js +96 -101
- package/dist/entities/auth/schema.js +42 -116
- package/dist/entities/history/index.js +80 -60
- package/dist/entities/history/schema.js +94 -88
- package/dist/events/bus.js +146 -93
- package/dist/events/definitions/analytics.js +1 -1
- package/dist/events/definitions/auth.js +1 -1
- package/dist/events/definitions/common.js +1 -1
- package/dist/events/definitions/cookie.js +1 -1
- package/dist/events/definitions/document.js +1 -1
- package/dist/events/definitions/environment.js +1 -1
- package/dist/events/definitions/hooks.js +1 -1
- package/dist/events/definitions/index.js +1 -1
- package/dist/events/definitions/meta.js +1 -1
- package/dist/events/definitions/operation.js +1 -1
- package/dist/events/definitions/server.js +1 -1
- package/dist/events/definitions/tabs.js +1 -1
- package/dist/events/definitions/tag.js +1 -1
- package/dist/events/definitions/ui.js +1 -1
- package/dist/events/definitions/workspace.js +1 -1
- package/dist/events/index.js +3 -9
- package/dist/events/listeners.js +20 -22
- package/dist/events/old-definitions.js +15 -12
- package/dist/helpers/deep-clone.js +33 -17
- package/dist/helpers/detect-changes-proxy.js +95 -57
- package/dist/helpers/general.js +78 -23
- package/dist/helpers/generate-unique-value.js +77 -45
- package/dist/helpers/get-fetch.js +12 -10
- package/dist/helpers/get-resolved-ref.js +11 -10
- package/dist/helpers/is-non-optional-security-requirement.js +3 -6
- package/dist/helpers/merge-object.js +71 -30
- package/dist/helpers/overrides-proxy.js +98 -58
- package/dist/helpers/unpack-proxy.js +60 -58
- package/dist/mutators/auth.js +358 -230
- package/dist/mutators/cookie.js +59 -42
- package/dist/mutators/document.js +104 -66
- package/dist/mutators/environment.js +97 -72
- package/dist/mutators/helpers.js +9 -13
- package/dist/mutators/index.js +62 -49
- package/dist/mutators/operation/body.js +88 -57
- package/dist/mutators/operation/extensions.js +20 -12
- package/dist/mutators/operation/helpers/fetch-request-to-har.js +144 -107
- package/dist/mutators/operation/helpers/fetch-response-to-har.js +143 -95
- package/dist/mutators/operation/helpers/get-parameter-position.js +12 -12
- package/dist/mutators/operation/helpers/har-to-operation.js +169 -132
- package/dist/mutators/operation/helpers/sync-path-parameters.js +109 -60
- package/dist/mutators/operation/history.js +60 -64
- package/dist/mutators/operation/index.js +25 -49
- package/dist/mutators/operation/operation.js +349 -240
- package/dist/mutators/operation/parameters.js +157 -93
- package/dist/mutators/server.js +213 -152
- package/dist/mutators/tabs.js +173 -130
- package/dist/mutators/tag.js +131 -97
- package/dist/mutators/workspace.js +72 -42
- package/dist/navigation/get-navigation-options.js +97 -84
- package/dist/navigation/helpers/get-openapi-object.js +46 -29
- package/dist/navigation/helpers/get-operation-entries.js +72 -32
- package/dist/navigation/helpers/get-parent-entry.js +16 -12
- package/dist/navigation/helpers/get-tag-entries.js +56 -29
- package/dist/navigation/helpers/get-tag.js +22 -23
- package/dist/navigation/helpers/get-x-keys.js +13 -9
- package/dist/navigation/helpers/traverse-description.js +90 -72
- package/dist/navigation/helpers/traverse-document.js +111 -98
- package/dist/navigation/helpers/traverse-examples.js +35 -31
- package/dist/navigation/helpers/traverse-paths.js +118 -106
- package/dist/navigation/helpers/traverse-schemas.js +65 -64
- package/dist/navigation/helpers/traverse-tags.js +158 -129
- package/dist/navigation/helpers/traverse-webhooks.js +96 -90
- package/dist/navigation/helpers/update-order-ids.js +59 -51
- package/dist/navigation/helpers/utils.js +71 -21
- package/dist/navigation/index.js +5 -13
- package/dist/navigation/types.js +1 -1
- package/dist/persistence/index.js +283 -285
- package/dist/persistence/indexdb.js +263 -126
- package/dist/plugins/bundler/helpers.js +21 -12
- package/dist/plugins/bundler/index.d.ts +7 -0
- package/dist/plugins/bundler/index.d.ts.map +1 -1
- package/dist/plugins/bundler/index.js +305 -171
- package/dist/plugins/client/index.js +1 -5
- package/dist/plugins/client/persistence.js +95 -90
- package/dist/resolve.js +18 -25
- package/dist/schemas/compose.js +6 -7
- package/dist/schemas/extensions/document/x-internal.js +3 -7
- package/dist/schemas/extensions/document/x-scalar-environments.js +16 -22
- package/dist/schemas/extensions/document/x-scalar-icon.js +3 -7
- package/dist/schemas/extensions/document/x-scalar-ignore.js +3 -7
- package/dist/schemas/extensions/document/x-scalar-is-dirty.js +21 -8
- package/dist/schemas/extensions/document/x-scalar-original-document-hash.js +17 -8
- package/dist/schemas/extensions/document/x-scalar-registry-meta.js +15 -21
- package/dist/schemas/extensions/document/x-scalar-sdk-installation.js +5 -13
- package/dist/schemas/extensions/document/x-scalar-watch-mode.js +4 -8
- package/dist/schemas/extensions/document/x-tags.js +3 -7
- package/dist/schemas/extensions/example/x-disabled.js +17 -7
- package/dist/schemas/extensions/general/x-scalar-active-environment.js +4 -7
- package/dist/schemas/extensions/general/x-scalar-cookies.js +9 -14
- package/dist/schemas/extensions/general/x-scalar-order.js +8 -7
- package/dist/schemas/extensions/operation/index.js +4 -23
- package/dist/schemas/extensions/operation/x-badge.js +42 -50
- package/dist/schemas/extensions/operation/x-code-samples.js +8 -12
- package/dist/schemas/extensions/operation/x-draft-examples.js +3 -7
- package/dist/schemas/extensions/operation/x-post-response.js +18 -7
- package/dist/schemas/extensions/operation/x-scalar-disable-parameters.js +57 -13
- package/dist/schemas/extensions/operation/x-scalar-selected-content-type.js +9 -7
- package/dist/schemas/extensions/operation/x-scalar-stability.js +15 -14
- package/dist/schemas/extensions/parameter/x-global.js +12 -7
- package/dist/schemas/extensions/schema/x-additional-properties-name.js +10 -7
- package/dist/schemas/extensions/schema/x-enum-descriptions.js +17 -12
- package/dist/schemas/extensions/schema/x-enum-varnames.js +21 -8
- package/dist/schemas/extensions/schema/x-examples.js +3 -7
- package/dist/schemas/extensions/schema/x-variable.js +3 -7
- package/dist/schemas/extensions/security/index.js +1 -1
- package/dist/schemas/extensions/security/x-default-scopes.js +16 -7
- package/dist/schemas/extensions/security/x-scalar-credentials-location.js +16 -7
- package/dist/schemas/extensions/security/x-scalar-security-body.js +14 -7
- package/dist/schemas/extensions/security/x-scalar-security-query.js +14 -7
- package/dist/schemas/extensions/security/x-scalar-security-secrets.js +66 -37
- package/dist/schemas/extensions/security/x-tokenName.js +11 -7
- package/dist/schemas/extensions/security/x-use-pkce.js +6 -10
- package/dist/schemas/extensions/server/x-scalar-selected-server.js +3 -7
- package/dist/schemas/extensions/tag/index.js +1 -1
- package/dist/schemas/extensions/tag/x-display-name.js +11 -7
- package/dist/schemas/extensions/tag/x-tag-groups.js +13 -15
- package/dist/schemas/extensions/workspace/index.js +2 -11
- package/dist/schemas/extensions/workspace/x-scalar-active-proxy.js +13 -7
- package/dist/schemas/extensions/workspace/x-scalar-tabs.js +15 -13
- package/dist/schemas/extensions.js +11 -15
- package/dist/schemas/inmemory-workspace.js +13 -21
- package/dist/schemas/navigation.js +48 -85
- package/dist/schemas/reference-config/appearance.js +15 -22
- package/dist/schemas/reference-config/features.js +14 -21
- package/dist/schemas/reference-config/index.js +42 -44
- package/dist/schemas/reference-config/meta.js +11 -18
- package/dist/schemas/reference-config/routing.js +7 -14
- package/dist/schemas/reference-config/settings.js +10 -17
- package/dist/schemas/typebox-coerce.js +23 -6
- package/dist/schemas/v3.1/strict/callback.js +6 -12
- package/dist/schemas/v3.1/strict/components.js +26 -48
- package/dist/schemas/v3.1/strict/contact.js +9 -12
- package/dist/schemas/v3.1/strict/discriminator.js +11 -10
- package/dist/schemas/v3.1/strict/encoding.js +15 -12
- package/dist/schemas/v3.1/strict/example.js +11 -13
- package/dist/schemas/v3.1/strict/external-documentation.js +7 -10
- package/dist/schemas/v3.1/strict/header.js +27 -28
- package/dist/schemas/v3.1/strict/info.js +10 -16
- package/dist/schemas/v3.1/strict/license.js +9 -12
- package/dist/schemas/v3.1/strict/link.js +22 -19
- package/dist/schemas/v3.1/strict/media-type.js +17 -16
- package/dist/schemas/v3.1/strict/oauth-flow.js +28 -62
- package/dist/schemas/v3.1/strict/oauthflows.js +14 -20
- package/dist/schemas/v3.1/strict/openapi-document.js +126 -210
- package/dist/schemas/v3.1/strict/operation.js +16 -43
- package/dist/schemas/v3.1/strict/parameter.js +27 -35
- package/dist/schemas/v3.1/strict/path-item.js +36 -40
- package/dist/schemas/v3.1/strict/paths.js +8 -11
- package/dist/schemas/v3.1/strict/ref-definitions.js +76 -100
- package/dist/schemas/v3.1/strict/reference.js +18 -21
- package/dist/schemas/v3.1/strict/request-body.js +8 -16
- package/dist/schemas/v3.1/strict/response.js +12 -16
- package/dist/schemas/v3.1/strict/responses.js +13 -11
- package/dist/schemas/v3.1/strict/schema.js +149 -157
- package/dist/schemas/v3.1/strict/security-requirement.js +13 -12
- package/dist/schemas/v3.1/strict/security-scheme.js +29 -41
- package/dist/schemas/v3.1/strict/server-variable.js +9 -12
- package/dist/schemas/v3.1/strict/server.js +10 -13
- package/dist/schemas/v3.1/strict/tag.js +11 -20
- package/dist/schemas/v3.1/strict/type-guards.js +20 -20
- package/dist/schemas/v3.1/strict/xml.js +17 -16
- package/dist/schemas/workspace-specification/index.js +11 -23
- package/dist/schemas/workspace-specification/info.js +4 -8
- package/dist/schemas/workspace.js +25 -51
- package/dist/schemas.js +2 -6
- package/dist/server.js +286 -176
- package/dist/workspace-plugin.js +1 -1
- package/package.json +13 -19
- package/dist/client.js.map +0 -7
- package/dist/entities/auth/index.js.map +0 -7
- package/dist/entities/auth/schema.js.map +0 -7
- package/dist/entities/history/index.js.map +0 -7
- package/dist/entities/history/schema.js.map +0 -7
- package/dist/events/bus.js.map +0 -7
- package/dist/events/definitions/analytics.js.map +0 -7
- package/dist/events/definitions/auth.js.map +0 -7
- package/dist/events/definitions/common.js.map +0 -7
- package/dist/events/definitions/cookie.js.map +0 -7
- package/dist/events/definitions/document.js.map +0 -7
- package/dist/events/definitions/environment.js.map +0 -7
- package/dist/events/definitions/hooks.js.map +0 -7
- package/dist/events/definitions/index.js.map +0 -7
- package/dist/events/definitions/meta.js.map +0 -7
- package/dist/events/definitions/operation.js.map +0 -7
- package/dist/events/definitions/server.js.map +0 -7
- package/dist/events/definitions/tabs.js.map +0 -7
- package/dist/events/definitions/tag.js.map +0 -7
- package/dist/events/definitions/ui.js.map +0 -7
- package/dist/events/definitions/workspace.js.map +0 -7
- package/dist/events/index.js.map +0 -7
- package/dist/events/listeners.js.map +0 -7
- package/dist/events/old-definitions.js.map +0 -7
- package/dist/helpers/apply-selective-updates.d.ts +0 -19
- package/dist/helpers/apply-selective-updates.d.ts.map +0 -1
- package/dist/helpers/apply-selective-updates.js +0 -37
- package/dist/helpers/apply-selective-updates.js.map +0 -7
- package/dist/helpers/deep-clone.js.map +0 -7
- package/dist/helpers/detect-changes-proxy.js.map +0 -7
- package/dist/helpers/general.js.map +0 -7
- package/dist/helpers/generate-unique-value.js.map +0 -7
- package/dist/helpers/get-fetch.js.map +0 -7
- package/dist/helpers/get-resolved-ref.js.map +0 -7
- package/dist/helpers/is-non-optional-security-requirement.js.map +0 -7
- package/dist/helpers/merge-object.js.map +0 -7
- package/dist/helpers/overrides-proxy.js.map +0 -7
- package/dist/helpers/unpack-proxy.js.map +0 -7
- package/dist/mutators/auth.js.map +0 -7
- package/dist/mutators/cookie.js.map +0 -7
- package/dist/mutators/document.js.map +0 -7
- package/dist/mutators/environment.js.map +0 -7
- package/dist/mutators/helpers.js.map +0 -7
- package/dist/mutators/index.js.map +0 -7
- package/dist/mutators/operation/body.js.map +0 -7
- package/dist/mutators/operation/extensions.js.map +0 -7
- package/dist/mutators/operation/helpers/fetch-request-to-har.js.map +0 -7
- package/dist/mutators/operation/helpers/fetch-response-to-har.js.map +0 -7
- package/dist/mutators/operation/helpers/get-parameter-position.js.map +0 -7
- package/dist/mutators/operation/helpers/har-to-operation.js.map +0 -7
- package/dist/mutators/operation/helpers/sync-path-parameters.js.map +0 -7
- package/dist/mutators/operation/history.js.map +0 -7
- package/dist/mutators/operation/index.js.map +0 -7
- package/dist/mutators/operation/operation.js.map +0 -7
- package/dist/mutators/operation/parameters.js.map +0 -7
- package/dist/mutators/server.js.map +0 -7
- package/dist/mutators/tabs.js.map +0 -7
- package/dist/mutators/tag.js.map +0 -7
- package/dist/mutators/workspace.js.map +0 -7
- package/dist/navigation/get-navigation-options.js.map +0 -7
- package/dist/navigation/helpers/get-openapi-object.js.map +0 -7
- package/dist/navigation/helpers/get-operation-entries.js.map +0 -7
- package/dist/navigation/helpers/get-parent-entry.js.map +0 -7
- package/dist/navigation/helpers/get-tag-entries.js.map +0 -7
- package/dist/navigation/helpers/get-tag.js.map +0 -7
- package/dist/navigation/helpers/get-x-keys.js.map +0 -7
- package/dist/navigation/helpers/traverse-description.js.map +0 -7
- package/dist/navigation/helpers/traverse-document.js.map +0 -7
- package/dist/navigation/helpers/traverse-examples.js.map +0 -7
- package/dist/navigation/helpers/traverse-paths.js.map +0 -7
- package/dist/navigation/helpers/traverse-schemas.js.map +0 -7
- package/dist/navigation/helpers/traverse-tags.js.map +0 -7
- package/dist/navigation/helpers/traverse-webhooks.js.map +0 -7
- package/dist/navigation/helpers/update-order-ids.js.map +0 -7
- package/dist/navigation/helpers/utils.js.map +0 -7
- package/dist/navigation/index.js.map +0 -7
- package/dist/navigation/types.js.map +0 -7
- package/dist/persistence/index.js.map +0 -7
- package/dist/persistence/indexdb.js.map +0 -7
- package/dist/plugins/bundler/helpers.js.map +0 -7
- package/dist/plugins/bundler/index.js.map +0 -7
- package/dist/plugins/client/index.js.map +0 -7
- package/dist/plugins/client/persistence.js.map +0 -7
- package/dist/resolve.js.map +0 -7
- package/dist/schemas/compose.js.map +0 -7
- package/dist/schemas/extensions/document/x-internal.js.map +0 -7
- package/dist/schemas/extensions/document/x-scalar-environments.js.map +0 -7
- package/dist/schemas/extensions/document/x-scalar-icon.js.map +0 -7
- package/dist/schemas/extensions/document/x-scalar-ignore.js.map +0 -7
- package/dist/schemas/extensions/document/x-scalar-is-dirty.js.map +0 -7
- package/dist/schemas/extensions/document/x-scalar-original-document-hash.js.map +0 -7
- package/dist/schemas/extensions/document/x-scalar-registry-meta.js.map +0 -7
- package/dist/schemas/extensions/document/x-scalar-sdk-installation.js.map +0 -7
- package/dist/schemas/extensions/document/x-scalar-watch-mode.js.map +0 -7
- package/dist/schemas/extensions/document/x-tags.js.map +0 -7
- package/dist/schemas/extensions/example/x-disabled.js.map +0 -7
- package/dist/schemas/extensions/general/x-scalar-active-environment.js.map +0 -7
- package/dist/schemas/extensions/general/x-scalar-cookies.js.map +0 -7
- package/dist/schemas/extensions/general/x-scalar-order.js.map +0 -7
- package/dist/schemas/extensions/operation/index.js.map +0 -7
- package/dist/schemas/extensions/operation/x-badge.js.map +0 -7
- package/dist/schemas/extensions/operation/x-code-samples.js.map +0 -7
- package/dist/schemas/extensions/operation/x-draft-examples.js.map +0 -7
- package/dist/schemas/extensions/operation/x-post-response.js.map +0 -7
- package/dist/schemas/extensions/operation/x-scalar-disable-parameters.js.map +0 -7
- package/dist/schemas/extensions/operation/x-scalar-selected-content-type.js.map +0 -7
- package/dist/schemas/extensions/operation/x-scalar-stability.js.map +0 -7
- package/dist/schemas/extensions/parameter/x-global.js.map +0 -7
- package/dist/schemas/extensions/schema/x-additional-properties-name.js.map +0 -7
- package/dist/schemas/extensions/schema/x-enum-descriptions.js.map +0 -7
- package/dist/schemas/extensions/schema/x-enum-varnames.js.map +0 -7
- package/dist/schemas/extensions/schema/x-examples.js.map +0 -7
- package/dist/schemas/extensions/schema/x-variable.js.map +0 -7
- package/dist/schemas/extensions/security/index.js.map +0 -7
- package/dist/schemas/extensions/security/x-default-scopes.js.map +0 -7
- package/dist/schemas/extensions/security/x-scalar-credentials-location.js.map +0 -7
- package/dist/schemas/extensions/security/x-scalar-security-body.js.map +0 -7
- package/dist/schemas/extensions/security/x-scalar-security-query.js.map +0 -7
- package/dist/schemas/extensions/security/x-scalar-security-secrets.js.map +0 -7
- package/dist/schemas/extensions/security/x-tokenName.js.map +0 -7
- package/dist/schemas/extensions/security/x-use-pkce.js.map +0 -7
- package/dist/schemas/extensions/server/x-scalar-selected-server.js.map +0 -7
- package/dist/schemas/extensions/tag/index.js.map +0 -7
- package/dist/schemas/extensions/tag/x-display-name.js.map +0 -7
- package/dist/schemas/extensions/tag/x-tag-groups.js.map +0 -7
- package/dist/schemas/extensions/workspace/index.js.map +0 -7
- package/dist/schemas/extensions/workspace/x-scalar-active-proxy.js.map +0 -7
- package/dist/schemas/extensions/workspace/x-scalar-tabs.js.map +0 -7
- package/dist/schemas/extensions.js.map +0 -7
- package/dist/schemas/inmemory-workspace.js.map +0 -7
- package/dist/schemas/navigation.js.map +0 -7
- package/dist/schemas/reference-config/appearance.js.map +0 -7
- package/dist/schemas/reference-config/features.js.map +0 -7
- package/dist/schemas/reference-config/index.js.map +0 -7
- package/dist/schemas/reference-config/meta.js.map +0 -7
- package/dist/schemas/reference-config/routing.js.map +0 -7
- package/dist/schemas/reference-config/settings.js.map +0 -7
- package/dist/schemas/typebox-coerce.js.map +0 -7
- package/dist/schemas/v3.1/strict/callback.js.map +0 -7
- package/dist/schemas/v3.1/strict/components.js.map +0 -7
- package/dist/schemas/v3.1/strict/contact.js.map +0 -7
- package/dist/schemas/v3.1/strict/discriminator.js.map +0 -7
- package/dist/schemas/v3.1/strict/encoding.js.map +0 -7
- package/dist/schemas/v3.1/strict/example.js.map +0 -7
- package/dist/schemas/v3.1/strict/external-documentation.js.map +0 -7
- package/dist/schemas/v3.1/strict/header.js.map +0 -7
- package/dist/schemas/v3.1/strict/info.js.map +0 -7
- package/dist/schemas/v3.1/strict/license.js.map +0 -7
- package/dist/schemas/v3.1/strict/link.js.map +0 -7
- package/dist/schemas/v3.1/strict/media-type.js.map +0 -7
- package/dist/schemas/v3.1/strict/oauth-flow.js.map +0 -7
- package/dist/schemas/v3.1/strict/oauthflows.js.map +0 -7
- package/dist/schemas/v3.1/strict/openapi-document.js.map +0 -7
- package/dist/schemas/v3.1/strict/operation.js.map +0 -7
- package/dist/schemas/v3.1/strict/parameter.js.map +0 -7
- package/dist/schemas/v3.1/strict/path-item.js.map +0 -7
- package/dist/schemas/v3.1/strict/paths.js.map +0 -7
- package/dist/schemas/v3.1/strict/ref-definitions.js.map +0 -7
- package/dist/schemas/v3.1/strict/reference.js.map +0 -7
- package/dist/schemas/v3.1/strict/request-body.js.map +0 -7
- package/dist/schemas/v3.1/strict/response.js.map +0 -7
- package/dist/schemas/v3.1/strict/responses.js.map +0 -7
- package/dist/schemas/v3.1/strict/schema.js.map +0 -7
- package/dist/schemas/v3.1/strict/security-requirement.js.map +0 -7
- package/dist/schemas/v3.1/strict/security-scheme.js.map +0 -7
- package/dist/schemas/v3.1/strict/server-variable.js.map +0 -7
- package/dist/schemas/v3.1/strict/server.js.map +0 -7
- package/dist/schemas/v3.1/strict/tag.js.map +0 -7
- package/dist/schemas/v3.1/strict/type-guards.js.map +0 -7
- package/dist/schemas/v3.1/strict/xml.js.map +0 -7
- package/dist/schemas/workspace-specification/index.js.map +0 -7
- package/dist/schemas/workspace-specification/info.js.map +0 -7
- package/dist/schemas/workspace.js.map +0 -7
- package/dist/schemas.js.map +0 -7
- package/dist/server.js.map +0 -7
- package/dist/workspace-plugin.js.map +0 -7
package/dist/client.js
CHANGED
|
@@ -1,82 +1,136 @@
|
|
|
1
|
-
import { getValueAtPath } from
|
|
2
|
-
import { isObject } from
|
|
3
|
-
import { preventPollution } from
|
|
4
|
-
import { generateHash } from
|
|
5
|
-
import { measureAsync, measureSync } from
|
|
6
|
-
import { bundle } from
|
|
7
|
-
import { fetchUrls } from
|
|
8
|
-
import { apply, diff, merge } from
|
|
9
|
-
import { createMagicProxy, getRaw } from
|
|
10
|
-
import { upgrade } from
|
|
11
|
-
import { Value } from
|
|
12
|
-
import { reactive } from
|
|
13
|
-
import YAML from
|
|
14
|
-
import { createAuthStore } from
|
|
15
|
-
import { createHistoryStore } from
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1
|
+
import { getValueAtPath } from '@scalar/helpers/object/get-value-at-path';
|
|
2
|
+
import { isObject } from '@scalar/helpers/object/is-object';
|
|
3
|
+
import { preventPollution } from '@scalar/helpers/object/prevent-pollution';
|
|
4
|
+
import { generateHash } from '@scalar/helpers/string/generate-hash';
|
|
5
|
+
import { measureAsync, measureSync } from '@scalar/helpers/testing/measure';
|
|
6
|
+
import { bundle } from '@scalar/json-magic/bundle';
|
|
7
|
+
import { fetchUrls } from '@scalar/json-magic/bundle/plugins/browser';
|
|
8
|
+
import { apply, diff, merge } from '@scalar/json-magic/diff';
|
|
9
|
+
import { createMagicProxy, getRaw } from '@scalar/json-magic/magic-proxy';
|
|
10
|
+
import { upgrade } from '@scalar/openapi-upgrader';
|
|
11
|
+
import { Value } from '@scalar/typebox/value';
|
|
12
|
+
import { reactive } from 'vue';
|
|
13
|
+
import YAML from 'yaml';
|
|
14
|
+
import { createAuthStore } from './entities/auth/index.js';
|
|
15
|
+
import { createHistoryStore } from './entities/history/index.js';
|
|
16
|
+
import { deepClone } from './helpers/deep-clone.js';
|
|
17
|
+
import { createDetectChangesProxy } from './helpers/detect-changes-proxy.js';
|
|
18
|
+
import { safeAssign } from './helpers/general.js';
|
|
19
|
+
import { getFetch } from './helpers/get-fetch.js';
|
|
20
|
+
import { mergeObjects } from './helpers/merge-object.js';
|
|
21
|
+
import { createOverridesProxy } from './helpers/overrides-proxy.js';
|
|
22
|
+
import { unpackProxyObject } from './helpers/unpack-proxy.js';
|
|
23
|
+
import { createNavigation } from './navigation/index.js';
|
|
24
|
+
import { externalValueResolver, loadingStatus, normalizeAuthSchemes, normalizeRefs, refsEverywhere, removeExtraScalarKeys, restoreOriginalRefs, syncPathParameters, } from './plugins/bundler/index.js';
|
|
25
|
+
import { extensions } from './schemas/extensions.js';
|
|
26
|
+
import { coerceValue } from './schemas/typebox-coerce.js';
|
|
27
|
+
import { OpenAPIDocumentSchema as OpenAPIDocumentSchemaStrict, } from './schemas/v3.1/strict/openapi-document.js';
|
|
28
|
+
/**
|
|
29
|
+
* Resolves a workspace document from various input sources (URL, local file, or direct document object).
|
|
30
|
+
*
|
|
31
|
+
* @param workspaceDocument - The document input to resolve, which can be:
|
|
32
|
+
* - A URL to fetch the document from
|
|
33
|
+
* - A direct document object
|
|
34
|
+
* @returns A promise that resolves to an object containing:
|
|
35
|
+
* - ok: boolean indicating if the resolution was successful
|
|
36
|
+
* - data: The resolved document data
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // Resolve from URL
|
|
40
|
+
* const urlDoc = await loadDocument({ name: 'api', url: 'https://api.example.com/openapi.json' })
|
|
41
|
+
*
|
|
42
|
+
* // Resolve direct document
|
|
43
|
+
* const directDoc = await loadDocument({
|
|
44
|
+
* name: 'inline',
|
|
45
|
+
* document: { openapi: '3.0.0', paths: {} }
|
|
46
|
+
* })
|
|
47
|
+
*/
|
|
39
48
|
function loadDocument(workspaceDocument) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
if ("path" in workspaceDocument) {
|
|
44
|
-
const loader = workspaceDocument.fileLoader;
|
|
45
|
-
if (!loader) {
|
|
46
|
-
console.error("No loader provided for loading files");
|
|
47
|
-
return Promise.resolve({
|
|
48
|
-
ok: false
|
|
49
|
-
});
|
|
49
|
+
if ('url' in workspaceDocument) {
|
|
50
|
+
return fetchUrls({ fetch: workspaceDocument.fetch }).exec(workspaceDocument.url);
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
if ('path' in workspaceDocument) {
|
|
53
|
+
const loader = workspaceDocument.fileLoader;
|
|
54
|
+
if (!loader) {
|
|
55
|
+
console.error('No loader provided for loading files');
|
|
56
|
+
return Promise.resolve({
|
|
57
|
+
ok: false,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return loader.exec(workspaceDocument.path);
|
|
61
|
+
}
|
|
62
|
+
return Promise.resolve({
|
|
63
|
+
ok: true,
|
|
64
|
+
data: workspaceDocument.document,
|
|
65
|
+
// string version of the raw document for hashing purposes
|
|
66
|
+
raw: JSON.stringify(workspaceDocument.document),
|
|
67
|
+
});
|
|
59
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Returns the base source of a workspace document if it was loaded from a URL or file.
|
|
71
|
+
* If the document was loaded from a file, returns the path to the file.
|
|
72
|
+
* If the document was provided directly as an object, returns undefined.
|
|
73
|
+
* Which can be used to resolve relative references in the document.
|
|
74
|
+
*
|
|
75
|
+
* @param input - The workspace document input (either UrlDoc or ObjectDoc)
|
|
76
|
+
* @returns The URL string if present, otherwise undefined
|
|
77
|
+
*/
|
|
60
78
|
const getDocumentSource = (input) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
if ('url' in input) {
|
|
80
|
+
return input.url;
|
|
81
|
+
}
|
|
82
|
+
if ('path' in input) {
|
|
83
|
+
return input.path;
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
68
86
|
};
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Creates a reactive workspace store that manages documents and their metadata.
|
|
89
|
+
* The store provides functionality for accessing, updating, and resolving document references.
|
|
90
|
+
*
|
|
91
|
+
* @param workspaceProps - Configuration object for the workspace
|
|
92
|
+
* @param workspaceProps.meta - Optional metadata for the workspace
|
|
93
|
+
* @param workspaceProps.documents - Optional record of documents to initialize the workspace with
|
|
94
|
+
* Documents that require asynchronous loading must be added using `1` after the store is created
|
|
95
|
+
* this allows atomic awaiting and does not block page load for the store initialization
|
|
96
|
+
* @returns An object containing methods and getters for managing the workspace
|
|
97
|
+
*/
|
|
98
|
+
export const createWorkspaceStore = (workspaceProps) => {
|
|
99
|
+
const { verbose = false } = workspaceProps ?? {};
|
|
100
|
+
const withMeasurementSync = (name, fn) => (verbose ? measureSync(name, fn) : fn());
|
|
101
|
+
const withMeasurementAsync = (name, fn) => verbose ? measureAsync(name, fn) : fn();
|
|
102
|
+
/**
|
|
103
|
+
* Holds additional configuration options for each document in the workspace.
|
|
104
|
+
*
|
|
105
|
+
* This can include settings that can not be persisted between sessions (not JSON serializable)
|
|
106
|
+
*/
|
|
107
|
+
const extraDocumentConfigurations = {};
|
|
108
|
+
/**
|
|
109
|
+
* Notifies all workspace plugins of a workspace state change event.
|
|
110
|
+
*
|
|
111
|
+
* This function iterates through all registered plugins (if any) and invokes
|
|
112
|
+
* their onWorkspaceStateChanges hook with the given event object.
|
|
113
|
+
*
|
|
114
|
+
* @param event - The workspace state change event to broadcast to plugins
|
|
115
|
+
*/
|
|
116
|
+
const fireWorkspaceChange = (event) => {
|
|
117
|
+
workspaceProps?.plugins?.forEach((plugin) => plugin.hooks?.onWorkspaceStateChanges?.(event));
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* An object containing the reactive workspace state.
|
|
121
|
+
*
|
|
122
|
+
* Every change to the workspace, is tracked and broadcast to all registered plugins.
|
|
123
|
+
* allowing for change tracking.
|
|
124
|
+
*
|
|
125
|
+
* NOTE:
|
|
126
|
+
* The detect changes proxy is applied separately beacause the vue reactitvity proxy have to be the outer most proxy.
|
|
127
|
+
* If the order is reversed, Vue cannot properly track mutations, leading to lost reactivity and bugs.
|
|
128
|
+
* By wrapping the contents with the detect changes proxy first, and then passing the result to Vue's `reactive`,
|
|
129
|
+
* we ensure that Vue manages its reactivity as expected and our change detection hooks
|
|
130
|
+
* are also triggered reliably.
|
|
131
|
+
* Do not reverse this order‼️
|
|
132
|
+
*/
|
|
133
|
+
const workspace = reactive(createDetectChangesProxy({
|
|
80
134
|
...workspaceProps?.meta,
|
|
81
135
|
documents: {},
|
|
82
136
|
/**
|
|
@@ -87,626 +141,711 @@ const createWorkspaceStore = (workspaceProps) => {
|
|
|
87
141
|
* @returns The active document or undefined if no document is found
|
|
88
142
|
*/
|
|
89
143
|
get activeDocument() {
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
{
|
|
144
|
+
return workspace.documents[getActiveDocumentName()];
|
|
145
|
+
},
|
|
146
|
+
}, {
|
|
94
147
|
hooks: {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
148
|
+
onAfterChange(path) {
|
|
149
|
+
const type = path[0];
|
|
150
|
+
/** Document changes */
|
|
151
|
+
if (type === 'documents') {
|
|
152
|
+
// We are overriding the while documents object, ignore. This should not happen
|
|
153
|
+
if (path.length < 2) {
|
|
154
|
+
console.log('[WARN]: Overriding entire documents object is not supported');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const documentName = path[1];
|
|
158
|
+
const document = workspace.documents[documentName] ?? {
|
|
159
|
+
openapi: '3.1.0',
|
|
160
|
+
info: { title: '', version: '' },
|
|
161
|
+
'x-scalar-original-document-hash': '',
|
|
162
|
+
};
|
|
163
|
+
const event = {
|
|
164
|
+
type: 'documents',
|
|
165
|
+
documentName,
|
|
166
|
+
value: unpackProxyObject(document),
|
|
167
|
+
path: path.slice(2),
|
|
168
|
+
};
|
|
169
|
+
// Don't mark as dirty when the document is first created
|
|
170
|
+
if (event.path.length > 0 && event.path[0] !== 'x-scalar-is-dirty') {
|
|
171
|
+
// The document has been modified since it was last saved
|
|
172
|
+
document['x-scalar-is-dirty'] = true;
|
|
173
|
+
}
|
|
174
|
+
fireWorkspaceChange(event);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
/** Active document changes */
|
|
178
|
+
if (type === 'activeDocument') {
|
|
179
|
+
const documentName = getActiveDocumentName();
|
|
180
|
+
const document = workspace.documents[documentName] ?? {
|
|
181
|
+
openapi: '3.1.0',
|
|
182
|
+
info: { title: '', version: '' },
|
|
183
|
+
'x-scalar-original-document-hash': '',
|
|
184
|
+
};
|
|
185
|
+
// Active document changed
|
|
186
|
+
const event = {
|
|
187
|
+
type: 'documents',
|
|
188
|
+
documentName,
|
|
189
|
+
value: unpackProxyObject(document),
|
|
190
|
+
path: path.slice(2),
|
|
191
|
+
};
|
|
192
|
+
// Don't mark as dirty when the document is first created
|
|
193
|
+
if (event.path.length > 0 && event.path[0] !== 'x-scalar-is-dirty') {
|
|
194
|
+
// The document has been modified since it was last saved
|
|
195
|
+
document['x-scalar-is-dirty'] = true;
|
|
196
|
+
}
|
|
197
|
+
fireWorkspaceChange(event);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
/** Workspace meta changes */
|
|
201
|
+
const { activeDocument: _a, documents: _d, ...meta } = workspace;
|
|
202
|
+
const event = {
|
|
203
|
+
type: 'meta',
|
|
204
|
+
value: unpackProxyObject(meta, { depth: 1 }),
|
|
205
|
+
};
|
|
206
|
+
fireWorkspaceChange(event);
|
|
100
207
|
return;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
* OpenAPI document representing the overridden fields.
|
|
182
|
-
*/
|
|
183
|
-
overrides: {}
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
hooks: {
|
|
187
|
-
onAfterChange(path) {
|
|
188
|
-
const type = path[0];
|
|
189
|
-
if (!type) {
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
if (path.length < 2) {
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
const documentName = path[1];
|
|
196
|
-
if (type === "originalDocuments") {
|
|
197
|
-
const event = {
|
|
198
|
-
type,
|
|
199
|
-
documentName,
|
|
200
|
-
value: unpackProxyObject(originalDocuments[documentName] ?? {}),
|
|
201
|
-
path: path.splice(2)
|
|
202
|
-
};
|
|
203
|
-
fireWorkspaceChange(event);
|
|
204
|
-
}
|
|
205
|
-
if (type === "intermediateDocuments") {
|
|
206
|
-
const event = {
|
|
207
|
-
type,
|
|
208
|
-
documentName,
|
|
209
|
-
value: unpackProxyObject(intermediateDocuments[documentName] ?? {}),
|
|
210
|
-
path: path.splice(2)
|
|
211
|
-
};
|
|
212
|
-
fireWorkspaceChange(event);
|
|
213
|
-
}
|
|
214
|
-
if (type === "overrides") {
|
|
215
|
-
const event = {
|
|
216
|
-
type,
|
|
217
|
-
documentName,
|
|
218
|
-
value: unpackProxyObject(overrides[documentName] ?? {})
|
|
219
|
-
};
|
|
220
|
-
fireWorkspaceChange(event);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
);
|
|
226
|
-
const history = createHistoryStore({
|
|
227
|
-
hooks: {
|
|
228
|
-
onHistoryChange: (documentName) => {
|
|
229
|
-
fireWorkspaceChange({
|
|
230
|
-
type: "history",
|
|
231
|
-
documentName,
|
|
232
|
-
value: history.export()[documentName] ?? {}
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
const auth = createAuthStore({
|
|
238
|
-
hooks: {
|
|
239
|
-
onAuthChange: (documentName) => {
|
|
240
|
-
fireWorkspaceChange({
|
|
241
|
-
type: "auth",
|
|
242
|
-
documentName,
|
|
243
|
-
value: auth.export()[documentName] ?? {
|
|
244
|
-
secrets: {},
|
|
245
|
-
selected: { document: { selectedIndex: 0, selectedSchemes: [] }, path: {} }
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
function getActiveDocumentName() {
|
|
252
|
-
return workspace[extensions.workspace.activeDocument] ?? Object.keys(workspace.documents)[0] ?? "";
|
|
253
|
-
}
|
|
254
|
-
function exportDocument(documentName, format, minify) {
|
|
255
|
-
const intermediateDocument = intermediateDocuments[documentName];
|
|
256
|
-
if (!intermediateDocument) {
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
if (format === "json") {
|
|
260
|
-
return minify ? JSON.stringify(intermediateDocument) : JSON.stringify(intermediateDocument, null, 2);
|
|
261
|
-
}
|
|
262
|
-
return YAML.stringify(intermediateDocument);
|
|
263
|
-
}
|
|
264
|
-
async function saveDocument(documentName) {
|
|
265
|
-
const intermediateDocument = intermediateDocuments[documentName];
|
|
266
|
-
const workspaceDocument = workspace.documents[documentName];
|
|
267
|
-
if (!workspaceDocument) {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
const activeDocumentRaw = unpackProxyObject(workspaceDocument);
|
|
271
|
-
if (!intermediateDocument || !activeDocumentRaw) {
|
|
272
|
-
console.warn("Failed to save document, intermediate document and/or active document is missing");
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
const updatedWithOriginalRefs = await bundle(deepClone(activeDocumentRaw), {
|
|
276
|
-
plugins: [restoreOriginalRefs()],
|
|
277
|
-
treeShake: false,
|
|
278
|
-
urlMap: true
|
|
279
|
-
});
|
|
280
|
-
const excludedDiffs = applySelectiveUpdates(intermediateDocument, updatedWithOriginalRefs);
|
|
281
|
-
workspaceDocument["x-scalar-is-dirty"] = false;
|
|
282
|
-
return excludedDiffs;
|
|
283
|
-
}
|
|
284
|
-
async function addInMemoryDocument(input, navigationOptions) {
|
|
285
|
-
const { name, meta } = input;
|
|
286
|
-
const clonedRawInputDocument = withMeasurementSync("deepClone", () => deepClone(input.document));
|
|
287
|
-
withMeasurementSync("initialize", () => {
|
|
288
|
-
if (input.initialize !== false) {
|
|
289
|
-
originalDocuments[name] = deepClone(clonedRawInputDocument);
|
|
290
|
-
intermediateDocuments[name] = deepClone(clonedRawInputDocument);
|
|
291
|
-
overrides[name] = input.overrides ?? {};
|
|
292
|
-
extraDocumentConfigurations[name] = { fetch: input.fetch };
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
const inputDocument = withMeasurementSync("upgrade", () => upgrade(deepClone(clonedRawInputDocument), "3.1"));
|
|
296
|
-
const strictDocument = createMagicProxy(
|
|
297
|
-
{
|
|
298
|
-
...inputDocument,
|
|
299
|
-
...meta,
|
|
300
|
-
"x-original-oas-version": originalDocuments[name]?.openapi ?? originalDocuments[name]?.swagger,
|
|
301
|
-
"x-scalar-original-document-hash": input.documentHash,
|
|
302
|
-
"x-scalar-original-source-url": input.documentSource
|
|
303
|
-
},
|
|
304
|
-
{ showInternal: true }
|
|
305
|
-
);
|
|
306
|
-
if (strictDocument[extensions.document.navigation] === void 0) {
|
|
307
|
-
const loaders = [
|
|
308
|
-
fetchUrls({
|
|
309
|
-
fetch: extraDocumentConfigurations[name]?.fetch ?? workspaceProps?.fetch
|
|
310
|
-
})
|
|
311
|
-
];
|
|
312
|
-
if (workspaceProps?.fileLoader) {
|
|
313
|
-
loaders.push(workspaceProps.fileLoader);
|
|
314
|
-
}
|
|
315
|
-
await withMeasurementAsync(
|
|
316
|
-
"bundle",
|
|
317
|
-
async () => await bundle(getRaw(strictDocument), {
|
|
318
|
-
treeShake: false,
|
|
319
|
-
plugins: [
|
|
320
|
-
...loaders,
|
|
321
|
-
normalizeRefs(),
|
|
322
|
-
externalValueResolver(),
|
|
323
|
-
refsEverywhere(),
|
|
324
|
-
normalizeAuthSchemes(),
|
|
325
|
-
syncPathParameters()
|
|
326
|
-
],
|
|
327
|
-
urlMap: true,
|
|
328
|
-
origin: input.documentSource
|
|
329
|
-
// use the document origin (if provided) as the base URL for resolution
|
|
330
|
-
})
|
|
331
|
-
);
|
|
332
|
-
const coerced = withMeasurementSync(
|
|
333
|
-
"coerceValue",
|
|
334
|
-
() => coerceValue(OpenAPIDocumentSchemaStrict, deepClone(strictDocument))
|
|
335
|
-
);
|
|
336
|
-
withMeasurementSync("mergeObjects", () => mergeObjects(strictDocument, coerced));
|
|
337
|
-
}
|
|
338
|
-
const isValid = Value.Check(OpenAPIDocumentSchemaStrict, strictDocument);
|
|
339
|
-
if (!isValid) {
|
|
340
|
-
const validationErrors = Array.from(Value.Errors(OpenAPIDocumentSchemaStrict, strictDocument));
|
|
341
|
-
console.warn("document validation errors: ");
|
|
342
|
-
console.warn(
|
|
343
|
-
validationErrors.map((error) => ({
|
|
344
|
-
message: error.message,
|
|
345
|
-
path: error.path,
|
|
346
|
-
schema: error.schema,
|
|
347
|
-
value: error.value
|
|
348
|
-
}))
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
if (strictDocument[extensions.document.navigation] === void 0) {
|
|
352
|
-
const navigation = createNavigation(name, strictDocument, navigationOptions);
|
|
353
|
-
strictDocument[extensions.document.navigation] = navigation;
|
|
354
|
-
}
|
|
355
|
-
workspace.documents[name] = createOverridesProxy(createMagicProxy(getRaw(strictDocument)), {
|
|
356
|
-
overrides: unpackProxyObject(overrides[name])
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
}));
|
|
211
|
+
/**
|
|
212
|
+
* An object containing all the workspace state, wrapped in a detect changes proxy.
|
|
213
|
+
*
|
|
214
|
+
* Every change to the workspace state (documents, configs, metadata, etc.) can be detected here,
|
|
215
|
+
* allowing for change tracking.
|
|
216
|
+
*/
|
|
217
|
+
const { originalDocuments, intermediateDocuments, overrides } = createDetectChangesProxy({
|
|
218
|
+
/**
|
|
219
|
+
* Holds the original, unmodified documents as they were initially loaded into the workspace.
|
|
220
|
+
* These documents are stored in their raw form—prior to any reactive wrapping, dereferencing, or bundling.
|
|
221
|
+
* This map preserves the pristine structure of each document, using deep clones to ensure that
|
|
222
|
+
* subsequent mutations in the workspace do not affect the originals.
|
|
223
|
+
* The originals are retained so that we can restore, compare, or sync with the remote registry as needed.
|
|
224
|
+
*/
|
|
225
|
+
originalDocuments: {},
|
|
226
|
+
/**
|
|
227
|
+
* Stores the intermediate state of documents after local edits but before syncing with the remote registry.
|
|
228
|
+
*
|
|
229
|
+
* This map acts as a local "saved" version of the document, reflecting the user's changes after they hit "save".
|
|
230
|
+
* The `originalDocuments` map, by contrast, always mirrors the document as it exists in the remote registry.
|
|
231
|
+
*
|
|
232
|
+
* Use this map to stage local changes that are ready to be propagated back to the remote registry.
|
|
233
|
+
* This separation allows us to distinguish between:
|
|
234
|
+
* - The last known remote version (`originalDocuments`)
|
|
235
|
+
* - The latest locally saved version (`intermediateDocuments`)
|
|
236
|
+
* - The current in-memory (possibly unsaved) workspace document (`workspace.documents`)
|
|
237
|
+
*/
|
|
238
|
+
intermediateDocuments: {},
|
|
239
|
+
/**
|
|
240
|
+
* Stores per-document overrides for OpenAPI documents.
|
|
241
|
+
* This object is used to override specific fields of a document
|
|
242
|
+
* when you cannot (or should not) modify the source document directly.
|
|
243
|
+
* For example, this enables UI-driven or temporary changes to be applied
|
|
244
|
+
* on top of the original document, without mutating the source.
|
|
245
|
+
* The key is the document name, and the value is a deep partial
|
|
246
|
+
* OpenAPI document representing the overridden fields.
|
|
247
|
+
*/
|
|
248
|
+
overrides: {},
|
|
249
|
+
}, {
|
|
250
|
+
hooks: {
|
|
251
|
+
onAfterChange(path) {
|
|
252
|
+
const type = path[0];
|
|
253
|
+
if (!type) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (path.length < 2) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const documentName = path[1];
|
|
260
|
+
if (type === 'originalDocuments') {
|
|
261
|
+
const event = {
|
|
262
|
+
type,
|
|
263
|
+
documentName: documentName,
|
|
264
|
+
value: unpackProxyObject(originalDocuments[documentName] ?? {}),
|
|
265
|
+
path: path.splice(2),
|
|
266
|
+
};
|
|
267
|
+
fireWorkspaceChange(event);
|
|
268
|
+
}
|
|
269
|
+
if (type === 'intermediateDocuments') {
|
|
270
|
+
const event = {
|
|
271
|
+
type,
|
|
272
|
+
documentName: documentName,
|
|
273
|
+
value: unpackProxyObject(intermediateDocuments[documentName] ?? {}),
|
|
274
|
+
path: path.splice(2),
|
|
275
|
+
};
|
|
276
|
+
fireWorkspaceChange(event);
|
|
277
|
+
}
|
|
278
|
+
if (type === 'overrides') {
|
|
279
|
+
const event = {
|
|
280
|
+
type,
|
|
281
|
+
documentName: documentName,
|
|
282
|
+
value: unpackProxyObject(overrides[documentName] ?? {}),
|
|
283
|
+
};
|
|
284
|
+
fireWorkspaceChange(event);
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
},
|
|
357
288
|
});
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
289
|
+
/**
|
|
290
|
+
* This store is used to track the history of requests and responses for documents and operations.
|
|
291
|
+
*/
|
|
292
|
+
const history = createHistoryStore({
|
|
293
|
+
hooks: {
|
|
294
|
+
onHistoryChange: (documentName) => {
|
|
295
|
+
fireWorkspaceChange({
|
|
296
|
+
type: 'history',
|
|
297
|
+
documentName,
|
|
298
|
+
value: history.export()[documentName] ?? {},
|
|
299
|
+
});
|
|
300
|
+
},
|
|
301
|
+
},
|
|
364
302
|
});
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
};
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
if (!isObject(resolve.data)) {
|
|
384
|
-
console.error(`Failed to load document '${name}': response data is not a valid object`);
|
|
385
|
-
workspace.documents[name] = {
|
|
386
|
-
...meta,
|
|
387
|
-
openapi: "3.1.0",
|
|
388
|
-
info: {
|
|
389
|
-
title: `Document '${name}' could not be loaded`,
|
|
390
|
-
version: "unknown"
|
|
391
|
-
},
|
|
392
|
-
"x-scalar-original-document-hash": "not-a-hash"
|
|
393
|
-
};
|
|
394
|
-
return false;
|
|
395
|
-
}
|
|
396
|
-
await addInMemoryDocument(
|
|
397
|
-
{
|
|
398
|
-
...input,
|
|
399
|
-
document: resolve.data,
|
|
400
|
-
documentSource: getDocumentSource(input),
|
|
401
|
-
documentHash: generateHash(resolve.raw)
|
|
303
|
+
/**
|
|
304
|
+
* The auth store for the workspace
|
|
305
|
+
*/
|
|
306
|
+
const auth = createAuthStore({
|
|
307
|
+
hooks: {
|
|
308
|
+
onAuthChange: (documentName) => {
|
|
309
|
+
fireWorkspaceChange({
|
|
310
|
+
type: 'auth',
|
|
311
|
+
documentName,
|
|
312
|
+
value: auth.export()[documentName] ?? {
|
|
313
|
+
secrets: {},
|
|
314
|
+
selected: { document: { selectedIndex: 0, selectedSchemes: [] }, path: {} },
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
},
|
|
402
318
|
},
|
|
403
|
-
navigationOptions
|
|
404
|
-
);
|
|
405
|
-
return true;
|
|
406
319
|
});
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
const rawDocument = unpackProxyObject(intermediateDocuments[documentName], { depth: 1 });
|
|
417
|
-
if (!rawDocument) {
|
|
418
|
-
return null;
|
|
419
|
-
}
|
|
420
|
-
return rawDocument;
|
|
421
|
-
};
|
|
422
|
-
const promoteIntermediateToOriginal = (documentName) => {
|
|
423
|
-
const intermediate = intermediateDocuments[documentName];
|
|
424
|
-
if (!intermediate) {
|
|
425
|
-
return false;
|
|
320
|
+
/**
|
|
321
|
+
* Returns the name of the currently active document in the workspace.
|
|
322
|
+
* The active document is determined by the 'x-scalar-active-document' metadata field,
|
|
323
|
+
* falling back to the first document in the workspace if no active document is specified.
|
|
324
|
+
*
|
|
325
|
+
* @returns The name of the active document or an empty string if no document is found
|
|
326
|
+
*/
|
|
327
|
+
function getActiveDocumentName() {
|
|
328
|
+
return workspace[extensions.workspace.activeDocument] ?? Object.keys(workspace.documents)[0] ?? '';
|
|
426
329
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
330
|
+
function exportDocument(documentName, format, minify) {
|
|
331
|
+
const intermediateDocument = intermediateDocuments[documentName];
|
|
332
|
+
if (!intermediateDocument) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (format === 'json') {
|
|
336
|
+
return minify ? JSON.stringify(intermediateDocument) : JSON.stringify(intermediateDocument, null, 2);
|
|
337
|
+
}
|
|
338
|
+
return YAML.stringify(intermediateDocument);
|
|
435
339
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
340
|
+
// Save the current state of the specified document to the intermediate documents map.
|
|
341
|
+
// This function captures the latest (reactive) state of the document from the workspace and
|
|
342
|
+
// applies its changes to the corresponding entry in the `intermediateDocuments` map.
|
|
343
|
+
// The `intermediateDocuments` map represents the most recently "saved" local version of the document,
|
|
344
|
+
// which may include edits not yet synced to the remote registry.
|
|
345
|
+
const saveDocument = async (documentName) => {
|
|
346
|
+
const activeDocument = workspace.documents[documentName];
|
|
347
|
+
const newDocument = await getEditableDocument(documentName);
|
|
348
|
+
if (!activeDocument || !newDocument) {
|
|
349
|
+
console.warn('Failed to save document, active document is missing');
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
// Store the new document in the intermediate documents map
|
|
353
|
+
intermediateDocuments[documentName] = newDocument;
|
|
354
|
+
// Mark the document as not dirty since we are saving it
|
|
355
|
+
activeDocument['x-scalar-is-dirty'] = false;
|
|
356
|
+
return true;
|
|
357
|
+
};
|
|
358
|
+
// Add a document to the store synchronously from an in-memory OpenAPI document
|
|
359
|
+
async function addInMemoryDocument(input, navigationOptions) {
|
|
360
|
+
const { name, meta } = input;
|
|
361
|
+
const clonedRawInputDocument = withMeasurementSync('deepClone', () => deepClone(input.document));
|
|
362
|
+
withMeasurementSync('initialize', () => {
|
|
363
|
+
if (input.initialize !== false) {
|
|
364
|
+
// Store the original document in the originalDocuments map
|
|
365
|
+
// This is used to track the original state of the document as it was loaded into the workspace
|
|
366
|
+
originalDocuments[name] = deepClone(clonedRawInputDocument);
|
|
367
|
+
// Store the intermediate document state for local edits
|
|
368
|
+
// This is used to track the last saved state of the document
|
|
369
|
+
// It allows us to differentiate between the original document and the latest saved version
|
|
370
|
+
// This is important for local edits that are not yet synced with the remote registry
|
|
371
|
+
// The intermediate document is used to store the latest saved state of the document
|
|
372
|
+
// This allows us to track changes and revert to the last saved state if needed
|
|
373
|
+
intermediateDocuments[name] = deepClone(clonedRawInputDocument);
|
|
374
|
+
// Store the overrides for this document, or an empty object if none are provided
|
|
375
|
+
overrides[name] = input.overrides ?? {};
|
|
376
|
+
// Store extra document configurations that can not be persisted
|
|
377
|
+
extraDocumentConfigurations[name] = { fetch: input.fetch };
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
const inputDocument = withMeasurementSync('upgrade', () => upgrade(deepClone(clonedRawInputDocument), '3.1'));
|
|
381
|
+
const strictDocument = createMagicProxy({
|
|
382
|
+
...inputDocument,
|
|
383
|
+
...meta,
|
|
384
|
+
'x-original-oas-version': originalDocuments[name]?.openapi ?? originalDocuments[name]?.swagger,
|
|
385
|
+
'x-scalar-original-document-hash': input.documentHash,
|
|
386
|
+
'x-scalar-original-source-url': input.documentSource,
|
|
387
|
+
}, { showInternal: true });
|
|
388
|
+
// If the document navigation is not already present, bundle the entire document to resolve all references.
|
|
389
|
+
// This typically applies when the document is not preprocessed by the server and needs local reference resolution.
|
|
390
|
+
// We need to bundle document first before we validate, so we can also validate the external references
|
|
391
|
+
if (strictDocument[extensions.document.navigation] === undefined) {
|
|
392
|
+
const loaders = [
|
|
393
|
+
fetchUrls({
|
|
394
|
+
fetch: extraDocumentConfigurations[name]?.fetch ?? workspaceProps?.fetch,
|
|
395
|
+
}),
|
|
396
|
+
];
|
|
397
|
+
// If a file loader plugin is provided, use it to resolve local file references
|
|
398
|
+
// This is useful for non browser environments
|
|
399
|
+
if (workspaceProps?.fileLoader) {
|
|
400
|
+
loaders.push(workspaceProps.fileLoader);
|
|
401
|
+
}
|
|
402
|
+
await withMeasurementAsync('bundle', async () => await bundle(getRaw(strictDocument), {
|
|
403
|
+
treeShake: false,
|
|
404
|
+
plugins: [
|
|
405
|
+
...loaders,
|
|
406
|
+
normalizeRefs(),
|
|
407
|
+
externalValueResolver(),
|
|
408
|
+
refsEverywhere(),
|
|
409
|
+
normalizeAuthSchemes(),
|
|
410
|
+
syncPathParameters(),
|
|
411
|
+
],
|
|
412
|
+
urlMap: true,
|
|
413
|
+
origin: input.documentSource, // use the document origin (if provided) as the base URL for resolution
|
|
414
|
+
}));
|
|
415
|
+
// We coerce the values only when the document is not preprocessed by the server-side-store
|
|
416
|
+
const coerced = withMeasurementSync('coerceValue', () => coerceValue(OpenAPIDocumentSchemaStrict, deepClone(strictDocument)));
|
|
417
|
+
withMeasurementSync('mergeObjects', () => mergeObjects(strictDocument, coerced));
|
|
418
|
+
}
|
|
419
|
+
const isValid = Value.Check(OpenAPIDocumentSchemaStrict, strictDocument);
|
|
420
|
+
if (!isValid) {
|
|
421
|
+
const validationErrors = Array.from(Value.Errors(OpenAPIDocumentSchemaStrict, strictDocument));
|
|
422
|
+
console.warn('document validation errors: ');
|
|
423
|
+
console.warn(validationErrors.map((error) => ({
|
|
424
|
+
message: error.message,
|
|
425
|
+
path: error.path,
|
|
426
|
+
schema: error.schema,
|
|
427
|
+
value: error.value,
|
|
428
|
+
})));
|
|
429
|
+
}
|
|
430
|
+
// Skip navigation generation if the document already has a server-side generated navigation structure
|
|
431
|
+
if (strictDocument[extensions.document.navigation] === undefined) {
|
|
432
|
+
const navigation = createNavigation(name, strictDocument, navigationOptions);
|
|
433
|
+
strictDocument[extensions.document.navigation] = navigation;
|
|
434
|
+
}
|
|
435
|
+
// Create a proxied document with magic proxy and apply any overrides, then store it in the workspace documents map
|
|
436
|
+
// We create a new proxy here in order to hide internal properties after validation and processing
|
|
437
|
+
// This ensures that the workspace document only exposes the intended OpenAPI properties and extensions
|
|
438
|
+
workspace.documents[name] = createOverridesProxy(createMagicProxy(getRaw(strictDocument)), {
|
|
439
|
+
overrides: unpackProxyObject(overrides[name]),
|
|
440
|
+
});
|
|
443
441
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
442
|
+
// Asynchronously adds a new document to the workspace by loading and validating the input.
|
|
443
|
+
// If loading fails, a placeholder error document is added instead.
|
|
444
|
+
async function addDocument(input, navigationOptions) {
|
|
445
|
+
const { name, meta } = input;
|
|
446
|
+
/** Ensure we use the active proxy to fetch documents unless we have a custom fetch override */
|
|
447
|
+
const fetch = getFetch({
|
|
448
|
+
fetch: input.fetch ?? workspaceProps?.fetch,
|
|
449
|
+
proxyUrl: workspace['x-scalar-active-proxy'] ?? undefined,
|
|
450
|
+
});
|
|
451
|
+
const resolve = await withMeasurementAsync('loadDocument', async () => await loadDocument({ ...input, fetch, fileLoader: workspaceProps?.fileLoader }));
|
|
452
|
+
// Log the time taken to add a document
|
|
453
|
+
return await withMeasurementAsync('addDocument', async () => {
|
|
454
|
+
if (!resolve.ok) {
|
|
455
|
+
console.error(`Failed to fetch document '${name}': request was not successful`);
|
|
456
|
+
workspace.documents[name] = {
|
|
457
|
+
...meta,
|
|
458
|
+
openapi: '3.1.0',
|
|
459
|
+
info: {
|
|
460
|
+
title: `Document '${name}' could not be loaded`,
|
|
461
|
+
version: 'unknown',
|
|
462
|
+
},
|
|
463
|
+
'x-scalar-original-document-hash': 'not-a-hash',
|
|
464
|
+
};
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
if (!isObject(resolve.data)) {
|
|
468
|
+
console.error(`Failed to load document '${name}': response data is not a valid object`);
|
|
469
|
+
workspace.documents[name] = {
|
|
470
|
+
...meta,
|
|
471
|
+
openapi: '3.1.0',
|
|
472
|
+
info: {
|
|
473
|
+
title: `Document '${name}' could not be loaded`,
|
|
474
|
+
version: 'unknown',
|
|
475
|
+
},
|
|
476
|
+
'x-scalar-original-document-hash': 'not-a-hash',
|
|
477
|
+
};
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
await addInMemoryDocument({
|
|
481
|
+
...input,
|
|
482
|
+
document: resolve.data,
|
|
483
|
+
documentSource: getDocumentSource(input),
|
|
484
|
+
documentHash: generateHash(resolve.raw),
|
|
485
|
+
}, navigationOptions);
|
|
486
|
+
return true;
|
|
487
|
+
});
|
|
451
488
|
}
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
return
|
|
478
|
-
|
|
479
|
-
preventPollution(key);
|
|
480
|
-
Object.assign(currentDocument, { [key]: value });
|
|
481
|
-
return true;
|
|
482
|
-
},
|
|
483
|
-
async replaceDocument(documentName, input) {
|
|
484
|
-
const currentDocument = workspace.documents[documentName];
|
|
485
|
-
if (!currentDocument) {
|
|
486
|
-
return console.error(`Document '${documentName}' does not exist in the workspace.`);
|
|
487
|
-
}
|
|
488
|
-
await addInMemoryDocument({
|
|
489
|
-
name: documentName,
|
|
490
|
-
document: input,
|
|
491
|
-
// Preserve the current metadata
|
|
492
|
-
documentSource: currentDocument["x-scalar-original-source-url"],
|
|
493
|
-
documentHash: currentDocument["x-scalar-original-document-hash"],
|
|
494
|
-
meta: {
|
|
495
|
-
// Set the document as dirty
|
|
496
|
-
"x-scalar-is-dirty": true,
|
|
497
|
-
// Clear the navigation to trigger a rebuild
|
|
498
|
-
"x-scalar-navigation": void 0
|
|
499
|
-
},
|
|
500
|
-
initialize: false
|
|
501
|
-
});
|
|
502
|
-
},
|
|
503
|
-
resolve: (path) => {
|
|
504
|
-
const activeDocument = workspace.activeDocument;
|
|
505
|
-
const target = getValueAtPath(activeDocument, path);
|
|
506
|
-
if (!isObject(target)) {
|
|
507
|
-
console.error(
|
|
508
|
-
`Invalid path provided for resolution. Path: [${path.join(", ")}]. Found value of type: ${typeof target}. Expected an object.`
|
|
509
|
-
);
|
|
510
|
-
return Promise.resolve();
|
|
511
|
-
}
|
|
512
|
-
return bundle(target, {
|
|
513
|
-
root: activeDocument,
|
|
514
|
-
treeShake: false,
|
|
515
|
-
plugins: [fetchUrls(), loadingStatus(), externalValueResolver()],
|
|
516
|
-
urlMap: true,
|
|
517
|
-
visitedNodes: visitedNodesCache
|
|
518
|
-
});
|
|
519
|
-
},
|
|
520
|
-
addDocument,
|
|
489
|
+
const getOriginalDocument = (documentName) => {
|
|
490
|
+
const rawDocument = unpackProxyObject(originalDocuments[documentName], { depth: 1 });
|
|
491
|
+
if (!rawDocument) {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
return rawDocument;
|
|
495
|
+
};
|
|
496
|
+
const getIntermediateDocument = (documentName) => {
|
|
497
|
+
const rawDocument = unpackProxyObject(intermediateDocuments[documentName], { depth: 1 });
|
|
498
|
+
if (!rawDocument) {
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
return rawDocument;
|
|
502
|
+
};
|
|
503
|
+
/**
|
|
504
|
+
* Promotes the intermediate document to the original document so the current
|
|
505
|
+
* intermediate becomes the new baseline. Fires workspace change for persistence.
|
|
506
|
+
*/
|
|
507
|
+
const promoteIntermediateToOriginal = (documentName) => {
|
|
508
|
+
const intermediate = intermediateDocuments[documentName];
|
|
509
|
+
if (!intermediate) {
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
const cloned = deepClone(unpackProxyObject(intermediate, { depth: 1 }));
|
|
513
|
+
originalDocuments[documentName] = cloned;
|
|
514
|
+
return true;
|
|
515
|
+
};
|
|
521
516
|
/**
|
|
522
|
-
*
|
|
517
|
+
* Retrieves an editable clone of a workspace document.
|
|
523
518
|
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
519
|
+
* - Unpacks the proxied document from the workspace.
|
|
520
|
+
* - Reverses all external references, restoring original $refs.
|
|
521
|
+
* - Removes transient/in-memory keys defined in EXCLUDE_KEYS.
|
|
522
|
+
*
|
|
523
|
+
* @param documentName The name of the document to retrieve.
|
|
524
|
+
* @returns The editable document object, or null if not found.
|
|
526
525
|
*/
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
return;
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
documents
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
526
|
+
const getEditableDocument = async (documentName) => {
|
|
527
|
+
const rawDocument = unpackProxyObject(workspace.documents[documentName], { depth: 1 });
|
|
528
|
+
if (!rawDocument) {
|
|
529
|
+
// If the document does not exist, return null
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
// Reverse all external references and restore original $refs
|
|
533
|
+
const original = (await bundle(deepClone(rawDocument), {
|
|
534
|
+
plugins: [restoreOriginalRefs(), removeExtraScalarKeys()],
|
|
535
|
+
treeShake: false,
|
|
536
|
+
urlMap: true,
|
|
537
|
+
}));
|
|
538
|
+
// Top level keys that need to be excluded from the original document
|
|
539
|
+
// Nested keys are removed buring the previous step of the bundler process
|
|
540
|
+
const EXCLUDE_KEYS = [
|
|
541
|
+
// Bundler metadata fields added temporarily during document processing
|
|
542
|
+
'x-ext',
|
|
543
|
+
'x-ext-urls',
|
|
544
|
+
// Scalar internal/external metadata fields
|
|
545
|
+
'x-scalar-navigation',
|
|
546
|
+
'x-scalar-is-dirty',
|
|
547
|
+
'x-original-oas-version',
|
|
548
|
+
'x-scalar-original-document-hash',
|
|
549
|
+
'x-scalar-original-source-url',
|
|
550
|
+
];
|
|
551
|
+
// Remove top level properties that should only exist in memory for the original document
|
|
552
|
+
// These properties are used for internal purposes and are not needed in the final bundled document
|
|
553
|
+
for (const property of EXCLUDE_KEYS) {
|
|
554
|
+
delete original[property];
|
|
555
|
+
}
|
|
556
|
+
return original;
|
|
557
|
+
};
|
|
558
|
+
/**
|
|
559
|
+
* Builds (or updates) the navigation sidebar for the specified document.
|
|
560
|
+
*
|
|
561
|
+
* This method generates the sidebar navigation structure for a workspace document,
|
|
562
|
+
* and attaches it to the document's metadata under the navigation extension key.
|
|
563
|
+
* The document is unpacked to avoid assigning proxy objects as direct property references.
|
|
564
|
+
*
|
|
565
|
+
* - Only the top-level object is proxied; all child objects should be unproxied.
|
|
566
|
+
* - This approach enables safe unpacking of the proxy object without recursively traversing the full object tree.
|
|
567
|
+
*
|
|
568
|
+
* @param documentName - The name/key of the document whose sidebar should be built.
|
|
569
|
+
* @returns {boolean} True if the sidebar was built successfully, false if the document does not exist.
|
|
570
|
+
*/
|
|
571
|
+
const buildSidebar = (documentName) => {
|
|
572
|
+
const document = workspace.documents[documentName];
|
|
573
|
+
if (!document) {
|
|
574
|
+
// Log and exit if the document does not exist in the workspace
|
|
575
|
+
console.error(`Document '${documentName}' does not exist in the workspace.`);
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
// Generate the navigation structure for the sidebar.
|
|
579
|
+
const navigation = createNavigation(documentName, document);
|
|
580
|
+
// Set the computed navigation structure on the document metadata.
|
|
581
|
+
document[extensions.document.navigation] = navigation;
|
|
582
|
+
return true;
|
|
583
|
+
};
|
|
584
|
+
// Cache to track visited nodes during reference resolution to prevent bundling the same subtree multiple times
|
|
585
|
+
// This is needed because we are doing partial bundle operations
|
|
586
|
+
const visitedNodesCache = new Set();
|
|
587
|
+
return {
|
|
588
|
+
get workspace() {
|
|
589
|
+
return workspace;
|
|
581
590
|
},
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
])
|
|
600
|
-
)
|
|
601
|
-
);
|
|
602
|
-
safeAssign(originalDocuments, input.originalDocuments);
|
|
603
|
-
safeAssign(intermediateDocuments, input.intermediateDocuments);
|
|
604
|
-
safeAssign(overrides, input.overrides);
|
|
605
|
-
safeAssign(workspace, input.meta);
|
|
606
|
-
history.load(input.history);
|
|
607
|
-
auth.load(input.auth);
|
|
608
|
-
},
|
|
609
|
-
importWorkspaceFromSpecification: (specification) => {
|
|
610
|
-
const { documents, overrides: overrides2, info: _info, workspace: _workspaceVersion, ...meta } = specification;
|
|
611
|
-
safeAssign(workspace, meta);
|
|
612
|
-
return Promise.all(
|
|
613
|
-
Object.entries(documents ?? {}).map(
|
|
614
|
-
([name, doc]) => addDocument({ url: doc.$ref, name, overrides: overrides2?.[name] })
|
|
615
|
-
)
|
|
616
|
-
);
|
|
617
|
-
},
|
|
618
|
-
rebaseDocument: async (input) => {
|
|
619
|
-
const { name } = input;
|
|
620
|
-
const originalDocument = unpackProxyObject(originalDocuments[name], { depth: 1 });
|
|
621
|
-
const intermediateDocument = unpackProxyObject(intermediateDocuments[name], { depth: 1 });
|
|
622
|
-
const activeDocument = workspace.documents[name] ? unpackProxyObject(workspace.documents[name], { depth: 1 }) : void 0;
|
|
623
|
-
if (!originalDocument || !intermediateDocument || !activeDocument) {
|
|
624
|
-
return {
|
|
625
|
-
ok: false,
|
|
626
|
-
type: "CORRUPTED_STATE",
|
|
627
|
-
message: `Cannot rebase document '${name}': missing original, intermediate, or active document state`
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
const resolve = await withMeasurementAsync(
|
|
631
|
-
"loadDocument",
|
|
632
|
-
async () => await loadDocument({
|
|
633
|
-
...input,
|
|
634
|
-
fetch: input.fetch ?? workspaceProps?.fetch,
|
|
635
|
-
fileLoader: workspaceProps?.fileLoader
|
|
636
|
-
})
|
|
637
|
-
);
|
|
638
|
-
if (!resolve.ok || !isObject(resolve.data)) {
|
|
639
|
-
return {
|
|
640
|
-
ok: false,
|
|
641
|
-
type: "FETCH_FAILED",
|
|
642
|
-
message: `Failed to fetch document '${name}': request was not successful or returned invalid data`
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
const newHash = generateHash(resolve.raw);
|
|
646
|
-
if (activeDocument["x-scalar-original-document-hash"] === newHash) {
|
|
647
|
-
return {
|
|
648
|
-
ok: false,
|
|
649
|
-
type: "NO_CHANGES_DETECTED",
|
|
650
|
-
message: `No changes detected for document '${name}': document hash matches the active document`
|
|
651
|
-
};
|
|
652
|
-
}
|
|
653
|
-
const newDocumentOrigin = resolve.data;
|
|
654
|
-
overrides[name] = input.overrides ?? {};
|
|
655
|
-
extraDocumentConfigurations[name] = { fetch: input.fetch };
|
|
656
|
-
const changelogAA = diff(originalDocument, newDocumentOrigin);
|
|
657
|
-
if (changelogAA.length === 0) {
|
|
658
|
-
return {
|
|
659
|
-
ok: false,
|
|
660
|
-
type: "NO_CHANGES_DETECTED",
|
|
661
|
-
message: `No changes detected for document '${name}' after fetching the latest version.`
|
|
662
|
-
};
|
|
663
|
-
}
|
|
664
|
-
const changelogAB = diff(originalDocument, intermediateDocument);
|
|
665
|
-
const changesA = merge(changelogAA, changelogAB);
|
|
666
|
-
return {
|
|
667
|
-
ok: true,
|
|
668
|
-
conflicts: changesA.conflicts,
|
|
669
|
-
changes: changesA.diffs,
|
|
670
|
-
applyChanges: async (applyChangesInput) => {
|
|
671
|
-
const getNewIntermediateDocument = () => {
|
|
672
|
-
if ("resolvedConflicts" in applyChangesInput) {
|
|
673
|
-
const changesetA = changesA.diffs.concat(applyChangesInput.resolvedConflicts);
|
|
674
|
-
return apply(deepClone(originalDocument), changesetA);
|
|
591
|
+
get history() {
|
|
592
|
+
return history;
|
|
593
|
+
},
|
|
594
|
+
get auth() {
|
|
595
|
+
return auth;
|
|
596
|
+
},
|
|
597
|
+
update(key, value) {
|
|
598
|
+
preventPollution(key);
|
|
599
|
+
Object.assign(workspace, { [key]: value });
|
|
600
|
+
},
|
|
601
|
+
getEditableDocument,
|
|
602
|
+
getOriginalDocument,
|
|
603
|
+
getIntermediateDocument,
|
|
604
|
+
updateDocument(name, key, value) {
|
|
605
|
+
const currentDocument = workspace.documents[name === 'active' ? getActiveDocumentName() : name];
|
|
606
|
+
if (!currentDocument) {
|
|
607
|
+
return false;
|
|
675
608
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
609
|
+
preventPollution(key);
|
|
610
|
+
Object.assign(currentDocument, { [key]: value });
|
|
611
|
+
return true;
|
|
612
|
+
},
|
|
613
|
+
async replaceDocument(documentName, input) {
|
|
614
|
+
const currentDocument = workspace.documents[documentName];
|
|
615
|
+
if (!currentDocument) {
|
|
616
|
+
return console.error(`Document '${documentName}' does not exist in the workspace.`);
|
|
617
|
+
}
|
|
618
|
+
// Replace the whole document
|
|
619
|
+
await addInMemoryDocument({
|
|
620
|
+
name: documentName,
|
|
621
|
+
document: input,
|
|
622
|
+
// Preserve the current metadata
|
|
623
|
+
documentSource: currentDocument['x-scalar-original-source-url'],
|
|
624
|
+
documentHash: currentDocument['x-scalar-original-document-hash'],
|
|
625
|
+
meta: {
|
|
626
|
+
// Set the document as dirty
|
|
627
|
+
'x-scalar-is-dirty': true,
|
|
628
|
+
// Clear the navigation to trigger a rebuild
|
|
629
|
+
'x-scalar-navigation': undefined,
|
|
630
|
+
},
|
|
631
|
+
initialize: false,
|
|
632
|
+
});
|
|
633
|
+
},
|
|
634
|
+
resolve: (path) => {
|
|
635
|
+
const activeDocument = workspace.activeDocument;
|
|
636
|
+
const target = getValueAtPath(activeDocument, path);
|
|
637
|
+
if (!isObject(target)) {
|
|
638
|
+
console.error(`Invalid path provided for resolution. Path: [${path.join(', ')}]. Found value of type: ${typeof target}. Expected an object.`);
|
|
639
|
+
return Promise.resolve();
|
|
640
|
+
}
|
|
641
|
+
// Bundle the target document with the active document as root, resolving any external references
|
|
642
|
+
// and tracking resolution status through hooks
|
|
643
|
+
return bundle(target, {
|
|
644
|
+
root: activeDocument,
|
|
645
|
+
treeShake: false,
|
|
646
|
+
plugins: [fetchUrls(), loadingStatus(), externalValueResolver()],
|
|
647
|
+
urlMap: true,
|
|
648
|
+
visitedNodes: visitedNodesCache,
|
|
649
|
+
});
|
|
650
|
+
},
|
|
651
|
+
addDocument,
|
|
652
|
+
/**
|
|
653
|
+
* Deletes a document from the workspace and all associated data.
|
|
654
|
+
*
|
|
655
|
+
* This function removes the document and all related data structures.
|
|
656
|
+
* If the deleted document was active, it automatically selects the first remaining document.
|
|
657
|
+
*/
|
|
658
|
+
deleteDocument: (documentName) => {
|
|
659
|
+
// Check if the document exists before attempting deletion
|
|
660
|
+
if (!workspace.documents[documentName]) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
// Delete the document from the workspace (this will trigger change detection events)
|
|
664
|
+
delete workspace.documents[documentName];
|
|
665
|
+
// Clean up all associated data structures
|
|
666
|
+
delete originalDocuments[documentName];
|
|
667
|
+
delete intermediateDocuments[documentName];
|
|
668
|
+
delete overrides[documentName];
|
|
669
|
+
delete extraDocumentConfigurations[documentName];
|
|
670
|
+
history.clearDocumentHistory(documentName);
|
|
671
|
+
auth.clearDocumentAuth(documentName);
|
|
672
|
+
// Get remaining documents before deletion to properly set the active document
|
|
673
|
+
const remainingDocuments = Object.keys(workspace.documents);
|
|
674
|
+
const wasActiveDocument = workspace['x-scalar-active-document'] === documentName;
|
|
675
|
+
// Reset the active document to the first remaining one if the deleted document was the active one
|
|
676
|
+
if (wasActiveDocument) {
|
|
677
|
+
workspace['x-scalar-active-document'] = remainingDocuments[0] ?? undefined;
|
|
678
|
+
}
|
|
679
|
+
// Fire the deleteDocument event
|
|
680
|
+
fireWorkspaceChange({
|
|
681
|
+
type: 'deleteDocument',
|
|
682
|
+
documentName,
|
|
683
|
+
});
|
|
684
|
+
},
|
|
685
|
+
exportDocument,
|
|
686
|
+
exportActiveDocument: (format, minify) => exportDocument(getActiveDocumentName(), format, minify),
|
|
687
|
+
buildSidebar,
|
|
688
|
+
saveDocument,
|
|
689
|
+
promoteIntermediateToOriginal,
|
|
690
|
+
async revertDocumentChanges(documentName) {
|
|
691
|
+
const workspaceDocument = workspace.documents[documentName];
|
|
692
|
+
const intermediate = intermediateDocuments[documentName];
|
|
693
|
+
if (!workspaceDocument || !intermediate) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
await addInMemoryDocument({
|
|
697
|
+
name: documentName,
|
|
698
|
+
document: intermediate,
|
|
699
|
+
documentSource: workspaceDocument['x-scalar-original-source-url'],
|
|
700
|
+
documentHash: workspaceDocument['x-scalar-original-document-hash'],
|
|
701
|
+
initialize: false,
|
|
702
|
+
});
|
|
703
|
+
},
|
|
704
|
+
commitDocument(documentName) {
|
|
705
|
+
// TODO: Implement commit logic
|
|
706
|
+
console.warn(`Commit operation for document '${documentName}' is not implemented yet.`);
|
|
707
|
+
},
|
|
708
|
+
exportWorkspace() {
|
|
709
|
+
const { activeDocument: _, documents, ...meta } = unpackProxyObject(workspace);
|
|
710
|
+
return {
|
|
711
|
+
documents: {
|
|
712
|
+
...Object.fromEntries(Object.entries(documents).map(([name, doc]) => [
|
|
713
|
+
name,
|
|
714
|
+
// Get the raw document without any proxies
|
|
715
|
+
unpackProxyObject(doc),
|
|
716
|
+
])),
|
|
717
|
+
},
|
|
718
|
+
meta: unpackProxyObject(meta) ?? {},
|
|
719
|
+
originalDocuments: unpackProxyObject(originalDocuments),
|
|
720
|
+
intermediateDocuments: unpackProxyObject(intermediateDocuments),
|
|
721
|
+
overrides: unpackProxyObject(overrides),
|
|
722
|
+
history: history.export(),
|
|
723
|
+
auth: auth.export(),
|
|
724
|
+
};
|
|
725
|
+
},
|
|
726
|
+
loadWorkspace(input) {
|
|
727
|
+
safeAssign(workspace.documents, Object.fromEntries(Object.entries(input.documents).map(([name, doc]) => [
|
|
728
|
+
name,
|
|
729
|
+
createOverridesProxy(createMagicProxy(doc), {
|
|
730
|
+
overrides: input.overrides[name],
|
|
731
|
+
}),
|
|
732
|
+
])));
|
|
733
|
+
safeAssign(originalDocuments, input.originalDocuments);
|
|
734
|
+
safeAssign(intermediateDocuments, input.intermediateDocuments);
|
|
735
|
+
safeAssign(overrides, input.overrides);
|
|
736
|
+
safeAssign(workspace, input.meta);
|
|
737
|
+
history.load(input.history);
|
|
738
|
+
auth.load(input.auth);
|
|
739
|
+
},
|
|
740
|
+
importWorkspaceFromSpecification: (specification) => {
|
|
741
|
+
const { documents, overrides, info: _info, workspace: _workspaceVersion, ...meta } = specification;
|
|
742
|
+
// Assign workspace metadata
|
|
743
|
+
safeAssign(workspace, meta);
|
|
744
|
+
// Add workspace documents
|
|
745
|
+
return Promise.all(Object.entries(documents ?? {}).map(([name, doc]) => addDocument({ url: doc.$ref, name, overrides: overrides?.[name] })));
|
|
746
|
+
},
|
|
747
|
+
rebaseDocument: async (input) => {
|
|
748
|
+
const { name } = input;
|
|
749
|
+
// ---- Get the current documents
|
|
750
|
+
const originalDocument = unpackProxyObject(originalDocuments[name], { depth: 1 });
|
|
751
|
+
const intermediateDocument = unpackProxyObject(intermediateDocuments[name], { depth: 1 });
|
|
752
|
+
// raw version without any proxies
|
|
753
|
+
const activeDocument = workspace.documents[name]
|
|
754
|
+
? unpackProxyObject(workspace.documents[name], { depth: 1 })
|
|
755
|
+
: undefined;
|
|
756
|
+
if (!originalDocument || !intermediateDocument || !activeDocument) {
|
|
757
|
+
// If any required document state is missing, do nothing
|
|
758
|
+
return {
|
|
759
|
+
ok: false,
|
|
760
|
+
type: 'CORRUPTED_STATE',
|
|
761
|
+
message: `Cannot rebase document '${name}': missing original, intermediate, or active document state`,
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
// ---- Resolve input document
|
|
765
|
+
const resolve = await withMeasurementAsync('loadDocument', async () => await loadDocument({
|
|
766
|
+
...input,
|
|
767
|
+
fetch: input.fetch ?? workspaceProps?.fetch,
|
|
768
|
+
fileLoader: workspaceProps?.fileLoader,
|
|
769
|
+
}));
|
|
770
|
+
if (!resolve.ok || !isObject(resolve.data)) {
|
|
771
|
+
return {
|
|
772
|
+
ok: false,
|
|
773
|
+
type: 'FETCH_FAILED',
|
|
774
|
+
message: `Failed to fetch document '${name}': request was not successful or returned invalid data`,
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
// Compare document hashes to see if the document has changed
|
|
778
|
+
// When the hashes match, we can skip the rebase process
|
|
779
|
+
const newHash = generateHash(resolve.raw);
|
|
780
|
+
if (activeDocument['x-scalar-original-document-hash'] === newHash) {
|
|
781
|
+
return {
|
|
782
|
+
ok: false,
|
|
783
|
+
type: 'NO_CHANGES_DETECTED',
|
|
784
|
+
message: `No changes detected for document '${name}': document hash matches the active document`,
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
const newDocumentOrigin = resolve.data;
|
|
788
|
+
// ---- Override the configurations and metadata
|
|
789
|
+
overrides[name] = input.overrides ?? {};
|
|
790
|
+
extraDocumentConfigurations[name] = { fetch: input.fetch };
|
|
791
|
+
// ---- Get the new intermediate document
|
|
792
|
+
const changelogAA = diff(originalDocument, newDocumentOrigin);
|
|
793
|
+
// When there are no changes, we can return early since we don't need to do anything
|
|
794
|
+
// This is not supposed to happen due to the hash check above, but just in case
|
|
795
|
+
if (changelogAA.length === 0) {
|
|
796
|
+
return {
|
|
797
|
+
ok: false,
|
|
798
|
+
type: 'NO_CHANGES_DETECTED',
|
|
799
|
+
message: `No changes detected for document '${name}' after fetching the latest version.`,
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
const changelogAB = diff(originalDocument, intermediateDocument);
|
|
803
|
+
const changesA = merge(changelogAA, changelogAB);
|
|
804
|
+
return {
|
|
805
|
+
ok: true,
|
|
806
|
+
conflicts: changesA.conflicts,
|
|
807
|
+
changes: changesA.diffs,
|
|
808
|
+
applyChanges: async (applyChangesInput) => {
|
|
809
|
+
// Helper function to compute the new intermediate document based on resolved conflicts or a resolved document
|
|
810
|
+
const getNewIntermediateDocument = () => {
|
|
811
|
+
if ('resolvedConflicts' in applyChangesInput) {
|
|
812
|
+
const changesetA = changesA.diffs.concat(applyChangesInput.resolvedConflicts);
|
|
813
|
+
// Apply the merged changes (diffs + resolved conflicts) to the original document
|
|
814
|
+
return apply(deepClone(originalDocument), changesetA);
|
|
815
|
+
}
|
|
816
|
+
// If there are no resolved conflicts, use the provided resolved document
|
|
817
|
+
return applyChangesInput.resolvedDocument;
|
|
818
|
+
};
|
|
819
|
+
const newIntermediateDocument = getNewIntermediateDocument();
|
|
820
|
+
intermediateDocuments[name] = newIntermediateDocument;
|
|
821
|
+
// Update the original document
|
|
822
|
+
originalDocuments[name] = newDocumentOrigin;
|
|
823
|
+
// ---- Get the new active document
|
|
824
|
+
const changelogBA = diff(intermediateDocument, newIntermediateDocument);
|
|
825
|
+
const changelogBB = diff(intermediateDocument, activeDocument);
|
|
826
|
+
const changesB = merge(changelogBA, changelogBB);
|
|
827
|
+
// Auto-conflict resolution: pick only the changes from the first changeset
|
|
828
|
+
// TODO: In the future, implement smarter conflict resolution if needed
|
|
829
|
+
const changesetB = changesB.diffs.concat(changesB.conflicts.flatMap((it) => it[0]));
|
|
830
|
+
const newActiveDocument = coerceValue(OpenAPIDocumentSchemaStrict, apply(deepClone(newIntermediateDocument), changesetB));
|
|
831
|
+
// add the new active document to the workspace but don't re-initialize
|
|
832
|
+
await addInMemoryDocument({
|
|
833
|
+
...input,
|
|
834
|
+
document: {
|
|
835
|
+
...newActiveDocument,
|
|
836
|
+
// force regeneration of navigation
|
|
837
|
+
// when we are rebasing, we want to ensure that the navigation is always up to date
|
|
838
|
+
[extensions.document.navigation]: undefined,
|
|
839
|
+
},
|
|
840
|
+
documentSource: getDocumentSource(input),
|
|
841
|
+
// Update the original document hash
|
|
842
|
+
documentHash: generateHash(resolve.raw),
|
|
843
|
+
initialize: false,
|
|
844
|
+
});
|
|
845
|
+
},
|
|
846
|
+
};
|
|
847
|
+
},
|
|
848
|
+
};
|
|
711
849
|
};
|
|
712
|
-
|
|
850
|
+
// biome-ignore lint/performance/noBarrelFile: It's a package entry point
|
|
851
|
+
export { generateClientMutators } from './mutators/index.js';
|