@scalar/api-client 3.5.1 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +0 -1
  5. package/dist/style.css +3916 -4768
  6. package/dist/styles/tailwind.config.css +20 -0
  7. package/dist/styles/utilities.css +45 -0
  8. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.d.ts.map +1 -1
  9. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.js +1 -1
  10. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.js.map +1 -1
  11. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.script.js +1 -1
  12. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.script.js.map +1 -1
  13. package/dist/v2/components/data-table/DataTableInput.vue.d.ts +1 -1
  14. package/dist/v2/components/data-table/DataTableInput.vue.d.ts.map +1 -1
  15. package/dist/v2/constants.js +1 -1
  16. package/dist/v2/features/app/App.vue.d.ts +15 -31
  17. package/dist/v2/features/app/App.vue.d.ts.map +1 -1
  18. package/dist/v2/features/app/App.vue.js.map +1 -1
  19. package/dist/v2/features/app/App.vue.script.js +107 -28
  20. package/dist/v2/features/app/App.vue.script.js.map +1 -1
  21. package/dist/v2/features/app/app-state.d.ts +10 -14
  22. package/dist/v2/features/app/app-state.d.ts.map +1 -1
  23. package/dist/v2/features/app/app-state.js +53 -21
  24. package/dist/v2/features/app/app-state.js.map +1 -1
  25. package/dist/v2/features/app/components/AppHeader.vue.d.ts.map +1 -1
  26. package/dist/v2/features/app/components/AppHeader.vue.js.map +1 -1
  27. package/dist/v2/features/app/components/AppHeader.vue.script.js +1 -1
  28. package/dist/v2/features/app/components/AppHeader.vue.script.js.map +1 -1
  29. package/dist/v2/features/app/components/AppHeaderActions.vue.d.ts +32 -0
  30. package/dist/v2/features/app/components/AppHeaderActions.vue.d.ts.map +1 -0
  31. package/dist/v2/features/app/components/AppHeaderActions.vue.js +7 -0
  32. package/dist/v2/features/app/components/AppHeaderActions.vue.js.map +1 -0
  33. package/dist/v2/features/app/components/AppHeaderActions.vue.script.js +170 -0
  34. package/dist/v2/features/app/components/AppHeaderActions.vue.script.js.map +1 -0
  35. package/dist/v2/features/app/components/AppSidebar.vue.d.ts +2 -3
  36. package/dist/v2/features/app/components/AppSidebar.vue.d.ts.map +1 -1
  37. package/dist/v2/features/app/components/AppSidebar.vue.js +1 -1
  38. package/dist/v2/features/app/components/AppSidebar.vue.js.map +1 -1
  39. package/dist/v2/features/app/components/AppSidebar.vue.script.js +1 -2
  40. package/dist/v2/features/app/components/AppSidebar.vue.script.js.map +1 -1
  41. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.d.ts +1 -2
  42. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.d.ts.map +1 -1
  43. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js +1 -1
  44. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js.map +1 -1
  45. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js +3 -34
  46. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js.map +1 -1
  47. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.d.ts +1 -1
  48. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.d.ts.map +1 -1
  49. package/dist/v2/features/app/components/PublishDocumentModal.vue.d.ts +77 -0
  50. package/dist/v2/features/app/components/PublishDocumentModal.vue.d.ts.map +1 -0
  51. package/dist/v2/features/app/components/PublishDocumentModal.vue.js +7 -0
  52. package/dist/v2/features/app/components/PublishDocumentModal.vue.js.map +1 -0
  53. package/dist/v2/features/app/components/PublishDocumentModal.vue.script.js +209 -0
  54. package/dist/v2/features/app/components/PublishDocumentModal.vue.script.js.map +1 -0
  55. package/dist/v2/features/app/components/SyncConflictResolutionEditor.vue.d.ts.map +1 -0
  56. package/dist/v2/features/{collection → app}/components/SyncConflictResolutionEditor.vue.js +2 -2
  57. package/dist/v2/features/app/components/SyncConflictResolutionEditor.vue.js.map +1 -0
  58. package/dist/v2/features/{collection → app}/components/SyncConflictResolutionEditor.vue.script.js +1 -1
  59. package/dist/v2/features/{collection/components/SyncConflictResolutionEditor.vue.js.map → app/components/SyncConflictResolutionEditor.vue.script.js.map} +1 -1
  60. package/dist/v2/features/app/helpers/check-version-conflict.d.ts +8 -5
  61. package/dist/v2/features/app/helpers/check-version-conflict.d.ts.map +1 -1
  62. package/dist/v2/features/app/helpers/check-version-conflict.js +10 -7
  63. package/dist/v2/features/app/helpers/check-version-conflict.js.map +1 -1
  64. package/dist/v2/features/app/helpers/create-api-client-app.d.ts +8 -5
  65. package/dist/v2/features/app/helpers/create-api-client-app.d.ts.map +1 -1
  66. package/dist/v2/features/app/helpers/create-api-client-app.js +2 -2
  67. package/dist/v2/features/app/helpers/create-api-client-app.js.map +1 -1
  68. package/dist/v2/features/app/helpers/load-registry-document.d.ts +1 -10
  69. package/dist/v2/features/app/helpers/load-registry-document.d.ts.map +1 -1
  70. package/dist/v2/features/app/helpers/load-registry-document.js +6 -5
  71. package/dist/v2/features/app/helpers/load-registry-document.js.map +1 -1
  72. package/dist/v2/features/app/helpers/registry-error-messages.d.ts +23 -0
  73. package/dist/v2/features/app/helpers/registry-error-messages.d.ts.map +1 -0
  74. package/dist/v2/features/app/helpers/registry-error-messages.js +63 -0
  75. package/dist/v2/features/app/helpers/registry-error-messages.js.map +1 -0
  76. package/dist/v2/features/app/hooks/use-active-document-version.d.ts +2 -1
  77. package/dist/v2/features/app/hooks/use-active-document-version.d.ts.map +1 -1
  78. package/dist/v2/features/app/hooks/use-active-document-version.js.map +1 -1
  79. package/dist/v2/features/app/hooks/use-document-sync.d.ts +126 -0
  80. package/dist/v2/features/app/hooks/use-document-sync.d.ts.map +1 -0
  81. package/dist/v2/features/app/hooks/use-document-sync.js +448 -0
  82. package/dist/v2/features/app/hooks/use-document-sync.js.map +1 -0
  83. package/dist/v2/features/app/hooks/use-network-status.d.ts +29 -0
  84. package/dist/v2/features/app/hooks/use-network-status.d.ts.map +1 -0
  85. package/dist/v2/features/app/hooks/use-network-status.js +58 -0
  86. package/dist/v2/features/app/hooks/use-network-status.js.map +1 -0
  87. package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts +1 -25
  88. package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts.map +1 -1
  89. package/dist/v2/features/app/hooks/use-sidebar-documents.js.map +1 -1
  90. package/dist/v2/features/app/index.d.ts +1 -1
  91. package/dist/v2/features/app/index.d.ts.map +1 -1
  92. package/dist/v2/features/collection/DocumentCollection.vue.d.ts.map +1 -1
  93. package/dist/v2/features/collection/DocumentCollection.vue.js.map +1 -1
  94. package/dist/v2/features/collection/DocumentCollection.vue.script.js +43 -277
  95. package/dist/v2/features/collection/DocumentCollection.vue.script.js.map +1 -1
  96. package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.d.ts.map +1 -1
  97. package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.js.map +1 -1
  98. package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js +25 -9
  99. package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js.map +1 -1
  100. package/dist/v2/features/editor/hooks/use-three-way-merge-editor.d.ts.map +1 -1
  101. package/dist/v2/features/editor/hooks/use-three-way-merge-editor.js +5 -5
  102. package/dist/v2/features/editor/hooks/use-three-way-merge-editor.js.map +1 -1
  103. package/dist/v2/types/configuration.d.ts +273 -7
  104. package/dist/v2/types/configuration.d.ts.map +1 -1
  105. package/dist/vue-styles.css +1389 -0
  106. package/package.json +21 -15
  107. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js +0 -7
  108. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js.map +0 -1
  109. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js +0 -51
  110. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js.map +0 -1
  111. package/dist/v2/features/collection/components/SyncConflictResolutionEditor.vue.d.ts.map +0 -1
  112. package/dist/v2/features/collection/components/SyncConflictResolutionEditor.vue.script.js.map +0 -1
  113. /package/dist/v2/features/{collection → app}/components/SyncConflictResolutionEditor.vue.d.ts +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"SyncConflictResolutionEditor.vue.js","names":[],"sources":["../../../../../src/v2/features/collection/components/SyncConflictResolutionEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { type merge } from '@scalar/json-magic/diff'\nimport { useToasts } from '@scalar/use-toasts'\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport { useSplitResize } from '@/v2/components/resize'\nimport { useThreeWayMergeEditor } from '@/v2/features/editor/hooks/use-three-way-merge-editor'\n\nconst { conflicts, baseDocument, resolvedDocument } = defineProps<{\n conflicts: ReturnType<typeof merge>['conflicts']\n baseDocument: Record<string, unknown>\n resolvedDocument: Record<string, unknown>\n}>()\n\nconst emit = defineEmits<{\n applyChanges: [\n payload: {\n resolvedDocument: Record<string, unknown>\n },\n ]\n}>()\n\nconst { toast } = useToasts()\n\nconst splitContainerRef = ref<HTMLDivElement>()\nconst topEditorsRowRef = ref<HTMLDivElement>()\nconst topPaneSize = ref(50)\nconst leftPaneSize = ref(50)\n\nconst { onHorizontalResizeStart, onVerticalResizeStart, stopActiveResize } =\n useSplitResize({\n horizontalContainerRef: topEditorsRowRef,\n verticalContainerRef: splitContainerRef,\n leftPaneSize,\n topPaneSize,\n horizontalMin: 20,\n horizontalMax: 80,\n verticalMin: 25,\n verticalMax: 75,\n })\n\nconst mergeEditor = useThreeWayMergeEditor({\n baseDocument,\n resolvedDocument,\n conflicts,\n onApplyChanges: (resolvedDoc) =>\n emit('applyChanges', { resolvedDocument: resolvedDoc }),\n onError: (message) => toast(message, 'error'),\n})\n\nconst conflictsLeft = computed(() => mergeEditor.conflictsLeft.value)\n\nconst topPaneStyle = computed(() => ({ height: `${topPaneSize.value}%` }))\nconst leftPaneStyle = computed(() => ({ width: `${leftPaneSize.value}%` }))\nconst rightPaneStyle = computed(() => ({\n width: `${100 - leftPaneSize.value}%`,\n}))\n\nconst localChangesEditorRef = ref<HTMLDivElement>()\nconst remoteChangesEditorRef = ref<HTMLDivElement>()\nconst resultEditorRef = ref<HTMLDivElement>()\n\nonMounted(() => {\n const localEl = localChangesEditorRef.value\n const remoteEl = remoteChangesEditorRef.value\n const resultEl = resultEditorRef.value\n if (localEl && remoteEl && resultEl) {\n mergeEditor.init({\n local: localEl,\n remote: remoteEl,\n result: resultEl,\n })\n }\n})\n\nonUnmounted(() => {\n stopActiveResize()\n mergeEditor.dispose()\n})\n</script>\n\n<template>\n <p class=\"text-c-2 text-xs\">\n Resolve conflicts inline in the full document editor. Use the buttons inside\n the editor for each conflict.\n </p>\n\n <div\n ref=\"splitContainerRef\"\n class=\"flex min-h-0 flex-1 flex-col overflow-hidden\">\n <div\n ref=\"topEditorsRowRef\"\n class=\"flex min-h-0 gap-1 p-1\"\n :style=\"topPaneStyle\">\n <div\n class=\"sync-editor-pane border-c-3 flex min-h-0 flex-col overflow-hidden rounded-lg border\"\n :style=\"leftPaneStyle\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 shrink-0 border-b px-2 py-1 text-[11px]\">\n Current\n </div>\n <div\n ref=\"localChangesEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n\n <button\n aria-label=\"Resize current and remote editors\"\n class=\"resize-handle resize-handle-col\"\n type=\"button\"\n @pointerdown=\"onHorizontalResizeStart\" />\n\n <div\n class=\"sync-editor-pane border-c-3 flex min-h-0 flex-col overflow-hidden rounded-lg border\"\n :style=\"rightPaneStyle\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 shrink-0 border-b px-2 py-1 text-[11px]\">\n Remote\n </div>\n <div\n ref=\"remoteChangesEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n </div>\n\n <button\n aria-label=\"Resize top and result editors\"\n class=\"resize-handle resize-handle-row\"\n type=\"button\"\n @pointerdown=\"onVerticalResizeStart\" />\n\n <div\n class=\"sync-editor-pane border-c-3 mx-1 mb-1 flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 flex shrink-0 items-center justify-between border-b px-2 py-1 text-[11px]\">\n <span> Result </span>\n <div class=\"flex items-center gap-2\">\n <span class=\"text-c-2 text-[11px] normal-case\">\n {{ conflictsLeft }} conflict{{ conflictsLeft === 1 ? '' : 's' }}\n left\n </span>\n <ScalarButton\n :disabled=\"conflictsLeft === 0\"\n size=\"xs\"\n type=\"button\"\n variant=\"outlined\"\n @click=\"mergeEditor.goToNextConflict\">\n Next Conflict\n </ScalarButton>\n </div>\n </div>\n <div\n ref=\"resultEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n\n <div class=\"flex shrink-0 items-center justify-end gap-2\">\n <ScalarButton\n :disabled=\"conflictsLeft > 0\"\n size=\"xs\"\n type=\"button\"\n @click=\"mergeEditor.applyResolvedConflicts\">\n Apply changes\n </ScalarButton>\n </div>\n </div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n.sync-layout-root {\n background: color-mix(\n in srgb,\n var(--scalar-color-background-1, #1e1e1e) 96%,\n transparent\n );\n}\n\n.sync-editor-pane {\n background: color-mix(\n in srgb,\n var(--scalar-color-background-1, #1e1e1e) 95%,\n transparent\n );\n}\n\n.sync-pane-title {\n letter-spacing: 0.03em;\n text-transform: uppercase;\n font-weight: 600;\n background: color-mix(\n in srgb,\n var(--scalar-color-background-2, #2d2d30) 85%,\n transparent\n );\n}\n\n.resize-handle {\n position: relative;\n display: block;\n flex-shrink: 0;\n border: none;\n border-radius: 999px;\n background: transparent;\n transition:\n background-color 0.12s ease,\n box-shadow 0.12s ease;\n}\n\n.resize-handle::before {\n content: '';\n position: absolute;\n border-radius: 999px;\n opacity: 1;\n transition:\n background-color 0.12s ease,\n transform 0.12s ease;\n}\n\n.resize-handle:hover {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 12%,\n transparent\n );\n}\n\n.resize-handle:active {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 18%,\n transparent\n );\n}\n\n.resize-handle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 1px\n color-mix(in srgb, var(--scalar-color-accent, #007acc) 70%, transparent);\n}\n\n.resize-handle-col {\n width: 8px;\n min-height: 44px;\n margin: 2px 0;\n cursor: col-resize;\n}\n\n.resize-handle-col::before {\n top: 50%;\n left: 50%;\n width: 1px;\n height: calc(100% - 8px);\n transform: translate(-50%, -50%);\n background: color-mix(\n in srgb,\n var(--scalar-color-border, #3c3c3c) 85%,\n transparent\n );\n}\n\n.resize-handle-row {\n height: 8px;\n margin: 0 4px;\n cursor: row-resize;\n}\n\n.resize-handle-row::before {\n top: 50%;\n left: 50%;\n width: calc(100% - 8px);\n height: 1px;\n transform: translate(-50%, -50%);\n background: color-mix(\n in srgb,\n var(--scalar-color-border, #3c3c3c) 85%,\n transparent\n );\n}\n\n.resize-handle-col:hover::before,\n.resize-handle-col:active::before,\n.resize-handle-row:hover::before,\n.resize-handle-row:active::before {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 78%,\n transparent\n );\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight-box-single) {\n border: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-top) {\n border-top: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-middle) {\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-bottom) {\n border-bottom: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, #fde047 35%, transparent);\n box-sizing: border-box;\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"SyncConflictResolutionEditor.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/SyncConflictResolutionEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { type merge } from '@scalar/json-magic/diff'\nimport { useToasts } from '@scalar/use-toasts'\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport { useSplitResize } from '@/v2/components/resize'\nimport { useThreeWayMergeEditor } from '@/v2/features/editor/hooks/use-three-way-merge-editor'\n\nconst { conflicts, baseDocument, resolvedDocument } = defineProps<{\n conflicts: ReturnType<typeof merge>['conflicts']\n baseDocument: Record<string, unknown>\n resolvedDocument: Record<string, unknown>\n}>()\n\nconst emit = defineEmits<{\n applyChanges: [\n payload: {\n resolvedDocument: Record<string, unknown>\n },\n ]\n}>()\n\nconst { toast } = useToasts()\n\nconst splitContainerRef = ref<HTMLDivElement>()\nconst topEditorsRowRef = ref<HTMLDivElement>()\nconst topPaneSize = ref(50)\nconst leftPaneSize = ref(50)\n\nconst { onHorizontalResizeStart, onVerticalResizeStart, stopActiveResize } =\n useSplitResize({\n horizontalContainerRef: topEditorsRowRef,\n verticalContainerRef: splitContainerRef,\n leftPaneSize,\n topPaneSize,\n horizontalMin: 20,\n horizontalMax: 80,\n verticalMin: 25,\n verticalMax: 75,\n })\n\nconst mergeEditor = useThreeWayMergeEditor({\n baseDocument,\n resolvedDocument,\n conflicts,\n onApplyChanges: (resolvedDoc) =>\n emit('applyChanges', { resolvedDocument: resolvedDoc }),\n onError: (message) => toast(message, 'error'),\n})\n\nconst conflictsLeft = computed(() => mergeEditor.conflictsLeft.value)\n\nconst topPaneStyle = computed(() => ({ height: `${topPaneSize.value}%` }))\nconst leftPaneStyle = computed(() => ({ width: `${leftPaneSize.value}%` }))\nconst rightPaneStyle = computed(() => ({\n width: `${100 - leftPaneSize.value}%`,\n}))\n\nconst localChangesEditorRef = ref<HTMLDivElement>()\nconst remoteChangesEditorRef = ref<HTMLDivElement>()\nconst resultEditorRef = ref<HTMLDivElement>()\n\nonMounted(() => {\n const localEl = localChangesEditorRef.value\n const remoteEl = remoteChangesEditorRef.value\n const resultEl = resultEditorRef.value\n if (localEl && remoteEl && resultEl) {\n mergeEditor.init({\n local: localEl,\n remote: remoteEl,\n result: resultEl,\n })\n }\n})\n\nonUnmounted(() => {\n stopActiveResize()\n mergeEditor.dispose()\n})\n</script>\n\n<template>\n <p class=\"text-c-2 text-xs\">\n Resolve conflicts inline in the full document editor. Use the buttons inside\n the editor for each conflict.\n </p>\n\n <div\n ref=\"splitContainerRef\"\n class=\"flex min-h-0 flex-1 flex-col overflow-hidden\">\n <div\n ref=\"topEditorsRowRef\"\n class=\"flex min-h-0 gap-1 p-1\"\n :style=\"topPaneStyle\">\n <div\n class=\"sync-editor-pane border-c-3 flex min-h-0 flex-col overflow-hidden rounded-lg border\"\n :style=\"leftPaneStyle\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 shrink-0 border-b px-2 py-1 text-[11px]\">\n Current\n </div>\n <div\n ref=\"localChangesEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n\n <button\n aria-label=\"Resize current and remote editors\"\n class=\"resize-handle resize-handle-col\"\n type=\"button\"\n @pointerdown=\"onHorizontalResizeStart\" />\n\n <div\n class=\"sync-editor-pane border-c-3 flex min-h-0 flex-col overflow-hidden rounded-lg border\"\n :style=\"rightPaneStyle\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 shrink-0 border-b px-2 py-1 text-[11px]\">\n Remote\n </div>\n <div\n ref=\"remoteChangesEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n </div>\n\n <button\n aria-label=\"Resize top and result editors\"\n class=\"resize-handle resize-handle-row\"\n type=\"button\"\n @pointerdown=\"onVerticalResizeStart\" />\n\n <div\n class=\"sync-editor-pane border-c-3 mx-1 mb-1 flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 flex shrink-0 items-center justify-between border-b px-2 py-1 text-[11px]\">\n <span> Result </span>\n <div class=\"flex items-center gap-2\">\n <span class=\"text-c-2 text-[11px] normal-case\">\n {{ conflictsLeft }} conflict{{ conflictsLeft === 1 ? '' : 's' }}\n left\n </span>\n <ScalarButton\n :disabled=\"conflictsLeft === 0\"\n size=\"xs\"\n type=\"button\"\n variant=\"outlined\"\n @click=\"mergeEditor.goToNextConflict\">\n Next Conflict\n </ScalarButton>\n </div>\n </div>\n <div\n ref=\"resultEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n\n <div class=\"flex shrink-0 items-center justify-end gap-2\">\n <ScalarButton\n :disabled=\"conflictsLeft > 0\"\n size=\"xs\"\n type=\"button\"\n @click=\"mergeEditor.applyResolvedConflicts\">\n Apply changes\n </ScalarButton>\n </div>\n </div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n.sync-layout-root {\n background: color-mix(\n in srgb,\n var(--scalar-color-background-1, #1e1e1e) 96%,\n transparent\n );\n}\n\n.sync-editor-pane {\n background: color-mix(\n in srgb,\n var(--scalar-color-background-1, #1e1e1e) 95%,\n transparent\n );\n}\n\n.sync-pane-title {\n letter-spacing: 0.03em;\n text-transform: uppercase;\n font-weight: 600;\n background: color-mix(\n in srgb,\n var(--scalar-color-background-2, #2d2d30) 85%,\n transparent\n );\n}\n\n.resize-handle {\n position: relative;\n display: block;\n flex-shrink: 0;\n border: none;\n border-radius: 999px;\n background: transparent;\n transition:\n background-color 0.12s ease,\n box-shadow 0.12s ease;\n}\n\n.resize-handle::before {\n content: '';\n position: absolute;\n border-radius: 999px;\n opacity: 1;\n transition:\n background-color 0.12s ease,\n transform 0.12s ease;\n}\n\n.resize-handle:hover {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 12%,\n transparent\n );\n}\n\n.resize-handle:active {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 18%,\n transparent\n );\n}\n\n.resize-handle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 1px\n color-mix(in srgb, var(--scalar-color-accent, #007acc) 70%, transparent);\n}\n\n.resize-handle-col {\n width: 8px;\n min-height: 44px;\n margin: 2px 0;\n cursor: col-resize;\n}\n\n.resize-handle-col::before {\n top: 50%;\n left: 50%;\n width: 1px;\n height: calc(100% - 8px);\n transform: translate(-50%, -50%);\n background: color-mix(\n in srgb,\n var(--scalar-color-border, #3c3c3c) 85%,\n transparent\n );\n}\n\n.resize-handle-row {\n height: 8px;\n margin: 0 4px;\n cursor: row-resize;\n}\n\n.resize-handle-row::before {\n top: 50%;\n left: 50%;\n width: calc(100% - 8px);\n height: 1px;\n transform: translate(-50%, -50%);\n background: color-mix(\n in srgb,\n var(--scalar-color-border, #3c3c3c) 85%,\n transparent\n );\n}\n\n.resize-handle-col:hover::before,\n.resize-handle-col:active::before,\n.resize-handle-row:hover::before,\n.resize-handle-row:active::before {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 78%,\n transparent\n );\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight-box-single) {\n border: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-top) {\n border-top: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-middle) {\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-bottom) {\n border-bottom: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, #fde047 35%, transparent);\n box-sizing: border-box;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;EAeA,MAAM,OAAO;EAQb,MAAM,EAAE,UAAU,WAAU;EAE5B,MAAM,oBAAoB,KAAoB;EAC9C,MAAM,mBAAmB,KAAoB;EAC7C,MAAM,cAAc,IAAI,GAAE;EAC1B,MAAM,eAAe,IAAI,GAAE;EAE3B,MAAM,EAAE,yBAAyB,uBAAuB,qBACtD,eAAe;GACb,wBAAwB;GACxB,sBAAsB;GACtB;GACA;GACA,eAAe;GACf,eAAe;GACf,aAAa;GACb,aAAa;GACd,CAAA;EAEH,MAAM,cAAc,uBAAuB;GACzC,cAAW,QAAA;GACX,kBAAe,QAAA;GACf,WAAQ,QAAA;GACR,iBAAiB,gBACf,KAAK,gBAAgB,EAAE,kBAAkB,aAAa,CAAC;GACzD,UAAU,YAAY,MAAM,SAAS,QAAQ;GAC9C,CAAA;EAED,MAAM,gBAAgB,eAAe,YAAY,cAAc,MAAK;EAEpE,MAAM,eAAe,gBAAgB,EAAE,QAAQ,GAAG,YAAY,MAAM,IAAI,EAAC;EACzE,MAAM,gBAAgB,gBAAgB,EAAE,OAAO,GAAG,aAAa,MAAM,IAAI,EAAC;EAC1E,MAAM,iBAAiB,gBAAgB,EACrC,OAAO,GAAG,MAAM,aAAa,MAAM,IACpC,EAAC;EAEF,MAAM,wBAAwB,KAAoB;EAClD,MAAM,yBAAyB,KAAoB;EACnD,MAAM,kBAAkB,KAAoB;AAE5C,kBAAgB;GACd,MAAM,UAAU,sBAAsB;GACtC,MAAM,WAAW,uBAAuB;GACxC,MAAM,WAAW,gBAAgB;AACjC,OAAI,WAAW,YAAY,SACzB,aAAY,KAAK;IACf,OAAO;IACP,QAAQ;IACR,QAAQ;IACT,CAAA;IAEJ;AAED,oBAAkB;AAChB,qBAAiB;AACjB,eAAY,SAAQ;IACrB;;qFAIC,mBAGI,KAAA,EAHD,OAAM,oBAAkB,EAAC,gHAG5B,GAAA,GAEA,mBA8EM,OAAA;aA7EA;IAAJ,KAAI;IACJ,OAAM;;IACN,mBAiCM,OAAA;cAhCA;KAAJ,KAAI;KACJ,OAAM;KACL,OAAK,eAAE,aAAA,MAAY;;KACpB,mBAUM,OAAA;MATJ,OAAM;MACL,OAAK,eAAE,cAAA,MAAa;mCACrB,mBAGM,OAAA,EAFJ,OAAM,+EAA6E,EAAC,aAEtF,GAAA,GACA,mBAE+B,OAAA;eADzB;MAAJ,KAAI;MACJ,OAAM;;KAGV,mBAI2C,UAAA;MAHzC,cAAW;MACX,OAAM;MACN,MAAK;MACJ,eAAW,OAAA,OAAA,OAAA,MAAA,GAAA,SAAE,MAAA,wBAAA,IAAA,MAAA,wBAAA,CAAA,GAAA,KAAuB;;KAEvC,mBAUM,OAAA;MATJ,OAAM;MACL,OAAK,eAAE,eAAA,MAAc;mCACtB,mBAGM,OAAA,EAFJ,OAAM,+EAA6E,EAAC,YAEtF,GAAA,GACA,mBAE+B,OAAA;eADzB;MAAJ,KAAI;MACJ,OAAM;;;IAIZ,mBAIyC,UAAA;KAHvC,cAAW;KACX,OAAM;KACN,MAAK;KACJ,eAAW,OAAA,OAAA,OAAA,MAAA,GAAA,SAAE,MAAA,sBAAA,IAAA,MAAA,sBAAA,CAAA,GAAA,KAAqB;;IAErC,mBAuBM,OAvBN,YAuBM,CArBJ,mBAiBM,OAjBN,YAiBM,CAAA,OAAA,OAAA,OAAA,KAfJ,mBAAqB,QAAA,MAAf,YAAQ,GAAA,GACd,mBAaM,OAbN,YAaM,CAZJ,mBAGO,QAHP,YAGO,gBAFF,cAAA,MAAa,GAAG,cAAS,gBAAG,cAAA,UAAa,IAAA,KAAA,IAAA,GAAoB,UAElE,EAAA,EACA,YAOe,MAAA,aAAA,EAAA;KANZ,UAAU,cAAA,UAAa;KACxB,MAAK;KACL,MAAK;KACL,SAAQ;KACP,SAAO,MAAA,YAAW,CAAC;;4BAEtB,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAFwC,mBAExC,GAAA,CAAA,EAAA,CAAA;;wCAGJ,mBAE+B,OAAA;cADzB;KAAJ,KAAI;KACJ,OAAM;;IAGV,mBAQM,OARN,YAQM,CAPJ,YAMe,MAAA,aAAA,EAAA;KALZ,UAAU,cAAA,QAAa;KACxB,MAAK;KACL,MAAK;KACJ,SAAO,MAAA,YAAW,CAAC;;4BAEtB,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAF8C,mBAE9C,GAAA,CAAA,EAAA,CAAA"}
@@ -21,12 +21,15 @@ type CheckVersionConflictResult = {
21
21
  * - the original document (last-known remote, kept in
22
22
  * `workspaceStore.getOriginalDocument`),
23
23
  * - the editable workspace document (with the user's local edits),
24
- * - the freshly-fetched remote document advertised at `registryCommitHash`.
24
+ * - the freshly-fetched remote document.
25
25
  *
26
- * The result is cached on the document under
27
- * `x-scalar-registry-meta.{conflictCheckedAgainstHash, hasConflict}` so
28
- * subsequent renders can short-circuit. The cache is only valid while
29
- * `conflictCheckedAgainstHash === registryCommitHash`.
26
+ * `registryCommitHash` is the hash advertised by the registry version
27
+ * listing and acts as the pre-fetch cache key: when it matches the
28
+ * previously stored `conflictCheckedAgainstHash`, we return the cached
29
+ * result without touching the network. After a fresh fetch, the cache
30
+ * is rewritten with the authoritative `versionSha` returned alongside
31
+ * the document body so subsequent comparisons settle on the hash the
32
+ * registry actually served.
30
33
  *
31
34
  * Writing to `x-scalar-registry-meta` does not flip the document's
32
35
  * `x-scalar-is-dirty` flag (the workspace store excludes registry meta from
@@ -1 +1 @@
1
- {"version":3,"file":"check-version-conflict.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/check-version-conflict.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAEpE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAI1E,iDAAiD;AACjD,KAAK,0BAA0B,GAC3B;IACE,EAAE,EAAE,IAAI,CAAA;IACR,WAAW,EAAE,OAAO,CAAA;IACpB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAA;CACnB,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,oBAAoB,GAAU,0FAQxC;IACD,cAAc,EAAE,cAAc,CAAA;IAC9B,OAAO,EAAE,0BAA0B,CAAA;IACnC,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,KAAG,OAAO,CAAC,0BAA0B,CAiDrC,CAAA"}
1
+ {"version":3,"file":"check-version-conflict.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/check-version-conflict.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAEpE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAI1E,iDAAiD;AACjD,KAAK,0BAA0B,GAC3B;IACE,EAAE,EAAE,IAAI,CAAA;IACR,WAAW,EAAE,OAAO,CAAA;IACpB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAA;CACnB,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,oBAAoB,GAAU,0FAQxC;IACD,cAAc,EAAE,cAAc,CAAA;IAC9B,OAAO,EAAE,0BAA0B,CAAA;IACnC,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,KAAG,OAAO,CAAC,0BAA0B,CAiDrC,CAAA"}
@@ -8,12 +8,15 @@ import { detectDocumentConflicts } from "./detect-document-conflicts.js";
8
8
  * - the original document (last-known remote, kept in
9
9
  * `workspaceStore.getOriginalDocument`),
10
10
  * - the editable workspace document (with the user's local edits),
11
- * - the freshly-fetched remote document advertised at `registryCommitHash`.
11
+ * - the freshly-fetched remote document.
12
12
  *
13
- * The result is cached on the document under
14
- * `x-scalar-registry-meta.{conflictCheckedAgainstHash, hasConflict}` so
15
- * subsequent renders can short-circuit. The cache is only valid while
16
- * `conflictCheckedAgainstHash === registryCommitHash`.
13
+ * `registryCommitHash` is the hash advertised by the registry version
14
+ * listing and acts as the pre-fetch cache key: when it matches the
15
+ * previously stored `conflictCheckedAgainstHash`, we return the cached
16
+ * result without touching the network. After a fresh fetch, the cache
17
+ * is rewritten with the authoritative `versionSha` returned alongside
18
+ * the document body so subsequent comparisons settle on the hash the
19
+ * registry actually served.
17
20
  *
18
21
  * Writing to `x-scalar-registry-meta` does not flip the document's
19
22
  * `x-scalar-is-dirty` flag (the workspace store excludes registry meta from
@@ -53,7 +56,7 @@ var checkVersionConflict = async ({ workspaceStore, fetcher, documentName, names
53
56
  const hasConflict = detectDocumentConflicts({
54
57
  original,
55
58
  local: document,
56
- remote: result.data
59
+ remote: result.data.document
57
60
  });
58
61
  document["x-scalar-registry-meta"] = {
59
62
  ...meta ?? {
@@ -64,7 +67,7 @@ var checkVersionConflict = async ({ workspaceStore, fetcher, documentName, names
64
67
  namespace,
65
68
  slug,
66
69
  version,
67
- conflictCheckedAgainstHash: registryCommitHash,
70
+ conflictCheckedAgainstHash: result.data.versionSha,
68
71
  hasConflict
69
72
  };
70
73
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"check-version-conflict.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/check-version-conflict.ts"],"sourcesContent":["import type { WorkspaceStore } from '@scalar/workspace-store/client'\n\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\n\nimport { detectDocumentConflicts } from './detect-document-conflicts'\n\n/** Result returned by `checkVersionConflict`. */\ntype CheckVersionConflictResult =\n | {\n ok: true\n hasConflict: boolean\n /**\n * `true` when the result was already cached on the document for the\n * current registry hash and we did not have to fetch / recompute.\n */\n fromCache: boolean\n }\n | { ok: false; error: string }\n\n/**\n * Compute (or reuse a cached) conflict-check result for a registry-backed\n * workspace document and persist it on `x-scalar-registry-meta`.\n *\n * The check is a three-way merge between:\n * - the original document (last-known remote, kept in\n * `workspaceStore.getOriginalDocument`),\n * - the editable workspace document (with the user's local edits),\n * - the freshly-fetched remote document advertised at `registryCommitHash`.\n *\n * The result is cached on the document under\n * `x-scalar-registry-meta.{conflictCheckedAgainstHash, hasConflict}` so\n * subsequent renders can short-circuit. The cache is only valid while\n * `conflictCheckedAgainstHash === registryCommitHash`.\n *\n * Writing to `x-scalar-registry-meta` does not flip the document's\n * `x-scalar-is-dirty` flag (the workspace store excludes registry meta from\n * the dirty tracker), so this side effect is safe to perform in the\n * background.\n */\nexport const checkVersionConflict = async ({\n workspaceStore,\n fetcher,\n documentName,\n namespace,\n slug,\n version,\n registryCommitHash,\n}: {\n workspaceStore: WorkspaceStore\n fetcher: ImportDocumentFromRegistry\n /** Name of the workspace document the cache lives on. */\n documentName: string\n namespace: string\n slug: string\n version: string\n /**\n * Commit hash advertised by the registry for this version. The conflict\n * cache is keyed on this hash; passing `undefined` skips the check.\n */\n registryCommitHash?: string\n}): Promise<CheckVersionConflictResult> => {\n if (!registryCommitHash) {\n return { ok: false, error: 'No registry commit hash available for this version.' }\n }\n\n const document = workspaceStore.workspace.documents[documentName]\n if (!document) {\n return { ok: false, error: `Document \"${documentName}\" is not loaded in the workspace.` }\n }\n\n const meta = document['x-scalar-registry-meta']\n\n // Cache hit - the previous check was performed against the registry hash\n // we are being asked about, so we can return the stored result without\n // touching the network.\n if (meta?.conflictCheckedAgainstHash === registryCommitHash && typeof meta?.hasConflict === 'boolean') {\n return { ok: true, hasConflict: meta.hasConflict, fromCache: true }\n }\n\n const original = workspaceStore.getOriginalDocument(documentName)\n if (!original) {\n return { ok: false, error: `Original document for \"${documentName}\" is unavailable.` }\n }\n\n const result = await fetcher({ namespace, slug, version })\n if (!result.ok) {\n return { ok: false, error: `Failed to fetch document: ${result.error || 'Unknown error'}` }\n }\n\n const hasConflict = detectDocumentConflicts({\n original: original as Record<string, unknown>,\n local: document as unknown as Record<string, unknown>,\n remote: result.data,\n })\n\n // Persist the cache on the document. The workspace store's dirty tracker\n // skips `x-scalar-registry-meta` writes, so this does not mark the\n // document as having local edits.\n const next = {\n ...(meta ?? { namespace, slug, version }),\n namespace,\n slug,\n version,\n conflictCheckedAgainstHash: registryCommitHash,\n hasConflict,\n }\n document['x-scalar-registry-meta'] = next\n\n return { ok: true, hasConflict, fromCache: false }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuCA,IAAa,uBAAuB,OAAO,EACzC,gBACA,SACA,cACA,WACA,MACA,SACA,yBAcyC;AACzC,KAAI,CAAC,mBACH,QAAO;EAAE,IAAI;EAAO,OAAO;EAAuD;CAGpF,MAAM,WAAW,eAAe,UAAU,UAAU;AACpD,KAAI,CAAC,SACH,QAAO;EAAE,IAAI;EAAO,OAAO,aAAa,aAAa;EAAoC;CAG3F,MAAM,OAAO,SAAS;AAKtB,KAAI,MAAM,+BAA+B,sBAAsB,OAAO,MAAM,gBAAgB,UAC1F,QAAO;EAAE,IAAI;EAAM,aAAa,KAAK;EAAa,WAAW;EAAM;CAGrE,MAAM,WAAW,eAAe,oBAAoB,aAAa;AACjE,KAAI,CAAC,SACH,QAAO;EAAE,IAAI;EAAO,OAAO,0BAA0B,aAAa;EAAoB;CAGxF,MAAM,SAAS,MAAM,QAAQ;EAAE;EAAW;EAAM;EAAS,CAAC;AAC1D,KAAI,CAAC,OAAO,GACV,QAAO;EAAE,IAAI;EAAO,OAAO,6BAA6B,OAAO,SAAS;EAAmB;CAG7F,MAAM,cAAc,wBAAwB;EAChC;EACV,OAAO;EACP,QAAQ,OAAO;EAChB,CAAC;AAaF,UAAS,4BARI;EACX,GAAI,QAAQ;GAAE;GAAW;GAAM;GAAS;EACxC;EACA;EACA;EACA,4BAA4B;EAC5B;EACD;AAGD,QAAO;EAAE,IAAI;EAAM;EAAa,WAAW;EAAO"}
1
+ {"version":3,"file":"check-version-conflict.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/check-version-conflict.ts"],"sourcesContent":["import type { WorkspaceStore } from '@scalar/workspace-store/client'\n\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\n\nimport { detectDocumentConflicts } from './detect-document-conflicts'\n\n/** Result returned by `checkVersionConflict`. */\ntype CheckVersionConflictResult =\n | {\n ok: true\n hasConflict: boolean\n /**\n * `true` when the result was already cached on the document for the\n * current registry hash and we did not have to fetch / recompute.\n */\n fromCache: boolean\n }\n | { ok: false; error: string }\n\n/**\n * Compute (or reuse a cached) conflict-check result for a registry-backed\n * workspace document and persist it on `x-scalar-registry-meta`.\n *\n * The check is a three-way merge between:\n * - the original document (last-known remote, kept in\n * `workspaceStore.getOriginalDocument`),\n * - the editable workspace document (with the user's local edits),\n * - the freshly-fetched remote document.\n *\n * `registryCommitHash` is the hash advertised by the registry version\n * listing and acts as the pre-fetch cache key: when it matches the\n * previously stored `conflictCheckedAgainstHash`, we return the cached\n * result without touching the network. After a fresh fetch, the cache\n * is rewritten with the authoritative `versionSha` returned alongside\n * the document body so subsequent comparisons settle on the hash the\n * registry actually served.\n *\n * Writing to `x-scalar-registry-meta` does not flip the document's\n * `x-scalar-is-dirty` flag (the workspace store excludes registry meta from\n * the dirty tracker), so this side effect is safe to perform in the\n * background.\n */\nexport const checkVersionConflict = async ({\n workspaceStore,\n fetcher,\n documentName,\n namespace,\n slug,\n version,\n registryCommitHash,\n}: {\n workspaceStore: WorkspaceStore\n fetcher: ImportDocumentFromRegistry\n /** Name of the workspace document the cache lives on. */\n documentName: string\n namespace: string\n slug: string\n version: string\n /**\n * Commit hash advertised by the registry for this version. The conflict\n * cache is keyed on this hash; passing `undefined` skips the check.\n */\n registryCommitHash?: string\n}): Promise<CheckVersionConflictResult> => {\n if (!registryCommitHash) {\n return { ok: false, error: 'No registry commit hash available for this version.' }\n }\n\n const document = workspaceStore.workspace.documents[documentName]\n if (!document) {\n return { ok: false, error: `Document \"${documentName}\" is not loaded in the workspace.` }\n }\n\n const meta = document['x-scalar-registry-meta']\n\n // Cache hit - the previous check was performed against the registry hash\n // we are being asked about, so we can return the stored result without\n // touching the network.\n if (meta?.conflictCheckedAgainstHash === registryCommitHash && typeof meta?.hasConflict === 'boolean') {\n return { ok: true, hasConflict: meta.hasConflict, fromCache: true }\n }\n\n const original = workspaceStore.getOriginalDocument(documentName)\n if (!original) {\n return { ok: false, error: `Original document for \"${documentName}\" is unavailable.` }\n }\n\n const result = await fetcher({ namespace, slug, version })\n if (!result.ok) {\n return { ok: false, error: `Failed to fetch document: ${result.error || 'Unknown error'}` }\n }\n\n const hasConflict = detectDocumentConflicts({\n original: original as Record<string, unknown>,\n local: document as unknown as Record<string, unknown>,\n remote: result.data.document,\n })\n\n // Persist the cache on the document. The workspace store's dirty tracker\n // skips `x-scalar-registry-meta` writes, so this does not mark the\n // document as having local edits.\n const next = {\n ...(meta ?? { namespace, slug, version }),\n namespace,\n slug,\n version,\n conflictCheckedAgainstHash: result.data.versionSha,\n hasConflict,\n }\n document['x-scalar-registry-meta'] = next\n\n return { ok: true, hasConflict, fromCache: false }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,IAAa,uBAAuB,OAAO,EACzC,gBACA,SACA,cACA,WACA,MACA,SACA,yBAcyC;AACzC,KAAI,CAAC,mBACH,QAAO;EAAE,IAAI;EAAO,OAAO;EAAuD;CAGpF,MAAM,WAAW,eAAe,UAAU,UAAU;AACpD,KAAI,CAAC,SACH,QAAO;EAAE,IAAI;EAAO,OAAO,aAAa,aAAa;EAAoC;CAG3F,MAAM,OAAO,SAAS;AAKtB,KAAI,MAAM,+BAA+B,sBAAsB,OAAO,MAAM,gBAAgB,UAC1F,QAAO;EAAE,IAAI;EAAM,aAAa,KAAK;EAAa,WAAW;EAAM;CAGrE,MAAM,WAAW,eAAe,oBAAoB,aAAa;AACjE,KAAI,CAAC,SACH,QAAO;EAAE,IAAI;EAAO,OAAO,0BAA0B,aAAa;EAAoB;CAGxF,MAAM,SAAS,MAAM,QAAQ;EAAE;EAAW;EAAM;EAAS,CAAC;AAC1D,KAAI,CAAC,OAAO,GACV,QAAO;EAAE,IAAI;EAAO,OAAO,6BAA6B,OAAO,SAAS;EAAmB;CAG7F,MAAM,cAAc,wBAAwB;EAChC;EACV,OAAO;EACP,QAAQ,OAAO,KAAK;EACrB,CAAC;AAaF,UAAS,4BARI;EACX,GAAI,QAAQ;GAAE;GAAW;GAAM;GAAS;EACxC;EACA;EACA;EACA,4BAA4B,OAAO,KAAK;EACxC;EACD;AAGD,QAAO;EAAE,IAAI;EAAM;EAAa,WAAW;EAAO"}
@@ -1,6 +1,6 @@
1
1
  import type { ClientPlugin } from '@scalar/oas-utils/helpers';
2
2
  import type { Theme } from '@scalar/themes';
3
- import type { ImportDocumentFromRegistry } from '../../../../v2/types/configuration';
3
+ import type { RegistryAdapter } from '../../../../v2/types/configuration';
4
4
  import type { ClientLayout } from '../../../../v2/types/layout';
5
5
  import type { ApiClientOptions } from '../../../../v2/types/options';
6
6
  type CreateApiClientOptions = {
@@ -25,10 +25,13 @@ type CreateApiClientOptions = {
25
25
  */
26
26
  fallbackThemeSlug?: () => string;
27
27
  /**
28
- * Fetches the full document from registry by meta. When set, registry meta takes priority
29
- * over x-scalar-original-source-url when syncing. Returns the document as a plain object.
28
+ * Adapter wiring the API client up to an external registry. The
29
+ * adapter bundles the registry document list, the per-document fetch
30
+ * callback (used for syncing) and the publish callback used to create
31
+ * a brand-new registry version. Optional at the top level - omit to
32
+ * opt out of registry features entirely.
30
33
  */
31
- fetchRegistryDocument?: ImportDocumentFromRegistry;
34
+ registry?: RegistryAdapter;
32
35
  /**
33
36
  * Whether or not to send telemetry events.
34
37
  */
@@ -48,7 +51,7 @@ export declare const createAppRouter: (layout: CreateApiClientOptions["layout"])
48
51
  /**
49
52
  * Create the API Client with router and passes in the workspace store as a prop
50
53
  */
51
- export declare const createApiClientApp: (el: HTMLElement | null, { layout, plugins, customThemes, fallbackThemeSlug, fetchRegistryDocument, telemetry, options, }: CreateApiClientOptions) => Promise<{
54
+ export declare const createApiClientApp: (el: HTMLElement | null, { layout, plugins, customThemes, fallbackThemeSlug, registry, telemetry, options, }: CreateApiClientOptions) => Promise<{
52
55
  app: import("vue").App<Element>;
53
56
  state: import("../../../../v2/features/app/app-state.js").AppState;
54
57
  } | undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"create-api-client-app.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/create-api-client-app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAO3C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAI1D,KAAK,sBAAsB,GAAG;IAC5B;;;;;OAKG;IACH,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IACtC;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,EAAE,CAAA;IACxB;;OAEG;IACH,YAAY,CAAC,EAAE,KAAK,EAAE,CAAA;IACtB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAA;IAChC;;;OAGG;IACH,qBAAqB,CAAC,EAAE,0BAA0B,CAAA;IAClD;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kCAAkC;IAClC,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAC9B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,gBAAgB,EAAE,aAAa,GAAG,mBAAmB,CAAC,CAAA;AAE7F;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,sBAAsB,CAAC,QAAQ,CAAC,gCAQvE,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC7B,IAAI,WAAW,GAAG,IAAI,EACtB,kGAQG,sBAAsB;;;cAuC1B,CAAA"}
1
+ {"version":3,"file":"create-api-client-app.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/create-api-client-app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAO3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAI1D,KAAK,sBAAsB,GAAG;IAC5B;;;;;OAKG;IACH,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IACtC;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,EAAE,CAAA;IACxB;;OAEG;IACH,YAAY,CAAC,EAAE,KAAK,EAAE,CAAA;IACtB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAA;IAChC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kCAAkC;IAClC,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAC9B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,gBAAgB,EAAE,aAAa,GAAG,mBAAmB,CAAC,CAAA;AAE7F;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,sBAAsB,CAAC,QAAQ,CAAC,gCAQvE,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC7B,IAAI,WAAW,GAAG,IAAI,EACtB,qFAQG,sBAAsB;;;cAuC1B,CAAA"}
@@ -21,7 +21,7 @@ var createAppRouter = (layout) => {
21
21
  /**
22
22
  * Create the API Client with router and passes in the workspace store as a prop
23
23
  */
24
- var createApiClientApp = async (el, { layout = "desktop", plugins, customThemes, fallbackThemeSlug, fetchRegistryDocument, telemetry = true, options }) => {
24
+ var createApiClientApp = async (el, { layout = "desktop", plugins, customThemes, fallbackThemeSlug, registry, telemetry = true, options }) => {
25
25
  const router = createAppRouter(layout);
26
26
  const state = await createAppState({
27
27
  router,
@@ -36,7 +36,7 @@ var createApiClientApp = async (el, { layout = "desktop", plugins, customThemes,
36
36
  plugins,
37
37
  getAppState: () => state,
38
38
  getCommandPaletteState: () => commandPaletteState,
39
- fetchRegistryDocument
39
+ registry
40
40
  });
41
41
  app.use(router);
42
42
  if (!el) {
@@ -1 +1 @@
1
- {"version":3,"file":"create-api-client-app.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/create-api-client-app.ts"],"sourcesContent":["import type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { Theme } from '@scalar/themes'\nimport { createApp } from 'vue'\nimport { createRouter as createVueRouter, createWebHashHistory, createWebHistory } from 'vue-router'\n\nimport App from '@/v2/features/app/App.vue'\nimport { createAppState } from '@/v2/features/app/app-state'\nimport { ROUTES } from '@/v2/features/app/helpers/routes'\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\nimport type { ClientLayout } from '@/v2/types/layout'\nimport type { ApiClientOptions } from '@/v2/types/options'\n\nimport { useCommandPaletteState } from '../../command-palette/hooks/use-command-palette-state'\n\ntype CreateApiClientOptions = {\n /**\n * The layout of the client, limited to web or desktop in app\n * @see {@link ClientLayout}\n *\n * @default 'desktop'\n */\n layout: Exclude<ClientLayout, 'modal'>\n /**\n * Api client plugins to include in the app\n */\n plugins?: ClientPlugin[]\n /**\n * Custom themes to include in the app\n */\n customThemes?: Theme[]\n /**\n * Fallback theme slug to use if no theme is selected for the workspace\n * @default 'default'\n */\n fallbackThemeSlug?: () => string\n /**\n * Fetches the full document from registry by meta. When set, registry meta takes priority\n * over x-scalar-original-source-url when syncing. Returns the document as a plain object.\n */\n fetchRegistryDocument?: ImportDocumentFromRegistry\n /**\n * Whether or not to send telemetry events.\n */\n telemetry?: boolean\n /** Runtime behaviour overrides */\n options?: ApiClientAppOptions\n}\n\n/**\n * Runtime behaviour overrides shared between createApiClientApp and createAppState.\n * Derived from the canonical ApiClientOptions to guarantee structural compatibility.\n */\nexport type ApiClientAppOptions = Pick<ApiClientOptions, 'customFetch' | 'oauth2RedirectUri'>\n\n/**\n * Creates the appropriate router with the appropriate routes based on the layout\n */\nexport const createAppRouter = (layout: CreateApiClientOptions['layout']) => {\n // Web uses the standard HTML5 history API\n if (layout === 'web') {\n return createVueRouter({ history: createWebHistory(), routes: ROUTES })\n }\n\n // Electron app has to use the webHashHistory due to file routing\n return createVueRouter({ history: createWebHashHistory(), routes: ROUTES })\n}\n\n/**\n * Create the API Client with router and passes in the workspace store as a prop\n */\nexport const createApiClientApp = async (\n el: HTMLElement | null,\n {\n layout = 'desktop',\n plugins,\n customThemes,\n fallbackThemeSlug,\n fetchRegistryDocument,\n telemetry = true,\n options,\n }: CreateApiClientOptions,\n) => {\n // Add the router\n const router = createAppRouter(layout)\n const state = await createAppState({\n router,\n customThemes,\n fallbackThemeSlug,\n telemetryDefault: telemetry,\n options,\n })\n const commandPaletteState = useCommandPaletteState()\n\n // Pass in our initial props at the top level\n const app = createApp(App, {\n layout,\n plugins,\n getAppState: () => state,\n getCommandPaletteState: () => commandPaletteState,\n fetchRegistryDocument,\n })\n app.use(router)\n\n // Mount the vue app\n if (!el) {\n console.error(\n '[@scalar/api-client-modal] Could not create the API client.',\n 'Invalid HTML element provided.',\n 'Read more: https://github.com/scalar/scalar/tree/main/packages/api-client',\n )\n\n return\n }\n app.mount(el)\n\n return {\n app,\n state,\n }\n}\n"],"mappings":";;;;;;;;;;AAyDA,IAAa,mBAAmB,WAA6C;AAE3E,KAAI,WAAW,MACb,QAAO,aAAgB;EAAE,SAAS,kBAAkB;EAAE,QAAQ;EAAQ,CAAC;AAIzE,QAAO,aAAgB;EAAE,SAAS,sBAAsB;EAAE,QAAQ;EAAQ,CAAC;;;;;AAM7E,IAAa,qBAAqB,OAChC,IACA,EACE,SAAS,WACT,SACA,cACA,mBACA,uBACA,YAAY,MACZ,cAEC;CAEH,MAAM,SAAS,gBAAgB,OAAO;CACtC,MAAM,QAAQ,MAAM,eAAe;EACjC;EACA;EACA;EACA,kBAAkB;EAClB;EACD,CAAC;CACF,MAAM,sBAAsB,wBAAwB;CAGpD,MAAM,MAAM,UAAU,aAAK;EACzB;EACA;EACA,mBAAmB;EACnB,8BAA8B;EAC9B;EACD,CAAC;AACF,KAAI,IAAI,OAAO;AAGf,KAAI,CAAC,IAAI;AACP,UAAQ,MACN,+DACA,kCACA,4EACD;AAED;;AAEF,KAAI,MAAM,GAAG;AAEb,QAAO;EACL;EACA;EACD"}
1
+ {"version":3,"file":"create-api-client-app.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/create-api-client-app.ts"],"sourcesContent":["import type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { Theme } from '@scalar/themes'\nimport { createApp } from 'vue'\nimport { createRouter as createVueRouter, createWebHashHistory, createWebHistory } from 'vue-router'\n\nimport App from '@/v2/features/app/App.vue'\nimport { createAppState } from '@/v2/features/app/app-state'\nimport { ROUTES } from '@/v2/features/app/helpers/routes'\nimport type { RegistryAdapter } from '@/v2/types/configuration'\nimport type { ClientLayout } from '@/v2/types/layout'\nimport type { ApiClientOptions } from '@/v2/types/options'\n\nimport { useCommandPaletteState } from '../../command-palette/hooks/use-command-palette-state'\n\ntype CreateApiClientOptions = {\n /**\n * The layout of the client, limited to web or desktop in app\n * @see {@link ClientLayout}\n *\n * @default 'desktop'\n */\n layout: Exclude<ClientLayout, 'modal'>\n /**\n * Api client plugins to include in the app\n */\n plugins?: ClientPlugin[]\n /**\n * Custom themes to include in the app\n */\n customThemes?: Theme[]\n /**\n * Fallback theme slug to use if no theme is selected for the workspace\n * @default 'default'\n */\n fallbackThemeSlug?: () => string\n /**\n * Adapter wiring the API client up to an external registry. The\n * adapter bundles the registry document list, the per-document fetch\n * callback (used for syncing) and the publish callback used to create\n * a brand-new registry version. Optional at the top level - omit to\n * opt out of registry features entirely.\n */\n registry?: RegistryAdapter\n /**\n * Whether or not to send telemetry events.\n */\n telemetry?: boolean\n /** Runtime behaviour overrides */\n options?: ApiClientAppOptions\n}\n\n/**\n * Runtime behaviour overrides shared between createApiClientApp and createAppState.\n * Derived from the canonical ApiClientOptions to guarantee structural compatibility.\n */\nexport type ApiClientAppOptions = Pick<ApiClientOptions, 'customFetch' | 'oauth2RedirectUri'>\n\n/**\n * Creates the appropriate router with the appropriate routes based on the layout\n */\nexport const createAppRouter = (layout: CreateApiClientOptions['layout']) => {\n // Web uses the standard HTML5 history API\n if (layout === 'web') {\n return createVueRouter({ history: createWebHistory(), routes: ROUTES })\n }\n\n // Electron app has to use the webHashHistory due to file routing\n return createVueRouter({ history: createWebHashHistory(), routes: ROUTES })\n}\n\n/**\n * Create the API Client with router and passes in the workspace store as a prop\n */\nexport const createApiClientApp = async (\n el: HTMLElement | null,\n {\n layout = 'desktop',\n plugins,\n customThemes,\n fallbackThemeSlug,\n registry,\n telemetry = true,\n options,\n }: CreateApiClientOptions,\n) => {\n // Add the router\n const router = createAppRouter(layout)\n const state = await createAppState({\n router,\n customThemes,\n fallbackThemeSlug,\n telemetryDefault: telemetry,\n options,\n })\n const commandPaletteState = useCommandPaletteState()\n\n // Pass in our initial props at the top level\n const app = createApp(App, {\n layout,\n plugins,\n getAppState: () => state,\n getCommandPaletteState: () => commandPaletteState,\n registry,\n })\n app.use(router)\n\n // Mount the vue app\n if (!el) {\n console.error(\n '[@scalar/api-client-modal] Could not create the API client.',\n 'Invalid HTML element provided.',\n 'Read more: https://github.com/scalar/scalar/tree/main/packages/api-client',\n )\n\n return\n }\n app.mount(el)\n\n return {\n app,\n state,\n }\n}\n"],"mappings":";;;;;;;;;;AA4DA,IAAa,mBAAmB,WAA6C;AAE3E,KAAI,WAAW,MACb,QAAO,aAAgB;EAAE,SAAS,kBAAkB;EAAE,QAAQ;EAAQ,CAAC;AAIzE,QAAO,aAAgB;EAAE,SAAS,sBAAsB;EAAE,QAAQ;EAAQ,CAAC;;;;;AAM7E,IAAa,qBAAqB,OAChC,IACA,EACE,SAAS,WACT,SACA,cACA,mBACA,UACA,YAAY,MACZ,cAEC;CAEH,MAAM,SAAS,gBAAgB,OAAO;CACtC,MAAM,QAAQ,MAAM,eAAe;EACjC;EACA;EACA;EACA,kBAAkB;EAClB;EACD,CAAC;CACF,MAAM,sBAAsB,wBAAwB;CAGpD,MAAM,MAAM,UAAU,aAAK;EACzB;EACA;EACA,mBAAmB;EACnB,8BAA8B;EAC9B;EACD,CAAC;AACF,KAAI,IAAI,OAAO;AAGf,KAAI,CAAC,IAAI;AACP,UAAQ,MACN,+DACA,kCACA,4EACD;AAED;;AAEF,KAAI,MAAM,GAAG;AAEb,QAAO;EACL;EACA;EACD"}
@@ -8,7 +8,7 @@ type LoadRegistryDocumentResult = {
8
8
  ok: false;
9
9
  error: string;
10
10
  };
11
- export declare const loadRegistryDocument: ({ workspaceStore, fetcher, namespace, slug, version, commitHash, }: {
11
+ export declare const loadRegistryDocument: ({ workspaceStore, fetcher, namespace, slug, version, }: {
12
12
  workspaceStore: WorkspaceStore;
13
13
  fetcher: ImportDocumentFromRegistry;
14
14
  namespace: string;
@@ -19,15 +19,6 @@ export declare const loadRegistryDocument: ({ workspaceStore, fetcher, namespace
19
19
  * not have a concrete version pinned for the document yet.
20
20
  */
21
21
  version?: string;
22
- /**
23
- * Commit hash the registry currently advertises for `version`. Callers
24
- * already know this from the version listing (sidebar row, breadcrumb
25
- * picker, etc.) so we accept it directly rather than parsing it back
26
- * out of the fetched document. When provided it is persisted on
27
- * `x-scalar-registry-meta.commitHash` so subsequent refreshes can
28
- * detect drift and surface upstream changes.
29
- */
30
- commitHash?: string;
31
22
  }) => Promise<LoadRegistryDocumentResult>;
32
23
  export {};
33
24
  //# sourceMappingURL=load-registry-document.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"load-registry-document.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/load-registry-document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAGpE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAE1E,iFAAiF;AACjF,KAAK,0BAA0B,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnG,eAAO,MAAM,oBAAoB,GAAU,oEAOxC;IACD,cAAc,EAAE,cAAc,CAAA;IAC9B,OAAO,EAAE,0BAA0B,CAAA;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,KAAG,OAAO,CAAC,0BAA0B,CAwDrC,CAAA"}
1
+ {"version":3,"file":"load-registry-document.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/load-registry-document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAGpE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAE1E,iFAAiF;AACjF,KAAK,0BAA0B,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnG,eAAO,MAAM,oBAAoB,GAAU,wDAMxC;IACD,cAAc,EAAE,cAAc,CAAA;IAC9B,OAAO,EAAE,0BAA0B,CAAA;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,KAAG,OAAO,CAAC,0BAA0B,CA6DrC,CAAA"}
@@ -1,7 +1,7 @@
1
1
  import { generateUniqueSlug } from "../../command-palette/helpers/generate-unique-slug.js";
2
2
  import { coerce, object, string } from "@scalar/validation";
3
3
  //#region src/v2/features/app/helpers/load-registry-document.ts
4
- var loadRegistryDocument = async ({ workspaceStore, fetcher, namespace, slug, version = "latest", commitHash }) => {
4
+ var loadRegistryDocument = async ({ workspaceStore, fetcher, namespace, slug, version = "latest" }) => {
5
5
  const documents = workspaceStore.workspace.documents;
6
6
  const existing = Object.entries(documents).find(([, doc]) => {
7
7
  const meta = doc?.["x-scalar-registry-meta"];
@@ -18,21 +18,22 @@ var loadRegistryDocument = async ({ workspaceStore, fetcher, namespace, slug, ve
18
18
  });
19
19
  if (!result.ok) return {
20
20
  ok: false,
21
- error: `Failed to fetch document: ${result.error || "Unknown error"}`
21
+ error: `Failed to fetch document: ${result.message ?? result.error}`
22
22
  };
23
- const documentName = await generateUniqueSlug(`${coerce(object({ info: object({ title: string() }) }), result.data).info.title.trim() || slug}-${version}`, new Set(Object.keys(documents)));
23
+ const { document, versionSha } = result.data;
24
+ const documentName = await generateUniqueSlug(`${coerce(object({ info: object({ title: string() }) }), document).info.title.trim() || slug}-${version}`, new Set(Object.keys(documents)));
24
25
  if (!documentName) return {
25
26
  ok: false,
26
27
  error: "Failed to generate a unique name for the document"
27
28
  };
28
29
  await workspaceStore.addDocument({
29
30
  name: documentName,
30
- document: result.data,
31
+ document,
31
32
  meta: { "x-scalar-registry-meta": {
32
33
  namespace,
33
34
  slug,
34
35
  version,
35
- ...commitHash ? { commitHash } : {}
36
+ ...versionSha ? { commitHash: versionSha } : {}
36
37
  } }
37
38
  });
38
39
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"load-registry-document.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/load-registry-document.ts"],"sourcesContent":["import { coerce, object, string } from '@scalar/validation'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\n\nimport { generateUniqueSlug } from '@/v2/features/command-palette/helpers/generate-unique-slug'\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\n\n/** Result of attempting to load a registry document into the workspace store. */\ntype LoadRegistryDocumentResult = { ok: true; documentName: string } | { ok: false; error: string }\n\nexport const loadRegistryDocument = async ({\n workspaceStore,\n fetcher,\n namespace,\n slug,\n version = 'latest',\n commitHash,\n}: {\n workspaceStore: WorkspaceStore\n fetcher: ImportDocumentFromRegistry\n namespace: string\n slug: string\n /**\n * Specific version to fetch from the registry. When omitted we ask the\n * registry for `latest`, which is what callers default to when they do\n * not have a concrete version pinned for the document yet.\n */\n version?: string\n /**\n * Commit hash the registry currently advertises for `version`. Callers\n * already know this from the version listing (sidebar row, breadcrumb\n * picker, etc.) so we accept it directly rather than parsing it back\n * out of the fetched document. When provided it is persisted on\n * `x-scalar-registry-meta.commitHash` so subsequent refreshes can\n * detect drift and surface upstream changes.\n */\n commitHash?: string\n}): Promise<LoadRegistryDocumentResult> => {\n const documents = workspaceStore.workspace.documents\n\n const existing = Object.entries(documents).find(([, doc]) => {\n const meta = doc?.['x-scalar-registry-meta']\n return meta?.namespace === namespace && meta?.slug === slug && meta?.version === version\n })\n\n if (existing) {\n return { ok: true, documentName: existing[0] }\n }\n\n const result = await fetcher({ namespace, slug, version })\n if (!result.ok) {\n return {\n ok: false,\n error: `Failed to fetch document: ${result.error || 'Unknown error'}`,\n }\n }\n\n // Parse the document data into a schema\n const schema = object({ info: object({ title: string() }) })\n const baseName = coerce(schema, result.data).info.title\n\n // Compose the workspace key as `slug(title)-slug(version)`\n // so the url will be like /workspace/acme/pets-api-1.0.0\n const title = baseName.trim() || slug\n const documentName = await generateUniqueSlug(`${title}-${version}`, new Set(Object.keys(documents)))\n\n if (!documentName) {\n return {\n ok: false,\n error: 'Failed to generate a unique name for the document',\n }\n }\n\n // Add the document to the workspace store. The registry meta records the\n // exact `version` we requested so subsequent lookups can match the local\n // document back to the version row in the sidebar. We also persist the\n // registry's commit hash (when the caller passed one) so later\n // refreshes can detect when the registry has moved on and surface\n // upstream changes to the user.\n await workspaceStore.addDocument({\n name: documentName,\n document: result.data,\n meta: {\n 'x-scalar-registry-meta': {\n namespace,\n slug,\n version,\n ...(commitHash ? { commitHash } : {}),\n },\n },\n })\n\n return { ok: true, documentName }\n}\n"],"mappings":";;;AASA,IAAa,uBAAuB,OAAO,EACzC,gBACA,SACA,WACA,MACA,UAAU,UACV,iBAqByC;CACzC,MAAM,YAAY,eAAe,UAAU;CAE3C,MAAM,WAAW,OAAO,QAAQ,UAAU,CAAC,MAAM,GAAG,SAAS;EAC3D,MAAM,OAAO,MAAM;AACnB,SAAO,MAAM,cAAc,aAAa,MAAM,SAAS,QAAQ,MAAM,YAAY;GACjF;AAEF,KAAI,SACF,QAAO;EAAE,IAAI;EAAM,cAAc,SAAS;EAAI;CAGhD,MAAM,SAAS,MAAM,QAAQ;EAAE;EAAW;EAAM;EAAS,CAAC;AAC1D,KAAI,CAAC,OAAO,GACV,QAAO;EACL,IAAI;EACJ,OAAO,6BAA6B,OAAO,SAAS;EACrD;CAUH,MAAM,eAAe,MAAM,mBAAmB,GAL7B,OADF,OAAO,EAAE,MAAM,OAAO,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAE,CAAC,EAC5B,OAAO,KAAK,CAAC,KAAK,MAI3B,MAAM,IAAI,KACsB,GAAG,WAAW,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,CAAC;AAErG,KAAI,CAAC,aACH,QAAO;EACL,IAAI;EACJ,OAAO;EACR;AASH,OAAM,eAAe,YAAY;EAC/B,MAAM;EACN,UAAU,OAAO;EACjB,MAAM,EACJ,0BAA0B;GACxB;GACA;GACA;GACA,GAAI,aAAa,EAAE,YAAY,GAAG,EAAE;GACrC,EACF;EACF,CAAC;AAEF,QAAO;EAAE,IAAI;EAAM;EAAc"}
1
+ {"version":3,"file":"load-registry-document.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/load-registry-document.ts"],"sourcesContent":["import { coerce, object, string } from '@scalar/validation'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\n\nimport { generateUniqueSlug } from '@/v2/features/command-palette/helpers/generate-unique-slug'\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\n\n/** Result of attempting to load a registry document into the workspace store. */\ntype LoadRegistryDocumentResult = { ok: true; documentName: string } | { ok: false; error: string }\n\nexport const loadRegistryDocument = async ({\n workspaceStore,\n fetcher,\n namespace,\n slug,\n version = 'latest',\n}: {\n workspaceStore: WorkspaceStore\n fetcher: ImportDocumentFromRegistry\n namespace: string\n slug: string\n /**\n * Specific version to fetch from the registry. When omitted we ask the\n * registry for `latest`, which is what callers default to when they do\n * not have a concrete version pinned for the document yet.\n */\n version?: string\n}): Promise<LoadRegistryDocumentResult> => {\n const documents = workspaceStore.workspace.documents\n\n const existing = Object.entries(documents).find(([, doc]) => {\n const meta = doc?.['x-scalar-registry-meta']\n return meta?.namespace === namespace && meta?.slug === slug && meta?.version === version\n })\n\n if (existing) {\n return { ok: true, documentName: existing[0] }\n }\n\n const result = await fetcher({ namespace, slug, version })\n if (!result.ok) {\n // The registry adapter returns a discriminated `FetchRegistryDocumentError`\n // code; we surface the human-readable `message` when one was provided\n // and fall back to the code itself so the user always sees something\n // actionable in the toast / log line.\n return {\n ok: false,\n error: `Failed to fetch document: ${result.message ?? result.error}`,\n }\n }\n\n const { document, versionSha } = result.data\n\n // Parse the document data into a schema\n const schema = object({ info: object({ title: string() }) })\n const baseName = coerce(schema, document).info.title\n\n // Compose the workspace key as `slug(title)-slug(version)`\n // so the url will be like /workspace/acme/pets-api-1.0.0\n const title = baseName.trim() || slug\n const documentName = await generateUniqueSlug(`${title}-${version}`, new Set(Object.keys(documents)))\n\n if (!documentName) {\n return {\n ok: false,\n error: 'Failed to generate a unique name for the document',\n }\n }\n\n await workspaceStore.addDocument({\n name: documentName,\n document,\n meta: {\n 'x-scalar-registry-meta': {\n namespace,\n slug,\n version,\n // Only include `commitHash` when the registry actually advertised\n // one. Writing `undefined` into the object would leave an explicit\n // `commitHash: undefined` key on the persisted meta, which the\n // downstream sync logic treats as \"we already know the hash\" and\n // short-circuits the first commit-hash stamp after a pull.\n ...(versionSha ? { commitHash: versionSha } : {}),\n },\n },\n })\n\n return { ok: true, documentName }\n}\n"],"mappings":";;;AASA,IAAa,uBAAuB,OAAO,EACzC,gBACA,SACA,WACA,MACA,UAAU,eAY+B;CACzC,MAAM,YAAY,eAAe,UAAU;CAE3C,MAAM,WAAW,OAAO,QAAQ,UAAU,CAAC,MAAM,GAAG,SAAS;EAC3D,MAAM,OAAO,MAAM;AACnB,SAAO,MAAM,cAAc,aAAa,MAAM,SAAS,QAAQ,MAAM,YAAY;GACjF;AAEF,KAAI,SACF,QAAO;EAAE,IAAI;EAAM,cAAc,SAAS;EAAI;CAGhD,MAAM,SAAS,MAAM,QAAQ;EAAE;EAAW;EAAM;EAAS,CAAC;AAC1D,KAAI,CAAC,OAAO,GAKV,QAAO;EACL,IAAI;EACJ,OAAO,6BAA6B,OAAO,WAAW,OAAO;EAC9D;CAGH,MAAM,EAAE,UAAU,eAAe,OAAO;CASxC,MAAM,eAAe,MAAM,mBAAmB,GAL7B,OADF,OAAO,EAAE,MAAM,OAAO,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAE,CAAC,EAC5B,SAAS,CAAC,KAAK,MAIxB,MAAM,IAAI,KACsB,GAAG,WAAW,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,CAAC;AAErG,KAAI,CAAC,aACH,QAAO;EACL,IAAI;EACJ,OAAO;EACR;AAGH,OAAM,eAAe,YAAY;EAC/B,MAAM;EACN;EACA,MAAM,EACJ,0BAA0B;GACxB;GACA;GACA;GAMA,GAAI,aAAa,EAAE,YAAY,YAAY,GAAG,EAAE;GACjD,EACF;EACF,CAAC;AAEF,QAAO;EAAE,IAAI;EAAM;EAAc"}
@@ -0,0 +1,23 @@
1
+ import type { FetchRegistryDocumentError, PublishRegistryDocumentError, PublishRegistryVersionError } from '../../../../v2/types/configuration';
2
+ /**
3
+ * Maps a {@link FetchRegistryDocumentError} code to a user-facing
4
+ * message used by the Pull flow when reading a document from the
5
+ * registry fails. The optional `detail` is the human-readable `message`
6
+ * field returned alongside the discriminated code; we only stitch it
7
+ * into the network branch because the other codes already describe a
8
+ * specific failure mode the user can act on.
9
+ */
10
+ export declare const messageForFetchError: (code: FetchRegistryDocumentError, detail?: string) => string;
11
+ /**
12
+ * Maps a {@link PublishRegistryVersionError} code to a user-facing
13
+ * message used by the Push flow when publishing a new commit on an
14
+ * existing version fails.
15
+ */
16
+ export declare const messageForPublishVersionError: (code: PublishRegistryVersionError, detail?: string) => string;
17
+ /**
18
+ * Maps a {@link PublishRegistryDocumentError} code to a user-facing
19
+ * message used by the first-time Publish flow (creating a brand-new
20
+ * registry entry for a document that does not have one yet).
21
+ */
22
+ export declare const messageForPublishDocumentError: (code: PublishRegistryDocumentError, detail?: string) => string;
23
+ //# sourceMappingURL=registry-error-messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-error-messages.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/registry-error-messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC5B,2BAA2B,EAC5B,MAAM,0BAA0B,CAAA;AAsBjC;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,0BAA0B,EAAE,SAAS,MAAM,KAAG,MAYxF,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GAAI,MAAM,2BAA2B,EAAE,SAAS,MAAM,KAAG,MAclG,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,GAAI,MAAM,4BAA4B,EAAE,SAAS,MAAM,KAAG,MAYpG,CAAA"}
@@ -0,0 +1,63 @@
1
+ //#region src/v2/features/app/helpers/registry-error-messages.ts
2
+ /**
3
+ * Generic fallback used when an adapter callback rejected for a
4
+ * registry call we could not classify any further. Splitting it out
5
+ * keeps the per-flow helpers below small and readable.
6
+ */
7
+ var NETWORK_FALLBACK = "Could not reach the registry. Check your connection and try again.";
8
+ /**
9
+ * Generic fallback used when an adapter rejects with the `UNKNOWN`
10
+ * catch-all code. We weave the optional `detail` in when present so
11
+ * the user still gets a hint of what actually went wrong even though
12
+ * we could not classify the failure into a specific branch.
13
+ */
14
+ var UNKNOWN_FALLBACK = "Something went wrong. Please try again.";
15
+ var networkMessage = (detail) => detail ? `Could not reach the registry: ${detail}` : NETWORK_FALLBACK;
16
+ var unknownMessage = (detail) => detail ? `Something went wrong: ${detail}` : UNKNOWN_FALLBACK;
17
+ /**
18
+ * Maps a {@link FetchRegistryDocumentError} code to a user-facing
19
+ * message used by the Pull flow when reading a document from the
20
+ * registry fails. The optional `detail` is the human-readable `message`
21
+ * field returned alongside the discriminated code; we only stitch it
22
+ * into the network branch because the other codes already describe a
23
+ * specific failure mode the user can act on.
24
+ */
25
+ var messageForFetchError = (code, detail) => {
26
+ switch (code) {
27
+ case "NOT_FOUND": return "This version is no longer available on the registry.";
28
+ case "UNAUTHORIZED": return "You are not allowed to read this document. Please sign in and try again.";
29
+ case "FETCH_FAILED": return networkMessage(detail);
30
+ default: return unknownMessage(detail);
31
+ }
32
+ };
33
+ /**
34
+ * Maps a {@link PublishRegistryVersionError} code to a user-facing
35
+ * message used by the Push flow when publishing a new commit on an
36
+ * existing version fails.
37
+ */
38
+ var messageForPublishVersionError = (code, detail) => {
39
+ switch (code) {
40
+ case "CONFLICT": return "Someone else pushed changes to this version. Pull the latest before pushing again.";
41
+ case "NOT_FOUND": return "This document is no longer available on the registry.";
42
+ case "UNAUTHORIZED": return "You are not allowed to publish to this namespace.";
43
+ case "FETCH_FAILED": return networkMessage(detail);
44
+ default: return unknownMessage(detail);
45
+ }
46
+ };
47
+ /**
48
+ * Maps a {@link PublishRegistryDocumentError} code to a user-facing
49
+ * message used by the first-time Publish flow (creating a brand-new
50
+ * registry entry for a document that does not have one yet).
51
+ */
52
+ var messageForPublishDocumentError = (code, detail) => {
53
+ switch (code) {
54
+ case "CONFLICT": return "A document with this namespace and slug already exists. Pick a different slug or pull the existing one.";
55
+ case "UNAUTHORIZED": return "You are not allowed to publish to this namespace.";
56
+ case "FETCH_FAILED": return networkMessage(detail);
57
+ default: return unknownMessage(detail);
58
+ }
59
+ };
60
+ //#endregion
61
+ export { messageForFetchError, messageForPublishDocumentError, messageForPublishVersionError };
62
+
63
+ //# sourceMappingURL=registry-error-messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-error-messages.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/registry-error-messages.ts"],"sourcesContent":["import type {\n FetchRegistryDocumentError,\n PublishRegistryDocumentError,\n PublishRegistryVersionError,\n} from '@/v2/types/configuration'\n\n/**\n * Generic fallback used when an adapter callback rejected for a\n * registry call we could not classify any further. Splitting it out\n * keeps the per-flow helpers below small and readable.\n */\nconst NETWORK_FALLBACK = 'Could not reach the registry. Check your connection and try again.'\n\n/**\n * Generic fallback used when an adapter rejects with the `UNKNOWN`\n * catch-all code. We weave the optional `detail` in when present so\n * the user still gets a hint of what actually went wrong even though\n * we could not classify the failure into a specific branch.\n */\nconst UNKNOWN_FALLBACK = 'Something went wrong. Please try again.'\n\nconst networkMessage = (detail?: string): string =>\n detail ? `Could not reach the registry: ${detail}` : NETWORK_FALLBACK\n\nconst unknownMessage = (detail?: string): string => (detail ? `Something went wrong: ${detail}` : UNKNOWN_FALLBACK)\n\n/**\n * Maps a {@link FetchRegistryDocumentError} code to a user-facing\n * message used by the Pull flow when reading a document from the\n * registry fails. The optional `detail` is the human-readable `message`\n * field returned alongside the discriminated code; we only stitch it\n * into the network branch because the other codes already describe a\n * specific failure mode the user can act on.\n */\nexport const messageForFetchError = (code: FetchRegistryDocumentError, detail?: string): string => {\n switch (code) {\n case 'NOT_FOUND':\n return 'This version is no longer available on the registry.'\n case 'UNAUTHORIZED':\n return 'You are not allowed to read this document. Please sign in and try again.'\n case 'FETCH_FAILED':\n return networkMessage(detail)\n case 'UNKNOWN':\n default:\n return unknownMessage(detail)\n }\n}\n\n/**\n * Maps a {@link PublishRegistryVersionError} code to a user-facing\n * message used by the Push flow when publishing a new commit on an\n * existing version fails.\n */\nexport const messageForPublishVersionError = (code: PublishRegistryVersionError, detail?: string): string => {\n switch (code) {\n case 'CONFLICT':\n return 'Someone else pushed changes to this version. Pull the latest before pushing again.'\n case 'NOT_FOUND':\n return 'This document is no longer available on the registry.'\n case 'UNAUTHORIZED':\n return 'You are not allowed to publish to this namespace.'\n case 'FETCH_FAILED':\n return networkMessage(detail)\n case 'UNKNOWN':\n default:\n return unknownMessage(detail)\n }\n}\n\n/**\n * Maps a {@link PublishRegistryDocumentError} code to a user-facing\n * message used by the first-time Publish flow (creating a brand-new\n * registry entry for a document that does not have one yet).\n */\nexport const messageForPublishDocumentError = (code: PublishRegistryDocumentError, detail?: string): string => {\n switch (code) {\n case 'CONFLICT':\n return 'A document with this namespace and slug already exists. Pick a different slug or pull the existing one.'\n case 'UNAUTHORIZED':\n return 'You are not allowed to publish to this namespace.'\n case 'FETCH_FAILED':\n return networkMessage(detail)\n case 'UNKNOWN':\n default:\n return unknownMessage(detail)\n }\n}\n"],"mappings":";;;;;;AAWA,IAAM,mBAAmB;;;;;;;AAQzB,IAAM,mBAAmB;AAEzB,IAAM,kBAAkB,WACtB,SAAS,iCAAiC,WAAW;AAEvD,IAAM,kBAAkB,WAA6B,SAAS,yBAAyB,WAAW;;;;;;;;;AAUlG,IAAa,wBAAwB,MAAkC,WAA4B;AACjG,SAAQ,MAAR;EACE,KAAK,YACH,QAAO;EACT,KAAK,eACH,QAAO;EACT,KAAK,eACH,QAAO,eAAe,OAAO;EAE/B,QACE,QAAO,eAAe,OAAO;;;;;;;;AASnC,IAAa,iCAAiC,MAAmC,WAA4B;AAC3G,SAAQ,MAAR;EACE,KAAK,WACH,QAAO;EACT,KAAK,YACH,QAAO;EACT,KAAK,eACH,QAAO;EACT,KAAK,eACH,QAAO,eAAe,OAAO;EAE/B,QACE,QAAO,eAAe,OAAO;;;;;;;;AASnC,IAAa,kCAAkC,MAAoC,WAA4B;AAC7G,SAAQ,MAAR;EACE,KAAK,WACH,QAAO;EACT,KAAK,eACH,QAAO;EACT,KAAK,eACH,QAAO,eAAe,OAAO;EAE/B,QACE,QAAO,eAAe,OAAO"}
@@ -1,6 +1,7 @@
1
1
  import { type ComputedRef } from 'vue';
2
2
  import type { AppState } from '../../../../v2/features/app/app-state.js';
3
- import { type RegistryDocumentsState, type SidebarDocumentItem, type SidebarDocumentVersion } from '../../../../v2/features/app/hooks/use-sidebar-documents.js';
3
+ import { type SidebarDocumentItem, type SidebarDocumentVersion } from '../../../../v2/features/app/hooks/use-sidebar-documents.js';
4
+ import type { RegistryDocumentsState } from '../../../../v2/types/configuration';
4
5
  /**
5
6
  * Resolves everything UI surfaces need to know about the document the user
6
7
  * is currently viewing in a registry-backed workspace.
@@ -1 +1 @@
1
- {"version":3,"file":"use-active-document-version.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/hooks/use-active-document-version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAY,MAAM,KAAK,CAAA;AAEhD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAE5B,MAAM,+CAA+C,CAAA;AAEtD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,wBAAwB,GAAI,6BAGtC;IACD,GAAG,EAAE,QAAQ,CAAA;IACb,wFAAwF;IACxF,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;CAChD,KAAG;IACF,SAAS,EAAE,WAAW,CAAC,mBAAmB,EAAE,CAAC,CAAA;IAC7C,kBAAkB,EAAE,WAAW,CAC3B;QACE,SAAS,EAAE,MAAM,CAAA;QACjB,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,0BAA0B,CAAC,EAAE,MAAM,CAAA;QACnC,WAAW,CAAC,EAAE,OAAO,CAAA;KACtB,GACD,SAAS,CACZ,CAAA;IACD,UAAU,EAAE,WAAW,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAA;IACxD,QAAQ,EAAE,WAAW,CAAC,sBAAsB,EAAE,CAAC,CAAA;IAC/C,aAAa,EAAE,WAAW,CAAC,sBAAsB,GAAG,SAAS,CAAC,CAAA;CAqD/D,CAAA"}
1
+ {"version":3,"file":"use-active-document-version.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/hooks/use-active-document-version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAY,MAAM,KAAK,CAAA;AAEhD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAE5B,MAAM,+CAA+C,CAAA;AACtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AAEtE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,wBAAwB,GAAI,6BAGtC;IACD,GAAG,EAAE,QAAQ,CAAA;IACb,wFAAwF;IACxF,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;CAChD,KAAG;IACF,SAAS,EAAE,WAAW,CAAC,mBAAmB,EAAE,CAAC,CAAA;IAC7C,kBAAkB,EAAE,WAAW,CAC3B;QACE,SAAS,EAAE,MAAM,CAAA;QACjB,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,0BAA0B,CAAC,EAAE,MAAM,CAAA;QACnC,WAAW,CAAC,EAAE,OAAO,CAAA;KACtB,GACD,SAAS,CACZ,CAAA;IACD,UAAU,EAAE,WAAW,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAA;IACxD,QAAQ,EAAE,WAAW,CAAC,sBAAsB,EAAE,CAAC,CAAA;IAC/C,aAAa,EAAE,WAAW,CAAC,sBAAsB,GAAG,SAAS,CAAC,CAAA;CAqD/D,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-active-document-version.js","names":[],"sources":["../../../../../src/v2/features/app/hooks/use-active-document-version.ts"],"sourcesContent":["import { type ComputedRef, computed } from 'vue'\n\nimport type { AppState } from '@/v2/features/app/app-state'\nimport {\n type RegistryDocumentsState,\n type SidebarDocumentItem,\n type SidebarDocumentVersion,\n useSidebarDocuments,\n} from '@/v2/features/app/hooks/use-sidebar-documents'\n\n/**\n * Resolves everything UI surfaces need to know about the document the user\n * is currently viewing in a registry-backed workspace.\n *\n * The breadcrumb's version picker and the right-side sync indicator both\n * need the same data:\n *\n * - the sidebar item for the active group (so titles match the registry),\n * - the full version list for that group (used to render dropdown rows /\n * feed `useVersionConflictCheck`),\n * - and the currently active version (used for the picker selection and\n * the standalone status icon).\n *\n * Pulling this into a composable keeps both surfaces aligned and avoids\n * duplicating the active-version selection rules — in particular the\n * fallback to the version declared on `x-scalar-registry-meta` when the\n * sidebar's notion of the active version has not caught up yet (e.g. during\n * a pending fetch).\n */\nexport const useActiveDocumentVersion = ({\n app,\n registryDocuments,\n}: {\n app: AppState\n /** Reactive accessor — components pass a getter so the composable stays prop-driven. */\n registryDocuments: () => RegistryDocumentsState\n}): {\n documents: ComputedRef<SidebarDocumentItem[]>\n activeRegistryMeta: ComputedRef<\n | {\n namespace: string\n slug: string\n version?: string\n commitHash?: string\n conflictCheckedAgainstHash?: string\n hasConflict?: boolean\n }\n | undefined\n >\n activeItem: ComputedRef<SidebarDocumentItem | undefined>\n versions: ComputedRef<SidebarDocumentVersion[]>\n activeVersion: ComputedRef<SidebarDocumentVersion | undefined>\n} => {\n const { documents } = useSidebarDocuments({\n app,\n managedDocs: () => registryDocuments().documents ?? [],\n })\n\n /** Registry meta for the currently active document (if any). */\n const activeRegistryMeta = computed(() => {\n const doc = app.store.value?.workspace.activeDocument\n return doc?.['x-scalar-registry-meta']\n })\n\n /**\n * Sidebar item representing the currently active registry-backed document.\n * We match by `namespace + slug` because a single group can contain\n * several versions and the active document may be any of them.\n */\n const activeItem = computed(() => {\n const meta = activeRegistryMeta.value\n if (!meta) {\n return undefined\n }\n return documents.value.find(\n (item) => item.registry?.namespace === meta.namespace && item.registry?.slug === meta.slug,\n )\n })\n\n /** Versions for the active group, ordered with the latest first. */\n const versions = computed<SidebarDocumentVersion[]>(() => activeItem.value?.versions ?? [])\n\n /**\n * The version currently active on screen. Prefers matching the version\n * declared on the active document's registry meta so consumers always\n * reflect what the user is viewing, even when the sidebar's notion of the\n * active version has not caught up yet (e.g. during a pending fetch).\n */\n const activeVersion = computed<SidebarDocumentVersion | undefined>(() => {\n const meta = activeRegistryMeta.value\n const list = versions.value\n if (!meta) {\n return list[0]\n }\n return list.find((v) => v.version === meta.version) ?? list[0]\n })\n\n return {\n documents,\n activeRegistryMeta,\n activeItem,\n versions,\n activeVersion,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,4BAA4B,EACvC,KACA,wBAqBG;CACH,MAAM,EAAE,cAAc,oBAAoB;EACxC;EACA,mBAAmB,mBAAmB,CAAC,aAAa,EAAE;EACvD,CAAC;;CAGF,MAAM,qBAAqB,eAAe;AAExC,UADY,IAAI,MAAM,OAAO,UAAU,kBAC1B;GACb;;;;;;CAOF,MAAM,aAAa,eAAe;EAChC,MAAM,OAAO,mBAAmB;AAChC,MAAI,CAAC,KACH;AAEF,SAAO,UAAU,MAAM,MACpB,SAAS,KAAK,UAAU,cAAc,KAAK,aAAa,KAAK,UAAU,SAAS,KAAK,KACvF;GACD;;CAGF,MAAM,WAAW,eAAyC,WAAW,OAAO,YAAY,EAAE,CAAC;AAiB3F,QAAO;EACL;EACA;EACA;EACA;EACA,eAdoB,eAAmD;GACvE,MAAM,OAAO,mBAAmB;GAChC,MAAM,OAAO,SAAS;AACtB,OAAI,CAAC,KACH,QAAO,KAAK;AAEd,UAAO,KAAK,MAAM,MAAM,EAAE,YAAY,KAAK,QAAQ,IAAI,KAAK;IAC5D;EAQD"}
1
+ {"version":3,"file":"use-active-document-version.js","names":[],"sources":["../../../../../src/v2/features/app/hooks/use-active-document-version.ts"],"sourcesContent":["import { type ComputedRef, computed } from 'vue'\n\nimport type { AppState } from '@/v2/features/app/app-state'\nimport {\n type SidebarDocumentItem,\n type SidebarDocumentVersion,\n useSidebarDocuments,\n} from '@/v2/features/app/hooks/use-sidebar-documents'\nimport type { RegistryDocumentsState } from '@/v2/types/configuration'\n\n/**\n * Resolves everything UI surfaces need to know about the document the user\n * is currently viewing in a registry-backed workspace.\n *\n * The breadcrumb's version picker and the right-side sync indicator both\n * need the same data:\n *\n * - the sidebar item for the active group (so titles match the registry),\n * - the full version list for that group (used to render dropdown rows /\n * feed `useVersionConflictCheck`),\n * - and the currently active version (used for the picker selection and\n * the standalone status icon).\n *\n * Pulling this into a composable keeps both surfaces aligned and avoids\n * duplicating the active-version selection rules — in particular the\n * fallback to the version declared on `x-scalar-registry-meta` when the\n * sidebar's notion of the active version has not caught up yet (e.g. during\n * a pending fetch).\n */\nexport const useActiveDocumentVersion = ({\n app,\n registryDocuments,\n}: {\n app: AppState\n /** Reactive accessor — components pass a getter so the composable stays prop-driven. */\n registryDocuments: () => RegistryDocumentsState\n}): {\n documents: ComputedRef<SidebarDocumentItem[]>\n activeRegistryMeta: ComputedRef<\n | {\n namespace: string\n slug: string\n version?: string\n commitHash?: string\n conflictCheckedAgainstHash?: string\n hasConflict?: boolean\n }\n | undefined\n >\n activeItem: ComputedRef<SidebarDocumentItem | undefined>\n versions: ComputedRef<SidebarDocumentVersion[]>\n activeVersion: ComputedRef<SidebarDocumentVersion | undefined>\n} => {\n const { documents } = useSidebarDocuments({\n app,\n managedDocs: () => registryDocuments().documents ?? [],\n })\n\n /** Registry meta for the currently active document (if any). */\n const activeRegistryMeta = computed(() => {\n const doc = app.store.value?.workspace.activeDocument\n return doc?.['x-scalar-registry-meta']\n })\n\n /**\n * Sidebar item representing the currently active registry-backed document.\n * We match by `namespace + slug` because a single group can contain\n * several versions and the active document may be any of them.\n */\n const activeItem = computed(() => {\n const meta = activeRegistryMeta.value\n if (!meta) {\n return undefined\n }\n return documents.value.find(\n (item) => item.registry?.namespace === meta.namespace && item.registry?.slug === meta.slug,\n )\n })\n\n /** Versions for the active group, ordered with the latest first. */\n const versions = computed<SidebarDocumentVersion[]>(() => activeItem.value?.versions ?? [])\n\n /**\n * The version currently active on screen. Prefers matching the version\n * declared on the active document's registry meta so consumers always\n * reflect what the user is viewing, even when the sidebar's notion of the\n * active version has not caught up yet (e.g. during a pending fetch).\n */\n const activeVersion = computed<SidebarDocumentVersion | undefined>(() => {\n const meta = activeRegistryMeta.value\n const list = versions.value\n if (!meta) {\n return list[0]\n }\n return list.find((v) => v.version === meta.version) ?? list[0]\n })\n\n return {\n documents,\n activeRegistryMeta,\n activeItem,\n versions,\n activeVersion,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,4BAA4B,EACvC,KACA,wBAqBG;CACH,MAAM,EAAE,cAAc,oBAAoB;EACxC;EACA,mBAAmB,mBAAmB,CAAC,aAAa,EAAE;EACvD,CAAC;;CAGF,MAAM,qBAAqB,eAAe;AAExC,UADY,IAAI,MAAM,OAAO,UAAU,kBAC1B;GACb;;;;;;CAOF,MAAM,aAAa,eAAe;EAChC,MAAM,OAAO,mBAAmB;AAChC,MAAI,CAAC,KACH;AAEF,SAAO,UAAU,MAAM,MACpB,SAAS,KAAK,UAAU,cAAc,KAAK,aAAa,KAAK,UAAU,SAAS,KAAK,KACvF;GACD;;CAGF,MAAM,WAAW,eAAyC,WAAW,OAAO,YAAY,EAAE,CAAC;AAiB3F,QAAO;EACL;EACA;EACA;EACA;EACA,eAdoB,eAAmD;GACvE,MAAM,OAAO,mBAAmB;GAChC,MAAM,OAAO,SAAS;AACtB,OAAI,CAAC,KACH,QAAO,KAAK;AAEd,UAAO,KAAK,MAAM,MAAM,EAAE,YAAY,KAAK,QAAQ,IAAI,KAAK;IAC5D;EAQD"}
@@ -0,0 +1,126 @@
1
+ import { useModal } from '@scalar/components';
2
+ import { type merge } from '@scalar/json-magic/diff';
3
+ import { type ComputedRef, type Ref } from 'vue';
4
+ import type { AppState } from '../../../../v2/features/app/app-state.js';
5
+ import { useActiveDocumentVersion } from '../../../../v2/features/app/hooks/use-active-document-version.js';
6
+ import type { RegistryAdapter, RegistryDocumentsState, RegistryNamespacesState } from '../../../../v2/types/configuration';
7
+ /**
8
+ * Registry meta as it was when a pull began. Captures the same shape
9
+ * `activeRegistryMeta` returns so we can hand it to the post-pull
10
+ * commit-hash stamp without re-deriving (and possibly missing) it once
11
+ * the rebase has finished mutating the active document.
12
+ */
13
+ type PendingPullRegistryMeta = NonNullable<ReturnType<typeof useActiveDocumentVersion>['activeRegistryMeta']['value']>;
14
+ /**
15
+ * In-flight pull state captured while the three-way merge editor is on
16
+ * screen. We need to hold onto the rebase result (so the user-resolved
17
+ * document can still be applied), the registry meta and slug we were
18
+ * pulling against, and the upstream commit hash advertised by the
19
+ * listing - none of those are reachable from inside the conflict
20
+ * modal once the surrounding closure has unwound.
21
+ *
22
+ * `null` while no pull is awaiting user input. The modal-close handler
23
+ * resets it so a dismissed pull cleanly aborts without applying any
24
+ * changes (the workspace store only mutates when `applyChanges` is
25
+ * actually invoked).
26
+ */
27
+ type PendingPullState = {
28
+ rebaseResult: {
29
+ originalDocument: Record<string, unknown>;
30
+ resolvedDocument: Record<string, unknown>;
31
+ conflicts: ReturnType<typeof merge>['conflicts'];
32
+ applyChanges: (input: {
33
+ resolvedDocument: Record<string, unknown>;
34
+ }) => Promise<void>;
35
+ };
36
+ meta: PendingPullRegistryMeta;
37
+ slug: string;
38
+ incomingCommitHash: string | undefined;
39
+ };
40
+ /**
41
+ * Owns the document-level sync flow for the API client header: save,
42
+ * revert, pull, push, and first-time publish. The top-level `App.vue`
43
+ * was accumulating all five flows alongside its routing / layout glue,
44
+ * which made the file hard to reason about in isolation - extracting
45
+ * the registry-bound logic into a single hook keeps the component
46
+ * declarative and gives the flow a single place to test against.
47
+ *
48
+ * The hook owns:
49
+ * - the visibility computeds for each header action cluster,
50
+ * - the network-aware enable / disable gates (`canPullActiveDocument`,
51
+ * `canPushActiveDocument`),
52
+ * - the modal states for the publish + sync-conflict flows,
53
+ * - the pending pull state captured while the three-way merge editor
54
+ * is open,
55
+ * - and every async handler invoked from the header buttons / modals.
56
+ */
57
+ export declare const useDocumentSync: ({ app, registry, registryDocuments, }: {
58
+ app: AppState;
59
+ /** Registry adapter wired up by the host application; `undefined` when registry features are disabled. */
60
+ registry: RegistryAdapter | undefined;
61
+ /** Reactive accessor — components pass a getter so the hook stays prop-driven. */
62
+ registryDocuments: () => RegistryDocumentsState;
63
+ }) => {
64
+ /** Whether the local-workspace Save / Revert cluster should mount. */
65
+ showLocalSaveActions: ComputedRef<boolean>;
66
+ /** Whether the team-workspace Pull / Push cluster should mount. */
67
+ showTeamSyncActions: ComputedRef<boolean>;
68
+ /** Whether the team-workspace Publish cluster (no registry meta yet) should mount. */
69
+ showTeamPublishAction: ComputedRef<boolean>;
70
+ /** Truthy when at least one of the action clusters is renderable. */
71
+ hasHeaderActionCluster: ComputedRef<boolean>;
72
+ /** Whether the active document has unsaved local edits. */
73
+ isActiveDocumentDirty: ComputedRef<boolean>;
74
+ /** Reactive online status. */
75
+ isOnline: Ref<boolean>;
76
+ /** Reactive offline status (inverse of `isOnline`). */
77
+ isOffline: ComputedRef<boolean>;
78
+ /** Whether the pull button should be clickable. */
79
+ canPullActiveDocument: ComputedRef<boolean>;
80
+ /** Whether the push button should be clickable. */
81
+ canPushActiveDocument: ComputedRef<boolean>;
82
+ /** Modal state for the first-time publish modal. */
83
+ publishDocumentModalState: ReturnType<typeof useModal>;
84
+ /** Modal state for the three-way merge editor. */
85
+ syncConflictModalState: ReturnType<typeof useModal>;
86
+ /** In-flight pull state shared with the three-way merge editor. */
87
+ pendingPullState: Ref<PendingPullState | null>;
88
+ /** Default slug pre-filled into the publish modal. */
89
+ publishDefaultSlug: ComputedRef<string>;
90
+ /** Default version pre-filled into the publish modal. */
91
+ publishDefaultVersion: ComputedRef<string>;
92
+ /** Reactive view of the namespaces the user can publish to. */
93
+ registryNamespaces: ComputedRef<RegistryNamespacesState>;
94
+ /** Persists local edits on the active document. */
95
+ handleSaveDocument: () => Promise<void>;
96
+ /** Discards local edits and restores the saved baseline. */
97
+ handleRevertDocument: () => Promise<void>;
98
+ /** Pulls the active document from the registry and rebases local edits. */
99
+ handlePullDocument: () => Promise<void>;
100
+ /** Publishes the active document as the next commit on the current registry version. */
101
+ handlePushDocument: () => Promise<void>;
102
+ /** Opens the publish modal for the active document. */
103
+ handlePublishDocument: () => void;
104
+ /** Submit handler invoked by the publish modal once the user confirms their inputs. */
105
+ handlePublishDocumentSubmit: (params: {
106
+ input: {
107
+ namespace: string;
108
+ slug: string;
109
+ version: string;
110
+ };
111
+ done: (outcome: {
112
+ ok: true;
113
+ } | {
114
+ ok: false;
115
+ message: string;
116
+ }) => void;
117
+ }) => Promise<void>;
118
+ /** Apply callback wired up to the three-way merge editor. */
119
+ handleSyncConflictApplyChanges: (params: {
120
+ resolvedDocument: Record<string, unknown>;
121
+ }) => Promise<void>;
122
+ /** Reset the pending pull when the conflict modal closes without an apply. */
123
+ handleSyncConflictModalClose: () => void;
124
+ };
125
+ export {};
126
+ //# sourceMappingURL=use-document-sync.d.ts.map