@jxsuite/studio 6.1.0 → 6.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/2020-f2gkpy8d.js +4 -0
- package/dist/chunks/{action-group-2mqby73c.js → action-group-4aan7g6g.js} +3 -3
- package/dist/chunks/adapter-4912g339.js +4 -0
- package/dist/chunks/cf-account-picker-wccaenh6.js +4 -0
- package/dist/chunks/cf-settings-br6105tr.js +4 -0
- package/dist/chunks/color-28ksk2qm.js +4 -0
- package/dist/chunks/color-area-0fj8a4ga.js +4 -0
- package/dist/chunks/color-field-xch8kx2g.js +4 -0
- package/dist/chunks/color-slider-jxd8v350.js +4 -0
- package/dist/chunks/combobox-p0efwjke.js +4 -0
- package/dist/chunks/components-db68vsn7.js +4 -0
- package/dist/chunks/content-source-1449qesq.js +4 -0
- package/dist/chunks/convert-to-repeater-5h457egj.js +4 -0
- package/dist/chunks/dialog-c4x05vyx.js +4 -0
- package/dist/chunks/editor-x9jn86a7.js +4 -0
- package/dist/chunks/entry-commands-may24cpt.js +4 -0
- package/dist/chunks/extension-sections-pvr14vxr.js +4 -0
- package/dist/chunks/file-ops-x9v54pcj.js +4 -0
- package/dist/chunks/files-j3knqf0s.js +4 -0
- package/dist/chunks/format-host-tyre2trb.js +4 -0
- package/dist/chunks/fs-events-64ma9htc.js +4 -0
- package/dist/chunks/git-panel-84ps5dex.js +4 -0
- package/dist/chunks/grid-view-d2wq9ezg.js +51 -0
- package/dist/chunks/icons-pnfz478q.js +4 -0
- package/dist/chunks/iframe-entry-fvbk89gm.js +5 -0
- package/dist/chunks/iframe-entry-jdcktez8.js +5 -0
- package/dist/chunks/iframe-entry-x0ygf2yr.js +5 -0
- package/dist/chunks/iframe-entry-zap785cw.js +5 -0
- package/dist/chunks/iframe-host-ck6vejt5.js +4 -0
- package/dist/chunks/index-b8xw3vy9.js +4 -0
- package/dist/chunks/{index-gybvv7a6.js → index-zpjmzqfk.js} +3 -3
- package/dist/chunks/{javascript-rnvdp3k4.js → javascript-m54mnwp6.js} +3 -3
- package/dist/chunks/jsonMode-w15cmrcm.js +13 -0
- package/dist/chunks/jx-validate-at3zn8wa.js +4 -0
- package/dist/chunks/layers-h4xp1pss.js +4 -0
- package/dist/chunks/layers-panel-m5hw1d7g.js +4 -0
- package/dist/chunks/listbox-e74qjyq6.js +4 -0
- package/dist/chunks/lit-html-8t8t3dnr.js +4 -0
- package/dist/chunks/media-picker-2cweexc5.js +4 -0
- package/dist/chunks/menu-n6yzswmy.js +4 -0
- package/dist/chunks/menu-ynw13y0s.js +4 -0
- package/dist/chunks/monaco-binding-7gx83x3r.js +4 -0
- package/dist/chunks/monaco-buffer-qzxn567r.js +4 -0
- package/dist/chunks/monaco-setup-gxf6656g.js +4 -0
- package/dist/chunks/notify-cj5zbrs2.js +4 -0
- package/dist/chunks/number-field-y62xq0ty.js +4 -0
- package/dist/chunks/parse-b7ddhr8m.js +4 -0
- package/dist/chunks/platform-3dfws9js.js +4 -0
- package/dist/chunks/popover-jt2gxmzs.js +4 -0
- package/dist/chunks/publish-panel-1gvxbj21.js +4 -0
- package/dist/chunks/quick-search-0dajrwqg.js +4 -0
- package/dist/chunks/references-49qgt404.js +4 -0
- package/dist/chunks/right-panel-jm1bwe53.js +4 -0
- package/dist/chunks/section-registry-6kwc4fky.js +4 -0
- package/dist/chunks/select-rzds841e.js +4 -0
- package/dist/chunks/split-n2gfpm5m.js +4 -0
- package/dist/chunks/{studio-5g0ag7c0.js → studio-0q988f9w.js} +3 -3
- package/dist/chunks/studio-0rt52vpq.js +5 -0
- package/dist/chunks/studio-18vgbhs5.js +5 -0
- package/dist/chunks/{studio-pxg4ap1e.js → studio-1fxkaaea.js} +4 -4
- package/dist/chunks/studio-24mccdrj.js +5 -0
- package/dist/chunks/studio-2eh507zk.js +9 -0
- package/dist/chunks/studio-2hcbccfv.js +5 -0
- package/dist/chunks/studio-2j1cb95x.js +5 -0
- package/dist/chunks/studio-2jbs3rxv.js +5 -0
- package/dist/chunks/{studio-qbj760tx.js → studio-485046te.js} +7 -7
- package/dist/chunks/studio-4h8k4dw9.js +5 -0
- package/dist/chunks/studio-5f6n18kx.js +5 -0
- package/dist/chunks/studio-5gthpky6.js +5 -0
- package/dist/chunks/studio-5ve6bbep.js +5 -0
- package/dist/chunks/studio-6ep3js6b.js +9 -0
- package/dist/chunks/studio-6wkmf3wz.js +9 -0
- package/dist/chunks/studio-841rf3cg.js +5 -0
- package/dist/chunks/studio-8gmwgheg.js +5 -0
- package/dist/chunks/studio-8qm0ep50.js +5 -0
- package/dist/chunks/{studio-anyrgzvb.js → studio-90pq9km1.js} +4 -4
- package/dist/chunks/studio-924p1tm1.js +6 -0
- package/dist/chunks/{studio-v2bvmbjj.js → studio-a3g98wjr.js} +4 -4
- package/dist/chunks/studio-a3shpz9y.js +5 -0
- package/dist/chunks/studio-amsbaaar.js +6 -0
- package/dist/chunks/studio-at5spjbc.js +7 -0
- package/dist/chunks/studio-b2xv945e.js +5 -0
- package/dist/chunks/studio-bn5nkafw.js +5 -0
- package/dist/chunks/studio-bw6q5xjf.js +9 -0
- package/dist/chunks/studio-c7q0t9x1.js +5 -0
- package/dist/chunks/studio-camnhc96.js +6 -0
- package/dist/chunks/studio-cffqvnzd.js +5 -0
- package/dist/chunks/studio-d81xfh1g.js +5 -0
- package/dist/chunks/studio-dgz8d0c1.js +5 -0
- package/dist/chunks/studio-efvfrsf8.js +5 -0
- package/dist/chunks/studio-er30bpcx.js +5 -0
- package/dist/chunks/studio-ezscgr64.js +5 -0
- package/dist/chunks/studio-ffrrncvz.js +5 -0
- package/dist/chunks/studio-fpphgn8y.js +10 -0
- package/dist/chunks/studio-j1kvhdjk.js +4 -0
- package/dist/chunks/{studio-txjjzrsm.js → studio-j430n16g.js} +5 -5
- package/dist/chunks/studio-jjkg84nj.js +5 -0
- package/dist/chunks/studio-m0q32ars.js +4 -0
- package/dist/chunks/studio-m6bdst11.js +5 -0
- package/dist/chunks/studio-nv2cptd4.js +64 -0
- package/dist/chunks/studio-nwfrc98n.js +5 -0
- package/dist/chunks/studio-p1wgvp7r.js +5 -0
- package/dist/chunks/studio-pag2wncm.js +5 -0
- package/dist/chunks/studio-qvanvzfd.js +5 -0
- package/dist/chunks/studio-qwa64kt7.js +5 -0
- package/dist/chunks/studio-rk1zc4x3.js +928 -0
- package/dist/chunks/studio-se2r3ptc.js +5 -0
- package/dist/chunks/studio-sg2a0dh0.js +5 -0
- package/dist/chunks/studio-tjzjhe68.js +5 -0
- package/dist/chunks/{studio-0x3jyb8r.js → studio-tq0rtfwj.js} +4 -4
- package/dist/chunks/{studio-cpxkgjrj.js → studio-v0jzqdjm.js} +4 -4
- package/dist/chunks/studio-v2w7q8r1.js +7 -0
- package/dist/chunks/studio-v5vjq1h0.js +4 -0
- package/dist/chunks/studio-v8aga3vp.js +14 -0
- package/dist/chunks/studio-vfcdrfmv.js +5 -0
- package/dist/chunks/studio-vmfkgdjf.js +5 -0
- package/dist/chunks/studio-wt1816jj.js +5 -0
- package/dist/chunks/studio-wz73q5kq.js +4 -0
- package/dist/chunks/studio-y04qtxvp.js +7 -0
- package/dist/chunks/studio-y21g8zzg.js +5 -0
- package/dist/chunks/studio-y64hrt8p.js +12 -0
- package/dist/chunks/studio-ynn90f58.js +16 -0
- package/dist/chunks/studio-yxz682sx.js +5 -0
- package/dist/chunks/studio-zp7snvkq.js +5 -0
- package/dist/chunks/studio-zq4z6tm6.js +5 -0
- package/dist/chunks/swatch-group-ewh24n1f.js +4 -0
- package/dist/chunks/tabs-f5cw3tgp.js +4 -0
- package/dist/chunks/textfield-wewc8rq2.js +4 -0
- package/dist/chunks/toast-c9fn075p.js +4 -0
- package/dist/chunks/toast-host-ke5z4ng7.js +4 -0
- package/dist/chunks/{toolbar-2nnzqhrj.js → toolbar-44cm5cta.js} +3 -3
- package/dist/chunks/tooltip-5v8ngwj2.js +4 -0
- package/dist/chunks/tree-mdq3v5cw.js +4 -0
- package/dist/chunks/tsMode-sf49ee9x.js +14 -0
- package/dist/chunks/workspace-jtfahpee.js +4 -0
- package/dist/chunks/ws-client-nze0k6mv.js +4 -0
- package/dist/iframe-entry.js +16 -16
- package/dist/manifest.json +63 -63
- package/dist/studio.js +98 -98
- package/dist/workers/ts.worker.js +3 -3
- package/package.json +11 -11
- package/src/about/about-modal.ts +4 -3
- package/src/account/action-flow.ts +45 -38
- package/src/account-status.ts +7 -2
- package/src/browse/library-layouts.ts +8 -5
- package/src/browse/library-model.ts +18 -6
- package/src/browse/library-pane.ts +37 -21
- package/src/browse/library-preview.ts +14 -9
- package/src/browse/library-source.ts +1 -1
- package/src/canvas/asset-refs.ts +24 -9
- package/src/canvas/asset-resolve.ts +19 -23
- package/src/canvas/canvas-helpers.ts +7 -6
- package/src/canvas/canvas-live-render.ts +44 -53
- package/src/canvas/canvas-patcher.ts +30 -30
- package/src/canvas/canvas-perf.ts +11 -5
- package/src/canvas/canvas-render.ts +66 -42
- package/src/canvas/canvas-surface.ts +5 -3
- package/src/canvas/canvas-utils.ts +163 -64
- package/src/canvas/dialog-path.ts +9 -4
- package/src/canvas/dialog-state.ts +1 -2
- package/src/canvas/diff-marks.ts +9 -16
- package/src/canvas/diff-toolbar.ts +8 -7
- package/src/canvas/edit-width-drag.ts +4 -1
- package/src/canvas/edit-width.ts +1 -3
- package/src/canvas/iframe-channel.ts +27 -13
- package/src/canvas/iframe-drop.ts +13 -12
- package/src/canvas/iframe-editable-root.ts +29 -16
- package/src/canvas/iframe-entry.ts +80 -63
- package/src/canvas/iframe-eval.ts +73 -49
- package/src/canvas/iframe-host.ts +203 -118
- package/src/canvas/iframe-inline-edit.ts +69 -32
- package/src/canvas/iframe-insert.ts +5 -4
- package/src/canvas/iframe-interaction.ts +37 -26
- package/src/canvas/iframe-overlay.ts +1 -1
- package/src/canvas/iframe-patch.ts +18 -15
- package/src/canvas/iframe-position.ts +12 -6
- package/src/canvas/iframe-protocol.ts +15 -16
- package/src/canvas/iframe-render.ts +41 -36
- package/src/canvas/iframe-slash.ts +24 -11
- package/src/canvas/iframe-subtree.ts +6 -2
- package/src/canvas/path-mapping.ts +19 -12
- package/src/canvas/popover-path.ts +7 -3
- package/src/canvas/popover-state.ts +1 -2
- package/src/canvas/serialize-scope.ts +13 -5
- package/src/canvas/stage-scrollbar.ts +2 -1
- package/src/canvas/surface-registry.ts +21 -3
- package/src/collab/collab-commands.ts +7 -2
- package/src/collab/collab-session.ts +84 -45
- package/src/collab/collab-state.ts +4 -3
- package/src/collab/monaco-binding.ts +16 -13
- package/src/collab/monaco-cursors.ts +5 -2
- package/src/commands/budget.ts +3 -3
- package/src/commands/command-args.ts +103 -71
- package/src/commands/context.ts +22 -20
- package/src/commands/defaults.ts +4 -1
- package/src/commands/keymap.ts +47 -43
- package/src/commands/levels.ts +3 -3
- package/src/commands/live-context.ts +10 -8
- package/src/commands/reference.ts +2 -1
- package/src/commands/registry.ts +12 -10
- package/src/commands/run-reported.ts +15 -10
- package/src/component-props.ts +46 -30
- package/src/content/collection-match.ts +5 -4
- package/src/content/draft-state.ts +4 -3
- package/src/content/entry-commands.ts +3 -2
- package/src/content/entry-editor.ts +29 -17
- package/src/content/entry-fields.ts +12 -7
- package/src/content/entry-model.ts +21 -12
- package/src/editor/canvas-context-menu.ts +1 -2
- package/src/editor/canvas-slash-bridge.ts +3 -6
- package/src/editor/content-slice.ts +16 -14
- package/src/editor/context-menu.ts +35 -22
- package/src/editor/convert-targets.ts +30 -35
- package/src/editor/convert-to-component.ts +11 -5
- package/src/editor/convert-to-repeater.ts +34 -22
- package/src/editor/editable-tags.ts +17 -2
- package/src/editor/file-drop-action.ts +21 -22
- package/src/editor/inline-edit-apply.ts +35 -17
- package/src/editor/inline-edit.ts +66 -50
- package/src/editor/inline-format.ts +59 -56
- package/src/editor/inline-link.ts +40 -15
- package/src/editor/insert-zone-action.ts +1 -1
- package/src/editor/merge-tags.ts +58 -19
- package/src/editor/repeater-scope.ts +39 -28
- package/src/editor/shortcuts.ts +87 -47
- package/src/editor/slash-menu.ts +1 -6
- package/src/files/asset-versions.ts +4 -4
- package/src/files/components.ts +15 -10
- package/src/files/elements.ts +5 -6
- package/src/files/file-ops.ts +77 -36
- package/src/files/files.ts +90 -95
- package/src/files/media-meta.ts +2 -1
- package/src/files/media-paths.ts +6 -1
- package/src/files/serialize-document.ts +2 -2
- package/src/format/constraints.ts +1 -1
- package/src/format/convert-file.ts +27 -13
- package/src/format/format-choices.ts +2 -1
- package/src/format/format-host.ts +76 -50
- package/src/github/github-auth-status.ts +27 -3
- package/src/github/github-auth.ts +19 -9
- package/src/github/github-publish.ts +24 -20
- package/src/grid/cell-editors.ts +46 -31
- package/src/grid/cell-popovers.ts +5 -3
- package/src/grid/edit-buffer.ts +10 -9
- package/src/grid/grid-controller.ts +20 -22
- package/src/grid/grid-layout.ts +21 -5
- package/src/grid/grid-open.ts +3 -6
- package/src/grid/grid-panel.ts +11 -4
- package/src/grid/grid-source.ts +8 -8
- package/src/grid/grid-view.ts +14 -17
- package/src/grid/redirects-grid.ts +14 -5
- package/src/grid/redirects.ts +16 -14
- package/src/grid/schema-columns.ts +25 -19
- package/src/grid/sources/connector-source.ts +47 -20
- package/src/grid/sources/content-source.ts +25 -24
- package/src/grid/sources/csv-file-source.ts +2 -2
- package/src/grid/tabulator-tables.d.ts +55 -34
- package/src/hosting/stage.ts +7 -7
- package/src/i18n/i18n-commands.ts +1 -2
- package/src/media/media-pane.ts +4 -1
- package/src/new-project/add-repo-modal.ts +20 -19
- package/src/new-project/import-tab.ts +10 -14
- package/src/new-project/location-fields.ts +2 -1
- package/src/new-project/new-project-modal.ts +12 -9
- package/src/packages/jxsuite-update.ts +4 -3
- package/src/packages/pull-package-sync.ts +26 -25
- package/src/page-params.ts +97 -68
- package/src/panels/activity-panel.ts +17 -9
- package/src/panels/ai-chat/attached-context.ts +13 -7
- package/src/panels/ai-chat/chat-view.ts +80 -73
- package/src/panels/ai-chat/composer.ts +30 -33
- package/src/panels/ai-chat/sessions-view.ts +1 -2
- package/src/panels/ai-panel.ts +23 -20
- package/src/panels/block-action-bar.ts +46 -35
- package/src/panels/bottom-dock.ts +5 -3
- package/src/panels/canvas-dnd-bridge.ts +31 -24
- package/src/panels/data-explorer.ts +62 -25
- package/src/panels/data-grid.ts +14 -11
- package/src/panels/dnd.ts +108 -118
- package/src/panels/editors.ts +55 -22
- package/src/panels/elements-panel.ts +10 -3
- package/src/panels/events-panel.ts +84 -69
- package/src/panels/formula-workspace.ts +76 -51
- package/src/panels/frontmatter-fields.ts +24 -19
- package/src/panels/frontmatter-panel.ts +44 -34
- package/src/panels/git-diff-open.ts +4 -4
- package/src/panels/git-panel.ts +49 -61
- package/src/panels/head-panel.ts +131 -111
- package/src/panels/i18n-panel.ts +10 -3
- package/src/panels/imports-panel.ts +23 -47
- package/src/panels/jump-bar.ts +48 -29
- package/src/panels/layers-panel.ts +97 -74
- package/src/panels/left-panel.ts +3 -3
- package/src/panels/pane-context.ts +31 -22
- package/src/panels/pane-grid.ts +49 -20
- package/src/panels/panel-registry.ts +22 -37
- package/src/panels/properties-panel.ts +175 -121
- package/src/panels/quick-search.ts +53 -31
- package/src/panels/right-panel.ts +3 -3
- package/src/panels/seo-modal.ts +13 -17
- package/src/panels/settings-menu.ts +15 -15
- package/src/panels/signals-panel.ts +153 -131
- package/src/panels/statement-editor.ts +230 -140
- package/src/panels/style-panel.ts +254 -216
- package/src/panels/style-utils.ts +114 -51
- package/src/panels/stylebook-doc.ts +57 -54
- package/src/panels/stylebook-layers-panel.ts +9 -9
- package/src/panels/stylebook-panel.ts +13 -17
- package/src/panels/tab-drop.ts +27 -11
- package/src/panels/tab-strip.ts +36 -26
- package/src/platform-errors.ts +57 -43
- package/src/platform.ts +58 -30
- package/src/platforms/cloud.ts +345 -175
- package/src/platforms/default-platform.ts +10 -4
- package/src/platforms/devserver.ts +174 -127
- package/src/platforms/read-batcher.ts +16 -8
- package/src/project-list.ts +2 -1
- package/src/publish/deploy-checklist.ts +8 -2
- package/src/publish/pages-service.ts +28 -13
- package/src/publish/publish-commands.ts +5 -4
- package/src/publish/publish-panel.ts +10 -6
- package/src/recent-projects.ts +15 -8
- package/src/resize-edges.ts +26 -36
- package/src/services/a11y-report.ts +26 -19
- package/src/services/agent-seed.ts +23 -11
- package/src/services/ai-ask.ts +9 -12
- package/src/services/ai-command-tools.ts +68 -62
- package/src/services/ai-import-tools.ts +80 -67
- package/src/services/ai-models.ts +17 -13
- package/src/services/ai-project-tools.ts +68 -32
- package/src/services/ai-session-store.ts +19 -10
- package/src/services/ai-system-prompt.ts +46 -20
- package/src/services/ai-tools.ts +71 -27
- package/src/services/ai-write-report.ts +20 -12
- package/src/services/ai-writes.ts +4 -4
- package/src/services/automation.ts +58 -31
- package/src/services/cem-export.ts +149 -58
- package/src/services/clock.ts +6 -1
- package/src/services/code-services.ts +37 -31
- package/src/services/context-manager.ts +14 -10
- package/src/services/context-resolver.ts +20 -8
- package/src/services/data-service.ts +19 -15
- package/src/services/directory-picker.ts +18 -12
- package/src/services/document-assistant.ts +39 -19
- package/src/services/gated-registry.ts +10 -6
- package/src/services/harness/chat-reducer.ts +23 -1
- package/src/services/import-client.ts +91 -38
- package/src/services/import-seed.ts +8 -2
- package/src/services/jx-validate.ts +28 -31
- package/src/services/live-preview.ts +0 -0
- package/src/services/live-surfaces.ts +6 -3
- package/src/services/model-uri.ts +10 -2
- package/src/services/module-preloads.ts +12 -8
- package/src/services/monaco-buffer.ts +43 -24
- package/src/services/monaco-lazy.ts +7 -8
- package/src/services/monaco-setup.ts +23 -16
- package/src/services/notify.ts +44 -17
- package/src/services/popover-report.ts +25 -10
- package/src/services/preview-eval.ts +20 -8
- package/src/services/profile.ts +13 -6
- package/src/services/project-path.ts +13 -10
- package/src/services/references.ts +0 -0
- package/src/services/render-critic.ts +8 -7
- package/src/services/settings/kernel.ts +13 -4
- package/src/services/settings/write-queue.ts +9 -9
- package/src/services/token-lint.ts +12 -10
- package/src/services/tool-executor.ts +17 -5
- package/src/services/tool-outcomes.ts +44 -24
- package/src/services/trusted-types.ts +24 -7
- package/src/settings/contexts-section.ts +11 -8
- package/src/settings/contributed-section.ts +79 -60
- package/src/settings/css-vars-editor.ts +37 -16
- package/src/settings/defs-editor.ts +38 -35
- package/src/settings/extension-rows.ts +16 -8
- package/src/settings/extension-sections.ts +23 -13
- package/src/settings/extensions-section.ts +4 -1
- package/src/settings/general-settings.ts +11 -8
- package/src/settings/locales-section.ts +20 -12
- package/src/settings/preferences-dialog.ts +9 -6
- package/src/settings/preferences-keymap.ts +7 -5
- package/src/settings/preferences-sections.ts +11 -5
- package/src/settings/project-sections.ts +1 -1
- package/src/settings/schema-field-ui.ts +9 -0
- package/src/settings/settings-document.ts +5 -5
- package/src/shell/tree.ts +6 -5
- package/src/shell.ts +104 -81
- package/src/site-context.ts +37 -22
- package/src/state.ts +41 -49
- package/src/store.ts +54 -34
- package/src/studio.ts +39 -77
- package/src/style/live-preview.ts +1 -1
- package/src/style/project-styles.ts +6 -7
- package/src/style/token-ref.ts +5 -4
- package/src/surfaces/about.ts +18 -19
- package/src/surfaces/add-repo.ts +17 -19
- package/src/surfaces/ai-chat.ts +31 -36
- package/src/surfaces/ai-credentials-form.ts +6 -16
- package/src/surfaces/ai-managed-connect.ts +3 -4
- package/src/surfaces/ai-model-picker.ts +3 -5
- package/src/surfaces/block-action-bar.ts +22 -11
- package/src/surfaces/boot-failure.ts +4 -5
- package/src/surfaces/bottom-dock.ts +9 -18
- package/src/surfaces/canvas-stage.ts +12 -11
- package/src/surfaces/cf-account-picker.ts +17 -18
- package/src/surfaces/commandbar.ts +14 -9
- package/src/surfaces/convert-repeater.ts +17 -19
- package/src/surfaces/data-actions.ts +9 -7
- package/src/surfaces/dialog.ts +26 -28
- package/src/surfaces/diff-toolbar.ts +9 -7
- package/src/surfaces/doc-header.ts +10 -19
- package/src/surfaces/empty-state.ts +7 -10
- package/src/surfaces/entry-editor.ts +14 -11
- package/src/surfaces/expression-editor.ts +11 -25
- package/src/surfaces/files-panel.ts +27 -14
- package/src/surfaces/formula-palette.ts +4 -5
- package/src/surfaces/git-panel.ts +7 -15
- package/src/surfaces/github-auth.ts +16 -20
- package/src/surfaces/github-publish.ts +17 -22
- package/src/surfaces/grid-cell.ts +30 -31
- package/src/surfaces/grid-open.ts +10 -14
- package/src/surfaces/grid-panel.ts +13 -9
- package/src/surfaces/grid-replace.ts +13 -13
- package/src/surfaces/grid-views.ts +15 -18
- package/src/surfaces/inspector-dock.ts +13 -12
- package/src/surfaces/jump-bar.ts +5 -6
- package/src/surfaces/jxsuite-update.ts +16 -22
- package/src/surfaces/library-pane.ts +25 -20
- package/src/surfaces/logic-panel.ts +17 -30
- package/src/surfaces/logic-workspace.ts +13 -15
- package/src/surfaces/media-browser.ts +11 -10
- package/src/surfaces/media-field.ts +4 -5
- package/src/surfaces/media-pane.ts +8 -7
- package/src/surfaces/menu.ts +29 -23
- package/src/surfaces/navigator-dock.ts +11 -12
- package/src/surfaces/new-project.ts +27 -26
- package/src/surfaces/pane-context.ts +20 -25
- package/src/surfaces/pane-grid.ts +15 -10
- package/src/surfaces/panel-activity.ts +5 -6
- package/src/surfaces/panel-data.ts +3 -4
- package/src/surfaces/panel-deploy-checklist.ts +5 -6
- package/src/surfaces/panel-elements.ts +5 -7
- package/src/surfaces/panel-i18n.ts +5 -7
- package/src/surfaces/panel-imports.ts +6 -13
- package/src/surfaces/panel-outline.ts +25 -19
- package/src/surfaces/panel-page.ts +11 -18
- package/src/surfaces/panel-problems.ts +5 -7
- package/src/surfaces/panel-signals.ts +20 -34
- package/src/surfaces/panel-stylebook-layers.ts +5 -8
- package/src/surfaces/preferences.ts +21 -11
- package/src/surfaces/progress-modal.ts +15 -9
- package/src/surfaces/properties-panel.json +1 -1
- package/src/surfaces/properties-panel.ts +20 -42
- package/src/surfaces/publish.ts +18 -18
- package/src/surfaces/push-plan.ts +7 -8
- package/src/surfaces/rail.json +126 -73
- package/src/surfaces/rail.ts +38 -12
- package/src/surfaces/reference-field.ts +11 -15
- package/src/surfaces/schema-builder.ts +16 -22
- package/src/surfaces/schema-form.ts +15 -25
- package/src/surfaces/secret-field.ts +9 -6
- package/src/surfaces/seo.ts +21 -15
- package/src/surfaces/settings-contexts.ts +9 -7
- package/src/surfaces/settings-contributed.ts +14 -10
- package/src/surfaces/settings-css-vars.ts +5 -12
- package/src/surfaces/settings-defs.json +2 -2
- package/src/surfaces/settings-defs.ts +17 -21
- package/src/surfaces/settings-deploy.ts +8 -15
- package/src/surfaces/settings-extensions.ts +5 -6
- package/src/surfaces/settings-head.ts +33 -39
- package/src/surfaces/settings-locales.ts +8 -20
- package/src/surfaces/settings-overview.ts +5 -6
- package/src/surfaces/settings-packages.ts +8 -7
- package/src/surfaces/settings-pane.ts +9 -18
- package/src/surfaces/settings-rawjson.ts +5 -6
- package/src/surfaces/shell.ts +1 -2
- package/src/surfaces/slash-menu.ts +19 -9
- package/src/surfaces/statements.ts +13 -18
- package/src/surfaces/statusbar.ts +17 -14
- package/src/surfaces/style-panel.ts +18 -31
- package/src/surfaces/stylebook-chrome.ts +3 -5
- package/src/surfaces/tab-strip.ts +14 -13
- package/src/surfaces/target-line.ts +6 -7
- package/src/surfaces/welcome.ts +5 -7
- package/src/tabs/patch-ops.ts +4 -3
- package/src/tabs/project-config.ts +32 -21
- package/src/tabs/selection.ts +36 -8
- package/src/tabs/tab.ts +52 -41
- package/src/tabs/transact.ts +98 -53
- package/src/tabs/typing.ts +31 -0
- package/src/types.ts +32 -17
- package/src/ui/ai-credentials-form.ts +10 -13
- package/src/ui/ai-model-picker.ts +3 -3
- package/src/ui/cf-account-picker.ts +7 -9
- package/src/ui/color-selector.ts +9 -5
- package/src/ui/dynamic-slot.ts +7 -8
- package/src/ui/expression-editor.ts +112 -105
- package/src/ui/form-controls.ts +57 -48
- package/src/ui/formula-catalog.ts +239 -230
- package/src/ui/formula-chips.ts +33 -18
- package/src/ui/layers.ts +44 -31
- package/src/ui/media-picker.ts +18 -9
- package/src/ui/popover-surface.ts +20 -12
- package/src/ui/progress-modal.ts +13 -8
- package/src/ui/regions.ts +7 -6
- package/src/ui/schema-form.ts +113 -70
- package/src/ui/surface.ts +71 -8
- package/src/ui/timing.ts +9 -0
- package/src/ui/value-source.ts +44 -31
- package/src/ui/virtual-window.ts +3 -2
- package/src/utils/base64.ts +3 -1
- package/src/utils/edit-display.ts +103 -74
- package/src/utils/element-selectors.ts +3 -2
- package/src/utils/google-fonts.ts +15 -14
- package/src/utils/inherited-style.ts +18 -20
- package/src/utils/link-target.ts +1 -1
- package/src/utils/preview-format.ts +25 -3
- package/src/utils/strip-events.ts +49 -22
- package/src/utils/studio-utils.ts +29 -14
- package/src/view.ts +2 -3
- package/src/workspace/pane-derive.ts +10 -12
- package/src/workspace/session.ts +53 -43
- package/src/workspace/workspace.ts +47 -36
- package/dist/chunks/2020-nkxms0ay.js +0 -4
- package/dist/chunks/adapter-m948ab5f.js +0 -4
- package/dist/chunks/cf-account-picker-n8hr38v7.js +0 -4
- package/dist/chunks/cf-settings-356xdtne.js +0 -4
- package/dist/chunks/color-area-jqhna8zy.js +0 -4
- package/dist/chunks/color-field-embq5ctg.js +0 -4
- package/dist/chunks/color-fpezvnkt.js +0 -4
- package/dist/chunks/color-slider-1qmw7wqy.js +0 -4
- package/dist/chunks/combobox-773hdzyj.js +0 -4
- package/dist/chunks/components-yk3t844e.js +0 -4
- package/dist/chunks/content-source-bawmyc09.js +0 -4
- package/dist/chunks/convert-to-repeater-7wns8kf1.js +0 -4
- package/dist/chunks/dialog-mtm9fq5t.js +0 -4
- package/dist/chunks/editor-6sc8nacq.js +0 -4
- package/dist/chunks/entry-commands-8ay9fh81.js +0 -4
- package/dist/chunks/extension-sections-na7rj3nr.js +0 -4
- package/dist/chunks/file-ops-gye0qw56.js +0 -4
- package/dist/chunks/files-dmc2xvcq.js +0 -4
- package/dist/chunks/format-host-t76b53f8.js +0 -4
- package/dist/chunks/fs-events-cg4tpz06.js +0 -4
- package/dist/chunks/git-panel-yef7bshy.js +0 -4
- package/dist/chunks/grid-view-7tt4bxw1.js +0 -51
- package/dist/chunks/icons-qbh8akb1.js +0 -4
- package/dist/chunks/iframe-entry-f7mqjwpm.js +0 -5
- package/dist/chunks/iframe-entry-haw7jkvz.js +0 -5
- package/dist/chunks/iframe-entry-z6qrytv3.js +0 -5
- package/dist/chunks/iframe-host-tmz30gfp.js +0 -4
- package/dist/chunks/index-nag78a9h.js +0 -4
- package/dist/chunks/jsonMode-zhwsprqk.js +0 -13
- package/dist/chunks/jx-validate-3gbp5nkn.js +0 -4
- package/dist/chunks/layers-fvz99859.js +0 -4
- package/dist/chunks/layers-panel-gchmbf9p.js +0 -4
- package/dist/chunks/listbox-s8fkmnq8.js +0 -4
- package/dist/chunks/lit-html-9agdspkt.js +0 -4
- package/dist/chunks/media-picker-rhjvyy00.js +0 -4
- package/dist/chunks/menu-2e4y9j7b.js +0 -4
- package/dist/chunks/menu-37d07z9m.js +0 -4
- package/dist/chunks/monaco-binding-2jnhjjvr.js +0 -4
- package/dist/chunks/monaco-buffer-rp21359g.js +0 -4
- package/dist/chunks/monaco-setup-vhh7gayr.js +0 -4
- package/dist/chunks/notify-h3www560.js +0 -4
- package/dist/chunks/number-field-gw7sq8rn.js +0 -4
- package/dist/chunks/parse-897fvqah.js +0 -4
- package/dist/chunks/platform-hz34wcd7.js +0 -4
- package/dist/chunks/popover-8htra4er.js +0 -4
- package/dist/chunks/publish-panel-exsmjzyj.js +0 -4
- package/dist/chunks/quick-search-pgaqeh0m.js +0 -4
- package/dist/chunks/references-2cgdxnb8.js +0 -4
- package/dist/chunks/right-panel-jf856w5w.js +0 -4
- package/dist/chunks/section-registry-jnf8r5gy.js +0 -4
- package/dist/chunks/select-n3v7gwas.js +0 -4
- package/dist/chunks/split-pff8f3ze.js +0 -4
- package/dist/chunks/studio-0f55h9ye.js +0 -5
- package/dist/chunks/studio-0sqdhnew.js +0 -12
- package/dist/chunks/studio-19x1c75w.js +0 -5
- package/dist/chunks/studio-2sqcndak.js +0 -5
- package/dist/chunks/studio-3aey7dd3.js +0 -5
- package/dist/chunks/studio-3bpdkq2s.js +0 -5
- package/dist/chunks/studio-47y6fbpf.js +0 -5
- package/dist/chunks/studio-4m3826v2.js +0 -5
- package/dist/chunks/studio-4nx2dj1g.js +0 -4
- package/dist/chunks/studio-4xv5s47r.js +0 -9
- package/dist/chunks/studio-5g4t8v6w.js +0 -5
- package/dist/chunks/studio-5py436sf.js +0 -5
- package/dist/chunks/studio-77kt594t.js +0 -5
- package/dist/chunks/studio-8f87n763.js +0 -10
- package/dist/chunks/studio-8mb00b84.js +0 -5
- package/dist/chunks/studio-8p51gq2h.js +0 -6
- package/dist/chunks/studio-954ds834.js +0 -5
- package/dist/chunks/studio-9ft45c83.js +0 -9
- package/dist/chunks/studio-a2138x5k.js +0 -7
- package/dist/chunks/studio-a9yzstqj.js +0 -5
- package/dist/chunks/studio-acdksxx6.js +0 -5
- package/dist/chunks/studio-ar8jcf2y.js +0 -5
- package/dist/chunks/studio-bmn4mn9j.js +0 -7
- package/dist/chunks/studio-e7xxa0sj.js +0 -5
- package/dist/chunks/studio-ecaz25vk.js +0 -5
- package/dist/chunks/studio-ery42sqr.js +0 -4
- package/dist/chunks/studio-fzdywr6p.js +0 -9
- package/dist/chunks/studio-g5fmf0c1.js +0 -5
- package/dist/chunks/studio-g7nse7zb.js +0 -5
- package/dist/chunks/studio-geq1ea6k.js +0 -14
- package/dist/chunks/studio-ghkb32r8.js +0 -5
- package/dist/chunks/studio-ghrhy2y7.js +0 -5
- package/dist/chunks/studio-grawhey1.js +0 -5
- package/dist/chunks/studio-h0xgswew.js +0 -5
- package/dist/chunks/studio-h9c4p2tx.js +0 -5
- package/dist/chunks/studio-jyzj5cxs.js +0 -5
- package/dist/chunks/studio-mgx8yhnz.js +0 -64
- package/dist/chunks/studio-mpb04y1d.js +0 -6
- package/dist/chunks/studio-n17mty64.js +0 -5
- package/dist/chunks/studio-nwh2eqsk.js +0 -6
- package/dist/chunks/studio-q1h883bt.js +0 -16
- package/dist/chunks/studio-q4tv82h7.js +0 -9
- package/dist/chunks/studio-qpqa7vk3.js +0 -5
- package/dist/chunks/studio-qqz8e5gk.js +0 -5
- package/dist/chunks/studio-qvasen3f.js +0 -5
- package/dist/chunks/studio-rggdb3hm.js +0 -5
- package/dist/chunks/studio-sghy936h.js +0 -4
- package/dist/chunks/studio-skprc6ef.js +0 -5
- package/dist/chunks/studio-tt2nactf.js +0 -5
- package/dist/chunks/studio-tyasmc39.js +0 -5
- package/dist/chunks/studio-vk9ezads.js +0 -5
- package/dist/chunks/studio-vzacdfsc.js +0 -4
- package/dist/chunks/studio-w4c0xmrw.js +0 -5
- package/dist/chunks/studio-wfjqhhbk.js +0 -5
- package/dist/chunks/studio-wgh5kxpj.js +0 -5
- package/dist/chunks/studio-xasj0796.js +0 -5
- package/dist/chunks/studio-y78s8r2n.js +0 -5
- package/dist/chunks/studio-z8w183sh.js +0 -5
- package/dist/chunks/studio-zscd9ty4.js +0 -928
- package/dist/chunks/studio-zxp13sss.js +0 -7
- package/dist/chunks/studio-zygnmm01.js +0 -5
- package/dist/chunks/swatch-group-8qh9a3bm.js +0 -4
- package/dist/chunks/tabs-jwthg40w.js +0 -4
- package/dist/chunks/textfield-spy3hc77.js +0 -4
- package/dist/chunks/toast-4d35zd64.js +0 -4
- package/dist/chunks/toast-host-8sjmfed3.js +0 -4
- package/dist/chunks/tooltip-fd6g9xy3.js +0 -4
- package/dist/chunks/tree-h19v1jfz.js +0 -4
- package/dist/chunks/tsMode-gr0m4dzr.js +0 -14
- package/dist/chunks/workspace-m15qtcqp.js +0 -4
- package/dist/chunks/ws-client-nf61frr1.js +0 -4
package/src/platforms/cloud.ts
CHANGED
|
@@ -16,7 +16,8 @@ import type { CollabNegotiation } from "@jxsuite/collab/negotiate";
|
|
|
16
16
|
// Static, and free to be: `reconnect.ts` imports nothing, so no Yjs reaches the initial bundle.
|
|
17
17
|
import { createReconnectScheduler } from "@jxsuite/collab/reconnect";
|
|
18
18
|
import type { WsCollabConnection } from "@jxsuite/collab/client";
|
|
19
|
-
import
|
|
19
|
+
import { isBoolean, isJsonObject, isNonNullObject, isString } from "@jxsuite/schema/guards";
|
|
20
|
+
import type { JsonObject, JsonValue, JxMutableNode, ProjectConfig } from "@jxsuite/schema/types";
|
|
20
21
|
import { componentMetaFrom } from "@jxsuite/schema/component-meta";
|
|
21
22
|
import { streamImport } from "../services/import-client";
|
|
22
23
|
import type {
|
|
@@ -55,7 +56,9 @@ import type {
|
|
|
55
56
|
import { problemDetail, problemSlug } from "@jxsuite/protocol";
|
|
56
57
|
import type { ReadFilesRequest, ReadFilesResult, UploadResult } from "@jxsuite/protocol";
|
|
57
58
|
import { reportActionRequired } from "../account/action-required";
|
|
59
|
+
import type { ActionRequiredReport } from "../account/action-required";
|
|
58
60
|
import { offeredActions, retryHint } from "../platform-errors";
|
|
61
|
+
import type { PlatformErrorInfo } from "../platform-errors";
|
|
59
62
|
import { createReadBatcher } from "./read-batcher";
|
|
60
63
|
import { normalizeProjectDir } from "../utils/project-dir";
|
|
61
64
|
import type { BatchAnswer } from "./read-batcher";
|
|
@@ -79,7 +82,7 @@ interface ProjectInfoWire {
|
|
|
79
82
|
name: string;
|
|
80
83
|
defaultBranch: string;
|
|
81
84
|
permission: "admin" | "write" | "read" | "none";
|
|
82
|
-
projectConfig:
|
|
85
|
+
projectConfig: ProjectConfig | null;
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
/** One row of the platform's project catalogue (`GET /api/v1/projects`). */
|
|
@@ -91,6 +94,64 @@ interface ProjectListWire {
|
|
|
91
94
|
permission: string;
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
/**
|
|
98
|
+
* True when a `POST /files/read` body is the route answering: a JSON object whose `files` is an
|
|
99
|
+
* array. Only that much is checked here, because the batcher checks every row, and `omitted`,
|
|
100
|
+
* before it trusts any of them (read-batcher.ts `settleAnswer`).
|
|
101
|
+
*/
|
|
102
|
+
function isReadFilesAnswer(body: JsonValue): body is JsonObject & ReadFilesResult {
|
|
103
|
+
return isJsonObject(body) && Array.isArray(body["files"]);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The options object {@link StudioPlatform.formatAction} hands through to `POST /format`. */
|
|
107
|
+
type FormatActionPayload = Parameters<NonNullable<StudioPlatform["formatAction"]>>[0];
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A Cloudflare proxy reply (`/api/v1/cf/proxy` here, `/__studio/cf/proxy` on the dev server):
|
|
111
|
+
* Cloudflare's v4 envelope, or the proxy's own `error`.
|
|
112
|
+
*/
|
|
113
|
+
export interface CfProxyEnvelope extends JsonObject {
|
|
114
|
+
success?: boolean;
|
|
115
|
+
result?: JsonValue;
|
|
116
|
+
errors?: { message: string }[];
|
|
117
|
+
error?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Where a session file sits in its project, as `resolveSiteContext` reports it. */
|
|
121
|
+
interface SiteContext {
|
|
122
|
+
sitePath: string | null;
|
|
123
|
+
projectConfig?: ProjectConfig;
|
|
124
|
+
fileRelPath?: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** The signed-in GitHub user, as `getUser` reports them. */
|
|
128
|
+
interface SignedInUser {
|
|
129
|
+
login: string;
|
|
130
|
+
name?: string;
|
|
131
|
+
avatarUrl?: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Body of `POST /api/v1/projects/import`: the repository, and the project folder when not its root. */
|
|
135
|
+
interface ImportProjectRequest {
|
|
136
|
+
owner: string;
|
|
137
|
+
name: string;
|
|
138
|
+
dir?: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* A manifest's `dependencies` or `devDependencies` block, `{}` when absent. The object itself, not
|
|
143
|
+
* a copy, so `addPackage` can write into it.
|
|
144
|
+
*/
|
|
145
|
+
function dependencyBlock(
|
|
146
|
+
pkg: JsonObject,
|
|
147
|
+
key: "dependencies" | "devDependencies",
|
|
148
|
+
): Record<string, string> {
|
|
149
|
+
/* SAFETY: npm's package.json format defines both blocks as an object of package name → version
|
|
150
|
+
range string, and every member here that writes one (`addPackage`, `removePackage`) keeps it
|
|
151
|
+
that way. */
|
|
152
|
+
return (pkg[key] ?? {}) as Record<string, string>;
|
|
153
|
+
}
|
|
154
|
+
|
|
94
155
|
interface SessionEventWire {
|
|
95
156
|
kind: "fs" | "git";
|
|
96
157
|
events?: FsEvent[];
|
|
@@ -110,9 +171,24 @@ interface ErrorBody {
|
|
|
110
171
|
code?: string;
|
|
111
172
|
}
|
|
112
173
|
|
|
174
|
+
/** Parse a fetch Response body as JSON, asserting the expected shape at the boundary. */
|
|
175
|
+
async function readJson<T>(res: Response): Promise<T> {
|
|
176
|
+
/* SAFETY: every response read here comes from a Studio Backend Protocol session route or one of
|
|
177
|
+
the platform's own `/api/v1/*` routes, whose bodies have the shapes @jxsuite/protocol, ../types
|
|
178
|
+
and the wire interfaces in this file describe; each caller names the type of the route it
|
|
179
|
+
called, and a JSON parse always yields a JsonValue. */
|
|
180
|
+
return (await res.json()) as T;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Parse JSON text a session read returned; JSON.parse only ever produces JSON values. */
|
|
184
|
+
function parseJson(text: string): JsonValue {
|
|
185
|
+
// SAFETY: JSON.parse returns exactly the JSON value the text encodes, or throws.
|
|
186
|
+
return JSON.parse(text) as JsonValue;
|
|
187
|
+
}
|
|
188
|
+
|
|
113
189
|
async function errorMessage(res: Response, fallback: string): Promise<string> {
|
|
114
190
|
try {
|
|
115
|
-
return problemDetail(await res
|
|
191
|
+
return problemDetail(await readJson<JsonValue>(res)) ?? fallback;
|
|
116
192
|
} catch {
|
|
117
193
|
return fallback;
|
|
118
194
|
}
|
|
@@ -124,9 +200,9 @@ async function errorMessage(res: Response, fallback: string): Promise<string> {
|
|
|
124
200
|
* says the same. Any other 403 (an expired grant, a revoked share) is not that verdict.
|
|
125
201
|
*/
|
|
126
202
|
async function isReadOnlyRefusal(res: Response): Promise<boolean> {
|
|
127
|
-
let body:
|
|
203
|
+
let body: StructuredBody | null;
|
|
128
204
|
try {
|
|
129
|
-
body =
|
|
205
|
+
body = await readJson<StructuredBody | null>(res);
|
|
130
206
|
} catch {
|
|
131
207
|
return false;
|
|
132
208
|
}
|
|
@@ -136,16 +212,22 @@ async function isReadOnlyRefusal(res: Response): Promise<boolean> {
|
|
|
136
212
|
|
|
137
213
|
/** A failure body's machine-readable members, each one optional and none of them trusted. */
|
|
138
214
|
interface StructuredBody extends ErrorBody {
|
|
139
|
-
type?:
|
|
140
|
-
installUrl?:
|
|
141
|
-
actions?:
|
|
142
|
-
heading?:
|
|
143
|
-
retry?:
|
|
215
|
+
type?: JsonValue;
|
|
216
|
+
installUrl?: JsonValue;
|
|
217
|
+
actions?: JsonValue;
|
|
218
|
+
heading?: JsonValue;
|
|
219
|
+
retry?: JsonValue;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** What {@link platformError} carries on the Error it returns: the HTTP status, plus what it found. */
|
|
223
|
+
interface PlatformErrorDetails extends PlatformErrorInfo {
|
|
224
|
+
status: number;
|
|
144
225
|
}
|
|
145
226
|
|
|
146
227
|
/** The refusal's heading, when the body carries a usable one. */
|
|
147
228
|
function headingOf(body: StructuredBody | null): string | undefined {
|
|
148
|
-
|
|
229
|
+
const heading = body?.heading;
|
|
230
|
+
return isString(heading) && heading ? heading : undefined;
|
|
149
231
|
}
|
|
150
232
|
|
|
151
233
|
/**
|
|
@@ -161,7 +243,7 @@ function headingOf(body: StructuredBody | null): string | undefined {
|
|
|
161
243
|
async function platformError(res: Response, fallback: string): Promise<Error> {
|
|
162
244
|
let body: StructuredBody | null;
|
|
163
245
|
try {
|
|
164
|
-
body =
|
|
246
|
+
body = await readJson<StructuredBody | null>(res);
|
|
165
247
|
} catch {
|
|
166
248
|
body = null;
|
|
167
249
|
}
|
|
@@ -169,21 +251,31 @@ async function platformError(res: Response, fallback: string): Promise<Error> {
|
|
|
169
251
|
const actions = offeredActions(body?.actions);
|
|
170
252
|
const heading = headingOf(body);
|
|
171
253
|
const retry = retryHint(body?.retry);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
254
|
+
const installUrl = body?.installUrl;
|
|
255
|
+
const details: PlatformErrorDetails = { status: res.status };
|
|
256
|
+
if (code) {
|
|
257
|
+
details.code = code;
|
|
258
|
+
}
|
|
259
|
+
if (isString(installUrl)) {
|
|
260
|
+
details.installUrl = installUrl;
|
|
261
|
+
}
|
|
262
|
+
if (actions.length > 0) {
|
|
263
|
+
details.actions = actions;
|
|
264
|
+
}
|
|
265
|
+
if (heading !== undefined) {
|
|
266
|
+
details.heading = heading;
|
|
267
|
+
}
|
|
268
|
+
if (retry !== undefined) {
|
|
269
|
+
details.retry = retry;
|
|
270
|
+
}
|
|
271
|
+
return Object.assign(new Error(problemDetail(body) ?? fallback), details);
|
|
180
272
|
}
|
|
181
273
|
|
|
182
274
|
async function okJson<T>(res: Response, fallback: string): Promise<T> {
|
|
183
275
|
if (!res.ok) {
|
|
184
276
|
throw await platformError(res, fallback);
|
|
185
277
|
}
|
|
186
|
-
return (
|
|
278
|
+
return readJson<T>(res);
|
|
187
279
|
}
|
|
188
280
|
|
|
189
281
|
/** Whether a failure body is an `action-required` refusal, by problem type or legacy code. */
|
|
@@ -201,7 +293,7 @@ function isActionRequiredBody(body: StructuredBody | null): boolean {
|
|
|
201
293
|
async function reportRefusal(res: Response): Promise<void> {
|
|
202
294
|
let body: StructuredBody | null;
|
|
203
295
|
try {
|
|
204
|
-
body =
|
|
296
|
+
body = await readJson<StructuredBody | null>(res);
|
|
205
297
|
} catch {
|
|
206
298
|
return;
|
|
207
299
|
}
|
|
@@ -215,12 +307,17 @@ async function reportRefusal(res: Response): Promise<void> {
|
|
|
215
307
|
const detail = problemDetail(body);
|
|
216
308
|
const heading = headingOf(body);
|
|
217
309
|
const retry = retryHint(body?.retry);
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
310
|
+
const report: ActionRequiredReport = { actions };
|
|
311
|
+
if (detail !== null) {
|
|
312
|
+
report.detail = detail;
|
|
313
|
+
}
|
|
314
|
+
if (heading !== undefined) {
|
|
315
|
+
report.heading = heading;
|
|
316
|
+
}
|
|
317
|
+
if (retry !== undefined) {
|
|
318
|
+
report.retry = retry;
|
|
319
|
+
}
|
|
320
|
+
reportActionRequired(report);
|
|
224
321
|
}
|
|
225
322
|
|
|
226
323
|
/**
|
|
@@ -298,6 +395,34 @@ export function projectRootKey(project: CloudProject): string {
|
|
|
298
395
|
return `${project.owner}/${project.repo}@${revisionSpec(project)}`;
|
|
299
396
|
}
|
|
300
397
|
|
|
398
|
+
/** `GET /api/v1/cf/connection`: the broker's row, with every diagnostic it may send. */
|
|
399
|
+
interface CfConnectionWire {
|
|
400
|
+
connected: boolean;
|
|
401
|
+
accountId?: string | null;
|
|
402
|
+
accountName?: string | null;
|
|
403
|
+
needsReconnect?: boolean;
|
|
404
|
+
needsAccount?: boolean;
|
|
405
|
+
code?: CfConnection["code"];
|
|
406
|
+
reason?: string;
|
|
407
|
+
hasRefreshToken?: boolean;
|
|
408
|
+
expiresAt?: number;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* A same-origin window relay (`{source, status, reason?}`) that a broker or action window posts
|
|
413
|
+
* back. Only `source` is checked; `status` and `reason` are read as the relay protocol sends them.
|
|
414
|
+
*/
|
|
415
|
+
interface WindowRelay {
|
|
416
|
+
source?: string;
|
|
417
|
+
status?: string;
|
|
418
|
+
reason?: string | null;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** True when a `message` event carries a relay from `source` (`"jx-cf"`, `"jx-action"`). */
|
|
422
|
+
function isRelayEvent(event: MessageEvent, source: string): event is MessageEvent<WindowRelay> {
|
|
423
|
+
return isNonNullObject(event.data) && "source" in event.data && event.data.source === source;
|
|
424
|
+
}
|
|
425
|
+
|
|
301
426
|
/**
|
|
302
427
|
* Current hosted Cloudflare connection; null when none is brokered yet.
|
|
303
428
|
*
|
|
@@ -312,33 +437,38 @@ async function fetchCfConnection(): Promise<CfConnection | null> {
|
|
|
312
437
|
if (!res.ok) {
|
|
313
438
|
return null;
|
|
314
439
|
}
|
|
315
|
-
const body =
|
|
316
|
-
connected: boolean;
|
|
317
|
-
accountId?: string | null;
|
|
318
|
-
accountName?: string | null;
|
|
319
|
-
needsReconnect?: boolean;
|
|
320
|
-
needsAccount?: boolean;
|
|
321
|
-
code?: CfConnection["code"];
|
|
322
|
-
reason?: string;
|
|
323
|
-
hasRefreshToken?: boolean;
|
|
324
|
-
expiresAt?: number;
|
|
325
|
-
};
|
|
440
|
+
const body = await readJson<CfConnectionWire>(res);
|
|
326
441
|
/* Cloud never emits {connected: false} to the PAL — no brokered row at all is `null`, and a row
|
|
327
442
|
that cannot be used arrives as connected + needsReconnect. See CfConnection. */
|
|
328
443
|
if (!body.connected) {
|
|
329
444
|
return null;
|
|
330
445
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
446
|
+
const connection: CfConnection = { connected: true };
|
|
447
|
+
if (body.accountId) {
|
|
448
|
+
connection.accountId = body.accountId;
|
|
449
|
+
}
|
|
450
|
+
if (body.accountName) {
|
|
451
|
+
connection.accountName = body.accountName;
|
|
452
|
+
}
|
|
453
|
+
if (body.needsReconnect) {
|
|
454
|
+
connection.needsReconnect = true;
|
|
455
|
+
}
|
|
456
|
+
if (body.needsAccount) {
|
|
457
|
+
connection.needsAccount = true;
|
|
458
|
+
}
|
|
459
|
+
if (body.code) {
|
|
460
|
+
connection.code = body.code;
|
|
461
|
+
}
|
|
462
|
+
if (body.reason) {
|
|
463
|
+
connection.reason = body.reason;
|
|
464
|
+
}
|
|
465
|
+
if (body.hasRefreshToken !== undefined) {
|
|
466
|
+
connection.hasRefreshToken = body.hasRefreshToken;
|
|
467
|
+
}
|
|
468
|
+
if (body.expiresAt !== undefined) {
|
|
469
|
+
connection.expiresAt = body.expiresAt;
|
|
470
|
+
}
|
|
471
|
+
return connection;
|
|
342
472
|
}
|
|
343
473
|
|
|
344
474
|
interface CfConnectFlow {
|
|
@@ -403,10 +533,10 @@ async function runCfConnect(
|
|
|
403
533
|
if (event.origin !== location.origin) {
|
|
404
534
|
return;
|
|
405
535
|
}
|
|
406
|
-
|
|
407
|
-
if (!data || data.source !== "jx-cf") {
|
|
536
|
+
if (!isRelayEvent(event, "jx-cf")) {
|
|
408
537
|
return;
|
|
409
538
|
}
|
|
539
|
+
const { data } = event;
|
|
410
540
|
if (data.status === "error") {
|
|
411
541
|
fail(data.reason ?? "Cloudflare authorization failed");
|
|
412
542
|
return;
|
|
@@ -467,68 +597,76 @@ async function runCfConnect(
|
|
|
467
597
|
interface MeWire {
|
|
468
598
|
installations?: { id: number; account: string | null; manageUrl?: string }[];
|
|
469
599
|
appInstallUrl?: string;
|
|
470
|
-
entries?:
|
|
471
|
-
notices?:
|
|
600
|
+
entries?: JsonValue;
|
|
601
|
+
notices?: JsonValue;
|
|
472
602
|
}
|
|
473
603
|
|
|
474
604
|
/** A wire value as a non-empty string, or undefined. */
|
|
475
|
-
function wireText(value:
|
|
476
|
-
return
|
|
605
|
+
function wireText(value: JsonValue | undefined): string | undefined {
|
|
606
|
+
return isString(value) && value ? value : undefined;
|
|
477
607
|
}
|
|
478
608
|
|
|
479
609
|
/**
|
|
480
610
|
* The account rows a `/me` body holds, each one checked. A row needs an id, a label and a detail to
|
|
481
611
|
* be drawn at all; a malformed one is dropped rather than drawn half-empty.
|
|
482
612
|
*/
|
|
483
|
-
function accountEntries(value:
|
|
613
|
+
function accountEntries(value: JsonValue | undefined): AccountEntry[] {
|
|
484
614
|
if (!Array.isArray(value)) {
|
|
485
615
|
return [];
|
|
486
616
|
}
|
|
487
|
-
return value.flatMap((raw
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
617
|
+
return value.flatMap((raw): AccountEntry[] => {
|
|
618
|
+
// A row that is not an object has no id, label or detail to draw.
|
|
619
|
+
if (!isJsonObject(raw)) {
|
|
620
|
+
return [];
|
|
621
|
+
}
|
|
622
|
+
const id = wireText(raw["id"]);
|
|
623
|
+
const label = wireText(raw["label"]);
|
|
624
|
+
const detail = wireText(raw["detail"]);
|
|
491
625
|
if (!id || !label || !detail) {
|
|
492
626
|
return [];
|
|
493
627
|
}
|
|
494
|
-
const actions = offeredActions(raw
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
];
|
|
628
|
+
const actions = offeredActions(raw["actions"]);
|
|
629
|
+
const { connected } = raw;
|
|
630
|
+
const entry: AccountEntry = { detail, id, label };
|
|
631
|
+
if (isBoolean(connected)) {
|
|
632
|
+
entry.connected = connected;
|
|
633
|
+
}
|
|
634
|
+
if (actions.length > 0) {
|
|
635
|
+
entry.actions = actions;
|
|
636
|
+
}
|
|
637
|
+
return [entry];
|
|
504
638
|
});
|
|
505
639
|
}
|
|
506
640
|
|
|
507
641
|
/** The notices a `/me` body holds, each one checked, on the same terms as the rows. */
|
|
508
|
-
function accountNotices(value:
|
|
642
|
+
function accountNotices(value: JsonValue | undefined): AccountNotice[] {
|
|
509
643
|
if (!Array.isArray(value)) {
|
|
510
644
|
return [];
|
|
511
645
|
}
|
|
512
|
-
return value.flatMap((raw
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
646
|
+
return value.flatMap((raw): AccountNotice[] => {
|
|
647
|
+
// A notice that is not an object has no id, title or message to show.
|
|
648
|
+
if (!isJsonObject(raw)) {
|
|
649
|
+
return [];
|
|
650
|
+
}
|
|
651
|
+
const id = wireText(raw["id"]);
|
|
652
|
+
const title = wireText(raw["title"]);
|
|
653
|
+
const message = wireText(raw["message"]);
|
|
516
654
|
if (!id || !title || !message) {
|
|
517
655
|
return [];
|
|
518
656
|
}
|
|
519
|
-
const actions = offeredActions(raw
|
|
520
|
-
const level = raw
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
];
|
|
657
|
+
const actions = offeredActions(raw["actions"]);
|
|
658
|
+
const { display, level } = raw;
|
|
659
|
+
const notice: AccountNotice = { id, message, title };
|
|
660
|
+
if (level === "info" || level === "warning") {
|
|
661
|
+
notice.level = level;
|
|
662
|
+
}
|
|
663
|
+
if (display === "banner" || display === "toast") {
|
|
664
|
+
notice.display = display;
|
|
665
|
+
}
|
|
666
|
+
if (actions.length > 0) {
|
|
667
|
+
notice.actions = actions;
|
|
668
|
+
}
|
|
669
|
+
return [notice];
|
|
532
670
|
});
|
|
533
671
|
}
|
|
534
672
|
|
|
@@ -578,10 +716,10 @@ function awaitAction(popup: Window): Promise<ActionOutcome> {
|
|
|
578
716
|
if (event.origin !== location.origin) {
|
|
579
717
|
return;
|
|
580
718
|
}
|
|
581
|
-
|
|
582
|
-
if (!data || data.source !== "jx-action") {
|
|
719
|
+
if (!isRelayEvent(event, "jx-action")) {
|
|
583
720
|
return;
|
|
584
721
|
}
|
|
722
|
+
const { data } = event;
|
|
585
723
|
if (data.status === "done" || data.status === "canceled") {
|
|
586
724
|
settle({ status: data.status });
|
|
587
725
|
} else if (data.status === "error") {
|
|
@@ -624,7 +762,7 @@ export function parseRootKey(root: string): CloudProject | null {
|
|
|
624
762
|
/** Every project this account can open, straight off the wire; [] when the catalogue is unreachable. */
|
|
625
763
|
async function fetchProjects(): Promise<ProjectListWire[]> {
|
|
626
764
|
const res = await fetch("/api/v1/projects", { credentials: "include" });
|
|
627
|
-
return res.ok ?
|
|
765
|
+
return res.ok ? readJson<ProjectListWire[]>(res) : [];
|
|
628
766
|
}
|
|
629
767
|
|
|
630
768
|
/**
|
|
@@ -705,7 +843,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
705
843
|
*/
|
|
706
844
|
const projectHint = project ? `${project.owner}/${project.repo}` : null;
|
|
707
845
|
|
|
708
|
-
function postJson(path: string, body:
|
|
846
|
+
function postJson(path: string, body: JsonValue | FormatActionPayload): Promise<Response> {
|
|
709
847
|
return api(path, {
|
|
710
848
|
method: "POST",
|
|
711
849
|
headers: { "Content-Type": "application/json" },
|
|
@@ -752,16 +890,16 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
752
890
|
if (!res.ok) {
|
|
753
891
|
return "retry-singly";
|
|
754
892
|
}
|
|
755
|
-
let body:
|
|
893
|
+
let body: JsonValue;
|
|
756
894
|
try {
|
|
757
|
-
body = await res
|
|
895
|
+
body = await readJson<JsonValue>(res);
|
|
758
896
|
} catch {
|
|
759
897
|
return "unsupported";
|
|
760
898
|
}
|
|
761
|
-
if (!
|
|
899
|
+
if (!isReadFilesAnswer(body)) {
|
|
762
900
|
return "unsupported";
|
|
763
901
|
}
|
|
764
|
-
return body
|
|
902
|
+
return body;
|
|
765
903
|
},
|
|
766
904
|
fetchOne: readOne,
|
|
767
905
|
});
|
|
@@ -774,7 +912,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
774
912
|
*/
|
|
775
913
|
let packageJsonRead: Promise<string> | null = null;
|
|
776
914
|
|
|
777
|
-
async function readPackageJson(): Promise<
|
|
915
|
+
async function readPackageJson(): Promise<JsonObject> {
|
|
778
916
|
let text: string;
|
|
779
917
|
try {
|
|
780
918
|
packageJsonRead ??= reader.read("package.json").finally(() => {
|
|
@@ -786,13 +924,15 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
786
924
|
}
|
|
787
925
|
// Parsed per caller: the members mutate what they get, and must not mutate each other's copy.
|
|
788
926
|
try {
|
|
789
|
-
|
|
927
|
+
// A manifest that is not a JSON object is as unusable as one that is not JSON at all.
|
|
928
|
+
const pkg = parseJson(text);
|
|
929
|
+
return isJsonObject(pkg) ? pkg : {};
|
|
790
930
|
} catch {
|
|
791
931
|
return {};
|
|
792
932
|
}
|
|
793
933
|
}
|
|
794
934
|
|
|
795
|
-
async function writePackageJson(pkg:
|
|
935
|
+
async function writePackageJson(pkg: JsonObject): Promise<void> {
|
|
796
936
|
const res = await api("/file", {
|
|
797
937
|
method: "PUT",
|
|
798
938
|
headers: { "Content-Type": "application/json" },
|
|
@@ -803,6 +943,13 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
803
943
|
}
|
|
804
944
|
}
|
|
805
945
|
|
|
946
|
+
// The bound session's /raw tree; see `documentBaseUrl` below for why both members ride together.
|
|
947
|
+
const repoAssets: Pick<StudioPlatform, "assetSpace" | "documentBaseUrl"> = {};
|
|
948
|
+
if (base) {
|
|
949
|
+
repoAssets.assetSpace = "repo";
|
|
950
|
+
repoAssets.documentBaseUrl = `${base}/raw/`;
|
|
951
|
+
}
|
|
952
|
+
|
|
806
953
|
const platform: StudioPlatform = {
|
|
807
954
|
id: "cloud",
|
|
808
955
|
projectRoot: root,
|
|
@@ -822,7 +969,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
822
969
|
broken, and logs nothing. Declaring "repo" tells Studio to resolve each reference to the
|
|
823
970
|
project file it names and address it under /raw. Until a project is bound there is no /raw to
|
|
824
971
|
address, so the project-less hub declares neither and stays exactly as it was. */
|
|
825
|
-
...
|
|
972
|
+
...repoAssets,
|
|
826
973
|
/* Open Project routes through Studio's repo picker (write-access repos via listRepos +
|
|
827
974
|
importProject) — sessions are URL-bound, so openProject() below never opens a dialog. */
|
|
828
975
|
openProjectPicker: "repo-list",
|
|
@@ -851,7 +998,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
851
998
|
return null;
|
|
852
999
|
}
|
|
853
1000
|
const info = await projectInfo();
|
|
854
|
-
const config =
|
|
1001
|
+
const config: ProjectConfig = info.projectConfig ?? {};
|
|
855
1002
|
return {
|
|
856
1003
|
config,
|
|
857
1004
|
handle: { root, name: config.name || info.name, projectConfig: config },
|
|
@@ -868,7 +1015,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
868
1015
|
meta: { root, name: info.name },
|
|
869
1016
|
info: {
|
|
870
1017
|
isSiteProject: info.projectConfig !== null,
|
|
871
|
-
projectConfig:
|
|
1018
|
+
projectConfig: info.projectConfig ?? null,
|
|
872
1019
|
},
|
|
873
1020
|
};
|
|
874
1021
|
} catch {
|
|
@@ -920,7 +1067,11 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
920
1067
|
const body = await okJson<Partial<UploadResult>>(res, `Upload failed: ${path}`);
|
|
921
1068
|
// The session echoes the path today; falling back to the request keeps a backend that says
|
|
922
1069
|
// Nothing working rather than writing `undefined` into a document.
|
|
923
|
-
|
|
1070
|
+
const uploaded: UploadResult = { path: body.path ?? path };
|
|
1071
|
+
if (body.size !== undefined) {
|
|
1072
|
+
uploaded.size = body.size;
|
|
1073
|
+
}
|
|
1074
|
+
return uploaded;
|
|
924
1075
|
},
|
|
925
1076
|
|
|
926
1077
|
async deleteFile(path: string) {
|
|
@@ -979,7 +1130,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
979
1130
|
}
|
|
980
1131
|
collabNegotiation ??= api("/collab")
|
|
981
1132
|
.then(async (res) =>
|
|
982
|
-
res.ok ? negotiateCollab(await res
|
|
1133
|
+
res.ok ? negotiateCollab(await readJson<JsonValue>(res)) : { offer: [], refused: null },
|
|
983
1134
|
)
|
|
984
1135
|
.catch(() => ({ offer: [], refused: null }));
|
|
985
1136
|
const negotiated = await collabNegotiation;
|
|
@@ -1040,10 +1191,12 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1040
1191
|
const scheme = location.protocol === "https:" ? "wss" : "ws";
|
|
1041
1192
|
const ws = new WebSocket(`${scheme}://${location.host}${base}/events`);
|
|
1042
1193
|
socket = ws;
|
|
1043
|
-
ws.addEventListener("message", (ev: MessageEvent) => {
|
|
1194
|
+
ws.addEventListener("message", (ev: MessageEvent<string>) => {
|
|
1044
1195
|
let payload: SessionEventWire;
|
|
1045
1196
|
try {
|
|
1046
|
-
|
|
1197
|
+
/* SAFETY: the session's /events socket sends only text frames, each one a JSON
|
|
1198
|
+
SessionEventWire (`kind` "fs" with its events, or "git" with the git event). */
|
|
1199
|
+
payload = JSON.parse(ev.data) as SessionEventWire;
|
|
1047
1200
|
} catch {
|
|
1048
1201
|
return;
|
|
1049
1202
|
}
|
|
@@ -1086,7 +1239,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1086
1239
|
if (!res.ok) {
|
|
1087
1240
|
return [];
|
|
1088
1241
|
}
|
|
1089
|
-
const body =
|
|
1242
|
+
const body = await readJson<{ formats?: JsonObject[] }>(res);
|
|
1090
1243
|
return body.formats ?? [];
|
|
1091
1244
|
} catch {
|
|
1092
1245
|
return [];
|
|
@@ -1100,7 +1253,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1100
1253
|
if (!res.ok) {
|
|
1101
1254
|
return [];
|
|
1102
1255
|
}
|
|
1103
|
-
const body =
|
|
1256
|
+
const body = await readJson<{ extensions?: ExtensionsInfo[] }>(res);
|
|
1104
1257
|
return body.extensions ?? [];
|
|
1105
1258
|
} catch {
|
|
1106
1259
|
return [];
|
|
@@ -1130,11 +1283,11 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1130
1283
|
if (!res.ok) {
|
|
1131
1284
|
return [];
|
|
1132
1285
|
}
|
|
1133
|
-
const entries =
|
|
1286
|
+
const entries = await readJson<ExtensionCatalogEntry[]>(res);
|
|
1134
1287
|
const pkg = await readPackageJson();
|
|
1135
1288
|
const declared = new Set([
|
|
1136
|
-
...Object.keys((pkg
|
|
1137
|
-
...Object.keys((pkg
|
|
1289
|
+
...Object.keys(dependencyBlock(pkg, "dependencies")),
|
|
1290
|
+
...Object.keys(dependencyBlock(pkg, "devDependencies")),
|
|
1138
1291
|
]);
|
|
1139
1292
|
for (const entry of entries) {
|
|
1140
1293
|
entry.bundled = true;
|
|
@@ -1161,7 +1314,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1161
1314
|
if (!res.ok) {
|
|
1162
1315
|
return {};
|
|
1163
1316
|
}
|
|
1164
|
-
return
|
|
1317
|
+
return await readJson<ProjectSchemasResponse>(res);
|
|
1165
1318
|
} catch {
|
|
1166
1319
|
return {};
|
|
1167
1320
|
}
|
|
@@ -1173,7 +1326,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1173
1326
|
* backends without the route surface the error below instead of silently mangling format
|
|
1174
1327
|
* documents.
|
|
1175
1328
|
*/
|
|
1176
|
-
async formatAction(payload
|
|
1329
|
+
async formatAction(payload) {
|
|
1177
1330
|
const res = await postJson("/format", payload);
|
|
1178
1331
|
if (!res.ok) {
|
|
1179
1332
|
throw new Error(
|
|
@@ -1183,7 +1336,8 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1183
1336
|
),
|
|
1184
1337
|
);
|
|
1185
1338
|
}
|
|
1186
|
-
|
|
1339
|
+
// A successful answer is `{ result }`: the parsed document, or the serialized text.
|
|
1340
|
+
const data = await readJson<{ result: JxMutableNode | string }>(res);
|
|
1187
1341
|
return data.result;
|
|
1188
1342
|
},
|
|
1189
1343
|
|
|
@@ -1242,12 +1396,15 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1242
1396
|
const metas = await Promise.all(
|
|
1243
1397
|
jsonPaths.map(async (path) => {
|
|
1244
1398
|
try {
|
|
1245
|
-
return componentMetaFrom(
|
|
1399
|
+
return componentMetaFrom(parseJson(await platform.readFile(path)), path);
|
|
1246
1400
|
} catch {
|
|
1247
1401
|
return null; // Not JSON, not readable, or not a component — all the same answer here.
|
|
1248
1402
|
}
|
|
1249
1403
|
}),
|
|
1250
1404
|
);
|
|
1405
|
+
/* SAFETY: the two Bun backends answer discoverComponents with this same componentMetaFrom
|
|
1406
|
+
output, so ComponentMeta's narrower prop typing describes exactly what Studio already
|
|
1407
|
+
receives for these documents everywhere else. */
|
|
1251
1408
|
return metas.filter((m): m is NonNullable<typeof m> => m !== null) as ComponentMeta[];
|
|
1252
1409
|
},
|
|
1253
1410
|
|
|
@@ -1263,36 +1420,34 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1263
1420
|
|
|
1264
1421
|
async addPackage(name: string) {
|
|
1265
1422
|
const pkg = await readPackageJson();
|
|
1266
|
-
const deps = (pkg
|
|
1423
|
+
const deps = dependencyBlock(pkg, "dependencies");
|
|
1267
1424
|
const at = name.lastIndexOf("@");
|
|
1268
1425
|
const [pkgName, version] =
|
|
1269
1426
|
at > 0 ? [name.slice(0, at), name.slice(at + 1)] : [name, "latest"];
|
|
1270
1427
|
deps[pkgName] = version;
|
|
1271
1428
|
pkg["dependencies"] = deps;
|
|
1272
1429
|
await writePackageJson(pkg);
|
|
1273
|
-
return { ok: true, name: pkgName, version };
|
|
1274
1430
|
},
|
|
1275
1431
|
|
|
1276
1432
|
async removePackage(name: string) {
|
|
1277
1433
|
const pkg = await readPackageJson();
|
|
1278
1434
|
for (const key of ["dependencies", "devDependencies"]) {
|
|
1279
|
-
const deps = pkg[key]
|
|
1280
|
-
if (deps && name in deps) {
|
|
1435
|
+
const deps = pkg[key];
|
|
1436
|
+
if (isJsonObject(deps) && name in deps) {
|
|
1281
1437
|
pkg[key] = Object.fromEntries(Object.entries(deps).filter(([dep]) => dep !== name));
|
|
1282
1438
|
}
|
|
1283
1439
|
}
|
|
1284
1440
|
await writePackageJson(pkg);
|
|
1285
|
-
return { ok: true, name };
|
|
1286
1441
|
},
|
|
1287
1442
|
|
|
1288
1443
|
async listPackages(): Promise<PackageInfo[]> {
|
|
1289
1444
|
const pkg = await readPackageJson();
|
|
1290
1445
|
const out: PackageInfo[] = [];
|
|
1291
|
-
const deps = (pkg
|
|
1446
|
+
const deps = dependencyBlock(pkg, "dependencies");
|
|
1292
1447
|
for (const [name, version] of Object.entries(deps)) {
|
|
1293
1448
|
out.push({ name, version });
|
|
1294
1449
|
}
|
|
1295
|
-
const devDeps = (pkg
|
|
1450
|
+
const devDeps = dependencyBlock(pkg, "devDependencies");
|
|
1296
1451
|
for (const [name, version] of Object.entries(devDeps)) {
|
|
1297
1452
|
out.push({ name, version, dev: true });
|
|
1298
1453
|
}
|
|
@@ -1304,12 +1459,15 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1304
1459
|
async resolveSiteContext(filePath: string) {
|
|
1305
1460
|
try {
|
|
1306
1461
|
const info = await projectInfo();
|
|
1307
|
-
const config =
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1462
|
+
const config = info.projectConfig ?? undefined;
|
|
1463
|
+
const context: SiteContext = { sitePath: root };
|
|
1464
|
+
if (config !== undefined) {
|
|
1465
|
+
context.projectConfig = config;
|
|
1466
|
+
}
|
|
1467
|
+
if (filePath !== root) {
|
|
1468
|
+
context.fileRelPath = filePath;
|
|
1469
|
+
}
|
|
1470
|
+
return context;
|
|
1313
1471
|
} catch {
|
|
1314
1472
|
return { sitePath: null };
|
|
1315
1473
|
}
|
|
@@ -1319,7 +1477,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1319
1477
|
try {
|
|
1320
1478
|
const res = await api(`/locate?name=${encodeURIComponent(name)}`);
|
|
1321
1479
|
if (res.ok) {
|
|
1322
|
-
const body =
|
|
1480
|
+
const body = await readJson<{ path?: string | null }>(res);
|
|
1323
1481
|
return body.path ?? null;
|
|
1324
1482
|
}
|
|
1325
1483
|
} catch {
|
|
@@ -1336,7 +1494,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1336
1494
|
if (!res.ok) {
|
|
1337
1495
|
return [];
|
|
1338
1496
|
}
|
|
1339
|
-
return
|
|
1497
|
+
return await readJson<DirEntry[]>(res);
|
|
1340
1498
|
},
|
|
1341
1499
|
|
|
1342
1500
|
// ─── Git (virtual engine in the ProjectSession DO) ─────────────────────
|
|
@@ -1458,7 +1616,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1458
1616
|
if (!res.ok) {
|
|
1459
1617
|
return [];
|
|
1460
1618
|
}
|
|
1461
|
-
return
|
|
1619
|
+
return await readJson<StarterInfo[]>(res);
|
|
1462
1620
|
},
|
|
1463
1621
|
|
|
1464
1622
|
/** Welcome/recents open path: navigate this tab into the project's editor. */
|
|
@@ -1531,14 +1689,14 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1531
1689
|
missing App installation, the offered actions of an `action-required` refusal. */
|
|
1532
1690
|
throw await platformError(res, "Failed to create project");
|
|
1533
1691
|
}
|
|
1534
|
-
const created =
|
|
1692
|
+
const created = await readJson<{ owner: string; name: string; defaultBranch: string }>(res);
|
|
1535
1693
|
return {
|
|
1536
1694
|
root: projectRootKey({
|
|
1537
1695
|
owner: created.owner,
|
|
1538
1696
|
repo: created.name,
|
|
1539
1697
|
branch: created.defaultBranch,
|
|
1540
1698
|
}),
|
|
1541
|
-
config: { name: opts.name }
|
|
1699
|
+
config: { name: opts.name } satisfies ProjectConfig,
|
|
1542
1700
|
};
|
|
1543
1701
|
},
|
|
1544
1702
|
|
|
@@ -1576,17 +1734,20 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1576
1734
|
if (!res.ok) {
|
|
1577
1735
|
return null;
|
|
1578
1736
|
}
|
|
1579
|
-
const me =
|
|
1737
|
+
const me = await readJson<{
|
|
1580
1738
|
user: { login: string; name: string | null; avatar_url: string | null } | null;
|
|
1581
|
-
};
|
|
1739
|
+
}>(res);
|
|
1582
1740
|
if (!me.user) {
|
|
1583
1741
|
return null;
|
|
1584
1742
|
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1743
|
+
const user: SignedInUser = { login: me.user.login };
|
|
1744
|
+
if (me.user.name) {
|
|
1745
|
+
user.name = me.user.name;
|
|
1746
|
+
}
|
|
1747
|
+
if (me.user.avatar_url) {
|
|
1748
|
+
user.avatarUrl = me.user.avatar_url;
|
|
1749
|
+
}
|
|
1750
|
+
return user;
|
|
1590
1751
|
},
|
|
1591
1752
|
|
|
1592
1753
|
/** Repositories reachable through the user's Jx Suite App installations (user + org). */
|
|
@@ -1595,7 +1756,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1595
1756
|
if (!res.ok) {
|
|
1596
1757
|
throw new Error(await errorMessage(res, "Failed to list repositories"));
|
|
1597
1758
|
}
|
|
1598
|
-
const entries =
|
|
1759
|
+
const entries = await readJson<(RepoInfo & { repoId: number })[]>(res);
|
|
1599
1760
|
return entries.map((r) => ({
|
|
1600
1761
|
owner: r.owner,
|
|
1601
1762
|
name: r.name,
|
|
@@ -1636,24 +1797,29 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1636
1797
|
if (dir === null) {
|
|
1637
1798
|
throw new Error("The project folder must stay inside the repository.");
|
|
1638
1799
|
}
|
|
1800
|
+
const importRequest: ImportProjectRequest = { owner: opts.owner, name: opts.name };
|
|
1801
|
+
if (dir) {
|
|
1802
|
+
importRequest.dir = dir;
|
|
1803
|
+
}
|
|
1639
1804
|
const res = await fetch("/api/v1/projects/import", {
|
|
1640
1805
|
method: "POST",
|
|
1641
1806
|
credentials: "include",
|
|
1642
1807
|
headers: { "Content-Type": "application/json" },
|
|
1643
|
-
body: JSON.stringify(
|
|
1808
|
+
body: JSON.stringify(importRequest),
|
|
1644
1809
|
});
|
|
1645
1810
|
const imported = await okJson<{ owner: string; name: string; defaultBranch: string }>(
|
|
1646
1811
|
res,
|
|
1647
1812
|
"Failed to import repository",
|
|
1648
1813
|
);
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
branch: imported.defaultBranch,
|
|
1654
|
-
...(dir ? { dir } : {}),
|
|
1655
|
-
}),
|
|
1814
|
+
const importedProject: CloudProject = {
|
|
1815
|
+
owner: imported.owner,
|
|
1816
|
+
repo: imported.name,
|
|
1817
|
+
branch: imported.defaultBranch,
|
|
1656
1818
|
};
|
|
1819
|
+
if (dir) {
|
|
1820
|
+
importedProject.dir = dir;
|
|
1821
|
+
}
|
|
1822
|
+
return { root: projectRootKey(importedProject) };
|
|
1657
1823
|
},
|
|
1658
1824
|
|
|
1659
1825
|
/**
|
|
@@ -1667,10 +1833,10 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1667
1833
|
if (!res.ok) {
|
|
1668
1834
|
return null;
|
|
1669
1835
|
}
|
|
1670
|
-
const me =
|
|
1836
|
+
const me = await readJson<MeWire>(res);
|
|
1671
1837
|
const entries = accountEntries(me.entries);
|
|
1672
1838
|
const notices = accountNotices(me.notices);
|
|
1673
|
-
|
|
1839
|
+
const status: AccountStatus = {
|
|
1674
1840
|
installations: (me.installations ?? []).map((entry) => {
|
|
1675
1841
|
const installation: AccountStatus["installations"][number] = {
|
|
1676
1842
|
id: entry.id,
|
|
@@ -1681,10 +1847,17 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1681
1847
|
}
|
|
1682
1848
|
return installation;
|
|
1683
1849
|
}),
|
|
1684
|
-
...(me.appInstallUrl ? { appInstallUrl: me.appInstallUrl } : {}),
|
|
1685
|
-
...(entries.length > 0 ? { entries } : {}),
|
|
1686
|
-
...(notices.length > 0 ? { notices } : {}),
|
|
1687
1850
|
};
|
|
1851
|
+
if (me.appInstallUrl) {
|
|
1852
|
+
status.appInstallUrl = me.appInstallUrl;
|
|
1853
|
+
}
|
|
1854
|
+
if (entries.length > 0) {
|
|
1855
|
+
status.entries = entries;
|
|
1856
|
+
}
|
|
1857
|
+
if (notices.length > 0) {
|
|
1858
|
+
status.notices = notices;
|
|
1859
|
+
}
|
|
1860
|
+
return status;
|
|
1688
1861
|
},
|
|
1689
1862
|
|
|
1690
1863
|
/**
|
|
@@ -1823,34 +1996,31 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1823
1996
|
async cfApi(apiPath: string, init?: { method?: string; body?: unknown }) {
|
|
1824
1997
|
/* The project rides in a HEADER, never the query: the platform forwards this path's query
|
|
1825
1998
|
string to Cloudflare verbatim. */
|
|
1826
|
-
const headers: Record<string, string> = {
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1999
|
+
const headers: Record<string, string> = {};
|
|
2000
|
+
if (projectHint) {
|
|
2001
|
+
headers["X-Jx-Project"] = projectHint;
|
|
2002
|
+
}
|
|
2003
|
+
if (init?.body !== undefined) {
|
|
2004
|
+
headers["Content-Type"] = "application/json";
|
|
2005
|
+
}
|
|
2006
|
+
const request: RequestInit = { method: init?.method ?? "GET", credentials: "include" };
|
|
2007
|
+
if (Object.keys(headers).length > 0) {
|
|
2008
|
+
request.headers = headers;
|
|
2009
|
+
}
|
|
2010
|
+
if (init?.body !== undefined) {
|
|
2011
|
+
request.body = JSON.stringify(init.body);
|
|
2012
|
+
}
|
|
2013
|
+
const res = await fetch(`/api/v1/cf/proxy${apiPath}`, request);
|
|
1836
2014
|
/* An `action-required` refusal is the platform's answer, not Cloudflare's, so it is not
|
|
1837
2015
|
dressed as one: it keeps its own sentence and the actions the publish flow offers. Read
|
|
1838
2016
|
from a clone, because every other 403 is Cloudflare's own and is parsed below. */
|
|
1839
2017
|
if (res.status === 403) {
|
|
1840
|
-
const refusal =
|
|
1841
|
-
.clone()
|
|
1842
|
-
.json()
|
|
1843
|
-
.catch(() => null)) as StructuredBody | null;
|
|
2018
|
+
const refusal = await readJson<StructuredBody | null>(res.clone()).catch(() => null);
|
|
1844
2019
|
if (isActionRequiredBody(refusal)) {
|
|
1845
2020
|
throw await platformError(res, "Publishing was refused");
|
|
1846
2021
|
}
|
|
1847
2022
|
}
|
|
1848
|
-
const envelope =
|
|
1849
|
-
success?: boolean;
|
|
1850
|
-
result?: unknown;
|
|
1851
|
-
errors?: { message: string }[];
|
|
1852
|
-
error?: string;
|
|
1853
|
-
};
|
|
2023
|
+
const envelope = await readJson<CfProxyEnvelope>(res);
|
|
1854
2024
|
if (!res.ok || envelope.success === false) {
|
|
1855
2025
|
const message =
|
|
1856
2026
|
envelope.errors?.map((e) => e.message).join("; ") ?? envelope.error ?? res.statusText;
|