@scalar/api-reference 1.62.4 → 1.62.6
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 +20 -0
- package/README.md +14 -0
- package/dist/blocks/scalar-info-block/components/SpecificationVersion.vue.d.ts.map +1 -1
- package/dist/blocks/scalar-info-block/components/SpecificationVersion.vue.js.map +1 -1
- package/dist/blocks/scalar-info-block/components/SpecificationVersion.vue.script.js +2 -1
- package/dist/blocks/scalar-info-block/components/SpecificationVersion.vue.script.js.map +1 -1
- package/dist/browser/chunks/{AgentScalarChatInterface-uEJhFXkB.js → AgentScalarChatInterface-Bd03__M5.js} +4 -4
- package/dist/browser/chunks/ScalarTextInput.vue-Cg0WEkPQ.js +27 -0
- package/dist/browser/chunks/basic-shape-diamond.svg-BTEOT1FL.js +1 -1
- package/dist/browser/chunks/basic-shape-hexagon.svg-BSPzaCgF.js +1 -1
- package/dist/browser/chunks/basic-shape-primary-circle-ellipse-round.svg-DKmZ8UUU.js +1 -1
- package/dist/browser/chunks/basic-shape-primary-square-rectangle.svg-n2YRykHD.js +1 -1
- package/dist/browser/chunks/basic-shape-shield.svg-Bec9cJDA.js +1 -1
- package/dist/browser/chunks/browser-67K6wmG9.js +1 -1
- package/dist/browser/chunks/computer-device-desktop-monitor.svg-m6j893Hb.js +1 -1
- package/dist/browser/chunks/computer-device-desktop.svg-Dk1VVO7k.js +1 -1
- package/dist/browser/chunks/computer-device-laptop.svg-BNGVUCYS.js +1 -1
- package/dist/browser/chunks/computer-device-mobile-phone-android-samsung-back.svg-CC4UUTFw.js +1 -1
- package/dist/browser/chunks/computer-device-mobile-phone-android-samsung.svg-D8ExpMpL.js +1 -1
- package/dist/browser/chunks/computer-device-mobile-phone-iphone-x-back.svg-CLm7sgMM.js +1 -1
- package/dist/browser/chunks/computer-device-mobile-phone-iphone-x.svg-Xrttg6_7.js +1 -1
- package/dist/browser/chunks/computer-device-mobile-tablet-touch.svg-DQ2oTvOG.js +1 -1
- package/dist/browser/chunks/computer-device-mobile-tablet.svg-DCr0XlRc.js +1 -1
- package/dist/browser/chunks/computer-device-network-ethernet-cat6.svg-AK9l0iOl.js +1 -1
- package/dist/browser/chunks/computer-device-network-lan-www.svg-gUIdFBlE.js +1 -1
- package/dist/browser/chunks/computer-device-network-wifi-connection.svg-Be-EMm4e.js +1 -1
- package/dist/browser/chunks/computer-device-network-wifi-router.svg-Dc2VzyqK.js +1 -1
- package/dist/browser/chunks/ecology-science-erlenmeyer-flask.svg-DHgy7Jfd.js +1 -1
- package/dist/browser/chunks/image-flash-lightning.svg-EETR_y1q.js +1 -1
- package/dist/browser/chunks/image-picture-flower.svg-DN1AYzJU.js +1 -1
- package/dist/browser/chunks/interface-alert-exclamation-diamond.svg-C5NozMV_.js +1 -1
- package/dist/browser/chunks/interface-alert-exclamation-triangle-warning.svg-DaAg2fF5.js +1 -1
- package/dist/browser/chunks/interface-alert-information-circle.svg-ckkkt9Mv.js +1 -1
- package/dist/browser/chunks/interface-award-crown.svg-pjvn9hY-.js +1 -1
- package/dist/browser/chunks/interface-bookmark-tag.svg-r2r-lQq4.js +1 -1
- package/dist/browser/chunks/interface-bookmark.svg-BK9HKQBT.js +1 -1
- package/dist/browser/chunks/interface-calendar-date-one.svg-DNnF0M1I.js +1 -1
- package/dist/browser/chunks/interface-content-book-open-pages.svg-CQj_w9Vp.js +1 -1
- package/dist/browser/chunks/interface-content-book-page.svg-DppoeJRe.js +1 -1
- package/dist/browser/chunks/interface-content-file.svg-CvcD7OiY.js +1 -1
- package/dist/browser/chunks/interface-content-folder.svg-BmtS5I9A.js +1 -1
- package/dist/browser/chunks/interface-copy-clipboard.svg-Be6EI50X.js +1 -1
- package/dist/browser/chunks/interface-edit-attachment.svg-G5TKzpWX.js +1 -1
- package/dist/browser/chunks/interface-edit-binocular.svg-jzL-DvGB.js +1 -1
- package/dist/browser/chunks/interface-edit-magic-wand.svg-BOTlymvp.js +1 -1
- package/dist/browser/chunks/interface-edit-tool-paint-roller.svg-s9dwryx7.js +1 -1
- package/dist/browser/chunks/interface-edit-tool-pencil.svg-C7v0dbB3.js +1 -1
- package/dist/browser/chunks/interface-favorite-award.svg-CYHUgUG0.js +1 -1
- package/dist/browser/chunks/interface-favorite-flag.svg-C10zlKOb.js +1 -1
- package/dist/browser/chunks/interface-favorite-heart.svg-eoduXxlz.js +1 -1
- package/dist/browser/chunks/interface-favorite-star.svg-BSmxJetr.js +1 -1
- package/dist/browser/chunks/interface-favorite-stars-sparkles.svg-JTFLj3Ls.js +1 -1
- package/dist/browser/chunks/interface-hierarchy-flowchart.svg-Cg_Wy0kN.js +1 -1
- package/dist/browser/chunks/interface-home-house.svg-DytoGRnv.js +1 -1
- package/dist/browser/chunks/interface-hyperlink.svg-CuEfmmQI.js +1 -1
- package/dist/browser/chunks/interface-lighting-brightness.svg-DSK0h_Pa.js +1 -1
- package/dist/browser/chunks/interface-lock-closed.svg-Cn6TaLYv.js +1 -1
- package/dist/browser/chunks/interface-lock-open-unlock.svg-CVkG5-9W.js +1 -1
- package/dist/browser/chunks/interface-login-key.svg-y-cwwXjN.js +1 -1
- package/dist/browser/chunks/interface-search.svg-S-puVFJB.js +1 -1
- package/dist/browser/chunks/interface-setting-cog.svg-CTH4_lLg.js +1 -1
- package/dist/browser/chunks/interface-share-megaphone-bullhorn.svg-Pbp39MyC.js +1 -1
- package/dist/browser/chunks/interface-share-rocket.svg-BgCsIRZO.js +1 -1
- package/dist/browser/chunks/interface-share-satellite.svg-DAMN9d5G.js +1 -1
- package/dist/browser/chunks/interface-share-space-ship.svg-Dt-Mejxk.js +1 -1
- package/dist/browser/chunks/interface-share.svg-vFYTXSti.js +1 -1
- package/dist/browser/chunks/interface-signal-square.svg-DNG4LpAe.js +1 -1
- package/dist/browser/chunks/interface-time-clock-circle.svg-Bpz-nLxo.js +1 -1
- package/dist/browser/chunks/interface-time-hour-glass.svg-D7agfKsH.js +1 -1
- package/dist/browser/chunks/interface-users-multiple.svg-CkPJBBnG.js +1 -1
- package/dist/browser/chunks/interface-weather-moon.svg-CaTZFMPO.js +1 -1
- package/dist/browser/chunks/mail-chat-bubble-square.svg-DC8RIihp.js +1 -1
- package/dist/browser/chunks/mail-send-email-paper-airplane.svg-D8OLs9jz.js +1 -1
- package/dist/browser/chunks/mail-send-envelope.svg-CV33h5Sx.js +1 -1
- package/dist/browser/chunks/{modal-D09WtmIH.js → modal-CssEjGY3.js} +2 -2
- package/dist/browser/chunks/modal-DXAlZ4Y8.js +99 -0
- package/dist/browser/chunks/money-cashier-receipt.svg-CJL8C2gs.js +1 -1
- package/dist/browser/chunks/money-currency-dollar-pay.svg-VdBplCTx.js +1 -1
- package/dist/browser/chunks/money-graph-arrow-increase.svg-DcRov3zk.js +1 -1
- package/dist/browser/chunks/money-graph-bar-chart-increase.svg-Cc7KxjX-.js +1 -1
- package/dist/browser/chunks/nature-ecology-leaf.svg-DR3cTZxt.js +1 -1
- package/dist/browser/chunks/phone-telephone.svg-DyY0gUlk.js +1 -1
- package/dist/browser/chunks/programming-bug.svg-BHLT-Qhp.js +1 -1
- package/dist/browser/chunks/programming-cloud.svg-B6YW33ba.js +1 -1
- package/dist/browser/chunks/programming-computer-database-server.svg-B6o5Sdn-.js +1 -1
- package/dist/browser/chunks/programming-computer-database.svg-Cl7OVtpJ.js +1 -1
- package/dist/browser/chunks/programming-module-four-layout.svg-BQrerL1x.js +1 -1
- package/dist/browser/chunks/programming-module-three.svg-B2KTQzBM.js +1 -1
- package/dist/browser/chunks/programming-module.svg-CiID7kPm.js +1 -1
- package/dist/browser/chunks/programming-script-code.svg-D1M5rtkN.js +1 -1
- package/dist/browser/chunks/runtime-core.esm-bundler-Byrbbu5Q.js +1 -1
- package/dist/browser/chunks/shopping-cart.svg-BEegiIR9.js +1 -1
- package/dist/browser/chunks/shopping-gift-present.svg-BUHwuLix.js +1 -1
- package/dist/browser/chunks/shopping-shipping-box-parcel-package.svg-D6fxWdYx.js +1 -1
- package/dist/browser/chunks/tag-new-circle.svg-jsg1bGs0.js +1 -1
- package/dist/browser/chunks/travel-map-earth-globe.svg-ClbMiU-f.js +1 -1
- package/dist/browser/chunks/vendor-DfF1NNVy.js +306 -0
- package/dist/browser/standalone.esm.js +20 -20
- package/dist/browser/standalone.js +246 -246
- package/dist/components/ApiReference.vue.d.ts.map +1 -1
- package/dist/components/ApiReference.vue.js +1 -1
- package/dist/components/ApiReference.vue.js.map +1 -1
- package/dist/components/ApiReference.vue.script.js +24 -20
- package/dist/components/ApiReference.vue.script.js.map +1 -1
- package/dist/components/Content/Auth/Auth.vue.d.ts.map +1 -1
- package/dist/components/Content/Auth/Auth.vue.js.map +1 -1
- package/dist/components/Content/Auth/Auth.vue.script.js +26 -4
- package/dist/components/Content/Auth/Auth.vue.script.js.map +1 -1
- package/dist/components/Content/Content.vue.d.ts.map +1 -1
- package/dist/components/Content/Content.vue.js.map +1 -1
- package/dist/components/Content/Content.vue.script.js +14 -2
- package/dist/components/Content/Content.vue.script.js.map +1 -1
- package/dist/components/Content/Schema/SchemaProperty.vue.d.ts.map +1 -1
- package/dist/components/Content/Schema/SchemaProperty.vue.js +1 -1
- package/dist/components/Content/Schema/SchemaProperty.vue.js.map +1 -1
- package/dist/components/Content/Schema/SchemaProperty.vue.script.js +1 -1
- package/dist/components/Content/Schema/SchemaProperty.vue.script.js.map +1 -1
- package/dist/components/Content/Schema/helpers/optimize-value-for-display.d.ts.map +1 -1
- package/dist/components/Content/Schema/helpers/optimize-value-for-display.js +36 -9
- package/dist/components/Content/Schema/helpers/optimize-value-for-display.js.map +1 -1
- package/dist/features/Operation/components/OperationResponses.vue.js.map +1 -1
- package/dist/features/Operation/components/OperationResponses.vue.script.js +1 -1
- package/dist/features/Operation/components/OperationResponses.vue.script.js.map +1 -1
- package/dist/features/Operation/components/ParameterListItem.vue.d.ts.map +1 -1
- package/dist/features/Operation/components/ParameterListItem.vue.js +1 -1
- package/dist/features/Operation/components/ParameterListItem.vue.js.map +1 -1
- package/dist/features/Operation/components/ParameterListItem.vue.script.js +21 -3
- package/dist/features/Operation/components/ParameterListItem.vue.script.js.map +1 -1
- package/dist/helpers/id-routing.d.ts.map +1 -1
- package/dist/helpers/id-routing.js +23 -6
- package/dist/helpers/id-routing.js.map +1 -1
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/plugin-manager.d.ts +17 -3
- package/dist/plugins/plugin-manager.d.ts.map +1 -1
- package/dist/plugins/plugin-manager.js +20 -3
- package/dist/plugins/plugin-manager.js.map +1 -1
- package/dist/style.css +77 -68
- package/dist/vue-styles.css +68 -68
- package/package.json +13 -13
- package/dist/browser/chunks/ScalarTextInput.vue-DfHAfVG5.js +0 -27
- package/dist/browser/chunks/modal-VSnkfbyL.js +0 -99
- package/dist/browser/chunks/vendor-C5HmjoOQ.js +0 -306
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ParameterListItem.vue.js","names":[],"sources":["../../../../src/features/Operation/components/ParameterListItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'\nimport {\n ScalarMarkdown,\n ScalarMarkdownSummary,\n} from '@scalar/components/markdown'\nimport { ScalarWrappingText } from '@scalar/components/wrapping-text'\nimport { ScalarIconCaretRight } from '@scalar/icons'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport type {\n OpenApiDocument,\n ParameterObject,\n ResponseObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref } from 'vue'\n\nimport { getRefName } from '@/components/Content/Schema/helpers/get-ref-name'\nimport SchemaProperty from '@/components/Content/Schema/SchemaProperty.vue'\nimport type { OperationProps } from '@/features/Operation/Operation.vue'\n\nimport ContentTypeSelect from './ContentTypeSelect.vue'\nimport Headers from './Headers.vue'\nimport { getParameterExamples } from './helpers/get-parameter-examples'\n\nconst { name, parameter, options, collapsableItems, document } = defineProps<{\n parameter: ParameterObject | ResponseObject\n name: string\n breadcrumb?: string[]\n eventBus: WorkspaceEventBus | null\n collapsableItems?: boolean\n /** The document the operation belongs to, used to resolve schema references for display */\n document?: OpenApiDocument\n options: Pick<\n OperationProps['options'],\n | 'hideModels'\n | 'orderRequiredPropertiesFirst'\n | 'orderSchemaPropertiesBy'\n | 'expandAllSchemaProperties'\n >\n}>()\n\n/** Whether the markdown summary is being truncated */\nconst truncated = ref(false)\n\n/** Responses and params may both have a schema */\nconst schema = computed<SchemaObject | null>(() =>\n 'schema' in parameter && parameter.schema\n ? getResolvedRef(parameter.schema)\n : null,\n)\n\n/** Response and params may both have content */\nconst content = computed(() => {\n if (!('content' in parameter) || !parameter.content) {\n return null\n }\n const keys = Object.keys(parameter.content)\n if (keys.length === 0) {\n return null\n }\n return parameter.content\n})\n\nconst selectedContentType = ref<string>(\n Object.keys(content.value || {})[0] ?? '',\n)\n\n/** Response headers */\nconst headers = computed<ResponseObject['headers'] | null>(() =>\n 'headers' in parameter && parameter.headers ? parameter.headers : null,\n)\n\n/** Raw schema (possibly with $ref) for the selected content type or param. */\nconst baseSchema = computed(() =>\n content.value\n ? content.value?.[selectedContentType.value]?.schema\n : 'schema' in parameter && parameter.schema\n ? parameter.schema\n : null,\n)\n\n/** When the schema is a $ref, preserve its name so the UI can show the ref name instead of just the type. */\nconst schemaModelName = computed(() => {\n const raw = baseSchema.value\n if (!raw) {\n return null\n }\n\n if ('$ref' in raw) {\n return getRefName(raw.$ref)\n }\n\n return null\n})\n\n/** Computed value from the combined schema param and content param */\nconst value = computed(() => {\n const base = baseSchema.value\n const resolvedBase = content.value ? getResolvedRef(base) : schema.value\n\n const deprecated =\n 'deprecated' in parameter ? parameter.deprecated : schema.value?.deprecated\n\n /** Combine param/content/schema examples while ignoring undefined values. */\n const examples = getParameterExamples({\n parameter,\n schemaExamples: schema.value?.examples,\n contentExamples: content.value?.[selectedContentType.value]?.examples,\n })\n\n return {\n ...resolvedBase,\n deprecated: deprecated,\n examples,\n } as SchemaObject\n})\n\n/**\n * Determines whether this parameter item should be rendered as a collapsible disclosure.\n * Only collapses when collapsableItems is enabled and the parameter has additional\n * content to display (content types, headers, or schema details).\n */\nconst shouldCollapse = computed<boolean>(() =>\n Boolean(content.value || headers.value || schema.value || truncated.value),\n)\n</script>\n<template>\n <li class=\"parameter-item group/parameter-item\">\n <Disclosure v-slot=\"{ open }\">\n <component\n :is=\"shouldCollapse ? DisclosureButton : 'div'\"\n v-if=\"collapsableItems\"\n class=\"parameter-item-trigger\"\n :class=\"{ 'parameter-item-trigger-open': open }\">\n <div class=\"parameter-item-name min-w-0\">\n <ScalarIconCaretRight\n v-if=\"shouldCollapse\"\n class=\"parameter-item-icon size-3 transition-transform duration-100\"\n :class=\"{ 'rotate-90': open }\"\n weight=\"bold\" />\n <div>\n <ScalarWrappingText\n preset=\"property\"\n :text=\"name\" />\n </div>\n </div>\n <ScalarMarkdownSummary\n v-if=\"!open && parameter.description\"\n v-model:truncated=\"truncated\"\n class=\"parameter-item-description-summary min-w-0 flex-1\"\n controlled\n :value=\"parameter.description\" />\n <div\n v-else\n class=\"flex-1\" />\n </component>\n <DisclosurePanel\n class=\"parameter-item-container parameter-item-container-markdown\"\n :static=\"!collapsableItems\">\n <ScalarMarkdown\n v-if=\"collapsableItems && parameter.description\"\n class=\"parameter-item-description\"\n :value=\"parameter.description\" />\n <!-- Headers -->\n <Headers\n v-if=\"headers\"\n :breadcrumb=\"breadcrumb\"\n :document=\"document\"\n :eventBus=\"eventBus\"\n :expandAllSchemaProperties=\"options.expandAllSchemaProperties\"\n :headers=\"headers\"\n :orderRequiredPropertiesFirst=\"options.orderRequiredPropertiesFirst\"\n :orderSchemaPropertiesBy=\"options.orderSchemaPropertiesBy\" />\n\n <!-- Schema -->\n <SchemaProperty\n is=\"div\"\n :breadcrumb=\"breadcrumb\"\n compact\n :description=\"collapsableItems ? '' : parameter.description\"\n :eventBus=\"eventBus\"\n :hideWriteOnly=\"true\"\n :modelName=\"schemaModelName\"\n :name=\"collapsableItems ? '' : name\"\n :noncollapsible=\"true\"\n :options=\"{\n hideWriteOnly: true,\n orderRequiredPropertiesFirst: options.orderRequiredPropertiesFirst,\n orderSchemaPropertiesBy: options.orderSchemaPropertiesBy,\n expandAllSchemaProperties: options.expandAllSchemaProperties,\n document,\n }\"\n :required=\"'required' in parameter && parameter.required\"\n :schema=\"value\" />\n </DisclosurePanel>\n <div\n v-if=\"shouldCollapse && content\"\n class=\"absolute top-[calc(10px+0.5lh)] right-0 z-0 flex -translate-y-1/2 items-center text-base\"\n :class=\"{\n 'opacity-0 group-focus-within/parameter-item:opacity-100 group-hover/parameter-item:opacity-100':\n !open,\n }\">\n <div\n class=\"from-b-1 absolute inset-y-0 -left-6 -z-1 w-8 bg-linear-to-l from-40% to-transparent\" />\n <ContentTypeSelect\n v-model=\"selectedContentType\"\n :content=\"content\" />\n </div>\n </Disclosure>\n </li>\n</template>\n\n<style scoped>\n.parameter-item {\n display: flex;\n flex-direction: column;\n position: relative;\n border-top: var(--scalar-border-width) solid var(--scalar-border-color);\n}\n\n.parameter-item:last-of-type .parameter-schema {\n padding-bottom: 0;\n}\n\n.parameter-item-container {\n padding: 0;\n}\n\n.parameter-item-headers {\n border: var(--scalar-border-width) solid var(--scalar-border-color);\n}\n\n.parameter-item-name {\n position: relative;\n font-weight: var(--scalar-bold);\n font-size: var(--scalar-font-size-4);\n font-family: var(--scalar-font-code);\n color: var(--scalar-color-1);\n overflow-wrap: break-word;\n}\n\n.parameter-item-description,\n.parameter-item-description-summary {\n font-size: var(--scalar-mini);\n color: var(--scalar-color-2);\n}\n\n.parameter-item-description-summary.parameter-item-description-summary > * {\n --markdown-line-height: var(--scalar-line-height-5);\n}\n\n/* Match font size of markdown for property-detail-value since first child within accordian is displayed as if it were in the markdown section */\n.parameter-item-trigger\n ~ .parameter-item-container\n :deep(.property--level-0 > .property-heading .property-detail-value) {\n font-size: var(--scalar-micro);\n}\n\n.parameter-item-required-optional {\n color: var(--scalar-color-2);\n font-weight: var(--scalar-semibold);\n margin-right: 6px;\n position: relative;\n}\n\n.parameter-item--required {\n text-transform: uppercase;\n font-size: var(--scalar-micro);\n font-weight: var(--scalar-semibold);\n color: var(--scalar-color-orange);\n}\n\n.parameter-item-description {\n margin-top: 6px;\n font-size: var(--scalar-small);\n color: var(--scalar-color-2);\n line-height: 1.4;\n}\n\n.parameter-item-description :deep(p) {\n margin-top: 4px;\n font-size: var(--scalar-small);\n color: var(--scalar-color-2);\n}\n\n.parameter-schema {\n padding-bottom: 9px;\n margin-top: 3px;\n}\n\n.parameter-item-trigger {\n display: flex;\n align-items: baseline;\n line-height: var(--scalar-line-height-5);\n gap: 6px;\n flex-wrap: wrap;\n padding: 10px 0;\n outline: none;\n}\n\n.parameter-item-trigger-open {\n padding-bottom: 0;\n}\n\n.parameter-item-icon {\n color: var(--scalar-color-3);\n left: -19px;\n top: 0.5lh;\n translate: 0 -50%;\n position: absolute;\n}\n\n.parameter-item-trigger:hover .parameter-item-icon,\n.parameter-item-trigger:focus-visible .parameter-item-icon {\n color: var(--scalar-color-1);\n}\n\n.parameter-item-trigger:focus-visible .parameter-item-icon {\n outline: 1px solid var(--scalar-color-accent);\n outline-offset: 2px;\n border-radius: var(--scalar-radius);\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ParameterListItem.vue.js","names":[],"sources":["../../../../src/features/Operation/components/ParameterListItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'\nimport {\n ScalarMarkdown,\n ScalarMarkdownSummary,\n} from '@scalar/components/markdown'\nimport { ScalarWrappingText } from '@scalar/components/wrapping-text'\nimport { ScalarIconCaretRight } from '@scalar/icons'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport type {\n OpenApiDocument,\n ParameterObject,\n ResponseObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref } from 'vue'\n\nimport { getRefName } from '@/components/Content/Schema/helpers/get-ref-name'\nimport SchemaProperty from '@/components/Content/Schema/SchemaProperty.vue'\nimport type { OperationProps } from '@/features/Operation/Operation.vue'\nimport { scrollTargetId } from '@/helpers/lazy-bus'\n\nimport ContentTypeSelect from './ContentTypeSelect.vue'\nimport Headers from './Headers.vue'\nimport { getParameterExamples } from './helpers/get-parameter-examples'\n\nconst { name, parameter, options, collapsableItems, breadcrumb, document } =\n defineProps<{\n parameter: ParameterObject | ResponseObject\n name: string\n breadcrumb?: string[]\n eventBus: WorkspaceEventBus | null\n collapsableItems?: boolean\n /** The document the operation belongs to, used to resolve schema references for display */\n document?: OpenApiDocument\n options: Pick<\n OperationProps['options'],\n | 'hideModels'\n | 'orderRequiredPropertiesFirst'\n | 'orderSchemaPropertiesBy'\n | 'expandAllSchemaProperties'\n >\n }>()\n\n/** Whether the markdown summary is being truncated */\nconst truncated = ref(false)\n\n/** Responses and params may both have a schema */\nconst schema = computed<SchemaObject | null>(() =>\n 'schema' in parameter && parameter.schema\n ? getResolvedRef(parameter.schema)\n : null,\n)\n\n/** Response and params may both have content */\nconst content = computed(() => {\n if (!('content' in parameter) || !parameter.content) {\n return null\n }\n const keys = Object.keys(parameter.content)\n if (keys.length === 0) {\n return null\n }\n return parameter.content\n})\n\nconst selectedContentType = ref<string>(\n Object.keys(content.value || {})[0] ?? '',\n)\n\n/** Response headers */\nconst headers = computed<ResponseObject['headers'] | null>(() =>\n 'headers' in parameter && parameter.headers ? parameter.headers : null,\n)\n\n/** Raw schema (possibly with $ref) for the selected content type or param. */\nconst baseSchema = computed(() =>\n content.value\n ? content.value?.[selectedContentType.value]?.schema\n : 'schema' in parameter && parameter.schema\n ? parameter.schema\n : null,\n)\n\n/** When the schema is a $ref, preserve its name so the UI can show the ref name instead of just the type. */\nconst schemaModelName = computed(() => {\n const raw = baseSchema.value\n if (!raw) {\n return null\n }\n\n if ('$ref' in raw) {\n return getRefName(raw.$ref)\n }\n\n return null\n})\n\n/** Computed value from the combined schema param and content param */\nconst value = computed(() => {\n const base = baseSchema.value\n const resolvedBase = content.value ? getResolvedRef(base) : schema.value\n\n const deprecated =\n 'deprecated' in parameter ? parameter.deprecated : schema.value?.deprecated\n\n /** Combine param/content/schema examples while ignoring undefined values. */\n const examples = getParameterExamples({\n parameter,\n schemaExamples: schema.value?.examples,\n contentExamples: content.value?.[selectedContentType.value]?.examples,\n })\n\n return {\n ...resolvedBase,\n deprecated: deprecated,\n examples,\n } as SchemaObject\n})\n\n/**\n * Determines whether this parameter item should be rendered as a collapsible disclosure.\n * Only collapses when collapsableItems is enabled and the parameter has additional\n * content to display (content types, headers, or schema details).\n */\nconst shouldCollapse = computed<boolean>(() =>\n Boolean(content.value || headers.value || schema.value || truncated.value),\n)\n\n/**\n * The breadcrumb passed to the schema. Collapsible items (responses) render their\n * schema without a name to avoid a duplicate heading, so we push the item name\n * (e.g. the status code) onto the breadcrumb here to keep property anchors unique.\n */\nconst schemaBreadcrumb = computed<string[] | undefined>(() =>\n collapsableItems && breadcrumb && name ? [...breadcrumb, name] : breadcrumb,\n)\n\n/**\n * Whether a deep link points at a property inside this collapsed item. When it\n * does, the disclosure opens on mount so a fresh navigation can render the target\n * and scroll it into view (mirrors how collapsible schema disclosures behave).\n */\nconst isOnScrollTargetPath = computed<boolean>(() => {\n const path = schemaBreadcrumb.value?.join('.')\n if (!path) {\n return false\n }\n const target = scrollTargetId.value\n return target === path || target.startsWith(`${path}.`)\n})\n</script>\n<template>\n <li class=\"parameter-item group/parameter-item\">\n <Disclosure\n v-slot=\"{ open }\"\n :defaultOpen=\"isOnScrollTargetPath\">\n <component\n :is=\"shouldCollapse ? DisclosureButton : 'div'\"\n v-if=\"collapsableItems\"\n class=\"parameter-item-trigger\"\n :class=\"{ 'parameter-item-trigger-open': open }\">\n <div class=\"parameter-item-name min-w-0\">\n <ScalarIconCaretRight\n v-if=\"shouldCollapse\"\n class=\"parameter-item-icon size-3 transition-transform duration-100\"\n :class=\"{ 'rotate-90': open }\"\n weight=\"bold\" />\n <div>\n <ScalarWrappingText\n preset=\"property\"\n :text=\"name\" />\n </div>\n </div>\n <ScalarMarkdownSummary\n v-if=\"!open && parameter.description\"\n v-model:truncated=\"truncated\"\n class=\"parameter-item-description-summary min-w-0 flex-1\"\n controlled\n :value=\"parameter.description\" />\n <div\n v-else\n class=\"flex-1\" />\n </component>\n <DisclosurePanel\n class=\"parameter-item-container parameter-item-container-markdown\"\n :static=\"!collapsableItems\">\n <ScalarMarkdown\n v-if=\"collapsableItems && parameter.description\"\n class=\"parameter-item-description\"\n :value=\"parameter.description\" />\n <!-- Headers -->\n <Headers\n v-if=\"headers\"\n :breadcrumb=\"breadcrumb\"\n :document=\"document\"\n :eventBus=\"eventBus\"\n :expandAllSchemaProperties=\"options.expandAllSchemaProperties\"\n :headers=\"headers\"\n :orderRequiredPropertiesFirst=\"options.orderRequiredPropertiesFirst\"\n :orderSchemaPropertiesBy=\"options.orderSchemaPropertiesBy\" />\n\n <!-- Schema -->\n <SchemaProperty\n is=\"div\"\n :breadcrumb=\"schemaBreadcrumb\"\n compact\n :description=\"collapsableItems ? '' : parameter.description\"\n :eventBus=\"eventBus\"\n :hideWriteOnly=\"true\"\n :modelName=\"schemaModelName\"\n :name=\"collapsableItems ? '' : name\"\n :noncollapsible=\"true\"\n :options=\"{\n hideWriteOnly: true,\n orderRequiredPropertiesFirst: options.orderRequiredPropertiesFirst,\n orderSchemaPropertiesBy: options.orderSchemaPropertiesBy,\n expandAllSchemaProperties: options.expandAllSchemaProperties,\n document,\n }\"\n :required=\"'required' in parameter && parameter.required\"\n :schema=\"value\" />\n </DisclosurePanel>\n <div\n v-if=\"shouldCollapse && content\"\n class=\"absolute top-[calc(10px+0.5lh)] right-0 z-0 flex -translate-y-1/2 items-center text-base\"\n :class=\"{\n 'opacity-0 group-focus-within/parameter-item:opacity-100 group-hover/parameter-item:opacity-100':\n !open,\n }\">\n <div\n class=\"from-b-1 absolute inset-y-0 -left-6 -z-1 w-8 bg-linear-to-l from-40% to-transparent\" />\n <ContentTypeSelect\n v-model=\"selectedContentType\"\n :content=\"content\" />\n </div>\n </Disclosure>\n </li>\n</template>\n\n<style scoped>\n.parameter-item {\n display: flex;\n flex-direction: column;\n position: relative;\n border-top: var(--scalar-border-width) solid var(--scalar-border-color);\n}\n\n.parameter-item:last-of-type .parameter-schema {\n padding-bottom: 0;\n}\n\n.parameter-item-container {\n padding: 0;\n}\n\n.parameter-item-headers {\n border: var(--scalar-border-width) solid var(--scalar-border-color);\n}\n\n.parameter-item-name {\n position: relative;\n font-weight: var(--scalar-bold);\n font-size: var(--scalar-font-size-4);\n font-family: var(--scalar-font-code);\n color: var(--scalar-color-1);\n overflow-wrap: break-word;\n}\n\n.parameter-item-description,\n.parameter-item-description-summary {\n font-size: var(--scalar-mini);\n color: var(--scalar-color-2);\n}\n\n.parameter-item-description-summary.parameter-item-description-summary > * {\n --markdown-line-height: var(--scalar-line-height-5);\n}\n\n/* Match font size of markdown for property-detail-value since first child within accordian is displayed as if it were in the markdown section */\n.parameter-item-trigger\n ~ .parameter-item-container\n :deep(.property--level-0 > .property-heading .property-detail-value) {\n font-size: var(--scalar-micro);\n}\n\n.parameter-item-required-optional {\n color: var(--scalar-color-2);\n font-weight: var(--scalar-semibold);\n margin-right: 6px;\n position: relative;\n}\n\n.parameter-item--required {\n text-transform: uppercase;\n font-size: var(--scalar-micro);\n font-weight: var(--scalar-semibold);\n color: var(--scalar-color-orange);\n}\n\n.parameter-item-description {\n margin-top: 6px;\n font-size: var(--scalar-small);\n color: var(--scalar-color-2);\n line-height: 1.4;\n}\n\n.parameter-item-description :deep(p) {\n margin-top: 4px;\n font-size: var(--scalar-small);\n color: var(--scalar-color-2);\n}\n\n.parameter-schema {\n padding-bottom: 9px;\n margin-top: 3px;\n}\n\n.parameter-item-trigger {\n display: flex;\n align-items: baseline;\n line-height: var(--scalar-line-height-5);\n gap: 6px;\n flex-wrap: wrap;\n padding: 10px 0;\n outline: none;\n}\n\n.parameter-item-trigger-open {\n padding-bottom: 0;\n}\n\n.parameter-item-icon {\n color: var(--scalar-color-3);\n left: -19px;\n top: 0.5lh;\n translate: 0 -50%;\n position: absolute;\n}\n\n.parameter-item-trigger:hover .parameter-item-icon,\n.parameter-item-trigger:focus-visible .parameter-item-icon {\n color: var(--scalar-color-1);\n}\n\n.parameter-item-trigger:focus-visible .parameter-item-icon {\n outline: 1px solid var(--scalar-color-accent);\n outline-offset: 2px;\n border-radius: var(--scalar-radius);\n}\n</style>\n"],"mappings":""}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { scrollTargetId } from "../../../helpers/lazy-bus.js";
|
|
1
2
|
import { getRefName } from "../../../components/Content/Schema/helpers/get-ref-name.js";
|
|
2
3
|
import SchemaProperty_default from "../../../components/Content/Schema/SchemaProperty.vue.js";
|
|
3
4
|
import ContentTypeSelect_default from "./ContentTypeSelect.vue.js";
|
|
@@ -73,8 +74,25 @@ var ParameterListItem_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
|
|
|
73
74
|
* content to display (content types, headers, or schema details).
|
|
74
75
|
*/
|
|
75
76
|
const shouldCollapse = computed(() => Boolean(content.value || headers.value || schema.value || truncated.value));
|
|
77
|
+
/**
|
|
78
|
+
* The breadcrumb passed to the schema. Collapsible items (responses) render their
|
|
79
|
+
* schema without a name to avoid a duplicate heading, so we push the item name
|
|
80
|
+
* (e.g. the status code) onto the breadcrumb here to keep property anchors unique.
|
|
81
|
+
*/
|
|
82
|
+
const schemaBreadcrumb = computed(() => __props.collapsableItems && __props.breadcrumb && __props.name ? [...__props.breadcrumb, __props.name] : __props.breadcrumb);
|
|
83
|
+
/**
|
|
84
|
+
* Whether a deep link points at a property inside this collapsed item. When it
|
|
85
|
+
* does, the disclosure opens on mount so a fresh navigation can render the target
|
|
86
|
+
* and scroll it into view (mirrors how collapsible schema disclosures behave).
|
|
87
|
+
*/
|
|
88
|
+
const isOnScrollTargetPath = computed(() => {
|
|
89
|
+
const path = schemaBreadcrumb.value?.join(".");
|
|
90
|
+
if (!path) return false;
|
|
91
|
+
const target = scrollTargetId.value;
|
|
92
|
+
return target === path || target.startsWith(`${path}.`);
|
|
93
|
+
});
|
|
76
94
|
return (_ctx, _cache) => {
|
|
77
|
-
return openBlock(), createElementBlock("li", _hoisted_1, [createVNode(unref(Disclosure),
|
|
95
|
+
return openBlock(), createElementBlock("li", _hoisted_1, [createVNode(unref(Disclosure), { defaultOpen: isOnScrollTargetPath.value }, {
|
|
78
96
|
default: withCtx(({ open }) => [
|
|
79
97
|
__props.collapsableItems ? (openBlock(), createBlock(resolveDynamicComponent(shouldCollapse.value ? unref(DisclosureButton) : "div"), {
|
|
80
98
|
key: 0,
|
|
@@ -127,7 +145,7 @@ var ParameterListItem_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
|
|
|
127
145
|
])) : createCommentVNode("", true),
|
|
128
146
|
createVNode(SchemaProperty_default, {
|
|
129
147
|
is: "div",
|
|
130
|
-
breadcrumb:
|
|
148
|
+
breadcrumb: schemaBreadcrumb.value,
|
|
131
149
|
compact: "",
|
|
132
150
|
description: __props.collapsableItems ? "" : __props.parameter.description,
|
|
133
151
|
eventBus: __props.eventBus,
|
|
@@ -167,7 +185,7 @@ var ParameterListItem_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
|
|
|
167
185
|
}, null, 8, ["modelValue", "content"])], 2)) : createCommentVNode("", true)
|
|
168
186
|
]),
|
|
169
187
|
_: 1
|
|
170
|
-
})]);
|
|
188
|
+
}, 8, ["defaultOpen"])]);
|
|
171
189
|
};
|
|
172
190
|
}
|
|
173
191
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ParameterListItem.vue.script.js","names":[],"sources":["../../../../src/features/Operation/components/ParameterListItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'\nimport {\n ScalarMarkdown,\n ScalarMarkdownSummary,\n} from '@scalar/components/markdown'\nimport { ScalarWrappingText } from '@scalar/components/wrapping-text'\nimport { ScalarIconCaretRight } from '@scalar/icons'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport type {\n OpenApiDocument,\n ParameterObject,\n ResponseObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref } from 'vue'\n\nimport { getRefName } from '@/components/Content/Schema/helpers/get-ref-name'\nimport SchemaProperty from '@/components/Content/Schema/SchemaProperty.vue'\nimport type { OperationProps } from '@/features/Operation/Operation.vue'\n\nimport ContentTypeSelect from './ContentTypeSelect.vue'\nimport Headers from './Headers.vue'\nimport { getParameterExamples } from './helpers/get-parameter-examples'\n\nconst { name, parameter, options, collapsableItems, document } = defineProps<{\n parameter: ParameterObject | ResponseObject\n name: string\n breadcrumb?: string[]\n eventBus: WorkspaceEventBus | null\n collapsableItems?: boolean\n /** The document the operation belongs to, used to resolve schema references for display */\n document?: OpenApiDocument\n options: Pick<\n OperationProps['options'],\n | 'hideModels'\n | 'orderRequiredPropertiesFirst'\n | 'orderSchemaPropertiesBy'\n | 'expandAllSchemaProperties'\n >\n}>()\n\n/** Whether the markdown summary is being truncated */\nconst truncated = ref(false)\n\n/** Responses and params may both have a schema */\nconst schema = computed<SchemaObject | null>(() =>\n 'schema' in parameter && parameter.schema\n ? getResolvedRef(parameter.schema)\n : null,\n)\n\n/** Response and params may both have content */\nconst content = computed(() => {\n if (!('content' in parameter) || !parameter.content) {\n return null\n }\n const keys = Object.keys(parameter.content)\n if (keys.length === 0) {\n return null\n }\n return parameter.content\n})\n\nconst selectedContentType = ref<string>(\n Object.keys(content.value || {})[0] ?? '',\n)\n\n/** Response headers */\nconst headers = computed<ResponseObject['headers'] | null>(() =>\n 'headers' in parameter && parameter.headers ? parameter.headers : null,\n)\n\n/** Raw schema (possibly with $ref) for the selected content type or param. */\nconst baseSchema = computed(() =>\n content.value\n ? content.value?.[selectedContentType.value]?.schema\n : 'schema' in parameter && parameter.schema\n ? parameter.schema\n : null,\n)\n\n/** When the schema is a $ref, preserve its name so the UI can show the ref name instead of just the type. */\nconst schemaModelName = computed(() => {\n const raw = baseSchema.value\n if (!raw) {\n return null\n }\n\n if ('$ref' in raw) {\n return getRefName(raw.$ref)\n }\n\n return null\n})\n\n/** Computed value from the combined schema param and content param */\nconst value = computed(() => {\n const base = baseSchema.value\n const resolvedBase = content.value ? getResolvedRef(base) : schema.value\n\n const deprecated =\n 'deprecated' in parameter ? parameter.deprecated : schema.value?.deprecated\n\n /** Combine param/content/schema examples while ignoring undefined values. */\n const examples = getParameterExamples({\n parameter,\n schemaExamples: schema.value?.examples,\n contentExamples: content.value?.[selectedContentType.value]?.examples,\n })\n\n return {\n ...resolvedBase,\n deprecated: deprecated,\n examples,\n } as SchemaObject\n})\n\n/**\n * Determines whether this parameter item should be rendered as a collapsible disclosure.\n * Only collapses when collapsableItems is enabled and the parameter has additional\n * content to display (content types, headers, or schema details).\n */\nconst shouldCollapse = computed<boolean>(() =>\n Boolean(content.value || headers.value || schema.value || truncated.value),\n)\n</script>\n<template>\n <li class=\"parameter-item group/parameter-item\">\n <Disclosure v-slot=\"{ open }\">\n <component\n :is=\"shouldCollapse ? DisclosureButton : 'div'\"\n v-if=\"collapsableItems\"\n class=\"parameter-item-trigger\"\n :class=\"{ 'parameter-item-trigger-open': open }\">\n <div class=\"parameter-item-name min-w-0\">\n <ScalarIconCaretRight\n v-if=\"shouldCollapse\"\n class=\"parameter-item-icon size-3 transition-transform duration-100\"\n :class=\"{ 'rotate-90': open }\"\n weight=\"bold\" />\n <div>\n <ScalarWrappingText\n preset=\"property\"\n :text=\"name\" />\n </div>\n </div>\n <ScalarMarkdownSummary\n v-if=\"!open && parameter.description\"\n v-model:truncated=\"truncated\"\n class=\"parameter-item-description-summary min-w-0 flex-1\"\n controlled\n :value=\"parameter.description\" />\n <div\n v-else\n class=\"flex-1\" />\n </component>\n <DisclosurePanel\n class=\"parameter-item-container parameter-item-container-markdown\"\n :static=\"!collapsableItems\">\n <ScalarMarkdown\n v-if=\"collapsableItems && parameter.description\"\n class=\"parameter-item-description\"\n :value=\"parameter.description\" />\n <!-- Headers -->\n <Headers\n v-if=\"headers\"\n :breadcrumb=\"breadcrumb\"\n :document=\"document\"\n :eventBus=\"eventBus\"\n :expandAllSchemaProperties=\"options.expandAllSchemaProperties\"\n :headers=\"headers\"\n :orderRequiredPropertiesFirst=\"options.orderRequiredPropertiesFirst\"\n :orderSchemaPropertiesBy=\"options.orderSchemaPropertiesBy\" />\n\n <!-- Schema -->\n <SchemaProperty\n is=\"div\"\n :breadcrumb=\"breadcrumb\"\n compact\n :description=\"collapsableItems ? '' : parameter.description\"\n :eventBus=\"eventBus\"\n :hideWriteOnly=\"true\"\n :modelName=\"schemaModelName\"\n :name=\"collapsableItems ? '' : name\"\n :noncollapsible=\"true\"\n :options=\"{\n hideWriteOnly: true,\n orderRequiredPropertiesFirst: options.orderRequiredPropertiesFirst,\n orderSchemaPropertiesBy: options.orderSchemaPropertiesBy,\n expandAllSchemaProperties: options.expandAllSchemaProperties,\n document,\n }\"\n :required=\"'required' in parameter && parameter.required\"\n :schema=\"value\" />\n </DisclosurePanel>\n <div\n v-if=\"shouldCollapse && content\"\n class=\"absolute top-[calc(10px+0.5lh)] right-0 z-0 flex -translate-y-1/2 items-center text-base\"\n :class=\"{\n 'opacity-0 group-focus-within/parameter-item:opacity-100 group-hover/parameter-item:opacity-100':\n !open,\n }\">\n <div\n class=\"from-b-1 absolute inset-y-0 -left-6 -z-1 w-8 bg-linear-to-l from-40% to-transparent\" />\n <ContentTypeSelect\n v-model=\"selectedContentType\"\n :content=\"content\" />\n </div>\n </Disclosure>\n </li>\n</template>\n\n<style scoped>\n.parameter-item {\n display: flex;\n flex-direction: column;\n position: relative;\n border-top: var(--scalar-border-width) solid var(--scalar-border-color);\n}\n\n.parameter-item:last-of-type .parameter-schema {\n padding-bottom: 0;\n}\n\n.parameter-item-container {\n padding: 0;\n}\n\n.parameter-item-headers {\n border: var(--scalar-border-width) solid var(--scalar-border-color);\n}\n\n.parameter-item-name {\n position: relative;\n font-weight: var(--scalar-bold);\n font-size: var(--scalar-font-size-4);\n font-family: var(--scalar-font-code);\n color: var(--scalar-color-1);\n overflow-wrap: break-word;\n}\n\n.parameter-item-description,\n.parameter-item-description-summary {\n font-size: var(--scalar-mini);\n color: var(--scalar-color-2);\n}\n\n.parameter-item-description-summary.parameter-item-description-summary > * {\n --markdown-line-height: var(--scalar-line-height-5);\n}\n\n/* Match font size of markdown for property-detail-value since first child within accordian is displayed as if it were in the markdown section */\n.parameter-item-trigger\n ~ .parameter-item-container\n :deep(.property--level-0 > .property-heading .property-detail-value) {\n font-size: var(--scalar-micro);\n}\n\n.parameter-item-required-optional {\n color: var(--scalar-color-2);\n font-weight: var(--scalar-semibold);\n margin-right: 6px;\n position: relative;\n}\n\n.parameter-item--required {\n text-transform: uppercase;\n font-size: var(--scalar-micro);\n font-weight: var(--scalar-semibold);\n color: var(--scalar-color-orange);\n}\n\n.parameter-item-description {\n margin-top: 6px;\n font-size: var(--scalar-small);\n color: var(--scalar-color-2);\n line-height: 1.4;\n}\n\n.parameter-item-description :deep(p) {\n margin-top: 4px;\n font-size: var(--scalar-small);\n color: var(--scalar-color-2);\n}\n\n.parameter-schema {\n padding-bottom: 9px;\n margin-top: 3px;\n}\n\n.parameter-item-trigger {\n display: flex;\n align-items: baseline;\n line-height: var(--scalar-line-height-5);\n gap: 6px;\n flex-wrap: wrap;\n padding: 10px 0;\n outline: none;\n}\n\n.parameter-item-trigger-open {\n padding-bottom: 0;\n}\n\n.parameter-item-icon {\n color: var(--scalar-color-3);\n left: -19px;\n top: 0.5lh;\n translate: 0 -50%;\n position: absolute;\n}\n\n.parameter-item-trigger:hover .parameter-item-icon,\n.parameter-item-trigger:focus-visible .parameter-item-icon {\n color: var(--scalar-color-1);\n}\n\n.parameter-item-trigger:focus-visible .parameter-item-icon {\n outline: 1px solid var(--scalar-color-accent);\n outline-offset: 2px;\n border-radius: var(--scalar-radius);\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ParameterListItem.vue.script.js","names":[],"sources":["../../../../src/features/Operation/components/ParameterListItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'\nimport {\n ScalarMarkdown,\n ScalarMarkdownSummary,\n} from '@scalar/components/markdown'\nimport { ScalarWrappingText } from '@scalar/components/wrapping-text'\nimport { ScalarIconCaretRight } from '@scalar/icons'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport type {\n OpenApiDocument,\n ParameterObject,\n ResponseObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref } from 'vue'\n\nimport { getRefName } from '@/components/Content/Schema/helpers/get-ref-name'\nimport SchemaProperty from '@/components/Content/Schema/SchemaProperty.vue'\nimport type { OperationProps } from '@/features/Operation/Operation.vue'\nimport { scrollTargetId } from '@/helpers/lazy-bus'\n\nimport ContentTypeSelect from './ContentTypeSelect.vue'\nimport Headers from './Headers.vue'\nimport { getParameterExamples } from './helpers/get-parameter-examples'\n\nconst { name, parameter, options, collapsableItems, breadcrumb, document } =\n defineProps<{\n parameter: ParameterObject | ResponseObject\n name: string\n breadcrumb?: string[]\n eventBus: WorkspaceEventBus | null\n collapsableItems?: boolean\n /** The document the operation belongs to, used to resolve schema references for display */\n document?: OpenApiDocument\n options: Pick<\n OperationProps['options'],\n | 'hideModels'\n | 'orderRequiredPropertiesFirst'\n | 'orderSchemaPropertiesBy'\n | 'expandAllSchemaProperties'\n >\n }>()\n\n/** Whether the markdown summary is being truncated */\nconst truncated = ref(false)\n\n/** Responses and params may both have a schema */\nconst schema = computed<SchemaObject | null>(() =>\n 'schema' in parameter && parameter.schema\n ? getResolvedRef(parameter.schema)\n : null,\n)\n\n/** Response and params may both have content */\nconst content = computed(() => {\n if (!('content' in parameter) || !parameter.content) {\n return null\n }\n const keys = Object.keys(parameter.content)\n if (keys.length === 0) {\n return null\n }\n return parameter.content\n})\n\nconst selectedContentType = ref<string>(\n Object.keys(content.value || {})[0] ?? '',\n)\n\n/** Response headers */\nconst headers = computed<ResponseObject['headers'] | null>(() =>\n 'headers' in parameter && parameter.headers ? parameter.headers : null,\n)\n\n/** Raw schema (possibly with $ref) for the selected content type or param. */\nconst baseSchema = computed(() =>\n content.value\n ? content.value?.[selectedContentType.value]?.schema\n : 'schema' in parameter && parameter.schema\n ? parameter.schema\n : null,\n)\n\n/** When the schema is a $ref, preserve its name so the UI can show the ref name instead of just the type. */\nconst schemaModelName = computed(() => {\n const raw = baseSchema.value\n if (!raw) {\n return null\n }\n\n if ('$ref' in raw) {\n return getRefName(raw.$ref)\n }\n\n return null\n})\n\n/** Computed value from the combined schema param and content param */\nconst value = computed(() => {\n const base = baseSchema.value\n const resolvedBase = content.value ? getResolvedRef(base) : schema.value\n\n const deprecated =\n 'deprecated' in parameter ? parameter.deprecated : schema.value?.deprecated\n\n /** Combine param/content/schema examples while ignoring undefined values. */\n const examples = getParameterExamples({\n parameter,\n schemaExamples: schema.value?.examples,\n contentExamples: content.value?.[selectedContentType.value]?.examples,\n })\n\n return {\n ...resolvedBase,\n deprecated: deprecated,\n examples,\n } as SchemaObject\n})\n\n/**\n * Determines whether this parameter item should be rendered as a collapsible disclosure.\n * Only collapses when collapsableItems is enabled and the parameter has additional\n * content to display (content types, headers, or schema details).\n */\nconst shouldCollapse = computed<boolean>(() =>\n Boolean(content.value || headers.value || schema.value || truncated.value),\n)\n\n/**\n * The breadcrumb passed to the schema. Collapsible items (responses) render their\n * schema without a name to avoid a duplicate heading, so we push the item name\n * (e.g. the status code) onto the breadcrumb here to keep property anchors unique.\n */\nconst schemaBreadcrumb = computed<string[] | undefined>(() =>\n collapsableItems && breadcrumb && name ? [...breadcrumb, name] : breadcrumb,\n)\n\n/**\n * Whether a deep link points at a property inside this collapsed item. When it\n * does, the disclosure opens on mount so a fresh navigation can render the target\n * and scroll it into view (mirrors how collapsible schema disclosures behave).\n */\nconst isOnScrollTargetPath = computed<boolean>(() => {\n const path = schemaBreadcrumb.value?.join('.')\n if (!path) {\n return false\n }\n const target = scrollTargetId.value\n return target === path || target.startsWith(`${path}.`)\n})\n</script>\n<template>\n <li class=\"parameter-item group/parameter-item\">\n <Disclosure\n v-slot=\"{ open }\"\n :defaultOpen=\"isOnScrollTargetPath\">\n <component\n :is=\"shouldCollapse ? DisclosureButton : 'div'\"\n v-if=\"collapsableItems\"\n class=\"parameter-item-trigger\"\n :class=\"{ 'parameter-item-trigger-open': open }\">\n <div class=\"parameter-item-name min-w-0\">\n <ScalarIconCaretRight\n v-if=\"shouldCollapse\"\n class=\"parameter-item-icon size-3 transition-transform duration-100\"\n :class=\"{ 'rotate-90': open }\"\n weight=\"bold\" />\n <div>\n <ScalarWrappingText\n preset=\"property\"\n :text=\"name\" />\n </div>\n </div>\n <ScalarMarkdownSummary\n v-if=\"!open && parameter.description\"\n v-model:truncated=\"truncated\"\n class=\"parameter-item-description-summary min-w-0 flex-1\"\n controlled\n :value=\"parameter.description\" />\n <div\n v-else\n class=\"flex-1\" />\n </component>\n <DisclosurePanel\n class=\"parameter-item-container parameter-item-container-markdown\"\n :static=\"!collapsableItems\">\n <ScalarMarkdown\n v-if=\"collapsableItems && parameter.description\"\n class=\"parameter-item-description\"\n :value=\"parameter.description\" />\n <!-- Headers -->\n <Headers\n v-if=\"headers\"\n :breadcrumb=\"breadcrumb\"\n :document=\"document\"\n :eventBus=\"eventBus\"\n :expandAllSchemaProperties=\"options.expandAllSchemaProperties\"\n :headers=\"headers\"\n :orderRequiredPropertiesFirst=\"options.orderRequiredPropertiesFirst\"\n :orderSchemaPropertiesBy=\"options.orderSchemaPropertiesBy\" />\n\n <!-- Schema -->\n <SchemaProperty\n is=\"div\"\n :breadcrumb=\"schemaBreadcrumb\"\n compact\n :description=\"collapsableItems ? '' : parameter.description\"\n :eventBus=\"eventBus\"\n :hideWriteOnly=\"true\"\n :modelName=\"schemaModelName\"\n :name=\"collapsableItems ? '' : name\"\n :noncollapsible=\"true\"\n :options=\"{\n hideWriteOnly: true,\n orderRequiredPropertiesFirst: options.orderRequiredPropertiesFirst,\n orderSchemaPropertiesBy: options.orderSchemaPropertiesBy,\n expandAllSchemaProperties: options.expandAllSchemaProperties,\n document,\n }\"\n :required=\"'required' in parameter && parameter.required\"\n :schema=\"value\" />\n </DisclosurePanel>\n <div\n v-if=\"shouldCollapse && content\"\n class=\"absolute top-[calc(10px+0.5lh)] right-0 z-0 flex -translate-y-1/2 items-center text-base\"\n :class=\"{\n 'opacity-0 group-focus-within/parameter-item:opacity-100 group-hover/parameter-item:opacity-100':\n !open,\n }\">\n <div\n class=\"from-b-1 absolute inset-y-0 -left-6 -z-1 w-8 bg-linear-to-l from-40% to-transparent\" />\n <ContentTypeSelect\n v-model=\"selectedContentType\"\n :content=\"content\" />\n </div>\n </Disclosure>\n </li>\n</template>\n\n<style scoped>\n.parameter-item {\n display: flex;\n flex-direction: column;\n position: relative;\n border-top: var(--scalar-border-width) solid var(--scalar-border-color);\n}\n\n.parameter-item:last-of-type .parameter-schema {\n padding-bottom: 0;\n}\n\n.parameter-item-container {\n padding: 0;\n}\n\n.parameter-item-headers {\n border: var(--scalar-border-width) solid var(--scalar-border-color);\n}\n\n.parameter-item-name {\n position: relative;\n font-weight: var(--scalar-bold);\n font-size: var(--scalar-font-size-4);\n font-family: var(--scalar-font-code);\n color: var(--scalar-color-1);\n overflow-wrap: break-word;\n}\n\n.parameter-item-description,\n.parameter-item-description-summary {\n font-size: var(--scalar-mini);\n color: var(--scalar-color-2);\n}\n\n.parameter-item-description-summary.parameter-item-description-summary > * {\n --markdown-line-height: var(--scalar-line-height-5);\n}\n\n/* Match font size of markdown for property-detail-value since first child within accordian is displayed as if it were in the markdown section */\n.parameter-item-trigger\n ~ .parameter-item-container\n :deep(.property--level-0 > .property-heading .property-detail-value) {\n font-size: var(--scalar-micro);\n}\n\n.parameter-item-required-optional {\n color: var(--scalar-color-2);\n font-weight: var(--scalar-semibold);\n margin-right: 6px;\n position: relative;\n}\n\n.parameter-item--required {\n text-transform: uppercase;\n font-size: var(--scalar-micro);\n font-weight: var(--scalar-semibold);\n color: var(--scalar-color-orange);\n}\n\n.parameter-item-description {\n margin-top: 6px;\n font-size: var(--scalar-small);\n color: var(--scalar-color-2);\n line-height: 1.4;\n}\n\n.parameter-item-description :deep(p) {\n margin-top: 4px;\n font-size: var(--scalar-small);\n color: var(--scalar-color-2);\n}\n\n.parameter-schema {\n padding-bottom: 9px;\n margin-top: 3px;\n}\n\n.parameter-item-trigger {\n display: flex;\n align-items: baseline;\n line-height: var(--scalar-line-height-5);\n gap: 6px;\n flex-wrap: wrap;\n padding: 10px 0;\n outline: none;\n}\n\n.parameter-item-trigger-open {\n padding-bottom: 0;\n}\n\n.parameter-item-icon {\n color: var(--scalar-color-3);\n left: -19px;\n top: 0.5lh;\n translate: 0 -50%;\n position: absolute;\n}\n\n.parameter-item-trigger:hover .parameter-item-icon,\n.parameter-item-trigger:focus-visible .parameter-item-icon {\n color: var(--scalar-color-1);\n}\n\n.parameter-item-trigger:focus-visible .parameter-item-icon {\n outline: 1px solid var(--scalar-color-accent);\n outline-offset: 2px;\n border-radius: var(--scalar-radius);\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id-routing.d.ts","sourceRoot":"","sources":["../../src/helpers/id-routing.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,WAEhD,CAAA;AAyCD,mEAAmE;AACnE,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,YAAY,MAAM,GAAG,SAAS,WAMnF,CAAA;AACD,mEAAmE;AACnE,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,EAAE,YAAY,MAAM,GAAG,SAAS,WAYrG,CAAA;AAED,2DAA2D;AAC3D,eAAO,MAAM,qBAAqB,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,EAAE,YAAY,MAAM,GAAG,SAAS,WAW7G,CAAA;AAED,8DAA8D;AAC9D,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,YAWvE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,GAAG,SAAS,EAAE,YAAY,MAAM,GAAG,SAAS,WAQ3G,CAAA;AAmBD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,EAAE,UAAU,MAAM,GAAG,SAAS,EAAE,iBAAiB,OAAO,oBAsBhG,CAAA;AAqKD;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,GACtB,KAAK,MAAM,GAAG,GAAG,EACjB,mBAAmB,MAAM,EACzB,cAAc,MAAM,EACpB,iBAAiB,OAAO,EACxB,WAAW,MAAM,KAChB,GAAG,GAAG,IAyBR,CAAA;
|
|
1
|
+
{"version":3,"file":"id-routing.d.ts","sourceRoot":"","sources":["../../src/helpers/id-routing.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,WAEhD,CAAA;AAyCD,mEAAmE;AACnE,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,YAAY,MAAM,GAAG,SAAS,WAMnF,CAAA;AACD,mEAAmE;AACnE,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,EAAE,YAAY,MAAM,GAAG,SAAS,WAYrG,CAAA;AAED,2DAA2D;AAC3D,eAAO,MAAM,qBAAqB,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,EAAE,YAAY,MAAM,GAAG,SAAS,WAW7G,CAAA;AAED,8DAA8D;AAC9D,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,YAWvE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,GAAG,SAAS,EAAE,YAAY,MAAM,GAAG,SAAS,WAQ3G,CAAA;AAmBD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,EAAE,UAAU,MAAM,GAAG,SAAS,EAAE,iBAAiB,OAAO,oBAsBhG,CAAA;AAqKD;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,GACtB,KAAK,MAAM,GAAG,GAAG,EACjB,mBAAmB,MAAM,EACzB,cAAc,MAAM,EACpB,iBAAiB,OAAO,EACxB,WAAW,MAAM,KAChB,GAAG,GAAG,IAyBR,CAAA;AAUD,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,GAAI,IAAI,MAAM,KAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CA2BjF,CAAA"}
|
|
@@ -199,17 +199,34 @@ var redirectUrl = (url, modelsSectionSlug, documentSlug, isMultiDocument, basePa
|
|
|
199
199
|
}
|
|
200
200
|
return didRedirect ? target : null;
|
|
201
201
|
};
|
|
202
|
+
/**
|
|
203
|
+
* Markers that separate an operation id from a schema path within an anchor id.
|
|
204
|
+
* `responses` mirrors the request-body/parameter markers so response property
|
|
205
|
+
* anchors (e.g. `operation.responses.200.name`) resolve back to the operation id.
|
|
206
|
+
* Without it, deep links into responses cannot find their operation.
|
|
207
|
+
*/
|
|
208
|
+
var SCHEMA_PARAM_MARKERS = [
|
|
209
|
+
".body.",
|
|
210
|
+
".path.",
|
|
211
|
+
".query.",
|
|
212
|
+
".header.",
|
|
213
|
+
".responses."
|
|
214
|
+
];
|
|
202
215
|
/** Extracts the schema parameters from the id if they are present */
|
|
203
216
|
var getSchemaParamsFromId = (id) => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
return {
|
|
217
|
+
let markerIndex = -1;
|
|
218
|
+
for (const marker of SCHEMA_PARAM_MARKERS) {
|
|
219
|
+
const index = id.indexOf(marker);
|
|
220
|
+
if (index !== -1 && (markerIndex === -1 || index < markerIndex)) markerIndex = index;
|
|
221
|
+
}
|
|
222
|
+
if (markerIndex === -1) return {
|
|
210
223
|
rawId: id,
|
|
211
224
|
params: ""
|
|
212
225
|
};
|
|
226
|
+
return {
|
|
227
|
+
rawId: id.slice(0, markerIndex),
|
|
228
|
+
params: id.slice(markerIndex + 1)
|
|
229
|
+
};
|
|
213
230
|
};
|
|
214
231
|
//#endregion
|
|
215
232
|
export { getIdFromUrl, getSchemaParamsFromId, makeUrlFromId, matchesBasePath, redirectUrl };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id-routing.js","names":[],"sources":["../../src/helpers/id-routing.ts"],"sourcesContent":["export const sanitizeBasePath = (basePath: string) => {\n return basePath.replace(/^\\/+|\\/+$/g, '')\n}\n\nconst isHashBasePath = (basePath: string) => basePath.startsWith('#')\n\nconst sanitizeHashBasePath = (basePath: string) => {\n return basePath.replace(/^#+/, '').replace(/\\/+$/g, '')\n}\n\nconst applySlugPrefix = (base: string, slugPrefix: string | undefined) => {\n return slugPrefix ? `${slugPrefix}${base ? '/' : ''}${base}` : base\n}\n\nconst stripBasePathPrefix = (value: string, basePath: string) => {\n if (value === basePath) {\n return ''\n }\n\n if (value.startsWith(`${basePath}/`)) {\n return value.slice(basePath.length + 1)\n }\n\n return null\n}\n\n/**\n * Builds the URL-encoded, slash-prefixed form of a sanitized base path.\n *\n * Each segment is encoded separately so the result matches how the browser stores\n * `location.pathname` (which encodes within segments but leaves the slashes between them).\n */\nconst encodeBasePath = (sanitized: string) => {\n if (!sanitized) {\n return ''\n }\n\n return `/${sanitized\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/')}`\n}\n\n/** Extracts an element id from the hash when using hash routing */\nexport const getIdFromHash = (location: string | URL, slugPrefix: string | undefined) => {\n const url = typeof location === 'string' ? new URL(location) : location\n\n const base = decodeURIComponent(url.hash.slice(1))\n\n return applySlugPrefix(base, slugPrefix)\n}\n/** Extracts an element id from the path when using path routing */\nexport const getIdFromPath = (location: string | URL, basePath: string, slugPrefix: string | undefined) => {\n const url = typeof location === 'string' ? new URL(location) : location\n const basePathWithSlash = encodeBasePath(sanitizeBasePath(basePath))\n\n // Extract the portion after the basePath\n if (url.pathname.startsWith(basePathWithSlash)) {\n const remainder = url.pathname.slice(basePathWithSlash.length)\n const base = decodeURIComponent(remainder.startsWith('/') ? remainder.slice(1) : remainder)\n return applySlugPrefix(base, slugPrefix)\n }\n\n return slugPrefix ?? ''\n}\n\n/** Extracts an element id from a hash-prefixed basePath */\nexport const getIdFromHashBasePath = (location: string | URL, basePath: string, slugPrefix: string | undefined) => {\n const url = typeof location === 'string' ? new URL(location) : location\n const hash = decodeURIComponent(url.hash.slice(1))\n const sanitized = sanitizeHashBasePath(basePath)\n\n const remainder = stripBasePathPrefix(hash, sanitized)\n if (remainder !== null) {\n return applySlugPrefix(remainder, slugPrefix)\n }\n\n return slugPrefix ?? ''\n}\n\n/** Determines whether a URL matches the provided basePath. */\nexport const matchesBasePath = (location: string | URL, basePath: string) => {\n const url = typeof location === 'string' ? new URL(location) : location\n\n if (isHashBasePath(basePath)) {\n const hash = decodeURIComponent(url.hash)\n return hash === basePath || hash.startsWith(`${basePath}/`)\n }\n\n const basePathWithSlash = encodeBasePath(sanitizeBasePath(basePath))\n\n return url.pathname === basePathWithSlash || url.pathname.startsWith(`${basePathWithSlash}/`)\n}\n\n/**\n * Extracts a navigation id from a URL based on the routing type\n *\n * @param url - The URL to extract the id from\n * @param basePath - The base path used in path routing\n * @param slugPrefix - If the document slug is not expected in the URL then we must prefix it\n */\nexport const getIdFromUrl = (url: string | URL, basePath: string | undefined, slugPrefix: string | undefined) => {\n if (typeof basePath !== 'string') {\n return getIdFromHash(url, slugPrefix)\n }\n\n return isHashBasePath(basePath)\n ? getIdFromHashBasePath(url, basePath, slugPrefix)\n : getIdFromPath(url, basePath, slugPrefix)\n}\n\n/**\n * Strips the first segment from an id and preserves trailing slashes\n * Used in single-document mode where the document slug is not needed in the URL\n *\n * @param id - The full id to process\n * @returns The id with the first segment removed, preserving trailing slash if present\n */\nconst stripFirstSegment = (id: string): string => {\n // For preserving trailing slash in the id\n const hasTrailingSlash = id.endsWith('/')\n const segments = id.split('/').filter(Boolean).slice(1)\n const result = segments.join('/')\n\n // Only preserve trailing slash if there's actual content\n return hasTrailingSlash && result ? `${result}/` : result\n}\n\n/**\n * Generate a new URL and applies the ID to the path or hash\n * depending on the type of routing used\n *\n * @param id - The id to apply to the URL\n * @param basePath - The base path used in path routing\n * @param isMultiDocument - Whether the document is multi-document or single-document. Single-document documents will strip the document slug from the id\n */\nexport const makeUrlFromId = (_id: string, basePath: string | undefined, isMultiDocument: boolean) => {\n if (typeof window === 'undefined') {\n return undefined\n }\n\n /** When there is only 1 document we don't need to include the document name in the URL */\n const id = isMultiDocument ? _id : stripFirstSegment(_id)\n const url = new URL(window.location.href)\n\n if (typeof basePath === 'string') {\n if (isHashBasePath(basePath)) {\n const base = sanitizeHashBasePath(basePath)\n url.hash = [base, id].filter(Boolean).join('/')\n } else {\n const base = sanitizeBasePath(basePath)\n url.pathname = `${base}/${id}`\n }\n } else {\n url.hash = id\n }\n\n return url\n}\n\nconst escapeRegex = (value: string) => value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\n/**\n * A located navigation id: the decoded id plus a way to write a rewritten id back.\n */\ntype IdCarrier = {\n /** The decoded navigation id with all routing chrome (`#`, base path) removed. */\n value: string\n /** Splices `next` back into the location this id came from, preserving the URL shape. */\n write: (next: string) => void\n}\n\n/** Carrier for an id that lives in the bare hash fragment (`#<id>`). */\nconst bareHashCarrier = (url: URL): IdCarrier => ({\n value: decodeURIComponent(url.hash.slice(1)),\n write: (next) => {\n url.hash = next\n },\n})\n\n/**\n * Locates the navigation id inside a URL regardless of the routing mode.\n *\n * The id can live in three places — the bare hash, a hash base path (`#<base>/<id>`), or the\n * pathname after a path base path. Each carrier pairs the decoded id with a `write` callback, so\n * callers edit ids in id-space without re-deriving the routing chrome for each mode.\n *\n * Multiple carriers are returned: the canonical location for the active routing mode comes first,\n * followed by a legacy bare-hash carrier. Both path and hash-base-path routing include the bare hash\n * so a stale `#default/model/User` bookmark (left over from before the base path was configured) is\n * still canonicalized — the previous implementation rewrote such doc-slug-anchored hashes on every\n * load regardless of the routing mode. Callers rewrite every carrier that matches, not just the\n * first, so a legacy id sitting in both the pathname and the hash is cleaned up in one pass.\n */\nconst locateIdCarriers = (url: URL, basePath: string | undefined): IdCarrier[] => {\n // Hash routing: the id is the bare fragment.\n if (typeof basePath !== 'string') {\n return [bareHashCarrier(url)]\n }\n\n // Hash base path routing: the id follows a `#<base>/` prefix.\n if (isHashBasePath(basePath)) {\n const base = sanitizeHashBasePath(basePath)\n const hash = decodeURIComponent(url.hash.slice(1))\n\n return [\n {\n value: stripBasePathPrefix(hash, base) ?? '',\n write: (next) => {\n url.hash = [base, next].filter(Boolean).join('/')\n },\n },\n // Legacy fallback: an old bookmark may carry the id in the fragment without the base prefix.\n bareHashCarrier(url),\n ]\n }\n\n // Path routing: the id follows the (URL-encoded) base path in the pathname.\n const base = sanitizeBasePath(basePath)\n const remainder = stripBasePathPrefix(url.pathname, encodeBasePath(base))\n\n return [\n {\n value: remainder === null ? '' : decodeURIComponent(remainder),\n write: (next) => {\n // Assigning to `pathname` re-encodes characters, so we splice with the decoded base.\n url.pathname = base ? `/${base}/${next}` : `/${next}`\n },\n },\n // Legacy fallback: an old hash-routing bookmark may still carry the id in the fragment.\n bareHashCarrier(url),\n ]\n}\n\n/**\n * A single redirect rule, expressed in routing-agnostic id-space.\n *\n * Ids are navigation strings with all routing chrome (`#`, base path) stripped — see\n * {@link locateIdCarrier}. A rule is just the shape of an id rewrite: `match` is tested against the\n * id and `replace` follows the native `String.prototype.replace` contract, so a rule can be a plain\n * segment swap or an arbitrary replacer function. A rule that does not match leaves the id alone.\n */\ntype IdRedirect = {\n /** Pattern tested against the routing-agnostic id. */\n match: RegExp\n /** Replacement passed straight to `String.prototype.replace` (template string or replacer fn). */\n replace: string | ((substring: string, ...groups: string[]) => string)\n}\n\n/**\n * Applies redirect rules to an id and returns the first rewrite that changes it.\n *\n * Rules are tried in order and the search stops at the first match, so list more specific rules\n * before more general ones. Returns the id unchanged when no rule applies.\n */\nconst applyIdRedirects = (id: string, redirects: IdRedirect[]): string => {\n for (const { match, replace } of redirects) {\n // The ternary narrows the union so each `String.prototype.replace` overload type-checks.\n const next = typeof replace === 'string' ? id.replace(match, replace) : id.replace(match, replace)\n if (next !== id) {\n return next\n }\n }\n\n return id\n}\n\n/** Optional `(tag-group/<n>/)?tag/<slug>/` chrome that may sit in front of a section segment. */\nconst TAG_CHROME = '(?:(?:tag-group/[^/]+/)?tag/[^/]+/)?'\n\n/**\n * Runtime values the redirect rules are built from.\n *\n * The rules are data, but a few entries depend on per-document values (the configurable models\n * section slug, the active document slug, single- vs multi-document mode), so the list is built per\n * call rather than declared as a static constant.\n */\ntype RedirectContext = {\n /** Current (possibly customized) slug of the models section, e.g. `models` or `schemas`. */\n modelsSectionSlug: string\n /** Slug of the active document; legacy ids are anchored to it to avoid false positives. */\n documentSlug: string\n /** Multi-document ids always carry the doc slug; single-document bookmarks may omit it. */\n isMultiDocument: boolean\n}\n\n/**\n * Builds the list of id redirects for the current document.\n *\n * Add an entry here to support a new redirect. Each rule runs against every URL shape (hash, hash\n * base path, path) for free, because rules operate on routing-agnostic ids.\n */\nconst buildRedirects = ({ modelsSectionSlug, documentSlug, isMultiDocument }: RedirectContext): IdRedirect[] => {\n const escapedDoc = escapeRegex(documentSlug)\n // Keeping the tag chrome inside the doc-slug group is deliberate: a slug-less\n // `tag/<slug>/<segment>/<name>` is left alone, since once the document slug is stripped it is\n // ambiguous with an operation under a tag literally named after the segment.\n const documentPrefix = isMultiDocument ? `${escapedDoc}/${TAG_CHROME}` : `(?:${escapedDoc}/${TAG_CHROME})?`\n\n // Rewrites a leading `<from>/` section segment to the current models section slug. The trailing\n // `/` leaves an already-correct segment and operation paths such as `default/POST/model/train`\n // untouched.\n const renameSectionSegment = (from: string): IdRedirect => ({\n match: new RegExp(`^(${documentPrefix})${escapeRegex(from)}/`),\n replace: (_match, prefix) => `${prefix}${modelsSectionSlug}/`,\n })\n\n const redirects = [\n // Earlier versions hardcoded a singular `model/` prefix for individual schema entries even\n // though the section itself was plural, so old bookmarks like `default/model/User` would 404.\n renameSectionSegment('model'),\n ]\n\n // The models section used to live at the literal `models/`. Once a custom label moves it\n // elsewhere (e.g. `schemas/`), older `models/<name>` bookmarks must follow it. When the slug is\n // still the default `models` this would be a no-op, so only register it when it actually differs.\n if (modelsSectionSlug !== 'models') {\n redirects.push(renameSectionSegment('models'))\n }\n\n return redirects\n}\n\n/**\n * Rewrites navigation ids in a URL to their current form.\n *\n * The URL is reduced to its routing-agnostic id (see {@link locateIdCarriers}), each redirect rule\n * is applied in id-space (see {@link buildRedirects}), and the result is spliced back into the\n * original location. This handles hash, hash-base-path, and path routing uniformly, so a new\n * redirect only has to be added to the list once.\n *\n * Returns the canonicalized URL when a rewrite happens, or null otherwise.\n */\nexport const redirectUrl = (\n url: string | URL,\n modelsSectionSlug: string,\n documentSlug: string,\n isMultiDocument: boolean,\n basePath?: string,\n): URL | null => {\n if (!documentSlug) {\n return null\n }\n\n const target = new URL(typeof url === 'string' ? url : url.toString())\n const redirects = buildRedirects({ modelsSectionSlug, documentSlug, isMultiDocument })\n\n // Canonicalize every place the id might live. The carriers are distinct physical locations (the\n // pathname and the bare hash), so a single page load can carry a legacy id in more than one — for\n // example a path-routing URL whose stale hash still holds an old bookmark. Rewriting all of them\n // avoids the address bar showing a corrected path next to an outdated hash. A redirect rule is\n // anchored to the document slug, so it never matches the base-prefixed value of a fallback carrier,\n // which keeps carriers that happen to share a location (hash base path + bare hash) from clobbering\n // each other.\n let didRedirect = false\n for (const carrier of locateIdCarriers(target, basePath)) {\n const rewritten = applyIdRedirects(carrier.value, redirects)\n if (rewritten !== carrier.value) {\n carrier.write(rewritten)\n didRedirect = true\n }\n }\n\n return didRedirect ? target : null\n}\n\n/** Extracts the schema parameters from the id if they are present */\nexport const getSchemaParamsFromId = (id: string): { rawId: string; params: string } => {\n const matcher = id.match(/(.*)(\\.body\\.|\\.path\\.|\\.query\\.|\\.header\\.)(.*)/)\n\n if (matcher && typeof matcher[1] === 'string' && typeof matcher[2] === 'string') {\n return {\n rawId: matcher[1],\n params: matcher[2].slice(1) + matcher[3],\n }\n }\n return {\n rawId: id,\n params: '',\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"id-routing.js","names":[],"sources":["../../src/helpers/id-routing.ts"],"sourcesContent":["export const sanitizeBasePath = (basePath: string) => {\n return basePath.replace(/^\\/+|\\/+$/g, '')\n}\n\nconst isHashBasePath = (basePath: string) => basePath.startsWith('#')\n\nconst sanitizeHashBasePath = (basePath: string) => {\n return basePath.replace(/^#+/, '').replace(/\\/+$/g, '')\n}\n\nconst applySlugPrefix = (base: string, slugPrefix: string | undefined) => {\n return slugPrefix ? `${slugPrefix}${base ? '/' : ''}${base}` : base\n}\n\nconst stripBasePathPrefix = (value: string, basePath: string) => {\n if (value === basePath) {\n return ''\n }\n\n if (value.startsWith(`${basePath}/`)) {\n return value.slice(basePath.length + 1)\n }\n\n return null\n}\n\n/**\n * Builds the URL-encoded, slash-prefixed form of a sanitized base path.\n *\n * Each segment is encoded separately so the result matches how the browser stores\n * `location.pathname` (which encodes within segments but leaves the slashes between them).\n */\nconst encodeBasePath = (sanitized: string) => {\n if (!sanitized) {\n return ''\n }\n\n return `/${sanitized\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/')}`\n}\n\n/** Extracts an element id from the hash when using hash routing */\nexport const getIdFromHash = (location: string | URL, slugPrefix: string | undefined) => {\n const url = typeof location === 'string' ? new URL(location) : location\n\n const base = decodeURIComponent(url.hash.slice(1))\n\n return applySlugPrefix(base, slugPrefix)\n}\n/** Extracts an element id from the path when using path routing */\nexport const getIdFromPath = (location: string | URL, basePath: string, slugPrefix: string | undefined) => {\n const url = typeof location === 'string' ? new URL(location) : location\n const basePathWithSlash = encodeBasePath(sanitizeBasePath(basePath))\n\n // Extract the portion after the basePath\n if (url.pathname.startsWith(basePathWithSlash)) {\n const remainder = url.pathname.slice(basePathWithSlash.length)\n const base = decodeURIComponent(remainder.startsWith('/') ? remainder.slice(1) : remainder)\n return applySlugPrefix(base, slugPrefix)\n }\n\n return slugPrefix ?? ''\n}\n\n/** Extracts an element id from a hash-prefixed basePath */\nexport const getIdFromHashBasePath = (location: string | URL, basePath: string, slugPrefix: string | undefined) => {\n const url = typeof location === 'string' ? new URL(location) : location\n const hash = decodeURIComponent(url.hash.slice(1))\n const sanitized = sanitizeHashBasePath(basePath)\n\n const remainder = stripBasePathPrefix(hash, sanitized)\n if (remainder !== null) {\n return applySlugPrefix(remainder, slugPrefix)\n }\n\n return slugPrefix ?? ''\n}\n\n/** Determines whether a URL matches the provided basePath. */\nexport const matchesBasePath = (location: string | URL, basePath: string) => {\n const url = typeof location === 'string' ? new URL(location) : location\n\n if (isHashBasePath(basePath)) {\n const hash = decodeURIComponent(url.hash)\n return hash === basePath || hash.startsWith(`${basePath}/`)\n }\n\n const basePathWithSlash = encodeBasePath(sanitizeBasePath(basePath))\n\n return url.pathname === basePathWithSlash || url.pathname.startsWith(`${basePathWithSlash}/`)\n}\n\n/**\n * Extracts a navigation id from a URL based on the routing type\n *\n * @param url - The URL to extract the id from\n * @param basePath - The base path used in path routing\n * @param slugPrefix - If the document slug is not expected in the URL then we must prefix it\n */\nexport const getIdFromUrl = (url: string | URL, basePath: string | undefined, slugPrefix: string | undefined) => {\n if (typeof basePath !== 'string') {\n return getIdFromHash(url, slugPrefix)\n }\n\n return isHashBasePath(basePath)\n ? getIdFromHashBasePath(url, basePath, slugPrefix)\n : getIdFromPath(url, basePath, slugPrefix)\n}\n\n/**\n * Strips the first segment from an id and preserves trailing slashes\n * Used in single-document mode where the document slug is not needed in the URL\n *\n * @param id - The full id to process\n * @returns The id with the first segment removed, preserving trailing slash if present\n */\nconst stripFirstSegment = (id: string): string => {\n // For preserving trailing slash in the id\n const hasTrailingSlash = id.endsWith('/')\n const segments = id.split('/').filter(Boolean).slice(1)\n const result = segments.join('/')\n\n // Only preserve trailing slash if there's actual content\n return hasTrailingSlash && result ? `${result}/` : result\n}\n\n/**\n * Generate a new URL and applies the ID to the path or hash\n * depending on the type of routing used\n *\n * @param id - The id to apply to the URL\n * @param basePath - The base path used in path routing\n * @param isMultiDocument - Whether the document is multi-document or single-document. Single-document documents will strip the document slug from the id\n */\nexport const makeUrlFromId = (_id: string, basePath: string | undefined, isMultiDocument: boolean) => {\n if (typeof window === 'undefined') {\n return undefined\n }\n\n /** When there is only 1 document we don't need to include the document name in the URL */\n const id = isMultiDocument ? _id : stripFirstSegment(_id)\n const url = new URL(window.location.href)\n\n if (typeof basePath === 'string') {\n if (isHashBasePath(basePath)) {\n const base = sanitizeHashBasePath(basePath)\n url.hash = [base, id].filter(Boolean).join('/')\n } else {\n const base = sanitizeBasePath(basePath)\n url.pathname = `${base}/${id}`\n }\n } else {\n url.hash = id\n }\n\n return url\n}\n\nconst escapeRegex = (value: string) => value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\n/**\n * A located navigation id: the decoded id plus a way to write a rewritten id back.\n */\ntype IdCarrier = {\n /** The decoded navigation id with all routing chrome (`#`, base path) removed. */\n value: string\n /** Splices `next` back into the location this id came from, preserving the URL shape. */\n write: (next: string) => void\n}\n\n/** Carrier for an id that lives in the bare hash fragment (`#<id>`). */\nconst bareHashCarrier = (url: URL): IdCarrier => ({\n value: decodeURIComponent(url.hash.slice(1)),\n write: (next) => {\n url.hash = next\n },\n})\n\n/**\n * Locates the navigation id inside a URL regardless of the routing mode.\n *\n * The id can live in three places — the bare hash, a hash base path (`#<base>/<id>`), or the\n * pathname after a path base path. Each carrier pairs the decoded id with a `write` callback, so\n * callers edit ids in id-space without re-deriving the routing chrome for each mode.\n *\n * Multiple carriers are returned: the canonical location for the active routing mode comes first,\n * followed by a legacy bare-hash carrier. Both path and hash-base-path routing include the bare hash\n * so a stale `#default/model/User` bookmark (left over from before the base path was configured) is\n * still canonicalized — the previous implementation rewrote such doc-slug-anchored hashes on every\n * load regardless of the routing mode. Callers rewrite every carrier that matches, not just the\n * first, so a legacy id sitting in both the pathname and the hash is cleaned up in one pass.\n */\nconst locateIdCarriers = (url: URL, basePath: string | undefined): IdCarrier[] => {\n // Hash routing: the id is the bare fragment.\n if (typeof basePath !== 'string') {\n return [bareHashCarrier(url)]\n }\n\n // Hash base path routing: the id follows a `#<base>/` prefix.\n if (isHashBasePath(basePath)) {\n const base = sanitizeHashBasePath(basePath)\n const hash = decodeURIComponent(url.hash.slice(1))\n\n return [\n {\n value: stripBasePathPrefix(hash, base) ?? '',\n write: (next) => {\n url.hash = [base, next].filter(Boolean).join('/')\n },\n },\n // Legacy fallback: an old bookmark may carry the id in the fragment without the base prefix.\n bareHashCarrier(url),\n ]\n }\n\n // Path routing: the id follows the (URL-encoded) base path in the pathname.\n const base = sanitizeBasePath(basePath)\n const remainder = stripBasePathPrefix(url.pathname, encodeBasePath(base))\n\n return [\n {\n value: remainder === null ? '' : decodeURIComponent(remainder),\n write: (next) => {\n // Assigning to `pathname` re-encodes characters, so we splice with the decoded base.\n url.pathname = base ? `/${base}/${next}` : `/${next}`\n },\n },\n // Legacy fallback: an old hash-routing bookmark may still carry the id in the fragment.\n bareHashCarrier(url),\n ]\n}\n\n/**\n * A single redirect rule, expressed in routing-agnostic id-space.\n *\n * Ids are navigation strings with all routing chrome (`#`, base path) stripped — see\n * {@link locateIdCarrier}. A rule is just the shape of an id rewrite: `match` is tested against the\n * id and `replace` follows the native `String.prototype.replace` contract, so a rule can be a plain\n * segment swap or an arbitrary replacer function. A rule that does not match leaves the id alone.\n */\ntype IdRedirect = {\n /** Pattern tested against the routing-agnostic id. */\n match: RegExp\n /** Replacement passed straight to `String.prototype.replace` (template string or replacer fn). */\n replace: string | ((substring: string, ...groups: string[]) => string)\n}\n\n/**\n * Applies redirect rules to an id and returns the first rewrite that changes it.\n *\n * Rules are tried in order and the search stops at the first match, so list more specific rules\n * before more general ones. Returns the id unchanged when no rule applies.\n */\nconst applyIdRedirects = (id: string, redirects: IdRedirect[]): string => {\n for (const { match, replace } of redirects) {\n // The ternary narrows the union so each `String.prototype.replace` overload type-checks.\n const next = typeof replace === 'string' ? id.replace(match, replace) : id.replace(match, replace)\n if (next !== id) {\n return next\n }\n }\n\n return id\n}\n\n/** Optional `(tag-group/<n>/)?tag/<slug>/` chrome that may sit in front of a section segment. */\nconst TAG_CHROME = '(?:(?:tag-group/[^/]+/)?tag/[^/]+/)?'\n\n/**\n * Runtime values the redirect rules are built from.\n *\n * The rules are data, but a few entries depend on per-document values (the configurable models\n * section slug, the active document slug, single- vs multi-document mode), so the list is built per\n * call rather than declared as a static constant.\n */\ntype RedirectContext = {\n /** Current (possibly customized) slug of the models section, e.g. `models` or `schemas`. */\n modelsSectionSlug: string\n /** Slug of the active document; legacy ids are anchored to it to avoid false positives. */\n documentSlug: string\n /** Multi-document ids always carry the doc slug; single-document bookmarks may omit it. */\n isMultiDocument: boolean\n}\n\n/**\n * Builds the list of id redirects for the current document.\n *\n * Add an entry here to support a new redirect. Each rule runs against every URL shape (hash, hash\n * base path, path) for free, because rules operate on routing-agnostic ids.\n */\nconst buildRedirects = ({ modelsSectionSlug, documentSlug, isMultiDocument }: RedirectContext): IdRedirect[] => {\n const escapedDoc = escapeRegex(documentSlug)\n // Keeping the tag chrome inside the doc-slug group is deliberate: a slug-less\n // `tag/<slug>/<segment>/<name>` is left alone, since once the document slug is stripped it is\n // ambiguous with an operation under a tag literally named after the segment.\n const documentPrefix = isMultiDocument ? `${escapedDoc}/${TAG_CHROME}` : `(?:${escapedDoc}/${TAG_CHROME})?`\n\n // Rewrites a leading `<from>/` section segment to the current models section slug. The trailing\n // `/` leaves an already-correct segment and operation paths such as `default/POST/model/train`\n // untouched.\n const renameSectionSegment = (from: string): IdRedirect => ({\n match: new RegExp(`^(${documentPrefix})${escapeRegex(from)}/`),\n replace: (_match, prefix) => `${prefix}${modelsSectionSlug}/`,\n })\n\n const redirects = [\n // Earlier versions hardcoded a singular `model/` prefix for individual schema entries even\n // though the section itself was plural, so old bookmarks like `default/model/User` would 404.\n renameSectionSegment('model'),\n ]\n\n // The models section used to live at the literal `models/`. Once a custom label moves it\n // elsewhere (e.g. `schemas/`), older `models/<name>` bookmarks must follow it. When the slug is\n // still the default `models` this would be a no-op, so only register it when it actually differs.\n if (modelsSectionSlug !== 'models') {\n redirects.push(renameSectionSegment('models'))\n }\n\n return redirects\n}\n\n/**\n * Rewrites navigation ids in a URL to their current form.\n *\n * The URL is reduced to its routing-agnostic id (see {@link locateIdCarriers}), each redirect rule\n * is applied in id-space (see {@link buildRedirects}), and the result is spliced back into the\n * original location. This handles hash, hash-base-path, and path routing uniformly, so a new\n * redirect only has to be added to the list once.\n *\n * Returns the canonicalized URL when a rewrite happens, or null otherwise.\n */\nexport const redirectUrl = (\n url: string | URL,\n modelsSectionSlug: string,\n documentSlug: string,\n isMultiDocument: boolean,\n basePath?: string,\n): URL | null => {\n if (!documentSlug) {\n return null\n }\n\n const target = new URL(typeof url === 'string' ? url : url.toString())\n const redirects = buildRedirects({ modelsSectionSlug, documentSlug, isMultiDocument })\n\n // Canonicalize every place the id might live. The carriers are distinct physical locations (the\n // pathname and the bare hash), so a single page load can carry a legacy id in more than one — for\n // example a path-routing URL whose stale hash still holds an old bookmark. Rewriting all of them\n // avoids the address bar showing a corrected path next to an outdated hash. A redirect rule is\n // anchored to the document slug, so it never matches the base-prefixed value of a fallback carrier,\n // which keeps carriers that happen to share a location (hash base path + bare hash) from clobbering\n // each other.\n let didRedirect = false\n for (const carrier of locateIdCarriers(target, basePath)) {\n const rewritten = applyIdRedirects(carrier.value, redirects)\n if (rewritten !== carrier.value) {\n carrier.write(rewritten)\n didRedirect = true\n }\n }\n\n return didRedirect ? target : null\n}\n\n/**\n * Markers that separate an operation id from a schema path within an anchor id.\n * `responses` mirrors the request-body/parameter markers so response property\n * anchors (e.g. `operation.responses.200.name`) resolve back to the operation id.\n * Without it, deep links into responses cannot find their operation.\n */\nconst SCHEMA_PARAM_MARKERS = ['.body.', '.path.', '.query.', '.header.', '.responses.']\n\n/** Extracts the schema parameters from the id if they are present */\nexport const getSchemaParamsFromId = (id: string): { rawId: string; params: string } => {\n // Split at the first marker: an operation id never contains one, so everything\n // before the first marker is the operation id and the rest is the schema path.\n // (Splitting at the last marker would mishandle a property named like a marker\n // keyword, e.g. a request body field called `responses`.) A plain string scan\n // avoids the polynomial backtracking a `(.*)marker(.*)` regex would incur on\n // long, marker-less ids.\n let markerIndex = -1\n for (const marker of SCHEMA_PARAM_MARKERS) {\n const index = id.indexOf(marker)\n if (index !== -1 && (markerIndex === -1 || index < markerIndex)) {\n markerIndex = index\n }\n }\n\n if (markerIndex === -1) {\n return {\n rawId: id,\n params: '',\n }\n }\n\n return {\n rawId: id.slice(0, markerIndex),\n // Drop the leading dot but keep the marker keyword (e.g. `body.name`).\n params: id.slice(markerIndex + 1),\n }\n}\n"],"mappings":"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"}
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { PLUGIN_MANAGER_SYMBOL, usePluginManager } from '../plugins/hooks/usePluginManager.js';
|
|
2
|
-
export { type ApiReferencePlugin, type PluginManager, type PluginViewComponent, createPluginManager, } from '../plugins/plugin-manager.js';
|
|
2
|
+
export { type ApiReferencePlugin, type PluginAuthState, type PluginManager, type PluginViewComponent, createPluginManager, } from '../plugins/plugin-manager.js';
|
|
3
3
|
export { persistencePlugin } from './persistence-plugin.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAC1F,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAC1F,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA"}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import type { ClientPlugin } from '@scalar/oas-utils/helpers';
|
|
2
|
-
import type { ApiReferencePlugin as OriginalApiReferencePlugin, SpecificationExtension, ViewComponent } from '@scalar/types/api-reference';
|
|
2
|
+
import type { ApiReferencePlugin as OriginalApiReferencePlugin, PluginAuthState, SpecificationExtension, ViewComponent } from '@scalar/types/api-reference';
|
|
3
3
|
export type ApiReferencePlugin = OriginalApiReferencePlugin;
|
|
4
|
+
export type { PluginAuthState };
|
|
4
5
|
type CreatePluginManagerParams = {
|
|
5
6
|
plugins?: ApiReferencePlugin[];
|
|
7
|
+
/**
|
|
8
|
+
* Read-only accessor for the global authentication state.
|
|
9
|
+
*
|
|
10
|
+
* Passed through to plugin lifecycle hooks and exposed via `getAuthState`, letting plugins read
|
|
11
|
+
* stored secrets and the selected security schemes without being able to mutate them.
|
|
12
|
+
*/
|
|
13
|
+
auth?: PluginAuthState;
|
|
6
14
|
};
|
|
7
15
|
/** Plugin view slots that can render custom components in the content area */
|
|
8
16
|
declare const PLUGIN_VIEW_NAMES: readonly ["content.start", "content.end"];
|
|
@@ -22,7 +30,7 @@ type PluginSidebarEntry = {
|
|
|
22
30
|
*
|
|
23
31
|
* This store manages all plugins registered with the API client
|
|
24
32
|
*/
|
|
25
|
-
export declare const createPluginManager: ({ plugins }: CreatePluginManagerParams) => {
|
|
33
|
+
export declare const createPluginManager: ({ plugins, auth }: CreatePluginManagerParams) => {
|
|
26
34
|
/**
|
|
27
35
|
* Get all extensions with the given name from registered plugins
|
|
28
36
|
*/
|
|
@@ -43,6 +51,13 @@ export declare const createPluginManager: ({ plugins }: CreatePluginManagerParam
|
|
|
43
51
|
* Notify all plugins that the configuration has changed
|
|
44
52
|
*/
|
|
45
53
|
notifyConfigChange: (config: Record<string, unknown>) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Get the read-only accessor for the global authentication state.
|
|
56
|
+
*
|
|
57
|
+
* Plugin view components can call this (via `usePluginManager`) to read stored secrets and the
|
|
58
|
+
* selected security schemes. Returns an empty auth state when no accessor was provided.
|
|
59
|
+
*/
|
|
60
|
+
getAuthState: () => PluginAuthState;
|
|
46
61
|
/**
|
|
47
62
|
* Notify all plugins that the API Reference is being destroyed
|
|
48
63
|
*/
|
|
@@ -61,5 +76,4 @@ export declare const createPluginManager: ({ plugins }: CreatePluginManagerParam
|
|
|
61
76
|
getSidebarEntries: (documentSlug: string) => PluginSidebarEntry[];
|
|
62
77
|
};
|
|
63
78
|
export type PluginManager = ReturnType<typeof createPluginManager>;
|
|
64
|
-
export {};
|
|
65
79
|
//# sourceMappingURL=plugin-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-manager.d.ts","sourceRoot":"","sources":["../../src/plugins/plugin-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EACV,kBAAkB,IAAI,0BAA0B,EAChD,sBAAsB,EACtB,aAAa,EACd,MAAM,6BAA6B,CAAA;AAEpC,MAAM,MAAM,kBAAkB,GAAG,0BAA0B,CAAA;AAE3D,KAAK,yBAAyB,GAAG;IAC/B,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin-manager.d.ts","sourceRoot":"","sources":["../../src/plugins/plugin-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EACV,kBAAkB,IAAI,0BAA0B,EAChD,eAAe,EACf,sBAAsB,EACtB,aAAa,EACd,MAAM,6BAA6B,CAAA;AAEpC,MAAM,MAAM,kBAAkB,GAAG,0BAA0B,CAAA;AAE3D,YAAY,EAAE,eAAe,EAAE,CAAA;AAE/B,KAAK,yBAAyB,GAAG;IAC/B,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAA;IAC9B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,CAAA;CACvB,CAAA;AAYD,8EAA8E;AAC9E,QAAA,MAAM,iBAAiB,2CAA4C,CAAA;AAEnE,KAAK,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD,gGAAgG;AAChG,MAAM,MAAM,mBAAmB,GAAG,aAAa,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhE,mDAAmD;AACnD,KAAK,kBAAkB,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,cAAc,CAAA;CACzB,CAAA;AAgBD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,mBAAwB,yBAAyB;IAajF;;OAEG;uCACgC,KAAK,MAAM,EAAE,KAAG,sBAAsB,EAAE;IAc3E;;;;;;OAMG;kCAC2B,cAAc,gBAAgB,MAAM,KAAG,mBAAmB,EAAE;IAe1F;;OAEG;yBACkB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;IAMnD;;OAEG;iCAC0B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;IAM3D;;;;;OAKG;wBACe,eAAe;IAEjC;;OAEG;yBACgB,IAAI;IAMvB;;OAEG;+BACsB,YAAY,EAAE;IAYvC;;;;;;OAMG;sCAC+B,MAAM,KAAG,kBAAkB,EAAE;CAqBlE,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAA"}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
//#region src/plugins/plugin-manager.ts
|
|
2
|
+
/**
|
|
3
|
+
* A no-op auth state used when no accessor is provided (e.g. in tests or a standalone manager).
|
|
4
|
+
* It reports an empty authentication state so plugins can call the read methods unconditionally.
|
|
5
|
+
*/
|
|
6
|
+
var createEmptyAuthState = () => ({
|
|
7
|
+
export: () => ({}),
|
|
8
|
+
getAuthSecrets: () => void 0,
|
|
9
|
+
getAuthSelectedSchemas: () => void 0
|
|
10
|
+
});
|
|
2
11
|
/** Plugin view slots that can render custom components in the content area */
|
|
3
12
|
var PLUGIN_VIEW_NAMES = ["content.start", "content.end"];
|
|
4
13
|
/**
|
|
@@ -18,8 +27,9 @@ var getPluginViewId = (documentSlug, pluginName, viewName, index) => `${document
|
|
|
18
27
|
*
|
|
19
28
|
* This store manages all plugins registered with the API client
|
|
20
29
|
*/
|
|
21
|
-
var createPluginManager = ({ plugins = [] }) => {
|
|
30
|
+
var createPluginManager = ({ plugins = [], auth }) => {
|
|
22
31
|
const registeredPlugins = /* @__PURE__ */ new Map();
|
|
32
|
+
const authState = auth ?? createEmptyAuthState();
|
|
23
33
|
plugins.forEach((plugin) => {
|
|
24
34
|
const pluginInstance = plugin();
|
|
25
35
|
registeredPlugins.set(pluginInstance.name, pluginInstance);
|
|
@@ -44,11 +54,18 @@ var createPluginManager = ({ plugins = [] }) => {
|
|
|
44
54
|
return components;
|
|
45
55
|
},
|
|
46
56
|
notifyInit: (config) => {
|
|
47
|
-
for (const plugin of registeredPlugins.values()) plugin.hooks?.onInit?.({
|
|
57
|
+
for (const plugin of registeredPlugins.values()) plugin.hooks?.onInit?.({
|
|
58
|
+
config,
|
|
59
|
+
auth: authState
|
|
60
|
+
});
|
|
48
61
|
},
|
|
49
62
|
notifyConfigChange: (config) => {
|
|
50
|
-
for (const plugin of registeredPlugins.values()) plugin.hooks?.onConfigChange?.({
|
|
63
|
+
for (const plugin of registeredPlugins.values()) plugin.hooks?.onConfigChange?.({
|
|
64
|
+
config,
|
|
65
|
+
auth: authState
|
|
66
|
+
});
|
|
51
67
|
},
|
|
68
|
+
getAuthState: () => authState,
|
|
52
69
|
notifyDestroy: () => {
|
|
53
70
|
for (const plugin of registeredPlugins.values()) plugin.hooks?.onDestroy?.();
|
|
54
71
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-manager.js","names":[],"sources":["../../src/plugins/plugin-manager.ts"],"sourcesContent":["import type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type {\n ApiReferencePlugin as OriginalApiReferencePlugin,\n SpecificationExtension,\n ViewComponent,\n} from '@scalar/types/api-reference'\n\nexport type ApiReferencePlugin = OriginalApiReferencePlugin\n\ntype CreatePluginManagerParams = {\n plugins?: ApiReferencePlugin[]\n}\n\n/** Plugin view slots that can render custom components in the content area */\nconst PLUGIN_VIEW_NAMES = ['content.start', 'content.end'] as const\n\ntype PluginViewName = (typeof PLUGIN_VIEW_NAMES)[number]\n\n/** A plugin view component paired with the stable id used for the DOM and sidebar navigation */\nexport type PluginViewComponent = ViewComponent & { id: string }\n\n/** A sidebar entry contributed by a plugin view */\ntype PluginSidebarEntry = {\n id: string\n label: string\n viewName: PluginViewName\n}\n\n/**\n * Build a stable, unique id for a plugin view component.\n *\n * The same id is used as the DOM element id (so scroll navigation can find it) and as the\n * sidebar navigation entry id (so clicking it scrolls to the element). Keeping both in sync\n * is what lets plugin views participate in the existing scroll-spy and navigation logic.\n *\n * The id is prefixed with the document slug so it matches the navigation id convention. That is\n * what lets URL deep-linking work: `getIdFromUrl` re-prepends the document slug on initial load,\n * so a hash like `plugin-view/<plugin>/<view>/<index>` resolves back to this exact id.\n */\nconst getPluginViewId = (documentSlug: string, pluginName: string, viewName: PluginViewName, index: number): string =>\n `${documentSlug}/plugin-view/${pluginName}/${viewName}/${index}`\n\n/**\n * Create the plugin manager store\n *\n * This store manages all plugins registered with the API client\n */\nexport const createPluginManager = ({ plugins = [] }: CreatePluginManagerParams) => {\n const registeredPlugins = new Map<string, ReturnType<ApiReferencePlugin>>()\n\n // Register initial plugins\n plugins.forEach((plugin) => {\n const pluginInstance = plugin()\n registeredPlugins.set(pluginInstance.name, pluginInstance)\n })\n\n return {\n /**\n * Get all extensions with the given name from registered plugins\n */\n getSpecificationExtensions: (name: `x-${string}`): SpecificationExtension[] => {\n const extensions: SpecificationExtension[] = []\n\n for (const plugin of registeredPlugins.values()) {\n for (const extension of plugin.extensions) {\n if (extension.name === name) {\n extensions.push(extension)\n }\n }\n }\n\n return extensions\n },\n\n /**\n * Get all components for a specific view from registered plugins.\n *\n * Each component carries a stable `id` (scoped to the active document slug) so the rendered\n * DOM element and the sidebar entry (see `getSidebarEntries`) share the same id and stay in\n * sync for scroll navigation and deep-linking.\n */\n getViewComponents: (viewName: PluginViewName, documentSlug: string): PluginViewComponent[] => {\n const components: PluginViewComponent[] = []\n\n for (const plugin of registeredPlugins.values()) {\n const viewComponents = plugin.views?.[viewName]\n if (viewComponents) {\n viewComponents.forEach((component: ViewComponent, index: number) => {\n components.push({ ...component, id: getPluginViewId(documentSlug, plugin.name, viewName, index) })\n })\n }\n }\n\n return components\n },\n\n /**\n * Notify all plugins that the API Reference has been initialized\n */\n notifyInit: (config: Record<string, unknown>): void => {\n for (const plugin of registeredPlugins.values()) {\n plugin.hooks?.onInit?.({ config })\n }\n },\n\n /**\n * Notify all plugins that the configuration has changed\n */\n notifyConfigChange: (config: Record<string, unknown>): void => {\n for (const plugin of registeredPlugins.values()) {\n plugin.hooks?.onConfigChange?.({ config })\n }\n },\n\n /**\n * Notify all plugins that the API Reference is being destroyed\n */\n notifyDestroy: (): void => {\n for (const plugin of registeredPlugins.values()) {\n plugin.hooks?.onDestroy?.()\n }\n },\n\n /**\n * Get all client plugins provided by registered plugins\n */\n getApiClientPlugins: (): ClientPlugin[] => {\n const apiClientPlugins: ClientPlugin[] = []\n\n for (const plugin of registeredPlugins.values()) {\n if (plugin.apiClientPlugins) {\n apiClientPlugins.push(...(plugin.apiClientPlugins as ClientPlugin[]))\n }\n }\n\n return apiClientPlugins\n },\n\n /**\n * Get all sidebar entries contributed by plugin views.\n *\n * Only views that opt in via `sidebar.show` are returned. Each entry's `id` matches the\n * id of the rendered component (see `getViewComponents`), so the API Reference can add it\n * to the sidebar navigation and scrolling/active-tracking work out of the box.\n */\n getSidebarEntries: (documentSlug: string): PluginSidebarEntry[] => {\n const entries: PluginSidebarEntry[] = []\n\n for (const plugin of registeredPlugins.values()) {\n for (const viewName of PLUGIN_VIEW_NAMES) {\n const viewComponents = plugin.views?.[viewName]\n viewComponents?.forEach((component: ViewComponent, index: number) => {\n if (component.sidebar?.show && component.sidebar.label) {\n entries.push({\n id: getPluginViewId(documentSlug, plugin.name, viewName, index),\n label: component.sidebar.label,\n viewName,\n })\n }\n })\n }\n }\n\n return entries\n },\n }\n}\n\nexport type PluginManager = ReturnType<typeof createPluginManager>\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"plugin-manager.js","names":[],"sources":["../../src/plugins/plugin-manager.ts"],"sourcesContent":["import type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type {\n ApiReferencePlugin as OriginalApiReferencePlugin,\n PluginAuthState,\n SpecificationExtension,\n ViewComponent,\n} from '@scalar/types/api-reference'\n\nexport type ApiReferencePlugin = OriginalApiReferencePlugin\n\nexport type { PluginAuthState }\n\ntype CreatePluginManagerParams = {\n plugins?: ApiReferencePlugin[]\n /**\n * Read-only accessor for the global authentication state.\n *\n * Passed through to plugin lifecycle hooks and exposed via `getAuthState`, letting plugins read\n * stored secrets and the selected security schemes without being able to mutate them.\n */\n auth?: PluginAuthState\n}\n\n/**\n * A no-op auth state used when no accessor is provided (e.g. in tests or a standalone manager).\n * It reports an empty authentication state so plugins can call the read methods unconditionally.\n */\nconst createEmptyAuthState = (): PluginAuthState => ({\n export: () => ({}),\n getAuthSecrets: () => undefined,\n getAuthSelectedSchemas: () => undefined,\n})\n\n/** Plugin view slots that can render custom components in the content area */\nconst PLUGIN_VIEW_NAMES = ['content.start', 'content.end'] as const\n\ntype PluginViewName = (typeof PLUGIN_VIEW_NAMES)[number]\n\n/** A plugin view component paired with the stable id used for the DOM and sidebar navigation */\nexport type PluginViewComponent = ViewComponent & { id: string }\n\n/** A sidebar entry contributed by a plugin view */\ntype PluginSidebarEntry = {\n id: string\n label: string\n viewName: PluginViewName\n}\n\n/**\n * Build a stable, unique id for a plugin view component.\n *\n * The same id is used as the DOM element id (so scroll navigation can find it) and as the\n * sidebar navigation entry id (so clicking it scrolls to the element). Keeping both in sync\n * is what lets plugin views participate in the existing scroll-spy and navigation logic.\n *\n * The id is prefixed with the document slug so it matches the navigation id convention. That is\n * what lets URL deep-linking work: `getIdFromUrl` re-prepends the document slug on initial load,\n * so a hash like `plugin-view/<plugin>/<view>/<index>` resolves back to this exact id.\n */\nconst getPluginViewId = (documentSlug: string, pluginName: string, viewName: PluginViewName, index: number): string =>\n `${documentSlug}/plugin-view/${pluginName}/${viewName}/${index}`\n\n/**\n * Create the plugin manager store\n *\n * This store manages all plugins registered with the API client\n */\nexport const createPluginManager = ({ plugins = [], auth }: CreatePluginManagerParams) => {\n const registeredPlugins = new Map<string, ReturnType<ApiReferencePlugin>>()\n\n // Fall back to an empty read-only auth state so hooks and `getAuthState` always have an accessor.\n const authState = auth ?? createEmptyAuthState()\n\n // Register initial plugins\n plugins.forEach((plugin) => {\n const pluginInstance = plugin()\n registeredPlugins.set(pluginInstance.name, pluginInstance)\n })\n\n return {\n /**\n * Get all extensions with the given name from registered plugins\n */\n getSpecificationExtensions: (name: `x-${string}`): SpecificationExtension[] => {\n const extensions: SpecificationExtension[] = []\n\n for (const plugin of registeredPlugins.values()) {\n for (const extension of plugin.extensions) {\n if (extension.name === name) {\n extensions.push(extension)\n }\n }\n }\n\n return extensions\n },\n\n /**\n * Get all components for a specific view from registered plugins.\n *\n * Each component carries a stable `id` (scoped to the active document slug) so the rendered\n * DOM element and the sidebar entry (see `getSidebarEntries`) share the same id and stay in\n * sync for scroll navigation and deep-linking.\n */\n getViewComponents: (viewName: PluginViewName, documentSlug: string): PluginViewComponent[] => {\n const components: PluginViewComponent[] = []\n\n for (const plugin of registeredPlugins.values()) {\n const viewComponents = plugin.views?.[viewName]\n if (viewComponents) {\n viewComponents.forEach((component: ViewComponent, index: number) => {\n components.push({ ...component, id: getPluginViewId(documentSlug, plugin.name, viewName, index) })\n })\n }\n }\n\n return components\n },\n\n /**\n * Notify all plugins that the API Reference has been initialized\n */\n notifyInit: (config: Record<string, unknown>): void => {\n for (const plugin of registeredPlugins.values()) {\n plugin.hooks?.onInit?.({ config, auth: authState })\n }\n },\n\n /**\n * Notify all plugins that the configuration has changed\n */\n notifyConfigChange: (config: Record<string, unknown>): void => {\n for (const plugin of registeredPlugins.values()) {\n plugin.hooks?.onConfigChange?.({ config, auth: authState })\n }\n },\n\n /**\n * Get the read-only accessor for the global authentication state.\n *\n * Plugin view components can call this (via `usePluginManager`) to read stored secrets and the\n * selected security schemes. Returns an empty auth state when no accessor was provided.\n */\n getAuthState: (): PluginAuthState => authState,\n\n /**\n * Notify all plugins that the API Reference is being destroyed\n */\n notifyDestroy: (): void => {\n for (const plugin of registeredPlugins.values()) {\n plugin.hooks?.onDestroy?.()\n }\n },\n\n /**\n * Get all client plugins provided by registered plugins\n */\n getApiClientPlugins: (): ClientPlugin[] => {\n const apiClientPlugins: ClientPlugin[] = []\n\n for (const plugin of registeredPlugins.values()) {\n if (plugin.apiClientPlugins) {\n apiClientPlugins.push(...(plugin.apiClientPlugins as ClientPlugin[]))\n }\n }\n\n return apiClientPlugins\n },\n\n /**\n * Get all sidebar entries contributed by plugin views.\n *\n * Only views that opt in via `sidebar.show` are returned. Each entry's `id` matches the\n * id of the rendered component (see `getViewComponents`), so the API Reference can add it\n * to the sidebar navigation and scrolling/active-tracking work out of the box.\n */\n getSidebarEntries: (documentSlug: string): PluginSidebarEntry[] => {\n const entries: PluginSidebarEntry[] = []\n\n for (const plugin of registeredPlugins.values()) {\n for (const viewName of PLUGIN_VIEW_NAMES) {\n const viewComponents = plugin.views?.[viewName]\n viewComponents?.forEach((component: ViewComponent, index: number) => {\n if (component.sidebar?.show && component.sidebar.label) {\n entries.push({\n id: getPluginViewId(documentSlug, plugin.name, viewName, index),\n label: component.sidebar.label,\n viewName,\n })\n }\n })\n }\n }\n\n return entries\n },\n }\n}\n\nexport type PluginManager = ReturnType<typeof createPluginManager>\n"],"mappings":";;;;;AA2BA,IAAM,8BAA+C;CACnD,eAAe,EAAE;CACjB,sBAAsB,KAAA;CACtB,8BAA8B,KAAA;CAC/B;;AAGD,IAAM,oBAAoB,CAAC,iBAAiB,cAAc;;;;;;;;;;;;AAyB1D,IAAM,mBAAmB,cAAsB,YAAoB,UAA0B,UAC3F,GAAG,aAAa,eAAe,WAAW,GAAG,SAAS,GAAG;;;;;;AAO3D,IAAa,uBAAuB,EAAE,UAAU,EAAE,EAAE,WAAsC;CACxF,MAAM,oCAAoB,IAAI,KAA6C;CAG3E,MAAM,YAAY,QAAQ,sBAAsB;AAGhD,SAAQ,SAAS,WAAW;EAC1B,MAAM,iBAAiB,QAAQ;AAC/B,oBAAkB,IAAI,eAAe,MAAM,eAAe;GAC1D;AAEF,QAAO;EAIL,6BAA6B,SAAkD;GAC7E,MAAM,aAAuC,EAAE;AAE/C,QAAK,MAAM,UAAU,kBAAkB,QAAQ,CAC7C,MAAK,MAAM,aAAa,OAAO,WAC7B,KAAI,UAAU,SAAS,KACrB,YAAW,KAAK,UAAU;AAKhC,UAAO;;EAUT,oBAAoB,UAA0B,iBAAgD;GAC5F,MAAM,aAAoC,EAAE;AAE5C,QAAK,MAAM,UAAU,kBAAkB,QAAQ,EAAE;IAC/C,MAAM,iBAAiB,OAAO,QAAQ;AACtC,QAAI,eACF,gBAAe,SAAS,WAA0B,UAAkB;AAClE,gBAAW,KAAK;MAAE,GAAG;MAAW,IAAI,gBAAgB,cAAc,OAAO,MAAM,UAAU,MAAM;MAAE,CAAC;MAClG;;AAIN,UAAO;;EAMT,aAAa,WAA0C;AACrD,QAAK,MAAM,UAAU,kBAAkB,QAAQ,CAC7C,QAAO,OAAO,SAAS;IAAE;IAAQ,MAAM;IAAW,CAAC;;EAOvD,qBAAqB,WAA0C;AAC7D,QAAK,MAAM,UAAU,kBAAkB,QAAQ,CAC7C,QAAO,OAAO,iBAAiB;IAAE;IAAQ,MAAM;IAAW,CAAC;;EAU/D,oBAAqC;EAKrC,qBAA2B;AACzB,QAAK,MAAM,UAAU,kBAAkB,QAAQ,CAC7C,QAAO,OAAO,aAAa;;EAO/B,2BAA2C;GACzC,MAAM,mBAAmC,EAAE;AAE3C,QAAK,MAAM,UAAU,kBAAkB,QAAQ,CAC7C,KAAI,OAAO,iBACT,kBAAiB,KAAK,GAAI,OAAO,iBAAoC;AAIzE,UAAO;;EAUT,oBAAoB,iBAA+C;GACjE,MAAM,UAAgC,EAAE;AAExC,QAAK,MAAM,UAAU,kBAAkB,QAAQ,CAC7C,MAAK,MAAM,YAAY,kBAErB,EADuB,OAAO,QAAQ,YACtB,SAAS,WAA0B,UAAkB;AACnE,QAAI,UAAU,SAAS,QAAQ,UAAU,QAAQ,MAC/C,SAAQ,KAAK;KACX,IAAI,gBAAgB,cAAc,OAAO,MAAM,UAAU,MAAM;KAC/D,OAAO,UAAU,QAAQ;KACzB;KACD,CAAC;KAEJ;AAIN,UAAO;;EAEV"}
|